From 6089ef19c9dadaf0e3378f75eca65af861cd3974 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Wed, 28 Jan 2015 03:54:06 +0800 Subject: clk: sunxi: Move USB clocks to separate file The USB clocks originally shared code with the gates clocks, but had additional reset controllers. Move these to a separate file. This will allow us to add new support for slightly different USB clocks, such as on the A80, without affecting gates clocks, and also facilitate the migration of gates clocks to a generic solution. This also cleans up the USB clocks code slightly, such as adding newlines, getting rid of the unused clkdev call, using a simple u32 instead of BITMAP for the clock masks, using BIT() macro to declare the clock bitmasks, and using of_io_request_and_map() to get the I/O address. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile index 3a5292e..058f273 100644 --- a/drivers/clk/sunxi/Makefile +++ b/drivers/clk/sunxi/Makefile @@ -9,6 +9,7 @@ obj-y += clk-mod0.o obj-y += clk-sun8i-mbus.o obj-y += clk-sun9i-core.o obj-y += clk-sun9i-mmc.o +obj-y += clk-usb.o obj-$(CONFIG_MFD_SUN6I_PRCM) += \ clk-sun6i-ar100.o clk-sun6i-apb0.o clk-sun6i-apb0-gates.o \ diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 379324e..b6f28ac 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -838,59 +838,6 @@ static void __init sunxi_divider_clk_setup(struct device_node *node, /** - * sunxi_gates_reset... - reset bits in leaf gate clk registers handling - */ - -struct gates_reset_data { - void __iomem *reg; - spinlock_t *lock; - struct reset_controller_dev rcdev; -}; - -static int sunxi_gates_reset_assert(struct reset_controller_dev *rcdev, - unsigned long id) -{ - struct gates_reset_data *data = container_of(rcdev, - struct gates_reset_data, - rcdev); - unsigned long flags; - u32 reg; - - spin_lock_irqsave(data->lock, flags); - - reg = readl(data->reg); - writel(reg & ~BIT(id), data->reg); - - spin_unlock_irqrestore(data->lock, flags); - - return 0; -} - -static int sunxi_gates_reset_deassert(struct reset_controller_dev *rcdev, - unsigned long id) -{ - struct gates_reset_data *data = container_of(rcdev, - struct gates_reset_data, - rcdev); - unsigned long flags; - u32 reg; - - spin_lock_irqsave(data->lock, flags); - - reg = readl(data->reg); - writel(reg | BIT(id), data->reg); - - spin_unlock_irqrestore(data->lock, flags); - - return 0; -} - -static struct reset_control_ops sunxi_gates_reset_ops = { - .assert = sunxi_gates_reset_assert, - .deassert = sunxi_gates_reset_deassert, -}; - -/** * sunxi_gates_clk_setup() - Setup function for leaf gates on clocks */ @@ -898,7 +845,6 @@ static struct reset_control_ops sunxi_gates_reset_ops = { struct gates_data { DECLARE_BITMAP(mask, SUNXI_GATES_MAX_SIZE); - u32 reset_mask; }; static const struct gates_data sun4i_axi_gates_data __initconst = { @@ -997,26 +943,10 @@ static const struct gates_data sun8i_a23_apb2_gates_data __initconst = { .mask = {0x1F0007}, }; -static const struct gates_data sun4i_a10_usb_gates_data __initconst = { - .mask = {0x1C0}, - .reset_mask = 0x07, -}; - -static const struct gates_data sun5i_a13_usb_gates_data __initconst = { - .mask = {0x140}, - .reset_mask = 0x03, -}; - -static const struct gates_data sun6i_a31_usb_gates_data __initconst = { - .mask = { BIT(18) | BIT(17) | BIT(16) | BIT(10) | BIT(9) | BIT(8) }, - .reset_mask = BIT(2) | BIT(1) | BIT(0), -}; - static void __init sunxi_gates_clk_setup(struct device_node *node, struct gates_data *data) { struct clk_onecell_data *clk_data; - struct gates_reset_data *reset_data; const char *clk_parent; const char *clk_name; void __iomem *reg; @@ -1057,21 +987,6 @@ static void __init sunxi_gates_clk_setup(struct device_node *node, clk_data->clk_num = i; of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); - - /* Register a reset controler for gates with reset bits */ - if (data->reset_mask == 0) - return; - - reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL); - if (!reset_data) - return; - - reset_data->reg = reg; - reset_data->lock = &clk_lock; - reset_data->rcdev.nr_resets = __fls(data->reset_mask) + 1; - reset_data->rcdev.ops = &sunxi_gates_reset_ops; - reset_data->rcdev.of_node = node; - reset_controller_register(&reset_data->rcdev); } @@ -1324,9 +1239,6 @@ static const struct of_device_id clk_gates_match[] __initconst = { {.compatible = "allwinner,sun9i-a80-apb1-gates-clk", .data = &sun9i_a80_apb1_gates_data,}, {.compatible = "allwinner,sun6i-a31-apb2-gates-clk", .data = &sun6i_a31_apb2_gates_data,}, {.compatible = "allwinner,sun8i-a23-apb2-gates-clk", .data = &sun8i_a23_apb2_gates_data,}, - {.compatible = "allwinner,sun4i-a10-usb-clk", .data = &sun4i_a10_usb_gates_data,}, - {.compatible = "allwinner,sun5i-a13-usb-clk", .data = &sun5i_a13_usb_gates_data,}, - {.compatible = "allwinner,sun6i-a31-usb-clk", .data = &sun6i_a31_usb_gates_data,}, {} }; diff --git a/drivers/clk/sunxi/clk-usb.c b/drivers/clk/sunxi/clk-usb.c new file mode 100644 index 0000000..f1dcc8f --- /dev/null +++ b/drivers/clk/sunxi/clk-usb.c @@ -0,0 +1,190 @@ +/* + * Copyright 2013-2015 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 +#include +#include + + +/** + * sunxi_usb_reset... - reset bits in usb clk registers handling + */ + +struct usb_reset_data { + void __iomem *reg; + spinlock_t *lock; + struct reset_controller_dev rcdev; +}; + +static int sunxi_usb_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct usb_reset_data *data = container_of(rcdev, + struct usb_reset_data, + rcdev); + unsigned long flags; + u32 reg; + + spin_lock_irqsave(data->lock, flags); + + reg = readl(data->reg); + writel(reg & ~BIT(id), data->reg); + + spin_unlock_irqrestore(data->lock, flags); + + return 0; +} + +static int sunxi_usb_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct usb_reset_data *data = container_of(rcdev, + struct usb_reset_data, + rcdev); + unsigned long flags; + u32 reg; + + spin_lock_irqsave(data->lock, flags); + + reg = readl(data->reg); + writel(reg | BIT(id), data->reg); + + spin_unlock_irqrestore(data->lock, flags); + + return 0; +} + +static struct reset_control_ops sunxi_usb_reset_ops = { + .assert = sunxi_usb_reset_assert, + .deassert = sunxi_usb_reset_deassert, +}; + +/** + * sunxi_usb_clk_setup() - Setup function for usb gate clocks + */ + +#define SUNXI_USB_MAX_SIZE 32 + +struct usb_clk_data { + u32 clk_mask; + u32 reset_mask; +}; + +static void __init sunxi_usb_clk_setup(struct device_node *node, + const struct usb_clk_data *data, + spinlock_t *lock) +{ + struct clk_onecell_data *clk_data; + struct usb_reset_data *reset_data; + const char *clk_parent; + const char *clk_name; + void __iomem *reg; + int qty; + int i = 0; + int j = 0; + + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(reg)) + return; + + clk_parent = of_clk_get_parent_name(node, 0); + if (!clk_parent) + return; + + /* Worst-case size approximation and memory allocation */ + qty = find_last_bit((unsigned long *)&data->clk_mask, + SUNXI_USB_MAX_SIZE); + + clk_data = kmalloc(sizeof(struct clk_onecell_data), GFP_KERNEL); + if (!clk_data) + return; + + clk_data->clks = kzalloc((qty+1) * sizeof(struct clk *), GFP_KERNEL); + if (!clk_data->clks) { + kfree(clk_data); + return; + } + + for_each_set_bit(i, (unsigned long *)&data->clk_mask, + SUNXI_USB_MAX_SIZE) { + of_property_read_string_index(node, "clock-output-names", + j, &clk_name); + clk_data->clks[i] = clk_register_gate(NULL, clk_name, + clk_parent, 0, + reg, i, 0, lock); + WARN_ON(IS_ERR(clk_data->clks[i])); + + j++; + } + + /* Adjust to the real max */ + clk_data->clk_num = i; + + of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + /* Register a reset controller for usb with reset bits */ + if (data->reset_mask == 0) + return; + + reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL); + if (!reset_data) + return; + + reset_data->reg = reg; + reset_data->lock = lock; + reset_data->rcdev.nr_resets = __fls(data->reset_mask) + 1; + reset_data->rcdev.ops = &sunxi_usb_reset_ops; + reset_data->rcdev.of_node = node; + reset_controller_register(&reset_data->rcdev); +} + +static const struct usb_clk_data sun4i_a10_usb_clk_data __initconst = { + .clk_mask = BIT(8) | BIT(7) | BIT(6), + .reset_mask = BIT(2) | BIT(1) | BIT(0), +}; + +static DEFINE_SPINLOCK(sun4i_a10_usb_lock); + +static void __init sun4i_a10_usb_setup(struct device_node *node) +{ + sunxi_usb_clk_setup(node, &sun4i_a10_usb_clk_data, &sun4i_a10_usb_lock); +} +CLK_OF_DECLARE(sun4i_a10_usb, "allwinner,sun4i-a10-usb-clk", sun4i_a10_usb_setup); + +static const struct usb_clk_data sun5i_a13_usb_clk_data __initconst = { + .clk_mask = BIT(8) | BIT(6), + .reset_mask = BIT(1) | BIT(0), +}; + +static void __init sun5i_a13_usb_setup(struct device_node *node) +{ + sunxi_usb_clk_setup(node, &sun5i_a13_usb_clk_data, &sun4i_a10_usb_lock); +} +CLK_OF_DECLARE(sun5i_a13_usb, "allwinner,sun5i-a13-usb-clk", sun5i_a13_usb_setup); + +static const struct usb_clk_data sun6i_a31_usb_clk_data __initconst = { + .clk_mask = BIT(18) | BIT(17) | BIT(16) | BIT(10) | BIT(9) | BIT(8), + .reset_mask = BIT(2) | BIT(1) | BIT(0), +}; + +static void __init sun6i_a31_usb_setup(struct device_node *node) +{ + sunxi_usb_clk_setup(node, &sun6i_a31_usb_clk_data, &sun4i_a10_usb_lock); +} +CLK_OF_DECLARE(sun6i_a31_usb, "allwinner,sun6i-a31-usb-clk", sun6i_a31_usb_setup); -- cgit v0.10.2 From 71f32f56cb54303a1b6ce6811373f57d87de40d3 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Wed, 28 Jan 2015 03:54:07 +0800 Subject: clk: sunxi: Add support for sun9i A80 USB clocks and resets The USB controller/phy clocks and reset controls are in a separate address block, unlike previous SoCs where they were in the clock controller. Also, access to the address block is controlled by a clock gate to AHB. Add support for resets requiring a clock to be enabled when asserting/deasserting the reset controls, and add the sun9i USB clocks. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index 60b4428..3f1dcd8 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -66,6 +66,8 @@ Required properties: "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 + "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 Required properties for all clocks: - reg : shall be the control register address for the clock. diff --git a/drivers/clk/sunxi/clk-usb.c b/drivers/clk/sunxi/clk-usb.c index f1dcc8f..a86ed2f 100644 --- a/drivers/clk/sunxi/clk-usb.c +++ b/drivers/clk/sunxi/clk-usb.c @@ -29,6 +29,7 @@ struct usb_reset_data { void __iomem *reg; spinlock_t *lock; + struct clk *clk; struct reset_controller_dev rcdev; }; @@ -41,12 +42,14 @@ static int sunxi_usb_reset_assert(struct reset_controller_dev *rcdev, unsigned long flags; u32 reg; + clk_prepare_enable(data->clk); spin_lock_irqsave(data->lock, flags); reg = readl(data->reg); writel(reg & ~BIT(id), data->reg); spin_unlock_irqrestore(data->lock, flags); + clk_disable_unprepare(data->clk); return 0; } @@ -60,12 +63,14 @@ static int sunxi_usb_reset_deassert(struct reset_controller_dev *rcdev, unsigned long flags; u32 reg; + clk_prepare_enable(data->clk); spin_lock_irqsave(data->lock, flags); reg = readl(data->reg); writel(reg | BIT(id), data->reg); spin_unlock_irqrestore(data->lock, flags); + clk_disable_unprepare(data->clk); return 0; } @@ -84,6 +89,7 @@ static struct reset_control_ops sunxi_usb_reset_ops = { struct usb_clk_data { u32 clk_mask; u32 reset_mask; + bool reset_needs_clk; }; static void __init sunxi_usb_clk_setup(struct device_node *node, @@ -146,6 +152,15 @@ static void __init sunxi_usb_clk_setup(struct device_node *node, if (!reset_data) return; + if (data->reset_needs_clk) { + reset_data->clk = of_clk_get(node, 0); + if (IS_ERR(reset_data->clk)) { + pr_err("Could not get clock for reset controls\n"); + kfree(reset_data); + return; + } + } + reset_data->reg = reg; reset_data->lock = lock; reset_data->rcdev.nr_resets = __fls(data->reset_mask) + 1; @@ -188,3 +203,31 @@ static void __init sun6i_a31_usb_setup(struct device_node *node) sunxi_usb_clk_setup(node, &sun6i_a31_usb_clk_data, &sun4i_a10_usb_lock); } CLK_OF_DECLARE(sun6i_a31_usb, "allwinner,sun6i-a31-usb-clk", sun6i_a31_usb_setup); + +static const struct usb_clk_data sun9i_a80_usb_mod_data __initconst = { + .clk_mask = BIT(6) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1), + .reset_mask = BIT(19) | BIT(18) | BIT(17), + .reset_needs_clk = 1, +}; + +static DEFINE_SPINLOCK(a80_usb_mod_lock); + +static void __init sun9i_a80_usb_mod_setup(struct device_node *node) +{ + sunxi_usb_clk_setup(node, &sun9i_a80_usb_mod_data, &a80_usb_mod_lock); +} +CLK_OF_DECLARE(sun9i_a80_usb_mod, "allwinner,sun9i-a80-usb-mod-clk", sun9i_a80_usb_mod_setup); + +static const struct usb_clk_data sun9i_a80_usb_phy_data __initconst = { + .clk_mask = BIT(10) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1), + .reset_mask = BIT(21) | BIT(20) | BIT(19) | BIT(18) | BIT(17), + .reset_needs_clk = 1, +}; + +static DEFINE_SPINLOCK(a80_usb_phy_lock); + +static void __init sun9i_a80_usb_phy_setup(struct device_node *node) +{ + sunxi_usb_clk_setup(node, &sun9i_a80_usb_phy_data, &a80_usb_phy_lock); +} +CLK_OF_DECLARE(sun9i_a80_usb_phy, "allwinner,sun9i-a80-usb-phy-clk", sun9i_a80_usb_phy_setup); -- cgit v0.10.2 From 9f2430973d6713b73b3d25990d0ceb77a12a13a6 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 20 Mar 2015 01:19:03 +0800 Subject: clk: sunxi: Add muxable ahb factors clock for sun5i and sun7i The AHB clock on sun5i and sun7i are muxable divider clocks. Use a factors clock to support them. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index 3f1dcd8..4fa11af 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -20,6 +20,7 @@ Required properties: "allwinner,sun8i-a23-axi-clk" - for the AXI clock on A23 "allwinner,sun4i-a10-axi-gates-clk" - for the AXI gates "allwinner,sun4i-a10-ahb-clk" - for the AHB clock + "allwinner,sun5i-a13-ahb-clk" - for the AHB clock on A13 "allwinner,sun9i-a80-ahb-clk" - for the AHB bus clocks on A80 "allwinner,sun4i-a10-ahb-gates-clk" - for the AHB gates on A10 "allwinner,sun5i-a13-ahb-gates-clk" - for the AHB gates on A13 diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index b6f28ac..ae7b1c4 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -482,6 +482,45 @@ static void sun6i_a31_get_pll6_factors(u32 *freq, u32 parent_rate, } /** + * sun5i_a13_get_ahb_factors() - calculates m, p factors for AHB + * AHB rate is calculated as follows + * rate = parent_rate >> p + */ + +static void sun5i_a13_get_ahb_factors(u32 *freq, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p) +{ + u32 div; + + /* divide only */ + if (parent_rate < *freq) + *freq = parent_rate; + + /* + * user manual says valid speed is 8k ~ 276M, but tests show it + * can work at speeds up to 300M, just after reparenting to pll6 + */ + if (*freq < 8000) + *freq = 8000; + if (*freq > 300000000) + *freq = 300000000; + + div = order_base_2(DIV_ROUND_UP(parent_rate, *freq)); + + /* p = 0 ~ 3 */ + if (div > 3) + div = 3; + + *freq = parent_rate >> div; + + /* we were called to round the frequency, we can now return */ + if (p == NULL) + return; + + *p = div; +} + +/** * sun4i_get_apb1_factors() - calculates m, p factors for APB1 * APB1 rate is calculated as follows * rate = (parent_rate >> p) / (m + 1); @@ -616,6 +655,11 @@ static struct clk_factors_config sun6i_a31_pll6_config = { .n_start = 1, }; +static struct clk_factors_config sun5i_a13_ahb_config = { + .pshift = 4, + .pwidth = 2, +}; + static struct clk_factors_config sun4i_apb1_config = { .mshift = 0, .mwidth = 5, @@ -676,6 +720,13 @@ static const struct factors_data sun6i_a31_pll6_data __initconst = { .name = "pll6x2", }; +static const struct factors_data sun5i_a13_ahb_data __initconst = { + .mux = 6, + .muxmask = BIT(1) | BIT(0), + .table = &sun5i_a13_ahb_config, + .getter = sun5i_a13_get_ahb_factors, +}; + static const struct factors_data sun4i_apb1_data __initconst = { .mux = 24, .muxmask = BIT(1) | BIT(0), @@ -1184,6 +1235,7 @@ static const struct of_device_id clk_factors_match[] __initconst = { {.compatible = "allwinner,sun6i-a31-pll1-clk", .data = &sun6i_a31_pll1_data,}, {.compatible = "allwinner,sun8i-a23-pll1-clk", .data = &sun8i_a23_pll1_data,}, {.compatible = "allwinner,sun7i-a20-pll4-clk", .data = &sun7i_a20_pll4_data,}, + {.compatible = "allwinner,sun5i-a13-ahb-clk", .data = &sun5i_a13_ahb_data,}, {.compatible = "allwinner,sun4i-a10-apb1-clk", .data = &sun4i_apb1_data,}, {.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,}, {} -- cgit v0.10.2 From 946fd40f2860bca61abb51676cf72b31ca79d3f8 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 20 Mar 2015 01:19:04 +0800 Subject: clk: sunxi: Add "cpu" to list of protected clocks for sun5i Now that the ahb clock on sun5i/sun7i is muxable, ahb is no longer guaranteed to be a child of the cpu clock. Add the cpu clock to the list of protected clocks so it doesn't get disabled. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index ae7b1c4..7580a1b 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -1349,6 +1349,7 @@ static void __init sun4i_a10_init_clocks(struct device_node *node) CLK_OF_DECLARE(sun4i_a10_clk_init, "allwinner,sun4i-a10", sun4i_a10_init_clocks); static const char *sun5i_critical_clocks[] __initdata = { + "cpu", "pll5_ddr", "ahb_sdram", }; -- cgit v0.10.2 From b712a623bd5c3b04b005e757945d43441e0aa4a8 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 20 Mar 2015 01:19:05 +0800 Subject: clk: sunxi: Register divs clocks before factor clocks We want to reparent AHB clock to PLL6 on sun5i/sun7i using the assigned clocks properties. AHB is a factor clock, while PLL6 is a divs clock. Register divs clocks before factor clocks so reparenting works. This is only needed because we do the reparenting on the clock provider. The proper way to fix this is to split out all the old sunxi clocks into separate CLK_OF_DECLARE statements, like we are doing for sun9i. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 7580a1b..d92e303 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -1312,15 +1312,15 @@ static void __init sunxi_init_clocks(const char *clocks[], int nclocks) { unsigned int i; + /* Register divided output clocks */ + of_sunxi_table_clock_setup(clk_divs_match, sunxi_divs_clk_setup); + /* Register factor clocks */ of_sunxi_table_clock_setup(clk_factors_match, sunxi_factors_clk_setup); /* Register divider clocks */ of_sunxi_table_clock_setup(clk_div_match, sunxi_divider_clk_setup); - /* Register divided output clocks */ - of_sunxi_table_clock_setup(clk_divs_match, sunxi_divs_clk_setup); - /* Register mux clocks */ of_sunxi_table_clock_setup(clk_mux_match, sunxi_mux_clk_setup); -- cgit v0.10.2 From 934fe5f48ae52841f8a5f5e0411147a8ccd171c1 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Wed, 25 Mar 2015 01:22:07 +0800 Subject: clk: sunxi: Make divs clocks specify which output is the base factor clock The current sunxi clock driver has the base factor clock of divs clocks as the last clock output of the clock node. This makes it rather difficult to add new outputs, such as fixed dividers, which were previously unknown. This patch makes the divs clocks data structure specify which output is the factor clock, and updates all current divs clocks accordingly. We can then add new outputs after the factor clocks, at least not breaking backward compatibility with regards to the devicetree bindings. Also replace kzalloc with kcalloc in sunxi_divs_clk_setup(). Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index d92e303..9f31314 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -1046,13 +1046,20 @@ static void __init sunxi_gates_clk_setup(struct device_node *node, * sunxi_divs_clk_setup() helper data */ -#define SUNXI_DIVS_MAX_QTY 2 +#define SUNXI_DIVS_MAX_QTY 4 #define SUNXI_DIVISOR_WIDTH 2 struct divs_data { const struct factors_data *factors; /* data for the factor clock */ - int ndivs; /* number of children */ + int ndivs; /* number of outputs */ + /* + * List of outputs. Refer to the diagram for sunxi_divs_clk_setup(): + * self or base factor clock refers to the output from the pll + * itself. The remaining refer to fixed or configurable divider + * outputs. + */ struct { + u8 self; /* is it the base factor clock? (only one) */ u8 fixed; /* is it a fixed divisor? if not... */ struct clk_div_table *table; /* is it a table based divisor? */ u8 shift; /* otherwise it's a normal divisor with this shift */ @@ -1075,23 +1082,26 @@ static const struct divs_data pll5_divs_data __initconst = { .div = { { .shift = 0, .pow = 0, }, /* M, DDR */ { .shift = 16, .pow = 1, }, /* P, other */ + /* No output for the base factor clock */ } }; static const struct divs_data pll6_divs_data __initconst = { .factors = &sun4i_pll6_data, - .ndivs = 2, + .ndivs = 3, .div = { { .shift = 0, .table = pll6_sata_tbl, .gate = 14 }, /* M, SATA */ { .fixed = 2 }, /* P, other */ + { .self = 1 }, /* base factor clock, 2x */ } }; static const struct divs_data sun6i_a31_pll6_divs_data __initconst = { .factors = &sun6i_a31_pll6_data, - .ndivs = 1, + .ndivs = 2, .div = { { .fixed = 2 }, /* normal output */ + { .self = 1 }, /* base factor clock, 2x */ } }; @@ -1122,6 +1132,10 @@ static void __init sunxi_divs_clk_setup(struct device_node *node, int ndivs = SUNXI_DIVS_MAX_QTY, i = 0; int flags, clkflags; + /* if number of children known, use it */ + if (data->ndivs) + ndivs = data->ndivs; + /* Set up factor clock that we will be dividing */ pclk = sunxi_factors_clk_setup(node, data->factors); parent = __clk_get_name(pclk); @@ -1132,7 +1146,7 @@ static void __init sunxi_divs_clk_setup(struct device_node *node, if (!clk_data) return; - clks = kzalloc((SUNXI_DIVS_MAX_QTY+1) * sizeof(*clks), GFP_KERNEL); + clks = kcalloc(ndivs, sizeof(*clks), GFP_KERNEL); if (!clks) goto free_clkdata; @@ -1142,15 +1156,17 @@ static void __init sunxi_divs_clk_setup(struct device_node *node, * our RAM clock! */ clkflags = !strcmp("pll5", parent) ? 0 : CLK_SET_RATE_PARENT; - /* if number of children known, use it */ - if (data->ndivs) - ndivs = data->ndivs; - for (i = 0; i < ndivs; i++) { if (of_property_read_string_index(node, "clock-output-names", i, &clk_name) != 0) break; + /* If this is the base factor clock, only update clks */ + if (data->div[i].self) { + clk_data->clks[i] = pclk; + continue; + } + gate_hw = NULL; rate_hw = NULL; rate_ops = NULL; @@ -1209,9 +1225,6 @@ static void __init sunxi_divs_clk_setup(struct device_node *node, clk_register_clkdev(clks[i], clk_name, NULL); } - /* The last clock available on the getter is the parent */ - clks[i++] = pclk; - /* Adjust to the real max */ clk_data->clk_num = i; -- cgit v0.10.2 From f1017969661dd33ead5ba7c3f4a0793c6611441a Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Wed, 25 Mar 2015 01:22:08 +0800 Subject: clk: sunxi: Add pll6 / 4 clock output to sun4i-a10-pll6 The pll6 has a /4 output that is used as an input to the ahb mux clock. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 9f31314..7e1e2bd 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -1088,11 +1088,12 @@ static const struct divs_data pll5_divs_data __initconst = { static const struct divs_data pll6_divs_data __initconst = { .factors = &sun4i_pll6_data, - .ndivs = 3, + .ndivs = 4, .div = { { .shift = 0, .table = pll6_sata_tbl, .gate = 14 }, /* M, SATA */ { .fixed = 2 }, /* P, other */ { .self = 1 }, /* base factor clock, 2x */ + { .fixed = 4 }, /* pll6 / 4, used as ahb input */ } }; -- cgit v0.10.2