diff options
author | Olof Johansson <olof@lixom.net> | 2015-10-26 01:42:37 -0400 |
---|---|---|
committer | Olof Johansson <olof@lixom.net> | 2015-10-26 01:42:37 -0400 |
commit | edd2a06d9c06a28f6f636929d586d17de835cd6b (patch) | |
tree | 97c8d63dd247494999bda213da1b845de853cf69 | |
parent | 64ebda3acd6ea592e6f97dcd0682e87aa7cf8d61 (diff) | |
parent | 9b038bc58ad2658c76fd8b50bb333dfd4454573c (diff) |
Merge tag 'sunxi-clocks-for-4.4' of https://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux into next/dt
Bringing in the sunxi clock branch since it introduces header file contents
that is needed by the DT branch. This is a stable tag shared with the clk tree.
Allwinner clock additions for 4.4
- Support for the Audio PLL and child clocks
- Support for the A33 AHB gates
- New clk-multiplier generic driver
* tag 'sunxi-clocks-for-4.4' of https://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux:
clk: sunxi: mod1 clock support
clk: sunxi: codec clock support
clk: sunxi: pll2: Add A13 support
clk: sunxi: Add a driver for the PLL2
clk: Add a basic multiplier clock
clk: sunxi: Add A33 gates support
Signed-off-by: Olof Johansson <olof@lixom.net>
-rw-r--r-- | drivers/clk/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/clk-multiplier.c | 181 | ||||
-rw-r--r-- | drivers/clk/sunxi/Makefile | 3 | ||||
-rw-r--r-- | drivers/clk/sunxi/clk-a10-codec.c | 44 | ||||
-rw-r--r-- | drivers/clk/sunxi/clk-a10-mod1.c | 81 | ||||
-rw-r--r-- | drivers/clk/sunxi/clk-a10-pll2.c | 216 | ||||
-rw-r--r-- | drivers/clk/sunxi/clk-simple-gates.c | 2 | ||||
-rw-r--r-- | include/dt-bindings/clock/sun4i-a10-pll2.h | 53 | ||||
-rw-r--r-- | include/linux/clk-provider.h | 42 |
9 files changed, 623 insertions, 0 deletions
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 9f7155855391..0066a06abbf3 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile | |||
@@ -6,6 +6,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-divider.o | |||
6 | obj-$(CONFIG_COMMON_CLK) += clk-fixed-factor.o | 6 | obj-$(CONFIG_COMMON_CLK) += clk-fixed-factor.o |
7 | obj-$(CONFIG_COMMON_CLK) += clk-fixed-rate.o | 7 | obj-$(CONFIG_COMMON_CLK) += clk-fixed-rate.o |
8 | obj-$(CONFIG_COMMON_CLK) += clk-gate.o | 8 | obj-$(CONFIG_COMMON_CLK) += clk-gate.o |
9 | obj-$(CONFIG_COMMON_CLK) += clk-multiplier.o | ||
9 | obj-$(CONFIG_COMMON_CLK) += clk-mux.o | 10 | obj-$(CONFIG_COMMON_CLK) += clk-mux.o |
10 | obj-$(CONFIG_COMMON_CLK) += clk-composite.o | 11 | obj-$(CONFIG_COMMON_CLK) += clk-composite.o |
11 | obj-$(CONFIG_COMMON_CLK) += clk-fractional-divider.o | 12 | obj-$(CONFIG_COMMON_CLK) += clk-fractional-divider.o |
diff --git a/drivers/clk/clk-multiplier.c b/drivers/clk/clk-multiplier.c new file mode 100644 index 000000000000..43ec269fcbff --- /dev/null +++ b/drivers/clk/clk-multiplier.c | |||
@@ -0,0 +1,181 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2015 Maxime Ripard <maxime.ripard@free-electrons.com> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #include <linux/bitops.h> | ||
10 | #include <linux/clk-provider.h> | ||
11 | #include <linux/err.h> | ||
12 | #include <linux/export.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/of.h> | ||
15 | #include <linux/slab.h> | ||
16 | |||
17 | #define to_clk_multiplier(_hw) container_of(_hw, struct clk_multiplier, hw) | ||
18 | |||
19 | static unsigned long __get_mult(struct clk_multiplier *mult, | ||
20 | unsigned long rate, | ||
21 | unsigned long parent_rate) | ||
22 | { | ||
23 | if (mult->flags & CLK_MULTIPLIER_ROUND_CLOSEST) | ||
24 | return DIV_ROUND_CLOSEST(rate, parent_rate); | ||
25 | |||
26 | return rate / parent_rate; | ||
27 | } | ||
28 | |||
29 | static unsigned long clk_multiplier_recalc_rate(struct clk_hw *hw, | ||
30 | unsigned long parent_rate) | ||
31 | { | ||
32 | struct clk_multiplier *mult = to_clk_multiplier(hw); | ||
33 | unsigned long val; | ||
34 | |||
35 | val = clk_readl(mult->reg) >> mult->shift; | ||
36 | val &= GENMASK(mult->width - 1, 0); | ||
37 | |||
38 | if (!val && mult->flags & CLK_MULTIPLIER_ZERO_BYPASS) | ||
39 | val = 1; | ||
40 | |||
41 | return parent_rate * val; | ||
42 | } | ||
43 | |||
44 | static bool __is_best_rate(unsigned long rate, unsigned long new, | ||
45 | unsigned long best, unsigned long flags) | ||
46 | { | ||
47 | if (flags & CLK_MULTIPLIER_ROUND_CLOSEST) | ||
48 | return abs(rate - new) < abs(rate - best); | ||
49 | |||
50 | return new >= rate && new < best; | ||
51 | } | ||
52 | |||
53 | static unsigned long __bestmult(struct clk_hw *hw, unsigned long rate, | ||
54 | unsigned long *best_parent_rate, | ||
55 | u8 width, unsigned long flags) | ||
56 | { | ||
57 | unsigned long orig_parent_rate = *best_parent_rate; | ||
58 | unsigned long parent_rate, current_rate, best_rate = ~0; | ||
59 | unsigned int i, bestmult = 0; | ||
60 | |||
61 | if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) | ||
62 | return rate / *best_parent_rate; | ||
63 | |||
64 | for (i = 1; i < ((1 << width) - 1); i++) { | ||
65 | if (rate == orig_parent_rate * i) { | ||
66 | /* | ||
67 | * This is the best case for us if we have a | ||
68 | * perfect match without changing the parent | ||
69 | * rate. | ||
70 | */ | ||
71 | *best_parent_rate = orig_parent_rate; | ||
72 | return i; | ||
73 | } | ||
74 | |||
75 | parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), | ||
76 | rate / i); | ||
77 | current_rate = parent_rate * i; | ||
78 | |||
79 | if (__is_best_rate(rate, current_rate, best_rate, flags)) { | ||
80 | bestmult = i; | ||
81 | best_rate = current_rate; | ||
82 | *best_parent_rate = parent_rate; | ||
83 | } | ||
84 | } | ||
85 | |||
86 | return bestmult; | ||
87 | } | ||
88 | |||
89 | static long clk_multiplier_round_rate(struct clk_hw *hw, unsigned long rate, | ||
90 | unsigned long *parent_rate) | ||
91 | { | ||
92 | struct clk_multiplier *mult = to_clk_multiplier(hw); | ||
93 | unsigned long factor = __bestmult(hw, rate, parent_rate, | ||
94 | mult->width, mult->flags); | ||
95 | |||
96 | return *parent_rate * factor; | ||
97 | } | ||
98 | |||
99 | static int clk_multiplier_set_rate(struct clk_hw *hw, unsigned long rate, | ||
100 | unsigned long parent_rate) | ||
101 | { | ||
102 | struct clk_multiplier *mult = to_clk_multiplier(hw); | ||
103 | unsigned long factor = __get_mult(mult, rate, parent_rate); | ||
104 | unsigned long flags = 0; | ||
105 | unsigned long val; | ||
106 | |||
107 | if (mult->lock) | ||
108 | spin_lock_irqsave(mult->lock, flags); | ||
109 | else | ||
110 | __acquire(mult->lock); | ||
111 | |||
112 | val = clk_readl(mult->reg); | ||
113 | val &= ~GENMASK(mult->width + mult->shift - 1, mult->shift); | ||
114 | val |= factor << mult->shift; | ||
115 | clk_writel(val, mult->reg); | ||
116 | |||
117 | if (mult->lock) | ||
118 | spin_unlock_irqrestore(mult->lock, flags); | ||
119 | else | ||
120 | __release(mult->lock); | ||
121 | |||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | const struct clk_ops clk_multiplier_ops = { | ||
126 | .recalc_rate = clk_multiplier_recalc_rate, | ||
127 | .round_rate = clk_multiplier_round_rate, | ||
128 | .set_rate = clk_multiplier_set_rate, | ||
129 | }; | ||
130 | EXPORT_SYMBOL_GPL(clk_multiplier_ops); | ||
131 | |||
132 | struct clk *clk_register_multiplier(struct device *dev, const char *name, | ||
133 | const char *parent_name, | ||
134 | unsigned long flags, | ||
135 | void __iomem *reg, u8 shift, u8 width, | ||
136 | u8 clk_mult_flags, spinlock_t *lock) | ||
137 | { | ||
138 | struct clk_init_data init; | ||
139 | struct clk_multiplier *mult; | ||
140 | struct clk *clk; | ||
141 | |||
142 | mult = kmalloc(sizeof(*mult), GFP_KERNEL); | ||
143 | if (!mult) | ||
144 | return ERR_PTR(-ENOMEM); | ||
145 | |||
146 | init.name = name; | ||
147 | init.ops = &clk_multiplier_ops; | ||
148 | init.flags = flags | CLK_IS_BASIC; | ||
149 | init.parent_names = &parent_name; | ||
150 | init.num_parents = 1; | ||
151 | |||
152 | mult->reg = reg; | ||
153 | mult->shift = shift; | ||
154 | mult->width = width; | ||
155 | mult->flags = clk_mult_flags; | ||
156 | mult->lock = lock; | ||
157 | mult->hw.init = &init; | ||
158 | |||
159 | clk = clk_register(dev, &mult->hw); | ||
160 | if (IS_ERR(clk)) | ||
161 | kfree(mult); | ||
162 | |||
163 | return clk; | ||
164 | } | ||
165 | EXPORT_SYMBOL_GPL(clk_register_multiplier); | ||
166 | |||
167 | void clk_unregister_multiplier(struct clk *clk) | ||
168 | { | ||
169 | struct clk_multiplier *mult; | ||
170 | struct clk_hw *hw; | ||
171 | |||
172 | hw = __clk_get_hw(clk); | ||
173 | if (!hw) | ||
174 | return; | ||
175 | |||
176 | mult = to_clk_multiplier(hw); | ||
177 | |||
178 | clk_unregister(clk); | ||
179 | kfree(mult); | ||
180 | } | ||
181 | EXPORT_SYMBOL_GPL(clk_unregister_multiplier); | ||
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile index f5a35b82cc1a..cb4c299214ce 100644 --- a/drivers/clk/sunxi/Makefile +++ b/drivers/clk/sunxi/Makefile | |||
@@ -3,7 +3,10 @@ | |||
3 | # | 3 | # |
4 | 4 | ||
5 | obj-y += clk-sunxi.o clk-factors.o | 5 | obj-y += clk-sunxi.o clk-factors.o |
6 | obj-y += clk-a10-codec.o | ||
6 | obj-y += clk-a10-hosc.o | 7 | obj-y += clk-a10-hosc.o |
8 | obj-y += clk-a10-mod1.o | ||
9 | obj-y += clk-a10-pll2.o | ||
7 | obj-y += clk-a20-gmac.o | 10 | obj-y += clk-a20-gmac.o |
8 | obj-y += clk-mod0.o | 11 | obj-y += clk-mod0.o |
9 | obj-y += clk-simple-gates.o | 12 | obj-y += clk-simple-gates.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 @@ | |||
1 | /* | ||
2 | * Copyright 2013 Emilio López | ||
3 | * | ||
4 | * Emilio López <emilio@elopez.com.ar> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | */ | ||
16 | |||
17 | #include <linux/clk-provider.h> | ||
18 | #include <linux/of.h> | ||
19 | #include <linux/of_address.h> | ||
20 | |||
21 | #define SUN4I_CODEC_GATE 31 | ||
22 | |||
23 | static void __init sun4i_codec_clk_setup(struct device_node *node) | ||
24 | { | ||
25 | struct clk *clk; | ||
26 | const char *clk_name = node->name, *parent_name; | ||
27 | void __iomem *reg; | ||
28 | |||
29 | reg = of_io_request_and_map(node, 0, of_node_full_name(node)); | ||
30 | if (IS_ERR(reg)) | ||
31 | return; | ||
32 | |||
33 | of_property_read_string(node, "clock-output-names", &clk_name); | ||
34 | parent_name = of_clk_get_parent_name(node, 0); | ||
35 | |||
36 | clk = clk_register_gate(NULL, clk_name, parent_name, | ||
37 | CLK_SET_RATE_PARENT, reg, | ||
38 | SUN4I_CODEC_GATE, 0, NULL); | ||
39 | |||
40 | if (!IS_ERR(clk)) | ||
41 | of_clk_add_provider(node, of_clk_src_simple_get, clk); | ||
42 | } | ||
43 | CLK_OF_DECLARE(sun4i_codec, "allwinner,sun4i-a10-codec-clk", | ||
44 | sun4i_codec_clk_setup); | ||
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 @@ | |||
1 | /* | ||
2 | * Copyright 2013 Emilio López | ||
3 | * | ||
4 | * Emilio López <emilio@elopez.com.ar> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | */ | ||
16 | |||
17 | #include <linux/clk-provider.h> | ||
18 | #include <linux/of.h> | ||
19 | #include <linux/of_address.h> | ||
20 | #include <linux/slab.h> | ||
21 | |||
22 | static DEFINE_SPINLOCK(mod1_lock); | ||
23 | |||
24 | #define SUN4I_MOD1_ENABLE 31 | ||
25 | #define SUN4I_MOD1_MUX 16 | ||
26 | #define SUN4I_MOD1_MUX_WIDTH 2 | ||
27 | #define SUN4I_MOD1_MAX_PARENTS 4 | ||
28 | |||
29 | static void __init sun4i_mod1_clk_setup(struct device_node *node) | ||
30 | { | ||
31 | struct clk *clk; | ||
32 | struct clk_mux *mux; | ||
33 | struct clk_gate *gate; | ||
34 | const char *parents[4]; | ||
35 | const char *clk_name = node->name; | ||
36 | void __iomem *reg; | ||
37 | int i; | ||
38 | |||
39 | reg = of_io_request_and_map(node, 0, of_node_full_name(node)); | ||
40 | if (IS_ERR(reg)) | ||
41 | return; | ||
42 | |||
43 | mux = kzalloc(sizeof(*mux), GFP_KERNEL); | ||
44 | if (!mux) | ||
45 | goto err_unmap; | ||
46 | |||
47 | gate = kzalloc(sizeof(*gate), GFP_KERNEL); | ||
48 | if (!gate) | ||
49 | goto err_free_mux; | ||
50 | |||
51 | of_property_read_string(node, "clock-output-names", &clk_name); | ||
52 | i = of_clk_parent_fill(node, parents, SUN4I_MOD1_MAX_PARENTS); | ||
53 | |||
54 | gate->reg = reg; | ||
55 | gate->bit_idx = SUN4I_MOD1_ENABLE; | ||
56 | gate->lock = &mod1_lock; | ||
57 | mux->reg = reg; | ||
58 | mux->shift = SUN4I_MOD1_MUX; | ||
59 | mux->mask = BIT(SUN4I_MOD1_MUX_WIDTH) - 1; | ||
60 | mux->lock = &mod1_lock; | ||
61 | |||
62 | clk = clk_register_composite(NULL, clk_name, parents, i, | ||
63 | &mux->hw, &clk_mux_ops, | ||
64 | NULL, NULL, | ||
65 | &gate->hw, &clk_gate_ops, 0); | ||
66 | if (IS_ERR(clk)) | ||
67 | goto err_free_gate; | ||
68 | |||
69 | of_clk_add_provider(node, of_clk_src_simple_get, clk); | ||
70 | |||
71 | return; | ||
72 | |||
73 | err_free_gate: | ||
74 | kfree(gate); | ||
75 | err_free_mux: | ||
76 | kfree(mux); | ||
77 | err_unmap: | ||
78 | iounmap(reg); | ||
79 | } | ||
80 | CLK_OF_DECLARE(sun4i_mod1, "allwinner,sun4i-a10-mod1-clk", | ||
81 | sun4i_mod1_clk_setup); | ||
diff --git a/drivers/clk/sunxi/clk-a10-pll2.c b/drivers/clk/sunxi/clk-a10-pll2.c new file mode 100644 index 000000000000..5484c31ec568 --- /dev/null +++ b/drivers/clk/sunxi/clk-a10-pll2.c | |||
@@ -0,0 +1,216 @@ | |||
1 | /* | ||
2 | * Copyright 2013 Emilio López | ||
3 | * Emilio López <emilio@elopez.com.ar> | ||
4 | * | ||
5 | * Copyright 2015 Maxime Ripard | ||
6 | * Maxime Ripard <maxime.ripard@free-electrons.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | */ | ||
18 | |||
19 | #include <linux/clk-provider.h> | ||
20 | #include <linux/of.h> | ||
21 | #include <linux/of_address.h> | ||
22 | #include <linux/slab.h> | ||
23 | |||
24 | #include <dt-bindings/clock/sun4i-a10-pll2.h> | ||
25 | |||
26 | #define SUN4I_PLL2_ENABLE 31 | ||
27 | |||
28 | #define SUN4I_PLL2_PRE_DIV_SHIFT 0 | ||
29 | #define SUN4I_PLL2_PRE_DIV_WIDTH 5 | ||
30 | #define SUN4I_PLL2_PRE_DIV_MASK GENMASK(SUN4I_PLL2_PRE_DIV_WIDTH - 1, 0) | ||
31 | |||
32 | #define SUN4I_PLL2_N_SHIFT 8 | ||
33 | #define SUN4I_PLL2_N_WIDTH 7 | ||
34 | #define SUN4I_PLL2_N_MASK GENMASK(SUN4I_PLL2_N_WIDTH - 1, 0) | ||
35 | |||
36 | #define SUN4I_PLL2_POST_DIV_SHIFT 26 | ||
37 | #define SUN4I_PLL2_POST_DIV_WIDTH 4 | ||
38 | #define SUN4I_PLL2_POST_DIV_MASK GENMASK(SUN4I_PLL2_POST_DIV_WIDTH - 1, 0) | ||
39 | |||
40 | #define SUN4I_PLL2_POST_DIV_VALUE 4 | ||
41 | |||
42 | #define SUN4I_PLL2_OUTPUTS 4 | ||
43 | |||
44 | struct sun4i_pll2_data { | ||
45 | u32 post_div_offset; | ||
46 | u32 pre_div_flags; | ||
47 | }; | ||
48 | |||
49 | static DEFINE_SPINLOCK(sun4i_a10_pll2_lock); | ||
50 | |||
51 | static void __init sun4i_pll2_setup(struct device_node *node, | ||
52 | struct sun4i_pll2_data *data) | ||
53 | { | ||
54 | const char *clk_name = node->name, *parent; | ||
55 | struct clk **clks, *base_clk, *prediv_clk; | ||
56 | struct clk_onecell_data *clk_data; | ||
57 | struct clk_multiplier *mult; | ||
58 | struct clk_gate *gate; | ||
59 | void __iomem *reg; | ||
60 | u32 val; | ||
61 | |||
62 | reg = of_io_request_and_map(node, 0, of_node_full_name(node)); | ||
63 | if (IS_ERR(reg)) | ||
64 | return; | ||
65 | |||
66 | clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); | ||
67 | if (!clk_data) | ||
68 | goto err_unmap; | ||
69 | |||
70 | clks = kcalloc(SUN4I_PLL2_OUTPUTS, sizeof(struct clk *), GFP_KERNEL); | ||
71 | if (!clks) | ||
72 | goto err_free_data; | ||
73 | |||
74 | parent = of_clk_get_parent_name(node, 0); | ||
75 | prediv_clk = clk_register_divider(NULL, "pll2-prediv", | ||
76 | parent, 0, reg, | ||
77 | SUN4I_PLL2_PRE_DIV_SHIFT, | ||
78 | SUN4I_PLL2_PRE_DIV_WIDTH, | ||
79 | data->pre_div_flags, | ||
80 | &sun4i_a10_pll2_lock); | ||
81 | if (!prediv_clk) { | ||
82 | pr_err("Couldn't register the prediv clock\n"); | ||
83 | goto err_free_array; | ||
84 | } | ||
85 | |||
86 | /* Setup the gate part of the PLL2 */ | ||
87 | gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); | ||
88 | if (!gate) | ||
89 | goto err_unregister_prediv; | ||
90 | |||
91 | gate->reg = reg; | ||
92 | gate->bit_idx = SUN4I_PLL2_ENABLE; | ||
93 | gate->lock = &sun4i_a10_pll2_lock; | ||
94 | |||
95 | /* Setup the multiplier part of the PLL2 */ | ||
96 | mult = kzalloc(sizeof(struct clk_multiplier), GFP_KERNEL); | ||
97 | if (!mult) | ||
98 | goto err_free_gate; | ||
99 | |||
100 | mult->reg = reg; | ||
101 | mult->shift = SUN4I_PLL2_N_SHIFT; | ||
102 | mult->width = 7; | ||
103 | mult->flags = CLK_MULTIPLIER_ZERO_BYPASS | | ||
104 | CLK_MULTIPLIER_ROUND_CLOSEST; | ||
105 | mult->lock = &sun4i_a10_pll2_lock; | ||
106 | |||
107 | parent = __clk_get_name(prediv_clk); | ||
108 | base_clk = clk_register_composite(NULL, "pll2-base", | ||
109 | &parent, 1, | ||
110 | NULL, NULL, | ||
111 | &mult->hw, &clk_multiplier_ops, | ||
112 | &gate->hw, &clk_gate_ops, | ||
113 | CLK_SET_RATE_PARENT); | ||
114 | if (!base_clk) { | ||
115 | pr_err("Couldn't register the base multiplier clock\n"); | ||
116 | goto err_free_multiplier; | ||
117 | } | ||
118 | |||
119 | parent = __clk_get_name(base_clk); | ||
120 | |||
121 | /* | ||
122 | * PLL2-1x | ||
123 | * | ||
124 | * This is supposed to have a post divider, but we won't need | ||
125 | * to use it, we just need to initialise it to 4, and use a | ||
126 | * fixed divider. | ||
127 | */ | ||
128 | val = readl(reg); | ||
129 | val &= ~(SUN4I_PLL2_POST_DIV_MASK << SUN4I_PLL2_POST_DIV_SHIFT); | ||
130 | val |= (SUN4I_PLL2_POST_DIV_VALUE - data->post_div_offset) << SUN4I_PLL2_POST_DIV_SHIFT; | ||
131 | writel(val, reg); | ||
132 | |||
133 | of_property_read_string_index(node, "clock-output-names", | ||
134 | SUN4I_A10_PLL2_1X, &clk_name); | ||
135 | clks[SUN4I_A10_PLL2_1X] = clk_register_fixed_factor(NULL, clk_name, | ||
136 | parent, | ||
137 | CLK_SET_RATE_PARENT, | ||
138 | 1, | ||
139 | SUN4I_PLL2_POST_DIV_VALUE); | ||
140 | WARN_ON(IS_ERR(clks[SUN4I_A10_PLL2_1X])); | ||
141 | |||
142 | /* | ||
143 | * PLL2-2x | ||
144 | * | ||
145 | * This clock doesn't use the post divider, and really is just | ||
146 | * a fixed divider from the PLL2 base clock. | ||
147 | */ | ||
148 | of_property_read_string_index(node, "clock-output-names", | ||
149 | SUN4I_A10_PLL2_2X, &clk_name); | ||
150 | clks[SUN4I_A10_PLL2_2X] = clk_register_fixed_factor(NULL, clk_name, | ||
151 | parent, | ||
152 | CLK_SET_RATE_PARENT, | ||
153 | 1, 2); | ||
154 | WARN_ON(IS_ERR(clks[SUN4I_A10_PLL2_2X])); | ||
155 | |||
156 | /* PLL2-4x */ | ||
157 | of_property_read_string_index(node, "clock-output-names", | ||
158 | SUN4I_A10_PLL2_4X, &clk_name); | ||
159 | clks[SUN4I_A10_PLL2_4X] = clk_register_fixed_factor(NULL, clk_name, | ||
160 | parent, | ||
161 | CLK_SET_RATE_PARENT, | ||
162 | 1, 1); | ||
163 | WARN_ON(IS_ERR(clks[SUN4I_A10_PLL2_4X])); | ||
164 | |||
165 | /* PLL2-8x */ | ||
166 | of_property_read_string_index(node, "clock-output-names", | ||
167 | SUN4I_A10_PLL2_8X, &clk_name); | ||
168 | clks[SUN4I_A10_PLL2_8X] = clk_register_fixed_factor(NULL, clk_name, | ||
169 | parent, | ||
170 | CLK_SET_RATE_PARENT, | ||
171 | 2, 1); | ||
172 | WARN_ON(IS_ERR(clks[SUN4I_A10_PLL2_8X])); | ||
173 | |||
174 | clk_data->clks = clks; | ||
175 | clk_data->clk_num = SUN4I_PLL2_OUTPUTS; | ||
176 | of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); | ||
177 | |||
178 | return; | ||
179 | |||
180 | err_free_multiplier: | ||
181 | kfree(mult); | ||
182 | err_free_gate: | ||
183 | kfree(gate); | ||
184 | err_unregister_prediv: | ||
185 | clk_unregister_divider(prediv_clk); | ||
186 | err_free_array: | ||
187 | kfree(clks); | ||
188 | err_free_data: | ||
189 | kfree(clk_data); | ||
190 | err_unmap: | ||
191 | iounmap(reg); | ||
192 | } | ||
193 | |||
194 | static struct sun4i_pll2_data sun4i_a10_pll2_data = { | ||
195 | .pre_div_flags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, | ||
196 | }; | ||
197 | |||
198 | static void __init sun4i_a10_pll2_setup(struct device_node *node) | ||
199 | { | ||
200 | sun4i_pll2_setup(node, &sun4i_a10_pll2_data); | ||
201 | } | ||
202 | |||
203 | CLK_OF_DECLARE(sun4i_a10_pll2, "allwinner,sun4i-a10-pll2-clk", | ||
204 | sun4i_a10_pll2_setup); | ||
205 | |||
206 | static struct sun4i_pll2_data sun5i_a13_pll2_data = { | ||
207 | .post_div_offset = 1, | ||
208 | }; | ||
209 | |||
210 | static void __init sun5i_a13_pll2_setup(struct device_node *node) | ||
211 | { | ||
212 | sun4i_pll2_setup(node, &sun5i_a13_pll2_data); | ||
213 | } | ||
214 | |||
215 | CLK_OF_DECLARE(sun5i_a13_pll2, "allwinner,sun5i-a13-pll2-clk", | ||
216 | sun5i_a13_pll2_setup); | ||
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", | |||
128 | sunxi_simple_gates_init); | 128 | sunxi_simple_gates_init); |
129 | CLK_OF_DECLARE(sun8i_a23_apb2, "allwinner,sun8i-a23-apb2-gates-clk", | 129 | CLK_OF_DECLARE(sun8i_a23_apb2, "allwinner,sun8i-a23-apb2-gates-clk", |
130 | sunxi_simple_gates_init); | 130 | sunxi_simple_gates_init); |
131 | CLK_OF_DECLARE(sun8i_a33_ahb1, "allwinner,sun8i-a33-ahb1-gates-clk", | ||
132 | sunxi_simple_gates_init); | ||
131 | CLK_OF_DECLARE(sun9i_a80_ahb0, "allwinner,sun9i-a80-ahb0-gates-clk", | 133 | CLK_OF_DECLARE(sun9i_a80_ahb0, "allwinner,sun9i-a80-ahb0-gates-clk", |
132 | sunxi_simple_gates_init); | 134 | sunxi_simple_gates_init); |
133 | CLK_OF_DECLARE(sun9i_a80_ahb1, "allwinner,sun9i-a80-ahb1-gates-clk", | 135 | CLK_OF_DECLARE(sun9i_a80_ahb1, "allwinner,sun9i-a80-ahb1-gates-clk", |
diff --git a/include/dt-bindings/clock/sun4i-a10-pll2.h b/include/dt-bindings/clock/sun4i-a10-pll2.h new file mode 100644 index 000000000000..071c8112d531 --- /dev/null +++ b/include/dt-bindings/clock/sun4i-a10-pll2.h | |||
@@ -0,0 +1,53 @@ | |||
1 | /* | ||
2 | * Copyright 2015 Maxime Ripard | ||
3 | * | ||
4 | * Maxime Ripard <maxime.ripard@free-electrons.com> | ||
5 | * | ||
6 | * This file is dual-licensed: you can use it either under the terms | ||
7 | * of the GPL or the X11 license, at your option. Note that this dual | ||
8 | * licensing only applies to this file, and not this project as a | ||
9 | * whole. | ||
10 | * | ||
11 | * a) This file is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public License as | ||
13 | * published by the Free Software Foundation; either version 2 of the | ||
14 | * License, or (at your option) any later version. | ||
15 | * | ||
16 | * This file is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * Or, alternatively, | ||
22 | * | ||
23 | * b) Permission is hereby granted, free of charge, to any person | ||
24 | * obtaining a copy of this software and associated documentation | ||
25 | * files (the "Software"), to deal in the Software without | ||
26 | * restriction, including without limitation the rights to use, | ||
27 | * copy, modify, merge, publish, distribute, sublicense, and/or | ||
28 | * sell copies of the Software, and to permit persons to whom the | ||
29 | * Software is furnished to do so, subject to the following | ||
30 | * conditions: | ||
31 | * | ||
32 | * The above copyright notice and this permission notice shall be | ||
33 | * included in all copies or substantial portions of the Software. | ||
34 | * | ||
35 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
36 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | ||
37 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
38 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | ||
39 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||
40 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
41 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
42 | * OTHER DEALINGS IN THE SOFTWARE. | ||
43 | */ | ||
44 | |||
45 | #ifndef __DT_BINDINGS_CLOCK_SUN4I_A10_PLL2_H_ | ||
46 | #define __DT_BINDINGS_CLOCK_SUN4I_A10_PLL2_H_ | ||
47 | |||
48 | #define SUN4I_A10_PLL2_1X 0 | ||
49 | #define SUN4I_A10_PLL2_2X 1 | ||
50 | #define SUN4I_A10_PLL2_4X 2 | ||
51 | #define SUN4I_A10_PLL2_8X 3 | ||
52 | |||
53 | #endif /* __DT_BINDINGS_CLOCK_SUN4I_A10_PLL2_H_ */ | ||
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 3ecc07d0da77..6a7dfe33a317 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h | |||
@@ -518,6 +518,48 @@ struct clk *clk_register_fractional_divider(struct device *dev, | |||
518 | void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth, | 518 | void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth, |
519 | u8 clk_divider_flags, spinlock_t *lock); | 519 | u8 clk_divider_flags, spinlock_t *lock); |
520 | 520 | ||
521 | /** | ||
522 | * struct clk_multiplier - adjustable multiplier clock | ||
523 | * | ||
524 | * @hw: handle between common and hardware-specific interfaces | ||
525 | * @reg: register containing the multiplier | ||
526 | * @shift: shift to the multiplier bit field | ||
527 | * @width: width of the multiplier bit field | ||
528 | * @lock: register lock | ||
529 | * | ||
530 | * Clock with an adjustable multiplier affecting its output frequency. | ||
531 | * Implements .recalc_rate, .set_rate and .round_rate | ||
532 | * | ||
533 | * Flags: | ||
534 | * CLK_MULTIPLIER_ZERO_BYPASS - By default, the multiplier is the value read | ||
535 | * from the register, with 0 being a valid value effectively | ||
536 | * zeroing the output clock rate. If CLK_MULTIPLIER_ZERO_BYPASS is | ||
537 | * set, then a null multiplier will be considered as a bypass, | ||
538 | * leaving the parent rate unmodified. | ||
539 | * CLK_MULTIPLIER_ROUND_CLOSEST - Makes the best calculated divider to be | ||
540 | * rounded to the closest integer instead of the down one. | ||
541 | */ | ||
542 | struct clk_multiplier { | ||
543 | struct clk_hw hw; | ||
544 | void __iomem *reg; | ||
545 | u8 shift; | ||
546 | u8 width; | ||
547 | u8 flags; | ||
548 | spinlock_t *lock; | ||
549 | }; | ||
550 | |||
551 | #define CLK_MULTIPLIER_ZERO_BYPASS BIT(0) | ||
552 | #define CLK_MULTIPLIER_ROUND_CLOSEST BIT(1) | ||
553 | |||
554 | extern const struct clk_ops clk_multiplier_ops; | ||
555 | |||
556 | struct clk *clk_register_multiplier(struct device *dev, const char *name, | ||
557 | const char *parent_name, | ||
558 | unsigned long flags, | ||
559 | void __iomem *reg, u8 shift, u8 width, | ||
560 | u8 clk_mult_flags, spinlock_t *lock); | ||
561 | void clk_unregister_multiplier(struct clk *clk); | ||
562 | |||
521 | /*** | 563 | /*** |
522 | * struct clk_composite - aggregate clock of mux, divider and gate clocks | 564 | * struct clk_composite - aggregate clock of mux, divider and gate clocks |
523 | * | 565 | * |