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, ®map_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