Merge remote-tracking branches 'regulator/topic/discharge', 'regulator/topic/fan53555...
[deliverable/linux.git] / drivers / soc / rockchip / pm_domains.c
1 /*
2 * Rockchip Generic power domain support.
3 *
4 * Copyright (c) 2015 ROCKCHIP, Co. Ltd.
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 version 2 as
8 * published by the Free Software Foundation.
9 */
10
11 #include <linux/io.h>
12 #include <linux/err.h>
13 #include <linux/pm_clock.h>
14 #include <linux/pm_domain.h>
15 #include <linux/of_address.h>
16 #include <linux/of_platform.h>
17 #include <linux/clk.h>
18 #include <linux/regmap.h>
19 #include <linux/mfd/syscon.h>
20 #include <dt-bindings/power/rk3288-power.h>
21
22 struct rockchip_domain_info {
23 int pwr_mask;
24 int status_mask;
25 int req_mask;
26 int idle_mask;
27 int ack_mask;
28 };
29
30 struct rockchip_pmu_info {
31 u32 pwr_offset;
32 u32 status_offset;
33 u32 req_offset;
34 u32 idle_offset;
35 u32 ack_offset;
36
37 u32 core_pwrcnt_offset;
38 u32 gpu_pwrcnt_offset;
39
40 unsigned int core_power_transition_time;
41 unsigned int gpu_power_transition_time;
42
43 int num_domains;
44 const struct rockchip_domain_info *domain_info;
45 };
46
47 struct rockchip_pm_domain {
48 struct generic_pm_domain genpd;
49 const struct rockchip_domain_info *info;
50 struct rockchip_pmu *pmu;
51 int num_clks;
52 struct clk *clks[];
53 };
54
55 struct rockchip_pmu {
56 struct device *dev;
57 struct regmap *regmap;
58 const struct rockchip_pmu_info *info;
59 struct mutex mutex; /* mutex lock for pmu */
60 struct genpd_onecell_data genpd_data;
61 struct generic_pm_domain *domains[];
62 };
63
64 #define to_rockchip_pd(gpd) container_of(gpd, struct rockchip_pm_domain, genpd)
65
66 #define DOMAIN(pwr, status, req, idle, ack) \
67 { \
68 .pwr_mask = BIT(pwr), \
69 .status_mask = BIT(status), \
70 .req_mask = BIT(req), \
71 .idle_mask = BIT(idle), \
72 .ack_mask = BIT(ack), \
73 }
74
75 #define DOMAIN_RK3288(pwr, status, req) \
76 DOMAIN(pwr, status, req, req, (req) + 16)
77
78 static bool rockchip_pmu_domain_is_idle(struct rockchip_pm_domain *pd)
79 {
80 struct rockchip_pmu *pmu = pd->pmu;
81 const struct rockchip_domain_info *pd_info = pd->info;
82 unsigned int val;
83
84 regmap_read(pmu->regmap, pmu->info->idle_offset, &val);
85 return (val & pd_info->idle_mask) == pd_info->idle_mask;
86 }
87
88 static int rockchip_pmu_set_idle_request(struct rockchip_pm_domain *pd,
89 bool idle)
90 {
91 const struct rockchip_domain_info *pd_info = pd->info;
92 struct rockchip_pmu *pmu = pd->pmu;
93 unsigned int val;
94
95 regmap_update_bits(pmu->regmap, pmu->info->req_offset,
96 pd_info->req_mask, idle ? -1U : 0);
97
98 dsb(sy);
99
100 do {
101 regmap_read(pmu->regmap, pmu->info->ack_offset, &val);
102 } while ((val & pd_info->ack_mask) != (idle ? pd_info->ack_mask : 0));
103
104 while (rockchip_pmu_domain_is_idle(pd) != idle)
105 cpu_relax();
106
107 return 0;
108 }
109
110 static bool rockchip_pmu_domain_is_on(struct rockchip_pm_domain *pd)
111 {
112 struct rockchip_pmu *pmu = pd->pmu;
113 unsigned int val;
114
115 regmap_read(pmu->regmap, pmu->info->status_offset, &val);
116
117 /* 1'b0: power on, 1'b1: power off */
118 return !(val & pd->info->status_mask);
119 }
120
121 static void rockchip_do_pmu_set_power_domain(struct rockchip_pm_domain *pd,
122 bool on)
123 {
124 struct rockchip_pmu *pmu = pd->pmu;
125
126 regmap_update_bits(pmu->regmap, pmu->info->pwr_offset,
127 pd->info->pwr_mask, on ? 0 : -1U);
128
129 dsb(sy);
130
131 while (rockchip_pmu_domain_is_on(pd) != on)
132 cpu_relax();
133 }
134
135 static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on)
136 {
137 int i;
138
139 mutex_lock(&pd->pmu->mutex);
140
141 if (rockchip_pmu_domain_is_on(pd) != power_on) {
142 for (i = 0; i < pd->num_clks; i++)
143 clk_enable(pd->clks[i]);
144
145 if (!power_on) {
146 /* FIXME: add code to save AXI_QOS */
147
148 /* if powering down, idle request to NIU first */
149 rockchip_pmu_set_idle_request(pd, true);
150 }
151
152 rockchip_do_pmu_set_power_domain(pd, power_on);
153
154 if (power_on) {
155 /* if powering up, leave idle mode */
156 rockchip_pmu_set_idle_request(pd, false);
157
158 /* FIXME: add code to restore AXI_QOS */
159 }
160
161 for (i = pd->num_clks - 1; i >= 0; i--)
162 clk_disable(pd->clks[i]);
163 }
164
165 mutex_unlock(&pd->pmu->mutex);
166 return 0;
167 }
168
169 static int rockchip_pd_power_on(struct generic_pm_domain *domain)
170 {
171 struct rockchip_pm_domain *pd = to_rockchip_pd(domain);
172
173 return rockchip_pd_power(pd, true);
174 }
175
176 static int rockchip_pd_power_off(struct generic_pm_domain *domain)
177 {
178 struct rockchip_pm_domain *pd = to_rockchip_pd(domain);
179
180 return rockchip_pd_power(pd, false);
181 }
182
183 static int rockchip_pd_attach_dev(struct generic_pm_domain *genpd,
184 struct device *dev)
185 {
186 struct clk *clk;
187 int i;
188 int error;
189
190 dev_dbg(dev, "attaching to power domain '%s'\n", genpd->name);
191
192 error = pm_clk_create(dev);
193 if (error) {
194 dev_err(dev, "pm_clk_create failed %d\n", error);
195 return error;
196 }
197
198 i = 0;
199 while ((clk = of_clk_get(dev->of_node, i++)) && !IS_ERR(clk)) {
200 dev_dbg(dev, "adding clock '%pC' to list of PM clocks\n", clk);
201 error = pm_clk_add_clk(dev, clk);
202 if (error) {
203 dev_err(dev, "pm_clk_add_clk failed %d\n", error);
204 clk_put(clk);
205 pm_clk_destroy(dev);
206 return error;
207 }
208 }
209
210 return 0;
211 }
212
213 static void rockchip_pd_detach_dev(struct generic_pm_domain *genpd,
214 struct device *dev)
215 {
216 dev_dbg(dev, "detaching from power domain '%s'\n", genpd->name);
217
218 pm_clk_destroy(dev);
219 }
220
221 static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
222 struct device_node *node)
223 {
224 const struct rockchip_domain_info *pd_info;
225 struct rockchip_pm_domain *pd;
226 struct clk *clk;
227 int clk_cnt;
228 int i;
229 u32 id;
230 int error;
231
232 error = of_property_read_u32(node, "reg", &id);
233 if (error) {
234 dev_err(pmu->dev,
235 "%s: failed to retrieve domain id (reg): %d\n",
236 node->name, error);
237 return -EINVAL;
238 }
239
240 if (id >= pmu->info->num_domains) {
241 dev_err(pmu->dev, "%s: invalid domain id %d\n",
242 node->name, id);
243 return -EINVAL;
244 }
245
246 pd_info = &pmu->info->domain_info[id];
247 if (!pd_info) {
248 dev_err(pmu->dev, "%s: undefined domain id %d\n",
249 node->name, id);
250 return -EINVAL;
251 }
252
253 clk_cnt = of_count_phandle_with_args(node, "clocks", "#clock-cells");
254 pd = devm_kzalloc(pmu->dev,
255 sizeof(*pd) + clk_cnt * sizeof(pd->clks[0]),
256 GFP_KERNEL);
257 if (!pd)
258 return -ENOMEM;
259
260 pd->info = pd_info;
261 pd->pmu = pmu;
262
263 for (i = 0; i < clk_cnt; i++) {
264 clk = of_clk_get(node, i);
265 if (IS_ERR(clk)) {
266 error = PTR_ERR(clk);
267 dev_err(pmu->dev,
268 "%s: failed to get clk at index %d: %d\n",
269 node->name, i, error);
270 goto err_out;
271 }
272
273 error = clk_prepare(clk);
274 if (error) {
275 dev_err(pmu->dev,
276 "%s: failed to prepare clk %pC (index %d): %d\n",
277 node->name, clk, i, error);
278 clk_put(clk);
279 goto err_out;
280 }
281
282 pd->clks[pd->num_clks++] = clk;
283
284 dev_dbg(pmu->dev, "added clock '%pC' to domain '%s'\n",
285 clk, node->name);
286 }
287
288 error = rockchip_pd_power(pd, true);
289 if (error) {
290 dev_err(pmu->dev,
291 "failed to power on domain '%s': %d\n",
292 node->name, error);
293 goto err_out;
294 }
295
296 pd->genpd.name = node->name;
297 pd->genpd.power_off = rockchip_pd_power_off;
298 pd->genpd.power_on = rockchip_pd_power_on;
299 pd->genpd.attach_dev = rockchip_pd_attach_dev;
300 pd->genpd.detach_dev = rockchip_pd_detach_dev;
301 pd->genpd.flags = GENPD_FLAG_PM_CLK;
302 pm_genpd_init(&pd->genpd, NULL, false);
303
304 pmu->genpd_data.domains[id] = &pd->genpd;
305 return 0;
306
307 err_out:
308 while (--i >= 0) {
309 clk_unprepare(pd->clks[i]);
310 clk_put(pd->clks[i]);
311 }
312 return error;
313 }
314
315 static void rockchip_pm_remove_one_domain(struct rockchip_pm_domain *pd)
316 {
317 int i;
318
319 for (i = 0; i < pd->num_clks; i++) {
320 clk_unprepare(pd->clks[i]);
321 clk_put(pd->clks[i]);
322 }
323
324 /* protect the zeroing of pm->num_clks */
325 mutex_lock(&pd->pmu->mutex);
326 pd->num_clks = 0;
327 mutex_unlock(&pd->pmu->mutex);
328
329 /* devm will free our memory */
330 }
331
332 static void rockchip_pm_domain_cleanup(struct rockchip_pmu *pmu)
333 {
334 struct generic_pm_domain *genpd;
335 struct rockchip_pm_domain *pd;
336 int i;
337
338 for (i = 0; i < pmu->genpd_data.num_domains; i++) {
339 genpd = pmu->genpd_data.domains[i];
340 if (genpd) {
341 pd = to_rockchip_pd(genpd);
342 rockchip_pm_remove_one_domain(pd);
343 }
344 }
345
346 /* devm will free our memory */
347 }
348
349 static void rockchip_configure_pd_cnt(struct rockchip_pmu *pmu,
350 u32 domain_reg_offset,
351 unsigned int count)
352 {
353 /* First configure domain power down transition count ... */
354 regmap_write(pmu->regmap, domain_reg_offset, count);
355 /* ... and then power up count. */
356 regmap_write(pmu->regmap, domain_reg_offset + 4, count);
357 }
358
359 static int rockchip_pm_domain_probe(struct platform_device *pdev)
360 {
361 struct device *dev = &pdev->dev;
362 struct device_node *np = dev->of_node;
363 struct device_node *node;
364 struct device *parent;
365 struct rockchip_pmu *pmu;
366 const struct of_device_id *match;
367 const struct rockchip_pmu_info *pmu_info;
368 int error;
369
370 if (!np) {
371 dev_err(dev, "device tree node not found\n");
372 return -ENODEV;
373 }
374
375 match = of_match_device(dev->driver->of_match_table, dev);
376 if (!match || !match->data) {
377 dev_err(dev, "missing pmu data\n");
378 return -EINVAL;
379 }
380
381 pmu_info = match->data;
382
383 pmu = devm_kzalloc(dev,
384 sizeof(*pmu) +
385 pmu_info->num_domains * sizeof(pmu->domains[0]),
386 GFP_KERNEL);
387 if (!pmu)
388 return -ENOMEM;
389
390 pmu->dev = &pdev->dev;
391 mutex_init(&pmu->mutex);
392
393 pmu->info = pmu_info;
394
395 pmu->genpd_data.domains = pmu->domains;
396 pmu->genpd_data.num_domains = pmu_info->num_domains;
397
398 parent = dev->parent;
399 if (!parent) {
400 dev_err(dev, "no parent for syscon devices\n");
401 return -ENODEV;
402 }
403
404 pmu->regmap = syscon_node_to_regmap(parent->of_node);
405
406 /*
407 * Configure power up and down transition delays for CORE
408 * and GPU domains.
409 */
410 rockchip_configure_pd_cnt(pmu, pmu_info->core_pwrcnt_offset,
411 pmu_info->core_power_transition_time);
412 rockchip_configure_pd_cnt(pmu, pmu_info->gpu_pwrcnt_offset,
413 pmu_info->gpu_power_transition_time);
414
415 error = -ENODEV;
416
417 for_each_available_child_of_node(np, node) {
418 error = rockchip_pm_add_one_domain(pmu, node);
419 if (error) {
420 dev_err(dev, "failed to handle node %s: %d\n",
421 node->name, error);
422 goto err_out;
423 }
424 }
425
426 if (error) {
427 dev_dbg(dev, "no power domains defined\n");
428 goto err_out;
429 }
430
431 of_genpd_add_provider_onecell(np, &pmu->genpd_data);
432
433 return 0;
434
435 err_out:
436 rockchip_pm_domain_cleanup(pmu);
437 return error;
438 }
439
440 static const struct rockchip_domain_info rk3288_pm_domains[] = {
441 [RK3288_PD_VIO] = DOMAIN_RK3288(7, 7, 4),
442 [RK3288_PD_HEVC] = DOMAIN_RK3288(14, 10, 9),
443 [RK3288_PD_VIDEO] = DOMAIN_RK3288(8, 8, 3),
444 [RK3288_PD_GPU] = DOMAIN_RK3288(9, 9, 2),
445 };
446
447 static const struct rockchip_pmu_info rk3288_pmu = {
448 .pwr_offset = 0x08,
449 .status_offset = 0x0c,
450 .req_offset = 0x10,
451 .idle_offset = 0x14,
452 .ack_offset = 0x14,
453
454 .core_pwrcnt_offset = 0x34,
455 .gpu_pwrcnt_offset = 0x3c,
456
457 .core_power_transition_time = 24, /* 1us */
458 .gpu_power_transition_time = 24, /* 1us */
459
460 .num_domains = ARRAY_SIZE(rk3288_pm_domains),
461 .domain_info = rk3288_pm_domains,
462 };
463
464 static const struct of_device_id rockchip_pm_domain_dt_match[] = {
465 {
466 .compatible = "rockchip,rk3288-power-controller",
467 .data = (void *)&rk3288_pmu,
468 },
469 { /* sentinel */ },
470 };
471
472 static struct platform_driver rockchip_pm_domain_driver = {
473 .probe = rockchip_pm_domain_probe,
474 .driver = {
475 .name = "rockchip-pm-domain",
476 .of_match_table = rockchip_pm_domain_dt_match,
477 /*
478 * We can't forcibly eject devices form power domain,
479 * so we can't really remove power domains once they
480 * were added.
481 */
482 .suppress_bind_attrs = true,
483 },
484 };
485
486 static int __init rockchip_pm_domain_drv_register(void)
487 {
488 return platform_driver_register(&rockchip_pm_domain_driver);
489 }
490 postcore_initcall(rockchip_pm_domain_drv_register);
This page took 0.040401 seconds and 5 git commands to generate.