xref: /kernel/linux/linux-5.10/drivers/soc/imx/gpc.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2015-2017 Pengutronix, Lucas Stach <kernel@pengutronix.de>
48c2ecf20Sopenharmony_ci * Copyright 2011-2013 Freescale Semiconductor, Inc.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/clk.h>
88c2ecf20Sopenharmony_ci#include <linux/delay.h>
98c2ecf20Sopenharmony_ci#include <linux/io.h>
108c2ecf20Sopenharmony_ci#include <linux/of_device.h>
118c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
128c2ecf20Sopenharmony_ci#include <linux/pm_domain.h>
138c2ecf20Sopenharmony_ci#include <linux/regmap.h>
148c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define GPC_CNTR		0x000
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define GPC_PGC_CTRL_OFFS	0x0
198c2ecf20Sopenharmony_ci#define GPC_PGC_PUPSCR_OFFS	0x4
208c2ecf20Sopenharmony_ci#define GPC_PGC_PDNSCR_OFFS	0x8
218c2ecf20Sopenharmony_ci#define GPC_PGC_SW2ISO_SHIFT	0x8
228c2ecf20Sopenharmony_ci#define GPC_PGC_SW_SHIFT	0x0
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define GPC_PGC_PCI_PDN		0x200
258c2ecf20Sopenharmony_ci#define GPC_PGC_PCI_SR		0x20c
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define GPC_PGC_GPU_PDN		0x260
288c2ecf20Sopenharmony_ci#define GPC_PGC_GPU_PUPSCR	0x264
298c2ecf20Sopenharmony_ci#define GPC_PGC_GPU_PDNSCR	0x268
308c2ecf20Sopenharmony_ci#define GPC_PGC_GPU_SR		0x26c
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define GPC_PGC_DISP_PDN	0x240
338c2ecf20Sopenharmony_ci#define GPC_PGC_DISP_SR		0x24c
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define GPU_VPU_PUP_REQ		BIT(1)
368c2ecf20Sopenharmony_ci#define GPU_VPU_PDN_REQ		BIT(0)
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define GPC_CLK_MAX		7
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define PGC_DOMAIN_FLAG_NO_PD		BIT(0)
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistruct imx_pm_domain {
438c2ecf20Sopenharmony_ci	struct generic_pm_domain base;
448c2ecf20Sopenharmony_ci	struct regmap *regmap;
458c2ecf20Sopenharmony_ci	struct regulator *supply;
468c2ecf20Sopenharmony_ci	struct clk *clk[GPC_CLK_MAX];
478c2ecf20Sopenharmony_ci	int num_clks;
488c2ecf20Sopenharmony_ci	unsigned int reg_offs;
498c2ecf20Sopenharmony_ci	signed char cntr_pdn_bit;
508c2ecf20Sopenharmony_ci	unsigned int ipg_rate_mhz;
518c2ecf20Sopenharmony_ci};
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic inline struct imx_pm_domain *
548c2ecf20Sopenharmony_cito_imx_pm_domain(struct generic_pm_domain *genpd)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	return container_of(genpd, struct imx_pm_domain, base);
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic int imx6_pm_domain_power_off(struct generic_pm_domain *genpd)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct imx_pm_domain *pd = to_imx_pm_domain(genpd);
628c2ecf20Sopenharmony_ci	int iso, iso2sw;
638c2ecf20Sopenharmony_ci	u32 val;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	/* Read ISO and ISO2SW power down delays */
668c2ecf20Sopenharmony_ci	regmap_read(pd->regmap, pd->reg_offs + GPC_PGC_PDNSCR_OFFS, &val);
678c2ecf20Sopenharmony_ci	iso = val & 0x3f;
688c2ecf20Sopenharmony_ci	iso2sw = (val >> 8) & 0x3f;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	/* Gate off domain when powered down */
718c2ecf20Sopenharmony_ci	regmap_update_bits(pd->regmap, pd->reg_offs + GPC_PGC_CTRL_OFFS,
728c2ecf20Sopenharmony_ci			   0x1, 0x1);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	/* Request GPC to power down domain */
758c2ecf20Sopenharmony_ci	val = BIT(pd->cntr_pdn_bit);
768c2ecf20Sopenharmony_ci	regmap_update_bits(pd->regmap, GPC_CNTR, val, val);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	/* Wait ISO + ISO2SW IPG clock cycles */
798c2ecf20Sopenharmony_ci	udelay(DIV_ROUND_UP(iso + iso2sw, pd->ipg_rate_mhz));
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (pd->supply)
828c2ecf20Sopenharmony_ci		regulator_disable(pd->supply);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	return 0;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int imx6_pm_domain_power_on(struct generic_pm_domain *genpd)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	struct imx_pm_domain *pd = to_imx_pm_domain(genpd);
908c2ecf20Sopenharmony_ci	int i, ret;
918c2ecf20Sopenharmony_ci	u32 val, req;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	if (pd->supply) {
948c2ecf20Sopenharmony_ci		ret = regulator_enable(pd->supply);
958c2ecf20Sopenharmony_ci		if (ret) {
968c2ecf20Sopenharmony_ci			pr_err("%s: failed to enable regulator: %d\n",
978c2ecf20Sopenharmony_ci			       __func__, ret);
988c2ecf20Sopenharmony_ci			return ret;
998c2ecf20Sopenharmony_ci		}
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	/* Enable reset clocks for all devices in the domain */
1038c2ecf20Sopenharmony_ci	for (i = 0; i < pd->num_clks; i++)
1048c2ecf20Sopenharmony_ci		clk_prepare_enable(pd->clk[i]);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	/* Gate off domain when powered down */
1078c2ecf20Sopenharmony_ci	regmap_update_bits(pd->regmap, pd->reg_offs + GPC_PGC_CTRL_OFFS,
1088c2ecf20Sopenharmony_ci			   0x1, 0x1);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	/* Request GPC to power up domain */
1118c2ecf20Sopenharmony_ci	req = BIT(pd->cntr_pdn_bit + 1);
1128c2ecf20Sopenharmony_ci	regmap_update_bits(pd->regmap, GPC_CNTR, req, req);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	/* Wait for the PGC to handle the request */
1158c2ecf20Sopenharmony_ci	ret = regmap_read_poll_timeout(pd->regmap, GPC_CNTR, val, !(val & req),
1168c2ecf20Sopenharmony_ci				       1, 50);
1178c2ecf20Sopenharmony_ci	if (ret)
1188c2ecf20Sopenharmony_ci		pr_err("powerup request on domain %s timed out\n", genpd->name);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	/* Wait for reset to propagate through peripherals */
1218c2ecf20Sopenharmony_ci	usleep_range(5, 10);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	/* Disable reset clocks for all devices in the domain */
1248c2ecf20Sopenharmony_ci	for (i = 0; i < pd->num_clks; i++)
1258c2ecf20Sopenharmony_ci		clk_disable_unprepare(pd->clk[i]);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	return 0;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic int imx_pgc_get_clocks(struct device *dev, struct imx_pm_domain *domain)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	int i, ret;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	for (i = 0; ; i++) {
1358c2ecf20Sopenharmony_ci		struct clk *clk = of_clk_get(dev->of_node, i);
1368c2ecf20Sopenharmony_ci		if (IS_ERR(clk))
1378c2ecf20Sopenharmony_ci			break;
1388c2ecf20Sopenharmony_ci		if (i >= GPC_CLK_MAX) {
1398c2ecf20Sopenharmony_ci			dev_err(dev, "more than %d clocks\n", GPC_CLK_MAX);
1408c2ecf20Sopenharmony_ci			ret = -EINVAL;
1418c2ecf20Sopenharmony_ci			goto clk_err;
1428c2ecf20Sopenharmony_ci		}
1438c2ecf20Sopenharmony_ci		domain->clk[i] = clk;
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci	domain->num_clks = i;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	return 0;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ciclk_err:
1508c2ecf20Sopenharmony_ci	while (i--)
1518c2ecf20Sopenharmony_ci		clk_put(domain->clk[i]);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	return ret;
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic void imx_pgc_put_clocks(struct imx_pm_domain *domain)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	int i;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	for (i = domain->num_clks - 1; i >= 0; i--)
1618c2ecf20Sopenharmony_ci		clk_put(domain->clk[i]);
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic int imx_pgc_parse_dt(struct device *dev, struct imx_pm_domain *domain)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	/* try to get the domain supply regulator */
1678c2ecf20Sopenharmony_ci	domain->supply = devm_regulator_get_optional(dev, "power");
1688c2ecf20Sopenharmony_ci	if (IS_ERR(domain->supply)) {
1698c2ecf20Sopenharmony_ci		if (PTR_ERR(domain->supply) == -ENODEV)
1708c2ecf20Sopenharmony_ci			domain->supply = NULL;
1718c2ecf20Sopenharmony_ci		else
1728c2ecf20Sopenharmony_ci			return PTR_ERR(domain->supply);
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	/* try to get all clocks needed for reset propagation */
1768c2ecf20Sopenharmony_ci	return imx_pgc_get_clocks(dev, domain);
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic int imx_pgc_power_domain_probe(struct platform_device *pdev)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	struct imx_pm_domain *domain = pdev->dev.platform_data;
1828c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
1838c2ecf20Sopenharmony_ci	int ret;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	/* if this PD is associated with a DT node try to parse it */
1868c2ecf20Sopenharmony_ci	if (dev->of_node) {
1878c2ecf20Sopenharmony_ci		ret = imx_pgc_parse_dt(dev, domain);
1888c2ecf20Sopenharmony_ci		if (ret)
1898c2ecf20Sopenharmony_ci			return ret;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	/* initially power on the domain */
1938c2ecf20Sopenharmony_ci	if (domain->base.power_on)
1948c2ecf20Sopenharmony_ci		domain->base.power_on(&domain->base);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) {
1978c2ecf20Sopenharmony_ci		pm_genpd_init(&domain->base, NULL, false);
1988c2ecf20Sopenharmony_ci		ret = of_genpd_add_provider_simple(dev->of_node, &domain->base);
1998c2ecf20Sopenharmony_ci		if (ret)
2008c2ecf20Sopenharmony_ci			goto genpd_err;
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	device_link_add(dev, dev->parent, DL_FLAG_AUTOREMOVE_CONSUMER);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	return 0;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cigenpd_err:
2088c2ecf20Sopenharmony_ci	pm_genpd_remove(&domain->base);
2098c2ecf20Sopenharmony_ci	imx_pgc_put_clocks(domain);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	return ret;
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic int imx_pgc_power_domain_remove(struct platform_device *pdev)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	struct imx_pm_domain *domain = pdev->dev.platform_data;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) {
2198c2ecf20Sopenharmony_ci		of_genpd_del_provider(pdev->dev.of_node);
2208c2ecf20Sopenharmony_ci		pm_genpd_remove(&domain->base);
2218c2ecf20Sopenharmony_ci		imx_pgc_put_clocks(domain);
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	return 0;
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic const struct platform_device_id imx_pgc_power_domain_id[] = {
2288c2ecf20Sopenharmony_ci	{ "imx-pgc-power-domain"},
2298c2ecf20Sopenharmony_ci	{ },
2308c2ecf20Sopenharmony_ci};
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic struct platform_driver imx_pgc_power_domain_driver = {
2338c2ecf20Sopenharmony_ci	.driver = {
2348c2ecf20Sopenharmony_ci		.name = "imx-pgc-pd",
2358c2ecf20Sopenharmony_ci	},
2368c2ecf20Sopenharmony_ci	.probe = imx_pgc_power_domain_probe,
2378c2ecf20Sopenharmony_ci	.remove = imx_pgc_power_domain_remove,
2388c2ecf20Sopenharmony_ci	.id_table = imx_pgc_power_domain_id,
2398c2ecf20Sopenharmony_ci};
2408c2ecf20Sopenharmony_cibuiltin_platform_driver(imx_pgc_power_domain_driver)
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci#define GPC_PGC_DOMAIN_ARM	0
2438c2ecf20Sopenharmony_ci#define GPC_PGC_DOMAIN_PU	1
2448c2ecf20Sopenharmony_ci#define GPC_PGC_DOMAIN_DISPLAY	2
2458c2ecf20Sopenharmony_ci#define GPC_PGC_DOMAIN_PCI	3
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic struct genpd_power_state imx6_pm_domain_pu_state = {
2488c2ecf20Sopenharmony_ci	.power_off_latency_ns = 25000,
2498c2ecf20Sopenharmony_ci	.power_on_latency_ns = 2000000,
2508c2ecf20Sopenharmony_ci};
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic struct imx_pm_domain imx_gpc_domains[] = {
2538c2ecf20Sopenharmony_ci	[GPC_PGC_DOMAIN_ARM] = {
2548c2ecf20Sopenharmony_ci		.base = {
2558c2ecf20Sopenharmony_ci			.name = "ARM",
2568c2ecf20Sopenharmony_ci			.flags = GENPD_FLAG_ALWAYS_ON,
2578c2ecf20Sopenharmony_ci		},
2588c2ecf20Sopenharmony_ci	},
2598c2ecf20Sopenharmony_ci	[GPC_PGC_DOMAIN_PU] = {
2608c2ecf20Sopenharmony_ci		.base = {
2618c2ecf20Sopenharmony_ci			.name = "PU",
2628c2ecf20Sopenharmony_ci			.power_off = imx6_pm_domain_power_off,
2638c2ecf20Sopenharmony_ci			.power_on = imx6_pm_domain_power_on,
2648c2ecf20Sopenharmony_ci			.states = &imx6_pm_domain_pu_state,
2658c2ecf20Sopenharmony_ci			.state_count = 1,
2668c2ecf20Sopenharmony_ci		},
2678c2ecf20Sopenharmony_ci		.reg_offs = 0x260,
2688c2ecf20Sopenharmony_ci		.cntr_pdn_bit = 0,
2698c2ecf20Sopenharmony_ci	},
2708c2ecf20Sopenharmony_ci	[GPC_PGC_DOMAIN_DISPLAY] = {
2718c2ecf20Sopenharmony_ci		.base = {
2728c2ecf20Sopenharmony_ci			.name = "DISPLAY",
2738c2ecf20Sopenharmony_ci			.power_off = imx6_pm_domain_power_off,
2748c2ecf20Sopenharmony_ci			.power_on = imx6_pm_domain_power_on,
2758c2ecf20Sopenharmony_ci		},
2768c2ecf20Sopenharmony_ci		.reg_offs = 0x240,
2778c2ecf20Sopenharmony_ci		.cntr_pdn_bit = 4,
2788c2ecf20Sopenharmony_ci	},
2798c2ecf20Sopenharmony_ci	[GPC_PGC_DOMAIN_PCI] = {
2808c2ecf20Sopenharmony_ci		.base = {
2818c2ecf20Sopenharmony_ci			.name = "PCI",
2828c2ecf20Sopenharmony_ci			.power_off = imx6_pm_domain_power_off,
2838c2ecf20Sopenharmony_ci			.power_on = imx6_pm_domain_power_on,
2848c2ecf20Sopenharmony_ci		},
2858c2ecf20Sopenharmony_ci		.reg_offs = 0x200,
2868c2ecf20Sopenharmony_ci		.cntr_pdn_bit = 6,
2878c2ecf20Sopenharmony_ci	},
2888c2ecf20Sopenharmony_ci};
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistruct imx_gpc_dt_data {
2918c2ecf20Sopenharmony_ci	int num_domains;
2928c2ecf20Sopenharmony_ci	bool err009619_present;
2938c2ecf20Sopenharmony_ci	bool err006287_present;
2948c2ecf20Sopenharmony_ci};
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic const struct imx_gpc_dt_data imx6q_dt_data = {
2978c2ecf20Sopenharmony_ci	.num_domains = 2,
2988c2ecf20Sopenharmony_ci	.err009619_present = false,
2998c2ecf20Sopenharmony_ci	.err006287_present = false,
3008c2ecf20Sopenharmony_ci};
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic const struct imx_gpc_dt_data imx6qp_dt_data = {
3038c2ecf20Sopenharmony_ci	.num_domains = 2,
3048c2ecf20Sopenharmony_ci	.err009619_present = true,
3058c2ecf20Sopenharmony_ci	.err006287_present = false,
3068c2ecf20Sopenharmony_ci};
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic const struct imx_gpc_dt_data imx6sl_dt_data = {
3098c2ecf20Sopenharmony_ci	.num_domains = 3,
3108c2ecf20Sopenharmony_ci	.err009619_present = false,
3118c2ecf20Sopenharmony_ci	.err006287_present = true,
3128c2ecf20Sopenharmony_ci};
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic const struct imx_gpc_dt_data imx6sx_dt_data = {
3158c2ecf20Sopenharmony_ci	.num_domains = 4,
3168c2ecf20Sopenharmony_ci	.err009619_present = false,
3178c2ecf20Sopenharmony_ci	.err006287_present = false,
3188c2ecf20Sopenharmony_ci};
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_cistatic const struct of_device_id imx_gpc_dt_ids[] = {
3218c2ecf20Sopenharmony_ci	{ .compatible = "fsl,imx6q-gpc", .data = &imx6q_dt_data },
3228c2ecf20Sopenharmony_ci	{ .compatible = "fsl,imx6qp-gpc", .data = &imx6qp_dt_data },
3238c2ecf20Sopenharmony_ci	{ .compatible = "fsl,imx6sl-gpc", .data = &imx6sl_dt_data },
3248c2ecf20Sopenharmony_ci	{ .compatible = "fsl,imx6sx-gpc", .data = &imx6sx_dt_data },
3258c2ecf20Sopenharmony_ci	{ }
3268c2ecf20Sopenharmony_ci};
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic const struct regmap_range yes_ranges[] = {
3298c2ecf20Sopenharmony_ci	regmap_reg_range(GPC_CNTR, GPC_CNTR),
3308c2ecf20Sopenharmony_ci	regmap_reg_range(GPC_PGC_PCI_PDN, GPC_PGC_PCI_SR),
3318c2ecf20Sopenharmony_ci	regmap_reg_range(GPC_PGC_GPU_PDN, GPC_PGC_GPU_SR),
3328c2ecf20Sopenharmony_ci	regmap_reg_range(GPC_PGC_DISP_PDN, GPC_PGC_DISP_SR),
3338c2ecf20Sopenharmony_ci};
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic const struct regmap_access_table access_table = {
3368c2ecf20Sopenharmony_ci	.yes_ranges	= yes_ranges,
3378c2ecf20Sopenharmony_ci	.n_yes_ranges	= ARRAY_SIZE(yes_ranges),
3388c2ecf20Sopenharmony_ci};
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistatic const struct regmap_config imx_gpc_regmap_config = {
3418c2ecf20Sopenharmony_ci	.reg_bits = 32,
3428c2ecf20Sopenharmony_ci	.val_bits = 32,
3438c2ecf20Sopenharmony_ci	.reg_stride = 4,
3448c2ecf20Sopenharmony_ci	.rd_table = &access_table,
3458c2ecf20Sopenharmony_ci	.wr_table = &access_table,
3468c2ecf20Sopenharmony_ci	.max_register = 0x2ac,
3478c2ecf20Sopenharmony_ci	.fast_io = true,
3488c2ecf20Sopenharmony_ci};
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic struct generic_pm_domain *imx_gpc_onecell_domains[] = {
3518c2ecf20Sopenharmony_ci	&imx_gpc_domains[GPC_PGC_DOMAIN_ARM].base,
3528c2ecf20Sopenharmony_ci	&imx_gpc_domains[GPC_PGC_DOMAIN_PU].base,
3538c2ecf20Sopenharmony_ci};
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic struct genpd_onecell_data imx_gpc_onecell_data = {
3568c2ecf20Sopenharmony_ci	.domains = imx_gpc_onecell_domains,
3578c2ecf20Sopenharmony_ci	.num_domains = 2,
3588c2ecf20Sopenharmony_ci};
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic int imx_gpc_old_dt_init(struct device *dev, struct regmap *regmap,
3618c2ecf20Sopenharmony_ci			       unsigned int num_domains)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	struct imx_pm_domain *domain;
3648c2ecf20Sopenharmony_ci	int i, ret;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	for (i = 0; i < num_domains; i++) {
3678c2ecf20Sopenharmony_ci		domain = &imx_gpc_domains[i];
3688c2ecf20Sopenharmony_ci		domain->regmap = regmap;
3698c2ecf20Sopenharmony_ci		domain->ipg_rate_mhz = 66;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci		if (i == 1) {
3728c2ecf20Sopenharmony_ci			domain->supply = devm_regulator_get(dev, "pu");
3738c2ecf20Sopenharmony_ci			if (IS_ERR(domain->supply))
3748c2ecf20Sopenharmony_ci				return PTR_ERR(domain->supply);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci			ret = imx_pgc_get_clocks(dev, domain);
3778c2ecf20Sopenharmony_ci			if (ret)
3788c2ecf20Sopenharmony_ci				goto clk_err;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci			domain->base.power_on(&domain->base);
3818c2ecf20Sopenharmony_ci		}
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	for (i = 0; i < num_domains; i++)
3858c2ecf20Sopenharmony_ci		pm_genpd_init(&imx_gpc_domains[i].base, NULL, false);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) {
3888c2ecf20Sopenharmony_ci		ret = of_genpd_add_provider_onecell(dev->of_node,
3898c2ecf20Sopenharmony_ci						    &imx_gpc_onecell_data);
3908c2ecf20Sopenharmony_ci		if (ret)
3918c2ecf20Sopenharmony_ci			goto genpd_err;
3928c2ecf20Sopenharmony_ci	}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	return 0;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cigenpd_err:
3978c2ecf20Sopenharmony_ci	for (i = 0; i < num_domains; i++)
3988c2ecf20Sopenharmony_ci		pm_genpd_remove(&imx_gpc_domains[i].base);
3998c2ecf20Sopenharmony_ci	imx_pgc_put_clocks(&imx_gpc_domains[GPC_PGC_DOMAIN_PU]);
4008c2ecf20Sopenharmony_ciclk_err:
4018c2ecf20Sopenharmony_ci	return ret;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic int imx_gpc_probe(struct platform_device *pdev)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	const struct of_device_id *of_id =
4078c2ecf20Sopenharmony_ci			of_match_device(imx_gpc_dt_ids, &pdev->dev);
4088c2ecf20Sopenharmony_ci	const struct imx_gpc_dt_data *of_id_data = of_id->data;
4098c2ecf20Sopenharmony_ci	struct device_node *pgc_node;
4108c2ecf20Sopenharmony_ci	struct regmap *regmap;
4118c2ecf20Sopenharmony_ci	void __iomem *base;
4128c2ecf20Sopenharmony_ci	int ret;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	pgc_node = of_get_child_by_name(pdev->dev.of_node, "pgc");
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	/* bail out if DT too old and doesn't provide the necessary info */
4178c2ecf20Sopenharmony_ci	if (!of_property_read_bool(pdev->dev.of_node, "#power-domain-cells") &&
4188c2ecf20Sopenharmony_ci	    !pgc_node)
4198c2ecf20Sopenharmony_ci		return 0;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	base = devm_platform_ioremap_resource(pdev, 0);
4228c2ecf20Sopenharmony_ci	if (IS_ERR(base))
4238c2ecf20Sopenharmony_ci		return PTR_ERR(base);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base,
4268c2ecf20Sopenharmony_ci					   &imx_gpc_regmap_config);
4278c2ecf20Sopenharmony_ci	if (IS_ERR(regmap)) {
4288c2ecf20Sopenharmony_ci		ret = PTR_ERR(regmap);
4298c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to init regmap: %d\n",
4308c2ecf20Sopenharmony_ci			ret);
4318c2ecf20Sopenharmony_ci		return ret;
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	/*
4358c2ecf20Sopenharmony_ci	 * Disable PU power down by runtime PM if ERR009619 is present.
4368c2ecf20Sopenharmony_ci	 *
4378c2ecf20Sopenharmony_ci	 * The PRE clock will be paused for several cycles when turning on the
4388c2ecf20Sopenharmony_ci	 * PU domain LDO from power down state. If PRE is in use at that time,
4398c2ecf20Sopenharmony_ci	 * the IPU/PRG cannot get the correct display data from the PRE.
4408c2ecf20Sopenharmony_ci	 *
4418c2ecf20Sopenharmony_ci	 * This is not a concern when the whole system enters suspend state, so
4428c2ecf20Sopenharmony_ci	 * it's safe to power down PU in this case.
4438c2ecf20Sopenharmony_ci	 */
4448c2ecf20Sopenharmony_ci	if (of_id_data->err009619_present)
4458c2ecf20Sopenharmony_ci		imx_gpc_domains[GPC_PGC_DOMAIN_PU].base.flags |=
4468c2ecf20Sopenharmony_ci				GENPD_FLAG_RPM_ALWAYS_ON;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	/* Keep DISP always on if ERR006287 is present */
4498c2ecf20Sopenharmony_ci	if (of_id_data->err006287_present)
4508c2ecf20Sopenharmony_ci		imx_gpc_domains[GPC_PGC_DOMAIN_DISPLAY].base.flags |=
4518c2ecf20Sopenharmony_ci				GENPD_FLAG_ALWAYS_ON;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	if (!pgc_node) {
4548c2ecf20Sopenharmony_ci		ret = imx_gpc_old_dt_init(&pdev->dev, regmap,
4558c2ecf20Sopenharmony_ci					  of_id_data->num_domains);
4568c2ecf20Sopenharmony_ci		if (ret)
4578c2ecf20Sopenharmony_ci			return ret;
4588c2ecf20Sopenharmony_ci	} else {
4598c2ecf20Sopenharmony_ci		struct imx_pm_domain *domain;
4608c2ecf20Sopenharmony_ci		struct platform_device *pd_pdev;
4618c2ecf20Sopenharmony_ci		struct device_node *np;
4628c2ecf20Sopenharmony_ci		struct clk *ipg_clk;
4638c2ecf20Sopenharmony_ci		unsigned int ipg_rate_mhz;
4648c2ecf20Sopenharmony_ci		int domain_index;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci		ipg_clk = devm_clk_get(&pdev->dev, "ipg");
4678c2ecf20Sopenharmony_ci		if (IS_ERR(ipg_clk))
4688c2ecf20Sopenharmony_ci			return PTR_ERR(ipg_clk);
4698c2ecf20Sopenharmony_ci		ipg_rate_mhz = clk_get_rate(ipg_clk) / 1000000;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci		for_each_child_of_node(pgc_node, np) {
4728c2ecf20Sopenharmony_ci			ret = of_property_read_u32(np, "reg", &domain_index);
4738c2ecf20Sopenharmony_ci			if (ret) {
4748c2ecf20Sopenharmony_ci				of_node_put(np);
4758c2ecf20Sopenharmony_ci				return ret;
4768c2ecf20Sopenharmony_ci			}
4778c2ecf20Sopenharmony_ci			if (domain_index >= of_id_data->num_domains)
4788c2ecf20Sopenharmony_ci				continue;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci			pd_pdev = platform_device_alloc("imx-pgc-power-domain",
4818c2ecf20Sopenharmony_ci							domain_index);
4828c2ecf20Sopenharmony_ci			if (!pd_pdev) {
4838c2ecf20Sopenharmony_ci				of_node_put(np);
4848c2ecf20Sopenharmony_ci				return -ENOMEM;
4858c2ecf20Sopenharmony_ci			}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci			ret = platform_device_add_data(pd_pdev,
4888c2ecf20Sopenharmony_ci						       &imx_gpc_domains[domain_index],
4898c2ecf20Sopenharmony_ci						       sizeof(imx_gpc_domains[domain_index]));
4908c2ecf20Sopenharmony_ci			if (ret) {
4918c2ecf20Sopenharmony_ci				platform_device_put(pd_pdev);
4928c2ecf20Sopenharmony_ci				of_node_put(np);
4938c2ecf20Sopenharmony_ci				return ret;
4948c2ecf20Sopenharmony_ci			}
4958c2ecf20Sopenharmony_ci			domain = pd_pdev->dev.platform_data;
4968c2ecf20Sopenharmony_ci			domain->regmap = regmap;
4978c2ecf20Sopenharmony_ci			domain->ipg_rate_mhz = ipg_rate_mhz;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci			pd_pdev->dev.parent = &pdev->dev;
5008c2ecf20Sopenharmony_ci			pd_pdev->dev.of_node = np;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci			ret = platform_device_add(pd_pdev);
5038c2ecf20Sopenharmony_ci			if (ret) {
5048c2ecf20Sopenharmony_ci				platform_device_put(pd_pdev);
5058c2ecf20Sopenharmony_ci				of_node_put(np);
5068c2ecf20Sopenharmony_ci				return ret;
5078c2ecf20Sopenharmony_ci			}
5088c2ecf20Sopenharmony_ci		}
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	return 0;
5128c2ecf20Sopenharmony_ci}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_cistatic int imx_gpc_remove(struct platform_device *pdev)
5158c2ecf20Sopenharmony_ci{
5168c2ecf20Sopenharmony_ci	struct device_node *pgc_node;
5178c2ecf20Sopenharmony_ci	int ret;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	pgc_node = of_get_child_by_name(pdev->dev.of_node, "pgc");
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	/* bail out if DT too old and doesn't provide the necessary info */
5228c2ecf20Sopenharmony_ci	if (!of_property_read_bool(pdev->dev.of_node, "#power-domain-cells") &&
5238c2ecf20Sopenharmony_ci	    !pgc_node)
5248c2ecf20Sopenharmony_ci		return 0;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	/*
5278c2ecf20Sopenharmony_ci	 * If the old DT binding is used the toplevel driver needs to
5288c2ecf20Sopenharmony_ci	 * de-register the power domains
5298c2ecf20Sopenharmony_ci	 */
5308c2ecf20Sopenharmony_ci	if (!pgc_node) {
5318c2ecf20Sopenharmony_ci		of_genpd_del_provider(pdev->dev.of_node);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci		ret = pm_genpd_remove(&imx_gpc_domains[GPC_PGC_DOMAIN_PU].base);
5348c2ecf20Sopenharmony_ci		if (ret)
5358c2ecf20Sopenharmony_ci			return ret;
5368c2ecf20Sopenharmony_ci		imx_pgc_put_clocks(&imx_gpc_domains[GPC_PGC_DOMAIN_PU]);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci		ret = pm_genpd_remove(&imx_gpc_domains[GPC_PGC_DOMAIN_ARM].base);
5398c2ecf20Sopenharmony_ci		if (ret)
5408c2ecf20Sopenharmony_ci			return ret;
5418c2ecf20Sopenharmony_ci	}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	return 0;
5448c2ecf20Sopenharmony_ci}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cistatic struct platform_driver imx_gpc_driver = {
5478c2ecf20Sopenharmony_ci	.driver = {
5488c2ecf20Sopenharmony_ci		.name = "imx-gpc",
5498c2ecf20Sopenharmony_ci		.of_match_table = imx_gpc_dt_ids,
5508c2ecf20Sopenharmony_ci	},
5518c2ecf20Sopenharmony_ci	.probe = imx_gpc_probe,
5528c2ecf20Sopenharmony_ci	.remove = imx_gpc_remove,
5538c2ecf20Sopenharmony_ci};
5548c2ecf20Sopenharmony_cibuiltin_platform_driver(imx_gpc_driver)
555