xref: /openbmc/linux/drivers/pmdomain/imx/imx8m-blk-ctrl.c (revision 5804c19b80bf625c6a9925317f845e497434d6d3)
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, &regmap_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