diff options
author | Maxime Ripard <maxime.ripard@free-electrons.com> | 2014-12-07 19:43:04 +0300 |
---|---|---|
committer | Maxime Ripard <maxime.ripard@free-electrons.com> | 2015-01-14 12:39:16 +0300 |
commit | 6b0b8ccff002414fab08a080c7a8a6ee3db22c0d (patch) | |
tree | 608d0cc1f59c46ae0f6afbe88eb0d368a88a606d | |
parent | 3ec72fabcc6f4f5c786c50e08b59e1251d0fdfeb (diff) | |
download | linux-6b0b8ccff002414fab08a080c7a8a6ee3db22c0d.tar.xz |
clk: sunxi: Rework MMC phase clocks
Instead of having three different clocks for the main MMC clock and the two
phase sub-clocks, which involved having three different drivers sharing the
same register, rework it to have the same single driver registering three
different clocks.
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Reviewed-by: Chen-Yu Tsai <wens@csie.org>
Tested-by: Chen-Yu Tsai <wens@csie.org>
Acked-by: Mike Turquette <mturquette@linaro.org>
-rw-r--r-- | Documentation/devicetree/bindings/clock/sunxi.txt | 13 | ||||
-rw-r--r-- | drivers/clk/sunxi/clk-mod0.c | 131 |
2 files changed, 77 insertions, 67 deletions
diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index 9dc4f55a04ad..e4c42276c577 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -55,8 +55,7 @@ Required properties: "allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31 "allwinner,sun8i-a23-apb2-gates-clk" - for the APB2 gates on A23 "allwinner,sun5i-a13-mbus-clk" - for the MBUS clock on A13 - "allwinner,sun4i-a10-mmc-output-clk" - for the MMC output clock on A10 - "allwinner,sun4i-a10-mmc-sample-clk" - for the MMC sample clock on A10 + "allwinner,sun4i-a10-mmc-clk" - for the MMC clock "allwinner,sun4i-a10-mod0-clk" - for the module 0 family of clocks "allwinner,sun8i-a23-mbus-clk" - for the MBUS clock on A23 "allwinner,sun7i-a20-out-clk" - for the external output clocks @@ -95,6 +94,10 @@ For "allwinner,sun6i-a31-pll6-clk", there are 2 outputs. The first output is the normal PLL6 output, or "pll6". The second output is rate doubled PLL6, or "pll6x2". +The "allwinner,sun4i-a10-mmc-clk" has three different outputs: the +main clock, with the ID 0, and the output and sample clocks, with the +IDs 1 and 2, respectively. + For example: osc24M: clk@01c20050 { @@ -138,11 +141,11 @@ cpu: cpu@01c20054 { }; mmc0_clk: clk@01c20088 { - #clock-cells = <0>; - compatible = "allwinner,sun4i-mod0-clk"; + #clock-cells = <1>; + compatible = "allwinner,sun4i-a10-mmc-clk"; reg = <0x01c20088 0x4>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; - clock-output-names = "mmc0"; + clock-output-names = "mmc0", "mmc0_output", "mmc0_sample"; }; mii_phy_tx_clk: clk@2 { diff --git a/drivers/clk/sunxi/clk-mod0.c b/drivers/clk/sunxi/clk-mod0.c index bf8fcd8c940e..e37eb6fe19e8 100644 --- a/drivers/clk/sunxi/clk-mod0.c +++ b/drivers/clk/sunxi/clk-mod0.c @@ -152,14 +152,10 @@ static void __init sun5i_a13_mbus_setup(struct device_node *node) } CLK_OF_DECLARE(sun5i_a13_mbus, "allwinner,sun5i-a13-mbus-clk", sun5i_a13_mbus_setup); -struct mmc_phase_data { - u8 offset; -}; - struct mmc_phase { struct clk_hw hw; + u8 offset; void __iomem *reg; - struct mmc_phase_data *data; spinlock_t *lock; }; @@ -175,7 +171,7 @@ static int mmc_get_phase(struct clk_hw *hw) u8 delay; value = readl(phase->reg); - delay = (value >> phase->data->offset) & 0x3; + delay = (value >> phase->offset) & 0x3; if (!delay) return 180; @@ -263,8 +259,8 @@ static int mmc_set_phase(struct clk_hw *hw, int degrees) spin_lock_irqsave(phase->lock, flags); value = readl(phase->reg); - value &= ~GENMASK(phase->data->offset + 3, phase->data->offset); - value |= delay << phase->data->offset; + value &= ~GENMASK(phase->offset + 3, phase->offset); + value |= delay << phase->offset; writel(value, phase->reg); spin_unlock_irqrestore(phase->lock, flags); @@ -276,66 +272,77 @@ static const struct clk_ops mmc_clk_ops = { .set_phase = mmc_set_phase, }; -static void __init sun4i_a10_mmc_phase_setup(struct device_node *node, - struct mmc_phase_data *data) -{ - const char *parent_names[1] = { of_clk_get_parent_name(node, 0) }; - struct clk_init_data init = { - .num_parents = 1, - .parent_names = parent_names, - .ops = &mmc_clk_ops, - }; - - struct mmc_phase *phase; - struct clk *clk; - - phase = kmalloc(sizeof(*phase), GFP_KERNEL); - if (!phase) - return; - - phase->hw.init = &init; +static DEFINE_SPINLOCK(sun4i_a10_mmc_lock); - phase->reg = of_iomap(node, 0); - if (!phase->reg) - goto err_free; +static void __init sun4i_a10_mmc_setup(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + const char *parent; + void __iomem *reg; + int i; - phase->data = data; - phase->lock = &sun4i_a10_mod0_lock; + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(reg)) { + pr_err("Couldn't map the %s clock registers\n", node->name); + return; + } - if (of_property_read_string(node, "clock-output-names", &init.name)) - init.name = node->name; + clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + return; - clk = clk_register(NULL, &phase->hw); - if (IS_ERR(clk)) - goto err_unmap; + clk_data->clks = kcalloc(3, sizeof(*clk_data->clks), GFP_KERNEL); + if (!clk_data->clks) + goto err_free_data; + + clk_data->clk_num = 3; + clk_data->clks[0] = sunxi_factors_register(node, + &sun4i_a10_mod0_data, + &sun4i_a10_mmc_lock, reg); + if (!clk_data->clks[0]) + goto err_free_clks; + + parent = __clk_get_name(clk_data->clks[0]); + + for (i = 1; i < 3; i++) { + struct clk_init_data init = { + .num_parents = 1, + .parent_names = &parent, + .ops = &mmc_clk_ops, + }; + struct mmc_phase *phase; + + phase = kmalloc(sizeof(*phase), GFP_KERNEL); + if (!phase) + continue; + + phase->hw.init = &init; + phase->reg = reg; + phase->lock = &sun4i_a10_mmc_lock; + + if (i == 1) + phase->offset = 8; + else + phase->offset = 20; + + if (of_property_read_string_index(node, "clock-output-names", + i, &init.name)) + init.name = node->name; + + clk_data->clks[i] = clk_register(NULL, &phase->hw); + if (IS_ERR(clk_data->clks[i])) { + kfree(phase); + continue; + } + } - of_clk_add_provider(node, of_clk_src_simple_get, clk); + of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); return; -err_unmap: - iounmap(phase->reg); -err_free: - kfree(phase); -} - - -static struct mmc_phase_data mmc_output_clk = { - .offset = 8, -}; - -static struct mmc_phase_data mmc_sample_clk = { - .offset = 20, -}; - -static void __init sun4i_a10_mmc_output_setup(struct device_node *node) -{ - sun4i_a10_mmc_phase_setup(node, &mmc_output_clk); -} -CLK_OF_DECLARE(sun4i_a10_mmc_output, "allwinner,sun4i-a10-mmc-output-clk", sun4i_a10_mmc_output_setup); - -static void __init sun4i_a10_mmc_sample_setup(struct device_node *node) -{ - sun4i_a10_mmc_phase_setup(node, &mmc_sample_clk); +err_free_clks: + kfree(clk_data->clks); +err_free_data: + kfree(clk_data); } -CLK_OF_DECLARE(sun4i_a10_mmc_sample, "allwinner,sun4i-a10-mmc-sample-clk", sun4i_a10_mmc_sample_setup); +CLK_OF_DECLARE(sun4i_a10_mmc, "allwinner,sun4i-a10-mmc-clk", sun4i_a10_mmc_setup); |