The existing driver is expecting the Versaclock to be pre-programmed, and only sets the output frequency. Unfortunately, not all devices are pre-programmed, and the Versaclock chip has more options beyond just the frequency. This patch enables the following additional features: - Programmable voltage: 1.8V, 2.5V, or 3.3V - Slew Percentage of normal: 85%, 90%, or 100% - Output Type: LVPECL, CMOS, HCSL, or LVDS Signed-off-by: Adam Ford <aford173@xxxxxxxxx> diff --git a/drivers/clk/clk-versaclock5.c b/drivers/clk/clk-versaclock5.c index 6e4b09f1f074..a1da88baf28c 100644 --- a/drivers/clk/clk-versaclock5.c +++ b/drivers/clk/clk-versaclock5.c @@ -24,6 +24,8 @@ #include <linux/regmap.h> #include <linux/slab.h> +#include <dt-bindings/clk/versaclock.h> + /* VersaClock5 registers */ #define VC5_OTP_CONTROL 0x00 @@ -89,6 +91,28 @@ /* Clock control register for clock 1,2 */ #define VC5_CLK_OUTPUT_CFG(idx, n) (0x60 + ((idx) * 0x2) + (n)) +#define VC5_CLK_OUTPUT_CFG0_CFG_SHIFT 5 +#define VC5_CLK_OUTPUT_CFG0_CFG_MASK GENMASK(7, VC5_CLK_OUTPUT_CFG0_CFG_SHIFT) + +#define VC5_CLK_OUTPUT_CFG0_CFG_LVPECL (VC5_LVPECL) +#define VC5_CLK_OUTPUT_CFG0_CFG_CMOS (VC5_CMOS) +#define VC5_CLK_OUTPUT_CFG0_CFG_HCSL33 (VC5_HCSL33) +#define VC5_CLK_OUTPUT_CFG0_CFG_LVDS (VC5_LVDS) +#define VC5_CLK_OUTPUT_CFG0_CFG_CMOS2 (VC5_CMOS2) +#define VC5_CLK_OUTPUT_CFG0_CFG_CMOSD (VC5_CMOSD) +#define VC5_CLK_OUTPUT_CFG0_CFG_HCSL25 (VC5_HCSL25) + +#define VC5_CLK_OUTPUT_CFG0_PWR_SHIFT 3 +#define VC5_CLK_OUTPUT_CFG0_PWR_MASK GENMASK(4, VC5_CLK_OUTPUT_CFG0_PWR_SHIFT) +#define VC5_CLK_OUTPUT_CFG0_PWR_18 (0<<VC5_CLK_OUTPUT_CFG0_PWR_SHIFT) +#define VC5_CLK_OUTPUT_CFG0_PWR_25 (2<<VC5_CLK_OUTPUT_CFG0_PWR_SHIFT) +#define VC5_CLK_OUTPUT_CFG0_PWR_33 (3<<VC5_CLK_OUTPUT_CFG0_PWR_SHIFT) +#define VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT 0 +#define VC5_CLK_OUTPUT_CFG0_SLEW_MASK GENMASK(1, VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) +#define VC5_CLK_OUTPUT_CFG0_SLEW_80 (0<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) +#define VC5_CLK_OUTPUT_CFG0_SLEW_85 (1<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) +#define VC5_CLK_OUTPUT_CFG0_SLEW_90 (2<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) +#define VC5_CLK_OUTPUT_CFG0_SLEW_100 (3<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) #define VC5_CLK_OUTPUT_CFG1_EN_CLKBUF BIT(0) #define VC5_CLK_OE_SHDN 0x68 @@ -143,6 +167,8 @@ struct vc5_hw_data { u32 div_int; u32 div_frc; unsigned int num; + unsigned int clk_output_cfg0; + unsigned int clk_output_cfg0_mask; }; struct vc5_driver_data { @@ -567,6 +593,16 @@ static int vc5_clk_out_prepare(struct clk_hw *hw) regmap_update_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1), VC5_CLK_OUTPUT_CFG1_EN_CLKBUF, VC5_CLK_OUTPUT_CFG1_EN_CLKBUF); + if (hwdata->clk_output_cfg0_mask) { + dev_dbg(&vc5->client->dev, "Update output %d mask 0x%0X val 0x%0X\n", + hwdata->num, hwdata->clk_output_cfg0_mask, + hwdata->clk_output_cfg0); + + regmap_update_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 0), + hwdata->clk_output_cfg0_mask, + hwdata->clk_output_cfg0); + } + return 0; } @@ -675,6 +711,9 @@ static int vc5_probe(struct i2c_client *client, struct clk_init_data init; const char *parent_names[2]; unsigned int n, idx = 0; + u32 value; + struct device_node *np_output; + char *child_name; int ret; vc5 = devm_kzalloc(&client->dev, sizeof(*vc5), GFP_KERNEL); @@ -865,6 +904,77 @@ static int vc5_probe(struct i2c_client *client, init.name); goto err_clk; } + + /* Fetch Clock Output configuration from DT (if specified) */ + child_name = kasprintf(GFP_KERNEL, "OUT%d", n); + np_output = of_get_child_by_name(client->dev.of_node, child_name); + kfree(child_name); + if (!np_output) + continue; + if (!(ret || of_property_read_u32(np_output, + "idt,mode", &value))) { + vc5->clk_out[n].clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_CFG_MASK; + switch (value) { + case VC5_CLK_OUTPUT_CFG0_CFG_LVPECL: + case VC5_CLK_OUTPUT_CFG0_CFG_CMOS: + case VC5_CLK_OUTPUT_CFG0_CFG_HCSL33: + case VC5_CLK_OUTPUT_CFG0_CFG_LVDS: + case VC5_CLK_OUTPUT_CFG0_CFG_CMOS2: + case VC5_CLK_OUTPUT_CFG0_CFG_CMOSD: + case VC5_CLK_OUTPUT_CFG0_CFG_HCSL25: + vc5->clk_out[n].clk_output_cfg0 |= value << VC5_CLK_OUTPUT_CFG0_CFG_SHIFT; + break; + default: + ret = -EINVAL; + break; + } + } + if (!(ret || of_property_read_u32(np_output, + "idt,voltage-microvolts", &value))) { + vc5->clk_out[n].clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_PWR_MASK; + switch (value) { + case 1800000: + vc5->clk_out[n].clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_18; + break; + case 2500000: + vc5->clk_out[n].clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_25; + break; + case 3300000: + vc5->clk_out[n].clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_33; + break; + default: + ret = -EINVAL; + break; + } + } + if (!(ret || of_property_read_u32(np_output, + "idt,slew-percent", &value))) { + vc5->clk_out[n].clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_SLEW_MASK; + switch (value) { + case 80: + vc5->clk_out[n].clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_80; + break; + case 85: + vc5->clk_out[n].clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_85; + break; + case 90: + vc5->clk_out[n].clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_90; + break; + case 100: + vc5->clk_out[n].clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_100; + break; + default: + ret = -EINVAL; + break; + } + } + of_node_put(np_output); + if (ret) { + dev_err(&client->dev, + "Invalid clock output configuration OUT%d\n", + n); + goto err_clk; + } } ret = of_clk_add_hw_provider(client->dev.of_node, vc5_of_clk_get, vc5); diff --git a/include/dt-bindings/clk/versaclock.h b/include/dt-bindings/clk/versaclock.h new file mode 100644 index 000000000000..30add3488713 --- /dev/null +++ b/include/dt-bindings/clk/versaclock.h @@ -0,0 +1,13 @@ +/* HEADER */ + +/* This file defines field values used by the versaclock 6 family + * for defining output type + */ + +#define VC5_LVPECL 0 +#define VC5_CMOS 1 +#define VC5_HCSL33 2 +#define VC5_LVDS 3 +#define VC5_CMOS2 4 +#define VC5_CMOSD 5 +#define VC5_HCSL25 6 -- 2.25.1