[RFC 2/9] clk: bcm2835: add support for parent selection in DT

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




From: Martin Sperl <kernel@xxxxxxxxxxxxxxxx>

Allow for a per clock custom selection of clocks in the device tree.

Basic setup in dt looks like this:
  clock@BCM2835_CLOCK_PCM {
    reg = <BCM2835_CLOCK_PCM>;
    parent-clock-names = "xosc", "plld_per", "plla_per", "pll_aux_per";
  };

Special care had to be taken when only a single clock is assigned,
as then set_parent_rate is not called - so the register has to get
set up immediately.

To allow for custom order of the clocks (giving preference)
an additional mapping between IDs is required when handling
get/set parent.

The default parent clocks are assumed to be all available clocks
against which the "selected" parents are checked.

Signed-off-by: Martin Sperl <kernel@xxxxxxxxxxxxxxxx>
---
 drivers/clk/bcm/clk-bcm2835.c |  124 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 119 insertions(+), 5 deletions(-)

diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c
index 1c714d0..8fccbd3 100644
--- a/drivers/clk/bcm/clk-bcm2835.c
+++ b/drivers/clk/bcm/clk-bcm2835.c
@@ -702,6 +702,7 @@ struct bcm2835_clock_data {
 
 	const char *const *parents;
 	int num_mux_parents;
+	u8 *parents_idx;
 
 	u32 ctl_reg;
 	u32 div_reg;
@@ -1706,15 +1707,30 @@ static int bcm2835_clock_determine_rate(struct clk_hw *hw,
 	return 0;
 }
 
+static int _bcm2835_clk_set_parent(struct bcm2835_cprman *cprman,
+				   const struct bcm2835_clock_data *data,
+				   u8 index)
+{
+	u8 src;
+
+	/* map the index if we got a map */
+	if (data->parents_idx)
+		index = data->parents_idx[index];
+
+	/* calc the source */
+	src = (index << CM_SRC_SHIFT) & CM_SRC_MASK;
+
+	cprman_write(cprman, data->ctl_reg, src);
+	return 0;
+}
+
 static int bcm2835_clock_set_parent(struct clk_hw *hw, u8 index)
 {
 	struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw);
 	struct bcm2835_cprman *cprman = clock->cprman;
 	const struct bcm2835_clock_data *data = clock->data;
-	u8 src = (index << CM_SRC_SHIFT) & CM_SRC_MASK;
 
-	cprman_write(cprman, data->ctl_reg, src);
-	return 0;
+	return _bcm2835_clk_set_parent(cprman, data, index);
 }
 
 static u8 bcm2835_clock_get_parent(struct clk_hw *hw)
@@ -1723,10 +1739,23 @@ static u8 bcm2835_clock_get_parent(struct clk_hw *hw)
 	struct bcm2835_cprman *cprman = clock->cprman;
 	const struct bcm2835_clock_data *data = clock->data;
 	u32 src = cprman_read(cprman, data->ctl_reg);
+	int i;
 
-	return (src & CM_SRC_MASK) >> CM_SRC_SHIFT;
-}
+	/* translate src to default index */
+	src = (src & CM_SRC_MASK) >> CM_SRC_SHIFT;
+
+	/* without overrides overrides return the value */
+	if (!data->parents_idx)
+		return src;
 
+	/* otherwise iterate over fields to find it */
+	for (i = 0; i < data->num_mux_parents ; i++)
+		if (data->parents_idx[i] == src)
+			return i;
+
+	/* if not found */
+	return -EINVAL;
+}
 
 static const struct clk_ops bcm2835_clock_clk_ops = {
 	.is_prepared = bcm2835_clock_is_on,
@@ -1874,6 +1903,88 @@ bcm2835_register_pll_divider(struct bcm2835_cprman *cprman,
 	return clk;
 }
 
+static void bcm2835_register_clock_of_parents(
+	struct bcm2835_cprman *cprman,
+	struct bcm2835_clock_data *data,
+	struct device_node *nc)
+{
+	struct device *dev = cprman->dev;
+	u8 *parents_idx;
+	const char **parents;
+	const char *name;
+	int err, names, i, j;
+
+	/* check number of strings */
+	names = of_property_count_strings(nc, "parent-clock-names");
+	if (names < 0)
+		return;
+	if (names > data->num_mux_parents) {
+		dev_err(dev,
+			"%s: dt-property parent-clock-names has more entries (%d) than available parent clocks (%d)\n",
+			of_node_full_name(nc), names, data->num_mux_parents);
+		return;
+	}
+
+	/* allocate parents */
+	parents = devm_kzalloc(dev, sizeof(*parents) * names, GFP_KERNEL);
+	if (!parents)
+		return;
+
+	/* allocate parents index */
+	parents_idx = devm_kzalloc(dev, sizeof(*parents_idx) * names,
+				   GFP_KERNEL);
+	if (!parents_idx) {
+		devm_kfree(dev, parents);
+		return;
+	}
+
+	/* and check the parents against list of allowed parents */
+	for (i = 0; i < names; i++) {
+		/* get the string */
+		err = of_property_read_string_index(nc, "parent-clock-names",
+						    i, &name);
+		if (err) {
+			devm_kfree(dev, parents_idx);
+			devm_kfree(dev, parents);
+			dev_err(dev,
+				"%s: could not get parent-clock-names[%d] - %d\n",
+				of_node_full_name(nc), i, err);
+			return;
+		}
+		/* check against available parents - the default list */
+		for (j = 0; j < data->num_mux_parents; j++) {
+			if (strcmp(data->parents[j],  name) == 0) {
+				parents[i] = data->parents[j];
+				parents_idx[i] = j;
+				break;
+			}
+		}
+		/* if there was no match */
+		if (!parents[i]) {
+			devm_kfree(dev, parents_idx);
+			devm_kfree(dev, parents);
+			dev_err(dev,
+				"%s: could not find %s in list of allowed parent clocks\n",
+				of_node_full_name(nc), name);
+			return;
+		}
+	}
+
+	/* finally assign it */
+	data->num_mux_parents = names;
+	data->parents = parents;
+	data->parents_idx = parents_idx;
+
+	/*
+	 * if there is only one parent, then use that one immediately,
+	 * as with only one parent clock_set_parent does not get called
+	 * and it is assumed that everything is set up, so
+	 * we need to do that before anything else...
+	 */
+	if (names == 1)
+		_bcm2835_clk_set_parent(cprman, data, 0);
+}
+
 static const struct bcm2835_clock_data *bcm2835_register_clock_of(
 	struct bcm2835_cprman *cprman,
 	const struct bcm2835_clock_data *data_orig,
@@ -1894,6 +2005,9 @@ static const struct bcm2835_clock_data *bcm2835_register_clock_of(
 		return data_orig;
 	memcpy(data, data_orig, sizeof(*data));
 
+	/* apply overrides */
+	bcm2835_register_clock_of_parents(cprman, data, nc);
+
 	/* and return the result */
 	return data;
 }
-- 
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]
  Powered by Linux