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.

pmdomain: mediatek: Add support for Hardware Voter power domains

New generation SoCs like MT8196/MT6991 feature a new type of power
domains, managed by a Hardware Voter (HWV) helper (through a SoC
internal fixed-function MCU): this is used to collect votes from
both the AP and the various other remote processors present in the
SoC and transparently power on/off various power domains, avoiding
unpowered access of registers in various internal IPs from all of
the integrated remote processors (or from the AP...!).

Add a new power domain type and differentiate between the old
SCPSYS_MTCMOS_TYPE_DIRECT_CTL - where power domains are controlled
directly by and exclusively from the Application Processor, and
the new SCPSYS_MTCMOS_TYPE_HW_VOTER, where the power domains are
voted through the HWV.

With the two needing different handling, check the power domain
type and assign a different power_{off,on} callback for pm_genpd:
for this specific reason, also move the check for the SCPD cap
MTK_SCPD_KEEP_DEFAULT_OFF after the assignment, and use the
assigned power_on function instead of calling scpsys_power_on()
directly to make that work for both HW_VOTER and DIRECT_CTL.

Reviewed-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>

authored by

AngeloGioacchino Del Regno and committed by
Ulf Hansson
88914db0 72b0a7b3

+266 -26
+222 -25
drivers/pmdomain/mediatek/mtk-pm-domains.c
··· 31 31 #define MTK_POLL_DELAY_US 10 32 32 #define MTK_POLL_TIMEOUT USEC_PER_SEC 33 33 34 + #define MTK_HWV_POLL_DELAY_US 5 35 + #define MTK_HWV_POLL_TIMEOUT (300 * USEC_PER_MSEC) 36 + 37 + #define MTK_HWV_PREPARE_DELAY_US 1 38 + #define MTK_HWV_PREPARE_TIMEOUT (3 * USEC_PER_MSEC) 39 + 34 40 #define PWR_RST_B_BIT BIT(0) 35 41 #define PWR_ISO_BIT BIT(1) 36 42 #define PWR_ON_BIT BIT(2) ··· 54 48 struct scpsys_domain { 55 49 struct generic_pm_domain genpd; 56 50 const struct scpsys_domain_data *data; 51 + const struct scpsys_hwv_domain_data *hwv_data; 57 52 struct scpsys *scpsys; 58 53 int num_clks; 59 54 struct clk_bulk_data *clks; ··· 88 81 89 82 /* A domain is on when both status bits are set. */ 90 83 return status && status2; 84 + } 85 + 86 + static bool scpsys_hwv_domain_is_disable_done(struct scpsys_domain *pd) 87 + { 88 + const struct scpsys_hwv_domain_data *hwv = pd->hwv_data; 89 + u32 regs[2] = { hwv->done, hwv->clr_sta }; 90 + u32 val[2]; 91 + u32 mask = BIT(hwv->setclr_bit); 92 + 93 + regmap_multi_reg_read(pd->scpsys->base, regs, val, 2); 94 + 95 + /* Disable is done when the bit is set in DONE, cleared in CLR_STA */ 96 + return (val[0] & mask) && !(val[1] & mask); 97 + } 98 + 99 + static bool scpsys_hwv_domain_is_enable_done(struct scpsys_domain *pd) 100 + { 101 + const struct scpsys_hwv_domain_data *hwv = pd->hwv_data; 102 + u32 regs[3] = { hwv->done, hwv->en, hwv->set_sta }; 103 + u32 val[3]; 104 + u32 mask = BIT(hwv->setclr_bit); 105 + 106 + regmap_multi_reg_read(pd->scpsys->base, regs, val, 3); 107 + 108 + /* Enable is done when the bit is set in DONE and EN, cleared in SET_STA */ 109 + return (val[0] & mask) && (val[1] & mask) && !(val[2] & mask); 91 110 } 92 111 93 112 static int scpsys_sram_enable(struct scpsys_domain *pd) ··· 282 249 { 283 250 return supply ? regulator_disable(supply) : 0; 284 251 } 252 + 253 + static int scpsys_hwv_power_on(struct generic_pm_domain *genpd) 254 + { 255 + struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd); 256 + const struct scpsys_hwv_domain_data *hwv = pd->hwv_data; 257 + struct scpsys *scpsys = pd->scpsys; 258 + u32 val; 259 + int ret; 260 + 261 + ret = scpsys_regulator_enable(pd->supply); 262 + if (ret) 263 + return ret; 264 + 265 + ret = clk_bulk_prepare_enable(pd->num_clks, pd->clks); 266 + if (ret) 267 + goto err_reg; 268 + 269 + /* For HWV the subsys clocks refer to the HWV low power subsystem */ 270 + ret = clk_bulk_prepare_enable(pd->num_subsys_clks, pd->subsys_clks); 271 + if (ret) 272 + goto err_disable_clks; 273 + 274 + /* Make sure the HW Voter is idle and able to accept commands */ 275 + ret = regmap_read_poll_timeout_atomic(scpsys->base, hwv->done, val, 276 + val & BIT(hwv->setclr_bit), 277 + MTK_HWV_POLL_DELAY_US, 278 + MTK_HWV_POLL_TIMEOUT); 279 + if (ret) { 280 + dev_err(scpsys->dev, "Failed to power on: HW Voter busy.\n"); 281 + goto err_disable_subsys_clks; 282 + } 283 + 284 + /* 285 + * Instruct the HWV to power on the MTCMOS (power domain): after that, 286 + * the same bit will be unset immediately by the hardware. 287 + */ 288 + regmap_write(scpsys->base, hwv->set, BIT(hwv->setclr_bit)); 289 + 290 + /* 291 + * Wait until the HWV sets the bit again, signalling that its internal 292 + * state machine was started and it now processing the vote command. 293 + */ 294 + ret = regmap_read_poll_timeout_atomic(scpsys->base, hwv->set, val, 295 + val & BIT(hwv->setclr_bit), 296 + MTK_HWV_PREPARE_DELAY_US, 297 + MTK_HWV_PREPARE_TIMEOUT); 298 + if (ret) { 299 + dev_err(scpsys->dev, "Failed to power on: HW Voter not starting.\n"); 300 + goto err_disable_subsys_clks; 301 + } 302 + 303 + /* Wait for ACK, signalling that the MTCMOS was enabled */ 304 + ret = readx_poll_timeout_atomic(scpsys_hwv_domain_is_enable_done, pd, val, val, 305 + MTK_HWV_POLL_DELAY_US, MTK_HWV_POLL_TIMEOUT); 306 + if (ret) { 307 + dev_err(scpsys->dev, "Failed to power on: HW Voter ACK timeout.\n"); 308 + goto err_disable_subsys_clks; 309 + } 310 + 311 + /* It's done! Disable the HWV low power subsystem clocks */ 312 + clk_bulk_disable_unprepare(pd->num_subsys_clks, pd->subsys_clks); 313 + 314 + return 0; 315 + 316 + err_disable_subsys_clks: 317 + clk_bulk_disable_unprepare(pd->num_subsys_clks, pd->subsys_clks); 318 + err_disable_clks: 319 + clk_bulk_disable_unprepare(pd->num_clks, pd->clks); 320 + err_reg: 321 + scpsys_regulator_disable(pd->supply); 322 + return ret; 323 + }; 324 + 325 + static int scpsys_hwv_power_off(struct generic_pm_domain *genpd) 326 + { 327 + struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd); 328 + const struct scpsys_hwv_domain_data *hwv = pd->hwv_data; 329 + struct scpsys *scpsys = pd->scpsys; 330 + u32 val; 331 + int ret; 332 + 333 + ret = clk_bulk_prepare_enable(pd->num_subsys_clks, pd->subsys_clks); 334 + if (ret) 335 + return ret; 336 + 337 + /* Make sure the HW Voter is idle and able to accept commands */ 338 + ret = regmap_read_poll_timeout_atomic(scpsys->base, hwv->done, val, 339 + val & BIT(hwv->setclr_bit), 340 + MTK_HWV_POLL_DELAY_US, 341 + MTK_HWV_POLL_TIMEOUT); 342 + if (ret) 343 + goto err_disable_subsys_clks; 344 + 345 + 346 + /* 347 + * Instruct the HWV to power off the MTCMOS (power domain): differently 348 + * from poweron, the bit will be kept set. 349 + */ 350 + regmap_write(scpsys->base, hwv->clr, BIT(hwv->setclr_bit)); 351 + 352 + /* 353 + * Wait until the HWV clears the bit, signalling that its internal 354 + * state machine was started and it now processing the clear command. 355 + */ 356 + ret = regmap_read_poll_timeout_atomic(scpsys->base, hwv->clr, val, 357 + !(val & BIT(hwv->setclr_bit)), 358 + MTK_HWV_PREPARE_DELAY_US, 359 + MTK_HWV_PREPARE_TIMEOUT); 360 + if (ret) 361 + goto err_disable_subsys_clks; 362 + 363 + /* Poweroff needs 100us for the HW to stabilize */ 364 + udelay(100); 365 + 366 + /* Wait for ACK, signalling that the MTCMOS was disabled */ 367 + ret = readx_poll_timeout_atomic(scpsys_hwv_domain_is_disable_done, pd, val, val, 368 + MTK_HWV_POLL_DELAY_US, MTK_HWV_POLL_TIMEOUT); 369 + if (ret) 370 + goto err_disable_subsys_clks; 371 + 372 + clk_bulk_disable_unprepare(pd->num_subsys_clks, pd->subsys_clks); 373 + clk_bulk_disable_unprepare(pd->num_clks, pd->clks); 374 + 375 + scpsys_regulator_disable(pd->supply); 376 + 377 + return 0; 378 + 379 + err_disable_subsys_clks: 380 + clk_bulk_disable_unprepare(pd->num_subsys_clks, pd->subsys_clks); 381 + return ret; 382 + }; 285 383 286 384 static int scpsys_ctl_pwrseq_on(struct scpsys_domain *pd) 287 385 { ··· 678 514 generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_node *node) 679 515 { 680 516 const struct scpsys_domain_data *domain_data; 517 + const struct scpsys_hwv_domain_data *hwv_domain_data; 681 518 struct scpsys_domain *pd; 682 519 struct property *prop; 683 520 const char *clk_name; ··· 694 529 return ERR_PTR(-EINVAL); 695 530 } 696 531 697 - if (id >= scpsys->soc_data->num_domains) { 698 - dev_err(scpsys->dev, "%pOF: invalid domain id %d\n", node, id); 699 - return ERR_PTR(-EINVAL); 700 - } 532 + switch (scpsys->soc_data->type) { 533 + case SCPSYS_MTCMOS_TYPE_DIRECT_CTL: 534 + if (id >= scpsys->soc_data->num_domains) { 535 + dev_err(scpsys->dev, "%pOF: invalid domain id %d\n", node, id); 536 + return ERR_PTR(-EINVAL); 537 + } 701 538 702 - domain_data = &scpsys->soc_data->domains_data[id]; 703 - if (domain_data->sta_mask == 0) { 704 - dev_err(scpsys->dev, "%pOF: undefined domain id %d\n", node, id); 539 + domain_data = &scpsys->soc_data->domains_data[id]; 540 + hwv_domain_data = NULL; 541 + 542 + if (domain_data->sta_mask == 0) { 543 + dev_err(scpsys->dev, "%pOF: undefined domain id %d\n", node, id); 544 + return ERR_PTR(-EINVAL); 545 + } 546 + 547 + break; 548 + case SCPSYS_MTCMOS_TYPE_HW_VOTER: 549 + if (id >= scpsys->soc_data->num_hwv_domains) { 550 + dev_err(scpsys->dev, "%pOF: invalid HWV domain id %d\n", node, id); 551 + return ERR_PTR(-EINVAL); 552 + } 553 + 554 + domain_data = NULL; 555 + hwv_domain_data = &scpsys->soc_data->hwv_domains_data[id]; 556 + 557 + break; 558 + default: 705 559 return ERR_PTR(-EINVAL); 706 560 } 707 561 ··· 729 545 return ERR_PTR(-ENOMEM); 730 546 731 547 pd->data = domain_data; 548 + pd->hwv_data = hwv_domain_data; 732 549 pd->scpsys = scpsys; 733 550 734 551 if (MTK_SCPD_CAPS(pd, MTK_SCPD_DOMAIN_SUPPLY)) { ··· 789 604 pd->subsys_clks[i].clk = clk; 790 605 } 791 606 607 + if (scpsys->domains[id]) { 608 + ret = -EINVAL; 609 + dev_err(scpsys->dev, 610 + "power domain with id %d already exists, check your device-tree\n", id); 611 + goto err_put_subsys_clocks; 612 + } 613 + 614 + if (pd->data && pd->data->name) 615 + pd->genpd.name = pd->data->name; 616 + else if (pd->hwv_data && pd->hwv_data->name) 617 + pd->genpd.name = pd->hwv_data->name; 618 + else 619 + pd->genpd.name = node->name; 620 + 621 + if (scpsys->soc_data->type == SCPSYS_MTCMOS_TYPE_DIRECT_CTL) { 622 + pd->genpd.power_off = scpsys_power_off; 623 + pd->genpd.power_on = scpsys_power_on; 624 + } else { 625 + pd->genpd.power_off = scpsys_hwv_power_off; 626 + pd->genpd.power_on = scpsys_hwv_power_on; 627 + 628 + /* HW-Voter code can be invoked in atomic context */ 629 + pd->genpd.flags |= GENPD_FLAG_IRQ_SAFE; 630 + } 631 + 792 632 /* 793 633 * Initially turn on all domains to make the domains usable 794 634 * with !CONFIG_PM and to get the hardware in sync with the ··· 825 615 dev_warn(scpsys->dev, 826 616 "%pOF: A default off power domain has been ON\n", node); 827 617 } else { 828 - ret = scpsys_power_on(&pd->genpd); 618 + ret = pd->genpd.power_on(&pd->genpd); 829 619 if (ret < 0) { 830 620 dev_err(scpsys->dev, "%pOF: failed to power on domain: %d\n", node, ret); 831 621 goto err_put_subsys_clocks; ··· 834 624 if (MTK_SCPD_CAPS(pd, MTK_SCPD_ALWAYS_ON)) 835 625 pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON; 836 626 } 837 - 838 - if (scpsys->domains[id]) { 839 - ret = -EINVAL; 840 - dev_err(scpsys->dev, 841 - "power domain with id %d already exists, check your device-tree\n", id); 842 - goto err_put_subsys_clocks; 843 - } 844 - 845 - if (!pd->data->name) 846 - pd->genpd.name = node->name; 847 - else 848 - pd->genpd.name = pd->data->name; 849 - 850 - pd->genpd.power_off = scpsys_power_off; 851 - pd->genpd.power_on = scpsys_power_on; 852 627 853 628 if (MTK_SCPD_CAPS(pd, MTK_SCPD_ACTIVE_WAKEUP)) 854 629 pd->genpd.flags |= GENPD_FLAG_ACTIVE_WAKEUP; ··· 1129 934 struct device_node *node; 1130 935 struct device *parent; 1131 936 struct scpsys *scpsys; 1132 - int ret; 937 + int num_domains, ret; 1133 938 1134 939 soc = of_device_get_match_data(&pdev->dev); 1135 940 if (!soc) { ··· 1137 942 return -EINVAL; 1138 943 } 1139 944 1140 - scpsys = devm_kzalloc(dev, struct_size(scpsys, domains, soc->num_domains), GFP_KERNEL); 945 + num_domains = soc->num_domains + soc->num_hwv_domains; 946 + 947 + scpsys = devm_kzalloc(dev, struct_size(scpsys, domains, num_domains), GFP_KERNEL); 1141 948 if (!scpsys) 1142 949 return -ENOMEM; 1143 950
+44 -1
drivers/pmdomain/mediatek/mtk-pm-domains.h
··· 16 16 #define MTK_SCPD_SRAM_PDN_INVERTED BIT(9) 17 17 #define MTK_SCPD_MODEM_PWRSEQ BIT(10) 18 18 #define MTK_SCPD_SKIP_RESET_B BIT(11) 19 - #define MTK_SCPD_CAPS(_scpd, _x) ((_scpd)->data->caps & (_x)) 19 + #define MTK_SCPD_CAPS(_scpd, _x) ((_scpd)->data ? \ 20 + (_scpd)->data->caps & (_x) : \ 21 + (_scpd)->hwv_data->caps & (_x)) 20 22 21 23 #define SPM_VDE_PWR_CON 0x0210 22 24 #define SPM_MFG_PWR_CON 0x0214 ··· 127 125 }; 128 126 129 127 /** 128 + * enum scpsys_mtcmos_type - Type of power domain controller 129 + * @SCPSYS_MTCMOS_TYPE_DIRECT_CTL: Power domains are controlled with direct access 130 + * @SCPSYS_MTCMOS_TYPE_HW_VOTER: Hardware-assisted voted power domain control 131 + * @SCPSYS_MTCMOS_TYPE_MAX: Number of supported power domain types 132 + */ 133 + enum scpsys_mtcmos_type { 134 + SCPSYS_MTCMOS_TYPE_DIRECT_CTL = 0, 135 + SCPSYS_MTCMOS_TYPE_HW_VOTER, 136 + SCPSYS_MTCMOS_TYPE_MAX 137 + }; 138 + 139 + /** 130 140 * struct scpsys_domain_data - scp domain data for power on/off flow 131 141 * @name: The name of the power domain. 132 142 * @sta_mask: The mask for power on/off status bit. ··· 166 152 int pwr_sta2nd_offs; 167 153 }; 168 154 155 + /** 156 + * struct scpsys_hwv_domain_data - Hardware Voter power domain data 157 + * @name: Name of the power domain 158 + * @set: Offset of the HWV SET register 159 + * @clr: Offset of the HWV CLEAR register 160 + * @done: Offset of the HWV DONE register 161 + * @en: Offset of the HWV ENABLE register 162 + * @set_sta: Offset of the HWV SET STATUS register 163 + * @clr_sta: Offset of the HWV CLEAR STATUS register 164 + * @setclr_bit: The SET/CLR bit to enable/disable the power domain 165 + * @sta_bit: The SET/CLR STA bit to check for on/off ACK 166 + * @caps: The flag for active wake-up action 167 + */ 168 + struct scpsys_hwv_domain_data { 169 + const char *name; 170 + u16 set; 171 + u16 clr; 172 + u16 done; 173 + u16 en; 174 + u16 set_sta; 175 + u16 clr_sta; 176 + u8 setclr_bit; 177 + u8 sta_bit; 178 + u16 caps; 179 + }; 180 + 169 181 struct scpsys_soc_data { 170 182 const struct scpsys_domain_data *domains_data; 171 183 int num_domains; 184 + const struct scpsys_hwv_domain_data *hwv_domains_data; 185 + int num_hwv_domains; 172 186 enum scpsys_bus_prot_block *bus_prot_blocks; 173 187 int num_bus_prot_blocks; 188 + enum scpsys_mtcmos_type type; 174 189 }; 175 190 176 191 #endif /* __SOC_MEDIATEK_MTK_PM_DOMAINS_H */