162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/*
462306a36Sopenharmony_ci * Copyright 2021 Pengutronix, Lucas Stach <kernel@pengutronix.de>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/bitfield.h>
862306a36Sopenharmony_ci#include <linux/device.h>
962306a36Sopenharmony_ci#include <linux/interconnect.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/of.h>
1262306a36Sopenharmony_ci#include <linux/of_platform.h>
1362306a36Sopenharmony_ci#include <linux/platform_device.h>
1462306a36Sopenharmony_ci#include <linux/pm_domain.h>
1562306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1662306a36Sopenharmony_ci#include <linux/regmap.h>
1762306a36Sopenharmony_ci#include <linux/clk.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <dt-bindings/power/imx8mm-power.h>
2062306a36Sopenharmony_ci#include <dt-bindings/power/imx8mn-power.h>
2162306a36Sopenharmony_ci#include <dt-bindings/power/imx8mp-power.h>
2262306a36Sopenharmony_ci#include <dt-bindings/power/imx8mq-power.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define BLK_SFT_RSTN	0x0
2562306a36Sopenharmony_ci#define BLK_CLK_EN	0x4
2662306a36Sopenharmony_ci#define BLK_MIPI_RESET_DIV	0x8 /* Mini/Nano/Plus DISPLAY_BLK_CTRL only */
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistruct imx8m_blk_ctrl_domain;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistruct imx8m_blk_ctrl {
3162306a36Sopenharmony_ci	struct device *dev;
3262306a36Sopenharmony_ci	struct notifier_block power_nb;
3362306a36Sopenharmony_ci	struct device *bus_power_dev;
3462306a36Sopenharmony_ci	struct regmap *regmap;
3562306a36Sopenharmony_ci	struct imx8m_blk_ctrl_domain *domains;
3662306a36Sopenharmony_ci	struct genpd_onecell_data onecell_data;
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistruct imx8m_blk_ctrl_domain_data {
4062306a36Sopenharmony_ci	const char *name;
4162306a36Sopenharmony_ci	const char * const *clk_names;
4262306a36Sopenharmony_ci	const char * const *path_names;
4362306a36Sopenharmony_ci	const char *gpc_name;
4462306a36Sopenharmony_ci	int num_clks;
4562306a36Sopenharmony_ci	int num_paths;
4662306a36Sopenharmony_ci	u32 rst_mask;
4762306a36Sopenharmony_ci	u32 clk_mask;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	/*
5062306a36Sopenharmony_ci	 * i.MX8M Mini, Nano and Plus have a third DISPLAY_BLK_CTRL register
5162306a36Sopenharmony_ci	 * which is used to control the reset for the MIPI Phy.
5262306a36Sopenharmony_ci	 * Since it's only present in certain circumstances,
5362306a36Sopenharmony_ci	 * an if-statement should be used before setting and clearing this
5462306a36Sopenharmony_ci	 * register.
5562306a36Sopenharmony_ci	 */
5662306a36Sopenharmony_ci	u32 mipi_phy_rst_mask;
5762306a36Sopenharmony_ci};
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define DOMAIN_MAX_CLKS 4
6062306a36Sopenharmony_ci#define DOMAIN_MAX_PATHS 4
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistruct imx8m_blk_ctrl_domain {
6362306a36Sopenharmony_ci	struct generic_pm_domain genpd;
6462306a36Sopenharmony_ci	const struct imx8m_blk_ctrl_domain_data *data;
6562306a36Sopenharmony_ci	struct clk_bulk_data clks[DOMAIN_MAX_CLKS];
6662306a36Sopenharmony_ci	struct icc_bulk_data paths[DOMAIN_MAX_PATHS];
6762306a36Sopenharmony_ci	struct device *power_dev;
6862306a36Sopenharmony_ci	struct imx8m_blk_ctrl *bc;
6962306a36Sopenharmony_ci	int num_paths;
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistruct imx8m_blk_ctrl_data {
7362306a36Sopenharmony_ci	int max_reg;
7462306a36Sopenharmony_ci	notifier_fn_t power_notifier_fn;
7562306a36Sopenharmony_ci	const struct imx8m_blk_ctrl_domain_data *domains;
7662306a36Sopenharmony_ci	int num_domains;
7762306a36Sopenharmony_ci};
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic inline struct imx8m_blk_ctrl_domain *
8062306a36Sopenharmony_cito_imx8m_blk_ctrl_domain(struct generic_pm_domain *genpd)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	return container_of(genpd, struct imx8m_blk_ctrl_domain, genpd);
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic int imx8m_blk_ctrl_power_on(struct generic_pm_domain *genpd)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct imx8m_blk_ctrl_domain *domain = to_imx8m_blk_ctrl_domain(genpd);
8862306a36Sopenharmony_ci	const struct imx8m_blk_ctrl_domain_data *data = domain->data;
8962306a36Sopenharmony_ci	struct imx8m_blk_ctrl *bc = domain->bc;
9062306a36Sopenharmony_ci	int ret;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	/* make sure bus domain is awake */
9362306a36Sopenharmony_ci	ret = pm_runtime_get_sync(bc->bus_power_dev);
9462306a36Sopenharmony_ci	if (ret < 0) {
9562306a36Sopenharmony_ci		pm_runtime_put_noidle(bc->bus_power_dev);
9662306a36Sopenharmony_ci		dev_err(bc->dev, "failed to power up bus domain\n");
9762306a36Sopenharmony_ci		return ret;
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	/* put devices into reset */
10162306a36Sopenharmony_ci	regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask);
10262306a36Sopenharmony_ci	if (data->mipi_phy_rst_mask)
10362306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, BLK_MIPI_RESET_DIV, data->mipi_phy_rst_mask);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	/* enable upstream and blk-ctrl clocks to allow reset to propagate */
10662306a36Sopenharmony_ci	ret = clk_bulk_prepare_enable(data->num_clks, domain->clks);
10762306a36Sopenharmony_ci	if (ret) {
10862306a36Sopenharmony_ci		dev_err(bc->dev, "failed to enable clocks\n");
10962306a36Sopenharmony_ci		goto bus_put;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci	regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	/* power up upstream GPC domain */
11462306a36Sopenharmony_ci	ret = pm_runtime_get_sync(domain->power_dev);
11562306a36Sopenharmony_ci	if (ret < 0) {
11662306a36Sopenharmony_ci		dev_err(bc->dev, "failed to power up peripheral domain\n");
11762306a36Sopenharmony_ci		goto clk_disable;
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/* wait for reset to propagate */
12162306a36Sopenharmony_ci	udelay(5);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	/* release reset */
12462306a36Sopenharmony_ci	regmap_set_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask);
12562306a36Sopenharmony_ci	if (data->mipi_phy_rst_mask)
12662306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, BLK_MIPI_RESET_DIV, data->mipi_phy_rst_mask);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	ret = icc_bulk_set_bw(domain->num_paths, domain->paths);
12962306a36Sopenharmony_ci	if (ret)
13062306a36Sopenharmony_ci		dev_err(bc->dev, "failed to set icc bw\n");
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	/* disable upstream clocks */
13362306a36Sopenharmony_ci	clk_bulk_disable_unprepare(data->num_clks, domain->clks);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	return 0;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ciclk_disable:
13862306a36Sopenharmony_ci	clk_bulk_disable_unprepare(data->num_clks, domain->clks);
13962306a36Sopenharmony_cibus_put:
14062306a36Sopenharmony_ci	pm_runtime_put(bc->bus_power_dev);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	return ret;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic int imx8m_blk_ctrl_power_off(struct generic_pm_domain *genpd)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	struct imx8m_blk_ctrl_domain *domain = to_imx8m_blk_ctrl_domain(genpd);
14862306a36Sopenharmony_ci	const struct imx8m_blk_ctrl_domain_data *data = domain->data;
14962306a36Sopenharmony_ci	struct imx8m_blk_ctrl *bc = domain->bc;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	/* put devices into reset and disable clocks */
15262306a36Sopenharmony_ci	if (data->mipi_phy_rst_mask)
15362306a36Sopenharmony_ci		regmap_clear_bits(bc->regmap, BLK_MIPI_RESET_DIV, data->mipi_phy_rst_mask);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask);
15662306a36Sopenharmony_ci	regmap_clear_bits(bc->regmap, BLK_CLK_EN, data->clk_mask);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	/* power down upstream GPC domain */
15962306a36Sopenharmony_ci	pm_runtime_put(domain->power_dev);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	/* allow bus domain to suspend */
16262306a36Sopenharmony_ci	pm_runtime_put(bc->bus_power_dev);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	return 0;
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic struct lock_class_key blk_ctrl_genpd_lock_class;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic int imx8m_blk_ctrl_probe(struct platform_device *pdev)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	const struct imx8m_blk_ctrl_data *bc_data;
17262306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
17362306a36Sopenharmony_ci	struct imx8m_blk_ctrl *bc;
17462306a36Sopenharmony_ci	void __iomem *base;
17562306a36Sopenharmony_ci	int i, ret;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	struct regmap_config regmap_config = {
17862306a36Sopenharmony_ci		.reg_bits	= 32,
17962306a36Sopenharmony_ci		.val_bits	= 32,
18062306a36Sopenharmony_ci		.reg_stride	= 4,
18162306a36Sopenharmony_ci	};
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL);
18462306a36Sopenharmony_ci	if (!bc)
18562306a36Sopenharmony_ci		return -ENOMEM;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	bc->dev = dev;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	bc_data = of_device_get_match_data(dev);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	base = devm_platform_ioremap_resource(pdev, 0);
19262306a36Sopenharmony_ci	if (IS_ERR(base))
19362306a36Sopenharmony_ci		return PTR_ERR(base);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	regmap_config.max_register = bc_data->max_reg;
19662306a36Sopenharmony_ci	bc->regmap = devm_regmap_init_mmio(dev, base, &regmap_config);
19762306a36Sopenharmony_ci	if (IS_ERR(bc->regmap))
19862306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(bc->regmap),
19962306a36Sopenharmony_ci				     "failed to init regmap\n");
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	bc->domains = devm_kcalloc(dev, bc_data->num_domains,
20262306a36Sopenharmony_ci				   sizeof(struct imx8m_blk_ctrl_domain),
20362306a36Sopenharmony_ci				   GFP_KERNEL);
20462306a36Sopenharmony_ci	if (!bc->domains)
20562306a36Sopenharmony_ci		return -ENOMEM;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	bc->onecell_data.num_domains = bc_data->num_domains;
20862306a36Sopenharmony_ci	bc->onecell_data.domains =
20962306a36Sopenharmony_ci		devm_kcalloc(dev, bc_data->num_domains,
21062306a36Sopenharmony_ci			     sizeof(struct generic_pm_domain *), GFP_KERNEL);
21162306a36Sopenharmony_ci	if (!bc->onecell_data.domains)
21262306a36Sopenharmony_ci		return -ENOMEM;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	bc->bus_power_dev = dev_pm_domain_attach_by_name(dev, "bus");
21562306a36Sopenharmony_ci	if (IS_ERR(bc->bus_power_dev)) {
21662306a36Sopenharmony_ci		if (PTR_ERR(bc->bus_power_dev) == -ENODEV)
21762306a36Sopenharmony_ci			return dev_err_probe(dev, -EPROBE_DEFER,
21862306a36Sopenharmony_ci					     "failed to attach power domain \"bus\"\n");
21962306a36Sopenharmony_ci		else
22062306a36Sopenharmony_ci			return dev_err_probe(dev, PTR_ERR(bc->bus_power_dev),
22162306a36Sopenharmony_ci					     "failed to attach power domain \"bus\"\n");
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	for (i = 0; i < bc_data->num_domains; i++) {
22562306a36Sopenharmony_ci		const struct imx8m_blk_ctrl_domain_data *data = &bc_data->domains[i];
22662306a36Sopenharmony_ci		struct imx8m_blk_ctrl_domain *domain = &bc->domains[i];
22762306a36Sopenharmony_ci		int j;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci		domain->data = data;
23062306a36Sopenharmony_ci		domain->num_paths = data->num_paths;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci		for (j = 0; j < data->num_clks; j++)
23362306a36Sopenharmony_ci			domain->clks[j].id = data->clk_names[j];
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci		for (j = 0; j < data->num_paths; j++) {
23662306a36Sopenharmony_ci			domain->paths[j].name = data->path_names[j];
23762306a36Sopenharmony_ci			/* Fake value for now, just let ICC could configure NoC mode/priority */
23862306a36Sopenharmony_ci			domain->paths[j].avg_bw = 1;
23962306a36Sopenharmony_ci			domain->paths[j].peak_bw = 1;
24062306a36Sopenharmony_ci		}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci		ret = devm_of_icc_bulk_get(dev, data->num_paths, domain->paths);
24362306a36Sopenharmony_ci		if (ret) {
24462306a36Sopenharmony_ci			if (ret != -EPROBE_DEFER) {
24562306a36Sopenharmony_ci				dev_warn_once(dev, "Could not get interconnect paths, NoC will stay unconfigured!\n");
24662306a36Sopenharmony_ci				domain->num_paths = 0;
24762306a36Sopenharmony_ci			} else {
24862306a36Sopenharmony_ci				dev_err_probe(dev, ret, "failed to get noc entries\n");
24962306a36Sopenharmony_ci				goto cleanup_pds;
25062306a36Sopenharmony_ci			}
25162306a36Sopenharmony_ci		}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci		ret = devm_clk_bulk_get(dev, data->num_clks, domain->clks);
25462306a36Sopenharmony_ci		if (ret) {
25562306a36Sopenharmony_ci			dev_err_probe(dev, ret, "failed to get clock\n");
25662306a36Sopenharmony_ci			goto cleanup_pds;
25762306a36Sopenharmony_ci		}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci		domain->power_dev =
26062306a36Sopenharmony_ci			dev_pm_domain_attach_by_name(dev, data->gpc_name);
26162306a36Sopenharmony_ci		if (IS_ERR(domain->power_dev)) {
26262306a36Sopenharmony_ci			dev_err_probe(dev, PTR_ERR(domain->power_dev),
26362306a36Sopenharmony_ci				      "failed to attach power domain \"%s\"\n",
26462306a36Sopenharmony_ci				      data->gpc_name);
26562306a36Sopenharmony_ci			ret = PTR_ERR(domain->power_dev);
26662306a36Sopenharmony_ci			goto cleanup_pds;
26762306a36Sopenharmony_ci		}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci		domain->genpd.name = data->name;
27062306a36Sopenharmony_ci		domain->genpd.power_on = imx8m_blk_ctrl_power_on;
27162306a36Sopenharmony_ci		domain->genpd.power_off = imx8m_blk_ctrl_power_off;
27262306a36Sopenharmony_ci		domain->bc = bc;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci		ret = pm_genpd_init(&domain->genpd, NULL, true);
27562306a36Sopenharmony_ci		if (ret) {
27662306a36Sopenharmony_ci			dev_err_probe(dev, ret,
27762306a36Sopenharmony_ci				      "failed to init power domain \"%s\"\n",
27862306a36Sopenharmony_ci				      data->gpc_name);
27962306a36Sopenharmony_ci			dev_pm_domain_detach(domain->power_dev, true);
28062306a36Sopenharmony_ci			goto cleanup_pds;
28162306a36Sopenharmony_ci		}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci		/*
28462306a36Sopenharmony_ci		 * We use runtime PM to trigger power on/off of the upstream GPC
28562306a36Sopenharmony_ci		 * domain, as a strict hierarchical parent/child power domain
28662306a36Sopenharmony_ci		 * setup doesn't allow us to meet the sequencing requirements.
28762306a36Sopenharmony_ci		 * This means we have nested locking of genpd locks, without the
28862306a36Sopenharmony_ci		 * nesting being visible at the genpd level, so we need a
28962306a36Sopenharmony_ci		 * separate lock class to make lockdep aware of the fact that
29062306a36Sopenharmony_ci		 * this are separate domain locks that can be nested without a
29162306a36Sopenharmony_ci		 * self-deadlock.
29262306a36Sopenharmony_ci		 */
29362306a36Sopenharmony_ci		lockdep_set_class(&domain->genpd.mlock,
29462306a36Sopenharmony_ci				  &blk_ctrl_genpd_lock_class);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci		bc->onecell_data.domains[i] = &domain->genpd;
29762306a36Sopenharmony_ci	}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	ret = of_genpd_add_provider_onecell(dev->of_node, &bc->onecell_data);
30062306a36Sopenharmony_ci	if (ret) {
30162306a36Sopenharmony_ci		dev_err_probe(dev, ret, "failed to add power domain provider\n");
30262306a36Sopenharmony_ci		goto cleanup_pds;
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	bc->power_nb.notifier_call = bc_data->power_notifier_fn;
30662306a36Sopenharmony_ci	ret = dev_pm_genpd_add_notifier(bc->bus_power_dev, &bc->power_nb);
30762306a36Sopenharmony_ci	if (ret) {
30862306a36Sopenharmony_ci		dev_err_probe(dev, ret, "failed to add power notifier\n");
30962306a36Sopenharmony_ci		goto cleanup_provider;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	dev_set_drvdata(dev, bc);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	ret = devm_of_platform_populate(dev);
31562306a36Sopenharmony_ci	if (ret)
31662306a36Sopenharmony_ci		goto cleanup_provider;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	return 0;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cicleanup_provider:
32162306a36Sopenharmony_ci	of_genpd_del_provider(dev->of_node);
32262306a36Sopenharmony_cicleanup_pds:
32362306a36Sopenharmony_ci	for (i--; i >= 0; i--) {
32462306a36Sopenharmony_ci		pm_genpd_remove(&bc->domains[i].genpd);
32562306a36Sopenharmony_ci		dev_pm_domain_detach(bc->domains[i].power_dev, true);
32662306a36Sopenharmony_ci	}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	dev_pm_domain_detach(bc->bus_power_dev, true);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	return ret;
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic int imx8m_blk_ctrl_remove(struct platform_device *pdev)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	struct imx8m_blk_ctrl *bc = dev_get_drvdata(&pdev->dev);
33662306a36Sopenharmony_ci	int i;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	of_genpd_del_provider(pdev->dev.of_node);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	for (i = 0; bc->onecell_data.num_domains; i++) {
34162306a36Sopenharmony_ci		struct imx8m_blk_ctrl_domain *domain = &bc->domains[i];
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci		pm_genpd_remove(&domain->genpd);
34462306a36Sopenharmony_ci		dev_pm_domain_detach(domain->power_dev, true);
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	dev_pm_genpd_remove_notifier(bc->bus_power_dev);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	dev_pm_domain_detach(bc->bus_power_dev, true);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	return 0;
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
35562306a36Sopenharmony_cistatic int imx8m_blk_ctrl_suspend(struct device *dev)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	struct imx8m_blk_ctrl *bc = dev_get_drvdata(dev);
35862306a36Sopenharmony_ci	int ret, i;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	/*
36162306a36Sopenharmony_ci	 * This may look strange, but is done so the generic PM_SLEEP code
36262306a36Sopenharmony_ci	 * can power down our domains and more importantly power them up again
36362306a36Sopenharmony_ci	 * after resume, without tripping over our usage of runtime PM to
36462306a36Sopenharmony_ci	 * control the upstream GPC domains. Things happen in the right order
36562306a36Sopenharmony_ci	 * in the system suspend/resume paths due to the device parent/child
36662306a36Sopenharmony_ci	 * hierarchy.
36762306a36Sopenharmony_ci	 */
36862306a36Sopenharmony_ci	ret = pm_runtime_get_sync(bc->bus_power_dev);
36962306a36Sopenharmony_ci	if (ret < 0) {
37062306a36Sopenharmony_ci		pm_runtime_put_noidle(bc->bus_power_dev);
37162306a36Sopenharmony_ci		return ret;
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	for (i = 0; i < bc->onecell_data.num_domains; i++) {
37562306a36Sopenharmony_ci		struct imx8m_blk_ctrl_domain *domain = &bc->domains[i];
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci		ret = pm_runtime_get_sync(domain->power_dev);
37862306a36Sopenharmony_ci		if (ret < 0) {
37962306a36Sopenharmony_ci			pm_runtime_put_noidle(domain->power_dev);
38062306a36Sopenharmony_ci			goto out_fail;
38162306a36Sopenharmony_ci		}
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	return 0;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ciout_fail:
38762306a36Sopenharmony_ci	for (i--; i >= 0; i--)
38862306a36Sopenharmony_ci		pm_runtime_put(bc->domains[i].power_dev);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	pm_runtime_put(bc->bus_power_dev);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	return ret;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic int imx8m_blk_ctrl_resume(struct device *dev)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	struct imx8m_blk_ctrl *bc = dev_get_drvdata(dev);
39862306a36Sopenharmony_ci	int i;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	for (i = 0; i < bc->onecell_data.num_domains; i++)
40162306a36Sopenharmony_ci		pm_runtime_put(bc->domains[i].power_dev);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	pm_runtime_put(bc->bus_power_dev);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	return 0;
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci#endif
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic const struct dev_pm_ops imx8m_blk_ctrl_pm_ops = {
41062306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(imx8m_blk_ctrl_suspend, imx8m_blk_ctrl_resume)
41162306a36Sopenharmony_ci};
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic int imx8mm_vpu_power_notifier(struct notifier_block *nb,
41462306a36Sopenharmony_ci				     unsigned long action, void *data)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl,
41762306a36Sopenharmony_ci						 power_nb);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF)
42062306a36Sopenharmony_ci		return NOTIFY_OK;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	/*
42362306a36Sopenharmony_ci	 * The ADB in the VPUMIX domain has no separate reset and clock
42462306a36Sopenharmony_ci	 * enable bits, but is ungated together with the VPU clocks. To
42562306a36Sopenharmony_ci	 * allow the handshake with the GPC to progress we put the VPUs
42662306a36Sopenharmony_ci	 * in reset and ungate the clocks.
42762306a36Sopenharmony_ci	 */
42862306a36Sopenharmony_ci	regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, BIT(0) | BIT(1) | BIT(2));
42962306a36Sopenharmony_ci	regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(0) | BIT(1) | BIT(2));
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	if (action == GENPD_NOTIFY_ON) {
43262306a36Sopenharmony_ci		/*
43362306a36Sopenharmony_ci		 * On power up we have no software backchannel to the GPC to
43462306a36Sopenharmony_ci		 * wait for the ADB handshake to happen, so we just delay for a
43562306a36Sopenharmony_ci		 * bit. On power down the GPC driver waits for the handshake.
43662306a36Sopenharmony_ci		 */
43762306a36Sopenharmony_ci		udelay(5);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci		/* set "fuse" bits to enable the VPUs */
44062306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, 0x8, 0xffffffff);
44162306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, 0xc, 0xffffffff);
44262306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, 0x10, 0xffffffff);
44362306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, 0x14, 0xffffffff);
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	return NOTIFY_OK;
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_cistatic const struct imx8m_blk_ctrl_domain_data imx8mm_vpu_blk_ctl_domain_data[] = {
45062306a36Sopenharmony_ci	[IMX8MM_VPUBLK_PD_G1] = {
45162306a36Sopenharmony_ci		.name = "vpublk-g1",
45262306a36Sopenharmony_ci		.clk_names = (const char *[]){ "g1", },
45362306a36Sopenharmony_ci		.num_clks = 1,
45462306a36Sopenharmony_ci		.gpc_name = "g1",
45562306a36Sopenharmony_ci		.rst_mask = BIT(1),
45662306a36Sopenharmony_ci		.clk_mask = BIT(1),
45762306a36Sopenharmony_ci	},
45862306a36Sopenharmony_ci	[IMX8MM_VPUBLK_PD_G2] = {
45962306a36Sopenharmony_ci		.name = "vpublk-g2",
46062306a36Sopenharmony_ci		.clk_names = (const char *[]){ "g2", },
46162306a36Sopenharmony_ci		.num_clks = 1,
46262306a36Sopenharmony_ci		.gpc_name = "g2",
46362306a36Sopenharmony_ci		.rst_mask = BIT(0),
46462306a36Sopenharmony_ci		.clk_mask = BIT(0),
46562306a36Sopenharmony_ci	},
46662306a36Sopenharmony_ci	[IMX8MM_VPUBLK_PD_H1] = {
46762306a36Sopenharmony_ci		.name = "vpublk-h1",
46862306a36Sopenharmony_ci		.clk_names = (const char *[]){ "h1", },
46962306a36Sopenharmony_ci		.num_clks = 1,
47062306a36Sopenharmony_ci		.gpc_name = "h1",
47162306a36Sopenharmony_ci		.rst_mask = BIT(2),
47262306a36Sopenharmony_ci		.clk_mask = BIT(2),
47362306a36Sopenharmony_ci	},
47462306a36Sopenharmony_ci};
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic const struct imx8m_blk_ctrl_data imx8mm_vpu_blk_ctl_dev_data = {
47762306a36Sopenharmony_ci	.max_reg = 0x18,
47862306a36Sopenharmony_ci	.power_notifier_fn = imx8mm_vpu_power_notifier,
47962306a36Sopenharmony_ci	.domains = imx8mm_vpu_blk_ctl_domain_data,
48062306a36Sopenharmony_ci	.num_domains = ARRAY_SIZE(imx8mm_vpu_blk_ctl_domain_data),
48162306a36Sopenharmony_ci};
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic const struct imx8m_blk_ctrl_domain_data imx8mp_vpu_blk_ctl_domain_data[] = {
48462306a36Sopenharmony_ci	[IMX8MP_VPUBLK_PD_G1] = {
48562306a36Sopenharmony_ci		.name = "vpublk-g1",
48662306a36Sopenharmony_ci		.clk_names = (const char *[]){ "g1", },
48762306a36Sopenharmony_ci		.num_clks = 1,
48862306a36Sopenharmony_ci		.gpc_name = "g1",
48962306a36Sopenharmony_ci		.rst_mask = BIT(1),
49062306a36Sopenharmony_ci		.clk_mask = BIT(1),
49162306a36Sopenharmony_ci		.path_names = (const char *[]){"g1"},
49262306a36Sopenharmony_ci		.num_paths = 1,
49362306a36Sopenharmony_ci	},
49462306a36Sopenharmony_ci	[IMX8MP_VPUBLK_PD_G2] = {
49562306a36Sopenharmony_ci		.name = "vpublk-g2",
49662306a36Sopenharmony_ci		.clk_names = (const char *[]){ "g2", },
49762306a36Sopenharmony_ci		.num_clks = 1,
49862306a36Sopenharmony_ci		.gpc_name = "g2",
49962306a36Sopenharmony_ci		.rst_mask = BIT(0),
50062306a36Sopenharmony_ci		.clk_mask = BIT(0),
50162306a36Sopenharmony_ci		.path_names = (const char *[]){"g2"},
50262306a36Sopenharmony_ci		.num_paths = 1,
50362306a36Sopenharmony_ci	},
50462306a36Sopenharmony_ci	[IMX8MP_VPUBLK_PD_VC8000E] = {
50562306a36Sopenharmony_ci		.name = "vpublk-vc8000e",
50662306a36Sopenharmony_ci		.clk_names = (const char *[]){ "vc8000e", },
50762306a36Sopenharmony_ci		.num_clks = 1,
50862306a36Sopenharmony_ci		.gpc_name = "vc8000e",
50962306a36Sopenharmony_ci		.rst_mask = BIT(2),
51062306a36Sopenharmony_ci		.clk_mask = BIT(2),
51162306a36Sopenharmony_ci		.path_names = (const char *[]){"vc8000e"},
51262306a36Sopenharmony_ci		.num_paths = 1,
51362306a36Sopenharmony_ci	},
51462306a36Sopenharmony_ci};
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cistatic const struct imx8m_blk_ctrl_data imx8mp_vpu_blk_ctl_dev_data = {
51762306a36Sopenharmony_ci	.max_reg = 0x18,
51862306a36Sopenharmony_ci	.power_notifier_fn = imx8mm_vpu_power_notifier,
51962306a36Sopenharmony_ci	.domains = imx8mp_vpu_blk_ctl_domain_data,
52062306a36Sopenharmony_ci	.num_domains = ARRAY_SIZE(imx8mp_vpu_blk_ctl_domain_data),
52162306a36Sopenharmony_ci};
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_cistatic int imx8mm_disp_power_notifier(struct notifier_block *nb,
52462306a36Sopenharmony_ci				      unsigned long action, void *data)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl,
52762306a36Sopenharmony_ci						 power_nb);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF)
53062306a36Sopenharmony_ci		return NOTIFY_OK;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	/* Enable bus clock and deassert bus reset */
53362306a36Sopenharmony_ci	regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(12));
53462306a36Sopenharmony_ci	regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(6));
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	/*
53762306a36Sopenharmony_ci	 * On power up we have no software backchannel to the GPC to
53862306a36Sopenharmony_ci	 * wait for the ADB handshake to happen, so we just delay for a
53962306a36Sopenharmony_ci	 * bit. On power down the GPC driver waits for the handshake.
54062306a36Sopenharmony_ci	 */
54162306a36Sopenharmony_ci	if (action == GENPD_NOTIFY_ON)
54262306a36Sopenharmony_ci		udelay(5);
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	return NOTIFY_OK;
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_cistatic const struct imx8m_blk_ctrl_domain_data imx8mm_disp_blk_ctl_domain_data[] = {
54962306a36Sopenharmony_ci	[IMX8MM_DISPBLK_PD_CSI_BRIDGE] = {
55062306a36Sopenharmony_ci		.name = "dispblk-csi-bridge",
55162306a36Sopenharmony_ci		.clk_names = (const char *[]){ "csi-bridge-axi", "csi-bridge-apb",
55262306a36Sopenharmony_ci					       "csi-bridge-core", },
55362306a36Sopenharmony_ci		.num_clks = 3,
55462306a36Sopenharmony_ci		.gpc_name = "csi-bridge",
55562306a36Sopenharmony_ci		.rst_mask = BIT(0) | BIT(1) | BIT(2),
55662306a36Sopenharmony_ci		.clk_mask = BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5),
55762306a36Sopenharmony_ci	},
55862306a36Sopenharmony_ci	[IMX8MM_DISPBLK_PD_LCDIF] = {
55962306a36Sopenharmony_ci		.name = "dispblk-lcdif",
56062306a36Sopenharmony_ci		.clk_names = (const char *[]){ "lcdif-axi", "lcdif-apb", "lcdif-pix", },
56162306a36Sopenharmony_ci		.num_clks = 3,
56262306a36Sopenharmony_ci		.gpc_name = "lcdif",
56362306a36Sopenharmony_ci		.clk_mask = BIT(6) | BIT(7),
56462306a36Sopenharmony_ci	},
56562306a36Sopenharmony_ci	[IMX8MM_DISPBLK_PD_MIPI_DSI] = {
56662306a36Sopenharmony_ci		.name = "dispblk-mipi-dsi",
56762306a36Sopenharmony_ci		.clk_names = (const char *[]){ "dsi-pclk", "dsi-ref", },
56862306a36Sopenharmony_ci		.num_clks = 2,
56962306a36Sopenharmony_ci		.gpc_name = "mipi-dsi",
57062306a36Sopenharmony_ci		.rst_mask = BIT(5),
57162306a36Sopenharmony_ci		.clk_mask = BIT(8) | BIT(9),
57262306a36Sopenharmony_ci		.mipi_phy_rst_mask = BIT(17),
57362306a36Sopenharmony_ci	},
57462306a36Sopenharmony_ci	[IMX8MM_DISPBLK_PD_MIPI_CSI] = {
57562306a36Sopenharmony_ci		.name = "dispblk-mipi-csi",
57662306a36Sopenharmony_ci		.clk_names = (const char *[]){ "csi-aclk", "csi-pclk" },
57762306a36Sopenharmony_ci		.num_clks = 2,
57862306a36Sopenharmony_ci		.gpc_name = "mipi-csi",
57962306a36Sopenharmony_ci		.rst_mask = BIT(3) | BIT(4),
58062306a36Sopenharmony_ci		.clk_mask = BIT(10) | BIT(11),
58162306a36Sopenharmony_ci		.mipi_phy_rst_mask = BIT(16),
58262306a36Sopenharmony_ci	},
58362306a36Sopenharmony_ci};
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_cistatic const struct imx8m_blk_ctrl_data imx8mm_disp_blk_ctl_dev_data = {
58662306a36Sopenharmony_ci	.max_reg = 0x2c,
58762306a36Sopenharmony_ci	.power_notifier_fn = imx8mm_disp_power_notifier,
58862306a36Sopenharmony_ci	.domains = imx8mm_disp_blk_ctl_domain_data,
58962306a36Sopenharmony_ci	.num_domains = ARRAY_SIZE(imx8mm_disp_blk_ctl_domain_data),
59062306a36Sopenharmony_ci};
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_cistatic int imx8mn_disp_power_notifier(struct notifier_block *nb,
59462306a36Sopenharmony_ci				      unsigned long action, void *data)
59562306a36Sopenharmony_ci{
59662306a36Sopenharmony_ci	struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl,
59762306a36Sopenharmony_ci						 power_nb);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF)
60062306a36Sopenharmony_ci		return NOTIFY_OK;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	/* Enable bus clock and deassert bus reset */
60362306a36Sopenharmony_ci	regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(8));
60462306a36Sopenharmony_ci	regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(8));
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	/*
60762306a36Sopenharmony_ci	 * On power up we have no software backchannel to the GPC to
60862306a36Sopenharmony_ci	 * wait for the ADB handshake to happen, so we just delay for a
60962306a36Sopenharmony_ci	 * bit. On power down the GPC driver waits for the handshake.
61062306a36Sopenharmony_ci	 */
61162306a36Sopenharmony_ci	if (action == GENPD_NOTIFY_ON)
61262306a36Sopenharmony_ci		udelay(5);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	return NOTIFY_OK;
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_cistatic const struct imx8m_blk_ctrl_domain_data imx8mn_disp_blk_ctl_domain_data[] = {
61962306a36Sopenharmony_ci	[IMX8MN_DISPBLK_PD_MIPI_DSI] = {
62062306a36Sopenharmony_ci		.name = "dispblk-mipi-dsi",
62162306a36Sopenharmony_ci		.clk_names = (const char *[]){ "dsi-pclk", "dsi-ref", },
62262306a36Sopenharmony_ci		.num_clks = 2,
62362306a36Sopenharmony_ci		.gpc_name = "mipi-dsi",
62462306a36Sopenharmony_ci		.rst_mask = BIT(0) | BIT(1),
62562306a36Sopenharmony_ci		.clk_mask = BIT(0) | BIT(1),
62662306a36Sopenharmony_ci		.mipi_phy_rst_mask = BIT(17),
62762306a36Sopenharmony_ci	},
62862306a36Sopenharmony_ci	[IMX8MN_DISPBLK_PD_MIPI_CSI] = {
62962306a36Sopenharmony_ci		.name = "dispblk-mipi-csi",
63062306a36Sopenharmony_ci		.clk_names = (const char *[]){ "csi-aclk", "csi-pclk" },
63162306a36Sopenharmony_ci		.num_clks = 2,
63262306a36Sopenharmony_ci		.gpc_name = "mipi-csi",
63362306a36Sopenharmony_ci		.rst_mask = BIT(2) | BIT(3),
63462306a36Sopenharmony_ci		.clk_mask = BIT(2) | BIT(3),
63562306a36Sopenharmony_ci		.mipi_phy_rst_mask = BIT(16),
63662306a36Sopenharmony_ci	},
63762306a36Sopenharmony_ci	[IMX8MN_DISPBLK_PD_LCDIF] = {
63862306a36Sopenharmony_ci		.name = "dispblk-lcdif",
63962306a36Sopenharmony_ci		.clk_names = (const char *[]){ "lcdif-axi", "lcdif-apb", "lcdif-pix", },
64062306a36Sopenharmony_ci		.num_clks = 3,
64162306a36Sopenharmony_ci		.gpc_name = "lcdif",
64262306a36Sopenharmony_ci		.rst_mask = BIT(4) | BIT(5),
64362306a36Sopenharmony_ci		.clk_mask = BIT(4) | BIT(5),
64462306a36Sopenharmony_ci	},
64562306a36Sopenharmony_ci	[IMX8MN_DISPBLK_PD_ISI] = {
64662306a36Sopenharmony_ci		.name = "dispblk-isi",
64762306a36Sopenharmony_ci		.clk_names = (const char *[]){ "disp_axi", "disp_apb", "disp_axi_root",
64862306a36Sopenharmony_ci						"disp_apb_root"},
64962306a36Sopenharmony_ci		.num_clks = 4,
65062306a36Sopenharmony_ci		.gpc_name = "isi",
65162306a36Sopenharmony_ci		.rst_mask = BIT(6) | BIT(7),
65262306a36Sopenharmony_ci		.clk_mask = BIT(6) | BIT(7),
65362306a36Sopenharmony_ci	},
65462306a36Sopenharmony_ci};
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_cistatic const struct imx8m_blk_ctrl_data imx8mn_disp_blk_ctl_dev_data = {
65762306a36Sopenharmony_ci	.max_reg = 0x84,
65862306a36Sopenharmony_ci	.power_notifier_fn = imx8mn_disp_power_notifier,
65962306a36Sopenharmony_ci	.domains = imx8mn_disp_blk_ctl_domain_data,
66062306a36Sopenharmony_ci	.num_domains = ARRAY_SIZE(imx8mn_disp_blk_ctl_domain_data),
66162306a36Sopenharmony_ci};
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci#define LCDIF_ARCACHE_CTRL	0x4c
66462306a36Sopenharmony_ci#define  LCDIF_1_RD_HURRY	GENMASK(15, 13)
66562306a36Sopenharmony_ci#define  LCDIF_0_RD_HURRY	GENMASK(12, 10)
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_cistatic int imx8mp_media_power_notifier(struct notifier_block *nb,
66862306a36Sopenharmony_ci				unsigned long action, void *data)
66962306a36Sopenharmony_ci{
67062306a36Sopenharmony_ci	struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl,
67162306a36Sopenharmony_ci						 power_nb);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF)
67462306a36Sopenharmony_ci		return NOTIFY_OK;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	/* Enable bus clock and deassert bus reset */
67762306a36Sopenharmony_ci	regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(8));
67862306a36Sopenharmony_ci	regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(8));
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	if (action == GENPD_NOTIFY_ON) {
68162306a36Sopenharmony_ci		/*
68262306a36Sopenharmony_ci		 * On power up we have no software backchannel to the GPC to
68362306a36Sopenharmony_ci		 * wait for the ADB handshake to happen, so we just delay for a
68462306a36Sopenharmony_ci		 * bit. On power down the GPC driver waits for the handshake.
68562306a36Sopenharmony_ci		 */
68662306a36Sopenharmony_ci		udelay(5);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci		/*
68962306a36Sopenharmony_ci		 * Set panic read hurry level for both LCDIF interfaces to
69062306a36Sopenharmony_ci		 * maximum priority to minimize chances of display FIFO
69162306a36Sopenharmony_ci		 * underflow.
69262306a36Sopenharmony_ci		 */
69362306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, LCDIF_ARCACHE_CTRL,
69462306a36Sopenharmony_ci				FIELD_PREP(LCDIF_1_RD_HURRY, 7) |
69562306a36Sopenharmony_ci				FIELD_PREP(LCDIF_0_RD_HURRY, 7));
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	return NOTIFY_OK;
69962306a36Sopenharmony_ci}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci/*
70262306a36Sopenharmony_ci * From i.MX 8M Plus Applications Processor Reference Manual, Rev. 1,
70362306a36Sopenharmony_ci * section 13.2.2, 13.2.3
70462306a36Sopenharmony_ci * isp-ahb and dwe are not in Figure 13-5. Media BLK_CTRL Clocks
70562306a36Sopenharmony_ci */
70662306a36Sopenharmony_cistatic const struct imx8m_blk_ctrl_domain_data imx8mp_media_blk_ctl_domain_data[] = {
70762306a36Sopenharmony_ci	[IMX8MP_MEDIABLK_PD_MIPI_DSI_1] = {
70862306a36Sopenharmony_ci		.name = "mediablk-mipi-dsi-1",
70962306a36Sopenharmony_ci		.clk_names = (const char *[]){ "apb", "phy", },
71062306a36Sopenharmony_ci		.num_clks = 2,
71162306a36Sopenharmony_ci		.gpc_name = "mipi-dsi1",
71262306a36Sopenharmony_ci		.rst_mask = BIT(0) | BIT(1),
71362306a36Sopenharmony_ci		.clk_mask = BIT(0) | BIT(1),
71462306a36Sopenharmony_ci		.mipi_phy_rst_mask = BIT(17),
71562306a36Sopenharmony_ci	},
71662306a36Sopenharmony_ci	[IMX8MP_MEDIABLK_PD_MIPI_CSI2_1] = {
71762306a36Sopenharmony_ci		.name = "mediablk-mipi-csi2-1",
71862306a36Sopenharmony_ci		.clk_names = (const char *[]){ "apb", "cam1" },
71962306a36Sopenharmony_ci		.num_clks = 2,
72062306a36Sopenharmony_ci		.gpc_name = "mipi-csi1",
72162306a36Sopenharmony_ci		.rst_mask = BIT(2) | BIT(3),
72262306a36Sopenharmony_ci		.clk_mask = BIT(2) | BIT(3),
72362306a36Sopenharmony_ci		.mipi_phy_rst_mask = BIT(16),
72462306a36Sopenharmony_ci	},
72562306a36Sopenharmony_ci	[IMX8MP_MEDIABLK_PD_LCDIF_1] = {
72662306a36Sopenharmony_ci		.name = "mediablk-lcdif-1",
72762306a36Sopenharmony_ci		.clk_names = (const char *[]){ "disp1", "apb", "axi", },
72862306a36Sopenharmony_ci		.num_clks = 3,
72962306a36Sopenharmony_ci		.gpc_name = "lcdif1",
73062306a36Sopenharmony_ci		.rst_mask = BIT(4) | BIT(5) | BIT(23),
73162306a36Sopenharmony_ci		.clk_mask = BIT(4) | BIT(5) | BIT(23),
73262306a36Sopenharmony_ci		.path_names = (const char *[]){"lcdif-rd", "lcdif-wr"},
73362306a36Sopenharmony_ci		.num_paths = 2,
73462306a36Sopenharmony_ci	},
73562306a36Sopenharmony_ci	[IMX8MP_MEDIABLK_PD_ISI] = {
73662306a36Sopenharmony_ci		.name = "mediablk-isi",
73762306a36Sopenharmony_ci		.clk_names = (const char *[]){ "axi", "apb" },
73862306a36Sopenharmony_ci		.num_clks = 2,
73962306a36Sopenharmony_ci		.gpc_name = "isi",
74062306a36Sopenharmony_ci		.rst_mask = BIT(6) | BIT(7),
74162306a36Sopenharmony_ci		.clk_mask = BIT(6) | BIT(7),
74262306a36Sopenharmony_ci		.path_names = (const char *[]){"isi0", "isi1", "isi2"},
74362306a36Sopenharmony_ci		.num_paths = 3,
74462306a36Sopenharmony_ci	},
74562306a36Sopenharmony_ci	[IMX8MP_MEDIABLK_PD_MIPI_CSI2_2] = {
74662306a36Sopenharmony_ci		.name = "mediablk-mipi-csi2-2",
74762306a36Sopenharmony_ci		.clk_names = (const char *[]){ "apb", "cam2" },
74862306a36Sopenharmony_ci		.num_clks = 2,
74962306a36Sopenharmony_ci		.gpc_name = "mipi-csi2",
75062306a36Sopenharmony_ci		.rst_mask = BIT(9) | BIT(10),
75162306a36Sopenharmony_ci		.clk_mask = BIT(9) | BIT(10),
75262306a36Sopenharmony_ci		.mipi_phy_rst_mask = BIT(30),
75362306a36Sopenharmony_ci	},
75462306a36Sopenharmony_ci	[IMX8MP_MEDIABLK_PD_LCDIF_2] = {
75562306a36Sopenharmony_ci		.name = "mediablk-lcdif-2",
75662306a36Sopenharmony_ci		.clk_names = (const char *[]){ "disp2", "apb", "axi", },
75762306a36Sopenharmony_ci		.num_clks = 3,
75862306a36Sopenharmony_ci		.gpc_name = "lcdif2",
75962306a36Sopenharmony_ci		.rst_mask = BIT(11) | BIT(12) | BIT(24),
76062306a36Sopenharmony_ci		.clk_mask = BIT(11) | BIT(12) | BIT(24),
76162306a36Sopenharmony_ci		.path_names = (const char *[]){"lcdif-rd", "lcdif-wr"},
76262306a36Sopenharmony_ci		.num_paths = 2,
76362306a36Sopenharmony_ci	},
76462306a36Sopenharmony_ci	[IMX8MP_MEDIABLK_PD_ISP] = {
76562306a36Sopenharmony_ci		.name = "mediablk-isp",
76662306a36Sopenharmony_ci		.clk_names = (const char *[]){ "isp", "axi", "apb" },
76762306a36Sopenharmony_ci		.num_clks = 3,
76862306a36Sopenharmony_ci		.gpc_name = "isp",
76962306a36Sopenharmony_ci		.rst_mask = BIT(16) | BIT(17) | BIT(18),
77062306a36Sopenharmony_ci		.clk_mask = BIT(16) | BIT(17) | BIT(18),
77162306a36Sopenharmony_ci		.path_names = (const char *[]){"isp0", "isp1"},
77262306a36Sopenharmony_ci		.num_paths = 2,
77362306a36Sopenharmony_ci	},
77462306a36Sopenharmony_ci	[IMX8MP_MEDIABLK_PD_DWE] = {
77562306a36Sopenharmony_ci		.name = "mediablk-dwe",
77662306a36Sopenharmony_ci		.clk_names = (const char *[]){ "axi", "apb" },
77762306a36Sopenharmony_ci		.num_clks = 2,
77862306a36Sopenharmony_ci		.gpc_name = "dwe",
77962306a36Sopenharmony_ci		.rst_mask = BIT(19) | BIT(20) | BIT(21),
78062306a36Sopenharmony_ci		.clk_mask = BIT(19) | BIT(20) | BIT(21),
78162306a36Sopenharmony_ci		.path_names = (const char *[]){"dwe"},
78262306a36Sopenharmony_ci		.num_paths = 1,
78362306a36Sopenharmony_ci	},
78462306a36Sopenharmony_ci	[IMX8MP_MEDIABLK_PD_MIPI_DSI_2] = {
78562306a36Sopenharmony_ci		.name = "mediablk-mipi-dsi-2",
78662306a36Sopenharmony_ci		.clk_names = (const char *[]){ "phy", },
78762306a36Sopenharmony_ci		.num_clks = 1,
78862306a36Sopenharmony_ci		.gpc_name = "mipi-dsi2",
78962306a36Sopenharmony_ci		.rst_mask = BIT(22),
79062306a36Sopenharmony_ci		.clk_mask = BIT(22),
79162306a36Sopenharmony_ci		.mipi_phy_rst_mask = BIT(29),
79262306a36Sopenharmony_ci	},
79362306a36Sopenharmony_ci};
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_cistatic const struct imx8m_blk_ctrl_data imx8mp_media_blk_ctl_dev_data = {
79662306a36Sopenharmony_ci	.max_reg = 0x138,
79762306a36Sopenharmony_ci	.power_notifier_fn = imx8mp_media_power_notifier,
79862306a36Sopenharmony_ci	.domains = imx8mp_media_blk_ctl_domain_data,
79962306a36Sopenharmony_ci	.num_domains = ARRAY_SIZE(imx8mp_media_blk_ctl_domain_data),
80062306a36Sopenharmony_ci};
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_cistatic int imx8mq_vpu_power_notifier(struct notifier_block *nb,
80362306a36Sopenharmony_ci				     unsigned long action, void *data)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl,
80662306a36Sopenharmony_ci						 power_nb);
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF)
80962306a36Sopenharmony_ci		return NOTIFY_OK;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	/*
81262306a36Sopenharmony_ci	 * The ADB in the VPUMIX domain has no separate reset and clock
81362306a36Sopenharmony_ci	 * enable bits, but is ungated and reset together with the VPUs. The
81462306a36Sopenharmony_ci	 * reset and clock enable inputs to the ADB is a logical OR of the
81562306a36Sopenharmony_ci	 * VPU bits. In order to set the G2 fuse bits, the G2 clock must
81662306a36Sopenharmony_ci	 * also be enabled.
81762306a36Sopenharmony_ci	 */
81862306a36Sopenharmony_ci	regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(0) | BIT(1));
81962306a36Sopenharmony_ci	regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(0) | BIT(1));
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	if (action == GENPD_NOTIFY_ON) {
82262306a36Sopenharmony_ci		/*
82362306a36Sopenharmony_ci		 * On power up we have no software backchannel to the GPC to
82462306a36Sopenharmony_ci		 * wait for the ADB handshake to happen, so we just delay for a
82562306a36Sopenharmony_ci		 * bit. On power down the GPC driver waits for the handshake.
82662306a36Sopenharmony_ci		 */
82762306a36Sopenharmony_ci		udelay(5);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci		/* set "fuse" bits to enable the VPUs */
83062306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, 0x8, 0xffffffff);
83162306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, 0xc, 0xffffffff);
83262306a36Sopenharmony_ci		regmap_set_bits(bc->regmap, 0x10, 0xffffffff);
83362306a36Sopenharmony_ci	}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	return NOTIFY_OK;
83662306a36Sopenharmony_ci}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_cistatic const struct imx8m_blk_ctrl_domain_data imx8mq_vpu_blk_ctl_domain_data[] = {
83962306a36Sopenharmony_ci	[IMX8MQ_VPUBLK_PD_G1] = {
84062306a36Sopenharmony_ci		.name = "vpublk-g1",
84162306a36Sopenharmony_ci		.clk_names = (const char *[]){ "g1", },
84262306a36Sopenharmony_ci		.num_clks = 1,
84362306a36Sopenharmony_ci		.gpc_name = "g1",
84462306a36Sopenharmony_ci		.rst_mask = BIT(1),
84562306a36Sopenharmony_ci		.clk_mask = BIT(1),
84662306a36Sopenharmony_ci	},
84762306a36Sopenharmony_ci	[IMX8MQ_VPUBLK_PD_G2] = {
84862306a36Sopenharmony_ci		.name = "vpublk-g2",
84962306a36Sopenharmony_ci		.clk_names = (const char *[]){ "g2", },
85062306a36Sopenharmony_ci		.num_clks = 1,
85162306a36Sopenharmony_ci		.gpc_name = "g2",
85262306a36Sopenharmony_ci		.rst_mask = BIT(0),
85362306a36Sopenharmony_ci		.clk_mask = BIT(0),
85462306a36Sopenharmony_ci	},
85562306a36Sopenharmony_ci};
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_cistatic const struct imx8m_blk_ctrl_data imx8mq_vpu_blk_ctl_dev_data = {
85862306a36Sopenharmony_ci	.max_reg = 0x14,
85962306a36Sopenharmony_ci	.power_notifier_fn = imx8mq_vpu_power_notifier,
86062306a36Sopenharmony_ci	.domains = imx8mq_vpu_blk_ctl_domain_data,
86162306a36Sopenharmony_ci	.num_domains = ARRAY_SIZE(imx8mq_vpu_blk_ctl_domain_data),
86262306a36Sopenharmony_ci};
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_cistatic const struct of_device_id imx8m_blk_ctrl_of_match[] = {
86562306a36Sopenharmony_ci	{
86662306a36Sopenharmony_ci		.compatible = "fsl,imx8mm-vpu-blk-ctrl",
86762306a36Sopenharmony_ci		.data = &imx8mm_vpu_blk_ctl_dev_data
86862306a36Sopenharmony_ci	}, {
86962306a36Sopenharmony_ci		.compatible = "fsl,imx8mm-disp-blk-ctrl",
87062306a36Sopenharmony_ci		.data = &imx8mm_disp_blk_ctl_dev_data
87162306a36Sopenharmony_ci	}, {
87262306a36Sopenharmony_ci		.compatible = "fsl,imx8mn-disp-blk-ctrl",
87362306a36Sopenharmony_ci		.data = &imx8mn_disp_blk_ctl_dev_data
87462306a36Sopenharmony_ci	}, {
87562306a36Sopenharmony_ci		.compatible = "fsl,imx8mp-media-blk-ctrl",
87662306a36Sopenharmony_ci		.data = &imx8mp_media_blk_ctl_dev_data
87762306a36Sopenharmony_ci	}, {
87862306a36Sopenharmony_ci		.compatible = "fsl,imx8mq-vpu-blk-ctrl",
87962306a36Sopenharmony_ci		.data = &imx8mq_vpu_blk_ctl_dev_data
88062306a36Sopenharmony_ci	}, {
88162306a36Sopenharmony_ci		.compatible = "fsl,imx8mp-vpu-blk-ctrl",
88262306a36Sopenharmony_ci		.data = &imx8mp_vpu_blk_ctl_dev_data
88362306a36Sopenharmony_ci	}, {
88462306a36Sopenharmony_ci		/* Sentinel */
88562306a36Sopenharmony_ci	}
88662306a36Sopenharmony_ci};
88762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, imx8m_blk_ctrl_of_match);
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_cistatic struct platform_driver imx8m_blk_ctrl_driver = {
89062306a36Sopenharmony_ci	.probe = imx8m_blk_ctrl_probe,
89162306a36Sopenharmony_ci	.remove = imx8m_blk_ctrl_remove,
89262306a36Sopenharmony_ci	.driver = {
89362306a36Sopenharmony_ci		.name = "imx8m-blk-ctrl",
89462306a36Sopenharmony_ci		.pm = &imx8m_blk_ctrl_pm_ops,
89562306a36Sopenharmony_ci		.of_match_table = imx8m_blk_ctrl_of_match,
89662306a36Sopenharmony_ci	},
89762306a36Sopenharmony_ci};
89862306a36Sopenharmony_cimodule_platform_driver(imx8m_blk_ctrl_driver);
89962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
900