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