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