i.MX early debug UART clock fixes
Subsystems (3)
v4 cover i.MX early debug UART clock fixes
This series fixes a long standing interaction issue between the kernels early debug facilities and the i.MX clock drivers.
Both earlyprintk and earlycon rely on the bootloader setup clocks for the UARTs to be retained during kernel init. As those clocks are only enabled in hardware, but not referenced in software they (or even their parent clocks) may get disabled as a result of other clocks being en-/disabled during boot.
This results in missing messages from the early debug console until the regular system console takes over, or even worse system hangs in response to unclocked register accesses. This renders those debugging facilities a lot less useful.
The fix implemented here is to check if any of this early debugging is enabled and keep the UART clocks referenced and enabled in that case. The clocks will then only be disabled at the end of kernel init, at which point the real console should have taken over and the early outputs being disabled.
I've runtime tested this on i.MX6Q and i.MX53, other platforms are only compile tested. This is mostly a mechanical change, but still I would appreciate some testing.
Changes since v1:
- reworked logic to require significantly less code in the individual clock drivers
Changes since v2:
- fix checkpatch warning about initdata annotation placement
- make code a bit more compact in the for(...) loops
(since 2 people already commented about this I made up my mind)
- change code style in driver as requested by Shawn
- add more const correctness
(note that this triggers a false positive checkpatch error in the first patch)
Changes since v3:
- make sure to only execute late_initcall code if init functions have been called (fixes OOPS on others SoCs in multiplatform config)
Regards,
Lucas
Lucas Stach (8):
clk: imx: add common logic to detect early UART usage
clk: imx25: retain early UART clocks during kernel init
clk: imx27: retain early UART clocks during kernel init
clk: imx31: retain early UART clocks during kernel init
clk: imx35: retain early UART clocks during kernel init
clk: imx5: retain early UART clocks during kernel init
clk: imx6: retain early UART clocks during kernel init
clk: imx7d: retain early UART clocks during kernel init
drivers/clk/imx/clk-imx25.c | 12 ++++++++++++ drivers/clk/imx/clk-imx27.c | 13 +++++++++++++ drivers/clk/imx/clk-imx31.c | 12 ++++++++++++ drivers/clk/imx/clk-imx35.c | 10 ++++++++++ drivers/clk/imx/clk-imx51-imx53.c | 16 ++++++++++++++++ drivers/clk/imx/clk-imx6q.c | 8 ++++++++ drivers/clk/imx/clk-imx6sl.c | 8 ++++++++ drivers/clk/imx/clk-imx6sx.c | 8 ++++++++ drivers/clk/imx/clk-imx7d.c | 13 +++++++++++++ drivers/clk/imx/clk.c | 38 ++++++++++++++++++++++++++++++++++++++ drivers/clk/imx/clk.h | 1 + 11 files changed, 139 insertions(+)
Signature · 1 line
2.5.1
PATCH v4 1/8 clk: imx: add common logic to detect early UART usage
Both earlycon and eralyprintk depend on the bootloader setup UART clocks being retained. This patch adds the common logic to detect such situations and make the information available to the clock drivers, as well as adding the facilities to disable those clocks at the end of the kernel init.
--- drivers/clk/imx/clk.c | 38 ++++++++++++++++++++++++++++++++++++++ drivers/clk/imx/clk.h | 1 + 2 files changed, 39 insertions(+)
drivers/clk/imx/clk.c +38 -0
@@ -73,3 +73,41 @@ void imx_cscmr1_fixup(u32 *val) *val ^=CSCMR1_FIXUP ;return ; } + +static int imx_keep_uart_clocks__initdata ; +static struct clk **const *imx_uart_clocks__initdata ; + +static int __init imx_keep_uart_clocks_param (char *str) +{ + imx_keep_uart_clocks =1 ; + +return 0 ; +} +__setup_param ("earlycon" , imx_keep_uart_earlycon, + imx_keep_uart_clocks_param,0 ); +__setup_param ("earlyprintk" , imx_keep_uart_earlyprintk, + imx_keep_uart_clocks_param,0 ); + +void __init imx_register_uart_clocks (struct clk **const clks[]) +{ +if (imx_keep_uart_clocks) { +int i; + + imx_uart_clocks = clks; +for (i =0 ; imx_uart_clocks[i]; i++) +clk_prepare_enable (*imx_uart_clocks[i]); + } +} + +static int __init imx_clk_disable_uart (void ) +{ +if (imx_keep_uart_clocks && imx_uart_clocks) { +int i; + +for (i =0 ; imx_uart_clocks[i]; i++) +clk_disable_unprepare (*imx_uart_clocks[i]); + } + +return 0 ; +} +late_initcall_sync (imx_clk_disable_uart);
drivers/clk/imx/clk.h +1 -0
@@ -7,6 +7,7 @@extern spinlock_t imx_ccm_lock;void imx_check_clocks (struct clk *clks[],unsigned int count); +void imx_register_uart_clocks (struct clk **const clks[]);extern void imx_cscmr1_fixup (u32 *val);
Signature · 1 line
2.5.1
Fabio Estevam
reviewerPATCH v4 1/8 clk: imx: add common logic to detect early UART usage
Quoted 1 line · Lucas Stach
Both earlycon and eralyprintk depend on the bootloader setup UART
s/eralyprintk/earlyprintk
drivers/clk/imx/clk.c void imx_cscmr1_fixup(u32 *val) · line 98
Quoted 13 lines
clocks being retained. This patch adds the common logic to detect such situations and make the information available to the clock drivers, as well as adding the facilities to disable those clocks at the end of the kernel init.
drivers/clk/imx/clk.c +8
-0+void __init imx_register_uart_clocks (struct clk **const clks[]) +{ +if (imx_keep_uart_clocks) { +int i; + + imx_uart_clocks = clks; +for (i =0 ; imx_uart_clocks[i]; i++) +clk_prepare_enable (*imx_uart_clocks[i]);
It would be better to check the return value from clk_prepare_enable() and propagate it in the case of error.
drivers/clk/imx/clk.c void imx_cscmr1_fixup(u32 *val) · line 111
Quoted 11 lines
drivers/clk/imx/clk.c +11
-0+ +static int __init imx_clk_disable_uart (void ) +{ +if (imx_keep_uart_clocks && imx_uart_clocks) { +int i; + +for (i =0 ; imx_uart_clocks[i]; i++) +clk_disable_unprepare (*imx_uart_clocks[i]); + } + +return 0 ;
This function could be made 'void' instead.
PATCH v4 1/8 clk: imx: add common logic to detect early UART usage
Am Montag, den 21.09.2015, 14:56 -0300 schrieb Fabio Estevam:
Quoted 10 lines · Lucas Stach
Quoted 1 line
Both earlycon and eralyprintk depend on the bootloader setup UART
s/eralyprintk/earlyprintk
Quoted 4 lines
clocks being retained. This patch adds the common logic to detect such situations and make the information available to the clock drivers, as well as adding the facilities to disable those clocks at the end of the kernel init.
Quoted 12 lines
drivers/clk/imx/clk.c void imx_cscmr1_fixup(u32 *val) · line 98
Quoted 8 lines
drivers/clk/imx/clk.c +8
-0+void __init imx_register_uart_clocks (struct clk **const clks[]) +{ +if (imx_keep_uart_clocks) { +int i; + + imx_uart_clocks = clks; +for (i =0 ; imx_uart_clocks[i]; i++) +clk_prepare_enable (*imx_uart_clocks[i]);It would be better to check the return value from
clk_prepare_enable()and propagate it in the case of error.
I don't see any value of that. We certainly don't want to abort probing of the clock driver just because keeping the debug UART clocks enabled did not work. Everything in this series is about improving the debug experience, so I'm not a fan of adding code that makes it more likely to break (possibly unrelated) things.
Quoted 5 lines
Quoted 5 lines
drivers/clk/imx/clk.c +5
-0+ +static int __init imx_clk_disable_uart (void ) +{ +if (imx_keep_uart_clocks && imx_uart_clocks) { +int i;
Quoted 8 lines
drivers/clk/imx/clk.c void imx_cscmr1_fixup(u32 *val) · line 111
Quoted 6 lines
drivers/clk/imx/clk.c +6
-0+ +for (i =0 ; imx_uart_clocks[i]; i++) +clk_disable_unprepare (*imx_uart_clocks[i]); + } + +return 0 ;This function could be made 'void' instead.
This is an initcall, which have a fixed function prototype.
Regards,
Lucas
Signature · 2 lines
Pengutronix e.K. | Lucas Stach | Industrial Linux Solutions | http://www.pengutronix.de/ |
PATCH v4 2/8 clk: imx25: retain early UART clocks during kernel init
In reply to Lucas StachMake sure to keep UART clocks enabled during kernel init if earlyprintk or earlycon are active.
--- drivers/clk/imx/clk-imx25.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
drivers/clk/imx/clk-imx25.c +12 -0
@@ -86,6 +86,16 @@ enum mx25_clks {static struct clk *clk[clk_max]; +static struct clk **const uart_clks[]__initconst = { + &clk[uart_ipg_per], + &clk[uart1_ipg], + &clk[uart2_ipg], + &clk[uart3_ipg], + &clk[uart4_ipg], + &clk[uart5_ipg], +NULL +}; +static int __init __mx25_clocks_init (unsigned long osc_rate,void __iomem *ccm_base) {
@@ -233,6 +243,8 @@ static int __init __mx25_clocks_init(unsigned long osc_rate,*/ clk_set_parent (clk[cko_sel], clk[ipg]); +imx_register_uart_clocks (uart_clks); +return 0 ; }
Signature · 1 line
2.5.1
PATCH v4 3/8 clk: imx27: retain early UART clocks during kernel init
In reply to Lucas StachMake sure to keep UART clocks enabled during kernel init if earlyprintk or earlycon are active.
--- drivers/clk/imx/clk-imx27.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
drivers/clk/imx/clk-imx27.c +13 -0
@@ -47,6 +47,17 @@ static const char *ssi_sel_clks[] = { "spll_gate", "mpll", };static struct clk *clk[IMX27_CLK_MAX ];static struct clk_onecell_data clk_data; +static struct clk **const uart_clks[]__initconst = { + &clk[IMX27_CLK_PER1_GATE ], + &clk[IMX27_CLK_UART1_IPG_GATE ], + &clk[IMX27_CLK_UART2_IPG_GATE ], + &clk[IMX27_CLK_UART3_IPG_GATE ], + &clk[IMX27_CLK_UART4_IPG_GATE ], + &clk[IMX27_CLK_UART5_IPG_GATE ], + &clk[IMX27_CLK_UART6_IPG_GATE ], +NULL +}; +static void __init _mx27_clocks_init (unsigned long fref) {BUG_ON (!ccm);
@@ -163,6 +174,8 @@ static void __init _mx27_clocks_init(unsigned long fref)clk_prepare_enable (clk[IMX27_CLK_EMI_AHB_GATE ]); +imx_register_uart_clocks (uart_clks); +imx_print_silicon_rev ("i.MX27" ,mx27_revision ()); }
Signature · 1 line
2.5.1
PATCH v4 4/8 clk: imx31: retain early UART clocks during kernel init
In reply to Lucas StachMake sure to keep UART clocks enabled during kernel init if earlyprintk or earlycon are active.
--- drivers/clk/imx/clk-imx31.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
drivers/clk/imx/clk-imx31.c +12 -0
@@ -62,6 +62,16 @@ enum mx31_clks {static struct clk *clk[clk_max];static struct clk_onecell_data clk_data; +static struct clk **const uart_clks[]__initconst = { + &clk[ipg], + &clk[uart1_gate], + &clk[uart2_gate], + &clk[uart3_gate], + &clk[uart4_gate], + &clk[uart5_gate], +NULL +}; +int __init mx31_clocks_init (unsigned long fref) {void __iomem *base;
@@ -200,6 +210,8 @@ int __init mx31_clocks_init(unsigned long fref)mx31_revision ();clk_disable_unprepare (clk[iim_gate]); +imx_register_uart_clocks (uart_clks); +mxc_timer_init (MX31_GPT1_BASE_ADDR ,MX31_INT_GPT ,GPT_TYPE_IMX31 );return 0 ;
Signature · 1 line
2.5.1
PATCH v4 5/8 clk: imx35: retain early UART clocks during kernel init
In reply to Lucas StachMake sure to keep UART clocks enabled during kernel init if earlyprintk or earlycon are active.
--- drivers/clk/imx/clk-imx35.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
drivers/clk/imx/clk-imx35.c +10 -0
@@ -84,6 +84,14 @@ enum mx35_clks {static struct clk *clk[clk_max]; +static struct clk **const uart_clks[]__initconst = { + &clk[ipg], + &clk[uart1_gate], + &clk[uart2_gate], + &clk[uart3_gate], +NULL +}; +int __init mx35_clocks_init (void ) {void __iomem *base;
@@ -296,6 +304,8 @@ int __init mx35_clocks_init(void)*/ clk_prepare_enable (clk[scc_gate]); +imx_register_uart_clocks (uart_clks); +imx_print_silicon_rev ("i.MX35" ,mx35_revision ());mxc_timer_init (MX35_GPT1_BASE_ADDR ,MX35_INT_GPT ,GPT_TYPE_IMX31 );
Signature · 1 line
2.5.1
PATCH v4 6/8 clk: imx5: retain early UART clocks during kernel init
In reply to Lucas StachMake sure to keep UART clocks enabled during kernel init if earlyprintk or earlycon are active.
--- drivers/clk/imx/clk-imx51-imx53.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
drivers/clk/imx/clk-imx51-imx53.c +16 -0
@@ -130,6 +130,20 @@ static const char *cpu_podf_sels[] = { "pll1_sw", "step_sel" };static struct clk *clk[IMX5_CLK_END ];static struct clk_onecell_data clk_data; +static struct clk **const uart_clks[]__initconst = { + &clk[IMX5_CLK_UART1_IPG_GATE ], + &clk[IMX5_CLK_UART1_PER_GATE ], + &clk[IMX5_CLK_UART2_IPG_GATE ], + &clk[IMX5_CLK_UART2_PER_GATE ], + &clk[IMX5_CLK_UART3_IPG_GATE ], + &clk[IMX5_CLK_UART3_PER_GATE ], + &clk[IMX5_CLK_UART4_IPG_GATE ], + &clk[IMX5_CLK_UART4_PER_GATE ], + &clk[IMX5_CLK_UART5_IPG_GATE ], + &clk[IMX5_CLK_UART5_PER_GATE ], +NULL +}; +static void __init mx5_clocks_common_init (void __iomem *ccm_base) { clk[IMX5_CLK_DUMMY ] =imx_clk_fixed ("dummy" ,0 );
@@ -310,6 +324,8 @@ static void __init mx5_clocks_common_init(void __iomem *ccm_base)clk_prepare_enable (clk[IMX5_CLK_TMAX1 ]);clk_prepare_enable (clk[IMX5_CLK_TMAX2 ]);/* esdhc2, fec */ clk_prepare_enable (clk[IMX5_CLK_TMAX3 ]);/* esdhc1, esdhc4 */ + +imx_register_uart_clocks (uart_clks); }static void __init mx50_clocks_init (struct device_node *np)
Signature · 1 line
2.5.1
PATCH v4 7/8 clk: imx6: retain early UART clocks during kernel init
In reply to Lucas StachMake sure to keep UART clocks enabled during kernel init if earlyprintk or earlycon are active.
--- drivers/clk/imx/clk-imx6q.c | 8 ++++++++ drivers/clk/imx/clk-imx6sl.c | 8 ++++++++ drivers/clk/imx/clk-imx6sx.c | 8 ++++++++ 3 files changed, 24 insertions(+)
drivers/clk/imx/clk-imx6q.c +8 -0
@@ -130,6 +130,12 @@ static inline int clk_on_imx6dl(void)return of_machine_is_compatible ("fsl,imx6dl" ); } +static struct clk **const uart_clks[]__initconst = { + &clk[IMX6QDL_CLK_UART_IPG ], + &clk[IMX6QDL_CLK_UART_SERIAL ], +NULL +}; +static void __init imx6q_clocks_init (struct device_node *ccm_node) {struct device_node *np;
@@ -541,5 +547,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)/* All existing boards with PCIe use LVDS1 */ if (IS_ENABLED (CONFIG_PCI_IMX6 ))clk_set_parent (clk[IMX6QDL_CLK_LVDS1_SEL ], clk[IMX6QDL_CLK_SATA_REF_100M ]); + +imx_register_uart_clocks (uart_clks); }CLK_OF_DECLARE (imx6q,"fsl,imx6q-ccm" , imx6q_clocks_init);
drivers/clk/imx/clk-imx6sl.c +8 -0
@@ -184,6 +184,12 @@ void imx6sl_set_wait_clk(bool enter)imx6sl_enable_pll_arm (false); } +static struct clk **const uart_clks[]__initconst = { + &clks[IMX6SL_CLK_UART ], + &clks[IMX6SL_CLK_UART_SERIAL ], +NULL +}; +static void __init imx6sl_clocks_init (struct device_node *ccm_node) {struct device_node *np;
@@ -439,5 +445,7 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)clk_set_parent (clks[IMX6SL_CLK_LCDIF_AXI_SEL ], clks[IMX6SL_CLK_PLL2_PFD2 ]); + +imx_register_uart_clocks (uart_clks); }CLK_OF_DECLARE (imx6sl,"fsl,imx6sl-ccm" , imx6sl_clocks_init);
drivers/clk/imx/clk-imx6sx.c +8 -0
@@ -135,6 +135,12 @@ static u32 share_count_ssi1;static u32 share_count_ssi2;static u32 share_count_ssi3; +static struct clk **const uart_clks[]__initconst = { + &clks[IMX6SX_CLK_UART_IPG ], + &clks[IMX6SX_CLK_UART_SERIAL ], +NULL +}; +static void __init imx6sx_clocks_init (struct device_node *ccm_node) {struct device_node *np;
@@ -557,5 +563,7 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node)clk_set_parent (clks[IMX6SX_CLK_QSPI1_SEL ], clks[IMX6SX_CLK_PLL2_BUS ]);clk_set_parent (clks[IMX6SX_CLK_QSPI2_SEL ], clks[IMX6SX_CLK_PLL2_BUS ]); + +imx_register_uart_clocks (uart_clks); }CLK_OF_DECLARE (imx6sx,"fsl,imx6sx-ccm" , imx6sx_clocks_init);
Signature · 1 line
2.5.1
PATCH v4 8/8 clk: imx7d: retain early UART clocks during kernel init
In reply to Lucas StachMake sure to keep UART clocks enabled during kernel init if earlyprintk or earlycon are active.
--- drivers/clk/imx/clk-imx7d.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
drivers/clk/imx/clk-imx7d.c +13 -0
@@ -363,6 +363,17 @@ static const char *pll_video_bypass_sel[] = { "pll_video_main", "pll_video_main_static struct clk_onecell_data clk_data; +static struct clk **const uart_clks[]__initconst = { + &clks[IMX7D_UART1_ROOT_CLK ], + &clks[IMX7D_UART2_ROOT_CLK ], + &clks[IMX7D_UART3_ROOT_CLK ], + &clks[IMX7D_UART4_ROOT_CLK ], + &clks[IMX7D_UART5_ROOT_CLK ], + &clks[IMX7D_UART6_ROOT_CLK ], + &clks[IMX7D_UART7_ROOT_CLK ], +NULL +}; +static void __init imx7d_clocks_init (struct device_node *ccm_node) {struct device_node *np;
@@ -856,5 +867,7 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node)/* set uart module clock's parent clock source that must be great then 80MHz */ clk_set_parent (clks[IMX7D_UART1_ROOT_SRC ], clks[IMX7D_OSC_24M_CLK ]); +imx_register_uart_clocks (uart_clks); + }CLK_OF_DECLARE (imx7d,"fsl,imx7d-ccm" , imx7d_clocks_init);
Signature · 1 line
2.5.1
v4 cover i.MX early debug UART clock fixes
In reply to Lucas StachQuoted 2 lines · Lucas Stach
This series fixes a long standing interaction issue between the kernels early debug facilities and the i.MX clock drivers.
This is fixing some issue but not regression. As the change basically has influence on all i.MX platforms, my take is that we should get them into linux-next for extensive testing and queue for 4.4. Agree?
Shawn
v4 cover i.MX early debug UART clock fixes
Am Donnerstag, den 24.09.2015, 08:20 -0700 schrieb Shawn Guo:
Quoted 8 lines · Lucas Stach
Quoted 2 lines
This series fixes a long standing interaction issue between the kernels early debug facilities and the i.MX clock drivers.
This is fixing some issue but not regression. As the change basically has influence on all i.MX platforms, my take is that we should get them into linux-next for extensive testing and queue for 4.4. Agree?
Absolutely, this fixes a bug which should mostly hit developers running with earlyprintk or earlycon, so it's definitely not fixes material.
Please queue it up for 4.4.
Thanks,
Lucas
Signature · 2 lines
Pengutronix e.K. | Lucas Stach | Industrial Linux Solutions | http://www.pengutronix.de/ |
v4 cover i.MX early debug UART clock fixes
Quoted 5 lines · Lucas Stach
Am Donnerstag, den 24.09.2015, 08:20 -0700 schrieb Shawn Guo:
Quoted 4 lines · Lucas Stach
Quoted 2 lines
This series fixes a long standing interaction issue between the kernels early debug facilities and the i.MX clock drivers.
Quoted 8 lines
Quoted 4 lines
This is fixing some issue but not regression. As the change basically has influence on all i.MX platforms, my take is that we should get them into linux-next for extensive testing and queue for 4.4. Agree?
Absolutely, this fixes a bug which should mostly hit developers running with earlyprintk or earlycon, so it's definitely not fixes material.
Please queue it up for 4.4.
Patches look mostly ok to me, but this certainly feels like some generic problem that we should handle in the core. Perhaps it would make sense to move this over to using the handoff mechanism Mike is working on, where we keep the clock on until a consumer grabs a reference to the clock?
So I'm not opposed to merging this for 4.4, but I'd like to see it become a generic solution sometime soon, instead of staying as a one-off fix for imx platforms.
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project