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