xref: /linux/drivers/pmdomain/imx/imx93-blk-ctrl.c (revision fab183d632628381b466a41479489541ac0e29a0)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright 2022 NXP, Peng Fan <peng.fan@nxp.com>
4  */
5 
6 #include <linux/clk.h>
7 #include <linux/device.h>
8 #include <linux/module.h>
9 #include <linux/of.h>
10 #include <linux/of_platform.h>
11 #include <linux/platform_device.h>
12 #include <linux/pm_domain.h>
13 #include <linux/pm_runtime.h>
14 #include <linux/regmap.h>
15 #include <linux/sizes.h>
16 
17 #include <dt-bindings/power/fsl,imx93-power.h>
18 
19 #define BLK_SFT_RSTN	0x0
20 #define BLK_CLK_EN	0x4
21 #define BLK_MAX_CLKS	4
22 
23 #define DOMAIN_MAX_CLKS 4
24 
25 #define LCDIF_QOS_REG		0xC
26 #define LCDIF_DEFAULT_QOS_OFF	12
27 #define LCDIF_CFG_QOS_OFF	8
28 
29 #define PXP_QOS_REG		0x10
30 #define PXP_R_DEFAULT_QOS_OFF	28
31 #define PXP_R_CFG_QOS_OFF	24
32 #define PXP_W_DEFAULT_QOS_OFF	20
33 #define PXP_W_CFG_QOS_OFF	16
34 
35 #define ISI_CACHE_REG		0x14
36 
37 #define ISI_QOS_REG		0x1C
38 #define ISI_V_DEFAULT_QOS_OFF	28
39 #define ISI_V_CFG_QOS_OFF	24
40 #define ISI_U_DEFAULT_QOS_OFF	20
41 #define ISI_U_CFG_QOS_OFF	16
42 #define ISI_Y_R_DEFAULT_QOS_OFF	12
43 #define ISI_Y_R_CFG_QOS_OFF	8
44 #define ISI_Y_W_DEFAULT_QOS_OFF	4
45 #define ISI_Y_W_CFG_QOS_OFF	0
46 
47 #define PRIO_MASK		0xF
48 
49 #define PRIO(X)			(X)
50 
51 #define BLK_CTRL_NO_PARENT	UINT_MAX
52 
53 struct imx93_blk_ctrl_domain;
54 
55 struct imx93_blk_ctrl {
56 	struct device *dev;
57 	struct regmap *regmap;
58 	int num_clks;
59 	struct clk_bulk_data clks[BLK_MAX_CLKS];
60 	struct imx93_blk_ctrl_domain *domains;
61 	struct genpd_onecell_data onecell_data;
62 };
63 
64 #define DOMAIN_MAX_QOS 4
65 
66 struct imx93_blk_ctrl_qos {
67 	u32 reg;
68 	u32 cfg_off;
69 	u32 default_prio;
70 	u32 cfg_prio;
71 };
72 
73 struct imx93_blk_ctrl_subdomain_link {
74 	struct generic_pm_domain *parent;
75 	struct generic_pm_domain *subdomain;
76 };
77 
78 struct imx93_blk_ctrl_domain_data {
79 	const char *name;
80 	const char * const *clk_names;
81 	int num_clks;
82 	u32 rst_mask;
83 	u32 clk_mask;
84 	u32 parent;
85 	int num_qos;
86 	struct imx93_blk_ctrl_qos qos[DOMAIN_MAX_QOS];
87 };
88 
89 struct imx93_blk_ctrl_domain {
90 	struct generic_pm_domain genpd;
91 	const struct imx93_blk_ctrl_domain_data *data;
92 	struct clk_bulk_data clks[DOMAIN_MAX_CLKS];
93 	struct imx93_blk_ctrl *bc;
94 };
95 
96 struct imx93_blk_ctrl_data {
97 	const struct imx93_blk_ctrl_domain_data *domains;
98 	u32 skip_mask;
99 	int num_domains;
100 	const char * const *clk_names;
101 	int num_clks;
102 	const struct regmap_access_table *reg_access_table;
103 };
104 
105 static inline struct imx93_blk_ctrl_domain *
to_imx93_blk_ctrl_domain(struct generic_pm_domain * genpd)106 to_imx93_blk_ctrl_domain(struct generic_pm_domain *genpd)
107 {
108 	return container_of(genpd, struct imx93_blk_ctrl_domain, genpd);
109 }
110 
imx93_blk_ctrl_set_qos(struct imx93_blk_ctrl_domain * domain)111 static int imx93_blk_ctrl_set_qos(struct imx93_blk_ctrl_domain *domain)
112 {
113 	const struct imx93_blk_ctrl_domain_data *data = domain->data;
114 	struct imx93_blk_ctrl *bc = domain->bc;
115 	const struct imx93_blk_ctrl_qos *qos;
116 	u32 val, mask;
117 	int i;
118 
119 	for (i = 0; i < data->num_qos; i++) {
120 		qos = &data->qos[i];
121 
122 		mask = PRIO_MASK << qos->cfg_off;
123 		mask |= PRIO_MASK << (qos->cfg_off + 4);
124 		val = qos->cfg_prio << qos->cfg_off;
125 		val |= qos->default_prio << (qos->cfg_off + 4);
126 
127 		regmap_write_bits(bc->regmap, qos->reg, mask, val);
128 
129 		dev_dbg(bc->dev, "data->qos[i].reg 0x%x 0x%x\n", qos->reg, val);
130 	}
131 
132 	return 0;
133 }
134 
imx93_blk_ctrl_power_on(struct generic_pm_domain * genpd)135 static int imx93_blk_ctrl_power_on(struct generic_pm_domain *genpd)
136 {
137 	struct imx93_blk_ctrl_domain *domain = to_imx93_blk_ctrl_domain(genpd);
138 	const struct imx93_blk_ctrl_domain_data *data = domain->data;
139 	struct imx93_blk_ctrl *bc = domain->bc;
140 	int ret;
141 
142 	ret = clk_bulk_prepare_enable(bc->num_clks, bc->clks);
143 	if (ret) {
144 		dev_err(bc->dev, "failed to enable bus clocks\n");
145 		return ret;
146 	}
147 
148 	ret = clk_bulk_prepare_enable(data->num_clks, domain->clks);
149 	if (ret) {
150 		clk_bulk_disable_unprepare(bc->num_clks, bc->clks);
151 		dev_err(bc->dev, "failed to enable clocks\n");
152 		return ret;
153 	}
154 
155 	ret = pm_runtime_get_sync(bc->dev);
156 	if (ret < 0) {
157 		pm_runtime_put_noidle(bc->dev);
158 		dev_err(bc->dev, "failed to power up domain\n");
159 		goto disable_clk;
160 	}
161 
162 	/* ungate clk */
163 	regmap_clear_bits(bc->regmap, BLK_CLK_EN, data->clk_mask);
164 
165 	/* release reset */
166 	regmap_set_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask);
167 
168 	dev_dbg(bc->dev, "pd_on: name: %s\n", genpd->name);
169 
170 	return imx93_blk_ctrl_set_qos(domain);
171 
172 disable_clk:
173 	clk_bulk_disable_unprepare(data->num_clks, domain->clks);
174 
175 	clk_bulk_disable_unprepare(bc->num_clks, bc->clks);
176 
177 	return ret;
178 }
179 
imx93_blk_ctrl_power_off(struct generic_pm_domain * genpd)180 static int imx93_blk_ctrl_power_off(struct generic_pm_domain *genpd)
181 {
182 	struct imx93_blk_ctrl_domain *domain = to_imx93_blk_ctrl_domain(genpd);
183 	const struct imx93_blk_ctrl_domain_data *data = domain->data;
184 	struct imx93_blk_ctrl *bc = domain->bc;
185 
186 	dev_dbg(bc->dev, "pd_off: name: %s\n", genpd->name);
187 
188 	regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask);
189 	regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask);
190 
191 	pm_runtime_put(bc->dev);
192 
193 	clk_bulk_disable_unprepare(data->num_clks, domain->clks);
194 
195 	clk_bulk_disable_unprepare(bc->num_clks, bc->clks);
196 
197 	return 0;
198 }
199 
imx93_release_genpd_provider(void * data)200 static void imx93_release_genpd_provider(void *data)
201 {
202 	struct device_node *of_node = data;
203 
204 	of_genpd_del_provider(of_node);
205 }
206 
imx93_release_pm_genpd(void * data)207 static void imx93_release_pm_genpd(void *data)
208 {
209 	struct generic_pm_domain *genpd = data;
210 
211 	pm_genpd_remove(genpd);
212 }
213 
imx93_release_subdomain(void * data)214 static void imx93_release_subdomain(void *data)
215 {
216 	struct imx93_blk_ctrl_subdomain_link *link = data;
217 
218 	pm_genpd_remove_subdomain(link->parent, link->subdomain);
219 }
220 
221 static struct lock_class_key blk_ctrl_genpd_lock_class;
222 
imx93_blk_ctrl_probe(struct platform_device * pdev)223 static int imx93_blk_ctrl_probe(struct platform_device *pdev)
224 {
225 	struct device *dev = &pdev->dev;
226 	const struct imx93_blk_ctrl_data *bc_data = of_device_get_match_data(dev);
227 	struct imx93_blk_ctrl *bc;
228 	void __iomem *base;
229 	int i, ret;
230 
231 	struct regmap_config regmap_config = {
232 		.reg_bits	= 32,
233 		.val_bits	= 32,
234 		.reg_stride	= 4,
235 		.rd_table	= bc_data->reg_access_table,
236 		.wr_table	= bc_data->reg_access_table,
237 		.max_register   = SZ_4K,
238 	};
239 
240 	bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL);
241 	if (!bc)
242 		return -ENOMEM;
243 
244 	bc->dev = dev;
245 
246 	base = devm_platform_ioremap_resource(pdev, 0);
247 	if (IS_ERR(base))
248 		return PTR_ERR(base);
249 
250 	bc->regmap = devm_regmap_init_mmio(dev, base, &regmap_config);
251 	if (IS_ERR(bc->regmap))
252 		return dev_err_probe(dev, PTR_ERR(bc->regmap),
253 				     "failed to init regmap\n");
254 
255 	bc->domains = devm_kcalloc(dev, bc_data->num_domains,
256 				   sizeof(struct imx93_blk_ctrl_domain),
257 				   GFP_KERNEL);
258 	if (!bc->domains)
259 		return -ENOMEM;
260 
261 	bc->onecell_data.num_domains = bc_data->num_domains;
262 	bc->onecell_data.domains =
263 		devm_kcalloc(dev, bc_data->num_domains,
264 			     sizeof(struct generic_pm_domain *), GFP_KERNEL);
265 	if (!bc->onecell_data.domains)
266 		return -ENOMEM;
267 
268 	for (i = 0; i < bc_data->num_clks; i++)
269 		bc->clks[i].id = bc_data->clk_names[i];
270 	bc->num_clks = bc_data->num_clks;
271 
272 	ret = devm_clk_bulk_get(dev, bc->num_clks, bc->clks);
273 	if (ret)
274 		return dev_err_probe(dev, ret, "failed to get bus clock\n");
275 
276 	for (i = 0; i < bc_data->num_domains; i++) {
277 		const struct imx93_blk_ctrl_domain_data *data = &bc_data->domains[i];
278 		struct imx93_blk_ctrl_domain *domain = &bc->domains[i];
279 		int j;
280 
281 		domain->data = data;
282 		if (bc_data->skip_mask & BIT(i))
283 			continue;
284 
285 		for (j = 0; j < data->num_clks; j++)
286 			domain->clks[j].id = data->clk_names[j];
287 
288 		ret = devm_clk_bulk_get(dev, data->num_clks, domain->clks);
289 		if (ret)
290 			return dev_err_probe(dev, ret, "failed to get clock\n");
291 
292 		domain->genpd.name = data->name;
293 		domain->genpd.power_on = imx93_blk_ctrl_power_on;
294 		domain->genpd.power_off = imx93_blk_ctrl_power_off;
295 		domain->bc = bc;
296 
297 		ret = pm_genpd_init(&domain->genpd, NULL, true);
298 		if (ret)
299 			return dev_err_probe(dev, ret, "failed to init power domain\n");
300 
301 		ret = devm_add_action_or_reset(dev, imx93_release_pm_genpd, &domain->genpd);
302 		if (ret)
303 			return dev_err_probe(dev, ret, "failed to add pm_genpd release callback\n");
304 		/*
305 		 * We use runtime PM to trigger power on/off of the upstream GPC
306 		 * domain, as a strict hierarchical parent/child power domain
307 		 * setup doesn't allow us to meet the sequencing requirements.
308 		 * This means we have nested locking of genpd locks, without the
309 		 * nesting being visible at the genpd level, so we need a
310 		 * separate lock class to make lockdep aware of the fact that
311 		 * this are separate domain locks that can be nested without a
312 		 * self-deadlock.
313 		 */
314 		lockdep_set_class(&domain->genpd.mlock,
315 				  &blk_ctrl_genpd_lock_class);
316 
317 		bc->onecell_data.domains[i] = &domain->genpd;
318 	}
319 
320 	for (i = 0; i < bc_data->num_domains; i++) {
321 		struct imx93_blk_ctrl_domain *domain = &bc->domains[i];
322 		const struct imx93_blk_ctrl_domain_data *data = domain->data;
323 		struct imx93_blk_ctrl_subdomain_link *link;
324 
325 		if (bc_data->skip_mask & BIT(i) ||
326 		    data->parent == BLK_CTRL_NO_PARENT)
327 			continue;
328 
329 		link = devm_kzalloc(dev, sizeof(*link), GFP_KERNEL);
330 		if (!link)
331 			return -ENOMEM;
332 
333 		link->parent = &bc->domains[data->parent].genpd;
334 		link->subdomain = &domain->genpd;
335 
336 		ret = pm_genpd_add_subdomain(&bc->domains[data->parent].genpd,
337 					     &domain->genpd);
338 		if (ret)
339 			return dev_err_probe(dev, ret, "failed to add subdomain %s\n",
340 					     domain->genpd.name);
341 
342 		ret = devm_add_action_or_reset(dev, imx93_release_subdomain, link);
343 		if (ret)
344 			return dev_err_probe(dev, ret,
345 					     "failed to add subdomain release callback\n");
346 	}
347 
348 	ret = devm_pm_runtime_enable(dev);
349 	if (ret)
350 		return dev_err_probe(dev, ret, "failed to enable pm-runtime\n");
351 
352 	ret = of_genpd_add_provider_onecell(dev->of_node, &bc->onecell_data);
353 	if (ret)
354 		return dev_err_probe(dev, ret, "failed to add power domain provider\n");
355 
356 	ret = devm_add_action_or_reset(dev, imx93_release_genpd_provider, dev->of_node);
357 	if (ret)
358 		return dev_err_probe(dev, ret, "failed to add genpd_provider release callback\n");
359 
360 	ret = devm_of_platform_populate(dev);
361 	if (ret)
362 		return dev_err_probe(dev, ret, "failed to populate blk-ctrl sub-devices\n");
363 
364 	return 0;
365 }
366 
367 static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] = {
368 	[IMX93_MEDIABLK_PD_MIPI_DSI] = {
369 		.name = "mediablk-mipi-dsi",
370 		.clk_names = (const char *[]){ "dsi" },
371 		.num_clks = 1,
372 		.rst_mask = BIT(11),
373 		.clk_mask = BIT(11),
374 		.parent = IMX93_MEDIABLK_PD_MIPI_PHY,
375 	},
376 	[IMX93_MEDIABLK_PD_MIPI_CSI] = {
377 		.name = "mediablk-mipi-csi",
378 		.clk_names = (const char *[]){ "cam", "csi" },
379 		.num_clks = 2,
380 		.rst_mask = BIT(9) | BIT(10),
381 		.clk_mask = BIT(9) | BIT(10),
382 		.parent = IMX93_MEDIABLK_PD_MIPI_PHY,
383 	},
384 	[IMX93_MEDIABLK_PD_PXP] = {
385 		.name = "mediablk-pxp",
386 		.clk_names = (const char *[]){ "pxp" },
387 		.num_clks = 1,
388 		.rst_mask = BIT(7) | BIT(8),
389 		.clk_mask = BIT(7) | BIT(8),
390 		.parent = BLK_CTRL_NO_PARENT,
391 		.num_qos = 2,
392 		.qos = {
393 			{
394 				.reg = PXP_QOS_REG,
395 				.cfg_off = PXP_R_CFG_QOS_OFF,
396 				.default_prio = PRIO(3),
397 				.cfg_prio = PRIO(6),
398 			}, {
399 				.reg = PXP_QOS_REG,
400 				.cfg_off = PXP_W_CFG_QOS_OFF,
401 				.default_prio = PRIO(3),
402 				.cfg_prio = PRIO(6),
403 			}
404 		}
405 	},
406 	[IMX93_MEDIABLK_PD_LCDIF] = {
407 		.name = "mediablk-lcdif",
408 		.clk_names = (const char *[]){ "disp", "lcdif" },
409 		.num_clks = 2,
410 		.rst_mask = BIT(4) | BIT(5) | BIT(6),
411 		.clk_mask = BIT(4) | BIT(5) | BIT(6),
412 		.parent = BLK_CTRL_NO_PARENT,
413 		.num_qos = 1,
414 		.qos = {
415 			{
416 			.reg = LCDIF_QOS_REG,
417 			.cfg_off = LCDIF_CFG_QOS_OFF,
418 			.default_prio = PRIO(3),
419 			.cfg_prio = PRIO(7),
420 			}
421 		}
422 	},
423 	[IMX93_MEDIABLK_PD_ISI] = {
424 		.name = "mediablk-isi",
425 		.clk_names = (const char *[]){ "isi" },
426 		.num_clks = 1,
427 		.rst_mask = BIT(2) | BIT(3),
428 		.clk_mask = BIT(2) | BIT(3),
429 		.parent = BLK_CTRL_NO_PARENT,
430 		.num_qos = 4,
431 		.qos = {
432 			{
433 				.reg = ISI_QOS_REG,
434 				.cfg_off = ISI_Y_W_CFG_QOS_OFF,
435 				.default_prio = PRIO(3),
436 				.cfg_prio = PRIO(7),
437 			}, {
438 				.reg = ISI_QOS_REG,
439 				.cfg_off = ISI_Y_R_CFG_QOS_OFF,
440 				.default_prio = PRIO(3),
441 				.cfg_prio = PRIO(7),
442 			}, {
443 				.reg = ISI_QOS_REG,
444 				.cfg_off = ISI_U_CFG_QOS_OFF,
445 				.default_prio = PRIO(3),
446 				.cfg_prio = PRIO(7),
447 			}, {
448 				.reg = ISI_QOS_REG,
449 				.cfg_off = ISI_V_CFG_QOS_OFF,
450 				.default_prio = PRIO(3),
451 				.cfg_prio = PRIO(7),
452 			}
453 		}
454 	},
455 	[IMX93_MEDIABLK_PD_MIPI_PHY] = {
456 		.name = "mediablk-mipi-phy",
457 		.clk_names = NULL,
458 		.num_clks = 0,
459 		.rst_mask = BIT(12),
460 		.clk_mask = BIT(12),
461 		.parent = BLK_CTRL_NO_PARENT,
462 	},
463 };
464 
465 static const struct regmap_range imx93_media_blk_ctl_yes_ranges[] = {
466 	regmap_reg_range(BLK_SFT_RSTN, BLK_CLK_EN),
467 	regmap_reg_range(LCDIF_QOS_REG, ISI_CACHE_REG),
468 	regmap_reg_range(ISI_QOS_REG, ISI_QOS_REG),
469 };
470 
471 static const struct regmap_access_table imx93_media_blk_ctl_access_table = {
472 	.yes_ranges = imx93_media_blk_ctl_yes_ranges,
473 	.n_yes_ranges = ARRAY_SIZE(imx93_media_blk_ctl_yes_ranges),
474 };
475 
476 static const char * const media_blk_clk_names[] = {
477 	"axi", "apb", "nic"
478 };
479 
480 static const struct imx93_blk_ctrl_data imx91_media_blk_ctl_dev_data = {
481 	.domains = imx93_media_blk_ctl_domain_data,
482 	.skip_mask = BIT(IMX93_MEDIABLK_PD_MIPI_DSI) | BIT(IMX93_MEDIABLK_PD_PXP),
483 	.num_domains = ARRAY_SIZE(imx93_media_blk_ctl_domain_data),
484 	.clk_names = media_blk_clk_names,
485 	.num_clks = ARRAY_SIZE(media_blk_clk_names),
486 	.reg_access_table = &imx93_media_blk_ctl_access_table,
487 };
488 
489 static const struct imx93_blk_ctrl_data imx93_media_blk_ctl_dev_data = {
490 	.domains = imx93_media_blk_ctl_domain_data,
491 	.num_domains = ARRAY_SIZE(imx93_media_blk_ctl_domain_data),
492 	.clk_names = media_blk_clk_names,
493 	.num_clks = ARRAY_SIZE(media_blk_clk_names),
494 	.reg_access_table = &imx93_media_blk_ctl_access_table,
495 };
496 
497 static const struct of_device_id imx93_blk_ctrl_of_match[] = {
498 	{
499 		.compatible = "fsl,imx91-media-blk-ctrl",
500 		.data = &imx91_media_blk_ctl_dev_data
501 	}, {
502 		.compatible = "fsl,imx93-media-blk-ctrl",
503 		.data = &imx93_media_blk_ctl_dev_data
504 	}, {
505 		/* Sentinel */
506 	}
507 };
508 MODULE_DEVICE_TABLE(of, imx93_blk_ctrl_of_match);
509 
510 static struct platform_driver imx93_blk_ctrl_driver = {
511 	.probe = imx93_blk_ctrl_probe,
512 	.driver = {
513 		.name = "imx93-blk-ctrl",
514 		.of_match_table = imx93_blk_ctrl_of_match,
515 	},
516 };
517 module_platform_driver(imx93_blk_ctrl_driver);
518 
519 MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
520 MODULE_DESCRIPTION("i.MX93 BLK CTRL driver");
521 MODULE_LICENSE("GPL");
522