Commit | Line | Data |
---|---|---|
afe51827 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
aa9f800d AF |
2 | /* |
3 | * Actions Semi Owl Smart Power System (SPS) | |
4 | * | |
5 | * Copyright 2012 Actions Semi Inc. | |
6 | * Author: Actions Semi, Inc. | |
7 | * | |
8 | * Copyright (c) 2017 Andreas Färber | |
aa9f800d AF |
9 | */ |
10 | ||
aa9f800d AF |
11 | #include <linux/of_address.h> |
12 | #include <linux/of_platform.h> | |
13 | #include <linux/pm_domain.h> | |
6932ec60 | 14 | #include <linux/soc/actions/owl-sps.h> |
aa9f800d | 15 | #include <dt-bindings/power/owl-s500-powergate.h> |
3ad85b08 | 16 | #include <dt-bindings/power/owl-s700-powergate.h> |
da8c37e1 | 17 | #include <dt-bindings/power/owl-s900-powergate.h> |
aa9f800d | 18 | |
aa9f800d AF |
19 | struct owl_sps_domain_info { |
20 | const char *name; | |
21 | int pwr_bit; | |
22 | int ack_bit; | |
23 | unsigned int genpd_flags; | |
24 | }; | |
25 | ||
26 | struct owl_sps_info { | |
27 | unsigned num_domains; | |
28 | const struct owl_sps_domain_info *domains; | |
29 | }; | |
30 | ||
31 | struct owl_sps { | |
32 | struct device *dev; | |
33 | const struct owl_sps_info *info; | |
34 | void __iomem *base; | |
35 | struct genpd_onecell_data genpd_data; | |
36 | struct generic_pm_domain *domains[]; | |
37 | }; | |
38 | ||
39 | #define to_owl_pd(gpd) container_of(gpd, struct owl_sps_domain, genpd) | |
40 | ||
41 | struct owl_sps_domain { | |
42 | struct generic_pm_domain genpd; | |
43 | const struct owl_sps_domain_info *info; | |
44 | struct owl_sps *sps; | |
45 | }; | |
46 | ||
47 | static int owl_sps_set_power(struct owl_sps_domain *pd, bool enable) | |
48 | { | |
6932ec60 | 49 | u32 pwr_mask, ack_mask; |
aa9f800d AF |
50 | |
51 | ack_mask = BIT(pd->info->ack_bit); | |
52 | pwr_mask = BIT(pd->info->pwr_bit); | |
aa9f800d | 53 | |
6932ec60 | 54 | return owl_sps_set_pg(pd->sps->base, pwr_mask, ack_mask, enable); |
aa9f800d AF |
55 | } |
56 | ||
57 | static int owl_sps_power_on(struct generic_pm_domain *domain) | |
58 | { | |
59 | struct owl_sps_domain *pd = to_owl_pd(domain); | |
60 | ||
61 | dev_dbg(pd->sps->dev, "%s power on", pd->info->name); | |
62 | ||
63 | return owl_sps_set_power(pd, true); | |
64 | } | |
65 | ||
66 | static int owl_sps_power_off(struct generic_pm_domain *domain) | |
67 | { | |
68 | struct owl_sps_domain *pd = to_owl_pd(domain); | |
69 | ||
70 | dev_dbg(pd->sps->dev, "%s power off", pd->info->name); | |
71 | ||
72 | return owl_sps_set_power(pd, false); | |
73 | } | |
74 | ||
75 | static int owl_sps_init_domain(struct owl_sps *sps, int index) | |
76 | { | |
77 | struct owl_sps_domain *pd; | |
78 | ||
79 | pd = devm_kzalloc(sps->dev, sizeof(*pd), GFP_KERNEL); | |
80 | if (!pd) | |
81 | return -ENOMEM; | |
82 | ||
83 | pd->info = &sps->info->domains[index]; | |
84 | pd->sps = sps; | |
85 | ||
86 | pd->genpd.name = pd->info->name; | |
87 | pd->genpd.power_on = owl_sps_power_on; | |
88 | pd->genpd.power_off = owl_sps_power_off; | |
89 | pd->genpd.flags = pd->info->genpd_flags; | |
90 | pm_genpd_init(&pd->genpd, NULL, false); | |
91 | ||
92 | sps->genpd_data.domains[index] = &pd->genpd; | |
93 | ||
94 | return 0; | |
95 | } | |
96 | ||
97 | static int owl_sps_probe(struct platform_device *pdev) | |
98 | { | |
99 | const struct of_device_id *match; | |
100 | const struct owl_sps_info *sps_info; | |
101 | struct owl_sps *sps; | |
102 | int i, ret; | |
103 | ||
104 | if (!pdev->dev.of_node) { | |
105 | dev_err(&pdev->dev, "no device node\n"); | |
106 | return -ENODEV; | |
107 | } | |
108 | ||
109 | match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev); | |
110 | if (!match || !match->data) { | |
111 | dev_err(&pdev->dev, "unknown compatible or missing data\n"); | |
112 | return -EINVAL; | |
113 | } | |
114 | ||
115 | sps_info = match->data; | |
116 | ||
0ed2dd03 KC |
117 | sps = devm_kzalloc(&pdev->dev, |
118 | struct_size(sps, domains, sps_info->num_domains), | |
aa9f800d AF |
119 | GFP_KERNEL); |
120 | if (!sps) | |
121 | return -ENOMEM; | |
122 | ||
123 | sps->base = of_io_request_and_map(pdev->dev.of_node, 0, "owl-sps"); | |
124 | if (IS_ERR(sps->base)) { | |
125 | dev_err(&pdev->dev, "failed to map sps registers\n"); | |
126 | return PTR_ERR(sps->base); | |
127 | } | |
128 | ||
129 | sps->dev = &pdev->dev; | |
130 | sps->info = sps_info; | |
131 | sps->genpd_data.domains = sps->domains; | |
132 | sps->genpd_data.num_domains = sps_info->num_domains; | |
133 | ||
134 | for (i = 0; i < sps_info->num_domains; i++) { | |
135 | ret = owl_sps_init_domain(sps, i); | |
136 | if (ret) | |
137 | return ret; | |
138 | } | |
139 | ||
140 | ret = of_genpd_add_provider_onecell(pdev->dev.of_node, &sps->genpd_data); | |
141 | if (ret) { | |
142 | dev_err(&pdev->dev, "failed to add provider (%d)", ret); | |
143 | return ret; | |
144 | } | |
145 | ||
146 | return 0; | |
147 | } | |
148 | ||
149 | static const struct owl_sps_domain_info s500_sps_domains[] = { | |
150 | [S500_PD_VDE] = { | |
151 | .name = "VDE", | |
152 | .pwr_bit = 0, | |
153 | .ack_bit = 16, | |
154 | }, | |
155 | [S500_PD_VCE_SI] = { | |
156 | .name = "VCE_SI", | |
157 | .pwr_bit = 1, | |
158 | .ack_bit = 17, | |
159 | }, | |
160 | [S500_PD_USB2_1] = { | |
161 | .name = "USB2_1", | |
162 | .pwr_bit = 2, | |
163 | .ack_bit = 18, | |
164 | }, | |
165 | [S500_PD_CPU2] = { | |
166 | .name = "CPU2", | |
167 | .pwr_bit = 5, | |
168 | .ack_bit = 21, | |
169 | .genpd_flags = GENPD_FLAG_ALWAYS_ON, | |
170 | }, | |
171 | [S500_PD_CPU3] = { | |
172 | .name = "CPU3", | |
173 | .pwr_bit = 6, | |
174 | .ack_bit = 22, | |
175 | .genpd_flags = GENPD_FLAG_ALWAYS_ON, | |
176 | }, | |
177 | [S500_PD_DMA] = { | |
178 | .name = "DMA", | |
179 | .pwr_bit = 8, | |
180 | .ack_bit = 12, | |
181 | }, | |
182 | [S500_PD_DS] = { | |
183 | .name = "DS", | |
184 | .pwr_bit = 9, | |
185 | .ack_bit = 13, | |
186 | }, | |
187 | [S500_PD_USB3] = { | |
188 | .name = "USB3", | |
189 | .pwr_bit = 10, | |
190 | .ack_bit = 14, | |
191 | }, | |
192 | [S500_PD_USB2_0] = { | |
193 | .name = "USB2_0", | |
194 | .pwr_bit = 11, | |
195 | .ack_bit = 15, | |
196 | }, | |
197 | }; | |
198 | ||
199 | static const struct owl_sps_info s500_sps_info = { | |
200 | .num_domains = ARRAY_SIZE(s500_sps_domains), | |
201 | .domains = s500_sps_domains, | |
202 | }; | |
203 | ||
3ad85b08 AF |
204 | static const struct owl_sps_domain_info s700_sps_domains[] = { |
205 | [S700_PD_VDE] = { | |
206 | .name = "VDE", | |
207 | .pwr_bit = 0, | |
208 | }, | |
209 | [S700_PD_VCE_SI] = { | |
210 | .name = "VCE_SI", | |
211 | .pwr_bit = 1, | |
212 | }, | |
213 | [S700_PD_USB2_1] = { | |
214 | .name = "USB2_1", | |
215 | .pwr_bit = 2, | |
216 | }, | |
217 | [S700_PD_HDE] = { | |
218 | .name = "HDE", | |
219 | .pwr_bit = 7, | |
220 | }, | |
221 | [S700_PD_DMA] = { | |
222 | .name = "DMA", | |
223 | .pwr_bit = 8, | |
224 | }, | |
225 | [S700_PD_DS] = { | |
226 | .name = "DS", | |
227 | .pwr_bit = 9, | |
228 | }, | |
229 | [S700_PD_USB3] = { | |
230 | .name = "USB3", | |
231 | .pwr_bit = 10, | |
232 | }, | |
233 | [S700_PD_USB2_0] = { | |
234 | .name = "USB2_0", | |
235 | .pwr_bit = 11, | |
236 | }, | |
237 | }; | |
238 | ||
239 | static const struct owl_sps_info s700_sps_info = { | |
240 | .num_domains = ARRAY_SIZE(s700_sps_domains), | |
241 | .domains = s700_sps_domains, | |
242 | }; | |
243 | ||
da8c37e1 MS |
244 | static const struct owl_sps_domain_info s900_sps_domains[] = { |
245 | [S900_PD_GPU_B] = { | |
246 | .name = "GPU_B", | |
247 | .pwr_bit = 3, | |
248 | }, | |
249 | [S900_PD_VCE] = { | |
250 | .name = "VCE", | |
251 | .pwr_bit = 4, | |
252 | }, | |
253 | [S900_PD_SENSOR] = { | |
254 | .name = "SENSOR", | |
255 | .pwr_bit = 5, | |
256 | }, | |
257 | [S900_PD_VDE] = { | |
258 | .name = "VDE", | |
259 | .pwr_bit = 6, | |
260 | }, | |
261 | [S900_PD_HDE] = { | |
262 | .name = "HDE", | |
263 | .pwr_bit = 7, | |
264 | }, | |
265 | [S900_PD_USB3] = { | |
266 | .name = "USB3", | |
267 | .pwr_bit = 8, | |
268 | }, | |
269 | [S900_PD_DDR0] = { | |
270 | .name = "DDR0", | |
271 | .pwr_bit = 9, | |
272 | }, | |
273 | [S900_PD_DDR1] = { | |
274 | .name = "DDR1", | |
275 | .pwr_bit = 10, | |
276 | }, | |
277 | [S900_PD_DE] = { | |
278 | .name = "DE", | |
279 | .pwr_bit = 13, | |
280 | }, | |
281 | [S900_PD_NAND] = { | |
282 | .name = "NAND", | |
283 | .pwr_bit = 14, | |
284 | }, | |
285 | [S900_PD_USB2_H0] = { | |
286 | .name = "USB2_H0", | |
287 | .pwr_bit = 15, | |
288 | }, | |
289 | [S900_PD_USB2_H1] = { | |
290 | .name = "USB2_H1", | |
291 | .pwr_bit = 16, | |
292 | }, | |
293 | }; | |
294 | ||
295 | static const struct owl_sps_info s900_sps_info = { | |
296 | .num_domains = ARRAY_SIZE(s900_sps_domains), | |
297 | .domains = s900_sps_domains, | |
298 | }; | |
299 | ||
aa9f800d AF |
300 | static const struct of_device_id owl_sps_of_matches[] = { |
301 | { .compatible = "actions,s500-sps", .data = &s500_sps_info }, | |
3ad85b08 | 302 | { .compatible = "actions,s700-sps", .data = &s700_sps_info }, |
da8c37e1 | 303 | { .compatible = "actions,s900-sps", .data = &s900_sps_info }, |
aa9f800d AF |
304 | { } |
305 | }; | |
306 | ||
307 | static struct platform_driver owl_sps_platform_driver = { | |
308 | .probe = owl_sps_probe, | |
309 | .driver = { | |
310 | .name = "owl-sps", | |
311 | .of_match_table = owl_sps_of_matches, | |
312 | .suppress_bind_attrs = true, | |
313 | }, | |
314 | }; | |
315 | ||
316 | static int __init owl_sps_init(void) | |
317 | { | |
318 | return platform_driver_register(&owl_sps_platform_driver); | |
319 | } | |
320 | postcore_initcall(owl_sps_init); |