From 8f0767611a0ed719caf975d899d8431834ace2d8 Mon Sep 17 00:00:00 2001 From: Andrea Venturi Date: Mon, 21 Mar 2016 17:10:38 +0100 Subject: [PATCH 1/7] clk: sunxi: mod1 clock should modify it's parent add CLK_SET_RATE_PARENT to modify the rate on clk upstream Signed-off-by: Marcus Cooper Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-a10-mod1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/sunxi/clk-a10-mod1.c b/drivers/clk/sunxi/clk-a10-mod1.c index e9d870de165c..e2819fa09637 100644 --- a/drivers/clk/sunxi/clk-a10-mod1.c +++ b/drivers/clk/sunxi/clk-a10-mod1.c @@ -62,7 +62,7 @@ static void __init sun4i_mod1_clk_setup(struct device_node *node) clk = clk_register_composite(NULL, clk_name, parents, i, &mux->hw, &clk_mux_ops, NULL, NULL, - &gate->hw, &clk_gate_ops, 0); + &gate->hw, &clk_gate_ops, CLK_SET_RATE_PARENT); if (IS_ERR(clk)) goto err_free_gate; From 5ed400dd96d8222e667ee473c7ebd676f05113f8 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 30 Mar 2016 18:43:29 +0200 Subject: [PATCH 2/7] clk: sunxi: Add sun6i/8i display support Add the clock type which is used by the sun6i/8i families for video display. Signed-off-by: Jean-Francois Moine Acked-by: Rob Herring Signed-off-by: Maxime Ripard --- .../devicetree/bindings/clock/sunxi.txt | 1 + drivers/clk/sunxi/clk-sunxi.c | 38 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index 834436fbe83d..9de34a832023 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -81,6 +81,7 @@ Required properties: "allwinner,sun9i-a80-usb-mod-clk" - for usb gates + resets on A80 "allwinner,sun9i-a80-usb-phy-clk" - for usb phy gates + resets on A80 "allwinner,sun4i-a10-ve-clk" - for the Video Engine clock + "allwinner,sun6i-a31-display-clk" - for the display clocks Required properties for all clocks: - reg : shall be the control register address for the clock. diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 91de0a006773..6ea7cf80a0ab 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -1127,3 +1127,41 @@ static void __init sun6i_pll6_clk_setup(struct device_node *node) } CLK_OF_DECLARE(sun6i_pll6, "allwinner,sun6i-a31-pll6-clk", sun6i_pll6_clk_setup); + +/* + * sun6i display + * + * rate = parent_rate / (m + 1); + */ +static void sun6i_display_factors(struct factors_request *req) +{ + u8 m; + + if (req->rate > req->parent_rate) + req->rate = req->parent_rate; + + m = DIV_ROUND_UP(req->parent_rate, req->rate); + + req->rate = req->parent_rate / m; + req->m = m - 1; +} + +static const struct clk_factors_config sun6i_display_config = { + .mshift = 0, + .mwidth = 4, +}; + +static const struct factors_data sun6i_display_data __initconst = { + .enable = 31, + .mux = 24, + .muxmask = BIT(2) | BIT(1) | BIT(0), + .table = &sun6i_display_config, + .getter = sun6i_display_factors, +}; + +static void __init sun6i_display_setup(struct device_node *node) +{ + sunxi_factors_clk_setup(node, &sun6i_display_data); +} +CLK_OF_DECLARE(sun6i_display, "allwinner,sun6i-a31-display-clk", + sun6i_display_setup); From f4b9ef653c047165f096d32816904be5ee337a63 Mon Sep 17 00:00:00 2001 From: Vaishali Thakkar Date: Mon, 11 Apr 2016 10:23:03 +0530 Subject: [PATCH 3/7] clk: sunxi: Use resource_size Use the function resource_size instaed of explicit computation. Problem found using Coccinelle. Signed-off-by: Vaishali Thakkar Acked-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sun9i-mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/sunxi/clk-sun9i-mmc.c b/drivers/clk/sunxi/clk-sun9i-mmc.c index a9b176139aca..0c2dd02d8dec 100644 --- a/drivers/clk/sunxi/clk-sun9i-mmc.c +++ b/drivers/clk/sunxi/clk-sun9i-mmc.c @@ -106,7 +106,7 @@ static int sun9i_a80_mmc_config_clk_probe(struct platform_device *pdev) r = platform_get_resource(pdev, IORESOURCE_MEM, 0); /* one clock/reset pair per word */ - count = DIV_ROUND_UP((r->end - r->start + 1), SUN9I_MMC_WIDTH); + count = DIV_ROUND_UP((resource_size(r)), SUN9I_MMC_WIDTH); data->membase = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(data->membase)) return PTR_ERR(data->membase); From 7f2ea3847d47d49929d41573a3b26c80ddebbef5 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 23 Mar 2016 17:38:28 +0100 Subject: [PATCH 4/7] dt-bindings: clk: sun5i: add DRAM gates compatible The Allwinner SoCs have a gate controller to gate the access to the DRAM clock to the some devices that need to access the DRAM directly (mostly display / image related IPs). Use a simple gates driver to support the one found in the A13 / R8 SoCs. Signed-off-by: Maxime Ripard Acked-by: Chen-Yu Tsai Acked-by: Rob Herring Acked-by: Stephen Boyd Signed-off-by: Maxime Ripard --- Documentation/devicetree/bindings/clock/sunxi.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index 9de34a832023..1bf588e11d46 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -64,6 +64,7 @@ Required properties: "allwinner,sun8i-h3-bus-gates-clk" - for the bus gates on H3 "allwinner,sun9i-a80-apbs-gates-clk" - for the APBS gates on A80 "allwinner,sun4i-a10-dram-gates-clk" - for the DRAM gates on A10 + "allwinner,sun5i-a13-dram-gates-clk" - for the DRAM gates on A13 "allwinner,sun5i-a13-mbus-clk" - for the MBUS clock on A13 "allwinner,sun4i-a10-mmc-clk" - for the MMC clock "allwinner,sun9i-a80-mmc-clk" - for mmc module clocks on A80 From fa4d0ca104bfdcda7b7e2bac855b358f302fd310 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 23 Mar 2016 17:38:26 +0100 Subject: [PATCH 5/7] clk: sunxi: Add PLL3 clock The A10 SoCs and relatives have a PLL controller to drive the PLL3 and PLL7, clocked from a 3MHz oscillator, that drives the display related clocks (GPU, display engine, TCON, etc.) Add a driver for it. Acked-by: Rob Herring Acked-by: Chen-Yu Tsai Acked-by: Stephen Boyd Signed-off-by: Maxime Ripard --- .../devicetree/bindings/clock/sunxi.txt | 1 + drivers/clk/sunxi/Makefile | 1 + drivers/clk/sunxi/clk-sun4i-pll3.c | 98 +++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 drivers/clk/sunxi/clk-sun4i-pll3.c diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index 1bf588e11d46..005a24c6bd74 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -10,6 +10,7 @@ Required properties: "allwinner,sun4i-a10-pll1-clk" - for the main PLL clock and PLL4 "allwinner,sun6i-a31-pll1-clk" - for the main PLL clock on A31 "allwinner,sun8i-a23-pll1-clk" - for the main PLL clock on A23 + "allwinner,sun4i-a10-pll3-clk" - for the video PLL clock on A10 "allwinner,sun9i-a80-pll4-clk" - for the peripheral PLLs on A80 "allwinner,sun4i-a10-pll5-clk" - for the PLL5 clock "allwinner,sun4i-a10-pll6-clk" - for the PLL6 clock diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile index 3fd7901d48e4..e2f72797bd9a 100644 --- a/drivers/clk/sunxi/Makefile +++ b/drivers/clk/sunxi/Makefile @@ -11,6 +11,7 @@ obj-y += clk-a10-ve.o obj-y += clk-a20-gmac.o obj-y += clk-mod0.o obj-y += clk-simple-gates.o +obj-y += clk-sun4i-pll3.o obj-y += clk-sun8i-bus-gates.o obj-y += clk-sun8i-mbus.o obj-y += clk-sun9i-core.o diff --git a/drivers/clk/sunxi/clk-sun4i-pll3.c b/drivers/clk/sunxi/clk-sun4i-pll3.c new file mode 100644 index 000000000000..f66267e77d9c --- /dev/null +++ b/drivers/clk/sunxi/clk-sun4i-pll3.c @@ -0,0 +1,98 @@ +/* + * Copyright 2015 Maxime Ripard + * + * Maxime Ripard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#define SUN4I_A10_PLL3_GATE_BIT 31 +#define SUN4I_A10_PLL3_DIV_WIDTH 7 +#define SUN4I_A10_PLL3_DIV_SHIFT 0 + +static DEFINE_SPINLOCK(sun4i_a10_pll3_lock); + +static void __init sun4i_a10_pll3_setup(struct device_node *node) +{ + const char *clk_name = node->name, *parent; + struct clk_multiplier *mult; + struct clk_gate *gate; + struct resource res; + void __iomem *reg; + struct clk *clk; + int ret; + + of_property_read_string(node, "clock-output-names", &clk_name); + parent = of_clk_get_parent_name(node, 0); + + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(reg)) { + pr_err("%s: Could not map the clock registers\n", clk_name); + return; + } + + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + goto err_unmap; + + gate->reg = reg; + gate->bit_idx = SUN4I_A10_PLL3_GATE_BIT; + gate->lock = &sun4i_a10_pll3_lock; + + mult = kzalloc(sizeof(*mult), GFP_KERNEL); + if (!mult) + goto err_free_gate; + + mult->reg = reg; + mult->shift = SUN4I_A10_PLL3_DIV_SHIFT; + mult->width = SUN4I_A10_PLL3_DIV_WIDTH; + mult->lock = &sun4i_a10_pll3_lock; + + clk = clk_register_composite(NULL, clk_name, + &parent, 1, + NULL, NULL, + &mult->hw, &clk_multiplier_ops, + &gate->hw, &clk_gate_ops, + 0); + if (IS_ERR(clk)) { + pr_err("%s: Couldn't register the clock\n", clk_name); + goto err_free_mult; + } + + ret = of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (ret) { + pr_err("%s: Couldn't register DT provider\n", + clk_name); + goto err_clk_unregister; + } + + return; + +err_clk_unregister: + clk_unregister_composite(clk); +err_free_mult: + kfree(mult); +err_free_gate: + kfree(gate); +err_unmap: + iounmap(reg); + of_address_to_resource(node, 0, &res); + release_mem_region(res.start, resource_size(&res)); +} + +CLK_OF_DECLARE(sun4i_a10_pll3, "allwinner,sun4i-a10-pll3-clk", + sun4i_a10_pll3_setup); From cc510c736b1b278a9925a4a051ecfa72ef8c21fc Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 1 Jul 2015 15:48:37 +0200 Subject: [PATCH 6/7] clk: sunxi: Add TCON channel1 clock The TCON is a controller generating the timings to output videos signals, acting like both a CRTC and an encoder. It has two channels depending on the output, each channel being driven by its own clock (and own clock controller). Add a driver for the channel 1 clock. Signed-off-by: Maxime Ripard Acked-by: Rob Herring Acked-by: Stephen Boyd --- .../devicetree/bindings/clock/sunxi.txt | 1 + drivers/clk/sunxi/Makefile | 1 + drivers/clk/sunxi/clk-sun4i-tcon-ch1.c | 300 ++++++++++++++++++ 3 files changed, 302 insertions(+) create mode 100644 drivers/clk/sunxi/clk-sun4i-tcon-ch1.c diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index 005a24c6bd74..f70f500256bb 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -75,6 +75,7 @@ Required properties: "allwinner,sun8i-a23-mbus-clk" - for the MBUS clock on A23 "allwinner,sun7i-a20-out-clk" - for the external output clocks "allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31 + "allwinner,sun4i-a10-tcon-ch1-clk" - for the TCON channel 1 clock on the A10 "allwinner,sun4i-a10-usb-clk" - for usb gates + resets on A10 / A20 "allwinner,sun5i-a13-usb-clk" - for usb gates + resets on A13 "allwinner,sun6i-a31-usb-clk" - for usb gates + resets on A31 diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile index e2f72797bd9a..2d77407a0037 100644 --- a/drivers/clk/sunxi/Makefile +++ b/drivers/clk/sunxi/Makefile @@ -12,6 +12,7 @@ obj-y += clk-a20-gmac.o obj-y += clk-mod0.o obj-y += clk-simple-gates.o obj-y += clk-sun4i-pll3.o +obj-y += clk-sun4i-tcon-ch1.o obj-y += clk-sun8i-bus-gates.o obj-y += clk-sun8i-mbus.o obj-y += clk-sun9i-core.o diff --git a/drivers/clk/sunxi/clk-sun4i-tcon-ch1.c b/drivers/clk/sunxi/clk-sun4i-tcon-ch1.c new file mode 100644 index 000000000000..98a4582de56a --- /dev/null +++ b/drivers/clk/sunxi/clk-sun4i-tcon-ch1.c @@ -0,0 +1,300 @@ +/* + * Copyright 2015 Maxime Ripard + * + * Maxime Ripard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#define TCON_CH1_SCLK2_PARENTS 4 + +#define TCON_CH1_SCLK2_GATE_BIT BIT(31) +#define TCON_CH1_SCLK2_MUX_MASK 3 +#define TCON_CH1_SCLK2_MUX_SHIFT 24 +#define TCON_CH1_SCLK2_DIV_MASK 0xf +#define TCON_CH1_SCLK2_DIV_SHIFT 0 + +#define TCON_CH1_SCLK1_GATE_BIT BIT(15) +#define TCON_CH1_SCLK1_HALF_BIT BIT(11) + +struct tcon_ch1_clk { + struct clk_hw hw; + spinlock_t lock; + void __iomem *reg; +}; + +#define hw_to_tclk(hw) container_of(hw, struct tcon_ch1_clk, hw) + +static void tcon_ch1_disable(struct clk_hw *hw) +{ + struct tcon_ch1_clk *tclk = hw_to_tclk(hw); + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&tclk->lock, flags); + reg = readl(tclk->reg); + reg &= ~(TCON_CH1_SCLK2_GATE_BIT | TCON_CH1_SCLK1_GATE_BIT); + writel(reg, tclk->reg); + spin_unlock_irqrestore(&tclk->lock, flags); +} + +static int tcon_ch1_enable(struct clk_hw *hw) +{ + struct tcon_ch1_clk *tclk = hw_to_tclk(hw); + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&tclk->lock, flags); + reg = readl(tclk->reg); + reg |= TCON_CH1_SCLK2_GATE_BIT | TCON_CH1_SCLK1_GATE_BIT; + writel(reg, tclk->reg); + spin_unlock_irqrestore(&tclk->lock, flags); + + return 0; +} + +static int tcon_ch1_is_enabled(struct clk_hw *hw) +{ + struct tcon_ch1_clk *tclk = hw_to_tclk(hw); + u32 reg; + + reg = readl(tclk->reg); + return reg & (TCON_CH1_SCLK2_GATE_BIT | TCON_CH1_SCLK1_GATE_BIT); +} + +static u8 tcon_ch1_get_parent(struct clk_hw *hw) +{ + struct tcon_ch1_clk *tclk = hw_to_tclk(hw); + int num_parents = clk_hw_get_num_parents(hw); + u32 reg; + + reg = readl(tclk->reg) >> TCON_CH1_SCLK2_MUX_SHIFT; + reg &= reg >> TCON_CH1_SCLK2_MUX_MASK; + + if (reg >= num_parents) + return -EINVAL; + + return reg; +} + +static int tcon_ch1_set_parent(struct clk_hw *hw, u8 index) +{ + struct tcon_ch1_clk *tclk = hw_to_tclk(hw); + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&tclk->lock, flags); + reg = readl(tclk->reg); + reg &= ~(TCON_CH1_SCLK2_MUX_MASK << TCON_CH1_SCLK2_MUX_SHIFT); + reg |= index << TCON_CH1_SCLK2_MUX_SHIFT; + writel(reg, tclk->reg); + spin_unlock_irqrestore(&tclk->lock, flags); + + return 0; +}; + +static unsigned long tcon_ch1_calc_divider(unsigned long rate, + unsigned long parent_rate, + u8 *div, + bool *half) +{ + unsigned long best_rate = 0; + u8 best_m = 0, m; + bool is_double; + + for (m = 1; m < 16; m++) { + u8 d; + + for (d = 1; d < 3; d++) { + unsigned long tmp_rate; + + tmp_rate = parent_rate / m / d; + + if (tmp_rate > rate) + continue; + + if (!best_rate || + (rate - tmp_rate) < (rate - best_rate)) { + best_rate = tmp_rate; + best_m = m; + is_double = d; + } + } + } + + if (div && half) { + *div = best_m; + *half = is_double; + } + + return best_rate; +} + +static int tcon_ch1_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + long best_rate = -EINVAL; + int i; + + for (i = 0; i < clk_hw_get_num_parents(hw); i++) { + unsigned long parent_rate; + unsigned long tmp_rate; + struct clk_hw *parent; + + parent = clk_hw_get_parent_by_index(hw, i); + if (!parent) + continue; + + parent_rate = clk_hw_get_rate(parent); + + tmp_rate = tcon_ch1_calc_divider(req->rate, parent_rate, + NULL, NULL); + + if (best_rate < 0 || + (req->rate - tmp_rate) < (req->rate - best_rate)) { + best_rate = tmp_rate; + req->best_parent_rate = parent_rate; + req->best_parent_hw = parent; + } + } + + if (best_rate < 0) + return best_rate; + + req->rate = best_rate; + return 0; +} + +static unsigned long tcon_ch1_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct tcon_ch1_clk *tclk = hw_to_tclk(hw); + u32 reg; + + reg = readl(tclk->reg); + + parent_rate /= (reg & TCON_CH1_SCLK2_DIV_MASK) + 1; + + if (reg & TCON_CH1_SCLK1_HALF_BIT) + parent_rate /= 2; + + return parent_rate; +} + +static int tcon_ch1_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct tcon_ch1_clk *tclk = hw_to_tclk(hw); + unsigned long flags; + bool half; + u8 div_m; + u32 reg; + + tcon_ch1_calc_divider(rate, parent_rate, &div_m, &half); + + spin_lock_irqsave(&tclk->lock, flags); + reg = readl(tclk->reg); + reg &= ~(TCON_CH1_SCLK2_DIV_MASK | TCON_CH1_SCLK1_HALF_BIT); + reg |= (div_m - 1) & TCON_CH1_SCLK2_DIV_MASK; + + if (half) + reg |= TCON_CH1_SCLK1_HALF_BIT; + + writel(reg, tclk->reg); + spin_unlock_irqrestore(&tclk->lock, flags); + + return 0; +} + +static const struct clk_ops tcon_ch1_ops = { + .disable = tcon_ch1_disable, + .enable = tcon_ch1_enable, + .is_enabled = tcon_ch1_is_enabled, + + .get_parent = tcon_ch1_get_parent, + .set_parent = tcon_ch1_set_parent, + + .determine_rate = tcon_ch1_determine_rate, + .recalc_rate = tcon_ch1_recalc_rate, + .set_rate = tcon_ch1_set_rate, +}; + +static void __init tcon_ch1_setup(struct device_node *node) +{ + const char *parents[TCON_CH1_SCLK2_PARENTS]; + const char *clk_name = node->name; + struct clk_init_data init; + struct tcon_ch1_clk *tclk; + struct resource res; + struct clk *clk; + void __iomem *reg; + int ret; + + of_property_read_string(node, "clock-output-names", &clk_name); + + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(reg)) { + pr_err("%s: Could not map the clock registers\n", clk_name); + return; + } + + ret = of_clk_parent_fill(node, parents, TCON_CH1_SCLK2_PARENTS); + if (ret != TCON_CH1_SCLK2_PARENTS) { + pr_err("%s Could not retrieve the parents\n", clk_name); + goto err_unmap; + } + + tclk = kzalloc(sizeof(*tclk), GFP_KERNEL); + if (!tclk) + goto err_unmap; + + init.name = clk_name; + init.ops = &tcon_ch1_ops; + init.parent_names = parents; + init.num_parents = TCON_CH1_SCLK2_PARENTS; + init.flags = CLK_SET_RATE_PARENT; + + tclk->reg = reg; + tclk->hw.init = &init; + spin_lock_init(&tclk->lock); + + clk = clk_register(NULL, &tclk->hw); + if (IS_ERR(clk)) { + pr_err("%s: Couldn't register the clock\n", clk_name); + goto err_free_data; + } + + ret = of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (ret) { + pr_err("%s: Couldn't register our clock provider\n", clk_name); + goto err_unregister_clk; + } + + return; + +err_unregister_clk: + clk_unregister(clk); +err_free_data: + kfree(tclk); +err_unmap: + iounmap(reg); + of_address_to_resource(node, 0, &res); + release_mem_region(res.start, resource_size(&res)); +} + +CLK_OF_DECLARE(tcon_ch1, "allwinner,sun4i-a10-tcon-ch1-clk", + tcon_ch1_setup); From ff2bb89335daec6053b5ac778369f7f72b931142 Mon Sep 17 00:00:00 2001 From: Jens Kuske Date: Fri, 18 Mar 2016 09:44:15 +0000 Subject: [PATCH 7/7] clk: sunxi: Let divs clocks read the base factor clock name from devicetree Currently, the sunxi clock driver gets the name for the base factor clock of divs clocks from the name field in factors_data. This prevents reusing of the factor clock for clocks with same properties, but different name. This commit makes the divs setup function try to get a name from clock-output-names in the devicetree. It also removes the name field where possible and merges the sun4i PLL5 and PLL6 clocks. [Andre: Make temporary name allocation dynamic.] Signed-off-by: Jens Kuske Signed-off-by: Andre Przywara Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-sunxi.c | 41 +++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 6ea7cf80a0ab..838b22aa8b67 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -523,21 +523,12 @@ static const struct factors_data sun4i_pll5_data __initconst = { .enable = 31, .table = &sun4i_pll5_config, .getter = sun4i_get_pll5_factors, - .name = "pll5", -}; - -static const struct factors_data sun4i_pll6_data __initconst = { - .enable = 31, - .table = &sun4i_pll5_config, - .getter = sun4i_get_pll5_factors, - .name = "pll6", }; static const struct factors_data sun6i_a31_pll6_data __initconst = { .enable = 31, .table = &sun6i_a31_pll6_config, .getter = sun6i_a31_get_pll6_factors, - .name = "pll6x2", }; static const struct factors_data sun5i_a13_ahb_data __initconst = { @@ -933,7 +924,7 @@ static const struct divs_data pll5_divs_data __initconst = { }; static const struct divs_data pll6_divs_data __initconst = { - .factors = &sun4i_pll6_data, + .factors = &sun4i_pll5_data, .ndivs = 4, .div = { { .shift = 0, .table = pll6_sata_tbl, .gate = 14 }, /* M, SATA */ @@ -975,6 +966,8 @@ static struct clk ** __init sunxi_divs_clk_setup(struct device_node *node, struct clk_gate *gate = NULL; struct clk_fixed_factor *fix_factor; struct clk_divider *divider; + struct factors_data factors = *data->factors; + char *derived_name = NULL; void __iomem *reg; int ndivs = SUNXI_DIVS_MAX_QTY, i = 0; int flags, clkflags; @@ -983,11 +976,37 @@ static struct clk ** __init sunxi_divs_clk_setup(struct device_node *node, if (data->ndivs) ndivs = data->ndivs; + /* Try to find a name for base factor clock */ + for (i = 0; i < ndivs; i++) { + if (data->div[i].self) { + of_property_read_string_index(node, "clock-output-names", + i, &factors.name); + break; + } + } + /* If we don't have a .self clk use the first output-name up to '_' */ + if (factors.name == NULL) { + char *endp; + + of_property_read_string_index(node, "clock-output-names", + 0, &clk_name); + endp = strchr(clk_name, '_'); + if (endp) { + derived_name = kstrndup(clk_name, endp - clk_name, + GFP_KERNEL); + factors.name = derived_name; + } else { + factors.name = clk_name; + } + } + /* Set up factor clock that we will be dividing */ - pclk = sunxi_factors_clk_setup(node, data->factors); + pclk = sunxi_factors_clk_setup(node, &factors); if (!pclk) return NULL; + parent = __clk_get_name(pclk); + kfree(derived_name); reg = of_iomap(node, 0); if (!reg) {