diff options
Diffstat (limited to 'arch/arm/mach-zx')
-rw-r--r-- | arch/arm/mach-zx/Kconfig | 1 | ||||
-rw-r--r-- | arch/arm/mach-zx/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-zx/zx296702-pm-domain.c | 202 |
3 files changed, 204 insertions, 1 deletions
diff --git a/arch/arm/mach-zx/Kconfig b/arch/arm/mach-zx/Kconfig index 2a910dc0d15e..7fdc5bf24f9b 100644 --- a/arch/arm/mach-zx/Kconfig +++ b/arch/arm/mach-zx/Kconfig | |||
@@ -13,6 +13,7 @@ config SOC_ZX296702 | |||
13 | select ARM_GLOBAL_TIMER | 13 | select ARM_GLOBAL_TIMER |
14 | select HAVE_ARM_SCU if SMP | 14 | select HAVE_ARM_SCU if SMP |
15 | select HAVE_ARM_TWD if SMP | 15 | select HAVE_ARM_TWD if SMP |
16 | select PM_GENERIC_DOMAINS | ||
16 | help | 17 | help |
17 | Support for ZTE ZX296702 SoC which is a dual core CortexA9MP | 18 | Support for ZTE ZX296702 SoC which is a dual core CortexA9MP |
18 | endif | 19 | endif |
diff --git a/arch/arm/mach-zx/Makefile b/arch/arm/mach-zx/Makefile index 7c2edf6e5f8b..a4b486433209 100644 --- a/arch/arm/mach-zx/Makefile +++ b/arch/arm/mach-zx/Makefile | |||
@@ -1,2 +1,2 @@ | |||
1 | obj-$(CONFIG_SOC_ZX296702) += zx296702.o | 1 | obj-$(CONFIG_SOC_ZX296702) += zx296702.o zx296702-pm-domain.o |
2 | obj-$(CONFIG_SMP) += headsmp.o platsmp.o | 2 | obj-$(CONFIG_SMP) += headsmp.o platsmp.o |
diff --git a/arch/arm/mach-zx/zx296702-pm-domain.c b/arch/arm/mach-zx/zx296702-pm-domain.c new file mode 100644 index 000000000000..e08574d4e2ca --- /dev/null +++ b/arch/arm/mach-zx/zx296702-pm-domain.c | |||
@@ -0,0 +1,202 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2015 Linaro Ltd. | ||
3 | * | ||
4 | * Author: Jun Nie <jun.nie@linaro.org> | ||
5 | * License terms: GNU General Public License (GPL) version 2 | ||
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 | |||
21 | enum { | ||
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 | |||
31 | static void __iomem *pcubase; | ||
32 | |||
33 | struct zx_pm_domain { | ||
34 | struct generic_pm_domain dm; | ||
35 | unsigned int bit; | ||
36 | }; | ||
37 | |||
38 | static 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 | |||
74 | static 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 | |||
109 | static 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 | |||
118 | static 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 | |||
127 | static 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 | |||
136 | static 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 | |||
145 | static 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 | |||
152 | static 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 (!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 | |||
184 | static const struct of_device_id zx296702_pm_domain_matches[] __initconst = { | ||
185 | { .compatible = "zte,zx296702-pcu", }, | ||
186 | { }, | ||
187 | }; | ||
188 | |||
189 | static 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 | |||
198 | static int __init zx296702_pd_init(void) | ||
199 | { | ||
200 | return platform_driver_register(&zx296702_pd_driver); | ||
201 | } | ||
202 | subsys_initcall(zx296702_pd_init); | ||