diff options
author | Michael Turquette <mturquette@baylibre.com> | 2016-07-08 21:08:56 -0400 |
---|---|---|
committer | Michael Turquette <mturquette@baylibre.com> | 2016-07-08 21:08:56 -0400 |
commit | 7adb76956189d10fd77f00b41b4d413480e94715 (patch) | |
tree | c15068fb36576a29846788bea9d15a327f970fd1 | |
parent | a06498297d233fd5074da14f25e72c2f104561d3 (diff) | |
parent | 0577e4853bfb4c65f620fa56d3157692df7f766e (diff) |
Merge branch 'clk-sunxi-ng' into clk-next
34 files changed, 3669 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/clock/sunxi-ccu.txt b/Documentation/devicetree/bindings/clock/sunxi-ccu.txt new file mode 100644 index 000000000000..cb91507ffb1e --- /dev/null +++ b/Documentation/devicetree/bindings/clock/sunxi-ccu.txt | |||
@@ -0,0 +1,24 @@ | |||
1 | Allwinner Clock Control Unit Binding | ||
2 | ------------------------------------ | ||
3 | |||
4 | Required properties : | ||
5 | - compatible: must contain one of the following compatible: | ||
6 | - "allwinner,sun8i-h3-ccu" | ||
7 | |||
8 | - reg: Must contain the registers base address and length | ||
9 | - clocks: phandle to the oscillators feeding the CCU. Two are needed: | ||
10 | - "hosc": the high frequency oscillator (usually at 24MHz) | ||
11 | - "losc": the low frequency oscillator (usually at 32kHz) | ||
12 | - clock-names: Must contain the clock names described just above | ||
13 | - #clock-cells : must contain 1 | ||
14 | - #reset-cells : must contain 1 | ||
15 | |||
16 | Example: | ||
17 | ccu: clock@01c20000 { | ||
18 | compatible = "allwinner,sun8i-h3-ccu"; | ||
19 | reg = <0x01c20000 0x400>; | ||
20 | clocks = <&osc24M>, <&osc32k>; | ||
21 | clock-names = "hosc", "losc"; | ||
22 | #clock-cells = <1>; | ||
23 | #reset-cells = <1>; | ||
24 | }; | ||
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 7cef0503429a..4d84be999f55 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig | |||
@@ -214,6 +214,7 @@ source "drivers/clk/mvebu/Kconfig" | |||
214 | source "drivers/clk/qcom/Kconfig" | 214 | source "drivers/clk/qcom/Kconfig" |
215 | source "drivers/clk/renesas/Kconfig" | 215 | source "drivers/clk/renesas/Kconfig" |
216 | source "drivers/clk/samsung/Kconfig" | 216 | source "drivers/clk/samsung/Kconfig" |
217 | source "drivers/clk/sunxi-ng/Kconfig" | ||
217 | source "drivers/clk/tegra/Kconfig" | 218 | source "drivers/clk/tegra/Kconfig" |
218 | source "drivers/clk/ti/Kconfig" | 219 | source "drivers/clk/ti/Kconfig" |
219 | 220 | ||
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index af03eb2f6c05..afbf1ad64cc2 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile | |||
@@ -79,6 +79,7 @@ obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/ | |||
79 | obj-$(CONFIG_PLAT_SPEAR) += spear/ | 79 | obj-$(CONFIG_PLAT_SPEAR) += spear/ |
80 | obj-$(CONFIG_ARCH_STI) += st/ | 80 | obj-$(CONFIG_ARCH_STI) += st/ |
81 | obj-$(CONFIG_ARCH_SUNXI) += sunxi/ | 81 | obj-$(CONFIG_ARCH_SUNXI) += sunxi/ |
82 | obj-$(CONFIG_ARCH_SUNXI) += sunxi-ng/ | ||
82 | obj-$(CONFIG_ARCH_TEGRA) += tegra/ | 83 | obj-$(CONFIG_ARCH_TEGRA) += tegra/ |
83 | obj-y += ti/ | 84 | obj-y += ti/ |
84 | obj-$(CONFIG_ARCH_U8500) += ux500/ | 85 | obj-$(CONFIG_ARCH_U8500) += ux500/ |
diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig new file mode 100644 index 000000000000..41e53e8d4d41 --- /dev/null +++ b/drivers/clk/sunxi-ng/Kconfig | |||
@@ -0,0 +1,65 @@ | |||
1 | config SUNXI_CCU | ||
2 | bool "Clock support for Allwinner SoCs" | ||
3 | default ARCH_SUNXI | ||
4 | |||
5 | if SUNXI_CCU | ||
6 | |||
7 | # Base clock types | ||
8 | |||
9 | config SUNXI_CCU_DIV | ||
10 | bool | ||
11 | select SUNXI_CCU_MUX | ||
12 | |||
13 | config SUNXI_CCU_FRAC | ||
14 | bool | ||
15 | |||
16 | config SUNXI_CCU_GATE | ||
17 | bool | ||
18 | |||
19 | config SUNXI_CCU_MUX | ||
20 | bool | ||
21 | |||
22 | config SUNXI_CCU_PHASE | ||
23 | bool | ||
24 | |||
25 | # Multi-factor clocks | ||
26 | |||
27 | config SUNXI_CCU_NK | ||
28 | bool | ||
29 | select SUNXI_CCU_GATE | ||
30 | |||
31 | config SUNXI_CCU_NKM | ||
32 | bool | ||
33 | select RATIONAL | ||
34 | select SUNXI_CCU_GATE | ||
35 | |||
36 | config SUNXI_CCU_NKMP | ||
37 | bool | ||
38 | select RATIONAL | ||
39 | select SUNXI_CCU_GATE | ||
40 | |||
41 | config SUNXI_CCU_NM | ||
42 | bool | ||
43 | select RATIONAL | ||
44 | select SUNXI_CCU_FRAC | ||
45 | select SUNXI_CCU_GATE | ||
46 | |||
47 | config SUNXI_CCU_MP | ||
48 | bool | ||
49 | select SUNXI_CCU_GATE | ||
50 | select SUNXI_CCU_MUX | ||
51 | |||
52 | # SoC Drivers | ||
53 | |||
54 | config SUN8I_H3_CCU | ||
55 | bool "Support for the Allwinner H3 CCU" | ||
56 | select SUNXI_CCU_DIV | ||
57 | select SUNXI_CCU_NK | ||
58 | select SUNXI_CCU_NKM | ||
59 | select SUNXI_CCU_NKMP | ||
60 | select SUNXI_CCU_NM | ||
61 | select SUNXI_CCU_MP | ||
62 | select SUNXI_CCU_PHASE | ||
63 | default ARCH_SUN8I | ||
64 | |||
65 | endif | ||
diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile new file mode 100644 index 000000000000..633ce642ffae --- /dev/null +++ b/drivers/clk/sunxi-ng/Makefile | |||
@@ -0,0 +1,20 @@ | |||
1 | # Common objects | ||
2 | obj-$(CONFIG_SUNXI_CCU) += ccu_common.o | ||
3 | obj-$(CONFIG_SUNXI_CCU) += ccu_reset.o | ||
4 | |||
5 | # Base clock types | ||
6 | obj-$(CONFIG_SUNXI_CCU_DIV) += ccu_div.o | ||
7 | obj-$(CONFIG_SUNXI_CCU_FRAC) += ccu_frac.o | ||
8 | obj-$(CONFIG_SUNXI_CCU_GATE) += ccu_gate.o | ||
9 | obj-$(CONFIG_SUNXI_CCU_MUX) += ccu_mux.o | ||
10 | obj-$(CONFIG_SUNXI_CCU_PHASE) += ccu_phase.o | ||
11 | |||
12 | # Multi-factor clocks | ||
13 | obj-$(CONFIG_SUNXI_CCU_NK) += ccu_nk.o | ||
14 | obj-$(CONFIG_SUNXI_CCU_NKM) += ccu_nkm.o | ||
15 | obj-$(CONFIG_SUNXI_CCU_NKMP) += ccu_nkmp.o | ||
16 | obj-$(CONFIG_SUNXI_CCU_NM) += ccu_nm.o | ||
17 | obj-$(CONFIG_SUNXI_CCU_MP) += ccu_mp.o | ||
18 | |||
19 | # SoC support | ||
20 | obj-$(CONFIG_SUN8I_H3_CCU) += ccu-sun8i-h3.o | ||
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c new file mode 100644 index 000000000000..bcc0a95549d3 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c | |||
@@ -0,0 +1,826 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016 Maxime Ripard. All rights reserved. | ||
3 | * | ||
4 | * This software is licensed under the terms of the GNU General Public | ||
5 | * License version 2, as published by the Free Software Foundation, and | ||
6 | * may be copied, distributed, and modified under those terms. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #include <linux/clk-provider.h> | ||
15 | #include <linux/of_address.h> | ||
16 | |||
17 | #include "ccu_common.h" | ||
18 | #include "ccu_reset.h" | ||
19 | |||
20 | #include "ccu_div.h" | ||
21 | #include "ccu_gate.h" | ||
22 | #include "ccu_mp.h" | ||
23 | #include "ccu_mult.h" | ||
24 | #include "ccu_nk.h" | ||
25 | #include "ccu_nkm.h" | ||
26 | #include "ccu_nkmp.h" | ||
27 | #include "ccu_nm.h" | ||
28 | #include "ccu_phase.h" | ||
29 | |||
30 | #include "ccu-sun8i-h3.h" | ||
31 | |||
32 | static SUNXI_CCU_NKMP_WITH_GATE_LOCK(pll_cpux_clk, "pll-cpux", | ||
33 | "osc24M", 0x000, | ||
34 | 8, 5, /* N */ | ||
35 | 4, 2, /* K */ | ||
36 | 0, 2, /* M */ | ||
37 | 16, 2, /* P */ | ||
38 | BIT(31), /* gate */ | ||
39 | BIT(28), /* lock */ | ||
40 | 0); | ||
41 | |||
42 | /* | ||
43 | * The Audio PLL is supposed to have 4 outputs: 3 fixed factors from | ||
44 | * the base (2x, 4x and 8x), and one variable divider (the one true | ||
45 | * pll audio). | ||
46 | * | ||
47 | * We don't have any need for the variable divider for now, so we just | ||
48 | * hardcode it to match with the clock names | ||
49 | */ | ||
50 | #define SUN8I_H3_PLL_AUDIO_REG 0x008 | ||
51 | |||
52 | static SUNXI_CCU_NM_WITH_GATE_LOCK(pll_audio_base_clk, "pll-audio-base", | ||
53 | "osc24M", 0x008, | ||
54 | 8, 7, /* N */ | ||
55 | 0, 5, /* M */ | ||
56 | BIT(31), /* gate */ | ||
57 | BIT(28), /* lock */ | ||
58 | 0); | ||
59 | |||
60 | static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video_clk, "pll-video", | ||
61 | "osc24M", 0x0010, | ||
62 | 8, 7, /* N */ | ||
63 | 0, 4, /* M */ | ||
64 | BIT(24), /* frac enable */ | ||
65 | BIT(25), /* frac select */ | ||
66 | 270000000, /* frac rate 0 */ | ||
67 | 297000000, /* frac rate 1 */ | ||
68 | BIT(31), /* gate */ | ||
69 | BIT(28), /* lock */ | ||
70 | 0); | ||
71 | |||
72 | static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_ve_clk, "pll-ve", | ||
73 | "osc24M", 0x0018, | ||
74 | 8, 7, /* N */ | ||
75 | 0, 4, /* M */ | ||
76 | BIT(24), /* frac enable */ | ||
77 | BIT(25), /* frac select */ | ||
78 | 270000000, /* frac rate 0 */ | ||
79 | 297000000, /* frac rate 1 */ | ||
80 | BIT(31), /* gate */ | ||
81 | BIT(28), /* lock */ | ||
82 | 0); | ||
83 | |||
84 | static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_ddr_clk, "pll-ddr", | ||
85 | "osc24M", 0x020, | ||
86 | 8, 5, /* N */ | ||
87 | 4, 2, /* K */ | ||
88 | 0, 2, /* M */ | ||
89 | BIT(31), /* gate */ | ||
90 | BIT(28), /* lock */ | ||
91 | 0); | ||
92 | |||
93 | static SUNXI_CCU_NK_WITH_GATE_LOCK_POSTDIV(pll_periph0_clk, "pll-periph0", | ||
94 | "osc24M", 0x028, | ||
95 | 8, 5, /* N */ | ||
96 | 4, 2, /* K */ | ||
97 | BIT(31), /* gate */ | ||
98 | BIT(28), /* lock */ | ||
99 | 2, /* post-div */ | ||
100 | 0); | ||
101 | |||
102 | static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_gpu_clk, "pll-gpu", | ||
103 | "osc24M", 0x0038, | ||
104 | 8, 7, /* N */ | ||
105 | 0, 4, /* M */ | ||
106 | BIT(24), /* frac enable */ | ||
107 | BIT(25), /* frac select */ | ||
108 | 270000000, /* frac rate 0 */ | ||
109 | 297000000, /* frac rate 1 */ | ||
110 | BIT(31), /* gate */ | ||
111 | BIT(28), /* lock */ | ||
112 | 0); | ||
113 | |||
114 | static SUNXI_CCU_NK_WITH_GATE_LOCK_POSTDIV(pll_periph1_clk, "pll-periph1", | ||
115 | "osc24M", 0x044, | ||
116 | 8, 5, /* N */ | ||
117 | 4, 2, /* K */ | ||
118 | BIT(31), /* gate */ | ||
119 | BIT(28), /* lock */ | ||
120 | 2, /* post-div */ | ||
121 | 0); | ||
122 | |||
123 | static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_de_clk, "pll-de", | ||
124 | "osc24M", 0x0048, | ||
125 | 8, 7, /* N */ | ||
126 | 0, 4, /* M */ | ||
127 | BIT(24), /* frac enable */ | ||
128 | BIT(25), /* frac select */ | ||
129 | 270000000, /* frac rate 0 */ | ||
130 | 297000000, /* frac rate 1 */ | ||
131 | BIT(31), /* gate */ | ||
132 | BIT(28), /* lock */ | ||
133 | 0); | ||
134 | |||
135 | static const char * const cpux_parents[] = { "osc32k", "osc24M", | ||
136 | "pll-cpux" , "pll-cpux" }; | ||
137 | static SUNXI_CCU_MUX(cpux_clk, "cpux", cpux_parents, | ||
138 | 0x050, 16, 2, CLK_IS_CRITICAL); | ||
139 | |||
140 | static SUNXI_CCU_M(axi_clk, "axi", "cpux", 0x050, 0, 2, 0); | ||
141 | |||
142 | static const char * const ahb1_parents[] = { "osc32k", "osc24M", | ||
143 | "axi" , "pll-periph0" }; | ||
144 | static struct ccu_div ahb1_clk = { | ||
145 | .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO), | ||
146 | |||
147 | .mux = { | ||
148 | .shift = 12, | ||
149 | .width = 2, | ||
150 | |||
151 | .variable_prediv = { | ||
152 | .index = 3, | ||
153 | .shift = 6, | ||
154 | .width = 2, | ||
155 | }, | ||
156 | }, | ||
157 | |||
158 | .common = { | ||
159 | .reg = 0x054, | ||
160 | .features = CCU_FEATURE_VARIABLE_PREDIV, | ||
161 | .hw.init = CLK_HW_INIT_PARENTS("ahb1", | ||
162 | ahb1_parents, | ||
163 | &ccu_div_ops, | ||
164 | 0), | ||
165 | }, | ||
166 | }; | ||
167 | |||
168 | static struct clk_div_table apb1_div_table[] = { | ||
169 | { .val = 0, .div = 2 }, | ||
170 | { .val = 1, .div = 2 }, | ||
171 | { .val = 2, .div = 4 }, | ||
172 | { .val = 3, .div = 8 }, | ||
173 | { /* Sentinel */ }, | ||
174 | }; | ||
175 | static SUNXI_CCU_DIV_TABLE(apb1_clk, "apb1", "ahb1", | ||
176 | 0x054, 8, 2, apb1_div_table, 0); | ||
177 | |||
178 | static const char * const apb2_parents[] = { "osc32k", "osc24M", | ||
179 | "pll-periph0" , "pll-periph0" }; | ||
180 | static SUNXI_CCU_MP_WITH_MUX(apb2_clk, "apb2", apb2_parents, 0x058, | ||
181 | 0, 5, /* M */ | ||
182 | 16, 2, /* P */ | ||
183 | 24, 2, /* mux */ | ||
184 | 0); | ||
185 | |||
186 | static const char * const ahb2_parents[] = { "ahb1" , "pll-periph0" }; | ||
187 | static struct ccu_mux ahb2_clk = { | ||
188 | .mux = { | ||
189 | .shift = 0, | ||
190 | .width = 1, | ||
191 | |||
192 | .fixed_prediv = { | ||
193 | .index = 1, | ||
194 | .div = 2, | ||
195 | }, | ||
196 | }, | ||
197 | |||
198 | .common = { | ||
199 | .reg = 0x05c, | ||
200 | .features = CCU_FEATURE_FIXED_PREDIV, | ||
201 | .hw.init = CLK_HW_INIT_PARENTS("ahb2", | ||
202 | ahb2_parents, | ||
203 | &ccu_mux_ops, | ||
204 | 0), | ||
205 | }, | ||
206 | }; | ||
207 | |||
208 | static SUNXI_CCU_GATE(bus_ce_clk, "bus-ce", "ahb1", | ||
209 | 0x060, BIT(5), 0); | ||
210 | static SUNXI_CCU_GATE(bus_dma_clk, "bus-dma", "ahb1", | ||
211 | 0x060, BIT(6), 0); | ||
212 | static SUNXI_CCU_GATE(bus_mmc0_clk, "bus-mmc0", "ahb1", | ||
213 | 0x060, BIT(8), 0); | ||
214 | static SUNXI_CCU_GATE(bus_mmc1_clk, "bus-mmc1", "ahb1", | ||
215 | 0x060, BIT(9), 0); | ||
216 | static SUNXI_CCU_GATE(bus_mmc2_clk, "bus-mmc2", "ahb1", | ||
217 | 0x060, BIT(10), 0); | ||
218 | static SUNXI_CCU_GATE(bus_nand_clk, "bus-nand", "ahb1", | ||
219 | 0x060, BIT(13), 0); | ||
220 | static SUNXI_CCU_GATE(bus_dram_clk, "bus-dram", "ahb1", | ||
221 | 0x060, BIT(14), 0); | ||
222 | static SUNXI_CCU_GATE(bus_emac_clk, "bus-emac", "ahb2", | ||
223 | 0x060, BIT(17), 0); | ||
224 | static SUNXI_CCU_GATE(bus_ts_clk, "bus-ts", "ahb1", | ||
225 | 0x060, BIT(18), 0); | ||
226 | static SUNXI_CCU_GATE(bus_hstimer_clk, "bus-hstimer", "ahb1", | ||
227 | 0x060, BIT(19), 0); | ||
228 | static SUNXI_CCU_GATE(bus_spi0_clk, "bus-spi0", "ahb1", | ||
229 | 0x060, BIT(20), 0); | ||
230 | static SUNXI_CCU_GATE(bus_spi1_clk, "bus-spi1", "ahb1", | ||
231 | 0x060, BIT(21), 0); | ||
232 | static SUNXI_CCU_GATE(bus_otg_clk, "bus-otg", "ahb1", | ||
233 | 0x060, BIT(23), 0); | ||
234 | static SUNXI_CCU_GATE(bus_ehci0_clk, "bus-ehci0", "ahb1", | ||
235 | 0x060, BIT(24), 0); | ||
236 | static SUNXI_CCU_GATE(bus_ehci1_clk, "bus-ehci1", "ahb2", | ||
237 | 0x060, BIT(25), 0); | ||
238 | static SUNXI_CCU_GATE(bus_ehci2_clk, "bus-ehci2", "ahb2", | ||
239 | 0x060, BIT(26), 0); | ||
240 | static SUNXI_CCU_GATE(bus_ehci3_clk, "bus-ehci3", "ahb2", | ||
241 | 0x060, BIT(27), 0); | ||
242 | static SUNXI_CCU_GATE(bus_ohci0_clk, "bus-ohci0", "ahb1", | ||
243 | 0x060, BIT(28), 0); | ||
244 | static SUNXI_CCU_GATE(bus_ohci1_clk, "bus-ohci1", "ahb2", | ||
245 | 0x060, BIT(29), 0); | ||
246 | static SUNXI_CCU_GATE(bus_ohci2_clk, "bus-ohci2", "ahb2", | ||
247 | 0x060, BIT(30), 0); | ||
248 | static SUNXI_CCU_GATE(bus_ohci3_clk, "bus-ohci3", "ahb2", | ||
249 | 0x060, BIT(31), 0); | ||
250 | |||
251 | static SUNXI_CCU_GATE(bus_ve_clk, "bus-ve", "ahb1", | ||
252 | 0x064, BIT(0), 0); | ||
253 | static SUNXI_CCU_GATE(bus_tcon0_clk, "bus-tcon0", "ahb1", | ||
254 | 0x064, BIT(3), 0); | ||
255 | static SUNXI_CCU_GATE(bus_tcon1_clk, "bus-tcon1", "ahb1", | ||
256 | 0x064, BIT(4), 0); | ||
257 | static SUNXI_CCU_GATE(bus_deinterlace_clk, "bus-deinterlace", "ahb1", | ||
258 | 0x064, BIT(5), 0); | ||
259 | static SUNXI_CCU_GATE(bus_csi_clk, "bus-csi", "ahb1", | ||
260 | 0x064, BIT(8), 0); | ||
261 | static SUNXI_CCU_GATE(bus_tve_clk, "bus-tve", "ahb1", | ||
262 | 0x064, BIT(9), 0); | ||
263 | static SUNXI_CCU_GATE(bus_hdmi_clk, "bus-hdmi", "ahb1", | ||
264 | 0x064, BIT(11), 0); | ||
265 | static SUNXI_CCU_GATE(bus_de_clk, "bus-de", "ahb1", | ||
266 | 0x064, BIT(12), 0); | ||
267 | static SUNXI_CCU_GATE(bus_gpu_clk, "bus-gpu", "ahb1", | ||
268 | 0x064, BIT(20), 0); | ||
269 | static SUNXI_CCU_GATE(bus_msgbox_clk, "bus-msgbox", "ahb1", | ||
270 | 0x064, BIT(21), 0); | ||
271 | static SUNXI_CCU_GATE(bus_spinlock_clk, "bus-spinlock", "ahb1", | ||
272 | 0x064, BIT(22), 0); | ||
273 | |||
274 | static SUNXI_CCU_GATE(bus_codec_clk, "bus-codec", "apb1", | ||
275 | 0x068, BIT(0), 0); | ||
276 | static SUNXI_CCU_GATE(bus_spdif_clk, "bus-spdif", "apb1", | ||
277 | 0x068, BIT(1), 0); | ||
278 | static SUNXI_CCU_GATE(bus_pio_clk, "bus-pio", "apb1", | ||
279 | 0x068, BIT(5), 0); | ||
280 | static SUNXI_CCU_GATE(bus_ths_clk, "bus-ths", "apb1", | ||
281 | 0x068, BIT(8), 0); | ||
282 | static SUNXI_CCU_GATE(bus_i2s0_clk, "bus-i2s0", "apb1", | ||
283 | 0x068, BIT(12), 0); | ||
284 | static SUNXI_CCU_GATE(bus_i2s1_clk, "bus-i2s1", "apb1", | ||
285 | 0x068, BIT(13), 0); | ||
286 | static SUNXI_CCU_GATE(bus_i2s2_clk, "bus-i2s2", "apb1", | ||
287 | 0x068, BIT(14), 0); | ||
288 | |||
289 | static SUNXI_CCU_GATE(bus_i2c0_clk, "bus-i2c0", "apb2", | ||
290 | 0x06c, BIT(0), 0); | ||
291 | static SUNXI_CCU_GATE(bus_i2c1_clk, "bus-i2c1", "apb2", | ||
292 | 0x06c, BIT(1), 0); | ||
293 | static SUNXI_CCU_GATE(bus_i2c2_clk, "bus-i2c2", "apb2", | ||
294 | 0x06c, BIT(2), 0); | ||
295 | static SUNXI_CCU_GATE(bus_uart0_clk, "bus-uart0", "apb2", | ||
296 | 0x06c, BIT(16), 0); | ||
297 | static SUNXI_CCU_GATE(bus_uart1_clk, "bus-uart1", "apb2", | ||
298 | 0x06c, BIT(17), 0); | ||
299 | static SUNXI_CCU_GATE(bus_uart2_clk, "bus-uart2", "apb2", | ||
300 | 0x06c, BIT(18), 0); | ||
301 | static SUNXI_CCU_GATE(bus_uart3_clk, "bus-uart3", "apb2", | ||
302 | 0x06c, BIT(19), 0); | ||
303 | static SUNXI_CCU_GATE(bus_scr_clk, "bus-scr", "apb2", | ||
304 | 0x06c, BIT(20), 0); | ||
305 | |||
306 | static SUNXI_CCU_GATE(bus_ephy_clk, "bus-ephy", "ahb1", | ||
307 | 0x070, BIT(0), 0); | ||
308 | static SUNXI_CCU_GATE(bus_dbg_clk, "bus-dbg", "ahb1", | ||
309 | 0x070, BIT(7), 0); | ||
310 | |||
311 | static struct clk_div_table ths_div_table[] = { | ||
312 | { .val = 0, .div = 1 }, | ||
313 | { .val = 1, .div = 2 }, | ||
314 | { .val = 2, .div = 4 }, | ||
315 | { .val = 3, .div = 6 }, | ||
316 | }; | ||
317 | static SUNXI_CCU_DIV_TABLE_WITH_GATE(ths_clk, "ths", "osc24M", | ||
318 | 0x074, 0, 2, ths_div_table, BIT(31), 0); | ||
319 | |||
320 | static const char * const mod0_default_parents[] = { "osc24M", "pll-periph0", | ||
321 | "pll-periph1" }; | ||
322 | static SUNXI_CCU_MP_WITH_MUX_GATE(nand_clk, "nand", mod0_default_parents, 0x080, | ||
323 | 0, 4, /* M */ | ||
324 | 16, 2, /* P */ | ||
325 | 24, 2, /* mux */ | ||
326 | BIT(31), /* gate */ | ||
327 | 0); | ||
328 | |||
329 | static SUNXI_CCU_MP_WITH_MUX_GATE(mmc0_clk, "mmc0", mod0_default_parents, 0x088, | ||
330 | 0, 4, /* M */ | ||
331 | 16, 2, /* P */ | ||
332 | 24, 2, /* mux */ | ||
333 | BIT(31), /* gate */ | ||
334 | 0); | ||
335 | |||
336 | static SUNXI_CCU_PHASE(mmc0_sample_clk, "mmc0_sample", "mmc0", | ||
337 | 0x088, 20, 3, 0); | ||
338 | static SUNXI_CCU_PHASE(mmc0_output_clk, "mmc0_output", "mmc0", | ||
339 | 0x088, 8, 3, 0); | ||
340 | |||
341 | static SUNXI_CCU_MP_WITH_MUX_GATE(mmc1_clk, "mmc1", mod0_default_parents, 0x08c, | ||
342 | 0, 4, /* M */ | ||
343 | 16, 2, /* P */ | ||
344 | 24, 2, /* mux */ | ||
345 | BIT(31), /* gate */ | ||
346 | 0); | ||
347 | |||
348 | static SUNXI_CCU_PHASE(mmc1_sample_clk, "mmc1_sample", "mmc1", | ||
349 | 0x08c, 20, 3, 0); | ||
350 | static SUNXI_CCU_PHASE(mmc1_output_clk, "mmc1_output", "mmc1", | ||
351 | 0x08c, 8, 3, 0); | ||
352 | |||
353 | static SUNXI_CCU_MP_WITH_MUX_GATE(mmc2_clk, "mmc2", mod0_default_parents, 0x090, | ||
354 | 0, 4, /* M */ | ||
355 | 16, 2, /* P */ | ||
356 | 24, 2, /* mux */ | ||
357 | BIT(31), /* gate */ | ||
358 | 0); | ||
359 | |||
360 | static SUNXI_CCU_PHASE(mmc2_sample_clk, "mmc2_sample", "mmc2", | ||
361 | 0x090, 20, 3, 0); | ||
362 | static SUNXI_CCU_PHASE(mmc2_output_clk, "mmc2_output", "mmc2", | ||
363 | 0x090, 8, 3, 0); | ||
364 | |||
365 | static const char * const ts_parents[] = { "osc24M", "pll-periph0", }; | ||
366 | static SUNXI_CCU_MP_WITH_MUX_GATE(ts_clk, "ts", ts_parents, 0x098, | ||
367 | 0, 4, /* M */ | ||
368 | 16, 2, /* P */ | ||
369 | 24, 2, /* mux */ | ||
370 | BIT(31), /* gate */ | ||
371 | 0); | ||
372 | |||
373 | static SUNXI_CCU_MP_WITH_MUX_GATE(ce_clk, "ce", mod0_default_parents, 0x09c, | ||
374 | 0, 4, /* M */ | ||
375 | 16, 2, /* P */ | ||
376 | 24, 2, /* mux */ | ||
377 | BIT(31), /* gate */ | ||
378 | 0); | ||
379 | |||
380 | static SUNXI_CCU_MP_WITH_MUX_GATE(spi0_clk, "spi0", mod0_default_parents, 0x0a0, | ||
381 | 0, 4, /* M */ | ||
382 | 16, 2, /* P */ | ||
383 | 24, 2, /* mux */ | ||
384 | BIT(31), /* gate */ | ||
385 | 0); | ||
386 | |||
387 | static SUNXI_CCU_MP_WITH_MUX_GATE(spi1_clk, "spi1", mod0_default_parents, 0x0a4, | ||
388 | 0, 4, /* M */ | ||
389 | 16, 2, /* P */ | ||
390 | 24, 2, /* mux */ | ||
391 | BIT(31), /* gate */ | ||
392 | 0); | ||
393 | |||
394 | static const char * const i2s_parents[] = { "pll-audio-8x", "pll-audio-4x", | ||
395 | "pll-audio-2x", "pll-audio" }; | ||
396 | static SUNXI_CCU_MUX_WITH_GATE(i2s0_clk, "i2s0", i2s_parents, | ||
397 | 0x0b0, 16, 2, BIT(31), 0); | ||
398 | |||
399 | static SUNXI_CCU_MUX_WITH_GATE(i2s1_clk, "i2s1", i2s_parents, | ||
400 | 0x0b4, 16, 2, BIT(31), 0); | ||
401 | |||
402 | static SUNXI_CCU_MUX_WITH_GATE(i2s2_clk, "i2s2", i2s_parents, | ||
403 | 0x0b8, 16, 2, BIT(31), 0); | ||
404 | |||
405 | static SUNXI_CCU_M_WITH_GATE(spdif_clk, "spdif", "pll-audio", | ||
406 | 0x0c0, 0, 4, BIT(31), 0); | ||
407 | |||
408 | static SUNXI_CCU_GATE(usb_phy0_clk, "usb-phy0", "osc24M", | ||
409 | 0x0cc, BIT(8), 0); | ||
410 | static SUNXI_CCU_GATE(usb_phy1_clk, "usb-phy1", "osc24M", | ||
411 | 0x0cc, BIT(9), 0); | ||
412 | static SUNXI_CCU_GATE(usb_phy2_clk, "usb-phy2", "osc24M", | ||
413 | 0x0cc, BIT(10), 0); | ||
414 | static SUNXI_CCU_GATE(usb_phy3_clk, "usb-phy3", "osc24M", | ||
415 | 0x0cc, BIT(11), 0); | ||
416 | static SUNXI_CCU_GATE(usb_ohci0_clk, "usb-ohci0", "osc24M", | ||
417 | 0x0cc, BIT(16), 0); | ||
418 | static SUNXI_CCU_GATE(usb_ohci1_clk, "usb-ohci1", "osc24M", | ||
419 | 0x0cc, BIT(17), 0); | ||
420 | static SUNXI_CCU_GATE(usb_ohci2_clk, "usb-ohci2", "osc24M", | ||
421 | 0x0cc, BIT(18), 0); | ||
422 | static SUNXI_CCU_GATE(usb_ohci3_clk, "usb-ohci3", "osc24M", | ||
423 | 0x0cc, BIT(19), 0); | ||
424 | |||
425 | static const char * const dram_parents[] = { "pll-ddr", "pll-periph0-2x" }; | ||
426 | static SUNXI_CCU_M_WITH_MUX(dram_clk, "dram", dram_parents, | ||
427 | 0x0f4, 0, 4, 20, 2, CLK_IS_CRITICAL); | ||
428 | |||
429 | static SUNXI_CCU_GATE(dram_ve_clk, "dram-ve", "dram", | ||
430 | 0x100, BIT(0), 0); | ||
431 | static SUNXI_CCU_GATE(dram_csi_clk, "dram-csi", "dram", | ||
432 | 0x100, BIT(1), 0); | ||
433 | static SUNXI_CCU_GATE(dram_deinterlace_clk, "dram-deinterlace", "dram", | ||
434 | 0x100, BIT(2), 0); | ||
435 | static SUNXI_CCU_GATE(dram_ts_clk, "dram-ts", "dram", | ||
436 | 0x100, BIT(3), 0); | ||
437 | |||
438 | static const char * const de_parents[] = { "pll-periph0-2x", "pll-de" }; | ||
439 | static SUNXI_CCU_M_WITH_MUX_GATE(de_clk, "de", de_parents, | ||
440 | 0x104, 0, 4, 24, 3, BIT(31), 0); | ||
441 | |||
442 | static const char * const tcon_parents[] = { "pll-video" }; | ||
443 | static SUNXI_CCU_M_WITH_MUX_GATE(tcon_clk, "tcon", tcon_parents, | ||
444 | 0x118, 0, 4, 24, 3, BIT(31), 0); | ||
445 | |||
446 | static const char * const tve_parents[] = { "pll-de", "pll-periph1" }; | ||
447 | static SUNXI_CCU_M_WITH_MUX_GATE(tve_clk, "tve", tve_parents, | ||
448 | 0x120, 0, 4, 24, 3, BIT(31), 0); | ||
449 | |||
450 | static const char * const deinterlace_parents[] = { "pll-periph0", "pll-periph1" }; | ||
451 | static SUNXI_CCU_M_WITH_MUX_GATE(deinterlace_clk, "deinterlace", deinterlace_parents, | ||
452 | 0x124, 0, 4, 24, 3, BIT(31), 0); | ||
453 | |||
454 | static SUNXI_CCU_GATE(csi_misc_clk, "csi-misc", "osc24M", | ||
455 | 0x130, BIT(31), 0); | ||
456 | |||
457 | static const char * const csi_sclk_parents[] = { "pll-periph0", "pll-periph1" }; | ||
458 | static SUNXI_CCU_M_WITH_MUX_GATE(csi_sclk_clk, "csi-sclk", csi_sclk_parents, | ||
459 | 0x134, 16, 4, 24, 3, BIT(31), 0); | ||
460 | |||
461 | static const char * const csi_mclk_parents[] = { "osc24M", "pll-video", "pll-periph0" }; | ||
462 | static SUNXI_CCU_M_WITH_MUX_GATE(csi_mclk_clk, "csi-mclk", csi_mclk_parents, | ||
463 | 0x134, 0, 5, 8, 3, BIT(15), 0); | ||
464 | |||
465 | static SUNXI_CCU_M_WITH_GATE(ve_clk, "ve", "pll-ve", | ||
466 | 0x13c, 16, 3, BIT(31), 0); | ||
467 | |||
468 | static SUNXI_CCU_GATE(ac_dig_clk, "ac-dig", "pll-audio", | ||
469 | 0x140, BIT(31), 0); | ||
470 | static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M", | ||
471 | 0x144, BIT(31), 0); | ||
472 | |||
473 | static const char * const hdmi_parents[] = { "pll-video" }; | ||
474 | static SUNXI_CCU_M_WITH_MUX_GATE(hdmi_clk, "hdmi", hdmi_parents, | ||
475 | 0x150, 0, 4, 24, 2, BIT(31), 0); | ||
476 | |||
477 | static SUNXI_CCU_GATE(hdmi_ddc_clk, "hdmi-ddc", "osc24M", | ||
478 | 0x154, BIT(31), 0); | ||
479 | |||
480 | static const char * const mbus_parents[] = { "osc24M", "pll-periph0-2x", "pll-ddr" }; | ||
481 | static SUNXI_CCU_M_WITH_MUX_GATE(mbus_clk, "mbus", mbus_parents, | ||
482 | 0x15c, 0, 3, 24, 2, BIT(31), CLK_IS_CRITICAL); | ||
483 | |||
484 | static SUNXI_CCU_M_WITH_GATE(gpu_clk, "gpu", "pll-gpu", | ||
485 | 0x1a0, 0, 3, BIT(31), 0); | ||
486 | |||
487 | static struct ccu_common *sun8i_h3_ccu_clks[] = { | ||
488 | &pll_cpux_clk.common, | ||
489 | &pll_audio_base_clk.common, | ||
490 | &pll_video_clk.common, | ||
491 | &pll_ve_clk.common, | ||
492 | &pll_ddr_clk.common, | ||
493 | &pll_periph0_clk.common, | ||
494 | &pll_gpu_clk.common, | ||
495 | &pll_periph1_clk.common, | ||
496 | &pll_de_clk.common, | ||
497 | &cpux_clk.common, | ||
498 | &axi_clk.common, | ||
499 | &ahb1_clk.common, | ||
500 | &apb1_clk.common, | ||
501 | &apb2_clk.common, | ||
502 | &ahb2_clk.common, | ||
503 | &bus_ce_clk.common, | ||
504 | &bus_dma_clk.common, | ||
505 | &bus_mmc0_clk.common, | ||
506 | &bus_mmc1_clk.common, | ||
507 | &bus_mmc2_clk.common, | ||
508 | &bus_nand_clk.common, | ||
509 | &bus_dram_clk.common, | ||
510 | &bus_emac_clk.common, | ||
511 | &bus_ts_clk.common, | ||
512 | &bus_hstimer_clk.common, | ||
513 | &bus_spi0_clk.common, | ||
514 | &bus_spi1_clk.common, | ||
515 | &bus_otg_clk.common, | ||
516 | &bus_ehci0_clk.common, | ||
517 | &bus_ehci1_clk.common, | ||
518 | &bus_ehci2_clk.common, | ||
519 | &bus_ehci3_clk.common, | ||
520 | &bus_ohci0_clk.common, | ||
521 | &bus_ohci1_clk.common, | ||
522 | &bus_ohci2_clk.common, | ||
523 | &bus_ohci3_clk.common, | ||
524 | &bus_ve_clk.common, | ||
525 | &bus_tcon0_clk.common, | ||
526 | &bus_tcon1_clk.common, | ||
527 | &bus_deinterlace_clk.common, | ||
528 | &bus_csi_clk.common, | ||
529 | &bus_tve_clk.common, | ||
530 | &bus_hdmi_clk.common, | ||
531 | &bus_de_clk.common, | ||
532 | &bus_gpu_clk.common, | ||
533 | &bus_msgbox_clk.common, | ||
534 | &bus_spinlock_clk.common, | ||
535 | &bus_codec_clk.common, | ||
536 | &bus_spdif_clk.common, | ||
537 | &bus_pio_clk.common, | ||
538 | &bus_ths_clk.common, | ||
539 | &bus_i2s0_clk.common, | ||
540 | &bus_i2s1_clk.common, | ||
541 | &bus_i2s2_clk.common, | ||
542 | &bus_i2c0_clk.common, | ||
543 | &bus_i2c1_clk.common, | ||
544 | &bus_i2c2_clk.common, | ||
545 | &bus_uart0_clk.common, | ||
546 | &bus_uart1_clk.common, | ||
547 | &bus_uart2_clk.common, | ||
548 | &bus_uart3_clk.common, | ||
549 | &bus_scr_clk.common, | ||
550 | &bus_ephy_clk.common, | ||
551 | &bus_dbg_clk.common, | ||
552 | &ths_clk.common, | ||
553 | &nand_clk.common, | ||
554 | &mmc0_clk.common, | ||
555 | &mmc0_sample_clk.common, | ||
556 | &mmc0_output_clk.common, | ||
557 | &mmc1_clk.common, | ||
558 | &mmc1_sample_clk.common, | ||
559 | &mmc1_output_clk.common, | ||
560 | &mmc2_clk.common, | ||
561 | &mmc2_sample_clk.common, | ||
562 | &mmc2_output_clk.common, | ||
563 | &ts_clk.common, | ||
564 | &ce_clk.common, | ||
565 | &spi0_clk.common, | ||
566 | &spi1_clk.common, | ||
567 | &i2s0_clk.common, | ||
568 | &i2s1_clk.common, | ||
569 | &i2s2_clk.common, | ||
570 | &spdif_clk.common, | ||
571 | &usb_phy0_clk.common, | ||
572 | &usb_phy1_clk.common, | ||
573 | &usb_phy2_clk.common, | ||
574 | &usb_phy3_clk.common, | ||
575 | &usb_ohci0_clk.common, | ||
576 | &usb_ohci1_clk.common, | ||
577 | &usb_ohci2_clk.common, | ||
578 | &usb_ohci3_clk.common, | ||
579 | &dram_clk.common, | ||
580 | &dram_ve_clk.common, | ||
581 | &dram_csi_clk.common, | ||
582 | &dram_deinterlace_clk.common, | ||
583 | &dram_ts_clk.common, | ||
584 | &de_clk.common, | ||
585 | &tcon_clk.common, | ||
586 | &tve_clk.common, | ||
587 | &deinterlace_clk.common, | ||
588 | &csi_misc_clk.common, | ||
589 | &csi_sclk_clk.common, | ||
590 | &csi_mclk_clk.common, | ||
591 | &ve_clk.common, | ||
592 | &ac_dig_clk.common, | ||
593 | &avs_clk.common, | ||
594 | &hdmi_clk.common, | ||
595 | &hdmi_ddc_clk.common, | ||
596 | &mbus_clk.common, | ||
597 | &gpu_clk.common, | ||
598 | }; | ||
599 | |||
600 | /* We hardcode the divider to 4 for now */ | ||
601 | static CLK_FIXED_FACTOR(pll_audio_clk, "pll-audio", | ||
602 | "pll-audio-base", 4, 1, CLK_SET_RATE_PARENT); | ||
603 | static CLK_FIXED_FACTOR(pll_audio_2x_clk, "pll-audio-2x", | ||
604 | "pll-audio-base", 2, 1, CLK_SET_RATE_PARENT); | ||
605 | static CLK_FIXED_FACTOR(pll_audio_4x_clk, "pll-audio-4x", | ||
606 | "pll-audio-base", 1, 1, CLK_SET_RATE_PARENT); | ||
607 | static CLK_FIXED_FACTOR(pll_audio_8x_clk, "pll-audio-8x", | ||
608 | "pll-audio-base", 1, 2, CLK_SET_RATE_PARENT); | ||
609 | static CLK_FIXED_FACTOR(pll_periph0_2x_clk, "pll-periph0-2x", | ||
610 | "pll-periph0", 1, 2, 0); | ||
611 | |||
612 | static struct clk_hw_onecell_data sun8i_h3_hw_clks = { | ||
613 | .hws = { | ||
614 | [CLK_PLL_CPUX] = &pll_cpux_clk.common.hw, | ||
615 | [CLK_PLL_AUDIO_BASE] = &pll_audio_base_clk.common.hw, | ||
616 | [CLK_PLL_AUDIO] = &pll_audio_clk.hw, | ||
617 | [CLK_PLL_AUDIO_2X] = &pll_audio_2x_clk.hw, | ||
618 | [CLK_PLL_AUDIO_4X] = &pll_audio_4x_clk.hw, | ||
619 | [CLK_PLL_AUDIO_8X] = &pll_audio_8x_clk.hw, | ||
620 | [CLK_PLL_VIDEO] = &pll_video_clk.common.hw, | ||
621 | [CLK_PLL_VE] = &pll_ve_clk.common.hw, | ||
622 | [CLK_PLL_DDR] = &pll_ddr_clk.common.hw, | ||
623 | [CLK_PLL_PERIPH0] = &pll_periph0_clk.common.hw, | ||
624 | [CLK_PLL_PERIPH0_2X] = &pll_periph0_2x_clk.hw, | ||
625 | [CLK_PLL_GPU] = &pll_gpu_clk.common.hw, | ||
626 | [CLK_PLL_PERIPH1] = &pll_periph1_clk.common.hw, | ||
627 | [CLK_PLL_DE] = &pll_de_clk.common.hw, | ||
628 | [CLK_CPUX] = &cpux_clk.common.hw, | ||
629 | [CLK_AXI] = &axi_clk.common.hw, | ||
630 | [CLK_AHB1] = &ahb1_clk.common.hw, | ||
631 | [CLK_APB1] = &apb1_clk.common.hw, | ||
632 | [CLK_APB2] = &apb2_clk.common.hw, | ||
633 | [CLK_AHB2] = &ahb2_clk.common.hw, | ||
634 | [CLK_BUS_CE] = &bus_ce_clk.common.hw, | ||
635 | [CLK_BUS_DMA] = &bus_dma_clk.common.hw, | ||
636 | [CLK_BUS_MMC0] = &bus_mmc0_clk.common.hw, | ||
637 | [CLK_BUS_MMC1] = &bus_mmc1_clk.common.hw, | ||
638 | [CLK_BUS_MMC2] = &bus_mmc2_clk.common.hw, | ||
639 | [CLK_BUS_NAND] = &bus_nand_clk.common.hw, | ||
640 | [CLK_BUS_DRAM] = &bus_dram_clk.common.hw, | ||
641 | [CLK_BUS_EMAC] = &bus_emac_clk.common.hw, | ||
642 | [CLK_BUS_TS] = &bus_ts_clk.common.hw, | ||
643 | [CLK_BUS_HSTIMER] = &bus_hstimer_clk.common.hw, | ||
644 | [CLK_BUS_SPI0] = &bus_spi0_clk.common.hw, | ||
645 | [CLK_BUS_SPI1] = &bus_spi1_clk.common.hw, | ||
646 | [CLK_BUS_OTG] = &bus_otg_clk.common.hw, | ||
647 | [CLK_BUS_EHCI0] = &bus_ehci0_clk.common.hw, | ||
648 | [CLK_BUS_EHCI1] = &bus_ehci1_clk.common.hw, | ||
649 | [CLK_BUS_EHCI2] = &bus_ehci2_clk.common.hw, | ||
650 | [CLK_BUS_EHCI3] = &bus_ehci3_clk.common.hw, | ||
651 | [CLK_BUS_OHCI0] = &bus_ohci0_clk.common.hw, | ||
652 | [CLK_BUS_OHCI1] = &bus_ohci1_clk.common.hw, | ||
653 | [CLK_BUS_OHCI2] = &bus_ohci2_clk.common.hw, | ||
654 | [CLK_BUS_OHCI3] = &bus_ohci3_clk.common.hw, | ||
655 | [CLK_BUS_VE] = &bus_ve_clk.common.hw, | ||
656 | [CLK_BUS_TCON0] = &bus_tcon0_clk.common.hw, | ||
657 | [CLK_BUS_TCON1] = &bus_tcon1_clk.common.hw, | ||
658 | [CLK_BUS_DEINTERLACE] = &bus_deinterlace_clk.common.hw, | ||
659 | [CLK_BUS_CSI] = &bus_csi_clk.common.hw, | ||
660 | [CLK_BUS_TVE] = &bus_tve_clk.common.hw, | ||
661 | [CLK_BUS_HDMI] = &bus_hdmi_clk.common.hw, | ||
662 | [CLK_BUS_DE] = &bus_de_clk.common.hw, | ||
663 | [CLK_BUS_GPU] = &bus_gpu_clk.common.hw, | ||
664 | [CLK_BUS_MSGBOX] = &bus_msgbox_clk.common.hw, | ||
665 | [CLK_BUS_SPINLOCK] = &bus_spinlock_clk.common.hw, | ||
666 | [CLK_BUS_CODEC] = &bus_codec_clk.common.hw, | ||
667 | [CLK_BUS_SPDIF] = &bus_spdif_clk.common.hw, | ||
668 | [CLK_BUS_PIO] = &bus_pio_clk.common.hw, | ||
669 | [CLK_BUS_THS] = &bus_ths_clk.common.hw, | ||
670 | [CLK_BUS_I2S0] = &bus_i2s0_clk.common.hw, | ||
671 | [CLK_BUS_I2S1] = &bus_i2s1_clk.common.hw, | ||
672 | [CLK_BUS_I2S2] = &bus_i2s2_clk.common.hw, | ||
673 | [CLK_BUS_I2C0] = &bus_i2c0_clk.common.hw, | ||
674 | [CLK_BUS_I2C1] = &bus_i2c1_clk.common.hw, | ||
675 | [CLK_BUS_I2C2] = &bus_i2c2_clk.common.hw, | ||
676 | [CLK_BUS_UART0] = &bus_uart0_clk.common.hw, | ||
677 | [CLK_BUS_UART1] = &bus_uart1_clk.common.hw, | ||
678 | [CLK_BUS_UART2] = &bus_uart2_clk.common.hw, | ||
679 | [CLK_BUS_UART3] = &bus_uart3_clk.common.hw, | ||
680 | [CLK_BUS_SCR] = &bus_scr_clk.common.hw, | ||
681 | [CLK_BUS_EPHY] = &bus_ephy_clk.common.hw, | ||
682 | [CLK_BUS_DBG] = &bus_dbg_clk.common.hw, | ||
683 | [CLK_THS] = &ths_clk.common.hw, | ||
684 | [CLK_NAND] = &nand_clk.common.hw, | ||
685 | [CLK_MMC0] = &mmc0_clk.common.hw, | ||
686 | [CLK_MMC0_SAMPLE] = &mmc0_sample_clk.common.hw, | ||
687 | [CLK_MMC0_OUTPUT] = &mmc0_output_clk.common.hw, | ||
688 | [CLK_MMC1] = &mmc1_clk.common.hw, | ||
689 | [CLK_MMC1_SAMPLE] = &mmc1_sample_clk.common.hw, | ||
690 | [CLK_MMC1_OUTPUT] = &mmc1_output_clk.common.hw, | ||
691 | [CLK_MMC2] = &mmc2_clk.common.hw, | ||
692 | [CLK_MMC2_SAMPLE] = &mmc2_sample_clk.common.hw, | ||
693 | [CLK_MMC2_OUTPUT] = &mmc2_output_clk.common.hw, | ||
694 | [CLK_TS] = &ts_clk.common.hw, | ||
695 | [CLK_CE] = &ce_clk.common.hw, | ||
696 | [CLK_SPI0] = &spi0_clk.common.hw, | ||
697 | [CLK_SPI1] = &spi1_clk.common.hw, | ||
698 | [CLK_I2S0] = &i2s0_clk.common.hw, | ||
699 | [CLK_I2S1] = &i2s1_clk.common.hw, | ||
700 | [CLK_I2S2] = &i2s2_clk.common.hw, | ||
701 | [CLK_SPDIF] = &spdif_clk.common.hw, | ||
702 | [CLK_USB_PHY0] = &usb_phy0_clk.common.hw, | ||
703 | [CLK_USB_PHY1] = &usb_phy1_clk.common.hw, | ||
704 | [CLK_USB_PHY2] = &usb_phy2_clk.common.hw, | ||
705 | [CLK_USB_PHY3] = &usb_phy3_clk.common.hw, | ||
706 | [CLK_USB_OHCI0] = &usb_ohci0_clk.common.hw, | ||
707 | [CLK_USB_OHCI1] = &usb_ohci1_clk.common.hw, | ||
708 | [CLK_USB_OHCI2] = &usb_ohci2_clk.common.hw, | ||
709 | [CLK_USB_OHCI3] = &usb_ohci3_clk.common.hw, | ||
710 | [CLK_DRAM] = &dram_clk.common.hw, | ||
711 | [CLK_DRAM_VE] = &dram_ve_clk.common.hw, | ||
712 | [CLK_DRAM_CSI] = &dram_csi_clk.common.hw, | ||
713 | [CLK_DRAM_DEINTERLACE] = &dram_deinterlace_clk.common.hw, | ||
714 | [CLK_DRAM_TS] = &dram_ts_clk.common.hw, | ||
715 | [CLK_DE] = &de_clk.common.hw, | ||
716 | [CLK_TCON0] = &tcon_clk.common.hw, | ||
717 | [CLK_TVE] = &tve_clk.common.hw, | ||
718 | [CLK_DEINTERLACE] = &deinterlace_clk.common.hw, | ||
719 | [CLK_CSI_MISC] = &csi_misc_clk.common.hw, | ||
720 | [CLK_CSI_SCLK] = &csi_sclk_clk.common.hw, | ||
721 | [CLK_CSI_MCLK] = &csi_mclk_clk.common.hw, | ||
722 | [CLK_VE] = &ve_clk.common.hw, | ||
723 | [CLK_AC_DIG] = &ac_dig_clk.common.hw, | ||
724 | [CLK_AVS] = &avs_clk.common.hw, | ||
725 | [CLK_HDMI] = &hdmi_clk.common.hw, | ||
726 | [CLK_HDMI_DDC] = &hdmi_ddc_clk.common.hw, | ||
727 | [CLK_MBUS] = &mbus_clk.common.hw, | ||
728 | [CLK_GPU] = &gpu_clk.common.hw, | ||
729 | }, | ||
730 | .num = CLK_NUMBER, | ||
731 | }; | ||
732 | |||
733 | static struct ccu_reset_map sun8i_h3_ccu_resets[] = { | ||
734 | [RST_USB_PHY0] = { 0x0cc, BIT(0) }, | ||
735 | [RST_USB_PHY1] = { 0x0cc, BIT(1) }, | ||
736 | [RST_USB_PHY2] = { 0x0cc, BIT(2) }, | ||
737 | [RST_USB_PHY3] = { 0x0cc, BIT(3) }, | ||
738 | |||
739 | [RST_MBUS] = { 0x0fc, BIT(31) }, | ||
740 | |||
741 | [RST_BUS_CE] = { 0x2c0, BIT(5) }, | ||
742 | [RST_BUS_DMA] = { 0x2c0, BIT(6) }, | ||
743 | [RST_BUS_MMC0] = { 0x2c0, BIT(8) }, | ||
744 | [RST_BUS_MMC1] = { 0x2c0, BIT(9) }, | ||
745 | [RST_BUS_MMC2] = { 0x2c0, BIT(10) }, | ||
746 | [RST_BUS_NAND] = { 0x2c0, BIT(13) }, | ||
747 | [RST_BUS_DRAM] = { 0x2c0, BIT(14) }, | ||
748 | [RST_BUS_EMAC] = { 0x2c0, BIT(17) }, | ||
749 | [RST_BUS_TS] = { 0x2c0, BIT(18) }, | ||
750 | [RST_BUS_HSTIMER] = { 0x2c0, BIT(19) }, | ||
751 | [RST_BUS_SPI0] = { 0x2c0, BIT(20) }, | ||
752 | [RST_BUS_SPI1] = { 0x2c0, BIT(21) }, | ||
753 | [RST_BUS_OTG] = { 0x2c0, BIT(23) }, | ||
754 | [RST_BUS_EHCI0] = { 0x2c0, BIT(24) }, | ||
755 | [RST_BUS_EHCI1] = { 0x2c0, BIT(25) }, | ||
756 | [RST_BUS_EHCI2] = { 0x2c0, BIT(26) }, | ||
757 | [RST_BUS_EHCI3] = { 0x2c0, BIT(27) }, | ||
758 | [RST_BUS_OHCI0] = { 0x2c0, BIT(28) }, | ||
759 | [RST_BUS_OHCI1] = { 0x2c0, BIT(29) }, | ||
760 | [RST_BUS_OHCI2] = { 0x2c0, BIT(30) }, | ||
761 | [RST_BUS_OHCI3] = { 0x2c0, BIT(31) }, | ||
762 | |||
763 | [RST_BUS_VE] = { 0x2c4, BIT(0) }, | ||
764 | [RST_BUS_TCON0] = { 0x2c4, BIT(3) }, | ||
765 | [RST_BUS_TCON1] = { 0x2c4, BIT(4) }, | ||
766 | [RST_BUS_DEINTERLACE] = { 0x2c4, BIT(5) }, | ||
767 | [RST_BUS_CSI] = { 0x2c4, BIT(8) }, | ||
768 | [RST_BUS_TVE] = { 0x2c4, BIT(9) }, | ||
769 | [RST_BUS_HDMI0] = { 0x2c4, BIT(10) }, | ||
770 | [RST_BUS_HDMI1] = { 0x2c4, BIT(11) }, | ||
771 | [RST_BUS_DE] = { 0x2c4, BIT(12) }, | ||
772 | [RST_BUS_GPU] = { 0x2c4, BIT(20) }, | ||
773 | [RST_BUS_MSGBOX] = { 0x2c4, BIT(21) }, | ||
774 | [RST_BUS_SPINLOCK] = { 0x2c4, BIT(22) }, | ||
775 | [RST_BUS_DBG] = { 0x2c4, BIT(31) }, | ||
776 | |||
777 | [RST_BUS_EPHY] = { 0x2c8, BIT(2) }, | ||
778 | |||
779 | [RST_BUS_CODEC] = { 0x2d0, BIT(0) }, | ||
780 | [RST_BUS_SPDIF] = { 0x2d0, BIT(1) }, | ||
781 | [RST_BUS_THS] = { 0x2d0, BIT(8) }, | ||
782 | [RST_BUS_I2S0] = { 0x2d0, BIT(12) }, | ||
783 | [RST_BUS_I2S1] = { 0x2d0, BIT(13) }, | ||
784 | [RST_BUS_I2S2] = { 0x2d0, BIT(14) }, | ||
785 | |||
786 | [RST_BUS_I2C0] = { 0x2d4, BIT(0) }, | ||
787 | [RST_BUS_I2C1] = { 0x2d4, BIT(1) }, | ||
788 | [RST_BUS_I2C2] = { 0x2d4, BIT(2) }, | ||
789 | [RST_BUS_UART0] = { 0x2d4, BIT(16) }, | ||
790 | [RST_BUS_UART1] = { 0x2d4, BIT(17) }, | ||
791 | [RST_BUS_UART2] = { 0x2d4, BIT(18) }, | ||
792 | [RST_BUS_UART3] = { 0x2d4, BIT(19) }, | ||
793 | [RST_BUS_SCR] = { 0x2d4, BIT(20) }, | ||
794 | }; | ||
795 | |||
796 | static const struct sunxi_ccu_desc sun8i_h3_ccu_desc = { | ||
797 | .ccu_clks = sun8i_h3_ccu_clks, | ||
798 | .num_ccu_clks = ARRAY_SIZE(sun8i_h3_ccu_clks), | ||
799 | |||
800 | .hw_clks = &sun8i_h3_hw_clks, | ||
801 | |||
802 | .resets = sun8i_h3_ccu_resets, | ||
803 | .num_resets = ARRAY_SIZE(sun8i_h3_ccu_resets), | ||
804 | }; | ||
805 | |||
806 | static void __init sun8i_h3_ccu_setup(struct device_node *node) | ||
807 | { | ||
808 | void __iomem *reg; | ||
809 | u32 val; | ||
810 | |||
811 | reg = of_io_request_and_map(node, 0, of_node_full_name(node)); | ||
812 | if (IS_ERR(reg)) { | ||
813 | pr_err("%s: Could not map the clock registers\n", | ||
814 | of_node_full_name(node)); | ||
815 | return; | ||
816 | } | ||
817 | |||
818 | /* Force the PLL-Audio-1x divider to 4 */ | ||
819 | val = readl(reg + SUN8I_H3_PLL_AUDIO_REG); | ||
820 | val &= ~GENMASK(4, 0); | ||
821 | writel(val | 3, reg + SUN8I_H3_PLL_AUDIO_REG); | ||
822 | |||
823 | sunxi_ccu_probe(node, reg, &sun8i_h3_ccu_desc); | ||
824 | } | ||
825 | CLK_OF_DECLARE(sun8i_h3_ccu, "allwinner,sun8i-h3-ccu", | ||
826 | sun8i_h3_ccu_setup); | ||
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.h b/drivers/clk/sunxi-ng/ccu-sun8i-h3.h new file mode 100644 index 000000000000..78be712c7487 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.h | |||
@@ -0,0 +1,62 @@ | |||
1 | /* | ||
2 | * Copyright 2016 Maxime Ripard | ||
3 | * | ||
4 | * Maxime Ripard <maxime.ripard@free-electrons.com> | ||
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 | #ifndef _CCU_SUN8I_H3_H_ | ||
18 | #define _CCU_SUN8I_H3_H_ | ||
19 | |||
20 | #include <dt-bindings/clock/sun8i-h3-ccu.h> | ||
21 | #include <dt-bindings/reset/sun8i-h3-ccu.h> | ||
22 | |||
23 | #define CLK_PLL_CPUX 0 | ||
24 | #define CLK_PLL_AUDIO_BASE 1 | ||
25 | #define CLK_PLL_AUDIO 2 | ||
26 | #define CLK_PLL_AUDIO_2X 3 | ||
27 | #define CLK_PLL_AUDIO_4X 4 | ||
28 | #define CLK_PLL_AUDIO_8X 5 | ||
29 | #define CLK_PLL_VIDEO 6 | ||
30 | #define CLK_PLL_VE 7 | ||
31 | #define CLK_PLL_DDR 8 | ||
32 | #define CLK_PLL_PERIPH0 9 | ||
33 | #define CLK_PLL_PERIPH0_2X 10 | ||
34 | #define CLK_PLL_GPU 11 | ||
35 | #define CLK_PLL_PERIPH1 12 | ||
36 | #define CLK_PLL_DE 13 | ||
37 | |||
38 | /* The CPUX clock is exported */ | ||
39 | |||
40 | #define CLK_AXI 15 | ||
41 | #define CLK_AHB1 16 | ||
42 | #define CLK_APB1 17 | ||
43 | #define CLK_APB2 18 | ||
44 | #define CLK_AHB2 19 | ||
45 | |||
46 | /* All the bus gates are exported */ | ||
47 | |||
48 | /* The first bunch of module clocks are exported */ | ||
49 | |||
50 | #define CLK_DRAM 96 | ||
51 | |||
52 | /* All the DRAM gates are exported */ | ||
53 | |||
54 | /* Some more module clocks are exported */ | ||
55 | |||
56 | #define CLK_MBUS 113 | ||
57 | |||
58 | /* And the GPU module clock is exported */ | ||
59 | |||
60 | #define CLK_NUMBER (CLK_GPU + 1) | ||
61 | |||
62 | #endif /* _CCU_SUN8I_H3_H_ */ | ||
diff --git a/drivers/clk/sunxi-ng/ccu_common.c b/drivers/clk/sunxi-ng/ccu_common.c new file mode 100644 index 000000000000..fc17b5295e16 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_common.c | |||
@@ -0,0 +1,90 @@ | |||
1 | /* | ||
2 | * Copyright 2016 Maxime Ripard | ||
3 | * | ||
4 | * Maxime Ripard <maxime.ripard@free-electrons.com> | ||
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/iopoll.h> | ||
19 | #include <linux/slab.h> | ||
20 | |||
21 | #include "ccu_common.h" | ||
22 | #include "ccu_reset.h" | ||
23 | |||
24 | static DEFINE_SPINLOCK(ccu_lock); | ||
25 | |||
26 | void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock) | ||
27 | { | ||
28 | u32 reg; | ||
29 | |||
30 | if (!lock) | ||
31 | return; | ||
32 | |||
33 | WARN_ON(readl_relaxed_poll_timeout(common->base + common->reg, reg, | ||
34 | !(reg & lock), 100, 70000)); | ||
35 | } | ||
36 | |||
37 | int sunxi_ccu_probe(struct device_node *node, void __iomem *reg, | ||
38 | const struct sunxi_ccu_desc *desc) | ||
39 | { | ||
40 | struct ccu_reset *reset; | ||
41 | int i, ret; | ||
42 | |||
43 | for (i = 0; i < desc->num_ccu_clks; i++) { | ||
44 | struct ccu_common *cclk = desc->ccu_clks[i]; | ||
45 | |||
46 | if (!cclk) | ||
47 | continue; | ||
48 | |||
49 | cclk->base = reg; | ||
50 | cclk->lock = &ccu_lock; | ||
51 | } | ||
52 | |||
53 | for (i = 0; i < desc->hw_clks->num ; i++) { | ||
54 | struct clk_hw *hw = desc->hw_clks->hws[i]; | ||
55 | |||
56 | if (!hw) | ||
57 | continue; | ||
58 | |||
59 | ret = clk_hw_register(NULL, hw); | ||
60 | if (ret) { | ||
61 | pr_err("Couldn't register clock %s\n", | ||
62 | clk_hw_get_name(hw)); | ||
63 | goto err_clk_unreg; | ||
64 | } | ||
65 | } | ||
66 | |||
67 | ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, | ||
68 | desc->hw_clks); | ||
69 | if (ret) | ||
70 | goto err_clk_unreg; | ||
71 | |||
72 | reset = kzalloc(sizeof(*reset), GFP_KERNEL); | ||
73 | reset->rcdev.of_node = node; | ||
74 | reset->rcdev.ops = &ccu_reset_ops; | ||
75 | reset->rcdev.owner = THIS_MODULE; | ||
76 | reset->rcdev.nr_resets = desc->num_resets; | ||
77 | reset->base = reg; | ||
78 | reset->lock = &ccu_lock; | ||
79 | reset->reset_map = desc->resets; | ||
80 | |||
81 | ret = reset_controller_register(&reset->rcdev); | ||
82 | if (ret) | ||
83 | goto err_of_clk_unreg; | ||
84 | |||
85 | return 0; | ||
86 | |||
87 | err_of_clk_unreg: | ||
88 | err_clk_unreg: | ||
89 | return ret; | ||
90 | } | ||
diff --git a/drivers/clk/sunxi-ng/ccu_common.h b/drivers/clk/sunxi-ng/ccu_common.h new file mode 100644 index 000000000000..b3d9abfbd721 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_common.h | |||
@@ -0,0 +1,85 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016 Maxime Ripard. All rights reserved. | ||
3 | * | ||
4 | * This software is licensed under the terms of the GNU General Public | ||
5 | * License version 2, as published by the Free Software Foundation, and | ||
6 | * may be copied, distributed, and modified under those terms. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #ifndef _COMMON_H_ | ||
15 | #define _COMMON_H_ | ||
16 | |||
17 | #include <linux/compiler.h> | ||
18 | #include <linux/clk-provider.h> | ||
19 | |||
20 | #define CCU_FEATURE_FRACTIONAL BIT(0) | ||
21 | #define CCU_FEATURE_VARIABLE_PREDIV BIT(1) | ||
22 | #define CCU_FEATURE_FIXED_PREDIV BIT(2) | ||
23 | #define CCU_FEATURE_FIXED_POSTDIV BIT(3) | ||
24 | |||
25 | struct device_node; | ||
26 | |||
27 | #define CLK_HW_INIT(_name, _parent, _ops, _flags) \ | ||
28 | &(struct clk_init_data) { \ | ||
29 | .flags = _flags, \ | ||
30 | .name = _name, \ | ||
31 | .parent_names = (const char *[]) { _parent }, \ | ||
32 | .num_parents = 1, \ | ||
33 | .ops = _ops, \ | ||
34 | } | ||
35 | |||
36 | #define CLK_HW_INIT_PARENTS(_name, _parents, _ops, _flags) \ | ||
37 | &(struct clk_init_data) { \ | ||
38 | .flags = _flags, \ | ||
39 | .name = _name, \ | ||
40 | .parent_names = _parents, \ | ||
41 | .num_parents = ARRAY_SIZE(_parents), \ | ||
42 | .ops = _ops, \ | ||
43 | } | ||
44 | |||
45 | #define CLK_FIXED_FACTOR(_struct, _name, _parent, \ | ||
46 | _div, _mult, _flags) \ | ||
47 | struct clk_fixed_factor _struct = { \ | ||
48 | .div = _div, \ | ||
49 | .mult = _mult, \ | ||
50 | .hw.init = CLK_HW_INIT(_name, \ | ||
51 | _parent, \ | ||
52 | &clk_fixed_factor_ops, \ | ||
53 | _flags), \ | ||
54 | } | ||
55 | |||
56 | struct ccu_common { | ||
57 | void __iomem *base; | ||
58 | u16 reg; | ||
59 | |||
60 | unsigned long features; | ||
61 | spinlock_t *lock; | ||
62 | struct clk_hw hw; | ||
63 | }; | ||
64 | |||
65 | static inline struct ccu_common *hw_to_ccu_common(struct clk_hw *hw) | ||
66 | { | ||
67 | return container_of(hw, struct ccu_common, hw); | ||
68 | } | ||
69 | |||
70 | struct sunxi_ccu_desc { | ||
71 | struct ccu_common **ccu_clks; | ||
72 | unsigned long num_ccu_clks; | ||
73 | |||
74 | struct clk_hw_onecell_data *hw_clks; | ||
75 | |||
76 | struct ccu_reset_map *resets; | ||
77 | unsigned long num_resets; | ||
78 | }; | ||
79 | |||
80 | void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock); | ||
81 | |||
82 | int sunxi_ccu_probe(struct device_node *node, void __iomem *reg, | ||
83 | const struct sunxi_ccu_desc *desc); | ||
84 | |||
85 | #endif /* _COMMON_H_ */ | ||
diff --git a/drivers/clk/sunxi-ng/ccu_div.c b/drivers/clk/sunxi-ng/ccu_div.c new file mode 100644 index 000000000000..8659b4cb6c20 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_div.c | |||
@@ -0,0 +1,136 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 Maxime Ripard | ||
3 | * Maxime Ripard <maxime.ripard@free-electrons.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License as | ||
7 | * published by the Free Software Foundation; either version 2 of | ||
8 | * the License, or (at your option) any later version. | ||
9 | */ | ||
10 | |||
11 | #include <linux/clk-provider.h> | ||
12 | |||
13 | #include "ccu_gate.h" | ||
14 | #include "ccu_div.h" | ||
15 | |||
16 | static unsigned long ccu_div_round_rate(struct ccu_mux_internal *mux, | ||
17 | unsigned long parent_rate, | ||
18 | unsigned long rate, | ||
19 | void *data) | ||
20 | { | ||
21 | struct ccu_div *cd = data; | ||
22 | unsigned long val; | ||
23 | |||
24 | /* | ||
25 | * We can't use divider_round_rate that assumes that there's | ||
26 | * several parents, while we might be called to evaluate | ||
27 | * several different parents. | ||
28 | */ | ||
29 | val = divider_get_val(rate, parent_rate, cd->div.table, cd->div.width, | ||
30 | cd->div.flags); | ||
31 | |||
32 | return divider_recalc_rate(&cd->common.hw, parent_rate, val, | ||
33 | cd->div.table, cd->div.flags); | ||
34 | } | ||
35 | |||
36 | static void ccu_div_disable(struct clk_hw *hw) | ||
37 | { | ||
38 | struct ccu_div *cd = hw_to_ccu_div(hw); | ||
39 | |||
40 | return ccu_gate_helper_disable(&cd->common, cd->enable); | ||
41 | } | ||
42 | |||
43 | static int ccu_div_enable(struct clk_hw *hw) | ||
44 | { | ||
45 | struct ccu_div *cd = hw_to_ccu_div(hw); | ||
46 | |||
47 | return ccu_gate_helper_enable(&cd->common, cd->enable); | ||
48 | } | ||
49 | |||
50 | static int ccu_div_is_enabled(struct clk_hw *hw) | ||
51 | { | ||
52 | struct ccu_div *cd = hw_to_ccu_div(hw); | ||
53 | |||
54 | return ccu_gate_helper_is_enabled(&cd->common, cd->enable); | ||
55 | } | ||
56 | |||
57 | static unsigned long ccu_div_recalc_rate(struct clk_hw *hw, | ||
58 | unsigned long parent_rate) | ||
59 | { | ||
60 | struct ccu_div *cd = hw_to_ccu_div(hw); | ||
61 | unsigned long val; | ||
62 | u32 reg; | ||
63 | |||
64 | reg = readl(cd->common.base + cd->common.reg); | ||
65 | val = reg >> cd->div.shift; | ||
66 | val &= (1 << cd->div.width) - 1; | ||
67 | |||
68 | ccu_mux_helper_adjust_parent_for_prediv(&cd->common, &cd->mux, -1, | ||
69 | &parent_rate); | ||
70 | |||
71 | return divider_recalc_rate(hw, parent_rate, val, cd->div.table, | ||
72 | cd->div.flags); | ||
73 | } | ||
74 | |||
75 | static int ccu_div_determine_rate(struct clk_hw *hw, | ||
76 | struct clk_rate_request *req) | ||
77 | { | ||
78 | struct ccu_div *cd = hw_to_ccu_div(hw); | ||
79 | |||
80 | return ccu_mux_helper_determine_rate(&cd->common, &cd->mux, | ||
81 | req, ccu_div_round_rate, cd); | ||
82 | } | ||
83 | |||
84 | static int ccu_div_set_rate(struct clk_hw *hw, unsigned long rate, | ||
85 | unsigned long parent_rate) | ||
86 | { | ||
87 | struct ccu_div *cd = hw_to_ccu_div(hw); | ||
88 | unsigned long flags; | ||
89 | unsigned long val; | ||
90 | u32 reg; | ||
91 | |||
92 | ccu_mux_helper_adjust_parent_for_prediv(&cd->common, &cd->mux, -1, | ||
93 | &parent_rate); | ||
94 | |||
95 | val = divider_get_val(rate, parent_rate, cd->div.table, cd->div.width, | ||
96 | cd->div.flags); | ||
97 | |||
98 | spin_lock_irqsave(cd->common.lock, flags); | ||
99 | |||
100 | reg = readl(cd->common.base + cd->common.reg); | ||
101 | reg &= ~GENMASK(cd->div.width + cd->div.shift - 1, cd->div.shift); | ||
102 | |||
103 | writel(reg | (val << cd->div.shift), | ||
104 | cd->common.base + cd->common.reg); | ||
105 | |||
106 | spin_unlock_irqrestore(cd->common.lock, flags); | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | static u8 ccu_div_get_parent(struct clk_hw *hw) | ||
112 | { | ||
113 | struct ccu_div *cd = hw_to_ccu_div(hw); | ||
114 | |||
115 | return ccu_mux_helper_get_parent(&cd->common, &cd->mux); | ||
116 | } | ||
117 | |||
118 | static int ccu_div_set_parent(struct clk_hw *hw, u8 index) | ||
119 | { | ||
120 | struct ccu_div *cd = hw_to_ccu_div(hw); | ||
121 | |||
122 | return ccu_mux_helper_set_parent(&cd->common, &cd->mux, index); | ||
123 | } | ||
124 | |||
125 | const struct clk_ops ccu_div_ops = { | ||
126 | .disable = ccu_div_disable, | ||
127 | .enable = ccu_div_enable, | ||
128 | .is_enabled = ccu_div_is_enabled, | ||
129 | |||
130 | .get_parent = ccu_div_get_parent, | ||
131 | .set_parent = ccu_div_set_parent, | ||
132 | |||
133 | .determine_rate = ccu_div_determine_rate, | ||
134 | .recalc_rate = ccu_div_recalc_rate, | ||
135 | .set_rate = ccu_div_set_rate, | ||
136 | }; | ||
diff --git a/drivers/clk/sunxi-ng/ccu_div.h b/drivers/clk/sunxi-ng/ccu_div.h new file mode 100644 index 000000000000..653ade5769b3 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_div.h | |||
@@ -0,0 +1,133 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016 Maxime Ripard. All rights reserved. | ||
3 | * | ||
4 | * This software is licensed under the terms of the GNU General Public | ||
5 | * License version 2, as published by the Free Software Foundation, and | ||
6 | * may be copied, distributed, and modified under those terms. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #ifndef _CCU_DIV_H_ | ||
15 | #define _CCU_DIV_H_ | ||
16 | |||
17 | #include <linux/clk-provider.h> | ||
18 | |||
19 | #include "ccu_common.h" | ||
20 | #include "ccu_mux.h" | ||
21 | |||
22 | struct _ccu_div { | ||
23 | u8 shift; | ||
24 | u8 width; | ||
25 | |||
26 | u32 flags; | ||
27 | |||
28 | struct clk_div_table *table; | ||
29 | }; | ||
30 | |||
31 | #define _SUNXI_CCU_DIV_TABLE_FLAGS(_shift, _width, _table, _flags) \ | ||
32 | { \ | ||
33 | .shift = _shift, \ | ||
34 | .width = _width, \ | ||
35 | .flags = _flags, \ | ||
36 | .table = _table, \ | ||
37 | } | ||
38 | |||
39 | #define _SUNXI_CCU_DIV_FLAGS(_shift, _width, _flags) \ | ||
40 | _SUNXI_CCU_DIV_TABLE_FLAGS(_shift, _width, NULL, _flags) | ||
41 | |||
42 | #define _SUNXI_CCU_DIV_TABLE(_shift, _width, _table) \ | ||
43 | _SUNXI_CCU_DIV_TABLE_FLAGS(_shift, _width, _table, 0) | ||
44 | |||
45 | #define _SUNXI_CCU_DIV(_shift, _width) \ | ||
46 | _SUNXI_CCU_DIV_TABLE_FLAGS(_shift, _width, NULL, 0) | ||
47 | |||
48 | struct ccu_div { | ||
49 | u32 enable; | ||
50 | |||
51 | struct _ccu_div div; | ||
52 | struct ccu_mux_internal mux; | ||
53 | struct ccu_common common; | ||
54 | }; | ||
55 | |||
56 | #define SUNXI_CCU_DIV_TABLE_WITH_GATE(_struct, _name, _parent, _reg, \ | ||
57 | _shift, _width, \ | ||
58 | _table, _gate, _flags) \ | ||
59 | struct ccu_div _struct = { \ | ||
60 | .div = _SUNXI_CCU_DIV_TABLE(_shift, _width, \ | ||
61 | _table), \ | ||
62 | .enable = _gate, \ | ||
63 | .common = { \ | ||
64 | .reg = _reg, \ | ||
65 | .hw.init = CLK_HW_INIT(_name, \ | ||
66 | _parent, \ | ||
67 | &ccu_div_ops, \ | ||
68 | _flags), \ | ||
69 | } \ | ||
70 | } | ||
71 | |||
72 | |||
73 | #define SUNXI_CCU_DIV_TABLE(_struct, _name, _parent, _reg, \ | ||
74 | _shift, _width, \ | ||
75 | _table, _flags) \ | ||
76 | SUNXI_CCU_DIV_TABLE_WITH_GATE(_struct, _name, _parent, _reg, \ | ||
77 | _shift, _width, _table, 0, \ | ||
78 | _flags) | ||
79 | |||
80 | #define SUNXI_CCU_M_WITH_MUX_GATE(_struct, _name, _parents, _reg, \ | ||
81 | _mshift, _mwidth, _muxshift, _muxwidth, \ | ||
82 | _gate, _flags) \ | ||
83 | struct ccu_div _struct = { \ | ||
84 | .enable = _gate, \ | ||
85 | .div = _SUNXI_CCU_DIV(_mshift, _mwidth), \ | ||
86 | .mux = SUNXI_CLK_MUX(_muxshift, _muxwidth), \ | ||
87 | .common = { \ | ||
88 | .reg = _reg, \ | ||
89 | .hw.init = CLK_HW_INIT_PARENTS(_name, \ | ||
90 | _parents, \ | ||
91 | &ccu_div_ops, \ | ||
92 | _flags), \ | ||
93 | }, \ | ||
94 | } | ||
95 | |||
96 | #define SUNXI_CCU_M_WITH_MUX(_struct, _name, _parents, _reg, \ | ||
97 | _mshift, _mwidth, _muxshift, _muxwidth, \ | ||
98 | _flags) \ | ||
99 | SUNXI_CCU_M_WITH_MUX_GATE(_struct, _name, _parents, _reg, \ | ||
100 | _mshift, _mwidth, _muxshift, _muxwidth, \ | ||
101 | 0, _flags) | ||
102 | |||
103 | |||
104 | #define SUNXI_CCU_M_WITH_GATE(_struct, _name, _parent, _reg, \ | ||
105 | _mshift, _mwidth, _gate, \ | ||
106 | _flags) \ | ||
107 | struct ccu_div _struct = { \ | ||
108 | .enable = _gate, \ | ||
109 | .div = _SUNXI_CCU_DIV(_mshift, _mwidth), \ | ||
110 | .common = { \ | ||
111 | .reg = _reg, \ | ||
112 | .hw.init = CLK_HW_INIT(_name, \ | ||
113 | _parent, \ | ||
114 | &ccu_div_ops, \ | ||
115 | _flags), \ | ||
116 | }, \ | ||
117 | } | ||
118 | |||
119 | #define SUNXI_CCU_M(_struct, _name, _parent, _reg, _mshift, _mwidth, \ | ||
120 | _flags) \ | ||
121 | SUNXI_CCU_M_WITH_GATE(_struct, _name, _parent, _reg, \ | ||
122 | _mshift, _mwidth, 0, _flags) | ||
123 | |||
124 | static inline struct ccu_div *hw_to_ccu_div(struct clk_hw *hw) | ||
125 | { | ||
126 | struct ccu_common *common = hw_to_ccu_common(hw); | ||
127 | |||
128 | return container_of(common, struct ccu_div, common); | ||
129 | } | ||
130 | |||
131 | extern const struct clk_ops ccu_div_ops; | ||
132 | |||
133 | #endif /* _CCU_DIV_H_ */ | ||
diff --git a/drivers/clk/sunxi-ng/ccu_frac.c b/drivers/clk/sunxi-ng/ccu_frac.c new file mode 100644 index 000000000000..5c4b10cd15b5 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_frac.c | |||
@@ -0,0 +1,110 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 Maxime Ripard | ||
3 | * Maxime Ripard <maxime.ripard@free-electrons.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License as | ||
7 | * published by the Free Software Foundation; either version 2 of | ||
8 | * the License, or (at your option) any later version. | ||
9 | */ | ||
10 | |||
11 | #include <linux/clk-provider.h> | ||
12 | #include <linux/spinlock.h> | ||
13 | |||
14 | #include "ccu_frac.h" | ||
15 | |||
16 | bool ccu_frac_helper_is_enabled(struct ccu_common *common, | ||
17 | struct _ccu_frac *cf) | ||
18 | { | ||
19 | if (!(common->features & CCU_FEATURE_FRACTIONAL)) | ||
20 | return false; | ||
21 | |||
22 | return !(readl(common->base + common->reg) & cf->enable); | ||
23 | } | ||
24 | |||
25 | void ccu_frac_helper_enable(struct ccu_common *common, | ||
26 | struct _ccu_frac *cf) | ||
27 | { | ||
28 | unsigned long flags; | ||
29 | u32 reg; | ||
30 | |||
31 | if (!(common->features & CCU_FEATURE_FRACTIONAL)) | ||
32 | return; | ||
33 | |||
34 | spin_lock_irqsave(common->lock, flags); | ||
35 | reg = readl(common->base + common->reg); | ||
36 | writel(reg & ~cf->enable, common->base + common->reg); | ||
37 | spin_unlock_irqrestore(common->lock, flags); | ||
38 | } | ||
39 | |||
40 | void ccu_frac_helper_disable(struct ccu_common *common, | ||
41 | struct _ccu_frac *cf) | ||
42 | { | ||
43 | unsigned long flags; | ||
44 | u32 reg; | ||
45 | |||
46 | if (!(common->features & CCU_FEATURE_FRACTIONAL)) | ||
47 | return; | ||
48 | |||
49 | spin_lock_irqsave(common->lock, flags); | ||
50 | reg = readl(common->base + common->reg); | ||
51 | writel(reg | cf->enable, common->base + common->reg); | ||
52 | spin_unlock_irqrestore(common->lock, flags); | ||
53 | } | ||
54 | |||
55 | bool ccu_frac_helper_has_rate(struct ccu_common *common, | ||
56 | struct _ccu_frac *cf, | ||
57 | unsigned long rate) | ||
58 | { | ||
59 | if (!(common->features & CCU_FEATURE_FRACTIONAL)) | ||
60 | return false; | ||
61 | |||
62 | return (cf->rates[0] == rate) || (cf->rates[1] == rate); | ||
63 | } | ||
64 | |||
65 | unsigned long ccu_frac_helper_read_rate(struct ccu_common *common, | ||
66 | struct _ccu_frac *cf) | ||
67 | { | ||
68 | u32 reg; | ||
69 | |||
70 | printk("%s: Read fractional\n", clk_hw_get_name(&common->hw)); | ||
71 | |||
72 | if (!(common->features & CCU_FEATURE_FRACTIONAL)) | ||
73 | return 0; | ||
74 | |||
75 | printk("%s: clock is fractional (rates %lu and %lu)\n", | ||
76 | clk_hw_get_name(&common->hw), cf->rates[0], cf->rates[1]); | ||
77 | |||
78 | reg = readl(common->base + common->reg); | ||
79 | |||
80 | printk("%s: clock reg is 0x%x (select is 0x%x)\n", | ||
81 | clk_hw_get_name(&common->hw), reg, cf->select); | ||
82 | |||
83 | return (reg & cf->select) ? cf->rates[1] : cf->rates[0]; | ||
84 | } | ||
85 | |||
86 | int ccu_frac_helper_set_rate(struct ccu_common *common, | ||
87 | struct _ccu_frac *cf, | ||
88 | unsigned long rate) | ||
89 | { | ||
90 | unsigned long flags; | ||
91 | u32 reg, sel; | ||
92 | |||
93 | if (!(common->features & CCU_FEATURE_FRACTIONAL)) | ||
94 | return -EINVAL; | ||
95 | |||
96 | if (cf->rates[0] == rate) | ||
97 | sel = 0; | ||
98 | else if (cf->rates[1] == rate) | ||
99 | sel = cf->select; | ||
100 | else | ||
101 | return -EINVAL; | ||
102 | |||
103 | spin_lock_irqsave(common->lock, flags); | ||
104 | reg = readl(common->base + common->reg); | ||
105 | reg &= ~cf->select; | ||
106 | writel(reg | sel, common->base + common->reg); | ||
107 | spin_unlock_irqrestore(common->lock, flags); | ||
108 | |||
109 | return 0; | ||
110 | } | ||
diff --git a/drivers/clk/sunxi-ng/ccu_frac.h b/drivers/clk/sunxi-ng/ccu_frac.h new file mode 100644 index 000000000000..e4c670b1cdfe --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_frac.h | |||
@@ -0,0 +1,53 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016 Maxime Ripard. All rights reserved. | ||
3 | * | ||
4 | * This software is licensed under the terms of the GNU General Public | ||
5 | * License version 2, as published by the Free Software Foundation, and | ||
6 | * may be copied, distributed, and modified under those terms. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #ifndef _CCU_FRAC_H_ | ||
15 | #define _CCU_FRAC_H_ | ||
16 | |||
17 | #include <linux/clk-provider.h> | ||
18 | |||
19 | #include "ccu_common.h" | ||
20 | |||
21 | struct _ccu_frac { | ||
22 | u32 enable; | ||
23 | u32 select; | ||
24 | |||
25 | unsigned long rates[2]; | ||
26 | }; | ||
27 | |||
28 | #define _SUNXI_CCU_FRAC(_enable, _select, _rate1, _rate2) \ | ||
29 | { \ | ||
30 | .enable = _enable, \ | ||
31 | .select = _select, \ | ||
32 | .rates = { _rate1, _rate2 }, \ | ||
33 | } | ||
34 | |||
35 | bool ccu_frac_helper_is_enabled(struct ccu_common *common, | ||
36 | struct _ccu_frac *cf); | ||
37 | void ccu_frac_helper_enable(struct ccu_common *common, | ||
38 | struct _ccu_frac *cf); | ||
39 | void ccu_frac_helper_disable(struct ccu_common *common, | ||
40 | struct _ccu_frac *cf); | ||
41 | |||
42 | bool ccu_frac_helper_has_rate(struct ccu_common *common, | ||
43 | struct _ccu_frac *cf, | ||
44 | unsigned long rate); | ||
45 | |||
46 | unsigned long ccu_frac_helper_read_rate(struct ccu_common *common, | ||
47 | struct _ccu_frac *cf); | ||
48 | |||
49 | int ccu_frac_helper_set_rate(struct ccu_common *common, | ||
50 | struct _ccu_frac *cf, | ||
51 | unsigned long rate); | ||
52 | |||
53 | #endif /* _CCU_FRAC_H_ */ | ||
diff --git a/drivers/clk/sunxi-ng/ccu_gate.c b/drivers/clk/sunxi-ng/ccu_gate.c new file mode 100644 index 000000000000..8a81f9d4a89f --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_gate.c | |||
@@ -0,0 +1,82 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 Maxime Ripard | ||
3 | * Maxime Ripard <maxime.ripard@free-electrons.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License as | ||
7 | * published by the Free Software Foundation; either version 2 of | ||
8 | * the License, or (at your option) any later version. | ||
9 | */ | ||
10 | |||
11 | #include <linux/clk-provider.h> | ||
12 | |||
13 | #include "ccu_gate.h" | ||
14 | |||
15 | void ccu_gate_helper_disable(struct ccu_common *common, u32 gate) | ||
16 | { | ||
17 | unsigned long flags; | ||
18 | u32 reg; | ||
19 | |||
20 | if (!gate) | ||
21 | return; | ||
22 | |||
23 | spin_lock_irqsave(common->lock, flags); | ||
24 | |||
25 | reg = readl(common->base + common->reg); | ||
26 | writel(reg & ~gate, common->base + common->reg); | ||
27 | |||
28 | spin_unlock_irqrestore(common->lock, flags); | ||
29 | } | ||
30 | |||
31 | static void ccu_gate_disable(struct clk_hw *hw) | ||
32 | { | ||
33 | struct ccu_gate *cg = hw_to_ccu_gate(hw); | ||
34 | |||
35 | return ccu_gate_helper_disable(&cg->common, cg->enable); | ||
36 | } | ||
37 | |||
38 | int ccu_gate_helper_enable(struct ccu_common *common, u32 gate) | ||
39 | { | ||
40 | unsigned long flags; | ||
41 | u32 reg; | ||
42 | |||
43 | if (!gate) | ||
44 | return 0; | ||
45 | |||
46 | spin_lock_irqsave(common->lock, flags); | ||
47 | |||
48 | reg = readl(common->base + common->reg); | ||
49 | writel(reg | gate, common->base + common->reg); | ||
50 | |||
51 | spin_unlock_irqrestore(common->lock, flags); | ||
52 | |||
53 | return 0; | ||
54 | } | ||
55 | |||
56 | static int ccu_gate_enable(struct clk_hw *hw) | ||
57 | { | ||
58 | struct ccu_gate *cg = hw_to_ccu_gate(hw); | ||
59 | |||
60 | return ccu_gate_helper_enable(&cg->common, cg->enable); | ||
61 | } | ||
62 | |||
63 | int ccu_gate_helper_is_enabled(struct ccu_common *common, u32 gate) | ||
64 | { | ||
65 | if (!gate) | ||
66 | return 1; | ||
67 | |||
68 | return readl(common->base + common->reg) & gate; | ||
69 | } | ||
70 | |||
71 | static int ccu_gate_is_enabled(struct clk_hw *hw) | ||
72 | { | ||
73 | struct ccu_gate *cg = hw_to_ccu_gate(hw); | ||
74 | |||
75 | return ccu_gate_helper_is_enabled(&cg->common, cg->enable); | ||
76 | } | ||
77 | |||
78 | const struct clk_ops ccu_gate_ops = { | ||
79 | .disable = ccu_gate_disable, | ||
80 | .enable = ccu_gate_enable, | ||
81 | .is_enabled = ccu_gate_is_enabled, | ||
82 | }; | ||
diff --git a/drivers/clk/sunxi-ng/ccu_gate.h b/drivers/clk/sunxi-ng/ccu_gate.h new file mode 100644 index 000000000000..4466169bd2d7 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_gate.h | |||
@@ -0,0 +1,52 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016 Maxime Ripard. All rights reserved. | ||
3 | * | ||
4 | * This software is licensed under the terms of the GNU General Public | ||
5 | * License version 2, as published by the Free Software Foundation, and | ||
6 | * may be copied, distributed, and modified under those terms. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #ifndef _CCU_GATE_H_ | ||
15 | #define _CCU_GATE_H_ | ||
16 | |||
17 | #include <linux/clk-provider.h> | ||
18 | |||
19 | #include "ccu_common.h" | ||
20 | |||
21 | struct ccu_gate { | ||
22 | u32 enable; | ||
23 | |||
24 | struct ccu_common common; | ||
25 | }; | ||
26 | |||
27 | #define SUNXI_CCU_GATE(_struct, _name, _parent, _reg, _gate, _flags) \ | ||
28 | struct ccu_gate _struct = { \ | ||
29 | .enable = _gate, \ | ||
30 | .common = { \ | ||
31 | .reg = _reg, \ | ||
32 | .hw.init = CLK_HW_INIT(_name, \ | ||
33 | _parent, \ | ||
34 | &ccu_gate_ops, \ | ||
35 | _flags), \ | ||
36 | } \ | ||
37 | } | ||
38 | |||
39 | static inline struct ccu_gate *hw_to_ccu_gate(struct clk_hw *hw) | ||
40 | { | ||
41 | struct ccu_common *common = hw_to_ccu_common(hw); | ||
42 | |||
43 | return container_of(common, struct ccu_gate, common); | ||
44 | } | ||
45 | |||
46 | void ccu_gate_helper_disable(struct ccu_common *common, u32 gate); | ||
47 | int ccu_gate_helper_enable(struct ccu_common *common, u32 gate); | ||
48 | int ccu_gate_helper_is_enabled(struct ccu_common *common, u32 gate); | ||
49 | |||
50 | extern const struct clk_ops ccu_gate_ops; | ||
51 | |||
52 | #endif /* _CCU_GATE_H_ */ | ||
diff --git a/drivers/clk/sunxi-ng/ccu_mp.c b/drivers/clk/sunxi-ng/ccu_mp.c new file mode 100644 index 000000000000..cbf33ef5faa9 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_mp.c | |||
@@ -0,0 +1,158 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 Maxime Ripard | ||
3 | * Maxime Ripard <maxime.ripard@free-electrons.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License as | ||
7 | * published by the Free Software Foundation; either version 2 of | ||
8 | * the License, or (at your option) any later version. | ||
9 | */ | ||
10 | |||
11 | #include <linux/clk-provider.h> | ||
12 | |||
13 | #include "ccu_gate.h" | ||
14 | #include "ccu_mp.h" | ||
15 | |||
16 | static void ccu_mp_find_best(unsigned long parent, unsigned long rate, | ||
17 | unsigned int max_m, unsigned int max_p, | ||
18 | unsigned int *m, unsigned int *p) | ||
19 | { | ||
20 | unsigned long best_rate = 0; | ||
21 | unsigned int best_m = 0, best_p = 0; | ||
22 | unsigned int _m, _p; | ||
23 | |||
24 | for (_p = 0; _p <= max_p; _p++) { | ||
25 | for (_m = 1; _m <= max_m; _m++) { | ||
26 | unsigned long tmp_rate = (parent >> _p) / _m; | ||
27 | |||
28 | if (tmp_rate > rate) | ||
29 | continue; | ||
30 | |||
31 | if ((rate - tmp_rate) < (rate - best_rate)) { | ||
32 | best_rate = tmp_rate; | ||
33 | best_m = _m; | ||
34 | best_p = _p; | ||
35 | } | ||
36 | } | ||
37 | } | ||
38 | |||
39 | *m = best_m; | ||
40 | *p = best_p; | ||
41 | } | ||
42 | |||
43 | static unsigned long ccu_mp_round_rate(struct ccu_mux_internal *mux, | ||
44 | unsigned long parent_rate, | ||
45 | unsigned long rate, | ||
46 | void *data) | ||
47 | { | ||
48 | struct ccu_mp *cmp = data; | ||
49 | unsigned int m, p; | ||
50 | |||
51 | ccu_mp_find_best(parent_rate, rate, | ||
52 | 1 << cmp->m.width, (1 << cmp->p.width) - 1, | ||
53 | &m, &p); | ||
54 | |||
55 | return (parent_rate >> p) / m; | ||
56 | } | ||
57 | |||
58 | static void ccu_mp_disable(struct clk_hw *hw) | ||
59 | { | ||
60 | struct ccu_mp *cmp = hw_to_ccu_mp(hw); | ||
61 | |||
62 | return ccu_gate_helper_disable(&cmp->common, cmp->enable); | ||
63 | } | ||
64 | |||
65 | static int ccu_mp_enable(struct clk_hw *hw) | ||
66 | { | ||
67 | struct ccu_mp *cmp = hw_to_ccu_mp(hw); | ||
68 | |||
69 | return ccu_gate_helper_enable(&cmp->common, cmp->enable); | ||
70 | } | ||
71 | |||
72 | static int ccu_mp_is_enabled(struct clk_hw *hw) | ||
73 | { | ||
74 | struct ccu_mp *cmp = hw_to_ccu_mp(hw); | ||
75 | |||
76 | return ccu_gate_helper_is_enabled(&cmp->common, cmp->enable); | ||
77 | } | ||
78 | |||
79 | static unsigned long ccu_mp_recalc_rate(struct clk_hw *hw, | ||
80 | unsigned long parent_rate) | ||
81 | { | ||
82 | struct ccu_mp *cmp = hw_to_ccu_mp(hw); | ||
83 | unsigned int m, p; | ||
84 | u32 reg; | ||
85 | |||
86 | reg = readl(cmp->common.base + cmp->common.reg); | ||
87 | |||
88 | m = reg >> cmp->m.shift; | ||
89 | m &= (1 << cmp->m.width) - 1; | ||
90 | |||
91 | p = reg >> cmp->p.shift; | ||
92 | p &= (1 << cmp->p.width) - 1; | ||
93 | |||
94 | return (parent_rate >> p) / (m + 1); | ||
95 | } | ||
96 | |||
97 | static int ccu_mp_determine_rate(struct clk_hw *hw, | ||
98 | struct clk_rate_request *req) | ||
99 | { | ||
100 | struct ccu_mp *cmp = hw_to_ccu_mp(hw); | ||
101 | |||
102 | return ccu_mux_helper_determine_rate(&cmp->common, &cmp->mux, | ||
103 | req, ccu_mp_round_rate, cmp); | ||
104 | } | ||
105 | |||
106 | static int ccu_mp_set_rate(struct clk_hw *hw, unsigned long rate, | ||
107 | unsigned long parent_rate) | ||
108 | { | ||
109 | struct ccu_mp *cmp = hw_to_ccu_mp(hw); | ||
110 | unsigned long flags; | ||
111 | unsigned int m, p; | ||
112 | u32 reg; | ||
113 | |||
114 | ccu_mp_find_best(parent_rate, rate, | ||
115 | 1 << cmp->m.width, (1 << cmp->p.width) - 1, | ||
116 | &m, &p); | ||
117 | |||
118 | |||
119 | spin_lock_irqsave(cmp->common.lock, flags); | ||
120 | |||
121 | reg = readl(cmp->common.base + cmp->common.reg); | ||
122 | reg &= ~GENMASK(cmp->m.width + cmp->m.shift - 1, cmp->m.shift); | ||
123 | reg &= ~GENMASK(cmp->p.width + cmp->p.shift - 1, cmp->p.shift); | ||
124 | |||
125 | writel(reg | (p << cmp->p.shift) | ((m - 1) << cmp->m.shift), | ||
126 | cmp->common.base + cmp->common.reg); | ||
127 | |||
128 | spin_unlock_irqrestore(cmp->common.lock, flags); | ||
129 | |||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | static u8 ccu_mp_get_parent(struct clk_hw *hw) | ||
134 | { | ||
135 | struct ccu_mp *cmp = hw_to_ccu_mp(hw); | ||
136 | |||
137 | return ccu_mux_helper_get_parent(&cmp->common, &cmp->mux); | ||
138 | } | ||
139 | |||
140 | static int ccu_mp_set_parent(struct clk_hw *hw, u8 index) | ||
141 | { | ||
142 | struct ccu_mp *cmp = hw_to_ccu_mp(hw); | ||
143 | |||
144 | return ccu_mux_helper_set_parent(&cmp->common, &cmp->mux, index); | ||
145 | } | ||
146 | |||
147 | const struct clk_ops ccu_mp_ops = { | ||
148 | .disable = ccu_mp_disable, | ||
149 | .enable = ccu_mp_enable, | ||
150 | .is_enabled = ccu_mp_is_enabled, | ||
151 | |||
152 | .get_parent = ccu_mp_get_parent, | ||
153 | .set_parent = ccu_mp_set_parent, | ||
154 | |||
155 | .determine_rate = ccu_mp_determine_rate, | ||
156 | .recalc_rate = ccu_mp_recalc_rate, | ||
157 | .set_rate = ccu_mp_set_rate, | ||
158 | }; | ||
diff --git a/drivers/clk/sunxi-ng/ccu_mp.h b/drivers/clk/sunxi-ng/ccu_mp.h new file mode 100644 index 000000000000..3cf12bf95962 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_mp.h | |||
@@ -0,0 +1,77 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016 Maxime Ripard. All rights reserved. | ||
3 | * | ||
4 | * This software is licensed under the terms of the GNU General Public | ||
5 | * License version 2, as published by the Free Software Foundation, and | ||
6 | * may be copied, distributed, and modified under those terms. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #ifndef _CCU_MP_H_ | ||
15 | #define _CCU_MP_H_ | ||
16 | |||
17 | #include <linux/clk-provider.h> | ||
18 | |||
19 | #include "ccu_common.h" | ||
20 | #include "ccu_div.h" | ||
21 | #include "ccu_mult.h" | ||
22 | #include "ccu_mux.h" | ||
23 | |||
24 | /* | ||
25 | * struct ccu_mp - Definition of an M-P clock | ||
26 | * | ||
27 | * Clocks based on the formula parent >> P / M | ||
28 | */ | ||
29 | struct ccu_mp { | ||
30 | u32 enable; | ||
31 | |||
32 | struct _ccu_div m; | ||
33 | struct _ccu_div p; | ||
34 | struct ccu_mux_internal mux; | ||
35 | struct ccu_common common; | ||
36 | }; | ||
37 | |||
38 | #define SUNXI_CCU_MP_WITH_MUX_GATE(_struct, _name, _parents, _reg, \ | ||
39 | _mshift, _mwidth, \ | ||
40 | _pshift, _pwidth, \ | ||
41 | _muxshift, _muxwidth, \ | ||
42 | _gate, _flags) \ | ||
43 | struct ccu_mp _struct = { \ | ||
44 | .enable = _gate, \ | ||
45 | .m = _SUNXI_CCU_DIV(_mshift, _mwidth), \ | ||
46 | .p = _SUNXI_CCU_DIV(_pshift, _pwidth), \ | ||
47 | .mux = SUNXI_CLK_MUX(_muxshift, _muxwidth), \ | ||
48 | .common = { \ | ||
49 | .reg = _reg, \ | ||
50 | .hw.init = CLK_HW_INIT_PARENTS(_name, \ | ||
51 | _parents, \ | ||
52 | &ccu_mp_ops, \ | ||
53 | _flags), \ | ||
54 | } \ | ||
55 | } | ||
56 | |||
57 | #define SUNXI_CCU_MP_WITH_MUX(_struct, _name, _parents, _reg, \ | ||
58 | _mshift, _mwidth, \ | ||
59 | _pshift, _pwidth, \ | ||
60 | _muxshift, _muxwidth, \ | ||
61 | _flags) \ | ||
62 | SUNXI_CCU_MP_WITH_MUX_GATE(_struct, _name, _parents, _reg, \ | ||
63 | _mshift, _mwidth, \ | ||
64 | _pshift, _pwidth, \ | ||
65 | _muxshift, _muxwidth, \ | ||
66 | 0, _flags) | ||
67 | |||
68 | static inline struct ccu_mp *hw_to_ccu_mp(struct clk_hw *hw) | ||
69 | { | ||
70 | struct ccu_common *common = hw_to_ccu_common(hw); | ||
71 | |||
72 | return container_of(common, struct ccu_mp, common); | ||
73 | } | ||
74 | |||
75 | extern const struct clk_ops ccu_mp_ops; | ||
76 | |||
77 | #endif /* _CCU_MP_H_ */ | ||
diff --git a/drivers/clk/sunxi-ng/ccu_mult.h b/drivers/clk/sunxi-ng/ccu_mult.h new file mode 100644 index 000000000000..609db6610880 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_mult.h | |||
@@ -0,0 +1,15 @@ | |||
1 | #ifndef _CCU_MULT_H_ | ||
2 | #define _CCU_MULT_H_ | ||
3 | |||
4 | struct _ccu_mult { | ||
5 | u8 shift; | ||
6 | u8 width; | ||
7 | }; | ||
8 | |||
9 | #define _SUNXI_CCU_MULT(_shift, _width) \ | ||
10 | { \ | ||
11 | .shift = _shift, \ | ||
12 | .width = _width, \ | ||
13 | } | ||
14 | |||
15 | #endif /* _CCU_MULT_H_ */ | ||
diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c new file mode 100644 index 000000000000..58fc36e7dcce --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_mux.c | |||
@@ -0,0 +1,187 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 Maxime Ripard | ||
3 | * Maxime Ripard <maxime.ripard@free-electrons.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License as | ||
7 | * published by the Free Software Foundation; either version 2 of | ||
8 | * the License, or (at your option) any later version. | ||
9 | */ | ||
10 | |||
11 | #include <linux/clk-provider.h> | ||
12 | |||
13 | #include "ccu_gate.h" | ||
14 | #include "ccu_mux.h" | ||
15 | |||
16 | void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common, | ||
17 | struct ccu_mux_internal *cm, | ||
18 | int parent_index, | ||
19 | unsigned long *parent_rate) | ||
20 | { | ||
21 | u8 prediv = 1; | ||
22 | u32 reg; | ||
23 | |||
24 | if (!((common->features & CCU_FEATURE_FIXED_PREDIV) || | ||
25 | (common->features & CCU_FEATURE_VARIABLE_PREDIV))) | ||
26 | return; | ||
27 | |||
28 | reg = readl(common->base + common->reg); | ||
29 | if (parent_index < 0) { | ||
30 | parent_index = reg >> cm->shift; | ||
31 | parent_index &= (1 << cm->width) - 1; | ||
32 | } | ||
33 | |||
34 | if (common->features & CCU_FEATURE_FIXED_PREDIV) | ||
35 | if (parent_index == cm->fixed_prediv.index) | ||
36 | prediv = cm->fixed_prediv.div; | ||
37 | |||
38 | if (common->features & CCU_FEATURE_VARIABLE_PREDIV) | ||
39 | if (parent_index == cm->variable_prediv.index) { | ||
40 | u8 div; | ||
41 | |||
42 | div = reg >> cm->variable_prediv.shift; | ||
43 | div &= (1 << cm->variable_prediv.width) - 1; | ||
44 | prediv = div + 1; | ||
45 | } | ||
46 | |||
47 | *parent_rate = *parent_rate / prediv; | ||
48 | } | ||
49 | |||
50 | int ccu_mux_helper_determine_rate(struct ccu_common *common, | ||
51 | struct ccu_mux_internal *cm, | ||
52 | struct clk_rate_request *req, | ||
53 | unsigned long (*round)(struct ccu_mux_internal *, | ||
54 | unsigned long, | ||
55 | unsigned long, | ||
56 | void *), | ||
57 | void *data) | ||
58 | { | ||
59 | unsigned long best_parent_rate = 0, best_rate = 0; | ||
60 | struct clk_hw *best_parent, *hw = &common->hw; | ||
61 | unsigned int i; | ||
62 | |||
63 | for (i = 0; i < clk_hw_get_num_parents(hw); i++) { | ||
64 | unsigned long tmp_rate, parent_rate; | ||
65 | struct clk_hw *parent; | ||
66 | |||
67 | parent = clk_hw_get_parent_by_index(hw, i); | ||
68 | if (!parent) | ||
69 | continue; | ||
70 | |||
71 | parent_rate = clk_hw_get_rate(parent); | ||
72 | ccu_mux_helper_adjust_parent_for_prediv(common, cm, i, | ||
73 | &parent_rate); | ||
74 | |||
75 | tmp_rate = round(cm, clk_hw_get_rate(parent), req->rate, data); | ||
76 | if (tmp_rate == req->rate) { | ||
77 | best_parent = parent; | ||
78 | best_parent_rate = parent_rate; | ||
79 | best_rate = tmp_rate; | ||
80 | goto out; | ||
81 | } | ||
82 | |||
83 | if ((req->rate - tmp_rate) < (req->rate - best_rate)) { | ||
84 | best_rate = tmp_rate; | ||
85 | best_parent_rate = parent_rate; | ||
86 | best_parent = parent; | ||
87 | } | ||
88 | } | ||
89 | |||
90 | if (best_rate == 0) | ||
91 | return -EINVAL; | ||
92 | |||
93 | out: | ||
94 | req->best_parent_hw = best_parent; | ||
95 | req->best_parent_rate = best_parent_rate; | ||
96 | req->rate = best_rate; | ||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | u8 ccu_mux_helper_get_parent(struct ccu_common *common, | ||
101 | struct ccu_mux_internal *cm) | ||
102 | { | ||
103 | u32 reg; | ||
104 | u8 parent; | ||
105 | |||
106 | reg = readl(common->base + common->reg); | ||
107 | parent = reg >> cm->shift; | ||
108 | parent &= (1 << cm->width) - 1; | ||
109 | |||
110 | return parent; | ||
111 | } | ||
112 | |||
113 | int ccu_mux_helper_set_parent(struct ccu_common *common, | ||
114 | struct ccu_mux_internal *cm, | ||
115 | u8 index) | ||
116 | { | ||
117 | unsigned long flags; | ||
118 | u32 reg; | ||
119 | |||
120 | spin_lock_irqsave(common->lock, flags); | ||
121 | |||
122 | reg = readl(common->base + common->reg); | ||
123 | reg &= ~GENMASK(cm->width + cm->shift - 1, cm->shift); | ||
124 | writel(reg | (index << cm->shift), common->base + common->reg); | ||
125 | |||
126 | spin_unlock_irqrestore(common->lock, flags); | ||
127 | |||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | static void ccu_mux_disable(struct clk_hw *hw) | ||
132 | { | ||
133 | struct ccu_mux *cm = hw_to_ccu_mux(hw); | ||
134 | |||
135 | return ccu_gate_helper_disable(&cm->common, cm->enable); | ||
136 | } | ||
137 | |||
138 | static int ccu_mux_enable(struct clk_hw *hw) | ||
139 | { | ||
140 | struct ccu_mux *cm = hw_to_ccu_mux(hw); | ||
141 | |||
142 | return ccu_gate_helper_enable(&cm->common, cm->enable); | ||
143 | } | ||
144 | |||
145 | static int ccu_mux_is_enabled(struct clk_hw *hw) | ||
146 | { | ||
147 | struct ccu_mux *cm = hw_to_ccu_mux(hw); | ||
148 | |||
149 | return ccu_gate_helper_is_enabled(&cm->common, cm->enable); | ||
150 | } | ||
151 | |||
152 | static u8 ccu_mux_get_parent(struct clk_hw *hw) | ||
153 | { | ||
154 | struct ccu_mux *cm = hw_to_ccu_mux(hw); | ||
155 | |||
156 | return ccu_mux_helper_get_parent(&cm->common, &cm->mux); | ||
157 | } | ||
158 | |||
159 | static int ccu_mux_set_parent(struct clk_hw *hw, u8 index) | ||
160 | { | ||
161 | struct ccu_mux *cm = hw_to_ccu_mux(hw); | ||
162 | |||
163 | return ccu_mux_helper_set_parent(&cm->common, &cm->mux, index); | ||
164 | } | ||
165 | |||
166 | static unsigned long ccu_mux_recalc_rate(struct clk_hw *hw, | ||
167 | unsigned long parent_rate) | ||
168 | { | ||
169 | struct ccu_mux *cm = hw_to_ccu_mux(hw); | ||
170 | |||
171 | ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1, | ||
172 | &parent_rate); | ||
173 | |||
174 | return parent_rate; | ||
175 | } | ||
176 | |||
177 | const struct clk_ops ccu_mux_ops = { | ||
178 | .disable = ccu_mux_disable, | ||
179 | .enable = ccu_mux_enable, | ||
180 | .is_enabled = ccu_mux_is_enabled, | ||
181 | |||
182 | .get_parent = ccu_mux_get_parent, | ||
183 | .set_parent = ccu_mux_set_parent, | ||
184 | |||
185 | .determine_rate = __clk_mux_determine_rate, | ||
186 | .recalc_rate = ccu_mux_recalc_rate, | ||
187 | }; | ||
diff --git a/drivers/clk/sunxi-ng/ccu_mux.h b/drivers/clk/sunxi-ng/ccu_mux.h new file mode 100644 index 000000000000..945082631e7d --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_mux.h | |||
@@ -0,0 +1,91 @@ | |||
1 | #ifndef _CCU_MUX_H_ | ||
2 | #define _CCU_MUX_H_ | ||
3 | |||
4 | #include <linux/clk-provider.h> | ||
5 | |||
6 | #include "ccu_common.h" | ||
7 | |||
8 | struct ccu_mux_internal { | ||
9 | u8 shift; | ||
10 | u8 width; | ||
11 | |||
12 | struct { | ||
13 | u8 index; | ||
14 | u8 div; | ||
15 | } fixed_prediv; | ||
16 | |||
17 | struct { | ||
18 | u8 index; | ||
19 | u8 shift; | ||
20 | u8 width; | ||
21 | } variable_prediv; | ||
22 | }; | ||
23 | |||
24 | #define SUNXI_CLK_MUX(_shift, _width) \ | ||
25 | { \ | ||
26 | .shift = _shift, \ | ||
27 | .width = _width, \ | ||
28 | } | ||
29 | |||
30 | struct ccu_mux { | ||
31 | u16 reg; | ||
32 | u32 enable; | ||
33 | |||
34 | struct ccu_mux_internal mux; | ||
35 | struct ccu_common common; | ||
36 | }; | ||
37 | |||
38 | #define SUNXI_CCU_MUX(_struct, _name, _parents, _reg, _shift, _width, _flags) \ | ||
39 | struct ccu_mux _struct = { \ | ||
40 | .mux = SUNXI_CLK_MUX(_shift, _width), \ | ||
41 | .common = { \ | ||
42 | .reg = _reg, \ | ||
43 | .hw.init = CLK_HW_INIT_PARENTS(_name, \ | ||
44 | _parents, \ | ||
45 | &ccu_mux_ops, \ | ||
46 | _flags), \ | ||
47 | } \ | ||
48 | } | ||
49 | |||
50 | #define SUNXI_CCU_MUX_WITH_GATE(_struct, _name, _parents, _reg, \ | ||
51 | _shift, _width, _gate, _flags) \ | ||
52 | struct ccu_mux _struct = { \ | ||
53 | .enable = _gate, \ | ||
54 | .mux = SUNXI_CLK_MUX(_shift, _width), \ | ||
55 | .common = { \ | ||
56 | .reg = _reg, \ | ||
57 | .hw.init = CLK_HW_INIT_PARENTS(_name, \ | ||
58 | _parents, \ | ||
59 | &ccu_mux_ops, \ | ||
60 | _flags), \ | ||
61 | } \ | ||
62 | } | ||
63 | |||
64 | static inline struct ccu_mux *hw_to_ccu_mux(struct clk_hw *hw) | ||
65 | { | ||
66 | struct ccu_common *common = hw_to_ccu_common(hw); | ||
67 | |||
68 | return container_of(common, struct ccu_mux, common); | ||
69 | } | ||
70 | |||
71 | extern const struct clk_ops ccu_mux_ops; | ||
72 | |||
73 | void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common, | ||
74 | struct ccu_mux_internal *cm, | ||
75 | int parent_index, | ||
76 | unsigned long *parent_rate); | ||
77 | int ccu_mux_helper_determine_rate(struct ccu_common *common, | ||
78 | struct ccu_mux_internal *cm, | ||
79 | struct clk_rate_request *req, | ||
80 | unsigned long (*round)(struct ccu_mux_internal *, | ||
81 | unsigned long, | ||
82 | unsigned long, | ||
83 | void *), | ||
84 | void *data); | ||
85 | u8 ccu_mux_helper_get_parent(struct ccu_common *common, | ||
86 | struct ccu_mux_internal *cm); | ||
87 | int ccu_mux_helper_set_parent(struct ccu_common *common, | ||
88 | struct ccu_mux_internal *cm, | ||
89 | u8 index); | ||
90 | |||
91 | #endif /* _CCU_MUX_H_ */ | ||
diff --git a/drivers/clk/sunxi-ng/ccu_nk.c b/drivers/clk/sunxi-ng/ccu_nk.c new file mode 100644 index 000000000000..4470ffc8cf0d --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_nk.c | |||
@@ -0,0 +1,147 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 Maxime Ripard | ||
3 | * Maxime Ripard <maxime.ripard@free-electrons.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License as | ||
7 | * published by the Free Software Foundation; either version 2 of | ||
8 | * the License, or (at your option) any later version. | ||
9 | */ | ||
10 | |||
11 | #include <linux/clk-provider.h> | ||
12 | #include <linux/rational.h> | ||
13 | |||
14 | #include "ccu_gate.h" | ||
15 | #include "ccu_nk.h" | ||
16 | |||
17 | void ccu_nk_find_best(unsigned long parent, unsigned long rate, | ||
18 | unsigned int max_n, unsigned int max_k, | ||
19 | unsigned int *n, unsigned int *k) | ||
20 | { | ||
21 | unsigned long best_rate = 0; | ||
22 | unsigned int best_k = 0, best_n = 0; | ||
23 | unsigned int _k, _n; | ||
24 | |||
25 | for (_k = 1; _k <= max_k; _k++) { | ||
26 | for (_n = 1; _n <= max_n; _n++) { | ||
27 | unsigned long tmp_rate = parent * _n * _k; | ||
28 | |||
29 | if (tmp_rate > rate) | ||
30 | continue; | ||
31 | |||
32 | if ((rate - tmp_rate) < (rate - best_rate)) { | ||
33 | best_rate = tmp_rate; | ||
34 | best_k = _k; | ||
35 | best_n = _n; | ||
36 | } | ||
37 | } | ||
38 | } | ||
39 | |||
40 | *k = best_k; | ||
41 | *n = best_n; | ||
42 | } | ||
43 | |||
44 | static void ccu_nk_disable(struct clk_hw *hw) | ||
45 | { | ||
46 | struct ccu_nk *nk = hw_to_ccu_nk(hw); | ||
47 | |||
48 | return ccu_gate_helper_disable(&nk->common, nk->enable); | ||
49 | } | ||
50 | |||
51 | static int ccu_nk_enable(struct clk_hw *hw) | ||
52 | { | ||
53 | struct ccu_nk *nk = hw_to_ccu_nk(hw); | ||
54 | |||
55 | return ccu_gate_helper_enable(&nk->common, nk->enable); | ||
56 | } | ||
57 | |||
58 | static int ccu_nk_is_enabled(struct clk_hw *hw) | ||
59 | { | ||
60 | struct ccu_nk *nk = hw_to_ccu_nk(hw); | ||
61 | |||
62 | return ccu_gate_helper_is_enabled(&nk->common, nk->enable); | ||
63 | } | ||
64 | |||
65 | static unsigned long ccu_nk_recalc_rate(struct clk_hw *hw, | ||
66 | unsigned long parent_rate) | ||
67 | { | ||
68 | struct ccu_nk *nk = hw_to_ccu_nk(hw); | ||
69 | unsigned long rate, n, k; | ||
70 | u32 reg; | ||
71 | |||
72 | reg = readl(nk->common.base + nk->common.reg); | ||
73 | |||
74 | n = reg >> nk->n.shift; | ||
75 | n &= (1 << nk->n.width) - 1; | ||
76 | |||
77 | k = reg >> nk->k.shift; | ||
78 | k &= (1 << nk->k.width) - 1; | ||
79 | |||
80 | rate = parent_rate * (n + 1) * (k + 1); | ||
81 | |||
82 | if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV) | ||
83 | rate /= nk->fixed_post_div; | ||
84 | |||
85 | return rate; | ||
86 | } | ||
87 | |||
88 | static long ccu_nk_round_rate(struct clk_hw *hw, unsigned long rate, | ||
89 | unsigned long *parent_rate) | ||
90 | { | ||
91 | struct ccu_nk *nk = hw_to_ccu_nk(hw); | ||
92 | unsigned int n, k; | ||
93 | |||
94 | if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV) | ||
95 | rate *= nk->fixed_post_div; | ||
96 | |||
97 | ccu_nk_find_best(*parent_rate, rate, | ||
98 | 1 << nk->n.width, 1 << nk->k.width, | ||
99 | &n, &k); | ||
100 | |||
101 | rate = *parent_rate * n * k; | ||
102 | if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV) | ||
103 | rate = rate / nk->fixed_post_div; | ||
104 | |||
105 | return rate; | ||
106 | } | ||
107 | |||
108 | static int ccu_nk_set_rate(struct clk_hw *hw, unsigned long rate, | ||
109 | unsigned long parent_rate) | ||
110 | { | ||
111 | struct ccu_nk *nk = hw_to_ccu_nk(hw); | ||
112 | unsigned long flags; | ||
113 | unsigned int n, k; | ||
114 | u32 reg; | ||
115 | |||
116 | if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV) | ||
117 | rate = rate * nk->fixed_post_div; | ||
118 | |||
119 | ccu_nk_find_best(parent_rate, rate, | ||
120 | 1 << nk->n.width, 1 << nk->k.width, | ||
121 | &n, &k); | ||
122 | |||
123 | spin_lock_irqsave(nk->common.lock, flags); | ||
124 | |||
125 | reg = readl(nk->common.base + nk->common.reg); | ||
126 | reg &= ~GENMASK(nk->n.width + nk->n.shift - 1, nk->n.shift); | ||
127 | reg &= ~GENMASK(nk->k.width + nk->k.shift - 1, nk->k.shift); | ||
128 | |||
129 | writel(reg | ((k - 1) << nk->k.shift) | ((n - 1) << nk->n.shift), | ||
130 | nk->common.base + nk->common.reg); | ||
131 | |||
132 | spin_unlock_irqrestore(nk->common.lock, flags); | ||
133 | |||
134 | ccu_helper_wait_for_lock(&nk->common, nk->lock); | ||
135 | |||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | const struct clk_ops ccu_nk_ops = { | ||
140 | .disable = ccu_nk_disable, | ||
141 | .enable = ccu_nk_enable, | ||
142 | .is_enabled = ccu_nk_is_enabled, | ||
143 | |||
144 | .recalc_rate = ccu_nk_recalc_rate, | ||
145 | .round_rate = ccu_nk_round_rate, | ||
146 | .set_rate = ccu_nk_set_rate, | ||
147 | }; | ||
diff --git a/drivers/clk/sunxi-ng/ccu_nk.h b/drivers/clk/sunxi-ng/ccu_nk.h new file mode 100644 index 000000000000..4b52da0c29fe --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_nk.h | |||
@@ -0,0 +1,71 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016 Maxime Ripard. All rights reserved. | ||
3 | * | ||
4 | * This software is licensed under the terms of the GNU General Public | ||
5 | * License version 2, as published by the Free Software Foundation, and | ||
6 | * may be copied, distributed, and modified under those terms. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #ifndef _CCU_NK_H_ | ||
15 | #define _CCU_NK_H_ | ||
16 | |||
17 | #include <linux/clk-provider.h> | ||
18 | |||
19 | #include "ccu_common.h" | ||
20 | #include "ccu_div.h" | ||
21 | #include "ccu_mult.h" | ||
22 | |||
23 | /* | ||
24 | * struct ccu_nk - Definition of an N-K clock | ||
25 | * | ||
26 | * Clocks based on the formula parent * N * K | ||
27 | */ | ||
28 | struct ccu_nk { | ||
29 | u16 reg; | ||
30 | u32 enable; | ||
31 | u32 lock; | ||
32 | |||
33 | struct _ccu_mult n; | ||
34 | struct _ccu_mult k; | ||
35 | |||
36 | unsigned int fixed_post_div; | ||
37 | |||
38 | struct ccu_common common; | ||
39 | }; | ||
40 | |||
41 | #define SUNXI_CCU_NK_WITH_GATE_LOCK_POSTDIV(_struct, _name, _parent, _reg, \ | ||
42 | _nshift, _nwidth, \ | ||
43 | _kshift, _kwidth, \ | ||
44 | _gate, _lock, _postdiv, \ | ||
45 | _flags) \ | ||
46 | struct ccu_nk _struct = { \ | ||
47 | .enable = _gate, \ | ||
48 | .lock = _lock, \ | ||
49 | .k = _SUNXI_CCU_MULT(_kshift, _kwidth), \ | ||
50 | .n = _SUNXI_CCU_MULT(_nshift, _nwidth), \ | ||
51 | .fixed_post_div = _postdiv, \ | ||
52 | .common = { \ | ||
53 | .reg = _reg, \ | ||
54 | .features = CCU_FEATURE_FIXED_POSTDIV, \ | ||
55 | .hw.init = CLK_HW_INIT(_name, \ | ||
56 | _parent, \ | ||
57 | &ccu_nk_ops, \ | ||
58 | _flags), \ | ||
59 | }, \ | ||
60 | } | ||
61 | |||
62 | static inline struct ccu_nk *hw_to_ccu_nk(struct clk_hw *hw) | ||
63 | { | ||
64 | struct ccu_common *common = hw_to_ccu_common(hw); | ||
65 | |||
66 | return container_of(common, struct ccu_nk, common); | ||
67 | } | ||
68 | |||
69 | extern const struct clk_ops ccu_nk_ops; | ||
70 | |||
71 | #endif /* _CCU_NK_H_ */ | ||
diff --git a/drivers/clk/sunxi-ng/ccu_nkm.c b/drivers/clk/sunxi-ng/ccu_nkm.c new file mode 100644 index 000000000000..2071822b1e9c --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_nkm.c | |||
@@ -0,0 +1,153 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 Maxime Ripard | ||
3 | * Maxime Ripard <maxime.ripard@free-electrons.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License as | ||
7 | * published by the Free Software Foundation; either version 2 of | ||
8 | * the License, or (at your option) any later version. | ||
9 | */ | ||
10 | |||
11 | #include <linux/clk-provider.h> | ||
12 | #include <linux/rational.h> | ||
13 | |||
14 | #include "ccu_gate.h" | ||
15 | #include "ccu_nkm.h" | ||
16 | |||
17 | struct _ccu_nkm { | ||
18 | unsigned long n, max_n; | ||
19 | unsigned long k, max_k; | ||
20 | unsigned long m, max_m; | ||
21 | }; | ||
22 | |||
23 | static void ccu_nkm_find_best(unsigned long parent, unsigned long rate, | ||
24 | struct _ccu_nkm *nkm) | ||
25 | { | ||
26 | unsigned long best_rate = 0; | ||
27 | unsigned long best_n = 0, best_k = 0, best_m = 0; | ||
28 | unsigned long _n, _k, _m; | ||
29 | |||
30 | for (_k = 1; _k <= nkm->max_k; _k++) { | ||
31 | unsigned long tmp_rate; | ||
32 | |||
33 | rational_best_approximation(rate / _k, parent, | ||
34 | nkm->max_n, nkm->max_m, &_n, &_m); | ||
35 | |||
36 | tmp_rate = parent * _n * _k / _m; | ||
37 | |||
38 | if (tmp_rate > rate) | ||
39 | continue; | ||
40 | |||
41 | if ((rate - tmp_rate) < (rate - best_rate)) { | ||
42 | best_rate = tmp_rate; | ||
43 | best_n = _n; | ||
44 | best_k = _k; | ||
45 | best_m = _m; | ||
46 | } | ||
47 | } | ||
48 | |||
49 | nkm->n = best_n; | ||
50 | nkm->k = best_k; | ||
51 | nkm->m = best_m; | ||
52 | } | ||
53 | |||
54 | static void ccu_nkm_disable(struct clk_hw *hw) | ||
55 | { | ||
56 | struct ccu_nkm *nkm = hw_to_ccu_nkm(hw); | ||
57 | |||
58 | return ccu_gate_helper_disable(&nkm->common, nkm->enable); | ||
59 | } | ||
60 | |||
61 | static int ccu_nkm_enable(struct clk_hw *hw) | ||
62 | { | ||
63 | struct ccu_nkm *nkm = hw_to_ccu_nkm(hw); | ||
64 | |||
65 | return ccu_gate_helper_enable(&nkm->common, nkm->enable); | ||
66 | } | ||
67 | |||
68 | static int ccu_nkm_is_enabled(struct clk_hw *hw) | ||
69 | { | ||
70 | struct ccu_nkm *nkm = hw_to_ccu_nkm(hw); | ||
71 | |||
72 | return ccu_gate_helper_is_enabled(&nkm->common, nkm->enable); | ||
73 | } | ||
74 | |||
75 | static unsigned long ccu_nkm_recalc_rate(struct clk_hw *hw, | ||
76 | unsigned long parent_rate) | ||
77 | { | ||
78 | struct ccu_nkm *nkm = hw_to_ccu_nkm(hw); | ||
79 | unsigned long n, m, k; | ||
80 | u32 reg; | ||
81 | |||
82 | reg = readl(nkm->common.base + nkm->common.reg); | ||
83 | |||
84 | n = reg >> nkm->n.shift; | ||
85 | n &= (1 << nkm->n.width) - 1; | ||
86 | |||
87 | k = reg >> nkm->k.shift; | ||
88 | k &= (1 << nkm->k.width) - 1; | ||
89 | |||
90 | m = reg >> nkm->m.shift; | ||
91 | m &= (1 << nkm->m.width) - 1; | ||
92 | |||
93 | return parent_rate * (n + 1) * (k + 1) / (m + 1); | ||
94 | } | ||
95 | |||
96 | static long ccu_nkm_round_rate(struct clk_hw *hw, unsigned long rate, | ||
97 | unsigned long *parent_rate) | ||
98 | { | ||
99 | struct ccu_nkm *nkm = hw_to_ccu_nkm(hw); | ||
100 | struct _ccu_nkm _nkm; | ||
101 | |||
102 | _nkm.max_n = 1 << nkm->n.width; | ||
103 | _nkm.max_k = 1 << nkm->k.width; | ||
104 | _nkm.max_m = 1 << nkm->m.width; | ||
105 | |||
106 | ccu_nkm_find_best(*parent_rate, rate, &_nkm); | ||
107 | |||
108 | return *parent_rate * _nkm.n * _nkm.k / _nkm.m; | ||
109 | } | ||
110 | |||
111 | static int ccu_nkm_set_rate(struct clk_hw *hw, unsigned long rate, | ||
112 | unsigned long parent_rate) | ||
113 | { | ||
114 | struct ccu_nkm *nkm = hw_to_ccu_nkm(hw); | ||
115 | struct _ccu_nkm _nkm; | ||
116 | unsigned long flags; | ||
117 | u32 reg; | ||
118 | |||
119 | _nkm.max_n = 1 << nkm->n.width; | ||
120 | _nkm.max_k = 1 << nkm->k.width; | ||
121 | _nkm.max_m = 1 << nkm->m.width; | ||
122 | |||
123 | ccu_nkm_find_best(parent_rate, rate, &_nkm); | ||
124 | |||
125 | spin_lock_irqsave(nkm->common.lock, flags); | ||
126 | |||
127 | reg = readl(nkm->common.base + nkm->common.reg); | ||
128 | reg &= ~GENMASK(nkm->n.width + nkm->n.shift - 1, nkm->n.shift); | ||
129 | reg &= ~GENMASK(nkm->k.width + nkm->k.shift - 1, nkm->k.shift); | ||
130 | reg &= ~GENMASK(nkm->m.width + nkm->m.shift - 1, nkm->m.shift); | ||
131 | |||
132 | reg |= (_nkm.n - 1) << nkm->n.shift; | ||
133 | reg |= (_nkm.k - 1) << nkm->k.shift; | ||
134 | reg |= (_nkm.m - 1) << nkm->m.shift; | ||
135 | |||
136 | writel(reg, nkm->common.base + nkm->common.reg); | ||
137 | |||
138 | spin_unlock_irqrestore(nkm->common.lock, flags); | ||
139 | |||
140 | ccu_helper_wait_for_lock(&nkm->common, nkm->lock); | ||
141 | |||
142 | return 0; | ||
143 | } | ||
144 | |||
145 | const struct clk_ops ccu_nkm_ops = { | ||
146 | .disable = ccu_nkm_disable, | ||
147 | .enable = ccu_nkm_enable, | ||
148 | .is_enabled = ccu_nkm_is_enabled, | ||
149 | |||
150 | .recalc_rate = ccu_nkm_recalc_rate, | ||
151 | .round_rate = ccu_nkm_round_rate, | ||
152 | .set_rate = ccu_nkm_set_rate, | ||
153 | }; | ||
diff --git a/drivers/clk/sunxi-ng/ccu_nkm.h b/drivers/clk/sunxi-ng/ccu_nkm.h new file mode 100644 index 000000000000..1936ac1c6b37 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_nkm.h | |||
@@ -0,0 +1,68 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016 Maxime Ripard. All rights reserved. | ||
3 | * | ||
4 | * This software is licensed under the terms of the GNU General Public | ||
5 | * License version 2, as published by the Free Software Foundation, and | ||
6 | * may be copied, distributed, and modified under those terms. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #ifndef _CCU_NKM_H_ | ||
15 | #define _CCU_NKM_H_ | ||
16 | |||
17 | #include <linux/clk-provider.h> | ||
18 | |||
19 | #include "ccu_common.h" | ||
20 | #include "ccu_div.h" | ||
21 | #include "ccu_mult.h" | ||
22 | |||
23 | /* | ||
24 | * struct ccu_nkm - Definition of an N-K-M clock | ||
25 | * | ||
26 | * Clocks based on the formula parent * N * K / M | ||
27 | */ | ||
28 | struct ccu_nkm { | ||
29 | u32 enable; | ||
30 | u32 lock; | ||
31 | |||
32 | struct _ccu_mult n; | ||
33 | struct _ccu_mult k; | ||
34 | struct _ccu_div m; | ||
35 | |||
36 | struct ccu_common common; | ||
37 | }; | ||
38 | |||
39 | #define SUNXI_CCU_NKM_WITH_GATE_LOCK(_struct, _name, _parent, _reg, \ | ||
40 | _nshift, _nwidth, \ | ||
41 | _kshift, _kwidth, \ | ||
42 | _mshift, _mwidth, \ | ||
43 | _gate, _lock, _flags) \ | ||
44 | struct ccu_nkm _struct = { \ | ||
45 | .enable = _gate, \ | ||
46 | .lock = _lock, \ | ||
47 | .k = _SUNXI_CCU_MULT(_kshift, _kwidth), \ | ||
48 | .n = _SUNXI_CCU_MULT(_nshift, _nwidth), \ | ||
49 | .m = _SUNXI_CCU_DIV(_mshift, _mwidth), \ | ||
50 | .common = { \ | ||
51 | .reg = _reg, \ | ||
52 | .hw.init = CLK_HW_INIT(_name, \ | ||
53 | _parent, \ | ||
54 | &ccu_nkm_ops, \ | ||
55 | _flags), \ | ||
56 | }, \ | ||
57 | } | ||
58 | |||
59 | static inline struct ccu_nkm *hw_to_ccu_nkm(struct clk_hw *hw) | ||
60 | { | ||
61 | struct ccu_common *common = hw_to_ccu_common(hw); | ||
62 | |||
63 | return container_of(common, struct ccu_nkm, common); | ||
64 | } | ||
65 | |||
66 | extern const struct clk_ops ccu_nkm_ops; | ||
67 | |||
68 | #endif /* _CCU_NKM_H_ */ | ||
diff --git a/drivers/clk/sunxi-ng/ccu_nkmp.c b/drivers/clk/sunxi-ng/ccu_nkmp.c new file mode 100644 index 000000000000..9f2b98e19dc9 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_nkmp.c | |||
@@ -0,0 +1,167 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 Maxime Ripard | ||
3 | * Maxime Ripard <maxime.ripard@free-electrons.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License as | ||
7 | * published by the Free Software Foundation; either version 2 of | ||
8 | * the License, or (at your option) any later version. | ||
9 | */ | ||
10 | |||
11 | #include <linux/clk-provider.h> | ||
12 | #include <linux/rational.h> | ||
13 | |||
14 | #include "ccu_gate.h" | ||
15 | #include "ccu_nkmp.h" | ||
16 | |||
17 | struct _ccu_nkmp { | ||
18 | unsigned long n, max_n; | ||
19 | unsigned long k, max_k; | ||
20 | unsigned long m, max_m; | ||
21 | unsigned long p, max_p; | ||
22 | }; | ||
23 | |||
24 | static void ccu_nkmp_find_best(unsigned long parent, unsigned long rate, | ||
25 | struct _ccu_nkmp *nkmp) | ||
26 | { | ||
27 | unsigned long best_rate = 0; | ||
28 | unsigned long best_n = 0, best_k = 0, best_m = 0, best_p = 0; | ||
29 | unsigned long _n, _k, _m, _p; | ||
30 | |||
31 | for (_k = 1; _k <= nkmp->max_k; _k++) { | ||
32 | for (_p = 0; _p <= nkmp->max_p; _p++) { | ||
33 | unsigned long tmp_rate; | ||
34 | |||
35 | rational_best_approximation(rate / _k, parent >> _p, | ||
36 | nkmp->max_n, nkmp->max_m, | ||
37 | &_n, &_m); | ||
38 | |||
39 | tmp_rate = (parent * _n * _k >> _p) / _m; | ||
40 | |||
41 | if (tmp_rate > rate) | ||
42 | continue; | ||
43 | |||
44 | if ((rate - tmp_rate) < (rate - best_rate)) { | ||
45 | best_rate = tmp_rate; | ||
46 | best_n = _n; | ||
47 | best_k = _k; | ||
48 | best_m = _m; | ||
49 | best_p = _p; | ||
50 | } | ||
51 | } | ||
52 | } | ||
53 | |||
54 | nkmp->n = best_n; | ||
55 | nkmp->k = best_k; | ||
56 | nkmp->m = best_m; | ||
57 | nkmp->p = best_p; | ||
58 | } | ||
59 | |||
60 | static void ccu_nkmp_disable(struct clk_hw *hw) | ||
61 | { | ||
62 | struct ccu_nkmp *nkmp = hw_to_ccu_nkmp(hw); | ||
63 | |||
64 | return ccu_gate_helper_disable(&nkmp->common, nkmp->enable); | ||
65 | } | ||
66 | |||
67 | static int ccu_nkmp_enable(struct clk_hw *hw) | ||
68 | { | ||
69 | struct ccu_nkmp *nkmp = hw_to_ccu_nkmp(hw); | ||
70 | |||
71 | return ccu_gate_helper_enable(&nkmp->common, nkmp->enable); | ||
72 | } | ||
73 | |||
74 | static int ccu_nkmp_is_enabled(struct clk_hw *hw) | ||
75 | { | ||
76 | struct ccu_nkmp *nkmp = hw_to_ccu_nkmp(hw); | ||
77 | |||
78 | return ccu_gate_helper_is_enabled(&nkmp->common, nkmp->enable); | ||
79 | } | ||
80 | |||
81 | static unsigned long ccu_nkmp_recalc_rate(struct clk_hw *hw, | ||
82 | unsigned long parent_rate) | ||
83 | { | ||
84 | struct ccu_nkmp *nkmp = hw_to_ccu_nkmp(hw); | ||
85 | unsigned long n, m, k, p; | ||
86 | u32 reg; | ||
87 | |||
88 | reg = readl(nkmp->common.base + nkmp->common.reg); | ||
89 | |||
90 | n = reg >> nkmp->n.shift; | ||
91 | n &= (1 << nkmp->n.width) - 1; | ||
92 | |||
93 | k = reg >> nkmp->k.shift; | ||
94 | k &= (1 << nkmp->k.width) - 1; | ||
95 | |||
96 | m = reg >> nkmp->m.shift; | ||
97 | m &= (1 << nkmp->m.width) - 1; | ||
98 | |||
99 | p = reg >> nkmp->p.shift; | ||
100 | p &= (1 << nkmp->p.width) - 1; | ||
101 | |||
102 | return (parent_rate * (n + 1) * (k + 1) >> p) / (m + 1); | ||
103 | } | ||
104 | |||
105 | static long ccu_nkmp_round_rate(struct clk_hw *hw, unsigned long rate, | ||
106 | unsigned long *parent_rate) | ||
107 | { | ||
108 | struct ccu_nkmp *nkmp = hw_to_ccu_nkmp(hw); | ||
109 | struct _ccu_nkmp _nkmp; | ||
110 | |||
111 | _nkmp.max_n = 1 << nkmp->n.width; | ||
112 | _nkmp.max_k = 1 << nkmp->k.width; | ||
113 | _nkmp.max_m = 1 << nkmp->m.width; | ||
114 | _nkmp.max_p = (1 << nkmp->p.width) - 1; | ||
115 | |||
116 | ccu_nkmp_find_best(*parent_rate, rate, | ||
117 | &_nkmp); | ||
118 | |||
119 | return (*parent_rate * _nkmp.n * _nkmp.k >> _nkmp.p) / _nkmp.m; | ||
120 | } | ||
121 | |||
122 | static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate, | ||
123 | unsigned long parent_rate) | ||
124 | { | ||
125 | struct ccu_nkmp *nkmp = hw_to_ccu_nkmp(hw); | ||
126 | struct _ccu_nkmp _nkmp; | ||
127 | unsigned long flags; | ||
128 | u32 reg; | ||
129 | |||
130 | _nkmp.max_n = 1 << nkmp->n.width; | ||
131 | _nkmp.max_k = 1 << nkmp->k.width; | ||
132 | _nkmp.max_m = 1 << nkmp->m.width; | ||
133 | _nkmp.max_p = (1 << nkmp->p.width) - 1; | ||
134 | |||
135 | ccu_nkmp_find_best(parent_rate, rate, &_nkmp); | ||
136 | |||
137 | spin_lock_irqsave(nkmp->common.lock, flags); | ||
138 | |||
139 | reg = readl(nkmp->common.base + nkmp->common.reg); | ||
140 | reg &= ~GENMASK(nkmp->n.width + nkmp->n.shift - 1, nkmp->n.shift); | ||
141 | reg &= ~GENMASK(nkmp->k.width + nkmp->k.shift - 1, nkmp->k.shift); | ||
142 | reg &= ~GENMASK(nkmp->m.width + nkmp->m.shift - 1, nkmp->m.shift); | ||
143 | reg &= ~GENMASK(nkmp->p.width + nkmp->p.shift - 1, nkmp->p.shift); | ||
144 | |||
145 | reg |= (_nkmp.n - 1) << nkmp->n.shift; | ||
146 | reg |= (_nkmp.k - 1) << nkmp->k.shift; | ||
147 | reg |= (_nkmp.m - 1) << nkmp->m.shift; | ||
148 | reg |= _nkmp.p << nkmp->p.shift; | ||
149 | |||
150 | writel(reg, nkmp->common.base + nkmp->common.reg); | ||
151 | |||
152 | spin_unlock_irqrestore(nkmp->common.lock, flags); | ||
153 | |||
154 | ccu_helper_wait_for_lock(&nkmp->common, nkmp->lock); | ||
155 | |||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | const struct clk_ops ccu_nkmp_ops = { | ||
160 | .disable = ccu_nkmp_disable, | ||
161 | .enable = ccu_nkmp_enable, | ||
162 | .is_enabled = ccu_nkmp_is_enabled, | ||
163 | |||
164 | .recalc_rate = ccu_nkmp_recalc_rate, | ||
165 | .round_rate = ccu_nkmp_round_rate, | ||
166 | .set_rate = ccu_nkmp_set_rate, | ||
167 | }; | ||
diff --git a/drivers/clk/sunxi-ng/ccu_nkmp.h b/drivers/clk/sunxi-ng/ccu_nkmp.h new file mode 100644 index 000000000000..5adb0c92a614 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_nkmp.h | |||
@@ -0,0 +1,71 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016 Maxime Ripard. All rights reserved. | ||
3 | * | ||
4 | * This software is licensed under the terms of the GNU General Public | ||
5 | * License version 2, as published by the Free Software Foundation, and | ||
6 | * may be copied, distributed, and modified under those terms. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #ifndef _CCU_NKMP_H_ | ||
15 | #define _CCU_NKMP_H_ | ||
16 | |||
17 | #include <linux/clk-provider.h> | ||
18 | |||
19 | #include "ccu_common.h" | ||
20 | #include "ccu_div.h" | ||
21 | #include "ccu_mult.h" | ||
22 | |||
23 | /* | ||
24 | * struct ccu_nkmp - Definition of an N-K-M-P clock | ||
25 | * | ||
26 | * Clocks based on the formula parent * N * K >> P / M | ||
27 | */ | ||
28 | struct ccu_nkmp { | ||
29 | u32 enable; | ||
30 | u32 lock; | ||
31 | |||
32 | struct _ccu_mult n; | ||
33 | struct _ccu_mult k; | ||
34 | struct _ccu_div m; | ||
35 | struct _ccu_div p; | ||
36 | |||
37 | struct ccu_common common; | ||
38 | }; | ||
39 | |||
40 | #define SUNXI_CCU_NKMP_WITH_GATE_LOCK(_struct, _name, _parent, _reg, \ | ||
41 | _nshift, _nwidth, \ | ||
42 | _kshift, _kwidth, \ | ||
43 | _mshift, _mwidth, \ | ||
44 | _pshift, _pwidth, \ | ||
45 | _gate, _lock, _flags) \ | ||
46 | struct ccu_nkmp _struct = { \ | ||
47 | .enable = _gate, \ | ||
48 | .lock = _lock, \ | ||
49 | .n = _SUNXI_CCU_MULT(_nshift, _nwidth), \ | ||
50 | .k = _SUNXI_CCU_MULT(_kshift, _kwidth), \ | ||
51 | .m = _SUNXI_CCU_DIV(_mshift, _mwidth), \ | ||
52 | .p = _SUNXI_CCU_DIV(_pshift, _pwidth), \ | ||
53 | .common = { \ | ||
54 | .reg = _reg, \ | ||
55 | .hw.init = CLK_HW_INIT(_name, \ | ||
56 | _parent, \ | ||
57 | &ccu_nkmp_ops, \ | ||
58 | _flags), \ | ||
59 | }, \ | ||
60 | } | ||
61 | |||
62 | static inline struct ccu_nkmp *hw_to_ccu_nkmp(struct clk_hw *hw) | ||
63 | { | ||
64 | struct ccu_common *common = hw_to_ccu_common(hw); | ||
65 | |||
66 | return container_of(common, struct ccu_nkmp, common); | ||
67 | } | ||
68 | |||
69 | extern const struct clk_ops ccu_nkmp_ops; | ||
70 | |||
71 | #endif /* _CCU_NKMP_H_ */ | ||
diff --git a/drivers/clk/sunxi-ng/ccu_nm.c b/drivers/clk/sunxi-ng/ccu_nm.c new file mode 100644 index 000000000000..e35ddd8eec8b --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_nm.c | |||
@@ -0,0 +1,114 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 Maxime Ripard | ||
3 | * Maxime Ripard <maxime.ripard@free-electrons.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License as | ||
7 | * published by the Free Software Foundation; either version 2 of | ||
8 | * the License, or (at your option) any later version. | ||
9 | */ | ||
10 | |||
11 | #include <linux/clk-provider.h> | ||
12 | #include <linux/rational.h> | ||
13 | |||
14 | #include "ccu_frac.h" | ||
15 | #include "ccu_gate.h" | ||
16 | #include "ccu_nm.h" | ||
17 | |||
18 | static void ccu_nm_disable(struct clk_hw *hw) | ||
19 | { | ||
20 | struct ccu_nm *nm = hw_to_ccu_nm(hw); | ||
21 | |||
22 | return ccu_gate_helper_disable(&nm->common, nm->enable); | ||
23 | } | ||
24 | |||
25 | static int ccu_nm_enable(struct clk_hw *hw) | ||
26 | { | ||
27 | struct ccu_nm *nm = hw_to_ccu_nm(hw); | ||
28 | |||
29 | return ccu_gate_helper_enable(&nm->common, nm->enable); | ||
30 | } | ||
31 | |||
32 | static int ccu_nm_is_enabled(struct clk_hw *hw) | ||
33 | { | ||
34 | struct ccu_nm *nm = hw_to_ccu_nm(hw); | ||
35 | |||
36 | return ccu_gate_helper_is_enabled(&nm->common, nm->enable); | ||
37 | } | ||
38 | |||
39 | static unsigned long ccu_nm_recalc_rate(struct clk_hw *hw, | ||
40 | unsigned long parent_rate) | ||
41 | { | ||
42 | struct ccu_nm *nm = hw_to_ccu_nm(hw); | ||
43 | unsigned long n, m; | ||
44 | u32 reg; | ||
45 | |||
46 | if (ccu_frac_helper_is_enabled(&nm->common, &nm->frac)) | ||
47 | return ccu_frac_helper_read_rate(&nm->common, &nm->frac); | ||
48 | |||
49 | reg = readl(nm->common.base + nm->common.reg); | ||
50 | |||
51 | n = reg >> nm->n.shift; | ||
52 | n &= (1 << nm->n.width) - 1; | ||
53 | |||
54 | m = reg >> nm->m.shift; | ||
55 | m &= (1 << nm->m.width) - 1; | ||
56 | |||
57 | return parent_rate * (n + 1) / (m + 1); | ||
58 | } | ||
59 | |||
60 | static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate, | ||
61 | unsigned long *parent_rate) | ||
62 | { | ||
63 | struct ccu_nm *nm = hw_to_ccu_nm(hw); | ||
64 | unsigned long n, m; | ||
65 | |||
66 | rational_best_approximation(rate, *parent_rate, | ||
67 | 1 << nm->n.width, 1 << nm->m.width, | ||
68 | &n, &m); | ||
69 | |||
70 | return *parent_rate * n / m; | ||
71 | } | ||
72 | |||
73 | static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate, | ||
74 | unsigned long parent_rate) | ||
75 | { | ||
76 | struct ccu_nm *nm = hw_to_ccu_nm(hw); | ||
77 | unsigned long flags; | ||
78 | unsigned long n, m; | ||
79 | u32 reg; | ||
80 | |||
81 | if (ccu_frac_helper_has_rate(&nm->common, &nm->frac, rate)) | ||
82 | return ccu_frac_helper_set_rate(&nm->common, &nm->frac, rate); | ||
83 | else | ||
84 | ccu_frac_helper_disable(&nm->common, &nm->frac); | ||
85 | |||
86 | rational_best_approximation(rate, parent_rate, | ||
87 | 1 << nm->n.width, 1 << nm->m.width, | ||
88 | &n, &m); | ||
89 | |||
90 | spin_lock_irqsave(nm->common.lock, flags); | ||
91 | |||
92 | reg = readl(nm->common.base + nm->common.reg); | ||
93 | reg &= ~GENMASK(nm->n.width + nm->n.shift - 1, nm->n.shift); | ||
94 | reg &= ~GENMASK(nm->m.width + nm->m.shift - 1, nm->m.shift); | ||
95 | |||
96 | writel(reg | ((m - 1) << nm->m.shift) | ((n - 1) << nm->n.shift), | ||
97 | nm->common.base + nm->common.reg); | ||
98 | |||
99 | spin_unlock_irqrestore(nm->common.lock, flags); | ||
100 | |||
101 | ccu_helper_wait_for_lock(&nm->common, nm->lock); | ||
102 | |||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | const struct clk_ops ccu_nm_ops = { | ||
107 | .disable = ccu_nm_disable, | ||
108 | .enable = ccu_nm_enable, | ||
109 | .is_enabled = ccu_nm_is_enabled, | ||
110 | |||
111 | .recalc_rate = ccu_nm_recalc_rate, | ||
112 | .round_rate = ccu_nm_round_rate, | ||
113 | .set_rate = ccu_nm_set_rate, | ||
114 | }; | ||
diff --git a/drivers/clk/sunxi-ng/ccu_nm.h b/drivers/clk/sunxi-ng/ccu_nm.h new file mode 100644 index 000000000000..0b7bcd33a2df --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_nm.h | |||
@@ -0,0 +1,91 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016 Maxime Ripard. All rights reserved. | ||
3 | * | ||
4 | * This software is licensed under the terms of the GNU General Public | ||
5 | * License version 2, as published by the Free Software Foundation, and | ||
6 | * may be copied, distributed, and modified under those terms. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #ifndef _CCU_NM_H_ | ||
15 | #define _CCU_NM_H_ | ||
16 | |||
17 | #include <linux/clk-provider.h> | ||
18 | |||
19 | #include "ccu_common.h" | ||
20 | #include "ccu_div.h" | ||
21 | #include "ccu_frac.h" | ||
22 | #include "ccu_mult.h" | ||
23 | |||
24 | /* | ||
25 | * struct ccu_nm - Definition of an N-M clock | ||
26 | * | ||
27 | * Clocks based on the formula parent * N / M | ||
28 | */ | ||
29 | struct ccu_nm { | ||
30 | u32 enable; | ||
31 | u32 lock; | ||
32 | |||
33 | struct _ccu_mult n; | ||
34 | struct _ccu_div m; | ||
35 | struct _ccu_frac frac; | ||
36 | |||
37 | struct ccu_common common; | ||
38 | }; | ||
39 | |||
40 | #define SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(_struct, _name, _parent, _reg, \ | ||
41 | _nshift, _nwidth, \ | ||
42 | _mshift, _mwidth, \ | ||
43 | _frac_en, _frac_sel, \ | ||
44 | _frac_rate_0, _frac_rate_1, \ | ||
45 | _gate, _lock, _flags) \ | ||
46 | struct ccu_nm _struct = { \ | ||
47 | .enable = _gate, \ | ||
48 | .lock = _lock, \ | ||
49 | .n = _SUNXI_CCU_MULT(_nshift, _nwidth), \ | ||
50 | .m = _SUNXI_CCU_DIV(_mshift, _mwidth), \ | ||
51 | .frac = _SUNXI_CCU_FRAC(_frac_en, _frac_sel, \ | ||
52 | _frac_rate_0, \ | ||
53 | _frac_rate_1), \ | ||
54 | .common = { \ | ||
55 | .reg = _reg, \ | ||
56 | .features = CCU_FEATURE_FRACTIONAL, \ | ||
57 | .hw.init = CLK_HW_INIT(_name, \ | ||
58 | _parent, \ | ||
59 | &ccu_nm_ops, \ | ||
60 | _flags), \ | ||
61 | }, \ | ||
62 | } | ||
63 | |||
64 | #define SUNXI_CCU_NM_WITH_GATE_LOCK(_struct, _name, _parent, _reg, \ | ||
65 | _nshift, _nwidth, \ | ||
66 | _mshift, _mwidth, \ | ||
67 | _gate, _lock, _flags) \ | ||
68 | struct ccu_nm _struct = { \ | ||
69 | .enable = _gate, \ | ||
70 | .lock = _lock, \ | ||
71 | .n = _SUNXI_CCU_MULT(_nshift, _nwidth), \ | ||
72 | .m = _SUNXI_CCU_DIV(_mshift, _mwidth), \ | ||
73 | .common = { \ | ||
74 | .reg = _reg, \ | ||
75 | .hw.init = CLK_HW_INIT(_name, \ | ||
76 | _parent, \ | ||
77 | &ccu_nm_ops, \ | ||
78 | _flags), \ | ||
79 | }, \ | ||
80 | } | ||
81 | |||
82 | static inline struct ccu_nm *hw_to_ccu_nm(struct clk_hw *hw) | ||
83 | { | ||
84 | struct ccu_common *common = hw_to_ccu_common(hw); | ||
85 | |||
86 | return container_of(common, struct ccu_nm, common); | ||
87 | } | ||
88 | |||
89 | extern const struct clk_ops ccu_nm_ops; | ||
90 | |||
91 | #endif /* _CCU_NM_H_ */ | ||
diff --git a/drivers/clk/sunxi-ng/ccu_phase.c b/drivers/clk/sunxi-ng/ccu_phase.c new file mode 100644 index 000000000000..400c58ad72fd --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_phase.c | |||
@@ -0,0 +1,126 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 Maxime Ripard | ||
3 | * Maxime Ripard <maxime.ripard@free-electrons.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License as | ||
7 | * published by the Free Software Foundation; either version 2 of | ||
8 | * the License, or (at your option) any later version. | ||
9 | */ | ||
10 | |||
11 | #include <linux/clk-provider.h> | ||
12 | #include <linux/spinlock.h> | ||
13 | |||
14 | #include "ccu_phase.h" | ||
15 | |||
16 | static int ccu_phase_get_phase(struct clk_hw *hw) | ||
17 | { | ||
18 | struct ccu_phase *phase = hw_to_ccu_phase(hw); | ||
19 | struct clk_hw *parent, *grandparent; | ||
20 | unsigned int parent_rate, grandparent_rate; | ||
21 | u16 step, parent_div; | ||
22 | u32 reg; | ||
23 | u8 delay; | ||
24 | |||
25 | reg = readl(phase->common.base + phase->common.reg); | ||
26 | delay = (reg >> phase->shift); | ||
27 | delay &= (1 << phase->width) - 1; | ||
28 | |||
29 | if (!delay) | ||
30 | return 180; | ||
31 | |||
32 | /* Get our parent clock, it's the one that can adjust its rate */ | ||
33 | parent = clk_hw_get_parent(hw); | ||
34 | if (!parent) | ||
35 | return -EINVAL; | ||
36 | |||
37 | /* And its rate */ | ||
38 | parent_rate = clk_hw_get_rate(parent); | ||
39 | if (!parent_rate) | ||
40 | return -EINVAL; | ||
41 | |||
42 | /* Now, get our parent's parent (most likely some PLL) */ | ||
43 | grandparent = clk_hw_get_parent(parent); | ||
44 | if (!grandparent) | ||
45 | return -EINVAL; | ||
46 | |||
47 | /* And its rate */ | ||
48 | grandparent_rate = clk_hw_get_rate(grandparent); | ||
49 | if (!grandparent_rate) | ||
50 | return -EINVAL; | ||
51 | |||
52 | /* Get our parent clock divider */ | ||
53 | parent_div = grandparent_rate / parent_rate; | ||
54 | |||
55 | step = DIV_ROUND_CLOSEST(360, parent_div); | ||
56 | return delay * step; | ||
57 | } | ||
58 | |||
59 | static int ccu_phase_set_phase(struct clk_hw *hw, int degrees) | ||
60 | { | ||
61 | struct ccu_phase *phase = hw_to_ccu_phase(hw); | ||
62 | struct clk_hw *parent, *grandparent; | ||
63 | unsigned int parent_rate, grandparent_rate; | ||
64 | unsigned long flags; | ||
65 | u32 reg; | ||
66 | u8 delay; | ||
67 | |||
68 | /* Get our parent clock, it's the one that can adjust its rate */ | ||
69 | parent = clk_hw_get_parent(hw); | ||
70 | if (!parent) | ||
71 | return -EINVAL; | ||
72 | |||
73 | /* And its rate */ | ||
74 | parent_rate = clk_hw_get_rate(parent); | ||
75 | if (!parent_rate) | ||
76 | return -EINVAL; | ||
77 | |||
78 | /* Now, get our parent's parent (most likely some PLL) */ | ||
79 | grandparent = clk_hw_get_parent(parent); | ||
80 | if (!grandparent) | ||
81 | return -EINVAL; | ||
82 | |||
83 | /* And its rate */ | ||
84 | grandparent_rate = clk_hw_get_rate(grandparent); | ||
85 | if (!grandparent_rate) | ||
86 | return -EINVAL; | ||
87 | |||
88 | if (degrees != 180) { | ||
89 | u16 step, parent_div; | ||
90 | |||
91 | /* Get our parent divider */ | ||
92 | parent_div = grandparent_rate / parent_rate; | ||
93 | |||
94 | /* | ||
95 | * We can only outphase the clocks by multiple of the | ||
96 | * PLL's period. | ||
97 | * | ||
98 | * Since our parent clock is only a divider, and the | ||
99 | * formula to get the outphasing in degrees is deg = | ||
100 | * 360 * delta / period | ||
101 | * | ||
102 | * If we simplify this formula, we can see that the | ||
103 | * only thing that we're concerned about is the number | ||
104 | * of period we want to outphase our clock from, and | ||
105 | * the divider set by our parent clock. | ||
106 | */ | ||
107 | step = DIV_ROUND_CLOSEST(360, parent_div); | ||
108 | delay = DIV_ROUND_CLOSEST(degrees, step); | ||
109 | } else { | ||
110 | delay = 0; | ||
111 | } | ||
112 | |||
113 | spin_lock_irqsave(phase->common.lock, flags); | ||
114 | reg = readl(phase->common.base + phase->common.reg); | ||
115 | reg &= ~GENMASK(phase->width + phase->shift - 1, phase->shift); | ||
116 | writel(reg | (delay << phase->shift), | ||
117 | phase->common.base + phase->common.reg); | ||
118 | spin_unlock_irqrestore(phase->common.lock, flags); | ||
119 | |||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | const struct clk_ops ccu_phase_ops = { | ||
124 | .get_phase = ccu_phase_get_phase, | ||
125 | .set_phase = ccu_phase_set_phase, | ||
126 | }; | ||
diff --git a/drivers/clk/sunxi-ng/ccu_phase.h b/drivers/clk/sunxi-ng/ccu_phase.h new file mode 100644 index 000000000000..75a091a4c565 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_phase.h | |||
@@ -0,0 +1,50 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016 Maxime Ripard. All rights reserved. | ||
3 | * | ||
4 | * This software is licensed under the terms of the GNU General Public | ||
5 | * License version 2, as published by the Free Software Foundation, and | ||
6 | * may be copied, distributed, and modified under those terms. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #ifndef _CCU_PHASE_H_ | ||
15 | #define _CCU_PHASE_H_ | ||
16 | |||
17 | #include <linux/clk-provider.h> | ||
18 | |||
19 | #include "ccu_common.h" | ||
20 | |||
21 | struct ccu_phase { | ||
22 | u8 shift; | ||
23 | u8 width; | ||
24 | |||
25 | struct ccu_common common; | ||
26 | }; | ||
27 | |||
28 | #define SUNXI_CCU_PHASE(_struct, _name, _parent, _reg, _shift, _width, _flags) \ | ||
29 | struct ccu_phase _struct = { \ | ||
30 | .shift = _shift, \ | ||
31 | .width = _width, \ | ||
32 | .common = { \ | ||
33 | .reg = _reg, \ | ||
34 | .hw.init = CLK_HW_INIT(_name, \ | ||
35 | _parent, \ | ||
36 | &ccu_phase_ops, \ | ||
37 | _flags), \ | ||
38 | } \ | ||
39 | } | ||
40 | |||
41 | static inline struct ccu_phase *hw_to_ccu_phase(struct clk_hw *hw) | ||
42 | { | ||
43 | struct ccu_common *common = hw_to_ccu_common(hw); | ||
44 | |||
45 | return container_of(common, struct ccu_phase, common); | ||
46 | } | ||
47 | |||
48 | extern const struct clk_ops ccu_phase_ops; | ||
49 | |||
50 | #endif /* _CCU_PHASE_H_ */ | ||
diff --git a/drivers/clk/sunxi-ng/ccu_reset.c b/drivers/clk/sunxi-ng/ccu_reset.c new file mode 100644 index 000000000000..6c31d48783a7 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_reset.c | |||
@@ -0,0 +1,55 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 Maxime Ripard | ||
3 | * Maxime Ripard <maxime.ripard@free-electrons.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License as | ||
7 | * published by the Free Software Foundation; either version 2 of | ||
8 | * the License, or (at your option) any later version. | ||
9 | */ | ||
10 | |||
11 | #include <linux/io.h> | ||
12 | #include <linux/reset-controller.h> | ||
13 | |||
14 | #include "ccu_reset.h" | ||
15 | |||
16 | static int ccu_reset_assert(struct reset_controller_dev *rcdev, | ||
17 | unsigned long id) | ||
18 | { | ||
19 | struct ccu_reset *ccu = rcdev_to_ccu_reset(rcdev); | ||
20 | const struct ccu_reset_map *map = &ccu->reset_map[id]; | ||
21 | unsigned long flags; | ||
22 | u32 reg; | ||
23 | |||
24 | spin_lock_irqsave(ccu->lock, flags); | ||
25 | |||
26 | reg = readl(ccu->base + map->reg); | ||
27 | writel(reg & ~map->bit, ccu->base + map->reg); | ||
28 | |||
29 | spin_unlock_irqrestore(ccu->lock, flags); | ||
30 | |||
31 | return 0; | ||
32 | } | ||
33 | |||
34 | static int ccu_reset_deassert(struct reset_controller_dev *rcdev, | ||
35 | unsigned long id) | ||
36 | { | ||
37 | struct ccu_reset *ccu = rcdev_to_ccu_reset(rcdev); | ||
38 | const struct ccu_reset_map *map = &ccu->reset_map[id]; | ||
39 | unsigned long flags; | ||
40 | u32 reg; | ||
41 | |||
42 | spin_lock_irqsave(ccu->lock, flags); | ||
43 | |||
44 | reg = readl(ccu->base + map->reg); | ||
45 | writel(reg | map->bit, ccu->base + map->reg); | ||
46 | |||
47 | spin_unlock_irqrestore(ccu->lock, flags); | ||
48 | |||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | const struct reset_control_ops ccu_reset_ops = { | ||
53 | .assert = ccu_reset_assert, | ||
54 | .deassert = ccu_reset_deassert, | ||
55 | }; | ||
diff --git a/drivers/clk/sunxi-ng/ccu_reset.h b/drivers/clk/sunxi-ng/ccu_reset.h new file mode 100644 index 000000000000..36a4679210bd --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_reset.h | |||
@@ -0,0 +1,40 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016 Maxime Ripard. All rights reserved. | ||
3 | * | ||
4 | * This software is licensed under the terms of the GNU General Public | ||
5 | * License version 2, as published by the Free Software Foundation, and | ||
6 | * may be copied, distributed, and modified under those terms. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #ifndef _CCU_RESET_H_ | ||
15 | #define _CCU_RESET_H_ | ||
16 | |||
17 | #include <linux/reset-controller.h> | ||
18 | |||
19 | struct ccu_reset_map { | ||
20 | u16 reg; | ||
21 | u32 bit; | ||
22 | }; | ||
23 | |||
24 | |||
25 | struct ccu_reset { | ||
26 | void __iomem *base; | ||
27 | struct ccu_reset_map *reset_map; | ||
28 | spinlock_t *lock; | ||
29 | |||
30 | struct reset_controller_dev rcdev; | ||
31 | }; | ||
32 | |||
33 | static inline struct ccu_reset *rcdev_to_ccu_reset(struct reset_controller_dev *rcdev) | ||
34 | { | ||
35 | return container_of(rcdev, struct ccu_reset, rcdev); | ||
36 | } | ||
37 | |||
38 | extern const struct reset_control_ops ccu_reset_ops; | ||
39 | |||
40 | #endif /* _CCU_RESET_H_ */ | ||
diff --git a/include/dt-bindings/clock/sun8i-h3-ccu.h b/include/dt-bindings/clock/sun8i-h3-ccu.h new file mode 100644 index 000000000000..efb7ba2bd515 --- /dev/null +++ b/include/dt-bindings/clock/sun8i-h3-ccu.h | |||
@@ -0,0 +1,145 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 Maxime Ripard <maxime.ripard@free-electrons.com> | ||
3 | * | ||
4 | * This file is dual-licensed: you can use it either under the terms | ||
5 | * of the GPL or the X11 license, at your option. Note that this dual | ||
6 | * licensing only applies to this file, and not this project as a | ||
7 | * whole. | ||
8 | * | ||
9 | * a) This file is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation; either version 2 of the | ||
12 | * License, or (at your option) any later version. | ||
13 | * | ||
14 | * This file is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * Or, alternatively, | ||
20 | * | ||
21 | * b) Permission is hereby granted, free of charge, to any person | ||
22 | * obtaining a copy of this software and associated documentation | ||
23 | * files (the "Software"), to deal in the Software without | ||
24 | * restriction, including without limitation the rights to use, | ||
25 | * copy, modify, merge, publish, distribute, sublicense, and/or | ||
26 | * sell copies of the Software, and to permit persons to whom the | ||
27 | * Software is furnished to do so, subject to the following | ||
28 | * conditions: | ||
29 | * | ||
30 | * The above copyright notice and this permission notice shall be | ||
31 | * included in all copies or substantial portions of the Software. | ||
32 | * | ||
33 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
34 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | ||
35 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
36 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | ||
37 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||
38 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
39 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
40 | * OTHER DEALINGS IN THE SOFTWARE. | ||
41 | */ | ||
42 | |||
43 | #ifndef _DT_BINDINGS_CLK_SUN8I_H3_H_ | ||
44 | #define _DT_BINDINGS_CLK_SUN8I_H3_H_ | ||
45 | |||
46 | #define CLK_CPUX 14 | ||
47 | |||
48 | #define CLK_BUS_CE 20 | ||
49 | #define CLK_BUS_DMA 21 | ||
50 | #define CLK_BUS_MMC0 22 | ||
51 | #define CLK_BUS_MMC1 23 | ||
52 | #define CLK_BUS_MMC2 24 | ||
53 | #define CLK_BUS_NAND 25 | ||
54 | #define CLK_BUS_DRAM 26 | ||
55 | #define CLK_BUS_EMAC 27 | ||
56 | #define CLK_BUS_TS 28 | ||
57 | #define CLK_BUS_HSTIMER 29 | ||
58 | #define CLK_BUS_SPI0 30 | ||
59 | #define CLK_BUS_SPI1 31 | ||
60 | #define CLK_BUS_OTG 32 | ||
61 | #define CLK_BUS_EHCI0 33 | ||
62 | #define CLK_BUS_EHCI1 34 | ||
63 | #define CLK_BUS_EHCI2 35 | ||
64 | #define CLK_BUS_EHCI3 36 | ||
65 | #define CLK_BUS_OHCI0 37 | ||
66 | #define CLK_BUS_OHCI1 38 | ||
67 | #define CLK_BUS_OHCI2 39 | ||
68 | #define CLK_BUS_OHCI3 40 | ||
69 | #define CLK_BUS_VE 41 | ||
70 | #define CLK_BUS_TCON0 42 | ||
71 | #define CLK_BUS_TCON1 43 | ||
72 | #define CLK_BUS_DEINTERLACE 44 | ||
73 | #define CLK_BUS_CSI 45 | ||
74 | #define CLK_BUS_TVE 46 | ||
75 | #define CLK_BUS_HDMI 47 | ||
76 | #define CLK_BUS_DE 48 | ||
77 | #define CLK_BUS_GPU 49 | ||
78 | #define CLK_BUS_MSGBOX 50 | ||
79 | #define CLK_BUS_SPINLOCK 51 | ||
80 | #define CLK_BUS_CODEC 52 | ||
81 | #define CLK_BUS_SPDIF 53 | ||
82 | #define CLK_BUS_PIO 54 | ||
83 | #define CLK_BUS_THS 55 | ||
84 | #define CLK_BUS_I2S0 56 | ||
85 | #define CLK_BUS_I2S1 57 | ||
86 | #define CLK_BUS_I2S2 58 | ||
87 | #define CLK_BUS_I2C0 59 | ||
88 | #define CLK_BUS_I2C1 60 | ||
89 | #define CLK_BUS_I2C2 61 | ||
90 | #define CLK_BUS_UART0 62 | ||
91 | #define CLK_BUS_UART1 63 | ||
92 | #define CLK_BUS_UART2 64 | ||
93 | #define CLK_BUS_UART3 65 | ||
94 | #define CLK_BUS_SCR 66 | ||
95 | #define CLK_BUS_EPHY 67 | ||
96 | #define CLK_BUS_DBG 68 | ||
97 | |||
98 | #define CLK_THS 69 | ||
99 | #define CLK_NAND 70 | ||
100 | #define CLK_MMC0 71 | ||
101 | #define CLK_MMC0_SAMPLE 72 | ||
102 | #define CLK_MMC0_OUTPUT 73 | ||
103 | #define CLK_MMC1 74 | ||
104 | #define CLK_MMC1_SAMPLE 75 | ||
105 | #define CLK_MMC1_OUTPUT 76 | ||
106 | #define CLK_MMC2 77 | ||
107 | #define CLK_MMC2_SAMPLE 78 | ||
108 | #define CLK_MMC2_OUTPUT 79 | ||
109 | #define CLK_TS 80 | ||
110 | #define CLK_CE 81 | ||
111 | #define CLK_SPI0 82 | ||
112 | #define CLK_SPI1 83 | ||
113 | #define CLK_I2S0 84 | ||
114 | #define CLK_I2S1 85 | ||
115 | #define CLK_I2S2 86 | ||
116 | #define CLK_SPDIF 87 | ||
117 | #define CLK_USB_PHY0 88 | ||
118 | #define CLK_USB_PHY1 89 | ||
119 | #define CLK_USB_PHY2 90 | ||
120 | #define CLK_USB_PHY3 91 | ||
121 | #define CLK_USB_OHCI0 92 | ||
122 | #define CLK_USB_OHCI1 93 | ||
123 | #define CLK_USB_OHCI2 94 | ||
124 | #define CLK_USB_OHCI3 95 | ||
125 | |||
126 | #define CLK_DRAM_VE 97 | ||
127 | #define CLK_DRAM_CSI 98 | ||
128 | #define CLK_DRAM_DEINTERLACE 99 | ||
129 | #define CLK_DRAM_TS 100 | ||
130 | #define CLK_DE 101 | ||
131 | #define CLK_TCON0 102 | ||
132 | #define CLK_TVE 103 | ||
133 | #define CLK_DEINTERLACE 104 | ||
134 | #define CLK_CSI_MISC 105 | ||
135 | #define CLK_CSI_SCLK 106 | ||
136 | #define CLK_CSI_MCLK 107 | ||
137 | #define CLK_VE 108 | ||
138 | #define CLK_AC_DIG 109 | ||
139 | #define CLK_AVS 110 | ||
140 | #define CLK_HDMI 111 | ||
141 | #define CLK_HDMI_DDC 112 | ||
142 | |||
143 | #define CLK_GPU 114 | ||
144 | |||
145 | #endif /* _DT_BINDINGS_CLK_SUN8I_H3_H_ */ | ||
diff --git a/include/dt-bindings/reset/sun8i-h3-ccu.h b/include/dt-bindings/reset/sun8i-h3-ccu.h new file mode 100644 index 000000000000..6b7af80c26ec --- /dev/null +++ b/include/dt-bindings/reset/sun8i-h3-ccu.h | |||
@@ -0,0 +1,103 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 Maxime Ripard <maxime.ripard@free-electrons.com> | ||
3 | * | ||
4 | * This file is dual-licensed: you can use it either under the terms | ||
5 | * of the GPL or the X11 license, at your option. Note that this dual | ||
6 | * licensing only applies to this file, and not this project as a | ||
7 | * whole. | ||
8 | * | ||
9 | * a) This file is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation; either version 2 of the | ||
12 | * License, or (at your option) any later version. | ||
13 | * | ||
14 | * This file is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * Or, alternatively, | ||
20 | * | ||
21 | * b) Permission is hereby granted, free of charge, to any person | ||
22 | * obtaining a copy of this software and associated documentation | ||
23 | * files (the "Software"), to deal in the Software without | ||
24 | * restriction, including without limitation the rights to use, | ||
25 | * copy, modify, merge, publish, distribute, sublicense, and/or | ||
26 | * sell copies of the Software, and to permit persons to whom the | ||
27 | * Software is furnished to do so, subject to the following | ||
28 | * conditions: | ||
29 | * | ||
30 | * The above copyright notice and this permission notice shall be | ||
31 | * included in all copies or substantial portions of the Software. | ||
32 | * | ||
33 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
34 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | ||
35 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
36 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | ||
37 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||
38 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
39 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
40 | * OTHER DEALINGS IN THE SOFTWARE. | ||
41 | */ | ||
42 | |||
43 | #ifndef _DT_BINDINGS_RST_SUN8I_H3_H_ | ||
44 | #define _DT_BINDINGS_RST_SUN8I_H3_H_ | ||
45 | |||
46 | #define RST_USB_PHY0 0 | ||
47 | #define RST_USB_PHY1 1 | ||
48 | #define RST_USB_PHY2 2 | ||
49 | #define RST_USB_PHY3 3 | ||
50 | |||
51 | #define RST_MBUS 4 | ||
52 | |||
53 | #define RST_BUS_CE 5 | ||
54 | #define RST_BUS_DMA 6 | ||
55 | #define RST_BUS_MMC0 7 | ||
56 | #define RST_BUS_MMC1 8 | ||
57 | #define RST_BUS_MMC2 9 | ||
58 | #define RST_BUS_NAND 10 | ||
59 | #define RST_BUS_DRAM 11 | ||
60 | #define RST_BUS_EMAC 12 | ||
61 | #define RST_BUS_TS 13 | ||
62 | #define RST_BUS_HSTIMER 14 | ||
63 | #define RST_BUS_SPI0 15 | ||
64 | #define RST_BUS_SPI1 16 | ||
65 | #define RST_BUS_OTG 17 | ||
66 | #define RST_BUS_EHCI0 18 | ||
67 | #define RST_BUS_EHCI1 19 | ||
68 | #define RST_BUS_EHCI2 20 | ||
69 | #define RST_BUS_EHCI3 21 | ||
70 | #define RST_BUS_OHCI0 22 | ||
71 | #define RST_BUS_OHCI1 23 | ||
72 | #define RST_BUS_OHCI2 24 | ||
73 | #define RST_BUS_OHCI3 25 | ||
74 | #define RST_BUS_VE 26 | ||
75 | #define RST_BUS_TCON0 27 | ||
76 | #define RST_BUS_TCON1 28 | ||
77 | #define RST_BUS_DEINTERLACE 29 | ||
78 | #define RST_BUS_CSI 30 | ||
79 | #define RST_BUS_TVE 31 | ||
80 | #define RST_BUS_HDMI0 32 | ||
81 | #define RST_BUS_HDMI1 33 | ||
82 | #define RST_BUS_DE 34 | ||
83 | #define RST_BUS_GPU 35 | ||
84 | #define RST_BUS_MSGBOX 36 | ||
85 | #define RST_BUS_SPINLOCK 37 | ||
86 | #define RST_BUS_DBG 38 | ||
87 | #define RST_BUS_EPHY 39 | ||
88 | #define RST_BUS_CODEC 40 | ||
89 | #define RST_BUS_SPDIF 41 | ||
90 | #define RST_BUS_THS 42 | ||
91 | #define RST_BUS_I2S0 43 | ||
92 | #define RST_BUS_I2S1 44 | ||
93 | #define RST_BUS_I2S2 45 | ||
94 | #define RST_BUS_I2C0 46 | ||
95 | #define RST_BUS_I2C1 47 | ||
96 | #define RST_BUS_I2C2 48 | ||
97 | #define RST_BUS_UART0 49 | ||
98 | #define RST_BUS_UART1 50 | ||
99 | #define RST_BUS_UART2 51 | ||
100 | #define RST_BUS_UART3 52 | ||
101 | #define RST_BUS_SCR 53 | ||
102 | |||
103 | #endif /* _DT_BINDINGS_RST_SUN8I_H3_H_ */ | ||