162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2015-2017 Pengutronix, Lucas Stach <kernel@pengutronix.de>
462306a36Sopenharmony_ci * Copyright 2011-2013 Freescale Semiconductor, Inc.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/clk.h>
862306a36Sopenharmony_ci#include <linux/delay.h>
962306a36Sopenharmony_ci#include <linux/io.h>
1062306a36Sopenharmony_ci#include <linux/of_device.h>
1162306a36Sopenharmony_ci#include <linux/platform_device.h>
1262306a36Sopenharmony_ci#include <linux/pm_domain.h>
1362306a36Sopenharmony_ci#include <linux/regmap.h>
1462306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define GPC_CNTR		0x000
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define GPC_PGC_CTRL_OFFS	0x0
1962306a36Sopenharmony_ci#define GPC_PGC_PUPSCR_OFFS	0x4
2062306a36Sopenharmony_ci#define GPC_PGC_PDNSCR_OFFS	0x8
2162306a36Sopenharmony_ci#define GPC_PGC_SW2ISO_SHIFT	0x8
2262306a36Sopenharmony_ci#define GPC_PGC_SW_SHIFT	0x0
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define GPC_PGC_PCI_PDN		0x200
2562306a36Sopenharmony_ci#define GPC_PGC_PCI_SR		0x20c
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define GPC_PGC_GPU_PDN		0x260
2862306a36Sopenharmony_ci#define GPC_PGC_GPU_PUPSCR	0x264
2962306a36Sopenharmony_ci#define GPC_PGC_GPU_PDNSCR	0x268
3062306a36Sopenharmony_ci#define GPC_PGC_GPU_SR		0x26c
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define GPC_PGC_DISP_PDN	0x240
3362306a36Sopenharmony_ci#define GPC_PGC_DISP_SR		0x24c
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define GPU_VPU_PUP_REQ		BIT(1)
3662306a36Sopenharmony_ci#define GPU_VPU_PDN_REQ		BIT(0)
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define GPC_CLK_MAX		7
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define PGC_DOMAIN_FLAG_NO_PD		BIT(0)
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistruct imx_pm_domain {
4362306a36Sopenharmony_ci	struct generic_pm_domain base;
4462306a36Sopenharmony_ci	struct regmap *regmap;
4562306a36Sopenharmony_ci	struct regulator *supply;
4662306a36Sopenharmony_ci	struct clk *clk[GPC_CLK_MAX];
4762306a36Sopenharmony_ci	int num_clks;
4862306a36Sopenharmony_ci	unsigned int reg_offs;
4962306a36Sopenharmony_ci	signed char cntr_pdn_bit;
5062306a36Sopenharmony_ci	unsigned int ipg_rate_mhz;
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic inline struct imx_pm_domain *
5462306a36Sopenharmony_cito_imx_pm_domain(struct generic_pm_domain *genpd)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	return container_of(genpd, struct imx_pm_domain, base);
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic int imx6_pm_domain_power_off(struct generic_pm_domain *genpd)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	struct imx_pm_domain *pd = to_imx_pm_domain(genpd);
6262306a36Sopenharmony_ci	int iso, iso2sw;
6362306a36Sopenharmony_ci	u32 val;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	/* Read ISO and ISO2SW power down delays */
6662306a36Sopenharmony_ci	regmap_read(pd->regmap, pd->reg_offs + GPC_PGC_PDNSCR_OFFS, &val);
6762306a36Sopenharmony_ci	iso = val & 0x3f;
6862306a36Sopenharmony_ci	iso2sw = (val >> 8) & 0x3f;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/* Gate off domain when powered down */
7162306a36Sopenharmony_ci	regmap_update_bits(pd->regmap, pd->reg_offs + GPC_PGC_CTRL_OFFS,
7262306a36Sopenharmony_ci			   0x1, 0x1);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	/* Request GPC to power down domain */
7562306a36Sopenharmony_ci	val = BIT(pd->cntr_pdn_bit);
7662306a36Sopenharmony_ci	regmap_update_bits(pd->regmap, GPC_CNTR, val, val);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	/* Wait ISO + ISO2SW IPG clock cycles */
7962306a36Sopenharmony_ci	udelay(DIV_ROUND_UP(iso + iso2sw, pd->ipg_rate_mhz));
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	if (pd->supply)
8262306a36Sopenharmony_ci		regulator_disable(pd->supply);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	return 0;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic int imx6_pm_domain_power_on(struct generic_pm_domain *genpd)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct imx_pm_domain *pd = to_imx_pm_domain(genpd);
9062306a36Sopenharmony_ci	int i, ret;
9162306a36Sopenharmony_ci	u32 val, req;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (pd->supply) {
9462306a36Sopenharmony_ci		ret = regulator_enable(pd->supply);
9562306a36Sopenharmony_ci		if (ret) {
9662306a36Sopenharmony_ci			pr_err("%s: failed to enable regulator: %d\n",
9762306a36Sopenharmony_ci			       __func__, ret);
9862306a36Sopenharmony_ci			return ret;
9962306a36Sopenharmony_ci		}
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	/* Enable reset clocks for all devices in the domain */
10362306a36Sopenharmony_ci	for (i = 0; i < pd->num_clks; i++)
10462306a36Sopenharmony_ci		clk_prepare_enable(pd->clk[i]);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	/* Gate off domain when powered down */
10762306a36Sopenharmony_ci	regmap_update_bits(pd->regmap, pd->reg_offs + GPC_PGC_CTRL_OFFS,
10862306a36Sopenharmony_ci			   0x1, 0x1);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	/* Request GPC to power up domain */
11162306a36Sopenharmony_ci	req = BIT(pd->cntr_pdn_bit + 1);
11262306a36Sopenharmony_ci	regmap_update_bits(pd->regmap, GPC_CNTR, req, req);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/* Wait for the PGC to handle the request */
11562306a36Sopenharmony_ci	ret = regmap_read_poll_timeout(pd->regmap, GPC_CNTR, val, !(val & req),
11662306a36Sopenharmony_ci				       1, 50);
11762306a36Sopenharmony_ci	if (ret)
11862306a36Sopenharmony_ci		pr_err("powerup request on domain %s timed out\n", genpd->name);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/* Wait for reset to propagate through peripherals */
12162306a36Sopenharmony_ci	usleep_range(5, 10);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	/* Disable reset clocks for all devices in the domain */
12462306a36Sopenharmony_ci	for (i = 0; i < pd->num_clks; i++)
12562306a36Sopenharmony_ci		clk_disable_unprepare(pd->clk[i]);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	return 0;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic int imx_pgc_get_clocks(struct device *dev, struct imx_pm_domain *domain)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	int i, ret;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	for (i = 0; ; i++) {
13562306a36Sopenharmony_ci		struct clk *clk = of_clk_get(dev->of_node, i);
13662306a36Sopenharmony_ci		if (IS_ERR(clk))
13762306a36Sopenharmony_ci			break;
13862306a36Sopenharmony_ci		if (i >= GPC_CLK_MAX) {
13962306a36Sopenharmony_ci			dev_err(dev, "more than %d clocks\n", GPC_CLK_MAX);
14062306a36Sopenharmony_ci			ret = -EINVAL;
14162306a36Sopenharmony_ci			goto clk_err;
14262306a36Sopenharmony_ci		}
14362306a36Sopenharmony_ci		domain->clk[i] = clk;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci	domain->num_clks = i;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	return 0;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ciclk_err:
15062306a36Sopenharmony_ci	while (i--)
15162306a36Sopenharmony_ci		clk_put(domain->clk[i]);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return ret;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic void imx_pgc_put_clocks(struct imx_pm_domain *domain)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	int i;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	for (i = domain->num_clks - 1; i >= 0; i--)
16162306a36Sopenharmony_ci		clk_put(domain->clk[i]);
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic int imx_pgc_parse_dt(struct device *dev, struct imx_pm_domain *domain)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	/* try to get the domain supply regulator */
16762306a36Sopenharmony_ci	domain->supply = devm_regulator_get_optional(dev, "power");
16862306a36Sopenharmony_ci	if (IS_ERR(domain->supply)) {
16962306a36Sopenharmony_ci		if (PTR_ERR(domain->supply) == -ENODEV)
17062306a36Sopenharmony_ci			domain->supply = NULL;
17162306a36Sopenharmony_ci		else
17262306a36Sopenharmony_ci			return PTR_ERR(domain->supply);
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	/* try to get all clocks needed for reset propagation */
17662306a36Sopenharmony_ci	return imx_pgc_get_clocks(dev, domain);
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic int imx_pgc_power_domain_probe(struct platform_device *pdev)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	struct imx_pm_domain *domain = pdev->dev.platform_data;
18262306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
18362306a36Sopenharmony_ci	int ret;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	/* if this PD is associated with a DT node try to parse it */
18662306a36Sopenharmony_ci	if (dev->of_node) {
18762306a36Sopenharmony_ci		ret = imx_pgc_parse_dt(dev, domain);
18862306a36Sopenharmony_ci		if (ret)
18962306a36Sopenharmony_ci			return ret;
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	/* initially power on the domain */
19362306a36Sopenharmony_ci	if (domain->base.power_on)
19462306a36Sopenharmony_ci		domain->base.power_on(&domain->base);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) {
19762306a36Sopenharmony_ci		pm_genpd_init(&domain->base, NULL, false);
19862306a36Sopenharmony_ci		ret = of_genpd_add_provider_simple(dev->of_node, &domain->base);
19962306a36Sopenharmony_ci		if (ret)
20062306a36Sopenharmony_ci			goto genpd_err;
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	device_link_add(dev, dev->parent, DL_FLAG_AUTOREMOVE_CONSUMER);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	return 0;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cigenpd_err:
20862306a36Sopenharmony_ci	pm_genpd_remove(&domain->base);
20962306a36Sopenharmony_ci	imx_pgc_put_clocks(domain);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	return ret;
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic int imx_pgc_power_domain_remove(struct platform_device *pdev)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	struct imx_pm_domain *domain = pdev->dev.platform_data;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) {
21962306a36Sopenharmony_ci		of_genpd_del_provider(pdev->dev.of_node);
22062306a36Sopenharmony_ci		pm_genpd_remove(&domain->base);
22162306a36Sopenharmony_ci		imx_pgc_put_clocks(domain);
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	return 0;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic const struct platform_device_id imx_pgc_power_domain_id[] = {
22862306a36Sopenharmony_ci	{ "imx-pgc-power-domain"},
22962306a36Sopenharmony_ci	{ },
23062306a36Sopenharmony_ci};
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic struct platform_driver imx_pgc_power_domain_driver = {
23362306a36Sopenharmony_ci	.driver = {
23462306a36Sopenharmony_ci		.name = "imx-pgc-pd",
23562306a36Sopenharmony_ci	},
23662306a36Sopenharmony_ci	.probe = imx_pgc_power_domain_probe,
23762306a36Sopenharmony_ci	.remove = imx_pgc_power_domain_remove,
23862306a36Sopenharmony_ci	.id_table = imx_pgc_power_domain_id,
23962306a36Sopenharmony_ci};
24062306a36Sopenharmony_cibuiltin_platform_driver(imx_pgc_power_domain_driver)
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci#define GPC_PGC_DOMAIN_ARM	0
24362306a36Sopenharmony_ci#define GPC_PGC_DOMAIN_PU	1
24462306a36Sopenharmony_ci#define GPC_PGC_DOMAIN_DISPLAY	2
24562306a36Sopenharmony_ci#define GPC_PGC_DOMAIN_PCI	3
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic struct genpd_power_state imx6_pm_domain_pu_state = {
24862306a36Sopenharmony_ci	.power_off_latency_ns = 25000,
24962306a36Sopenharmony_ci	.power_on_latency_ns = 2000000,
25062306a36Sopenharmony_ci};
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic struct imx_pm_domain imx_gpc_domains[] = {
25362306a36Sopenharmony_ci	[GPC_PGC_DOMAIN_ARM] = {
25462306a36Sopenharmony_ci		.base = {
25562306a36Sopenharmony_ci			.name = "ARM",
25662306a36Sopenharmony_ci			.flags = GENPD_FLAG_ALWAYS_ON,
25762306a36Sopenharmony_ci		},
25862306a36Sopenharmony_ci	},
25962306a36Sopenharmony_ci	[GPC_PGC_DOMAIN_PU] = {
26062306a36Sopenharmony_ci		.base = {
26162306a36Sopenharmony_ci			.name = "PU",
26262306a36Sopenharmony_ci			.power_off = imx6_pm_domain_power_off,
26362306a36Sopenharmony_ci			.power_on = imx6_pm_domain_power_on,
26462306a36Sopenharmony_ci			.states = &imx6_pm_domain_pu_state,
26562306a36Sopenharmony_ci			.state_count = 1,
26662306a36Sopenharmony_ci		},
26762306a36Sopenharmony_ci		.reg_offs = 0x260,
26862306a36Sopenharmony_ci		.cntr_pdn_bit = 0,
26962306a36Sopenharmony_ci	},
27062306a36Sopenharmony_ci	[GPC_PGC_DOMAIN_DISPLAY] = {
27162306a36Sopenharmony_ci		.base = {
27262306a36Sopenharmony_ci			.name = "DISPLAY",
27362306a36Sopenharmony_ci			.power_off = imx6_pm_domain_power_off,
27462306a36Sopenharmony_ci			.power_on = imx6_pm_domain_power_on,
27562306a36Sopenharmony_ci		},
27662306a36Sopenharmony_ci		.reg_offs = 0x240,
27762306a36Sopenharmony_ci		.cntr_pdn_bit = 4,
27862306a36Sopenharmony_ci	},
27962306a36Sopenharmony_ci	[GPC_PGC_DOMAIN_PCI] = {
28062306a36Sopenharmony_ci		.base = {
28162306a36Sopenharmony_ci			.name = "PCI",
28262306a36Sopenharmony_ci			.power_off = imx6_pm_domain_power_off,
28362306a36Sopenharmony_ci			.power_on = imx6_pm_domain_power_on,
28462306a36Sopenharmony_ci		},
28562306a36Sopenharmony_ci		.reg_offs = 0x200,
28662306a36Sopenharmony_ci		.cntr_pdn_bit = 6,
28762306a36Sopenharmony_ci	},
28862306a36Sopenharmony_ci};
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistruct imx_gpc_dt_data {
29162306a36Sopenharmony_ci	int num_domains;
29262306a36Sopenharmony_ci	bool err009619_present;
29362306a36Sopenharmony_ci	bool err006287_present;
29462306a36Sopenharmony_ci};
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic const struct imx_gpc_dt_data imx6q_dt_data = {
29762306a36Sopenharmony_ci	.num_domains = 2,
29862306a36Sopenharmony_ci	.err009619_present = false,
29962306a36Sopenharmony_ci	.err006287_present = false,
30062306a36Sopenharmony_ci};
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic const struct imx_gpc_dt_data imx6qp_dt_data = {
30362306a36Sopenharmony_ci	.num_domains = 2,
30462306a36Sopenharmony_ci	.err009619_present = true,
30562306a36Sopenharmony_ci	.err006287_present = false,
30662306a36Sopenharmony_ci};
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic const struct imx_gpc_dt_data imx6sl_dt_data = {
30962306a36Sopenharmony_ci	.num_domains = 3,
31062306a36Sopenharmony_ci	.err009619_present = false,
31162306a36Sopenharmony_ci	.err006287_present = true,
31262306a36Sopenharmony_ci};
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic const struct imx_gpc_dt_data imx6sx_dt_data = {
31562306a36Sopenharmony_ci	.num_domains = 4,
31662306a36Sopenharmony_ci	.err009619_present = false,
31762306a36Sopenharmony_ci	.err006287_present = false,
31862306a36Sopenharmony_ci};
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic const struct of_device_id imx_gpc_dt_ids[] = {
32162306a36Sopenharmony_ci	{ .compatible = "fsl,imx6q-gpc", .data = &imx6q_dt_data },
32262306a36Sopenharmony_ci	{ .compatible = "fsl,imx6qp-gpc", .data = &imx6qp_dt_data },
32362306a36Sopenharmony_ci	{ .compatible = "fsl,imx6sl-gpc", .data = &imx6sl_dt_data },
32462306a36Sopenharmony_ci	{ .compatible = "fsl,imx6sx-gpc", .data = &imx6sx_dt_data },
32562306a36Sopenharmony_ci	{ }
32662306a36Sopenharmony_ci};
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic const struct regmap_range yes_ranges[] = {
32962306a36Sopenharmony_ci	regmap_reg_range(GPC_CNTR, GPC_CNTR),
33062306a36Sopenharmony_ci	regmap_reg_range(GPC_PGC_PCI_PDN, GPC_PGC_PCI_SR),
33162306a36Sopenharmony_ci	regmap_reg_range(GPC_PGC_GPU_PDN, GPC_PGC_GPU_SR),
33262306a36Sopenharmony_ci	regmap_reg_range(GPC_PGC_DISP_PDN, GPC_PGC_DISP_SR),
33362306a36Sopenharmony_ci};
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic const struct regmap_access_table access_table = {
33662306a36Sopenharmony_ci	.yes_ranges	= yes_ranges,
33762306a36Sopenharmony_ci	.n_yes_ranges	= ARRAY_SIZE(yes_ranges),
33862306a36Sopenharmony_ci};
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cistatic const struct regmap_config imx_gpc_regmap_config = {
34162306a36Sopenharmony_ci	.reg_bits = 32,
34262306a36Sopenharmony_ci	.val_bits = 32,
34362306a36Sopenharmony_ci	.reg_stride = 4,
34462306a36Sopenharmony_ci	.rd_table = &access_table,
34562306a36Sopenharmony_ci	.wr_table = &access_table,
34662306a36Sopenharmony_ci	.max_register = 0x2ac,
34762306a36Sopenharmony_ci	.fast_io = true,
34862306a36Sopenharmony_ci};
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic struct generic_pm_domain *imx_gpc_onecell_domains[] = {
35162306a36Sopenharmony_ci	&imx_gpc_domains[GPC_PGC_DOMAIN_ARM].base,
35262306a36Sopenharmony_ci	&imx_gpc_domains[GPC_PGC_DOMAIN_PU].base,
35362306a36Sopenharmony_ci};
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic struct genpd_onecell_data imx_gpc_onecell_data = {
35662306a36Sopenharmony_ci	.domains = imx_gpc_onecell_domains,
35762306a36Sopenharmony_ci	.num_domains = 2,
35862306a36Sopenharmony_ci};
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic int imx_gpc_old_dt_init(struct device *dev, struct regmap *regmap,
36162306a36Sopenharmony_ci			       unsigned int num_domains)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	struct imx_pm_domain *domain;
36462306a36Sopenharmony_ci	int i, ret;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	for (i = 0; i < num_domains; i++) {
36762306a36Sopenharmony_ci		domain = &imx_gpc_domains[i];
36862306a36Sopenharmony_ci		domain->regmap = regmap;
36962306a36Sopenharmony_ci		domain->ipg_rate_mhz = 66;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci		if (i == 1) {
37262306a36Sopenharmony_ci			domain->supply = devm_regulator_get(dev, "pu");
37362306a36Sopenharmony_ci			if (IS_ERR(domain->supply))
37462306a36Sopenharmony_ci				return PTR_ERR(domain->supply);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci			ret = imx_pgc_get_clocks(dev, domain);
37762306a36Sopenharmony_ci			if (ret)
37862306a36Sopenharmony_ci				goto clk_err;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci			domain->base.power_on(&domain->base);
38162306a36Sopenharmony_ci		}
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	for (i = 0; i < num_domains; i++)
38562306a36Sopenharmony_ci		pm_genpd_init(&imx_gpc_domains[i].base, NULL, false);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) {
38862306a36Sopenharmony_ci		ret = of_genpd_add_provider_onecell(dev->of_node,
38962306a36Sopenharmony_ci						    &imx_gpc_onecell_data);
39062306a36Sopenharmony_ci		if (ret)
39162306a36Sopenharmony_ci			goto genpd_err;
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	return 0;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cigenpd_err:
39762306a36Sopenharmony_ci	for (i = 0; i < num_domains; i++)
39862306a36Sopenharmony_ci		pm_genpd_remove(&imx_gpc_domains[i].base);
39962306a36Sopenharmony_ci	imx_pgc_put_clocks(&imx_gpc_domains[GPC_PGC_DOMAIN_PU]);
40062306a36Sopenharmony_ciclk_err:
40162306a36Sopenharmony_ci	return ret;
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic int imx_gpc_probe(struct platform_device *pdev)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	const struct of_device_id *of_id =
40762306a36Sopenharmony_ci			of_match_device(imx_gpc_dt_ids, &pdev->dev);
40862306a36Sopenharmony_ci	const struct imx_gpc_dt_data *of_id_data = of_id->data;
40962306a36Sopenharmony_ci	struct device_node *pgc_node;
41062306a36Sopenharmony_ci	struct regmap *regmap;
41162306a36Sopenharmony_ci	void __iomem *base;
41262306a36Sopenharmony_ci	int ret;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	pgc_node = of_get_child_by_name(pdev->dev.of_node, "pgc");
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	/* bail out if DT too old and doesn't provide the necessary info */
41762306a36Sopenharmony_ci	if (!of_property_read_bool(pdev->dev.of_node, "#power-domain-cells") &&
41862306a36Sopenharmony_ci	    !pgc_node)
41962306a36Sopenharmony_ci		return 0;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	base = devm_platform_ioremap_resource(pdev, 0);
42262306a36Sopenharmony_ci	if (IS_ERR(base))
42362306a36Sopenharmony_ci		return PTR_ERR(base);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base,
42662306a36Sopenharmony_ci					   &imx_gpc_regmap_config);
42762306a36Sopenharmony_ci	if (IS_ERR(regmap)) {
42862306a36Sopenharmony_ci		ret = PTR_ERR(regmap);
42962306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to init regmap: %d\n",
43062306a36Sopenharmony_ci			ret);
43162306a36Sopenharmony_ci		return ret;
43262306a36Sopenharmony_ci	}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	/*
43562306a36Sopenharmony_ci	 * Disable PU power down by runtime PM if ERR009619 is present.
43662306a36Sopenharmony_ci	 *
43762306a36Sopenharmony_ci	 * The PRE clock will be paused for several cycles when turning on the
43862306a36Sopenharmony_ci	 * PU domain LDO from power down state. If PRE is in use at that time,
43962306a36Sopenharmony_ci	 * the IPU/PRG cannot get the correct display data from the PRE.
44062306a36Sopenharmony_ci	 *
44162306a36Sopenharmony_ci	 * This is not a concern when the whole system enters suspend state, so
44262306a36Sopenharmony_ci	 * it's safe to power down PU in this case.
44362306a36Sopenharmony_ci	 */
44462306a36Sopenharmony_ci	if (of_id_data->err009619_present)
44562306a36Sopenharmony_ci		imx_gpc_domains[GPC_PGC_DOMAIN_PU].base.flags |=
44662306a36Sopenharmony_ci				GENPD_FLAG_RPM_ALWAYS_ON;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	/* Keep DISP always on if ERR006287 is present */
44962306a36Sopenharmony_ci	if (of_id_data->err006287_present)
45062306a36Sopenharmony_ci		imx_gpc_domains[GPC_PGC_DOMAIN_DISPLAY].base.flags |=
45162306a36Sopenharmony_ci				GENPD_FLAG_ALWAYS_ON;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	if (!pgc_node) {
45462306a36Sopenharmony_ci		ret = imx_gpc_old_dt_init(&pdev->dev, regmap,
45562306a36Sopenharmony_ci					  of_id_data->num_domains);
45662306a36Sopenharmony_ci		if (ret)
45762306a36Sopenharmony_ci			return ret;
45862306a36Sopenharmony_ci	} else {
45962306a36Sopenharmony_ci		struct imx_pm_domain *domain;
46062306a36Sopenharmony_ci		struct platform_device *pd_pdev;
46162306a36Sopenharmony_ci		struct device_node *np;
46262306a36Sopenharmony_ci		struct clk *ipg_clk;
46362306a36Sopenharmony_ci		unsigned int ipg_rate_mhz;
46462306a36Sopenharmony_ci		int domain_index;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci		ipg_clk = devm_clk_get(&pdev->dev, "ipg");
46762306a36Sopenharmony_ci		if (IS_ERR(ipg_clk))
46862306a36Sopenharmony_ci			return PTR_ERR(ipg_clk);
46962306a36Sopenharmony_ci		ipg_rate_mhz = clk_get_rate(ipg_clk) / 1000000;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci		for_each_child_of_node(pgc_node, np) {
47262306a36Sopenharmony_ci			ret = of_property_read_u32(np, "reg", &domain_index);
47362306a36Sopenharmony_ci			if (ret) {
47462306a36Sopenharmony_ci				of_node_put(np);
47562306a36Sopenharmony_ci				return ret;
47662306a36Sopenharmony_ci			}
47762306a36Sopenharmony_ci			if (domain_index >= of_id_data->num_domains)
47862306a36Sopenharmony_ci				continue;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci			pd_pdev = platform_device_alloc("imx-pgc-power-domain",
48162306a36Sopenharmony_ci							domain_index);
48262306a36Sopenharmony_ci			if (!pd_pdev) {
48362306a36Sopenharmony_ci				of_node_put(np);
48462306a36Sopenharmony_ci				return -ENOMEM;
48562306a36Sopenharmony_ci			}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci			ret = platform_device_add_data(pd_pdev,
48862306a36Sopenharmony_ci						       &imx_gpc_domains[domain_index],
48962306a36Sopenharmony_ci						       sizeof(imx_gpc_domains[domain_index]));
49062306a36Sopenharmony_ci			if (ret) {
49162306a36Sopenharmony_ci				platform_device_put(pd_pdev);
49262306a36Sopenharmony_ci				of_node_put(np);
49362306a36Sopenharmony_ci				return ret;
49462306a36Sopenharmony_ci			}
49562306a36Sopenharmony_ci			domain = pd_pdev->dev.platform_data;
49662306a36Sopenharmony_ci			domain->regmap = regmap;
49762306a36Sopenharmony_ci			domain->ipg_rate_mhz = ipg_rate_mhz;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci			pd_pdev->dev.parent = &pdev->dev;
50062306a36Sopenharmony_ci			pd_pdev->dev.of_node = np;
50162306a36Sopenharmony_ci			pd_pdev->dev.fwnode = of_fwnode_handle(np);
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci			ret = platform_device_add(pd_pdev);
50462306a36Sopenharmony_ci			if (ret) {
50562306a36Sopenharmony_ci				platform_device_put(pd_pdev);
50662306a36Sopenharmony_ci				of_node_put(np);
50762306a36Sopenharmony_ci				return ret;
50862306a36Sopenharmony_ci			}
50962306a36Sopenharmony_ci		}
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	return 0;
51362306a36Sopenharmony_ci}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_cistatic int imx_gpc_remove(struct platform_device *pdev)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	struct device_node *pgc_node;
51862306a36Sopenharmony_ci	int ret;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	pgc_node = of_get_child_by_name(pdev->dev.of_node, "pgc");
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	/* bail out if DT too old and doesn't provide the necessary info */
52362306a36Sopenharmony_ci	if (!of_property_read_bool(pdev->dev.of_node, "#power-domain-cells") &&
52462306a36Sopenharmony_ci	    !pgc_node)
52562306a36Sopenharmony_ci		return 0;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	/*
52862306a36Sopenharmony_ci	 * If the old DT binding is used the toplevel driver needs to
52962306a36Sopenharmony_ci	 * de-register the power domains
53062306a36Sopenharmony_ci	 */
53162306a36Sopenharmony_ci	if (!pgc_node) {
53262306a36Sopenharmony_ci		of_genpd_del_provider(pdev->dev.of_node);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci		ret = pm_genpd_remove(&imx_gpc_domains[GPC_PGC_DOMAIN_PU].base);
53562306a36Sopenharmony_ci		if (ret)
53662306a36Sopenharmony_ci			return ret;
53762306a36Sopenharmony_ci		imx_pgc_put_clocks(&imx_gpc_domains[GPC_PGC_DOMAIN_PU]);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci		ret = pm_genpd_remove(&imx_gpc_domains[GPC_PGC_DOMAIN_ARM].base);
54062306a36Sopenharmony_ci		if (ret)
54162306a36Sopenharmony_ci			return ret;
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	return 0;
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic struct platform_driver imx_gpc_driver = {
54862306a36Sopenharmony_ci	.driver = {
54962306a36Sopenharmony_ci		.name = "imx-gpc",
55062306a36Sopenharmony_ci		.of_match_table = imx_gpc_dt_ids,
55162306a36Sopenharmony_ci	},
55262306a36Sopenharmony_ci	.probe = imx_gpc_probe,
55362306a36Sopenharmony_ci	.remove = imx_gpc_remove,
55462306a36Sopenharmony_ci};
55562306a36Sopenharmony_cibuiltin_platform_driver(imx_gpc_driver)
556