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 
rockchip_pmu_domain_is_idle(struct rockchip_pm_domain * pd)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 
rockchip_pmu_set_idle_request(struct rockchip_pm_domain * pd,bool idle)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 
rockchip_pmu_domain_is_on(struct rockchip_pm_domain * pd)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 
rockchip_do_pmu_set_power_domain(struct rockchip_pm_domain * pd,bool on)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 
rockchip_pd_power(struct rockchip_pm_domain * pd,bool power_on)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 
rockchip_pd_power_on(struct generic_pm_domain * domain)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 
rockchip_pd_power_off(struct generic_pm_domain * domain)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 
rockchip_pd_attach_dev(struct generic_pm_domain * genpd,struct device * dev)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 
rockchip_pd_detach_dev(struct generic_pm_domain * genpd,struct device * dev)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 
rockchip_pm_add_one_domain(struct rockchip_pmu * pmu,struct device_node * node)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 
rockchip_pm_remove_one_domain(struct rockchip_pm_domain * pd)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 
rockchip_pm_domain_cleanup(struct rockchip_pmu * pmu)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 
rockchip_configure_pd_cnt(struct rockchip_pmu * pmu,u32 domain_reg_offset,unsigned int count)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 
rockchip_pm_domain_probe(struct platform_device * pdev)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 			of_node_put(node);
423 			goto err_out;
424 		}
425 	}
426 
427 	if (error) {
428 		dev_dbg(dev, "no power domains defined\n");
429 		goto err_out;
430 	}
431 
432 	of_genpd_add_provider_onecell(np, &pmu->genpd_data);
433 
434 	return 0;
435 
436 err_out:
437 	rockchip_pm_domain_cleanup(pmu);
438 	return error;
439 }
440 
441 static const struct rockchip_domain_info rk3288_pm_domains[] = {
442 	[RK3288_PD_VIO]		= DOMAIN_RK3288(7, 7, 4),
443 	[RK3288_PD_HEVC]	= DOMAIN_RK3288(14, 10, 9),
444 	[RK3288_PD_VIDEO]	= DOMAIN_RK3288(8, 8, 3),
445 	[RK3288_PD_GPU]		= DOMAIN_RK3288(9, 9, 2),
446 };
447 
448 static const struct rockchip_pmu_info rk3288_pmu = {
449 	.pwr_offset = 0x08,
450 	.status_offset = 0x0c,
451 	.req_offset = 0x10,
452 	.idle_offset = 0x14,
453 	.ack_offset = 0x14,
454 
455 	.core_pwrcnt_offset = 0x34,
456 	.gpu_pwrcnt_offset = 0x3c,
457 
458 	.core_power_transition_time = 24, /* 1us */
459 	.gpu_power_transition_time = 24, /* 1us */
460 
461 	.num_domains = ARRAY_SIZE(rk3288_pm_domains),
462 	.domain_info = rk3288_pm_domains,
463 };
464 
465 static const struct of_device_id rockchip_pm_domain_dt_match[] = {
466 	{
467 		.compatible = "rockchip,rk3288-power-controller",
468 		.data = (void *)&rk3288_pmu,
469 	},
470 	{ /* sentinel */ },
471 };
472 
473 static struct platform_driver rockchip_pm_domain_driver = {
474 	.probe = rockchip_pm_domain_probe,
475 	.driver = {
476 		.name   = "rockchip-pm-domain",
477 		.of_match_table = rockchip_pm_domain_dt_match,
478 		/*
479 		 * We can't forcibly eject devices form power domain,
480 		 * so we can't really remove power domains once they
481 		 * were added.
482 		 */
483 		.suppress_bind_attrs = true,
484 	},
485 };
486 
rockchip_pm_domain_drv_register(void)487 static int __init rockchip_pm_domain_drv_register(void)
488 {
489 	return platform_driver_register(&rockchip_pm_domain_driver);
490 }
491 postcore_initcall(rockchip_pm_domain_drv_register);
492