Commit | Line | Data |
---|---|---|
279b7e8a RN |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (c) 2018, The Linux Foundation. All rights reserved.*/ | |
3 | ||
4 | #include <linux/err.h> | |
5 | #include <linux/init.h> | |
6 | #include <linux/kernel.h> | |
d4889ec1 | 7 | #include <linux/module.h> |
279b7e8a RN |
8 | #include <linux/mutex.h> |
9 | #include <linux/pm_domain.h> | |
10 | #include <linux/slab.h> | |
11 | #include <linux/of.h> | |
12 | #include <linux/of_device.h> | |
13 | #include <linux/platform_device.h> | |
14 | #include <linux/pm_opp.h> | |
15 | #include <soc/qcom/cmd-db.h> | |
16 | #include <soc/qcom/rpmh.h> | |
17 | #include <dt-bindings/power/qcom-rpmpd.h> | |
18 | ||
19 | #define domain_to_rpmhpd(domain) container_of(domain, struct rpmhpd, pd) | |
20 | ||
21 | #define RPMH_ARC_MAX_LEVELS 16 | |
22 | ||
23 | /** | |
24 | * struct rpmhpd - top level RPMh power domain resource data structure | |
25 | * @dev: rpmh power domain controller device | |
a20e55d4 JL |
26 | * @pd: generic_pm_domain corresponding to the power domain |
27 | * @parent: generic_pm_domain corresponding to the parent's power domain | |
279b7e8a RN |
28 | * @peer: A peer power domain in case Active only Voting is |
29 | * supported | |
30 | * @active_only: True if it represents an Active only peer | |
5d16af6a LJ |
31 | * @corner: current corner |
32 | * @active_corner: current active corner | |
e3e56c05 | 33 | * @enable_corner: lowest non-zero corner |
279b7e8a RN |
34 | * @level: An array of level (vlvl) to corner (hlvl) mappings |
35 | * derived from cmd-db | |
36 | * @level_count: Number of levels supported by the power domain. max | |
37 | * being 16 (0 - 15) | |
38 | * @enabled: true if the power domain is enabled | |
39 | * @res_name: Resource name used for cmd-db lookup | |
40 | * @addr: Resource address as looped up using resource name from | |
41 | * cmd-db | |
42 | */ | |
43 | struct rpmhpd { | |
44 | struct device *dev; | |
45 | struct generic_pm_domain pd; | |
46 | struct generic_pm_domain *parent; | |
47 | struct rpmhpd *peer; | |
48 | const bool active_only; | |
49 | unsigned int corner; | |
50 | unsigned int active_corner; | |
e3e56c05 | 51 | unsigned int enable_corner; |
279b7e8a RN |
52 | u32 level[RPMH_ARC_MAX_LEVELS]; |
53 | size_t level_count; | |
54 | bool enabled; | |
55 | const char *res_name; | |
56 | u32 addr; | |
57 | }; | |
58 | ||
59 | struct rpmhpd_desc { | |
60 | struct rpmhpd **rpmhpds; | |
61 | size_t num_pds; | |
62 | }; | |
63 | ||
64 | static DEFINE_MUTEX(rpmhpd_lock); | |
65 | ||
09bb67c1 | 66 | /* RPMH powerdomains */ |
279b7e8a | 67 | |
09bb67c1 | 68 | static struct rpmhpd cx_ao; |
90c74c1c RN |
69 | static struct rpmhpd mx; |
70 | static struct rpmhpd mx_ao; | |
09bb67c1 RN |
71 | static struct rpmhpd cx = { |
72 | .pd = { .name = "cx", }, | |
73 | .peer = &cx_ao, | |
74 | .res_name = "cx.lvl", | |
75 | }; | |
76 | ||
77 | static struct rpmhpd cx_ao = { | |
78 | .pd = { .name = "cx_ao", }, | |
79 | .active_only = true, | |
80 | .peer = &cx, | |
81 | .res_name = "cx.lvl", | |
82 | }; | |
83 | ||
84 | static struct rpmhpd cx_ao_w_mx_parent; | |
85 | static struct rpmhpd cx_w_mx_parent = { | |
279b7e8a | 86 | .pd = { .name = "cx", }, |
09bb67c1 RN |
87 | .peer = &cx_ao_w_mx_parent, |
88 | .parent = &mx.pd, | |
279b7e8a RN |
89 | .res_name = "cx.lvl", |
90 | }; | |
91 | ||
09bb67c1 | 92 | static struct rpmhpd cx_ao_w_mx_parent = { |
279b7e8a | 93 | .pd = { .name = "cx_ao", }, |
5d0d4d42 | 94 | .active_only = true, |
09bb67c1 RN |
95 | .peer = &cx_w_mx_parent, |
96 | .parent = &mx_ao.pd, | |
279b7e8a RN |
97 | .res_name = "cx.lvl", |
98 | }; | |
99 | ||
90c74c1c RN |
100 | static struct rpmhpd ebi = { |
101 | .pd = { .name = "ebi", }, | |
102 | .res_name = "ebi.lvl", | |
103 | }; | |
104 | ||
105 | static struct rpmhpd gfx = { | |
106 | .pd = { .name = "gfx", }, | |
107 | .res_name = "gfx.lvl", | |
108 | }; | |
109 | ||
110 | static struct rpmhpd lcx = { | |
111 | .pd = { .name = "lcx", }, | |
112 | .res_name = "lcx.lvl", | |
113 | }; | |
114 | ||
115 | static struct rpmhpd lmx = { | |
116 | .pd = { .name = "lmx", }, | |
117 | .res_name = "lmx.lvl", | |
118 | }; | |
119 | ||
09bb67c1 RN |
120 | static struct rpmhpd mmcx_ao; |
121 | static struct rpmhpd mmcx = { | |
122 | .pd = { .name = "mmcx", }, | |
123 | .peer = &mmcx_ao, | |
124 | .res_name = "mmcx.lvl", | |
125 | }; | |
126 | ||
127 | static struct rpmhpd mmcx_ao = { | |
128 | .pd = { .name = "mmcx_ao", }, | |
129 | .active_only = true, | |
130 | .peer = &mmcx, | |
131 | .res_name = "mmcx.lvl", | |
132 | }; | |
133 | ||
134 | static struct rpmhpd mmcx_ao_w_cx_parent; | |
135 | static struct rpmhpd mmcx_w_cx_parent = { | |
136 | .pd = { .name = "mmcx", }, | |
137 | .peer = &mmcx_ao_w_cx_parent, | |
138 | .parent = &cx.pd, | |
139 | .res_name = "mmcx.lvl", | |
140 | }; | |
141 | ||
142 | static struct rpmhpd mmcx_ao_w_cx_parent = { | |
143 | .pd = { .name = "mmcx_ao", }, | |
144 | .active_only = true, | |
145 | .peer = &mmcx_w_cx_parent, | |
146 | .parent = &cx_ao.pd, | |
147 | .res_name = "mmcx.lvl", | |
148 | }; | |
149 | ||
90c74c1c RN |
150 | static struct rpmhpd mss = { |
151 | .pd = { .name = "mss", }, | |
152 | .res_name = "mss.lvl", | |
153 | }; | |
154 | ||
155 | static struct rpmhpd mx_ao; | |
156 | static struct rpmhpd mx = { | |
157 | .pd = { .name = "mx", }, | |
158 | .peer = &mx_ao, | |
159 | .res_name = "mx.lvl", | |
160 | }; | |
161 | ||
162 | static struct rpmhpd mx_ao = { | |
163 | .pd = { .name = "mx_ao", }, | |
164 | .active_only = true, | |
165 | .peer = &mx, | |
166 | .res_name = "mx.lvl", | |
167 | }; | |
168 | ||
09bb67c1 RN |
169 | static struct rpmhpd mxc_ao; |
170 | static struct rpmhpd mxc = { | |
171 | .pd = { .name = "mxc", }, | |
172 | .peer = &mxc_ao, | |
173 | .res_name = "mxc.lvl", | |
174 | }; | |
175 | ||
176 | static struct rpmhpd mxc_ao = { | |
177 | .pd = { .name = "mxc_ao", }, | |
178 | .active_only = true, | |
179 | .peer = &mxc, | |
180 | .res_name = "mxc.lvl", | |
181 | }; | |
182 | ||
f68f1cb3 BA |
183 | static struct rpmhpd nsp = { |
184 | .pd = { .name = "nsp", }, | |
185 | .res_name = "nsp.lvl", | |
186 | }; | |
187 | ||
188 | static struct rpmhpd qphy = { | |
189 | .pd = { .name = "qphy", }, | |
190 | .res_name = "qphy.lvl", | |
191 | }; | |
192 | ||
193 | /* SA8540P RPMH powerdomains */ | |
194 | static struct rpmhpd *sa8540p_rpmhpds[] = { | |
195 | [SC8280XP_CX] = &cx, | |
196 | [SC8280XP_CX_AO] = &cx_ao, | |
197 | [SC8280XP_EBI] = &ebi, | |
198 | [SC8280XP_GFX] = &gfx, | |
199 | [SC8280XP_LCX] = &lcx, | |
200 | [SC8280XP_LMX] = &lmx, | |
201 | [SC8280XP_MMCX] = &mmcx, | |
202 | [SC8280XP_MMCX_AO] = &mmcx_ao, | |
203 | [SC8280XP_MX] = &mx, | |
204 | [SC8280XP_MX_AO] = &mx_ao, | |
205 | [SC8280XP_NSP] = &nsp, | |
206 | }; | |
207 | ||
208 | static const struct rpmhpd_desc sa8540p_desc = { | |
209 | .rpmhpds = sa8540p_rpmhpds, | |
210 | .num_pds = ARRAY_SIZE(sa8540p_rpmhpds), | |
211 | }; | |
212 | ||
09bb67c1 | 213 | /* SDM845 RPMH powerdomains */ |
279b7e8a | 214 | static struct rpmhpd *sdm845_rpmhpds[] = { |
09bb67c1 RN |
215 | [SDM845_CX] = &cx_w_mx_parent, |
216 | [SDM845_CX_AO] = &cx_ao_w_mx_parent, | |
90c74c1c | 217 | [SDM845_EBI] = &ebi, |
09bb67c1 | 218 | [SDM845_GFX] = &gfx, |
90c74c1c RN |
219 | [SDM845_LCX] = &lcx, |
220 | [SDM845_LMX] = &lmx, | |
09bb67c1 | 221 | [SDM845_MSS] = &mss, |
90c74c1c RN |
222 | [SDM845_MX] = &mx, |
223 | [SDM845_MX_AO] = &mx_ao, | |
279b7e8a RN |
224 | }; |
225 | ||
226 | static const struct rpmhpd_desc sdm845_desc = { | |
227 | .rpmhpds = sdm845_rpmhpds, | |
228 | .num_pds = ARRAY_SIZE(sdm845_rpmhpds), | |
229 | }; | |
230 | ||
9c456626 VK |
231 | /* SDX55 RPMH powerdomains */ |
232 | static struct rpmhpd *sdx55_rpmhpds[] = { | |
90c74c1c | 233 | [SDX55_CX] = &cx_w_mx_parent, |
09bb67c1 RN |
234 | [SDX55_MSS] = &mss, |
235 | [SDX55_MX] = &mx, | |
9c456626 VK |
236 | }; |
237 | ||
238 | static const struct rpmhpd_desc sdx55_desc = { | |
239 | .rpmhpds = sdx55_rpmhpds, | |
240 | .num_pds = ARRAY_SIZE(sdx55_rpmhpds), | |
241 | }; | |
242 | ||
3edff626 RA |
243 | /* SDX65 RPMH powerdomains */ |
244 | static struct rpmhpd *sdx65_rpmhpds[] = { | |
245 | [SDX65_CX] = &cx_w_mx_parent, | |
246 | [SDX65_CX_AO] = &cx_ao_w_mx_parent, | |
247 | [SDX65_MSS] = &mss, | |
248 | [SDX65_MX] = &mx, | |
249 | [SDX65_MX_AO] = &mx_ao, | |
250 | [SDX65_MXC] = &mxc, | |
251 | }; | |
252 | ||
253 | static const struct rpmhpd_desc sdx65_desc = { | |
254 | .rpmhpds = sdx65_rpmhpds, | |
255 | .num_pds = ARRAY_SIZE(sdx65_rpmhpds), | |
256 | }; | |
257 | ||
c2b854b0 KD |
258 | /* SM6350 RPMH powerdomains */ |
259 | static struct rpmhpd *sm6350_rpmhpds[] = { | |
09bb67c1 RN |
260 | [SM6350_CX] = &cx_w_mx_parent, |
261 | [SM6350_GFX] = &gfx, | |
262 | [SM6350_LCX] = &lcx, | |
263 | [SM6350_LMX] = &lmx, | |
264 | [SM6350_MSS] = &mss, | |
265 | [SM6350_MX] = &mx, | |
c2b854b0 KD |
266 | }; |
267 | ||
268 | static const struct rpmhpd_desc sm6350_desc = { | |
269 | .rpmhpds = sm6350_rpmhpds, | |
270 | .num_pds = ARRAY_SIZE(sm6350_rpmhpds), | |
271 | }; | |
272 | ||
4e6a2011 | 273 | /* SM8150 RPMH powerdomains */ |
4e6a2011 | 274 | static struct rpmhpd *sm8150_rpmhpds[] = { |
09bb67c1 RN |
275 | [SM8150_CX] = &cx_w_mx_parent, |
276 | [SM8150_CX_AO] = &cx_ao_w_mx_parent, | |
90c74c1c RN |
277 | [SM8150_EBI] = &ebi, |
278 | [SM8150_GFX] = &gfx, | |
279 | [SM8150_LCX] = &lcx, | |
280 | [SM8150_LMX] = &lmx, | |
09bb67c1 RN |
281 | [SM8150_MMCX] = &mmcx, |
282 | [SM8150_MMCX_AO] = &mmcx_ao, | |
90c74c1c RN |
283 | [SM8150_MSS] = &mss, |
284 | [SM8150_MX] = &mx, | |
285 | [SM8150_MX_AO] = &mx_ao, | |
4e6a2011 SS |
286 | }; |
287 | ||
288 | static const struct rpmhpd_desc sm8150_desc = { | |
289 | .rpmhpds = sm8150_rpmhpds, | |
290 | .num_pds = ARRAY_SIZE(sm8150_rpmhpds), | |
291 | }; | |
292 | ||
09bb67c1 | 293 | /* SM8250 RPMH powerdomains */ |
64016bb8 | 294 | static struct rpmhpd *sm8250_rpmhpds[] = { |
09bb67c1 RN |
295 | [SM8250_CX] = &cx_w_mx_parent, |
296 | [SM8250_CX_AO] = &cx_ao_w_mx_parent, | |
297 | [SM8250_EBI] = &ebi, | |
298 | [SM8250_GFX] = &gfx, | |
299 | [SM8250_LCX] = &lcx, | |
300 | [SM8250_LMX] = &lmx, | |
301 | [SM8250_MMCX] = &mmcx, | |
302 | [SM8250_MMCX_AO] = &mmcx_ao, | |
303 | [SM8250_MX] = &mx, | |
304 | [SM8250_MX_AO] = &mx_ao, | |
64016bb8 BA |
305 | }; |
306 | ||
307 | static const struct rpmhpd_desc sm8250_desc = { | |
308 | .rpmhpds = sm8250_rpmhpds, | |
309 | .num_pds = ARRAY_SIZE(sm8250_rpmhpds), | |
310 | }; | |
311 | ||
639c8562 | 312 | /* SM8350 Power domains */ |
639c8562 | 313 | static struct rpmhpd *sm8350_rpmhpds[] = { |
09bb67c1 RN |
314 | [SM8350_CX] = &cx_w_mx_parent, |
315 | [SM8350_CX_AO] = &cx_ao_w_mx_parent, | |
316 | [SM8350_EBI] = &ebi, | |
317 | [SM8350_GFX] = &gfx, | |
318 | [SM8350_LCX] = &lcx, | |
319 | [SM8350_LMX] = &lmx, | |
320 | [SM8350_MMCX] = &mmcx, | |
321 | [SM8350_MMCX_AO] = &mmcx_ao, | |
90c74c1c | 322 | [SM8350_MSS] = &mss, |
09bb67c1 RN |
323 | [SM8350_MX] = &mx, |
324 | [SM8350_MX_AO] = &mx_ao, | |
325 | [SM8350_MXC] = &mxc, | |
326 | [SM8350_MXC_AO] = &mxc_ao, | |
639c8562 VK |
327 | }; |
328 | ||
329 | static const struct rpmhpd_desc sm8350_desc = { | |
330 | .rpmhpds = sm8350_rpmhpds, | |
331 | .num_pds = ARRAY_SIZE(sm8350_rpmhpds), | |
332 | }; | |
333 | ||
5d122895 | 334 | /* SM8450 RPMH powerdomains */ |
5d122895 | 335 | static struct rpmhpd *sm8450_rpmhpds[] = { |
09bb67c1 RN |
336 | [SM8450_CX] = &cx, |
337 | [SM8450_CX_AO] = &cx_ao, | |
338 | [SM8450_EBI] = &ebi, | |
339 | [SM8450_GFX] = &gfx, | |
340 | [SM8450_LCX] = &lcx, | |
341 | [SM8450_LMX] = &lmx, | |
342 | [SM8450_MMCX] = &mmcx_w_cx_parent, | |
343 | [SM8450_MMCX_AO] = &mmcx_ao_w_cx_parent, | |
90c74c1c | 344 | [SM8450_MSS] = &mss, |
09bb67c1 RN |
345 | [SM8450_MX] = &mx, |
346 | [SM8450_MX_AO] = &mx_ao, | |
347 | [SM8450_MXC] = &mxc, | |
348 | [SM8450_MXC_AO] = &mxc_ao, | |
5d122895 DB |
349 | }; |
350 | ||
351 | static const struct rpmhpd_desc sm8450_desc = { | |
352 | .rpmhpds = sm8450_rpmhpds, | |
353 | .num_pds = ARRAY_SIZE(sm8450_rpmhpds), | |
354 | }; | |
355 | ||
a30657b6 SS |
356 | /* SC7180 RPMH powerdomains */ |
357 | static struct rpmhpd *sc7180_rpmhpds[] = { | |
09bb67c1 RN |
358 | [SC7180_CX] = &cx_w_mx_parent, |
359 | [SC7180_CX_AO] = &cx_ao_w_mx_parent, | |
360 | [SC7180_GFX] = &gfx, | |
09bb67c1 | 361 | [SC7180_LCX] = &lcx, |
90c74c1c | 362 | [SC7180_LMX] = &lmx, |
09bb67c1 | 363 | [SC7180_MSS] = &mss, |
90c74c1c RN |
364 | [SC7180_MX] = &mx, |
365 | [SC7180_MX_AO] = &mx_ao, | |
a30657b6 SS |
366 | }; |
367 | ||
368 | static const struct rpmhpd_desc sc7180_desc = { | |
369 | .rpmhpds = sc7180_rpmhpds, | |
370 | .num_pds = ARRAY_SIZE(sc7180_rpmhpds), | |
371 | }; | |
372 | ||
9937447d RN |
373 | /* SC7280 RPMH powerdomains */ |
374 | static struct rpmhpd *sc7280_rpmhpds[] = { | |
7d6a0a4d RN |
375 | [SC7280_CX] = &cx, |
376 | [SC7280_CX_AO] = &cx_ao, | |
09bb67c1 RN |
377 | [SC7280_EBI] = &ebi, |
378 | [SC7280_GFX] = &gfx, | |
09bb67c1 | 379 | [SC7280_LCX] = &lcx, |
90c74c1c | 380 | [SC7280_LMX] = &lmx, |
09bb67c1 | 381 | [SC7280_MSS] = &mss, |
90c74c1c RN |
382 | [SC7280_MX] = &mx, |
383 | [SC7280_MX_AO] = &mx_ao, | |
9937447d RN |
384 | }; |
385 | ||
386 | static const struct rpmhpd_desc sc7280_desc = { | |
387 | .rpmhpds = sc7280_rpmhpds, | |
388 | .num_pds = ARRAY_SIZE(sc7280_rpmhpds), | |
389 | }; | |
390 | ||
3b1a0582 BA |
391 | /* SC8180x RPMH powerdomains */ |
392 | static struct rpmhpd *sc8180x_rpmhpds[] = { | |
09bb67c1 RN |
393 | [SC8180X_CX] = &cx_w_mx_parent, |
394 | [SC8180X_CX_AO] = &cx_ao_w_mx_parent, | |
395 | [SC8180X_EBI] = &ebi, | |
396 | [SC8180X_GFX] = &gfx, | |
397 | [SC8180X_LCX] = &lcx, | |
398 | [SC8180X_LMX] = &lmx, | |
399 | [SC8180X_MMCX] = &mmcx, | |
400 | [SC8180X_MMCX_AO] = &mmcx_ao, | |
401 | [SC8180X_MSS] = &mss, | |
402 | [SC8180X_MX] = &mx, | |
403 | [SC8180X_MX_AO] = &mx_ao, | |
3b1a0582 BA |
404 | }; |
405 | ||
406 | static const struct rpmhpd_desc sc8180x_desc = { | |
407 | .rpmhpds = sc8180x_rpmhpds, | |
408 | .num_pds = ARRAY_SIZE(sc8180x_rpmhpds), | |
409 | }; | |
410 | ||
f68f1cb3 BA |
411 | /* SC8280xp RPMH powerdomains */ |
412 | static struct rpmhpd *sc8280xp_rpmhpds[] = { | |
413 | [SC8280XP_CX] = &cx, | |
414 | [SC8280XP_CX_AO] = &cx_ao, | |
415 | [SC8280XP_EBI] = &ebi, | |
416 | [SC8280XP_GFX] = &gfx, | |
417 | [SC8280XP_LCX] = &lcx, | |
418 | [SC8280XP_LMX] = &lmx, | |
419 | [SC8280XP_MMCX] = &mmcx, | |
420 | [SC8280XP_MMCX_AO] = &mmcx_ao, | |
421 | [SC8280XP_MX] = &mx, | |
422 | [SC8280XP_MX_AO] = &mx_ao, | |
423 | [SC8280XP_NSP] = &nsp, | |
424 | [SC8280XP_QPHY] = &qphy, | |
425 | }; | |
426 | ||
427 | static const struct rpmhpd_desc sc8280xp_desc = { | |
428 | .rpmhpds = sc8280xp_rpmhpds, | |
429 | .num_pds = ARRAY_SIZE(sc8280xp_rpmhpds), | |
430 | }; | |
431 | ||
279b7e8a | 432 | static const struct of_device_id rpmhpd_match_table[] = { |
f68f1cb3 | 433 | { .compatible = "qcom,sa8540p-rpmhpd", .data = &sa8540p_desc }, |
a30657b6 | 434 | { .compatible = "qcom,sc7180-rpmhpd", .data = &sc7180_desc }, |
9937447d | 435 | { .compatible = "qcom,sc7280-rpmhpd", .data = &sc7280_desc }, |
3b1a0582 | 436 | { .compatible = "qcom,sc8180x-rpmhpd", .data = &sc8180x_desc }, |
f68f1cb3 | 437 | { .compatible = "qcom,sc8280xp-rpmhpd", .data = &sc8280xp_desc }, |
279b7e8a | 438 | { .compatible = "qcom,sdm845-rpmhpd", .data = &sdm845_desc }, |
9c456626 | 439 | { .compatible = "qcom,sdx55-rpmhpd", .data = &sdx55_desc}, |
3edff626 | 440 | { .compatible = "qcom,sdx65-rpmhpd", .data = &sdx65_desc}, |
c2b854b0 | 441 | { .compatible = "qcom,sm6350-rpmhpd", .data = &sm6350_desc }, |
4e6a2011 | 442 | { .compatible = "qcom,sm8150-rpmhpd", .data = &sm8150_desc }, |
64016bb8 | 443 | { .compatible = "qcom,sm8250-rpmhpd", .data = &sm8250_desc }, |
639c8562 | 444 | { .compatible = "qcom,sm8350-rpmhpd", .data = &sm8350_desc }, |
5d122895 | 445 | { .compatible = "qcom,sm8450-rpmhpd", .data = &sm8450_desc }, |
279b7e8a RN |
446 | { } |
447 | }; | |
d4889ec1 | 448 | MODULE_DEVICE_TABLE(of, rpmhpd_match_table); |
279b7e8a RN |
449 | |
450 | static int rpmhpd_send_corner(struct rpmhpd *pd, int state, | |
451 | unsigned int corner, bool sync) | |
452 | { | |
453 | struct tcs_cmd cmd = { | |
454 | .addr = pd->addr, | |
455 | .data = corner, | |
456 | }; | |
457 | ||
458 | /* | |
459 | * Wait for an ack only when we are increasing the | |
460 | * perf state of the power domain | |
461 | */ | |
462 | if (sync) | |
463 | return rpmh_write(pd->dev, state, &cmd, 1); | |
464 | else | |
465 | return rpmh_write_async(pd->dev, state, &cmd, 1); | |
466 | } | |
467 | ||
468 | static void to_active_sleep(struct rpmhpd *pd, unsigned int corner, | |
469 | unsigned int *active, unsigned int *sleep) | |
470 | { | |
471 | *active = corner; | |
472 | ||
473 | if (pd->active_only) | |
474 | *sleep = 0; | |
475 | else | |
476 | *sleep = *active; | |
477 | } | |
478 | ||
479 | /* | |
480 | * This function is used to aggregate the votes across the active only | |
481 | * resources and its peers. The aggregated votes are sent to RPMh as | |
482 | * ACTIVE_ONLY votes (which take effect immediately), as WAKE_ONLY votes | |
483 | * (applied by RPMh on system wakeup) and as SLEEP votes (applied by RPMh | |
484 | * on system sleep). | |
485 | * We send ACTIVE_ONLY votes for resources without any peers. For others, | |
486 | * which have an active only peer, all 3 votes are sent. | |
487 | */ | |
488 | static int rpmhpd_aggregate_corner(struct rpmhpd *pd, unsigned int corner) | |
489 | { | |
490 | int ret; | |
491 | struct rpmhpd *peer = pd->peer; | |
492 | unsigned int active_corner, sleep_corner; | |
493 | unsigned int this_active_corner = 0, this_sleep_corner = 0; | |
494 | unsigned int peer_active_corner = 0, peer_sleep_corner = 0; | |
495 | ||
496 | to_active_sleep(pd, corner, &this_active_corner, &this_sleep_corner); | |
497 | ||
498 | if (peer && peer->enabled) | |
499 | to_active_sleep(peer, peer->corner, &peer_active_corner, | |
500 | &peer_sleep_corner); | |
501 | ||
502 | active_corner = max(this_active_corner, peer_active_corner); | |
503 | ||
504 | ret = rpmhpd_send_corner(pd, RPMH_ACTIVE_ONLY_STATE, active_corner, | |
505 | active_corner > pd->active_corner); | |
506 | if (ret) | |
507 | return ret; | |
508 | ||
509 | pd->active_corner = active_corner; | |
510 | ||
511 | if (peer) { | |
512 | peer->active_corner = active_corner; | |
513 | ||
514 | ret = rpmhpd_send_corner(pd, RPMH_WAKE_ONLY_STATE, | |
515 | active_corner, false); | |
516 | if (ret) | |
517 | return ret; | |
518 | ||
519 | sleep_corner = max(this_sleep_corner, peer_sleep_corner); | |
520 | ||
521 | return rpmhpd_send_corner(pd, RPMH_SLEEP_STATE, sleep_corner, | |
522 | false); | |
523 | } | |
524 | ||
525 | return ret; | |
526 | } | |
527 | ||
528 | static int rpmhpd_power_on(struct generic_pm_domain *domain) | |
529 | { | |
530 | struct rpmhpd *pd = domain_to_rpmhpd(domain); | |
e3e56c05 BA |
531 | unsigned int corner; |
532 | int ret; | |
279b7e8a RN |
533 | |
534 | mutex_lock(&rpmhpd_lock); | |
535 | ||
e3e56c05 BA |
536 | corner = max(pd->corner, pd->enable_corner); |
537 | ret = rpmhpd_aggregate_corner(pd, corner); | |
279b7e8a RN |
538 | if (!ret) |
539 | pd->enabled = true; | |
540 | ||
541 | mutex_unlock(&rpmhpd_lock); | |
542 | ||
543 | return ret; | |
544 | } | |
545 | ||
546 | static int rpmhpd_power_off(struct generic_pm_domain *domain) | |
547 | { | |
548 | struct rpmhpd *pd = domain_to_rpmhpd(domain); | |
d43b3a98 | 549 | int ret; |
279b7e8a RN |
550 | |
551 | mutex_lock(&rpmhpd_lock); | |
552 | ||
d43b3a98 | 553 | ret = rpmhpd_aggregate_corner(pd, 0); |
279b7e8a RN |
554 | if (!ret) |
555 | pd->enabled = false; | |
556 | ||
557 | mutex_unlock(&rpmhpd_lock); | |
558 | ||
559 | return ret; | |
560 | } | |
561 | ||
562 | static int rpmhpd_set_performance_state(struct generic_pm_domain *domain, | |
563 | unsigned int level) | |
564 | { | |
565 | struct rpmhpd *pd = domain_to_rpmhpd(domain); | |
566 | int ret = 0, i; | |
567 | ||
568 | mutex_lock(&rpmhpd_lock); | |
569 | ||
570 | for (i = 0; i < pd->level_count; i++) | |
571 | if (level <= pd->level[i]) | |
572 | break; | |
573 | ||
574 | /* | |
575 | * If the level requested is more than that supported by the | |
576 | * max corner, just set it to max anyway. | |
577 | */ | |
578 | if (i == pd->level_count) | |
579 | i--; | |
580 | ||
581 | if (pd->enabled) { | |
e3e56c05 BA |
582 | /* Ensure that the domain isn't turn off */ |
583 | if (i < pd->enable_corner) | |
584 | i = pd->enable_corner; | |
585 | ||
279b7e8a RN |
586 | ret = rpmhpd_aggregate_corner(pd, i); |
587 | if (ret) | |
588 | goto out; | |
589 | } | |
590 | ||
591 | pd->corner = i; | |
592 | out: | |
593 | mutex_unlock(&rpmhpd_lock); | |
594 | ||
595 | return ret; | |
596 | } | |
597 | ||
598 | static unsigned int rpmhpd_get_performance_state(struct generic_pm_domain *genpd, | |
599 | struct dev_pm_opp *opp) | |
600 | { | |
601 | return dev_pm_opp_get_level(opp); | |
602 | } | |
603 | ||
604 | static int rpmhpd_update_level_mapping(struct rpmhpd *rpmhpd) | |
605 | { | |
606 | int i; | |
607 | const u16 *buf; | |
608 | ||
609 | buf = cmd_db_read_aux_data(rpmhpd->res_name, &rpmhpd->level_count); | |
610 | if (IS_ERR(buf)) | |
611 | return PTR_ERR(buf); | |
612 | ||
613 | /* 2 bytes used for each command DB aux data entry */ | |
614 | rpmhpd->level_count >>= 1; | |
615 | ||
616 | if (rpmhpd->level_count > RPMH_ARC_MAX_LEVELS) | |
617 | return -EINVAL; | |
618 | ||
619 | for (i = 0; i < rpmhpd->level_count; i++) { | |
620 | rpmhpd->level[i] = buf[i]; | |
621 | ||
e3e56c05 BA |
622 | /* Remember the first corner with non-zero level */ |
623 | if (!rpmhpd->level[rpmhpd->enable_corner] && rpmhpd->level[i]) | |
624 | rpmhpd->enable_corner = i; | |
625 | ||
279b7e8a RN |
626 | /* |
627 | * The AUX data may be zero padded. These 0 valued entries at | |
628 | * the end of the map must be ignored. | |
629 | */ | |
630 | if (i > 0 && rpmhpd->level[i] == 0) { | |
631 | rpmhpd->level_count = i; | |
632 | break; | |
633 | } | |
634 | pr_debug("%s: ARC hlvl=%2d --> vlvl=%4u\n", rpmhpd->res_name, i, | |
635 | rpmhpd->level[i]); | |
636 | } | |
637 | ||
638 | return 0; | |
639 | } | |
640 | ||
641 | static int rpmhpd_probe(struct platform_device *pdev) | |
642 | { | |
643 | int i, ret; | |
644 | size_t num_pds; | |
645 | struct device *dev = &pdev->dev; | |
646 | struct genpd_onecell_data *data; | |
647 | struct rpmhpd **rpmhpds; | |
648 | const struct rpmhpd_desc *desc; | |
649 | ||
650 | desc = of_device_get_match_data(dev); | |
651 | if (!desc) | |
652 | return -EINVAL; | |
653 | ||
654 | rpmhpds = desc->rpmhpds; | |
655 | num_pds = desc->num_pds; | |
656 | ||
657 | data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); | |
658 | if (!data) | |
659 | return -ENOMEM; | |
660 | ||
661 | data->domains = devm_kcalloc(dev, num_pds, sizeof(*data->domains), | |
662 | GFP_KERNEL); | |
663 | if (!data->domains) | |
664 | return -ENOMEM; | |
665 | ||
666 | data->num_domains = num_pds; | |
667 | ||
668 | for (i = 0; i < num_pds; i++) { | |
af77132e | 669 | if (!rpmhpds[i]) |
279b7e8a | 670 | continue; |
279b7e8a RN |
671 | |
672 | rpmhpds[i]->dev = dev; | |
673 | rpmhpds[i]->addr = cmd_db_read_addr(rpmhpds[i]->res_name); | |
674 | if (!rpmhpds[i]->addr) { | |
675 | dev_err(dev, "Could not find RPMh address for resource %s\n", | |
676 | rpmhpds[i]->res_name); | |
677 | return -ENODEV; | |
678 | } | |
679 | ||
680 | ret = cmd_db_read_slave_id(rpmhpds[i]->res_name); | |
681 | if (ret != CMD_DB_HW_ARC) { | |
682 | dev_err(dev, "RPMh slave ID mismatch\n"); | |
683 | return -EINVAL; | |
684 | } | |
685 | ||
686 | ret = rpmhpd_update_level_mapping(rpmhpds[i]); | |
687 | if (ret) | |
688 | return ret; | |
689 | ||
690 | rpmhpds[i]->pd.power_off = rpmhpd_power_off; | |
691 | rpmhpds[i]->pd.power_on = rpmhpd_power_on; | |
692 | rpmhpds[i]->pd.set_performance_state = rpmhpd_set_performance_state; | |
693 | rpmhpds[i]->pd.opp_to_performance_state = rpmhpd_get_performance_state; | |
694 | pm_genpd_init(&rpmhpds[i]->pd, NULL, true); | |
695 | ||
696 | data->domains[i] = &rpmhpds[i]->pd; | |
697 | } | |
698 | ||
0503aec2 RN |
699 | /* Add subdomains */ |
700 | for (i = 0; i < num_pds; i++) { | |
701 | if (!rpmhpds[i]) | |
702 | continue; | |
703 | if (rpmhpds[i]->parent) | |
704 | pm_genpd_add_subdomain(rpmhpds[i]->parent, | |
705 | &rpmhpds[i]->pd); | |
706 | } | |
707 | ||
279b7e8a RN |
708 | return of_genpd_add_provider_onecell(pdev->dev.of_node, data); |
709 | } | |
710 | ||
711 | static struct platform_driver rpmhpd_driver = { | |
712 | .driver = { | |
713 | .name = "qcom-rpmhpd", | |
714 | .of_match_table = rpmhpd_match_table, | |
715 | .suppress_bind_attrs = true, | |
716 | }, | |
717 | .probe = rpmhpd_probe, | |
718 | }; | |
719 | ||
720 | static int __init rpmhpd_init(void) | |
721 | { | |
722 | return platform_driver_register(&rpmhpd_driver); | |
723 | } | |
724 | core_initcall(rpmhpd_init); | |
d4889ec1 JS |
725 | |
726 | MODULE_DESCRIPTION("Qualcomm Technologies, Inc. RPMh Power Domain Driver"); | |
727 | MODULE_LICENSE("GPL v2"); |