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