Commit 7aae3c16 authored by Stephen Boyd's avatar Stephen Boyd
Browse files

Merge branch 'clk-vc5' into clk-next

* clk-vc5:
  clk: vc5: Enable addition output configurations of the Versaclock
  dt: Add additional option bindings for IDT VersaClock
  clk: vc5: Allow Versaclock driver to support multiple instances
parents ef01ab61 260249f9
Loading
Loading
Loading
Loading
+33 −0
Original line number Diff line number Diff line
@@ -31,6 +31,29 @@ Required properties:
		- 5p49v5933 and
		- 5p49v5935: (optional) property not present or "clkin".

For all output ports, a corresponding, optional child node named OUT1,
OUT2, etc. can represent a each output, and the node can be used to
specify the following:

- itd,mode: can be one of the following:
                 - VC5_LVPECL
                 - VC5_CMOS
                 - VC5_HCSL33
                 - VC5_LVDS
                 - VC5_CMOS2
                 - VC5_CMOSD
                 - VC5_HCSL25

- idt,voltage-microvolts:  can be one of the following
                 - 1800000
                 - 2500000
                 - 3300000
-  idt,slew-percent: Percent of normal, can be one of
                 - 80
                 - 85
                 - 90
                 - 100

==Mapping between clock specifier and physical pins==

When referencing the provided clock in the DT using phandle and
@@ -81,6 +104,16 @@ i2c-master-node {
		/* Connect XIN input to 25MHz reference */
		clocks = <&ref25m>;
		clock-names = "xin";

		OUT1 {
			itd,mode = <VC5_CMOS>;
			idt,voltage-microvolts = <1800000>;
			idt,slew-percent = <80>;
		};
		OUT2 {
			...
		};
		...
	};
};

+193 −47
Original line number Diff line number Diff line
@@ -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 {
@@ -161,30 +187,6 @@ struct vc5_driver_data {
	struct vc5_hw_data	clk_out[VC5_MAX_CLK_OUT_NUM];
};

static const char * const vc5_mux_names[] = {
	"mux"
};

static const char * const vc5_dbl_names[] = {
	"dbl"
};

static const char * const vc5_pfd_names[] = {
	"pfd"
};

static const char * const vc5_pll_names[] = {
	"pll"
};

static const char * const vc5_fod_names[] = {
	"fod0", "fod1", "fod2", "fod3",
};

static const char * const vc5_clk_out_names[] = {
	"out0_sel_i2cb", "out1", "out2", "out3", "out4",
};

/*
 * VersaClock5 i2c regmap
 */
@@ -591,6 +593,17 @@ 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;
}

@@ -690,10 +703,123 @@ static int vc5_map_index_to_output(const enum vc5_model model,
	}
}

static int vc5_update_mode(struct device_node *np_output,
			   struct vc5_hw_data *clk_out)
{
	u32 value;

	if (!of_property_read_u32(np_output, "idt,mode", &value)) {
		clk_out->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:
			clk_out->clk_output_cfg0 |=
			    value << VC5_CLK_OUTPUT_CFG0_CFG_SHIFT;
			break;
		default:
			return -EINVAL;
		}
	}
	return 0;
}

static int vc5_update_power(struct device_node *np_output,
			    struct vc5_hw_data *clk_out)
{
	u32 value;

	if (!of_property_read_u32(np_output,
				  "idt,voltage-microvolts", &value)) {
		clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_PWR_MASK;
		switch (value) {
		case 1800000:
			clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_18;
			break;
		case 2500000:
			clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_25;
			break;
		case 3300000:
			clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_33;
			break;
		default:
			return -EINVAL;
		}
	}
	return 0;
}

static int vc5_update_slew(struct device_node *np_output,
			   struct vc5_hw_data *clk_out)
{
	u32 value;

	if (!of_property_read_u32(np_output, "idt,slew-percent", &value)) {
		clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_SLEW_MASK;
		switch (value) {
		case 80:
			clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_80;
			break;
		case 85:
			clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_85;
			break;
		case 90:
			clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_90;
			break;
		case 100:
			clk_out->clk_output_cfg0 |=
			    VC5_CLK_OUTPUT_CFG0_SLEW_100;
			break;
		default:
			return -EINVAL;
		}
	}
	return 0;
}

static int vc5_get_output_config(struct i2c_client *client,
				 struct vc5_hw_data *clk_out)
{
	struct device_node *np_output;
	char *child_name;
	int ret = 0;

