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