From 7d6ddad659f7220438b863977b0c4032e3d77dda Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 18 Aug 2015 19:25:17 +0200 Subject: clk: sunxi: Add A33 gates support The A33 gates are different from the A23 ones, add a new hook to simple gates to handle this clock Reported-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi/clk-simple-gates.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/clk/sunxi') diff --git a/drivers/clk/sunxi/clk-simple-gates.c b/drivers/clk/sunxi/clk-simple-gates.c index 6ce91180da1b..0214c6548afd 100644 --- a/drivers/clk/sunxi/clk-simple-gates.c +++ b/drivers/clk/sunxi/clk-simple-gates.c @@ -128,6 +128,8 @@ CLK_OF_DECLARE(sun8i_a23_apb1, "allwinner,sun8i-a23-apb1-gates-clk", sunxi_simple_gates_init); CLK_OF_DECLARE(sun8i_a23_apb2, "allwinner,sun8i-a23-apb2-gates-clk", sunxi_simple_gates_init); +CLK_OF_DECLARE(sun8i_a33_ahb1, "allwinner,sun8i-a33-ahb1-gates-clk", + sunxi_simple_gates_init); CLK_OF_DECLARE(sun9i_a80_ahb0, "allwinner,sun9i-a80-ahb0-gates-clk", sunxi_simple_gates_init); CLK_OF_DECLARE(sun9i_a80_ahb1, "allwinner,sun9i-a80-ahb1-gates-clk", -- cgit From 460d0d444822e9032a2573fc051b45c68b89a97a Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Fri, 18 Jul 2014 15:48:35 -0300 Subject: clk: sunxi: Add a driver for the PLL2 The PLL2 on the A10 and later SoCs is the clock used for all the audio related operations. This clock has a somewhat complex output tree, with three outputs (2X, 4X and 8X) with a fixed divider from the base clock, and an output (1X) with a post divider. However, we can simplify things since the 1X divider can be fixed, and we end up by having a base clock not exposed to any device (or at least directly, since the 4X output doesn't have any divider), and 4 fixed divider clocks that will be exposed. This clock seems to have been introduced, at least in this form, in the revision B of the A10, but we don't have any information on the clock used on the revision A. Signed-off-by: Maxime Ripard Reviewed-by: Chen-Yu Tsai --- drivers/clk/sunxi/Makefile | 1 + drivers/clk/sunxi/clk-a10-pll2.c | 188 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 drivers/clk/sunxi/clk-a10-pll2.c (limited to 'drivers/clk/sunxi') diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile index f5a35b82cc1a..c658a18ba7cb 100644 --- a/drivers/clk/sunxi/Makefile +++ b/drivers/clk/sunxi/Makefile @@ -4,6 +4,7 @@ obj-y += clk-sunxi.o clk-factors.o obj-y += clk-a10-hosc.o +obj-y += clk-a10-pll2.o obj-y += clk-a20-gmac.o obj-y += clk-mod0.o obj-y += clk-simple-gates.o diff --git a/drivers/clk/sunxi/clk-a10-pll2.c b/drivers/clk/sunxi/clk-a10-pll2.c new file mode 100644 index 000000000000..a57742a8576d --- /dev/null +++ b/drivers/clk/sunxi/clk-a10-pll2.c @@ -0,0 +1,188 @@ +/* + * Copyright 2013 Emilio López + * Emilio López + * + * 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_PLL2_ENABLE 31 + +#define SUN4I_PLL2_PRE_DIV_SHIFT 0 +#define SUN4I_PLL2_PRE_DIV_WIDTH 5 +#define SUN4I_PLL2_PRE_DIV_MASK GENMASK(SUN4I_PLL2_PRE_DIV_WIDTH - 1, 0) + +#define SUN4I_PLL2_N_SHIFT 8 +#define SUN4I_PLL2_N_WIDTH 7 +#define SUN4I_PLL2_N_MASK GENMASK(SUN4I_PLL2_N_WIDTH - 1, 0) + +#define SUN4I_PLL2_POST_DIV_SHIFT 26 +#define SUN4I_PLL2_POST_DIV_WIDTH 4 +#define SUN4I_PLL2_POST_DIV_MASK GENMASK(SUN4I_PLL2_POST_DIV_WIDTH - 1, 0) + +#define SUN4I_PLL2_POST_DIV_VALUE 4 + +#define SUN4I_PLL2_OUTPUTS 4 + +static DEFINE_SPINLOCK(sun4i_a10_pll2_lock); + +static void __init sun4i_pll2_setup(struct device_node *node) +{ + const char *clk_name = node->name, *parent; + struct clk **clks, *base_clk, *prediv_clk; + struct clk_onecell_data *clk_data; + struct clk_multiplier *mult; + struct clk_gate *gate; + void __iomem *reg; + u32 val; + + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(reg)) + return; + + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + goto err_unmap; + + clks = kcalloc(SUN4I_PLL2_OUTPUTS, sizeof(struct clk *), GFP_KERNEL); + if (!clks) + goto err_free_data; + + parent = of_clk_get_parent_name(node, 0); + prediv_clk = clk_register_divider(NULL, "pll2-prediv", + parent, 0, reg, + SUN4I_PLL2_PRE_DIV_SHIFT, + SUN4I_PLL2_PRE_DIV_WIDTH, + CLK_DIVIDER_ONE_BASED | + CLK_DIVIDER_ALLOW_ZERO, + &sun4i_a10_pll2_lock); + if (!prediv_clk) { + pr_err("Couldn't register the prediv clock\n"); + goto err_free_array; + } + + /* Setup the gate part of the PLL2 */ + gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); + if (!gate) + goto err_unregister_prediv; + + gate->reg = reg; + gate->bit_idx = SUN4I_PLL2_ENABLE; + gate->lock = &sun4i_a10_pll2_lock; + + /* Setup the multiplier part of the PLL2 */ + mult = kzalloc(sizeof(struct clk_multiplier), GFP_KERNEL); + if (!mult) + goto err_free_gate; + + mult->reg = reg; + mult->shift = SUN4I_PLL2_N_SHIFT; + mult->width = 7; + mult->flags = CLK_MULTIPLIER_ZERO_BYPASS | + CLK_MULTIPLIER_ROUND_CLOSEST; + mult->lock = &sun4i_a10_pll2_lock; + + parent = __clk_get_name(prediv_clk); + base_clk = clk_register_composite(NULL, "pll2-base", + &parent, 1, + NULL, NULL, + &mult->hw, &clk_multiplier_ops, + &gate->hw, &clk_gate_ops, + CLK_SET_RATE_PARENT); + if (!base_clk) { + pr_err("Couldn't register the base multiplier clock\n"); + goto err_free_multiplier; + } + + parent = __clk_get_name(base_clk); + + /* + * PLL2-1x + * + * This is supposed to have a post divider, but we won't need + * to use it, we just need to initialise it to 4, and use a + * fixed divider. + */ + val = readl(reg); + val &= ~(SUN4I_PLL2_POST_DIV_MASK << SUN4I_PLL2_POST_DIV_SHIFT); + val |= SUN4I_PLL2_POST_DIV_VALUE << SUN4I_PLL2_POST_DIV_SHIFT; + writel(val, reg); + + of_property_read_string_index(node, "clock-output-names", + SUN4I_A10_PLL2_1X, &clk_name); + clks[SUN4I_A10_PLL2_1X] = clk_register_fixed_factor(NULL, clk_name, + parent, + CLK_SET_RATE_PARENT, + 1, + SUN4I_PLL2_POST_DIV_VALUE); + WARN_ON(IS_ERR(clks[SUN4I_A10_PLL2_1X])); + + /* + * PLL2-2x + * + * This clock doesn't use the post divider, and really is just + * a fixed divider from the PLL2 base clock. + */ + of_property_read_string_index(node, "clock-output-names", + SUN4I_A10_PLL2_2X, &clk_name); + clks[SUN4I_A10_PLL2_2X] = clk_register_fixed_factor(NULL, clk_name, + parent, + CLK_SET_RATE_PARENT, + 1, 2); + WARN_ON(IS_ERR(clks[SUN4I_A10_PLL2_2X])); + + /* PLL2-4x */ + of_property_read_string_index(node, "clock-output-names", + SUN4I_A10_PLL2_4X, &clk_name); + clks[SUN4I_A10_PLL2_4X] = clk_register_fixed_factor(NULL, clk_name, + parent, + CLK_SET_RATE_PARENT, + 1, 1); + WARN_ON(IS_ERR(clks[SUN4I_A10_PLL2_4X])); + + /* PLL2-8x */ + of_property_read_string_index(node, "clock-output-names", + SUN4I_A10_PLL2_8X, &clk_name); + clks[SUN4I_A10_PLL2_8X] = clk_register_fixed_factor(NULL, clk_name, + parent, + CLK_SET_RATE_PARENT, + 2, 1); + WARN_ON(IS_ERR(clks[SUN4I_A10_PLL2_8X])); + + clk_data->clks = clks; + clk_data->clk_num = SUN4I_PLL2_OUTPUTS; + of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + return; + +err_free_multiplier: + kfree(mult); +err_free_gate: + kfree(gate); +err_unregister_prediv: + clk_unregister_divider(prediv_clk); +err_free_array: + kfree(clks); +err_free_data: + kfree(clk_data); +err_unmap: + iounmap(reg); +} +CLK_OF_DECLARE(sun4i_pll2, "allwinner,sun4i-a10-pll2-clk", sun4i_pll2_setup); -- cgit From eb662f854710e6a438789a4b0d1d0cce8c12379d Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 21 Sep 2015 13:32:43 +0200 Subject: clk: sunxi: pll2: Add A13 support The A13, unlike the A10 and A20, doesn't use a pass-through exception for the 0 value in the pre and post dividers, but increments all the values written in the register by one. Add an exception for both these cases to handle them nicely. Signed-off-by: Maxime Ripard Reviewed-by: Chen-Yu Tsai --- drivers/clk/sunxi/clk-a10-pll2.c | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) (limited to 'drivers/clk/sunxi') diff --git a/drivers/clk/sunxi/clk-a10-pll2.c b/drivers/clk/sunxi/clk-a10-pll2.c index a57742a8576d..5484c31ec568 100644 --- a/drivers/clk/sunxi/clk-a10-pll2.c +++ b/drivers/clk/sunxi/clk-a10-pll2.c @@ -41,9 +41,15 @@ #define SUN4I_PLL2_OUTPUTS 4 +struct sun4i_pll2_data { + u32 post_div_offset; + u32 pre_div_flags; +}; + static DEFINE_SPINLOCK(sun4i_a10_pll2_lock); -static void __init sun4i_pll2_setup(struct device_node *node) +static void __init sun4i_pll2_setup(struct device_node *node, + struct sun4i_pll2_data *data) { const char *clk_name = node->name, *parent; struct clk **clks, *base_clk, *prediv_clk; @@ -70,8 +76,7 @@ static void __init sun4i_pll2_setup(struct device_node *node) parent, 0, reg, SUN4I_PLL2_PRE_DIV_SHIFT, SUN4I_PLL2_PRE_DIV_WIDTH, - CLK_DIVIDER_ONE_BASED | - CLK_DIVIDER_ALLOW_ZERO, + data->pre_div_flags, &sun4i_a10_pll2_lock); if (!prediv_clk) { pr_err("Couldn't register the prediv clock\n"); @@ -122,7 +127,7 @@ static void __init sun4i_pll2_setup(struct device_node *node) */ val = readl(reg); val &= ~(SUN4I_PLL2_POST_DIV_MASK << SUN4I_PLL2_POST_DIV_SHIFT); - val |= SUN4I_PLL2_POST_DIV_VALUE << SUN4I_PLL2_POST_DIV_SHIFT; + val |= (SUN4I_PLL2_POST_DIV_VALUE - data->post_div_offset) << SUN4I_PLL2_POST_DIV_SHIFT; writel(val, reg); of_property_read_string_index(node, "clock-output-names", @@ -185,4 +190,27 @@ err_free_data: err_unmap: iounmap(reg); } -CLK_OF_DECLARE(sun4i_pll2, "allwinner,sun4i-a10-pll2-clk", sun4i_pll2_setup); + +static struct sun4i_pll2_data sun4i_a10_pll2_data = { + .pre_div_flags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, +}; + +static void __init sun4i_a10_pll2_setup(struct device_node *node) +{ + sun4i_pll2_setup(node, &sun4i_a10_pll2_data); +} + +CLK_OF_DECLARE(sun4i_a10_pll2, "allwinner,sun4i-a10-pll2-clk", + sun4i_a10_pll2_setup); + +static struct sun4i_pll2_data sun5i_a13_pll2_data = { + .post_div_offset = 1, +}; + +static void __init sun5i_a13_pll2_setup(struct device_node *node) +{ + sun4i_pll2_setup(node, &sun5i_a13_pll2_data); +} + +CLK_OF_DECLARE(sun5i_a13_pll2, "allwinner,sun5i-a13-pll2-clk", + sun5i_a13_pll2_setup); -- cgit From e2771545f49fbfec874642533058a3423fa29e16 Mon Sep 17 00:00:00 2001 From: Emilio López Date: Fri, 18 Jul 2014 15:49:37 -0300 Subject: clk: sunxi: codec clock support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The codec clock on sun4i, sun5i and sun7i is a simple gate with PLL2 as parent. Add a driver for such a clock. Signed-off-by: Emilio López Signed-off-by: Hans de Goede Signed-off-by: Maxime Ripard Reviewed-by: Chen-Yu Tsai --- drivers/clk/sunxi/Makefile | 1 + drivers/clk/sunxi/clk-a10-codec.c | 44 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 drivers/clk/sunxi/clk-a10-codec.c (limited to 'drivers/clk/sunxi') diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile index c658a18ba7cb..70a449a419e6 100644 --- a/drivers/clk/sunxi/Makefile +++ b/drivers/clk/sunxi/Makefile @@ -3,6 +3,7 @@ # obj-y += clk-sunxi.o clk-factors.o +obj-y += clk-a10-codec.o obj-y += clk-a10-hosc.o obj-y += clk-a10-pll2.o obj-y += clk-a20-gmac.o diff --git a/drivers/clk/sunxi/clk-a10-codec.c b/drivers/clk/sunxi/clk-a10-codec.c new file mode 100644 index 000000000000..ac321d6a0df5 --- /dev/null +++ b/drivers/clk/sunxi/clk-a10-codec.c @@ -0,0 +1,44 @@ +/* + * Copyright 2013 Emilio López + * + * Emilio López + * + * 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 + +#define SUN4I_CODEC_GATE 31 + +static void __init sun4i_codec_clk_setup(struct device_node *node) +{ + struct clk *clk; + const char *clk_name = node->name, *parent_name; + void __iomem *reg; + + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(reg)) + return; + + of_property_read_string(node, "clock-output-names", &clk_name); + parent_name = of_clk_get_parent_name(node, 0); + + clk = clk_register_gate(NULL, clk_name, parent_name, + CLK_SET_RATE_PARENT, reg, + SUN4I_CODEC_GATE, 0, NULL); + + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); +} +CLK_OF_DECLARE(sun4i_codec, "allwinner,sun4i-a10-codec-clk", + sun4i_codec_clk_setup); -- cgit From 9b038bc58ad2658c76fd8b50bb333dfd4454573c Mon Sep 17 00:00:00 2001 From: Emilio López Date: Fri, 18 Jul 2014 15:28:02 -0300 Subject: clk: sunxi: mod1 clock support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The module 1 type of clocks consist of a gate and a mux and are used on the audio blocks to mux and gate the PLL2 outputs for AC97, IIS or SPDIF. This commit adds support for them on the sunxi clock driver. Signed-off-by: Emilio López Signed-off-by: Hans de Goede Signed-off-by: Maxime Ripard Reviewed-by: Chen-Yu Tsai --- drivers/clk/sunxi/Makefile | 1 + drivers/clk/sunxi/clk-a10-mod1.c | 81 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 drivers/clk/sunxi/clk-a10-mod1.c (limited to 'drivers/clk/sunxi') diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile index 70a449a419e6..cb4c299214ce 100644 --- a/drivers/clk/sunxi/Makefile +++ b/drivers/clk/sunxi/Makefile @@ -5,6 +5,7 @@ obj-y += clk-sunxi.o clk-factors.o obj-y += clk-a10-codec.o obj-y += clk-a10-hosc.o +obj-y += clk-a10-mod1.o obj-y += clk-a10-pll2.o obj-y += clk-a20-gmac.o obj-y += clk-mod0.o diff --git a/drivers/clk/sunxi/clk-a10-mod1.c b/drivers/clk/sunxi/clk-a10-mod1.c new file mode 100644 index 000000000000..e9d870de165c --- /dev/null +++ b/drivers/clk/sunxi/clk-a10-mod1.c @@ -0,0 +1,81 @@ +/* + * Copyright 2013 Emilio López + * + * Emilio López + * + * 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 + +static DEFINE_SPINLOCK(mod1_lock); + +#define SUN4I_MOD1_ENABLE 31 +#define SUN4I_MOD1_MUX 16 +#define SUN4I_MOD1_MUX_WIDTH 2 +#define SUN4I_MOD1_MAX_PARENTS 4 + +static void __init sun4i_mod1_clk_setup(struct device_node *node) +{ + struct clk *clk; + struct clk_mux *mux; + struct clk_gate *gate; + const char *parents[4]; + const char *clk_name = node->name; + void __iomem *reg; + int i; + + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(reg)) + return; + + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + goto err_unmap; + + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + goto err_free_mux; + + of_property_read_string(node, "clock-output-names", &clk_name); + i = of_clk_parent_fill(node, parents, SUN4I_MOD1_MAX_PARENTS); + + gate->reg = reg; + gate->bit_idx = SUN4I_MOD1_ENABLE; + gate->lock = &mod1_lock; + mux->reg = reg; + mux->shift = SUN4I_MOD1_MUX; + mux->mask = BIT(SUN4I_MOD1_MUX_WIDTH) - 1; + mux->lock = &mod1_lock; + + clk = clk_register_composite(NULL, clk_name, parents, i, + &mux->hw, &clk_mux_ops, + NULL, NULL, + &gate->hw, &clk_gate_ops, 0); + if (IS_ERR(clk)) + goto err_free_gate; + + of_clk_add_provider(node, of_clk_src_simple_get, clk); + + return; + +err_free_gate: + kfree(gate); +err_free_mux: + kfree(mux); +err_unmap: + iounmap(reg); +} +CLK_OF_DECLARE(sun4i_mod1, "allwinner,sun4i-a10-mod1-clk", + sun4i_mod1_clk_setup); -- cgit