xref: /linux/drivers/pmdomain/imx/imx93-blk-ctrl.c (revision 2d7eaeb05ae265bd64270376e6a2c0d80244771f)
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 *
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 
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 
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_resume_and_get(bc->dev);
156 	if (ret < 0) {
157 		dev_err(bc->dev, "failed to power up domain\n");
158 		goto disable_clk;
159 	}
160 
161 	/* ungate clk */
162 	regmap_clear_bits(bc->regmap, BLK_CLK_EN, data->clk_mask);
163 
164 	/* release reset */
165 	regmap_set_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask);
166 
167 	dev_dbg(bc->dev, "pd_on: name: %s\n", genpd->name);
168 
169 	return imx93_blk_ctrl_set_qos(domain);
170 
171 disable_clk:
172 	clk_bulk_disable_unprepare(data->num_clks, domain->clks);
173 
174 	clk_bulk_disable_unprepare(bc->num_clks, bc->clks);
175 
176 	return ret;
177 }
178 
179 static int imx93_blk_ctrl_power_off(struct generic_pm_domain *genpd)
180 {
181 	struct imx93_blk_ctrl_domain *domain = to_imx93_blk_ctrl_domain(genpd);
182 	const struct imx93_blk_ctrl_domain_data *data = domain->data;
183 	struct imx93_blk_ctrl *bc = domain->bc;
184 
185 	dev_dbg(bc->dev, "pd_off: name: %s\n", genpd->name);
186 
187 	regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask);
188 	regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask);
189 
190 	pm_runtime_put(bc->dev);
191 
192 	clk_bulk_disable_unprepare(data->num_clks, domain->clks);
193 
194 	clk_bulk_disable_unprepare(bc->num_clks, bc->clks);
195 
196 	return 0;
197 }
198 
199 static void imx93_release_genpd_provider(void *data)
200 {
201 	struct device_node *of_node = data;
202 
203 	of_genpd_del_provider(of_node);
204 }
205 
206 static void imx93_release_pm_genpd(void *data)
207 {
208 	struct generic_pm_domain *genpd = data;
209 
210 	pm_genpd_remove(genpd);
211 }
212 
213 static void imx93_release_subdomain(void *data)
214 {
215 	struct imx93_blk_ctrl_subdomain_link *link = data;
216 
217 	pm_genpd_remove_subdomain(link->parent, link->subdomain);
218 }
219 
220 static struct lock_class_key blk_ctrl_genpd_lock_class;
221 
222 static int imx93_blk_ctrl_probe(struct platform_device *pdev)
223 {
224 	struct device *dev = &pdev->dev;
225 	const struct imx93_blk_ctrl_data *bc_data = of_device_get_match_data(dev);
226 	struct imx93_blk_ctrl *bc;
227 	void __iomem *base;
228 	int i, ret;
229 
230 	struct regmap_config regmap_config = {
231 		.reg_bits	= 32,
232 		.val_bits	= 32,
233 		.reg_stride	= 4,
234 		.rd_table	= bc_data->reg_access_table,
235 		.wr_table	= bc_data->reg_access_table,
236 		.max_register   = SZ_4K,
237 	};
238 
239 	bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL);
240 	if (!bc)
241 		return -ENOMEM;
242 
243 	bc->dev = dev;
244 
245 	base = devm_platform_ioremap_resource(pdev, 0);
246 	if (IS_ERR(base))
247 		return PTR_ERR(base);
248 
249 	bc->regmap = devm_regmap_init_mmio(dev, base, &regmap_config);
250 	if (IS_ERR(bc->regmap))
251 		return dev_err_probe(dev, PTR_ERR(bc->regmap),
252 				     "failed to init regmap\n");
253 
254 	bc->domains = devm_kcalloc(dev, bc_data->num_domains,
255 				   sizeof(struct imx93_blk_ctrl_domain),
256 				   GFP_KERNEL);
257 	if (!bc->domains)
258 		return -ENOMEM;
259 
260 	bc->onecell_data.num_domains = bc_data->num_domains;
261 	bc->onecell_data.domains =
262 		devm_kcalloc(dev, bc_data->num_domains,
263 			     sizeof(struct generic_pm_domain *), GFP_KERNEL);
264 	if (!bc->onecell_data.domains)
265 		return -ENOMEM;
266 
267 	for (i = 0; i < bc_data->num_clks; i++)
268 		bc->clks[i].id = bc_data->clk_names[i];
269 	bc->num_clks = bc_data->num_clks;
270 
271 	ret = devm_clk_bulk_get(dev, bc->num_clks, bc->clks);
272 	if (ret)
273 		return dev_err_probe(dev, ret, "failed to get bus clock\n");
274 
275 	for (i = 0; i < bc_data->num_domains; i++) {
276 		const struct imx93_blk_ctrl_domain_data *data = &bc_data->domains[i];
277 		struct imx93_blk_ctrl_domain *domain = &bc->domains[i];
278 		int j;
279 
280 		domain->data = data;
281 		if (bc_data->skip_mask & BIT(i))
282 			continue;
283 
284 		for (j = 0; j < data->num_clks; j++)
285 			domain->clks[j].id = data->clk_names[j];
286 
287 		ret = devm_clk_bulk_get(dev, data->num_clks, domain->clks);
288 		if (ret)
289 			return dev_err_probe(dev, ret, "failed to get clock\n");
290 
291 		domain->genpd.name = data->name;
292 		domain->genpd.power_on = imx93_blk_ctrl_power_on;
293 		domain->genpd.power_off = imx93_blk_ctrl_power_off;
294 		domain->bc = bc;
295 
296 		ret = pm_genpd_init(&domain->genpd, NULL, true);
297 		if (ret)
298 			return dev_err_probe(dev, ret, "failed to init power domain\n");
299 
300 		ret = devm_add_action_or_reset(dev, imx93_release_pm_genpd, &domain->genpd);
301 		if (ret)
302 			return dev_err_probe(dev, ret, "failed to add pm_genpd release callback\n");
303 		/*
304 		 * We use runtime PM to trigger power on/off of the upstream GPC
305 		 * domain, as a strict hierarchical parent/child power domain
306 		 * setup doesn't allow us to meet the sequencing requirements.
307 		 * This means we have nested locking of genpd locks, without the
308 		 * nesting being visible at the genpd level, so we need a
309 		 * separate lock class to make lockdep aware of the fact that
310 		 * this are separate domain locks that can be nested without a
311 		 * self-deadlock.
312 		 */
313 		lockdep_set_class(&domain->genpd.mlock,
314 				  &blk_ctrl_genpd_lock_class);
315 
316 		bc->onecell_data.domains[i] = &domain->genpd;
317 	}
318 
319 	for (i = 0; i < bc_data->num_domains; i++) {
320 		struct imx93_blk_ctrl_domain *domain = &bc->domains[i];
321 		const struct imx93_blk_ctrl_domain_data *data = domain->data;
322 		struct imx93_blk_ctrl_subdomain_link *link;
323 
324 		if (bc_data->skip_mask & BIT(i) ||
325 		    data->parent == BLK_CTRL_NO_PARENT)
326 			continue;
327 
328 		link = devm_kzalloc(dev, sizeof(*link), GFP_KERNEL);
329 		if (!link)
330 			return -ENOMEM;
331 
332 		link->parent = &bc->domains[data->parent].genpd;
333 		link->subdomain = &domain->genpd;
334 
335 		ret = pm_genpd_add_subdomain(&bc->domains[data->parent].genpd,
336 					     &domain->genpd);
337 		if (ret)
338 			return dev_err_probe(dev, ret, "failed to add subdomain %s\n",
339 					     domain->genpd.name);
340 
341 		ret = devm_add_action_or_reset(dev, imx93_release_subdomain, link);
342 		if (ret)
343 			return dev_err_probe(dev, ret,
344 					     "failed to add subdomain release callback\n");
345 	}
346 
347 	ret = devm_pm_runtime_enable(dev);
348 	if (ret)
349 		return dev_err_probe(dev, ret, "failed to enable pm-runtime\n");
350 
351 	ret = of_genpd_add_provider_onecell(dev->of_node, &bc->onecell_data);
352 	if (ret)
353 		return dev_err_probe(dev, ret, "failed to add power domain provider\n");
354 
355 	ret = devm_add_action_or_reset(dev, imx93_release_genpd_provider, dev->of_node);
356 	if (ret)
357 		return dev_err_probe(dev, ret, "failed to add genpd_provider release callback\n");
358 
359 	ret = devm_of_platform_populate(dev);
360 	if (ret)
361 		return dev_err_probe(dev, ret, "failed to populate blk-ctrl sub-devices\n");
362 
363 	return 0;
364 }
365 
366 static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] = {
367 	[IMX93_MEDIABLK_PD_MIPI_DSI] = {
368 		.name = "mediablk-mipi-dsi",
369 		.clk_names = (const char *[]){ "dsi" },
370 		.num_clks = 1,
371 		.rst_mask = BIT(11),
372 		.clk_mask = BIT(11),
373 		.parent = IMX93_MEDIABLK_PD_MIPI_PHY,
374 	},
375 	[IMX93_MEDIABLK_PD_MIPI_CSI] = {
376 		.name = "mediablk-mipi-csi",
377 		.clk_names = (const char *[]){ "cam", "csi" },
378 		.num_clks = 2,
379 		.rst_mask = BIT(9) | BIT(10),
380 		.clk_mask = BIT(9) | BIT(10),
381 		.parent = IMX93_MEDIABLK_PD_MIPI_PHY,
382 	},
383 	[IMX93_MEDIABLK_PD_PXP] = {
384 		.name = "mediablk-pxp",
385 		.clk_names = (const char *[]){ "pxp" },
386 		.num_clks = 1,
387 		.rst_mask = BIT(7) | BIT(8),
388 		.clk_mask = BIT(7) | BIT(8),
389 		.parent = BLK_CTRL_NO_PARENT,
390 		.num_qos = 2,
391 		.qos = {
392 			{
393 				.reg = PXP_QOS_REG,
394 				.cfg_off = PXP_R_CFG_QOS_OFF,
395 				.default_prio = PRIO(3),
396 				.cfg_prio = PRIO(6),
397 			}, {
398 				.reg = PXP_QOS_REG,
399 				.cfg_off = PXP_W_CFG_QOS_OFF,
400 				.default_prio = PRIO(3),
401 				.cfg_prio = PRIO(6),
402 			}
403 		}
404 	},
405 	[IMX93_MEDIABLK_PD_LCDIF] = {
406 		.name = "mediablk-lcdif",
407 		.clk_names = (const char *[]){ "disp", "lcdif" },
408 		.num_clks = 2,
409 		.rst_mask = BIT(4) | BIT(5) | BIT(6),
410 		.clk_mask = BIT(4) | BIT(5) | BIT(6),
411 		.parent = BLK_CTRL_NO_PARENT,
412 		.num_qos = 1,
413 		.qos = {
414 			{
415 			.reg = LCDIF_QOS_REG,
416 			.cfg_off = LCDIF_CFG_QOS_OFF,
417 			.default_prio = PRIO(3),
418 			.cfg_prio = PRIO(7),
419 			}
420 		}
421 	},
422 	[IMX93_MEDIABLK_PD_ISI] = {
423 		.name = "mediablk-isi",
424 		.clk_names = (const char *[]){ "isi" },
425 		.num_clks = 1,
426 		.rst_mask = BIT(2) | BIT(3),
427 		.clk_mask = BIT(2) | BIT(3),
428 		.parent = BLK_CTRL_NO_PARENT,
429 		.num_qos = 4,
430 		.qos = {
431 			{
432 				.reg = ISI_QOS_REG,
433 				.cfg_off = ISI_Y_W_CFG_QOS_OFF,
434 				.default_prio = PRIO(3),
435 				.cfg_prio = PRIO(7),
436 			}, {
437 				.reg = ISI_QOS_REG,
438 				.cfg_off = ISI_Y_R_CFG_QOS_OFF,
439 				.default_prio = PRIO(3),
440 				.cfg_prio = PRIO(7),
441 			}, {
442 				.reg = ISI_QOS_REG,
443 				.cfg_off = ISI_U_CFG_QOS_OFF,
444 				.default_prio = PRIO(3),
445 				.cfg_prio = PRIO(7),
446 			}, {
447 				.reg = ISI_QOS_REG,
448 				.cfg_off = ISI_V_CFG_QOS_OFF,
449 				.default_prio = PRIO(3),
450 				.cfg_prio = PRIO(7),
451 			}
452 		}
453 	},
454 	[IMX93_MEDIABLK_PD_MIPI_PHY] = {
455 		.name = "mediablk-mipi-phy",
456 		.clk_names = NULL,
457 		.num_clks = 0,
458 		.rst_mask = BIT(12),
459 		.clk_mask = BIT(12),
460 		.parent = BLK_CTRL_NO_PARENT,
461 	},
462 };
463 
464 static const struct regmap_range imx93_media_blk_ctl_yes_ranges[] = {
465 	regmap_reg_range(BLK_SFT_RSTN, BLK_CLK_EN),
466 	regmap_reg_range(LCDIF_QOS_REG, ISI_CACHE_REG),
467 	regmap_reg_range(ISI_QOS_REG, ISI_QOS_REG),
468 };
469 
470 static const struct regmap_access_table imx93_media_blk_ctl_access_table = {
471 	.yes_ranges = imx93_media_blk_ctl_yes_ranges,
472 	.n_yes_ranges = ARRAY_SIZE(imx93_media_blk_ctl_yes_ranges),
473 };
474 
475 static const char * const media_blk_clk_names[] = {
476 	"axi", "apb", "nic"
477 };
478 
479 static const struct imx93_blk_ctrl_data imx91_media_blk_ctl_dev_data = {
480 	.domains = imx93_media_blk_ctl_domain_data,
481 	.skip_mask = BIT(IMX93_MEDIABLK_PD_MIPI_DSI) | BIT(IMX93_MEDIABLK_PD_PXP),
482 	.num_domains = ARRAY_SIZE(imx93_media_blk_ctl_domain_data),
483 	.clk_names = media_blk_clk_names,
484 	.num_clks = ARRAY_SIZE(media_blk_clk_names),
485 	.reg_access_table = &imx93_media_blk_ctl_access_table,
486 };
487 
488 static const struct imx93_blk_ctrl_data imx93_media_blk_ctl_dev_data = {
489 	.domains = imx93_media_blk_ctl_domain_data,
490 	.num_domains = ARRAY_SIZE(imx93_media_blk_ctl_domain_data),
491 	.clk_names = media_blk_clk_names,
492 	.num_clks = ARRAY_SIZE(media_blk_clk_names),
493 	.reg_access_table = &imx93_media_blk_ctl_access_table,
494 };
495 
496 static const struct of_device_id imx93_blk_ctrl_of_match[] = {
497 	{
498 		.compatible = "fsl,imx91-media-blk-ctrl",
499 		.data = &imx91_media_blk_ctl_dev_data
500 	}, {
501 		.compatible = "fsl,imx93-media-blk-ctrl",
502 		.data = &imx93_media_blk_ctl_dev_data
503 	}, {
504 		/* Sentinel */
505 	}
506 };
507 MODULE_DEVICE_TABLE(of, imx93_blk_ctrl_of_match);
508 
509 static struct platform_driver imx93_blk_ctrl_driver = {
510 	.probe = imx93_blk_ctrl_probe,
511 	.driver = {
512 		.name = "imx93-blk-ctrl",
513 		.of_match_table = imx93_blk_ctrl_of_match,
514 	},
515 };
516 module_platform_driver(imx93_blk_ctrl_driver);
517 
518 MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
519 MODULE_DESCRIPTION("i.MX93 BLK CTRL driver");
520 MODULE_LICENSE("GPL");
521