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> | |
279b7e8a RN |
12 | #include <linux/platform_device.h> |
13 | #include <linux/pm_opp.h> | |
14 | #include <soc/qcom/cmd-db.h> | |
15 | #include <soc/qcom/rpmh.h> | |
16 | #include <dt-bindings/power/qcom-rpmpd.h> | |
de3acb7a | 17 | #include <dt-bindings/power/qcom,rpmhpd.h> |
279b7e8a RN |
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 | |
3a39049f | 42 | * @state_synced: Indicator that sync_state has been invoked for the rpmhpd resource |
279b7e8a RN |
43 | */ |
44 | struct rpmhpd { | |
45 | struct device *dev; | |
46 | struct generic_pm_domain pd; | |
47 | struct generic_pm_domain *parent; | |
48 | struct rpmhpd *peer; | |
49 | const bool active_only; | |
50 | unsigned int corner; | |
51 | unsigned int active_corner; | |
e3e56c05 | 52 | unsigned int enable_corner; |
279b7e8a RN |
53 | u32 level[RPMH_ARC_MAX_LEVELS]; |
54 | size_t level_count; | |
55 | bool enabled; | |
56 | const char *res_name; | |
57 | u32 addr; | |
3a39049f | 58 | bool state_synced; |
279b7e8a RN |
59 | }; |
60 | ||
61 | struct rpmhpd_desc { | |
62 | struct rpmhpd **rpmhpds; | |
63 | size_t num_pds; | |
64 | }; | |
65 | ||
66 | static DEFINE_MUTEX(rpmhpd_lock); | |
67 | ||
09bb67c1 | 68 | /* RPMH powerdomains */ |
279b7e8a | 69 | |
09bb67c1 | 70 | static struct rpmhpd cx_ao; |
90c74c1c RN |
71 | static struct rpmhpd mx; |
72 | static struct rpmhpd mx_ao; | |
09bb67c1 RN |
73 | static struct rpmhpd cx = { |
74 | .pd = { .name = "cx", }, | |
75 | .peer = &cx_ao, | |
76 | .res_name = "cx.lvl", | |
77 | }; | |
78 | ||
79 | static struct rpmhpd cx_ao = { | |
80 | .pd = { .name = "cx_ao", }, | |
81 | .active_only = true, | |
82 | .peer = &cx, | |
83 | .res_name = "cx.lvl", | |
84 | }; | |
85 | ||
86 | static struct rpmhpd cx_ao_w_mx_parent; | |
87 | static struct rpmhpd cx_w_mx_parent = { | |
279b7e8a | 88 | .pd = { .name = "cx", }, |
09bb67c1 RN |
89 | .peer = &cx_ao_w_mx_parent, |
90 | .parent = &mx.pd, | |
279b7e8a RN |
91 | .res_name = "cx.lvl", |
92 | }; | |
93 | ||
09bb67c1 | 94 | static struct rpmhpd cx_ao_w_mx_parent = { |
279b7e8a | 95 | .pd = { .name = "cx_ao", }, |
5d0d4d42 | 96 | .active_only = true, |
09bb67c1 RN |
97 | .peer = &cx_w_mx_parent, |
98 | .parent = &mx_ao.pd, | |
279b7e8a RN |
99 | .res_name = "cx.lvl", |
100 | }; | |
101 | ||
90c74c1c RN |
102 | static struct rpmhpd ebi = { |
103 | .pd = { .name = "ebi", }, | |
104 | .res_name = "ebi.lvl", | |
105 | }; | |
106 | ||
107 | static struct rpmhpd gfx = { | |
108 | .pd = { .name = "gfx", }, | |
109 | .res_name = "gfx.lvl", | |
110 | }; | |
111 | ||
112 | static struct rpmhpd lcx = { | |
113 | .pd = { .name = "lcx", }, | |
114 | .res_name = "lcx.lvl", | |
115 | }; | |
116 | ||
117 | static struct rpmhpd lmx = { | |
118 | .pd = { .name = "lmx", }, | |
119 | .res_name = "lmx.lvl", | |
120 | }; | |
121 | ||
09bb67c1 RN |
122 | static struct rpmhpd mmcx_ao; |
123 | static struct rpmhpd mmcx = { | |
124 | .pd = { .name = "mmcx", }, | |
125 | .peer = &mmcx_ao, | |
126 | .res_name = "mmcx.lvl", | |
127 | }; | |
128 | ||
129 | static struct rpmhpd mmcx_ao = { | |
130 | .pd = { .name = "mmcx_ao", }, | |
131 | .active_only = true, | |
132 | .peer = &mmcx, | |
133 | .res_name = "mmcx.lvl", | |
134 | }; | |
135 | ||
136 | static struct rpmhpd mmcx_ao_w_cx_parent; | |
137 | static struct rpmhpd mmcx_w_cx_parent = { | |
138 | .pd = { .name = "mmcx", }, | |
139 | .peer = &mmcx_ao_w_cx_parent, | |
140 | .parent = &cx.pd, | |
141 | .res_name = "mmcx.lvl", | |
142 | }; | |
143 | ||
144 | static struct rpmhpd mmcx_ao_w_cx_parent = { | |
145 | .pd = { .name = "mmcx_ao", }, | |
146 | .active_only = true, | |
147 | .peer = &mmcx_w_cx_parent, | |
148 | .parent = &cx_ao.pd, | |
149 | .res_name = "mmcx.lvl", | |
150 | }; | |
151 | ||
90c74c1c RN |
152 | static struct rpmhpd mss = { |
153 | .pd = { .name = "mss", }, | |
154 | .res_name = "mss.lvl", | |
155 | }; | |
156 | ||
157 | static struct rpmhpd mx_ao; | |
158 | static struct rpmhpd mx = { | |
159 | .pd = { .name = "mx", }, | |
160 | .peer = &mx_ao, | |
161 | .res_name = "mx.lvl", | |
162 | }; | |
163 | ||
164 | static struct rpmhpd mx_ao = { | |
165 | .pd = { .name = "mx_ao", }, | |
166 | .active_only = true, | |
167 | .peer = &mx, | |
168 | .res_name = "mx.lvl", | |
169 | }; | |
170 | ||
09bb67c1 RN |
171 | static struct rpmhpd mxc_ao; |
172 | static struct rpmhpd mxc = { | |
173 | .pd = { .name = "mxc", }, | |
174 | .peer = &mxc_ao, | |
175 | .res_name = "mxc.lvl", | |
176 | }; | |
177 | ||
178 | static struct rpmhpd mxc_ao = { | |
179 | .pd = { .name = "mxc_ao", }, | |
180 | .active_only = true, | |
181 | .peer = &mxc, | |
182 | .res_name = "mxc.lvl", | |
183 | }; | |
184 | ||
f68f1cb3 BA |
185 | static struct rpmhpd nsp = { |
186 | .pd = { .name = "nsp", }, | |
187 | .res_name = "nsp.lvl", | |
188 | }; | |
189 | ||
91e910ad BG |
190 | static struct rpmhpd nsp0 = { |
191 | .pd = { .name = "nsp0", }, | |
192 | .res_name = "nsp0.lvl", | |
193 | }; | |
194 | ||
195 | static struct rpmhpd nsp1 = { | |
196 | .pd = { .name = "nsp1", }, | |
197 | .res_name = "nsp1.lvl", | |
198 | }; | |
199 | ||
9c82c900 NA |
200 | static struct rpmhpd nsp2 = { |
201 | .pd = { .name = "nsp2", }, | |
202 | .res_name = "nsp2.lvl", | |
203 | }; | |
204 | ||
f68f1cb3 BA |
205 | static struct rpmhpd qphy = { |
206 | .pd = { .name = "qphy", }, | |
207 | .res_name = "qphy.lvl", | |
208 | }; | |
209 | ||
2050c9bc AV |
210 | static struct rpmhpd gmxc = { |
211 | .pd = { .name = "gmxc", }, | |
212 | .res_name = "gmxc.lvl", | |
213 | }; | |
214 | ||
f68f1cb3 BA |
215 | /* SA8540P RPMH powerdomains */ |
216 | static struct rpmhpd *sa8540p_rpmhpds[] = { | |
217 | [SC8280XP_CX] = &cx, | |
218 | [SC8280XP_CX_AO] = &cx_ao, | |
219 | [SC8280XP_EBI] = &ebi, | |
220 | [SC8280XP_GFX] = &gfx, | |
221 | [SC8280XP_LCX] = &lcx, | |
222 | [SC8280XP_LMX] = &lmx, | |
223 | [SC8280XP_MMCX] = &mmcx, | |
224 | [SC8280XP_MMCX_AO] = &mmcx_ao, | |
225 | [SC8280XP_MX] = &mx, | |
226 | [SC8280XP_MX_AO] = &mx_ao, | |
227 | [SC8280XP_NSP] = &nsp, | |
228 | }; | |
229 | ||
230 | static const struct rpmhpd_desc sa8540p_desc = { | |
231 | .rpmhpds = sa8540p_rpmhpds, | |
232 | .num_pds = ARRAY_SIZE(sa8540p_rpmhpds), | |
233 | }; | |
234 | ||
91e910ad BG |
235 | /* SA8775P RPMH power domains */ |
236 | static struct rpmhpd *sa8775p_rpmhpds[] = { | |
237 | [SA8775P_CX] = &cx, | |
238 | [SA8775P_CX_AO] = &cx_ao, | |
239 | [SA8775P_EBI] = &ebi, | |
240 | [SA8775P_GFX] = &gfx, | |
241 | [SA8775P_LCX] = &lcx, | |
242 | [SA8775P_LMX] = &lmx, | |
243 | [SA8775P_MMCX] = &mmcx, | |
244 | [SA8775P_MMCX_AO] = &mmcx_ao, | |
245 | [SA8775P_MXC] = &mxc, | |
246 | [SA8775P_MXC_AO] = &mxc_ao, | |
247 | [SA8775P_MX] = &mx, | |
248 | [SA8775P_MX_AO] = &mx_ao, | |
249 | [SA8775P_NSP0] = &nsp0, | |
250 | [SA8775P_NSP1] = &nsp1, | |
251 | }; | |
252 | ||
253 | static const struct rpmhpd_desc sa8775p_desc = { | |
254 | .rpmhpds = sa8775p_rpmhpds, | |
255 | .num_pds = ARRAY_SIZE(sa8775p_rpmhpds), | |
256 | }; | |
257 | ||
5faeae4e RA |
258 | /* SDM670 RPMH powerdomains */ |
259 | static struct rpmhpd *sdm670_rpmhpds[] = { | |
260 | [SDM670_CX] = &cx_w_mx_parent, | |
261 | [SDM670_CX_AO] = &cx_ao_w_mx_parent, | |
262 | [SDM670_GFX] = &gfx, | |
263 | [SDM670_LCX] = &lcx, | |
264 | [SDM670_LMX] = &lmx, | |
265 | [SDM670_MSS] = &mss, | |
266 | [SDM670_MX] = &mx, | |
267 | [SDM670_MX_AO] = &mx_ao, | |
268 | }; | |
269 | ||
270 | static const struct rpmhpd_desc sdm670_desc = { | |
271 | .rpmhpds = sdm670_rpmhpds, | |
272 | .num_pds = ARRAY_SIZE(sdm670_rpmhpds), | |
273 | }; | |
274 | ||
09bb67c1 | 275 | /* SDM845 RPMH powerdomains */ |
279b7e8a | 276 | static struct rpmhpd *sdm845_rpmhpds[] = { |
09bb67c1 RN |
277 | [SDM845_CX] = &cx_w_mx_parent, |
278 | [SDM845_CX_AO] = &cx_ao_w_mx_parent, | |
90c74c1c | 279 | [SDM845_EBI] = &ebi, |
09bb67c1 | 280 | [SDM845_GFX] = &gfx, |
90c74c1c RN |
281 | [SDM845_LCX] = &lcx, |
282 | [SDM845_LMX] = &lmx, | |
09bb67c1 | 283 | [SDM845_MSS] = &mss, |
90c74c1c RN |
284 | [SDM845_MX] = &mx, |
285 | [SDM845_MX_AO] = &mx_ao, | |
279b7e8a RN |
286 | }; |
287 | ||
288 | static const struct rpmhpd_desc sdm845_desc = { | |
289 | .rpmhpds = sdm845_rpmhpds, | |
290 | .num_pds = ARRAY_SIZE(sdm845_rpmhpds), | |
291 | }; | |
292 | ||
9c456626 VK |
293 | /* SDX55 RPMH powerdomains */ |
294 | static struct rpmhpd *sdx55_rpmhpds[] = { | |
90c74c1c | 295 | [SDX55_CX] = &cx_w_mx_parent, |
09bb67c1 RN |
296 | [SDX55_MSS] = &mss, |
297 | [SDX55_MX] = &mx, | |
9c456626 VK |
298 | }; |
299 | ||
300 | static const struct rpmhpd_desc sdx55_desc = { | |
301 | .rpmhpds = sdx55_rpmhpds, | |
302 | .num_pds = ARRAY_SIZE(sdx55_rpmhpds), | |
303 | }; | |
304 | ||
3edff626 RA |
305 | /* SDX65 RPMH powerdomains */ |
306 | static struct rpmhpd *sdx65_rpmhpds[] = { | |
307 | [SDX65_CX] = &cx_w_mx_parent, | |
308 | [SDX65_CX_AO] = &cx_ao_w_mx_parent, | |
309 | [SDX65_MSS] = &mss, | |
310 | [SDX65_MX] = &mx, | |
311 | [SDX65_MX_AO] = &mx_ao, | |
312 | [SDX65_MXC] = &mxc, | |
313 | }; | |
314 | ||
315 | static const struct rpmhpd_desc sdx65_desc = { | |
316 | .rpmhpds = sdx65_rpmhpds, | |
317 | .num_pds = ARRAY_SIZE(sdx65_rpmhpds), | |
318 | }; | |
319 | ||
668e08c2 RA |
320 | /* SDX75 RPMH powerdomains */ |
321 | static struct rpmhpd *sdx75_rpmhpds[] = { | |
322 | [RPMHPD_CX] = &cx, | |
323 | [RPMHPD_CX_AO] = &cx_ao, | |
324 | [RPMHPD_MSS] = &mss, | |
325 | [RPMHPD_MX] = &mx, | |
326 | [RPMHPD_MX_AO] = &mx_ao, | |
327 | [RPMHPD_MXC] = &mxc, | |
328 | }; | |
329 | ||
330 | static const struct rpmhpd_desc sdx75_desc = { | |
331 | .rpmhpds = sdx75_rpmhpds, | |
332 | .num_pds = ARRAY_SIZE(sdx75_rpmhpds), | |
333 | }; | |
334 | ||
c2b854b0 KD |
335 | /* SM6350 RPMH powerdomains */ |
336 | static struct rpmhpd *sm6350_rpmhpds[] = { | |
09bb67c1 RN |
337 | [SM6350_CX] = &cx_w_mx_parent, |
338 | [SM6350_GFX] = &gfx, | |
339 | [SM6350_LCX] = &lcx, | |
340 | [SM6350_LMX] = &lmx, | |
341 | [SM6350_MSS] = &mss, | |
342 | [SM6350_MX] = &mx, | |
c2b854b0 KD |
343 | }; |
344 | ||
345 | static const struct rpmhpd_desc sm6350_desc = { | |
346 | .rpmhpds = sm6350_rpmhpds, | |
347 | .num_pds = ARRAY_SIZE(sm6350_rpmhpds), | |
348 | }; | |
349 | ||
0958eccb DT |
350 | /* SM7150 RPMH powerdomains */ |
351 | static struct rpmhpd *sm7150_rpmhpds[] = { | |
352 | [RPMHPD_CX] = &cx_w_mx_parent, | |
353 | [RPMHPD_CX_AO] = &cx_ao_w_mx_parent, | |
354 | [RPMHPD_GFX] = &gfx, | |
355 | [RPMHPD_LCX] = &lcx, | |
356 | [RPMHPD_LMX] = &lmx, | |
357 | [RPMHPD_MX] = &mx, | |
358 | [RPMHPD_MX_AO] = &mx_ao, | |
359 | [RPMHPD_MSS] = &mss, | |
360 | }; | |
361 | ||
362 | static const struct rpmhpd_desc sm7150_desc = { | |
363 | .rpmhpds = sm7150_rpmhpds, | |
364 | .num_pds = ARRAY_SIZE(sm7150_rpmhpds), | |
365 | }; | |
366 | ||
4e6a2011 | 367 | /* SM8150 RPMH powerdomains */ |
4e6a2011 | 368 | static struct rpmhpd *sm8150_rpmhpds[] = { |
09bb67c1 RN |
369 | [SM8150_CX] = &cx_w_mx_parent, |
370 | [SM8150_CX_AO] = &cx_ao_w_mx_parent, | |
90c74c1c RN |
371 | [SM8150_EBI] = &ebi, |
372 | [SM8150_GFX] = &gfx, | |
373 | [SM8150_LCX] = &lcx, | |
374 | [SM8150_LMX] = &lmx, | |
09bb67c1 RN |
375 | [SM8150_MMCX] = &mmcx, |
376 | [SM8150_MMCX_AO] = &mmcx_ao, | |
90c74c1c RN |
377 | [SM8150_MSS] = &mss, |
378 | [SM8150_MX] = &mx, | |
379 | [SM8150_MX_AO] = &mx_ao, | |
4e6a2011 SS |
380 | }; |
381 | ||
382 | static const struct rpmhpd_desc sm8150_desc = { | |
383 | .rpmhpds = sm8150_rpmhpds, | |
384 | .num_pds = ARRAY_SIZE(sm8150_rpmhpds), | |
385 | }; | |
386 | ||
4a08af2f KD |
387 | static struct rpmhpd *sa8155p_rpmhpds[] = { |
388 | [SA8155P_CX] = &cx_w_mx_parent, | |
389 | [SA8155P_CX_AO] = &cx_ao_w_mx_parent, | |
390 | [SA8155P_EBI] = &ebi, | |
391 | [SA8155P_GFX] = &gfx, | |
392 | [SA8155P_MSS] = &mss, | |
393 | [SA8155P_MX] = &mx, | |
394 | [SA8155P_MX_AO] = &mx_ao, | |
395 | }; | |
396 | ||
397 | static const struct rpmhpd_desc sa8155p_desc = { | |
398 | .rpmhpds = sa8155p_rpmhpds, | |
399 | .num_pds = ARRAY_SIZE(sa8155p_rpmhpds), | |
400 | }; | |
401 | ||
09bb67c1 | 402 | /* SM8250 RPMH powerdomains */ |
64016bb8 | 403 | static struct rpmhpd *sm8250_rpmhpds[] = { |
de3acb7a RA |
404 | [RPMHPD_CX] = &cx_w_mx_parent, |
405 | [RPMHPD_CX_AO] = &cx_ao_w_mx_parent, | |
406 | [RPMHPD_EBI] = &ebi, | |
407 | [RPMHPD_GFX] = &gfx, | |
408 | [RPMHPD_LCX] = &lcx, | |
409 | [RPMHPD_LMX] = &lmx, | |
410 | [RPMHPD_MMCX] = &mmcx, | |
411 | [RPMHPD_MMCX_AO] = &mmcx_ao, | |
412 | [RPMHPD_MX] = &mx, | |
413 | [RPMHPD_MX_AO] = &mx_ao, | |
64016bb8 BA |
414 | }; |
415 | ||
416 | static const struct rpmhpd_desc sm8250_desc = { | |
417 | .rpmhpds = sm8250_rpmhpds, | |
418 | .num_pds = ARRAY_SIZE(sm8250_rpmhpds), | |
419 | }; | |
420 | ||
639c8562 | 421 | /* SM8350 Power domains */ |
639c8562 | 422 | static struct rpmhpd *sm8350_rpmhpds[] = { |
de3acb7a RA |
423 | [RPMHPD_CX] = &cx_w_mx_parent, |
424 | [RPMHPD_CX_AO] = &cx_ao_w_mx_parent, | |
425 | [RPMHPD_EBI] = &ebi, | |
426 | [RPMHPD_GFX] = &gfx, | |
427 | [RPMHPD_LCX] = &lcx, | |
428 | [RPMHPD_LMX] = &lmx, | |
429 | [RPMHPD_MMCX] = &mmcx, | |
430 | [RPMHPD_MMCX_AO] = &mmcx_ao, | |
431 | [RPMHPD_MSS] = &mss, | |
432 | [RPMHPD_MX] = &mx, | |
433 | [RPMHPD_MX_AO] = &mx_ao, | |
434 | [RPMHPD_MXC] = &mxc, | |
435 | [RPMHPD_MXC_AO] = &mxc_ao, | |
639c8562 VK |
436 | }; |
437 | ||
438 | static const struct rpmhpd_desc sm8350_desc = { | |
439 | .rpmhpds = sm8350_rpmhpds, | |
440 | .num_pds = ARRAY_SIZE(sm8350_rpmhpds), | |
441 | }; | |
442 | ||
5d122895 | 443 | /* SM8450 RPMH powerdomains */ |
5d122895 | 444 | static struct rpmhpd *sm8450_rpmhpds[] = { |
de3acb7a RA |
445 | [RPMHPD_CX] = &cx, |
446 | [RPMHPD_CX_AO] = &cx_ao, | |
447 | [RPMHPD_EBI] = &ebi, | |
448 | [RPMHPD_GFX] = &gfx, | |
449 | [RPMHPD_LCX] = &lcx, | |
450 | [RPMHPD_LMX] = &lmx, | |
451 | [RPMHPD_MMCX] = &mmcx_w_cx_parent, | |
452 | [RPMHPD_MMCX_AO] = &mmcx_ao_w_cx_parent, | |
453 | [RPMHPD_MSS] = &mss, | |
454 | [RPMHPD_MX] = &mx, | |
455 | [RPMHPD_MX_AO] = &mx_ao, | |
456 | [RPMHPD_MXC] = &mxc, | |
457 | [RPMHPD_MXC_AO] = &mxc_ao, | |
5d122895 DB |
458 | }; |
459 | ||
460 | static const struct rpmhpd_desc sm8450_desc = { | |
461 | .rpmhpds = sm8450_rpmhpds, | |
462 | .num_pds = ARRAY_SIZE(sm8450_rpmhpds), | |
463 | }; | |
464 | ||
d1d9d62b AV |
465 | /* SM8550 RPMH powerdomains */ |
466 | static struct rpmhpd *sm8550_rpmhpds[] = { | |
de3acb7a RA |
467 | [RPMHPD_CX] = &cx, |
468 | [RPMHPD_CX_AO] = &cx_ao, | |
469 | [RPMHPD_EBI] = &ebi, | |
470 | [RPMHPD_GFX] = &gfx, | |
471 | [RPMHPD_LCX] = &lcx, | |
472 | [RPMHPD_LMX] = &lmx, | |
473 | [RPMHPD_MMCX] = &mmcx_w_cx_parent, | |
474 | [RPMHPD_MMCX_AO] = &mmcx_ao_w_cx_parent, | |
475 | [RPMHPD_MSS] = &mss, | |
476 | [RPMHPD_MX] = &mx, | |
477 | [RPMHPD_MX_AO] = &mx_ao, | |
478 | [RPMHPD_MXC] = &mxc, | |
479 | [RPMHPD_MXC_AO] = &mxc_ao, | |
480 | [RPMHPD_NSP] = &nsp, | |
d1d9d62b AV |
481 | }; |
482 | ||
483 | static const struct rpmhpd_desc sm8550_desc = { | |
484 | .rpmhpds = sm8550_rpmhpds, | |
485 | .num_pds = ARRAY_SIZE(sm8550_rpmhpds), | |
486 | }; | |
487 | ||
9c82c900 NA |
488 | /* SM8650 RPMH powerdomains */ |
489 | static struct rpmhpd *sm8650_rpmhpds[] = { | |
490 | [RPMHPD_CX] = &cx, | |
491 | [RPMHPD_CX_AO] = &cx_ao, | |
492 | [RPMHPD_EBI] = &ebi, | |
493 | [RPMHPD_GFX] = &gfx, | |
494 | [RPMHPD_LCX] = &lcx, | |
495 | [RPMHPD_LMX] = &lmx, | |
496 | [RPMHPD_MMCX] = &mmcx_w_cx_parent, | |
497 | [RPMHPD_MMCX_AO] = &mmcx_ao_w_cx_parent, | |
498 | [RPMHPD_MSS] = &mss, | |
499 | [RPMHPD_MX] = &mx, | |
500 | [RPMHPD_MX_AO] = &mx_ao, | |
501 | [RPMHPD_MXC] = &mxc, | |
502 | [RPMHPD_MXC_AO] = &mxc_ao, | |
503 | [RPMHPD_NSP] = &nsp, | |
504 | [RPMHPD_NSP2] = &nsp2, | |
505 | }; | |
506 | ||
507 | static const struct rpmhpd_desc sm8650_desc = { | |
508 | .rpmhpds = sm8650_rpmhpds, | |
509 | .num_pds = ARRAY_SIZE(sm8650_rpmhpds), | |
510 | }; | |
511 | ||
94949a01 MO |
512 | /* QDU1000/QRU1000 RPMH powerdomains */ |
513 | static struct rpmhpd *qdu1000_rpmhpds[] = { | |
514 | [QDU1000_CX] = &cx, | |
515 | [QDU1000_EBI] = &ebi, | |
516 | [QDU1000_MSS] = &mss, | |
517 | [QDU1000_MX] = &mx, | |
518 | }; | |
519 | ||
520 | static const struct rpmhpd_desc qdu1000_desc = { | |
521 | .rpmhpds = qdu1000_rpmhpds, | |
522 | .num_pds = ARRAY_SIZE(qdu1000_rpmhpds), | |
523 | }; | |
524 | ||
a30657b6 SS |
525 | /* SC7180 RPMH powerdomains */ |
526 | static struct rpmhpd *sc7180_rpmhpds[] = { | |
09bb67c1 RN |
527 | [SC7180_CX] = &cx_w_mx_parent, |
528 | [SC7180_CX_AO] = &cx_ao_w_mx_parent, | |
529 | [SC7180_GFX] = &gfx, | |
09bb67c1 | 530 | [SC7180_LCX] = &lcx, |
90c74c1c | 531 | [SC7180_LMX] = &lmx, |
09bb67c1 | 532 | [SC7180_MSS] = &mss, |
90c74c1c RN |
533 | [SC7180_MX] = &mx, |
534 | [SC7180_MX_AO] = &mx_ao, | |
a30657b6 SS |
535 | }; |
536 | ||
537 | static const struct rpmhpd_desc sc7180_desc = { | |
538 | .rpmhpds = sc7180_rpmhpds, | |
539 | .num_pds = ARRAY_SIZE(sc7180_rpmhpds), | |
540 | }; | |
541 | ||
9937447d RN |
542 | /* SC7280 RPMH powerdomains */ |
543 | static struct rpmhpd *sc7280_rpmhpds[] = { | |
7d6a0a4d RN |
544 | [SC7280_CX] = &cx, |
545 | [SC7280_CX_AO] = &cx_ao, | |
09bb67c1 RN |
546 | [SC7280_EBI] = &ebi, |
547 | [SC7280_GFX] = &gfx, | |
09bb67c1 | 548 | [SC7280_LCX] = &lcx, |
90c74c1c | 549 | [SC7280_LMX] = &lmx, |
09bb67c1 | 550 | [SC7280_MSS] = &mss, |
90c74c1c RN |
551 | [SC7280_MX] = &mx, |
552 | [SC7280_MX_AO] = &mx_ao, | |
9937447d RN |
553 | }; |
554 | ||
555 | static const struct rpmhpd_desc sc7280_desc = { | |
556 | .rpmhpds = sc7280_rpmhpds, | |
557 | .num_pds = ARRAY_SIZE(sc7280_rpmhpds), | |
558 | }; | |
559 | ||
3b1a0582 BA |
560 | /* SC8180x RPMH powerdomains */ |
561 | static struct rpmhpd *sc8180x_rpmhpds[] = { | |
09bb67c1 RN |
562 | [SC8180X_CX] = &cx_w_mx_parent, |
563 | [SC8180X_CX_AO] = &cx_ao_w_mx_parent, | |
564 | [SC8180X_EBI] = &ebi, | |
565 | [SC8180X_GFX] = &gfx, | |
566 | [SC8180X_LCX] = &lcx, | |
567 | [SC8180X_LMX] = &lmx, | |
568 | [SC8180X_MMCX] = &mmcx, | |
569 | [SC8180X_MMCX_AO] = &mmcx_ao, | |
570 | [SC8180X_MSS] = &mss, | |
571 | [SC8180X_MX] = &mx, | |
572 | [SC8180X_MX_AO] = &mx_ao, | |
3b1a0582 BA |
573 | }; |
574 | ||
575 | static const struct rpmhpd_desc sc8180x_desc = { | |
576 | .rpmhpds = sc8180x_rpmhpds, | |
577 | .num_pds = ARRAY_SIZE(sc8180x_rpmhpds), | |
578 | }; | |
579 | ||
f68f1cb3 BA |
580 | /* SC8280xp RPMH powerdomains */ |
581 | static struct rpmhpd *sc8280xp_rpmhpds[] = { | |
582 | [SC8280XP_CX] = &cx, | |
583 | [SC8280XP_CX_AO] = &cx_ao, | |
584 | [SC8280XP_EBI] = &ebi, | |
585 | [SC8280XP_GFX] = &gfx, | |
586 | [SC8280XP_LCX] = &lcx, | |
587 | [SC8280XP_LMX] = &lmx, | |
588 | [SC8280XP_MMCX] = &mmcx, | |
589 | [SC8280XP_MMCX_AO] = &mmcx_ao, | |
590 | [SC8280XP_MX] = &mx, | |
591 | [SC8280XP_MX_AO] = &mx_ao, | |
592 | [SC8280XP_NSP] = &nsp, | |
593 | [SC8280XP_QPHY] = &qphy, | |
594 | }; | |
595 | ||
596 | static const struct rpmhpd_desc sc8280xp_desc = { | |
597 | .rpmhpds = sc8280xp_rpmhpds, | |
598 | .num_pds = ARRAY_SIZE(sc8280xp_rpmhpds), | |
599 | }; | |
600 | ||
e60b6c18 SS |
601 | /* X1E80100 RPMH powerdomains */ |
602 | static struct rpmhpd *x1e80100_rpmhpds[] = { | |
2050c9bc AV |
603 | [RPMHPD_CX] = &cx, |
604 | [RPMHPD_CX_AO] = &cx_ao, | |
605 | [RPMHPD_EBI] = &ebi, | |
606 | [RPMHPD_GFX] = &gfx, | |
607 | [RPMHPD_LCX] = &lcx, | |
608 | [RPMHPD_LMX] = &lmx, | |
609 | [RPMHPD_MMCX] = &mmcx, | |
610 | [RPMHPD_MMCX_AO] = &mmcx_ao, | |
611 | [RPMHPD_MX] = &mx, | |
612 | [RPMHPD_MX_AO] = &mx_ao, | |
613 | [RPMHPD_NSP] = &nsp, | |
614 | [RPMHPD_MXC] = &mxc, | |
615 | [RPMHPD_GMXC] = &gmxc, | |
616 | }; | |
617 | ||
e60b6c18 SS |
618 | static const struct rpmhpd_desc x1e80100_desc = { |
619 | .rpmhpds = x1e80100_rpmhpds, | |
620 | .num_pds = ARRAY_SIZE(x1e80100_rpmhpds), | |
2050c9bc AV |
621 | }; |
622 | ||
279b7e8a | 623 | static const struct of_device_id rpmhpd_match_table[] = { |
94949a01 | 624 | { .compatible = "qcom,qdu1000-rpmhpd", .data = &qdu1000_desc }, |
4a08af2f | 625 | { .compatible = "qcom,sa8155p-rpmhpd", .data = &sa8155p_desc }, |
f68f1cb3 | 626 | { .compatible = "qcom,sa8540p-rpmhpd", .data = &sa8540p_desc }, |
91e910ad | 627 | { .compatible = "qcom,sa8775p-rpmhpd", .data = &sa8775p_desc }, |
a30657b6 | 628 | { .compatible = "qcom,sc7180-rpmhpd", .data = &sc7180_desc }, |
9937447d | 629 | { .compatible = "qcom,sc7280-rpmhpd", .data = &sc7280_desc }, |
3b1a0582 | 630 | { .compatible = "qcom,sc8180x-rpmhpd", .data = &sc8180x_desc }, |
f68f1cb3 | 631 | { .compatible = "qcom,sc8280xp-rpmhpd", .data = &sc8280xp_desc }, |
5faeae4e | 632 | { .compatible = "qcom,sdm670-rpmhpd", .data = &sdm670_desc }, |
279b7e8a | 633 | { .compatible = "qcom,sdm845-rpmhpd", .data = &sdm845_desc }, |
9c456626 | 634 | { .compatible = "qcom,sdx55-rpmhpd", .data = &sdx55_desc}, |
3edff626 | 635 | { .compatible = "qcom,sdx65-rpmhpd", .data = &sdx65_desc}, |
668e08c2 | 636 | { .compatible = "qcom,sdx75-rpmhpd", .data = &sdx75_desc}, |
c2b854b0 | 637 | { .compatible = "qcom,sm6350-rpmhpd", .data = &sm6350_desc }, |
0958eccb | 638 | { .compatible = "qcom,sm7150-rpmhpd", .data = &sm7150_desc }, |
4e6a2011 | 639 | { .compatible = "qcom,sm8150-rpmhpd", .data = &sm8150_desc }, |
64016bb8 | 640 | { .compatible = "qcom,sm8250-rpmhpd", .data = &sm8250_desc }, |
639c8562 | 641 | { .compatible = "qcom,sm8350-rpmhpd", .data = &sm8350_desc }, |
5d122895 | 642 | { .compatible = "qcom,sm8450-rpmhpd", .data = &sm8450_desc }, |
d1d9d62b | 643 | { .compatible = "qcom,sm8550-rpmhpd", .data = &sm8550_desc }, |
9c82c900 | 644 | { .compatible = "qcom,sm8650-rpmhpd", .data = &sm8650_desc }, |
e60b6c18 | 645 | { .compatible = "qcom,x1e80100-rpmhpd", .data = &x1e80100_desc }, |
279b7e8a RN |
646 | { } |
647 | }; | |
d4889ec1 | 648 | MODULE_DEVICE_TABLE(of, rpmhpd_match_table); |
279b7e8a RN |
649 | |
650 | static int rpmhpd_send_corner(struct rpmhpd *pd, int state, | |
651 | unsigned int corner, bool sync) | |
652 | { | |
653 | struct tcs_cmd cmd = { | |
654 | .addr = pd->addr, | |
655 | .data = corner, | |
656 | }; | |
657 | ||
658 | /* | |
659 | * Wait for an ack only when we are increasing the | |
660 | * perf state of the power domain | |
661 | */ | |
662 | if (sync) | |
663 | return rpmh_write(pd->dev, state, &cmd, 1); | |
664 | else | |
665 | return rpmh_write_async(pd->dev, state, &cmd, 1); | |
666 | } | |
667 | ||
668 | static void to_active_sleep(struct rpmhpd *pd, unsigned int corner, | |
669 | unsigned int *active, unsigned int *sleep) | |
670 | { | |
671 | *active = corner; | |
672 | ||
673 | if (pd->active_only) | |
674 | *sleep = 0; | |
675 | else | |
676 | *sleep = *active; | |
677 | } | |
678 | ||
679 | /* | |
680 | * This function is used to aggregate the votes across the active only | |
681 | * resources and its peers. The aggregated votes are sent to RPMh as | |
682 | * ACTIVE_ONLY votes (which take effect immediately), as WAKE_ONLY votes | |
683 | * (applied by RPMh on system wakeup) and as SLEEP votes (applied by RPMh | |
684 | * on system sleep). | |
685 | * We send ACTIVE_ONLY votes for resources without any peers. For others, | |
686 | * which have an active only peer, all 3 votes are sent. | |
687 | */ | |
688 | static int rpmhpd_aggregate_corner(struct rpmhpd *pd, unsigned int corner) | |
689 | { | |
690 | int ret; | |
691 | struct rpmhpd *peer = pd->peer; | |
692 | unsigned int active_corner, sleep_corner; | |
693 | unsigned int this_active_corner = 0, this_sleep_corner = 0; | |
694 | unsigned int peer_active_corner = 0, peer_sleep_corner = 0; | |
2a93c6cb | 695 | unsigned int peer_enabled_corner; |
279b7e8a | 696 | |
3a39049f BA |
697 | if (pd->state_synced) { |
698 | to_active_sleep(pd, corner, &this_active_corner, &this_sleep_corner); | |
699 | } else { | |
700 | /* Clamp to highest corner if sync_state hasn't happened */ | |
701 | this_active_corner = pd->level_count - 1; | |
702 | this_sleep_corner = pd->level_count - 1; | |
703 | } | |
279b7e8a | 704 | |
2a93c6cb BA |
705 | if (peer && peer->enabled) { |
706 | peer_enabled_corner = max(peer->corner, peer->enable_corner); | |
707 | to_active_sleep(peer, peer_enabled_corner, &peer_active_corner, | |
279b7e8a | 708 | &peer_sleep_corner); |
2a93c6cb | 709 | } |
279b7e8a RN |
710 | |
711 | active_corner = max(this_active_corner, peer_active_corner); | |
712 | ||
713 | ret = rpmhpd_send_corner(pd, RPMH_ACTIVE_ONLY_STATE, active_corner, | |
714 | active_corner > pd->active_corner); | |
715 | if (ret) | |
716 | return ret; | |
717 | ||
718 | pd->active_corner = active_corner; | |
719 | ||
720 | if (peer) { | |
721 | peer->active_corner = active_corner; | |
722 | ||
723 | ret = rpmhpd_send_corner(pd, RPMH_WAKE_ONLY_STATE, | |
724 | active_corner, false); | |
725 | if (ret) | |
726 | return ret; | |
727 | ||
728 | sleep_corner = max(this_sleep_corner, peer_sleep_corner); | |
729 | ||
730 | return rpmhpd_send_corner(pd, RPMH_SLEEP_STATE, sleep_corner, | |
731 | false); | |
732 | } | |
733 | ||
734 | return ret; | |
735 | } | |
736 | ||
737 | static int rpmhpd_power_on(struct generic_pm_domain *domain) | |
738 | { | |
739 | struct rpmhpd *pd = domain_to_rpmhpd(domain); | |
e3e56c05 BA |
740 | unsigned int corner; |
741 | int ret; | |
279b7e8a RN |
742 | |
743 | mutex_lock(&rpmhpd_lock); | |
744 | ||
e3e56c05 BA |
745 | corner = max(pd->corner, pd->enable_corner); |
746 | ret = rpmhpd_aggregate_corner(pd, corner); | |
279b7e8a RN |
747 | if (!ret) |
748 | pd->enabled = true; | |
749 | ||
750 | mutex_unlock(&rpmhpd_lock); | |
751 | ||
752 | return ret; | |
753 | } | |
754 | ||
755 | static int rpmhpd_power_off(struct generic_pm_domain *domain) | |
756 | { | |
757 | struct rpmhpd *pd = domain_to_rpmhpd(domain); | |
d43b3a98 | 758 | int ret; |
279b7e8a RN |
759 | |
760 | mutex_lock(&rpmhpd_lock); | |
761 | ||
d43b3a98 | 762 | ret = rpmhpd_aggregate_corner(pd, 0); |
279b7e8a RN |
763 | if (!ret) |
764 | pd->enabled = false; | |
765 | ||
766 | mutex_unlock(&rpmhpd_lock); | |
767 | ||
768 | return ret; | |
769 | } | |
770 | ||
771 | static int rpmhpd_set_performance_state(struct generic_pm_domain *domain, | |
772 | unsigned int level) | |
773 | { | |
774 | struct rpmhpd *pd = domain_to_rpmhpd(domain); | |
775 | int ret = 0, i; | |
776 | ||
777 | mutex_lock(&rpmhpd_lock); | |
778 | ||
779 | for (i = 0; i < pd->level_count; i++) | |
780 | if (level <= pd->level[i]) | |
781 | break; | |
782 | ||
783 | /* | |
784 | * If the level requested is more than that supported by the | |
785 | * max corner, just set it to max anyway. | |
786 | */ | |
787 | if (i == pd->level_count) | |
788 | i--; | |
789 | ||
790 | if (pd->enabled) { | |
e3e56c05 BA |
791 | /* Ensure that the domain isn't turn off */ |
792 | if (i < pd->enable_corner) | |
793 | i = pd->enable_corner; | |
794 | ||
279b7e8a RN |
795 | ret = rpmhpd_aggregate_corner(pd, i); |
796 | if (ret) | |
797 | goto out; | |
798 | } | |
799 | ||
800 | pd->corner = i; | |
801 | out: | |
802 | mutex_unlock(&rpmhpd_lock); | |
803 | ||
804 | return ret; | |
805 | } | |
806 | ||
279b7e8a RN |
807 | static int rpmhpd_update_level_mapping(struct rpmhpd *rpmhpd) |
808 | { | |
809 | int i; | |
810 | const u16 *buf; | |
811 | ||
812 | buf = cmd_db_read_aux_data(rpmhpd->res_name, &rpmhpd->level_count); | |
813 | if (IS_ERR(buf)) | |
814 | return PTR_ERR(buf); | |
815 | ||
816 | /* 2 bytes used for each command DB aux data entry */ | |
817 | rpmhpd->level_count >>= 1; | |
818 | ||
819 | if (rpmhpd->level_count > RPMH_ARC_MAX_LEVELS) | |
820 | return -EINVAL; | |
821 | ||
822 | for (i = 0; i < rpmhpd->level_count; i++) { | |
823 | rpmhpd->level[i] = buf[i]; | |
824 | ||
e3e56c05 BA |
825 | /* Remember the first corner with non-zero level */ |
826 | if (!rpmhpd->level[rpmhpd->enable_corner] && rpmhpd->level[i]) | |
827 | rpmhpd->enable_corner = i; | |
828 | ||
279b7e8a RN |
829 | /* |
830 | * The AUX data may be zero padded. These 0 valued entries at | |
831 | * the end of the map must be ignored. | |
832 | */ | |
833 | if (i > 0 && rpmhpd->level[i] == 0) { | |
834 | rpmhpd->level_count = i; | |
835 | break; | |
836 | } | |
837 | pr_debug("%s: ARC hlvl=%2d --> vlvl=%4u\n", rpmhpd->res_name, i, | |
838 | rpmhpd->level[i]); | |
839 | } | |
840 | ||
841 | return 0; | |
842 | } | |
843 | ||
844 | static int rpmhpd_probe(struct platform_device *pdev) | |
845 | { | |
846 | int i, ret; | |
847 | size_t num_pds; | |
848 | struct device *dev = &pdev->dev; | |
849 | struct genpd_onecell_data *data; | |
850 | struct rpmhpd **rpmhpds; | |
851 | const struct rpmhpd_desc *desc; | |
852 | ||
853 | desc = of_device_get_match_data(dev); | |
854 | if (!desc) | |
855 | return -EINVAL; | |
856 | ||
857 | rpmhpds = desc->rpmhpds; | |
858 | num_pds = desc->num_pds; | |
859 | ||
860 | data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); | |
861 | if (!data) | |
862 | return -ENOMEM; | |
863 | ||
864 | data->domains = devm_kcalloc(dev, num_pds, sizeof(*data->domains), | |
865 | GFP_KERNEL); | |
866 | if (!data->domains) | |
867 | return -ENOMEM; | |
868 | ||
869 | data->num_domains = num_pds; | |
870 | ||
871 | for (i = 0; i < num_pds; i++) { | |
af77132e | 872 | if (!rpmhpds[i]) |
279b7e8a | 873 | continue; |
279b7e8a RN |
874 | |
875 | rpmhpds[i]->dev = dev; | |
876 | rpmhpds[i]->addr = cmd_db_read_addr(rpmhpds[i]->res_name); | |
877 | if (!rpmhpds[i]->addr) { | |
878 | dev_err(dev, "Could not find RPMh address for resource %s\n", | |
879 | rpmhpds[i]->res_name); | |
880 | return -ENODEV; | |
881 | } | |
882 | ||
883 | ret = cmd_db_read_slave_id(rpmhpds[i]->res_name); | |
884 | if (ret != CMD_DB_HW_ARC) { | |
885 | dev_err(dev, "RPMh slave ID mismatch\n"); | |
886 | return -EINVAL; | |
887 | } | |
888 | ||
889 | ret = rpmhpd_update_level_mapping(rpmhpds[i]); | |
890 | if (ret) | |
891 | return ret; | |
892 | ||
893 | rpmhpds[i]->pd.power_off = rpmhpd_power_off; | |
894 | rpmhpds[i]->pd.power_on = rpmhpd_power_on; | |
895 | rpmhpds[i]->pd.set_performance_state = rpmhpd_set_performance_state; | |
279b7e8a RN |
896 | pm_genpd_init(&rpmhpds[i]->pd, NULL, true); |
897 | ||
898 | data->domains[i] = &rpmhpds[i]->pd; | |
899 | } | |
900 | ||
0503aec2 RN |
901 | /* Add subdomains */ |
902 | for (i = 0; i < num_pds; i++) { | |
903 | if (!rpmhpds[i]) | |
904 | continue; | |
905 | if (rpmhpds[i]->parent) | |
906 | pm_genpd_add_subdomain(rpmhpds[i]->parent, | |
907 | &rpmhpds[i]->pd); | |
908 | } | |
909 | ||
279b7e8a RN |
910 | return of_genpd_add_provider_onecell(pdev->dev.of_node, data); |
911 | } | |
912 | ||
3a39049f BA |
913 | static void rpmhpd_sync_state(struct device *dev) |
914 | { | |
915 | const struct rpmhpd_desc *desc = of_device_get_match_data(dev); | |
916 | struct rpmhpd **rpmhpds = desc->rpmhpds; | |
917 | unsigned int corner; | |
918 | struct rpmhpd *pd; | |
919 | unsigned int i; | |
920 | int ret; | |
921 | ||
922 | mutex_lock(&rpmhpd_lock); | |
923 | for (i = 0; i < desc->num_pds; i++) { | |
924 | pd = rpmhpds[i]; | |
925 | if (!pd) | |
926 | continue; | |
927 | ||
928 | pd->state_synced = true; | |
929 | if (pd->enabled) | |
930 | corner = max(pd->corner, pd->enable_corner); | |
931 | else | |
932 | corner = 0; | |
933 | ||
934 | ret = rpmhpd_aggregate_corner(pd, corner); | |
935 | if (ret) | |
936 | dev_err(dev, "failed to sync %s\n", pd->res_name); | |
937 | } | |
938 | mutex_unlock(&rpmhpd_lock); | |
939 | } | |
940 | ||
279b7e8a RN |
941 | static struct platform_driver rpmhpd_driver = { |
942 | .driver = { | |
943 | .name = "qcom-rpmhpd", | |
944 | .of_match_table = rpmhpd_match_table, | |
945 | .suppress_bind_attrs = true, | |
3a39049f | 946 | .sync_state = rpmhpd_sync_state, |
279b7e8a RN |
947 | }, |
948 | .probe = rpmhpd_probe, | |
949 | }; | |
950 | ||
951 | static int __init rpmhpd_init(void) | |
952 | { | |
953 | return platform_driver_register(&rpmhpd_driver); | |
954 | } | |
955 | core_initcall(rpmhpd_init); | |
d4889ec1 JS |
956 | |
957 | MODULE_DESCRIPTION("Qualcomm Technologies, Inc. RPMh Power Domain Driver"); | |
958 | MODULE_LICENSE("GPL v2"); |