1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (C) 2015 Linaro Ltd. 4 * 5 * Author: Jun Nie <jun.nie@linaro.org> 6 */ 7#include <linux/delay.h> 8#include <linux/err.h> 9#include <linux/io.h> 10#include <linux/of.h> 11#include <linux/platform_device.h> 12#include <linux/pm_domain.h> 13#include <linux/slab.h> 14 15#define PCU_DM_CLKEN 0x18 16#define PCU_DM_RSTEN 0x1C 17#define PCU_DM_ISOEN 0x20 18#define PCU_DM_PWRDN 0x24 19#define PCU_DM_ACK_SYNC 0x28 20 21enum { 22 PCU_DM_NEON0 = 0, 23 PCU_DM_NEON1, 24 PCU_DM_GPU, 25 PCU_DM_DECPPU, 26 PCU_DM_VOU, 27 PCU_DM_R2D, 28 PCU_DM_TOP, 29}; 30 31static void __iomem *pcubase; 32 33struct zx_pm_domain { 34 struct generic_pm_domain dm; 35 unsigned int bit; 36}; 37 38static int normal_power_off(struct generic_pm_domain *domain) 39{ 40 struct zx_pm_domain *zpd = (struct zx_pm_domain *)domain; 41 unsigned long loop = 1000; 42 u32 tmp; 43 44 tmp = readl_relaxed(pcubase + PCU_DM_CLKEN); 45 tmp &= ~BIT(zpd->bit); 46 writel_relaxed(tmp, pcubase + PCU_DM_CLKEN); 47 udelay(5); 48 49 tmp = readl_relaxed(pcubase + PCU_DM_ISOEN); 50 tmp &= ~BIT(zpd->bit); 51 writel_relaxed(tmp | BIT(zpd->bit), pcubase + PCU_DM_ISOEN); 52 udelay(5); 53 54 tmp = readl_relaxed(pcubase + PCU_DM_RSTEN); 55 tmp &= ~BIT(zpd->bit); 56 writel_relaxed(tmp, pcubase + PCU_DM_RSTEN); 57 udelay(5); 58 59 tmp = readl_relaxed(pcubase + PCU_DM_PWRDN); 60 tmp &= ~BIT(zpd->bit); 61 writel_relaxed(tmp | BIT(zpd->bit), pcubase + PCU_DM_PWRDN); 62 do { 63 tmp = readl_relaxed(pcubase + PCU_DM_ACK_SYNC) & BIT(zpd->bit); 64 } while (--loop && !tmp); 65 66 if (!loop) { 67 pr_err("Error: %s %s fail\n", __func__, domain->name); 68 return -EIO; 69 } 70 71 return 0; 72} 73 74static int normal_power_on(struct generic_pm_domain *domain) 75{ 76 struct zx_pm_domain *zpd = (struct zx_pm_domain *)domain; 77 unsigned long loop = 10000; 78 u32 tmp; 79 80 tmp = readl_relaxed(pcubase + PCU_DM_PWRDN); 81 tmp &= ~BIT(zpd->bit); 82 writel_relaxed(tmp, pcubase + PCU_DM_PWRDN); 83 do { 84 tmp = readl_relaxed(pcubase + PCU_DM_ACK_SYNC) & BIT(zpd->bit); 85 } while (--loop && tmp); 86 87 if (!loop) { 88 pr_err("Error: %s %s fail\n", __func__, domain->name); 89 return -EIO; 90 } 91 92 tmp = readl_relaxed(pcubase + PCU_DM_RSTEN); 93 tmp &= ~BIT(zpd->bit); 94 writel_relaxed(tmp | BIT(zpd->bit), pcubase + PCU_DM_RSTEN); 95 udelay(5); 96 97 tmp = readl_relaxed(pcubase + PCU_DM_ISOEN); 98 tmp &= ~BIT(zpd->bit); 99 writel_relaxed(tmp, pcubase + PCU_DM_ISOEN); 100 udelay(5); 101 102 tmp = readl_relaxed(pcubase + PCU_DM_CLKEN); 103 tmp &= ~BIT(zpd->bit); 104 writel_relaxed(tmp | BIT(zpd->bit), pcubase + PCU_DM_CLKEN); 105 udelay(5); 106 return 0; 107} 108 109static struct zx_pm_domain gpu_domain = { 110 .dm = { 111 .name = "gpu_domain", 112 .power_off = normal_power_off, 113 .power_on = normal_power_on, 114 }, 115 .bit = PCU_DM_GPU, 116}; 117 118static struct zx_pm_domain decppu_domain = { 119 .dm = { 120 .name = "decppu_domain", 121 .power_off = normal_power_off, 122 .power_on = normal_power_on, 123 }, 124 .bit = PCU_DM_DECPPU, 125}; 126 127static struct zx_pm_domain vou_domain = { 128 .dm = { 129 .name = "vou_domain", 130 .power_off = normal_power_off, 131 .power_on = normal_power_on, 132 }, 133 .bit = PCU_DM_VOU, 134}; 135 136static struct zx_pm_domain r2d_domain = { 137 .dm = { 138 .name = "r2d_domain", 139 .power_off = normal_power_off, 140 .power_on = normal_power_on, 141 }, 142 .bit = PCU_DM_R2D, 143}; 144 145static struct generic_pm_domain *zx296702_pm_domains[] = { 146 &vou_domain.dm, 147 &gpu_domain.dm, 148 &decppu_domain.dm, 149 &r2d_domain.dm, 150}; 151 152static int zx296702_pd_probe(struct platform_device *pdev) 153{ 154 struct genpd_onecell_data *genpd_data; 155 struct resource *res; 156 int i; 157 158 genpd_data = devm_kzalloc(&pdev->dev, sizeof(*genpd_data), GFP_KERNEL); 159 if (!genpd_data) 160 return -ENOMEM; 161 162 genpd_data->domains = zx296702_pm_domains; 163 genpd_data->num_domains = ARRAY_SIZE(zx296702_pm_domains); 164 165 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 166 if (!res) { 167 dev_err(&pdev->dev, "no memory resource defined\n"); 168 return -ENODEV; 169 } 170 171 pcubase = devm_ioremap_resource(&pdev->dev, res); 172 if (IS_ERR(pcubase)) { 173 dev_err(&pdev->dev, "ioremap fail.\n"); 174 return -EIO; 175 } 176 177 for (i = 0; i < ARRAY_SIZE(zx296702_pm_domains); ++i) 178 pm_genpd_init(zx296702_pm_domains[i], NULL, false); 179 180 of_genpd_add_provider_onecell(pdev->dev.of_node, genpd_data); 181 return 0; 182} 183 184static const struct of_device_id zx296702_pm_domain_matches[] __initconst = { 185 { .compatible = "zte,zx296702-pcu", }, 186 { }, 187}; 188 189static struct platform_driver zx296702_pd_driver __initdata = { 190 .driver = { 191 .name = "zx-powerdomain", 192 .owner = THIS_MODULE, 193 .of_match_table = zx296702_pm_domain_matches, 194 }, 195 .probe = zx296702_pd_probe, 196}; 197 198static int __init zx296702_pd_init(void) 199{ 200 return platform_driver_register(&zx296702_pd_driver); 201} 202subsys_initcall(zx296702_pd_init); 203