File Index
Files and directories
Sign in
FILE

rpmhpd.c

File preview with syntax highlighting.

linux-6.19.14/drivers/pmdomain/qcom/rpmhpd.c C
   1// SPDX-License-Identifier: GPL-2.0
   2/* 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	32
  23
  24/**
  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
  30 *			supported
  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
  36 *			derived from cmd-db
  37 * @level_count:	Number of levels supported by the power domain. max
  38 *			being 16 (0 - 15)
  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
  42 *			cmd-db
  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
  45 */
  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/* Milos RPMH powerdomains */
 221static struct rpmhpd *milos_rpmhpds[] = {
 222	[RPMHPD_CX] = &cx,
 223	[RPMHPD_CX_AO] = &cx_ao,
 224	[RPMHPD_EBI] = &ebi,
 225	[RPMHPD_GFX] = &gfx,
 226	[RPMHPD_LCX] = &lcx,
 227	[RPMHPD_LMX] = &lmx,
 228	[RPMHPD_MSS] = &mss,
 229	[RPMHPD_MX] = &mx,
 230	[RPMHPD_MX_AO] = &mx_ao,
 231};
 232
 233static const struct rpmhpd_desc milos_desc = {
 234	.rpmhpds = milos_rpmhpds,
 235	.num_pds = ARRAY_SIZE(milos_rpmhpds),
 236};
 237
 238/* SA8540P RPMH powerdomains */
 239static struct rpmhpd *sa8540p_rpmhpds[] = {
 240	[SC8280XP_CX] = &cx,
 241	[SC8280XP_CX_AO] = &cx_ao,
 242	[SC8280XP_EBI] = &ebi,
 243	[SC8280XP_LCX] = &lcx,
 244	[SC8280XP_LMX] = &lmx,
 245	[SC8280XP_MMCX] = &mmcx,
 246	[SC8280XP_MMCX_AO] = &mmcx_ao,
 247	[SC8280XP_MX] = &mx,
 248	[SC8280XP_MX_AO] = &mx_ao,
 249	[SC8280XP_MXC] = &mxc,
 250	[SC8280XP_MXC_AO] = &mxc_ao,
 251	[SC8280XP_NSP] = &nsp,
 252};
 253
 254static const struct rpmhpd_desc sa8540p_desc = {
 255	.rpmhpds = sa8540p_rpmhpds,
 256	.num_pds = ARRAY_SIZE(sa8540p_rpmhpds),
 257};
 258
 259/* SA8775P RPMH power domains */
 260static struct rpmhpd *sa8775p_rpmhpds[] = {
 261	[SA8775P_CX] = &cx,
 262	[SA8775P_CX_AO] = &cx_ao,
 263	[SA8775P_EBI] = &ebi,
 264	[SA8775P_GFX] = &gfx,
 265	[SA8775P_LCX] = &lcx,
 266	[SA8775P_LMX] = &lmx,
 267	[SA8775P_MMCX] = &mmcx,
 268	[SA8775P_MMCX_AO] = &mmcx_ao,
 269	[SA8775P_MXC] = &mxc,
 270	[SA8775P_MXC_AO] = &mxc_ao,
 271	[SA8775P_MX] = &mx,
 272	[SA8775P_MX_AO] = &mx_ao,
 273	[SA8775P_NSP0] = &nsp0,
 274	[SA8775P_NSP1] = &nsp1,
 275};
 276
 277static const struct rpmhpd_desc sa8775p_desc = {
 278	.rpmhpds = sa8775p_rpmhpds,
 279	.num_pds = ARRAY_SIZE(sa8775p_rpmhpds),
 280};
 281
 282/* SAR2130P RPMH powerdomains */
 283static struct rpmhpd *sar2130p_rpmhpds[] = {
 284	[RPMHPD_CX] = &cx,
 285	[RPMHPD_CX_AO] = &cx_ao,
 286	[RPMHPD_EBI] = &ebi,
 287	[RPMHPD_GFX] = &gfx,
 288	[RPMHPD_LCX] = &lcx,
 289	[RPMHPD_LMX] = &lmx,
 290	[RPMHPD_MMCX] = &mmcx_w_cx_parent,
 291	[RPMHPD_MMCX_AO] = &mmcx_ao_w_cx_parent,
 292	[RPMHPD_MSS] = &mss,
 293	[RPMHPD_MX] = &mx,
 294	[RPMHPD_MX_AO] = &mx_ao,
 295	[RPMHPD_MXC] = &mxc,
 296	[RPMHPD_MXC_AO] = &mxc_ao,
 297	[RPMHPD_NSP] = &nsp,
 298	[RPMHPD_QPHY] = &qphy,
 299};
 300
 301static const struct rpmhpd_desc sar2130p_desc = {
 302	.rpmhpds = sar2130p_rpmhpds,
 303	.num_pds = ARRAY_SIZE(sar2130p_rpmhpds),
 304};
 305
 306/* SDM670 RPMH powerdomains */
 307static struct rpmhpd *sdm670_rpmhpds[] = {
 308	[SDM670_CX] = &cx_w_mx_parent,
 309	[SDM670_CX_AO] = &cx_ao_w_mx_parent,
 310	[SDM670_GFX] = &gfx,
 311	[SDM670_LCX] = &lcx,
 312	[SDM670_LMX] = &lmx,
 313	[SDM670_MSS] = &mss,
 314	[SDM670_MX] = &mx,
 315	[SDM670_MX_AO] = &mx_ao,
 316};
 317
 318static const struct rpmhpd_desc sdm670_desc = {
 319	.rpmhpds = sdm670_rpmhpds,
 320	.num_pds = ARRAY_SIZE(sdm670_rpmhpds),
 321};
 322
 323/* SDM845 RPMH powerdomains */
 324static struct rpmhpd *sdm845_rpmhpds[] = {
 325	[SDM845_CX] = &cx_w_mx_parent,
 326	[SDM845_CX_AO] = &cx_ao_w_mx_parent,
 327	[SDM845_EBI] = &ebi,
 328	[SDM845_GFX] = &gfx,
 329	[SDM845_LCX] = &lcx,
 330	[SDM845_LMX] = &lmx,
 331	[SDM845_MSS] = &mss,
 332	[SDM845_MX] = &mx,
 333	[SDM845_MX_AO] = &mx_ao,
 334};
 335
 336static const struct rpmhpd_desc sdm845_desc = {
 337	.rpmhpds = sdm845_rpmhpds,
 338	.num_pds = ARRAY_SIZE(sdm845_rpmhpds),
 339};
 340
 341/* SDX55 RPMH powerdomains */
 342static struct rpmhpd *sdx55_rpmhpds[] = {
 343	[SDX55_CX] = &cx_w_mx_parent,
 344	[SDX55_MSS] = &mss,
 345	[SDX55_MX] = &mx,
 346};
 347
 348static const struct rpmhpd_desc sdx55_desc = {
 349	.rpmhpds = sdx55_rpmhpds,
 350	.num_pds = ARRAY_SIZE(sdx55_rpmhpds),
 351};
 352
 353/* SDX65 RPMH powerdomains */
 354static struct rpmhpd *sdx65_rpmhpds[] = {
 355	[SDX65_CX] = &cx_w_mx_parent,
 356	[SDX65_CX_AO] = &cx_ao_w_mx_parent,
 357	[SDX65_MSS] = &mss,
 358	[SDX65_MX] = &mx,
 359	[SDX65_MX_AO] = &mx_ao,
 360	[SDX65_MXC] = &mxc,
 361};
 362
 363static const struct rpmhpd_desc sdx65_desc = {
 364	.rpmhpds = sdx65_rpmhpds,
 365	.num_pds = ARRAY_SIZE(sdx65_rpmhpds),
 366};
 367
 368/* SDX75 RPMH powerdomains */
 369static struct rpmhpd *sdx75_rpmhpds[] = {
 370	[RPMHPD_CX] = &cx,
 371	[RPMHPD_CX_AO] = &cx_ao,
 372	[RPMHPD_MSS] = &mss,
 373	[RPMHPD_MX] = &mx,
 374	[RPMHPD_MX_AO] = &mx_ao,
 375	[RPMHPD_MXC] = &mxc,
 376};
 377
 378static const struct rpmhpd_desc sdx75_desc = {
 379	.rpmhpds = sdx75_rpmhpds,
 380	.num_pds = ARRAY_SIZE(sdx75_rpmhpds),
 381};
 382
 383/* SM4450 RPMH powerdomains */
 384static struct rpmhpd *sm4450_rpmhpds[] = {
 385	[RPMHPD_CX] = &cx,
 386	[RPMHPD_CX_AO] = &cx_ao,
 387	[RPMHPD_EBI] = &ebi,
 388	[RPMHPD_LMX] = &lmx,
 389	[RPMHPD_MSS] = &mss,
 390	[RPMHPD_MX] = &mx,
 391};
 392
 393static const struct rpmhpd_desc sm4450_desc = {
 394	.rpmhpds = sm4450_rpmhpds,
 395	.num_pds = ARRAY_SIZE(sm4450_rpmhpds),
 396};
 397
 398/* SM6350 RPMH powerdomains */
 399static struct rpmhpd *sm6350_rpmhpds[] = {
 400	[SM6350_CX] = &cx_w_mx_parent,
 401	[SM6350_GFX] = &gfx,
 402	[SM6350_LCX] = &lcx,
 403	[SM6350_LMX] = &lmx,
 404	[SM6350_MSS] = &mss,
 405	[SM6350_MX] = &mx,
 406};
 407
 408static const struct rpmhpd_desc sm6350_desc = {
 409	.rpmhpds = sm6350_rpmhpds,
 410	.num_pds = ARRAY_SIZE(sm6350_rpmhpds),
 411};
 412
 413/* SM7150 RPMH powerdomains */
 414static struct rpmhpd *sm7150_rpmhpds[] = {
 415	[RPMHPD_CX] = &cx_w_mx_parent,
 416	[RPMHPD_CX_AO] = &cx_ao_w_mx_parent,
 417	[RPMHPD_GFX] = &gfx,
 418	[RPMHPD_LCX] = &lcx,
 419	[RPMHPD_LMX] = &lmx,
 420	[RPMHPD_MX] = &mx,
 421	[RPMHPD_MX_AO] = &mx_ao,
 422	[RPMHPD_MSS] = &mss,
 423};
 424
 425static const struct rpmhpd_desc sm7150_desc = {
 426	.rpmhpds = sm7150_rpmhpds,
 427	.num_pds = ARRAY_SIZE(sm7150_rpmhpds),
 428};
 429
 430/* SM8150 RPMH powerdomains */
 431static struct rpmhpd *sm8150_rpmhpds[] = {
 432	[SM8150_CX] = &cx_w_mx_parent,
 433	[SM8150_CX_AO] = &cx_ao_w_mx_parent,
 434	[SM8150_EBI] = &ebi,
 435	[SM8150_GFX] = &gfx,
 436	[SM8150_LCX] = &lcx,
 437	[SM8150_LMX] = &lmx,
 438	[SM8150_MMCX] = &mmcx,
 439	[SM8150_MMCX_AO] = &mmcx_ao,
 440	[SM8150_MSS] = &mss,
 441	[SM8150_MX] = &mx,
 442	[SM8150_MX_AO] = &mx_ao,
 443};
 444
 445static const struct rpmhpd_desc sm8150_desc = {
 446	.rpmhpds = sm8150_rpmhpds,
 447	.num_pds = ARRAY_SIZE(sm8150_rpmhpds),
 448};
 449
 450static struct rpmhpd *sa8155p_rpmhpds[] = {
 451	[SA8155P_CX] = &cx_w_mx_parent,
 452	[SA8155P_CX_AO] = &cx_ao_w_mx_parent,
 453	[SA8155P_EBI] = &ebi,
 454	[SA8155P_GFX] = &gfx,
 455	[SA8155P_MSS] = &mss,
 456	[SA8155P_MX] = &mx,
 457	[SA8155P_MX_AO] = &mx_ao,
 458};
 459
 460static const struct rpmhpd_desc sa8155p_desc = {
 461	.rpmhpds = sa8155p_rpmhpds,
 462	.num_pds = ARRAY_SIZE(sa8155p_rpmhpds),
 463};
 464
 465/* SM8250 RPMH powerdomains */
 466static struct rpmhpd *sm8250_rpmhpds[] = {
 467	[RPMHPD_CX] = &cx_w_mx_parent,
 468	[RPMHPD_CX_AO] = &cx_ao_w_mx_parent,
 469	[RPMHPD_EBI] = &ebi,
 470	[RPMHPD_GFX] = &gfx,
 471	[RPMHPD_LCX] = &lcx,
 472	[RPMHPD_LMX] = &lmx,
 473	[RPMHPD_MMCX] = &mmcx,
 474	[RPMHPD_MMCX_AO] = &mmcx_ao,
 475	[RPMHPD_MX] = &mx,
 476	[RPMHPD_MX_AO] = &mx_ao,
 477};
 478
 479static const struct rpmhpd_desc sm8250_desc = {
 480	.rpmhpds = sm8250_rpmhpds,
 481	.num_pds = ARRAY_SIZE(sm8250_rpmhpds),
 482};
 483
 484/* SM8350 Power domains */
 485static struct rpmhpd *sm8350_rpmhpds[] = {
 486	[RPMHPD_CX] = &cx_w_mx_parent,
 487	[RPMHPD_CX_AO] = &cx_ao_w_mx_parent,
 488	[RPMHPD_EBI] = &ebi,
 489	[RPMHPD_GFX] = &gfx,
 490	[RPMHPD_LCX] = &lcx,
 491	[RPMHPD_LMX] = &lmx,
 492	[RPMHPD_MMCX] = &mmcx,
 493	[RPMHPD_MMCX_AO] = &mmcx_ao,
 494	[RPMHPD_MSS] = &mss,
 495	[RPMHPD_MX] = &mx,
 496	[RPMHPD_MX_AO] = &mx_ao,
 497	[RPMHPD_MXC] = &mxc,
 498	[RPMHPD_MXC_AO] = &mxc_ao,
 499};
 500
 501static const struct rpmhpd_desc sm8350_desc = {
 502	.rpmhpds = sm8350_rpmhpds,
 503	.num_pds = ARRAY_SIZE(sm8350_rpmhpds),
 504};
 505
 506/* SM8450 RPMH powerdomains */
 507static struct rpmhpd *sm8450_rpmhpds[] = {
 508	[RPMHPD_CX] = &cx,
 509	[RPMHPD_CX_AO] = &cx_ao,
 510	[RPMHPD_EBI] = &ebi,
 511	[RPMHPD_GFX] = &gfx,
 512	[RPMHPD_LCX] = &lcx,
 513	[RPMHPD_LMX] = &lmx,
 514	[RPMHPD_MMCX] = &mmcx_w_cx_parent,
 515	[RPMHPD_MMCX_AO] = &mmcx_ao_w_cx_parent,
 516	[RPMHPD_MSS] = &mss,
 517	[RPMHPD_MX] = &mx,
 518	[RPMHPD_MX_AO] = &mx_ao,
 519	[RPMHPD_MXC] = &mxc,
 520	[RPMHPD_MXC_AO] = &mxc_ao,
 521};
 522
 523static const struct rpmhpd_desc sm8450_desc = {
 524	.rpmhpds = sm8450_rpmhpds,
 525	.num_pds = ARRAY_SIZE(sm8450_rpmhpds),
 526};
 527
 528/* SM8550 RPMH powerdomains */
 529static struct rpmhpd *sm8550_rpmhpds[] = {
 530	[RPMHPD_CX] = &cx,
 531	[RPMHPD_CX_AO] = &cx_ao,
 532	[RPMHPD_EBI] = &ebi,
 533	[RPMHPD_GFX] = &gfx,
 534	[RPMHPD_LCX] = &lcx,
 535	[RPMHPD_LMX] = &lmx,
 536	[RPMHPD_MMCX] = &mmcx_w_cx_parent,
 537	[RPMHPD_MMCX_AO] = &mmcx_ao_w_cx_parent,
 538	[RPMHPD_MSS] = &mss,
 539	[RPMHPD_MX] = &mx,
 540	[RPMHPD_MX_AO] = &mx_ao,
 541	[RPMHPD_MXC] = &mxc,
 542	[RPMHPD_MXC_AO] = &mxc_ao,
 543	[RPMHPD_NSP] = &nsp,
 544};
 545
 546static const struct rpmhpd_desc sm8550_desc = {
 547	.rpmhpds = sm8550_rpmhpds,
 548	.num_pds = ARRAY_SIZE(sm8550_rpmhpds),
 549};
 550
 551/* SM8650 RPMH powerdomains */
 552static struct rpmhpd *sm8650_rpmhpds[] = {
 553	[RPMHPD_CX] = &cx,
 554	[RPMHPD_CX_AO] = &cx_ao,
 555	[RPMHPD_EBI] = &ebi,
 556	[RPMHPD_GFX] = &gfx,
 557	[RPMHPD_LCX] = &lcx,
 558	[RPMHPD_LMX] = &lmx,
 559	[RPMHPD_MMCX] = &mmcx_w_cx_parent,
 560	[RPMHPD_MMCX_AO] = &mmcx_ao_w_cx_parent,
 561	[RPMHPD_MSS] = &mss,
 562	[RPMHPD_MX] = &mx,
 563	[RPMHPD_MX_AO] = &mx_ao,
 564	[RPMHPD_MXC] = &mxc,
 565	[RPMHPD_MXC_AO] = &mxc_ao,
 566	[RPMHPD_NSP] = &nsp,
 567	[RPMHPD_NSP2] = &nsp2,
 568};
 569
 570static const struct rpmhpd_desc sm8650_desc = {
 571	.rpmhpds = sm8650_rpmhpds,
 572	.num_pds = ARRAY_SIZE(sm8650_rpmhpds),
 573};
 574
 575/* SM8750 RPMH powerdomains */
 576static struct rpmhpd *sm8750_rpmhpds[] = {
 577	[RPMHPD_CX] = &cx,
 578	[RPMHPD_CX_AO] = &cx_ao,
 579	[RPMHPD_EBI] = &ebi,
 580	[RPMHPD_GFX] = &gfx,
 581	[RPMHPD_GMXC] = &gmxc,
 582	[RPMHPD_LCX] = &lcx,
 583	[RPMHPD_LMX] = &lmx,
 584	[RPMHPD_MX] = &mx,
 585	[RPMHPD_MX_AO] = &mx_ao,
 586	[RPMHPD_MMCX] = &mmcx_w_cx_parent,
 587	[RPMHPD_MMCX_AO] = &mmcx_ao_w_cx_parent,
 588	[RPMHPD_MSS] = &mss,
 589	[RPMHPD_MXC] = &mxc,
 590	[RPMHPD_MXC_AO] = &mxc_ao,
 591	[RPMHPD_NSP] = &nsp,
 592	[RPMHPD_NSP2] = &nsp2,
 593};
 594
 595static const struct rpmhpd_desc sm8750_desc = {
 596	.rpmhpds = sm8750_rpmhpds,
 597	.num_pds = ARRAY_SIZE(sm8750_rpmhpds),
 598};
 599
 600/* KAANAPALI RPMH powerdomains */
 601static struct rpmhpd *kaanapali_rpmhpds[] = {
 602	[RPMHPD_CX] = &cx,
 603	[RPMHPD_CX_AO] = &cx_ao,
 604	[RPMHPD_EBI] = &ebi,
 605	[RPMHPD_GFX] = &gfx,
 606	[RPMHPD_GMXC] = &gmxc,
 607	[RPMHPD_LCX] = &lcx,
 608	[RPMHPD_LMX] = &lmx,
 609	[RPMHPD_MX] = &mx,
 610	[RPMHPD_MX_AO] = &mx_ao,
 611	[RPMHPD_MMCX] = &mmcx,
 612	[RPMHPD_MMCX_AO] = &mmcx_ao,
 613	[RPMHPD_MSS] = &mss,
 614	[RPMHPD_MXC] = &mxc,
 615	[RPMHPD_MXC_AO] = &mxc_ao,
 616	[RPMHPD_NSP] = &nsp,
 617	[RPMHPD_NSP2] = &nsp2,
 618};
 619
 620static const struct rpmhpd_desc kaanapali_desc = {
 621	.rpmhpds = kaanapali_rpmhpds,
 622	.num_pds = ARRAY_SIZE(kaanapali_rpmhpds),
 623};
 624
 625/* QDU1000/QRU1000 RPMH powerdomains */
 626static struct rpmhpd *qdu1000_rpmhpds[] = {
 627	[QDU1000_CX] = &cx,
 628	[QDU1000_EBI] = &ebi,
 629	[QDU1000_MSS] = &mss,
 630	[QDU1000_MX] = &mx,
 631};
 632
 633static const struct rpmhpd_desc qdu1000_desc = {
 634	.rpmhpds = qdu1000_rpmhpds,
 635	.num_pds = ARRAY_SIZE(qdu1000_rpmhpds),
 636};
 637
 638/* SC7180 RPMH powerdomains */
 639static struct rpmhpd *sc7180_rpmhpds[] = {
 640	[SC7180_CX] = &cx_w_mx_parent,
 641	[SC7180_CX_AO] = &cx_ao_w_mx_parent,
 642	[SC7180_GFX] = &gfx,
 643	[SC7180_LCX] = &lcx,
 644	[SC7180_LMX] = &lmx,
 645	[SC7180_MSS] = &mss,
 646	[SC7180_MX] = &mx,
 647	[SC7180_MX_AO] = &mx_ao,
 648};
 649
 650static const struct rpmhpd_desc sc7180_desc = {
 651	.rpmhpds = sc7180_rpmhpds,
 652	.num_pds = ARRAY_SIZE(sc7180_rpmhpds),
 653};
 654
 655/* SC7280 RPMH powerdomains */
 656static struct rpmhpd *sc7280_rpmhpds[] = {
 657	[SC7280_CX] = &cx,
 658	[SC7280_CX_AO] = &cx_ao,
 659	[SC7280_EBI] = &ebi,
 660	[SC7280_GFX] = &gfx,
 661	[SC7280_LCX] = &lcx,
 662	[SC7280_LMX] = &lmx,
 663	[SC7280_MSS] = &mss,
 664	[SC7280_MX] = &mx,
 665	[SC7280_MX_AO] = &mx_ao,
 666};
 667
 668static const struct rpmhpd_desc sc7280_desc = {
 669	.rpmhpds = sc7280_rpmhpds,
 670	.num_pds = ARRAY_SIZE(sc7280_rpmhpds),
 671};
 672
 673/* SC8180x RPMH powerdomains */
 674static struct rpmhpd *sc8180x_rpmhpds[] = {
 675	[SC8180X_CX] = &cx_w_mx_parent,
 676	[SC8180X_CX_AO] = &cx_ao_w_mx_parent,
 677	[SC8180X_EBI] = &ebi,
 678	[SC8180X_GFX] = &gfx,
 679	[SC8180X_LCX] = &lcx,
 680	[SC8180X_LMX] = &lmx,
 681	[SC8180X_MMCX] = &mmcx,
 682	[SC8180X_MMCX_AO] = &mmcx_ao,
 683	[SC8180X_MSS] = &mss,
 684	[SC8180X_MX] = &mx,
 685	[SC8180X_MX_AO] = &mx_ao,
 686};
 687
 688static const struct rpmhpd_desc sc8180x_desc = {
 689	.rpmhpds = sc8180x_rpmhpds,
 690	.num_pds = ARRAY_SIZE(sc8180x_rpmhpds),
 691};
 692
 693/* SC8280xp RPMH powerdomains */
 694static struct rpmhpd *sc8280xp_rpmhpds[] = {
 695	[SC8280XP_CX] = &cx,
 696	[SC8280XP_CX_AO] = &cx_ao,
 697	[SC8280XP_EBI] = &ebi,
 698	[SC8280XP_GFX] = &gfx,
 699	[SC8280XP_LCX] = &lcx,
 700	[SC8280XP_LMX] = &lmx,
 701	[SC8280XP_MMCX] = &mmcx,
 702	[SC8280XP_MMCX_AO] = &mmcx_ao,
 703	[SC8280XP_MX] = &mx,
 704	[SC8280XP_MX_AO] = &mx_ao,
 705	[SC8280XP_MXC] = &mxc,
 706	[SC8280XP_MXC_AO] = &mxc_ao,
 707	[SC8280XP_NSP] = &nsp,
 708	[SC8280XP_QPHY] = &qphy,
 709};
 710
 711static const struct rpmhpd_desc sc8280xp_desc = {
 712	.rpmhpds = sc8280xp_rpmhpds,
 713	.num_pds = ARRAY_SIZE(sc8280xp_rpmhpds),
 714};
 715
 716/* Glymur RPMH powerdomains */
 717static struct rpmhpd *glymur_rpmhpds[] = {
 718	[RPMHPD_CX] = &cx,
 719	[RPMHPD_CX_AO] = &cx_ao,
 720	[RPMHPD_EBI] = &ebi,
 721	[RPMHPD_GFX] = &gfx,
 722	[RPMHPD_LCX] = &lcx,
 723	[RPMHPD_LMX] = &lmx,
 724	[RPMHPD_MMCX] = &mmcx,
 725	[RPMHPD_MMCX_AO] = &mmcx_ao,
 726	[RPMHPD_MX] = &mx,
 727	[RPMHPD_MX_AO] = &mx_ao,
 728	[RPMHPD_MXC] = &mxc,
 729	[RPMHPD_MXC_AO] = &mxc_ao,
 730	[RPMHPD_MSS] = &mss,
 731	[RPMHPD_NSP] = &nsp,
 732	[RPMHPD_NSP2] = &nsp2,
 733	[RPMHPD_GMXC] = &gmxc,
 734};
 735
 736static const struct rpmhpd_desc glymur_desc = {
 737	.rpmhpds = glymur_rpmhpds,
 738	.num_pds = ARRAY_SIZE(glymur_rpmhpds),
 739};
 740
 741/* X1E80100 RPMH powerdomains */
 742static struct rpmhpd *x1e80100_rpmhpds[] = {
 743	[RPMHPD_CX] = &cx,
 744	[RPMHPD_CX_AO] = &cx_ao,
 745	[RPMHPD_EBI] = &ebi,
 746	[RPMHPD_GFX] = &gfx,
 747	[RPMHPD_LCX] = &lcx,
 748	[RPMHPD_LMX] = &lmx,
 749	[RPMHPD_MMCX] = &mmcx,
 750	[RPMHPD_MMCX_AO] = &mmcx_ao,
 751	[RPMHPD_MX] = &mx,
 752	[RPMHPD_MX_AO] = &mx_ao,
 753	[RPMHPD_NSP] = &nsp,
 754	[RPMHPD_MXC] = &mxc,
 755	[RPMHPD_GMXC] = &gmxc,
 756};
 757
 758static const struct rpmhpd_desc x1e80100_desc = {
 759	.rpmhpds = x1e80100_rpmhpds,
 760	.num_pds = ARRAY_SIZE(x1e80100_rpmhpds),
 761};
 762
 763/* QCS8300 RPMH power domains */
 764static struct rpmhpd *qcs8300_rpmhpds[] = {
 765	[RPMHPD_CX] = &cx,
 766	[RPMHPD_CX_AO] = &cx_ao,
 767	[RPMHPD_EBI] = &ebi,
 768	[RPMHPD_GFX] = &gfx,
 769	[RPMHPD_LCX] = &lcx,
 770	[RPMHPD_LMX] = &lmx,
 771	[RPMHPD_MMCX] = &mmcx_w_cx_parent,
 772	[RPMHPD_MMCX_AO] = &mmcx_ao_w_cx_parent,
 773	[RPMHPD_MXC] = &mxc,
 774	[RPMHPD_MXC_AO] = &mxc_ao,
 775	[RPMHPD_MX] = &mx,
 776	[RPMHPD_MX_AO] = &mx_ao,
 777	[RPMHPD_NSP0] = &nsp0,
 778	[RPMHPD_NSP1] = &nsp1,
 779};
 780
 781static const struct rpmhpd_desc qcs8300_desc = {
 782	.rpmhpds = qcs8300_rpmhpds,
 783	.num_pds = ARRAY_SIZE(qcs8300_rpmhpds),
 784};
 785
 786/* QCS615 RPMH powerdomains */
 787static struct rpmhpd *qcs615_rpmhpds[] = {
 788	[RPMHPD_CX] = &cx,
 789	[RPMHPD_CX_AO] = &cx_ao,
 790};
 791
 792static const struct rpmhpd_desc qcs615_desc = {
 793	.rpmhpds = qcs615_rpmhpds,
 794	.num_pds = ARRAY_SIZE(qcs615_rpmhpds),
 795};
 796
 797static const struct of_device_id rpmhpd_match_table[] = {
 798	{ .compatible = "qcom,glymur-rpmhpd", .data = &glymur_desc },
 799	{ .compatible = "qcom,kaanapali-rpmhpd", .data = &kaanapali_desc },
 800	{ .compatible = "qcom,milos-rpmhpd", .data = &milos_desc },
 801	{ .compatible = "qcom,qcs615-rpmhpd", .data = &qcs615_desc },
 802	{ .compatible = "qcom,qcs8300-rpmhpd", .data = &qcs8300_desc },
 803	{ .compatible = "qcom,qdu1000-rpmhpd", .data = &qdu1000_desc },
 804	{ .compatible = "qcom,sa8155p-rpmhpd", .data = &sa8155p_desc },
 805	{ .compatible = "qcom,sa8540p-rpmhpd", .data = &sa8540p_desc },
 806	{ .compatible = "qcom,sa8775p-rpmhpd", .data = &sa8775p_desc },
 807	{ .compatible = "qcom,sar2130p-rpmhpd", .data = &sar2130p_desc},
 808	{ .compatible = "qcom,sc7180-rpmhpd", .data = &sc7180_desc },
 809	{ .compatible = "qcom,sc7280-rpmhpd", .data = &sc7280_desc },
 810	{ .compatible = "qcom,sc8180x-rpmhpd", .data = &sc8180x_desc },
 811	{ .compatible = "qcom,sc8280xp-rpmhpd", .data = &sc8280xp_desc },
 812	{ .compatible = "qcom,sdm670-rpmhpd", .data = &sdm670_desc },
 813	{ .compatible = "qcom,sdm845-rpmhpd", .data = &sdm845_desc },
 814	{ .compatible = "qcom,sdx55-rpmhpd", .data = &sdx55_desc},
 815	{ .compatible = "qcom,sdx65-rpmhpd", .data = &sdx65_desc},
 816	{ .compatible = "qcom,sdx75-rpmhpd", .data = &sdx75_desc},
 817	{ .compatible = "qcom,sm4450-rpmhpd", .data = &sm4450_desc },
 818	{ .compatible = "qcom,sm6350-rpmhpd", .data = &sm6350_desc },
 819	{ .compatible = "qcom,sm7150-rpmhpd", .data = &sm7150_desc },
 820	{ .compatible = "qcom,sm8150-rpmhpd", .data = &sm8150_desc },
 821	{ .compatible = "qcom,sm8250-rpmhpd", .data = &sm8250_desc },
 822	{ .compatible = "qcom,sm8350-rpmhpd", .data = &sm8350_desc },
 823	{ .compatible = "qcom,sm8450-rpmhpd", .data = &sm8450_desc },
 824	{ .compatible = "qcom,sm8550-rpmhpd", .data = &sm8550_desc },
 825	{ .compatible = "qcom,sm8650-rpmhpd", .data = &sm8650_desc },
 826	{ .compatible = "qcom,sm8750-rpmhpd", .data = &sm8750_desc },
 827	{ .compatible = "qcom,x1e80100-rpmhpd", .data = &x1e80100_desc },
 828	{ }
 829};
 830MODULE_DEVICE_TABLE(of, rpmhpd_match_table);
 831
 832static int rpmhpd_send_corner(struct rpmhpd *pd, int state,
 833			      unsigned int corner, bool sync)
 834{
 835	struct tcs_cmd cmd = {
 836		.addr = pd->addr,
 837		.data = corner,
 838	};
 839
 840	/*
 841	 * Wait for an ack only when we are increasing the
 842	 * perf state of the power domain
 843	 */
 844	if (sync)
 845		return rpmh_write(pd->dev, state, &cmd, 1);
 846	else
 847		return rpmh_write_async(pd->dev, state, &cmd, 1);
 848}
 849
 850static void to_active_sleep(struct rpmhpd *pd, unsigned int corner,
 851			    unsigned int *active, unsigned int *sleep)
 852{
 853	*active = corner;
 854
 855	if (pd->active_only)
 856		*sleep = 0;
 857	else
 858		*sleep = *active;
 859}
 860
 861/*
 862 * This function is used to aggregate the votes across the active only
 863 * resources and its peers. The aggregated votes are sent to RPMh as
 864 * ACTIVE_ONLY votes (which take effect immediately), as WAKE_ONLY votes
 865 * (applied by RPMh on system wakeup) and as SLEEP votes (applied by RPMh
 866 * on system sleep).
 867 * We send ACTIVE_ONLY votes for resources without any peers. For others,
 868 * which have an active only peer, all 3 votes are sent.
 869 */
 870static int rpmhpd_aggregate_corner(struct rpmhpd *pd, unsigned int corner)
 871{
 872	int ret;
 873	struct rpmhpd *peer = pd->peer;
 874	unsigned int active_corner, sleep_corner;
 875	unsigned int this_active_corner = 0, this_sleep_corner = 0;
 876	unsigned int peer_active_corner = 0, peer_sleep_corner = 0;
 877	unsigned int peer_enabled_corner;
 878
 879	if (pd->state_synced) {
 880		to_active_sleep(pd, corner, &this_active_corner, &this_sleep_corner);
 881	} else {
 882		/* Clamp to highest corner if sync_state hasn't happened */
 883		this_active_corner = pd->level_count - 1;
 884		this_sleep_corner = pd->level_count - 1;
 885	}
 886
 887	if (peer && peer->enabled) {
 888		peer_enabled_corner = max(peer->corner, peer->enable_corner);
 889		to_active_sleep(peer, peer_enabled_corner, &peer_active_corner,
 890				&peer_sleep_corner);
 891	}
 892
 893	active_corner = max(this_active_corner, peer_active_corner);
 894
 895	ret = rpmhpd_send_corner(pd, RPMH_ACTIVE_ONLY_STATE, active_corner,
 896				 active_corner > pd->active_corner);
 897	if (ret)
 898		return ret;
 899
 900	pd->active_corner = active_corner;
 901
 902	if (peer) {
 903		peer->active_corner = active_corner;
 904
 905		ret = rpmhpd_send_corner(pd, RPMH_WAKE_ONLY_STATE,
 906					 active_corner, false);
 907		if (ret)
 908			return ret;
 909
 910		sleep_corner = max(this_sleep_corner, peer_sleep_corner);
 911
 912		return rpmhpd_send_corner(pd, RPMH_SLEEP_STATE, sleep_corner,
 913					  false);
 914	}
 915
 916	return ret;
 917}
 918
 919static int rpmhpd_power_on(struct generic_pm_domain *domain)
 920{
 921	struct rpmhpd *pd = domain_to_rpmhpd(domain);
 922	unsigned int corner;
 923	int ret;
 924
 925	mutex_lock(&rpmhpd_lock);
 926
 927	corner = max(pd->corner, pd->enable_corner);
 928	ret = rpmhpd_aggregate_corner(pd, corner);
 929	if (!ret)
 930		pd->enabled = true;
 931
 932	mutex_unlock(&rpmhpd_lock);
 933
 934	return ret;
 935}
 936
 937static int rpmhpd_power_off(struct generic_pm_domain *domain)
 938{
 939	struct rpmhpd *pd = domain_to_rpmhpd(domain);
 940	int ret;
 941
 942	mutex_lock(&rpmhpd_lock);
 943
 944	ret = rpmhpd_aggregate_corner(pd, 0);
 945	if (!ret)
 946		pd->enabled = false;
 947
 948	mutex_unlock(&rpmhpd_lock);
 949
 950	return ret;
 951}
 952
 953static int rpmhpd_set_performance_state(struct generic_pm_domain *domain,
 954					unsigned int level)
 955{
 956	struct rpmhpd *pd = domain_to_rpmhpd(domain);
 957	int ret, i;
 958
 959	guard(mutex)(&rpmhpd_lock);
 960
 961	for (i = 0; i < pd->level_count; i++)
 962		if (level <= pd->level[i])
 963			break;
 964
 965	/*
 966	 * If the level requested is more than that supported by the
 967	 * max corner, just set it to max anyway.
 968	 */
 969	if (i == pd->level_count)
 970		i--;
 971
 972	if (pd->enabled) {
 973		/* Ensure that the domain isn't turn off */
 974		if (i < pd->enable_corner)
 975			i = pd->enable_corner;
 976
 977		ret = rpmhpd_aggregate_corner(pd, i);
 978		if (ret)
 979			return ret;
 980	}
 981
 982	pd->corner = i;
 983
 984	return 0;
 985}
 986
 987static int rpmhpd_update_level_mapping(struct rpmhpd *rpmhpd)
 988{
 989	int i;
 990	const u16 *buf;
 991
 992	buf = cmd_db_read_aux_data(rpmhpd->res_name, &rpmhpd->level_count);
 993	if (IS_ERR(buf))
 994		return PTR_ERR(buf);
 995
 996	/* 2 bytes used for each command DB aux data entry */
 997	rpmhpd->level_count >>= 1;
 998
 999	if (rpmhpd->level_count > RPMH_ARC_MAX_LEVELS)
1000		return -EINVAL;
1001
1002	for (i = 0; i < rpmhpd->level_count; i++) {
1003		if (rpmhpd->skip_retention_level && buf[i] == RPMH_REGULATOR_LEVEL_RETENTION)
1004			continue;
1005
1006		rpmhpd->level[i] = buf[i];
1007
1008		/* Remember the first corner with non-zero level */
1009		if (!rpmhpd->level[rpmhpd->enable_corner] && rpmhpd->level[i])
1010			rpmhpd->enable_corner = i;
1011
1012		/*
1013		 * The AUX data may be zero padded.  These 0 valued entries at
1014		 * the end of the map must be ignored.
1015		 */
1016		if (i > 0 && rpmhpd->level[i] == 0) {
1017			rpmhpd->level_count = i;
1018			break;
1019		}
1020		pr_debug("%s: ARC hlvl=%2d --> vlvl=%4u\n", rpmhpd->res_name, i,
1021			 rpmhpd->level[i]);
1022	}
1023
1024	return 0;
1025}
1026
1027static int rpmhpd_probe(struct platform_device *pdev)
1028{
1029	int i, ret;
1030	size_t num_pds;
1031	struct device *dev = &pdev->dev;
1032	struct genpd_onecell_data *data;
1033	struct rpmhpd **rpmhpds;
1034	const struct rpmhpd_desc *desc;
1035
1036	desc = of_device_get_match_data(dev);
1037	if (!desc)
1038		return -EINVAL;
1039
1040	rpmhpds = desc->rpmhpds;
1041	num_pds = desc->num_pds;
1042
1043	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
1044	if (!data)
1045		return -ENOMEM;
1046
1047	data->domains = devm_kcalloc(dev, num_pds, sizeof(*data->domains),
1048				     GFP_KERNEL);
1049	if (!data->domains)
1050		return -ENOMEM;
1051
1052	data->num_domains = num_pds;
1053
1054	for (i = 0; i < num_pds; i++) {
1055		if (!rpmhpds[i])
1056			continue;
1057
1058		rpmhpds[i]->dev = dev;
1059		rpmhpds[i]->addr = cmd_db_read_addr(rpmhpds[i]->res_name);
1060		if (!rpmhpds[i]->addr) {
1061			dev_err(dev, "Could not find RPMh address for resource %s\n",
1062				rpmhpds[i]->res_name);
1063			return -ENODEV;
1064		}
1065
1066		ret = cmd_db_read_slave_id(rpmhpds[i]->res_name);
1067		if (ret != CMD_DB_HW_ARC) {
1068			dev_err(dev, "RPMh slave ID mismatch\n");
1069			return -EINVAL;
1070		}
1071
1072		ret = rpmhpd_update_level_mapping(rpmhpds[i]);
1073		if (ret)
1074			return ret;
1075
1076		rpmhpds[i]->pd.power_off = rpmhpd_power_off;
1077		rpmhpds[i]->pd.power_on = rpmhpd_power_on;
1078		rpmhpds[i]->pd.set_performance_state = rpmhpd_set_performance_state;
1079		pm_genpd_init(&rpmhpds[i]->pd, NULL, true);
1080
1081		data->domains[i] = &rpmhpds[i]->pd;
1082	}
1083
1084	/* Add subdomains */
1085	for (i = 0; i < num_pds; i++) {
1086		if (!rpmhpds[i])
1087			continue;
1088		if (rpmhpds[i]->parent)
1089			pm_genpd_add_subdomain(rpmhpds[i]->parent,
1090					       &rpmhpds[i]->pd);
1091	}
1092
1093	return of_genpd_add_provider_onecell(pdev->dev.of_node, data);
1094}
1095
1096static void rpmhpd_sync_state(struct device *dev)
1097{
1098	const struct rpmhpd_desc *desc = of_device_get_match_data(dev);
1099	struct rpmhpd **rpmhpds = desc->rpmhpds;
1100	unsigned int corner;
1101	struct rpmhpd *pd;
1102	unsigned int i;
1103	int ret;
1104
1105	of_genpd_sync_state(dev->of_node);
1106
1107	mutex_lock(&rpmhpd_lock);
1108	for (i = 0; i < desc->num_pds; i++) {
1109		pd = rpmhpds[i];
1110		if (!pd)
1111			continue;
1112
1113		pd->state_synced = true;
1114		if (pd->enabled)
1115			corner = max(pd->corner, pd->enable_corner);
1116		else
1117			corner = 0;
1118
1119		ret = rpmhpd_aggregate_corner(pd, corner);
1120		if (ret)
1121			dev_err(dev, "failed to sync %s\n", pd->res_name);
1122	}
1123	mutex_unlock(&rpmhpd_lock);
1124}
1125
1126static struct platform_driver rpmhpd_driver = {
1127	.driver = {
1128		.name = "qcom-rpmhpd",
1129		.of_match_table = rpmhpd_match_table,
1130		.suppress_bind_attrs = true,
1131		.sync_state = rpmhpd_sync_state,
1132	},
1133	.probe = rpmhpd_probe,
1134};
1135
1136static int __init rpmhpd_init(void)
1137{
1138	return platform_driver_register(&rpmhpd_driver);
1139}
1140core_initcall(rpmhpd_init);
1141
1142MODULE_DESCRIPTION("Qualcomm Technologies, Inc. RPMh Power Domain Driver");
1143MODULE_LICENSE("GPL v2");
~/ Read-only file preview.