Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.15-rc5 578 lines 14 kB view raw
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 * @level: An array of level (vlvl) to corner (hlvl) mappings 34 * derived from cmd-db 35 * @level_count: Number of levels supported by the power domain. max 36 * being 16 (0 - 15) 37 * @enabled: true if the power domain is enabled 38 * @res_name: Resource name used for cmd-db lookup 39 * @addr: Resource address as looped up using resource name from 40 * cmd-db 41 */ 42struct rpmhpd { 43 struct device *dev; 44 struct generic_pm_domain pd; 45 struct generic_pm_domain *parent; 46 struct rpmhpd *peer; 47 const bool active_only; 48 unsigned int corner; 49 unsigned int active_corner; 50 u32 level[RPMH_ARC_MAX_LEVELS]; 51 size_t level_count; 52 bool enabled; 53 const char *res_name; 54 u32 addr; 55}; 56 57struct rpmhpd_desc { 58 struct rpmhpd **rpmhpds; 59 size_t num_pds; 60}; 61 62static DEFINE_MUTEX(rpmhpd_lock); 63 64/* SDM845 RPMH powerdomains */ 65 66static struct rpmhpd sdm845_ebi = { 67 .pd = { .name = "ebi", }, 68 .res_name = "ebi.lvl", 69}; 70 71static struct rpmhpd sdm845_lmx = { 72 .pd = { .name = "lmx", }, 73 .res_name = "lmx.lvl", 74}; 75 76static struct rpmhpd sdm845_lcx = { 77 .pd = { .name = "lcx", }, 78 .res_name = "lcx.lvl", 79}; 80 81static struct rpmhpd sdm845_gfx = { 82 .pd = { .name = "gfx", }, 83 .res_name = "gfx.lvl", 84}; 85 86static struct rpmhpd sdm845_mss = { 87 .pd = { .name = "mss", }, 88 .res_name = "mss.lvl", 89}; 90 91static struct rpmhpd sdm845_mx_ao; 92static struct rpmhpd sdm845_mx = { 93 .pd = { .name = "mx", }, 94 .peer = &sdm845_mx_ao, 95 .res_name = "mx.lvl", 96}; 97 98static struct rpmhpd sdm845_mx_ao = { 99 .pd = { .name = "mx_ao", }, 100 .active_only = true, 101 .peer = &sdm845_mx, 102 .res_name = "mx.lvl", 103}; 104 105static struct rpmhpd sdm845_cx_ao; 106static struct rpmhpd sdm845_cx = { 107 .pd = { .name = "cx", }, 108 .peer = &sdm845_cx_ao, 109 .parent = &sdm845_mx.pd, 110 .res_name = "cx.lvl", 111}; 112 113static struct rpmhpd sdm845_cx_ao = { 114 .pd = { .name = "cx_ao", }, 115 .active_only = true, 116 .peer = &sdm845_cx, 117 .parent = &sdm845_mx_ao.pd, 118 .res_name = "cx.lvl", 119}; 120 121static struct rpmhpd *sdm845_rpmhpds[] = { 122 [SDM845_EBI] = &sdm845_ebi, 123 [SDM845_MX] = &sdm845_mx, 124 [SDM845_MX_AO] = &sdm845_mx_ao, 125 [SDM845_CX] = &sdm845_cx, 126 [SDM845_CX_AO] = &sdm845_cx_ao, 127 [SDM845_LMX] = &sdm845_lmx, 128 [SDM845_LCX] = &sdm845_lcx, 129 [SDM845_GFX] = &sdm845_gfx, 130 [SDM845_MSS] = &sdm845_mss, 131}; 132 133static const struct rpmhpd_desc sdm845_desc = { 134 .rpmhpds = sdm845_rpmhpds, 135 .num_pds = ARRAY_SIZE(sdm845_rpmhpds), 136}; 137 138/* SDX55 RPMH powerdomains */ 139static struct rpmhpd *sdx55_rpmhpds[] = { 140 [SDX55_MSS] = &sdm845_mss, 141 [SDX55_MX] = &sdm845_mx, 142 [SDX55_CX] = &sdm845_cx, 143}; 144 145static const struct rpmhpd_desc sdx55_desc = { 146 .rpmhpds = sdx55_rpmhpds, 147 .num_pds = ARRAY_SIZE(sdx55_rpmhpds), 148}; 149 150/* SM8150 RPMH powerdomains */ 151 152static struct rpmhpd sm8150_mmcx_ao; 153static struct rpmhpd sm8150_mmcx = { 154 .pd = { .name = "mmcx", }, 155 .peer = &sm8150_mmcx_ao, 156 .res_name = "mmcx.lvl", 157}; 158 159static struct rpmhpd sm8150_mmcx_ao = { 160 .pd = { .name = "mmcx_ao", }, 161 .active_only = true, 162 .peer = &sm8150_mmcx, 163 .res_name = "mmcx.lvl", 164}; 165 166static struct rpmhpd *sm8150_rpmhpds[] = { 167 [SM8150_MSS] = &sdm845_mss, 168 [SM8150_EBI] = &sdm845_ebi, 169 [SM8150_LMX] = &sdm845_lmx, 170 [SM8150_LCX] = &sdm845_lcx, 171 [SM8150_GFX] = &sdm845_gfx, 172 [SM8150_MX] = &sdm845_mx, 173 [SM8150_MX_AO] = &sdm845_mx_ao, 174 [SM8150_CX] = &sdm845_cx, 175 [SM8150_CX_AO] = &sdm845_cx_ao, 176 [SM8150_MMCX] = &sm8150_mmcx, 177 [SM8150_MMCX_AO] = &sm8150_mmcx_ao, 178}; 179 180static const struct rpmhpd_desc sm8150_desc = { 181 .rpmhpds = sm8150_rpmhpds, 182 .num_pds = ARRAY_SIZE(sm8150_rpmhpds), 183}; 184 185static struct rpmhpd *sm8250_rpmhpds[] = { 186 [SM8250_CX] = &sdm845_cx, 187 [SM8250_CX_AO] = &sdm845_cx_ao, 188 [SM8250_EBI] = &sdm845_ebi, 189 [SM8250_GFX] = &sdm845_gfx, 190 [SM8250_LCX] = &sdm845_lcx, 191 [SM8250_LMX] = &sdm845_lmx, 192 [SM8250_MMCX] = &sm8150_mmcx, 193 [SM8250_MMCX_AO] = &sm8150_mmcx_ao, 194 [SM8250_MX] = &sdm845_mx, 195 [SM8250_MX_AO] = &sdm845_mx_ao, 196}; 197 198static const struct rpmhpd_desc sm8250_desc = { 199 .rpmhpds = sm8250_rpmhpds, 200 .num_pds = ARRAY_SIZE(sm8250_rpmhpds), 201}; 202 203/* SM8350 Power domains */ 204static struct rpmhpd sm8350_mxc_ao; 205static struct rpmhpd sm8350_mxc = { 206 .pd = { .name = "mxc", }, 207 .peer = &sm8150_mmcx_ao, 208 .res_name = "mxc.lvl", 209}; 210 211static struct rpmhpd sm8350_mxc_ao = { 212 .pd = { .name = "mxc_ao", }, 213 .active_only = true, 214 .peer = &sm8350_mxc, 215 .res_name = "mxc.lvl", 216}; 217 218static struct rpmhpd *sm8350_rpmhpds[] = { 219 [SM8350_CX] = &sdm845_cx, 220 [SM8350_CX_AO] = &sdm845_cx_ao, 221 [SM8350_EBI] = &sdm845_ebi, 222 [SM8350_GFX] = &sdm845_gfx, 223 [SM8350_LCX] = &sdm845_lcx, 224 [SM8350_LMX] = &sdm845_lmx, 225 [SM8350_MMCX] = &sm8150_mmcx, 226 [SM8350_MMCX_AO] = &sm8150_mmcx_ao, 227 [SM8350_MX] = &sdm845_mx, 228 [SM8350_MX_AO] = &sdm845_mx_ao, 229 [SM8350_MXC] = &sm8350_mxc, 230 [SM8350_MXC_AO] = &sm8350_mxc_ao, 231 [SM8350_MSS] = &sdm845_mss, 232}; 233 234static const struct rpmhpd_desc sm8350_desc = { 235 .rpmhpds = sm8350_rpmhpds, 236 .num_pds = ARRAY_SIZE(sm8350_rpmhpds), 237}; 238 239/* SC7180 RPMH powerdomains */ 240static struct rpmhpd *sc7180_rpmhpds[] = { 241 [SC7180_CX] = &sdm845_cx, 242 [SC7180_CX_AO] = &sdm845_cx_ao, 243 [SC7180_GFX] = &sdm845_gfx, 244 [SC7180_MX] = &sdm845_mx, 245 [SC7180_MX_AO] = &sdm845_mx_ao, 246 [SC7180_LMX] = &sdm845_lmx, 247 [SC7180_LCX] = &sdm845_lcx, 248 [SC7180_MSS] = &sdm845_mss, 249}; 250 251static const struct rpmhpd_desc sc7180_desc = { 252 .rpmhpds = sc7180_rpmhpds, 253 .num_pds = ARRAY_SIZE(sc7180_rpmhpds), 254}; 255 256/* SC7280 RPMH powerdomains */ 257static struct rpmhpd *sc7280_rpmhpds[] = { 258 [SC7280_CX] = &sdm845_cx, 259 [SC7280_CX_AO] = &sdm845_cx_ao, 260 [SC7280_EBI] = &sdm845_ebi, 261 [SC7280_GFX] = &sdm845_gfx, 262 [SC7280_MX] = &sdm845_mx, 263 [SC7280_MX_AO] = &sdm845_mx_ao, 264 [SC7280_LMX] = &sdm845_lmx, 265 [SC7280_LCX] = &sdm845_lcx, 266 [SC7280_MSS] = &sdm845_mss, 267}; 268 269static const struct rpmhpd_desc sc7280_desc = { 270 .rpmhpds = sc7280_rpmhpds, 271 .num_pds = ARRAY_SIZE(sc7280_rpmhpds), 272}; 273 274/* SC8180x RPMH powerdomains */ 275static struct rpmhpd *sc8180x_rpmhpds[] = { 276 [SC8180X_CX] = &sdm845_cx, 277 [SC8180X_CX_AO] = &sdm845_cx_ao, 278 [SC8180X_EBI] = &sdm845_ebi, 279 [SC8180X_GFX] = &sdm845_gfx, 280 [SC8180X_LCX] = &sdm845_lcx, 281 [SC8180X_LMX] = &sdm845_lmx, 282 [SC8180X_MMCX] = &sm8150_mmcx, 283 [SC8180X_MMCX_AO] = &sm8150_mmcx_ao, 284 [SC8180X_MSS] = &sdm845_mss, 285 [SC8180X_MX] = &sdm845_mx, 286 [SC8180X_MX_AO] = &sdm845_mx_ao, 287}; 288 289static const struct rpmhpd_desc sc8180x_desc = { 290 .rpmhpds = sc8180x_rpmhpds, 291 .num_pds = ARRAY_SIZE(sc8180x_rpmhpds), 292}; 293 294static const struct of_device_id rpmhpd_match_table[] = { 295 { .compatible = "qcom,sc7180-rpmhpd", .data = &sc7180_desc }, 296 { .compatible = "qcom,sc7280-rpmhpd", .data = &sc7280_desc }, 297 { .compatible = "qcom,sc8180x-rpmhpd", .data = &sc8180x_desc }, 298 { .compatible = "qcom,sdm845-rpmhpd", .data = &sdm845_desc }, 299 { .compatible = "qcom,sdx55-rpmhpd", .data = &sdx55_desc}, 300 { .compatible = "qcom,sm8150-rpmhpd", .data = &sm8150_desc }, 301 { .compatible = "qcom,sm8250-rpmhpd", .data = &sm8250_desc }, 302 { .compatible = "qcom,sm8350-rpmhpd", .data = &sm8350_desc }, 303 { } 304}; 305MODULE_DEVICE_TABLE(of, rpmhpd_match_table); 306 307static int rpmhpd_send_corner(struct rpmhpd *pd, int state, 308 unsigned int corner, bool sync) 309{ 310 struct tcs_cmd cmd = { 311 .addr = pd->addr, 312 .data = corner, 313 }; 314 315 /* 316 * Wait for an ack only when we are increasing the 317 * perf state of the power domain 318 */ 319 if (sync) 320 return rpmh_write(pd->dev, state, &cmd, 1); 321 else 322 return rpmh_write_async(pd->dev, state, &cmd, 1); 323} 324 325static void to_active_sleep(struct rpmhpd *pd, unsigned int corner, 326 unsigned int *active, unsigned int *sleep) 327{ 328 *active = corner; 329 330 if (pd->active_only) 331 *sleep = 0; 332 else 333 *sleep = *active; 334} 335 336/* 337 * This function is used to aggregate the votes across the active only 338 * resources and its peers. The aggregated votes are sent to RPMh as 339 * ACTIVE_ONLY votes (which take effect immediately), as WAKE_ONLY votes 340 * (applied by RPMh on system wakeup) and as SLEEP votes (applied by RPMh 341 * on system sleep). 342 * We send ACTIVE_ONLY votes for resources without any peers. For others, 343 * which have an active only peer, all 3 votes are sent. 344 */ 345static int rpmhpd_aggregate_corner(struct rpmhpd *pd, unsigned int corner) 346{ 347 int ret; 348 struct rpmhpd *peer = pd->peer; 349 unsigned int active_corner, sleep_corner; 350 unsigned int this_active_corner = 0, this_sleep_corner = 0; 351 unsigned int peer_active_corner = 0, peer_sleep_corner = 0; 352 353 to_active_sleep(pd, corner, &this_active_corner, &this_sleep_corner); 354 355 if (peer && peer->enabled) 356 to_active_sleep(peer, peer->corner, &peer_active_corner, 357 &peer_sleep_corner); 358 359 active_corner = max(this_active_corner, peer_active_corner); 360 361 ret = rpmhpd_send_corner(pd, RPMH_ACTIVE_ONLY_STATE, active_corner, 362 active_corner > pd->active_corner); 363 if (ret) 364 return ret; 365 366 pd->active_corner = active_corner; 367 368 if (peer) { 369 peer->active_corner = active_corner; 370 371 ret = rpmhpd_send_corner(pd, RPMH_WAKE_ONLY_STATE, 372 active_corner, false); 373 if (ret) 374 return ret; 375 376 sleep_corner = max(this_sleep_corner, peer_sleep_corner); 377 378 return rpmhpd_send_corner(pd, RPMH_SLEEP_STATE, sleep_corner, 379 false); 380 } 381 382 return ret; 383} 384 385static int rpmhpd_power_on(struct generic_pm_domain *domain) 386{ 387 struct rpmhpd *pd = domain_to_rpmhpd(domain); 388 int ret = 0; 389 390 mutex_lock(&rpmhpd_lock); 391 392 if (pd->corner) 393 ret = rpmhpd_aggregate_corner(pd, pd->corner); 394 395 if (!ret) 396 pd->enabled = true; 397 398 mutex_unlock(&rpmhpd_lock); 399 400 return ret; 401} 402 403static int rpmhpd_power_off(struct generic_pm_domain *domain) 404{ 405 struct rpmhpd *pd = domain_to_rpmhpd(domain); 406 int ret; 407 408 mutex_lock(&rpmhpd_lock); 409 410 ret = rpmhpd_aggregate_corner(pd, 0); 411 if (!ret) 412 pd->enabled = false; 413 414 mutex_unlock(&rpmhpd_lock); 415 416 return ret; 417} 418 419static int rpmhpd_set_performance_state(struct generic_pm_domain *domain, 420 unsigned int level) 421{ 422 struct rpmhpd *pd = domain_to_rpmhpd(domain); 423 int ret = 0, i; 424 425 mutex_lock(&rpmhpd_lock); 426 427 for (i = 0; i < pd->level_count; i++) 428 if (level <= pd->level[i]) 429 break; 430 431 /* 432 * If the level requested is more than that supported by the 433 * max corner, just set it to max anyway. 434 */ 435 if (i == pd->level_count) 436 i--; 437 438 if (pd->enabled) { 439 ret = rpmhpd_aggregate_corner(pd, i); 440 if (ret) 441 goto out; 442 } 443 444 pd->corner = i; 445out: 446 mutex_unlock(&rpmhpd_lock); 447 448 return ret; 449} 450 451static unsigned int rpmhpd_get_performance_state(struct generic_pm_domain *genpd, 452 struct dev_pm_opp *opp) 453{ 454 return dev_pm_opp_get_level(opp); 455} 456 457static int rpmhpd_update_level_mapping(struct rpmhpd *rpmhpd) 458{ 459 int i; 460 const u16 *buf; 461 462 buf = cmd_db_read_aux_data(rpmhpd->res_name, &rpmhpd->level_count); 463 if (IS_ERR(buf)) 464 return PTR_ERR(buf); 465 466 /* 2 bytes used for each command DB aux data entry */ 467 rpmhpd->level_count >>= 1; 468 469 if (rpmhpd->level_count > RPMH_ARC_MAX_LEVELS) 470 return -EINVAL; 471 472 for (i = 0; i < rpmhpd->level_count; i++) { 473 rpmhpd->level[i] = buf[i]; 474 475 /* 476 * The AUX data may be zero padded. These 0 valued entries at 477 * the end of the map must be ignored. 478 */ 479 if (i > 0 && rpmhpd->level[i] == 0) { 480 rpmhpd->level_count = i; 481 break; 482 } 483 pr_debug("%s: ARC hlvl=%2d --> vlvl=%4u\n", rpmhpd->res_name, i, 484 rpmhpd->level[i]); 485 } 486 487 return 0; 488} 489 490static int rpmhpd_probe(struct platform_device *pdev) 491{ 492 int i, ret; 493 size_t num_pds; 494 struct device *dev = &pdev->dev; 495 struct genpd_onecell_data *data; 496 struct rpmhpd **rpmhpds; 497 const struct rpmhpd_desc *desc; 498 499 desc = of_device_get_match_data(dev); 500 if (!desc) 501 return -EINVAL; 502 503 rpmhpds = desc->rpmhpds; 504 num_pds = desc->num_pds; 505 506 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 507 if (!data) 508 return -ENOMEM; 509 510 data->domains = devm_kcalloc(dev, num_pds, sizeof(*data->domains), 511 GFP_KERNEL); 512 if (!data->domains) 513 return -ENOMEM; 514 515 data->num_domains = num_pds; 516 517 for (i = 0; i < num_pds; i++) { 518 if (!rpmhpds[i]) { 519 dev_warn(dev, "rpmhpds[%d] is empty\n", i); 520 continue; 521 } 522 523 rpmhpds[i]->dev = dev; 524 rpmhpds[i]->addr = cmd_db_read_addr(rpmhpds[i]->res_name); 525 if (!rpmhpds[i]->addr) { 526 dev_err(dev, "Could not find RPMh address for resource %s\n", 527 rpmhpds[i]->res_name); 528 return -ENODEV; 529 } 530 531 ret = cmd_db_read_slave_id(rpmhpds[i]->res_name); 532 if (ret != CMD_DB_HW_ARC) { 533 dev_err(dev, "RPMh slave ID mismatch\n"); 534 return -EINVAL; 535 } 536 537 ret = rpmhpd_update_level_mapping(rpmhpds[i]); 538 if (ret) 539 return ret; 540 541 rpmhpds[i]->pd.power_off = rpmhpd_power_off; 542 rpmhpds[i]->pd.power_on = rpmhpd_power_on; 543 rpmhpds[i]->pd.set_performance_state = rpmhpd_set_performance_state; 544 rpmhpds[i]->pd.opp_to_performance_state = rpmhpd_get_performance_state; 545 pm_genpd_init(&rpmhpds[i]->pd, NULL, true); 546 547 data->domains[i] = &rpmhpds[i]->pd; 548 } 549 550 /* Add subdomains */ 551 for (i = 0; i < num_pds; i++) { 552 if (!rpmhpds[i]) 553 continue; 554 if (rpmhpds[i]->parent) 555 pm_genpd_add_subdomain(rpmhpds[i]->parent, 556 &rpmhpds[i]->pd); 557 } 558 559 return of_genpd_add_provider_onecell(pdev->dev.of_node, data); 560} 561 562static struct platform_driver rpmhpd_driver = { 563 .driver = { 564 .name = "qcom-rpmhpd", 565 .of_match_table = rpmhpd_match_table, 566 .suppress_bind_attrs = true, 567 }, 568 .probe = rpmhpd_probe, 569}; 570 571static int __init rpmhpd_init(void) 572{ 573 return platform_driver_register(&rpmhpd_driver); 574} 575core_initcall(rpmhpd_init); 576 577MODULE_DESCRIPTION("Qualcomm Technologies, Inc. RPMh Power Domain Driver"); 578MODULE_LICENSE("GPL v2");