You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
239 lines
7.1 KiB
239 lines
7.1 KiB
8 years ago
|
From 876f8ef32ec09fb566cc6ecdf4c96a8348f135b6 Mon Sep 17 00:00:00 2001
|
||
|
From: Eric Anholt <eric@anholt.net>
|
||
|
Date: Wed, 18 Jan 2017 07:31:56 +1100
|
||
|
Subject: [PATCH] clk: bcm2835: Register the DSI0/DSI1 pixel clocks.
|
||
|
|
||
|
The DSI pixel clocks are muxed from clocks generated in the analog phy
|
||
|
by the DSI driver. In order to set them as parents, we need to do the
|
||
|
same name lookup dance on them as we do for our root oscillator.
|
||
|
|
||
|
Signed-off-by: Eric Anholt <eric@anholt.net>
|
||
|
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||
|
(cherry picked from commit 8a39e9fa578229fd4604266c6ebb1a3a77d7994c)
|
||
|
---
|
||
|
.../bindings/clock/brcm,bcm2835-cprman.txt | 15 ++-
|
||
|
drivers/clk/bcm/clk-bcm2835.c | 121 +++++++++++++++++++--
|
||
|
include/dt-bindings/clock/bcm2835.h | 2 +
|
||
|
3 files changed, 125 insertions(+), 13 deletions(-)
|
||
|
|
||
|
--- a/Documentation/devicetree/bindings/clock/brcm,bcm2835-cprman.txt
|
||
|
+++ b/Documentation/devicetree/bindings/clock/brcm,bcm2835-cprman.txt
|
||
|
@@ -16,7 +16,20 @@ Required properties:
|
||
|
- #clock-cells: Should be <1>. The permitted clock-specifier values can be
|
||
|
found in include/dt-bindings/clock/bcm2835.h
|
||
|
- reg: Specifies base physical address and size of the registers
|
||
|
-- clocks: The external oscillator clock phandle
|
||
|
+- clocks: phandles to the parent clocks used as input to the module, in
|
||
|
+ the following order:
|
||
|
+
|
||
|
+ - External oscillator
|
||
|
+ - DSI0 byte clock
|
||
|
+ - DSI0 DDR2 clock
|
||
|
+ - DSI0 DDR clock
|
||
|
+ - DSI1 byte clock
|
||
|
+ - DSI1 DDR2 clock
|
||
|
+ - DSI1 DDR clock
|
||
|
+
|
||
|
+ Only external oscillator is required. The DSI clocks may
|
||
|
+ not be present, in which case their children will be
|
||
|
+ unusable.
|
||
|
|
||
|
Example:
|
||
|
|
||
|
--- a/drivers/clk/bcm/clk-bcm2835.c
|
||
|
+++ b/drivers/clk/bcm/clk-bcm2835.c
|
||
|
@@ -297,11 +297,32 @@
|
||
|
#define LOCK_TIMEOUT_NS 100000000
|
||
|
#define BCM2835_MAX_FB_RATE 1750000000u
|
||
|
|
||
|
+/*
|
||
|
+ * Names of clocks used within the driver that need to be replaced
|
||
|
+ * with an external parent's name. This array is in the order that
|
||
|
+ * the clocks node in the DT references external clocks.
|
||
|
+ */
|
||
|
+static const char *const cprman_parent_names[] = {
|
||
|
+ "xosc",
|
||
|
+ "dsi0_byte",
|
||
|
+ "dsi0_ddr2",
|
||
|
+ "dsi0_ddr",
|
||
|
+ "dsi1_byte",
|
||
|
+ "dsi1_ddr2",
|
||
|
+ "dsi1_ddr",
|
||
|
+};
|
||
|
+
|
||
|
struct bcm2835_cprman {
|
||
|
struct device *dev;
|
||
|
void __iomem *regs;
|
||
|
spinlock_t regs_lock; /* spinlock for all clocks */
|
||
|
- const char *osc_name;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Real names of cprman clock parents looked up through
|
||
|
+ * of_clk_get_parent_name(), which will be used in the
|
||
|
+ * parent_names[] arrays for clock registration.
|
||
|
+ */
|
||
|
+ const char *real_parent_names[ARRAY_SIZE(cprman_parent_names)];
|
||
|
|
||
|
/* Must be last */
|
||
|
struct clk_hw_onecell_data onecell;
|
||
|
@@ -907,6 +928,9 @@ static long bcm2835_clock_rate_from_divi
|
||
|
const struct bcm2835_clock_data *data = clock->data;
|
||
|
u64 temp;
|
||
|
|
||
|
+ if (data->int_bits == 0 && data->frac_bits == 0)
|
||
|
+ return parent_rate;
|
||
|
+
|
||
|
/*
|
||
|
* The divisor is a 12.12 fixed point field, but only some of
|
||
|
* the bits are populated in any given clock.
|
||
|
@@ -930,7 +954,12 @@ static unsigned long bcm2835_clock_get_r
|
||
|
struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw);
|
||
|
struct bcm2835_cprman *cprman = clock->cprman;
|
||
|
const struct bcm2835_clock_data *data = clock->data;
|
||
|
- u32 div = cprman_read(cprman, data->div_reg);
|
||
|
+ u32 div;
|
||
|
+
|
||
|
+ if (data->int_bits == 0 && data->frac_bits == 0)
|
||
|
+ return parent_rate;
|
||
|
+
|
||
|
+ div = cprman_read(cprman, data->div_reg);
|
||
|
|
||
|
return bcm2835_clock_rate_from_divisor(clock, parent_rate, div);
|
||
|
}
|
||
|
@@ -1209,7 +1238,7 @@ static struct clk_hw *bcm2835_register_p
|
||
|
memset(&init, 0, sizeof(init));
|
||
|
|
||
|
/* All of the PLLs derive from the external oscillator. */
|
||
|
- init.parent_names = &cprman->osc_name;
|
||
|
+ init.parent_names = &cprman->real_parent_names[0];
|
||
|
init.num_parents = 1;
|
||
|
init.name = data->name;
|
||
|
init.ops = &bcm2835_pll_clk_ops;
|
||
|
@@ -1295,18 +1324,22 @@ static struct clk_hw *bcm2835_register_c
|
||
|
struct bcm2835_clock *clock;
|
||
|
struct clk_init_data init;
|
||
|
const char *parents[1 << CM_SRC_BITS];
|
||
|
- size_t i;
|
||
|
+ size_t i, j;
|
||
|
int ret;
|
||
|
|
||
|
/*
|
||
|
- * Replace our "xosc" references with the oscillator's
|
||
|
- * actual name.
|
||
|
+ * Replace our strings referencing parent clocks with the
|
||
|
+ * actual clock-output-name of the parent.
|
||
|
*/
|
||
|
for (i = 0; i < data->num_mux_parents; i++) {
|
||
|
- if (strcmp(data->parents[i], "xosc") == 0)
|
||
|
- parents[i] = cprman->osc_name;
|
||
|
- else
|
||
|
- parents[i] = data->parents[i];
|
||
|
+ parents[i] = data->parents[i];
|
||
|
+
|
||
|
+ for (j = 0; j < ARRAY_SIZE(cprman_parent_names); j++) {
|
||
|
+ if (strcmp(parents[i], cprman_parent_names[j]) == 0) {
|
||
|
+ parents[i] = cprman->real_parent_names[j];
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
}
|
||
|
|
||
|
memset(&init, 0, sizeof(init));
|
||
|
@@ -1442,6 +1475,47 @@ static const char *const bcm2835_clock_v
|
||
|
__VA_ARGS__)
|
||
|
|
||
|
/*
|
||
|
+ * DSI parent clocks. The DSI byte/DDR/DDR2 clocks come from the DSI
|
||
|
+ * analog PHY. The _inv variants are generated internally to cprman,
|
||
|
+ * but we don't use them so they aren't hooked up.
|
||
|
+ */
|
||
|
+static const char *const bcm2835_clock_dsi0_parents[] = {
|
||
|
+ "gnd",
|
||
|
+ "xosc",
|
||
|
+ "testdebug0",
|
||
|
+ "testdebug1",
|
||
|
+ "dsi0_ddr",
|
||
|
+ "dsi0_ddr_inv",
|
||
|
+ "dsi0_ddr2",
|
||
|
+ "dsi0_ddr2_inv",
|
||
|
+ "dsi0_byte",
|
||
|
+ "dsi0_byte_inv",
|
||
|
+};
|
||
|
+
|
||
|
+static const char *const bcm2835_clock_dsi1_parents[] = {
|
||
|
+ "gnd",
|
||
|
+ "xosc",
|
||
|
+ "testdebug0",
|
||
|
+ "testdebug1",
|
||
|
+ "dsi1_ddr",
|
||
|
+ "dsi1_ddr_inv",
|
||
|
+ "dsi1_ddr2",
|
||
|
+ "dsi1_ddr2_inv",
|
||
|
+ "dsi1_byte",
|
||
|
+ "dsi1_byte_inv",
|
||
|
+};
|
||
|
+
|
||
|
+#define REGISTER_DSI0_CLK(...) REGISTER_CLK( \
|
||
|
+ .num_mux_parents = ARRAY_SIZE(bcm2835_clock_dsi0_parents), \
|
||
|
+ .parents = bcm2835_clock_dsi0_parents, \
|
||
|
+ __VA_ARGS__)
|
||
|
+
|
||
|
+#define REGISTER_DSI1_CLK(...) REGISTER_CLK( \
|
||
|
+ .num_mux_parents = ARRAY_SIZE(bcm2835_clock_dsi1_parents), \
|
||
|
+ .parents = bcm2835_clock_dsi1_parents, \
|
||
|
+ __VA_ARGS__)
|
||
|
+
|
||
|
+/*
|
||
|
* the real definition of all the pll, pll_dividers and clocks
|
||
|
* these make use of the above REGISTER_* macros
|
||
|
*/
|
||
|
@@ -1904,6 +1978,18 @@ static const struct bcm2835_clk_desc clk
|
||
|
.div_reg = CM_DSI1EDIV,
|
||
|
.int_bits = 4,
|
||
|
.frac_bits = 8),
|
||
|
+ [BCM2835_CLOCK_DSI0P] = REGISTER_DSI0_CLK(
|
||
|
+ .name = "dsi0p",
|
||
|
+ .ctl_reg = CM_DSI0PCTL,
|
||
|
+ .div_reg = CM_DSI0PDIV,
|
||
|
+ .int_bits = 0,
|
||
|
+ .frac_bits = 0),
|
||
|
+ [BCM2835_CLOCK_DSI1P] = REGISTER_DSI1_CLK(
|
||
|
+ .name = "dsi1p",
|
||
|
+ .ctl_reg = CM_DSI1PCTL,
|
||
|
+ .div_reg = CM_DSI1PDIV,
|
||
|
+ .int_bits = 0,
|
||
|
+ .frac_bits = 0),
|
||
|
|
||
|
/* the gates */
|
||
|
|
||
|
@@ -1962,8 +2048,19 @@ static int bcm2835_clk_probe(struct plat
|
||
|
if (IS_ERR(cprman->regs))
|
||
|
return PTR_ERR(cprman->regs);
|
||
|
|
||
|
- cprman->osc_name = of_clk_get_parent_name(dev->of_node, 0);
|
||
|
- if (!cprman->osc_name)
|
||
|
+ memcpy(cprman->real_parent_names, cprman_parent_names,
|
||
|
+ sizeof(cprman_parent_names));
|
||
|
+ of_clk_parent_fill(dev->of_node, cprman->real_parent_names,
|
||
|
+ ARRAY_SIZE(cprman_parent_names));
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Make sure the external oscillator has been registered.
|
||
|
+ *
|
||
|
+ * The other (DSI) clocks are not present on older device
|
||
|
+ * trees, which we still need to support for backwards
|
||
|
+ * compatibility.
|
||
|
+ */
|
||
|
+ if (!cprman->real_parent_names[0])
|
||
|
return -ENODEV;
|
||
|
|
||
|
platform_set_drvdata(pdev, cprman);
|
||
|
--- a/include/dt-bindings/clock/bcm2835.h
|
||
|
+++ b/include/dt-bindings/clock/bcm2835.h
|
||
|
@@ -64,3 +64,5 @@
|
||
|
#define BCM2835_CLOCK_CAM1 46
|
||
|
#define BCM2835_CLOCK_DSI0E 47
|
||
|
#define BCM2835_CLOCK_DSI1E 48
|
||
|
+#define BCM2835_CLOCK_DSI0P 49
|
||
|
+#define BCM2835_CLOCK_DSI1P 50
|