1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2016 Chen-Yu Tsai. All rights reserved. |
4 | */ |
5 | |
6 | #include <linux/clk.h> |
7 | #include <linux/clk-provider.h> |
8 | #include <linux/module.h> |
9 | #include <linux/platform_device.h> |
10 | #include <linux/reset.h> |
11 | |
12 | #include "ccu_common.h" |
13 | #include "ccu_div.h" |
14 | #include "ccu_gate.h" |
15 | #include "ccu_reset.h" |
16 | |
17 | #include "ccu-sun9i-a80-de.h" |
18 | |
19 | static SUNXI_CCU_GATE(fe0_clk, "fe0" , "fe0-div" , |
20 | 0x00, BIT(0), 0); |
21 | static SUNXI_CCU_GATE(fe1_clk, "fe1" , "fe1-div" , |
22 | 0x00, BIT(1), 0); |
23 | static SUNXI_CCU_GATE(fe2_clk, "fe2" , "fe2-div" , |
24 | 0x00, BIT(2), 0); |
25 | static SUNXI_CCU_GATE(iep_deu0_clk, "iep-deu0" , "de" , |
26 | 0x00, BIT(4), 0); |
27 | static SUNXI_CCU_GATE(iep_deu1_clk, "iep-deu1" , "de" , |
28 | 0x00, BIT(5), 0); |
29 | static SUNXI_CCU_GATE(be0_clk, "be0" , "be0-div" , |
30 | 0x00, BIT(8), 0); |
31 | static SUNXI_CCU_GATE(be1_clk, "be1" , "be1-div" , |
32 | 0x00, BIT(9), 0); |
33 | static SUNXI_CCU_GATE(be2_clk, "be2" , "be2-div" , |
34 | 0x00, BIT(10), 0); |
35 | static SUNXI_CCU_GATE(iep_drc0_clk, "iep-drc0" , "de" , |
36 | 0x00, BIT(12), 0); |
37 | static SUNXI_CCU_GATE(iep_drc1_clk, "iep-drc1" , "de" , |
38 | 0x00, BIT(13), 0); |
39 | static SUNXI_CCU_GATE(merge_clk, "merge" , "de" , |
40 | 0x00, BIT(20), 0); |
41 | |
42 | static SUNXI_CCU_GATE(dram_fe0_clk, "dram-fe0" , "sdram" , |
43 | 0x04, BIT(0), 0); |
44 | static SUNXI_CCU_GATE(dram_fe1_clk, "dram-fe1" , "sdram" , |
45 | 0x04, BIT(1), 0); |
46 | static SUNXI_CCU_GATE(dram_fe2_clk, "dram-fe2" , "sdram" , |
47 | 0x04, BIT(2), 0); |
48 | static SUNXI_CCU_GATE(dram_deu0_clk, "dram-deu0" , "sdram" , |
49 | 0x04, BIT(4), 0); |
50 | static SUNXI_CCU_GATE(dram_deu1_clk, "dram-deu1" , "sdram" , |
51 | 0x04, BIT(5), 0); |
52 | static SUNXI_CCU_GATE(dram_be0_clk, "dram-be0" , "sdram" , |
53 | 0x04, BIT(8), 0); |
54 | static SUNXI_CCU_GATE(dram_be1_clk, "dram-be1" , "sdram" , |
55 | 0x04, BIT(9), 0); |
56 | static SUNXI_CCU_GATE(dram_be2_clk, "dram-be2" , "sdram" , |
57 | 0x04, BIT(10), 0); |
58 | static SUNXI_CCU_GATE(dram_drc0_clk, "dram-drc0" , "sdram" , |
59 | 0x04, BIT(12), 0); |
60 | static SUNXI_CCU_GATE(dram_drc1_clk, "dram-drc1" , "sdram" , |
61 | 0x04, BIT(13), 0); |
62 | |
63 | static SUNXI_CCU_GATE(bus_fe0_clk, "bus-fe0" , "bus-de" , |
64 | 0x08, BIT(0), 0); |
65 | static SUNXI_CCU_GATE(bus_fe1_clk, "bus-fe1" , "bus-de" , |
66 | 0x08, BIT(1), 0); |
67 | static SUNXI_CCU_GATE(bus_fe2_clk, "bus-fe2" , "bus-de" , |
68 | 0x08, BIT(2), 0); |
69 | static SUNXI_CCU_GATE(bus_deu0_clk, "bus-deu0" , "bus-de" , |
70 | 0x08, BIT(4), 0); |
71 | static SUNXI_CCU_GATE(bus_deu1_clk, "bus-deu1" , "bus-de" , |
72 | 0x08, BIT(5), 0); |
73 | static SUNXI_CCU_GATE(bus_be0_clk, "bus-be0" , "bus-de" , |
74 | 0x08, BIT(8), 0); |
75 | static SUNXI_CCU_GATE(bus_be1_clk, "bus-be1" , "bus-de" , |
76 | 0x08, BIT(9), 0); |
77 | static SUNXI_CCU_GATE(bus_be2_clk, "bus-be2" , "bus-de" , |
78 | 0x08, BIT(10), 0); |
79 | static SUNXI_CCU_GATE(bus_drc0_clk, "bus-drc0" , "bus-de" , |
80 | 0x08, BIT(12), 0); |
81 | static SUNXI_CCU_GATE(bus_drc1_clk, "bus-drc1" , "bus-de" , |
82 | 0x08, BIT(13), 0); |
83 | |
84 | static SUNXI_CCU_M(fe0_div_clk, "fe0-div" , "de" , 0x20, 0, 4, 0); |
85 | static SUNXI_CCU_M(fe1_div_clk, "fe1-div" , "de" , 0x20, 4, 4, 0); |
86 | static SUNXI_CCU_M(fe2_div_clk, "fe2-div" , "de" , 0x20, 8, 4, 0); |
87 | static SUNXI_CCU_M(be0_div_clk, "be0-div" , "de" , 0x20, 16, 4, 0); |
88 | static SUNXI_CCU_M(be1_div_clk, "be1-div" , "de" , 0x20, 20, 4, 0); |
89 | static SUNXI_CCU_M(be2_div_clk, "be2-div" , "de" , 0x20, 24, 4, 0); |
90 | |
91 | static struct ccu_common *sun9i_a80_de_clks[] = { |
92 | &fe0_clk.common, |
93 | &fe1_clk.common, |
94 | &fe2_clk.common, |
95 | &iep_deu0_clk.common, |
96 | &iep_deu1_clk.common, |
97 | &be0_clk.common, |
98 | &be1_clk.common, |
99 | &be2_clk.common, |
100 | &iep_drc0_clk.common, |
101 | &iep_drc1_clk.common, |
102 | &merge_clk.common, |
103 | |
104 | &dram_fe0_clk.common, |
105 | &dram_fe1_clk.common, |
106 | &dram_fe2_clk.common, |
107 | &dram_deu0_clk.common, |
108 | &dram_deu1_clk.common, |
109 | &dram_be0_clk.common, |
110 | &dram_be1_clk.common, |
111 | &dram_be2_clk.common, |
112 | &dram_drc0_clk.common, |
113 | &dram_drc1_clk.common, |
114 | |
115 | &bus_fe0_clk.common, |
116 | &bus_fe1_clk.common, |
117 | &bus_fe2_clk.common, |
118 | &bus_deu0_clk.common, |
119 | &bus_deu1_clk.common, |
120 | &bus_be0_clk.common, |
121 | &bus_be1_clk.common, |
122 | &bus_be2_clk.common, |
123 | &bus_drc0_clk.common, |
124 | &bus_drc1_clk.common, |
125 | |
126 | &fe0_div_clk.common, |
127 | &fe1_div_clk.common, |
128 | &fe2_div_clk.common, |
129 | &be0_div_clk.common, |
130 | &be1_div_clk.common, |
131 | &be2_div_clk.common, |
132 | }; |
133 | |
134 | static struct clk_hw_onecell_data sun9i_a80_de_hw_clks = { |
135 | .hws = { |
136 | [CLK_FE0] = &fe0_clk.common.hw, |
137 | [CLK_FE1] = &fe1_clk.common.hw, |
138 | [CLK_FE2] = &fe2_clk.common.hw, |
139 | [CLK_IEP_DEU0] = &iep_deu0_clk.common.hw, |
140 | [CLK_IEP_DEU1] = &iep_deu1_clk.common.hw, |
141 | [CLK_BE0] = &be0_clk.common.hw, |
142 | [CLK_BE1] = &be1_clk.common.hw, |
143 | [CLK_BE2] = &be2_clk.common.hw, |
144 | [CLK_IEP_DRC0] = &iep_drc0_clk.common.hw, |
145 | [CLK_IEP_DRC1] = &iep_drc1_clk.common.hw, |
146 | [CLK_MERGE] = &merge_clk.common.hw, |
147 | |
148 | [CLK_DRAM_FE0] = &dram_fe0_clk.common.hw, |
149 | [CLK_DRAM_FE1] = &dram_fe1_clk.common.hw, |
150 | [CLK_DRAM_FE2] = &dram_fe2_clk.common.hw, |
151 | [CLK_DRAM_DEU0] = &dram_deu0_clk.common.hw, |
152 | [CLK_DRAM_DEU1] = &dram_deu1_clk.common.hw, |
153 | [CLK_DRAM_BE0] = &dram_be0_clk.common.hw, |
154 | [CLK_DRAM_BE1] = &dram_be1_clk.common.hw, |
155 | [CLK_DRAM_BE2] = &dram_be2_clk.common.hw, |
156 | [CLK_DRAM_DRC0] = &dram_drc0_clk.common.hw, |
157 | [CLK_DRAM_DRC1] = &dram_drc1_clk.common.hw, |
158 | |
159 | [CLK_BUS_FE0] = &bus_fe0_clk.common.hw, |
160 | [CLK_BUS_FE1] = &bus_fe1_clk.common.hw, |
161 | [CLK_BUS_FE2] = &bus_fe2_clk.common.hw, |
162 | [CLK_BUS_DEU0] = &bus_deu0_clk.common.hw, |
163 | [CLK_BUS_DEU1] = &bus_deu1_clk.common.hw, |
164 | [CLK_BUS_BE0] = &bus_be0_clk.common.hw, |
165 | [CLK_BUS_BE1] = &bus_be1_clk.common.hw, |
166 | [CLK_BUS_BE2] = &bus_be2_clk.common.hw, |
167 | [CLK_BUS_DRC0] = &bus_drc0_clk.common.hw, |
168 | [CLK_BUS_DRC1] = &bus_drc1_clk.common.hw, |
169 | |
170 | [CLK_FE0_DIV] = &fe0_div_clk.common.hw, |
171 | [CLK_FE1_DIV] = &fe1_div_clk.common.hw, |
172 | [CLK_FE2_DIV] = &fe2_div_clk.common.hw, |
173 | [CLK_BE0_DIV] = &be0_div_clk.common.hw, |
174 | [CLK_BE1_DIV] = &be1_div_clk.common.hw, |
175 | [CLK_BE2_DIV] = &be2_div_clk.common.hw, |
176 | }, |
177 | .num = CLK_NUMBER, |
178 | }; |
179 | |
180 | static struct ccu_reset_map sun9i_a80_de_resets[] = { |
181 | [RST_FE0] = { 0x0c, BIT(0) }, |
182 | [RST_FE1] = { 0x0c, BIT(1) }, |
183 | [RST_FE2] = { 0x0c, BIT(2) }, |
184 | [RST_DEU0] = { 0x0c, BIT(4) }, |
185 | [RST_DEU1] = { 0x0c, BIT(5) }, |
186 | [RST_BE0] = { 0x0c, BIT(8) }, |
187 | [RST_BE1] = { 0x0c, BIT(9) }, |
188 | [RST_BE2] = { 0x0c, BIT(10) }, |
189 | [RST_DRC0] = { 0x0c, BIT(12) }, |
190 | [RST_DRC1] = { 0x0c, BIT(13) }, |
191 | [RST_MERGE] = { 0x0c, BIT(20) }, |
192 | }; |
193 | |
194 | static const struct sunxi_ccu_desc sun9i_a80_de_clk_desc = { |
195 | .ccu_clks = sun9i_a80_de_clks, |
196 | .num_ccu_clks = ARRAY_SIZE(sun9i_a80_de_clks), |
197 | |
198 | .hw_clks = &sun9i_a80_de_hw_clks, |
199 | |
200 | .resets = sun9i_a80_de_resets, |
201 | .num_resets = ARRAY_SIZE(sun9i_a80_de_resets), |
202 | }; |
203 | |
204 | static int sun9i_a80_de_clk_probe(struct platform_device *pdev) |
205 | { |
206 | struct clk *bus_clk; |
207 | struct reset_control *rstc; |
208 | void __iomem *reg; |
209 | int ret; |
210 | |
211 | reg = devm_platform_ioremap_resource(pdev, index: 0); |
212 | if (IS_ERR(ptr: reg)) |
213 | return PTR_ERR(ptr: reg); |
214 | |
215 | bus_clk = devm_clk_get(dev: &pdev->dev, id: "bus" ); |
216 | if (IS_ERR(ptr: bus_clk)) |
217 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: bus_clk), |
218 | fmt: "Couldn't get bus clk\n" ); |
219 | |
220 | rstc = devm_reset_control_get_exclusive(dev: &pdev->dev, NULL); |
221 | if (IS_ERR(ptr: rstc)) |
222 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: rstc), |
223 | fmt: "Couldn't get reset control\n" ); |
224 | |
225 | /* The bus clock needs to be enabled for us to access the registers */ |
226 | ret = clk_prepare_enable(clk: bus_clk); |
227 | if (ret) { |
228 | dev_err(&pdev->dev, "Couldn't enable bus clk: %d\n" , ret); |
229 | return ret; |
230 | } |
231 | |
232 | /* The reset control needs to be asserted for the controls to work */ |
233 | ret = reset_control_deassert(rstc); |
234 | if (ret) { |
235 | dev_err(&pdev->dev, |
236 | "Couldn't deassert reset control: %d\n" , ret); |
237 | goto err_disable_clk; |
238 | } |
239 | |
240 | ret = devm_sunxi_ccu_probe(dev: &pdev->dev, reg, desc: &sun9i_a80_de_clk_desc); |
241 | if (ret) |
242 | goto err_assert_reset; |
243 | |
244 | return 0; |
245 | |
246 | err_assert_reset: |
247 | reset_control_assert(rstc); |
248 | err_disable_clk: |
249 | clk_disable_unprepare(clk: bus_clk); |
250 | return ret; |
251 | } |
252 | |
253 | static const struct of_device_id sun9i_a80_de_clk_ids[] = { |
254 | { .compatible = "allwinner,sun9i-a80-de-clks" }, |
255 | { } |
256 | }; |
257 | |
258 | static struct platform_driver sun9i_a80_de_clk_driver = { |
259 | .probe = sun9i_a80_de_clk_probe, |
260 | .driver = { |
261 | .name = "sun9i-a80-de-clks" , |
262 | .suppress_bind_attrs = true, |
263 | .of_match_table = sun9i_a80_de_clk_ids, |
264 | }, |
265 | }; |
266 | module_platform_driver(sun9i_a80_de_clk_driver); |
267 | |
268 | MODULE_IMPORT_NS(SUNXI_CCU); |
269 | MODULE_LICENSE("GPL" ); |
270 | |