1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2018, The Linux Foundation. All rights reserved.*/
4 #include <linux/cleanup.h>
6 #include <linux/init.h>
7 #include <linux/kernel.h>
8 #include <linux/module.h>
9 #include <linux/mutex.h>
10 #include <linux/pm_domain.h>
11 #include <linux/slab.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 #include <dt-bindings/power/qcom,rpmhpd.h>
20 #define domain_to_rpmhpd(domain) container_of(domain, struct rpmhpd, pd)
22 #define RPMH_ARC_MAX_LEVELS 16
25 * struct rpmhpd - top level RPMh power domain resource data structure
26 * @dev: rpmh power domain controller device
27 * @pd: generic_pm_domain corresponding to the power domain
28 * @parent: generic_pm_domain corresponding to the parent's power domain
29 * @peer: A peer power domain in case Active only Voting is
31 * @active_only: True if it represents an Active only peer
32 * @corner: current corner
33 * @active_corner: current active corner
34 * @enable_corner: lowest non-zero corner
35 * @level: An array of level (vlvl) to corner (hlvl) mappings
37 * @level_count: Number of levels supported by the power domain. max
39 * @enabled: true if the power domain is enabled
40 * @res_name: Resource name used for cmd-db lookup
41 * @addr: Resource address as looped up using resource name from
43 * @state_synced: Indicator that sync_state has been invoked for the rpmhpd resource
44 * @skip_retention_level: Indicate that retention level should not be used for the power domain
48 struct generic_pm_domain pd
;
49 struct generic_pm_domain
*parent
;
51 const bool active_only
;
53 unsigned int active_corner
;
54 unsigned int enable_corner
;
55 u32 level
[RPMH_ARC_MAX_LEVELS
];
61 bool skip_retention_level
;
65 struct rpmhpd
**rpmhpds
;
69 static DEFINE_MUTEX(rpmhpd_lock
);
71 /* RPMH powerdomains */
73 static struct rpmhpd cx_ao
;
74 static struct rpmhpd mx
;
75 static struct rpmhpd mx_ao
;
76 static struct rpmhpd cx
= {
77 .pd
= { .name
= "cx", },
82 static struct rpmhpd cx_ao
= {
83 .pd
= { .name
= "cx_ao", },
89 static struct rpmhpd cx_ao_w_mx_parent
;
90 static struct rpmhpd cx_w_mx_parent
= {
91 .pd
= { .name
= "cx", },
92 .peer
= &cx_ao_w_mx_parent
,
97 static struct rpmhpd cx_ao_w_mx_parent
= {
98 .pd
= { .name
= "cx_ao", },
100 .peer
= &cx_w_mx_parent
,
102 .res_name
= "cx.lvl",
105 static struct rpmhpd ebi
= {
106 .pd
= { .name
= "ebi", },
107 .res_name
= "ebi.lvl",
110 static struct rpmhpd gfx
= {
111 .pd
= { .name
= "gfx", },
112 .res_name
= "gfx.lvl",
115 static struct rpmhpd lcx
= {
116 .pd
= { .name
= "lcx", },
117 .res_name
= "lcx.lvl",
120 static struct rpmhpd lmx
= {
121 .pd
= { .name
= "lmx", },
122 .res_name
= "lmx.lvl",
125 static struct rpmhpd mmcx_ao
;
126 static struct rpmhpd mmcx
= {
127 .pd
= { .name
= "mmcx", },
129 .res_name
= "mmcx.lvl",
132 static struct rpmhpd mmcx_ao
= {
133 .pd
= { .name
= "mmcx_ao", },
136 .res_name
= "mmcx.lvl",
139 static struct rpmhpd mmcx_ao_w_cx_parent
;
140 static struct rpmhpd mmcx_w_cx_parent
= {
141 .pd
= { .name
= "mmcx", },
142 .peer
= &mmcx_ao_w_cx_parent
,
144 .res_name
= "mmcx.lvl",
147 static struct rpmhpd mmcx_ao_w_cx_parent
= {
148 .pd
= { .name
= "mmcx_ao", },
150 .peer
= &mmcx_w_cx_parent
,
152 .res_name
= "mmcx.lvl",
155 static struct rpmhpd mss
= {
156 .pd
= { .name
= "mss", },
157 .res_name
= "mss.lvl",
160 static struct rpmhpd mx_ao
;
161 static struct rpmhpd mx
= {
162 .pd
= { .name
= "mx", },
164 .res_name
= "mx.lvl",
167 static struct rpmhpd mx_ao
= {
168 .pd
= { .name
= "mx_ao", },
171 .res_name
= "mx.lvl",
174 static struct rpmhpd mxc_ao
;
175 static struct rpmhpd mxc
= {
176 .pd
= { .name
= "mxc", },
178 .res_name
= "mxc.lvl",
179 .skip_retention_level
= true,
182 static struct rpmhpd mxc_ao
= {
183 .pd
= { .name
= "mxc_ao", },
186 .res_name
= "mxc.lvl",
187 .skip_retention_level
= true,
190 static struct rpmhpd nsp
= {
191 .pd
= { .name
= "nsp", },
192 .res_name
= "nsp.lvl",
195 static struct rpmhpd nsp0
= {
196 .pd
= { .name
= "nsp0", },
197 .res_name
= "nsp0.lvl",
200 static struct rpmhpd nsp1
= {
201 .pd
= { .name
= "nsp1", },
202 .res_name
= "nsp1.lvl",
205 static struct rpmhpd nsp2
= {
206 .pd
= { .name
= "nsp2", },
207 .res_name
= "nsp2.lvl",
210 static struct rpmhpd qphy
= {
211 .pd
= { .name
= "qphy", },
212 .res_name
= "qphy.lvl",
215 static struct rpmhpd gmxc
= {
216 .pd
= { .name
= "gmxc", },
217 .res_name
= "gmxc.lvl",
220 /* SA8540P RPMH powerdomains */
221 static struct rpmhpd
*sa8540p_rpmhpds
[] = {
223 [SC8280XP_CX_AO
] = &cx_ao
,
224 [SC8280XP_EBI
] = &ebi
,
225 [SC8280XP_LCX
] = &lcx
,
226 [SC8280XP_LMX
] = &lmx
,
227 [SC8280XP_MMCX
] = &mmcx
,
228 [SC8280XP_MMCX_AO
] = &mmcx_ao
,
230 [SC8280XP_MX_AO
] = &mx_ao
,
231 [SC8280XP_NSP
] = &nsp
,
234 static const struct rpmhpd_desc sa8540p_desc
= {
235 .rpmhpds
= sa8540p_rpmhpds
,
236 .num_pds
= ARRAY_SIZE(sa8540p_rpmhpds
),
239 /* SA8775P RPMH power domains */
240 static struct rpmhpd
*sa8775p_rpmhpds
[] = {
242 [SA8775P_CX_AO
] = &cx_ao
,
243 [SA8775P_EBI
] = &ebi
,
244 [SA8775P_GFX
] = &gfx
,
245 [SA8775P_LCX
] = &lcx
,
246 [SA8775P_LMX
] = &lmx
,
247 [SA8775P_MMCX
] = &mmcx
,
248 [SA8775P_MMCX_AO
] = &mmcx_ao
,
249 [SA8775P_MXC
] = &mxc
,
250 [SA8775P_MXC_AO
] = &mxc_ao
,
252 [SA8775P_MX_AO
] = &mx_ao
,
253 [SA8775P_NSP0
] = &nsp0
,
254 [SA8775P_NSP1
] = &nsp1
,
257 static const struct rpmhpd_desc sa8775p_desc
= {
258 .rpmhpds
= sa8775p_rpmhpds
,
259 .num_pds
= ARRAY_SIZE(sa8775p_rpmhpds
),
262 /* SAR2130P RPMH powerdomains */
263 static struct rpmhpd
*sar2130p_rpmhpds
[] = {
265 [RPMHPD_CX_AO
] = &cx_ao
,
270 [RPMHPD_MMCX
] = &mmcx_w_cx_parent
,
271 [RPMHPD_MMCX_AO
] = &mmcx_ao_w_cx_parent
,
274 [RPMHPD_MX_AO
] = &mx_ao
,
276 [RPMHPD_MXC_AO
] = &mxc_ao
,
278 [RPMHPD_QPHY
] = &qphy
,
281 static const struct rpmhpd_desc sar2130p_desc
= {
282 .rpmhpds
= sar2130p_rpmhpds
,
283 .num_pds
= ARRAY_SIZE(sar2130p_rpmhpds
),
286 /* SDM670 RPMH powerdomains */
287 static struct rpmhpd
*sdm670_rpmhpds
[] = {
288 [SDM670_CX
] = &cx_w_mx_parent
,
289 [SDM670_CX_AO
] = &cx_ao_w_mx_parent
,
295 [SDM670_MX_AO
] = &mx_ao
,
298 static const struct rpmhpd_desc sdm670_desc
= {
299 .rpmhpds
= sdm670_rpmhpds
,
300 .num_pds
= ARRAY_SIZE(sdm670_rpmhpds
),
303 /* SDM845 RPMH powerdomains */
304 static struct rpmhpd
*sdm845_rpmhpds
[] = {
305 [SDM845_CX
] = &cx_w_mx_parent
,
306 [SDM845_CX_AO
] = &cx_ao_w_mx_parent
,
313 [SDM845_MX_AO
] = &mx_ao
,
316 static const struct rpmhpd_desc sdm845_desc
= {
317 .rpmhpds
= sdm845_rpmhpds
,
318 .num_pds
= ARRAY_SIZE(sdm845_rpmhpds
),
321 /* SDX55 RPMH powerdomains */
322 static struct rpmhpd
*sdx55_rpmhpds
[] = {
323 [SDX55_CX
] = &cx_w_mx_parent
,
328 static const struct rpmhpd_desc sdx55_desc
= {
329 .rpmhpds
= sdx55_rpmhpds
,
330 .num_pds
= ARRAY_SIZE(sdx55_rpmhpds
),
333 /* SDX65 RPMH powerdomains */
334 static struct rpmhpd
*sdx65_rpmhpds
[] = {
335 [SDX65_CX
] = &cx_w_mx_parent
,
336 [SDX65_CX_AO
] = &cx_ao_w_mx_parent
,
339 [SDX65_MX_AO
] = &mx_ao
,
343 static const struct rpmhpd_desc sdx65_desc
= {
344 .rpmhpds
= sdx65_rpmhpds
,
345 .num_pds
= ARRAY_SIZE(sdx65_rpmhpds
),
348 /* SDX75 RPMH powerdomains */
349 static struct rpmhpd
*sdx75_rpmhpds
[] = {
351 [RPMHPD_CX_AO
] = &cx_ao
,
354 [RPMHPD_MX_AO
] = &mx_ao
,
358 static const struct rpmhpd_desc sdx75_desc
= {
359 .rpmhpds
= sdx75_rpmhpds
,
360 .num_pds
= ARRAY_SIZE(sdx75_rpmhpds
),
363 /* SM6350 RPMH powerdomains */
364 static struct rpmhpd
*sm6350_rpmhpds
[] = {
365 [SM6350_CX
] = &cx_w_mx_parent
,
373 static const struct rpmhpd_desc sm6350_desc
= {
374 .rpmhpds
= sm6350_rpmhpds
,
375 .num_pds
= ARRAY_SIZE(sm6350_rpmhpds
),
378 /* SM7150 RPMH powerdomains */
379 static struct rpmhpd
*sm7150_rpmhpds
[] = {
380 [RPMHPD_CX
] = &cx_w_mx_parent
,
381 [RPMHPD_CX_AO
] = &cx_ao_w_mx_parent
,
386 [RPMHPD_MX_AO
] = &mx_ao
,
390 static const struct rpmhpd_desc sm7150_desc
= {
391 .rpmhpds
= sm7150_rpmhpds
,
392 .num_pds
= ARRAY_SIZE(sm7150_rpmhpds
),
395 /* SM8150 RPMH powerdomains */
396 static struct rpmhpd
*sm8150_rpmhpds
[] = {
397 [SM8150_CX
] = &cx_w_mx_parent
,
398 [SM8150_CX_AO
] = &cx_ao_w_mx_parent
,
403 [SM8150_MMCX
] = &mmcx
,
404 [SM8150_MMCX_AO
] = &mmcx_ao
,
407 [SM8150_MX_AO
] = &mx_ao
,
410 static const struct rpmhpd_desc sm8150_desc
= {
411 .rpmhpds
= sm8150_rpmhpds
,
412 .num_pds
= ARRAY_SIZE(sm8150_rpmhpds
),
415 static struct rpmhpd
*sa8155p_rpmhpds
[] = {
416 [SA8155P_CX
] = &cx_w_mx_parent
,
417 [SA8155P_CX_AO
] = &cx_ao_w_mx_parent
,
418 [SA8155P_EBI
] = &ebi
,
419 [SA8155P_GFX
] = &gfx
,
420 [SA8155P_MSS
] = &mss
,
422 [SA8155P_MX_AO
] = &mx_ao
,
425 static const struct rpmhpd_desc sa8155p_desc
= {
426 .rpmhpds
= sa8155p_rpmhpds
,
427 .num_pds
= ARRAY_SIZE(sa8155p_rpmhpds
),
430 /* SM8250 RPMH powerdomains */
431 static struct rpmhpd
*sm8250_rpmhpds
[] = {
432 [RPMHPD_CX
] = &cx_w_mx_parent
,
433 [RPMHPD_CX_AO
] = &cx_ao_w_mx_parent
,
438 [RPMHPD_MMCX
] = &mmcx
,
439 [RPMHPD_MMCX_AO
] = &mmcx_ao
,
441 [RPMHPD_MX_AO
] = &mx_ao
,
444 static const struct rpmhpd_desc sm8250_desc
= {
445 .rpmhpds
= sm8250_rpmhpds
,
446 .num_pds
= ARRAY_SIZE(sm8250_rpmhpds
),
449 /* SM8350 Power domains */
450 static struct rpmhpd
*sm8350_rpmhpds
[] = {
451 [RPMHPD_CX
] = &cx_w_mx_parent
,
452 [RPMHPD_CX_AO
] = &cx_ao_w_mx_parent
,
457 [RPMHPD_MMCX
] = &mmcx
,
458 [RPMHPD_MMCX_AO
] = &mmcx_ao
,
461 [RPMHPD_MX_AO
] = &mx_ao
,
463 [RPMHPD_MXC_AO
] = &mxc_ao
,
466 static const struct rpmhpd_desc sm8350_desc
= {
467 .rpmhpds
= sm8350_rpmhpds
,
468 .num_pds
= ARRAY_SIZE(sm8350_rpmhpds
),
471 /* SM8450 RPMH powerdomains */
472 static struct rpmhpd
*sm8450_rpmhpds
[] = {
474 [RPMHPD_CX_AO
] = &cx_ao
,
479 [RPMHPD_MMCX
] = &mmcx_w_cx_parent
,
480 [RPMHPD_MMCX_AO
] = &mmcx_ao_w_cx_parent
,
483 [RPMHPD_MX_AO
] = &mx_ao
,
485 [RPMHPD_MXC_AO
] = &mxc_ao
,
488 static const struct rpmhpd_desc sm8450_desc
= {
489 .rpmhpds
= sm8450_rpmhpds
,
490 .num_pds
= ARRAY_SIZE(sm8450_rpmhpds
),
493 /* SM8550 RPMH powerdomains */
494 static struct rpmhpd
*sm8550_rpmhpds
[] = {
496 [RPMHPD_CX_AO
] = &cx_ao
,
501 [RPMHPD_MMCX
] = &mmcx_w_cx_parent
,
502 [RPMHPD_MMCX_AO
] = &mmcx_ao_w_cx_parent
,
505 [RPMHPD_MX_AO
] = &mx_ao
,
507 [RPMHPD_MXC_AO
] = &mxc_ao
,
511 static const struct rpmhpd_desc sm8550_desc
= {
512 .rpmhpds
= sm8550_rpmhpds
,
513 .num_pds
= ARRAY_SIZE(sm8550_rpmhpds
),
516 /* SM8650 RPMH powerdomains */
517 static struct rpmhpd
*sm8650_rpmhpds
[] = {
519 [RPMHPD_CX_AO
] = &cx_ao
,
524 [RPMHPD_MMCX
] = &mmcx_w_cx_parent
,
525 [RPMHPD_MMCX_AO
] = &mmcx_ao_w_cx_parent
,
528 [RPMHPD_MX_AO
] = &mx_ao
,
530 [RPMHPD_MXC_AO
] = &mxc_ao
,
532 [RPMHPD_NSP2
] = &nsp2
,
535 static const struct rpmhpd_desc sm8650_desc
= {
536 .rpmhpds
= sm8650_rpmhpds
,
537 .num_pds
= ARRAY_SIZE(sm8650_rpmhpds
),
540 /* SM8750 RPMH powerdomains */
541 static struct rpmhpd
*sm8750_rpmhpds
[] = {
543 [RPMHPD_CX_AO
] = &cx_ao
,
546 [RPMHPD_GMXC
] = &gmxc
,
550 [RPMHPD_MX_AO
] = &mx_ao
,
551 [RPMHPD_MMCX
] = &mmcx_w_cx_parent
,
552 [RPMHPD_MMCX_AO
] = &mmcx_ao_w_cx_parent
,
555 [RPMHPD_MXC_AO
] = &mxc_ao
,
557 [RPMHPD_NSP2
] = &nsp2
,
560 static const struct rpmhpd_desc sm8750_desc
= {
561 .rpmhpds
= sm8750_rpmhpds
,
562 .num_pds
= ARRAY_SIZE(sm8750_rpmhpds
),
565 /* QDU1000/QRU1000 RPMH powerdomains */
566 static struct rpmhpd
*qdu1000_rpmhpds
[] = {
568 [QDU1000_EBI
] = &ebi
,
569 [QDU1000_MSS
] = &mss
,
573 static const struct rpmhpd_desc qdu1000_desc
= {
574 .rpmhpds
= qdu1000_rpmhpds
,
575 .num_pds
= ARRAY_SIZE(qdu1000_rpmhpds
),
578 /* SC7180 RPMH powerdomains */
579 static struct rpmhpd
*sc7180_rpmhpds
[] = {
580 [SC7180_CX
] = &cx_w_mx_parent
,
581 [SC7180_CX_AO
] = &cx_ao_w_mx_parent
,
587 [SC7180_MX_AO
] = &mx_ao
,
590 static const struct rpmhpd_desc sc7180_desc
= {
591 .rpmhpds
= sc7180_rpmhpds
,
592 .num_pds
= ARRAY_SIZE(sc7180_rpmhpds
),
595 /* SC7280 RPMH powerdomains */
596 static struct rpmhpd
*sc7280_rpmhpds
[] = {
598 [SC7280_CX_AO
] = &cx_ao
,
605 [SC7280_MX_AO
] = &mx_ao
,
608 static const struct rpmhpd_desc sc7280_desc
= {
609 .rpmhpds
= sc7280_rpmhpds
,
610 .num_pds
= ARRAY_SIZE(sc7280_rpmhpds
),
613 /* SC8180x RPMH powerdomains */
614 static struct rpmhpd
*sc8180x_rpmhpds
[] = {
615 [SC8180X_CX
] = &cx_w_mx_parent
,
616 [SC8180X_CX_AO
] = &cx_ao_w_mx_parent
,
617 [SC8180X_EBI
] = &ebi
,
618 [SC8180X_GFX
] = &gfx
,
619 [SC8180X_LCX
] = &lcx
,
620 [SC8180X_LMX
] = &lmx
,
621 [SC8180X_MMCX
] = &mmcx
,
622 [SC8180X_MMCX_AO
] = &mmcx_ao
,
623 [SC8180X_MSS
] = &mss
,
625 [SC8180X_MX_AO
] = &mx_ao
,
628 static const struct rpmhpd_desc sc8180x_desc
= {
629 .rpmhpds
= sc8180x_rpmhpds
,
630 .num_pds
= ARRAY_SIZE(sc8180x_rpmhpds
),
633 /* SC8280xp RPMH powerdomains */
634 static struct rpmhpd
*sc8280xp_rpmhpds
[] = {
636 [SC8280XP_CX_AO
] = &cx_ao
,
637 [SC8280XP_EBI
] = &ebi
,
638 [SC8280XP_GFX
] = &gfx
,
639 [SC8280XP_LCX
] = &lcx
,
640 [SC8280XP_LMX
] = &lmx
,
641 [SC8280XP_MMCX
] = &mmcx
,
642 [SC8280XP_MMCX_AO
] = &mmcx_ao
,
644 [SC8280XP_MX_AO
] = &mx_ao
,
645 [SC8280XP_NSP
] = &nsp
,
646 [SC8280XP_QPHY
] = &qphy
,
649 static const struct rpmhpd_desc sc8280xp_desc
= {
650 .rpmhpds
= sc8280xp_rpmhpds
,
651 .num_pds
= ARRAY_SIZE(sc8280xp_rpmhpds
),
654 /* X1E80100 RPMH powerdomains */
655 static struct rpmhpd
*x1e80100_rpmhpds
[] = {
657 [RPMHPD_CX_AO
] = &cx_ao
,
662 [RPMHPD_MMCX
] = &mmcx
,
663 [RPMHPD_MMCX_AO
] = &mmcx_ao
,
665 [RPMHPD_MX_AO
] = &mx_ao
,
668 [RPMHPD_GMXC
] = &gmxc
,
671 static const struct rpmhpd_desc x1e80100_desc
= {
672 .rpmhpds
= x1e80100_rpmhpds
,
673 .num_pds
= ARRAY_SIZE(x1e80100_rpmhpds
),
676 /* QCS8300 RPMH power domains */
677 static struct rpmhpd
*qcs8300_rpmhpds
[] = {
679 [RPMHPD_CX_AO
] = &cx_ao
,
684 [RPMHPD_MMCX
] = &mmcx_w_cx_parent
,
685 [RPMHPD_MMCX_AO
] = &mmcx_ao_w_cx_parent
,
687 [RPMHPD_MXC_AO
] = &mxc_ao
,
689 [RPMHPD_MX_AO
] = &mx_ao
,
690 [RPMHPD_NSP0
] = &nsp0
,
691 [RPMHPD_NSP1
] = &nsp1
,
694 static const struct rpmhpd_desc qcs8300_desc
= {
695 .rpmhpds
= qcs8300_rpmhpds
,
696 .num_pds
= ARRAY_SIZE(qcs8300_rpmhpds
),
699 /* QCS615 RPMH powerdomains */
700 static struct rpmhpd
*qcs615_rpmhpds
[] = {
702 [RPMHPD_CX_AO
] = &cx_ao
,
705 static const struct rpmhpd_desc qcs615_desc
= {
706 .rpmhpds
= qcs615_rpmhpds
,
707 .num_pds
= ARRAY_SIZE(qcs615_rpmhpds
),
710 static const struct of_device_id rpmhpd_match_table
[] = {
711 { .compatible
= "qcom,qcs615-rpmhpd", .data
= &qcs615_desc
},
712 { .compatible
= "qcom,qcs8300-rpmhpd", .data
= &qcs8300_desc
},
713 { .compatible
= "qcom,qdu1000-rpmhpd", .data
= &qdu1000_desc
},
714 { .compatible
= "qcom,sa8155p-rpmhpd", .data
= &sa8155p_desc
},
715 { .compatible
= "qcom,sa8540p-rpmhpd", .data
= &sa8540p_desc
},
716 { .compatible
= "qcom,sa8775p-rpmhpd", .data
= &sa8775p_desc
},
717 { .compatible
= "qcom,sar2130p-rpmhpd", .data
= &sar2130p_desc
},
718 { .compatible
= "qcom,sc7180-rpmhpd", .data
= &sc7180_desc
},
719 { .compatible
= "qcom,sc7280-rpmhpd", .data
= &sc7280_desc
},
720 { .compatible
= "qcom,sc8180x-rpmhpd", .data
= &sc8180x_desc
},
721 { .compatible
= "qcom,sc8280xp-rpmhpd", .data
= &sc8280xp_desc
},
722 { .compatible
= "qcom,sdm670-rpmhpd", .data
= &sdm670_desc
},
723 { .compatible
= "qcom,sdm845-rpmhpd", .data
= &sdm845_desc
},
724 { .compatible
= "qcom,sdx55-rpmhpd", .data
= &sdx55_desc
},
725 { .compatible
= "qcom,sdx65-rpmhpd", .data
= &sdx65_desc
},
726 { .compatible
= "qcom,sdx75-rpmhpd", .data
= &sdx75_desc
},
727 { .compatible
= "qcom,sm6350-rpmhpd", .data
= &sm6350_desc
},
728 { .compatible
= "qcom,sm7150-rpmhpd", .data
= &sm7150_desc
},
729 { .compatible
= "qcom,sm8150-rpmhpd", .data
= &sm8150_desc
},
730 { .compatible
= "qcom,sm8250-rpmhpd", .data
= &sm8250_desc
},
731 { .compatible
= "qcom,sm8350-rpmhpd", .data
= &sm8350_desc
},
732 { .compatible
= "qcom,sm8450-rpmhpd", .data
= &sm8450_desc
},
733 { .compatible
= "qcom,sm8550-rpmhpd", .data
= &sm8550_desc
},
734 { .compatible
= "qcom,sm8650-rpmhpd", .data
= &sm8650_desc
},
735 { .compatible
= "qcom,sm8750-rpmhpd", .data
= &sm8750_desc
},
736 { .compatible
= "qcom,x1e80100-rpmhpd", .data
= &x1e80100_desc
},
739 MODULE_DEVICE_TABLE(of
, rpmhpd_match_table
);
741 static int rpmhpd_send_corner(struct rpmhpd
*pd
, int state
,
742 unsigned int corner
, bool sync
)
744 struct tcs_cmd cmd
= {
750 * Wait for an ack only when we are increasing the
751 * perf state of the power domain
754 return rpmh_write(pd
->dev
, state
, &cmd
, 1);
756 return rpmh_write_async(pd
->dev
, state
, &cmd
, 1);
759 static void to_active_sleep(struct rpmhpd
*pd
, unsigned int corner
,
760 unsigned int *active
, unsigned int *sleep
)
771 * This function is used to aggregate the votes across the active only
772 * resources and its peers. The aggregated votes are sent to RPMh as
773 * ACTIVE_ONLY votes (which take effect immediately), as WAKE_ONLY votes
774 * (applied by RPMh on system wakeup) and as SLEEP votes (applied by RPMh
776 * We send ACTIVE_ONLY votes for resources without any peers. For others,
777 * which have an active only peer, all 3 votes are sent.
779 static int rpmhpd_aggregate_corner(struct rpmhpd
*pd
, unsigned int corner
)
782 struct rpmhpd
*peer
= pd
->peer
;
783 unsigned int active_corner
, sleep_corner
;
784 unsigned int this_active_corner
= 0, this_sleep_corner
= 0;
785 unsigned int peer_active_corner
= 0, peer_sleep_corner
= 0;
786 unsigned int peer_enabled_corner
;
788 if (pd
->state_synced
) {
789 to_active_sleep(pd
, corner
, &this_active_corner
, &this_sleep_corner
);
791 /* Clamp to highest corner if sync_state hasn't happened */
792 this_active_corner
= pd
->level_count
- 1;
793 this_sleep_corner
= pd
->level_count
- 1;
796 if (peer
&& peer
->enabled
) {
797 peer_enabled_corner
= max(peer
->corner
, peer
->enable_corner
);
798 to_active_sleep(peer
, peer_enabled_corner
, &peer_active_corner
,
802 active_corner
= max(this_active_corner
, peer_active_corner
);
804 ret
= rpmhpd_send_corner(pd
, RPMH_ACTIVE_ONLY_STATE
, active_corner
,
805 active_corner
> pd
->active_corner
);
809 pd
->active_corner
= active_corner
;
812 peer
->active_corner
= active_corner
;
814 ret
= rpmhpd_send_corner(pd
, RPMH_WAKE_ONLY_STATE
,
815 active_corner
, false);
819 sleep_corner
= max(this_sleep_corner
, peer_sleep_corner
);
821 return rpmhpd_send_corner(pd
, RPMH_SLEEP_STATE
, sleep_corner
,
828 static int rpmhpd_power_on(struct generic_pm_domain
*domain
)
830 struct rpmhpd
*pd
= domain_to_rpmhpd(domain
);
834 mutex_lock(&rpmhpd_lock
);
836 corner
= max(pd
->corner
, pd
->enable_corner
);
837 ret
= rpmhpd_aggregate_corner(pd
, corner
);
841 mutex_unlock(&rpmhpd_lock
);
846 static int rpmhpd_power_off(struct generic_pm_domain
*domain
)
848 struct rpmhpd
*pd
= domain_to_rpmhpd(domain
);
851 mutex_lock(&rpmhpd_lock
);
853 ret
= rpmhpd_aggregate_corner(pd
, 0);
857 mutex_unlock(&rpmhpd_lock
);
862 static int rpmhpd_set_performance_state(struct generic_pm_domain
*domain
,
865 struct rpmhpd
*pd
= domain_to_rpmhpd(domain
);
868 guard(mutex
)(&rpmhpd_lock
);
870 for (i
= 0; i
< pd
->level_count
; i
++)
871 if (level
<= pd
->level
[i
])
875 * If the level requested is more than that supported by the
876 * max corner, just set it to max anyway.
878 if (i
== pd
->level_count
)
882 /* Ensure that the domain isn't turn off */
883 if (i
< pd
->enable_corner
)
884 i
= pd
->enable_corner
;
886 ret
= rpmhpd_aggregate_corner(pd
, i
);
896 static int rpmhpd_update_level_mapping(struct rpmhpd
*rpmhpd
)
901 buf
= cmd_db_read_aux_data(rpmhpd
->res_name
, &rpmhpd
->level_count
);
905 /* 2 bytes used for each command DB aux data entry */
906 rpmhpd
->level_count
>>= 1;
908 if (rpmhpd
->level_count
> RPMH_ARC_MAX_LEVELS
)
911 for (i
= 0; i
< rpmhpd
->level_count
; i
++) {
912 if (rpmhpd
->skip_retention_level
&& buf
[i
] == RPMH_REGULATOR_LEVEL_RETENTION
)
915 rpmhpd
->level
[i
] = buf
[i
];
917 /* Remember the first corner with non-zero level */
918 if (!rpmhpd
->level
[rpmhpd
->enable_corner
] && rpmhpd
->level
[i
])
919 rpmhpd
->enable_corner
= i
;
922 * The AUX data may be zero padded. These 0 valued entries at
923 * the end of the map must be ignored.
925 if (i
> 0 && rpmhpd
->level
[i
] == 0) {
926 rpmhpd
->level_count
= i
;
929 pr_debug("%s: ARC hlvl=%2d --> vlvl=%4u\n", rpmhpd
->res_name
, i
,
936 static int rpmhpd_probe(struct platform_device
*pdev
)
940 struct device
*dev
= &pdev
->dev
;
941 struct genpd_onecell_data
*data
;
942 struct rpmhpd
**rpmhpds
;
943 const struct rpmhpd_desc
*desc
;
945 desc
= of_device_get_match_data(dev
);
949 rpmhpds
= desc
->rpmhpds
;
950 num_pds
= desc
->num_pds
;
952 data
= devm_kzalloc(dev
, sizeof(*data
), GFP_KERNEL
);
956 data
->domains
= devm_kcalloc(dev
, num_pds
, sizeof(*data
->domains
),
961 data
->num_domains
= num_pds
;
963 for (i
= 0; i
< num_pds
; i
++) {
967 rpmhpds
[i
]->dev
= dev
;
968 rpmhpds
[i
]->addr
= cmd_db_read_addr(rpmhpds
[i
]->res_name
);
969 if (!rpmhpds
[i
]->addr
) {
970 dev_err(dev
, "Could not find RPMh address for resource %s\n",
971 rpmhpds
[i
]->res_name
);
975 ret
= cmd_db_read_slave_id(rpmhpds
[i
]->res_name
);
976 if (ret
!= CMD_DB_HW_ARC
) {
977 dev_err(dev
, "RPMh slave ID mismatch\n");
981 ret
= rpmhpd_update_level_mapping(rpmhpds
[i
]);
985 rpmhpds
[i
]->pd
.power_off
= rpmhpd_power_off
;
986 rpmhpds
[i
]->pd
.power_on
= rpmhpd_power_on
;
987 rpmhpds
[i
]->pd
.set_performance_state
= rpmhpd_set_performance_state
;
988 pm_genpd_init(&rpmhpds
[i
]->pd
, NULL
, true);
990 data
->domains
[i
] = &rpmhpds
[i
]->pd
;
994 for (i
= 0; i
< num_pds
; i
++) {
997 if (rpmhpds
[i
]->parent
)
998 pm_genpd_add_subdomain(rpmhpds
[i
]->parent
,
1002 return of_genpd_add_provider_onecell(pdev
->dev
.of_node
, data
);
1005 static void rpmhpd_sync_state(struct device
*dev
)
1007 const struct rpmhpd_desc
*desc
= of_device_get_match_data(dev
);
1008 struct rpmhpd
**rpmhpds
= desc
->rpmhpds
;
1009 unsigned int corner
;
1014 mutex_lock(&rpmhpd_lock
);
1015 for (i
= 0; i
< desc
->num_pds
; i
++) {
1020 pd
->state_synced
= true;
1022 corner
= max(pd
->corner
, pd
->enable_corner
);
1026 ret
= rpmhpd_aggregate_corner(pd
, corner
);
1028 dev_err(dev
, "failed to sync %s\n", pd
->res_name
);
1030 mutex_unlock(&rpmhpd_lock
);
1033 static struct platform_driver rpmhpd_driver
= {
1035 .name
= "qcom-rpmhpd",
1036 .of_match_table
= rpmhpd_match_table
,
1037 .suppress_bind_attrs
= true,
1038 .sync_state
= rpmhpd_sync_state
,
1040 .probe
= rpmhpd_probe
,
1043 static int __init
rpmhpd_init(void)
1045 return platform_driver_register(&rpmhpd_driver
);
1047 core_initcall(rpmhpd_init
);
1049 MODULE_DESCRIPTION("Qualcomm Technologies, Inc. RPMh Power Domain Driver");
1050 MODULE_LICENSE("GPL v2");