[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250307034454.12243-6-guangjie.song@mediatek.com>
Date: Fri, 7 Mar 2025 11:44:29 +0800
From: Guangjie Song <guangjie.song@...iatek.com>
To: Rob Herring <robh@...nel.org>, Krzysztof Kozlowski <krzk+dt@...nel.org>,
Conor Dooley <conor+dt@...nel.org>, Matthias Brugger
<matthias.bgg@...il.com>, AngeloGioacchino Del Regno
<angelogioacchino.delregno@...labora.com>, Ulf Hansson
<ulf.hansson@...aro.org>
CC: <devicetree@...r.kernel.org>, <linux-kernel@...r.kernel.org>,
<linux-arm-kernel@...ts.infradead.org>, <linux-mediatek@...ts.infradead.org>,
<linux-pm@...r.kernel.org>, Guangjie Song <guangjie.song@...iatek.com>,
<Project_Global_Chrome_Upstream_Group@...iatek.com>
Subject: [PATCH 05/13] pmdomain: mediatek: Support voting for power domain
Power domain supports voting mechanism. If any xPU votes power domain
on, the power domain keep on.
Add power domain vote support.
Signed-off-by: Guangjie Song <guangjie.song@...iatek.com>
---
drivers/pmdomain/mediatek/mtk-scpsys.c | 207 ++++++++++++++++++++++++-
1 file changed, 205 insertions(+), 2 deletions(-)
diff --git a/drivers/pmdomain/mediatek/mtk-scpsys.c b/drivers/pmdomain/mediatek/mtk-scpsys.c
index 2f75c606f7ba..df9cd012006c 100644
--- a/drivers/pmdomain/mediatek/mtk-scpsys.c
+++ b/drivers/pmdomain/mediatek/mtk-scpsys.c
@@ -10,6 +10,7 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
+#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/soc/mediatek/infracfg.h>
@@ -22,7 +23,12 @@
#define MTK_POLL_DELAY_US 10
#define MTK_POLL_TIMEOUT USEC_PER_SEC
+#define MTK_POLL_TIMEOUT_300MS (300 * USEC_PER_MSEC)
+#define MTK_POLL_IRQ_TIMEOUT USEC_PER_SEC
+#define MTK_POLL_VOTE_PREPARE_CNT 2500
+#define MTK_POLL_VOTE_PREPARE_US 2
#define MTK_ACK_DELAY_US 50
+#define MTK_STABLE_DELAY_US 100
#define MTK_SCPD_ACTIVE_WAKEUP BIT(0)
#define MTK_SCPD_FWAIT_SRAM BIT(1)
@@ -30,6 +36,7 @@
#define MTK_SCPD_SRAM_SLP BIT(3)
#define MTK_SCPD_BYPASS_INIT_ON BIT(4)
#define MTK_SCPD_IS_PWR_CON_ON BIT(5)
+#define MTK_SCPD_VOTE_OPS BIT(6)
#define MTK_SCPD_CAPS(_scpd, _x) ((_scpd)->data->caps & (_x))
#define SPM_VDE_PWR_CON 0x0210
@@ -120,8 +127,18 @@ static const char * const clk_names[] = {
/**
* struct scp_domain_data - scp domain data for power on/off flow
* @name: The domain name.
+ * @vote_comp: The vote name.
* @sta_mask: The mask for power on/off status bit.
* @ctl_offs: The offset for main power control register.
+ * @vote_done_ofs: The offset for vote done register.
+ * @vote_ofs: The offset for vote register.
+ * @vote_set_ofs: The offset for vote set register.
+ * @vote_clr_ofs: The offset for vote clear register.
+ * @vote_en_ofs: The offset for voted register.
+ * @vote_set_sta_ofs: The offset for vote set status register.
+ * @vote_clr_sta_ofs: The offset for vote clear status register.
+ * @vote_ack_ofs: The offset for power control ack register.
+ * @vote_shift: The bit of vote.
* @sram_pdn_bits: The mask for sram power control bits.
* @sram_pdn_ack_bits: The mask for sram power control acked bits.
* @sram_slp_bits: The mask for sram low power control bits.
@@ -132,8 +149,18 @@ static const char * const clk_names[] = {
*/
struct scp_domain_data {
const char *name;
+ const char *vote_comp;
u32 sta_mask;
int ctl_offs;
+ u32 vote_done_ofs;
+ u32 vote_ofs;
+ u32 vote_set_ofs;
+ u32 vote_clr_ofs;
+ u32 vote_en_ofs;
+ u32 vote_set_sta_ofs;
+ u32 vote_clr_sta_ofs;
+ u32 vote_ack_ofs;
+ u8 vote_shift;
u32 sram_pdn_bits;
u32 sram_pdn_ack_bits;
u32 sram_slp_bits;
@@ -151,6 +178,7 @@ struct scp_domain {
struct clk *clk[MAX_CLKS];
const struct scp_domain_data *data;
struct regulator *supply;
+ struct regmap *vote_regmap;
};
struct scp_ctrl_reg {
@@ -493,6 +521,154 @@ static int scpsys_power_off(struct generic_pm_domain *genpd)
return ret;
}
+static int mtk_vote_is_done(struct scp_domain *scpd)
+{
+ u32 val = 0, mask = 0;
+
+ regmap_read(scpd->vote_regmap, scpd->data->vote_done_ofs, &val);
+ mask = BIT(scpd->data->vote_shift);
+ if ((val & mask) == mask)
+ return 1;
+
+ return 0;
+}
+
+static int mtk_vote_is_enable_done(struct scp_domain *scpd)
+{
+ u32 done = 0, en = 0, set_sta = 0, mask = 0, ack = 0;
+
+ regmap_read(scpd->vote_regmap, scpd->data->vote_done_ofs, &done);
+ regmap_read(scpd->vote_regmap, scpd->data->vote_en_ofs, &en);
+ regmap_read(scpd->vote_regmap, scpd->data->vote_set_sta_ofs, &set_sta);
+ mask = BIT(scpd->data->vote_shift);
+
+ if ((done & mask) && (en & mask) && !(set_sta & mask)) {
+ if (scpd->data->vote_ack_ofs) {
+ regmap_read(scpd->vote_regmap, scpd->data->vote_ack_ofs, &ack);
+ if (!(ack & mask))
+ return 0;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int mtk_vote_is_disable_done(struct scp_domain *scpd)
+{
+ u32 val = 0, val2 = 0;
+
+ regmap_read(scpd->vote_regmap, scpd->data->vote_done_ofs, &val);
+ regmap_read(scpd->vote_regmap, scpd->data->vote_clr_sta_ofs, &val2);
+
+ if ((val & BIT(scpd->data->vote_shift)) &&
+ ((val2 & BIT(scpd->data->vote_shift)) == 0x0))
+ return 1;
+
+ return 0;
+}
+
+static int scpsys_vote_power_on(struct generic_pm_domain *genpd)
+{
+ struct scp_domain *scpd = container_of(genpd, struct scp_domain, genpd);
+ struct scp *scp = scpd->scp;
+ u32 val = 0;
+ int ret = 0;
+ int tmp;
+ int i = 0;
+
+ ret = scpsys_regulator_enable(scpd);
+ if (ret < 0)
+ goto out;
+
+ ret = scpsys_clk_enable(scpd->clk, MAX_CLKS);
+ if (ret)
+ goto out;
+
+ ret = readx_poll_timeout_atomic(mtk_vote_is_done, scpd, tmp, tmp > 0,
+ MTK_POLL_DELAY_US, MTK_POLL_IRQ_TIMEOUT);
+ if (ret < 0)
+ goto out;
+
+ val = BIT(scpd->data->vote_shift);
+ regmap_write(scpd->vote_regmap, scpd->data->vote_set_ofs, val);
+ do {
+ regmap_read(scpd->vote_regmap, scpd->data->vote_set_ofs, &val);
+ if ((val & BIT(scpd->data->vote_shift)) != 0)
+ break;
+
+ if (i > MTK_POLL_VOTE_PREPARE_CNT)
+ goto out;
+
+ udelay(MTK_POLL_VOTE_PREPARE_US);
+ i++;
+ } while (1);
+
+ /* add debounce time */
+ udelay(1);
+
+ /* wait until VOTER_ACK = 1 */
+ ret = readx_poll_timeout_atomic(mtk_vote_is_enable_done, scpd, tmp, tmp > 0,
+ MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT_300MS);
+ if (ret < 0)
+ goto out;
+
+ return 0;
+out:
+ dev_err(scp->dev, "Failed to power on domain %s(%d)\n", genpd->name, ret);
+ return ret;
+}
+
+static int scpsys_vote_power_off(struct generic_pm_domain *genpd)
+{
+ struct scp_domain *scpd = container_of(genpd, struct scp_domain, genpd);
+ struct scp *scp = scpd->scp;
+ u32 val = 0;
+ int ret = 0;
+ int tmp;
+ int i = 0;
+
+ ret = readx_poll_timeout_atomic(mtk_vote_is_done, scpd, tmp, tmp > 0,
+ MTK_POLL_DELAY_US, MTK_POLL_IRQ_TIMEOUT);
+ if (ret < 0)
+ goto out;
+
+ val = BIT(scpd->data->vote_shift);
+ regmap_write(scpd->vote_regmap, scpd->data->vote_clr_ofs, val);
+ do {
+ regmap_read(scpd->vote_regmap, scpd->data->vote_clr_ofs, &val);
+ if ((val & BIT(scpd->data->vote_shift)) == 0)
+ break;
+
+ if (i > MTK_POLL_VOTE_PREPARE_CNT)
+ goto out;
+
+ i++;
+ udelay(MTK_POLL_VOTE_PREPARE_US);
+ } while (1);
+
+ /* delay 100us for stable status */
+ udelay(MTK_STABLE_DELAY_US);
+
+ /* wait until VOTER_ACK = 0 */
+ ret = readx_poll_timeout_atomic(mtk_vote_is_disable_done, scpd, tmp, tmp > 0,
+ MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT_300MS);
+ if (ret < 0)
+ goto out;
+
+ scpsys_clk_disable(scpd->clk, MAX_CLKS);
+
+ ret = scpsys_regulator_disable(scpd);
+ if (ret < 0)
+ goto out;
+
+ return 0;
+out:
+ dev_err(scp->dev, "Failed to power off domain %s(%d)\n", genpd->name, ret);
+ return ret;
+}
+
static void init_clks(struct platform_device *pdev, struct clk **clk)
{
int i;
@@ -501,6 +677,21 @@ static void init_clks(struct platform_device *pdev, struct clk **clk)
clk[i] = devm_clk_get(&pdev->dev, clk_names[i]);
}
+static int mtk_pd_get_regmap(struct platform_device *pdev, struct regmap **regmap,
+ const char *name)
+{
+ *regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, name);
+ if (PTR_ERR(*regmap) == -ENODEV) {
+ dev_notice(&pdev->dev, "%s regmap is null(%ld)\n", name, PTR_ERR(*regmap));
+ *regmap = NULL;
+ } else if (IS_ERR(*regmap)) {
+ dev_notice(&pdev->dev, "Cannot find %s controller: %ld\n", name, PTR_ERR(*regmap));
+ return PTR_ERR(*regmap);
+ }
+
+ return 0;
+}
+
static struct scp *init_scp(struct platform_device *pdev,
const struct scp_domain_data *scp_domain_data, int num,
const struct scp_ctrl_reg *scp_ctrl_reg,
@@ -510,6 +701,7 @@ static struct scp *init_scp(struct platform_device *pdev,
int i, j;
struct scp *scp;
struct clk *clk[CLK_MAX];
+ int ret;
scp = devm_kzalloc(&pdev->dev, sizeof(*scp), GFP_KERNEL);
if (!scp)
@@ -585,9 +777,20 @@ static struct scp *init_scp(struct platform_device *pdev,
scpd->clk[j] = c;
}
+ if (data->vote_comp) {
+ ret = mtk_pd_get_regmap(pdev, &scpd->vote_regmap, data->vote_comp);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
genpd->name = data->name;
- genpd->power_off = scpsys_power_off;
- genpd->power_on = scpsys_power_on;
+ if (MTK_SCPD_CAPS(scpd, MTK_SCPD_VOTE_OPS)) {
+ genpd->power_on = scpsys_vote_power_on;
+ genpd->power_off = scpsys_vote_power_off;
+ } else {
+ genpd->power_off = scpsys_power_off;
+ genpd->power_on = scpsys_power_on;
+ }
if (MTK_SCPD_CAPS(scpd, MTK_SCPD_ACTIVE_WAKEUP))
genpd->flags |= GENPD_FLAG_ACTIVE_WAKEUP;
}
--
2.45.2
Powered by blists - more mailing lists