cancel
Showing results for 
Search instead for 
Did you mean: 

How to configure Device Trees (TF-A/UBoot/Kernel) to output the MCO2 clock on an external pin of the STM32MP15x?

Moises Araya
Associate III

Here the relevant parts of my device trees:

******************TF-A*******************

&pinctrl {
	rcc_pins_mx: rcc_mx-0 {
		pins {
			pinmux = <STM32_PINMUX('G', 2, AF1)>; /* RCC_MCO_2 */
			bias-disable;
			drive-push-pull;
			slew-rate = <2>;
		};
};
 
&rcc {
	st,hsi-cal;
	st,cal-sec = <60>;
	st,clksrc = <
		CLK_MPU_PLL1P
		CLK_AXI_PLL2P
		CLK_MCU_PLL3P
		CLK_PLL12_HSE
		CLK_PLL3_HSE
		CLK_PLL4_HSE
		CLK_RTC_LSI
		CLK_MCO1_DISABLED
		CLK_MCO2_HSE
	>;
 
	st,clkdiv = <
		1 		/*MPU*/
		0 		/*AXI*/
		0 		/*MCU*/
		1 		/*APB1*/
		1 		/*APB2*/
		1 		/*APB3*/
		1 		/*APB4*/
		2 		/*APB5*/
		0 		/*RTC*/
		0 		/*MCO1*/
		0 		/*MCO2*/
	>;
};
 
&rcc{
	pinctrl-names = "default";
	pinctrl-0 = <&rcc_pins_mx>;
	status = "okay";
	secure-status = "okay";
};

******************UBoot*****************

#ifndef CONFIG_TFABOOT
 
&rcc {
	u-boot,dm-pre-reloc;
	st,clksrc = <
		CLK_MPU_PLL1P
		CLK_AXI_PLL2P
		CLK_MCU_PLL3P
		CLK_PLL12_HSE
		CLK_PLL3_HSE
		CLK_PLL4_HSE
		CLK_RTC_LSI
		CLK_MCO1_DISABLED
		CLK_MCO2_HSE
	>;
	st,clkdiv = <
		1 		/*MPU*/
		0 		/*AXI*/
		0 		/*MCU*/
		1 		/*APB1*/
		1 		/*APB2*/
		1 		/*APB3*/
		1 		/*APB4*/
		2 		/*APB5*/
		0 		/*RTC*/
		0 		/*MCO1*/
		0 		/*MCO2*/
	>;
.
.
.

There are no other appearances of &rcc in UBoot DT.

******************Linux Kernel*****************

&pinctrl {
	u-boot,dm-pre-reloc;
	rcc_pins_mx: rcc_mx-0 {
		u-boot,dm-pre-reloc;
		pins {
			u-boot,dm-pre-reloc;
			pinmux = <STM32_PINMUX('G', 2, AF1)>; /* RCC_MCO_2 */
			bias-disable;
			drive-push-pull;
			slew-rate = <0>;
		};
	};
 
	rcc_sleep_pins_mx: rcc_sleep_mx-0 {
		u-boot,dm-pre-reloc;
		pins {
			u-boot,dm-pre-reloc;
			pinmux = <STM32_PINMUX('G', 2, ANALOG)>; /* RCC_MCO_2 */
		};
	};
};
 
&rcc{
	u-boot,dm-pre-reloc;
	pinctrl-names = "default", "sleep";
	pinctrl-0 = <&rcc_pins_mx>;
	pinctrl-1 = <&rcc_sleep_pins_mx>;
	status = "okay";
 
	/* USER CODE BEGIN rcc */
	/* USER CODE END rcc */
};

My questions:

  • Despite of the device tree configuration, there is no MCO2 output (24MHz clock) at PG2 pin while running TF-A nor UBoot. Any idea why?

  • If I pass the pinctrl-names, pinctrl-0 and pinctrl-1 properties to the rcc node in the kernel DT (see above) it just doesn't boot at all at least unless I remove those properties in &rcc. PG2 is not being used by any other node. Am I missing something?

Thanks in advance!

PS. I'm using the TFBGA361 Package

1 ACCEPTED SOLUTION

Accepted Solutions
Moises Araya
Associate III

Hello @Community member​ , thanks for your response.

I implemented a (quick and dirty) platform driver to activate the MCOx clocks and make sure the output pin is configured with the default state as entered in the device tree (rcc_pins_mx node above). I share my implementation here as a reference for other people having the same issue with MCO1/MCO2:

You have to add an element in the parent node of the kernel's DT and enable it:

/ {
 
        /* other nodes*/
 
	mco2_clk: mco2_clk {
		compatible = "bugfix,mco_clock";
		pinctrl-names = "default", "sleep";
		pinctrl-0 = <&rcc_pins_mx>;
		pinctrl-1 = <&rcc_sleep_pins_mx>;
		clocks = <&rcc CK_MCO2>;
		clock-names = "mco_2";	
	};
        
        /* other nodes*/
}; /*root*/
 
&mco2_clk{
	status = "okay";
};

And compile/load the following driver to activate the clock entered in the "clocks" field of the DT as shown above:

/*  
 *   mco_clock.c - Workaround to get MCOx clocks working
 *
 *  Author:
 *    - Moises Araya <moiaraya@hotmail.com>
 */
 
#include <linux/module.h>		/* Needed by all modules */
#include <linux/kernel.h>		/* Needed for KERN_INFO */
#include <linux/init.h>			/* Needed for the macros */
#include <linux/platform_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/of.h>			/* For DT*/
#include <linux/clk.h>
 
static int mco_clk_probe(struct platform_device *pdev)
{
	int ret;
	struct pinctrl *mco_clk_pin_ctrl;
	struct clk *mco2_clk;
 
	mco_clk_pin_ctrl = pinctrl_get_select_default(&pdev->dev); /*Resource managed pinctrl_get()*/
	if (IS_ERR(mco_clk_pin_ctrl)) {
		printk("PG2 pin initialization failed (MCO2)\n");
		ret = PTR_ERR(mco_clk_pin_ctrl);
		return ret;
	}
 
	mco2_clk = devm_clk_get(&pdev->dev, "mco_2");
	if (IS_ERR(mco2_clk)) {
		printk("MCO2 clock initialization failed\n");
		ret = PTR_ERR(mco2_clk);
		return ret;
	}
 
	ret = clk_prepare_enable(mco2_clk);
	if (ret) {
		printk("Cannot enable  mco2 clock\n");
		return ret;
	}
	
	return 0;
}
 
static int mco_clk_remove(struct platform_device *dev){
	/*************/
	return 0;
}
 
static const struct of_device_id mco_clk_gpio_of_id[] = {
	{.compatible = "bugfix,mco_clock",},
	{}
};
 
static struct platform_driver mco_clk_platform_driver = {
	.probe = mco_clk_probe,
	.remove = mco_clk_remove,
	.driver = {
		   .name = "mco_clock",
		   .owner = THIS_MODULE,
		   .of_match_table = mco_clk_gpio_of_id, 	  
	},
};
 
static int __init mco_clk_init(void)
{
	int ret=0;
	ret = platform_driver_register(&mco_clk_platform_driver);
	if (ret) {
		printk("failed\n");
		return ret;
	}
	return ret;
}
 
static void __exit mco_clk_exit(void)
{
	platform_driver_unregister(&mco_clk_platform_driver);
}
 
module_init(mco_clk_init);
module_exit(mco_clk_exit);
 
MODULE_DESCRIPTION("MCOx clocks workaround");
MODULE_AUTHOR("Moises Araya <moiaraya@hotmail.com>");
MODULE_LICENSE("GPL v2");

I hope it helps anyone.

Kind regards,

Moises

View solution in original post

2 REPLIES 2
Olivier GALLIEN
ST Employee

Hi @Moises Araya​ ,

You are right, passing pinctrl properties in rcc node lead to kernel crash.

We are working to implement it inside BSP, in the meantime solution is to declare MC0 properties inside a dummy driver.

See latest comment in :

Clock output on MCO1 pin (STM32MP157)

Hope it help,

Olivier

Olivier GALLIEN
In order to give better visibility on the answered topics, please click on 'Accept as Solution' on the reply which solved your issue or answered your question.
Moises Araya
Associate III

Hello @Community member​ , thanks for your response.

I implemented a (quick and dirty) platform driver to activate the MCOx clocks and make sure the output pin is configured with the default state as entered in the device tree (rcc_pins_mx node above). I share my implementation here as a reference for other people having the same issue with MCO1/MCO2:

You have to add an element in the parent node of the kernel's DT and enable it:

/ {
 
        /* other nodes*/
 
	mco2_clk: mco2_clk {
		compatible = "bugfix,mco_clock";
		pinctrl-names = "default", "sleep";
		pinctrl-0 = <&rcc_pins_mx>;
		pinctrl-1 = <&rcc_sleep_pins_mx>;
		clocks = <&rcc CK_MCO2>;
		clock-names = "mco_2";	
	};
        
        /* other nodes*/
}; /*root*/
 
&mco2_clk{
	status = "okay";
};

And compile/load the following driver to activate the clock entered in the "clocks" field of the DT as shown above:

/*  
 *   mco_clock.c - Workaround to get MCOx clocks working
 *
 *  Author:
 *    - Moises Araya <moiaraya@hotmail.com>
 */
 
#include <linux/module.h>		/* Needed by all modules */
#include <linux/kernel.h>		/* Needed for KERN_INFO */
#include <linux/init.h>			/* Needed for the macros */
#include <linux/platform_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/of.h>			/* For DT*/
#include <linux/clk.h>
 
static int mco_clk_probe(struct platform_device *pdev)
{
	int ret;
	struct pinctrl *mco_clk_pin_ctrl;
	struct clk *mco2_clk;
 
	mco_clk_pin_ctrl = pinctrl_get_select_default(&pdev->dev); /*Resource managed pinctrl_get()*/
	if (IS_ERR(mco_clk_pin_ctrl)) {
		printk("PG2 pin initialization failed (MCO2)\n");
		ret = PTR_ERR(mco_clk_pin_ctrl);
		return ret;
	}
 
	mco2_clk = devm_clk_get(&pdev->dev, "mco_2");
	if (IS_ERR(mco2_clk)) {
		printk("MCO2 clock initialization failed\n");
		ret = PTR_ERR(mco2_clk);
		return ret;
	}
 
	ret = clk_prepare_enable(mco2_clk);
	if (ret) {
		printk("Cannot enable  mco2 clock\n");
		return ret;
	}
	
	return 0;
}
 
static int mco_clk_remove(struct platform_device *dev){
	/*************/
	return 0;
}
 
static const struct of_device_id mco_clk_gpio_of_id[] = {
	{.compatible = "bugfix,mco_clock",},
	{}
};
 
static struct platform_driver mco_clk_platform_driver = {
	.probe = mco_clk_probe,
	.remove = mco_clk_remove,
	.driver = {
		   .name = "mco_clock",
		   .owner = THIS_MODULE,
		   .of_match_table = mco_clk_gpio_of_id, 	  
	},
};
 
static int __init mco_clk_init(void)
{
	int ret=0;
	ret = platform_driver_register(&mco_clk_platform_driver);
	if (ret) {
		printk("failed\n");
		return ret;
	}
	return ret;
}
 
static void __exit mco_clk_exit(void)
{
	platform_driver_unregister(&mco_clk_platform_driver);
}
 
module_init(mco_clk_init);
module_exit(mco_clk_exit);
 
MODULE_DESCRIPTION("MCOx clocks workaround");
MODULE_AUTHOR("Moises Araya <moiaraya@hotmail.com>");
MODULE_LICENSE("GPL v2");

I hope it helps anyone.

Kind regards,

Moises