xref: /openbmc/linux/drivers/pmdomain/imx/imx8mp-blk-ctrl.c (revision e2ad626f8f409899baf1bf192d0533a851128b19)
1*e2ad626fSUlf Hansson // SPDX-License-Identifier: GPL-2.0+
2*e2ad626fSUlf Hansson 
3*e2ad626fSUlf Hansson /*
4*e2ad626fSUlf Hansson  * Copyright 2022 Pengutronix, Lucas Stach <kernel@pengutronix.de>
5*e2ad626fSUlf Hansson  */
6*e2ad626fSUlf Hansson 
7*e2ad626fSUlf Hansson #include <linux/bitfield.h>
8*e2ad626fSUlf Hansson #include <linux/clk.h>
9*e2ad626fSUlf Hansson #include <linux/clk-provider.h>
10*e2ad626fSUlf Hansson #include <linux/device.h>
11*e2ad626fSUlf Hansson #include <linux/interconnect.h>
12*e2ad626fSUlf Hansson #include <linux/module.h>
13*e2ad626fSUlf Hansson #include <linux/of.h>
14*e2ad626fSUlf Hansson #include <linux/platform_device.h>
15*e2ad626fSUlf Hansson #include <linux/pm_domain.h>
16*e2ad626fSUlf Hansson #include <linux/pm_runtime.h>
17*e2ad626fSUlf Hansson #include <linux/regmap.h>
18*e2ad626fSUlf Hansson 
19*e2ad626fSUlf Hansson #include <dt-bindings/power/imx8mp-power.h>
20*e2ad626fSUlf Hansson 
21*e2ad626fSUlf Hansson #define GPR_REG0		0x0
22*e2ad626fSUlf Hansson #define  PCIE_CLOCK_MODULE_EN	BIT(0)
23*e2ad626fSUlf Hansson #define  USB_CLOCK_MODULE_EN	BIT(1)
24*e2ad626fSUlf Hansson #define  PCIE_PHY_APB_RST	BIT(4)
25*e2ad626fSUlf Hansson #define  PCIE_PHY_INIT_RST	BIT(5)
26*e2ad626fSUlf Hansson #define GPR_REG1		0x4
27*e2ad626fSUlf Hansson #define  PLL_LOCK		BIT(13)
28*e2ad626fSUlf Hansson #define GPR_REG2		0x8
29*e2ad626fSUlf Hansson #define  P_PLL_MASK		GENMASK(5, 0)
30*e2ad626fSUlf Hansson #define  M_PLL_MASK		GENMASK(15, 6)
31*e2ad626fSUlf Hansson #define  S_PLL_MASK		GENMASK(18, 16)
32*e2ad626fSUlf Hansson #define GPR_REG3		0xc
33*e2ad626fSUlf Hansson #define  PLL_CKE		BIT(17)
34*e2ad626fSUlf Hansson #define  PLL_RST		BIT(31)
35*e2ad626fSUlf Hansson 
36*e2ad626fSUlf Hansson struct imx8mp_blk_ctrl_domain;
37*e2ad626fSUlf Hansson 
38*e2ad626fSUlf Hansson struct imx8mp_blk_ctrl {
39*e2ad626fSUlf Hansson 	struct device *dev;
40*e2ad626fSUlf Hansson 	struct notifier_block power_nb;
41*e2ad626fSUlf Hansson 	struct device *bus_power_dev;
42*e2ad626fSUlf Hansson 	struct regmap *regmap;
43*e2ad626fSUlf Hansson 	struct imx8mp_blk_ctrl_domain *domains;
44*e2ad626fSUlf Hansson 	struct genpd_onecell_data onecell_data;
45*e2ad626fSUlf Hansson 	void (*power_off) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain);
46*e2ad626fSUlf Hansson 	void (*power_on) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain);
47*e2ad626fSUlf Hansson };
48*e2ad626fSUlf Hansson 
49*e2ad626fSUlf Hansson struct imx8mp_blk_ctrl_domain_data {
50*e2ad626fSUlf Hansson 	const char *name;
51*e2ad626fSUlf Hansson 	const char * const *clk_names;
52*e2ad626fSUlf Hansson 	int num_clks;
53*e2ad626fSUlf Hansson 	const char * const *path_names;
54*e2ad626fSUlf Hansson 	int num_paths;
55*e2ad626fSUlf Hansson 	const char *gpc_name;
56*e2ad626fSUlf Hansson };
57*e2ad626fSUlf Hansson 
58*e2ad626fSUlf Hansson #define DOMAIN_MAX_CLKS 2
59*e2ad626fSUlf Hansson #define DOMAIN_MAX_PATHS 3
60*e2ad626fSUlf Hansson 
61*e2ad626fSUlf Hansson struct imx8mp_blk_ctrl_domain {
62*e2ad626fSUlf Hansson 	struct generic_pm_domain genpd;
63*e2ad626fSUlf Hansson 	const struct imx8mp_blk_ctrl_domain_data *data;
64*e2ad626fSUlf Hansson 	struct clk_bulk_data clks[DOMAIN_MAX_CLKS];
65*e2ad626fSUlf Hansson 	struct icc_bulk_data paths[DOMAIN_MAX_PATHS];
66*e2ad626fSUlf Hansson 	struct device *power_dev;
67*e2ad626fSUlf Hansson 	struct imx8mp_blk_ctrl *bc;
68*e2ad626fSUlf Hansson 	int num_paths;
69*e2ad626fSUlf Hansson 	int id;
70*e2ad626fSUlf Hansson };
71*e2ad626fSUlf Hansson 
72*e2ad626fSUlf Hansson struct imx8mp_blk_ctrl_data {
73*e2ad626fSUlf Hansson 	int max_reg;
74*e2ad626fSUlf Hansson 	int (*probe) (struct imx8mp_blk_ctrl *bc);
75*e2ad626fSUlf Hansson 	notifier_fn_t power_notifier_fn;
76*e2ad626fSUlf Hansson 	void (*power_off) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain);
77*e2ad626fSUlf Hansson 	void (*power_on) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain);
78*e2ad626fSUlf Hansson 	const struct imx8mp_blk_ctrl_domain_data *domains;
79*e2ad626fSUlf Hansson 	int num_domains;
80*e2ad626fSUlf Hansson };
81*e2ad626fSUlf Hansson 
82*e2ad626fSUlf Hansson static inline struct imx8mp_blk_ctrl_domain *
83*e2ad626fSUlf Hansson to_imx8mp_blk_ctrl_domain(struct generic_pm_domain *genpd)
84*e2ad626fSUlf Hansson {
85*e2ad626fSUlf Hansson 	return container_of(genpd, struct imx8mp_blk_ctrl_domain, genpd);
86*e2ad626fSUlf Hansson }
87*e2ad626fSUlf Hansson 
88*e2ad626fSUlf Hansson struct clk_hsio_pll {
89*e2ad626fSUlf Hansson 	struct clk_hw	hw;
90*e2ad626fSUlf Hansson 	struct regmap *regmap;
91*e2ad626fSUlf Hansson };
92*e2ad626fSUlf Hansson 
93*e2ad626fSUlf Hansson static inline struct clk_hsio_pll *to_clk_hsio_pll(struct clk_hw *hw)
94*e2ad626fSUlf Hansson {
95*e2ad626fSUlf Hansson 	return container_of(hw, struct clk_hsio_pll, hw);
96*e2ad626fSUlf Hansson }
97*e2ad626fSUlf Hansson 
98*e2ad626fSUlf Hansson static int clk_hsio_pll_prepare(struct clk_hw *hw)
99*e2ad626fSUlf Hansson {
100*e2ad626fSUlf Hansson 	struct clk_hsio_pll *clk = to_clk_hsio_pll(hw);
101*e2ad626fSUlf Hansson 	u32 val;
102*e2ad626fSUlf Hansson 
103*e2ad626fSUlf Hansson 	/* set the PLL configuration */
104*e2ad626fSUlf Hansson 	regmap_update_bits(clk->regmap, GPR_REG2,
105*e2ad626fSUlf Hansson 			   P_PLL_MASK | M_PLL_MASK | S_PLL_MASK,
106*e2ad626fSUlf Hansson 			   FIELD_PREP(P_PLL_MASK, 12) |
107*e2ad626fSUlf Hansson 			   FIELD_PREP(M_PLL_MASK, 800) |
108*e2ad626fSUlf Hansson 			   FIELD_PREP(S_PLL_MASK, 4));
109*e2ad626fSUlf Hansson 
110*e2ad626fSUlf Hansson 	/* de-assert PLL reset */
111*e2ad626fSUlf Hansson 	regmap_update_bits(clk->regmap, GPR_REG3, PLL_RST, PLL_RST);
112*e2ad626fSUlf Hansson 
113*e2ad626fSUlf Hansson 	/* enable PLL */
114*e2ad626fSUlf Hansson 	regmap_update_bits(clk->regmap, GPR_REG3, PLL_CKE, PLL_CKE);
115*e2ad626fSUlf Hansson 
116*e2ad626fSUlf Hansson 	return regmap_read_poll_timeout(clk->regmap, GPR_REG1, val,
117*e2ad626fSUlf Hansson 					val & PLL_LOCK, 10, 100);
118*e2ad626fSUlf Hansson }
119*e2ad626fSUlf Hansson 
120*e2ad626fSUlf Hansson static void clk_hsio_pll_unprepare(struct clk_hw *hw)
121*e2ad626fSUlf Hansson {
122*e2ad626fSUlf Hansson 	struct clk_hsio_pll *clk = to_clk_hsio_pll(hw);
123*e2ad626fSUlf Hansson 
124*e2ad626fSUlf Hansson 	regmap_update_bits(clk->regmap, GPR_REG3, PLL_RST | PLL_CKE, 0);
125*e2ad626fSUlf Hansson }
126*e2ad626fSUlf Hansson 
127*e2ad626fSUlf Hansson static int clk_hsio_pll_is_prepared(struct clk_hw *hw)
128*e2ad626fSUlf Hansson {
129*e2ad626fSUlf Hansson 	struct clk_hsio_pll *clk = to_clk_hsio_pll(hw);
130*e2ad626fSUlf Hansson 
131*e2ad626fSUlf Hansson 	return regmap_test_bits(clk->regmap, GPR_REG1, PLL_LOCK);
132*e2ad626fSUlf Hansson }
133*e2ad626fSUlf Hansson 
134*e2ad626fSUlf Hansson static unsigned long clk_hsio_pll_recalc_rate(struct clk_hw *hw,
135*e2ad626fSUlf Hansson 					      unsigned long parent_rate)
136*e2ad626fSUlf Hansson {
137*e2ad626fSUlf Hansson 	return 100000000;
138*e2ad626fSUlf Hansson }
139*e2ad626fSUlf Hansson 
140*e2ad626fSUlf Hansson static const struct clk_ops clk_hsio_pll_ops = {
141*e2ad626fSUlf Hansson 	.prepare = clk_hsio_pll_prepare,
142*e2ad626fSUlf Hansson 	.unprepare = clk_hsio_pll_unprepare,
143*e2ad626fSUlf Hansson 	.is_prepared = clk_hsio_pll_is_prepared,
144*e2ad626fSUlf Hansson 	.recalc_rate = clk_hsio_pll_recalc_rate,
145*e2ad626fSUlf Hansson };
146*e2ad626fSUlf Hansson 
147*e2ad626fSUlf Hansson static int imx8mp_hsio_blk_ctrl_probe(struct imx8mp_blk_ctrl *bc)
148*e2ad626fSUlf Hansson {
149*e2ad626fSUlf Hansson 	struct clk_hsio_pll *clk_hsio_pll;
150*e2ad626fSUlf Hansson 	struct clk_hw *hw;
151*e2ad626fSUlf Hansson 	struct clk_init_data init = {};
152*e2ad626fSUlf Hansson 	int ret;
153*e2ad626fSUlf Hansson 
154*e2ad626fSUlf Hansson 	clk_hsio_pll = devm_kzalloc(bc->dev, sizeof(*clk_hsio_pll), GFP_KERNEL);
155*e2ad626fSUlf Hansson 	if (!clk_hsio_pll)
156*e2ad626fSUlf Hansson 		return -ENOMEM;
157*e2ad626fSUlf Hansson 
158*e2ad626fSUlf Hansson 	init.name = "hsio_pll";
159*e2ad626fSUlf Hansson 	init.ops = &clk_hsio_pll_ops;
160*e2ad626fSUlf Hansson 	init.parent_names = (const char *[]){"osc_24m"};
161*e2ad626fSUlf Hansson 	init.num_parents = 1;
162*e2ad626fSUlf Hansson 
163*e2ad626fSUlf Hansson 	clk_hsio_pll->regmap = bc->regmap;
164*e2ad626fSUlf Hansson 	clk_hsio_pll->hw.init = &init;
165*e2ad626fSUlf Hansson 
166*e2ad626fSUlf Hansson 	hw = &clk_hsio_pll->hw;
167*e2ad626fSUlf Hansson 	ret = devm_clk_hw_register(bc->bus_power_dev, hw);
168*e2ad626fSUlf Hansson 	if (ret)
169*e2ad626fSUlf Hansson 		return ret;
170*e2ad626fSUlf Hansson 
171*e2ad626fSUlf Hansson 	return devm_of_clk_add_hw_provider(bc->dev, of_clk_hw_simple_get, hw);
172*e2ad626fSUlf Hansson }
173*e2ad626fSUlf Hansson 
174*e2ad626fSUlf Hansson static void imx8mp_hsio_blk_ctrl_power_on(struct imx8mp_blk_ctrl *bc,
175*e2ad626fSUlf Hansson 					  struct imx8mp_blk_ctrl_domain *domain)
176*e2ad626fSUlf Hansson {
177*e2ad626fSUlf Hansson 	switch (domain->id) {
178*e2ad626fSUlf Hansson 	case IMX8MP_HSIOBLK_PD_USB:
179*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN);
180*e2ad626fSUlf Hansson 		break;
181*e2ad626fSUlf Hansson 	case IMX8MP_HSIOBLK_PD_PCIE:
182*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, GPR_REG0, PCIE_CLOCK_MODULE_EN);
183*e2ad626fSUlf Hansson 		break;
184*e2ad626fSUlf Hansson 	case IMX8MP_HSIOBLK_PD_PCIE_PHY:
185*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, GPR_REG0,
186*e2ad626fSUlf Hansson 				PCIE_PHY_APB_RST | PCIE_PHY_INIT_RST);
187*e2ad626fSUlf Hansson 		break;
188*e2ad626fSUlf Hansson 	default:
189*e2ad626fSUlf Hansson 		break;
190*e2ad626fSUlf Hansson 	}
191*e2ad626fSUlf Hansson }
192*e2ad626fSUlf Hansson 
193*e2ad626fSUlf Hansson static void imx8mp_hsio_blk_ctrl_power_off(struct imx8mp_blk_ctrl *bc,
194*e2ad626fSUlf Hansson 					   struct imx8mp_blk_ctrl_domain *domain)
195*e2ad626fSUlf Hansson {
196*e2ad626fSUlf Hansson 	switch (domain->id) {
197*e2ad626fSUlf Hansson 	case IMX8MP_HSIOBLK_PD_USB:
198*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN);
199*e2ad626fSUlf Hansson 		break;
200*e2ad626fSUlf Hansson 	case IMX8MP_HSIOBLK_PD_PCIE:
201*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, GPR_REG0, PCIE_CLOCK_MODULE_EN);
202*e2ad626fSUlf Hansson 		break;
203*e2ad626fSUlf Hansson 	case IMX8MP_HSIOBLK_PD_PCIE_PHY:
204*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, GPR_REG0,
205*e2ad626fSUlf Hansson 				  PCIE_PHY_APB_RST | PCIE_PHY_INIT_RST);
206*e2ad626fSUlf Hansson 		break;
207*e2ad626fSUlf Hansson 	default:
208*e2ad626fSUlf Hansson 		break;
209*e2ad626fSUlf Hansson 	}
210*e2ad626fSUlf Hansson }
211*e2ad626fSUlf Hansson 
212*e2ad626fSUlf Hansson static int imx8mp_hsio_power_notifier(struct notifier_block *nb,
213*e2ad626fSUlf Hansson 				      unsigned long action, void *data)
214*e2ad626fSUlf Hansson {
215*e2ad626fSUlf Hansson 	struct imx8mp_blk_ctrl *bc = container_of(nb, struct imx8mp_blk_ctrl,
216*e2ad626fSUlf Hansson 						 power_nb);
217*e2ad626fSUlf Hansson 	struct clk_bulk_data *usb_clk = bc->domains[IMX8MP_HSIOBLK_PD_USB].clks;
218*e2ad626fSUlf Hansson 	int num_clks = bc->domains[IMX8MP_HSIOBLK_PD_USB].data->num_clks;
219*e2ad626fSUlf Hansson 	int ret;
220*e2ad626fSUlf Hansson 
221*e2ad626fSUlf Hansson 	switch (action) {
222*e2ad626fSUlf Hansson 	case GENPD_NOTIFY_ON:
223*e2ad626fSUlf Hansson 		/*
224*e2ad626fSUlf Hansson 		 * enable USB clock for a moment for the power-on ADB handshake
225*e2ad626fSUlf Hansson 		 * to proceed
226*e2ad626fSUlf Hansson 		 */
227*e2ad626fSUlf Hansson 		ret = clk_bulk_prepare_enable(num_clks, usb_clk);
228*e2ad626fSUlf Hansson 		if (ret)
229*e2ad626fSUlf Hansson 			return NOTIFY_BAD;
230*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN);
231*e2ad626fSUlf Hansson 
232*e2ad626fSUlf Hansson 		udelay(5);
233*e2ad626fSUlf Hansson 
234*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN);
235*e2ad626fSUlf Hansson 		clk_bulk_disable_unprepare(num_clks, usb_clk);
236*e2ad626fSUlf Hansson 		break;
237*e2ad626fSUlf Hansson 	case GENPD_NOTIFY_PRE_OFF:
238*e2ad626fSUlf Hansson 		/* enable USB clock for the power-down ADB handshake to work */
239*e2ad626fSUlf Hansson 		ret = clk_bulk_prepare_enable(num_clks, usb_clk);
240*e2ad626fSUlf Hansson 		if (ret)
241*e2ad626fSUlf Hansson 			return NOTIFY_BAD;
242*e2ad626fSUlf Hansson 
243*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN);
244*e2ad626fSUlf Hansson 		break;
245*e2ad626fSUlf Hansson 	case GENPD_NOTIFY_OFF:
246*e2ad626fSUlf Hansson 		clk_bulk_disable_unprepare(num_clks, usb_clk);
247*e2ad626fSUlf Hansson 		break;
248*e2ad626fSUlf Hansson 	default:
249*e2ad626fSUlf Hansson 		break;
250*e2ad626fSUlf Hansson 	}
251*e2ad626fSUlf Hansson 
252*e2ad626fSUlf Hansson 	return NOTIFY_OK;
253*e2ad626fSUlf Hansson }
254*e2ad626fSUlf Hansson 
255*e2ad626fSUlf Hansson static const struct imx8mp_blk_ctrl_domain_data imx8mp_hsio_domain_data[] = {
256*e2ad626fSUlf Hansson 	[IMX8MP_HSIOBLK_PD_USB] = {
257*e2ad626fSUlf Hansson 		.name = "hsioblk-usb",
258*e2ad626fSUlf Hansson 		.clk_names = (const char *[]){ "usb" },
259*e2ad626fSUlf Hansson 		.num_clks = 1,
260*e2ad626fSUlf Hansson 		.gpc_name = "usb",
261*e2ad626fSUlf Hansson 		.path_names = (const char *[]){"usb1", "usb2"},
262*e2ad626fSUlf Hansson 		.num_paths = 2,
263*e2ad626fSUlf Hansson 	},
264*e2ad626fSUlf Hansson 	[IMX8MP_HSIOBLK_PD_USB_PHY1] = {
265*e2ad626fSUlf Hansson 		.name = "hsioblk-usb-phy1",
266*e2ad626fSUlf Hansson 		.gpc_name = "usb-phy1",
267*e2ad626fSUlf Hansson 	},
268*e2ad626fSUlf Hansson 	[IMX8MP_HSIOBLK_PD_USB_PHY2] = {
269*e2ad626fSUlf Hansson 		.name = "hsioblk-usb-phy2",
270*e2ad626fSUlf Hansson 		.gpc_name = "usb-phy2",
271*e2ad626fSUlf Hansson 	},
272*e2ad626fSUlf Hansson 	[IMX8MP_HSIOBLK_PD_PCIE] = {
273*e2ad626fSUlf Hansson 		.name = "hsioblk-pcie",
274*e2ad626fSUlf Hansson 		.clk_names = (const char *[]){ "pcie" },
275*e2ad626fSUlf Hansson 		.num_clks = 1,
276*e2ad626fSUlf Hansson 		.gpc_name = "pcie",
277*e2ad626fSUlf Hansson 		.path_names = (const char *[]){"noc-pcie", "pcie"},
278*e2ad626fSUlf Hansson 		.num_paths = 2,
279*e2ad626fSUlf Hansson 	},
280*e2ad626fSUlf Hansson 	[IMX8MP_HSIOBLK_PD_PCIE_PHY] = {
281*e2ad626fSUlf Hansson 		.name = "hsioblk-pcie-phy",
282*e2ad626fSUlf Hansson 		.gpc_name = "pcie-phy",
283*e2ad626fSUlf Hansson 	},
284*e2ad626fSUlf Hansson };
285*e2ad626fSUlf Hansson 
286*e2ad626fSUlf Hansson static const struct imx8mp_blk_ctrl_data imx8mp_hsio_blk_ctl_dev_data = {
287*e2ad626fSUlf Hansson 	.max_reg = 0x24,
288*e2ad626fSUlf Hansson 	.probe = imx8mp_hsio_blk_ctrl_probe,
289*e2ad626fSUlf Hansson 	.power_on = imx8mp_hsio_blk_ctrl_power_on,
290*e2ad626fSUlf Hansson 	.power_off = imx8mp_hsio_blk_ctrl_power_off,
291*e2ad626fSUlf Hansson 	.power_notifier_fn = imx8mp_hsio_power_notifier,
292*e2ad626fSUlf Hansson 	.domains = imx8mp_hsio_domain_data,
293*e2ad626fSUlf Hansson 	.num_domains = ARRAY_SIZE(imx8mp_hsio_domain_data),
294*e2ad626fSUlf Hansson };
295*e2ad626fSUlf Hansson 
296*e2ad626fSUlf Hansson #define HDMI_RTX_RESET_CTL0	0x20
297*e2ad626fSUlf Hansson #define HDMI_RTX_CLK_CTL0	0x40
298*e2ad626fSUlf Hansson #define HDMI_RTX_CLK_CTL1	0x50
299*e2ad626fSUlf Hansson #define HDMI_RTX_CLK_CTL2	0x60
300*e2ad626fSUlf Hansson #define HDMI_RTX_CLK_CTL3	0x70
301*e2ad626fSUlf Hansson #define HDMI_RTX_CLK_CTL4	0x80
302*e2ad626fSUlf Hansson #define HDMI_TX_CONTROL0	0x200
303*e2ad626fSUlf Hansson #define  HDMI_LCDIF_NOC_HURRY_MASK		GENMASK(14, 12)
304*e2ad626fSUlf Hansson 
305*e2ad626fSUlf Hansson static void imx8mp_hdmi_blk_ctrl_power_on(struct imx8mp_blk_ctrl *bc,
306*e2ad626fSUlf Hansson 					  struct imx8mp_blk_ctrl_domain *domain)
307*e2ad626fSUlf Hansson {
308*e2ad626fSUlf Hansson 	switch (domain->id) {
309*e2ad626fSUlf Hansson 	case IMX8MP_HDMIBLK_PD_IRQSTEER:
310*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(9));
311*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(16));
312*e2ad626fSUlf Hansson 		break;
313*e2ad626fSUlf Hansson 	case IMX8MP_HDMIBLK_PD_LCDIF:
314*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0,
315*e2ad626fSUlf Hansson 				BIT(16) | BIT(17) | BIT(18) |
316*e2ad626fSUlf Hansson 				BIT(19) | BIT(20));
317*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(11));
318*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0,
319*e2ad626fSUlf Hansson 				BIT(4) | BIT(5) | BIT(6));
320*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, HDMI_TX_CONTROL0,
321*e2ad626fSUlf Hansson 				FIELD_PREP(HDMI_LCDIF_NOC_HURRY_MASK, 7));
322*e2ad626fSUlf Hansson 		break;
323*e2ad626fSUlf Hansson 	case IMX8MP_HDMIBLK_PD_PAI:
324*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(17));
325*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(18));
326*e2ad626fSUlf Hansson 		break;
327*e2ad626fSUlf Hansson 	case IMX8MP_HDMIBLK_PD_PVI:
328*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(28));
329*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(22));
330*e2ad626fSUlf Hansson 		break;
331*e2ad626fSUlf Hansson 	case IMX8MP_HDMIBLK_PD_TRNG:
332*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(27) | BIT(30));
333*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(20));
334*e2ad626fSUlf Hansson 		break;
335*e2ad626fSUlf Hansson 	case IMX8MP_HDMIBLK_PD_HDMI_TX:
336*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0,
337*e2ad626fSUlf Hansson 				BIT(2) | BIT(4) | BIT(5));
338*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1,
339*e2ad626fSUlf Hansson 				BIT(12) | BIT(13) | BIT(14) | BIT(15) | BIT(16) |
340*e2ad626fSUlf Hansson 				BIT(18) | BIT(19) | BIT(20) | BIT(21));
341*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0,
342*e2ad626fSUlf Hansson 				BIT(7) | BIT(10) | BIT(11));
343*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(1));
344*e2ad626fSUlf Hansson 		break;
345*e2ad626fSUlf Hansson 	case IMX8MP_HDMIBLK_PD_HDMI_TX_PHY:
346*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(7));
347*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(22) | BIT(24));
348*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(12));
349*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(3));
350*e2ad626fSUlf Hansson 		break;
351*e2ad626fSUlf Hansson 	case IMX8MP_HDMIBLK_PD_HDCP:
352*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(11));
353*e2ad626fSUlf Hansson 		break;
354*e2ad626fSUlf Hansson 	case IMX8MP_HDMIBLK_PD_HRV:
355*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(3) | BIT(4) | BIT(5));
356*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(15));
357*e2ad626fSUlf Hansson 		break;
358*e2ad626fSUlf Hansson 	default:
359*e2ad626fSUlf Hansson 		break;
360*e2ad626fSUlf Hansson 	}
361*e2ad626fSUlf Hansson }
362*e2ad626fSUlf Hansson 
363*e2ad626fSUlf Hansson static void imx8mp_hdmi_blk_ctrl_power_off(struct imx8mp_blk_ctrl *bc,
364*e2ad626fSUlf Hansson 					   struct imx8mp_blk_ctrl_domain *domain)
365*e2ad626fSUlf Hansson {
366*e2ad626fSUlf Hansson 	switch (domain->id) {
367*e2ad626fSUlf Hansson 	case IMX8MP_HDMIBLK_PD_IRQSTEER:
368*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(9));
369*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(16));
370*e2ad626fSUlf Hansson 		break;
371*e2ad626fSUlf Hansson 	case IMX8MP_HDMIBLK_PD_LCDIF:
372*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0,
373*e2ad626fSUlf Hansson 				  BIT(4) | BIT(5) | BIT(6));
374*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(11));
375*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0,
376*e2ad626fSUlf Hansson 				  BIT(16) | BIT(17) | BIT(18) |
377*e2ad626fSUlf Hansson 				  BIT(19) | BIT(20));
378*e2ad626fSUlf Hansson 		break;
379*e2ad626fSUlf Hansson 	case IMX8MP_HDMIBLK_PD_PAI:
380*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(18));
381*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(17));
382*e2ad626fSUlf Hansson 		break;
383*e2ad626fSUlf Hansson 	case IMX8MP_HDMIBLK_PD_PVI:
384*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(22));
385*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(28));
386*e2ad626fSUlf Hansson 		break;
387*e2ad626fSUlf Hansson 	case IMX8MP_HDMIBLK_PD_TRNG:
388*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(20));
389*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(27) | BIT(30));
390*e2ad626fSUlf Hansson 		break;
391*e2ad626fSUlf Hansson 	case IMX8MP_HDMIBLK_PD_HDMI_TX:
392*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(1));
393*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0,
394*e2ad626fSUlf Hansson 				  BIT(7) | BIT(10) | BIT(11));
395*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1,
396*e2ad626fSUlf Hansson 				  BIT(12) | BIT(13) | BIT(14) | BIT(15) | BIT(16) |
397*e2ad626fSUlf Hansson 				  BIT(18) | BIT(19) | BIT(20) | BIT(21));
398*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0,
399*e2ad626fSUlf Hansson 				  BIT(2) | BIT(4) | BIT(5));
400*e2ad626fSUlf Hansson 		break;
401*e2ad626fSUlf Hansson 	case IMX8MP_HDMIBLK_PD_HDMI_TX_PHY:
402*e2ad626fSUlf Hansson 		regmap_set_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(3));
403*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(12));
404*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(7));
405*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(22) | BIT(24));
406*e2ad626fSUlf Hansson 		break;
407*e2ad626fSUlf Hansson 	case IMX8MP_HDMIBLK_PD_HDCP:
408*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(11));
409*e2ad626fSUlf Hansson 		break;
410*e2ad626fSUlf Hansson 	case IMX8MP_HDMIBLK_PD_HRV:
411*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(15));
412*e2ad626fSUlf Hansson 		regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(3) | BIT(4) | BIT(5));
413*e2ad626fSUlf Hansson 		break;
414*e2ad626fSUlf Hansson 	default:
415*e2ad626fSUlf Hansson 		break;
416*e2ad626fSUlf Hansson 	}
417*e2ad626fSUlf Hansson }
418*e2ad626fSUlf Hansson 
419*e2ad626fSUlf Hansson static int imx8mp_hdmi_power_notifier(struct notifier_block *nb,
420*e2ad626fSUlf Hansson 				      unsigned long action, void *data)
421*e2ad626fSUlf Hansson {
422*e2ad626fSUlf Hansson 	struct imx8mp_blk_ctrl *bc = container_of(nb, struct imx8mp_blk_ctrl,
423*e2ad626fSUlf Hansson 						 power_nb);
424*e2ad626fSUlf Hansson 
425*e2ad626fSUlf Hansson 	if (action != GENPD_NOTIFY_ON)
426*e2ad626fSUlf Hansson 		return NOTIFY_OK;
427*e2ad626fSUlf Hansson 
428*e2ad626fSUlf Hansson 	/*
429*e2ad626fSUlf Hansson 	 * Contrary to other blk-ctrls the reset and clock don't clear when the
430*e2ad626fSUlf Hansson 	 * power domain is powered down. To ensure the proper reset pulsing,
431*e2ad626fSUlf Hansson 	 * first clear them all to asserted state, then enable the bus clocks
432*e2ad626fSUlf Hansson 	 * and then release the ADB reset.
433*e2ad626fSUlf Hansson 	 */
434*e2ad626fSUlf Hansson 	regmap_write(bc->regmap, HDMI_RTX_RESET_CTL0, 0x0);
435*e2ad626fSUlf Hansson 	regmap_write(bc->regmap, HDMI_RTX_CLK_CTL0, 0x0);
436*e2ad626fSUlf Hansson 	regmap_write(bc->regmap, HDMI_RTX_CLK_CTL1, 0x0);
437*e2ad626fSUlf Hansson 	regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0,
438*e2ad626fSUlf Hansson 			BIT(0) | BIT(1) | BIT(10));
439*e2ad626fSUlf Hansson 	regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(0));
440*e2ad626fSUlf Hansson 
441*e2ad626fSUlf Hansson 	/*
442*e2ad626fSUlf Hansson 	 * On power up we have no software backchannel to the GPC to
443*e2ad626fSUlf Hansson 	 * wait for the ADB handshake to happen, so we just delay for a
444*e2ad626fSUlf Hansson 	 * bit. On power down the GPC driver waits for the handshake.
445*e2ad626fSUlf Hansson 	 */
446*e2ad626fSUlf Hansson 	udelay(5);
447*e2ad626fSUlf Hansson 
448*e2ad626fSUlf Hansson 	return NOTIFY_OK;
449*e2ad626fSUlf Hansson }
450*e2ad626fSUlf Hansson 
451*e2ad626fSUlf Hansson static const struct imx8mp_blk_ctrl_domain_data imx8mp_hdmi_domain_data[] = {
452*e2ad626fSUlf Hansson 	[IMX8MP_HDMIBLK_PD_IRQSTEER] = {
453*e2ad626fSUlf Hansson 		.name = "hdmiblk-irqsteer",
454*e2ad626fSUlf Hansson 		.clk_names = (const char *[]){ "apb" },
455*e2ad626fSUlf Hansson 		.num_clks = 1,
456*e2ad626fSUlf Hansson 		.gpc_name = "irqsteer",
457*e2ad626fSUlf Hansson 	},
458*e2ad626fSUlf Hansson 	[IMX8MP_HDMIBLK_PD_LCDIF] = {
459*e2ad626fSUlf Hansson 		.name = "hdmiblk-lcdif",
460*e2ad626fSUlf Hansson 		.clk_names = (const char *[]){ "axi", "apb" },
461*e2ad626fSUlf Hansson 		.num_clks = 2,
462*e2ad626fSUlf Hansson 		.gpc_name = "lcdif",
463*e2ad626fSUlf Hansson 		.path_names = (const char *[]){"lcdif-hdmi"},
464*e2ad626fSUlf Hansson 		.num_paths = 1,
465*e2ad626fSUlf Hansson 	},
466*e2ad626fSUlf Hansson 	[IMX8MP_HDMIBLK_PD_PAI] = {
467*e2ad626fSUlf Hansson 		.name = "hdmiblk-pai",
468*e2ad626fSUlf Hansson 		.clk_names = (const char *[]){ "apb" },
469*e2ad626fSUlf Hansson 		.num_clks = 1,
470*e2ad626fSUlf Hansson 		.gpc_name = "pai",
471*e2ad626fSUlf Hansson 	},
472*e2ad626fSUlf Hansson 	[IMX8MP_HDMIBLK_PD_PVI] = {
473*e2ad626fSUlf Hansson 		.name = "hdmiblk-pvi",
474*e2ad626fSUlf Hansson 		.clk_names = (const char *[]){ "apb" },
475*e2ad626fSUlf Hansson 		.num_clks = 1,
476*e2ad626fSUlf Hansson 		.gpc_name = "pvi",
477*e2ad626fSUlf Hansson 	},
478*e2ad626fSUlf Hansson 	[IMX8MP_HDMIBLK_PD_TRNG] = {
479*e2ad626fSUlf Hansson 		.name = "hdmiblk-trng",
480*e2ad626fSUlf Hansson 		.clk_names = (const char *[]){ "apb" },
481*e2ad626fSUlf Hansson 		.num_clks = 1,
482*e2ad626fSUlf Hansson 		.gpc_name = "trng",
483*e2ad626fSUlf Hansson 	},
484*e2ad626fSUlf Hansson 	[IMX8MP_HDMIBLK_PD_HDMI_TX] = {
485*e2ad626fSUlf Hansson 		.name = "hdmiblk-hdmi-tx",
486*e2ad626fSUlf Hansson 		.clk_names = (const char *[]){ "apb", "ref_266m" },
487*e2ad626fSUlf Hansson 		.num_clks = 2,
488*e2ad626fSUlf Hansson 		.gpc_name = "hdmi-tx",
489*e2ad626fSUlf Hansson 	},
490*e2ad626fSUlf Hansson 	[IMX8MP_HDMIBLK_PD_HDMI_TX_PHY] = {
491*e2ad626fSUlf Hansson 		.name = "hdmiblk-hdmi-tx-phy",
492*e2ad626fSUlf Hansson 		.clk_names = (const char *[]){ "apb", "ref_24m" },
493*e2ad626fSUlf Hansson 		.num_clks = 2,
494*e2ad626fSUlf Hansson 		.gpc_name = "hdmi-tx-phy",
495*e2ad626fSUlf Hansson 	},
496*e2ad626fSUlf Hansson 	[IMX8MP_HDMIBLK_PD_HRV] = {
497*e2ad626fSUlf Hansson 		.name = "hdmiblk-hrv",
498*e2ad626fSUlf Hansson 		.clk_names = (const char *[]){ "axi", "apb" },
499*e2ad626fSUlf Hansson 		.num_clks = 2,
500*e2ad626fSUlf Hansson 		.gpc_name = "hrv",
501*e2ad626fSUlf Hansson 		.path_names = (const char *[]){"hrv"},
502*e2ad626fSUlf Hansson 		.num_paths = 1,
503*e2ad626fSUlf Hansson 	},
504*e2ad626fSUlf Hansson 	[IMX8MP_HDMIBLK_PD_HDCP] = {
505*e2ad626fSUlf Hansson 		.name = "hdmiblk-hdcp",
506*e2ad626fSUlf Hansson 		.clk_names = (const char *[]){ "axi", "apb" },
507*e2ad626fSUlf Hansson 		.num_clks = 2,
508*e2ad626fSUlf Hansson 		.gpc_name = "hdcp",
509*e2ad626fSUlf Hansson 		.path_names = (const char *[]){"hdcp"},
510*e2ad626fSUlf Hansson 		.num_paths = 1,
511*e2ad626fSUlf Hansson 	},
512*e2ad626fSUlf Hansson };
513*e2ad626fSUlf Hansson 
514*e2ad626fSUlf Hansson static const struct imx8mp_blk_ctrl_data imx8mp_hdmi_blk_ctl_dev_data = {
515*e2ad626fSUlf Hansson 	.max_reg = 0x23c,
516*e2ad626fSUlf Hansson 	.power_on = imx8mp_hdmi_blk_ctrl_power_on,
517*e2ad626fSUlf Hansson 	.power_off = imx8mp_hdmi_blk_ctrl_power_off,
518*e2ad626fSUlf Hansson 	.power_notifier_fn = imx8mp_hdmi_power_notifier,
519*e2ad626fSUlf Hansson 	.domains = imx8mp_hdmi_domain_data,
520*e2ad626fSUlf Hansson 	.num_domains = ARRAY_SIZE(imx8mp_hdmi_domain_data),
521*e2ad626fSUlf Hansson };
522*e2ad626fSUlf Hansson 
523*e2ad626fSUlf Hansson static int imx8mp_blk_ctrl_power_on(struct generic_pm_domain *genpd)
524*e2ad626fSUlf Hansson {
525*e2ad626fSUlf Hansson 	struct imx8mp_blk_ctrl_domain *domain = to_imx8mp_blk_ctrl_domain(genpd);
526*e2ad626fSUlf Hansson 	const struct imx8mp_blk_ctrl_domain_data *data = domain->data;
527*e2ad626fSUlf Hansson 	struct imx8mp_blk_ctrl *bc = domain->bc;
528*e2ad626fSUlf Hansson 	int ret;
529*e2ad626fSUlf Hansson 
530*e2ad626fSUlf Hansson 	/* make sure bus domain is awake */
531*e2ad626fSUlf Hansson 	ret = pm_runtime_resume_and_get(bc->bus_power_dev);
532*e2ad626fSUlf Hansson 	if (ret < 0) {
533*e2ad626fSUlf Hansson 		dev_err(bc->dev, "failed to power up bus domain\n");
534*e2ad626fSUlf Hansson 		return ret;
535*e2ad626fSUlf Hansson 	}
536*e2ad626fSUlf Hansson 
537*e2ad626fSUlf Hansson 	/* enable upstream clocks */
538*e2ad626fSUlf Hansson 	ret = clk_bulk_prepare_enable(data->num_clks, domain->clks);
539*e2ad626fSUlf Hansson 	if (ret) {
540*e2ad626fSUlf Hansson 		dev_err(bc->dev, "failed to enable clocks\n");
541*e2ad626fSUlf Hansson 		goto bus_put;
542*e2ad626fSUlf Hansson 	}
543*e2ad626fSUlf Hansson 
544*e2ad626fSUlf Hansson 	/* domain specific blk-ctrl manipulation */
545*e2ad626fSUlf Hansson 	bc->power_on(bc, domain);
546*e2ad626fSUlf Hansson 
547*e2ad626fSUlf Hansson 	/* power up upstream GPC domain */
548*e2ad626fSUlf Hansson 	ret = pm_runtime_resume_and_get(domain->power_dev);
549*e2ad626fSUlf Hansson 	if (ret < 0) {
550*e2ad626fSUlf Hansson 		dev_err(bc->dev, "failed to power up peripheral domain\n");
551*e2ad626fSUlf Hansson 		goto clk_disable;
552*e2ad626fSUlf Hansson 	}
553*e2ad626fSUlf Hansson 
554*e2ad626fSUlf Hansson 	ret = icc_bulk_set_bw(domain->num_paths, domain->paths);
555*e2ad626fSUlf Hansson 	if (ret)
556*e2ad626fSUlf Hansson 		dev_err(bc->dev, "failed to set icc bw\n");
557*e2ad626fSUlf Hansson 
558*e2ad626fSUlf Hansson 	clk_bulk_disable_unprepare(data->num_clks, domain->clks);
559*e2ad626fSUlf Hansson 
560*e2ad626fSUlf Hansson 	return 0;
561*e2ad626fSUlf Hansson 
562*e2ad626fSUlf Hansson clk_disable:
563*e2ad626fSUlf Hansson 	clk_bulk_disable_unprepare(data->num_clks, domain->clks);
564*e2ad626fSUlf Hansson bus_put:
565*e2ad626fSUlf Hansson 	pm_runtime_put(bc->bus_power_dev);
566*e2ad626fSUlf Hansson 
567*e2ad626fSUlf Hansson 	return ret;
568*e2ad626fSUlf Hansson }
569*e2ad626fSUlf Hansson 
570*e2ad626fSUlf Hansson static int imx8mp_blk_ctrl_power_off(struct generic_pm_domain *genpd)
571*e2ad626fSUlf Hansson {
572*e2ad626fSUlf Hansson 	struct imx8mp_blk_ctrl_domain *domain = to_imx8mp_blk_ctrl_domain(genpd);
573*e2ad626fSUlf Hansson 	const struct imx8mp_blk_ctrl_domain_data *data = domain->data;
574*e2ad626fSUlf Hansson 	struct imx8mp_blk_ctrl *bc = domain->bc;
575*e2ad626fSUlf Hansson 	int ret;
576*e2ad626fSUlf Hansson 
577*e2ad626fSUlf Hansson 	ret = clk_bulk_prepare_enable(data->num_clks, domain->clks);
578*e2ad626fSUlf Hansson 	if (ret) {
579*e2ad626fSUlf Hansson 		dev_err(bc->dev, "failed to enable clocks\n");
580*e2ad626fSUlf Hansson 		return ret;
581*e2ad626fSUlf Hansson 	}
582*e2ad626fSUlf Hansson 
583*e2ad626fSUlf Hansson 	/* domain specific blk-ctrl manipulation */
584*e2ad626fSUlf Hansson 	bc->power_off(bc, domain);
585*e2ad626fSUlf Hansson 
586*e2ad626fSUlf Hansson 	clk_bulk_disable_unprepare(data->num_clks, domain->clks);
587*e2ad626fSUlf Hansson 
588*e2ad626fSUlf Hansson 	/* power down upstream GPC domain */
589*e2ad626fSUlf Hansson 	pm_runtime_put(domain->power_dev);
590*e2ad626fSUlf Hansson 
591*e2ad626fSUlf Hansson 	/* allow bus domain to suspend */
592*e2ad626fSUlf Hansson 	pm_runtime_put(bc->bus_power_dev);
593*e2ad626fSUlf Hansson 
594*e2ad626fSUlf Hansson 	return 0;
595*e2ad626fSUlf Hansson }
596*e2ad626fSUlf Hansson 
597*e2ad626fSUlf Hansson static struct lock_class_key blk_ctrl_genpd_lock_class;
598*e2ad626fSUlf Hansson 
599*e2ad626fSUlf Hansson static int imx8mp_blk_ctrl_probe(struct platform_device *pdev)
600*e2ad626fSUlf Hansson {
601*e2ad626fSUlf Hansson 	const struct imx8mp_blk_ctrl_data *bc_data;
602*e2ad626fSUlf Hansson 	struct device *dev = &pdev->dev;
603*e2ad626fSUlf Hansson 	struct imx8mp_blk_ctrl *bc;
604*e2ad626fSUlf Hansson 	void __iomem *base;
605*e2ad626fSUlf Hansson 	int num_domains, i, ret;
606*e2ad626fSUlf Hansson 
607*e2ad626fSUlf Hansson 	struct regmap_config regmap_config = {
608*e2ad626fSUlf Hansson 		.reg_bits	= 32,
609*e2ad626fSUlf Hansson 		.val_bits	= 32,
610*e2ad626fSUlf Hansson 		.reg_stride	= 4,
611*e2ad626fSUlf Hansson 	};
612*e2ad626fSUlf Hansson 
613*e2ad626fSUlf Hansson 	bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL);
614*e2ad626fSUlf Hansson 	if (!bc)
615*e2ad626fSUlf Hansson 		return -ENOMEM;
616*e2ad626fSUlf Hansson 
617*e2ad626fSUlf Hansson 	bc->dev = dev;
618*e2ad626fSUlf Hansson 
619*e2ad626fSUlf Hansson 	bc_data = of_device_get_match_data(dev);
620*e2ad626fSUlf Hansson 	num_domains = bc_data->num_domains;
621*e2ad626fSUlf Hansson 
622*e2ad626fSUlf Hansson 	base = devm_platform_ioremap_resource(pdev, 0);
623*e2ad626fSUlf Hansson 	if (IS_ERR(base))
624*e2ad626fSUlf Hansson 		return PTR_ERR(base);
625*e2ad626fSUlf Hansson 
626*e2ad626fSUlf Hansson 	regmap_config.max_register = bc_data->max_reg;
627*e2ad626fSUlf Hansson 	bc->regmap = devm_regmap_init_mmio(dev, base, &regmap_config);
628*e2ad626fSUlf Hansson 	if (IS_ERR(bc->regmap))
629*e2ad626fSUlf Hansson 		return dev_err_probe(dev, PTR_ERR(bc->regmap),
630*e2ad626fSUlf Hansson 				     "failed to init regmap\n");
631*e2ad626fSUlf Hansson 
632*e2ad626fSUlf Hansson 	bc->domains = devm_kcalloc(dev, num_domains,
633*e2ad626fSUlf Hansson 				   sizeof(struct imx8mp_blk_ctrl_domain),
634*e2ad626fSUlf Hansson 				   GFP_KERNEL);
635*e2ad626fSUlf Hansson 	if (!bc->domains)
636*e2ad626fSUlf Hansson 		return -ENOMEM;
637*e2ad626fSUlf Hansson 
638*e2ad626fSUlf Hansson 	bc->onecell_data.num_domains = num_domains;
639*e2ad626fSUlf Hansson 	bc->onecell_data.domains =
640*e2ad626fSUlf Hansson 		devm_kcalloc(dev, num_domains,
641*e2ad626fSUlf Hansson 			     sizeof(struct generic_pm_domain *), GFP_KERNEL);
642*e2ad626fSUlf Hansson 	if (!bc->onecell_data.domains)
643*e2ad626fSUlf Hansson 		return -ENOMEM;
644*e2ad626fSUlf Hansson 
645*e2ad626fSUlf Hansson 	bc->bus_power_dev = dev_pm_domain_attach_by_name(dev, "bus");
646*e2ad626fSUlf Hansson 	if (IS_ERR(bc->bus_power_dev))
647*e2ad626fSUlf Hansson 		return dev_err_probe(dev, PTR_ERR(bc->bus_power_dev),
648*e2ad626fSUlf Hansson 				     "failed to attach bus power domain\n");
649*e2ad626fSUlf Hansson 
650*e2ad626fSUlf Hansson 	bc->power_off = bc_data->power_off;
651*e2ad626fSUlf Hansson 	bc->power_on = bc_data->power_on;
652*e2ad626fSUlf Hansson 
653*e2ad626fSUlf Hansson 	for (i = 0; i < num_domains; i++) {
654*e2ad626fSUlf Hansson 		const struct imx8mp_blk_ctrl_domain_data *data = &bc_data->domains[i];
655*e2ad626fSUlf Hansson 		struct imx8mp_blk_ctrl_domain *domain = &bc->domains[i];
656*e2ad626fSUlf Hansson 		int j;
657*e2ad626fSUlf Hansson 
658*e2ad626fSUlf Hansson 		domain->data = data;
659*e2ad626fSUlf Hansson 		domain->num_paths = data->num_paths;
660*e2ad626fSUlf Hansson 
661*e2ad626fSUlf Hansson 		for (j = 0; j < data->num_clks; j++)
662*e2ad626fSUlf Hansson 			domain->clks[j].id = data->clk_names[j];
663*e2ad626fSUlf Hansson 
664*e2ad626fSUlf Hansson 		for (j = 0; j < data->num_paths; j++) {
665*e2ad626fSUlf Hansson 			domain->paths[j].name = data->path_names[j];
666*e2ad626fSUlf Hansson 			/* Fake value for now, just let ICC could configure NoC mode/priority */
667*e2ad626fSUlf Hansson 			domain->paths[j].avg_bw = 1;
668*e2ad626fSUlf Hansson 			domain->paths[j].peak_bw = 1;
669*e2ad626fSUlf Hansson 		}
670*e2ad626fSUlf Hansson 
671*e2ad626fSUlf Hansson 		ret = devm_of_icc_bulk_get(dev, data->num_paths, domain->paths);
672*e2ad626fSUlf Hansson 		if (ret) {
673*e2ad626fSUlf Hansson 			if (ret != -EPROBE_DEFER) {
674*e2ad626fSUlf Hansson 				dev_warn_once(dev, "Could not get interconnect paths, NoC will stay unconfigured!\n");
675*e2ad626fSUlf Hansson 				domain->num_paths = 0;
676*e2ad626fSUlf Hansson 			} else {
677*e2ad626fSUlf Hansson 				dev_err_probe(dev, ret, "failed to get noc entries\n");
678*e2ad626fSUlf Hansson 				goto cleanup_pds;
679*e2ad626fSUlf Hansson 			}
680*e2ad626fSUlf Hansson 		}
681*e2ad626fSUlf Hansson 
682*e2ad626fSUlf Hansson 		ret = devm_clk_bulk_get(dev, data->num_clks, domain->clks);
683*e2ad626fSUlf Hansson 		if (ret) {
684*e2ad626fSUlf Hansson 			dev_err_probe(dev, ret, "failed to get clock\n");
685*e2ad626fSUlf Hansson 			goto cleanup_pds;
686*e2ad626fSUlf Hansson 		}
687*e2ad626fSUlf Hansson 
688*e2ad626fSUlf Hansson 		domain->power_dev =
689*e2ad626fSUlf Hansson 			dev_pm_domain_attach_by_name(dev, data->gpc_name);
690*e2ad626fSUlf Hansson 		if (IS_ERR(domain->power_dev)) {
691*e2ad626fSUlf Hansson 			dev_err_probe(dev, PTR_ERR(domain->power_dev),
692*e2ad626fSUlf Hansson 				      "failed to attach power domain %s\n",
693*e2ad626fSUlf Hansson 				      data->gpc_name);
694*e2ad626fSUlf Hansson 			ret = PTR_ERR(domain->power_dev);
695*e2ad626fSUlf Hansson 			goto cleanup_pds;
696*e2ad626fSUlf Hansson 		}
697*e2ad626fSUlf Hansson 
698*e2ad626fSUlf Hansson 		domain->genpd.name = data->name;
699*e2ad626fSUlf Hansson 		domain->genpd.power_on = imx8mp_blk_ctrl_power_on;
700*e2ad626fSUlf Hansson 		domain->genpd.power_off = imx8mp_blk_ctrl_power_off;
701*e2ad626fSUlf Hansson 		domain->bc = bc;
702*e2ad626fSUlf Hansson 		domain->id = i;
703*e2ad626fSUlf Hansson 
704*e2ad626fSUlf Hansson 		ret = pm_genpd_init(&domain->genpd, NULL, true);
705*e2ad626fSUlf Hansson 		if (ret) {
706*e2ad626fSUlf Hansson 			dev_err_probe(dev, ret, "failed to init power domain\n");
707*e2ad626fSUlf Hansson 			dev_pm_domain_detach(domain->power_dev, true);
708*e2ad626fSUlf Hansson 			goto cleanup_pds;
709*e2ad626fSUlf Hansson 		}
710*e2ad626fSUlf Hansson 
711*e2ad626fSUlf Hansson 		/*
712*e2ad626fSUlf Hansson 		 * We use runtime PM to trigger power on/off of the upstream GPC
713*e2ad626fSUlf Hansson 		 * domain, as a strict hierarchical parent/child power domain
714*e2ad626fSUlf Hansson 		 * setup doesn't allow us to meet the sequencing requirements.
715*e2ad626fSUlf Hansson 		 * This means we have nested locking of genpd locks, without the
716*e2ad626fSUlf Hansson 		 * nesting being visible at the genpd level, so we need a
717*e2ad626fSUlf Hansson 		 * separate lock class to make lockdep aware of the fact that
718*e2ad626fSUlf Hansson 		 * this are separate domain locks that can be nested without a
719*e2ad626fSUlf Hansson 		 * self-deadlock.
720*e2ad626fSUlf Hansson 		 */
721*e2ad626fSUlf Hansson 		lockdep_set_class(&domain->genpd.mlock,
722*e2ad626fSUlf Hansson 				  &blk_ctrl_genpd_lock_class);
723*e2ad626fSUlf Hansson 
724*e2ad626fSUlf Hansson 		bc->onecell_data.domains[i] = &domain->genpd;
725*e2ad626fSUlf Hansson 	}
726*e2ad626fSUlf Hansson 
727*e2ad626fSUlf Hansson 	ret = of_genpd_add_provider_onecell(dev->of_node, &bc->onecell_data);
728*e2ad626fSUlf Hansson 	if (ret) {
729*e2ad626fSUlf Hansson 		dev_err_probe(dev, ret, "failed to add power domain provider\n");
730*e2ad626fSUlf Hansson 		goto cleanup_pds;
731*e2ad626fSUlf Hansson 	}
732*e2ad626fSUlf Hansson 
733*e2ad626fSUlf Hansson 	bc->power_nb.notifier_call = bc_data->power_notifier_fn;
734*e2ad626fSUlf Hansson 	ret = dev_pm_genpd_add_notifier(bc->bus_power_dev, &bc->power_nb);
735*e2ad626fSUlf Hansson 	if (ret) {
736*e2ad626fSUlf Hansson 		dev_err_probe(dev, ret, "failed to add power notifier\n");
737*e2ad626fSUlf Hansson 		goto cleanup_provider;
738*e2ad626fSUlf Hansson 	}
739*e2ad626fSUlf Hansson 
740*e2ad626fSUlf Hansson 	if (bc_data->probe) {
741*e2ad626fSUlf Hansson 		ret = bc_data->probe(bc);
742*e2ad626fSUlf Hansson 		if (ret)
743*e2ad626fSUlf Hansson 			goto cleanup_provider;
744*e2ad626fSUlf Hansson 	}
745*e2ad626fSUlf Hansson 
746*e2ad626fSUlf Hansson 	dev_set_drvdata(dev, bc);
747*e2ad626fSUlf Hansson 
748*e2ad626fSUlf Hansson 	return 0;
749*e2ad626fSUlf Hansson 
750*e2ad626fSUlf Hansson cleanup_provider:
751*e2ad626fSUlf Hansson 	of_genpd_del_provider(dev->of_node);
752*e2ad626fSUlf Hansson cleanup_pds:
753*e2ad626fSUlf Hansson 	for (i--; i >= 0; i--) {
754*e2ad626fSUlf Hansson 		pm_genpd_remove(&bc->domains[i].genpd);
755*e2ad626fSUlf Hansson 		dev_pm_domain_detach(bc->domains[i].power_dev, true);
756*e2ad626fSUlf Hansson 	}
757*e2ad626fSUlf Hansson 
758*e2ad626fSUlf Hansson 	dev_pm_domain_detach(bc->bus_power_dev, true);
759*e2ad626fSUlf Hansson 
760*e2ad626fSUlf Hansson 	return ret;
761*e2ad626fSUlf Hansson }
762*e2ad626fSUlf Hansson 
763*e2ad626fSUlf Hansson static int imx8mp_blk_ctrl_remove(struct platform_device *pdev)
764*e2ad626fSUlf Hansson {
765*e2ad626fSUlf Hansson 	struct imx8mp_blk_ctrl *bc = dev_get_drvdata(&pdev->dev);
766*e2ad626fSUlf Hansson 	int i;
767*e2ad626fSUlf Hansson 
768*e2ad626fSUlf Hansson 	of_genpd_del_provider(pdev->dev.of_node);
769*e2ad626fSUlf Hansson 
770*e2ad626fSUlf Hansson 	for (i = 0; bc->onecell_data.num_domains; i++) {
771*e2ad626fSUlf Hansson 		struct imx8mp_blk_ctrl_domain *domain = &bc->domains[i];
772*e2ad626fSUlf Hansson 
773*e2ad626fSUlf Hansson 		pm_genpd_remove(&domain->genpd);
774*e2ad626fSUlf Hansson 		dev_pm_domain_detach(domain->power_dev, true);
775*e2ad626fSUlf Hansson 	}
776*e2ad626fSUlf Hansson 
777*e2ad626fSUlf Hansson 	dev_pm_genpd_remove_notifier(bc->bus_power_dev);
778*e2ad626fSUlf Hansson 
779*e2ad626fSUlf Hansson 	dev_pm_domain_detach(bc->bus_power_dev, true);
780*e2ad626fSUlf Hansson 
781*e2ad626fSUlf Hansson 	return 0;
782*e2ad626fSUlf Hansson }
783*e2ad626fSUlf Hansson 
784*e2ad626fSUlf Hansson #ifdef CONFIG_PM_SLEEP
785*e2ad626fSUlf Hansson static int imx8mp_blk_ctrl_suspend(struct device *dev)
786*e2ad626fSUlf Hansson {
787*e2ad626fSUlf Hansson 	struct imx8mp_blk_ctrl *bc = dev_get_drvdata(dev);
788*e2ad626fSUlf Hansson 	int ret, i;
789*e2ad626fSUlf Hansson 
790*e2ad626fSUlf Hansson 	/*
791*e2ad626fSUlf Hansson 	 * This may look strange, but is done so the generic PM_SLEEP code
792*e2ad626fSUlf Hansson 	 * can power down our domains and more importantly power them up again
793*e2ad626fSUlf Hansson 	 * after resume, without tripping over our usage of runtime PM to
794*e2ad626fSUlf Hansson 	 * control the upstream GPC domains. Things happen in the right order
795*e2ad626fSUlf Hansson 	 * in the system suspend/resume paths due to the device parent/child
796*e2ad626fSUlf Hansson 	 * hierarchy.
797*e2ad626fSUlf Hansson 	 */
798*e2ad626fSUlf Hansson 	ret = pm_runtime_get_sync(bc->bus_power_dev);
799*e2ad626fSUlf Hansson 	if (ret < 0) {
800*e2ad626fSUlf Hansson 		pm_runtime_put_noidle(bc->bus_power_dev);
801*e2ad626fSUlf Hansson 		return ret;
802*e2ad626fSUlf Hansson 	}
803*e2ad626fSUlf Hansson 
804*e2ad626fSUlf Hansson 	for (i = 0; i < bc->onecell_data.num_domains; i++) {
805*e2ad626fSUlf Hansson 		struct imx8mp_blk_ctrl_domain *domain = &bc->domains[i];
806*e2ad626fSUlf Hansson 
807*e2ad626fSUlf Hansson 		ret = pm_runtime_get_sync(domain->power_dev);
808*e2ad626fSUlf Hansson 		if (ret < 0) {
809*e2ad626fSUlf Hansson 			pm_runtime_put_noidle(domain->power_dev);
810*e2ad626fSUlf Hansson 			goto out_fail;
811*e2ad626fSUlf Hansson 		}
812*e2ad626fSUlf Hansson 	}
813*e2ad626fSUlf Hansson 
814*e2ad626fSUlf Hansson 	return 0;
815*e2ad626fSUlf Hansson 
816*e2ad626fSUlf Hansson out_fail:
817*e2ad626fSUlf Hansson 	for (i--; i >= 0; i--)
818*e2ad626fSUlf Hansson 		pm_runtime_put(bc->domains[i].power_dev);
819*e2ad626fSUlf Hansson 
820*e2ad626fSUlf Hansson 	pm_runtime_put(bc->bus_power_dev);
821*e2ad626fSUlf Hansson 
822*e2ad626fSUlf Hansson 	return ret;
823*e2ad626fSUlf Hansson }
824*e2ad626fSUlf Hansson 
825*e2ad626fSUlf Hansson static int imx8mp_blk_ctrl_resume(struct device *dev)
826*e2ad626fSUlf Hansson {
827*e2ad626fSUlf Hansson 	struct imx8mp_blk_ctrl *bc = dev_get_drvdata(dev);
828*e2ad626fSUlf Hansson 	int i;
829*e2ad626fSUlf Hansson 
830*e2ad626fSUlf Hansson 	for (i = 0; i < bc->onecell_data.num_domains; i++)
831*e2ad626fSUlf Hansson 		pm_runtime_put(bc->domains[i].power_dev);
832*e2ad626fSUlf Hansson 
833*e2ad626fSUlf Hansson 	pm_runtime_put(bc->bus_power_dev);
834*e2ad626fSUlf Hansson 
835*e2ad626fSUlf Hansson 	return 0;
836*e2ad626fSUlf Hansson }
837*e2ad626fSUlf Hansson #endif
838*e2ad626fSUlf Hansson 
839*e2ad626fSUlf Hansson static const struct dev_pm_ops imx8mp_blk_ctrl_pm_ops = {
840*e2ad626fSUlf Hansson 	SET_SYSTEM_SLEEP_PM_OPS(imx8mp_blk_ctrl_suspend,
841*e2ad626fSUlf Hansson 				imx8mp_blk_ctrl_resume)
842*e2ad626fSUlf Hansson };
843*e2ad626fSUlf Hansson 
844*e2ad626fSUlf Hansson static const struct of_device_id imx8mp_blk_ctrl_of_match[] = {
845*e2ad626fSUlf Hansson 	{
846*e2ad626fSUlf Hansson 		.compatible = "fsl,imx8mp-hsio-blk-ctrl",
847*e2ad626fSUlf Hansson 		.data = &imx8mp_hsio_blk_ctl_dev_data,
848*e2ad626fSUlf Hansson 	}, {
849*e2ad626fSUlf Hansson 		.compatible = "fsl,imx8mp-hdmi-blk-ctrl",
850*e2ad626fSUlf Hansson 		.data = &imx8mp_hdmi_blk_ctl_dev_data,
851*e2ad626fSUlf Hansson 	}, {
852*e2ad626fSUlf Hansson 		/* Sentinel */
853*e2ad626fSUlf Hansson 	}
854*e2ad626fSUlf Hansson };
855*e2ad626fSUlf Hansson MODULE_DEVICE_TABLE(of, imx8mp_blk_ctrl_of_match);
856*e2ad626fSUlf Hansson 
857*e2ad626fSUlf Hansson static struct platform_driver imx8mp_blk_ctrl_driver = {
858*e2ad626fSUlf Hansson 	.probe = imx8mp_blk_ctrl_probe,
859*e2ad626fSUlf Hansson 	.remove = imx8mp_blk_ctrl_remove,
860*e2ad626fSUlf Hansson 	.driver = {
861*e2ad626fSUlf Hansson 		.name = "imx8mp-blk-ctrl",
862*e2ad626fSUlf Hansson 		.pm = &imx8mp_blk_ctrl_pm_ops,
863*e2ad626fSUlf Hansson 		.of_match_table = imx8mp_blk_ctrl_of_match,
864*e2ad626fSUlf Hansson 	},
865*e2ad626fSUlf Hansson };
866*e2ad626fSUlf Hansson module_platform_driver(imx8mp_blk_ctrl_driver);
867*e2ad626fSUlf Hansson MODULE_LICENSE("GPL");
868