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, ®map_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