On Thu, Jul 2, 2020 at 5:40 PM Luca Ceresoli <luca@xxxxxxxxxxxxxxxx> wrote: > > The Versaclock chips can drive the output pins in several modes: LVDS, > CMOS, LVPECL etc. Allow configuring the output mode from device tree. > > The configuration is optional. If not specified, the mode will not be > configured and the drive mode will be the chip default. > > Signed-off-by: Luca Ceresoli <luca@xxxxxxxxxxxxxxxx> This might be duplicating what's been applied to linux-next already. https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/drivers/clk/clk-versaclock5.c?h=next-20200706&id=260249f929e81d3d5764117fdd6b9e43eb8fb1d5 > --- > drivers/clk/clk-versaclock5.c | 71 +++++++++++++++++++++++++++++++++++ > 1 file changed, 71 insertions(+) > > diff --git a/drivers/clk/clk-versaclock5.c b/drivers/clk/clk-versaclock5.c > index 60c7cf9acde3..eec57286fae0 100644 > --- a/drivers/clk/clk-versaclock5.c > +++ b/drivers/clk/clk-versaclock5.c > @@ -89,6 +89,8 @@ > > /* Clock control register for clock 1,2 */ > #define VC5_CLK_OUTPUT_CFG(idx, n) (0x60 + ((idx) * 0x2) + (n)) > +#define VC5_CLK_OUTPUT_CFG0_MODE_SHIFT 5 > +#define VC5_CLK_OUTPUT_CFG0_MODE_MASK GENMASK(7, 5) > #define VC5_CLK_OUTPUT_CFG1_EN_CLKBUF BIT(0) > > #define VC5_CLK_OE_SHDN 0x68 > @@ -117,6 +119,23 @@ > /* chip has PFD requency doubler */ > #define VC5_HAS_PFD_FREQ_DBL BIT(1) > > +/* > + * Output modes. Values for VC5_CLK_OUTPUT_CFG(idx,0) bits [7:5]. > + * IDT_VC5_OUT_UNKNOWN = keep the hardware default. > + */ > +enum vc5_out_mode { > + IDT_VC5_OUT_MODE_LVPECL = 0, > + IDT_VC5_OUT_MODE_CMOS = 1, > + IDT_VC5_OUT_MODE_HCSL33 = 2, > + IDT_VC5_OUT_MODE_LVDS = 3, > + IDT_VC5_OUT_MODE_CMOS2 = 4, > + IDT_VC5_OUT_MODE_CMOSD = 5, > + IDT_VC5_OUT_MODE_HCSL25 = 6, > + > + IDT_VC5_OUT_NUM_MODES, > + IDT_VC5_OUT_MODE_UNKNOWN = 99, > +}; > + > /* Supported IDT VC5 models. */ > enum vc5_model { > IDT_VC5_5P49V5923, > @@ -149,6 +168,7 @@ struct vc5_out_data { > struct clk_hw hw; > struct vc5_driver_data *vc5; > unsigned int num; > + enum vc5_out_mode mode:8; > }; > > struct vc5_driver_data { > @@ -593,6 +613,13 @@ static int vc5_clk_out_prepare(struct clk_hw *hw) > return ret; > } > > + /* Set output drive mode */ > + if (hwdata->mode != IDT_VC5_OUT_MODE_UNKNOWN) > + regmap_update_bits(vc5->regmap, > + VC5_CLK_OUTPUT_CFG(hwdata->num, 0), > + VC5_CLK_OUTPUT_CFG0_MODE_MASK, > + (hwdata->mode << VC5_CLK_OUTPUT_CFG0_MODE_SHIFT)); > + > /* Enable the clock buffer */ > regmap_update_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1), > VC5_CLK_OUTPUT_CFG1_EN_CLKBUF, > @@ -696,6 +723,46 @@ static int vc5_map_index_to_output(const enum vc5_model model, > } > } > > +static int vc5_parse_dt(struct vc5_driver_data *vc5) > +{ > + struct device *dev = &vc5->client->dev; > + struct device_node *np = dev->of_node; > + struct device_node *child; > + u32 val; > + int n; > + > + for (n = 1; n < vc5->chip_info->clk_out_cnt; n++) > + vc5->clk_out[n].mode = IDT_VC5_OUT_MODE_UNKNOWN; > + > + for_each_child_of_node(np, child) { > + if (of_property_read_u32(child, "reg", &n)) { > + dev_err(dev, "%pOF: missing reg property\n", child); > + break; > + } > + > + if (n == 0 || n >= vc5->chip_info->clk_out_cnt) { > + dev_err(dev, "%pOF: invalid reg %d\n", child, n); > + break; > + } > + > + if (!of_property_read_u32(child, "idt,drive-mode", &val)) { > + if (val >= IDT_VC5_OUT_NUM_MODES) { > + dev_err(dev, "%pOF: invalid idt,drive-mode %u\n", > + child, val); > + break; > + } > + vc5->clk_out[n].mode = val; > + } > + } > + > + if (child) { > + of_node_put(child); > + return -EINVAL; > + } > + > + return 0; > +} > + > static const struct of_device_id clk_vc5_of_match[]; > > static int vc5_probe(struct i2c_client *client, > @@ -723,6 +790,10 @@ static int vc5_probe(struct i2c_client *client, > if (PTR_ERR(vc5->pin_clkin) == -EPROBE_DEFER) > return -EPROBE_DEFER; > > + ret = vc5_parse_dt(vc5); > + if (ret) > + return ret; > + > vc5->regmap = devm_regmap_init_i2c(client, &vc5_regmap_config); > if (IS_ERR(vc5->regmap)) { > dev_err(&client->dev, "failed to allocate register map\n"); > -- > 2.27.0 >