1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright 2022 NXP, Peng Fan <peng.fan@nxp.com>
7 #include <linux/device.h>
8 #include <linux/module.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>
16 #include <dt-bindings/power/fsl,imx93-power.h>
18 #define BLK_SFT_RSTN 0x0
19 #define BLK_CLK_EN 0x4
20 #define BLK_MAX_CLKS 4
22 #define DOMAIN_MAX_CLKS 4
24 #define LCDIF_QOS_REG 0xC
25 #define LCDIF_DEFAULT_QOS_OFF 12
26 #define LCDIF_CFG_QOS_OFF 8
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
34 #define ISI_CACHE_REG 0x14
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
50 struct imx93_blk_ctrl_domain
;
52 struct imx93_blk_ctrl
{
54 struct regmap
*regmap
;
56 struct clk_bulk_data clks
[BLK_MAX_CLKS
];
57 struct imx93_blk_ctrl_domain
*domains
;
58 struct genpd_onecell_data onecell_data
;
61 #define DOMAIN_MAX_QOS 4
63 struct imx93_blk_ctrl_qos
{
70 struct imx93_blk_ctrl_domain_data
{
72 const char * const *clk_names
;
77 struct imx93_blk_ctrl_qos qos
[DOMAIN_MAX_QOS
];
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
;
87 struct imx93_blk_ctrl_data
{
88 const struct imx93_blk_ctrl_domain_data
*domains
;
90 const char * const *clk_names
;
92 const struct regmap_access_table
*reg_access_table
;
95 static inline struct imx93_blk_ctrl_domain
*
96 to_imx93_blk_ctrl_domain(struct generic_pm_domain
*genpd
)
98 return container_of(genpd
, struct imx93_blk_ctrl_domain
, genpd
);
101 static int imx93_blk_ctrl_set_qos(struct imx93_blk_ctrl_domain
*domain
)
103 const struct imx93_blk_ctrl_domain_data
*data
= domain
->data
;
104 struct imx93_blk_ctrl
*bc
= domain
->bc
;
105 const struct imx93_blk_ctrl_qos
*qos
;
109 for (i
= 0; i
< data
->num_qos
; i
++) {
112 mask
= PRIO_MASK
<< qos
->cfg_off
;
113 mask
|= PRIO_MASK
<< (qos
->cfg_off
+ 4);
114 val
= qos
->cfg_prio
<< qos
->cfg_off
;
115 val
|= qos
->default_prio
<< (qos
->cfg_off
+ 4);
117 regmap_write_bits(bc
->regmap
, qos
->reg
, mask
, val
);
119 dev_dbg(bc
->dev
, "data->qos[i].reg 0x%x 0x%x\n", qos
->reg
, val
);
125 static int imx93_blk_ctrl_power_on(struct generic_pm_domain
*genpd
)
127 struct imx93_blk_ctrl_domain
*domain
= to_imx93_blk_ctrl_domain(genpd
);
128 const struct imx93_blk_ctrl_domain_data
*data
= domain
->data
;
129 struct imx93_blk_ctrl
*bc
= domain
->bc
;
132 ret
= clk_bulk_prepare_enable(bc
->num_clks
, bc
->clks
);
134 dev_err(bc
->dev
, "failed to enable bus clocks\n");
138 ret
= clk_bulk_prepare_enable(data
->num_clks
, domain
->clks
);
140 clk_bulk_disable_unprepare(bc
->num_clks
, bc
->clks
);
141 dev_err(bc
->dev
, "failed to enable clocks\n");
145 ret
= pm_runtime_get_sync(bc
->dev
);
147 pm_runtime_put_noidle(bc
->dev
);
148 dev_err(bc
->dev
, "failed to power up domain\n");
153 regmap_clear_bits(bc
->regmap
, BLK_CLK_EN
, data
->clk_mask
);
156 regmap_set_bits(bc
->regmap
, BLK_SFT_RSTN
, data
->rst_mask
);
158 dev_dbg(bc
->dev
, "pd_on: name: %s\n", genpd
->name
);
160 return imx93_blk_ctrl_set_qos(domain
);
163 clk_bulk_disable_unprepare(data
->num_clks
, domain
->clks
);
165 clk_bulk_disable_unprepare(bc
->num_clks
, bc
->clks
);
170 static int imx93_blk_ctrl_power_off(struct generic_pm_domain
*genpd
)
172 struct imx93_blk_ctrl_domain
*domain
= to_imx93_blk_ctrl_domain(genpd
);
173 const struct imx93_blk_ctrl_domain_data
*data
= domain
->data
;
174 struct imx93_blk_ctrl
*bc
= domain
->bc
;
176 dev_dbg(bc
->dev
, "pd_off: name: %s\n", genpd
->name
);
178 regmap_clear_bits(bc
->regmap
, BLK_SFT_RSTN
, data
->rst_mask
);
179 regmap_set_bits(bc
->regmap
, BLK_CLK_EN
, data
->clk_mask
);
181 pm_runtime_put(bc
->dev
);
183 clk_bulk_disable_unprepare(data
->num_clks
, domain
->clks
);
185 clk_bulk_disable_unprepare(bc
->num_clks
, bc
->clks
);
190 static struct lock_class_key blk_ctrl_genpd_lock_class
;
192 static int imx93_blk_ctrl_probe(struct platform_device
*pdev
)
194 struct device
*dev
= &pdev
->dev
;
195 const struct imx93_blk_ctrl_data
*bc_data
= of_device_get_match_data(dev
);
196 struct imx93_blk_ctrl
*bc
;
200 struct regmap_config regmap_config
= {
204 .rd_table
= bc_data
->reg_access_table
,
205 .wr_table
= bc_data
->reg_access_table
,
206 .max_register
= SZ_4K
,
209 bc
= devm_kzalloc(dev
, sizeof(*bc
), GFP_KERNEL
);
215 base
= devm_platform_ioremap_resource(pdev
, 0);
217 return PTR_ERR(base
);
219 bc
->regmap
= devm_regmap_init_mmio(dev
, base
, ®map_config
);
220 if (IS_ERR(bc
->regmap
))
221 return dev_err_probe(dev
, PTR_ERR(bc
->regmap
),
222 "failed to init regmap\n");
224 bc
->domains
= devm_kcalloc(dev
, bc_data
->num_domains
,
225 sizeof(struct imx93_blk_ctrl_domain
),
230 bc
->onecell_data
.num_domains
= bc_data
->num_domains
;
231 bc
->onecell_data
.domains
=
232 devm_kcalloc(dev
, bc_data
->num_domains
,
233 sizeof(struct generic_pm_domain
*), GFP_KERNEL
);
234 if (!bc
->onecell_data
.domains
)
237 for (i
= 0; i
< bc_data
->num_clks
; i
++)
238 bc
->clks
[i
].id
= bc_data
->clk_names
[i
];
239 bc
->num_clks
= bc_data
->num_clks
;
241 ret
= devm_clk_bulk_get(dev
, bc
->num_clks
, bc
->clks
);
243 dev_err_probe(dev
, ret
, "failed to get bus clock\n");
247 for (i
= 0; i
< bc_data
->num_domains
; i
++) {
248 const struct imx93_blk_ctrl_domain_data
*data
= &bc_data
->domains
[i
];
249 struct imx93_blk_ctrl_domain
*domain
= &bc
->domains
[i
];
254 for (j
= 0; j
< data
->num_clks
; j
++)
255 domain
->clks
[j
].id
= data
->clk_names
[j
];
257 ret
= devm_clk_bulk_get(dev
, data
->num_clks
, domain
->clks
);
259 dev_err_probe(dev
, ret
, "failed to get clock\n");
263 domain
->genpd
.name
= data
->name
;
264 domain
->genpd
.power_on
= imx93_blk_ctrl_power_on
;
265 domain
->genpd
.power_off
= imx93_blk_ctrl_power_off
;
268 ret
= pm_genpd_init(&domain
->genpd
, NULL
, true);
270 dev_err_probe(dev
, ret
, "failed to init power domain\n");
275 * We use runtime PM to trigger power on/off of the upstream GPC
276 * domain, as a strict hierarchical parent/child power domain
277 * setup doesn't allow us to meet the sequencing requirements.
278 * This means we have nested locking of genpd locks, without the
279 * nesting being visible at the genpd level, so we need a
280 * separate lock class to make lockdep aware of the fact that
281 * this are separate domain locks that can be nested without a
284 lockdep_set_class(&domain
->genpd
.mlock
,
285 &blk_ctrl_genpd_lock_class
);
287 bc
->onecell_data
.domains
[i
] = &domain
->genpd
;
290 pm_runtime_enable(dev
);
292 ret
= of_genpd_add_provider_onecell(dev
->of_node
, &bc
->onecell_data
);
294 dev_err_probe(dev
, ret
, "failed to add power domain provider\n");
298 dev_set_drvdata(dev
, bc
);
303 for (i
--; i
>= 0; i
--)
304 pm_genpd_remove(&bc
->domains
[i
].genpd
);
309 static void imx93_blk_ctrl_remove(struct platform_device
*pdev
)
311 struct imx93_blk_ctrl
*bc
= dev_get_drvdata(&pdev
->dev
);
314 of_genpd_del_provider(pdev
->dev
.of_node
);
316 pm_runtime_disable(&pdev
->dev
);
318 for (i
= 0; i
< bc
->onecell_data
.num_domains
; i
++) {
319 struct imx93_blk_ctrl_domain
*domain
= &bc
->domains
[i
];
321 pm_genpd_remove(&domain
->genpd
);
325 static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data
[] = {
326 [IMX93_MEDIABLK_PD_MIPI_DSI
] = {
327 .name
= "mediablk-mipi-dsi",
328 .clk_names
= (const char *[]){ "dsi" },
330 .rst_mask
= BIT(11) | BIT(12),
331 .clk_mask
= BIT(11) | BIT(12),
333 [IMX93_MEDIABLK_PD_MIPI_CSI
] = {
334 .name
= "mediablk-mipi-csi",
335 .clk_names
= (const char *[]){ "cam", "csi" },
337 .rst_mask
= BIT(9) | BIT(10),
338 .clk_mask
= BIT(9) | BIT(10),
340 [IMX93_MEDIABLK_PD_PXP
] = {
341 .name
= "mediablk-pxp",
342 .clk_names
= (const char *[]){ "pxp" },
344 .rst_mask
= BIT(7) | BIT(8),
345 .clk_mask
= BIT(7) | BIT(8),
350 .cfg_off
= PXP_R_CFG_QOS_OFF
,
351 .default_prio
= PRIO(3),
355 .cfg_off
= PXP_W_CFG_QOS_OFF
,
356 .default_prio
= PRIO(3),
361 [IMX93_MEDIABLK_PD_LCDIF
] = {
362 .name
= "mediablk-lcdif",
363 .clk_names
= (const char *[]){ "disp", "lcdif" },
365 .rst_mask
= BIT(4) | BIT(5) | BIT(6),
366 .clk_mask
= BIT(4) | BIT(5) | BIT(6),
370 .reg
= LCDIF_QOS_REG
,
371 .cfg_off
= LCDIF_CFG_QOS_OFF
,
372 .default_prio
= PRIO(3),
377 [IMX93_MEDIABLK_PD_ISI
] = {
378 .name
= "mediablk-isi",
379 .clk_names
= (const char *[]){ "isi" },
381 .rst_mask
= BIT(2) | BIT(3),
382 .clk_mask
= BIT(2) | BIT(3),
387 .cfg_off
= ISI_Y_W_CFG_QOS_OFF
,
388 .default_prio
= PRIO(3),
392 .cfg_off
= ISI_Y_R_CFG_QOS_OFF
,
393 .default_prio
= PRIO(3),
397 .cfg_off
= ISI_U_CFG_QOS_OFF
,
398 .default_prio
= PRIO(3),
402 .cfg_off
= ISI_V_CFG_QOS_OFF
,
403 .default_prio
= PRIO(3),
410 static const struct regmap_range imx93_media_blk_ctl_yes_ranges
[] = {
411 regmap_reg_range(BLK_SFT_RSTN
, BLK_CLK_EN
),
412 regmap_reg_range(LCDIF_QOS_REG
, ISI_CACHE_REG
),
413 regmap_reg_range(ISI_QOS_REG
, ISI_QOS_REG
),
416 static const struct regmap_access_table imx93_media_blk_ctl_access_table
= {
417 .yes_ranges
= imx93_media_blk_ctl_yes_ranges
,
418 .n_yes_ranges
= ARRAY_SIZE(imx93_media_blk_ctl_yes_ranges
),
421 static const struct imx93_blk_ctrl_data imx93_media_blk_ctl_dev_data
= {
422 .domains
= imx93_media_blk_ctl_domain_data
,
423 .num_domains
= ARRAY_SIZE(imx93_media_blk_ctl_domain_data
),
424 .clk_names
= (const char *[]){ "axi", "apb", "nic", },
426 .reg_access_table
= &imx93_media_blk_ctl_access_table
,
429 static const struct of_device_id imx93_blk_ctrl_of_match
[] = {
431 .compatible
= "fsl,imx93-media-blk-ctrl",
432 .data
= &imx93_media_blk_ctl_dev_data
437 MODULE_DEVICE_TABLE(of
, imx93_blk_ctrl_of_match
);
439 static struct platform_driver imx93_blk_ctrl_driver
= {
440 .probe
= imx93_blk_ctrl_probe
,
441 .remove
= imx93_blk_ctrl_remove
,
443 .name
= "imx93-blk-ctrl",
444 .of_match_table
= imx93_blk_ctrl_of_match
,
447 module_platform_driver(imx93_blk_ctrl_driver
);
449 MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
450 MODULE_DESCRIPTION("i.MX93 BLK CTRL driver");
451 MODULE_LICENSE("GPL");