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