Skip to content

Commit 88914db

Browse files
AngeloGioacchino Del RegnoUlf Hansson
authored andcommitted
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>
1 parent 72b0a7b commit 88914db

2 files changed

Lines changed: 266 additions & 26 deletions

File tree

drivers/pmdomain/mediatek/mtk-pm-domains.c

Lines changed: 222 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@
3131
#define MTK_POLL_DELAY_US 10
3232
#define MTK_POLL_TIMEOUT USEC_PER_SEC
3333

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+
3440
#define PWR_RST_B_BIT BIT(0)
3541
#define PWR_ISO_BIT BIT(1)
3642
#define PWR_ON_BIT BIT(2)
@@ -48,6 +54,7 @@
4854
struct scpsys_domain {
4955
struct generic_pm_domain genpd;
5056
const struct scpsys_domain_data *data;
57+
const struct scpsys_hwv_domain_data *hwv_data;
5158
struct scpsys *scpsys;
5259
int num_clks;
5360
struct clk_bulk_data *clks;
@@ -83,6 +90,32 @@ static bool scpsys_domain_is_on(struct scpsys_domain *pd)
8390
return status && status2;
8491
}
8592

93+
static bool scpsys_hwv_domain_is_disable_done(struct scpsys_domain *pd)
94+
{
95+
const struct scpsys_hwv_domain_data *hwv = pd->hwv_data;
96+
u32 regs[2] = { hwv->done, hwv->clr_sta };
97+
u32 val[2];
98+
u32 mask = BIT(hwv->setclr_bit);
99+
100+
regmap_multi_reg_read(pd->scpsys->base, regs, val, 2);
101+
102+
/* Disable is done when the bit is set in DONE, cleared in CLR_STA */
103+
return (val[0] & mask) && !(val[1] & mask);
104+
}
105+
106+
static bool scpsys_hwv_domain_is_enable_done(struct scpsys_domain *pd)
107+
{
108+
const struct scpsys_hwv_domain_data *hwv = pd->hwv_data;
109+
u32 regs[3] = { hwv->done, hwv->en, hwv->set_sta };
110+
u32 val[3];
111+
u32 mask = BIT(hwv->setclr_bit);
112+
113+
regmap_multi_reg_read(pd->scpsys->base, regs, val, 3);
114+
115+
/* Enable is done when the bit is set in DONE and EN, cleared in SET_STA */
116+
return (val[0] & mask) && (val[1] & mask) && !(val[2] & mask);
117+
}
118+
86119
static int scpsys_sram_enable(struct scpsys_domain *pd)
87120
{
88121
u32 expected_ack, pdn_ack = pd->data->sram_pdn_ack_bits;
@@ -250,6 +283,137 @@ static int scpsys_regulator_disable(struct regulator *supply)
250283
return supply ? regulator_disable(supply) : 0;
251284
}
252285

