cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

rpmhpd.c (17394B)


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