1 // SPDX-License-Identifier: GPL-2.0
7 #include <linux/delay.h>
8 #include <linux/iopoll.h>
9 #include <linux/mod_devicetable.h>
10 #include <linux/module.h>
11 #include <linux/platform_device.h>
12 #include <linux/pm_domain.h>
14 #define MIX_SLICE_SW_CTRL_OFF 0x20
15 #define SLICE_SW_CTRL_PSW_CTRL_OFF_MASK BIT(4)
16 #define SLICE_SW_CTRL_PDN_SOFT_MASK BIT(31)
18 #define MIX_FUNC_STAT_OFF 0xB4
20 #define FUNC_STAT_PSW_STAT_MASK BIT(0)
21 #define FUNC_STAT_RST_STAT_MASK BIT(2)
22 #define FUNC_STAT_ISO_STAT_MASK BIT(4)
23 #define FUNC_STAT_SSAR_STAT_MASK BIT(8)
25 struct imx93_power_domain
{
26 struct generic_pm_domain genpd
;
29 struct clk_bulk_data
*clks
;
33 #define to_imx93_pd(_genpd) container_of(_genpd, struct imx93_power_domain, genpd)
35 static int imx93_pd_on(struct generic_pm_domain
*genpd
)
37 struct imx93_power_domain
*domain
= to_imx93_pd(genpd
);
38 void __iomem
*addr
= domain
->addr
;
42 ret
= clk_bulk_prepare_enable(domain
->num_clks
, domain
->clks
);
44 dev_err(domain
->dev
, "failed to enable clocks for domain: %s\n", genpd
->name
);
48 val
= readl(addr
+ MIX_SLICE_SW_CTRL_OFF
);
49 val
&= ~SLICE_SW_CTRL_PDN_SOFT_MASK
;
50 writel(val
, addr
+ MIX_SLICE_SW_CTRL_OFF
);
52 ret
= readl_poll_timeout(addr
+ MIX_FUNC_STAT_OFF
, val
,
53 !(val
& FUNC_STAT_SSAR_STAT_MASK
), 1, 10000);
55 dev_err(domain
->dev
, "pd_on timeout: name: %s, stat: %x\n", genpd
->name
, val
);
62 static int imx93_pd_off(struct generic_pm_domain
*genpd
)
64 struct imx93_power_domain
*domain
= to_imx93_pd(genpd
);
65 void __iomem
*addr
= domain
->addr
;
70 val
= readl(addr
+ MIX_SLICE_SW_CTRL_OFF
);
71 val
|= SLICE_SW_CTRL_PDN_SOFT_MASK
;
72 writel(val
, addr
+ MIX_SLICE_SW_CTRL_OFF
);
74 ret
= readl_poll_timeout(addr
+ MIX_FUNC_STAT_OFF
, val
,
75 val
& FUNC_STAT_PSW_STAT_MASK
, 1, 10000);
77 dev_err(domain
->dev
, "pd_off timeout: name: %s, stat: %x\n", genpd
->name
, val
);
81 clk_bulk_disable_unprepare(domain
->num_clks
, domain
->clks
);
86 static void imx93_pd_remove(struct platform_device
*pdev
)
88 struct imx93_power_domain
*domain
= platform_get_drvdata(pdev
);
89 struct device
*dev
= &pdev
->dev
;
90 struct device_node
*np
= dev
->of_node
;
92 of_genpd_del_provider(np
);
93 pm_genpd_remove(&domain
->genpd
);
96 static int imx93_pd_probe(struct platform_device
*pdev
)
98 struct device
*dev
= &pdev
->dev
;
99 struct device_node
*np
= dev
->of_node
;
100 struct imx93_power_domain
*domain
;
104 domain
= devm_kzalloc(dev
, sizeof(*domain
), GFP_KERNEL
);
108 domain
->addr
= devm_platform_ioremap_resource(pdev
, 0);
109 if (IS_ERR(domain
->addr
))
110 return PTR_ERR(domain
->addr
);
112 domain
->num_clks
= devm_clk_bulk_get_all(dev
, &domain
->clks
);
113 if (domain
->num_clks
< 0)
114 return dev_err_probe(dev
, domain
->num_clks
, "Failed to get domain's clocks\n");
116 domain
->genpd
.name
= dev_name(dev
);
117 domain
->genpd
.power_off
= imx93_pd_off
;
118 domain
->genpd
.power_on
= imx93_pd_on
;
121 init_off
= readl(domain
->addr
+ MIX_FUNC_STAT_OFF
) & FUNC_STAT_ISO_STAT_MASK
;
122 /* Just to sync the status of hardware */
124 ret
= clk_bulk_prepare_enable(domain
->num_clks
, domain
->clks
);
126 return dev_err_probe(domain
->dev
, ret
,
127 "failed to enable clocks for domain: %s\n",
131 ret
= pm_genpd_init(&domain
->genpd
, NULL
, init_off
);
133 goto err_clk_unprepare
;
135 platform_set_drvdata(pdev
, domain
);
137 ret
= of_genpd_add_provider_simple(np
, &domain
->genpd
);
139 goto err_genpd_remove
;
144 pm_genpd_remove(&domain
->genpd
);
148 clk_bulk_disable_unprepare(domain
->num_clks
, domain
->clks
);
153 static const struct of_device_id imx93_pd_ids
[] = {
154 { .compatible
= "fsl,imx93-src-slice" },
157 MODULE_DEVICE_TABLE(of
, imx93_pd_ids
);
159 static struct platform_driver imx93_power_domain_driver
= {
161 .name
= "imx93_power_domain",
162 .of_match_table
= imx93_pd_ids
,
164 .probe
= imx93_pd_probe
,
165 .remove
= imx93_pd_remove
,
167 module_platform_driver(imx93_power_domain_driver
);
169 MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
170 MODULE_DESCRIPTION("NXP i.MX93 power domain driver");
171 MODULE_LICENSE("GPL");