	child_name = kasprintf(GFP_KERNEL, "OUT%d", clk_out->num + 1);
	np_output = of_get_child_by_name(client->dev.of_node, child_name);
	kfree(child_name);
	if (!np_output)
		goto output_done;

	ret = vc5_update_mode(np_output, clk_out);
	if (ret)
		goto output_error;

	ret = vc5_update_power(np_output, clk_out);
	if (ret)
		goto output_error;

	ret = vc5_update_slew(np_output, clk_out);

output_error:
	if (ret) {
		dev_err(&client->dev,
			"Invalid clock output configuration OUT%d\n",
			clk_out->num + 1);
	}

	of_node_put(np_output);

output_done:
	return ret;
}

static const struct of_device_id clk_vc5_of_match[];

static int vc5_probe(struct i2c_client *client,
		     const struct i2c_device_id *id)
static int vc5_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	struct vc5_driver_data *vc5;
	struct clk_init_data init;
@@ -750,12 +876,13 @@ static int vc5_probe(struct i2c_client *client,
		return -EINVAL;
	}

	init.name = vc5_mux_names[0];
	init.name = kasprintf(GFP_KERNEL, "%pOFn.mux", client->dev.of_node);
	init.ops = &vc5_mux_ops;
	init.flags = 0;
	init.parent_names = parent_names;
	vc5->clk_mux.init = &init;
	ret = devm_clk_hw_register(&client->dev, &vc5->clk_mux);
	kfree(init.name);	/* clock framework made a copy of the name */
	if (ret) {
		dev_err(&client->dev, "unable to register %s\n", init.name);
		goto err_clk;
@@ -764,13 +891,16 @@ static int vc5_probe(struct i2c_client *client,
	if (vc5->chip_info->flags & VC5_HAS_PFD_FREQ_DBL) {
		/* Register frequency doubler */
		memset(&init, 0, sizeof(init));
		init.name = vc5_dbl_names[0];
		init.name = kasprintf(GFP_KERNEL, "%pOFn.dbl",
				      client->dev.of_node);
		init.ops = &vc5_dbl_ops;
		init.flags = CLK_SET_RATE_PARENT;
		init.parent_names = vc5_mux_names;
		init.parent_names = parent_names;
		parent_names[0] = clk_hw_get_name(&vc5->clk_mux);
		init.num_parents = 1;
		vc5->clk_mul.init = &init;
		ret = devm_clk_hw_register(&client->dev, &vc5->clk_mul);
		kfree(init.name); /* clock framework made a copy of the name */
		if (ret) {
			dev_err(&client->dev, "unable to register %s\n",
				init.name);
@@ -780,16 +910,18 @@ static int vc5_probe(struct i2c_client *client,

	/* Register PFD */
	memset(&init, 0, sizeof(init));
	init.name = vc5_pfd_names[0];
	init.name = kasprintf(GFP_KERNEL, "%pOFn.pfd", client->dev.of_node);
	init.ops = &vc5_pfd_ops;
	init.flags = CLK_SET_RATE_PARENT;
	init.parent_names = parent_names;
	if (vc5->chip_info->flags & VC5_HAS_PFD_FREQ_DBL)
		init.parent_names = vc5_dbl_names;
		parent_names[0] = clk_hw_get_name(&vc5->clk_mul);
	else
		init.parent_names = vc5_mux_names;
		parent_names[0] = clk_hw_get_name(&vc5->clk_mux);
	init.num_parents = 1;
	vc5->clk_pfd.init = &init;
	ret = devm_clk_hw_register(&client->dev, &vc5->clk_pfd);
	kfree(init.name);	/* clock framework made a copy of the name */
	if (ret) {
		dev_err(&client->dev, "unable to register %s\n", init.name);
		goto err_clk;
@@ -797,15 +929,17 @@ static int vc5_probe(struct i2c_client *client,

	/* Register PLL */
	memset(&init, 0, sizeof(init));
	init.name = vc5_pll_names[0];
	init.name = kasprintf(GFP_KERNEL, "%pOFn.pll", client->dev.of_node);
	init.ops = &vc5_pll_ops;
	init.flags = CLK_SET_RATE_PARENT;
	init.parent_names = vc5_pfd_names;
	init.parent_names = parent_names;
	parent_names[0] = clk_hw_get_name(&vc5->clk_pfd);
	init.num_parents = 1;
	vc5->clk_pll.num = 0;
	vc5->clk_pll.vc5 = vc5;
	vc5->clk_pll.hw.init = &init;
	ret = devm_clk_hw_register(&client->dev, &vc5->clk_pll.hw);
	kfree(init.name); /* clock framework made a copy of the name */
	if (ret) {
		dev_err(&client->dev, "unable to register %s\n", init.name);
		goto err_clk;
@@ -815,15 +949,18 @@ static int vc5_probe(struct i2c_client *client,
	for (n = 0; n < vc5->chip_info->clk_fod_cnt; n++) {
		idx = vc5_map_index_to_output(vc5->chip_info->model, n);
		memset(&init, 0, sizeof(init));
		init.name = vc5_fod_names[idx];
		init.name = kasprintf(GFP_KERNEL, "%pOFn.fod%d",
				      client->dev.of_node, idx);
		init.ops = &vc5_fod_ops;
		init.flags = CLK_SET_RATE_PARENT;
		init.parent_names = vc5_pll_names;
		init.parent_names = parent_names;
		parent_names[0] = clk_hw_get_name(&vc5->clk_pll.hw);
		init.num_parents = 1;
		vc5->clk_fod[n].num = idx;
		vc5->clk_fod[n].vc5 = vc5;
		vc5->clk_fod[n].hw.init = &init;
		ret = devm_clk_hw_register(&client->dev, &vc5->clk_fod[n].hw);
		kfree(init.name); /* clock framework made a copy of the name */
		if (ret) {
			dev_err(&client->dev, "unable to register %s\n",
				init.name);
@@ -833,32 +970,36 @@ static int vc5_probe(struct i2c_client *client,

	/* Register MUX-connected OUT0_I2C_SELB output */
	memset(&init, 0, sizeof(init));
	init.name = vc5_clk_out_names[0];
	init.name = kasprintf(GFP_KERNEL, "%pOFn.out0_sel_i2cb",
			      client->dev.of_node);
	init.ops = &vc5_clk_out_ops;
	init.flags = CLK_SET_RATE_PARENT;
	init.parent_names = vc5_mux_names;
	init.parent_names = parent_names;
	parent_names[0] = clk_hw_get_name(&vc5->clk_mux);
	init.num_parents = 1;
	vc5->clk_out[0].num = idx;
	vc5->clk_out[0].vc5 = vc5;
	vc5->clk_out[0].hw.init = &init;
	ret = devm_clk_hw_register(&client->dev, &vc5->clk_out[0].hw);
	kfree(init.name);	/* clock framework made a copy of the name */
	if (ret) {
		dev_err(&client->dev, "unable to register %s\n",
			init.name);
		dev_err(&client->dev, "unable to register %s\n", init.name);
		goto err_clk;
	}

	/* Register FOD-connected OUTx outputs */
	for (n = 1; n < vc5->chip_info->clk_out_cnt; n++) {
		idx = vc5_map_index_to_output(vc5->chip_info->model, n - 1);
		parent_names[0] = vc5_fod_names[idx];
		parent_names[0] = clk_hw_get_name(&vc5->clk_fod[idx].hw);
		if (n == 1)
			parent_names[1] = vc5_mux_names[0];
			parent_names[1] = clk_hw_get_name(&vc5->clk_mux);
		else
			parent_names[1] = vc5_clk_out_names[n - 1];
			parent_names[1] =
			    clk_hw_get_name(&vc5->clk_out[n - 1].hw);

		memset(&init, 0, sizeof(init));
		init.name = vc5_clk_out_names[idx + 1];
		init.name = kasprintf(GFP_KERNEL, "%pOFn.out%d",
				      client->dev.of_node, idx + 1);
		init.ops = &vc5_clk_out_ops;
		init.flags = CLK_SET_RATE_PARENT;
		init.parent_names = parent_names;
@@ -866,13 +1007,18 @@ static int vc5_probe(struct i2c_client *client,
		vc5->clk_out[n].num = idx;
		vc5->clk_out[n].vc5 = vc5;
		vc5->clk_out[n].hw.init = &init;
		ret = devm_clk_hw_register(&client->dev,
					   &vc5->clk_out[n].hw);
		ret = devm_clk_hw_register(&client->dev, &vc5->clk_out[n].hw);
		kfree(init.name); /* clock framework made a copy of the name */
		if (ret) {
			dev_err(&client->dev, "unable to register %s\n",
				init.name);
			goto err_clk;
		}

		/* Fetch Clock Output configuration from DT (if specified) */
		ret = vc5_get_output_config(client, &vc5->clk_out[n]);
		if (ret)
			goto err_clk;
	}

	ret = of_clk_add_hw_provider(client->dev.of_node, vc5_of_clk_get, vc5);
+13 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */

/* 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