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");