Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
7c696693 CW |
2 | /* |
3 | * Rockchip Generic power domain support. | |
4 | * | |
5 | * Copyright (c) 2015 ROCKCHIP, Co. Ltd. | |
7c696693 CW |
6 | */ |
7 | ||
8 | #include <linux/io.h> | |
e4c8cd82 | 9 | #include <linux/iopoll.h> |
7c696693 CW |
10 | #include <linux/err.h> |
11 | #include <linux/pm_clock.h> | |
12 | #include <linux/pm_domain.h> | |
13 | #include <linux/of_address.h> | |
54d52ad9 | 14 | #include <linux/of_clk.h> |
7c696693 CW |
15 | #include <linux/of_platform.h> |
16 | #include <linux/clk.h> | |
17 | #include <linux/regmap.h> | |
18 | #include <linux/mfd/syscon.h> | |
9bdf401b | 19 | #include <dt-bindings/power/px30-power.h> |
6f686e85 | 20 | #include <dt-bindings/power/rk3036-power.h> |
24869610 | 21 | #include <dt-bindings/power/rk3066-power.h> |
277535f7 | 22 | #include <dt-bindings/power/rk3128-power.h> |
a0d5e7d4 | 23 | #include <dt-bindings/power/rk3188-power.h> |
2dacbd10 | 24 | #include <dt-bindings/power/rk3228-power.h> |
7c696693 | 25 | #include <dt-bindings/power/rk3288-power.h> |
e9284603 | 26 | #include <dt-bindings/power/rk3328-power.h> |
bd85f4b7 | 27 | #include <dt-bindings/power/rk3366-power.h> |
8c20b67f | 28 | #include <dt-bindings/power/rk3368-power.h> |
fd8b62cc | 29 | #include <dt-bindings/power/rk3399-power.h> |
7c696693 CW |
30 | |
31 | struct rockchip_domain_info { | |
32 | int pwr_mask; | |
33 | int status_mask; | |
34 | int req_mask; | |
35 | int idle_mask; | |
36 | int ack_mask; | |
6f27ab3e | 37 | bool active_wakeup; |
79bb17ce EZ |
38 | int pwr_w_mask; |
39 | int req_w_mask; | |
7c696693 CW |
40 | }; |
41 | ||
42 | struct rockchip_pmu_info { | |
43 | u32 pwr_offset; | |
44 | u32 status_offset; | |
45 | u32 req_offset; | |
46 | u32 idle_offset; | |
47 | u32 ack_offset; | |
48 | ||
49 | u32 core_pwrcnt_offset; | |
50 | u32 gpu_pwrcnt_offset; | |
51 | ||
52 | unsigned int core_power_transition_time; | |
53 | unsigned int gpu_power_transition_time; | |
54 | ||
55 | int num_domains; | |
56 | const struct rockchip_domain_info *domain_info; | |
57 | }; | |
58 | ||
074c6a42 EZ |
59 | #define MAX_QOS_REGS_NUM 5 |
60 | #define QOS_PRIORITY 0x08 | |
61 | #define QOS_MODE 0x0c | |
62 | #define QOS_BANDWIDTH 0x10 | |
63 | #define QOS_SATURATION 0x14 | |
64 | #define QOS_EXTCONTROL 0x18 | |
65 | ||
7c696693 CW |
66 | struct rockchip_pm_domain { |
67 | struct generic_pm_domain genpd; | |
68 | const struct rockchip_domain_info *info; | |
69 | struct rockchip_pmu *pmu; | |
074c6a42 EZ |
70 | int num_qos; |
71 | struct regmap **qos_regmap; | |
72 | u32 *qos_save_regs[MAX_QOS_REGS_NUM]; | |
7c696693 | 73 | int num_clks; |
d909072d | 74 | struct clk_bulk_data *clks; |
7c696693 CW |
75 | }; |
76 | ||
77 | struct rockchip_pmu { | |
78 | struct device *dev; | |
79 | struct regmap *regmap; | |
80 | const struct rockchip_pmu_info *info; | |
81 | struct mutex mutex; /* mutex lock for pmu */ | |
82 | struct genpd_onecell_data genpd_data; | |
83 | struct generic_pm_domain *domains[]; | |
84 | }; | |
85 | ||
86 | #define to_rockchip_pd(gpd) container_of(gpd, struct rockchip_pm_domain, genpd) | |
87 | ||
6f27ab3e | 88 | #define DOMAIN(pwr, status, req, idle, ack, wakeup) \ |
7c696693 | 89 | { \ |
1fe767a5 EZ |
90 | .pwr_mask = (pwr >= 0) ? BIT(pwr) : 0, \ |
91 | .status_mask = (status >= 0) ? BIT(status) : 0, \ | |
6aa841c8 EZ |
92 | .req_mask = (req >= 0) ? BIT(req) : 0, \ |
93 | .idle_mask = (idle >= 0) ? BIT(idle) : 0, \ | |
94 | .ack_mask = (ack >= 0) ? BIT(ack) : 0, \ | |
6f27ab3e | 95 | .active_wakeup = wakeup, \ |
7c696693 CW |
96 | } |
97 | ||
79bb17ce EZ |
98 | #define DOMAIN_M(pwr, status, req, idle, ack, wakeup) \ |
99 | { \ | |
100 | .pwr_w_mask = (pwr >= 0) ? BIT(pwr + 16) : 0, \ | |
101 | .pwr_mask = (pwr >= 0) ? BIT(pwr) : 0, \ | |
102 | .status_mask = (status >= 0) ? BIT(status) : 0, \ | |
103 | .req_w_mask = (req >= 0) ? BIT(req + 16) : 0, \ | |
104 | .req_mask = (req >= 0) ? BIT(req) : 0, \ | |
105 | .idle_mask = (idle >= 0) ? BIT(idle) : 0, \ | |
106 | .ack_mask = (ack >= 0) ? BIT(ack) : 0, \ | |
107 | .active_wakeup = wakeup, \ | |
108 | } | |
109 | ||
6f686e85 CW |
110 | #define DOMAIN_RK3036(req, ack, idle, wakeup) \ |
111 | { \ | |
112 | .req_mask = (req >= 0) ? BIT(req) : 0, \ | |
113 | .req_w_mask = (req >= 0) ? BIT(req + 16) : 0, \ | |
114 | .ack_mask = (ack >= 0) ? BIT(ack) : 0, \ | |
115 | .idle_mask = (idle >= 0) ? BIT(idle) : 0, \ | |
116 | .active_wakeup = wakeup, \ | |
117 | } | |
118 | ||
9bdf401b FX |
119 | #define DOMAIN_PX30(pwr, status, req, wakeup) \ |
120 | DOMAIN_M(pwr, status, req, (req) + 16, req, wakeup) | |
121 | ||
6f27ab3e EZ |
122 | #define DOMAIN_RK3288(pwr, status, req, wakeup) \ |
123 | DOMAIN(pwr, status, req, req, (req) + 16, wakeup) | |
7c696693 | 124 | |
e9284603 EZ |
125 | #define DOMAIN_RK3328(pwr, status, req, wakeup) \ |
126 | DOMAIN_M(pwr, pwr, req, (req) + 10, req, wakeup) | |
127 | ||
6f27ab3e EZ |
128 | #define DOMAIN_RK3368(pwr, status, req, wakeup) \ |
129 | DOMAIN(pwr, status, req, (req) + 16, req, wakeup) | |
8c20b67f | 130 | |
6f27ab3e EZ |
131 | #define DOMAIN_RK3399(pwr, status, req, wakeup) \ |
132 | DOMAIN(pwr, status, req, req, req, wakeup) | |
fd8b62cc | 133 | |
7c696693 CW |
134 | static bool rockchip_pmu_domain_is_idle(struct rockchip_pm_domain *pd) |
135 | { | |
136 | struct rockchip_pmu *pmu = pd->pmu; | |
137 | const struct rockchip_domain_info *pd_info = pd->info; | |
138 | unsigned int val; | |
139 | ||
140 | regmap_read(pmu->regmap, pmu->info->idle_offset, &val); | |
141 | return (val & pd_info->idle_mask) == pd_info->idle_mask; | |
142 | } | |
143 | ||
e4c8cd82 CW |
144 | static unsigned int rockchip_pmu_read_ack(struct rockchip_pmu *pmu) |
145 | { | |
146 | unsigned int val; | |
147 | ||
148 | regmap_read(pmu->regmap, pmu->info->ack_offset, &val); | |
149 | return val; | |
150 | } | |
151 | ||
7c696693 CW |
152 | static int rockchip_pmu_set_idle_request(struct rockchip_pm_domain *pd, |
153 | bool idle) | |
154 | { | |
155 | const struct rockchip_domain_info *pd_info = pd->info; | |
e4c8cd82 | 156 | struct generic_pm_domain *genpd = &pd->genpd; |
7c696693 | 157 | struct rockchip_pmu *pmu = pd->pmu; |
e4c8cd82 | 158 | unsigned int target_ack; |
7c696693 | 159 | unsigned int val; |
e4c8cd82 CW |
160 | bool is_idle; |
161 | int ret; | |
7c696693 | 162 | |
6aa841c8 EZ |
163 | if (pd_info->req_mask == 0) |
164 | return 0; | |
79bb17ce EZ |
165 | else if (pd_info->req_w_mask) |
166 | regmap_write(pmu->regmap, pmu->info->req_offset, | |
167 | idle ? (pd_info->req_mask | pd_info->req_w_mask) : | |
168 | pd_info->req_w_mask); | |
169 | else | |
170 | regmap_update_bits(pmu->regmap, pmu->info->req_offset, | |
171 | pd_info->req_mask, idle ? -1U : 0); | |
7c696693 CW |
172 | |
173 | dsb(sy); | |
174 | ||
e4c8cd82 CW |
175 | /* Wait util idle_ack = 1 */ |
176 | target_ack = idle ? pd_info->ack_mask : 0; | |
177 | ret = readx_poll_timeout_atomic(rockchip_pmu_read_ack, pmu, val, | |
178 | (val & pd_info->ack_mask) == target_ack, | |
179 | 0, 10000); | |
180 | if (ret) { | |
181 | dev_err(pmu->dev, | |
182 | "failed to get ack on domain '%s', val=0x%x\n", | |
183 | genpd->name, val); | |
184 | return ret; | |
185 | } | |
7c696693 | 186 | |
e4c8cd82 CW |
187 | ret = readx_poll_timeout_atomic(rockchip_pmu_domain_is_idle, pd, |
188 | is_idle, is_idle == idle, 0, 10000); | |
189 | if (ret) { | |
190 | dev_err(pmu->dev, | |
191 | "failed to set idle on domain '%s', val=%d\n", | |
192 | genpd->name, is_idle); | |
193 | return ret; | |
194 | } | |
7c696693 CW |
195 | |
196 | return 0; | |
197 | } | |
198 | ||
074c6a42 EZ |
199 | static int rockchip_pmu_save_qos(struct rockchip_pm_domain *pd) |
200 | { | |
201 | int i; | |
202 | ||
203 | for (i = 0; i < pd->num_qos; i++) { | |
204 | regmap_read(pd->qos_regmap[i], | |
205 | QOS_PRIORITY, | |
206 | &pd->qos_save_regs[0][i]); | |
207 | regmap_read(pd->qos_regmap[i], | |
208 | QOS_MODE, | |
209 | &pd->qos_save_regs[1][i]); | |
210 | regmap_read(pd->qos_regmap[i], | |
211 | QOS_BANDWIDTH, | |
212 | &pd->qos_save_regs[2][i]); | |
213 | regmap_read(pd->qos_regmap[i], | |
214 | QOS_SATURATION, | |
215 | &pd->qos_save_regs[3][i]); | |
216 | regmap_read(pd->qos_regmap[i], | |
217 | QOS_EXTCONTROL, | |
218 | &pd->qos_save_regs[4][i]); | |
219 | } | |
220 | return 0; | |
221 | } | |
222 | ||
223 | static int rockchip_pmu_restore_qos(struct rockchip_pm_domain *pd) | |
224 | { | |
225 | int i; | |
226 | ||
227 | for (i = 0; i < pd->num_qos; i++) { | |
228 | regmap_write(pd->qos_regmap[i], | |
229 | QOS_PRIORITY, | |
230 | pd->qos_save_regs[0][i]); | |
231 | regmap_write(pd->qos_regmap[i], | |
232 | QOS_MODE, | |
233 | pd->qos_save_regs[1][i]); | |
234 | regmap_write(pd->qos_regmap[i], | |
235 | QOS_BANDWIDTH, | |
236 | pd->qos_save_regs[2][i]); | |
237 | regmap_write(pd->qos_regmap[i], | |
238 | QOS_SATURATION, | |
239 | pd->qos_save_regs[3][i]); | |
240 | regmap_write(pd->qos_regmap[i], | |
241 | QOS_EXTCONTROL, | |
242 | pd->qos_save_regs[4][i]); | |
243 | } | |
244 | ||
245 | return 0; | |
246 | } | |
247 | ||
7c696693 CW |
248 | static bool rockchip_pmu_domain_is_on(struct rockchip_pm_domain *pd) |
249 | { | |
250 | struct rockchip_pmu *pmu = pd->pmu; | |
251 | unsigned int val; | |
252 | ||
1fe767a5 EZ |
253 | /* check idle status for idle-only domains */ |
254 | if (pd->info->status_mask == 0) | |
255 | return !rockchip_pmu_domain_is_idle(pd); | |
256 | ||
7c696693 CW |
257 | regmap_read(pmu->regmap, pmu->info->status_offset, &val); |
258 | ||
259 | /* 1'b0: power on, 1'b1: power off */ | |
260 | return !(val & pd->info->status_mask); | |
261 | } | |
262 | ||
263 | static void rockchip_do_pmu_set_power_domain(struct rockchip_pm_domain *pd, | |
264 | bool on) | |
265 | { | |
266 | struct rockchip_pmu *pmu = pd->pmu; | |
e4c8cd82 CW |
267 | struct generic_pm_domain *genpd = &pd->genpd; |
268 | bool is_on; | |
7c696693 | 269 | |
1fe767a5 EZ |
270 | if (pd->info->pwr_mask == 0) |
271 | return; | |
79bb17ce EZ |
272 | else if (pd->info->pwr_w_mask) |
273 | regmap_write(pmu->regmap, pmu->info->pwr_offset, | |
9e59c5f6 | 274 | on ? pd->info->pwr_w_mask : |
79bb17ce EZ |
275 | (pd->info->pwr_mask | pd->info->pwr_w_mask)); |
276 | else | |
277 | regmap_update_bits(pmu->regmap, pmu->info->pwr_offset, | |
278 | pd->info->pwr_mask, on ? 0 : -1U); | |
7c696693 CW |
279 | |
280 | dsb(sy); | |
281 | ||
e4c8cd82 CW |
282 | if (readx_poll_timeout_atomic(rockchip_pmu_domain_is_on, pd, is_on, |
283 | is_on == on, 0, 10000)) { | |
284 | dev_err(pmu->dev, | |
285 | "failed to set domain '%s', val=%d\n", | |
286 | genpd->name, is_on); | |
287 | return; | |
288 | } | |
7c696693 CW |
289 | } |
290 | ||
291 | static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on) | |
292 | { | |
d909072d JC |
293 | struct rockchip_pmu *pmu = pd->pmu; |
294 | int ret; | |
7c696693 | 295 | |
d909072d | 296 | mutex_lock(&pmu->mutex); |
7c696693 CW |
297 | |
298 | if (rockchip_pmu_domain_is_on(pd) != power_on) { | |
d909072d JC |
299 | ret = clk_bulk_enable(pd->num_clks, pd->clks); |
300 | if (ret < 0) { | |
301 | dev_err(pmu->dev, "failed to enable clocks\n"); | |
302 | mutex_unlock(&pmu->mutex); | |
303 | return ret; | |
304 | } | |
7c696693 CW |
305 | |
306 | if (!power_on) { | |
074c6a42 | 307 | rockchip_pmu_save_qos(pd); |
7c696693 CW |
308 | |
309 | /* if powering down, idle request to NIU first */ | |
310 | rockchip_pmu_set_idle_request(pd, true); | |
311 | } | |
312 | ||
313 | rockchip_do_pmu_set_power_domain(pd, power_on); | |
314 | ||
315 | if (power_on) { | |
316 | /* if powering up, leave idle mode */ | |
317 | rockchip_pmu_set_idle_request(pd, false); | |
318 | ||
074c6a42 | 319 | rockchip_pmu_restore_qos(pd); |
7c696693 CW |
320 | } |
321 | ||
d909072d | 322 | clk_bulk_disable(pd->num_clks, pd->clks); |
7c696693 CW |
323 | } |
324 | ||
d909072d | 325 | mutex_unlock(&pmu->mutex); |
7c696693 CW |
326 | return 0; |
327 | } | |
328 | ||
329 | static int rockchip_pd_power_on(struct generic_pm_domain *domain) | |
330 | { | |
331 | struct rockchip_pm_domain *pd = to_rockchip_pd(domain); | |
332 | ||
333 | return rockchip_pd_power(pd, true); | |
334 | } | |
335 | ||
336 | static int rockchip_pd_power_off(struct generic_pm_domain *domain) | |
337 | { | |
338 | struct rockchip_pm_domain *pd = to_rockchip_pd(domain); | |
339 | ||
340 | return rockchip_pd_power(pd, false); | |
341 | } | |
342 | ||
343 | static int rockchip_pd_attach_dev(struct generic_pm_domain *genpd, | |
344 | struct device *dev) | |
345 | { | |
346 | struct clk *clk; | |
347 | int i; | |
348 | int error; | |
349 | ||
350 | dev_dbg(dev, "attaching to power domain '%s'\n", genpd->name); | |
351 | ||
352 | error = pm_clk_create(dev); | |
353 | if (error) { | |
354 | dev_err(dev, "pm_clk_create failed %d\n", error); | |
355 | return error; | |
356 | } | |
357 | ||
358 | i = 0; | |
359 | while ((clk = of_clk_get(dev->of_node, i++)) && !IS_ERR(clk)) { | |
360 | dev_dbg(dev, "adding clock '%pC' to list of PM clocks\n", clk); | |
361 | error = pm_clk_add_clk(dev, clk); | |
362 | if (error) { | |
363 | dev_err(dev, "pm_clk_add_clk failed %d\n", error); | |
364 | clk_put(clk); | |
365 | pm_clk_destroy(dev); | |
366 | return error; | |
367 | } | |
368 | } | |
369 | ||
370 | return 0; | |
371 | } | |
372 | ||
373 | static void rockchip_pd_detach_dev(struct generic_pm_domain *genpd, | |
374 | struct device *dev) | |
375 | { | |
376 | dev_dbg(dev, "detaching from power domain '%s'\n", genpd->name); | |
377 | ||
378 | pm_clk_destroy(dev); | |
379 | } | |
380 | ||
381 | static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu, | |
382 | struct device_node *node) | |
383 | { | |
384 | const struct rockchip_domain_info *pd_info; | |
385 | struct rockchip_pm_domain *pd; | |
074c6a42 | 386 | struct device_node *qos_node; |
074c6a42 | 387 | int i, j; |
7c696693 CW |
388 | u32 id; |
389 | int error; | |
390 | ||
391 | error = of_property_read_u32(node, "reg", &id); | |
392 | if (error) { | |
393 | dev_err(pmu->dev, | |
dc37a252 RH |
394 | "%pOFn: failed to retrieve domain id (reg): %d\n", |
395 | node, error); | |
7c696693 CW |
396 | return -EINVAL; |
397 | } | |
398 | ||
399 | if (id >= pmu->info->num_domains) { | |
dc37a252 RH |
400 | dev_err(pmu->dev, "%pOFn: invalid domain id %d\n", |
401 | node, id); | |
7c696693 CW |
402 | return -EINVAL; |
403 | } | |
404 | ||
405 | pd_info = &pmu->info->domain_info[id]; | |
406 | if (!pd_info) { | |
dc37a252 RH |
407 | dev_err(pmu->dev, "%pOFn: undefined domain id %d\n", |
408 | node, id); | |
7c696693 CW |
409 | return -EINVAL; |
410 | } | |
411 | ||
d909072d | 412 | pd = devm_kzalloc(pmu->dev, sizeof(*pd), GFP_KERNEL); |
7c696693 CW |
413 | if (!pd) |
414 | return -ENOMEM; | |
415 | ||
416 | pd->info = pd_info; | |
417 | pd->pmu = pmu; | |
418 | ||
54d52ad9 | 419 | pd->num_clks = of_clk_get_parent_count(node); |
b1271993 JC |
420 | if (pd->num_clks > 0) { |
421 | pd->clks = devm_kcalloc(pmu->dev, pd->num_clks, | |
422 | sizeof(*pd->clks), GFP_KERNEL); | |
423 | if (!pd->clks) | |
424 | return -ENOMEM; | |
425 | } else { | |
dc37a252 RH |
426 | dev_dbg(pmu->dev, "%pOFn: doesn't have clocks: %d\n", |
427 | node, pd->num_clks); | |
b1271993 JC |
428 | pd->num_clks = 0; |
429 | } | |
d909072d JC |
430 | |
431 | for (i = 0; i < pd->num_clks; i++) { | |
432 | pd->clks[i].clk = of_clk_get(node, i); | |
433 | if (IS_ERR(pd->clks[i].clk)) { | |
434 | error = PTR_ERR(pd->clks[i].clk); | |
7c696693 | 435 | dev_err(pmu->dev, |
dc37a252 RH |
436 | "%pOFn: failed to get clk at index %d: %d\n", |
437 | node, i, error); | |
d909072d | 438 | return error; |
7c696693 | 439 | } |
7c696693 CW |
440 | } |
441 | ||
d909072d JC |
442 | error = clk_bulk_prepare(pd->num_clks, pd->clks); |
443 | if (error) | |
444 | goto err_put_clocks; | |
445 | ||
074c6a42 EZ |
446 | pd->num_qos = of_count_phandle_with_args(node, "pm_qos", |
447 | NULL); | |
448 | ||
449 | if (pd->num_qos > 0) { | |
450 | pd->qos_regmap = devm_kcalloc(pmu->dev, pd->num_qos, | |
451 | sizeof(*pd->qos_regmap), | |
452 | GFP_KERNEL); | |
453 | if (!pd->qos_regmap) { | |
454 | error = -ENOMEM; | |
d909072d | 455 | goto err_unprepare_clocks; |
074c6a42 EZ |
456 | } |
457 | ||
458 | for (j = 0; j < MAX_QOS_REGS_NUM; j++) { | |
459 | pd->qos_save_regs[j] = devm_kcalloc(pmu->dev, | |
460 | pd->num_qos, | |
461 | sizeof(u32), | |
462 | GFP_KERNEL); | |
463 | if (!pd->qos_save_regs[j]) { | |
464 | error = -ENOMEM; | |
d909072d | 465 | goto err_unprepare_clocks; |
074c6a42 EZ |
466 | } |
467 | } | |
468 | ||
469 | for (j = 0; j < pd->num_qos; j++) { | |
470 | qos_node = of_parse_phandle(node, "pm_qos", j); | |
471 | if (!qos_node) { | |
472 | error = -ENODEV; | |
d909072d | 473 | goto err_unprepare_clocks; |
074c6a42 EZ |
474 | } |
475 | pd->qos_regmap[j] = syscon_node_to_regmap(qos_node); | |
476 | if (IS_ERR(pd->qos_regmap[j])) { | |
477 | error = -ENODEV; | |
478 | of_node_put(qos_node); | |
d909072d | 479 | goto err_unprepare_clocks; |
074c6a42 EZ |
480 | } |
481 | of_node_put(qos_node); | |
482 | } | |
483 | } | |
484 | ||
7c696693 CW |
485 | error = rockchip_pd_power(pd, true); |
486 | if (error) { | |
487 | dev_err(pmu->dev, | |
dc37a252 RH |
488 | "failed to power on domain '%pOFn': %d\n", |
489 | node, error); | |
d909072d | 490 | goto err_unprepare_clocks; |
7c696693 CW |
491 | } |
492 | ||
493 | pd->genpd.name = node->name; | |
494 | pd->genpd.power_off = rockchip_pd_power_off; | |
495 | pd->genpd.power_on = rockchip_pd_power_on; | |
496 | pd->genpd.attach_dev = rockchip_pd_attach_dev; | |
497 | pd->genpd.detach_dev = rockchip_pd_detach_dev; | |
498 | pd->genpd.flags = GENPD_FLAG_PM_CLK; | |
89c7aea9 GU |
499 | if (pd_info->active_wakeup) |
500 | pd->genpd.flags |= GENPD_FLAG_ACTIVE_WAKEUP; | |
7c696693 CW |
501 | pm_genpd_init(&pd->genpd, NULL, false); |
502 | ||
503 | pmu->genpd_data.domains[id] = &pd->genpd; | |
504 | return 0; | |
505 | ||
d909072d JC |
506 | err_unprepare_clocks: |
507 | clk_bulk_unprepare(pd->num_clks, pd->clks); | |
508 | err_put_clocks: | |
509 | clk_bulk_put(pd->num_clks, pd->clks); | |
7c696693 CW |
510 | return error; |
511 | } | |
512 | ||
513 | static void rockchip_pm_remove_one_domain(struct rockchip_pm_domain *pd) | |
514 | { | |
d909072d | 515 | int ret; |
79d12b7a HS |
516 | |
517 | /* | |
518 | * We're in the error cleanup already, so we only complain, | |
519 | * but won't emit another error on top of the original one. | |
520 | */ | |
521 | ret = pm_genpd_remove(&pd->genpd); | |
522 | if (ret < 0) | |
523 | dev_err(pd->pmu->dev, "failed to remove domain '%s' : %d - state may be inconsistent\n", | |
524 | pd->genpd.name, ret); | |
7c696693 | 525 | |
d909072d JC |
526 | clk_bulk_unprepare(pd->num_clks, pd->clks); |
527 | clk_bulk_put(pd->num_clks, pd->clks); | |
7c696693 CW |
528 | |
529 | /* protect the zeroing of pm->num_clks */ | |
530 | mutex_lock(&pd->pmu->mutex); | |
531 | pd->num_clks = 0; | |
532 | mutex_unlock(&pd->pmu->mutex); | |
533 | ||
534 | /* devm will free our memory */ | |
535 | } | |
536 | ||
537 | static void rockchip_pm_domain_cleanup(struct rockchip_pmu *pmu) | |
538 | { | |
539 | struct generic_pm_domain *genpd; | |
540 | struct rockchip_pm_domain *pd; | |
541 | int i; | |
542 | ||
543 | for (i = 0; i < pmu->genpd_data.num_domains; i++) { | |
544 | genpd = pmu->genpd_data.domains[i]; | |
545 | if (genpd) { | |
546 | pd = to_rockchip_pd(genpd); | |
547 | rockchip_pm_remove_one_domain(pd); | |
548 | } | |
549 | } | |
550 | ||
551 | /* devm will free our memory */ | |
552 | } | |
553 | ||
554 | static void rockchip_configure_pd_cnt(struct rockchip_pmu *pmu, | |
555 | u32 domain_reg_offset, | |
556 | unsigned int count) | |
557 | { | |
558 | /* First configure domain power down transition count ... */ | |
559 | regmap_write(pmu->regmap, domain_reg_offset, count); | |
560 | /* ... and then power up count. */ | |
561 | regmap_write(pmu->regmap, domain_reg_offset + 4, count); | |
562 | } | |
563 | ||
6be05b5e EZ |
564 | static int rockchip_pm_add_subdomain(struct rockchip_pmu *pmu, |
565 | struct device_node *parent) | |
566 | { | |
567 | struct device_node *np; | |
568 | struct generic_pm_domain *child_domain, *parent_domain; | |
569 | int error; | |
570 | ||
571 | for_each_child_of_node(parent, np) { | |
572 | u32 idx; | |
573 | ||
574 | error = of_property_read_u32(parent, "reg", &idx); | |
575 | if (error) { | |
576 | dev_err(pmu->dev, | |
dc37a252 RH |
577 | "%pOFn: failed to retrieve domain id (reg): %d\n", |
578 | parent, error); | |
6be05b5e EZ |
579 | goto err_out; |
580 | } | |
581 | parent_domain = pmu->genpd_data.domains[idx]; | |
582 | ||
583 | error = rockchip_pm_add_one_domain(pmu, np); | |
584 | if (error) { | |
dc37a252 RH |
585 | dev_err(pmu->dev, "failed to handle node %pOFn: %d\n", |
586 | np, error); | |
6be05b5e EZ |
587 | goto err_out; |
588 | } | |
589 | ||
590 | error = of_property_read_u32(np, "reg", &idx); | |
591 | if (error) { | |
592 | dev_err(pmu->dev, | |
dc37a252 RH |
593 | "%pOFn: failed to retrieve domain id (reg): %d\n", |
594 | np, error); | |
6be05b5e EZ |
595 | goto err_out; |
596 | } | |
597 | child_domain = pmu->genpd_data.domains[idx]; | |
598 | ||
599 | error = pm_genpd_add_subdomain(parent_domain, child_domain); | |
600 | if (error) { | |
601 | dev_err(pmu->dev, "%s failed to add subdomain %s: %d\n", | |
602 | parent_domain->name, child_domain->name, error); | |
603 | goto err_out; | |
604 | } else { | |
605 | dev_dbg(pmu->dev, "%s add subdomain: %s\n", | |
606 | parent_domain->name, child_domain->name); | |
607 | } | |
608 | ||
609 | rockchip_pm_add_subdomain(pmu, np); | |
610 | } | |
611 | ||
612 | return 0; | |
613 | ||
614 | err_out: | |
615 | of_node_put(np); | |
616 | return error; | |
617 | } | |
618 | ||
7c696693 CW |
619 | static int rockchip_pm_domain_probe(struct platform_device *pdev) |
620 | { | |
621 | struct device *dev = &pdev->dev; | |
622 | struct device_node *np = dev->of_node; | |
623 | struct device_node *node; | |
624 | struct device *parent; | |
625 | struct rockchip_pmu *pmu; | |
626 | const struct of_device_id *match; | |
627 | const struct rockchip_pmu_info *pmu_info; | |
628 | int error; | |
629 | ||
630 | if (!np) { | |
631 | dev_err(dev, "device tree node not found\n"); | |
632 | return -ENODEV; | |
633 | } | |
634 | ||
635 | match = of_match_device(dev->driver->of_match_table, dev); | |
636 | if (!match || !match->data) { | |
637 | dev_err(dev, "missing pmu data\n"); | |
638 | return -EINVAL; | |
639 | } | |
640 | ||
641 | pmu_info = match->data; | |
642 | ||
643 | pmu = devm_kzalloc(dev, | |
0ed2dd03 | 644 | struct_size(pmu, domains, pmu_info->num_domains), |
7c696693 CW |
645 | GFP_KERNEL); |
646 | if (!pmu) | |
647 | return -ENOMEM; | |
648 | ||
649 | pmu->dev = &pdev->dev; | |
650 | mutex_init(&pmu->mutex); | |
651 | ||
652 | pmu->info = pmu_info; | |
653 | ||
654 | pmu->genpd_data.domains = pmu->domains; | |
655 | pmu->genpd_data.num_domains = pmu_info->num_domains; | |
656 | ||
657 | parent = dev->parent; | |
658 | if (!parent) { | |
659 | dev_err(dev, "no parent for syscon devices\n"); | |
660 | return -ENODEV; | |
661 | } | |
662 | ||
663 | pmu->regmap = syscon_node_to_regmap(parent->of_node); | |
4506697d SL |
664 | if (IS_ERR(pmu->regmap)) { |
665 | dev_err(dev, "no regmap available\n"); | |
666 | return PTR_ERR(pmu->regmap); | |
667 | } | |
7c696693 CW |
668 | |
669 | /* | |
670 | * Configure power up and down transition delays for CORE | |
671 | * and GPU domains. | |
672 | */ | |
3f2fe461 DA |
673 | if (pmu_info->core_power_transition_time) |
674 | rockchip_configure_pd_cnt(pmu, pmu_info->core_pwrcnt_offset, | |
675 | pmu_info->core_power_transition_time); | |
676 | if (pmu_info->gpu_pwrcnt_offset) | |
677 | rockchip_configure_pd_cnt(pmu, pmu_info->gpu_pwrcnt_offset, | |
678 | pmu_info->gpu_power_transition_time); | |
7c696693 CW |
679 | |
680 | error = -ENODEV; | |
681 | ||
682 | for_each_available_child_of_node(np, node) { | |
683 | error = rockchip_pm_add_one_domain(pmu, node); | |
684 | if (error) { | |
dc37a252 RH |
685 | dev_err(dev, "failed to handle node %pOFn: %d\n", |
686 | node, error); | |
1d961f11 | 687 | of_node_put(node); |
7c696693 CW |
688 | goto err_out; |
689 | } | |
6be05b5e EZ |
690 | |
691 | error = rockchip_pm_add_subdomain(pmu, node); | |
692 | if (error < 0) { | |
dc37a252 RH |
693 | dev_err(dev, "failed to handle subdomain node %pOFn: %d\n", |
694 | node, error); | |
6be05b5e EZ |
695 | of_node_put(node); |
696 | goto err_out; | |
697 | } | |
7c696693 CW |
698 | } |
699 | ||
700 | if (error) { | |
701 | dev_dbg(dev, "no power domains defined\n"); | |
702 | goto err_out; | |
703 | } | |
704 | ||
dabc0259 TV |
705 | error = of_genpd_add_provider_onecell(np, &pmu->genpd_data); |
706 | if (error) { | |
707 | dev_err(dev, "failed to add provider: %d\n", error); | |
708 | goto err_out; | |
709 | } | |
7c696693 CW |
710 | |
711 | return 0; | |
712 | ||
713 | err_out: | |
714 | rockchip_pm_domain_cleanup(pmu); | |
715 | return error; | |
716 | } | |
717 | ||
9bdf401b FX |
718 | static const struct rockchip_domain_info px30_pm_domains[] = { |
719 | [PX30_PD_USB] = DOMAIN_PX30(5, 5, 10, false), | |
720 | [PX30_PD_SDCARD] = DOMAIN_PX30(8, 8, 9, false), | |
721 | [PX30_PD_GMAC] = DOMAIN_PX30(10, 10, 6, false), | |
722 | [PX30_PD_MMC_NAND] = DOMAIN_PX30(11, 11, 5, false), | |
723 | [PX30_PD_VPU] = DOMAIN_PX30(12, 12, 14, false), | |
724 | [PX30_PD_VO] = DOMAIN_PX30(13, 13, 7, false), | |
725 | [PX30_PD_VI] = DOMAIN_PX30(14, 14, 8, false), | |
726 | [PX30_PD_GPU] = DOMAIN_PX30(15, 15, 2, false), | |
727 | }; | |
728 | ||
6f686e85 CW |
729 | static const struct rockchip_domain_info rk3036_pm_domains[] = { |
730 | [RK3036_PD_MSCH] = DOMAIN_RK3036(14, 23, 30, true), | |
731 | [RK3036_PD_CORE] = DOMAIN_RK3036(13, 17, 24, false), | |
732 | [RK3036_PD_PERI] = DOMAIN_RK3036(12, 18, 25, false), | |
733 | [RK3036_PD_VIO] = DOMAIN_RK3036(11, 19, 26, false), | |
734 | [RK3036_PD_VPU] = DOMAIN_RK3036(10, 20, 27, false), | |
735 | [RK3036_PD_GPU] = DOMAIN_RK3036(9, 21, 28, false), | |
736 | [RK3036_PD_SYS] = DOMAIN_RK3036(8, 22, 29, false), | |
737 | }; | |
738 | ||
24869610 HS |
739 | static const struct rockchip_domain_info rk3066_pm_domains[] = { |
740 | [RK3066_PD_GPU] = DOMAIN(9, 9, 3, 24, 29, false), | |
741 | [RK3066_PD_VIDEO] = DOMAIN(8, 8, 4, 23, 28, false), | |
742 | [RK3066_PD_VIO] = DOMAIN(7, 7, 5, 22, 27, false), | |
743 | [RK3066_PD_PERI] = DOMAIN(6, 6, 2, 25, 30, false), | |
744 | [RK3066_PD_CPU] = DOMAIN(-1, 5, 1, 26, 31, false), | |
745 | }; | |
746 | ||
277535f7 EZ |
747 | static const struct rockchip_domain_info rk3128_pm_domains[] = { |
748 | [RK3128_PD_CORE] = DOMAIN_RK3288(0, 0, 4, false), | |
749 | [RK3128_PD_MSCH] = DOMAIN_RK3288(-1, -1, 6, true), | |
750 | [RK3128_PD_VIO] = DOMAIN_RK3288(3, 3, 2, false), | |
751 | [RK3128_PD_VIDEO] = DOMAIN_RK3288(2, 2, 1, false), | |
752 | [RK3128_PD_GPU] = DOMAIN_RK3288(1, 1, 3, false), | |
753 | }; | |
754 | ||
a0d5e7d4 HS |
755 | static const struct rockchip_domain_info rk3188_pm_domains[] = { |
756 | [RK3188_PD_GPU] = DOMAIN(9, 9, 3, 24, 29, false), | |
757 | [RK3188_PD_VIDEO] = DOMAIN(8, 8, 4, 23, 28, false), | |
758 | [RK3188_PD_VIO] = DOMAIN(7, 7, 5, 22, 27, false), | |
759 | [RK3188_PD_PERI] = DOMAIN(6, 6, 2, 25, 30, false), | |
760 | [RK3188_PD_CPU] = DOMAIN(5, 5, 1, 26, 31, false), | |
761 | }; | |
762 | ||
2dacbd10 EZ |
763 | static const struct rockchip_domain_info rk3228_pm_domains[] = { |
764 | [RK3228_PD_CORE] = DOMAIN_RK3036(0, 0, 16, true), | |
765 | [RK3228_PD_MSCH] = DOMAIN_RK3036(1, 1, 17, true), | |
766 | [RK3228_PD_BUS] = DOMAIN_RK3036(2, 2, 18, true), | |
767 | [RK3228_PD_SYS] = DOMAIN_RK3036(3, 3, 19, true), | |
768 | [RK3228_PD_VIO] = DOMAIN_RK3036(4, 4, 20, false), | |
769 | [RK3228_PD_VOP] = DOMAIN_RK3036(5, 5, 21, false), | |
770 | [RK3228_PD_VPU] = DOMAIN_RK3036(6, 6, 22, false), | |
771 | [RK3228_PD_RKVDEC] = DOMAIN_RK3036(7, 7, 23, false), | |
772 | [RK3228_PD_GPU] = DOMAIN_RK3036(8, 8, 24, false), | |
773 | [RK3228_PD_PERI] = DOMAIN_RK3036(9, 9, 25, true), | |
774 | [RK3228_PD_GMAC] = DOMAIN_RK3036(10, 10, 26, false), | |
775 | }; | |
776 | ||
7c696693 | 777 | static const struct rockchip_domain_info rk3288_pm_domains[] = { |
6f27ab3e EZ |
778 | [RK3288_PD_VIO] = DOMAIN_RK3288(7, 7, 4, false), |
779 | [RK3288_PD_HEVC] = DOMAIN_RK3288(14, 10, 9, false), | |
780 | [RK3288_PD_VIDEO] = DOMAIN_RK3288(8, 8, 3, false), | |
781 | [RK3288_PD_GPU] = DOMAIN_RK3288(9, 9, 2, false), | |
7c696693 CW |
782 | }; |
783 | ||
e9284603 EZ |
784 | static const struct rockchip_domain_info rk3328_pm_domains[] = { |
785 | [RK3328_PD_CORE] = DOMAIN_RK3328(-1, 0, 0, false), | |
786 | [RK3328_PD_GPU] = DOMAIN_RK3328(-1, 1, 1, false), | |
787 | [RK3328_PD_BUS] = DOMAIN_RK3328(-1, 2, 2, true), | |
788 | [RK3328_PD_MSCH] = DOMAIN_RK3328(-1, 3, 3, true), | |
789 | [RK3328_PD_PERI] = DOMAIN_RK3328(-1, 4, 4, true), | |
790 | [RK3328_PD_VIDEO] = DOMAIN_RK3328(-1, 5, 5, false), | |
791 | [RK3328_PD_HEVC] = DOMAIN_RK3328(-1, 6, 6, false), | |
792 | [RK3328_PD_VIO] = DOMAIN_RK3328(-1, 8, 8, false), | |
793 | [RK3328_PD_VPU] = DOMAIN_RK3328(-1, 9, 9, false), | |
794 | }; | |
795 | ||
bd85f4b7 EZ |
796 | static const struct rockchip_domain_info rk3366_pm_domains[] = { |
797 | [RK3366_PD_PERI] = DOMAIN_RK3368(10, 10, 6, true), | |
798 | [RK3366_PD_VIO] = DOMAIN_RK3368(14, 14, 8, false), | |
799 | [RK3366_PD_VIDEO] = DOMAIN_RK3368(13, 13, 7, false), | |
800 | [RK3366_PD_RKVDEC] = DOMAIN_RK3368(11, 11, 7, false), | |
801 | [RK3366_PD_WIFIBT] = DOMAIN_RK3368(8, 8, 9, false), | |
802 | [RK3366_PD_VPU] = DOMAIN_RK3368(12, 12, 7, false), | |
803 | [RK3366_PD_GPU] = DOMAIN_RK3368(15, 15, 2, false), | |
804 | }; | |
805 | ||
8c20b67f | 806 | static const struct rockchip_domain_info rk3368_pm_domains[] = { |
6f27ab3e EZ |
807 | [RK3368_PD_PERI] = DOMAIN_RK3368(13, 12, 6, true), |
808 | [RK3368_PD_VIO] = DOMAIN_RK3368(15, 14, 8, false), | |
809 | [RK3368_PD_VIDEO] = DOMAIN_RK3368(14, 13, 7, false), | |
810 | [RK3368_PD_GPU_0] = DOMAIN_RK3368(16, 15, 2, false), | |
811 | [RK3368_PD_GPU_1] = DOMAIN_RK3368(17, 16, 2, false), | |
8c20b67f | 812 | }; |
813 | ||
fd8b62cc | 814 | static const struct rockchip_domain_info rk3399_pm_domains[] = { |
6f27ab3e EZ |
815 | [RK3399_PD_TCPD0] = DOMAIN_RK3399(8, 8, -1, false), |
816 | [RK3399_PD_TCPD1] = DOMAIN_RK3399(9, 9, -1, false), | |
817 | [RK3399_PD_CCI] = DOMAIN_RK3399(10, 10, -1, true), | |
818 | [RK3399_PD_CCI0] = DOMAIN_RK3399(-1, -1, 15, true), | |
819 | [RK3399_PD_CCI1] = DOMAIN_RK3399(-1, -1, 16, true), | |
820 | [RK3399_PD_PERILP] = DOMAIN_RK3399(11, 11, 1, true), | |
821 | [RK3399_PD_PERIHP] = DOMAIN_RK3399(12, 12, 2, true), | |
822 | [RK3399_PD_CENTER] = DOMAIN_RK3399(13, 13, 14, true), | |
823 | [RK3399_PD_VIO] = DOMAIN_RK3399(14, 14, 17, false), | |
824 | [RK3399_PD_GPU] = DOMAIN_RK3399(15, 15, 0, false), | |
825 | [RK3399_PD_VCODEC] = DOMAIN_RK3399(16, 16, 3, false), | |
826 | [RK3399_PD_VDU] = DOMAIN_RK3399(17, 17, 4, false), | |
827 | [RK3399_PD_RGA] = DOMAIN_RK3399(18, 18, 5, false), | |
828 | [RK3399_PD_IEP] = DOMAIN_RK3399(19, 19, 6, false), | |
829 | [RK3399_PD_VO] = DOMAIN_RK3399(20, 20, -1, false), | |
830 | [RK3399_PD_VOPB] = DOMAIN_RK3399(-1, -1, 7, false), | |
831 | [RK3399_PD_VOPL] = DOMAIN_RK3399(-1, -1, 8, false), | |
832 | [RK3399_PD_ISP0] = DOMAIN_RK3399(22, 22, 9, false), | |
833 | [RK3399_PD_ISP1] = DOMAIN_RK3399(23, 23, 10, false), | |
834 | [RK3399_PD_HDCP] = DOMAIN_RK3399(24, 24, 11, false), | |
835 | [RK3399_PD_GMAC] = DOMAIN_RK3399(25, 25, 23, true), | |
836 | [RK3399_PD_EMMC] = DOMAIN_RK3399(26, 26, 24, true), | |
837 | [RK3399_PD_USB3] = DOMAIN_RK3399(27, 27, 12, true), | |
838 | [RK3399_PD_EDP] = DOMAIN_RK3399(28, 28, 22, false), | |
839 | [RK3399_PD_GIC] = DOMAIN_RK3399(29, 29, 27, true), | |
840 | [RK3399_PD_SD] = DOMAIN_RK3399(30, 30, 28, true), | |
841 | [RK3399_PD_SDIOAUDIO] = DOMAIN_RK3399(31, 31, 29, true), | |
fd8b62cc EZ |
842 | }; |
843 | ||
9bdf401b FX |
844 | static const struct rockchip_pmu_info px30_pmu = { |
845 | .pwr_offset = 0x18, | |
846 | .status_offset = 0x20, | |
847 | .req_offset = 0x64, | |
848 | .idle_offset = 0x6c, | |
849 | .ack_offset = 0x6c, | |
850 | ||
851 | .num_domains = ARRAY_SIZE(px30_pm_domains), | |
852 | .domain_info = px30_pm_domains, | |
853 | }; | |
854 | ||
6f686e85 CW |
855 | static const struct rockchip_pmu_info rk3036_pmu = { |
856 | .req_offset = 0x148, | |
857 | .idle_offset = 0x14c, | |
858 | .ack_offset = 0x14c, | |
859 | ||
860 | .num_domains = ARRAY_SIZE(rk3036_pm_domains), | |
861 | .domain_info = rk3036_pm_domains, | |
862 | }; | |
863 | ||
24869610 HS |
864 | static const struct rockchip_pmu_info rk3066_pmu = { |
865 | .pwr_offset = 0x08, | |
866 | .status_offset = 0x0c, | |
867 | .req_offset = 0x38, /* PMU_MISC_CON1 */ | |
868 | .idle_offset = 0x0c, | |
869 | .ack_offset = 0x0c, | |
870 | ||
871 | .num_domains = ARRAY_SIZE(rk3066_pm_domains), | |
872 | .domain_info = rk3066_pm_domains, | |
873 | }; | |
874 | ||
277535f7 EZ |
875 | static const struct rockchip_pmu_info rk3128_pmu = { |
876 | .pwr_offset = 0x04, | |
877 | .status_offset = 0x08, | |
878 | .req_offset = 0x0c, | |
879 | .idle_offset = 0x10, | |
880 | .ack_offset = 0x10, | |
881 | ||
882 | .num_domains = ARRAY_SIZE(rk3128_pm_domains), | |
883 | .domain_info = rk3128_pm_domains, | |
884 | }; | |
885 | ||
a0d5e7d4 HS |
886 | static const struct rockchip_pmu_info rk3188_pmu = { |
887 | .pwr_offset = 0x08, | |
888 | .status_offset = 0x0c, | |
889 | .req_offset = 0x38, /* PMU_MISC_CON1 */ | |
890 | .idle_offset = 0x0c, | |
891 | .ack_offset = 0x0c, | |
892 | ||
893 | .num_domains = ARRAY_SIZE(rk3188_pm_domains), | |
894 | .domain_info = rk3188_pm_domains, | |
895 | }; | |
896 | ||
2dacbd10 EZ |
897 | static const struct rockchip_pmu_info rk3228_pmu = { |
898 | .req_offset = 0x40c, | |
899 | .idle_offset = 0x488, | |
900 | .ack_offset = 0x488, | |
901 | ||
902 | .num_domains = ARRAY_SIZE(rk3228_pm_domains), | |
903 | .domain_info = rk3228_pm_domains, | |
904 | }; | |
905 | ||
7c696693 CW |
906 | static const struct rockchip_pmu_info rk3288_pmu = { |
907 | .pwr_offset = 0x08, | |
908 | .status_offset = 0x0c, | |
909 | .req_offset = 0x10, | |
910 | .idle_offset = 0x14, | |
911 | .ack_offset = 0x14, | |
912 | ||
913 | .core_pwrcnt_offset = 0x34, | |
914 | .gpu_pwrcnt_offset = 0x3c, | |
915 | ||
916 | .core_power_transition_time = 24, /* 1us */ | |
917 | .gpu_power_transition_time = 24, /* 1us */ | |
918 | ||
919 | .num_domains = ARRAY_SIZE(rk3288_pm_domains), | |
920 | .domain_info = rk3288_pm_domains, | |
921 | }; | |
922 | ||
e9284603 EZ |
923 | static const struct rockchip_pmu_info rk3328_pmu = { |
924 | .req_offset = 0x414, | |
925 | .idle_offset = 0x484, | |
926 | .ack_offset = 0x484, | |
927 | ||
928 | .num_domains = ARRAY_SIZE(rk3328_pm_domains), | |
929 | .domain_info = rk3328_pm_domains, | |
930 | }; | |
931 | ||
bd85f4b7 EZ |
932 | static const struct rockchip_pmu_info rk3366_pmu = { |
933 | .pwr_offset = 0x0c, | |
934 | .status_offset = 0x10, | |
935 | .req_offset = 0x3c, | |
936 | .idle_offset = 0x40, | |
937 | .ack_offset = 0x40, | |
938 | ||
939 | .core_pwrcnt_offset = 0x48, | |
940 | .gpu_pwrcnt_offset = 0x50, | |
941 | ||
942 | .core_power_transition_time = 24, | |
943 | .gpu_power_transition_time = 24, | |
944 | ||
945 | .num_domains = ARRAY_SIZE(rk3366_pm_domains), | |
946 | .domain_info = rk3366_pm_domains, | |
947 | }; | |
948 | ||
8c20b67f | 949 | static const struct rockchip_pmu_info rk3368_pmu = { |
950 | .pwr_offset = 0x0c, | |
951 | .status_offset = 0x10, | |
952 | .req_offset = 0x3c, | |
953 | .idle_offset = 0x40, | |
954 | .ack_offset = 0x40, | |
955 | ||
956 | .core_pwrcnt_offset = 0x48, | |
957 | .gpu_pwrcnt_offset = 0x50, | |
958 | ||
959 | .core_power_transition_time = 24, | |
960 | .gpu_power_transition_time = 24, | |
961 | ||
962 | .num_domains = ARRAY_SIZE(rk3368_pm_domains), | |
963 | .domain_info = rk3368_pm_domains, | |
964 | }; | |
965 | ||
fd8b62cc EZ |
966 | static const struct rockchip_pmu_info rk3399_pmu = { |
967 | .pwr_offset = 0x14, | |
968 | .status_offset = 0x18, | |
969 | .req_offset = 0x60, | |
970 | .idle_offset = 0x64, | |
971 | .ack_offset = 0x68, | |
972 | ||
3f2fe461 | 973 | /* ARM Trusted Firmware manages power transition times */ |
fd8b62cc EZ |
974 | |
975 | .num_domains = ARRAY_SIZE(rk3399_pm_domains), | |
976 | .domain_info = rk3399_pm_domains, | |
977 | }; | |
978 | ||
7c696693 | 979 | static const struct of_device_id rockchip_pm_domain_dt_match[] = { |
9bdf401b FX |
980 | { |
981 | .compatible = "rockchip,px30-power-controller", | |
982 | .data = (void *)&px30_pmu, | |
983 | }, | |
6f686e85 CW |
984 | { |
985 | .compatible = "rockchip,rk3036-power-controller", | |
986 | .data = (void *)&rk3036_pmu, | |
277535f7 | 987 | }, |
24869610 HS |
988 | { |
989 | .compatible = "rockchip,rk3066-power-controller", | |
990 | .data = (void *)&rk3066_pmu, | |
991 | }, | |
277535f7 EZ |
992 | { |
993 | .compatible = "rockchip,rk3128-power-controller", | |
994 | .data = (void *)&rk3128_pmu, | |
2dacbd10 | 995 | }, |
a0d5e7d4 HS |
996 | { |
997 | .compatible = "rockchip,rk3188-power-controller", | |
998 | .data = (void *)&rk3188_pmu, | |
999 | }, | |
2dacbd10 EZ |
1000 | { |
1001 | .compatible = "rockchip,rk3228-power-controller", | |
1002 | .data = (void *)&rk3228_pmu, | |
6f686e85 | 1003 | }, |
7c696693 CW |
1004 | { |
1005 | .compatible = "rockchip,rk3288-power-controller", | |
1006 | .data = (void *)&rk3288_pmu, | |
1007 | }, | |
e9284603 EZ |
1008 | { |
1009 | .compatible = "rockchip,rk3328-power-controller", | |
1010 | .data = (void *)&rk3328_pmu, | |
1011 | }, | |
bd85f4b7 EZ |
1012 | { |
1013 | .compatible = "rockchip,rk3366-power-controller", | |
1014 | .data = (void *)&rk3366_pmu, | |
1015 | }, | |
8c20b67f | 1016 | { |
1017 | .compatible = "rockchip,rk3368-power-controller", | |
1018 | .data = (void *)&rk3368_pmu, | |
1019 | }, | |
fd8b62cc EZ |
1020 | { |
1021 | .compatible = "rockchip,rk3399-power-controller", | |
1022 | .data = (void *)&rk3399_pmu, | |
1023 | }, | |
7c696693 CW |
1024 | { /* sentinel */ }, |
1025 | }; | |
1026 | ||
1027 | static struct platform_driver rockchip_pm_domain_driver = { | |
1028 | .probe = rockchip_pm_domain_probe, | |
1029 | .driver = { | |
1030 | .name = "rockchip-pm-domain", | |
1031 | .of_match_table = rockchip_pm_domain_dt_match, | |
1032 | /* | |
1033 | * We can't forcibly eject devices form power domain, | |
1034 | * so we can't really remove power domains once they | |
1035 | * were added. | |
1036 | */ | |
1037 | .suppress_bind_attrs = true, | |
1038 | }, | |
1039 | }; | |
1040 | ||
1041 | static int __init rockchip_pm_domain_drv_register(void) | |
1042 | { | |
1043 | return platform_driver_register(&rockchip_pm_domain_driver); | |
1044 | } | |
1045 | postcore_initcall(rockchip_pm_domain_drv_register); |