162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/*
462306a36Sopenharmony_ci * Copyright 2022 Pengutronix, Lucas Stach <kernel@pengutronix.de>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/bitfield.h>
862306a36Sopenharmony_ci#include <linux/clk.h>
962306a36Sopenharmony_ci#include <linux/clk-provider.h>
1062306a36Sopenharmony_ci#include <linux/device.h>
1162306a36Sopenharmony_ci#include <linux/interconnect.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/of.h>
1462306a36Sopenharmony_ci#include <linux/platform_device.h>
1562306a36Sopenharmony_ci#include <linux/pm_domain.h>
1662306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1762306a36Sopenharmony_ci#include <linux/regmap.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <dt-bindings/power/imx8mp-power.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define GPR_REG0		0x0
2262306a36Sopenharmony_ci#define  PCIE_CLOCK_MODULE_EN	BIT(0)
2362306a36Sopenharmony_ci#define  USB_CLOCK_MODULE_EN	BIT(1)
2462306a36Sopenharmony_ci#define  PCIE_PHY_APB_RST	BIT(4)
2562306a36Sopenharmony_ci#define  PCIE_PHY_INIT_RST	BIT(5)
2662306a36Sopenharmony_ci#define GPR_REG1		0x4
2762306a36Sopenharmony_ci#define  PLL_LOCK		BIT(13)
2862306a36Sopenharmony_ci#define GPR_REG2		0x8
2962306a36Sopenharmony_ci#define  P_PLL_MASK		GENMASK(5, 0)
3062306a36Sopenharmony_ci#define  M_PLL_MASK		GENMASK(15, 6)
3162306a36Sopenharmony_ci#define  S_PLL_MASK		GENMASK(18, 16)
3262306a36Sopenharmony_ci#define GPR_REG3		0xc
3362306a36Sopenharmony_ci#define  PLL_CKE		BIT(17)
3462306a36Sopenharmony_ci#define  PLL_RST		BIT(31)
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistruct imx8mp_blk_ctrl_domain;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistruct imx8mp_blk_ctrl {
3962306a36Sopenharmony_ci	struct device *dev;
4062306a36Sopenharmony_ci	struct notifier_block power_nb;
4162306a36Sopenharmony_ci	struct device *bus_power_dev;
4262306a36Sopenharmony_ci	struct regmap *regmap;
4362306a36Sopenharmony_ci	struct imx8mp_blk_ctrl_domain *domains;
4462306a36Sopenharmony_ci	struct genpd_onecell_data onecell_data;
4562306a36Sopenharmony_ci	void (*power_off) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain);
4662306a36Sopenharmony_ci	void (*power_on) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain);
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistruct imx8mp_blk_ctrl_domain_data {
5062306a36Sopenharmony_ci	const char *name;
5162306a36Sopenharmony_ci	const char * const *clk_names;
5262306a36Sopenharmony_ci	int num_clks;
5362306a36Sopenharmony_ci	const char * const *path_names;
5462306a36Sopenharmony_ci	int num_paths;
5562306a36Sopenharmony_ci	const char *gpc_name;
5662306a36Sopenharmony_ci};
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#define DOMAIN_MAX_CLKS 2
5962306a36Sopenharmony_ci#define DOMAIN_MAX_PATHS 3
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistruct imx8mp_blk_ctrl_domain {
6262306a36Sopenharmony_ci	struct generic_pm_domain genpd;
6362306a36Sopenharmony_ci	const struct imx8mp_blk_ctrl_domain_data *data;
6462306a36Sopenharmony_ci	struct clk_bulk_data clks[DOMAIN_MAX_CLKS];
6562306a36Sopenharmony_ci	struct icc_bulk_data paths[DOMAIN_MAX_PATHS];
6662306a36Sopenharmony_ci	struct device *power_dev;
6762306a36Sopenharmony_ci	struct imx8mp_blk_ctrl *bc;
6862306a36Sopenharmony_ci	int num_paths;
6962306a36Sopenharmony_ci	int id;
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistruct imx8mp_blk_ctrl_data {
7362306a36Sopenharmony_ci	int max_reg;
7462306a36Sopenharmony_ci	int (*probe) (struct imx8mp_blk_ctrl *bc);
7562306a36Sopenharmony_ci	notifier_fn_t power_notifier_fn;
7662306a36Sopenharmony_ci	void (*power_off) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain);
7762306a36Sopenharmony_ci	void (*power_on) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain);
7862306a36Sopenharmony_ci	const struct imx8mp_blk_ctrl_domain_data *domains;
7962306a36Sopenharmony_ci	int num_domains;
8062306a36Sopenharmony_ci};
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic inline struct imx8mp_blk_ctrl_domain *
8362306a36Sopenharmony_cito_imx8mp_blk_ctrl_domain(struct generic_pm_domain *genpd)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	return container_of(genpd, struct imx8mp_blk_ctrl_domain, genpd);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistruct clk_hsio_pll {
8962306a36Sopenharmony_ci	struct clk_hw	hw;
9062306a36Sopenharmony_ci	struct regmap *regmap;
9162306a36Sopenharmony_ci};
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic inline struct clk_hsio_pll *to_clk_hsio_pll(struct clk_hw *hw)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	return container_of(hw, struct clk_hsio_pll, hw);
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic int clk_hsio_pll_prepare(struct clk_hw *hw)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	struct clk_hsio_pll *clk = to_clk_hsio_pll(hw);
10162306a36Sopenharmony_ci	u32 val;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	/* set the PLL configuration */
10462306a36Sopenharmony_ci	regmap_update_bits(clk->regmap, GPR_REG2,
10562306a36Sopenharmony_ci			   P_PLL_MASK | M_PLL_MASK | S_PLL_MASK,
10662306a36Sopenharmony_ci			   FIELD_PREP(P_PLL_MASK, 12) |
10762306a36Sopenharmony_ci			   FIELD_PREP(M_PLL_MASK, 800) |
10862306a36Sopenharmony_ci			   FIELD_PREP(S_PLL_MASK, 4));
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	/* de-assert PLL reset */
11162306a36Sopenharmony_ci	regmap_update_bits(clk->regmap, GPR_REG3, PLL_RST, PLL_RST);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	/* enable PLL */
11462306a36Sopenharmony_ci	regmap_update_bits(clk->regmap, GPR_REG3, PLL_CKE, PLL_CKE);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return regmap_read_poll_timeout(clk->regmap, GPR_REG1, val,
11762306a36Sopenharmony_ci					val & PLL_LOCK, 10, 100);
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic void clk_hsio_pll_unprepare(struct clk_hw *hw)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	struct clk_hsio_pll *clk = to_clk_hsio_pll(hw);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	regmap_update_bits(clk->regmap, GPR_REG3, PLL_RST | PLL_CKE, 0);
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic int clk_hsio_pll_is_prepared(struct clk_hw *hw)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct clk_hsio_pll *clk = to_clk_hsio_pll(hw);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	return regmap_test_bits(clk->regmap, GPR_REG1, PLL_LOCK);
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic unsigned long clk_hsio_pll_recalc_rate(struct clk_hw *hw,
13562306a36Sopenharmony_ci					      unsigned long parent_rate)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	return 100000000;
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic const struct clk_ops clk_hsio_pll_ops = {
14162306a36Sopenharmony_ci	.prepare = clk_hsio_pll_prepare,
14262306a36Sopenharmony_ci	.unprepare = clk_hsio_pll_unprepare,
14362306a36Sopenharmony_ci	.is_prepared = clk_hsio_pll_is_prepared,
14462306a36Sopenharmony_ci	.recalc_rate = clk_hsio_pll_recalc_rate,
14562306a36Sopenharmony_ci};
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic int imx8mp_hsio_blk_ctrl_probe(struct imx8mp_blk_ctrl *bc)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	struct clk_hsio_pll *clk_hsio_pll;
15062306a36Sopenharmony_ci	struct clk_hw *hw;
15162306a36Sopenharmony_ci	struct clk_init_data init = {};
15262306a36Sopenharmony_ci	int ret;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	clk_hsio_pll = devm_kzalloc(bc->dev, sizeof(*clk_hsio_pll), GFP_KERNEL);
15562306a36Sopenharmony_ci	if (!clk_hsio_pll)
15662306a36Sopenharmony_ci		return -ENOMEM;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	init.name = "hsio_pll";
15962306a36Sopenharmony_ci	init.ops = &clk_hsio_pll_ops;
16062306a36Sopenharmony_ci	init.parent_names = (const char *[]){"osc_24m"};
16162306a36Sopenharmony_ci	init.num_parents = 1;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	clk_hsio_pll->regmap = bc->regmap;
16462306a36Sopenharmony_ci	clk_hsio_pll->hw.init = &init;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	hw = &clk_hsio_pll->hw;
16762306a36Sopenharmony_ci	ret = devm_clk_hw_register(bc->bus_power_dev, hw);
16862306a36Sopenharmony_ci	if (ret)
16962306a36Sopenharmony_ci		return ret;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	return devm_of_clk_add_hw_provider(bc->dev, of_clk_hw_simple_get, hw);
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic void imx8mp_hsio_blk_ctrl_power_on(struct imx8mp_blk_ctrl *bc,
17562306a36Sopenharmony_ci					  struct imx8mp_blk_ctrl_domain *domain)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	switch (domain->id) {
17862306a36Sopenharmony_ci	case IMX8MP_HSIOBLK_PD_USB:
17962306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN);
18062306a36Sopenharmony_ci		break;
18162306a36Sopenharmony_ci	case IMX8MP_HSIOBLK_PD_PCIE:
18262306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, GPR_REG0, PCIE_CLOCK_MODULE_EN);
18362306a36Sopenharmony_ci		break;
18462306a36Sopenharmony_ci	case IMX8MP_HSIOBLK_PD_PCIE_PHY:
18562306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, GPR_REG0,
18662306a36Sopenharmony_ci				PCIE_PHY_APB_RST | PCIE_PHY_INIT_RST);
18762306a36Sopenharmony_ci		break;
18862306a36Sopenharmony_ci	default:
18962306a36Sopenharmony_ci		break;
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic void imx8mp_hsio_blk_ctrl_power_off(struct imx8mp_blk_ctrl *bc,
19462306a36Sopenharmony_ci					   struct imx8mp_blk_ctrl_domain *domain)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	switch (domain->id) {
19762306a36Sopenharmony_ci	case IMX8MP_HSIOBLK_PD_USB:
19862306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN);
19962306a36Sopenharmony_ci		break;
20062306a36Sopenharmony_ci	case IMX8MP_HSIOBLK_PD_PCIE:
20162306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, GPR_REG0, PCIE_CLOCK_MODULE_EN);
20262306a36Sopenharmony_ci		break;
20362306a36Sopenharmony_ci	case IMX8MP_HSIOBLK_PD_PCIE_PHY:
20462306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, GPR_REG0,
20562306a36Sopenharmony_ci				  PCIE_PHY_APB_RST | PCIE_PHY_INIT_RST);
20662306a36Sopenharmony_ci		break;
20762306a36Sopenharmony_ci	default:
20862306a36Sopenharmony_ci		break;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic int imx8mp_hsio_power_notifier(struct notifier_block *nb,
21362306a36Sopenharmony_ci				      unsigned long action, void *data)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	struct imx8mp_blk_ctrl *bc = container_of(nb, struct imx8mp_blk_ctrl,
21662306a36Sopenharmony_ci						 power_nb);
21762306a36Sopenharmony_ci	struct clk_bulk_data *usb_clk = bc->domains[IMX8MP_HSIOBLK_PD_USB].clks;
21862306a36Sopenharmony_ci	int num_clks = bc->domains[IMX8MP_HSIOBLK_PD_USB].data->num_clks;
21962306a36Sopenharmony_ci	int ret;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	switch (action) {
22262306a36Sopenharmony_ci	case GENPD_NOTIFY_ON:
22362306a36Sopenharmony_ci		/*
22462306a36Sopenharmony_ci		 * enable USB clock for a moment for the power-on ADB handshake
22562306a36Sopenharmony_ci		 * to proceed
22662306a36Sopenharmony_ci		 */
22762306a36Sopenharmony_ci		ret = clk_bulk_prepare_enable(num_clks, usb_clk);
22862306a36Sopenharmony_ci		if (ret)
22962306a36Sopenharmony_ci			return NOTIFY_BAD;
23062306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci		udelay(5);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN);
23562306a36Sopenharmony_ci		clk_bulk_disable_unprepare(num_clks, usb_clk);
23662306a36Sopenharmony_ci		break;
23762306a36Sopenharmony_ci	case GENPD_NOTIFY_PRE_OFF:
23862306a36Sopenharmony_ci		/* enable USB clock for the power-down ADB handshake to work */
23962306a36Sopenharmony_ci		ret = clk_bulk_prepare_enable(num_clks, usb_clk);
24062306a36Sopenharmony_ci		if (ret)
24162306a36Sopenharmony_ci			return NOTIFY_BAD;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN);
24462306a36Sopenharmony_ci		break;
24562306a36Sopenharmony_ci	case GENPD_NOTIFY_OFF:
24662306a36Sopenharmony_ci		clk_bulk_disable_unprepare(num_clks, usb_clk);
24762306a36Sopenharmony_ci		break;
24862306a36Sopenharmony_ci	default:
24962306a36Sopenharmony_ci		break;
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	return NOTIFY_OK;
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_cistatic const struct imx8mp_blk_ctrl_domain_data imx8mp_hsio_domain_data[] = {
25662306a36Sopenharmony_ci	[IMX8MP_HSIOBLK_PD_USB] = {
25762306a36Sopenharmony_ci		.name = "hsioblk-usb",
25862306a36Sopenharmony_ci		.clk_names = (const char *[]){ "usb" },
25962306a36Sopenharmony_ci		.num_clks = 1,
26062306a36Sopenharmony_ci		.gpc_name = "usb",
26162306a36Sopenharmony_ci		.path_names = (const char *[]){"usb1", "usb2"},
26262306a36Sopenharmony_ci		.num_paths = 2,
26362306a36Sopenharmony_ci	},
26462306a36Sopenharmony_ci	[IMX8MP_HSIOBLK_PD_USB_PHY1] = {
26562306a36Sopenharmony_ci		.name = "hsioblk-usb-phy1",
26662306a36Sopenharmony_ci		.gpc_name = "usb-phy1",
26762306a36Sopenharmony_ci	},
26862306a36Sopenharmony_ci	[IMX8MP_HSIOBLK_PD_USB_PHY2] = {
26962306a36Sopenharmony_ci		.name = "hsioblk-usb-phy2",
27062306a36Sopenharmony_ci		.gpc_name = "usb-phy2",
27162306a36Sopenharmony_ci	},
27262306a36Sopenharmony_ci	[IMX8MP_HSIOBLK_PD_PCIE] = {
27362306a36Sopenharmony_ci		.name = "hsioblk-pcie",
27462306a36Sopenharmony_ci		.clk_names = (const char *[]){ "pcie" },
27562306a36Sopenharmony_ci		.num_clks = 1,
27662306a36Sopenharmony_ci		.gpc_name = "pcie",
27762306a36Sopenharmony_ci		.path_names = (const char *[]){"noc-pcie", "pcie"},
27862306a36Sopenharmony_ci		.num_paths = 2,
27962306a36Sopenharmony_ci	},
28062306a36Sopenharmony_ci	[IMX8MP_HSIOBLK_PD_PCIE_PHY] = {
28162306a36Sopenharmony_ci		.name = "hsioblk-pcie-phy",
28262306a36Sopenharmony_ci		.gpc_name = "pcie-phy",
28362306a36Sopenharmony_ci	},
28462306a36Sopenharmony_ci};
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic const struct imx8mp_blk_ctrl_data imx8mp_hsio_blk_ctl_dev_data = {
28762306a36Sopenharmony_ci	.max_reg = 0x24,
28862306a36Sopenharmony_ci	.probe = imx8mp_hsio_blk_ctrl_probe,
28962306a36Sopenharmony_ci	.power_on = imx8mp_hsio_blk_ctrl_power_on,
29062306a36Sopenharmony_ci	.power_off = imx8mp_hsio_blk_ctrl_power_off,
29162306a36Sopenharmony_ci	.power_notifier_fn = imx8mp_hsio_power_notifier,
29262306a36Sopenharmony_ci	.domains = imx8mp_hsio_domain_data,
29362306a36Sopenharmony_ci	.num_domains = ARRAY_SIZE(imx8mp_hsio_domain_data),
29462306a36Sopenharmony_ci};
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci#define HDMI_RTX_RESET_CTL0	0x20
29762306a36Sopenharmony_ci#define HDMI_RTX_CLK_CTL0	0x40
29862306a36Sopenharmony_ci#define HDMI_RTX_CLK_CTL1	0x50
29962306a36Sopenharmony_ci#define HDMI_RTX_CLK_CTL2	0x60
30062306a36Sopenharmony_ci#define HDMI_RTX_CLK_CTL3	0x70
30162306a36Sopenharmony_ci#define HDMI_RTX_CLK_CTL4	0x80
30262306a36Sopenharmony_ci#define HDMI_TX_CONTROL0	0x200
30362306a36Sopenharmony_ci#define  HDMI_LCDIF_NOC_HURRY_MASK		GENMASK(14, 12)
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic void imx8mp_hdmi_blk_ctrl_power_on(struct imx8mp_blk_ctrl *bc,
30662306a36Sopenharmony_ci					  struct imx8mp_blk_ctrl_domain *domain)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	switch (domain->id) {
30962306a36Sopenharmony_ci	case IMX8MP_HDMIBLK_PD_IRQSTEER:
31062306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(9));
31162306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(16));
31262306a36Sopenharmony_ci		break;
31362306a36Sopenharmony_ci	case IMX8MP_HDMIBLK_PD_LCDIF:
31462306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0,
31562306a36Sopenharmony_ci				BIT(16) | BIT(17) | BIT(18) |
31662306a36Sopenharmony_ci				BIT(19) | BIT(20));
31762306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(11));
31862306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0,
31962306a36Sopenharmony_ci				BIT(4) | BIT(5) | BIT(6));
32062306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, HDMI_TX_CONTROL0,
32162306a36Sopenharmony_ci				FIELD_PREP(HDMI_LCDIF_NOC_HURRY_MASK, 7));
32262306a36Sopenharmony_ci		break;
32362306a36Sopenharmony_ci	case IMX8MP_HDMIBLK_PD_PAI:
32462306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(17));
32562306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(18));
32662306a36Sopenharmony_ci		break;
32762306a36Sopenharmony_ci	case IMX8MP_HDMIBLK_PD_PVI:
32862306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(28));
32962306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(22));
33062306a36Sopenharmony_ci		break;
33162306a36Sopenharmony_ci	case IMX8MP_HDMIBLK_PD_TRNG:
33262306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(27) | BIT(30));
33362306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(20));
33462306a36Sopenharmony_ci		break;
33562306a36Sopenharmony_ci	case IMX8MP_HDMIBLK_PD_HDMI_TX:
33662306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0,
33762306a36Sopenharmony_ci				BIT(2) | BIT(4) | BIT(5));
33862306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1,
33962306a36Sopenharmony_ci				BIT(12) | BIT(13) | BIT(14) | BIT(15) | BIT(16) |
34062306a36Sopenharmony_ci				BIT(18) | BIT(19) | BIT(20) | BIT(21));
34162306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0,
34262306a36Sopenharmony_ci				BIT(7) | BIT(10) | BIT(11));
34362306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(1));
34462306a36Sopenharmony_ci		break;
34562306a36Sopenharmony_ci	case IMX8MP_HDMIBLK_PD_HDMI_TX_PHY:
34662306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(7));
34762306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(22) | BIT(24));
34862306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(12));
34962306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(3));
35062306a36Sopenharmony_ci		break;
35162306a36Sopenharmony_ci	case IMX8MP_HDMIBLK_PD_HDCP:
35262306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(11));
35362306a36Sopenharmony_ci		break;
35462306a36Sopenharmony_ci	case IMX8MP_HDMIBLK_PD_HRV:
35562306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(3) | BIT(4) | BIT(5));
35662306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(15));
35762306a36Sopenharmony_ci		break;
35862306a36Sopenharmony_ci	default:
35962306a36Sopenharmony_ci		break;
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic void imx8mp_hdmi_blk_ctrl_power_off(struct imx8mp_blk_ctrl *bc,
36462306a36Sopenharmony_ci					   struct imx8mp_blk_ctrl_domain *domain)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	switch (domain->id) {
36762306a36Sopenharmony_ci	case IMX8MP_HDMIBLK_PD_IRQSTEER:
36862306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(9));
36962306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(16));
37062306a36Sopenharmony_ci		break;
37162306a36Sopenharmony_ci	case IMX8MP_HDMIBLK_PD_LCDIF:
37262306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0,
37362306a36Sopenharmony_ci				  BIT(4) | BIT(5) | BIT(6));
37462306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(11));
37562306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0,
37662306a36Sopenharmony_ci				  BIT(16) | BIT(17) | BIT(18) |
37762306a36Sopenharmony_ci				  BIT(19) | BIT(20));
37862306a36Sopenharmony_ci		break;
37962306a36Sopenharmony_ci	case IMX8MP_HDMIBLK_PD_PAI:
38062306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(18));
38162306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(17));
38262306a36Sopenharmony_ci		break;
38362306a36Sopenharmony_ci	case IMX8MP_HDMIBLK_PD_PVI:
38462306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(22));
38562306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(28));
38662306a36Sopenharmony_ci		break;
38762306a36Sopenharmony_ci	case IMX8MP_HDMIBLK_PD_TRNG:
38862306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(20));
38962306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(27) | BIT(30));
39062306a36Sopenharmony_ci		break;
39162306a36Sopenharmony_ci	case IMX8MP_HDMIBLK_PD_HDMI_TX:
39262306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(1));
39362306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0,
39462306a36Sopenharmony_ci				  BIT(7) | BIT(10) | BIT(11));
39562306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1,
39662306a36Sopenharmony_ci				  BIT(12) | BIT(13) | BIT(14) | BIT(15) | BIT(16) |
39762306a36Sopenharmony_ci				  BIT(18) | BIT(19) | BIT(20) | BIT(21));
39862306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0,
39962306a36Sopenharmony_ci				  BIT(2) | BIT(4) | BIT(5));
40062306a36Sopenharmony_ci		break;
40162306a36Sopenharmony_ci	case IMX8MP_HDMIBLK_PD_HDMI_TX_PHY:
40262306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(3));
40362306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(12));
40462306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(7));
40562306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(22) | BIT(24));
40662306a36Sopenharmony_ci		break;
40762306a36Sopenharmony_ci	case IMX8MP_HDMIBLK_PD_HDCP:
40862306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(11));
40962306a36Sopenharmony_ci		break;
41062306a36Sopenharmony_ci	case IMX8MP_HDMIBLK_PD_HRV:
41162306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(15));
41262306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(3) | BIT(4) | BIT(5));
41362306a36Sopenharmony_ci		break;
41462306a36Sopenharmony_ci	default:
41562306a36Sopenharmony_ci		break;
41662306a36Sopenharmony_ci	}
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_cistatic int imx8mp_hdmi_power_notifier(struct notifier_block *nb,
42062306a36Sopenharmony_ci				      unsigned long action, void *data)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	struct imx8mp_blk_ctrl *bc = container_of(nb, struct imx8mp_blk_ctrl,
42362306a36Sopenharmony_ci						 power_nb);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	if (action != GENPD_NOTIFY_ON)
42662306a36Sopenharmony_ci		return NOTIFY_OK;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	/*
42962306a36Sopenharmony_ci	 * Contrary to other blk-ctrls the reset and clock don't clear when the
43062306a36Sopenharmony_ci	 * power domain is powered down. To ensure the proper reset pulsing,
43162306a36Sopenharmony_ci	 * first clear them all to asserted state, then enable the bus clocks
43262306a36Sopenharmony_ci	 * and then release the ADB reset.
43362306a36Sopenharmony_ci	 */
43462306a36Sopenharmony_ci	regmap_write(bc->regmap, HDMI_RTX_RESET_CTL0, 0x0);
43562306a36Sopenharmony_ci	regmap_write(bc->regmap, HDMI_RTX_CLK_CTL0, 0x0);
43662306a36Sopenharmony_ci	regmap_write(bc->regmap, HDMI_RTX_CLK_CTL1, 0x0);
43762306a36Sopenharmony_ci	regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0,
43862306a36Sopenharmony_ci			BIT(0) | BIT(1) | BIT(10));
43962306a36Sopenharmony_ci	regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(0));
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	/*
44262306a36Sopenharmony_ci	 * On power up we have no software backchannel to the GPC to
44362306a36Sopenharmony_ci	 * wait for the ADB handshake to happen, so we just delay for a
44462306a36Sopenharmony_ci	 * bit. On power down the GPC driver waits for the handshake.
44562306a36Sopenharmony_ci	 */
44662306a36Sopenharmony_ci	udelay(5);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	return NOTIFY_OK;
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_cistatic const struct imx8mp_blk_ctrl_domain_data imx8mp_hdmi_domain_data[] = {
45262306a36Sopenharmony_ci	[IMX8MP_HDMIBLK_PD_IRQSTEER] = {
45362306a36Sopenharmony_ci		.name = "hdmiblk-irqsteer",
45462306a36Sopenharmony_ci		.clk_names = (const char *[]){ "apb" },
45562306a36Sopenharmony_ci		.num_clks = 1,
45662306a36Sopenharmony_ci		.gpc_name = "irqsteer",
45762306a36Sopenharmony_ci	},
45862306a36Sopenharmony_ci	[IMX8MP_HDMIBLK_PD_LCDIF] = {
45962306a36Sopenharmony_ci		.name = "hdmiblk-lcdif",
46062306a36Sopenharmony_ci		.clk_names = (const char *[]){ "axi", "apb" },
46162306a36Sopenharmony_ci		.num_clks = 2,
46262306a36Sopenharmony_ci		.gpc_name = "lcdif",
46362306a36Sopenharmony_ci		.path_names = (const char *[]){"lcdif-hdmi"},
46462306a36Sopenharmony_ci		.num_paths = 1,
46562306a36Sopenharmony_ci	},
46662306a36Sopenharmony_ci	[IMX8MP_HDMIBLK_PD_PAI] = {
46762306a36Sopenharmony_ci		.name = "hdmiblk-pai",
46862306a36Sopenharmony_ci		.clk_names = (const char *[]){ "apb" },
46962306a36Sopenharmony_ci		.num_clks = 1,
47062306a36Sopenharmony_ci		.gpc_name = "pai",
47162306a36Sopenharmony_ci	},
47262306a36Sopenharmony_ci	[IMX8MP_HDMIBLK_PD_PVI] = {
47362306a36Sopenharmony_ci		.name = "hdmiblk-pvi",
47462306a36Sopenharmony_ci		.clk_names = (const char *[]){ "apb" },
47562306a36Sopenharmony_ci		.num_clks = 1,
47662306a36Sopenharmony_ci		.gpc_name = "pvi",
47762306a36Sopenharmony_ci	},
47862306a36Sopenharmony_ci	[IMX8MP_HDMIBLK_PD_TRNG] = {
47962306a36Sopenharmony_ci		.name = "hdmiblk-trng",
48062306a36Sopenharmony_ci		.clk_names = (const char *[]){ "apb" },
48162306a36Sopenharmony_ci		.num_clks = 1,
48262306a36Sopenharmony_ci		.gpc_name = "trng",
48362306a36Sopenharmony_ci	},
48462306a36Sopenharmony_ci	[IMX8MP_HDMIBLK_PD_HDMI_TX] = {
48562306a36Sopenharmony_ci		.name = "hdmiblk-hdmi-tx",
48662306a36Sopenharmony_ci		.clk_names = (const char *[]){ "apb", "ref_266m" },
48762306a36Sopenharmony_ci		.num_clks = 2,
48862306a36Sopenharmony_ci		.gpc_name = "hdmi-tx",
48962306a36Sopenharmony_ci	},
49062306a36Sopenharmony_ci	[IMX8MP_HDMIBLK_PD_HDMI_TX_PHY] = {
49162306a36Sopenharmony_ci		.name = "hdmiblk-hdmi-tx-phy",
49262306a36Sopenharmony_ci		.clk_names = (const char *[]){ "apb", "ref_24m" },
49362306a36Sopenharmony_ci		.num_clks = 2,
49462306a36Sopenharmony_ci		.gpc_name = "hdmi-tx-phy",
49562306a36Sopenharmony_ci	},
49662306a36Sopenharmony_ci	[IMX8MP_HDMIBLK_PD_HRV] = {
49762306a36Sopenharmony_ci		.name = "hdmiblk-hrv",
49862306a36Sopenharmony_ci		.clk_names = (const char *[]){ "axi", "apb" },
49962306a36Sopenharmony_ci		.num_clks = 2,
50062306a36Sopenharmony_ci		.gpc_name = "hrv",
50162306a36Sopenharmony_ci		.path_names = (const char *[]){"hrv"},
50262306a36Sopenharmony_ci		.num_paths = 1,
50362306a36Sopenharmony_ci	},
50462306a36Sopenharmony_ci	[IMX8MP_HDMIBLK_PD_HDCP] = {
50562306a36Sopenharmony_ci		.name = "hdmiblk-hdcp",
50662306a36Sopenharmony_ci		.clk_names = (const char *[]){ "axi", "apb" },
50762306a36Sopenharmony_ci		.num_clks = 2,
50862306a36Sopenharmony_ci		.gpc_name = "hdcp",
50962306a36Sopenharmony_ci		.path_names = (const char *[]){"hdcp"},
51062306a36Sopenharmony_ci		.num_paths = 1,
51162306a36Sopenharmony_ci	},
51262306a36Sopenharmony_ci};
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_cistatic const struct imx8mp_blk_ctrl_data imx8mp_hdmi_blk_ctl_dev_data = {
51562306a36Sopenharmony_ci	.max_reg = 0x23c,
51662306a36Sopenharmony_ci	.power_on = imx8mp_hdmi_blk_ctrl_power_on,
51762306a36Sopenharmony_ci	.power_off = imx8mp_hdmi_blk_ctrl_power_off,
51862306a36Sopenharmony_ci	.power_notifier_fn = imx8mp_hdmi_power_notifier,
51962306a36Sopenharmony_ci	.domains = imx8mp_hdmi_domain_data,
52062306a36Sopenharmony_ci	.num_domains = ARRAY_SIZE(imx8mp_hdmi_domain_data),
52162306a36Sopenharmony_ci};
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_cistatic int imx8mp_blk_ctrl_power_on(struct generic_pm_domain *genpd)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci	struct imx8mp_blk_ctrl_domain *domain = to_imx8mp_blk_ctrl_domain(genpd);
52662306a36Sopenharmony_ci	const struct imx8mp_blk_ctrl_domain_data *data = domain->data;
52762306a36Sopenharmony_ci	struct imx8mp_blk_ctrl *bc = domain->bc;
52862306a36Sopenharmony_ci	int ret;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	/* make sure bus domain is awake */
53162306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(bc->bus_power_dev);
53262306a36Sopenharmony_ci	if (ret < 0) {
53362306a36Sopenharmony_ci		dev_err(bc->dev, "failed to power up bus domain\n");
53462306a36Sopenharmony_ci		return ret;
53562306a36Sopenharmony_ci	}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	/* enable upstream clocks */
53862306a36Sopenharmony_ci	ret = clk_bulk_prepare_enable(data->num_clks, domain->clks);
53962306a36Sopenharmony_ci	if (ret) {
54062306a36Sopenharmony_ci		dev_err(bc->dev, "failed to enable clocks\n");
54162306a36Sopenharmony_ci		goto bus_put;
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	/* domain specific blk-ctrl manipulation */
54562306a36Sopenharmony_ci	bc->power_on(bc, domain);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	/* power up upstream GPC domain */
54862306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(domain->power_dev);
54962306a36Sopenharmony_ci	if (ret < 0) {
55062306a36Sopenharmony_ci		dev_err(bc->dev, "failed to power up peripheral domain\n");
55162306a36Sopenharmony_ci		goto clk_disable;
55262306a36Sopenharmony_ci	}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	ret = icc_bulk_set_bw(domain->num_paths, domain->paths);
55562306a36Sopenharmony_ci	if (ret)
55662306a36Sopenharmony_ci		dev_err(bc->dev, "failed to set icc bw\n");
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	clk_bulk_disable_unprepare(data->num_clks, domain->clks);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	return 0;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ciclk_disable:
56362306a36Sopenharmony_ci	clk_bulk_disable_unprepare(data->num_clks, domain->clks);
56462306a36Sopenharmony_cibus_put:
56562306a36Sopenharmony_ci	pm_runtime_put(bc->bus_power_dev);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	return ret;
56862306a36Sopenharmony_ci}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_cistatic int imx8mp_blk_ctrl_power_off(struct generic_pm_domain *genpd)
57162306a36Sopenharmony_ci{
57262306a36Sopenharmony_ci	struct imx8mp_blk_ctrl_domain *domain = to_imx8mp_blk_ctrl_domain(genpd);
57362306a36Sopenharmony_ci	const struct imx8mp_blk_ctrl_domain_data *data = domain->data;
57462306a36Sopenharmony_ci	struct imx8mp_blk_ctrl *bc = domain->bc;
57562306a36Sopenharmony_ci	int ret;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	ret = clk_bulk_prepare_enable(data->num_clks, domain->clks);
57862306a36Sopenharmony_ci	if (ret) {
57962306a36Sopenharmony_ci		dev_err(bc->dev, "failed to enable clocks\n");
58062306a36Sopenharmony_ci		return ret;
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	/* domain specific blk-ctrl manipulation */
58462306a36Sopenharmony_ci	bc->power_off(bc, domain);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	clk_bulk_disable_unprepare(data->num_clks, domain->clks);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	/* power down upstream GPC domain */
58962306a36Sopenharmony_ci	pm_runtime_put(domain->power_dev);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	/* allow bus domain to suspend */
59262306a36Sopenharmony_ci	pm_runtime_put(bc->bus_power_dev);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	return 0;
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_cistatic struct lock_class_key blk_ctrl_genpd_lock_class;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_cistatic int imx8mp_blk_ctrl_probe(struct platform_device *pdev)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	const struct imx8mp_blk_ctrl_data *bc_data;
60262306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
60362306a36Sopenharmony_ci	struct imx8mp_blk_ctrl *bc;
60462306a36Sopenharmony_ci	void __iomem *base;
60562306a36Sopenharmony_ci	int num_domains, i, ret;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	struct regmap_config regmap_config = {
60862306a36Sopenharmony_ci		.reg_bits	= 32,
60962306a36Sopenharmony_ci		.val_bits	= 32,
61062306a36Sopenharmony_ci		.reg_stride	= 4,
61162306a36Sopenharmony_ci	};
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL);
61462306a36Sopenharmony_ci	if (!bc)
61562306a36Sopenharmony_ci		return -ENOMEM;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	bc->dev = dev;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	bc_data = of_device_get_match_data(dev);
62062306a36Sopenharmony_ci	num_domains = bc_data->num_domains;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	base = devm_platform_ioremap_resource(pdev, 0);
62362306a36Sopenharmony_ci	if (IS_ERR(base))
62462306a36Sopenharmony_ci		return PTR_ERR(base);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	regmap_config.max_register = bc_data->max_reg;
62762306a36Sopenharmony_ci	bc->regmap = devm_regmap_init_mmio(dev, base, &regmap_config);
62862306a36Sopenharmony_ci	if (IS_ERR(bc->regmap))
62962306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(bc->regmap),
63062306a36Sopenharmony_ci				     "failed to init regmap\n");
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	bc->domains = devm_kcalloc(dev, num_domains,
63362306a36Sopenharmony_ci				   sizeof(struct imx8mp_blk_ctrl_domain),
63462306a36Sopenharmony_ci				   GFP_KERNEL);
63562306a36Sopenharmony_ci	if (!bc->domains)
63662306a36Sopenharmony_ci		return -ENOMEM;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	bc->onecell_data.num_domains = num_domains;
63962306a36Sopenharmony_ci	bc->onecell_data.domains =
64062306a36Sopenharmony_ci		devm_kcalloc(dev, num_domains,
64162306a36Sopenharmony_ci			     sizeof(struct generic_pm_domain *), GFP_KERNEL);
64262306a36Sopenharmony_ci	if (!bc->onecell_data.domains)
64362306a36Sopenharmony_ci		return -ENOMEM;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	bc->bus_power_dev = dev_pm_domain_attach_by_name(dev, "bus");
64662306a36Sopenharmony_ci	if (IS_ERR(bc->bus_power_dev))
64762306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(bc->bus_power_dev),
64862306a36Sopenharmony_ci				     "failed to attach bus power domain\n");
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	bc->power_off = bc_data->power_off;
65162306a36Sopenharmony_ci	bc->power_on = bc_data->power_on;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	for (i = 0; i < num_domains; i++) {
65462306a36Sopenharmony_ci		const struct imx8mp_blk_ctrl_domain_data *data = &bc_data->domains[i];
65562306a36Sopenharmony_ci		struct imx8mp_blk_ctrl_domain *domain = &bc->domains[i];
65662306a36Sopenharmony_ci		int j;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci		domain->data = data;
65962306a36Sopenharmony_ci		domain->num_paths = data->num_paths;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci		for (j = 0; j < data->num_clks; j++)
66262306a36Sopenharmony_ci			domain->clks[j].id = data->clk_names[j];
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci		for (j = 0; j < data->num_paths; j++) {
66562306a36Sopenharmony_ci			domain->paths[j].name = data->path_names[j];
66662306a36Sopenharmony_ci			/* Fake value for now, just let ICC could configure NoC mode/priority */
66762306a36Sopenharmony_ci			domain->paths[j].avg_bw = 1;
66862306a36Sopenharmony_ci			domain->paths[j].peak_bw = 1;
66962306a36Sopenharmony_ci		}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci		ret = devm_of_icc_bulk_get(dev, data->num_paths, domain->paths);
67262306a36Sopenharmony_ci		if (ret) {
67362306a36Sopenharmony_ci			if (ret != -EPROBE_DEFER) {
67462306a36Sopenharmony_ci				dev_warn_once(dev, "Could not get interconnect paths, NoC will stay unconfigured!\n");
67562306a36Sopenharmony_ci				domain->num_paths = 0;
67662306a36Sopenharmony_ci			} else {
67762306a36Sopenharmony_ci				dev_err_probe(dev, ret, "failed to get noc entries\n");
67862306a36Sopenharmony_ci				goto cleanup_pds;
67962306a36Sopenharmony_ci			}
68062306a36Sopenharmony_ci		}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci		ret = devm_clk_bulk_get(dev, data->num_clks, domain->clks);
68362306a36Sopenharmony_ci		if (ret) {
68462306a36Sopenharmony_ci			dev_err_probe(dev, ret, "failed to get clock\n");
68562306a36Sopenharmony_ci			goto cleanup_pds;
68662306a36Sopenharmony_ci		}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci		domain->power_dev =
68962306a36Sopenharmony_ci			dev_pm_domain_attach_by_name(dev, data->gpc_name);
69062306a36Sopenharmony_ci		if (IS_ERR(domain->power_dev)) {
69162306a36Sopenharmony_ci			dev_err_probe(dev, PTR_ERR(domain->power_dev),
69262306a36Sopenharmony_ci				      "failed to attach power domain %s\n",
69362306a36Sopenharmony_ci				      data->gpc_name);
69462306a36Sopenharmony_ci			ret = PTR_ERR(domain->power_dev);
69562306a36Sopenharmony_ci			goto cleanup_pds;
69662306a36Sopenharmony_ci		}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci		domain->genpd.name = data->name;
69962306a36Sopenharmony_ci		domain->genpd.power_on = imx8mp_blk_ctrl_power_on;
70062306a36Sopenharmony_ci		domain->genpd.power_off = imx8mp_blk_ctrl_power_off;
70162306a36Sopenharmony_ci		domain->bc = bc;
70262306a36Sopenharmony_ci		domain->id = i;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci		ret = pm_genpd_init(&domain->genpd, NULL, true);
70562306a36Sopenharmony_ci		if (ret) {
70662306a36Sopenharmony_ci			dev_err_probe(dev, ret, "failed to init power domain\n");
70762306a36Sopenharmony_ci			dev_pm_domain_detach(domain->power_dev, true);
70862306a36Sopenharmony_ci			goto cleanup_pds;
70962306a36Sopenharmony_ci		}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci		/*
71262306a36Sopenharmony_ci		 * We use runtime PM to trigger power on/off of the upstream GPC
71362306a36Sopenharmony_ci		 * domain, as a strict hierarchical parent/child power domain
71462306a36Sopenharmony_ci		 * setup doesn't allow us to meet the sequencing requirements.
71562306a36Sopenharmony_ci		 * This means we have nested locking of genpd locks, without the
71662306a36Sopenharmony_ci		 * nesting being visible at the genpd level, so we need a
71762306a36Sopenharmony_ci		 * separate lock class to make lockdep aware of the fact that
71862306a36Sopenharmony_ci		 * this are separate domain locks that can be nested without a
71962306a36Sopenharmony_ci		 * self-deadlock.
72062306a36Sopenharmony_ci		 */
72162306a36Sopenharmony_ci		lockdep_set_class(&domain->genpd.mlock,
72262306a36Sopenharmony_ci				  &blk_ctrl_genpd_lock_class);
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci		bc->onecell_data.domains[i] = &domain->genpd;
72562306a36Sopenharmony_ci	}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	ret = of_genpd_add_provider_onecell(dev->of_node, &bc->onecell_data);
72862306a36Sopenharmony_ci	if (ret) {
72962306a36Sopenharmony_ci		dev_err_probe(dev, ret, "failed to add power domain provider\n");
73062306a36Sopenharmony_ci		goto cleanup_pds;
73162306a36Sopenharmony_ci	}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	bc->power_nb.notifier_call = bc_data->power_notifier_fn;
73462306a36Sopenharmony_ci	ret = dev_pm_genpd_add_notifier(bc->bus_power_dev, &bc->power_nb);
73562306a36Sopenharmony_ci	if (ret) {
73662306a36Sopenharmony_ci		dev_err_probe(dev, ret, "failed to add power notifier\n");
73762306a36Sopenharmony_ci		goto cleanup_provider;
73862306a36Sopenharmony_ci	}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	if (bc_data->probe) {
74162306a36Sopenharmony_ci		ret = bc_data->probe(bc);
74262306a36Sopenharmony_ci		if (ret)
74362306a36Sopenharmony_ci			goto cleanup_provider;
74462306a36Sopenharmony_ci	}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	dev_set_drvdata(dev, bc);
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	return 0;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_cicleanup_provider:
75162306a36Sopenharmony_ci	of_genpd_del_provider(dev->of_node);
75262306a36Sopenharmony_cicleanup_pds:
75362306a36Sopenharmony_ci	for (i--; i >= 0; i--) {
75462306a36Sopenharmony_ci		pm_genpd_remove(&bc->domains[i].genpd);
75562306a36Sopenharmony_ci		dev_pm_domain_detach(bc->domains[i].power_dev, true);
75662306a36Sopenharmony_ci	}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	dev_pm_domain_detach(bc->bus_power_dev, true);
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	return ret;
76162306a36Sopenharmony_ci}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_cistatic int imx8mp_blk_ctrl_remove(struct platform_device *pdev)
76462306a36Sopenharmony_ci{
76562306a36Sopenharmony_ci	struct imx8mp_blk_ctrl *bc = dev_get_drvdata(&pdev->dev);
76662306a36Sopenharmony_ci	int i;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	of_genpd_del_provider(pdev->dev.of_node);
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	for (i = 0; bc->onecell_data.num_domains; i++) {
77162306a36Sopenharmony_ci		struct imx8mp_blk_ctrl_domain *domain = &bc->domains[i];
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci		pm_genpd_remove(&domain->genpd);
77462306a36Sopenharmony_ci		dev_pm_domain_detach(domain->power_dev, true);
77562306a36Sopenharmony_ci	}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	dev_pm_genpd_remove_notifier(bc->bus_power_dev);
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	dev_pm_domain_detach(bc->bus_power_dev, true);
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	return 0;
78262306a36Sopenharmony_ci}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
78562306a36Sopenharmony_cistatic int imx8mp_blk_ctrl_suspend(struct device *dev)
78662306a36Sopenharmony_ci{
78762306a36Sopenharmony_ci	struct imx8mp_blk_ctrl *bc = dev_get_drvdata(dev);
78862306a36Sopenharmony_ci	int ret, i;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	/*
79162306a36Sopenharmony_ci	 * This may look strange, but is done so the generic PM_SLEEP code
79262306a36Sopenharmony_ci	 * can power down our domains and more importantly power them up again
79362306a36Sopenharmony_ci	 * after resume, without tripping over our usage of runtime PM to
79462306a36Sopenharmony_ci	 * control the upstream GPC domains. Things happen in the right order
79562306a36Sopenharmony_ci	 * in the system suspend/resume paths due to the device parent/child
79662306a36Sopenharmony_ci	 * hierarchy.
79762306a36Sopenharmony_ci	 */
79862306a36Sopenharmony_ci	ret = pm_runtime_get_sync(bc->bus_power_dev);
79962306a36Sopenharmony_ci	if (ret < 0) {
80062306a36Sopenharmony_ci		pm_runtime_put_noidle(bc->bus_power_dev);
80162306a36Sopenharmony_ci		return ret;
80262306a36Sopenharmony_ci	}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	for (i = 0; i < bc->onecell_data.num_domains; i++) {
80562306a36Sopenharmony_ci		struct imx8mp_blk_ctrl_domain *domain = &bc->domains[i];
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci		ret = pm_runtime_get_sync(domain->power_dev);
80862306a36Sopenharmony_ci		if (ret < 0) {
80962306a36Sopenharmony_ci			pm_runtime_put_noidle(domain->power_dev);
81062306a36Sopenharmony_ci			goto out_fail;
81162306a36Sopenharmony_ci		}
81262306a36Sopenharmony_ci	}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	return 0;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ciout_fail:
81762306a36Sopenharmony_ci	for (i--; i >= 0; i--)
81862306a36Sopenharmony_ci		pm_runtime_put(bc->domains[i].power_dev);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	pm_runtime_put(bc->bus_power_dev);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	return ret;
82362306a36Sopenharmony_ci}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_cistatic int imx8mp_blk_ctrl_resume(struct device *dev)
82662306a36Sopenharmony_ci{
82762306a36Sopenharmony_ci	struct imx8mp_blk_ctrl *bc = dev_get_drvdata(dev);
82862306a36Sopenharmony_ci	int i;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	for (i = 0; i < bc->onecell_data.num_domains; i++)
83162306a36Sopenharmony_ci		pm_runtime_put(bc->domains[i].power_dev);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	pm_runtime_put(bc->bus_power_dev);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	return 0;
83662306a36Sopenharmony_ci}
83762306a36Sopenharmony_ci#endif
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_cistatic const struct dev_pm_ops imx8mp_blk_ctrl_pm_ops = {
84062306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(imx8mp_blk_ctrl_suspend,
84162306a36Sopenharmony_ci				imx8mp_blk_ctrl_resume)
84262306a36Sopenharmony_ci};
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_cistatic const struct of_device_id imx8mp_blk_ctrl_of_match[] = {
84562306a36Sopenharmony_ci	{
84662306a36Sopenharmony_ci		.compatible = "fsl,imx8mp-hsio-blk-ctrl",
84762306a36Sopenharmony_ci		.data = &imx8mp_hsio_blk_ctl_dev_data,
84862306a36Sopenharmony_ci	}, {
84962306a36Sopenharmony_ci		.compatible = "fsl,imx8mp-hdmi-blk-ctrl",
85062306a36Sopenharmony_ci		.data = &imx8mp_hdmi_blk_ctl_dev_data,
85162306a36Sopenharmony_ci	}, {
85262306a36Sopenharmony_ci		/* Sentinel */
85362306a36Sopenharmony_ci	}
85462306a36Sopenharmony_ci};
85562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, imx8mp_blk_ctrl_of_match);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_cistatic struct platform_driver imx8mp_blk_ctrl_driver = {
85862306a36Sopenharmony_ci	.probe = imx8mp_blk_ctrl_probe,
85962306a36Sopenharmony_ci	.remove = imx8mp_blk_ctrl_remove,
86062306a36Sopenharmony_ci	.driver = {
86162306a36Sopenharmony_ci		.name = "imx8mp-blk-ctrl",
86262306a36Sopenharmony_ci		.pm = &imx8mp_blk_ctrl_pm_ops,
86362306a36Sopenharmony_ci		.of_match_table = imx8mp_blk_ctrl_of_match,
86462306a36Sopenharmony_ci	},
86562306a36Sopenharmony_ci};
86662306a36Sopenharmony_cimodule_platform_driver(imx8mp_blk_ctrl_driver);
86762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
868