Quoting Wen He (2019-08-21 19:08:47) > Add clock driver for QorIQ LS1028A Display output interfaces(LCD, DPHY), > as implemented in TSMC CLN28HPM PLL, this PLL supports the programmable > integer division and range of the display output pixel clock's 27-594MHz. > > Signed-off-by: Wen He <wen.he_1@xxxxxxx> > --- > change in v3: > - remove the OF dependency > - use clk_parent_data instead of parent_name > > drivers/clk/Kconfig | 10 ++ > drivers/clk/Makefile | 1 + > drivers/clk/clk-plldig.c | 283 +++++++++++++++++++++++++++++++++++++++ > 3 files changed, 294 insertions(+) > create mode 100644 drivers/clk/clk-plldig.c > > diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig > index 801fa1cd0321..ab05f342af04 100644 > --- a/drivers/clk/Kconfig > +++ b/drivers/clk/Kconfig > @@ -223,6 +223,16 @@ config CLK_QORIQ > This adds the clock driver support for Freescale QorIQ platforms > using common clock framework. > > +config CLK_LS1028A_PLLDIG > + bool "Clock driver for LS1028A Display output" > + depends on ARCH_LAYERSCAPE || COMPILE_TEST > + default ARCH_LAYERSCAPE > + help > + This driver support the Display output interfaces(LCD, DPHY) pixel clocks > + of the QorIQ Layerscape LS1028A, as implemented TSMC CLN28HPM PLL. Not all > + features of the PLL are currently supported by the driver. By default, > + configured bypass mode with this PLL. > + > config COMMON_CLK_XGENE > bool "Clock driver for APM XGene SoC" > default ARCH_XGENE > diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile > index 0cad76021297..c8e22a764c4d 100644 > --- a/drivers/clk/Makefile > +++ b/drivers/clk/Makefile > @@ -44,6 +44,7 @@ obj-$(CONFIG_COMMON_CLK_OXNAS) += clk-oxnas.o > obj-$(CONFIG_COMMON_CLK_PALMAS) += clk-palmas.o > obj-$(CONFIG_COMMON_CLK_PWM) += clk-pwm.o > obj-$(CONFIG_CLK_QORIQ) += clk-qoriq.o > +obj-$(CONFIG_CLK_LS1028A_PLLDIG) += clk-plldig.o > obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o > obj-$(CONFIG_COMMON_CLK_HI655X) += clk-hi655x.o > obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o > diff --git a/drivers/clk/clk-plldig.c b/drivers/clk/clk-plldig.c > new file mode 100644 > index 000000000000..c5ce80a46fd4 > --- /dev/null > +++ b/drivers/clk/clk-plldig.c > @@ -0,0 +1,283 @@ > +// SPDX-License-Identifier: GPL-2.0 > +// Copyright 2019 NXP Please leave this as C style /* */ comment for the NXP part, but comply with the SPDX comment style of // on the first line. > + > +static long plldig_round_rate(struct clk_hw *hw, unsigned long rate, > + unsigned long *parent) > +{ > + unsigned long parent_rate = *parent; > + unsigned long round_rate; > + u32 mult = 0, rfdphi1 = 0; > + bool found = false; > + > + found = plldig_is_valid_range(rate, parent_rate, &mult, > + &rfdphi1, &round_rate); > + if (!found) { > + pr_warn("%s: unable to round rate %lu, parent rate :%lu\n", > + clk_hw_get_name(hw), rate, parent_rate); > + return 0; This can return an error instead? In fact, you may want to use determine_rate clk op instead. > + } > + > + return round_rate / rfdphi1; > +} > + > +static int plldig_set_rate(struct clk_hw *hw, unsigned long rate, > + unsigned long parent_rate) > +{ > + struct clk_plldig *data = to_clk_plldig(hw); > + bool valid = false; > + unsigned long round_rate = 0; > + u32 rfdphi1 = 0, val, mult = 0, cond = 0; > + int ret = -ETIMEDOUT; > + > + valid = plldig_is_valid_range(rate, parent_rate, &mult, > + &rfdphi1, &round_rate); > + if (!valid) { > + pr_warn("%s: unable to support rate %lu, parent_rate: %lu\n", > + clk_hw_get_name(hw), rate, parent_rate); Shouldn't determine_rate or round_rate make this impossible to hit in practice? I mean that those ops should prevent the rate from being rounded to such a frequency that it becomes invalid. > + return -EINVAL; > + } > + > + val = readl(data->regs + PLLDIG_REG_PLLDV); > + val = mult; > + rfdphi1 = PLLDIG_SET_RFDPHI1(rfdphi1); > + val |= rfdphi1; > + > + writel(val, data->regs + PLLDIG_REG_PLLDV); > + > + /* delay 200us make sure that old lock state is cleared */ > + udelay(200); > + > + /* Wait until PLL is locked or timeout (maximum 1000 usecs) */ > + ret = readl_poll_timeout_atomic(data->regs + PLLDIG_REG_PLLSR, cond, > + cond & PLLDIG_LOCK_MASK, 0, > + USEC_PER_MSEC); > + > + return ret; > +} > + > +static const struct clk_ops plldig_clk_ops = { > + .enable = plldig_enable, > + .disable = plldig_disable, > + .is_enabled = plldig_is_enabled, > + .recalc_rate = plldig_recalc_rate, > + .round_rate = plldig_round_rate, > + .set_rate = plldig_set_rate, > +}; [...] > + > + ret = devm_clk_hw_register(dev, &data->hw); > + if (ret) { > + dev_err(dev, "failed to register %s clock\n", init.name); > + return ret; > + } > + > + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &data->hw); > + if (ret) > + dev_err(dev, "failed adding the clock provider\n"); > + > + return ret; > +} > + > +static int plldig_clk_remove(struct platform_device *pdev) > +{ > + of_clk_del_provider(pdev->dev.of_node); This isn't required. devm already does it. > + return 0; > +} > + > +static const struct of_device_id plldig_clk_id[] = { > + { .compatible = "fsl,ls1028a-plldig", .data = NULL}, You can leave out the data assignment.