1*e2ad626fSUlf Hansson // SPDX-License-Identifier: GPL-2.0+
2*e2ad626fSUlf Hansson
3*e2ad626fSUlf Hansson /*
4*e2ad626fSUlf Hansson * Copyright 2021 Pengutronix, Lucas Stach <kernel@pengutronix.de>
5*e2ad626fSUlf Hansson */
6*e2ad626fSUlf Hansson
7*e2ad626fSUlf Hansson #include <linux/bitfield.h>
8*e2ad626fSUlf Hansson #include <linux/device.h>
9*e2ad626fSUlf Hansson #include <linux/interconnect.h>
10*e2ad626fSUlf Hansson #include <linux/module.h>
11*e2ad626fSUlf Hansson #include <linux/of.h>
12*e2ad626fSUlf Hansson #include <linux/of_platform.h>
13*e2ad626fSUlf Hansson #include <linux/platform_device.h>
14*e2ad626fSUlf Hansson #include <linux/pm_domain.h>
15*e2ad626fSUlf Hansson #include <linux/pm_runtime.h>
16*e2ad626fSUlf Hansson #include <linux/regmap.h>
17*e2ad626fSUlf Hansson #include <linux/clk.h>
18*e2ad626fSUlf Hansson
19*e2ad626fSUlf Hansson #include <dt-bindings/power/imx8mm-power.h>
20*e2ad626fSUlf Hansson #include <dt-bindings/power/imx8mn-power.h>
21*e2ad626fSUlf Hansson #include <dt-bindings/power/imx8mp-power.h>
22*e2ad626fSUlf Hansson #include <dt-bindings/power/imx8mq-power.h>
23*e2ad626fSUlf Hansson
24*e2ad626fSUlf Hansson #define BLK_SFT_RSTN 0x0
25*e2ad626fSUlf Hansson #define BLK_CLK_EN 0x4
26*e2ad626fSUlf Hansson #define BLK_MIPI_RESET_DIV 0x8 /* Mini/Nano/Plus DISPLAY_BLK_CTRL only */
27*e2ad626fSUlf Hansson
28*e2ad626fSUlf Hansson struct imx8m_blk_ctrl_domain;
29*e2ad626fSUlf Hansson
30*e2ad626fSUlf Hansson struct imx8m_blk_ctrl {
31*e2ad626fSUlf Hansson struct device *dev;
32*e2ad626fSUlf Hansson struct notifier_block power_nb;
33*e2ad626fSUlf Hansson struct device *bus_power_dev;
34*e2ad626fSUlf Hansson struct regmap *regmap;
35*e2ad626fSUlf Hansson struct imx8m_blk_ctrl_domain *domains;
36*e2ad626fSUlf Hansson struct genpd_onecell_data onecell_data;
37*e2ad626fSUlf Hansson };
38*e2ad626fSUlf Hansson
39*e2ad626fSUlf Hansson struct imx8m_blk_ctrl_domain_data {
40*e2ad626fSUlf Hansson const char *name;
41*e2ad626fSUlf Hansson const char * const *clk_names;
42*e2ad626fSUlf Hansson const char * const *path_names;
43*e2ad626fSUlf Hansson const char *gpc_name;
44*e2ad626fSUlf Hansson int num_clks;
45*e2ad626fSUlf Hansson int num_paths;
46*e2ad626fSUlf Hansson u32 rst_mask;
47*e2ad626fSUlf Hansson u32 clk_mask;
48*e2ad626fSUlf Hansson
49*e2ad626fSUlf Hansson /*
50*e2ad626fSUlf Hansson * i.MX8M Mini, Nano and Plus have a third DISPLAY_BLK_CTRL register
51*e2ad626fSUlf Hansson * which is used to control the reset for the MIPI Phy.
52*e2ad626fSUlf Hansson * Since it's only present in certain circumstances,
53*e2ad626fSUlf Hansson * an if-statement should be used before setting and clearing this
54*e2ad626fSUlf Hansson * register.
55*e2ad626fSUlf Hansson */
56*e2ad626fSUlf Hansson u32 mipi_phy_rst_mask;
57*e2ad626fSUlf Hansson };
58*e2ad626fSUlf Hansson
59*e2ad626fSUlf Hansson #define DOMAIN_MAX_CLKS 4
60*e2ad626fSUlf Hansson #define DOMAIN_MAX_PATHS 4
61*e2ad626fSUlf Hansson
62*e2ad626fSUlf Hansson struct imx8m_blk_ctrl_domain {
63*e2ad626fSUlf Hansson struct generic_pm_domain genpd;
64*e2ad626fSUlf Hansson const struct imx8m_blk_ctrl_domain_data *data;
65*e2ad626fSUlf Hansson struct clk_bulk_data clks[DOMAIN_MAX_CLKS];
66*e2ad626fSUlf Hansson struct icc_bulk_data paths[DOMAIN_MAX_PATHS];
67*e2ad626fSUlf Hansson struct device *power_dev;
68*e2ad626fSUlf Hansson struct imx8m_blk_ctrl *bc;
69*e2ad626fSUlf Hansson int num_paths;
70*e2ad626fSUlf Hansson };
71*e2ad626fSUlf Hansson
72*e2ad626fSUlf Hansson struct imx8m_blk_ctrl_data {
73*e2ad626fSUlf Hansson int max_reg;
74*e2ad626fSUlf Hansson notifier_fn_t power_notifier_fn;
75*e2ad626fSUlf Hansson const struct imx8m_blk_ctrl_domain_data *domains;
76*e2ad626fSUlf Hansson int num_domains;
77*e2ad626fSUlf Hansson };
78*e2ad626fSUlf Hansson
79*e2ad626fSUlf Hansson static inline struct imx8m_blk_ctrl_domain *
to_imx8m_blk_ctrl_domain(struct generic_pm_domain * genpd)80*e2ad626fSUlf Hansson to_imx8m_blk_ctrl_domain(struct generic_pm_domain *genpd)
81*e2ad626fSUlf Hansson {
82*e2ad626fSUlf Hansson return container_of(genpd, struct imx8m_blk_ctrl_domain, genpd);
83*e2ad626fSUlf Hansson }
84*e2ad626fSUlf Hansson
imx8m_blk_ctrl_power_on(struct generic_pm_domain * genpd)85*e2ad626fSUlf Hansson static int imx8m_blk_ctrl_power_on(struct generic_pm_domain *genpd)
86*e2ad626fSUlf Hansson {
87*e2ad626fSUlf Hansson struct imx8m_blk_ctrl_domain *domain = to_imx8m_blk_ctrl_domain(genpd);
88*e2ad626fSUlf Hansson const struct imx8m_blk_ctrl_domain_data *data = domain->data;
89*e2ad626fSUlf Hansson struct imx8m_blk_ctrl *bc = domain->bc;
90*e2ad626fSUlf Hansson int ret;
91*e2ad626fSUlf Hansson
92*e2ad626fSUlf Hansson /* make sure bus domain is awake */
93*e2ad626fSUlf Hansson ret = pm_runtime_get_sync(bc->bus_power_dev);
94*e2ad626fSUlf Hansson if (ret < 0) {
95*e2ad626fSUlf Hansson pm_runtime_put_noidle(bc->bus_power_dev);
96*e2ad626fSUlf Hansson dev_err(bc->dev, "failed to power up bus domain\n");
97*e2ad626fSUlf Hansson return ret;
98*e2ad626fSUlf Hansson }
99*e2ad626fSUlf Hansson
100*e2ad626fSUlf Hansson /* put devices into reset */
101*e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask);
102*e2ad626fSUlf Hansson if (data->mipi_phy_rst_mask)
103*e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, BLK_MIPI_RESET_DIV, data->mipi_phy_rst_mask);
104*e2ad626fSUlf Hansson
105*e2ad626fSUlf Hansson /* enable upstream and blk-ctrl clocks to allow reset to propagate */
106*e2ad626fSUlf Hansson ret = clk_bulk_prepare_enable(data->num_clks, domain->clks);
107*e2ad626fSUlf Hansson if (ret) {
108*e2ad626fSUlf Hansson dev_err(bc->dev, "failed to enable clocks\n");
109*e2ad626fSUlf Hansson goto bus_put;
110*e2ad626fSUlf Hansson }
111*e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask);
112*e2ad626fSUlf Hansson
113*e2ad626fSUlf Hansson /* power up upstream GPC domain */
114*e2ad626fSUlf Hansson ret = pm_runtime_get_sync(domain->power_dev);
115*e2ad626fSUlf Hansson if (ret < 0) {
116*e2ad626fSUlf Hansson dev_err(bc->dev, "failed to power up peripheral domain\n");
117*e2ad626fSUlf Hansson goto clk_disable;
118*e2ad626fSUlf Hansson }
119*e2ad626fSUlf Hansson
120*e2ad626fSUlf Hansson /* wait for reset to propagate */
121*e2ad626fSUlf Hansson udelay(5);
122*e2ad626fSUlf Hansson
123*e2ad626fSUlf Hansson /* release reset */
124*e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask);
125*e2ad626fSUlf Hansson if (data->mipi_phy_rst_mask)
126*e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, BLK_MIPI_RESET_DIV, data->mipi_phy_rst_mask);
127*e2ad626fSUlf Hansson
128*e2ad626fSUlf Hansson ret = icc_bulk_set_bw(domain->num_paths, domain->paths);
129*e2ad626fSUlf Hansson if (ret)
130*e2ad626fSUlf Hansson dev_err(bc->dev, "failed to set icc bw\n");
131*e2ad626fSUlf Hansson
132*e2ad626fSUlf Hansson /* disable upstream clocks */
133*e2ad626fSUlf Hansson clk_bulk_disable_unprepare(data->num_clks, domain->clks);
134*e2ad626fSUlf Hansson
135*e2ad626fSUlf Hansson return 0;
136*e2ad626fSUlf Hansson
137*e2ad626fSUlf Hansson clk_disable:
138*e2ad626fSUlf Hansson clk_bulk_disable_unprepare(data->num_clks, domain->clks);
139*e2ad626fSUlf Hansson bus_put:
140*e2ad626fSUlf Hansson pm_runtime_put(bc->bus_power_dev);
141*e2ad626fSUlf Hansson
142*e2ad626fSUlf Hansson return ret;
143*e2ad626fSUlf Hansson }
144*e2ad626fSUlf Hansson
imx8m_blk_ctrl_power_off(struct generic_pm_domain * genpd)145*e2ad626fSUlf Hansson static int imx8m_blk_ctrl_power_off(struct generic_pm_domain *genpd)
146*e2ad626fSUlf Hansson {
147*e2ad626fSUlf Hansson struct imx8m_blk_ctrl_domain *domain = to_imx8m_blk_ctrl_domain(genpd);
148*e2ad626fSUlf Hansson const struct imx8m_blk_ctrl_domain_data *data = domain->data;
149*e2ad626fSUlf Hansson struct imx8m_blk_ctrl *bc = domain->bc;
150*e2ad626fSUlf Hansson
151*e2ad626fSUlf Hansson /* put devices into reset and disable clocks */
152*e2ad626fSUlf Hansson if (data->mipi_phy_rst_mask)
153*e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, BLK_MIPI_RESET_DIV, data->mipi_phy_rst_mask);
154*e2ad626fSUlf Hansson
155*e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask);
156*e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, BLK_CLK_EN, data->clk_mask);
157*e2ad626fSUlf Hansson
158*e2ad626fSUlf Hansson /* power down upstream GPC domain */
159*e2ad626fSUlf Hansson pm_runtime_put(domain->power_dev);
160*e2ad626fSUlf Hansson
161*e2ad626fSUlf Hansson /* allow bus domain to suspend */
162*e2ad626fSUlf Hansson pm_runtime_put(bc->bus_power_dev);
163*e2ad626fSUlf Hansson
164*e2ad626fSUlf Hansson return 0;
165*e2ad626fSUlf Hansson }
166*e2ad626fSUlf Hansson
167*e2ad626fSUlf Hansson static struct lock_class_key blk_ctrl_genpd_lock_class;
168*e2ad626fSUlf Hansson
imx8m_blk_ctrl_probe(struct platform_device * pdev)169*e2ad626fSUlf Hansson static int imx8m_blk_ctrl_probe(struct platform_device *pdev)
170*e2ad626fSUlf Hansson {
171*e2ad626fSUlf Hansson const struct imx8m_blk_ctrl_data *bc_data;
172*e2ad626fSUlf Hansson struct device *dev = &pdev->dev;
173*e2ad626fSUlf Hansson struct imx8m_blk_ctrl *bc;
174*e2ad626fSUlf Hansson void __iomem *base;
175*e2ad626fSUlf Hansson int i, ret;
176*e2ad626fSUlf Hansson
177*e2ad626fSUlf Hansson struct regmap_config regmap_config = {
178*e2ad626fSUlf Hansson .reg_bits = 32,
179*e2ad626fSUlf Hansson .val_bits = 32,
180*e2ad626fSUlf Hansson .reg_stride = 4,
181*e2ad626fSUlf Hansson };
182*e2ad626fSUlf Hansson
183*e2ad626fSUlf Hansson bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL);
184*e2ad626fSUlf Hansson if (!bc)
185*e2ad626fSUlf Hansson return -ENOMEM;
186*e2ad626fSUlf Hansson
187*e2ad626fSUlf Hansson bc->dev = dev;
188*e2ad626fSUlf Hansson
189*e2ad626fSUlf Hansson bc_data = of_device_get_match_data(dev);
190*e2ad626fSUlf Hansson
191*e2ad626fSUlf Hansson base = devm_platform_ioremap_resource(pdev, 0);
192*e2ad626fSUlf Hansson if (IS_ERR(base))
193*e2ad626fSUlf Hansson return PTR_ERR(base);
194*e2ad626fSUlf Hansson
195*e2ad626fSUlf Hansson regmap_config.max_register = bc_data->max_reg;
196*e2ad626fSUlf Hansson bc->regmap = devm_regmap_init_mmio(dev, base, ®map_config);
197*e2ad626fSUlf Hansson if (IS_ERR(bc->regmap))
198*e2ad626fSUlf Hansson return dev_err_probe(dev, PTR_ERR(bc->regmap),
199*e2ad626fSUlf Hansson "failed to init regmap\n");
200*e2ad626fSUlf Hansson
201*e2ad626fSUlf Hansson bc->domains = devm_kcalloc(dev, bc_data->num_domains,
202*e2ad626fSUlf Hansson sizeof(struct imx8m_blk_ctrl_domain),
203*e2ad626fSUlf Hansson GFP_KERNEL);
204*e2ad626fSUlf Hansson if (!bc->domains)
205*e2ad626fSUlf Hansson return -ENOMEM;
206*e2ad626fSUlf Hansson
207*e2ad626fSUlf Hansson bc->onecell_data.num_domains = bc_data->num_domains;
208*e2ad626fSUlf Hansson bc->onecell_data.domains =
209*e2ad626fSUlf Hansson devm_kcalloc(dev, bc_data->num_domains,
210*e2ad626fSUlf Hansson sizeof(struct generic_pm_domain *), GFP_KERNEL);
211*e2ad626fSUlf Hansson if (!bc->onecell_data.domains)
212*e2ad626fSUlf Hansson return -ENOMEM;
213*e2ad626fSUlf Hansson
214*e2ad626fSUlf Hansson bc->bus_power_dev = dev_pm_domain_attach_by_name(dev, "bus");
215*e2ad626fSUlf Hansson if (IS_ERR(bc->bus_power_dev)) {
216*e2ad626fSUlf Hansson if (PTR_ERR(bc->bus_power_dev) == -ENODEV)
217*e2ad626fSUlf Hansson return dev_err_probe(dev, -EPROBE_DEFER,
218*e2ad626fSUlf Hansson "failed to attach power domain \"bus\"\n");
219*e2ad626fSUlf Hansson else
220*e2ad626fSUlf Hansson return dev_err_probe(dev, PTR_ERR(bc->bus_power_dev),
221*e2ad626fSUlf Hansson "failed to attach power domain \"bus\"\n");
222*e2ad626fSUlf Hansson }
223*e2ad626fSUlf Hansson
224*e2ad626fSUlf Hansson for (i = 0; i < bc_data->num_domains; i++) {
225*e2ad626fSUlf Hansson const struct imx8m_blk_ctrl_domain_data *data = &bc_data->domains[i];
226*e2ad626fSUlf Hansson struct imx8m_blk_ctrl_domain *domain = &bc->domains[i];
227*e2ad626fSUlf Hansson int j;
228*e2ad626fSUlf Hansson
229*e2ad626fSUlf Hansson domain->data = data;
230*e2ad626fSUlf Hansson domain->num_paths = data->num_paths;
231*e2ad626fSUlf Hansson
232*e2ad626fSUlf Hansson for (j = 0; j < data->num_clks; j++)
233*e2ad626fSUlf Hansson domain->clks[j].id = data->clk_names[j];
234*e2ad626fSUlf Hansson
235*e2ad626fSUlf Hansson for (j = 0; j < data->num_paths; j++) {
236*e2ad626fSUlf Hansson domain->paths[j].name = data->path_names[j];
237*e2ad626fSUlf Hansson /* Fake value for now, just let ICC could configure NoC mode/priority */
238*e2ad626fSUlf Hansson domain->paths[j].avg_bw = 1;
239*e2ad626fSUlf Hansson domain->paths[j].peak_bw = 1;
240*e2ad626fSUlf Hansson }
241*e2ad626fSUlf Hansson
242*e2ad626fSUlf Hansson ret = devm_of_icc_bulk_get(dev, data->num_paths, domain->paths);
243*e2ad626fSUlf Hansson if (ret) {
244*e2ad626fSUlf Hansson if (ret != -EPROBE_DEFER) {
245*e2ad626fSUlf Hansson dev_warn_once(dev, "Could not get interconnect paths, NoC will stay unconfigured!\n");
246*e2ad626fSUlf Hansson domain->num_paths = 0;
247*e2ad626fSUlf Hansson } else {
248*e2ad626fSUlf Hansson dev_err_probe(dev, ret, "failed to get noc entries\n");
249*e2ad626fSUlf Hansson goto cleanup_pds;
250*e2ad626fSUlf Hansson }
251*e2ad626fSUlf Hansson }
252*e2ad626fSUlf Hansson
253*e2ad626fSUlf Hansson ret = devm_clk_bulk_get(dev, data->num_clks, domain->clks);
254*e2ad626fSUlf Hansson if (ret) {
255*e2ad626fSUlf Hansson dev_err_probe(dev, ret, "failed to get clock\n");
256*e2ad626fSUlf Hansson goto cleanup_pds;
257*e2ad626fSUlf Hansson }
258*e2ad626fSUlf Hansson
259*e2ad626fSUlf Hansson domain->power_dev =
260*e2ad626fSUlf Hansson dev_pm_domain_attach_by_name(dev, data->gpc_name);
261*e2ad626fSUlf Hansson if (IS_ERR(domain->power_dev)) {
262*e2ad626fSUlf Hansson dev_err_probe(dev, PTR_ERR(domain->power_dev),
263*e2ad626fSUlf Hansson "failed to attach power domain \"%s\"\n",
264*e2ad626fSUlf Hansson data->gpc_name);
265*e2ad626fSUlf Hansson ret = PTR_ERR(domain->power_dev);
266*e2ad626fSUlf Hansson goto cleanup_pds;
267*e2ad626fSUlf Hansson }
268*e2ad626fSUlf Hansson
269*e2ad626fSUlf Hansson domain->genpd.name = data->name;
270*e2ad626fSUlf Hansson domain->genpd.power_on = imx8m_blk_ctrl_power_on;
271*e2ad626fSUlf Hansson domain->genpd.power_off = imx8m_blk_ctrl_power_off;
272*e2ad626fSUlf Hansson domain->bc = bc;
273*e2ad626fSUlf Hansson
274*e2ad626fSUlf Hansson ret = pm_genpd_init(&domain->genpd, NULL, true);
275*e2ad626fSUlf Hansson if (ret) {
276*e2ad626fSUlf Hansson dev_err_probe(dev, ret,
277*e2ad626fSUlf Hansson "failed to init power domain \"%s\"\n",
278*e2ad626fSUlf Hansson data->gpc_name);
279*e2ad626fSUlf Hansson dev_pm_domain_detach(domain->power_dev, true);
280*e2ad626fSUlf Hansson goto cleanup_pds;
281*e2ad626fSUlf Hansson }
282*e2ad626fSUlf Hansson
283*e2ad626fSUlf Hansson /*
284*e2ad626fSUlf Hansson * We use runtime PM to trigger power on/off of the upstream GPC
285*e2ad626fSUlf Hansson * domain, as a strict hierarchical parent/child power domain
286*e2ad626fSUlf Hansson * setup doesn't allow us to meet the sequencing requirements.
287*e2ad626fSUlf Hansson * This means we have nested locking of genpd locks, without the
288*e2ad626fSUlf Hansson * nesting being visible at the genpd level, so we need a
289*e2ad626fSUlf Hansson * separate lock class to make lockdep aware of the fact that
290*e2ad626fSUlf Hansson * this are separate domain locks that can be nested without a
291*e2ad626fSUlf Hansson * self-deadlock.
292*e2ad626fSUlf Hansson */
293*e2ad626fSUlf Hansson lockdep_set_class(&domain->genpd.mlock,
294*e2ad626fSUlf Hansson &blk_ctrl_genpd_lock_class);
295*e2ad626fSUlf Hansson
296*e2ad626fSUlf Hansson bc->onecell_data.domains[i] = &domain->genpd;
297*e2ad626fSUlf Hansson }
298*e2ad626fSUlf Hansson
299*e2ad626fSUlf Hansson ret = of_genpd_add_provider_onecell(dev->of_node, &bc->onecell_data);
300*e2ad626fSUlf Hansson if (ret) {
301*e2ad626fSUlf Hansson dev_err_probe(dev, ret, "failed to add power domain provider\n");
302*e2ad626fSUlf Hansson goto cleanup_pds;
303*e2ad626fSUlf Hansson }
304*e2ad626fSUlf Hansson
305*e2ad626fSUlf Hansson bc->power_nb.notifier_call = bc_data->power_notifier_fn;
306*e2ad626fSUlf Hansson ret = dev_pm_genpd_add_notifier(bc->bus_power_dev, &bc->power_nb);
307*e2ad626fSUlf Hansson if (ret) {
308*e2ad626fSUlf Hansson dev_err_probe(dev, ret, "failed to add power notifier\n");
309*e2ad626fSUlf Hansson goto cleanup_provider;
310*e2ad626fSUlf Hansson }
311*e2ad626fSUlf Hansson
312*e2ad626fSUlf Hansson dev_set_drvdata(dev, bc);
313*e2ad626fSUlf Hansson
314*e2ad626fSUlf Hansson ret = devm_of_platform_populate(dev);
315*e2ad626fSUlf Hansson if (ret)
316*e2ad626fSUlf Hansson goto cleanup_provider;
317*e2ad626fSUlf Hansson
318*e2ad626fSUlf Hansson return 0;
319*e2ad626fSUlf Hansson
320*e2ad626fSUlf Hansson cleanup_provider:
321*e2ad626fSUlf Hansson of_genpd_del_provider(dev->of_node);
322*e2ad626fSUlf Hansson cleanup_pds:
323*e2ad626fSUlf Hansson for (i--; i >= 0; i--) {
324*e2ad626fSUlf Hansson pm_genpd_remove(&bc->domains[i].genpd);
325*e2ad626fSUlf Hansson dev_pm_domain_detach(bc->domains[i].power_dev, true);
326*e2ad626fSUlf Hansson }
327*e2ad626fSUlf Hansson
328*e2ad626fSUlf Hansson dev_pm_domain_detach(bc->bus_power_dev, true);
329*e2ad626fSUlf Hansson
330*e2ad626fSUlf Hansson return ret;
331*e2ad626fSUlf Hansson }
332*e2ad626fSUlf Hansson
imx8m_blk_ctrl_remove(struct platform_device * pdev)333*e2ad626fSUlf Hansson static int imx8m_blk_ctrl_remove(struct platform_device *pdev)
334*e2ad626fSUlf Hansson {
335*e2ad626fSUlf Hansson struct imx8m_blk_ctrl *bc = dev_get_drvdata(&pdev->dev);
336*e2ad626fSUlf Hansson int i;
337*e2ad626fSUlf Hansson
338*e2ad626fSUlf Hansson of_genpd_del_provider(pdev->dev.of_node);
339*e2ad626fSUlf Hansson
340*e2ad626fSUlf Hansson for (i = 0; bc->onecell_data.num_domains; i++) {
341*e2ad626fSUlf Hansson struct imx8m_blk_ctrl_domain *domain = &bc->domains[i];
342*e2ad626fSUlf Hansson
343*e2ad626fSUlf Hansson pm_genpd_remove(&domain->genpd);
344*e2ad626fSUlf Hansson dev_pm_domain_detach(domain->power_dev, true);
345*e2ad626fSUlf Hansson }
346*e2ad626fSUlf Hansson
347*e2ad626fSUlf Hansson dev_pm_genpd_remove_notifier(bc->bus_power_dev);
348*e2ad626fSUlf Hansson
349*e2ad626fSUlf Hansson dev_pm_domain_detach(bc->bus_power_dev, true);
350*e2ad626fSUlf Hansson
351*e2ad626fSUlf Hansson return 0;
352*e2ad626fSUlf Hansson }
353*e2ad626fSUlf Hansson
354*e2ad626fSUlf Hansson #ifdef CONFIG_PM_SLEEP
imx8m_blk_ctrl_suspend(struct device * dev)355*e2ad626fSUlf Hansson static int imx8m_blk_ctrl_suspend(struct device *dev)
356*e2ad626fSUlf Hansson {
357*e2ad626fSUlf Hansson struct imx8m_blk_ctrl *bc = dev_get_drvdata(dev);
358*e2ad626fSUlf Hansson int ret, i;
359*e2ad626fSUlf Hansson
360*e2ad626fSUlf Hansson /*
361*e2ad626fSUlf Hansson * This may look strange, but is done so the generic PM_SLEEP code
362*e2ad626fSUlf Hansson * can power down our domains and more importantly power them up again
363*e2ad626fSUlf Hansson * after resume, without tripping over our usage of runtime PM to
364*e2ad626fSUlf Hansson * control the upstream GPC domains. Things happen in the right order
365*e2ad626fSUlf Hansson * in the system suspend/resume paths due to the device parent/child
366*e2ad626fSUlf Hansson * hierarchy.
367*e2ad626fSUlf Hansson */
368*e2ad626fSUlf Hansson ret = pm_runtime_get_sync(bc->bus_power_dev);
369*e2ad626fSUlf Hansson if (ret < 0) {
370*e2ad626fSUlf Hansson pm_runtime_put_noidle(bc->bus_power_dev);
371*e2ad626fSUlf Hansson return ret;
372*e2ad626fSUlf Hansson }
373*e2ad626fSUlf Hansson
374*e2ad626fSUlf Hansson for (i = 0; i < bc->onecell_data.num_domains; i++) {
375*e2ad626fSUlf Hansson struct imx8m_blk_ctrl_domain *domain = &bc->domains[i];
376*e2ad626fSUlf Hansson
377*e2ad626fSUlf Hansson ret = pm_runtime_get_sync(domain->power_dev);
378*e2ad626fSUlf Hansson if (ret < 0) {
379*e2ad626fSUlf Hansson pm_runtime_put_noidle(domain->power_dev);
380*e2ad626fSUlf Hansson goto out_fail;
381*e2ad626fSUlf Hansson }
382*e2ad626fSUlf Hansson }
383*e2ad626fSUlf Hansson
384*e2ad626fSUlf Hansson return 0;
385*e2ad626fSUlf Hansson
386*e2ad626fSUlf Hansson out_fail:
387*e2ad626fSUlf Hansson for (i--; i >= 0; i--)
388*e2ad626fSUlf Hansson pm_runtime_put(bc->domains[i].power_dev);
389*e2ad626fSUlf Hansson
390*e2ad626fSUlf Hansson pm_runtime_put(bc->bus_power_dev);
391*e2ad626fSUlf Hansson
392*e2ad626fSUlf Hansson return ret;
393*e2ad626fSUlf Hansson }
394*e2ad626fSUlf Hansson
imx8m_blk_ctrl_resume(struct device * dev)395*e2ad626fSUlf Hansson static int imx8m_blk_ctrl_resume(struct device *dev)
396*e2ad626fSUlf Hansson {
397*e2ad626fSUlf Hansson struct imx8m_blk_ctrl *bc = dev_get_drvdata(dev);
398*e2ad626fSUlf Hansson int i;
399*e2ad626fSUlf Hansson
400*e2ad626fSUlf Hansson for (i = 0; i < bc->onecell_data.num_domains; i++)
401*e2ad626fSUlf Hansson pm_runtime_put(bc->domains[i].power_dev);
402*e2ad626fSUlf Hansson
403*e2ad626fSUlf Hansson pm_runtime_put(bc->bus_power_dev);
404*e2ad626fSUlf Hansson
405*e2ad626fSUlf Hansson return 0;
406*e2ad626fSUlf Hansson }
407*e2ad626fSUlf Hansson #endif
408*e2ad626fSUlf Hansson
409*e2ad626fSUlf Hansson static const struct dev_pm_ops imx8m_blk_ctrl_pm_ops = {
410*e2ad626fSUlf Hansson SET_SYSTEM_SLEEP_PM_OPS(imx8m_blk_ctrl_suspend, imx8m_blk_ctrl_resume)
411*e2ad626fSUlf Hansson };
412*e2ad626fSUlf Hansson
imx8mm_vpu_power_notifier(struct notifier_block * nb,unsigned long action,void * data)413*e2ad626fSUlf Hansson static int imx8mm_vpu_power_notifier(struct notifier_block *nb,
414*e2ad626fSUlf Hansson unsigned long action, void *data)
415*e2ad626fSUlf Hansson {
416*e2ad626fSUlf Hansson struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl,
417*e2ad626fSUlf Hansson power_nb);
418*e2ad626fSUlf Hansson
419*e2ad626fSUlf Hansson if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF)
420*e2ad626fSUlf Hansson return NOTIFY_OK;
421*e2ad626fSUlf Hansson
422*e2ad626fSUlf Hansson /*
423*e2ad626fSUlf Hansson * The ADB in the VPUMIX domain has no separate reset and clock
424*e2ad626fSUlf Hansson * enable bits, but is ungated together with the VPU clocks. To
425*e2ad626fSUlf Hansson * allow the handshake with the GPC to progress we put the VPUs
426*e2ad626fSUlf Hansson * in reset and ungate the clocks.
427*e2ad626fSUlf Hansson */
428*e2ad626fSUlf Hansson regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, BIT(0) | BIT(1) | BIT(2));
429*e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(0) | BIT(1) | BIT(2));
430*e2ad626fSUlf Hansson
431*e2ad626fSUlf Hansson if (action == GENPD_NOTIFY_ON) {
432*e2ad626fSUlf Hansson /*
433*e2ad626fSUlf Hansson * On power up we have no software backchannel to the GPC to
434*e2ad626fSUlf Hansson * wait for the ADB handshake to happen, so we just delay for a
435*e2ad626fSUlf Hansson * bit. On power down the GPC driver waits for the handshake.
436*e2ad626fSUlf Hansson */
437*e2ad626fSUlf Hansson udelay(5);
438*e2ad626fSUlf Hansson
439*e2ad626fSUlf Hansson /* set "fuse" bits to enable the VPUs */
440*e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, 0x8, 0xffffffff);
441*e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, 0xc, 0xffffffff);
442*e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, 0x10, 0xffffffff);
443*e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, 0x14, 0xffffffff);
444*e2ad626fSUlf Hansson }
445*e2ad626fSUlf Hansson
446*e2ad626fSUlf Hansson return NOTIFY_OK;
447*e2ad626fSUlf Hansson }
448*e2ad626fSUlf Hansson
449*e2ad626fSUlf Hansson static const struct imx8m_blk_ctrl_domain_data imx8mm_vpu_blk_ctl_domain_data[] = {
450*e2ad626fSUlf Hansson [IMX8MM_VPUBLK_PD_G1] = {
451*e2ad626fSUlf Hansson .name = "vpublk-g1",
452*e2ad626fSUlf Hansson .clk_names = (const char *[]){ "g1", },
453*e2ad626fSUlf Hansson .num_clks = 1,
454*e2ad626fSUlf Hansson .gpc_name = "g1",
455*e2ad626fSUlf Hansson .rst_mask = BIT(1),
456*e2ad626fSUlf Hansson .clk_mask = BIT(1),
457*e2ad626fSUlf Hansson },
458*e2ad626fSUlf Hansson [IMX8MM_VPUBLK_PD_G2] = {
459*e2ad626fSUlf Hansson .name = "vpublk-g2",
460*e2ad626fSUlf Hansson .clk_names = (const char *[]){ "g2", },
461*e2ad626fSUlf Hansson .num_clks = 1,
462*e2ad626fSUlf Hansson .gpc_name = "g2",
463*e2ad626fSUlf Hansson .rst_mask = BIT(0),
464*e2ad626fSUlf Hansson .clk_mask = BIT(0),
465*e2ad626fSUlf Hansson },
466*e2ad626fSUlf Hansson [IMX8MM_VPUBLK_PD_H1] = {
467*e2ad626fSUlf Hansson .name = "vpublk-h1",
468*e2ad626fSUlf Hansson .clk_names = (const char *[]){ "h1", },
469*e2ad626fSUlf Hansson .num_clks = 1,
470*e2ad626fSUlf Hansson .gpc_name = "h1",
471*e2ad626fSUlf Hansson .rst_mask = BIT(2),
472*e2ad626fSUlf Hansson .clk_mask = BIT(2),
473*e2ad626fSUlf Hansson },
474*e2ad626fSUlf Hansson };
475*e2ad626fSUlf Hansson
476*e2ad626fSUlf Hansson static const struct imx8m_blk_ctrl_data imx8mm_vpu_blk_ctl_dev_data = {
477*e2ad626fSUlf Hansson .max_reg = 0x18,
478*e2ad626fSUlf Hansson .power_notifier_fn = imx8mm_vpu_power_notifier,
479*e2ad626fSUlf Hansson .domains = imx8mm_vpu_blk_ctl_domain_data,
480*e2ad626fSUlf Hansson .num_domains = ARRAY_SIZE(imx8mm_vpu_blk_ctl_domain_data),
481*e2ad626fSUlf Hansson };
482*e2ad626fSUlf Hansson
483*e2ad626fSUlf Hansson static const struct imx8m_blk_ctrl_domain_data imx8mp_vpu_blk_ctl_domain_data[] = {
484*e2ad626fSUlf Hansson [IMX8MP_VPUBLK_PD_G1] = {
485*e2ad626fSUlf Hansson .name = "vpublk-g1",
486*e2ad626fSUlf Hansson .clk_names = (const char *[]){ "g1", },
487*e2ad626fSUlf Hansson .num_clks = 1,
488*e2ad626fSUlf Hansson .gpc_name = "g1",
489*e2ad626fSUlf Hansson .rst_mask = BIT(1),
490*e2ad626fSUlf Hansson .clk_mask = BIT(1),
491*e2ad626fSUlf Hansson .path_names = (const char *[]){"g1"},
492*e2ad626fSUlf Hansson .num_paths = 1,
493*e2ad626fSUlf Hansson },
494*e2ad626fSUlf Hansson [IMX8MP_VPUBLK_PD_G2] = {
495*e2ad626fSUlf Hansson .name = "vpublk-g2",
496*e2ad626fSUlf Hansson .clk_names = (const char *[]){ "g2", },
497*e2ad626fSUlf Hansson .num_clks = 1,
498*e2ad626fSUlf Hansson .gpc_name = "g2",
499*e2ad626fSUlf Hansson .rst_mask = BIT(0),
500*e2ad626fSUlf Hansson .clk_mask = BIT(0),
501*e2ad626fSUlf Hansson .path_names = (const char *[]){"g2"},
502*e2ad626fSUlf Hansson .num_paths = 1,
503*e2ad626fSUlf Hansson },
504*e2ad626fSUlf Hansson [IMX8MP_VPUBLK_PD_VC8000E] = {
505*e2ad626fSUlf Hansson .name = "vpublk-vc8000e",
506*e2ad626fSUlf Hansson .clk_names = (const char *[]){ "vc8000e", },
507*e2ad626fSUlf Hansson .num_clks = 1,
508*e2ad626fSUlf Hansson .gpc_name = "vc8000e",
509*e2ad626fSUlf Hansson .rst_mask = BIT(2),
510*e2ad626fSUlf Hansson .clk_mask = BIT(2),
511*e2ad626fSUlf Hansson .path_names = (const char *[]){"vc8000e"},
512*e2ad626fSUlf Hansson .num_paths = 1,
513*e2ad626fSUlf Hansson },
514*e2ad626fSUlf Hansson };
515*e2ad626fSUlf Hansson
516*e2ad626fSUlf Hansson static const struct imx8m_blk_ctrl_data imx8mp_vpu_blk_ctl_dev_data = {
517*e2ad626fSUlf Hansson .max_reg = 0x18,
518*e2ad626fSUlf Hansson .power_notifier_fn = imx8mm_vpu_power_notifier,
519*e2ad626fSUlf Hansson .domains = imx8mp_vpu_blk_ctl_domain_data,
520*e2ad626fSUlf Hansson .num_domains = ARRAY_SIZE(imx8mp_vpu_blk_ctl_domain_data),
521*e2ad626fSUlf Hansson };
522*e2ad626fSUlf Hansson
imx8mm_disp_power_notifier(struct notifier_block * nb,unsigned long action,void * data)523*e2ad626fSUlf Hansson static int imx8mm_disp_power_notifier(struct notifier_block *nb,
524*e2ad626fSUlf Hansson unsigned long action, void *data)
525*e2ad626fSUlf Hansson {
526*e2ad626fSUlf Hansson struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl,
527*e2ad626fSUlf Hansson power_nb);
528*e2ad626fSUlf Hansson
529*e2ad626fSUlf Hansson if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF)
530*e2ad626fSUlf Hansson return NOTIFY_OK;
531*e2ad626fSUlf Hansson
532*e2ad626fSUlf Hansson /* Enable bus clock and deassert bus reset */
533*e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(12));
534*e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(6));
535*e2ad626fSUlf Hansson
536*e2ad626fSUlf Hansson /*
537*e2ad626fSUlf Hansson * On power up we have no software backchannel to the GPC to
538*e2ad626fSUlf Hansson * wait for the ADB handshake to happen, so we just delay for a
539*e2ad626fSUlf Hansson * bit. On power down the GPC driver waits for the handshake.
540*e2ad626fSUlf Hansson */
541*e2ad626fSUlf Hansson if (action == GENPD_NOTIFY_ON)
542*e2ad626fSUlf Hansson udelay(5);
543*e2ad626fSUlf Hansson
544*e2ad626fSUlf Hansson
545*e2ad626fSUlf Hansson return NOTIFY_OK;
546*e2ad626fSUlf Hansson }
547*e2ad626fSUlf Hansson
548*e2ad626fSUlf Hansson static const struct imx8m_blk_ctrl_domain_data imx8mm_disp_blk_ctl_domain_data[] = {
549*e2ad626fSUlf Hansson [IMX8MM_DISPBLK_PD_CSI_BRIDGE] = {
550*e2ad626fSUlf Hansson .name = "dispblk-csi-bridge",
551*e2ad626fSUlf Hansson .clk_names = (const char *[]){ "csi-bridge-axi", "csi-bridge-apb",
552*e2ad626fSUlf Hansson "csi-bridge-core", },
553*e2ad626fSUlf Hansson .num_clks = 3,
554*e2ad626fSUlf Hansson .gpc_name = "csi-bridge",
555*e2ad626fSUlf Hansson .rst_mask = BIT(0) | BIT(1) | BIT(2),
556*e2ad626fSUlf Hansson .clk_mask = BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5),
557*e2ad626fSUlf Hansson },
558*e2ad626fSUlf Hansson [IMX8MM_DISPBLK_PD_LCDIF] = {
559*e2ad626fSUlf Hansson .name = "dispblk-lcdif",
560*e2ad626fSUlf Hansson .clk_names = (const char *[]){ "lcdif-axi", "lcdif-apb", "lcdif-pix", },
561*e2ad626fSUlf Hansson .num_clks = 3,
562*e2ad626fSUlf Hansson .gpc_name = "lcdif",
563*e2ad626fSUlf Hansson .clk_mask = BIT(6) | BIT(7),
564*e2ad626fSUlf Hansson },
565*e2ad626fSUlf Hansson [IMX8MM_DISPBLK_PD_MIPI_DSI] = {
566*e2ad626fSUlf Hansson .name = "dispblk-mipi-dsi",
567*e2ad626fSUlf Hansson .clk_names = (const char *[]){ "dsi-pclk", "dsi-ref", },
568*e2ad626fSUlf Hansson .num_clks = 2,
569*e2ad626fSUlf Hansson .gpc_name = "mipi-dsi",
570*e2ad626fSUlf Hansson .rst_mask = BIT(5),
571*e2ad626fSUlf Hansson .clk_mask = BIT(8) | BIT(9),
572*e2ad626fSUlf Hansson .mipi_phy_rst_mask = BIT(17),
573*e2ad626fSUlf Hansson },
574*e2ad626fSUlf Hansson [IMX8MM_DISPBLK_PD_MIPI_CSI] = {
575*e2ad626fSUlf Hansson .name = "dispblk-mipi-csi",
576*e2ad626fSUlf Hansson .clk_names = (const char *[]){ "csi-aclk", "csi-pclk" },
577*e2ad626fSUlf Hansson .num_clks = 2,
578*e2ad626fSUlf Hansson .gpc_name = "mipi-csi",
579*e2ad626fSUlf Hansson .rst_mask = BIT(3) | BIT(4),
580*e2ad626fSUlf Hansson .clk_mask = BIT(10) | BIT(11),
581*e2ad626fSUlf Hansson .mipi_phy_rst_mask = BIT(16),
582*e2ad626fSUlf Hansson },
583*e2ad626fSUlf Hansson };
584*e2ad626fSUlf Hansson
585*e2ad626fSUlf Hansson static const struct imx8m_blk_ctrl_data imx8mm_disp_blk_ctl_dev_data = {
586*e2ad626fSUlf Hansson .max_reg = 0x2c,
587*e2ad626fSUlf Hansson .power_notifier_fn = imx8mm_disp_power_notifier,
588*e2ad626fSUlf Hansson .domains = imx8mm_disp_blk_ctl_domain_data,
589*e2ad626fSUlf Hansson .num_domains = ARRAY_SIZE(imx8mm_disp_blk_ctl_domain_data),
590*e2ad626fSUlf Hansson };
591*e2ad626fSUlf Hansson
592*e2ad626fSUlf Hansson
imx8mn_disp_power_notifier(struct notifier_block * nb,unsigned long action,void * data)593*e2ad626fSUlf Hansson static int imx8mn_disp_power_notifier(struct notifier_block *nb,
594*e2ad626fSUlf Hansson unsigned long action, void *data)
595*e2ad626fSUlf Hansson {
596*e2ad626fSUlf Hansson struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl,
597*e2ad626fSUlf Hansson power_nb);
598*e2ad626fSUlf Hansson
599*e2ad626fSUlf Hansson if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF)
600*e2ad626fSUlf Hansson return NOTIFY_OK;
601*e2ad626fSUlf Hansson
602*e2ad626fSUlf Hansson /* Enable bus clock and deassert bus reset */
603*e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(8));
604*e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(8));
605*e2ad626fSUlf Hansson
606*e2ad626fSUlf Hansson /*
607*e2ad626fSUlf Hansson * On power up we have no software backchannel to the GPC to
608*e2ad626fSUlf Hansson * wait for the ADB handshake to happen, so we just delay for a
609*e2ad626fSUlf Hansson * bit. On power down the GPC driver waits for the handshake.
610*e2ad626fSUlf Hansson */
611*e2ad626fSUlf Hansson if (action == GENPD_NOTIFY_ON)
612*e2ad626fSUlf Hansson udelay(5);
613*e2ad626fSUlf Hansson
614*e2ad626fSUlf Hansson
615*e2ad626fSUlf Hansson return NOTIFY_OK;
616*e2ad626fSUlf Hansson }
617*e2ad626fSUlf Hansson
618*e2ad626fSUlf Hansson static const struct imx8m_blk_ctrl_domain_data imx8mn_disp_blk_ctl_domain_data[] = {
619*e2ad626fSUlf Hansson [IMX8MN_DISPBLK_PD_MIPI_DSI] = {
620*e2ad626fSUlf Hansson .name = "dispblk-mipi-dsi",
621*e2ad626fSUlf Hansson .clk_names = (const char *[]){ "dsi-pclk", "dsi-ref", },
622*e2ad626fSUlf Hansson .num_clks = 2,
623*e2ad626fSUlf Hansson .gpc_name = "mipi-dsi",
624*e2ad626fSUlf Hansson .rst_mask = BIT(0) | BIT(1),
625*e2ad626fSUlf Hansson .clk_mask = BIT(0) | BIT(1),
626*e2ad626fSUlf Hansson .mipi_phy_rst_mask = BIT(17),
627*e2ad626fSUlf Hansson },
628*e2ad626fSUlf Hansson [IMX8MN_DISPBLK_PD_MIPI_CSI] = {
629*e2ad626fSUlf Hansson .name = "dispblk-mipi-csi",
630*e2ad626fSUlf Hansson .clk_names = (const char *[]){ "csi-aclk", "csi-pclk" },
631*e2ad626fSUlf Hansson .num_clks = 2,
632*e2ad626fSUlf Hansson .gpc_name = "mipi-csi",
633*e2ad626fSUlf Hansson .rst_mask = BIT(2) | BIT(3),
634*e2ad626fSUlf Hansson .clk_mask = BIT(2) | BIT(3),
635*e2ad626fSUlf Hansson .mipi_phy_rst_mask = BIT(16),
636*e2ad626fSUlf Hansson },
637*e2ad626fSUlf Hansson [IMX8MN_DISPBLK_PD_LCDIF] = {
638*e2ad626fSUlf Hansson .name = "dispblk-lcdif",
639*e2ad626fSUlf Hansson .clk_names = (const char *[]){ "lcdif-axi", "lcdif-apb", "lcdif-pix", },
640*e2ad626fSUlf Hansson .num_clks = 3,
641*e2ad626fSUlf Hansson .gpc_name = "lcdif",
642*e2ad626fSUlf Hansson .rst_mask = BIT(4) | BIT(5),
643*e2ad626fSUlf Hansson .clk_mask = BIT(4) | BIT(5),
644*e2ad626fSUlf Hansson },
645*e2ad626fSUlf Hansson [IMX8MN_DISPBLK_PD_ISI] = {
646*e2ad626fSUlf Hansson .name = "dispblk-isi",
647*e2ad626fSUlf Hansson .clk_names = (const char *[]){ "disp_axi", "disp_apb", "disp_axi_root",
648*e2ad626fSUlf Hansson "disp_apb_root"},
649*e2ad626fSUlf Hansson .num_clks = 4,
650*e2ad626fSUlf Hansson .gpc_name = "isi",
651*e2ad626fSUlf Hansson .rst_mask = BIT(6) | BIT(7),
652*e2ad626fSUlf Hansson .clk_mask = BIT(6) | BIT(7),
653*e2ad626fSUlf Hansson },
654*e2ad626fSUlf Hansson };
655*e2ad626fSUlf Hansson
656*e2ad626fSUlf Hansson static const struct imx8m_blk_ctrl_data imx8mn_disp_blk_ctl_dev_data = {
657*e2ad626fSUlf Hansson .max_reg = 0x84,
658*e2ad626fSUlf Hansson .power_notifier_fn = imx8mn_disp_power_notifier,
659*e2ad626fSUlf Hansson .domains = imx8mn_disp_blk_ctl_domain_data,
660*e2ad626fSUlf Hansson .num_domains = ARRAY_SIZE(imx8mn_disp_blk_ctl_domain_data),
661*e2ad626fSUlf Hansson };
662*e2ad626fSUlf Hansson
663*e2ad626fSUlf Hansson #define LCDIF_ARCACHE_CTRL 0x4c
664*e2ad626fSUlf Hansson #define LCDIF_1_RD_HURRY GENMASK(15, 13)
665*e2ad626fSUlf Hansson #define LCDIF_0_RD_HURRY GENMASK(12, 10)
666*e2ad626fSUlf Hansson
imx8mp_media_power_notifier(struct notifier_block * nb,unsigned long action,void * data)667*e2ad626fSUlf Hansson static int imx8mp_media_power_notifier(struct notifier_block *nb,
668*e2ad626fSUlf Hansson unsigned long action, void *data)
669*e2ad626fSUlf Hansson {
670*e2ad626fSUlf Hansson struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl,
671*e2ad626fSUlf Hansson power_nb);
672*e2ad626fSUlf Hansson
673*e2ad626fSUlf Hansson if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF)
674*e2ad626fSUlf Hansson return NOTIFY_OK;
675*e2ad626fSUlf Hansson
676*e2ad626fSUlf Hansson /* Enable bus clock and deassert bus reset */
677*e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(8));
678*e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(8));
679*e2ad626fSUlf Hansson
680*e2ad626fSUlf Hansson if (action == GENPD_NOTIFY_ON) {
681*e2ad626fSUlf Hansson /*
682*e2ad626fSUlf Hansson * On power up we have no software backchannel to the GPC to
683*e2ad626fSUlf Hansson * wait for the ADB handshake to happen, so we just delay for a
684*e2ad626fSUlf Hansson * bit. On power down the GPC driver waits for the handshake.
685*e2ad626fSUlf Hansson */
686*e2ad626fSUlf Hansson udelay(5);
687*e2ad626fSUlf Hansson
688*e2ad626fSUlf Hansson /*
689*e2ad626fSUlf Hansson * Set panic read hurry level for both LCDIF interfaces to
690*e2ad626fSUlf Hansson * maximum priority to minimize chances of display FIFO
691*e2ad626fSUlf Hansson * underflow.
692*e2ad626fSUlf Hansson */
693*e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, LCDIF_ARCACHE_CTRL,
694*e2ad626fSUlf Hansson FIELD_PREP(LCDIF_1_RD_HURRY, 7) |
695*e2ad626fSUlf Hansson FIELD_PREP(LCDIF_0_RD_HURRY, 7));
696*e2ad626fSUlf Hansson }
697*e2ad626fSUlf Hansson
698*e2ad626fSUlf Hansson return NOTIFY_OK;
699*e2ad626fSUlf Hansson }
700*e2ad626fSUlf Hansson
701*e2ad626fSUlf Hansson /*
702*e2ad626fSUlf Hansson * From i.MX 8M Plus Applications Processor Reference Manual, Rev. 1,
703*e2ad626fSUlf Hansson * section 13.2.2, 13.2.3
704*e2ad626fSUlf Hansson * isp-ahb and dwe are not in Figure 13-5. Media BLK_CTRL Clocks
705*e2ad626fSUlf Hansson */
706*e2ad626fSUlf Hansson static const struct imx8m_blk_ctrl_domain_data imx8mp_media_blk_ctl_domain_data[] = {
707*e2ad626fSUlf Hansson [IMX8MP_MEDIABLK_PD_MIPI_DSI_1] = {
708*e2ad626fSUlf Hansson .name = "mediablk-mipi-dsi-1",
709*e2ad626fSUlf Hansson .clk_names = (const char *[]){ "apb", "phy", },
710*e2ad626fSUlf Hansson .num_clks = 2,
711*e2ad626fSUlf Hansson .gpc_name = "mipi-dsi1",
712*e2ad626fSUlf Hansson .rst_mask = BIT(0) | BIT(1),
713*e2ad626fSUlf Hansson .clk_mask = BIT(0) | BIT(1),
714*e2ad626fSUlf Hansson .mipi_phy_rst_mask = BIT(17),
715*e2ad626fSUlf Hansson },
716*e2ad626fSUlf Hansson [IMX8MP_MEDIABLK_PD_MIPI_CSI2_1] = {
717*e2ad626fSUlf Hansson .name = "mediablk-mipi-csi2-1",
718*e2ad626fSUlf Hansson .clk_names = (const char *[]){ "apb", "cam1" },
719*e2ad626fSUlf Hansson .num_clks = 2,
720*e2ad626fSUlf Hansson .gpc_name = "mipi-csi1",
721*e2ad626fSUlf Hansson .rst_mask = BIT(2) | BIT(3),
722*e2ad626fSUlf Hansson .clk_mask = BIT(2) | BIT(3),
723*e2ad626fSUlf Hansson .mipi_phy_rst_mask = BIT(16),
724*e2ad626fSUlf Hansson },
725*e2ad626fSUlf Hansson [IMX8MP_MEDIABLK_PD_LCDIF_1] = {
726*e2ad626fSUlf Hansson .name = "mediablk-lcdif-1",
727*e2ad626fSUlf Hansson .clk_names = (const char *[]){ "disp1", "apb", "axi", },
728*e2ad626fSUlf Hansson .num_clks = 3,
729*e2ad626fSUlf Hansson .gpc_name = "lcdif1",
730*e2ad626fSUlf Hansson .rst_mask = BIT(4) | BIT(5) | BIT(23),
731*e2ad626fSUlf Hansson .clk_mask = BIT(4) | BIT(5) | BIT(23),
732*e2ad626fSUlf Hansson .path_names = (const char *[]){"lcdif-rd", "lcdif-wr"},
733*e2ad626fSUlf Hansson .num_paths = 2,
734*e2ad626fSUlf Hansson },
735*e2ad626fSUlf Hansson [IMX8MP_MEDIABLK_PD_ISI] = {
736*e2ad626fSUlf Hansson .name = "mediablk-isi",
737*e2ad626fSUlf Hansson .clk_names = (const char *[]){ "axi", "apb" },
738*e2ad626fSUlf Hansson .num_clks = 2,
739*e2ad626fSUlf Hansson .gpc_name = "isi",
740*e2ad626fSUlf Hansson .rst_mask = BIT(6) | BIT(7),
741*e2ad626fSUlf Hansson .clk_mask = BIT(6) | BIT(7),
742*e2ad626fSUlf Hansson .path_names = (const char *[]){"isi0", "isi1", "isi2"},
743*e2ad626fSUlf Hansson .num_paths = 3,
744*e2ad626fSUlf Hansson },
745*e2ad626fSUlf Hansson [IMX8MP_MEDIABLK_PD_MIPI_CSI2_2] = {
746*e2ad626fSUlf Hansson .name = "mediablk-mipi-csi2-2",
747*e2ad626fSUlf Hansson .clk_names = (const char *[]){ "apb", "cam2" },
748*e2ad626fSUlf Hansson .num_clks = 2,
749*e2ad626fSUlf Hansson .gpc_name = "mipi-csi2",
750*e2ad626fSUlf Hansson .rst_mask = BIT(9) | BIT(10),
751*e2ad626fSUlf Hansson .clk_mask = BIT(9) | BIT(10),
752*e2ad626fSUlf Hansson .mipi_phy_rst_mask = BIT(30),
753*e2ad626fSUlf Hansson },
754*e2ad626fSUlf Hansson [IMX8MP_MEDIABLK_PD_LCDIF_2] = {
755*e2ad626fSUlf Hansson .name = "mediablk-lcdif-2",
756*e2ad626fSUlf Hansson .clk_names = (const char *[]){ "disp2", "apb", "axi", },
757*e2ad626fSUlf Hansson .num_clks = 3,
758*e2ad626fSUlf Hansson .gpc_name = "lcdif2",
759*e2ad626fSUlf Hansson .rst_mask = BIT(11) | BIT(12) | BIT(24),
760*e2ad626fSUlf Hansson .clk_mask = BIT(11) | BIT(12) | BIT(24),
761*e2ad626fSUlf Hansson .path_names = (const char *[]){"lcdif-rd", "lcdif-wr"},
762*e2ad626fSUlf Hansson .num_paths = 2,
763*e2ad626fSUlf Hansson },
764*e2ad626fSUlf Hansson [IMX8MP_MEDIABLK_PD_ISP] = {
765*e2ad626fSUlf Hansson .name = "mediablk-isp",
766*e2ad626fSUlf Hansson .clk_names = (const char *[]){ "isp", "axi", "apb" },
767*e2ad626fSUlf Hansson .num_clks = 3,
768*e2ad626fSUlf Hansson .gpc_name = "isp",
769*e2ad626fSUlf Hansson .rst_mask = BIT(16) | BIT(17) | BIT(18),
770*e2ad626fSUlf Hansson .clk_mask = BIT(16) | BIT(17) | BIT(18),
771*e2ad626fSUlf Hansson .path_names = (const char *[]){"isp0", "isp1"},
772*e2ad626fSUlf Hansson .num_paths = 2,
773*e2ad626fSUlf Hansson },
774*e2ad626fSUlf Hansson [IMX8MP_MEDIABLK_PD_DWE] = {
775*e2ad626fSUlf Hansson .name = "mediablk-dwe",
776*e2ad626fSUlf Hansson .clk_names = (const char *[]){ "axi", "apb" },
777*e2ad626fSUlf Hansson .num_clks = 2,
778*e2ad626fSUlf Hansson .gpc_name = "dwe",
779*e2ad626fSUlf Hansson .rst_mask = BIT(19) | BIT(20) | BIT(21),
780*e2ad626fSUlf Hansson .clk_mask = BIT(19) | BIT(20) | BIT(21),
781*e2ad626fSUlf Hansson .path_names = (const char *[]){"dwe"},
782*e2ad626fSUlf Hansson .num_paths = 1,
783*e2ad626fSUlf Hansson },
784*e2ad626fSUlf Hansson [IMX8MP_MEDIABLK_PD_MIPI_DSI_2] = {
785*e2ad626fSUlf Hansson .name = "mediablk-mipi-dsi-2",
786*e2ad626fSUlf Hansson .clk_names = (const char *[]){ "phy", },
787*e2ad626fSUlf Hansson .num_clks = 1,
788*e2ad626fSUlf Hansson .gpc_name = "mipi-dsi2",
789*e2ad626fSUlf Hansson .rst_mask = BIT(22),
790*e2ad626fSUlf Hansson .clk_mask = BIT(22),
791*e2ad626fSUlf Hansson .mipi_phy_rst_mask = BIT(29),
792*e2ad626fSUlf Hansson },
793*e2ad626fSUlf Hansson };
794*e2ad626fSUlf Hansson
795*e2ad626fSUlf Hansson static const struct imx8m_blk_ctrl_data imx8mp_media_blk_ctl_dev_data = {
796*e2ad626fSUlf Hansson .max_reg = 0x138,
797*e2ad626fSUlf Hansson .power_notifier_fn = imx8mp_media_power_notifier,
798*e2ad626fSUlf Hansson .domains = imx8mp_media_blk_ctl_domain_data,
799*e2ad626fSUlf Hansson .num_domains = ARRAY_SIZE(imx8mp_media_blk_ctl_domain_data),
800*e2ad626fSUlf Hansson };
801*e2ad626fSUlf Hansson
imx8mq_vpu_power_notifier(struct notifier_block * nb,unsigned long action,void * data)802*e2ad626fSUlf Hansson static int imx8mq_vpu_power_notifier(struct notifier_block *nb,
803*e2ad626fSUlf Hansson unsigned long action, void *data)
804*e2ad626fSUlf Hansson {
805*e2ad626fSUlf Hansson struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl,
806*e2ad626fSUlf Hansson power_nb);
807*e2ad626fSUlf Hansson
808*e2ad626fSUlf Hansson if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF)
809*e2ad626fSUlf Hansson return NOTIFY_OK;
810*e2ad626fSUlf Hansson
811*e2ad626fSUlf Hansson /*
812*e2ad626fSUlf Hansson * The ADB in the VPUMIX domain has no separate reset and clock
813*e2ad626fSUlf Hansson * enable bits, but is ungated and reset together with the VPUs. The
814*e2ad626fSUlf Hansson * reset and clock enable inputs to the ADB is a logical OR of the
815*e2ad626fSUlf Hansson * VPU bits. In order to set the G2 fuse bits, the G2 clock must
816*e2ad626fSUlf Hansson * also be enabled.
817*e2ad626fSUlf Hansson */
818*e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(0) | BIT(1));
819*e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(0) | BIT(1));
820*e2ad626fSUlf Hansson
821*e2ad626fSUlf Hansson if (action == GENPD_NOTIFY_ON) {
822*e2ad626fSUlf Hansson /*
823*e2ad626fSUlf Hansson * On power up we have no software backchannel to the GPC to
824*e2ad626fSUlf Hansson * wait for the ADB handshake to happen, so we just delay for a
825*e2ad626fSUlf Hansson * bit. On power down the GPC driver waits for the handshake.
826*e2ad626fSUlf Hansson */
827*e2ad626fSUlf Hansson udelay(5);
828*e2ad626fSUlf Hansson
829*e2ad626fSUlf Hansson /* set "fuse" bits to enable the VPUs */
830*e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, 0x8, 0xffffffff);
831*e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, 0xc, 0xffffffff);
832*e2ad626fSUlf Hansson regmap_set_bits(bc->regmap, 0x10, 0xffffffff);
833*e2ad626fSUlf Hansson }
834*e2ad626fSUlf Hansson
835*e2ad626fSUlf Hansson return NOTIFY_OK;
836*e2ad626fSUlf Hansson }
837*e2ad626fSUlf Hansson
838*e2ad626fSUlf Hansson static const struct imx8m_blk_ctrl_domain_data imx8mq_vpu_blk_ctl_domain_data[] = {
839*e2ad626fSUlf Hansson [IMX8MQ_VPUBLK_PD_G1] = {
840*e2ad626fSUlf Hansson .name = "vpublk-g1",
841*e2ad626fSUlf Hansson .clk_names = (const char *[]){ "g1", },
842*e2ad626fSUlf Hansson .num_clks = 1,
843*e2ad626fSUlf Hansson .gpc_name = "g1",
844*e2ad626fSUlf Hansson .rst_mask = BIT(1),
845*e2ad626fSUlf Hansson .clk_mask = BIT(1),
846*e2ad626fSUlf Hansson },
847*e2ad626fSUlf Hansson [IMX8MQ_VPUBLK_PD_G2] = {
848*e2ad626fSUlf Hansson .name = "vpublk-g2",
849*e2ad626fSUlf Hansson .clk_names = (const char *[]){ "g2", },
850*e2ad626fSUlf Hansson .num_clks = 1,
851*e2ad626fSUlf Hansson .gpc_name = "g2",
852*e2ad626fSUlf Hansson .rst_mask = BIT(0),
853*e2ad626fSUlf Hansson .clk_mask = BIT(0),
854*e2ad626fSUlf Hansson },
855*e2ad626fSUlf Hansson };
856*e2ad626fSUlf Hansson
857*e2ad626fSUlf Hansson static const struct imx8m_blk_ctrl_data imx8mq_vpu_blk_ctl_dev_data = {
858*e2ad626fSUlf Hansson .max_reg = 0x14,
859*e2ad626fSUlf Hansson .power_notifier_fn = imx8mq_vpu_power_notifier,
860*e2ad626fSUlf Hansson .domains = imx8mq_vpu_blk_ctl_domain_data,
861*e2ad626fSUlf Hansson .num_domains = ARRAY_SIZE(imx8mq_vpu_blk_ctl_domain_data),
862*e2ad626fSUlf Hansson };
863*e2ad626fSUlf Hansson
864*e2ad626fSUlf Hansson static const struct of_device_id imx8m_blk_ctrl_of_match[] = {
865*e2ad626fSUlf Hansson {
866*e2ad626fSUlf Hansson .compatible = "fsl,imx8mm-vpu-blk-ctrl",
867*e2ad626fSUlf Hansson .data = &imx8mm_vpu_blk_ctl_dev_data
868*e2ad626fSUlf Hansson }, {
869*e2ad626fSUlf Hansson .compatible = "fsl,imx8mm-disp-blk-ctrl",
870*e2ad626fSUlf Hansson .data = &imx8mm_disp_blk_ctl_dev_data
871*e2ad626fSUlf Hansson }, {
872*e2ad626fSUlf Hansson .compatible = "fsl,imx8mn-disp-blk-ctrl",
873*e2ad626fSUlf Hansson .data = &imx8mn_disp_blk_ctl_dev_data
874*e2ad626fSUlf Hansson }, {
875*e2ad626fSUlf Hansson .compatible = "fsl,imx8mp-media-blk-ctrl",
876*e2ad626fSUlf Hansson .data = &imx8mp_media_blk_ctl_dev_data
877*e2ad626fSUlf Hansson }, {
878*e2ad626fSUlf Hansson .compatible = "fsl,imx8mq-vpu-blk-ctrl",
879*e2ad626fSUlf Hansson .data = &imx8mq_vpu_blk_ctl_dev_data
880*e2ad626fSUlf Hansson }, {
881*e2ad626fSUlf Hansson .compatible = "fsl,imx8mp-vpu-blk-ctrl",
882*e2ad626fSUlf Hansson .data = &imx8mp_vpu_blk_ctl_dev_data
883*e2ad626fSUlf Hansson }, {
884*e2ad626fSUlf Hansson /* Sentinel */
885*e2ad626fSUlf Hansson }
886*e2ad626fSUlf Hansson };
887*e2ad626fSUlf Hansson MODULE_DEVICE_TABLE(of, imx8m_blk_ctrl_of_match);
888*e2ad626fSUlf Hansson
889*e2ad626fSUlf Hansson static struct platform_driver imx8m_blk_ctrl_driver = {
890*e2ad626fSUlf Hansson .probe = imx8m_blk_ctrl_probe,
891*e2ad626fSUlf Hansson .remove = imx8m_blk_ctrl_remove,
892*e2ad626fSUlf Hansson .driver = {
893*e2ad626fSUlf Hansson .name = "imx8m-blk-ctrl",
894*e2ad626fSUlf Hansson .pm = &imx8m_blk_ctrl_pm_ops,
895*e2ad626fSUlf Hansson .of_match_table = imx8m_blk_ctrl_of_match,
896*e2ad626fSUlf Hansson },
897*e2ad626fSUlf Hansson };
898*e2ad626fSUlf Hansson module_platform_driver(imx8m_blk_ctrl_driver);
899*e2ad626fSUlf Hansson MODULE_LICENSE("GPL");
900