2021-11-18 09:07 AM
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:
Thanks in advance!
PS. I'm using the TFBGA361 Package
Solved! Go to Solution.
2021-11-24 07:22 AM
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
2021-11-22 01:18 AM
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
2021-11-24 07:22 AM
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