286+
static int scpsys_hwv_power_on(struct generic_pm_domain *genpd)
287+
{
288+
struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd);
289+
const struct scpsys_hwv_domain_data *hwv = pd->hwv_data;
290+
struct scpsys *scpsys = pd->scpsys;
291+
u32 val;
292+
int ret;
293+
294+
ret = scpsys_regulator_enable(pd->supply);
295+
if (ret)
296+
return ret;
297+
298+
ret = clk_bulk_prepare_enable(pd->num_clks, pd->clks);
299+
if (ret)
300+
goto err_reg;
301+
302+
/* For HWV the subsys clocks refer to the HWV low power subsystem */
303+
ret = clk_bulk_prepare_enable(pd->num_subsys_clks, pd->subsys_clks);
304+
if (ret)
305+
goto err_disable_clks;
306+
307+
/* Make sure the HW Voter is idle and able to accept commands */
308+
ret = regmap_read_poll_timeout_atomic(scpsys->base, hwv->done, val,
309+
val & BIT(hwv->setclr_bit),
310+
MTK_HWV_POLL_DELAY_US,
311+
MTK_HWV_POLL_TIMEOUT);
312+
if (ret) {
313+
dev_err(scpsys->dev, "Failed to power on: HW Voter busy.\n");
314+
goto err_disable_subsys_clks;
315+
}
316+
317+
/*
318+
* Instruct the HWV to power on the MTCMOS (power domain): after that,
319+
* the same bit will be unset immediately by the hardware.
320+
*/
321+
regmap_write(scpsys->base, hwv->set, BIT(hwv->setclr_bit));
322+
323+
/*
324+
* Wait until the HWV sets the bit again, signalling that its internal
325+
* state machine was started and it now processing the vote command.
326+
*/
327+
ret = regmap_read_poll_timeout_atomic(scpsys->base, hwv->set, val,
328+
val & BIT(hwv->setclr_bit),
329+
MTK_HWV_PREPARE_DELAY_US,
330+
MTK_HWV_PREPARE_TIMEOUT);
331+
if (ret) {
332+
dev_err(scpsys->dev, "Failed to power on: HW Voter not starting.\n");
333+
goto err_disable_subsys_clks;
334+
}
335+
336+
/* Wait for ACK, signalling that the MTCMOS was enabled */
337+
ret = readx_poll_timeout_atomic(scpsys_hwv_domain_is_enable_done, pd, val, val,
338+
MTK_HWV_POLL_DELAY_US, MTK_HWV_POLL_TIMEOUT);
339+
if (ret) {
340+
dev_err(scpsys->dev, "Failed to power on: HW Voter ACK timeout.\n");
341+
goto err_disable_subsys_clks;
342+
}
343+
344+
/* It's done! Disable the HWV low power subsystem clocks */
345+
clk_bulk_disable_unprepare(pd->num_subsys_clks, pd->subsys_clks);
346+
347+
return 0;
348+
349+
err_disable_subsys_clks:
350+
clk_bulk_disable_unprepare(pd->num_subsys_clks, pd->subsys_clks);
351+
err_disable_clks:
352+
clk_bulk_disable_unprepare(pd->num_clks, pd->clks);
353+
err_reg:
354+
scpsys_regulator_disable(pd->supply);
355+
return ret;
356+
};
357+
358+
static int scpsys_hwv_power_off(struct generic_pm_domain *genpd)
359+
{
360+
struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd);
361+
const struct scpsys_hwv_domain_data *hwv = pd->hwv_data;
362+
struct scpsys *scpsys = pd->scpsys;
363+
u32 val;
364+
int ret;
365+
366+
ret = clk_bulk_prepare_enable(pd->num_subsys_clks, pd->subsys_clks);
367+
if (ret)
368+
return ret;
369+
370+
/* Make sure the HW Voter is idle and able to accept commands */
371+
ret = regmap_read_poll_timeout_atomic(scpsys->base, hwv->done, val,
372+
val & BIT(hwv->setclr_bit),
373+
MTK_HWV_POLL_DELAY_US,
374+
MTK_HWV_POLL_TIMEOUT);
375+
if (ret)
376+
goto err_disable_subsys_clks;
377+
378+
379+
/*
380+
* Instruct the HWV to power off the MTCMOS (power domain): differently
381+
* from poweron, the bit will be kept set.
382+
*/
383+
regmap_write(scpsys->base, hwv->clr, BIT(hwv->setclr_bit));
384+
385+
/*
386+
* Wait until the HWV clears the bit, signalling that its internal
387+
* state machine was started and it now processing the clear command.
388+
*/
389+
ret = regmap_read_poll_timeout_atomic(scpsys->base, hwv->clr, val,
390+
!(val & BIT(hwv->setclr_bit)),
391+
MTK_HWV_PREPARE_DELAY_US,
392+
MTK_HWV_PREPARE_TIMEOUT);
393+
if (ret)
394+
goto err_disable_subsys_clks;
395+
396+
/* Poweroff needs 100us for the HW to stabilize */
397+
udelay(100);
398+
399+
/* Wait for ACK, signalling that the MTCMOS was disabled */
400+
ret = readx_poll_timeout_atomic(scpsys_hwv_domain_is_disable_done, pd, val, val,
401+
MTK_HWV_POLL_DELAY_US, MTK_HWV_POLL_TIMEOUT);
402+
if (ret)
403+
goto err_disable_subsys_clks;
404+
405+
clk_bulk_disable_unprepare(pd->num_subsys_clks, pd->subsys_clks);
406+
clk_bulk_disable_unprepare(pd->num_clks, pd->clks);
407+
408+
scpsys_regulator_disable(pd->supply);
409+
410+
return 0;
411+
412+
err_disable_subsys_clks:
413+
clk_bulk_disable_unprepare(pd->num_subsys_clks, pd->subsys_clks);
414+
return ret;
415+
};
416+
253417
static int scpsys_ctl_pwrseq_on(struct scpsys_domain *pd)
254418
{
255419
struct scpsys *scpsys = pd->scpsys;
@@ -514,6 +678,7 @@ static struct
514678
generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_node *node)
515679
{
516680
const struct scpsys_domain_data *domain_data;
681+
const struct scpsys_hwv_domain_data *hwv_domain_data;
517682
struct scpsys_domain *pd;
518683
struct property *prop;
519684
const char *clk_name;
@@ -529,14 +694,33 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no
529694
return ERR_PTR(-EINVAL);
530695
}
531696

