brintos

brintos / linux-shallow public Read only

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