On Mon, Nov 11, 2024 at 01:29:21PM +0200, Ciprian Costea wrote: > From: Ciprian Marian Costea <ciprianmarian.costea@xxxxxxxxxxx> > > 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@xxxxxxxxxxx> > --- > 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 >