532-
if (id >= scpsys->soc_data->num_domains) {
533-
dev_err(scpsys->dev, "%pOF: invalid domain id %d\n", node, id);
534-
return ERR_PTR(-EINVAL);
535-
}
697+
switch (scpsys->soc_data->type) {
698+
case SCPSYS_MTCMOS_TYPE_DIRECT_CTL:
699+
if (id >= scpsys->soc_data->num_domains) {
700+
dev_err(scpsys->dev, "%pOF: invalid domain id %d\n", node, id);
701+
return ERR_PTR(-EINVAL);
702+
}
703+
704+
domain_data = &scpsys->soc_data->domains_data[id];
705+
hwv_domain_data = NULL;
536706

537-
domain_data = &scpsys->soc_data->domains_data[id];
538-
if (domain_data->sta_mask == 0) {
539-
dev_err(scpsys->dev, "%pOF: undefined domain id %d\n", node, id);
707+
if (domain_data->sta_mask == 0) {
708+
dev_err(scpsys->dev, "%pOF: undefined domain id %d\n", node, id);
709+
return ERR_PTR(-EINVAL);
710+
}
711+
712+
break;
713+
case SCPSYS_MTCMOS_TYPE_HW_VOTER:
714+
if (id >= scpsys->soc_data->num_hwv_domains) {
715+
dev_err(scpsys->dev, "%pOF: invalid HWV domain id %d\n", node, id);
716+
return ERR_PTR(-EINVAL);
717+
}
718+
719+
domain_data = NULL;
720+
hwv_domain_data = &scpsys->soc_data->hwv_domains_data[id];
721+
722+
break;
723+
default:
540724
return ERR_PTR(-EINVAL);
541725
}
542726

@@ -545,6 +729,7 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no
545729
return ERR_PTR(-ENOMEM);
546730

547731
pd->data = domain_data;
732+
pd->hwv_data = hwv_domain_data;
548733
pd->scpsys = scpsys;
549734

550735
if (MTK_SCPD_CAPS(pd, MTK_SCPD_DOMAIN_SUPPLY)) {
@@ -604,6 +789,31 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no
604789
pd->subsys_clks[i].clk = clk;
605790
}
606791

792+
if (scpsys->domains[id]) {
793+
ret = -EINVAL;
794+
dev_err(scpsys->dev,
795+
"power domain with id %d already exists, check your device-tree\n", id);
796+
goto err_put_subsys_clocks;
797+
}
798+
799+
if (pd->data && pd->data->name)
800+
pd->genpd.name = pd->data->name;
801+
else if (pd->hwv_data && pd->hwv_data->name)
802+
pd->genpd.name = pd->hwv_data->name;
803+
else
804+
pd->genpd.name = node->name;
805+
806+
if (scpsys->soc_data->type == SCPSYS_MTCMOS_TYPE_DIRECT_CTL) {
807+
pd->genpd.power_off = scpsys_power_off;
808+
pd->genpd.power_on = scpsys_power_on;
809+
} else {
810+
pd->genpd.power_off = scpsys_hwv_power_off;
811+
pd->genpd.power_on = scpsys_hwv_power_on;
812+
813+
/* HW-Voter code can be invoked in atomic context */
814+
pd->genpd.flags |= GENPD_FLAG_IRQ_SAFE;
815+
}
816+
607817
/*
608818
* Initially turn on all domains to make the domains usable
609819
* with !CONFIG_PM and to get the hardware in sync with the
@@ -615,7 +825,7 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no
615825
dev_warn(scpsys->dev,
616826
"%pOF: A default off power domain has been ON\n", node);
617827
} else {
618-
ret = scpsys_power_on(&pd->genpd);
828+
ret = pd->genpd.power_on(&pd->genpd);
619829
if (ret < 0) {
620830
dev_err(scpsys->dev, "%pOF: failed to power on domain: %d\n", node, ret);
621831
goto err_put_subsys_clocks;
@@ -625,21 +835,6 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no
625835
pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON;
626836
}
627837

628-
if (scpsys->domains[id]) {
629-
ret = -EINVAL;
630-
dev_err(scpsys->dev,
631-
"power domain with id %d already exists, check your device-tree\n", id);
632-
goto err_put_subsys_clocks;
633-
}
634-
635-
if (!pd->data->name)
636-
pd->genpd.name = node->name;
637-
else
638-
pd->genpd.name = pd->data->name;
639-
640-
pd->genpd.power_off = scpsys_power_off;
641-
pd->genpd.power_on = scpsys_power_on;
642-
643838
if (MTK_SCPD_CAPS(pd, MTK_SCPD_ACTIVE_WAKEUP))
644839
pd->genpd.flags |= GENPD_FLAG_ACTIVE_WAKEUP;
645840

@@ -934,15 +1129,17 @@ static int scpsys_probe(struct platform_device *pdev)
9341129
struct device_node *node;
9351130
struct device *parent;
9361131
struct scpsys *scpsys;
937-
int ret;
1132+
int num_domains, ret;
9381133

9391134
soc = of_device_get_match_data(&pdev->dev);
9401135
if (!soc) {
9411136
dev_err(&pdev->dev, "no power controller data\n");
9421137
return -EINVAL;
9431138
}
9441139

945-
scpsys = devm_kzalloc(dev, struct_size(scpsys, domains, soc->num_domains), GFP_KERNEL);
1140+
num_domains = soc->num_domains + soc->num_hwv_domains;
1141+
1142+
scpsys = devm_kzalloc(dev, struct_size(scpsys, domains, num_domains), GFP_KERNEL);
9461143
if (!scpsys)
9471144
return -ENOMEM;
9481145

0 commit comments

Comments
 (0)