Message ID | 20241111112921.2411242-3-ciprianmarian.costea@oss.nxp.com |
---|---|
State | New |
Headers | show |
Series | add NXP LINFlexD UART clock support for S32G2/S32G3 | expand |
On Mon, Nov 11, 2024 at 01:29:21PM +0200, Ciprian Costea wrote: > From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> > > Add optional clock 'ipg' and 'lin' support to NXP LINFlexD UART driver, > which is used by S32G2 and S32G3 SoCs. > > LINFlex driver should perform clock management and not rely on a previous > bootloader configuration. > > Clocking support is added as optional in order to not break existing > support for S32V234 SoC. Therefore, there should be no impact if not > providing LINFlexD clocks and continue to rely on a bootloader clock > configuration and enablement. > > Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> > --- > drivers/tty/serial/fsl_linflexuart.c | 82 +++++++++++++++++++++++----- > 1 file changed, 68 insertions(+), 14 deletions(-) > > diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c > index e972df4b188d..66b822f36d06 100644 > --- a/drivers/tty/serial/fsl_linflexuart.c > +++ b/drivers/tty/serial/fsl_linflexuart.c > @@ -3,9 +3,10 @@ > * Freescale LINFlexD UART serial port driver > * > * Copyright 2012-2016 Freescale Semiconductor, Inc. > - * Copyright 2017-2019 NXP > + * Copyright 2017-2019, 2024 NXP > */ > > +#include <linux/clk.h> > #include <linux/console.h> > #include <linux/io.h> > #include <linux/irq.h> > @@ -120,9 +121,29 @@ > > #define PREINIT_DELAY 2000 /* us */ > > +struct linflex_devtype_data { > + const char * const *clks_names; > + int n_clks; > +}; > + > +struct linflex_port { > + struct uart_port port; > + struct clk_bulk_data *clks; > + const struct linflex_devtype_data *devtype_data; > +}; > + > +static const char * const s32v234_clk_names[] = { > + "ipg", "lin", > +}; > + > +static const struct linflex_devtype_data s32v234_data = { > + .clks_names = s32v234_clk_names, > + .n_clks = ARRAY_SIZE(s32v234_clk_names), > +}; > + > static const struct of_device_id linflex_dt_ids[] = { > { > - .compatible = "fsl,s32v234-linflexuart", > + .compatible = "fsl,s32v234-linflexuart", .data = &s32v234_data, > }, > { /* sentinel */ } > }; > @@ -807,12 +828,13 @@ static struct uart_driver linflex_reg = { > static int linflex_probe(struct platform_device *pdev) > { > struct device_node *np = pdev->dev.of_node; > + struct linflex_port *lfport; > struct uart_port *sport; > struct resource *res; > - int ret; > + int i, ret; > > - sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL); > - if (!sport) > + lfport = devm_kzalloc(&pdev->dev, sizeof(*lfport), GFP_KERNEL); > + if (!lfport) > return -ENOMEM; > > ret = of_alias_get_id(np, "serial"); > @@ -826,8 +848,14 @@ static int linflex_probe(struct platform_device *pdev) > return -ENOMEM; > } > > + sport = &lfport->port; > sport->line = ret; > > + lfport->devtype_data = of_device_get_match_data(&pdev->dev); > + if (!lfport->devtype_data) > + return dev_err_probe(&pdev->dev, -ENODEV, > + "Failed to get linflexuart driver data\n"); > + > sport->membase = devm_platform_get_and_ioremap_resource(pdev, 0, &res); > if (IS_ERR(sport->membase)) > return PTR_ERR(sport->membase); > @@ -844,37 +872,63 @@ static int linflex_probe(struct platform_device *pdev) > sport->flags = UPF_BOOT_AUTOCONF; > sport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE); > > - linflex_ports[sport->line] = sport; > + lfport->clks = devm_kmalloc_array(&pdev->dev, lfport->devtype_data->n_clks, > + sizeof(*lfport->clks), GFP_KERNEL); > + if (!lfport->clks) > + return -ENOMEM; > + > + for (i = 0; i < lfport->devtype_data->n_clks; i++) > + lfport->clks[i].id = lfport->devtype_data->clks_names[i]; > + > + ret = devm_clk_bulk_get_optional(&pdev->dev, > + lfport->devtype_data->n_clks, lfport->clks); > + if (ret) > + return dev_err_probe(&pdev->dev, ret, > + "Failed to get linflexuart clocks\n"); > + > + ret = clk_bulk_prepare_enable(lfport->devtype_data->n_clks, > + lfport->clks); > + if (ret) > + return dev_err_probe(&pdev->dev, ret, > + "Failed to enable linflexuart clocks\n"); devm_clk_bulk_get_all_enable() will be more simple. If use clk_bulk_prepare_enable() here, you need add a customer action devm_add_action_or_reset() otherwise if uart_add_one_port() failure, all clocks will be enabled. Frank > > - platform_set_drvdata(pdev, sport); > + linflex_ports[sport->line] = sport; > + platform_set_drvdata(pdev, lfport); > > return uart_add_one_port(&linflex_reg, sport); > } > > static void linflex_remove(struct platform_device *pdev) > { > - struct uart_port *sport = platform_get_drvdata(pdev); > + struct linflex_port *lfport = platform_get_drvdata(pdev); > > - uart_remove_one_port(&linflex_reg, sport); > + uart_remove_one_port(&linflex_reg, &lfport->port); > } > > #ifdef CONFIG_PM_SLEEP > static int linflex_suspend(struct device *dev) > { > - struct uart_port *sport = dev_get_drvdata(dev); > + struct linflex_port *lfport = dev_get_drvdata(dev); > + > + uart_suspend_port(&linflex_reg, &lfport->port); > > - uart_suspend_port(&linflex_reg, sport); > + clk_bulk_disable_unprepare(lfport->devtype_data->n_clks, > + lfport->clks); > > return 0; > } > > static int linflex_resume(struct device *dev) > { > - struct uart_port *sport = dev_get_drvdata(dev); > + struct linflex_port *lfport = dev_get_drvdata(dev); > + int ret; > > - uart_resume_port(&linflex_reg, sport); > + ret = clk_bulk_prepare_enable(lfport->devtype_data->n_clks, > + lfport->clks); > + if (ret) > + return ret; > > - return 0; > + return uart_resume_port(&linflex_reg, &lfport->port); > } > #endif > > -- > 2.45.2 >
On 11/11/2024 11:57 PM, Frank Li wrote: > On Mon, Nov 11, 2024 at 01:29:21PM +0200, Ciprian Costea wrote: >> From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> >> >> Add optional clock 'ipg' and 'lin' support to NXP LINFlexD UART driver, >> which is used by S32G2 and S32G3 SoCs. >> >> LINFlex driver should perform clock management and not rely on a previous >> bootloader configuration. >> >> Clocking support is added as optional in order to not break existing >> support for S32V234 SoC. Therefore, there should be no impact if not >> providing LINFlexD clocks and continue to rely on a bootloader clock >> configuration and enablement. >> >> Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com> >> --- >> drivers/tty/serial/fsl_linflexuart.c | 82 +++++++++++++++++++++++----- >> 1 file changed, 68 insertions(+), 14 deletions(-) >> >> diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c >> index e972df4b188d..66b822f36d06 100644 >> --- a/drivers/tty/serial/fsl_linflexuart.c >> +++ b/drivers/tty/serial/fsl_linflexuart.c >> @@ -3,9 +3,10 @@ >> * Freescale LINFlexD UART serial port driver >> * >> * Copyright 2012-2016 Freescale Semiconductor, Inc. >> - * Copyright 2017-2019 NXP >> + * Copyright 2017-2019, 2024 NXP >> */ >> >> +#include <linux/clk.h> >> #include <linux/console.h> >> #include <linux/io.h> >> #include <linux/irq.h> >> @@ -120,9 +121,29 @@ >> >> #define PREINIT_DELAY 2000 /* us */ >> >> +struct linflex_devtype_data { >> + const char * const *clks_names; >> + int n_clks; >> +}; >> + >> +struct linflex_port { >> + struct uart_port port; >> + struct clk_bulk_data *clks; >> + const struct linflex_devtype_data *devtype_data; >> +}; >> + >> +static const char * const s32v234_clk_names[] = { >> + "ipg", "lin", >> +}; >> + >> +static const struct linflex_devtype_data s32v234_data = { >> + .clks_names = s32v234_clk_names, >> + .n_clks = ARRAY_SIZE(s32v234_clk_names), >> +}; >> + >> static const struct of_device_id linflex_dt_ids[] = { >> { >> - .compatible = "fsl,s32v234-linflexuart", >> + .compatible = "fsl,s32v234-linflexuart", .data = &s32v234_data, >> }, >> { /* sentinel */ } >> }; >> @@ -807,12 +828,13 @@ static struct uart_driver linflex_reg = { >> static int linflex_probe(struct platform_device *pdev) >> { >> struct device_node *np = pdev->dev.of_node; >> + struct linflex_port *lfport; >> struct uart_port *sport; >> struct resource *res; >> - int ret; >> + int i, ret; >> >> - sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL); >> - if (!sport) >> + lfport = devm_kzalloc(&pdev->dev, sizeof(*lfport), GFP_KERNEL); >> + if (!lfport) >> return -ENOMEM; >> >> ret = of_alias_get_id(np, "serial"); >> @@ -826,8 +848,14 @@ static int linflex_probe(struct platform_device *pdev) >> return -ENOMEM; >> } >> >> + sport = &lfport->port; >> sport->line = ret; >> >> + lfport->devtype_data = of_device_get_match_data(&pdev->dev); >> + if (!lfport->devtype_data) >> + return dev_err_probe(&pdev->dev, -ENODEV, >> + "Failed to get linflexuart driver data\n"); >> + >> sport->membase = devm_platform_get_and_ioremap_resource(pdev, 0, &res); >> if (IS_ERR(sport->membase)) >> return PTR_ERR(sport->membase); >> @@ -844,37 +872,63 @@ static int linflex_probe(struct platform_device *pdev) >> sport->flags = UPF_BOOT_AUTOCONF; >> sport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE); >> >> - linflex_ports[sport->line] = sport; >> + lfport->clks = devm_kmalloc_array(&pdev->dev, lfport->devtype_data->n_clks, >> + sizeof(*lfport->clks), GFP_KERNEL); >> + if (!lfport->clks) >> + return -ENOMEM; >> + >> + for (i = 0; i < lfport->devtype_data->n_clks; i++) >> + lfport->clks[i].id = lfport->devtype_data->clks_names[i]; >> + >> + ret = devm_clk_bulk_get_optional(&pdev->dev, >> + lfport->devtype_data->n_clks, lfport->clks); >> + if (ret) >> + return dev_err_probe(&pdev->dev, ret, >> + "Failed to get linflexuart clocks\n"); >> + >> + ret = clk_bulk_prepare_enable(lfport->devtype_data->n_clks, >> + lfport->clks); >> + if (ret) >> + return dev_err_probe(&pdev->dev, ret, >> + "Failed to enable linflexuart clocks\n"); > > devm_clk_bulk_get_all_enable() will be more simple. If use > clk_bulk_prepare_enable() here, you need add a customer action > devm_add_action_or_reset() otherwise if uart_add_one_port() failure, > all clocks will be enabled. > > Frank > Thanks for pointing this out. I will add 'devm_add_action_or_reset' for handling clks disablement and keep clock management as optional in the LINFlexD driver. Ciprian >> >> - platform_set_drvdata(pdev, sport); >> + linflex_ports[sport->line] = sport; >> + platform_set_drvdata(pdev, lfport); >> >> return uart_add_one_port(&linflex_reg, sport); >> } >> >> static void linflex_remove(struct platform_device *pdev) >> { >> - struct uart_port *sport = platform_get_drvdata(pdev); >> + struct linflex_port *lfport = platform_get_drvdata(pdev); >> >> - uart_remove_one_port(&linflex_reg, sport); >> + uart_remove_one_port(&linflex_reg, &lfport->port); >> } >> >> #ifdef CONFIG_PM_SLEEP >> static int linflex_suspend(struct device *dev) >> { >> - struct uart_port *sport = dev_get_drvdata(dev); >> + struct linflex_port *lfport = dev_get_drvdata(dev); >> + >> + uart_suspend_port(&linflex_reg, &lfport->port); >> >> - uart_suspend_port(&linflex_reg, sport); >> + clk_bulk_disable_unprepare(lfport->devtype_data->n_clks, >> + lfport->clks); >> >> return 0; >> } >> >> static int linflex_resume(struct device *dev) >> { >> - struct uart_port *sport = dev_get_drvdata(dev); >> + struct linflex_port *lfport = dev_get_drvdata(dev); >> + int ret; >> >> - uart_resume_port(&linflex_reg, sport); >> + ret = clk_bulk_prepare_enable(lfport->devtype_data->n_clks, >> + lfport->clks); >> + if (ret) >> + return ret; >> >> - return 0; >> + return uart_resume_port(&linflex_reg, &lfport->port); >> } >> #endif >> >> -- >> 2.45.2 >>
diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index e972df4b188d..66b822f36d06 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -3,9 +3,10 @@ * Freescale LINFlexD UART serial port driver * * Copyright 2012-2016 Freescale Semiconductor, Inc. - * Copyright 2017-2019 NXP + * Copyright 2017-2019, 2024 NXP */ +#include <linux/clk.h> #include <linux/console.h> #include <linux/io.h> #include <linux/irq.h> @@ -120,9 +121,29 @@ #define PREINIT_DELAY 2000 /* us */ +struct linflex_devtype_data { + const char * const *clks_names; + int n_clks; +}; + +struct linflex_port { + struct uart_port port; + struct clk_bulk_data *clks; + const struct linflex_devtype_data *devtype_data; +}; + +static const char * const s32v234_clk_names[] = { + "ipg", "lin", +}; + +static const struct linflex_devtype_data s32v234_data = { + .clks_names = s32v234_clk_names, + .n_clks = ARRAY_SIZE(s32v234_clk_names), +}; + static const struct of_device_id linflex_dt_ids[] = { { - .compatible = "fsl,s32v234-linflexuart", + .compatible = "fsl,s32v234-linflexuart", .data = &s32v234_data, }, { /* sentinel */ } }; @@ -807,12 +828,13 @@ static struct uart_driver linflex_reg = { static int linflex_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; + struct linflex_port *lfport; struct uart_port *sport; struct resource *res; - int ret; + int i, ret; - sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL); - if (!sport) + lfport = devm_kzalloc(&pdev->dev, sizeof(*lfport), GFP_KERNEL); + if (!lfport) return -ENOMEM; ret = of_alias_get_id(np, "serial"); @@ -826,8 +848,14 @@ static int linflex_probe(struct platform_device *pdev) return -ENOMEM; } + sport = &lfport->port; sport->line = ret; + lfport->devtype_data = of_device_get_match_data(&pdev->dev); + if (!lfport->devtype_data) + return dev_err_probe(&pdev->dev, -ENODEV, + "Failed to get linflexuart driver data\n"); + sport->membase = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(sport->membase)) return PTR_ERR(sport->membase); @@ -844,37 +872,63 @@ static int linflex_probe(struct platform_device *pdev) sport->flags = UPF_BOOT_AUTOCONF; sport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE); - linflex_ports[sport->line] = sport; + lfport->clks = devm_kmalloc_array(&pdev->dev, lfport->devtype_data->n_clks, + sizeof(*lfport->clks), GFP_KERNEL); + if (!lfport->clks) + return -ENOMEM; + + for (i = 0; i < lfport->devtype_data->n_clks; i++) + lfport->clks[i].id = lfport->devtype_data->clks_names[i]; + + ret = devm_clk_bulk_get_optional(&pdev->dev, + lfport->devtype_data->n_clks, lfport->clks); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to get linflexuart clocks\n"); + + ret = clk_bulk_prepare_enable(lfport->devtype_data->n_clks, + lfport->clks); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to enable linflexuart clocks\n"); - platform_set_drvdata(pdev, sport); + linflex_ports[sport->line] = sport; + platform_set_drvdata(pdev, lfport); return uart_add_one_port(&linflex_reg, sport); } static void linflex_remove(struct platform_device *pdev) { - struct uart_port *sport = platform_get_drvdata(pdev); + struct linflex_port *lfport = platform_get_drvdata(pdev); - uart_remove_one_port(&linflex_reg, sport); + uart_remove_one_port(&linflex_reg, &lfport->port); } #ifdef CONFIG_PM_SLEEP static int linflex_suspend(struct device *dev) { - struct uart_port *sport = dev_get_drvdata(dev); + struct linflex_port *lfport = dev_get_drvdata(dev); + + uart_suspend_port(&linflex_reg, &lfport->port); - uart_suspend_port(&linflex_reg, sport); + clk_bulk_disable_unprepare(lfport->devtype_data->n_clks, + lfport->clks); return 0; } static int linflex_resume(struct device *dev) { - struct uart_port *sport = dev_get_drvdata(dev); + struct linflex_port *lfport = dev_get_drvdata(dev); + int ret; - uart_resume_port(&linflex_reg, sport); + ret = clk_bulk_prepare_enable(lfport->devtype_data->n_clks, + lfport->clks); + if (ret) + return ret; - return 0; + return uart_resume_port(&linflex_reg, &lfport->port); } #endif