From a2106f38077e78afcb4bf98fdda3e162118cfb3d Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Tue, 24 May 2022 10:57:29 +0400 Subject: meson-mx-socinfo: Fix refcount leak in meson_mx_socinfo_init of_find_matching_node() returns a node pointer with refcount incremented, we should use of_node_put() on it when not need anymore. Add missing of_node_put() to avoid refcount leak. Fixes: 5e68c0fc8df8 ("soc: amlogic: Add Meson6/Meson8/Meson8b/Meson8m2 SoC Information driver") Signed-off-by: Miaoqian Lin Reviewed-by: Martin Blumenstingl Signed-off-by: Neil Armstrong Link: https://lore.kernel.org/r/20220524065729.33689-1-linmq006@gmail.com --- drivers/soc/amlogic/meson-mx-socinfo.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/soc') diff --git a/drivers/soc/amlogic/meson-mx-socinfo.c b/drivers/soc/amlogic/meson-mx-socinfo.c index 78f0f1aeca57..92125dd65f33 100644 --- a/drivers/soc/amlogic/meson-mx-socinfo.c +++ b/drivers/soc/amlogic/meson-mx-socinfo.c @@ -126,6 +126,7 @@ static int __init meson_mx_socinfo_init(void) np = of_find_matching_node(NULL, meson_mx_socinfo_analog_top_ids); if (np) { analog_top_regmap = syscon_node_to_regmap(np); + of_node_put(np); if (IS_ERR(analog_top_regmap)) return PTR_ERR(analog_top_regmap); -- cgit v1.2.3 From 18c7deca2b812537aa4d928900e208710f1300aa Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 17 May 2022 12:47:08 +0200 Subject: soc: mediatek: pwrap: Use readx_poll_timeout() instead of custom function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Function pwrap_wait_for_state() is a function that polls an address through a helper function, but this is the very same operation that the readx_poll_timeout macro means to do. Convert all instances of calling pwrap_wait_for_state() to instead use the read_poll_timeout macro. Signed-off-by: AngeloGioacchino Del Regno Reviewed-by: Nícolas F. R. A. Prado Tested-by: Nícolas F. R. A. Prado Link: https://lore.kernel.org/r/20220517104712.24579-2-angelogioacchino.delregno@collabora.com Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-pmic-wrap.c | 60 ++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 27 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c index bf39a64f3ecc..54a5300ab72b 100644 --- a/drivers/soc/mediatek/mtk-pmic-wrap.c +++ b/drivers/soc/mediatek/mtk-pmic-wrap.c @@ -13,6 +13,9 @@ #include #include +#define PWRAP_POLL_DELAY_US 10 +#define PWRAP_POLL_TIMEOUT_US 10000 + #define PWRAP_MT8135_BRIDGE_IORD_ARB_EN 0x4 #define PWRAP_MT8135_BRIDGE_WACS3_EN 0x10 #define PWRAP_MT8135_BRIDGE_INIT_DONE3 0x14 @@ -1241,27 +1244,14 @@ static bool pwrap_is_fsm_idle_and_sync_idle(struct pmic_wrapper *wrp) (val & PWRAP_STATE_SYNC_IDLE0); } -static int pwrap_wait_for_state(struct pmic_wrapper *wrp, - bool (*fp)(struct pmic_wrapper *)) -{ - unsigned long timeout; - - timeout = jiffies + usecs_to_jiffies(10000); - - do { - if (time_after(jiffies, timeout)) - return fp(wrp) ? 0 : -ETIMEDOUT; - if (fp(wrp)) - return 0; - } while (1); -} - static int pwrap_read16(struct pmic_wrapper *wrp, u32 adr, u32 *rdata) { + bool tmp; int ret; u32 val; - ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle); + ret = readx_poll_timeout(pwrap_is_fsm_idle, wrp, tmp, tmp, + PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US); if (ret) { pwrap_leave_fsm_vldclr(wrp); return ret; @@ -1273,7 +1263,8 @@ static int pwrap_read16(struct pmic_wrapper *wrp, u32 adr, u32 *rdata) val = (adr >> 1) << 16; pwrap_writel(wrp, val, PWRAP_WACS2_CMD); - ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_vldclr); + ret = readx_poll_timeout(pwrap_is_fsm_vldclr, wrp, tmp, tmp, + PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US); if (ret) return ret; @@ -1290,11 +1281,14 @@ static int pwrap_read16(struct pmic_wrapper *wrp, u32 adr, u32 *rdata) static int pwrap_read32(struct pmic_wrapper *wrp, u32 adr, u32 *rdata) { + bool tmp; int ret, msb; *rdata = 0; for (msb = 0; msb < 2; msb++) { - ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle); + ret = readx_poll_timeout(pwrap_is_fsm_idle, wrp, tmp, tmp, + PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US); + if (ret) { pwrap_leave_fsm_vldclr(wrp); return ret; @@ -1303,7 +1297,8 @@ static int pwrap_read32(struct pmic_wrapper *wrp, u32 adr, u32 *rdata) pwrap_writel(wrp, ((msb << 30) | (adr << 16)), PWRAP_WACS2_CMD); - ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_vldclr); + ret = readx_poll_timeout(pwrap_is_fsm_vldclr, wrp, tmp, tmp, + PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US); if (ret) return ret; @@ -1323,9 +1318,11 @@ static int pwrap_read(struct pmic_wrapper *wrp, u32 adr, u32 *rdata) static int pwrap_write16(struct pmic_wrapper *wrp, u32 adr, u32 wdata) { + bool tmp; int ret; - ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle); + ret = readx_poll_timeout(pwrap_is_fsm_idle, wrp, tmp, tmp, + PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US); if (ret) { pwrap_leave_fsm_vldclr(wrp); return ret; @@ -1344,10 +1341,12 @@ static int pwrap_write16(struct pmic_wrapper *wrp, u32 adr, u32 wdata) static int pwrap_write32(struct pmic_wrapper *wrp, u32 adr, u32 wdata) { + bool tmp; int ret, msb, rdata; for (msb = 0; msb < 2; msb++) { - ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle); + ret = readx_poll_timeout(pwrap_is_fsm_idle, wrp, tmp, tmp, + PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US); if (ret) { pwrap_leave_fsm_vldclr(wrp); return ret; @@ -1388,6 +1387,7 @@ static int pwrap_regmap_write(void *context, u32 adr, u32 wdata) static int pwrap_reset_spislave(struct pmic_wrapper *wrp) { + bool tmp; int ret, i; pwrap_writel(wrp, 0, PWRAP_HIPRIO_ARB_EN); @@ -1407,7 +1407,8 @@ static int pwrap_reset_spislave(struct pmic_wrapper *wrp) pwrap_writel(wrp, wrp->master->spi_w | PWRAP_MAN_CMD_OP_OUTS, PWRAP_MAN_CMD); - ret = pwrap_wait_for_state(wrp, pwrap_is_sync_idle); + ret = readx_poll_timeout(pwrap_is_sync_idle, wrp, tmp, tmp, + PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US); if (ret) { dev_err(wrp->dev, "%s fail, ret=%d\n", __func__, ret); return ret; @@ -1458,14 +1459,15 @@ static int pwrap_init_sidly(struct pmic_wrapper *wrp) static int pwrap_init_dual_io(struct pmic_wrapper *wrp) { int ret; + bool tmp; u32 rdata; /* Enable dual IO mode */ pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_DIO_EN], 1); /* Check IDLE & INIT_DONE in advance */ - ret = pwrap_wait_for_state(wrp, - pwrap_is_fsm_idle_and_sync_idle); + ret = readx_poll_timeout(pwrap_is_fsm_idle_and_sync_idle, wrp, tmp, tmp, + PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US); if (ret) { dev_err(wrp->dev, "%s fail, ret=%d\n", __func__, ret); return ret; @@ -1570,6 +1572,7 @@ static bool pwrap_is_pmic_cipher_ready(struct pmic_wrapper *wrp) static int pwrap_init_cipher(struct pmic_wrapper *wrp) { int ret; + bool tmp; u32 rdata = 0; pwrap_writel(wrp, 0x1, PWRAP_CIPHER_SWRST); @@ -1624,14 +1627,16 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp) } /* wait for cipher data ready@AP */ - ret = pwrap_wait_for_state(wrp, pwrap_is_cipher_ready); + ret = readx_poll_timeout(pwrap_is_cipher_ready, wrp, tmp, tmp, + PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US); if (ret) { dev_err(wrp->dev, "cipher data ready@AP fail, ret=%d\n", ret); return ret; } /* wait for cipher data ready@PMIC */ - ret = pwrap_wait_for_state(wrp, pwrap_is_pmic_cipher_ready); + ret = readx_poll_timeout(pwrap_is_pmic_cipher_ready, wrp, tmp, tmp, + PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US); if (ret) { dev_err(wrp->dev, "timeout waiting for cipher data ready@PMIC\n"); @@ -1640,7 +1645,8 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp) /* wait for cipher mode idle */ pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_CIPHER_MODE], 0x1); - ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle_and_sync_idle); + ret = readx_poll_timeout(pwrap_is_fsm_idle_and_sync_idle, wrp, tmp, tmp, + PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US); if (ret) { dev_err(wrp->dev, "cipher mode idle fail, ret=%d\n", ret); return ret; -- cgit v1.2.3 From e84187fa6f55caa897ee7d408046907a65e88a78 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 17 May 2022 12:47:09 +0200 Subject: soc: mediatek: pwrap: Switch to devm_platform_ioremap_resource_byname() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to simplify ioremapping resources, instead of calling platform_get_resource_byname() and then devm_ioremap_resource(), simply call devm_platform_ioremap_resource_byname(). Signed-off-by: AngeloGioacchino Del Regno Reviewed-by: Nícolas F. R. A. Prado Tested-by: Nícolas F. R. A. Prado Link: https://lore.kernel.org/r/20220517104712.24579-3-angelogioacchino.delregno@collabora.com Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-pmic-wrap.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c index 54a5300ab72b..852514366f1f 100644 --- a/drivers/soc/mediatek/mtk-pmic-wrap.c +++ b/drivers/soc/mediatek/mtk-pmic-wrap.c @@ -2191,7 +2191,6 @@ static int pwrap_probe(struct platform_device *pdev) struct pmic_wrapper *wrp; struct device_node *np = pdev->dev.of_node; const struct of_device_id *of_slave_id = NULL; - struct resource *res; if (np->child) of_slave_id = of_match_node(of_slave_match_tbl, np->child); @@ -2211,8 +2210,7 @@ static int pwrap_probe(struct platform_device *pdev) wrp->slave = of_slave_id->data; wrp->dev = &pdev->dev; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwrap"); - wrp->base = devm_ioremap_resource(wrp->dev, res); + wrp->base = devm_platform_ioremap_resource_byname(pdev, "pwrap"); if (IS_ERR(wrp->base)) return PTR_ERR(wrp->base); @@ -2226,9 +2224,7 @@ static int pwrap_probe(struct platform_device *pdev) } if (HAS_CAP(wrp->master->caps, PWRAP_CAP_BRIDGE)) { - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "pwrap-bridge"); - wrp->bridge_base = devm_ioremap_resource(wrp->dev, res); + wrp->bridge_base = devm_platform_ioremap_resource_byname(pdev, "pwrap-bridge"); if (IS_ERR(wrp->bridge_base)) return PTR_ERR(wrp->bridge_base); -- cgit v1.2.3 From 56a468b5f6454aae0e32e4c6ead1abde5cc4dd23 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 17 May 2022 12:47:10 +0200 Subject: soc: mediatek: pwrap: Check return value of platform_get_irq() Check for the return value of platform_get_irq(): if no interrupt is specified, it wouldn't make sense to call devm_request_irq(). Signed-off-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20220517104712.24579-4-angelogioacchino.delregno@collabora.com Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-pmic-wrap.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c index 852514366f1f..04b51d21a5b0 100644 --- a/drivers/soc/mediatek/mtk-pmic-wrap.c +++ b/drivers/soc/mediatek/mtk-pmic-wrap.c @@ -2317,6 +2317,11 @@ static int pwrap_probe(struct platform_device *pdev) pwrap_writel(wrp, wrp->master->int1_en_all, PWRAP_INT1_EN); irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = irq; + goto err_out2; + } + ret = devm_request_irq(wrp->dev, irq, pwrap_interrupt, IRQF_TRIGGER_HIGH, "mt-pmic-pwrap", wrp); -- cgit v1.2.3 From 43406cebf06b3519d10c665e6e0228be197b534c Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 17 May 2022 12:47:11 +0200 Subject: soc: mediatek: pwrap: Move IO pointers to new structure In the PMIC Wrapper driver each PMIC has its own regmap configuration and its own pwrap_{read/write}() callbacks, but it's just about either a 32 bits vs 16 bits register, and only one of them uses 32bits regs: this means that the same ops are assigned over and over again to all of the supported PMICs. It is therefore possible to avoid reassigning the same things over and over, reducing the amount of lines, without any impact on human readability of this driver: add a pwrap_slv_regops structure and move the callbacks and regmap_config pointer in there instead. This allows to assign just one pointer to that shared data in the per-pmic struct pwrap_slv_type. Signed-off-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20220517104712.24579-5-angelogioacchino.delregno@collabora.com Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-pmic-wrap.c | 61 +++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 29 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c index 04b51d21a5b0..14f31a9fc792 100644 --- a/drivers/soc/mediatek/mtk-pmic-wrap.c +++ b/drivers/soc/mediatek/mtk-pmic-wrap.c @@ -1143,12 +1143,9 @@ enum pwrap_type { }; struct pmic_wrapper; -struct pwrap_slv_type { - const u32 *dew_regs; - enum pmic_type type; + +struct pwrap_slv_regops { const struct regmap_config *regmap; - /* Flags indicating the capability for the target slave */ - u32 caps; /* * pwrap operations are highly associated with the PMIC types, * so the pointers added increases flexibility allowing determination @@ -1158,6 +1155,14 @@ struct pwrap_slv_type { int (*pwrap_write)(struct pmic_wrapper *wrp, u32 adr, u32 wdata); }; +struct pwrap_slv_type { + const u32 *dew_regs; + enum pmic_type type; + const struct pwrap_slv_regops *regops; + /* Flags indicating the capability for the target slave */ + u32 caps; +}; + struct pmic_wrapper { struct device *dev; void __iomem *base; @@ -1313,7 +1318,7 @@ static int pwrap_read32(struct pmic_wrapper *wrp, u32 adr, u32 *rdata) static int pwrap_read(struct pmic_wrapper *wrp, u32 adr, u32 *rdata) { - return wrp->slave->pwrap_read(wrp, adr, rdata); + return wrp->slave->regops->pwrap_read(wrp, adr, rdata); } static int pwrap_write16(struct pmic_wrapper *wrp, u32 adr, u32 wdata) @@ -1372,7 +1377,7 @@ static int pwrap_write32(struct pmic_wrapper *wrp, u32 adr, u32 wdata) static int pwrap_write(struct pmic_wrapper *wrp, u32 adr, u32 wdata) { - return wrp->slave->pwrap_write(wrp, adr, wdata); + return wrp->slave->regops->pwrap_write(wrp, adr, wdata); } static int pwrap_regmap_read(void *context, u32 adr, u32 *rdata) @@ -1891,69 +1896,67 @@ static const struct regmap_config pwrap_regmap_config32 = { .max_register = 0xffff, }; +static const struct pwrap_slv_regops pwrap_regops16 = { + .pwrap_read = pwrap_read16, + .pwrap_write = pwrap_write16, + .regmap = &pwrap_regmap_config16, +}; + +static const struct pwrap_slv_regops pwrap_regops32 = { + .pwrap_read = pwrap_read32, + .pwrap_write = pwrap_write32, + .regmap = &pwrap_regmap_config32, +}; + static const struct pwrap_slv_type pmic_mt6323 = { .dew_regs = mt6323_regs, .type = PMIC_MT6323, - .regmap = &pwrap_regmap_config16, + .regops = &pwrap_regops16, .caps = PWRAP_SLV_CAP_SPI | PWRAP_SLV_CAP_DUALIO | PWRAP_SLV_CAP_SECURITY, - .pwrap_read = pwrap_read16, - .pwrap_write = pwrap_write16, }; static const struct pwrap_slv_type pmic_mt6351 = { .dew_regs = mt6351_regs, .type = PMIC_MT6351, - .regmap = &pwrap_regmap_config16, + .regops = &pwrap_regops16, .caps = 0, - .pwrap_read = pwrap_read16, - .pwrap_write = pwrap_write16, }; static const struct pwrap_slv_type pmic_mt6357 = { .dew_regs = mt6357_regs, .type = PMIC_MT6357, - .regmap = &pwrap_regmap_config16, + .regops = &pwrap_regops16, .caps = 0, - .pwrap_read = pwrap_read16, - .pwrap_write = pwrap_write16, }; static const struct pwrap_slv_type pmic_mt6358 = { .dew_regs = mt6358_regs, .type = PMIC_MT6358, - .regmap = &pwrap_regmap_config16, + .regops = &pwrap_regops16, .caps = PWRAP_SLV_CAP_SPI | PWRAP_SLV_CAP_DUALIO, - .pwrap_read = pwrap_read16, - .pwrap_write = pwrap_write16, }; static const struct pwrap_slv_type pmic_mt6359 = { .dew_regs = mt6359_regs, .type = PMIC_MT6359, - .regmap = &pwrap_regmap_config16, + .regops = &pwrap_regops16, .caps = PWRAP_SLV_CAP_DUALIO, - .pwrap_read = pwrap_read16, - .pwrap_write = pwrap_write16, }; static const struct pwrap_slv_type pmic_mt6380 = { .dew_regs = NULL, .type = PMIC_MT6380, - .regmap = &pwrap_regmap_config32, + .regops = &pwrap_regops32, .caps = 0, - .pwrap_read = pwrap_read32, - .pwrap_write = pwrap_write32, }; static const struct pwrap_slv_type pmic_mt6397 = { .dew_regs = mt6397_regs, .type = PMIC_MT6397, - .regmap = &pwrap_regmap_config16, + .regops = &pwrap_regops16, .caps = PWRAP_SLV_CAP_SPI | PWRAP_SLV_CAP_DUALIO | PWRAP_SLV_CAP_SECURITY, - .pwrap_read = pwrap_read16, - .pwrap_write = pwrap_write16, }; static const struct of_device_id of_slave_match_tbl[] = { @@ -2328,7 +2331,7 @@ static int pwrap_probe(struct platform_device *pdev) if (ret) goto err_out2; - wrp->regmap = devm_regmap_init(wrp->dev, NULL, wrp, wrp->slave->regmap); + wrp->regmap = devm_regmap_init(wrp->dev, NULL, wrp, wrp->slave->regops->regmap); if (IS_ERR(wrp->regmap)) { ret = PTR_ERR(wrp->regmap); goto err_out2; -- cgit v1.2.3 From f1a95a356daa9790151a5a60535fcdbfc0ca2e9c Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 17 May 2022 12:47:12 +0200 Subject: soc: mediatek: pwrap: Compress of_device_id entries to one line Compress each entry of the of_device_id tables in this driver to one line instead of three, as they fit just fine in a single line. Signed-off-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20220517104712.24579-6-angelogioacchino.delregno@collabora.com Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-pmic-wrap.c | 91 ++++++++++-------------------------- 1 file changed, 25 insertions(+), 66 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c index 14f31a9fc792..d8cb0f833645 100644 --- a/drivers/soc/mediatek/mtk-pmic-wrap.c +++ b/drivers/soc/mediatek/mtk-pmic-wrap.c @@ -1960,33 +1960,18 @@ static const struct pwrap_slv_type pmic_mt6397 = { }; static const struct of_device_id of_slave_match_tbl[] = { - { - .compatible = "mediatek,mt6323", - .data = &pmic_mt6323, - }, { - .compatible = "mediatek,mt6351", - .data = &pmic_mt6351, - }, { - .compatible = "mediatek,mt6357", - .data = &pmic_mt6357, - }, { - .compatible = "mediatek,mt6358", - .data = &pmic_mt6358, - }, { - .compatible = "mediatek,mt6359", - .data = &pmic_mt6359, - }, { - /* The MT6380 PMIC only implements a regulator, so we bind it - * directly instead of using a MFD. - */ - .compatible = "mediatek,mt6380-regulator", - .data = &pmic_mt6380, - }, { - .compatible = "mediatek,mt6397", - .data = &pmic_mt6397, - }, { - /* sentinel */ - } + { .compatible = "mediatek,mt6323", .data = &pmic_mt6323 }, + { .compatible = "mediatek,mt6351", .data = &pmic_mt6351 }, + { .compatible = "mediatek,mt6357", .data = &pmic_mt6357 }, + { .compatible = "mediatek,mt6358", .data = &pmic_mt6358 }, + { .compatible = "mediatek,mt6359", .data = &pmic_mt6359 }, + + /* The MT6380 PMIC only implements a regulator, so we bind it + * directly instead of using a MFD. + */ + { .compatible = "mediatek,mt6380-regulator", .data = &pmic_mt6380 }, + { .compatible = "mediatek,mt6397", .data = &pmic_mt6397 }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, of_slave_match_tbl); @@ -2145,45 +2130,19 @@ static struct pmic_wrapper_type pwrap_mt8186 = { }; static const struct of_device_id of_pwrap_match_tbl[] = { - { - .compatible = "mediatek,mt2701-pwrap", - .data = &pwrap_mt2701, - }, { - .compatible = "mediatek,mt6765-pwrap", - .data = &pwrap_mt6765, - }, { - .compatible = "mediatek,mt6779-pwrap", - .data = &pwrap_mt6779, - }, { - .compatible = "mediatek,mt6797-pwrap", - .data = &pwrap_mt6797, - }, { - .compatible = "mediatek,mt6873-pwrap", - .data = &pwrap_mt6873, - }, { - .compatible = "mediatek,mt7622-pwrap", - .data = &pwrap_mt7622, - }, { - .compatible = "mediatek,mt8135-pwrap", - .data = &pwrap_mt8135, - }, { - .compatible = "mediatek,mt8173-pwrap", - .data = &pwrap_mt8173, - }, { - .compatible = "mediatek,mt8183-pwrap", - .data = &pwrap_mt8183, - }, { - .compatible = "mediatek,mt8186-pwrap", - .data = &pwrap_mt8186, - }, { - .compatible = "mediatek,mt8195-pwrap", - .data = &pwrap_mt8195, - }, { - .compatible = "mediatek,mt8516-pwrap", - .data = &pwrap_mt8516, - }, { - /* sentinel */ - } + { .compatible = "mediatek,mt2701-pwrap", .data = &pwrap_mt2701 }, + { .compatible = "mediatek,mt6765-pwrap", .data = &pwrap_mt6765 }, + { .compatible = "mediatek,mt6779-pwrap", .data = &pwrap_mt6779 }, + { .compatible = "mediatek,mt6797-pwrap", .data = &pwrap_mt6797 }, + { .compatible = "mediatek,mt6873-pwrap", .data = &pwrap_mt6873 }, + { .compatible = "mediatek,mt7622-pwrap", .data = &pwrap_mt7622 }, + { .compatible = "mediatek,mt8135-pwrap", .data = &pwrap_mt8135 }, + { .compatible = "mediatek,mt8173-pwrap", .data = &pwrap_mt8173 }, + { .compatible = "mediatek,mt8183-pwrap", .data = &pwrap_mt8183 }, + { .compatible = "mediatek,mt8186-pwrap", .data = &pwrap_mt8186 }, + { .compatible = "mediatek,mt8195-pwrap", .data = &pwrap_mt8195 }, + { .compatible = "mediatek,mt8516-pwrap", .data = &pwrap_mt8516 }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, of_pwrap_match_tbl); -- cgit v1.2.3 From 2cb41ed0d73d8b15b30c917392d7e97c22866818 Mon Sep 17 00:00:00 2001 From: Rex-BC Chen Date: Wed, 4 May 2022 19:55:00 +0800 Subject: soc: mediatek: devapc: Separate register offsets from mtk_devapc_data The register offsets is the same between MT6779 and MT8186. To reuse the register offsets, we move these register offsets to another structure "mtk_devapc_regs_ofs". Signed-off-by: Rex-BC Chen Reviewed-by: Chun-Kuang Hu Reviewed-by: AngeloGioacchino Del Regno Tested-by: Chen-Yu Tsai Link: https://lore.kernel.org/r/20220504115501.3490-3-rex-bc.chen@mediatek.com Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-devapc.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-devapc.c b/drivers/soc/mediatek/mtk-devapc.c index 7c65ad3d1f8a..f27e22cbb770 100644 --- a/drivers/soc/mediatek/mtk-devapc.c +++ b/drivers/soc/mediatek/mtk-devapc.c @@ -31,10 +31,7 @@ struct mtk_devapc_vio_dbgs { u32 vio_dbg1; }; -struct mtk_devapc_data { - /* numbers of violation index */ - u32 vio_idx_num; - +struct mtk_devapc_regs_ofs { /* reg offset */ u32 vio_mask_offset; u32 vio_sta_offset; @@ -46,6 +43,12 @@ struct mtk_devapc_data { u32 vio_shift_con_offset; }; +struct mtk_devapc_data { + /* numbers of violation index */ + u32 vio_idx_num; + const struct mtk_devapc_regs_ofs *regs_ofs; +}; + struct mtk_devapc_context { struct device *dev; void __iomem *infra_base; @@ -58,7 +61,7 @@ static void clear_vio_status(struct mtk_devapc_context *ctx) void __iomem *reg; int i; - reg = ctx->infra_base + ctx->data->vio_sta_offset; + reg = ctx->infra_base + ctx->data->regs_ofs->vio_sta_offset; for (i = 0; i < VIO_MOD_TO_REG_IND(ctx->data->vio_idx_num) - 1; i++) writel(GENMASK(31, 0), reg + 4 * i); @@ -73,7 +76,7 @@ static void mask_module_irq(struct mtk_devapc_context *ctx, bool mask) u32 val; int i; - reg = ctx->infra_base + ctx->data->vio_mask_offset; + reg = ctx->infra_base + ctx->data->regs_ofs->vio_mask_offset; if (mask) val = GENMASK(31, 0); @@ -116,11 +119,11 @@ static int devapc_sync_vio_dbg(struct mtk_devapc_context *ctx) u32 val; pd_vio_shift_sta_reg = ctx->infra_base + - ctx->data->vio_shift_sta_offset; + ctx->data->regs_ofs->vio_shift_sta_offset; pd_vio_shift_sel_reg = ctx->infra_base + - ctx->data->vio_shift_sel_offset; + ctx->data->regs_ofs->vio_shift_sel_offset; pd_vio_shift_con_reg = ctx->infra_base + - ctx->data->vio_shift_con_offset; + ctx->data->regs_ofs->vio_shift_con_offset; /* Find the minimum shift group which has violation */ val = readl(pd_vio_shift_sta_reg); @@ -161,8 +164,8 @@ static void devapc_extract_vio_dbg(struct mtk_devapc_context *ctx) void __iomem *vio_dbg0_reg; void __iomem *vio_dbg1_reg; - vio_dbg0_reg = ctx->infra_base + ctx->data->vio_dbg0_offset; - vio_dbg1_reg = ctx->infra_base + ctx->data->vio_dbg1_offset; + vio_dbg0_reg = ctx->infra_base + ctx->data->regs_ofs->vio_dbg0_offset; + vio_dbg1_reg = ctx->infra_base + ctx->data->regs_ofs->vio_dbg1_offset; vio_dbgs.vio_dbg0 = readl(vio_dbg0_reg); vio_dbgs.vio_dbg1 = readl(vio_dbg1_reg); @@ -200,7 +203,7 @@ static irqreturn_t devapc_violation_irq(int irq_number, void *data) */ static void start_devapc(struct mtk_devapc_context *ctx) { - writel(BIT(31), ctx->infra_base + ctx->data->apc_con_offset); + writel(BIT(31), ctx->infra_base + ctx->data->regs_ofs->apc_con_offset); mask_module_irq(ctx, false); } @@ -212,11 +215,10 @@ static void stop_devapc(struct mtk_devapc_context *ctx) { mask_module_irq(ctx, true); - writel(BIT(2), ctx->infra_base + ctx->data->apc_con_offset); + writel(BIT(2), ctx->infra_base + ctx->data->regs_ofs->apc_con_offset); } -static const struct mtk_devapc_data devapc_mt6779 = { - .vio_idx_num = 511, +static const struct mtk_devapc_regs_ofs devapc_regs_ofs_mt6779 = { .vio_mask_offset = 0x0, .vio_sta_offset = 0x400, .vio_dbg0_offset = 0x900, @@ -227,6 +229,11 @@ static const struct mtk_devapc_data devapc_mt6779 = { .vio_shift_con_offset = 0xF20, }; +static const struct mtk_devapc_data devapc_mt6779 = { + .vio_idx_num = 511, + .regs_ofs = &devapc_regs_ofs_mt6779, +}; + static const struct of_device_id mtk_devapc_dt_match[] = { { .compatible = "mediatek,mt6779-devapc", -- cgit v1.2.3 From 1c9faace10172e965012e0fb2d2061f95f8ff0e3 Mon Sep 17 00:00:00 2001 From: Rex-BC Chen Date: Wed, 4 May 2022 19:55:01 +0800 Subject: soc: mediatek: devapc: Add support for MT8186 Add devapc data and compatible to support MT8186 devapc. Signed-off-by: Runyang Chen Signed-off-by: Rex-BC Chen Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Chun-Kuang Hu Tested-by: Chen-Yu Tsai Link: https://lore.kernel.org/r/20220504115501.3490-4-rex-bc.chen@mediatek.com Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-devapc.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-devapc.c b/drivers/soc/mediatek/mtk-devapc.c index f27e22cbb770..fc13334db1b1 100644 --- a/drivers/soc/mediatek/mtk-devapc.c +++ b/drivers/soc/mediatek/mtk-devapc.c @@ -234,10 +234,18 @@ static const struct mtk_devapc_data devapc_mt6779 = { .regs_ofs = &devapc_regs_ofs_mt6779, }; +static const struct mtk_devapc_data devapc_mt8186 = { + .vio_idx_num = 519, + .regs_ofs = &devapc_regs_ofs_mt6779, +}; + static const struct of_device_id mtk_devapc_dt_match[] = { { .compatible = "mediatek,mt6779-devapc", .data = &devapc_mt6779, + }, { + .compatible = "mediatek,mt8186-devapc", + .data = &devapc_mt8186, }, { }, }; -- cgit v1.2.3 From 9b656879042e1eb6aaf2306910f346bd6a03e58f Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 21 May 2022 13:10:26 +0200 Subject: soc: bcm: brcmstb: pm: pm-arm: fix typo in comment Spelling mistake (triple letters) in comment. Detected with the help of Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: Florian Fainelli --- drivers/soc/bcm/brcmstb/pm/pm-arm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/bcm/brcmstb/pm/pm-arm.c b/drivers/soc/bcm/brcmstb/pm/pm-arm.c index 3cbb165d6e30..7a07e8f9ae6d 100644 --- a/drivers/soc/bcm/brcmstb/pm/pm-arm.c +++ b/drivers/soc/bcm/brcmstb/pm/pm-arm.c @@ -721,7 +721,7 @@ static int brcmstb_pm_probe(struct platform_device *pdev) ctrl.phy_a_standby_ctrl_offs = ddr_phy_data->phy_a_standby_ctrl_offs; ctrl.phy_b_standby_ctrl_offs = ddr_phy_data->phy_b_standby_ctrl_offs; /* - * Slightly grosss to use the phy ver to get a memc, + * Slightly gross to use the phy ver to get a memc, * offset but that is the only versioned things so far * we can test for. */ -- cgit v1.2.3 From bccceabb92ce8eb78bbf2de08308e2cc2761a2e5 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 8 Jun 2022 15:51:35 +0200 Subject: soc: renesas: r8a779a0-sysc: Fix A2DP1 and A2CV[2357] PDR values The PDR values for the A2DP1 and A2CV[2357] power areas on R-Car V3U are incorrect (copied-and-pasted from A2DP0 and A2CV[0146]). Fix them. Reported-by: Renesas Vietnam via Yoshihiro Shimoda Fixes: 1b4298f000064cc2 ("soc: renesas: r8a779a0-sysc: Add r8a779a0 support") Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/87bc2e70ba4082970cf8c65871beae4be3503189.1654696188.git.geert+renesas@glider.be --- drivers/soc/renesas/r8a779a0-sysc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/r8a779a0-sysc.c b/drivers/soc/renesas/r8a779a0-sysc.c index fdfc857df334..04f1bc322ae7 100644 --- a/drivers/soc/renesas/r8a779a0-sysc.c +++ b/drivers/soc/renesas/r8a779a0-sysc.c @@ -57,11 +57,11 @@ static struct rcar_gen4_sysc_area r8a779a0_areas[] __initdata = { { "a2cv6", R8A779A0_PD_A2CV6, R8A779A0_PD_A3IR }, { "a2cn2", R8A779A0_PD_A2CN2, R8A779A0_PD_A3IR }, { "a2imp23", R8A779A0_PD_A2IMP23, R8A779A0_PD_A3IR }, - { "a2dp1", R8A779A0_PD_A2DP0, R8A779A0_PD_A3IR }, - { "a2cv2", R8A779A0_PD_A2CV0, R8A779A0_PD_A3IR }, - { "a2cv3", R8A779A0_PD_A2CV1, R8A779A0_PD_A3IR }, - { "a2cv5", R8A779A0_PD_A2CV4, R8A779A0_PD_A3IR }, - { "a2cv7", R8A779A0_PD_A2CV6, R8A779A0_PD_A3IR }, + { "a2dp1", R8A779A0_PD_A2DP1, R8A779A0_PD_A3IR }, + { "a2cv2", R8A779A0_PD_A2CV2, R8A779A0_PD_A3IR }, + { "a2cv3", R8A779A0_PD_A2CV3, R8A779A0_PD_A3IR }, + { "a2cv5", R8A779A0_PD_A2CV5, R8A779A0_PD_A3IR }, + { "a2cv7", R8A779A0_PD_A2CV7, R8A779A0_PD_A3IR }, { "a2cn1", R8A779A0_PD_A2CN1, R8A779A0_PD_A3IR }, { "a1cnn0", R8A779A0_PD_A1CNN0, R8A779A0_PD_A2CN0 }, { "a1cnn2", R8A779A0_PD_A1CNN2, R8A779A0_PD_A2CN2 }, -- cgit v1.2.3 From 4f5bc54d28ecd0739c99dc71ed93c2ebb5d9ba32 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 8 Jun 2022 17:14:21 +0200 Subject: soc: renesas: rcar-sysc: Optimize rcar_sysc_area struct sizes Reduce the sizes of the rcar_sysc_area and rcar_gen4_sysc_area structures: - parent is in the range [-1..32] or [-1..64], so s8 is sufficient, - flags needs just 3 bits (for now), so u8 is plenty. This reduces kernel size by: - 288 bytes for an R-Car Gen1+Gen2 kernel (arm32), - 1772 bytes for an R-Car Gen3+Gen4 kernel (arm64). Signed-off-by: Geert Uytterhoeven Tested-by: Wolfram Sang Reviewed-by: Yoshihiro Shimoda Tested-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/cbbc8f464743d636895c048de61aa610d75392de.1654701156.git.geert+renesas@glider.be --- drivers/soc/renesas/rcar-gen4-sysc.h | 4 ++-- drivers/soc/renesas/rcar-sysc.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/rcar-gen4-sysc.h b/drivers/soc/renesas/rcar-gen4-sysc.h index fe2d98254754..388cfa8f8f9f 100644 --- a/drivers/soc/renesas/rcar-gen4-sysc.h +++ b/drivers/soc/renesas/rcar-gen4-sysc.h @@ -25,8 +25,8 @@ struct rcar_gen4_sysc_area { const char *name; u8 pdr; /* PDRn */ - int parent; /* -1 if none */ - unsigned int flags; /* See PD_* */ + s8 parent; /* -1 if none */ + u8 flags; /* See PD_* */ }; /* diff --git a/drivers/soc/renesas/rcar-sysc.h b/drivers/soc/renesas/rcar-sysc.h index 8d861c1cfdf7..266c599a0a9b 100644 --- a/drivers/soc/renesas/rcar-sysc.h +++ b/drivers/soc/renesas/rcar-sysc.h @@ -31,8 +31,8 @@ struct rcar_sysc_area { u16 chan_offs; /* Offset of PWRSR register for this area */ u8 chan_bit; /* Bit in PWR* (except for PWRUP in PWRSR) */ u8 isr_bit; /* Bit in SYSCI*R */ - int parent; /* -1 if none */ - unsigned int flags; /* See PD_* */ + s8 parent; /* -1 if none */ + u8 flags; /* See PD_* */ }; -- cgit v1.2.3 From d18529a4c12f66d83daac78045ea54063bd43257 Mon Sep 17 00:00:00 2001 From: Liang He Date: Thu, 16 Jun 2022 22:49:15 +0800 Subject: soc: amlogic: Fix refcount leak in meson-secure-pwrc.c In meson_secure_pwrc_probe(), there is a refcount leak in one fail path. Signed-off-by: Liang He Acked-by: Martin Blumenstingl Fixes: b3dde5013e13 ("soc: amlogic: Add support for Secure power domains controller") Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://lore.kernel.org/r/20220616144915.3988071-1-windhl@126.com --- drivers/soc/amlogic/meson-secure-pwrc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/amlogic/meson-secure-pwrc.c b/drivers/soc/amlogic/meson-secure-pwrc.c index a10a417a87db..e93518763526 100644 --- a/drivers/soc/amlogic/meson-secure-pwrc.c +++ b/drivers/soc/amlogic/meson-secure-pwrc.c @@ -152,8 +152,10 @@ static int meson_secure_pwrc_probe(struct platform_device *pdev) } pwrc = devm_kzalloc(&pdev->dev, sizeof(*pwrc), GFP_KERNEL); - if (!pwrc) + if (!pwrc) { + of_node_put(sm_np); return -ENOMEM; + } pwrc->fw = meson_sm_get(sm_np); of_node_put(sm_np); -- cgit v1.2.3 From 681a02e9500073cd8b9c25a04f06166254b5a879 Mon Sep 17 00:00:00 2001 From: Roger Lu Date: Mon, 16 May 2022 08:43:07 +0800 Subject: soc: mediatek: SVS: introduce MTK SVS engine The Smart Voltage Scaling(SVS) engine is a piece of hardware which calculates suitable SVS bank voltages to OPP voltage table. Then, DVFS driver could apply those SVS bank voltages to PMIC/Buck when receiving OPP_EVENT_ADJUST_VOLTAGE. Signed-off-by: Roger Lu Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Kevin Hilman Link: https://lore.kernel.org/r/20220516004311.18358-4-roger.lu@mediatek.com Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/Kconfig | 10 + drivers/soc/mediatek/Makefile | 1 + drivers/soc/mediatek/mtk-svs.c | 1417 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1428 insertions(+) create mode 100644 drivers/soc/mediatek/mtk-svs.c (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig index fdd8bc08569e..3c3eedea35f7 100644 --- a/drivers/soc/mediatek/Kconfig +++ b/drivers/soc/mediatek/Kconfig @@ -73,4 +73,14 @@ config MTK_MMSYS Say yes here to add support for the MediaTek Multimedia Subsystem (MMSYS). +config MTK_SVS + tristate "MediaTek Smart Voltage Scaling(SVS)" + depends on MTK_EFUSE && NVMEM + help + The Smart Voltage Scaling(SVS) engine is a piece of hardware + which has several controllers(banks) for calculating suitable + voltage to different power domains(CPU/GPU/CCI) according to + chip process corner, temperatures and other factors. Then DVFS + driver could apply SVS bank voltage to PMIC/Buck. + endmenu diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile index 90270f8114ed..0e9e703c931a 100644 --- a/drivers/soc/mediatek/Makefile +++ b/drivers/soc/mediatek/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o obj-$(CONFIG_MTK_SCPSYS_PM_DOMAINS) += mtk-pm-domains.o obj-$(CONFIG_MTK_MMSYS) += mtk-mmsys.o obj-$(CONFIG_MTK_MMSYS) += mtk-mutex.o +obj-$(CONFIG_MTK_SVS) += mtk-svs.o diff --git a/drivers/soc/mediatek/mtk-svs.c b/drivers/soc/mediatek/mtk-svs.c new file mode 100644 index 000000000000..cc23a701159e --- /dev/null +++ b/drivers/soc/mediatek/mtk-svs.c @@ -0,0 +1,1417 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* svs bank 1-line software id */ +#define SVSB_CPU_LITTLE BIT(0) +#define SVSB_CPU_BIG BIT(1) +#define SVSB_CCI BIT(2) +#define SVSB_GPU BIT(3) + +/* svs bank mode support */ +#define SVSB_MODE_ALL_DISABLE 0 +#define SVSB_MODE_INIT01 BIT(1) +#define SVSB_MODE_INIT02 BIT(2) + +/* svs bank volt flags */ +#define SVSB_INIT01_PD_REQ BIT(0) +#define SVSB_INIT01_VOLT_IGNORE BIT(1) +#define SVSB_INIT01_VOLT_INC_ONLY BIT(2) + +/* svs bank register common configuration */ +#define SVSB_DET_MAX 0xffff +#define SVSB_DET_WINDOW 0xa28 +#define SVSB_DTHI 0x1 +#define SVSB_DTLO 0xfe +#define SVSB_EN_INIT01 0x1 +#define SVSB_EN_INIT02 0x5 +#define SVSB_EN_OFF 0x0 +#define SVSB_INTEN_INIT0x 0x00005f01 +#define SVSB_INTSTS_CLEAN 0x00ffffff +#define SVSB_INTSTS_COMPLETE 0x1 +#define SVSB_RUNCONFIG_DEFAULT 0x80000000 + +/* svs bank related setting */ +#define MAX_OPP_ENTRIES 16 +#define SVSB_DC_SIGNED_BIT BIT(15) +#define SVSB_DET_CLK_EN BIT(31) + +static DEFINE_SPINLOCK(svs_lock); + +/** + * enum svsb_phase - svs bank phase enumeration + * @SVSB_PHASE_ERROR: svs bank encounters unexpected condition + * @SVSB_PHASE_INIT01: svs bank basic init for data calibration + * @SVSB_PHASE_INIT02: svs bank can provide voltages to opp table + * @SVSB_PHASE_MAX: total number of svs bank phase (debug purpose) + * + * Each svs bank has its own independent phase and we enable each svs bank by + * running their phase orderly. However, when svs bank encounters unexpected + * condition, it will fire an irq (PHASE_ERROR) to inform svs software. + * + * svs bank general phase-enabled order: + * SVSB_PHASE_INIT01 -> SVSB_PHASE_INIT02 + */ +enum svsb_phase { + SVSB_PHASE_ERROR = 0, + SVSB_PHASE_INIT01, + SVSB_PHASE_INIT02, + SVSB_PHASE_MAX, +}; + +enum svs_reg_index { + DESCHAR = 0, + TEMPCHAR, + DETCHAR, + AGECHAR, + DCCONFIG, + AGECONFIG, + FREQPCT30, + FREQPCT74, + LIMITVALS, + VBOOT, + DETWINDOW, + CONFIG, + TSCALCS, + RUNCONFIG, + SVSEN, + INIT2VALS, + DCVALUES, + AGEVALUES, + VOP30, + VOP74, + TEMP, + INTSTS, + INTSTSRAW, + INTEN, + CHKINT, + CHKSHIFT, + STATUS, + VDESIGN30, + VDESIGN74, + DVT30, + DVT74, + AGECOUNT, + SMSTATE0, + SMSTATE1, + CTL0, + DESDETSEC, + TEMPAGESEC, + CTRLSPARE0, + CTRLSPARE1, + CTRLSPARE2, + CTRLSPARE3, + CORESEL, + THERMINTST, + INTST, + THSTAGE0ST, + THSTAGE1ST, + THSTAGE2ST, + THAHBST0, + THAHBST1, + SPARE0, + SPARE1, + SPARE2, + SPARE3, + THSLPEVEB, + SVS_REG_MAX, +}; + +static const u32 svs_regs_v2[] = { + [DESCHAR] = 0xc00, + [TEMPCHAR] = 0xc04, + [DETCHAR] = 0xc08, + [AGECHAR] = 0xc0c, + [DCCONFIG] = 0xc10, + [AGECONFIG] = 0xc14, + [FREQPCT30] = 0xc18, + [FREQPCT74] = 0xc1c, + [LIMITVALS] = 0xc20, + [VBOOT] = 0xc24, + [DETWINDOW] = 0xc28, + [CONFIG] = 0xc2c, + [TSCALCS] = 0xc30, + [RUNCONFIG] = 0xc34, + [SVSEN] = 0xc38, + [INIT2VALS] = 0xc3c, + [DCVALUES] = 0xc40, + [AGEVALUES] = 0xc44, + [VOP30] = 0xc48, + [VOP74] = 0xc4c, + [TEMP] = 0xc50, + [INTSTS] = 0xc54, + [INTSTSRAW] = 0xc58, + [INTEN] = 0xc5c, + [CHKINT] = 0xc60, + [CHKSHIFT] = 0xc64, + [STATUS] = 0xc68, + [VDESIGN30] = 0xc6c, + [VDESIGN74] = 0xc70, + [DVT30] = 0xc74, + [DVT74] = 0xc78, + [AGECOUNT] = 0xc7c, + [SMSTATE0] = 0xc80, + [SMSTATE1] = 0xc84, + [CTL0] = 0xc88, + [DESDETSEC] = 0xce0, + [TEMPAGESEC] = 0xce4, + [CTRLSPARE0] = 0xcf0, + [CTRLSPARE1] = 0xcf4, + [CTRLSPARE2] = 0xcf8, + [CTRLSPARE3] = 0xcfc, + [CORESEL] = 0xf00, + [THERMINTST] = 0xf04, + [INTST] = 0xf08, + [THSTAGE0ST] = 0xf0c, + [THSTAGE1ST] = 0xf10, + [THSTAGE2ST] = 0xf14, + [THAHBST0] = 0xf18, + [THAHBST1] = 0xf1c, + [SPARE0] = 0xf20, + [SPARE1] = 0xf24, + [SPARE2] = 0xf28, + [SPARE3] = 0xf2c, + [THSLPEVEB] = 0xf30, +}; + +/** + * struct svs_platform - svs platform control + * @name: svs platform name + * @base: svs platform register base + * @dev: svs platform device + * @main_clk: main clock for svs bank + * @pbank: svs bank pointer needing to be protected by spin_lock section + * @banks: svs banks that svs platform supports + * @efuse_parsing: svs platform efuse parsing function pointer + * @probe: svs platform probe function pointer + * @irqflags: svs platform irq settings flags + * @efuse_max: total number of svs efuse + * @regs: svs platform registers map + * @bank_max: total number of svs banks + * @efuse: svs efuse data received from NVMEM framework + */ +struct svs_platform { + char *name; + void __iomem *base; + struct device *dev; + struct clk *main_clk; + struct svs_bank *pbank; + struct svs_bank *banks; + bool (*efuse_parsing)(struct svs_platform *svsp); + int (*probe)(struct svs_platform *svsp); + unsigned long irqflags; + size_t efuse_max; + const u32 *regs; + u32 bank_max; + u32 *efuse; +}; + +struct svs_platform_data { + char *name; + struct svs_bank *banks; + bool (*efuse_parsing)(struct svs_platform *svsp); + int (*probe)(struct svs_platform *svsp); + unsigned long irqflags; + const u32 *regs; + u32 bank_max; +}; + +/** + * struct svs_bank - svs bank representation + * @dev: bank device + * @opp_dev: device for opp table/buck control + * @init_completion: the timeout completion for bank init + * @buck: regulator used by opp_dev + * @lock: mutex lock to protect voltage update process + * @set_freq_pct: function pointer to set bank frequency percent table + * @get_volts: function pointer to get bank voltages + * @name: bank name + * @buck_name: regulator name + * @phase: bank current phase + * @volt_od: bank voltage overdrive + * @pm_runtime_enabled_count: bank pm runtime enabled count + * @mode_support: bank mode support. + * @freq_base: reference frequency for bank init + * @vboot: voltage request for bank init01 only + * @opp_dfreq: default opp frequency table + * @opp_dvolt: default opp voltage table + * @freq_pct: frequency percent table for bank init + * @volt: bank voltage table + * @volt_step: bank voltage step + * @volt_base: bank voltage base + * @volt_flags: bank voltage flags + * @vmax: bank voltage maximum + * @vmin: bank voltage minimum + * @age_config: bank age configuration + * @age_voffset_in: bank age voltage offset + * @dc_config: bank dc configuration + * @dc_voffset_in: bank dc voltage offset + * @dvt_fixed: bank dvt fixed value + * @vco: bank VCO value + * @chk_shift: bank chicken shift + * @core_sel: bank selection + * @opp_count: bank opp count + * @int_st: bank interrupt identification + * @sw_id: bank software identification + * @cpu_id: cpu core id for SVS CPU bank use only + * @ctl0: TS-x selection + * @bdes: svs efuse data + * @mdes: svs efuse data + * @mtdes: svs efuse data + * @dcbdet: svs efuse data + * @dcmdet: svs efuse data + * + * Svs bank will generate suitalbe voltages by below general math equation + * and provide these voltages to opp voltage table. + * + * opp_volt[i] = (volt[i] * volt_step) + volt_base; + */ +struct svs_bank { + struct device *dev; + struct device *opp_dev; + struct completion init_completion; + struct regulator *buck; + struct mutex lock; /* lock to protect voltage update process */ + void (*set_freq_pct)(struct svs_platform *svsp); + void (*get_volts)(struct svs_platform *svsp); + char *name; + char *buck_name; + enum svsb_phase phase; + s32 volt_od; + u32 pm_runtime_enabled_count; + u32 mode_support; + u32 freq_base; + u32 vboot; + u32 opp_dfreq[MAX_OPP_ENTRIES]; + u32 opp_dvolt[MAX_OPP_ENTRIES]; + u32 freq_pct[MAX_OPP_ENTRIES]; + u32 volt[MAX_OPP_ENTRIES]; + u32 volt_step; + u32 volt_base; + u32 volt_flags; + u32 vmax; + u32 vmin; + u32 age_config; + u32 age_voffset_in; + u32 dc_config; + u32 dc_voffset_in; + u32 dvt_fixed; + u32 vco; + u32 chk_shift; + u32 core_sel; + u32 opp_count; + u32 int_st; + u32 sw_id; + u32 cpu_id; + u32 ctl0; + u32 bdes; + u32 mdes; + u32 mtdes; + u32 dcbdet; + u32 dcmdet; +}; + +static u32 percent(u32 numerator, u32 denominator) +{ + /* If not divide 1000, "numerator * 100" will have data overflow. */ + numerator /= 1000; + denominator /= 1000; + + return DIV_ROUND_UP(numerator * 100, denominator); +} + +static u32 svs_readl_relaxed(struct svs_platform *svsp, enum svs_reg_index rg_i) +{ + return readl_relaxed(svsp->base + svsp->regs[rg_i]); +} + +static void svs_writel_relaxed(struct svs_platform *svsp, u32 val, + enum svs_reg_index rg_i) +{ + writel_relaxed(val, svsp->base + svsp->regs[rg_i]); +} + +static void svs_switch_bank(struct svs_platform *svsp) +{ + struct svs_bank *svsb = svsp->pbank; + + svs_writel_relaxed(svsp, svsb->core_sel, CORESEL); +} + +static u32 svs_bank_volt_to_opp_volt(u32 svsb_volt, u32 svsb_volt_step, + u32 svsb_volt_base) +{ + return (svsb_volt * svsb_volt_step) + svsb_volt_base; +} + +static int svs_adjust_pm_opp_volts(struct svs_bank *svsb) +{ + int ret = -EPERM; + u32 i, svsb_volt, opp_volt; + + mutex_lock(&svsb->lock); + + /* vmin <= svsb_volt (opp_volt) <= default opp voltage */ + for (i = 0; i < svsb->opp_count; i++) { + switch (svsb->phase) { + case SVSB_PHASE_ERROR: + opp_volt = svsb->opp_dvolt[i]; + break; + case SVSB_PHASE_INIT01: + /* do nothing */ + goto unlock_mutex; + case SVSB_PHASE_INIT02: + svsb_volt = max(svsb->volt[i], svsb->vmin); + opp_volt = svs_bank_volt_to_opp_volt(svsb_volt, + svsb->volt_step, + svsb->volt_base); + break; + default: + dev_err(svsb->dev, "unknown phase: %u\n", svsb->phase); + ret = -EINVAL; + goto unlock_mutex; + } + + opp_volt = min(opp_volt, svsb->opp_dvolt[i]); + ret = dev_pm_opp_adjust_voltage(svsb->opp_dev, + svsb->opp_dfreq[i], + opp_volt, opp_volt, + svsb->opp_dvolt[i]); + if (ret) { + dev_err(svsb->dev, "set %uuV fail: %d\n", + opp_volt, ret); + goto unlock_mutex; + } + } + +unlock_mutex: + mutex_unlock(&svsb->lock); + + return ret; +} + +static u32 interpolate(u32 f0, u32 f1, u32 v0, u32 v1, u32 fx) +{ + u32 vx; + + if (v0 == v1 || f0 == f1) + return v0; + + /* *100 to have decimal fraction factor */ + vx = (v0 * 100) - ((((v0 - v1) * 100) / (f0 - f1)) * (f0 - fx)); + + return DIV_ROUND_UP(vx, 100); +} + +static void svs_get_bank_volts_v2(struct svs_platform *svsp) +{ + struct svs_bank *svsb = svsp->pbank; + u32 temp, i; + + temp = svs_readl_relaxed(svsp, VOP74); + svsb->volt[14] = (temp >> 24) & GENMASK(7, 0); + svsb->volt[12] = (temp >> 16) & GENMASK(7, 0); + svsb->volt[10] = (temp >> 8) & GENMASK(7, 0); + svsb->volt[8] = (temp & GENMASK(7, 0)); + + temp = svs_readl_relaxed(svsp, VOP30); + svsb->volt[6] = (temp >> 24) & GENMASK(7, 0); + svsb->volt[4] = (temp >> 16) & GENMASK(7, 0); + svsb->volt[2] = (temp >> 8) & GENMASK(7, 0); + svsb->volt[0] = (temp & GENMASK(7, 0)); + + for (i = 0; i <= 12; i += 2) + svsb->volt[i + 1] = interpolate(svsb->freq_pct[i], + svsb->freq_pct[i + 2], + svsb->volt[i], + svsb->volt[i + 2], + svsb->freq_pct[i + 1]); + + svsb->volt[15] = interpolate(svsb->freq_pct[12], + svsb->freq_pct[14], + svsb->volt[12], + svsb->volt[14], + svsb->freq_pct[15]); + + for (i = 0; i < svsb->opp_count; i++) + svsb->volt[i] += svsb->volt_od; +} + +static void svs_set_bank_freq_pct_v2(struct svs_platform *svsp) +{ + struct svs_bank *svsb = svsp->pbank; + + svs_writel_relaxed(svsp, + (svsb->freq_pct[14] << 24) | + (svsb->freq_pct[12] << 16) | + (svsb->freq_pct[10] << 8) | + svsb->freq_pct[8], + FREQPCT74); + + svs_writel_relaxed(svsp, + (svsb->freq_pct[6] << 24) | + (svsb->freq_pct[4] << 16) | + (svsb->freq_pct[2] << 8) | + svsb->freq_pct[0], + FREQPCT30); +} + +static void svs_set_bank_phase(struct svs_platform *svsp, + enum svsb_phase target_phase) +{ + struct svs_bank *svsb = svsp->pbank; + u32 des_char, temp_char, det_char, limit_vals, init2vals; + + svs_switch_bank(svsp); + + des_char = (svsb->bdes << 8) | svsb->mdes; + svs_writel_relaxed(svsp, des_char, DESCHAR); + + temp_char = (svsb->vco << 16) | (svsb->mtdes << 8) | svsb->dvt_fixed; + svs_writel_relaxed(svsp, temp_char, TEMPCHAR); + + det_char = (svsb->dcbdet << 8) | svsb->dcmdet; + svs_writel_relaxed(svsp, det_char, DETCHAR); + + svs_writel_relaxed(svsp, svsb->dc_config, DCCONFIG); + svs_writel_relaxed(svsp, svsb->age_config, AGECONFIG); + svs_writel_relaxed(svsp, SVSB_RUNCONFIG_DEFAULT, RUNCONFIG); + + svsb->set_freq_pct(svsp); + + limit_vals = (svsb->vmax << 24) | (svsb->vmin << 16) | + (SVSB_DTHI << 8) | SVSB_DTLO; + svs_writel_relaxed(svsp, limit_vals, LIMITVALS); + + svs_writel_relaxed(svsp, SVSB_DET_WINDOW, DETWINDOW); + svs_writel_relaxed(svsp, SVSB_DET_MAX, CONFIG); + svs_writel_relaxed(svsp, svsb->chk_shift, CHKSHIFT); + svs_writel_relaxed(svsp, svsb->ctl0, CTL0); + svs_writel_relaxed(svsp, SVSB_INTSTS_CLEAN, INTSTS); + + switch (target_phase) { + case SVSB_PHASE_INIT01: + svs_writel_relaxed(svsp, svsb->vboot, VBOOT); + svs_writel_relaxed(svsp, SVSB_INTEN_INIT0x, INTEN); + svs_writel_relaxed(svsp, SVSB_EN_INIT01, SVSEN); + break; + case SVSB_PHASE_INIT02: + svs_writel_relaxed(svsp, SVSB_INTEN_INIT0x, INTEN); + init2vals = (svsb->age_voffset_in << 16) | svsb->dc_voffset_in; + svs_writel_relaxed(svsp, init2vals, INIT2VALS); + svs_writel_relaxed(svsp, SVSB_EN_INIT02, SVSEN); + break; + default: + dev_err(svsb->dev, "requested unknown target phase: %u\n", + target_phase); + break; + } +} + +static inline void svs_error_isr_handler(struct svs_platform *svsp) +{ + struct svs_bank *svsb = svsp->pbank; + + dev_err(svsb->dev, "%s: CORESEL = 0x%08x\n", + __func__, svs_readl_relaxed(svsp, CORESEL)); + dev_err(svsb->dev, "SVSEN = 0x%08x, INTSTS = 0x%08x\n", + svs_readl_relaxed(svsp, SVSEN), + svs_readl_relaxed(svsp, INTSTS)); + dev_err(svsb->dev, "SMSTATE0 = 0x%08x, SMSTATE1 = 0x%08x\n", + svs_readl_relaxed(svsp, SMSTATE0), + svs_readl_relaxed(svsp, SMSTATE1)); + + svsb->phase = SVSB_PHASE_ERROR; + svs_writel_relaxed(svsp, SVSB_EN_OFF, SVSEN); + svs_writel_relaxed(svsp, SVSB_INTSTS_CLEAN, INTSTS); +} + +static inline void svs_init01_isr_handler(struct svs_platform *svsp) +{ + struct svs_bank *svsb = svsp->pbank; + + dev_info(svsb->dev, "%s: VDN74~30:0x%08x~0x%08x, DC:0x%08x\n", + __func__, svs_readl_relaxed(svsp, VDESIGN74), + svs_readl_relaxed(svsp, VDESIGN30), + svs_readl_relaxed(svsp, DCVALUES)); + + svsb->phase = SVSB_PHASE_INIT01; + svsb->dc_voffset_in = ~(svs_readl_relaxed(svsp, DCVALUES) & + GENMASK(15, 0)) + 1; + if (svsb->volt_flags & SVSB_INIT01_VOLT_IGNORE || + (svsb->dc_voffset_in & SVSB_DC_SIGNED_BIT && + svsb->volt_flags & SVSB_INIT01_VOLT_INC_ONLY)) + svsb->dc_voffset_in = 0; + + svsb->age_voffset_in = svs_readl_relaxed(svsp, AGEVALUES) & + GENMASK(15, 0); + + svs_writel_relaxed(svsp, SVSB_EN_OFF, SVSEN); + svs_writel_relaxed(svsp, SVSB_INTSTS_COMPLETE, INTSTS); + svsb->core_sel &= ~SVSB_DET_CLK_EN; +} + +static inline void svs_init02_isr_handler(struct svs_platform *svsp) +{ + struct svs_bank *svsb = svsp->pbank; + + dev_info(svsb->dev, "%s: VOP74~30:0x%08x~0x%08x, DC:0x%08x\n", + __func__, svs_readl_relaxed(svsp, VOP74), + svs_readl_relaxed(svsp, VOP30), + svs_readl_relaxed(svsp, DCVALUES)); + + svsb->phase = SVSB_PHASE_INIT02; + svsb->get_volts(svsp); + + svs_writel_relaxed(svsp, SVSB_EN_OFF, SVSEN); + svs_writel_relaxed(svsp, SVSB_INTSTS_COMPLETE, INTSTS); +} + +static irqreturn_t svs_isr(int irq, void *data) +{ + struct svs_platform *svsp = data; + struct svs_bank *svsb = NULL; + unsigned long flags; + u32 idx, int_sts, svs_en; + + for (idx = 0; idx < svsp->bank_max; idx++) { + svsb = &svsp->banks[idx]; + WARN(!svsb, "%s: svsb(%s) is null", __func__, svsb->name); + + spin_lock_irqsave(&svs_lock, flags); + svsp->pbank = svsb; + + /* Find out which svs bank fires interrupt */ + if (svsb->int_st & svs_readl_relaxed(svsp, INTST)) { + spin_unlock_irqrestore(&svs_lock, flags); + continue; + } + + svs_switch_bank(svsp); + int_sts = svs_readl_relaxed(svsp, INTSTS); + svs_en = svs_readl_relaxed(svsp, SVSEN); + + if (int_sts == SVSB_INTSTS_COMPLETE && + svs_en == SVSB_EN_INIT01) + svs_init01_isr_handler(svsp); + else if (int_sts == SVSB_INTSTS_COMPLETE && + svs_en == SVSB_EN_INIT02) + svs_init02_isr_handler(svsp); + else + svs_error_isr_handler(svsp); + + spin_unlock_irqrestore(&svs_lock, flags); + break; + } + + svs_adjust_pm_opp_volts(svsb); + + if (svsb->phase == SVSB_PHASE_INIT01 || + svsb->phase == SVSB_PHASE_INIT02) + complete(&svsb->init_completion); + + return IRQ_HANDLED; +} + +static int svs_init01(struct svs_platform *svsp) +{ + struct svs_bank *svsb; + unsigned long flags, time_left; + bool search_done; + int ret = 0, r; + u32 opp_freq, opp_vboot, buck_volt, idx, i; + + /* Keep CPUs' core power on for svs_init01 initialization */ + cpuidle_pause_and_lock(); + + /* Svs bank init01 preparation - power enable */ + for (idx = 0; idx < svsp->bank_max; idx++) { + svsb = &svsp->banks[idx]; + + if (!(svsb->mode_support & SVSB_MODE_INIT01)) + continue; + + ret = regulator_enable(svsb->buck); + if (ret) { + dev_err(svsb->dev, "%s enable fail: %d\n", + svsb->buck_name, ret); + goto svs_init01_resume_cpuidle; + } + + /* Some buck doesn't support mode change. Show fail msg only */ + ret = regulator_set_mode(svsb->buck, REGULATOR_MODE_FAST); + if (ret) + dev_notice(svsb->dev, "set fast mode fail: %d\n", ret); + + if (svsb->volt_flags & SVSB_INIT01_PD_REQ) { + if (!pm_runtime_enabled(svsb->opp_dev)) { + pm_runtime_enable(svsb->opp_dev); + svsb->pm_runtime_enabled_count++; + } + + ret = pm_runtime_get_sync(svsb->opp_dev); + if (ret < 0) { + dev_err(svsb->dev, "mtcmos on fail: %d\n", ret); + goto svs_init01_resume_cpuidle; + } + } + } + + /* + * Svs bank init01 preparation - vboot voltage adjustment + * Sometimes two svs banks use the same buck. Therefore, + * we have to set each svs bank to target voltage(vboot) first. + */ + for (idx = 0; idx < svsp->bank_max; idx++) { + svsb = &svsp->banks[idx]; + + if (!(svsb->mode_support & SVSB_MODE_INIT01)) + continue; + + /* + * Find the fastest freq that can be run at vboot and + * fix to that freq until svs_init01 is done. + */ + search_done = false; + opp_vboot = svs_bank_volt_to_opp_volt(svsb->vboot, + svsb->volt_step, + svsb->volt_base); + + for (i = 0; i < svsb->opp_count; i++) { + opp_freq = svsb->opp_dfreq[i]; + if (!search_done && svsb->opp_dvolt[i] <= opp_vboot) { + ret = dev_pm_opp_adjust_voltage(svsb->opp_dev, + opp_freq, + opp_vboot, + opp_vboot, + opp_vboot); + if (ret) { + dev_err(svsb->dev, + "set opp %uuV vboot fail: %d\n", + opp_vboot, ret); + goto svs_init01_finish; + } + + search_done = true; + } else { + ret = dev_pm_opp_disable(svsb->opp_dev, + svsb->opp_dfreq[i]); + if (ret) { + dev_err(svsb->dev, + "opp %uHz disable fail: %d\n", + svsb->opp_dfreq[i], ret); + goto svs_init01_finish; + } + } + } + } + + /* Svs bank init01 begins */ + for (idx = 0; idx < svsp->bank_max; idx++) { + svsb = &svsp->banks[idx]; + + if (!(svsb->mode_support & SVSB_MODE_INIT01)) + continue; + + opp_vboot = svs_bank_volt_to_opp_volt(svsb->vboot, + svsb->volt_step, + svsb->volt_base); + + buck_volt = regulator_get_voltage(svsb->buck); + if (buck_volt != opp_vboot) { + dev_err(svsb->dev, + "buck voltage: %uuV, expected vboot: %uuV\n", + buck_volt, opp_vboot); + ret = -EPERM; + goto svs_init01_finish; + } + + spin_lock_irqsave(&svs_lock, flags); + svsp->pbank = svsb; + svs_set_bank_phase(svsp, SVSB_PHASE_INIT01); + spin_unlock_irqrestore(&svs_lock, flags); + + time_left = wait_for_completion_timeout(&svsb->init_completion, + msecs_to_jiffies(5000)); + if (!time_left) { + dev_err(svsb->dev, "init01 completion timeout\n"); + ret = -EBUSY; + goto svs_init01_finish; + } + } + +svs_init01_finish: + for (idx = 0; idx < svsp->bank_max; idx++) { + svsb = &svsp->banks[idx]; + + if (!(svsb->mode_support & SVSB_MODE_INIT01)) + continue; + + for (i = 0; i < svsb->opp_count; i++) { + r = dev_pm_opp_enable(svsb->opp_dev, + svsb->opp_dfreq[i]); + if (r) + dev_err(svsb->dev, "opp %uHz enable fail: %d\n", + svsb->opp_dfreq[i], r); + } + + if (svsb->volt_flags & SVSB_INIT01_PD_REQ) { + r = pm_runtime_put_sync(svsb->opp_dev); + if (r) + dev_err(svsb->dev, "mtcmos off fail: %d\n", r); + + if (svsb->pm_runtime_enabled_count > 0) { + pm_runtime_disable(svsb->opp_dev); + svsb->pm_runtime_enabled_count--; + } + } + + r = regulator_set_mode(svsb->buck, REGULATOR_MODE_NORMAL); + if (r) + dev_notice(svsb->dev, "set normal mode fail: %d\n", r); + + r = regulator_disable(svsb->buck); + if (r) + dev_err(svsb->dev, "%s disable fail: %d\n", + svsb->buck_name, r); + } + +svs_init01_resume_cpuidle: + cpuidle_resume_and_unlock(); + + return ret; +} + +static int svs_init02(struct svs_platform *svsp) +{ + struct svs_bank *svsb; + unsigned long flags, time_left; + u32 idx; + + for (idx = 0; idx < svsp->bank_max; idx++) { + svsb = &svsp->banks[idx]; + + if (!(svsb->mode_support & SVSB_MODE_INIT02)) + continue; + + reinit_completion(&svsb->init_completion); + spin_lock_irqsave(&svs_lock, flags); + svsp->pbank = svsb; + svs_set_bank_phase(svsp, SVSB_PHASE_INIT02); + spin_unlock_irqrestore(&svs_lock, flags); + + time_left = wait_for_completion_timeout(&svsb->init_completion, + msecs_to_jiffies(5000)); + if (!time_left) { + dev_err(svsb->dev, "init02 completion timeout\n"); + return -EBUSY; + } + } + + return 0; +} + +static int svs_start(struct svs_platform *svsp) +{ + int ret; + + ret = svs_init01(svsp); + if (ret) + return ret; + + ret = svs_init02(svsp); + if (ret) + return ret; + + return 0; +} + +static int svs_suspend(struct device *dev) +{ + struct svs_platform *svsp = dev_get_drvdata(dev); + struct svs_bank *svsb; + unsigned long flags; + u32 idx; + + for (idx = 0; idx < svsp->bank_max; idx++) { + svsb = &svsp->banks[idx]; + + /* This might wait for svs_isr() process */ + spin_lock_irqsave(&svs_lock, flags); + svsp->pbank = svsb; + svs_switch_bank(svsp); + svs_writel_relaxed(svsp, SVSB_EN_OFF, SVSEN); + svs_writel_relaxed(svsp, SVSB_INTSTS_CLEAN, INTSTS); + spin_unlock_irqrestore(&svs_lock, flags); + + svsb->phase = SVSB_PHASE_ERROR; + svs_adjust_pm_opp_volts(svsb); + } + + clk_disable_unprepare(svsp->main_clk); + + return 0; +} + +static int svs_resume(struct device *dev) +{ + struct svs_platform *svsp = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(svsp->main_clk); + if (ret) { + dev_err(svsp->dev, "cannot enable main_clk, disable svs\n"); + return ret; + } + + ret = svs_init02(svsp); + if (ret) + return ret; + + return 0; +} + +static int svs_bank_resource_setup(struct svs_platform *svsp) +{ + struct svs_bank *svsb; + struct dev_pm_opp *opp; + unsigned long freq; + int count, ret; + u32 idx, i; + + dev_set_drvdata(svsp->dev, svsp); + + for (idx = 0; idx < svsp->bank_max; idx++) { + svsb = &svsp->banks[idx]; + + switch (svsb->sw_id) { + case SVSB_CPU_LITTLE: + svsb->name = "SVSB_CPU_LITTLE"; + break; + case SVSB_CPU_BIG: + svsb->name = "SVSB_CPU_BIG"; + break; + case SVSB_CCI: + svsb->name = "SVSB_CCI"; + break; + case SVSB_GPU: + svsb->name = "SVSB_GPU"; + break; + default: + dev_err(svsb->dev, "unknown sw_id: %u\n", svsb->sw_id); + return -EINVAL; + } + + svsb->dev = devm_kzalloc(svsp->dev, sizeof(*svsb->dev), + GFP_KERNEL); + if (!svsb->dev) + return -ENOMEM; + + ret = dev_set_name(svsb->dev, "%s", svsb->name); + if (ret) + return ret; + + dev_set_drvdata(svsb->dev, svsp); + + ret = dev_pm_opp_of_add_table(svsb->opp_dev); + if (ret) { + dev_err(svsb->dev, "add opp table fail: %d\n", ret); + return ret; + } + + mutex_init(&svsb->lock); + init_completion(&svsb->init_completion); + + if (svsb->mode_support & SVSB_MODE_INIT01) { + svsb->buck = devm_regulator_get_optional(svsb->opp_dev, + svsb->buck_name); + if (IS_ERR(svsb->buck)) { + dev_err(svsb->dev, "cannot get \"%s-supply\"\n", + svsb->buck_name); + return PTR_ERR(svsb->buck); + } + } + + count = dev_pm_opp_get_opp_count(svsb->opp_dev); + if (svsb->opp_count != count) { + dev_err(svsb->dev, + "opp_count not \"%u\" but get \"%d\"?\n", + svsb->opp_count, count); + return count; + } + + for (i = 0, freq = U32_MAX; i < svsb->opp_count; i++, freq--) { + opp = dev_pm_opp_find_freq_floor(svsb->opp_dev, &freq); + if (IS_ERR(opp)) { + dev_err(svsb->dev, "cannot find freq = %ld\n", + PTR_ERR(opp)); + return PTR_ERR(opp); + } + + svsb->opp_dfreq[i] = freq; + svsb->opp_dvolt[i] = dev_pm_opp_get_voltage(opp); + svsb->freq_pct[i] = percent(svsb->opp_dfreq[i], + svsb->freq_base); + dev_pm_opp_put(opp); + } + } + + return 0; +} + +static bool svs_mt8183_efuse_parsing(struct svs_platform *svsp) +{ + struct svs_bank *svsb; + u32 idx, i, ft_pgm; + + for (i = 0; i < svsp->efuse_max; i++) + if (svsp->efuse[i]) + dev_info(svsp->dev, "M_HW_RES%d: 0x%08x\n", + i, svsp->efuse[i]); + + if (!svsp->efuse[2]) { + dev_notice(svsp->dev, "svs_efuse[2] = 0x0?\n"); + return false; + } + + /* Svs efuse parsing */ + ft_pgm = (svsp->efuse[0] >> 4) & GENMASK(3, 0); + + for (idx = 0; idx < svsp->bank_max; idx++) { + svsb = &svsp->banks[idx]; + + if (ft_pgm <= 1) + svsb->volt_flags |= SVSB_INIT01_VOLT_IGNORE; + + switch (svsb->sw_id) { + case SVSB_CPU_LITTLE: + svsb->bdes = svsp->efuse[16] & GENMASK(7, 0); + svsb->mdes = (svsp->efuse[16] >> 8) & GENMASK(7, 0); + svsb->dcbdet = (svsp->efuse[16] >> 16) & GENMASK(7, 0); + svsb->dcmdet = (svsp->efuse[16] >> 24) & GENMASK(7, 0); + svsb->mtdes = (svsp->efuse[17] >> 16) & GENMASK(7, 0); + + if (ft_pgm <= 3) + svsb->volt_od += 10; + else + svsb->volt_od += 2; + break; + case SVSB_CPU_BIG: + svsb->bdes = svsp->efuse[18] & GENMASK(7, 0); + svsb->mdes = (svsp->efuse[18] >> 8) & GENMASK(7, 0); + svsb->dcbdet = (svsp->efuse[18] >> 16) & GENMASK(7, 0); + svsb->dcmdet = (svsp->efuse[18] >> 24) & GENMASK(7, 0); + svsb->mtdes = svsp->efuse[17] & GENMASK(7, 0); + + if (ft_pgm <= 3) + svsb->volt_od += 15; + else + svsb->volt_od += 12; + break; + case SVSB_CCI: + svsb->bdes = svsp->efuse[4] & GENMASK(7, 0); + svsb->mdes = (svsp->efuse[4] >> 8) & GENMASK(7, 0); + svsb->dcbdet = (svsp->efuse[4] >> 16) & GENMASK(7, 0); + svsb->dcmdet = (svsp->efuse[4] >> 24) & GENMASK(7, 0); + svsb->mtdes = (svsp->efuse[5] >> 16) & GENMASK(7, 0); + + if (ft_pgm <= 3) + svsb->volt_od += 10; + else + svsb->volt_od += 2; + break; + case SVSB_GPU: + svsb->bdes = svsp->efuse[6] & GENMASK(7, 0); + svsb->mdes = (svsp->efuse[6] >> 8) & GENMASK(7, 0); + svsb->dcbdet = (svsp->efuse[6] >> 16) & GENMASK(7, 0); + svsb->dcmdet = (svsp->efuse[6] >> 24) & GENMASK(7, 0); + svsb->mtdes = svsp->efuse[5] & GENMASK(7, 0); + + if (ft_pgm >= 2) { + svsb->freq_base = 800000000; /* 800MHz */ + svsb->dvt_fixed = 2; + } + break; + default: + dev_err(svsb->dev, "unknown sw_id: %u\n", svsb->sw_id); + return false; + } + } + + return true; +} + +static bool svs_is_efuse_data_correct(struct svs_platform *svsp) +{ + struct nvmem_cell *cell; + + /* Get svs efuse by nvmem */ + cell = nvmem_cell_get(svsp->dev, "svs-calibration-data"); + if (IS_ERR(cell)) { + dev_err(svsp->dev, "no \"svs-calibration-data\"? %ld\n", + PTR_ERR(cell)); + return false; + } + + svsp->efuse = nvmem_cell_read(cell, &svsp->efuse_max); + if (IS_ERR(svsp->efuse)) { + dev_err(svsp->dev, "cannot read svs efuse: %ld\n", + PTR_ERR(svsp->efuse)); + nvmem_cell_put(cell); + return false; + } + + svsp->efuse_max /= sizeof(u32); + nvmem_cell_put(cell); + + return svsp->efuse_parsing(svsp); +} + +static struct device *svs_get_subsys_device(struct svs_platform *svsp, + const char *node_name) +{ + struct platform_device *pdev; + struct device_node *np; + + np = of_find_node_by_name(NULL, node_name); + if (!np) { + dev_err(svsp->dev, "cannot find %s node\n", node_name); + return ERR_PTR(-ENODEV); + } + + pdev = of_find_device_by_node(np); + if (!pdev) { + of_node_put(np); + dev_err(svsp->dev, "cannot find pdev by %s\n", node_name); + return ERR_PTR(-ENXIO); + } + + of_node_put(np); + + return &pdev->dev; +} + +static struct device *svs_add_device_link(struct svs_platform *svsp, + const char *node_name) +{ + struct device *dev; + struct device_link *sup_link; + + if (!node_name) { + dev_err(svsp->dev, "node name cannot be null\n"); + return ERR_PTR(-EINVAL); + } + + dev = svs_get_subsys_device(svsp, node_name); + if (IS_ERR(dev)) + return dev; + + sup_link = device_link_add(svsp->dev, dev, + DL_FLAG_AUTOREMOVE_CONSUMER); + if (!sup_link) { + dev_err(svsp->dev, "sup_link is NULL\n"); + return ERR_PTR(-EINVAL); + } + + if (sup_link->supplier->links.status != DL_DEV_DRIVER_BOUND) + return ERR_PTR(-EPROBE_DEFER); + + return dev; +} + +static int svs_mt8183_platform_probe(struct svs_platform *svsp) +{ + struct svs_bank *svsb; + u32 idx; + + for (idx = 0; idx < svsp->bank_max; idx++) { + svsb = &svsp->banks[idx]; + + switch (svsb->sw_id) { + case SVSB_CPU_LITTLE: + case SVSB_CPU_BIG: + svsb->opp_dev = get_cpu_device(svsb->cpu_id); + break; + case SVSB_CCI: + svsb->opp_dev = svs_add_device_link(svsp, "cci"); + break; + case SVSB_GPU: + svsb->opp_dev = svs_add_device_link(svsp, "gpu"); + break; + default: + dev_err(svsb->dev, "unknown sw_id: %u\n", svsb->sw_id); + return -EINVAL; + } + + if (IS_ERR(svsb->opp_dev)) + return dev_err_probe(svsp->dev, PTR_ERR(svsb->opp_dev), + "failed to get OPP device for bank %d\n", + idx); + } + + return 0; +} + +static struct svs_bank svs_mt8183_banks[] = { + { + .sw_id = SVSB_CPU_LITTLE, + .set_freq_pct = svs_set_bank_freq_pct_v2, + .get_volts = svs_get_bank_volts_v2, + .cpu_id = 0, + .buck_name = "proc", + .volt_flags = SVSB_INIT01_VOLT_INC_ONLY, + .mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02, + .opp_count = MAX_OPP_ENTRIES, + .freq_base = 1989000000, + .vboot = 0x30, + .volt_step = 6250, + .volt_base = 500000, + .vmax = 0x64, + .vmin = 0x18, + .age_config = 0x555555, + .dc_config = 0x555555, + .dvt_fixed = 0x7, + .vco = 0x10, + .chk_shift = 0x77, + .core_sel = 0x8fff0000, + .int_st = BIT(0), + .ctl0 = 0x00010001, + }, + { + .sw_id = SVSB_CPU_BIG, + .set_freq_pct = svs_set_bank_freq_pct_v2, + .get_volts = svs_get_bank_volts_v2, + .cpu_id = 4, + .buck_name = "proc", + .volt_flags = SVSB_INIT01_VOLT_INC_ONLY, + .mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02, + .opp_count = MAX_OPP_ENTRIES, + .freq_base = 1989000000, + .vboot = 0x30, + .volt_step = 6250, + .volt_base = 500000, + .vmax = 0x58, + .vmin = 0x10, + .age_config = 0x555555, + .dc_config = 0x555555, + .dvt_fixed = 0x7, + .vco = 0x10, + .chk_shift = 0x77, + .core_sel = 0x8fff0001, + .int_st = BIT(1), + .ctl0 = 0x00000001, + }, + { + .sw_id = SVSB_CCI, + .set_freq_pct = svs_set_bank_freq_pct_v2, + .get_volts = svs_get_bank_volts_v2, + .buck_name = "proc", + .volt_flags = SVSB_INIT01_VOLT_INC_ONLY, + .mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02, + .opp_count = MAX_OPP_ENTRIES, + .freq_base = 1196000000, + .vboot = 0x30, + .volt_step = 6250, + .volt_base = 500000, + .vmax = 0x64, + .vmin = 0x18, + .age_config = 0x555555, + .dc_config = 0x555555, + .dvt_fixed = 0x7, + .vco = 0x10, + .chk_shift = 0x77, + .core_sel = 0x8fff0002, + .int_st = BIT(2), + .ctl0 = 0x00100003, + }, + { + .sw_id = SVSB_GPU, + .set_freq_pct = svs_set_bank_freq_pct_v2, + .get_volts = svs_get_bank_volts_v2, + .buck_name = "mali", + .volt_flags = SVSB_INIT01_PD_REQ | + SVSB_INIT01_VOLT_INC_ONLY, + .mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02, + .opp_count = MAX_OPP_ENTRIES, + .freq_base = 900000000, + .vboot = 0x30, + .volt_step = 6250, + .volt_base = 500000, + .vmax = 0x40, + .vmin = 0x14, + .age_config = 0x555555, + .dc_config = 0x555555, + .dvt_fixed = 0x3, + .vco = 0x10, + .chk_shift = 0x77, + .core_sel = 0x8fff0003, + .int_st = BIT(3), + .ctl0 = 0x00050001, + }, +}; + +static const struct svs_platform_data svs_mt8183_platform_data = { + .name = "mt8183-svs", + .banks = svs_mt8183_banks, + .efuse_parsing = svs_mt8183_efuse_parsing, + .probe = svs_mt8183_platform_probe, + .irqflags = IRQF_TRIGGER_LOW, + .regs = svs_regs_v2, + .bank_max = ARRAY_SIZE(svs_mt8183_banks), +}; + +static const struct of_device_id svs_of_match[] = { + { + .compatible = "mediatek,mt8183-svs", + .data = &svs_mt8183_platform_data, + }, { + /* Sentinel */ + }, +}; + +static struct svs_platform *svs_platform_probe(struct platform_device *pdev) +{ + struct svs_platform *svsp; + const struct svs_platform_data *svsp_data; + int ret; + + svsp_data = of_device_get_match_data(&pdev->dev); + if (!svsp_data) { + dev_err(&pdev->dev, "no svs platform data?\n"); + return ERR_PTR(-EPERM); + } + + svsp = devm_kzalloc(&pdev->dev, sizeof(*svsp), GFP_KERNEL); + if (!svsp) + return ERR_PTR(-ENOMEM); + + svsp->dev = &pdev->dev; + svsp->name = svsp_data->name; + svsp->banks = svsp_data->banks; + svsp->efuse_parsing = svsp_data->efuse_parsing; + svsp->probe = svsp_data->probe; + svsp->irqflags = svsp_data->irqflags; + svsp->regs = svsp_data->regs; + svsp->bank_max = svsp_data->bank_max; + + ret = svsp->probe(svsp); + if (ret) + return ERR_PTR(ret); + + return svsp; +} + +static int svs_probe(struct platform_device *pdev) +{ + struct svs_platform *svsp; + unsigned int svsp_irq; + int ret; + + svsp = svs_platform_probe(pdev); + if (IS_ERR(svsp)) + return PTR_ERR(svsp); + + if (!svs_is_efuse_data_correct(svsp)) { + dev_notice(svsp->dev, "efuse data isn't correct\n"); + ret = -EPERM; + goto svs_probe_free_resource; + } + + ret = svs_bank_resource_setup(svsp); + if (ret) { + dev_err(svsp->dev, "svs bank resource setup fail: %d\n", ret); + goto svs_probe_free_resource; + } + + svsp_irq = irq_of_parse_and_map(svsp->dev->of_node, 0); + ret = devm_request_threaded_irq(svsp->dev, svsp_irq, NULL, svs_isr, + svsp->irqflags | IRQF_ONESHOT, + svsp->name, svsp); + if (ret) { + dev_err(svsp->dev, "register irq(%d) failed: %d\n", + svsp_irq, ret); + goto svs_probe_free_resource; + } + + svsp->main_clk = devm_clk_get(svsp->dev, "main"); + if (IS_ERR(svsp->main_clk)) { + dev_err(svsp->dev, "failed to get clock: %ld\n", + PTR_ERR(svsp->main_clk)); + ret = PTR_ERR(svsp->main_clk); + goto svs_probe_free_resource; + } + + ret = clk_prepare_enable(svsp->main_clk); + if (ret) { + dev_err(svsp->dev, "cannot enable main clk: %d\n", ret); + goto svs_probe_free_resource; + } + + svsp->base = of_iomap(svsp->dev->of_node, 0); + if (IS_ERR_OR_NULL(svsp->base)) { + dev_err(svsp->dev, "cannot find svs register base\n"); + ret = -EINVAL; + goto svs_probe_clk_disable; + } + + ret = svs_start(svsp); + if (ret) { + dev_err(svsp->dev, "svs start fail: %d\n", ret); + goto svs_probe_iounmap; + } + + return 0; + +svs_probe_iounmap: + iounmap(svsp->base); + +svs_probe_clk_disable: + clk_disable_unprepare(svsp->main_clk); + +svs_probe_free_resource: + if (!IS_ERR_OR_NULL(svsp->efuse)) + kfree(svsp->efuse); + + return ret; +} + +static SIMPLE_DEV_PM_OPS(svs_pm_ops, svs_suspend, svs_resume); + +static struct platform_driver svs_driver = { + .probe = svs_probe, + .driver = { + .name = "mtk-svs", + .pm = &svs_pm_ops, + .of_match_table = of_match_ptr(svs_of_match), + }, +}; + +module_platform_driver(svs_driver); + +MODULE_AUTHOR("Roger Lu "); +MODULE_DESCRIPTION("MediaTek SVS driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 6c69271926e9aca9254bf844b976958dfa0089c7 Mon Sep 17 00:00:00 2001 From: Roger Lu Date: Mon, 16 May 2022 08:43:08 +0800 Subject: soc: mediatek: SVS: add monitor mode SVS monitor mode is based on different thermal temperature to provide suitable SVS bank voltages. Signed-off-by: Roger Lu Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Kevin Hilman Link: https://lore.kernel.org/r/20220516004311.18358-5-roger.lu@mediatek.com Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-svs.c | 256 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 250 insertions(+), 6 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-svs.c b/drivers/soc/mediatek/mtk-svs.c index cc23a701159e..44ccad1e3327 100644 --- a/drivers/soc/mediatek/mtk-svs.c +++ b/drivers/soc/mediatek/mtk-svs.c @@ -25,6 +25,7 @@ #include #include #include +#include /* svs bank 1-line software id */ #define SVSB_CPU_LITTLE BIT(0) @@ -36,6 +37,7 @@ #define SVSB_MODE_ALL_DISABLE 0 #define SVSB_MODE_INIT01 BIT(1) #define SVSB_MODE_INIT02 BIT(2) +#define SVSB_MODE_MON BIT(3) /* svs bank volt flags */ #define SVSB_INIT01_PD_REQ BIT(0) @@ -49,16 +51,21 @@ #define SVSB_DTLO 0xfe #define SVSB_EN_INIT01 0x1 #define SVSB_EN_INIT02 0x5 +#define SVSB_EN_MON 0x2 #define SVSB_EN_OFF 0x0 #define SVSB_INTEN_INIT0x 0x00005f01 +#define SVSB_INTEN_MONVOPEN 0x00ff0000 #define SVSB_INTSTS_CLEAN 0x00ffffff #define SVSB_INTSTS_COMPLETE 0x1 +#define SVSB_INTSTS_MONVOP 0x00ff0000 #define SVSB_RUNCONFIG_DEFAULT 0x80000000 /* svs bank related setting */ #define MAX_OPP_ENTRIES 16 #define SVSB_DC_SIGNED_BIT BIT(15) #define SVSB_DET_CLK_EN BIT(31) +#define SVSB_TEMP_LOWER_BOUND 0xb2 +#define SVSB_TEMP_UPPER_BOUND 0x64 static DEFINE_SPINLOCK(svs_lock); @@ -67,6 +74,7 @@ static DEFINE_SPINLOCK(svs_lock); * @SVSB_PHASE_ERROR: svs bank encounters unexpected condition * @SVSB_PHASE_INIT01: svs bank basic init for data calibration * @SVSB_PHASE_INIT02: svs bank can provide voltages to opp table + * @SVSB_PHASE_MON: svs bank can provide voltages with thermal effect * @SVSB_PHASE_MAX: total number of svs bank phase (debug purpose) * * Each svs bank has its own independent phase and we enable each svs bank by @@ -74,12 +82,13 @@ static DEFINE_SPINLOCK(svs_lock); * condition, it will fire an irq (PHASE_ERROR) to inform svs software. * * svs bank general phase-enabled order: - * SVSB_PHASE_INIT01 -> SVSB_PHASE_INIT02 + * SVSB_PHASE_INIT01 -> SVSB_PHASE_INIT02 -> SVSB_PHASE_MON */ enum svsb_phase { SVSB_PHASE_ERROR = 0, SVSB_PHASE_INIT01, SVSB_PHASE_INIT02, + SVSB_PHASE_MON, SVSB_PHASE_MAX, }; @@ -210,9 +219,11 @@ static const u32 svs_regs_v2[] = { * @probe: svs platform probe function pointer * @irqflags: svs platform irq settings flags * @efuse_max: total number of svs efuse + * @tefuse_max: total number of thermal efuse * @regs: svs platform registers map * @bank_max: total number of svs banks * @efuse: svs efuse data received from NVMEM framework + * @tefuse: thermal efuse data received from NVMEM framework */ struct svs_platform { char *name; @@ -225,9 +236,11 @@ struct svs_platform { int (*probe)(struct svs_platform *svsp); unsigned long irqflags; size_t efuse_max; + size_t tefuse_max; const u32 *regs; u32 bank_max; u32 *efuse; + u32 *tefuse; }; struct svs_platform_data { @@ -246,11 +259,13 @@ struct svs_platform_data { * @opp_dev: device for opp table/buck control * @init_completion: the timeout completion for bank init * @buck: regulator used by opp_dev + * @tzd: thermal zone device for getting temperature * @lock: mutex lock to protect voltage update process * @set_freq_pct: function pointer to set bank frequency percent table * @get_volts: function pointer to get bank voltages * @name: bank name * @buck_name: regulator name + * @tzone_name: thermal zone name * @phase: bank current phase * @volt_od: bank voltage overdrive * @pm_runtime_enabled_count: bank pm runtime enabled count @@ -279,6 +294,13 @@ struct svs_platform_data { * @sw_id: bank software identification * @cpu_id: cpu core id for SVS CPU bank use only * @ctl0: TS-x selection + * @temp: bank temperature + * @tzone_htemp: thermal zone high temperature threshold + * @tzone_htemp_voffset: thermal zone high temperature voltage offset + * @tzone_ltemp: thermal zone low temperature threshold + * @tzone_ltemp_voffset: thermal zone low temperature voltage offset + * @bts: svs efuse data + * @mts: svs efuse data * @bdes: svs efuse data * @mdes: svs efuse data * @mtdes: svs efuse data @@ -295,11 +317,13 @@ struct svs_bank { struct device *opp_dev; struct completion init_completion; struct regulator *buck; + struct thermal_zone_device *tzd; struct mutex lock; /* lock to protect voltage update process */ void (*set_freq_pct)(struct svs_platform *svsp); void (*get_volts)(struct svs_platform *svsp); char *name; char *buck_name; + char *tzone_name; enum svsb_phase phase; s32 volt_od; u32 pm_runtime_enabled_count; @@ -328,6 +352,13 @@ struct svs_bank { u32 sw_id; u32 cpu_id; u32 ctl0; + u32 temp; + u32 tzone_htemp; + u32 tzone_htemp_voffset; + u32 tzone_ltemp; + u32 tzone_ltemp_voffset; + u32 bts; + u32 mts; u32 bdes; u32 mdes; u32 mtdes; @@ -370,11 +401,27 @@ static u32 svs_bank_volt_to_opp_volt(u32 svsb_volt, u32 svsb_volt_step, static int svs_adjust_pm_opp_volts(struct svs_bank *svsb) { - int ret = -EPERM; - u32 i, svsb_volt, opp_volt; + int ret = -EPERM, tzone_temp = 0; + u32 i, svsb_volt, opp_volt, temp_voffset = 0; mutex_lock(&svsb->lock); + /* Get thermal effect */ + if (svsb->phase == SVSB_PHASE_MON) { + ret = thermal_zone_get_temp(svsb->tzd, &tzone_temp); + if (ret || (svsb->temp > SVSB_TEMP_UPPER_BOUND && + svsb->temp < SVSB_TEMP_LOWER_BOUND)) { + dev_err(svsb->dev, "%s: %d (0x%x), run default volts\n", + svsb->tzone_name, ret, svsb->temp); + svsb->phase = SVSB_PHASE_ERROR; + } + + if (tzone_temp >= svsb->tzone_htemp) + temp_voffset += svsb->tzone_htemp_voffset; + else if (tzone_temp <= svsb->tzone_ltemp) + temp_voffset += svsb->tzone_ltemp_voffset; + } + /* vmin <= svsb_volt (opp_volt) <= default opp voltage */ for (i = 0; i < svsb->opp_count; i++) { switch (svsb->phase) { @@ -390,6 +437,12 @@ static int svs_adjust_pm_opp_volts(struct svs_bank *svsb) svsb->volt_step, svsb->volt_base); break; + case SVSB_PHASE_MON: + svsb_volt = max(svsb->volt[i] + temp_voffset, svsb->vmin); + opp_volt = svs_bank_volt_to_opp_volt(svsb_volt, + svsb->volt_step, + svsb->volt_base); + break; default: dev_err(svsb->dev, "unknown phase: %u\n", svsb->phase); ret = -EINVAL; @@ -484,7 +537,7 @@ static void svs_set_bank_phase(struct svs_platform *svsp, enum svsb_phase target_phase) { struct svs_bank *svsb = svsp->pbank; - u32 des_char, temp_char, det_char, limit_vals, init2vals; + u32 des_char, temp_char, det_char, limit_vals, init2vals, ts_calcs; svs_switch_bank(svsp); @@ -525,6 +578,12 @@ static void svs_set_bank_phase(struct svs_platform *svsp, svs_writel_relaxed(svsp, init2vals, INIT2VALS); svs_writel_relaxed(svsp, SVSB_EN_INIT02, SVSEN); break; + case SVSB_PHASE_MON: + ts_calcs = (svsb->bts << 12) | svsb->mts; + svs_writel_relaxed(svsp, ts_calcs, TSCALCS); + svs_writel_relaxed(svsp, SVSB_INTEN_MONVOPEN, INTEN); + svs_writel_relaxed(svsp, SVSB_EN_MON, SVSEN); + break; default: dev_err(svsb->dev, "requested unknown target phase: %u\n", target_phase); @@ -544,6 +603,7 @@ static inline void svs_error_isr_handler(struct svs_platform *svsp) dev_err(svsb->dev, "SMSTATE0 = 0x%08x, SMSTATE1 = 0x%08x\n", svs_readl_relaxed(svsp, SMSTATE0), svs_readl_relaxed(svsp, SMSTATE1)); + dev_err(svsb->dev, "TEMP = 0x%08x\n", svs_readl_relaxed(svsp, TEMP)); svsb->phase = SVSB_PHASE_ERROR; svs_writel_relaxed(svsp, SVSB_EN_OFF, SVSEN); @@ -591,6 +651,17 @@ static inline void svs_init02_isr_handler(struct svs_platform *svsp) svs_writel_relaxed(svsp, SVSB_INTSTS_COMPLETE, INTSTS); } +static inline void svs_mon_mode_isr_handler(struct svs_platform *svsp) +{ + struct svs_bank *svsb = svsp->pbank; + + svsb->phase = SVSB_PHASE_MON; + svsb->get_volts(svsp); + + svsb->temp = svs_readl_relaxed(svsp, TEMP) & GENMASK(7, 0); + svs_writel_relaxed(svsp, SVSB_INTSTS_MONVOP, INTSTS); +} + static irqreturn_t svs_isr(int irq, void *data) { struct svs_platform *svsp = data; @@ -621,6 +692,8 @@ static irqreturn_t svs_isr(int irq, void *data) else if (int_sts == SVSB_INTSTS_COMPLETE && svs_en == SVSB_EN_INIT02) svs_init02_isr_handler(svsp); + else if (int_sts & SVSB_INTSTS_MONVOP) + svs_mon_mode_isr_handler(svsp); else svs_error_isr_handler(svsp); @@ -835,6 +908,25 @@ static int svs_init02(struct svs_platform *svsp) return 0; } +static void svs_mon_mode(struct svs_platform *svsp) +{ + struct svs_bank *svsb; + unsigned long flags; + u32 idx; + + for (idx = 0; idx < svsp->bank_max; idx++) { + svsb = &svsp->banks[idx]; + + if (!(svsb->mode_support & SVSB_MODE_MON)) + continue; + + spin_lock_irqsave(&svs_lock, flags); + svsp->pbank = svsb; + svs_set_bank_phase(svsp, SVSB_PHASE_MON); + spin_unlock_irqrestore(&svs_lock, flags); + } +} + static int svs_start(struct svs_platform *svsp) { int ret; @@ -847,6 +939,8 @@ static int svs_start(struct svs_platform *svsp) if (ret) return ret; + svs_mon_mode(svsp); + return 0; } @@ -892,6 +986,8 @@ static int svs_resume(struct device *dev) if (ret) return ret; + svs_mon_mode(svsp); + return 0; } @@ -956,6 +1052,15 @@ static int svs_bank_resource_setup(struct svs_platform *svsp) } } + if (svsb->mode_support & SVSB_MODE_MON) { + svsb->tzd = thermal_zone_get_zone_by_name(svsb->tzone_name); + if (IS_ERR(svsb->tzd)) { + dev_err(svsb->dev, "cannot get \"%s\" thermal zone\n", + svsb->tzone_name); + return PTR_ERR(svsb->tzd); + } + } + count = dev_pm_opp_get_opp_count(svsb->opp_dev); if (svsb->opp_count != count) { dev_err(svsb->dev, @@ -986,7 +1091,11 @@ static int svs_bank_resource_setup(struct svs_platform *svsp) static bool svs_mt8183_efuse_parsing(struct svs_platform *svsp) { struct svs_bank *svsb; - u32 idx, i, ft_pgm; + struct nvmem_cell *cell; + int format[6], x_roomt[6], o_vtsmcu[5], o_vtsabb, tb_roomt = 0; + int adc_ge_t, adc_oe_t, ge, oe, gain, degc_cali, adc_cali_en_t; + int o_slope, o_slope_sign, ts_id; + u32 idx, i, ft_pgm, mts, temp0, temp1, temp2; for (i = 0; i < svsp->efuse_max; i++) if (svsp->efuse[i]) @@ -1062,6 +1171,127 @@ static bool svs_mt8183_efuse_parsing(struct svs_platform *svsp) } } + /* Get thermal efuse by nvmem */ + cell = nvmem_cell_get(svsp->dev, "t-calibration-data"); + if (IS_ERR(cell)) { + dev_err(svsp->dev, "no \"t-calibration-data\"? %ld\n", + PTR_ERR(cell)); + goto remove_mt8183_svsb_mon_mode; + } + + svsp->tefuse = nvmem_cell_read(cell, &svsp->tefuse_max); + if (IS_ERR(svsp->tefuse)) { + dev_err(svsp->dev, "cannot read thermal efuse: %ld\n", + PTR_ERR(svsp->tefuse)); + nvmem_cell_put(cell); + goto remove_mt8183_svsb_mon_mode; + } + + svsp->tefuse_max /= sizeof(u32); + nvmem_cell_put(cell); + + /* Thermal efuse parsing */ + adc_ge_t = (svsp->tefuse[1] >> 22) & GENMASK(9, 0); + adc_oe_t = (svsp->tefuse[1] >> 12) & GENMASK(9, 0); + + o_vtsmcu[0] = (svsp->tefuse[0] >> 17) & GENMASK(8, 0); + o_vtsmcu[1] = (svsp->tefuse[0] >> 8) & GENMASK(8, 0); + o_vtsmcu[2] = svsp->tefuse[1] & GENMASK(8, 0); + o_vtsmcu[3] = (svsp->tefuse[2] >> 23) & GENMASK(8, 0); + o_vtsmcu[4] = (svsp->tefuse[2] >> 5) & GENMASK(8, 0); + o_vtsabb = (svsp->tefuse[2] >> 14) & GENMASK(8, 0); + + degc_cali = (svsp->tefuse[0] >> 1) & GENMASK(5, 0); + adc_cali_en_t = svsp->tefuse[0] & BIT(0); + o_slope_sign = (svsp->tefuse[0] >> 7) & BIT(0); + + ts_id = (svsp->tefuse[1] >> 9) & BIT(0); + o_slope = (svsp->tefuse[0] >> 26) & GENMASK(5, 0); + + if (adc_cali_en_t == 1) { + if (!ts_id) + o_slope = 0; + + if (adc_ge_t < 265 || adc_ge_t > 758 || + adc_oe_t < 265 || adc_oe_t > 758 || + o_vtsmcu[0] < -8 || o_vtsmcu[0] > 484 || + o_vtsmcu[1] < -8 || o_vtsmcu[1] > 484 || + o_vtsmcu[2] < -8 || o_vtsmcu[2] > 484 || + o_vtsmcu[3] < -8 || o_vtsmcu[3] > 484 || + o_vtsmcu[4] < -8 || o_vtsmcu[4] > 484 || + o_vtsabb < -8 || o_vtsabb > 484 || + degc_cali < 1 || degc_cali > 63) { + dev_err(svsp->dev, "bad thermal efuse, no mon mode\n"); + goto remove_mt8183_svsb_mon_mode; + } + } else { + dev_err(svsp->dev, "no thermal efuse, no mon mode\n"); + goto remove_mt8183_svsb_mon_mode; + } + + ge = ((adc_ge_t - 512) * 10000) / 4096; + oe = (adc_oe_t - 512); + gain = (10000 + ge); + + format[0] = (o_vtsmcu[0] + 3350 - oe); + format[1] = (o_vtsmcu[1] + 3350 - oe); + format[2] = (o_vtsmcu[2] + 3350 - oe); + format[3] = (o_vtsmcu[3] + 3350 - oe); + format[4] = (o_vtsmcu[4] + 3350 - oe); + format[5] = (o_vtsabb + 3350 - oe); + + for (i = 0; i < 6; i++) + x_roomt[i] = (((format[i] * 10000) / 4096) * 10000) / gain; + + temp0 = (10000 * 100000 / gain) * 15 / 18; + + if (!o_slope_sign) + mts = (temp0 * 10) / (1534 + o_slope * 10); + else + mts = (temp0 * 10) / (1534 - o_slope * 10); + + for (idx = 0; idx < svsp->bank_max; idx++) { + svsb = &svsp->banks[idx]; + svsb->mts = mts; + + switch (svsb->sw_id) { + case SVSB_CPU_LITTLE: + tb_roomt = x_roomt[3]; + break; + case SVSB_CPU_BIG: + tb_roomt = x_roomt[4]; + break; + case SVSB_CCI: + tb_roomt = x_roomt[3]; + break; + case SVSB_GPU: + tb_roomt = x_roomt[1]; + break; + default: + dev_err(svsb->dev, "unknown sw_id: %u\n", svsb->sw_id); + goto remove_mt8183_svsb_mon_mode; + } + + temp0 = (degc_cali * 10 / 2); + temp1 = ((10000 * 100000 / 4096 / gain) * + oe + tb_roomt * 10) * 15 / 18; + + if (!o_slope_sign) + temp2 = temp1 * 100 / (1534 + o_slope * 10); + else + temp2 = temp1 * 100 / (1534 - o_slope * 10); + + svsb->bts = (temp0 + temp2 - 250) * 4 / 10; + } + + return true; + +remove_mt8183_svsb_mon_mode: + for (idx = 0; idx < svsp->bank_max; idx++) { + svsb = &svsp->banks[idx]; + svsb->mode_support &= ~SVSB_MODE_MON; + } + return true; } @@ -1145,9 +1375,15 @@ static struct device *svs_add_device_link(struct svs_platform *svsp, static int svs_mt8183_platform_probe(struct svs_platform *svsp) { + struct device *dev; struct svs_bank *svsb; u32 idx; + dev = svs_add_device_link(svsp, "thermal"); + if (IS_ERR(dev)) + return dev_err_probe(svsp->dev, PTR_ERR(dev), + "failed to get thermal device\n"); + for (idx = 0; idx < svsp->bank_max; idx++) { svsb = &svsp->banks[idx]; @@ -1253,9 +1489,11 @@ static struct svs_bank svs_mt8183_banks[] = { .set_freq_pct = svs_set_bank_freq_pct_v2, .get_volts = svs_get_bank_volts_v2, .buck_name = "mali", + .tzone_name = "tzts2", .volt_flags = SVSB_INIT01_PD_REQ | SVSB_INIT01_VOLT_INC_ONLY, - .mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02, + .mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02 | + SVSB_MODE_MON, .opp_count = MAX_OPP_ENTRIES, .freq_base = 900000000, .vboot = 0x30, @@ -1271,6 +1509,10 @@ static struct svs_bank svs_mt8183_banks[] = { .core_sel = 0x8fff0003, .int_st = BIT(3), .ctl0 = 0x00050001, + .tzone_htemp = 85000, + .tzone_htemp_voffset = 0, + .tzone_ltemp = 25000, + .tzone_ltemp_voffset = 3, }, }; @@ -1395,6 +1637,8 @@ svs_probe_clk_disable: svs_probe_free_resource: if (!IS_ERR_OR_NULL(svsp->efuse)) kfree(svsp->efuse); + if (!IS_ERR_OR_NULL(svsp->tefuse)) + kfree(svsp->tefuse); return ret; } -- cgit v1.2.3 From 13f1bbcfb5822fcaacfc652017b57b79ffcacefa Mon Sep 17 00:00:00 2001 From: Roger Lu Date: Mon, 16 May 2022 08:43:09 +0800 Subject: soc: mediatek: SVS: add debug commands The purpose of SVS is to help find the suitable voltages for DVFS. Therefore, if SVS bank voltages are concerned to be wrong, we can show/disable SVS bank voltages by this patch. Signed-off-by: Roger Lu Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Kevin Hilman Link: https://lore.kernel.org/r/20220516004311.18358-6-roger.lu@mediatek.com Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-svs.c | 275 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 275 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-svs.c b/drivers/soc/mediatek/mtk-svs.c index 44ccad1e3327..aa0665725339 100644 --- a/drivers/soc/mediatek/mtk-svs.c +++ b/drivers/soc/mediatek/mtk-svs.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +71,39 @@ static DEFINE_SPINLOCK(svs_lock); +#define debug_fops_ro(name) \ + static int svs_##name##_debug_open(struct inode *inode, \ + struct file *filp) \ + { \ + return single_open(filp, svs_##name##_debug_show, \ + inode->i_private); \ + } \ + static const struct file_operations svs_##name##_debug_fops = { \ + .owner = THIS_MODULE, \ + .open = svs_##name##_debug_open, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ + } + +#define debug_fops_rw(name) \ + static int svs_##name##_debug_open(struct inode *inode, \ + struct file *filp) \ + { \ + return single_open(filp, svs_##name##_debug_show, \ + inode->i_private); \ + } \ + static const struct file_operations svs_##name##_debug_fops = { \ + .owner = THIS_MODULE, \ + .open = svs_##name##_debug_open, \ + .read = seq_read, \ + .write = svs_##name##_debug_write, \ + .llseek = seq_lseek, \ + .release = single_release, \ + } + +#define svs_dentry_data(name) {__stringify(name), &svs_##name##_debug_fops} + /** * enum svsb_phase - svs bank phase enumeration * @SVSB_PHASE_ERROR: svs bank encounters unexpected condition @@ -268,6 +303,7 @@ struct svs_platform_data { * @tzone_name: thermal zone name * @phase: bank current phase * @volt_od: bank voltage overdrive + * @reg_data: bank register data in different phase for debug purpose * @pm_runtime_enabled_count: bank pm runtime enabled count * @mode_support: bank mode support. * @freq_base: reference frequency for bank init @@ -326,6 +362,7 @@ struct svs_bank { char *tzone_name; enum svsb_phase phase; s32 volt_od; + u32 reg_data[SVSB_PHASE_MAX][SVS_REG_MAX]; u32 pm_runtime_enabled_count; u32 mode_support; u32 freq_base; @@ -467,6 +504,220 @@ unlock_mutex: return ret; } +static int svs_dump_debug_show(struct seq_file *m, void *p) +{ + struct svs_platform *svsp = (struct svs_platform *)m->private; + struct svs_bank *svsb; + unsigned long svs_reg_addr; + u32 idx, i, j, bank_id; + + for (i = 0; i < svsp->efuse_max; i++) + if (svsp->efuse && svsp->efuse[i]) + seq_printf(m, "M_HW_RES%d = 0x%08x\n", + i, svsp->efuse[i]); + + for (i = 0; i < svsp->tefuse_max; i++) + if (svsp->tefuse) + seq_printf(m, "THERMAL_EFUSE%d = 0x%08x\n", + i, svsp->tefuse[i]); + + for (bank_id = 0, idx = 0; idx < svsp->bank_max; idx++, bank_id++) { + svsb = &svsp->banks[idx]; + + for (i = SVSB_PHASE_INIT01; i <= SVSB_PHASE_MON; i++) { + seq_printf(m, "Bank_number = %u\n", bank_id); + + if (i == SVSB_PHASE_INIT01 || i == SVSB_PHASE_INIT02) + seq_printf(m, "mode = init%d\n", i); + else if (i == SVSB_PHASE_MON) + seq_puts(m, "mode = mon\n"); + else + seq_puts(m, "mode = error\n"); + + for (j = DESCHAR; j < SVS_REG_MAX; j++) { + svs_reg_addr = (unsigned long)(svsp->base + + svsp->regs[j]); + seq_printf(m, "0x%08lx = 0x%08x\n", + svs_reg_addr, svsb->reg_data[i][j]); + } + } + } + + return 0; +} + +debug_fops_ro(dump); + +static int svs_enable_debug_show(struct seq_file *m, void *v) +{ + struct svs_bank *svsb = (struct svs_bank *)m->private; + + switch (svsb->phase) { + case SVSB_PHASE_ERROR: + seq_puts(m, "disabled\n"); + break; + case SVSB_PHASE_INIT01: + seq_puts(m, "init1\n"); + break; + case SVSB_PHASE_INIT02: + seq_puts(m, "init2\n"); + break; + case SVSB_PHASE_MON: + seq_puts(m, "mon mode\n"); + break; + default: + seq_puts(m, "unknown\n"); + break; + } + + return 0; +} + +static ssize_t svs_enable_debug_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *pos) +{ + struct svs_bank *svsb = file_inode(filp)->i_private; + struct svs_platform *svsp = dev_get_drvdata(svsb->dev); + unsigned long flags; + int enabled, ret; + char *buf = NULL; + + if (count >= PAGE_SIZE) + return -EINVAL; + + buf = (char *)memdup_user_nul(buffer, count); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + ret = kstrtoint(buf, 10, &enabled); + if (ret) + return ret; + + if (!enabled) { + spin_lock_irqsave(&svs_lock, flags); + svsp->pbank = svsb; + svsb->mode_support = SVSB_MODE_ALL_DISABLE; + svs_switch_bank(svsp); + svs_writel_relaxed(svsp, SVSB_EN_OFF, SVSEN); + svs_writel_relaxed(svsp, SVSB_INTSTS_CLEAN, INTSTS); + spin_unlock_irqrestore(&svs_lock, flags); + + svsb->phase = SVSB_PHASE_ERROR; + svs_adjust_pm_opp_volts(svsb); + } + + kfree(buf); + + return count; +} + +debug_fops_rw(enable); + +static int svs_status_debug_show(struct seq_file *m, void *v) +{ + struct svs_bank *svsb = (struct svs_bank *)m->private; + struct dev_pm_opp *opp; + int tzone_temp = 0, ret; + u32 i; + + ret = thermal_zone_get_temp(svsb->tzd, &tzone_temp); + if (ret) + seq_printf(m, "%s: temperature ignore\n", svsb->name); + else + seq_printf(m, "%s: temperature = %d\n", svsb->name, tzone_temp); + + for (i = 0; i < svsb->opp_count; i++) { + opp = dev_pm_opp_find_freq_exact(svsb->opp_dev, + svsb->opp_dfreq[i], true); + if (IS_ERR(opp)) { + seq_printf(m, "%s: cannot find freq = %u (%ld)\n", + svsb->name, svsb->opp_dfreq[i], + PTR_ERR(opp)); + return PTR_ERR(opp); + } + + seq_printf(m, "opp_freq[%02u]: %u, opp_volt[%02u]: %lu, ", + i, svsb->opp_dfreq[i], i, + dev_pm_opp_get_voltage(opp)); + seq_printf(m, "svsb_volt[%02u]: 0x%x, freq_pct[%02u]: %u\n", + i, svsb->volt[i], i, svsb->freq_pct[i]); + dev_pm_opp_put(opp); + } + + return 0; +} + +debug_fops_ro(status); + +static int svs_create_debug_cmds(struct svs_platform *svsp) +{ + struct svs_bank *svsb; + struct dentry *svs_dir, *svsb_dir, *file_entry; + const char *d = "/sys/kernel/debug/svs"; + u32 i, idx; + + struct svs_dentry { + const char *name; + const struct file_operations *fops; + }; + + struct svs_dentry svs_entries[] = { + svs_dentry_data(dump), + }; + + struct svs_dentry svsb_entries[] = { + svs_dentry_data(enable), + svs_dentry_data(status), + }; + + svs_dir = debugfs_create_dir("svs", NULL); + if (IS_ERR(svs_dir)) { + dev_err(svsp->dev, "cannot create %s: %ld\n", + d, PTR_ERR(svs_dir)); + return PTR_ERR(svs_dir); + } + + for (i = 0; i < ARRAY_SIZE(svs_entries); i++) { + file_entry = debugfs_create_file(svs_entries[i].name, 0664, + svs_dir, svsp, + svs_entries[i].fops); + if (IS_ERR(file_entry)) { + dev_err(svsp->dev, "cannot create %s/%s: %ld\n", + d, svs_entries[i].name, PTR_ERR(file_entry)); + return PTR_ERR(file_entry); + } + } + + for (idx = 0; idx < svsp->bank_max; idx++) { + svsb = &svsp->banks[idx]; + + if (svsb->mode_support == SVSB_MODE_ALL_DISABLE) + continue; + + svsb_dir = debugfs_create_dir(svsb->name, svs_dir); + if (IS_ERR(svsb_dir)) { + dev_err(svsp->dev, "cannot create %s/%s: %ld\n", + d, svsb->name, PTR_ERR(svsb_dir)); + return PTR_ERR(svsb_dir); + } + + for (i = 0; i < ARRAY_SIZE(svsb_entries); i++) { + file_entry = debugfs_create_file(svsb_entries[i].name, + 0664, svsb_dir, svsb, + svsb_entries[i].fops); + if (IS_ERR(file_entry)) { + dev_err(svsp->dev, "no %s/%s/%s?: %ld\n", + d, svsb->name, svsb_entries[i].name, + PTR_ERR(file_entry)); + return PTR_ERR(file_entry); + } + } + } + + return 0; +} + static u32 interpolate(u32 f0, u32 f1, u32 v0, u32 v1, u32 fx) { u32 vx; @@ -591,6 +842,16 @@ static void svs_set_bank_phase(struct svs_platform *svsp, } } +static inline void svs_save_bank_register_data(struct svs_platform *svsp, + enum svsb_phase phase) +{ + struct svs_bank *svsb = svsp->pbank; + enum svs_reg_index rg_i; + + for (rg_i = DESCHAR; rg_i < SVS_REG_MAX; rg_i++) + svsb->reg_data[phase][rg_i] = svs_readl_relaxed(svsp, rg_i); +} + static inline void svs_error_isr_handler(struct svs_platform *svsp) { struct svs_bank *svsb = svsp->pbank; @@ -605,6 +866,8 @@ static inline void svs_error_isr_handler(struct svs_platform *svsp) svs_readl_relaxed(svsp, SMSTATE1)); dev_err(svsb->dev, "TEMP = 0x%08x\n", svs_readl_relaxed(svsp, TEMP)); + svs_save_bank_register_data(svsp, SVSB_PHASE_ERROR); + svsb->phase = SVSB_PHASE_ERROR; svs_writel_relaxed(svsp, SVSB_EN_OFF, SVSEN); svs_writel_relaxed(svsp, SVSB_INTSTS_CLEAN, INTSTS); @@ -619,6 +882,8 @@ static inline void svs_init01_isr_handler(struct svs_platform *svsp) svs_readl_relaxed(svsp, VDESIGN30), svs_readl_relaxed(svsp, DCVALUES)); + svs_save_bank_register_data(svsp, SVSB_PHASE_INIT01); + svsb->phase = SVSB_PHASE_INIT01; svsb->dc_voffset_in = ~(svs_readl_relaxed(svsp, DCVALUES) & GENMASK(15, 0)) + 1; @@ -644,6 +909,8 @@ static inline void svs_init02_isr_handler(struct svs_platform *svsp) svs_readl_relaxed(svsp, VOP30), svs_readl_relaxed(svsp, DCVALUES)); + svs_save_bank_register_data(svsp, SVSB_PHASE_INIT02); + svsb->phase = SVSB_PHASE_INIT02; svsb->get_volts(svsp); @@ -655,6 +922,8 @@ static inline void svs_mon_mode_isr_handler(struct svs_platform *svsp) { struct svs_bank *svsb = svsp->pbank; + svs_save_bank_register_data(svsp, SVSB_PHASE_MON); + svsb->phase = SVSB_PHASE_MON; svsb->get_volts(svsp); @@ -1626,6 +1895,12 @@ static int svs_probe(struct platform_device *pdev) goto svs_probe_iounmap; } + ret = svs_create_debug_cmds(svsp); + if (ret) { + dev_err(svsp->dev, "svs create debug cmds fail: %d\n", ret); + goto svs_probe_iounmap; + } + return 0; svs_probe_iounmap: -- cgit v1.2.3 From 0bbb09b2af9dd24377341899538d3e452c4f7936 Mon Sep 17 00:00:00 2001 From: Roger Lu Date: Mon, 16 May 2022 08:43:11 +0800 Subject: soc: mediatek: SVS: add mt8192 SVS GPU driver mt8192 SVS GPU uses 2-line (high/low bank) HW architecture to provide bank voltages. High bank helps update higher frequency's voltage and low bank helps update lower frequency's voltage. Signed-off-by: Roger Lu Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Kevin Hilman Link: https://lore.kernel.org/r/20220516004311.18358-8-roger.lu@mediatek.com Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-svs.c | 473 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 468 insertions(+), 5 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-svs.c b/drivers/soc/mediatek/mtk-svs.c index aa0665725339..606a00a2e57d 100644 --- a/drivers/soc/mediatek/mtk-svs.c +++ b/drivers/soc/mediatek/mtk-svs.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,10 @@ #define SVSB_CCI BIT(2) #define SVSB_GPU BIT(3) +/* svs bank 2-line type */ +#define SVSB_LOW BIT(8) +#define SVSB_HIGH BIT(9) + /* svs bank mode support */ #define SVSB_MODE_ALL_DISABLE 0 #define SVSB_MODE_INIT01 BIT(1) @@ -45,6 +50,8 @@ #define SVSB_INIT01_PD_REQ BIT(0) #define SVSB_INIT01_VOLT_IGNORE BIT(1) #define SVSB_INIT01_VOLT_INC_ONLY BIT(2) +#define SVSB_MON_VOLT_IGNORE BIT(16) +#define SVSB_REMOVE_DVTFIXED_VOLT BIT(24) /* svs bank register common configuration */ #define SVSB_DET_MAX 0xffff @@ -63,7 +70,9 @@ #define SVSB_RUNCONFIG_DEFAULT 0x80000000 /* svs bank related setting */ +#define BITS8 8 #define MAX_OPP_ENTRIES 16 +#define REG_BYTES 4 #define SVSB_DC_SIGNED_BIT BIT(15) #define SVSB_DET_CLK_EN BIT(31) #define SVSB_TEMP_LOWER_BOUND 0xb2 @@ -250,6 +259,7 @@ static const u32 svs_regs_v2[] = { * @main_clk: main clock for svs bank * @pbank: svs bank pointer needing to be protected by spin_lock section * @banks: svs banks that svs platform supports + * @rst: svs platform reset control * @efuse_parsing: svs platform efuse parsing function pointer * @probe: svs platform probe function pointer * @irqflags: svs platform irq settings flags @@ -267,6 +277,7 @@ struct svs_platform { struct clk *main_clk; struct svs_bank *pbank; struct svs_bank *banks; + struct reset_control *rst; bool (*efuse_parsing)(struct svs_platform *svsp); int (*probe)(struct svs_platform *svsp); unsigned long irqflags; @@ -307,6 +318,7 @@ struct svs_platform_data { * @pm_runtime_enabled_count: bank pm runtime enabled count * @mode_support: bank mode support. * @freq_base: reference frequency for bank init + * @turn_freq_base: refenrece frequency for 2-line turn point * @vboot: voltage request for bank init01 only * @opp_dfreq: default opp frequency table * @opp_dvolt: default opp voltage table @@ -342,6 +354,8 @@ struct svs_platform_data { * @mtdes: svs efuse data * @dcbdet: svs efuse data * @dcmdet: svs efuse data + * @turn_pt: 2-line turn point tells which opp_volt calculated by high/low bank + * @type: bank type to represent it is 2-line (high/low) bank or 1-line bank * * Svs bank will generate suitalbe voltages by below general math equation * and provide these voltages to opp voltage table. @@ -366,6 +380,7 @@ struct svs_bank { u32 pm_runtime_enabled_count; u32 mode_support; u32 freq_base; + u32 turn_freq_base; u32 vboot; u32 opp_dfreq[MAX_OPP_ENTRIES]; u32 opp_dvolt[MAX_OPP_ENTRIES]; @@ -401,6 +416,8 @@ struct svs_bank { u32 mtdes; u32 dcbdet; u32 dcmdet; + u32 turn_pt; + u32 type; }; static u32 percent(u32 numerator, u32 denominator) @@ -436,13 +453,59 @@ static u32 svs_bank_volt_to_opp_volt(u32 svsb_volt, u32 svsb_volt_step, return (svsb_volt * svsb_volt_step) + svsb_volt_base; } +static u32 svs_opp_volt_to_bank_volt(u32 opp_u_volt, u32 svsb_volt_step, + u32 svsb_volt_base) +{ + return (opp_u_volt - svsb_volt_base) / svsb_volt_step; +} + +static int svs_sync_bank_volts_from_opp(struct svs_bank *svsb) +{ + struct dev_pm_opp *opp; + u32 i, opp_u_volt; + + for (i = 0; i < svsb->opp_count; i++) { + opp = dev_pm_opp_find_freq_exact(svsb->opp_dev, + svsb->opp_dfreq[i], + true); + if (IS_ERR(opp)) { + dev_err(svsb->dev, "cannot find freq = %u (%ld)\n", + svsb->opp_dfreq[i], PTR_ERR(opp)); + return PTR_ERR(opp); + } + + opp_u_volt = dev_pm_opp_get_voltage(opp); + svsb->volt[i] = svs_opp_volt_to_bank_volt(opp_u_volt, + svsb->volt_step, + svsb->volt_base); + dev_pm_opp_put(opp); + } + + return 0; +} + static int svs_adjust_pm_opp_volts(struct svs_bank *svsb) { int ret = -EPERM, tzone_temp = 0; - u32 i, svsb_volt, opp_volt, temp_voffset = 0; + u32 i, svsb_volt, opp_volt, temp_voffset = 0, opp_start, opp_stop; mutex_lock(&svsb->lock); + /* + * 2-line bank updates its corresponding opp volts. + * 1-line bank updates all opp volts. + */ + if (svsb->type == SVSB_HIGH) { + opp_start = 0; + opp_stop = svsb->turn_pt; + } else if (svsb->type == SVSB_LOW) { + opp_start = svsb->turn_pt; + opp_stop = svsb->opp_count; + } else { + opp_start = 0; + opp_stop = svsb->opp_count; + } + /* Get thermal effect */ if (svsb->phase == SVSB_PHASE_MON) { ret = thermal_zone_get_temp(svsb->tzd, &tzone_temp); @@ -457,10 +520,16 @@ static int svs_adjust_pm_opp_volts(struct svs_bank *svsb) temp_voffset += svsb->tzone_htemp_voffset; else if (tzone_temp <= svsb->tzone_ltemp) temp_voffset += svsb->tzone_ltemp_voffset; + + /* 2-line bank update all opp volts when running mon mode */ + if (svsb->type == SVSB_HIGH || svsb->type == SVSB_LOW) { + opp_start = 0; + opp_stop = svsb->opp_count; + } } /* vmin <= svsb_volt (opp_volt) <= default opp voltage */ - for (i = 0; i < svsb->opp_count; i++) { + for (i = opp_start; i < opp_stop; i++) { switch (svsb->phase) { case SVSB_PHASE_ERROR: opp_volt = svsb->opp_dvolt[i]; @@ -623,9 +692,11 @@ static int svs_status_debug_show(struct seq_file *m, void *v) ret = thermal_zone_get_temp(svsb->tzd, &tzone_temp); if (ret) - seq_printf(m, "%s: temperature ignore\n", svsb->name); + seq_printf(m, "%s: temperature ignore, turn_pt = %u\n", + svsb->name, svsb->turn_pt); else - seq_printf(m, "%s: temperature = %d\n", svsb->name, tzone_temp); + seq_printf(m, "%s: temperature = %d, turn_pt = %u\n", + svsb->name, tzone_temp, svsb->turn_pt); for (i = 0; i < svsb->opp_count; i++) { opp = dev_pm_opp_find_freq_exact(svsb->opp_dev, @@ -731,6 +802,181 @@ static u32 interpolate(u32 f0, u32 f1, u32 v0, u32 v1, u32 fx) return DIV_ROUND_UP(vx, 100); } +static void svs_get_bank_volts_v3(struct svs_platform *svsp) +{ + struct svs_bank *svsb = svsp->pbank; + u32 i, j, *vop, vop74, vop30, turn_pt = svsb->turn_pt; + u32 b_sft, shift_byte = 0, opp_start = 0, opp_stop = 0; + u32 middle_index = (svsb->opp_count / 2); + + if (svsb->phase == SVSB_PHASE_MON && + svsb->volt_flags & SVSB_MON_VOLT_IGNORE) + return; + + vop74 = svs_readl_relaxed(svsp, VOP74); + vop30 = svs_readl_relaxed(svsp, VOP30); + + /* Target is to set svsb->volt[] by algorithm */ + if (turn_pt < middle_index) { + if (svsb->type == SVSB_HIGH) { + /* volt[0] ~ volt[turn_pt - 1] */ + for (i = 0; i < turn_pt; i++) { + b_sft = BITS8 * (shift_byte % REG_BYTES); + vop = (shift_byte < REG_BYTES) ? &vop30 : + &vop74; + svsb->volt[i] = (*vop >> b_sft) & GENMASK(7, 0); + shift_byte++; + } + } else if (svsb->type == SVSB_LOW) { + /* volt[turn_pt] + volt[j] ~ volt[opp_count - 1] */ + j = svsb->opp_count - 7; + svsb->volt[turn_pt] = vop30 & GENMASK(7, 0); + shift_byte++; + for (i = j; i < svsb->opp_count; i++) { + b_sft = BITS8 * (shift_byte % REG_BYTES); + vop = (shift_byte < REG_BYTES) ? &vop30 : + &vop74; + svsb->volt[i] = (*vop >> b_sft) & GENMASK(7, 0); + shift_byte++; + } + + /* volt[turn_pt + 1] ~ volt[j - 1] by interpolate */ + for (i = turn_pt + 1; i < j; i++) + svsb->volt[i] = interpolate(svsb->freq_pct[turn_pt], + svsb->freq_pct[j], + svsb->volt[turn_pt], + svsb->volt[j], + svsb->freq_pct[i]); + } + } else { + if (svsb->type == SVSB_HIGH) { + /* volt[0] + volt[j] ~ volt[turn_pt - 1] */ + j = turn_pt - 7; + svsb->volt[0] = vop30 & GENMASK(7, 0); + shift_byte++; + for (i = j; i < turn_pt; i++) { + b_sft = BITS8 * (shift_byte % REG_BYTES); + vop = (shift_byte < REG_BYTES) ? &vop30 : + &vop74; + svsb->volt[i] = (*vop >> b_sft) & GENMASK(7, 0); + shift_byte++; + } + + /* volt[1] ~ volt[j - 1] by interpolate */ + for (i = 1; i < j; i++) + svsb->volt[i] = interpolate(svsb->freq_pct[0], + svsb->freq_pct[j], + svsb->volt[0], + svsb->volt[j], + svsb->freq_pct[i]); + } else if (svsb->type == SVSB_LOW) { + /* volt[turn_pt] ~ volt[opp_count - 1] */ + for (i = turn_pt; i < svsb->opp_count; i++) { + b_sft = BITS8 * (shift_byte % REG_BYTES); + vop = (shift_byte < REG_BYTES) ? &vop30 : + &vop74; + svsb->volt[i] = (*vop >> b_sft) & GENMASK(7, 0); + shift_byte++; + } + } + } + + if (svsb->type == SVSB_HIGH) { + opp_start = 0; + opp_stop = svsb->turn_pt; + } else if (svsb->type == SVSB_LOW) { + opp_start = svsb->turn_pt; + opp_stop = svsb->opp_count; + } + + for (i = opp_start; i < opp_stop; i++) + if (svsb->volt_flags & SVSB_REMOVE_DVTFIXED_VOLT) + svsb->volt[i] -= svsb->dvt_fixed; +} + +static void svs_set_bank_freq_pct_v3(struct svs_platform *svsp) +{ + struct svs_bank *svsb = svsp->pbank; + u32 i, j, *freq_pct, freq_pct74 = 0, freq_pct30 = 0; + u32 b_sft, shift_byte = 0, turn_pt; + u32 middle_index = (svsb->opp_count / 2); + + for (i = 0; i < svsb->opp_count; i++) { + if (svsb->opp_dfreq[i] <= svsb->turn_freq_base) { + svsb->turn_pt = i; + break; + } + } + + turn_pt = svsb->turn_pt; + + /* Target is to fill out freq_pct74 / freq_pct30 by algorithm */ + if (turn_pt < middle_index) { + if (svsb->type == SVSB_HIGH) { + /* + * If we don't handle this situation, + * SVSB_HIGH's FREQPCT74 / FREQPCT30 would keep "0" + * and this leads SVSB_LOW to work abnormally. + */ + if (turn_pt == 0) + freq_pct30 = svsb->freq_pct[0]; + + /* freq_pct[0] ~ freq_pct[turn_pt - 1] */ + for (i = 0; i < turn_pt; i++) { + b_sft = BITS8 * (shift_byte % REG_BYTES); + freq_pct = (shift_byte < REG_BYTES) ? + &freq_pct30 : &freq_pct74; + *freq_pct |= (svsb->freq_pct[i] << b_sft); + shift_byte++; + } + } else if (svsb->type == SVSB_LOW) { + /* + * freq_pct[turn_pt] + + * freq_pct[opp_count - 7] ~ freq_pct[opp_count -1] + */ + freq_pct30 = svsb->freq_pct[turn_pt]; + shift_byte++; + j = svsb->opp_count - 7; + for (i = j; i < svsb->opp_count; i++) { + b_sft = BITS8 * (shift_byte % REG_BYTES); + freq_pct = (shift_byte < REG_BYTES) ? + &freq_pct30 : &freq_pct74; + *freq_pct |= (svsb->freq_pct[i] << b_sft); + shift_byte++; + } + } + } else { + if (svsb->type == SVSB_HIGH) { + /* + * freq_pct[0] + + * freq_pct[turn_pt - 7] ~ freq_pct[turn_pt - 1] + */ + freq_pct30 = svsb->freq_pct[0]; + shift_byte++; + j = turn_pt - 7; + for (i = j; i < turn_pt; i++) { + b_sft = BITS8 * (shift_byte % REG_BYTES); + freq_pct = (shift_byte < REG_BYTES) ? + &freq_pct30 : &freq_pct74; + *freq_pct |= (svsb->freq_pct[i] << b_sft); + shift_byte++; + } + } else if (svsb->type == SVSB_LOW) { + /* freq_pct[turn_pt] ~ freq_pct[opp_count - 1] */ + for (i = turn_pt; i < svsb->opp_count; i++) { + b_sft = BITS8 * (shift_byte % REG_BYTES); + freq_pct = (shift_byte < REG_BYTES) ? + &freq_pct30 : &freq_pct74; + *freq_pct |= (svsb->freq_pct[i] << b_sft); + shift_byte++; + } + } + } + + svs_writel_relaxed(svsp, freq_pct74, FREQPCT74); + svs_writel_relaxed(svsp, freq_pct30, FREQPCT30); +} + static void svs_get_bank_volts_v2(struct svs_platform *svsp) { struct svs_bank *svsb = svsp->pbank; @@ -1174,6 +1420,25 @@ static int svs_init02(struct svs_platform *svsp) } } + /* + * 2-line high/low bank update its corresponding opp voltages only. + * Therefore, we sync voltages from opp for high/low bank voltages + * consistency. + */ + for (idx = 0; idx < svsp->bank_max; idx++) { + svsb = &svsp->banks[idx]; + + if (!(svsb->mode_support & SVSB_MODE_INIT02)) + continue; + + if (svsb->type == SVSB_HIGH || svsb->type == SVSB_LOW) { + if (svs_sync_bank_volts_from_opp(svsb)) { + dev_err(svsb->dev, "sync volt fail\n"); + return -EPERM; + } + } + } + return 0; } @@ -1218,6 +1483,7 @@ static int svs_suspend(struct device *dev) struct svs_platform *svsp = dev_get_drvdata(dev); struct svs_bank *svsb; unsigned long flags; + int ret; u32 idx; for (idx = 0; idx < svsp->bank_max; idx++) { @@ -1235,6 +1501,12 @@ static int svs_suspend(struct device *dev) svs_adjust_pm_opp_volts(svsb); } + ret = reset_control_assert(svsp->rst); + if (ret) { + dev_err(svsp->dev, "cannot assert reset %d\n", ret); + return ret; + } + clk_disable_unprepare(svsp->main_clk); return 0; @@ -1251,6 +1523,12 @@ static int svs_resume(struct device *dev) return ret; } + ret = reset_control_deassert(svsp->rst); + if (ret) { + dev_err(svsp->dev, "cannot deassert reset %d\n", ret); + return ret; + } + ret = svs_init02(svsp); if (ret) return ret; @@ -1284,7 +1562,12 @@ static int svs_bank_resource_setup(struct svs_platform *svsp) svsb->name = "SVSB_CCI"; break; case SVSB_GPU: - svsb->name = "SVSB_GPU"; + if (svsb->type == SVSB_HIGH) + svsb->name = "SVSB_GPU_HIGH"; + else if (svsb->type == SVSB_LOW) + svsb->name = "SVSB_GPU_LOW"; + else + svsb->name = "SVSB_GPU"; break; default: dev_err(svsb->dev, "unknown sw_id: %u\n", svsb->sw_id); @@ -1357,6 +1640,85 @@ static int svs_bank_resource_setup(struct svs_platform *svsp) return 0; } +static bool svs_mt8192_efuse_parsing(struct svs_platform *svsp) +{ + struct svs_bank *svsb; + struct nvmem_cell *cell; + u32 idx, i, vmin, golden_temp; + + for (i = 0; i < svsp->efuse_max; i++) + if (svsp->efuse[i]) + dev_info(svsp->dev, "M_HW_RES%d: 0x%08x\n", + i, svsp->efuse[i]); + + if (!svsp->efuse[9]) { + dev_notice(svsp->dev, "svs_efuse[9] = 0x0?\n"); + return false; + } + + /* Svs efuse parsing */ + vmin = (svsp->efuse[19] >> 4) & GENMASK(1, 0); + + for (idx = 0; idx < svsp->bank_max; idx++) { + svsb = &svsp->banks[idx]; + + if (vmin == 0x1) + svsb->vmin = 0x1e; + + if (svsb->type == SVSB_LOW) { + svsb->mtdes = svsp->efuse[10] & GENMASK(7, 0); + svsb->bdes = (svsp->efuse[10] >> 16) & GENMASK(7, 0); + svsb->mdes = (svsp->efuse[10] >> 24) & GENMASK(7, 0); + svsb->dcbdet = (svsp->efuse[17]) & GENMASK(7, 0); + svsb->dcmdet = (svsp->efuse[17] >> 8) & GENMASK(7, 0); + } else if (svsb->type == SVSB_HIGH) { + svsb->mtdes = svsp->efuse[9] & GENMASK(7, 0); + svsb->bdes = (svsp->efuse[9] >> 16) & GENMASK(7, 0); + svsb->mdes = (svsp->efuse[9] >> 24) & GENMASK(7, 0); + svsb->dcbdet = (svsp->efuse[17] >> 16) & GENMASK(7, 0); + svsb->dcmdet = (svsp->efuse[17] >> 24) & GENMASK(7, 0); + } + + svsb->vmax += svsb->dvt_fixed; + } + + /* Thermal efuse parsing */ + cell = nvmem_cell_get(svsp->dev, "t-calibration-data"); + if (IS_ERR_OR_NULL(cell)) { + dev_err(svsp->dev, "no \"t-calibration-data\"? %ld\n", + PTR_ERR(cell)); + return false; + } + + svsp->tefuse = nvmem_cell_read(cell, &svsp->tefuse_max); + if (IS_ERR(svsp->tefuse)) { + dev_err(svsp->dev, "cannot read thermal efuse: %ld\n", + PTR_ERR(svsp->tefuse)); + nvmem_cell_put(cell); + return false; + } + + svsp->tefuse_max /= sizeof(u32); + nvmem_cell_put(cell); + + for (i = 0; i < svsp->tefuse_max; i++) + if (svsp->tefuse[i] != 0) + break; + + if (i == svsp->tefuse_max) + golden_temp = 50; /* All thermal efuse data are 0 */ + else + golden_temp = (svsp->tefuse[0] >> 24) & GENMASK(7, 0); + + for (idx = 0; idx < svsp->bank_max; idx++) { + svsb = &svsp->banks[idx]; + svsb->mts = 500; + svsb->bts = (((500 * golden_temp + 250460) / 1000) - 25) * 4; + } + + return true; +} + static bool svs_mt8183_efuse_parsing(struct svs_platform *svsp) { struct svs_bank *svsb; @@ -1642,6 +2004,39 @@ static struct device *svs_add_device_link(struct svs_platform *svsp, return dev; } +static int svs_mt8192_platform_probe(struct svs_platform *svsp) +{ + struct device *dev; + struct svs_bank *svsb; + u32 idx; + + svsp->rst = devm_reset_control_get_optional(svsp->dev, "svs_rst"); + if (IS_ERR(svsp->rst)) + return dev_err_probe(svsp->dev, PTR_ERR(svsp->rst), + "cannot get svs reset control\n"); + + dev = svs_add_device_link(svsp, "lvts"); + if (IS_ERR(dev)) + return dev_err_probe(svsp->dev, PTR_ERR(dev), + "failed to get lvts device\n"); + + for (idx = 0; idx < svsp->bank_max; idx++) { + svsb = &svsp->banks[idx]; + + if (svsb->type == SVSB_HIGH) + svsb->opp_dev = svs_add_device_link(svsp, "mali"); + else if (svsb->type == SVSB_LOW) + svsb->opp_dev = svs_get_subsys_device(svsp, "mali"); + + if (IS_ERR(svsb->opp_dev)) + return dev_err_probe(svsp->dev, PTR_ERR(svsb->opp_dev), + "failed to get OPP device for bank %d\n", + idx); + } + + return 0; +} + static int svs_mt8183_platform_probe(struct svs_platform *svsp) { struct device *dev; @@ -1681,6 +2076,61 @@ static int svs_mt8183_platform_probe(struct svs_platform *svsp) return 0; } +static struct svs_bank svs_mt8192_banks[] = { + { + .sw_id = SVSB_GPU, + .type = SVSB_LOW, + .set_freq_pct = svs_set_bank_freq_pct_v3, + .get_volts = svs_get_bank_volts_v3, + .volt_flags = SVSB_REMOVE_DVTFIXED_VOLT, + .mode_support = SVSB_MODE_INIT02, + .opp_count = MAX_OPP_ENTRIES, + .freq_base = 688000000, + .turn_freq_base = 688000000, + .volt_step = 6250, + .volt_base = 400000, + .vmax = 0x60, + .vmin = 0x1a, + .age_config = 0x555555, + .dc_config = 0x1, + .dvt_fixed = 0x1, + .vco = 0x18, + .chk_shift = 0x87, + .core_sel = 0x0fff0100, + .int_st = BIT(0), + .ctl0 = 0x00540003, + }, + { + .sw_id = SVSB_GPU, + .type = SVSB_HIGH, + .set_freq_pct = svs_set_bank_freq_pct_v3, + .get_volts = svs_get_bank_volts_v3, + .tzone_name = "gpu1", + .volt_flags = SVSB_REMOVE_DVTFIXED_VOLT | + SVSB_MON_VOLT_IGNORE, + .mode_support = SVSB_MODE_INIT02 | SVSB_MODE_MON, + .opp_count = MAX_OPP_ENTRIES, + .freq_base = 902000000, + .turn_freq_base = 688000000, + .volt_step = 6250, + .volt_base = 400000, + .vmax = 0x60, + .vmin = 0x1a, + .age_config = 0x555555, + .dc_config = 0x1, + .dvt_fixed = 0x6, + .vco = 0x18, + .chk_shift = 0x87, + .core_sel = 0x0fff0101, + .int_st = BIT(1), + .ctl0 = 0x00540003, + .tzone_htemp = 85000, + .tzone_htemp_voffset = 0, + .tzone_ltemp = 25000, + .tzone_ltemp_voffset = 7, + }, +}; + static struct svs_bank svs_mt8183_banks[] = { { .sw_id = SVSB_CPU_LITTLE, @@ -1785,6 +2235,16 @@ static struct svs_bank svs_mt8183_banks[] = { }, }; +static const struct svs_platform_data svs_mt8192_platform_data = { + .name = "mt8192-svs", + .banks = svs_mt8192_banks, + .efuse_parsing = svs_mt8192_efuse_parsing, + .probe = svs_mt8192_platform_probe, + .irqflags = IRQF_TRIGGER_HIGH, + .regs = svs_regs_v2, + .bank_max = ARRAY_SIZE(svs_mt8192_banks), +}; + static const struct svs_platform_data svs_mt8183_platform_data = { .name = "mt8183-svs", .banks = svs_mt8183_banks, @@ -1797,6 +2257,9 @@ static const struct svs_platform_data svs_mt8183_platform_data = { static const struct of_device_id svs_of_match[] = { { + .compatible = "mediatek,mt8192-svs", + .data = &svs_mt8192_platform_data, + }, { .compatible = "mediatek,mt8183-svs", .data = &svs_mt8183_platform_data, }, { -- cgit v1.2.3 From 141311b856d8ab8ef5903e24d66c3a17d4ee31ae Mon Sep 17 00:00:00 2001 From: Fabien Parent Date: Mon, 30 May 2022 22:14:33 +0200 Subject: soc: mediatek: mutex: add MT8365 support Add mutex support for MT8365 SoC. Signed-off-by: Fabien Parent Reviewed-by: CK Hu Link: https://lore.kernel.org/r/20220530201436.902505-4-fparent@baylibre.com Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-mutex.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-mutex.c b/drivers/soc/mediatek/mtk-mutex.c index 981d56967e7a..b8d5c4a62542 100644 --- a/drivers/soc/mediatek/mtk-mutex.c +++ b/drivers/soc/mediatek/mtk-mutex.c @@ -110,6 +110,20 @@ #define MT8195_MUTEX_MOD_DISP_DP_INTF0 21 #define MT8195_MUTEX_MOD_DISP_PWM0 27 +#define MT8365_MUTEX_MOD_DISP_OVL0 7 +#define MT8365_MUTEX_MOD_DISP_OVL0_2L 8 +#define MT8365_MUTEX_MOD_DISP_RDMA0 9 +#define MT8365_MUTEX_MOD_DISP_RDMA1 10 +#define MT8365_MUTEX_MOD_DISP_WDMA0 11 +#define MT8365_MUTEX_MOD_DISP_COLOR0 12 +#define MT8365_MUTEX_MOD_DISP_CCORR 13 +#define MT8365_MUTEX_MOD_DISP_AAL 14 +#define MT8365_MUTEX_MOD_DISP_GAMMA 15 +#define MT8365_MUTEX_MOD_DISP_DITHER 16 +#define MT8365_MUTEX_MOD_DISP_DSI0 17 +#define MT8365_MUTEX_MOD_DISP_PWM0 20 +#define MT8365_MUTEX_MOD_DISP_DPI0 22 + #define MT2712_MUTEX_MOD_DISP_PWM2 10 #define MT2712_MUTEX_MOD_DISP_OVL0 11 #define MT2712_MUTEX_MOD_DISP_OVL1 12 @@ -315,6 +329,22 @@ static const unsigned int mt8195_mutex_mod[DDP_COMPONENT_ID_MAX] = { [DDP_COMPONENT_DP_INTF0] = MT8195_MUTEX_MOD_DISP_DP_INTF0, }; +static const unsigned int mt8365_mutex_mod[DDP_COMPONENT_ID_MAX] = { + [DDP_COMPONENT_AAL0] = MT8365_MUTEX_MOD_DISP_AAL, + [DDP_COMPONENT_CCORR] = MT8365_MUTEX_MOD_DISP_CCORR, + [DDP_COMPONENT_COLOR0] = MT8365_MUTEX_MOD_DISP_COLOR0, + [DDP_COMPONENT_DITHER] = MT8365_MUTEX_MOD_DISP_DITHER, + [DDP_COMPONENT_DPI0] = MT8365_MUTEX_MOD_DISP_DPI0, + [DDP_COMPONENT_DSI0] = MT8365_MUTEX_MOD_DISP_DSI0, + [DDP_COMPONENT_GAMMA] = MT8365_MUTEX_MOD_DISP_GAMMA, + [DDP_COMPONENT_OVL0] = MT8365_MUTEX_MOD_DISP_OVL0, + [DDP_COMPONENT_OVL_2L0] = MT8365_MUTEX_MOD_DISP_OVL0_2L, + [DDP_COMPONENT_PWM0] = MT8365_MUTEX_MOD_DISP_PWM0, + [DDP_COMPONENT_RDMA0] = MT8365_MUTEX_MOD_DISP_RDMA0, + [DDP_COMPONENT_RDMA1] = MT8365_MUTEX_MOD_DISP_RDMA1, + [DDP_COMPONENT_WDMA0] = MT8365_MUTEX_MOD_DISP_WDMA0, +}; + static const unsigned int mt2712_mutex_sof[DDP_MUTEX_SOF_MAX] = { [MUTEX_SOF_SINGLE_MODE] = MUTEX_SOF_SINGLE_MODE, [MUTEX_SOF_DSI0] = MUTEX_SOF_DSI0, @@ -423,6 +453,14 @@ static const struct mtk_mutex_data mt8195_mutex_driver_data = { .mutex_sof_reg = MT8183_MUTEX0_SOF0, }; +static const struct mtk_mutex_data mt8365_mutex_driver_data = { + .mutex_mod = mt8365_mutex_mod, + .mutex_sof = mt8183_mutex_sof, + .mutex_mod_reg = MT8183_MUTEX0_MOD0, + .mutex_sof_reg = MT8183_MUTEX0_SOF0, + .no_clk = true, +}; + struct mtk_mutex *mtk_mutex_get(struct device *dev) { struct mtk_mutex_ctx *mtx = dev_get_drvdata(dev); @@ -665,6 +703,8 @@ static const struct of_device_id mutex_driver_dt_match[] = { .data = &mt8192_mutex_driver_data}, { .compatible = "mediatek,mt8195-disp-mutex", .data = &mt8195_mutex_driver_data}, + { .compatible = "mediatek,mt8365-disp-mutex", + .data = &mt8365_mutex_driver_data}, {}, }; MODULE_DEVICE_TABLE(of, mutex_driver_dt_match); -- cgit v1.2.3 From 38077cb39b42edc5cf22500af94335827b171af9 Mon Sep 17 00:00:00 2001 From: Fabien Parent Date: Mon, 30 May 2022 22:14:34 +0200 Subject: soc: mediatek: mt8365-mmsys: add DPI/HDMI display path Right now only the DSI path connections are described in the mt8365 mmsys driver. The external path will be DPI/HDMI. This commit adds the connections for DPI/HDMI. Signed-off-by: Fabien Parent Reviewed-by: CK Hu Link: https://lore.kernel.org/r/20220530201436.902505-5-fparent@baylibre.com Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mt8365-mmsys.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mt8365-mmsys.h b/drivers/soc/mediatek/mt8365-mmsys.h index 24129a6c25f8..7abaf048d91e 100644 --- a/drivers/soc/mediatek/mt8365-mmsys.h +++ b/drivers/soc/mediatek/mt8365-mmsys.h @@ -10,6 +10,9 @@ #define MT8365_DISP_REG_CONFIG_DISP_RDMA0_RSZ0_SEL_IN 0xf60 #define MT8365_DISP_REG_CONFIG_DISP_COLOR0_SEL_IN 0xf64 #define MT8365_DISP_REG_CONFIG_DISP_DSI0_SEL_IN 0xf68 +#define MT8365_DISP_REG_CONFIG_DISP_RDMA1_SOUT_SEL 0xfd0 +#define MT8365_DISP_REG_CONFIG_DISP_DPI0_SEL_IN 0xfd8 +#define MT8365_DISP_REG_CONFIG_DISP_LVDS_SYS_CFG_00 0xfdc #define MT8365_RDMA0_SOUT_COLOR0 0x1 #define MT8365_DITHER_MOUT_EN_DSI0 0x1 @@ -18,6 +21,10 @@ #define MT8365_RDMA0_RSZ0_SEL_IN_RDMA0 0x0 #define MT8365_DISP_COLOR_SEL_IN_COLOR0 0x0 #define MT8365_OVL0_MOUT_PATH0_SEL BIT(0) +#define MT8365_RDMA1_SOUT_DPI0 0x1 +#define MT8365_DPI0_SEL_IN_RDMA1 0x0 +#define MT8365_LVDS_SYS_CFG_00_SEL_LVDS_PXL_CLK 0x1 +#define MT8365_DPI0_SEL_IN_RDMA1 0x0 static const struct mtk_mmsys_routes mt8365_mmsys_routing_table[] = { { @@ -55,6 +62,21 @@ static const struct mtk_mmsys_routes mt8365_mmsys_routing_table[] = { MT8365_DISP_REG_CONFIG_DISP_RDMA0_RSZ0_SEL_IN, MT8365_RDMA0_RSZ0_SEL_IN_RDMA0, MT8365_RDMA0_RSZ0_SEL_IN_RDMA0 }, + { + DDP_COMPONENT_RDMA1, DDP_COMPONENT_DPI0, + MT8365_DISP_REG_CONFIG_DISP_LVDS_SYS_CFG_00, + MT8365_LVDS_SYS_CFG_00_SEL_LVDS_PXL_CLK, MT8365_LVDS_SYS_CFG_00_SEL_LVDS_PXL_CLK + }, + { + DDP_COMPONENT_RDMA1, DDP_COMPONENT_DPI0, + MT8365_DISP_REG_CONFIG_DISP_DPI0_SEL_IN, + MT8365_DPI0_SEL_IN_RDMA1, MT8365_DPI0_SEL_IN_RDMA1 + }, + { + DDP_COMPONENT_RDMA1, DDP_COMPONENT_DPI0, + MT8365_DISP_REG_CONFIG_DISP_RDMA1_SOUT_SEL, + MT8365_RDMA1_SOUT_DPI0, MT8365_RDMA1_SOUT_DPI0 + }, }; #endif /* __SOC_MEDIATEK_MT8365_MMSYS_H */ -- cgit v1.2.3 From 72be1e7a447add1536653592b317e93aed46588d Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Fri, 17 Jun 2022 17:58:41 +0800 Subject: soc: mediatek: pm-domains: Add support always on flag There is a t-phy shared by PCIe and USB3 on mt8195, if the t-phy is used by PCIe, when power off its mtcmos, need software reset it (workaround way, usually hardware do it, but has an issue on mt8195), but it has side effect to USB2 phy(works with USB3 phy to support USB3.2 Gen1), so add support GENPD_FLAG_ALWAYS_ON flag, and make its power always on. Another reason is that USB3.2 Gen1/2 need keep power always on when support runtime-pm due to hardware limitation until now. Signed-off-by: Chunfeng Yun Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20220617095841.23031-1-chunfeng.yun@mediatek.com [mb: smaller nit fixes on commit message] Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mt8195-pm-domains.h | 2 +- drivers/soc/mediatek/mtk-pm-domains.c | 3 +++ drivers/soc/mediatek/mtk-pm-domains.h | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mt8195-pm-domains.h b/drivers/soc/mediatek/mt8195-pm-domains.h index 938f4d51f5ae..0529d130b675 100644 --- a/drivers/soc/mediatek/mt8195-pm-domains.h +++ b/drivers/soc/mediatek/mt8195-pm-domains.h @@ -67,7 +67,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8195[] = { .ctl_offs = 0x334, .pwr_sta_offs = 0x174, .pwr_sta2nd_offs = 0x178, - .caps = MTK_SCPD_ACTIVE_WAKEUP, + .caps = MTK_SCPD_ACTIVE_WAKEUP | MTK_SCPD_ALWAYS_ON, }, [MT8195_POWER_DOMAIN_CSI_RX_TOP] = { .name = "csi_rx_top", diff --git a/drivers/soc/mediatek/mtk-pm-domains.c b/drivers/soc/mediatek/mtk-pm-domains.c index 5ced254b082b..a3dae391a38a 100644 --- a/drivers/soc/mediatek/mtk-pm-domains.c +++ b/drivers/soc/mediatek/mtk-pm-domains.c @@ -428,6 +428,9 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no dev_err(scpsys->dev, "%pOF: failed to power on domain: %d\n", node, ret); goto err_put_subsys_clocks; } + + if (MTK_SCPD_CAPS(pd, MTK_SCPD_ALWAYS_ON)) + pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON; } if (scpsys->domains[id]) { diff --git a/drivers/soc/mediatek/mtk-pm-domains.h b/drivers/soc/mediatek/mtk-pm-domains.h index daa24e890dd4..7d3c0c36316c 100644 --- a/drivers/soc/mediatek/mtk-pm-domains.h +++ b/drivers/soc/mediatek/mtk-pm-domains.h @@ -8,6 +8,8 @@ #define MTK_SCPD_SRAM_ISO BIT(2) #define MTK_SCPD_KEEP_DEFAULT_OFF BIT(3) #define MTK_SCPD_DOMAIN_SUPPLY BIT(4) +/* can't set MTK_SCPD_KEEP_DEFAULT_OFF at the same time */ +#define MTK_SCPD_ALWAYS_ON BIT(5) #define MTK_SCPD_CAPS(_scpd, _x) ((_scpd)->data->caps & (_x)) #define SPM_VDE_PWR_CON 0x0210 -- cgit v1.2.3 From d0804085c5a7feb557bd3219777b51f5598dfdc6 Mon Sep 17 00:00:00 2001 From: Moudy Ho Date: Fri, 10 Jun 2022 14:34:18 +0800 Subject: soc: mediatek: mutex: add common interface for modules setting In order to allow multiple modules to operate MUTEX hardware through a common interfrace, two flexible indexes "mtk_mutex_mod_index" and "mtk_mutex_sof_index" need to be added to replace original component ID so that like DDP and MDP can add their own MOD table or SOF settings independently. In addition, 2 generic interface "mtk_mutex_write_mod" and "mtk_mutex_write_sof" have been added, which is expected to replace the "mtk_mutex_add_comp" and "mtk_mutex_remove_comp" pair originally dedicated to DDP in the future. Signed-off-by: Moudy Ho Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Rex-BC Chen Reviewed-by: CK Hu Link: https://lore.kernel.org/r/20220610063424.7800-2-moudy.ho@mediatek.com Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-mutex.c | 53 ++++++++++++++++++++++++++++++++++ include/linux/soc/mediatek/mtk-mutex.h | 25 ++++++++++++++++ 2 files changed, 78 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-mutex.c b/drivers/soc/mediatek/mtk-mutex.c index b8d5c4a62542..56987e2ff906 100644 --- a/drivers/soc/mediatek/mtk-mutex.c +++ b/drivers/soc/mediatek/mtk-mutex.c @@ -199,6 +199,7 @@ struct mtk_mutex_data { const unsigned int *mutex_sof; const unsigned int mutex_mod_reg; const unsigned int mutex_sof_reg; + const unsigned int *mutex_table_mod; const bool no_clk; }; @@ -644,6 +645,58 @@ void mtk_mutex_release(struct mtk_mutex *mutex) } EXPORT_SYMBOL_GPL(mtk_mutex_release); +int mtk_mutex_write_mod(struct mtk_mutex *mutex, + enum mtk_mutex_mod_index idx, bool clear) +{ + struct mtk_mutex_ctx *mtx = container_of(mutex, struct mtk_mutex_ctx, + mutex[mutex->id]); + unsigned int reg; + unsigned int offset; + + WARN_ON(&mtx->mutex[mutex->id] != mutex); + + if (idx < MUTEX_MOD_IDX_MDP_RDMA0 || + idx >= MUTEX_MOD_IDX_MAX) { + dev_err(mtx->dev, "Not supported MOD table index : %d", idx); + return -EINVAL; + } + + offset = DISP_REG_MUTEX_MOD(mtx->data->mutex_mod_reg, + mutex->id); + reg = readl_relaxed(mtx->regs + offset); + + if (clear) + reg &= ~BIT(mtx->data->mutex_table_mod[idx]); + else + reg |= BIT(mtx->data->mutex_table_mod[idx]); + + writel_relaxed(reg, mtx->regs + offset); + + return 0; +} +EXPORT_SYMBOL_GPL(mtk_mutex_write_mod); + +int mtk_mutex_write_sof(struct mtk_mutex *mutex, + enum mtk_mutex_sof_index idx) +{ + struct mtk_mutex_ctx *mtx = container_of(mutex, struct mtk_mutex_ctx, + mutex[mutex->id]); + + WARN_ON(&mtx->mutex[mutex->id] != mutex); + + if (idx < MUTEX_SOF_IDX_SINGLE_MODE || + idx >= MUTEX_SOF_IDX_MAX) { + dev_err(mtx->dev, "Not supported SOF index : %d", idx); + return -EINVAL; + } + + writel_relaxed(idx, mtx->regs + + DISP_REG_MUTEX_SOF(mtx->data->mutex_sof_reg, mutex->id)); + + return 0; +} +EXPORT_SYMBOL_GPL(mtk_mutex_write_sof); + static int mtk_mutex_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; diff --git a/include/linux/soc/mediatek/mtk-mutex.h b/include/linux/soc/mediatek/mtk-mutex.h index 6fe4ffbde290..2ddab9d2b85d 100644 --- a/include/linux/soc/mediatek/mtk-mutex.h +++ b/include/linux/soc/mediatek/mtk-mutex.h @@ -10,6 +10,26 @@ struct regmap; struct device; struct mtk_mutex; +enum mtk_mutex_mod_index { + /* MDP table index */ + MUTEX_MOD_IDX_MDP_RDMA0, + MUTEX_MOD_IDX_MDP_RSZ0, + MUTEX_MOD_IDX_MDP_RSZ1, + MUTEX_MOD_IDX_MDP_TDSHP0, + MUTEX_MOD_IDX_MDP_WROT0, + MUTEX_MOD_IDX_MDP_WDMA, + MUTEX_MOD_IDX_MDP_AAL0, + MUTEX_MOD_IDX_MDP_CCORR0, + + MUTEX_MOD_IDX_MAX /* ALWAYS keep at the end */ +}; + +enum mtk_mutex_sof_index { + MUTEX_SOF_IDX_SINGLE_MODE, + + MUTEX_SOF_IDX_MAX /* ALWAYS keep at the end */ +}; + struct mtk_mutex *mtk_mutex_get(struct device *dev); int mtk_mutex_prepare(struct mtk_mutex *mutex); void mtk_mutex_add_comp(struct mtk_mutex *mutex, @@ -22,5 +42,10 @@ void mtk_mutex_unprepare(struct mtk_mutex *mutex); void mtk_mutex_put(struct mtk_mutex *mutex); void mtk_mutex_acquire(struct mtk_mutex *mutex); void mtk_mutex_release(struct mtk_mutex *mutex); +int mtk_mutex_write_mod(struct mtk_mutex *mutex, + enum mtk_mutex_mod_index idx, + bool clear); +int mtk_mutex_write_sof(struct mtk_mutex *mutex, + enum mtk_mutex_sof_index idx); #endif /* MTK_MUTEX_H */ -- cgit v1.2.3 From 2c9f8d1e01af64dcc36cea0ff5a49fc625bc815a Mon Sep 17 00:00:00 2001 From: Moudy Ho Date: Fri, 10 Jun 2022 14:34:19 +0800 Subject: soc: mediatek: mutex: add 8183 MUTEX MOD settings for MDP For the purpose of module independence, related settings should be moved from MDP to the corresponding driver. This patch adds 8183 MUTEX MOD settings for MDP. Signed-off-by: Moudy Ho Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Rex-BC Chen Reviewed-by: CK Hu Link: https://lore.kernel.org/r/20220610063424.7800-3-moudy.ho@mediatek.com Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-mutex.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-mutex.c b/drivers/soc/mediatek/mtk-mutex.c index 56987e2ff906..a566f4cf6372 100644 --- a/drivers/soc/mediatek/mtk-mutex.c +++ b/drivers/soc/mediatek/mtk-mutex.c @@ -80,6 +80,15 @@ #define MT8183_MUTEX_MOD_DISP_GAMMA0 16 #define MT8183_MUTEX_MOD_DISP_DITHER0 17 +#define MT8183_MUTEX_MOD_MDP_RDMA0 2 +#define MT8183_MUTEX_MOD_MDP_RSZ0 4 +#define MT8183_MUTEX_MOD_MDP_RSZ1 5 +#define MT8183_MUTEX_MOD_MDP_TDSHP0 6 +#define MT8183_MUTEX_MOD_MDP_WROT0 7 +#define MT8183_MUTEX_MOD_MDP_WDMA 8 +#define MT8183_MUTEX_MOD_MDP_AAL0 23 +#define MT8183_MUTEX_MOD_MDP_CCORR0 24 + #define MT8173_MUTEX_MOD_DISP_OVL0 11 #define MT8173_MUTEX_MOD_DISP_OVL1 12 #define MT8173_MUTEX_MOD_DISP_RDMA0 13 @@ -287,6 +296,17 @@ static const unsigned int mt8183_mutex_mod[DDP_COMPONENT_ID_MAX] = { [DDP_COMPONENT_WDMA0] = MT8183_MUTEX_MOD_DISP_WDMA0, }; +static const unsigned int mt8183_mutex_table_mod[MUTEX_MOD_IDX_MAX] = { + [MUTEX_MOD_IDX_MDP_RDMA0] = MT8183_MUTEX_MOD_MDP_RDMA0, + [MUTEX_MOD_IDX_MDP_RSZ0] = MT8183_MUTEX_MOD_MDP_RSZ0, + [MUTEX_MOD_IDX_MDP_RSZ1] = MT8183_MUTEX_MOD_MDP_RSZ1, + [MUTEX_MOD_IDX_MDP_TDSHP0] = MT8183_MUTEX_MOD_MDP_TDSHP0, + [MUTEX_MOD_IDX_MDP_WROT0] = MT8183_MUTEX_MOD_MDP_WROT0, + [MUTEX_MOD_IDX_MDP_WDMA] = MT8183_MUTEX_MOD_MDP_WDMA, + [MUTEX_MOD_IDX_MDP_AAL0] = MT8183_MUTEX_MOD_MDP_AAL0, + [MUTEX_MOD_IDX_MDP_CCORR0] = MT8183_MUTEX_MOD_MDP_CCORR0, +}; + static const unsigned int mt8186_mutex_mod[DDP_COMPONENT_ID_MAX] = { [DDP_COMPONENT_AAL0] = MT8186_MUTEX_MOD_DISP_AAL0, [DDP_COMPONENT_CCORR] = MT8186_MUTEX_MOD_DISP_CCORR0, @@ -430,6 +450,7 @@ static const struct mtk_mutex_data mt8183_mutex_driver_data = { .mutex_sof = mt8183_mutex_sof, .mutex_mod_reg = MT8183_MUTEX0_MOD0, .mutex_sof_reg = MT8183_MUTEX0_SOF0, + .mutex_table_mod = mt8183_mutex_table_mod, .no_clk = true, }; -- cgit v1.2.3 From e5758850c2ea448dd750a280d128a3590d68b899 Mon Sep 17 00:00:00 2001 From: Moudy Ho Date: Fri, 10 Jun 2022 14:34:23 +0800 Subject: soc: mediatek: mutex: add functions that operate registers by CMDQ Due to HW limitations, MDP3 is necessary to enable MUTEX in each frame for SOF triggering and cooperate with CMDQ control to reduce the amount of interrupts generated(also, reduce frame latency). In response to the above situation, a new interface "mtk_mutex_enable_by_cmdq" has been added to achieve the purpose. Signed-off-by: Moudy Ho Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Rex-BC Chen Reviewed-by: CK Hu Link: https://lore.kernel.org/r/20220610063424.7800-7-moudy.ho@mediatek.com Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-mutex.c | 45 +++++++++++++++++++++++++++++++++- include/linux/soc/mediatek/mtk-mutex.h | 2 ++ 2 files changed, 46 insertions(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-mutex.c b/drivers/soc/mediatek/mtk-mutex.c index a566f4cf6372..fa8e0ba38803 100644 --- a/drivers/soc/mediatek/mtk-mutex.c +++ b/drivers/soc/mediatek/mtk-mutex.c @@ -7,10 +7,12 @@ #include #include #include +#include #include #include #include #include +#include #define MT2701_MUTEX0_MOD0 0x2c #define MT2701_MUTEX0_SOF0 0x30 @@ -218,6 +220,8 @@ struct mtk_mutex_ctx { void __iomem *regs; struct mtk_mutex mutex[10]; const struct mtk_mutex_data *data; + phys_addr_t addr; + struct cmdq_client_reg cmdq_reg; }; static const unsigned int mt2701_mutex_mod[DDP_COMPONENT_ID_MAX] = { @@ -632,6 +636,30 @@ void mtk_mutex_enable(struct mtk_mutex *mutex) } EXPORT_SYMBOL_GPL(mtk_mutex_enable); +int mtk_mutex_enable_by_cmdq(struct mtk_mutex *mutex, void *pkt) +{ + struct mtk_mutex_ctx *mtx = container_of(mutex, struct mtk_mutex_ctx, + mutex[mutex->id]); +#if IS_REACHABLE(CONFIG_MTK_CMDQ) + struct cmdq_pkt *cmdq_pkt = (struct cmdq_pkt *)pkt; + + WARN_ON(&mtx->mutex[mutex->id] != mutex); + + if (!mtx->cmdq_reg.size) { + dev_err(mtx->dev, "mediatek,gce-client-reg hasn't been set"); + return -EINVAL; + } + + cmdq_pkt_write(cmdq_pkt, mtx->cmdq_reg.subsys, + mtx->addr + DISP_REG_MUTEX_EN(mutex->id), 1); + return 0; +#else + dev_err(mtx->dev, "Not support for enable MUTEX by CMDQ"); + return -ENODEV; +#endif +} +EXPORT_SYMBOL_GPL(mtk_mutex_enable_by_cmdq); + void mtk_mutex_disable(struct mtk_mutex *mutex) { struct mtk_mutex_ctx *mtx = container_of(mutex, struct mtk_mutex_ctx, @@ -722,8 +750,11 @@ static int mtk_mutex_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct mtk_mutex_ctx *mtx; - struct resource *regs; + struct resource *regs, addr; int i; +#if IS_REACHABLE(CONFIG_MTK_CMDQ) + int ret; +#endif mtx = devm_kzalloc(dev, sizeof(*mtx), GFP_KERNEL); if (!mtx) @@ -743,6 +774,18 @@ static int mtk_mutex_probe(struct platform_device *pdev) } } + if (of_address_to_resource(dev->of_node, 0, &addr) < 0) { + dev_err(dev, "Failed to get addr\n"); + return -EINVAL; + } + mtx->addr = addr.start; + +#if IS_REACHABLE(CONFIG_MTK_CMDQ) + ret = cmdq_dev_get_client_reg(dev, &mtx->cmdq_reg, 0); + if (ret) + dev_dbg(dev, "No mediatek,gce-client-reg!\n"); +#endif + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); mtx->regs = devm_ioremap_resource(dev, regs); if (IS_ERR(mtx->regs)) { diff --git a/include/linux/soc/mediatek/mtk-mutex.h b/include/linux/soc/mediatek/mtk-mutex.h index 2ddab9d2b85d..a0f4f51a3b45 100644 --- a/include/linux/soc/mediatek/mtk-mutex.h +++ b/include/linux/soc/mediatek/mtk-mutex.h @@ -35,6 +35,8 @@ int mtk_mutex_prepare(struct mtk_mutex *mutex); void mtk_mutex_add_comp(struct mtk_mutex *mutex, enum mtk_ddp_comp_id id); void mtk_mutex_enable(struct mtk_mutex *mutex); +int mtk_mutex_enable_by_cmdq(struct mtk_mutex *mutex, + void *pkt); void mtk_mutex_disable(struct mtk_mutex *mutex); void mtk_mutex_remove_comp(struct mtk_mutex *mutex, enum mtk_ddp_comp_id id); -- cgit v1.2.3 From 9a073d4fbb182be8ee9d1539ed0484800f3fc832 Mon Sep 17 00:00:00 2001 From: Liang He Date: Wed, 15 Jun 2022 22:02:31 +0800 Subject: soc: bcm: brcmstb: biuctrl: Add missing of_node_put() In brcmstb_biuctrl_init(), of_find_compatible_node() will return a node pointer with refcount incremented. We should use of_node_put() in each fail path or when it is not used anymore. Signed-off-by: Liang He Signed-off-by: Florian Fainelli --- drivers/soc/bcm/brcmstb/biuctrl.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/bcm/brcmstb/biuctrl.c b/drivers/soc/bcm/brcmstb/biuctrl.c index 2c975d79fe8e..1467bbd59690 100644 --- a/drivers/soc/bcm/brcmstb/biuctrl.c +++ b/drivers/soc/bcm/brcmstb/biuctrl.c @@ -340,12 +340,12 @@ static int __init brcmstb_biuctrl_init(void) ret = setup_hifcpubiuctrl_regs(np); if (ret) - return ret; + goto out_put; ret = mcp_write_pairing_set(); if (ret) { pr_err("MCP: Unable to disable write pairing!\n"); - return ret; + goto out_put; } a72_b53_rac_enable_all(np); @@ -353,6 +353,9 @@ static int __init brcmstb_biuctrl_init(void) #ifdef CONFIG_PM_SLEEP register_syscore_ops(&brcmstb_cpu_credit_syscore_ops); #endif - return 0; + ret = 0; +out_put: + of_node_put(np); + return ret; } early_initcall(brcmstb_biuctrl_init); -- cgit v1.2.3 From b710673e2ddef96ccf74185e72e379520d7984fd Mon Sep 17 00:00:00 2001 From: Yang Li Date: Thu, 5 May 2022 06:51:25 +0800 Subject: soc: ti: wkup_m3_ipc: Remove unneeded semicolon Eliminate the following coccicheck warning: ./drivers/soc/ti/wkup_m3_ipc.c:691:2-3: Unneeded semicolon Reported-by: Abaci Robot Signed-off-by: Yang Li Signed-off-by: Nishanth Menon Link: https://lore.kernel.org/r/20220504225125.45830-1-yang.lee@linux.alibaba.com --- drivers/soc/ti/wkup_m3_ipc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/ti/wkup_m3_ipc.c b/drivers/soc/ti/wkup_m3_ipc.c index 0076d467ff6b..343c58ed5896 100644 --- a/drivers/soc/ti/wkup_m3_ipc.c +++ b/drivers/soc/ti/wkup_m3_ipc.c @@ -688,7 +688,7 @@ static int wkup_m3_ipc_probe(struct platform_device *pdev) &m3_ipc->sd_fw_name); if (ret) { dev_dbg(dev, "Voltage scaling data blob not provided from DT.\n"); - }; + } /* * Wait for firmware loading completion in a thread so we -- cgit v1.2.3 From 43b240d41b5173eaebb825f19c423b4278435d2c Mon Sep 17 00:00:00 2001 From: Martin Kepplinger Date: Thu, 9 Jun 2022 13:17:07 +0200 Subject: soc: imx: gpcv2: print errno for regulator errors Make debugging of power management issues easier by printing the reason why a regulator fails to be enabled or disabled. Signed-off-by: Martin Kepplinger Signed-off-by: Shawn Guo --- drivers/soc/imx/gpcv2.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c index 85aa86e1338a..6383a4edc360 100644 --- a/drivers/soc/imx/gpcv2.c +++ b/drivers/soc/imx/gpcv2.c @@ -328,7 +328,9 @@ static int imx_pgc_power_up(struct generic_pm_domain *genpd) if (!IS_ERR(domain->regulator)) { ret = regulator_enable(domain->regulator); if (ret) { - dev_err(domain->dev, "failed to enable regulator\n"); + dev_err(domain->dev, + "failed to enable regulator: %pe\n", + ERR_PTR(ret)); goto out_put_pm; } } @@ -467,7 +469,9 @@ static int imx_pgc_power_down(struct generic_pm_domain *genpd) if (!IS_ERR(domain->regulator)) { ret = regulator_disable(domain->regulator); if (ret) { - dev_err(domain->dev, "failed to disable regulator\n"); + dev_err(domain->dev, + "failed to disable regulator: %pe\n", + ERR_PTR(ret)); return ret; } } -- cgit v1.2.3 From 44e36d7552760716525a8aba62a6bcbd48f3559d Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 20 Jun 2022 12:24:54 +0200 Subject: soc: mediatek: mutex: Use DDP_COMPONENT_DITHER0 mod index for MT8365 In commit 4e8988c634a1 ("soc: mediatek: add DDP_DOMPONENT_DITHER0 enum for mt8195 vdosys0") the enum mtk_ddp_comp_id was modified to add an index number to the DITHER component because some new SoCs have multiple dither blocks. As a result, all of the mutex mod arrays have been changed to use the new definition instead of the old one (even if that's retained): for consistency purposes, follow the same trend on the newly introduced MT8365 mutex mod array. This commit brings no functional changes. Signed-off-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20220620102454.131417-1-angelogioacchino.delregno@collabora.com Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-mutex.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-mutex.c b/drivers/soc/mediatek/mtk-mutex.c index fa8e0ba38803..2fd8318c3bdc 100644 --- a/drivers/soc/mediatek/mtk-mutex.c +++ b/drivers/soc/mediatek/mtk-mutex.c @@ -358,7 +358,7 @@ static const unsigned int mt8365_mutex_mod[DDP_COMPONENT_ID_MAX] = { [DDP_COMPONENT_AAL0] = MT8365_MUTEX_MOD_DISP_AAL, [DDP_COMPONENT_CCORR] = MT8365_MUTEX_MOD_DISP_CCORR, [DDP_COMPONENT_COLOR0] = MT8365_MUTEX_MOD_DISP_COLOR0, - [DDP_COMPONENT_DITHER] = MT8365_MUTEX_MOD_DISP_DITHER, + [DDP_COMPONENT_DITHER0] = MT8365_MUTEX_MOD_DISP_DITHER, [DDP_COMPONENT_DPI0] = MT8365_MUTEX_MOD_DISP_DPI0, [DDP_COMPONENT_DSI0] = MT8365_MUTEX_MOD_DISP_DSI0, [DDP_COMPONENT_GAMMA] = MT8365_MUTEX_MOD_DISP_GAMMA, -- cgit v1.2.3 From a825d72f74a318b479da7b79ba5e1611ccfc511f Mon Sep 17 00:00:00 2001 From: Peng Wu Date: Thu, 23 Jun 2022 07:02:40 +0000 Subject: soc: mediatek: fix missing clk_disable_unprepare() on err in svs_resume() Fix the missing clk_disable_unprepare() before return from svs_resume() in the error handling case. Signed-off-by: Peng Wu Reported-by: Hulk Robot Link: https://lore.kernel.org/r/20220623070240.63382-1-wupeng58@huawei.com Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-svs.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-svs.c b/drivers/soc/mediatek/mtk-svs.c index 606a00a2e57d..87e05ab51552 100644 --- a/drivers/soc/mediatek/mtk-svs.c +++ b/drivers/soc/mediatek/mtk-svs.c @@ -1526,16 +1526,20 @@ static int svs_resume(struct device *dev) ret = reset_control_deassert(svsp->rst); if (ret) { dev_err(svsp->dev, "cannot deassert reset %d\n", ret); - return ret; + goto out_of_resume; } ret = svs_init02(svsp); if (ret) - return ret; + goto out_of_resume; svs_mon_mode(svsp); return 0; + +out_of_resume: + clk_disable_unprepare(svsp->main_clk); + return ret; } static int svs_bank_resource_setup(struct svs_platform *svsp) -- cgit v1.2.3 From 0f0eca0ccb9b37743466fbd1b388a04ea0307b89 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 23 May 2022 08:54:57 -0700 Subject: soc: qcom: socinfo: Add an ID for sc7180P Some sc7180 Chromebooks actually have sc7180P (known by many names, apparently, including possibly sc7180 Pro and sc7185). This is a sc7180 part that has slightly higher clock speeds. The official ID number allocated to these devices by Qualcomm is 495 so we'll add an entry to the table for them. Note that currently shipping BIOS for these devices will actually end up reporting an ID of 407 due to a bug but eventually a new BIOS will be released which corrects it to 495. Signed-off-by: Douglas Anderson Reviewed-by: Sai Prakash Ranjan Reviewed-by: Stephen Boyd Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220523085437.v3.1.I26eca1856f99e6160d30de6d50ecab60e6226354@changeid --- drivers/soc/qcom/socinfo.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index cee579a267a6..c2c879ccc6c0 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -332,6 +332,7 @@ static const struct soc_id soc_id[] = { { 480, "SM8450" }, { 482, "SM8450" }, { 487, "SC7280" }, + { 495, "SC7180P" }, }; static const char *socinfo_machine(struct device *dev, unsigned int id) -- cgit v1.2.3 From bc0f149376894343839c4b156fa5653958fda496 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sat, 18 Jun 2022 22:39:13 +0200 Subject: soc: qcom: apr: Drop redundant check in .remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The remove callback is only called by the driver core if there is a driver to unbind, so there is no need to check dev->driver to be non-NULL. Signed-off-by: Uwe Kleine-König Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220618203913.35785-1-u.kleine-koenig@pengutronix.de --- drivers/soc/qcom/apr.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/apr.c b/drivers/soc/qcom/apr.c index 3caabd873322..b4046f393575 100644 --- a/drivers/soc/qcom/apr.c +++ b/drivers/soc/qcom/apr.c @@ -377,17 +377,14 @@ static int apr_device_probe(struct device *dev) static void apr_device_remove(struct device *dev) { struct apr_device *adev = to_apr_device(dev); - struct apr_driver *adrv; + struct apr_driver *adrv = to_apr_driver(dev->driver); struct packet_router *apr = dev_get_drvdata(adev->dev.parent); - if (dev->driver) { - adrv = to_apr_driver(dev->driver); - if (adrv->remove) - adrv->remove(adev); - spin_lock(&apr->svcs_lock); - idr_remove(&apr->svcs_idr, adev->svc.id); - spin_unlock(&apr->svcs_lock); - } + if (adrv->remove) + adrv->remove(adev); + spin_lock(&apr->svcs_lock); + idr_remove(&apr->svcs_idr, adev->svc.id); + spin_unlock(&apr->svcs_lock); } static int apr_uevent(struct device *dev, struct kobj_uevent_env *env) -- cgit v1.2.3 From ac0126a0173531d91d164e244ed1ebbee64bcd54 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 19 May 2022 09:33:00 +0200 Subject: soc: qcom: cmd-db: replace strncpy() with strscpy_pad() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The use of strncpy() is considered deprecated for NUL-terminated strings[1]. Replace strncpy() with strscpy_pad(), to keep existing pad-behavior of strncpy. This fixes W=1 warning: drivers/soc/qcom/cmd-db.c: In function ‘cmd_db_get_header.part.0’: drivers/soc/qcom/cmd-db.c:151:9: warning: ‘strncpy’ specified bound 8 equals destination size [-Wstringop-truncation] 151 | strncpy(query, id, sizeof(query)); [1] https://www.kernel.org/doc/html/latest/process/deprecated.html#strncpy-on-nul-terminated-strings Signed-off-by: Krzysztof Kozlowski Reviewed-by: Stephen Boyd Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220519073301.7072-1-krzysztof.kozlowski@linaro.org --- drivers/soc/qcom/cmd-db.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c index dd872017f345..c5137c25d819 100644 --- a/drivers/soc/qcom/cmd-db.c +++ b/drivers/soc/qcom/cmd-db.c @@ -148,7 +148,7 @@ static int cmd_db_get_header(const char *id, const struct entry_header **eh, return ret; /* Pad out query string to same length as in DB */ - strncpy(query, id, sizeof(query)); + strscpy_pad(query, id, sizeof(query)); for (i = 0; i < MAX_SLV_ID; i++) { rsc_hdr = &cmd_db_header->header[i]; -- cgit v1.2.3 From d11a34a404ee5565ce8e0abe3e2b9ce6f5cc0a4b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 19 May 2022 09:33:01 +0200 Subject: soc: qcom: correct kerneldoc Correct kerneldoc warnings like: drivers/soc/qcom/mdt_loader.c:126: warning: Function parameter or member 'fw_name' not described in 'qcom_mdt_read_metadata' Signed-off-by: Krzysztof Kozlowski Reviewed-by: Stephen Boyd Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220519073301.7072-2-krzysztof.kozlowski@linaro.org --- drivers/soc/qcom/mdt_loader.c | 4 +++- drivers/soc/qcom/smp2p.c | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c index 366db493579b..3f11554df2f3 100644 --- a/drivers/soc/qcom/mdt_loader.c +++ b/drivers/soc/qcom/mdt_loader.c @@ -108,6 +108,8 @@ EXPORT_SYMBOL_GPL(qcom_mdt_get_size); * qcom_mdt_read_metadata() - read header and metadata from mdt or mbn * @fw: firmware of mdt header or mbn * @data_len: length of the read metadata blob + * @fw_name: name of the firmware, for construction of segment file names + * @dev: device handle to associate resources with * * The mechanism that performs the authentication of the loading firmware * expects an ELF header directly followed by the segment of hashes, with no @@ -192,7 +194,7 @@ EXPORT_SYMBOL_GPL(qcom_mdt_read_metadata); * qcom_mdt_pas_init() - initialize PAS region for firmware loading * @dev: device handle to associate resources with * @fw: firmware object for the mdt file - * @firmware: name of the firmware, for construction of segment file names + * @fw_name: name of the firmware, for construction of segment file names * @pas_id: PAS identifier * @mem_phys: physical address of allocated memory region * @ctx: PAS metadata context, to be released by caller diff --git a/drivers/soc/qcom/smp2p.c b/drivers/soc/qcom/smp2p.c index 59dbf4b61e6c..d9c28a8a7cbf 100644 --- a/drivers/soc/qcom/smp2p.c +++ b/drivers/soc/qcom/smp2p.c @@ -119,6 +119,9 @@ struct smp2p_entry { * @out: pointer to the outbound smem item * @smem_items: ids of the two smem items * @valid_entries: already scanned inbound entries + * @ssr_ack_enabled: SMP2P_FEATURE_SSR_ACK feature is supported and was enabled + * @ssr_ack: current cached state of the local ack bit + * @negotiation_done: whether negotiating finished * @local_pid: processor id of the inbound edge * @remote_pid: processor id of the outbound edge * @ipc_regmap: regmap for the outbound ipc -- cgit v1.2.3 From a20e55d4771c1bcb531f77434a953cd7acdd1687 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 21 May 2022 13:10:19 +0200 Subject: soc: qcom: rpmhpd: fix typos in comment Spelling mistakes (triple letters) in comment. Detected with the help of Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220521111145.81697-9-Julia.Lawall@inria.fr --- drivers/soc/qcom/rpmhpd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c index 05fff8691ee3..092f6ab09acf 100644 --- a/drivers/soc/qcom/rpmhpd.c +++ b/drivers/soc/qcom/rpmhpd.c @@ -23,8 +23,8 @@ /** * struct rpmhpd - top level RPMh power domain resource data structure * @dev: rpmh power domain controller device - * @pd: generic_pm_domain corrresponding to the power domain - * @parent: generic_pm_domain corrresponding to the parent's power domain + * @pd: generic_pm_domain corresponding to the power domain + * @parent: generic_pm_domain corresponding to the parent's power domain * @peer: A peer power domain in case Active only Voting is * supported * @active_only: True if it represents an Active only peer -- cgit v1.2.3 From ab3f045774f704c4e7b6a878102f4e9d4ae7bc74 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 4 Apr 2022 11:56:03 +0200 Subject: soc: fsl: guts: machine variable might be unset If both the model and the compatible properties are missing, then machine will not be set. Initialize it with NULL. Fixes: 34c1c21e94ac ("soc: fsl: fix section mismatch build warnings") Signed-off-by: Michael Walle Acked-by: Arnd Bergmann Signed-off-by: Shawn Guo --- drivers/soc/fsl/guts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c index 5ed2fc1c53a0..be18d46c7b0f 100644 --- a/drivers/soc/fsl/guts.c +++ b/drivers/soc/fsl/guts.c @@ -140,7 +140,7 @@ static int fsl_guts_probe(struct platform_device *pdev) struct device_node *root, *np = pdev->dev.of_node; struct device *dev = &pdev->dev; const struct fsl_soc_die_attr *soc_die; - const char *machine; + const char *machine = NULL; u32 svr; /* Initialize guts */ -- cgit v1.2.3 From 6de6cb89fc031cf95cb759bbf9fa00b139e0281a Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 4 Apr 2022 11:56:04 +0200 Subject: soc: fsl: guts: remove module_exit() and fsl_guts_remove() This driver will never be unloaded. Firstly, it is not available as a module, but more importantly, other drivers will depend on this one to apply possible chip errata. Signed-off-by: Michael Walle Acked-by: Arnd Bergmann Signed-off-by: Shawn Guo --- drivers/soc/fsl/guts.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c index be18d46c7b0f..0bea43770d51 100644 --- a/drivers/soc/fsl/guts.c +++ b/drivers/soc/fsl/guts.c @@ -27,7 +27,6 @@ struct fsl_soc_die_attr { static struct guts *guts; static struct soc_device_attribute soc_dev_attr; -static struct soc_device *soc_dev; /* SoC die attribute definition for QorIQ platform */ @@ -138,6 +137,7 @@ static u32 fsl_guts_get_svr(void) static int fsl_guts_probe(struct platform_device *pdev) { struct device_node *root, *np = pdev->dev.of_node; + static struct soc_device *soc_dev; struct device *dev = &pdev->dev; const struct fsl_soc_die_attr *soc_die; const char *machine = NULL; @@ -197,12 +197,6 @@ static int fsl_guts_probe(struct platform_device *pdev) return 0; } -static int fsl_guts_remove(struct platform_device *dev) -{ - soc_device_unregister(soc_dev); - return 0; -} - /* * Table for matching compatible strings, for device tree * guts node, for Freescale QorIQ SOCs. @@ -242,7 +236,6 @@ static struct platform_driver fsl_guts_driver = { .of_match_table = fsl_guts_of_match, }, .probe = fsl_guts_probe, - .remove = fsl_guts_remove, }; static int __init fsl_guts_init(void) @@ -250,9 +243,3 @@ static int __init fsl_guts_init(void) return platform_driver_register(&fsl_guts_driver); } core_initcall(fsl_guts_init); - -static void __exit fsl_guts_exit(void) -{ - platform_driver_unregister(&fsl_guts_driver); -} -module_exit(fsl_guts_exit); -- cgit v1.2.3 From ab4988d6a39348dd8c031ef6db946afb9a543f0f Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 4 Apr 2022 11:56:05 +0200 Subject: soc: fsl: guts: embed fsl_guts_get_svr() in probe() Move the reading of the SVR into the probe function as fsl_guts_get_svr() is the only user of the static guts variable and this lets us drop that as well as the malloc() for this variable. Also, we can unmap the memory region after we accessed it, which will simplify error handling later. Signed-off-by: Michael Walle Acked-by: Arnd Bergmann Signed-off-by: Shawn Guo --- drivers/soc/fsl/guts.c | 42 +++++++++++------------------------------- 1 file changed, 11 insertions(+), 31 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c index 0bea43770d51..4e5675ab5f73 100644 --- a/drivers/soc/fsl/guts.c +++ b/drivers/soc/fsl/guts.c @@ -14,18 +14,12 @@ #include #include -struct guts { - struct ccsr_guts __iomem *regs; - bool little_endian; -}; - struct fsl_soc_die_attr { char *die; u32 svr; u32 mask; }; -static struct guts *guts; static struct soc_device_attribute soc_dev_attr; @@ -119,40 +113,27 @@ static const struct fsl_soc_die_attr *fsl_soc_die_match( return NULL; } -static u32 fsl_guts_get_svr(void) -{ - u32 svr = 0; - - if (!guts || !guts->regs) - return svr; - - if (guts->little_endian) - svr = ioread32(&guts->regs->svr); - else - svr = ioread32be(&guts->regs->svr); - - return svr; -} - static int fsl_guts_probe(struct platform_device *pdev) { struct device_node *root, *np = pdev->dev.of_node; static struct soc_device *soc_dev; struct device *dev = &pdev->dev; const struct fsl_soc_die_attr *soc_die; + struct ccsr_guts __iomem *regs; const char *machine = NULL; + bool little_endian; u32 svr; - /* Initialize guts */ - guts = devm_kzalloc(dev, sizeof(*guts), GFP_KERNEL); - if (!guts) - return -ENOMEM; - - guts->little_endian = of_property_read_bool(np, "little-endian"); + regs = of_iomap(np, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); - guts->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(guts->regs)) - return PTR_ERR(guts->regs); + little_endian = of_property_read_bool(np, "little-endian"); + if (little_endian) + svr = ioread32(®s->svr); + else + svr = ioread32be(®s->svr); + iounmap(regs); /* Register soc device */ root = of_find_node_by_path("/"); @@ -167,7 +148,6 @@ static int fsl_guts_probe(struct platform_device *pdev) } of_node_put(root); - svr = fsl_guts_get_svr(); soc_die = fsl_soc_die_match(svr, fsl_soc_die); if (soc_die) { soc_dev_attr.family = devm_kasprintf(dev, GFP_KERNEL, -- cgit v1.2.3 From 5d707e4e3fb5185b24a41d72f75599cc77afab61 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 4 Apr 2022 11:56:06 +0200 Subject: soc: fsl: guts: allocate soc_dev_attr on the heap This is the last global static variable. Drop it and allocate the memory on the heap instead. Signed-off-by: Michael Walle Acked-by: Arnd Bergmann Signed-off-by: Shawn Guo --- drivers/soc/fsl/guts.c | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c index 4e5675ab5f73..44e7a9bf6392 100644 --- a/drivers/soc/fsl/guts.c +++ b/drivers/soc/fsl/guts.c @@ -20,9 +20,6 @@ struct fsl_soc_die_attr { u32 mask; }; -static struct soc_device_attribute soc_dev_attr; - - /* SoC die attribute definition for QorIQ platform */ static const struct fsl_soc_die_attr fsl_soc_die[] = { /* @@ -116,6 +113,7 @@ static const struct fsl_soc_die_attr *fsl_soc_die_match( static int fsl_guts_probe(struct platform_device *pdev) { struct device_node *root, *np = pdev->dev.of_node; + struct soc_device_attribute *soc_dev_attr; static struct soc_device *soc_dev; struct device *dev = &pdev->dev; const struct fsl_soc_die_attr *soc_die; @@ -136,12 +134,16 @@ static int fsl_guts_probe(struct platform_device *pdev) iounmap(regs); /* Register soc device */ + soc_dev_attr = devm_kzalloc(dev, sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + return -ENOMEM; + root = of_find_node_by_path("/"); if (of_property_read_string(root, "model", &machine)) of_property_read_string_index(root, "compatible", 0, &machine); if (machine) { - soc_dev_attr.machine = devm_kstrdup(dev, machine, GFP_KERNEL); - if (!soc_dev_attr.machine) { + soc_dev_attr->machine = devm_kstrdup(dev, machine, GFP_KERNEL); + if (!soc_dev_attr->machine) { of_node_put(root); return -ENOMEM; } @@ -150,30 +152,30 @@ static int fsl_guts_probe(struct platform_device *pdev) soc_die = fsl_soc_die_match(svr, fsl_soc_die); if (soc_die) { - soc_dev_attr.family = devm_kasprintf(dev, GFP_KERNEL, - "QorIQ %s", soc_die->die); + soc_dev_attr->family = devm_kasprintf(dev, GFP_KERNEL, + "QorIQ %s", soc_die->die); } else { - soc_dev_attr.family = devm_kasprintf(dev, GFP_KERNEL, "QorIQ"); + soc_dev_attr->family = devm_kasprintf(dev, GFP_KERNEL, "QorIQ"); } - if (!soc_dev_attr.family) + if (!soc_dev_attr->family) return -ENOMEM; - soc_dev_attr.soc_id = devm_kasprintf(dev, GFP_KERNEL, - "svr:0x%08x", svr); - if (!soc_dev_attr.soc_id) + soc_dev_attr->soc_id = devm_kasprintf(dev, GFP_KERNEL, + "svr:0x%08x", svr); + if (!soc_dev_attr->soc_id) return -ENOMEM; - soc_dev_attr.revision = devm_kasprintf(dev, GFP_KERNEL, "%d.%d", - (svr >> 4) & 0xf, svr & 0xf); - if (!soc_dev_attr.revision) + soc_dev_attr->revision = devm_kasprintf(dev, GFP_KERNEL, "%d.%d", + (svr >> 4) & 0xf, svr & 0xf); + if (!soc_dev_attr->revision) return -ENOMEM; - soc_dev = soc_device_register(&soc_dev_attr); + soc_dev = soc_device_register(soc_dev_attr); if (IS_ERR(soc_dev)) return PTR_ERR(soc_dev); - pr_info("Machine: %s\n", soc_dev_attr.machine); - pr_info("SoC family: %s\n", soc_dev_attr.family); + pr_info("Machine: %s\n", soc_dev_attr->machine); + pr_info("SoC family: %s\n", soc_dev_attr->family); pr_info("SoC ID: %s, Revision: %s\n", - soc_dev_attr.soc_id, soc_dev_attr.revision); + soc_dev_attr->soc_id, soc_dev_attr->revision); return 0; } -- cgit v1.2.3 From b46dd0cb93decf58c43db0a39c1c047fc5315901 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 4 Apr 2022 11:56:07 +0200 Subject: soc: fsl: guts: use of_root instead of own reference There is already a global of_root reference. Use that instead of getting one on our own. We don't need to care about the reference count either this way. Signed-off-by: Michael Walle Acked-by: Arnd Bergmann Signed-off-by: Shawn Guo --- drivers/soc/fsl/guts.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c index 44e7a9bf6392..585cbd9d8224 100644 --- a/drivers/soc/fsl/guts.c +++ b/drivers/soc/fsl/guts.c @@ -112,7 +112,7 @@ static const struct fsl_soc_die_attr *fsl_soc_die_match( static int fsl_guts_probe(struct platform_device *pdev) { - struct device_node *root, *np = pdev->dev.of_node; + struct device_node *np = pdev->dev.of_node; struct soc_device_attribute *soc_dev_attr; static struct soc_device *soc_dev; struct device *dev = &pdev->dev; @@ -138,17 +138,13 @@ static int fsl_guts_probe(struct platform_device *pdev) if (!soc_dev_attr) return -ENOMEM; - root = of_find_node_by_path("/"); - if (of_property_read_string(root, "model", &machine)) - of_property_read_string_index(root, "compatible", 0, &machine); + if (of_property_read_string(of_root, "model", &machine)) + of_property_read_string_index(of_root, "compatible", 0, &machine); if (machine) { soc_dev_attr->machine = devm_kstrdup(dev, machine, GFP_KERNEL); - if (!soc_dev_attr->machine) { - of_node_put(root); + if (!soc_dev_attr->machine) return -ENOMEM; - } } - of_node_put(root); soc_die = fsl_soc_die_match(svr, fsl_soc_die); if (soc_die) { -- cgit v1.2.3 From 55488c90b392d74a6a8bbd358b7129aab0b19aee Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 4 Apr 2022 11:56:08 +0200 Subject: soc: fsl: guts: drop platform driver This driver cannot be unloaded and it will be needed very early in the boot process because other driver (weakly) depend on it (eg. for chip errata handling). Drop all the platform driver and devres stuff and simply make it a core_initcall. Signed-off-by: Michael Walle Acked-by: Arnd Bergmann Signed-off-by: Shawn Guo --- drivers/soc/fsl/guts.c | 134 ++++++++++++++++++++++++++----------------------- 1 file changed, 71 insertions(+), 63 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c index 585cbd9d8224..370be923aa0f 100644 --- a/drivers/soc/fsl/guts.c +++ b/drivers/soc/fsl/guts.c @@ -110,21 +110,59 @@ static const struct fsl_soc_die_attr *fsl_soc_die_match( return NULL; } -static int fsl_guts_probe(struct platform_device *pdev) +/* + * Table for matching compatible strings, for device tree + * guts node, for Freescale QorIQ SOCs. + */ +static const struct of_device_id fsl_guts_of_match[] = { + { .compatible = "fsl,qoriq-device-config-1.0", }, + { .compatible = "fsl,qoriq-device-config-2.0", }, + { .compatible = "fsl,p1010-guts", }, + { .compatible = "fsl,p1020-guts", }, + { .compatible = "fsl,p1021-guts", }, + { .compatible = "fsl,p1022-guts", }, + { .compatible = "fsl,p1023-guts", }, + { .compatible = "fsl,p2020-guts", }, + { .compatible = "fsl,bsc9131-guts", }, + { .compatible = "fsl,bsc9132-guts", }, + { .compatible = "fsl,mpc8536-guts", }, + { .compatible = "fsl,mpc8544-guts", }, + { .compatible = "fsl,mpc8548-guts", }, + { .compatible = "fsl,mpc8568-guts", }, + { .compatible = "fsl,mpc8569-guts", }, + { .compatible = "fsl,mpc8572-guts", }, + { .compatible = "fsl,ls1021a-dcfg", }, + { .compatible = "fsl,ls1043a-dcfg", }, + { .compatible = "fsl,ls2080a-dcfg", }, + { .compatible = "fsl,ls1088a-dcfg", }, + { .compatible = "fsl,ls1012a-dcfg", }, + { .compatible = "fsl,ls1046a-dcfg", }, + { .compatible = "fsl,lx2160a-dcfg", }, + { .compatible = "fsl,ls1028a-dcfg", }, + {} +}; + +static int __init fsl_guts_init(void) { - struct device_node *np = pdev->dev.of_node; struct soc_device_attribute *soc_dev_attr; static struct soc_device *soc_dev; - struct device *dev = &pdev->dev; const struct fsl_soc_die_attr *soc_die; struct ccsr_guts __iomem *regs; const char *machine = NULL; + struct device_node *np; bool little_endian; u32 svr; + int ret; + + np = of_find_matching_node_and_match(NULL, fsl_guts_of_match, NULL); + if (!np) + return 0; regs = of_iomap(np, 0); - if (IS_ERR(regs)) + if (IS_ERR(regs)) { + of_node_put(np); return PTR_ERR(regs); + } little_endian = of_property_read_bool(np, "little-endian"); if (little_endian) @@ -132,92 +170,62 @@ static int fsl_guts_probe(struct platform_device *pdev) else svr = ioread32be(®s->svr); iounmap(regs); + of_node_put(np); /* Register soc device */ - soc_dev_attr = devm_kzalloc(dev, sizeof(*soc_dev_attr), GFP_KERNEL); + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); if (!soc_dev_attr) return -ENOMEM; if (of_property_read_string(of_root, "model", &machine)) of_property_read_string_index(of_root, "compatible", 0, &machine); if (machine) { - soc_dev_attr->machine = devm_kstrdup(dev, machine, GFP_KERNEL); + soc_dev_attr->machine = kstrdup(machine, GFP_KERNEL); if (!soc_dev_attr->machine) - return -ENOMEM; + goto err_nomem; } soc_die = fsl_soc_die_match(svr, fsl_soc_die); if (soc_die) { - soc_dev_attr->family = devm_kasprintf(dev, GFP_KERNEL, - "QorIQ %s", soc_die->die); + soc_dev_attr->family = kasprintf(GFP_KERNEL, "QorIQ %s", + soc_die->die); } else { - soc_dev_attr->family = devm_kasprintf(dev, GFP_KERNEL, "QorIQ"); + soc_dev_attr->family = kasprintf(GFP_KERNEL, "QorIQ"); } if (!soc_dev_attr->family) - return -ENOMEM; - soc_dev_attr->soc_id = devm_kasprintf(dev, GFP_KERNEL, - "svr:0x%08x", svr); + goto err_nomem; + + soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "svr:0x%08x", svr); if (!soc_dev_attr->soc_id) - return -ENOMEM; - soc_dev_attr->revision = devm_kasprintf(dev, GFP_KERNEL, "%d.%d", - (svr >> 4) & 0xf, svr & 0xf); + goto err_nomem; + + soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%d.%d", + (svr >> 4) & 0xf, svr & 0xf); if (!soc_dev_attr->revision) - return -ENOMEM; + goto err_nomem; soc_dev = soc_device_register(soc_dev_attr); - if (IS_ERR(soc_dev)) - return PTR_ERR(soc_dev); + if (IS_ERR(soc_dev)) { + ret = PTR_ERR(soc_dev); + goto err; + } pr_info("Machine: %s\n", soc_dev_attr->machine); pr_info("SoC family: %s\n", soc_dev_attr->family); pr_info("SoC ID: %s, Revision: %s\n", soc_dev_attr->soc_id, soc_dev_attr->revision); - return 0; -} -/* - * Table for matching compatible strings, for device tree - * guts node, for Freescale QorIQ SOCs. - */ -static const struct of_device_id fsl_guts_of_match[] = { - { .compatible = "fsl,qoriq-device-config-1.0", }, - { .compatible = "fsl,qoriq-device-config-2.0", }, - { .compatible = "fsl,p1010-guts", }, - { .compatible = "fsl,p1020-guts", }, - { .compatible = "fsl,p1021-guts", }, - { .compatible = "fsl,p1022-guts", }, - { .compatible = "fsl,p1023-guts", }, - { .compatible = "fsl,p2020-guts", }, - { .compatible = "fsl,bsc9131-guts", }, - { .compatible = "fsl,bsc9132-guts", }, - { .compatible = "fsl,mpc8536-guts", }, - { .compatible = "fsl,mpc8544-guts", }, - { .compatible = "fsl,mpc8548-guts", }, - { .compatible = "fsl,mpc8568-guts", }, - { .compatible = "fsl,mpc8569-guts", }, - { .compatible = "fsl,mpc8572-guts", }, - { .compatible = "fsl,ls1021a-dcfg", }, - { .compatible = "fsl,ls1043a-dcfg", }, - { .compatible = "fsl,ls2080a-dcfg", }, - { .compatible = "fsl,ls1088a-dcfg", }, - { .compatible = "fsl,ls1012a-dcfg", }, - { .compatible = "fsl,ls1046a-dcfg", }, - { .compatible = "fsl,lx2160a-dcfg", }, - { .compatible = "fsl,ls1028a-dcfg", }, - {} -}; -MODULE_DEVICE_TABLE(of, fsl_guts_of_match); + return 0; -static struct platform_driver fsl_guts_driver = { - .driver = { - .name = "fsl-guts", - .of_match_table = fsl_guts_of_match, - }, - .probe = fsl_guts_probe, -}; +err_nomem: + ret = -ENOMEM; +err: + kfree(soc_dev_attr->machine); + kfree(soc_dev_attr->family); + kfree(soc_dev_attr->soc_id); + kfree(soc_dev_attr->revision); + kfree(soc_dev_attr); -static int __init fsl_guts_init(void) -{ - return platform_driver_register(&fsl_guts_driver); + return ret; } core_initcall(fsl_guts_init); -- cgit v1.2.3 From 786dde1e59d7122f55e42f5ab1167bc10ad87a41 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 4 Apr 2022 11:56:09 +0200 Subject: soc: fsl: guts: add serial_number support Most layerscapes provide a security fuse processor where the vendor will store a unique id per part. Unfortunately, we cannot use the corresponding efuse driver because this driver needs to be ready early during the boot phase. To get the unique identifier, we just need to access two registers. Thus we just search the device tree for the corresponding device, map its memory to read the id and then unmap it again. Because it is likely that the offset within the fuses is dependent on the SoC, we need a per SoC data. Also, the compatible string is different among the SoCs. For now, this add support for the LS1028A SoC. Signed-off-by: Michael Walle Acked-by: Arnd Bergmann Signed-off-by: Shawn Guo --- drivers/soc/fsl/guts.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c index 370be923aa0f..27035de062f8 100644 --- a/drivers/soc/fsl/guts.c +++ b/drivers/soc/fsl/guts.c @@ -20,6 +20,11 @@ struct fsl_soc_die_attr { u32 mask; }; +struct fsl_soc_data { + const char *sfp_compat; + u32 uid_offset; +}; + /* SoC die attribute definition for QorIQ platform */ static const struct fsl_soc_die_attr fsl_soc_die[] = { /* @@ -110,6 +115,33 @@ static const struct fsl_soc_die_attr *fsl_soc_die_match( return NULL; } +static u64 fsl_guts_get_soc_uid(const char *compat, unsigned int offset) +{ + struct device_node *np; + void __iomem *sfp_base; + u64 uid; + + np = of_find_compatible_node(NULL, NULL, compat); + if (!np) + return 0; + + sfp_base = of_iomap(np, 0); + + uid = ioread32(sfp_base + offset); + uid <<= 32; + uid |= ioread32(sfp_base + offset + 4); + + iounmap(sfp_base); + of_node_put(np); + + return uid; +} + +static const struct fsl_soc_data ls1028a_data = { + .sfp_compat = "fsl,ls1028a-sfp", + .uid_offset = 0x21c, +}; + /* * Table for matching compatible strings, for device tree * guts node, for Freescale QorIQ SOCs. @@ -138,7 +170,7 @@ static const struct of_device_id fsl_guts_of_match[] = { { .compatible = "fsl,ls1012a-dcfg", }, { .compatible = "fsl,ls1046a-dcfg", }, { .compatible = "fsl,lx2160a-dcfg", }, - { .compatible = "fsl,ls1028a-dcfg", }, + { .compatible = "fsl,ls1028a-dcfg", .data = &ls1028a_data}, {} }; @@ -147,16 +179,20 @@ static int __init fsl_guts_init(void) struct soc_device_attribute *soc_dev_attr; static struct soc_device *soc_dev; const struct fsl_soc_die_attr *soc_die; + const struct fsl_soc_data *soc_data; + const struct of_device_id *match; struct ccsr_guts __iomem *regs; const char *machine = NULL; struct device_node *np; bool little_endian; + u64 soc_uid = 0; u32 svr; int ret; - np = of_find_matching_node_and_match(NULL, fsl_guts_of_match, NULL); + np = of_find_matching_node_and_match(NULL, fsl_guts_of_match, &match); if (!np) return 0; + soc_data = match->data; regs = of_iomap(np, 0); if (IS_ERR(regs)) { @@ -204,6 +240,13 @@ static int __init fsl_guts_init(void) if (!soc_dev_attr->revision) goto err_nomem; + if (soc_data) + soc_uid = fsl_guts_get_soc_uid(soc_data->sfp_compat, + soc_data->uid_offset); + if (soc_uid) + soc_dev_attr->serial_number = kasprintf(GFP_KERNEL, "%016llX", + soc_uid); + soc_dev = soc_device_register(soc_dev_attr); if (IS_ERR(soc_dev)) { ret = PTR_ERR(soc_dev); @@ -224,6 +267,7 @@ err: kfree(soc_dev_attr->family); kfree(soc_dev_attr->soc_id); kfree(soc_dev_attr->revision); + kfree(soc_dev_attr->serial_number); kfree(soc_dev_attr); return ret; -- cgit v1.2.3 From 1ec32a4f1c694b5424f6fab1a977d3e556c539bf Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 24 Jun 2022 20:29:39 +0200 Subject: soc: imx: imx8m-blk-ctrl: Make error prints useful Print the name of the power domain which failed to make the error prints actually useful for finding the error. Signed-off-by: Marek Vasut Cc: Alexander Stein Cc: Lucas Stach Cc: Martyn Welch Cc: Paul Elder Cc: Shawn Guo Acked-by: Alexander Stein Signed-off-by: Shawn Guo --- drivers/soc/imx/imx8m-blk-ctrl.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/imx/imx8m-blk-ctrl.c b/drivers/soc/imx/imx8m-blk-ctrl.c index 7f49385ed2f8..2a1a47d06eab 100644 --- a/drivers/soc/imx/imx8m-blk-ctrl.c +++ b/drivers/soc/imx/imx8m-blk-ctrl.c @@ -216,7 +216,7 @@ static int imx8m_blk_ctrl_probe(struct platform_device *pdev) bc->bus_power_dev = genpd_dev_pm_attach_by_name(dev, "bus"); if (IS_ERR(bc->bus_power_dev)) return dev_err_probe(dev, PTR_ERR(bc->bus_power_dev), - "failed to attach power domain\n"); + "failed to attach power domain \"bus\"\n"); for (i = 0; i < bc_data->num_domains; i++) { const struct imx8m_blk_ctrl_domain_data *data = &bc_data->domains[i]; @@ -238,7 +238,8 @@ static int imx8m_blk_ctrl_probe(struct platform_device *pdev) dev_pm_domain_attach_by_name(dev, data->gpc_name); if (IS_ERR(domain->power_dev)) { dev_err_probe(dev, PTR_ERR(domain->power_dev), - "failed to attach power domain\n"); + "failed to attach power domain \"%s\"\n", + data->gpc_name); ret = PTR_ERR(domain->power_dev); goto cleanup_pds; } @@ -251,7 +252,9 @@ static int imx8m_blk_ctrl_probe(struct platform_device *pdev) ret = pm_genpd_init(&domain->genpd, NULL, true); if (ret) { - dev_err_probe(dev, ret, "failed to init power domain\n"); + dev_err_probe(dev, ret, + "failed to init power domain \"%s\"\n", + data->gpc_name); dev_pm_domain_detach(domain->power_dev, true); goto cleanup_pds; } -- cgit v1.2.3 From fe72f9bce137055fb744d4f8a91baa234ec07baa Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Tue, 28 Jun 2022 06:43:13 -0700 Subject: soc: qcom: cmd-db: replace strscpy_pad() with strncpy() Commit ac0126a01735 ("soc: qcom: cmd-db: replace strncpy() with strscpy_pad()") breaks booting on my sc7280-herobrine-herobrine device. From printouts I see that at bootup the function is called with an id of "lnbclka2" which is 8 bytes big. Previously all 8 bytes of this string were copied to the destination. Now only 7 bytes will be copied since strscpy_pad() saves a byte for '\0' termination. We don't need the '\0' termination in the destination. Let's go back to strncpy(). According to the warning: If a caller is using non-NUL-terminated strings, strncpy() can still be used, but destinations should be marked with the __nonstring attribute to avoid future compiler warnings. ...so we'll do that. While we're at it, let's change the query array to use "sizeof(ent->id)" so it can't possibly go out of sync with our later copy. Fixes: ac0126a01735 ("soc: qcom: cmd-db: replace strncpy() with strscpy_pad()") Signed-off-by: Douglas Anderson Reviewed-by: Matthias Kaehlcke Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220628064301.v3.1.Ie7b480cd99e2c13319220cbc108caf2bcd41286b@changeid --- drivers/soc/qcom/cmd-db.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c index c5137c25d819..629a7188b576 100644 --- a/drivers/soc/qcom/cmd-db.c +++ b/drivers/soc/qcom/cmd-db.c @@ -141,14 +141,18 @@ static int cmd_db_get_header(const char *id, const struct entry_header **eh, const struct rsc_hdr *rsc_hdr; const struct entry_header *ent; int ret, i, j; - u8 query[8]; + u8 query[sizeof(ent->id)] __nonstring; ret = cmd_db_ready(); if (ret) return ret; - /* Pad out query string to same length as in DB */ - strscpy_pad(query, id, sizeof(query)); + /* + * Pad out query string to same length as in DB. NOTE: the output + * query string is not necessarily '\0' terminated if it bumps up + * against the max size. That's OK and expected. + */ + strncpy(query, id, sizeof(query)); for (i = 0; i < MAX_SLV_ID; i++) { rsc_hdr = &cmd_db_header->header[i]; -- cgit v1.2.3 From 92a563fcf14b3093226fb36f12e9b5cf630c5a5d Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Thu, 2 Jun 2022 08:24:30 +0400 Subject: soc: qcom: ocmem: Fix refcount leak in of_get_ocmem of_parse_phandle() returns a node pointer with refcount incremented, we should use of_node_put() on it when not need anymore. Add missing of_node_put() to avoid refcount leak. of_node_put() will check NULL pointer. Fixes: 88c1e9404f1d ("soc: qcom: add OCMEM driver") Signed-off-by: Miaoqian Lin Reviewed-by: Brian Masney Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220602042430.1114-1-linmq006@gmail.com --- drivers/soc/qcom/ocmem.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/ocmem.c b/drivers/soc/qcom/ocmem.c index 97fd24c178f8..c92d26b73e6f 100644 --- a/drivers/soc/qcom/ocmem.c +++ b/drivers/soc/qcom/ocmem.c @@ -194,14 +194,17 @@ struct ocmem *of_get_ocmem(struct device *dev) devnode = of_parse_phandle(dev->of_node, "sram", 0); if (!devnode || !devnode->parent) { dev_err(dev, "Cannot look up sram phandle\n"); + of_node_put(devnode); return ERR_PTR(-ENODEV); } pdev = of_find_device_by_node(devnode->parent); if (!pdev) { dev_err(dev, "Cannot find device node %s\n", devnode->name); + of_node_put(devnode); return ERR_PTR(-EPROBE_DEFER); } + of_node_put(devnode); ocmem = platform_get_drvdata(pdev); if (!ocmem) { -- cgit v1.2.3 From a3e7259f9ebe04054dc202b9b9f887abf3e4b12a Mon Sep 17 00:00:00 2001 From: Xiang wangx Date: Sat, 4 Jun 2022 22:23:27 +0800 Subject: soc: qcom: llcc: Fix syntax errors in comments Delete the redundant word 'and'. Signed-off-by: Xiang wangx Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220604142327.14714-1-wangxiang@cdjrlc.com --- drivers/soc/qcom/llcc-qcom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index 4b143cf7b4ce..38d7296315a2 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -382,7 +382,7 @@ static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER; * llcc_slice_getd - get llcc slice descriptor * @uid: usecase_id for the client * - * A pointer to llcc slice descriptor will be returned on success and + * A pointer to llcc slice descriptor will be returned on success * and error pointer is returned on failure */ struct llcc_slice_desc *llcc_slice_getd(u32 uid) -- cgit v1.2.3 From e6e0951414a314e7db3e9e24fd924b3e15515288 Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Mon, 6 Jun 2022 10:42:52 +0400 Subject: soc: qcom: aoss: Fix refcount leak in qmp_cooling_devices_register Every iteration of for_each_available_child_of_node() decrements the reference count of the previous node. When breaking early from a for_each_available_child_of_node() loop, we need to explicitly call of_node_put() on the child node. Add missing of_node_put() to avoid refcount leak. Fixes: 05589b30b21a ("soc: qcom: Extend AOSS QMP driver to support resources that are used to wake up the SoC.") Signed-off-by: Miaoqian Lin Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220606064252.42595-1-linmq006@gmail.com --- drivers/soc/qcom/qcom_aoss.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c index a59bb34e5eba..18c856056475 100644 --- a/drivers/soc/qcom/qcom_aoss.c +++ b/drivers/soc/qcom/qcom_aoss.c @@ -399,8 +399,10 @@ static int qmp_cooling_devices_register(struct qmp *qmp) continue; ret = qmp_cooling_device_add(qmp, &qmp->cooling_devs[count++], child); - if (ret) + if (ret) { + of_node_put(child); goto unroll; + } } if (!count) -- cgit v1.2.3 From c494a447c14e7a8b997b4b8c4eaf9808e216ea70 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Sat, 25 Jun 2022 13:36:16 +0200 Subject: soc: bcm: bcm2835-power: Refactor ASB control The functions to control the async AXI bridges are almost identical. So define a general function to handle it and keep the original ones as wrapper. This should make this driver easier to extend. Signed-off-by: Stefan Wahren Reviewed-by: Peter Robinson Acked-by: Florian Fainelli Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20220625113619.15944-9-stefan.wahren@i2se.com --- drivers/soc/bcm/bcm2835-power.c | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/bcm/bcm2835-power.c b/drivers/soc/bcm/bcm2835-power.c index 1e0041ec8132..77dc9e62b207 100644 --- a/drivers/soc/bcm/bcm2835-power.c +++ b/drivers/soc/bcm/bcm2835-power.c @@ -148,7 +148,7 @@ struct bcm2835_power { struct reset_controller_dev reset; }; -static int bcm2835_asb_enable(struct bcm2835_power *power, u32 reg) +static int bcm2835_asb_control(struct bcm2835_power *power, u32 reg, bool enable) { u64 start; @@ -158,7 +158,12 @@ static int bcm2835_asb_enable(struct bcm2835_power *power, u32 reg) start = ktime_get_ns(); /* Enable the module's async AXI bridges. */ - ASB_WRITE(reg, ASB_READ(reg) & ~ASB_REQ_STOP); + if (enable) { + ASB_WRITE(reg, ASB_READ(reg) & ~ASB_REQ_STOP); + } else { + ASB_WRITE(reg, ASB_READ(reg) | ASB_REQ_STOP); + } + while (ASB_READ(reg) & ASB_ACK) { cpu_relax(); if (ktime_get_ns() - start >= 1000) @@ -168,24 +173,14 @@ static int bcm2835_asb_enable(struct bcm2835_power *power, u32 reg) return 0; } -static int bcm2835_asb_disable(struct bcm2835_power *power, u32 reg) +static int bcm2835_asb_enable(struct bcm2835_power *power, u32 reg) { - u64 start; - - if (!reg) - return 0; - - start = ktime_get_ns(); - - /* Enable the module's async AXI bridges. */ - ASB_WRITE(reg, ASB_READ(reg) | ASB_REQ_STOP); - while (!(ASB_READ(reg) & ASB_ACK)) { - cpu_relax(); - if (ktime_get_ns() - start >= 1000) - return -ETIMEDOUT; - } + return bcm2835_asb_control(power, reg, true); +} - return 0; +static int bcm2835_asb_disable(struct bcm2835_power *power, u32 reg) +{ + return bcm2835_asb_control(power, reg, false); } static int bcm2835_power_power_off(struct bcm2835_power_domain *pd, u32 pm_reg) -- cgit v1.2.3 From a6a1747a5ca90051e49f8a394dd0464cef8d755d Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Sat, 25 Jun 2022 13:36:17 +0200 Subject: soc: bcm: bcm2835-power: Resolve ASB register macros The macros in order to access the ASB registers have a hard coded base address. So extending them for other platforms would make them harder to read. As a solution resolve these macros. Signed-off-by: Stefan Wahren Reviewed-by: Peter Robinson Acked-by: Florian Fainelli Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20220625113619.15944-10-stefan.wahren@i2se.com --- drivers/soc/bcm/bcm2835-power.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/bcm/bcm2835-power.c b/drivers/soc/bcm/bcm2835-power.c index 77dc9e62b207..9c352f66e6d5 100644 --- a/drivers/soc/bcm/bcm2835-power.c +++ b/drivers/soc/bcm/bcm2835-power.c @@ -126,9 +126,6 @@ #define ASB_AXI_BRDG_ID 0x20 -#define ASB_READ(reg) readl(power->asb + (reg)) -#define ASB_WRITE(reg, val) writel(PM_PASSWORD | (val), power->asb + (reg)) - struct bcm2835_power_domain { struct generic_pm_domain base; struct bcm2835_power *power; @@ -150,7 +147,9 @@ struct bcm2835_power { static int bcm2835_asb_control(struct bcm2835_power *power, u32 reg, bool enable) { + void __iomem *base = power->asb; u64 start; + u32 val; if (!reg) return 0; @@ -159,12 +158,13 @@ static int bcm2835_asb_control(struct bcm2835_power *power, u32 reg, bool enable /* Enable the module's async AXI bridges. */ if (enable) { - ASB_WRITE(reg, ASB_READ(reg) & ~ASB_REQ_STOP); + val = readl(base + reg) & ~ASB_REQ_STOP; } else { - ASB_WRITE(reg, ASB_READ(reg) | ASB_REQ_STOP); + val = readl(base + reg) | ASB_REQ_STOP; } + writel(PM_PASSWORD | val, base + reg); - while (ASB_READ(reg) & ASB_ACK) { + while (readl(base + reg) & ASB_ACK) { cpu_relax(); if (ktime_get_ns() - start >= 1000) return -ETIMEDOUT; @@ -622,7 +622,7 @@ static int bcm2835_power_probe(struct platform_device *pdev) power->base = pm->base; power->asb = pm->asb; - id = ASB_READ(ASB_AXI_BRDG_ID); + id = readl(power->asb + ASB_AXI_BRDG_ID); if (id != 0x62726467 /* "BRDG" */) { dev_err(dev, "ASB register ID returned 0x%08x\n", id); return -ENODEV; -- cgit v1.2.3 From a7120238c2086d95172abb8507c8819e11e195f1 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Sat, 25 Jun 2022 13:36:18 +0200 Subject: soc: bcm: bcm2835-power: Add support for BCM2711's RPiVid ASB In BCM2711 the new RPiVid ASB took over V3D. The old ASB is still present with the ISP and H264 bits, and V3D is in the same place in the new ASB as the old one. Use the fact that 'pm->rpivid_asb' is populated as a hint that we're on BCM2711. Signed-off-by: Stefan Wahren Reviewed-by: Peter Robinson Acked-by: Florian Fainelli Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20220625113619.15944-11-stefan.wahren@i2se.com --- drivers/soc/bcm/bcm2835-power.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/bcm/bcm2835-power.c b/drivers/soc/bcm/bcm2835-power.c index 9c352f66e6d5..1e06d91c0739 100644 --- a/drivers/soc/bcm/bcm2835-power.c +++ b/drivers/soc/bcm/bcm2835-power.c @@ -126,6 +126,8 @@ #define ASB_AXI_BRDG_ID 0x20 +#define BCM2835_BRDG_ID 0x62726467 + struct bcm2835_power_domain { struct generic_pm_domain base; struct bcm2835_power *power; @@ -139,6 +141,8 @@ struct bcm2835_power { void __iomem *base; /* AXI Async bridge registers. */ void __iomem *asb; + /* RPiVid bridge registers. */ + void __iomem *rpivid_asb; struct genpd_onecell_data pd_xlate; struct bcm2835_power_domain domains[BCM2835_POWER_DOMAIN_COUNT]; @@ -151,8 +155,15 @@ static int bcm2835_asb_control(struct bcm2835_power *power, u32 reg, bool enable u64 start; u32 val; - if (!reg) + switch (reg) { + case 0: return 0; + case ASB_V3D_S_CTRL: + case ASB_V3D_M_CTRL: + if (power->rpivid_asb) + base = power->rpivid_asb; + break; + } start = ktime_get_ns(); @@ -621,13 +632,23 @@ static int bcm2835_power_probe(struct platform_device *pdev) power->dev = dev; power->base = pm->base; power->asb = pm->asb; + power->rpivid_asb = pm->rpivid_asb; id = readl(power->asb + ASB_AXI_BRDG_ID); - if (id != 0x62726467 /* "BRDG" */) { + if (id != BCM2835_BRDG_ID /* "BRDG" */) { dev_err(dev, "ASB register ID returned 0x%08x\n", id); return -ENODEV; } + if (power->rpivid_asb) { + id = readl(power->rpivid_asb + ASB_AXI_BRDG_ID); + if (id != BCM2835_BRDG_ID /* "BRDG" */) { + dev_err(dev, "RPiVid ASB register ID returned 0x%08x\n", + id); + return -ENODEV; + } + } + power->pd_xlate.domains = devm_kcalloc(dev, ARRAY_SIZE(power_domain_names), sizeof(*power->pd_xlate.domains), -- cgit v1.2.3 From 9e95c67efa8aa26f03b08147a552eb71e83e1a77 Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Sat, 25 Jun 2022 13:36:19 +0200 Subject: soc: bcm: bcm2835-power: Bypass power_on/off() calls Bypass power_on/power_off() when running on BCM2711 as they are not needed. Signed-off-by: Nicolas Saenz Julienne Signed-off-by: Stefan Wahren Reviewed-by: Peter Robinson Acked-by: Florian Fainelli Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20220625113619.15944-12-stefan.wahren@i2se.com --- drivers/soc/bcm/bcm2835-power.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/bcm/bcm2835-power.c b/drivers/soc/bcm/bcm2835-power.c index 1e06d91c0739..5bcd047768b6 100644 --- a/drivers/soc/bcm/bcm2835-power.c +++ b/drivers/soc/bcm/bcm2835-power.c @@ -198,6 +198,10 @@ static int bcm2835_power_power_off(struct bcm2835_power_domain *pd, u32 pm_reg) { struct bcm2835_power *power = pd->power; + /* We don't run this on BCM2711 */ + if (power->rpivid_asb) + return 0; + /* Enable functional isolation */ PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISFUNC); @@ -219,6 +223,10 @@ static int bcm2835_power_power_on(struct bcm2835_power_domain *pd, u32 pm_reg) int inrush; bool powok; + /* We don't run this on BCM2711 */ + if (power->rpivid_asb) + return 0; + /* If it was already powered on by the fw, leave it that way. */ if (PM_READ(pm_reg) & PM_POWUP) return 0; -- cgit v1.2.3 From edf12b913abb69abce4cd6996398a4ce23e5b047 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Tue, 28 Jun 2022 22:02:48 +0800 Subject: soc: fsl: guts: fix return value check in fsl_guts_init() In case of error, of_iomap() returns NULL pointer not ERR_PTR(). The IS_ERR() test in the return value check should be replaced with NULL test and return -ENOMEM as error value. Reported-by: Hulk Robot Signed-off-by: Yang Yingliang Reviewed-by: Michael Walle Signed-off-by: Shawn Guo --- drivers/soc/fsl/guts.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c index 27035de062f8..8038c599ad83 100644 --- a/drivers/soc/fsl/guts.c +++ b/drivers/soc/fsl/guts.c @@ -195,9 +195,9 @@ static int __init fsl_guts_init(void) soc_data = match->data; regs = of_iomap(np, 0); - if (IS_ERR(regs)) { + if (!regs) { of_node_put(np); - return PTR_ERR(regs); + return -ENOMEM; } little_endian = of_property_read_bool(np, "little-endian"); -- cgit v1.2.3 From 63f98153075167a6c88d8ac90bfd26f1887c670c Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Tue, 28 Jun 2022 22:02:49 +0800 Subject: soc: fsl: guts: check return value after calling of_iomap() in fsl_guts_get_soc_uid() of_iomap() may return NULL, so we need check the return value. Reported-by: Hulk Robot Signed-off-by: Yang Yingliang Reviewed-by: Michael Walle Signed-off-by: Shawn Guo --- drivers/soc/fsl/guts.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c index 8038c599ad83..6bf3e6a980ff 100644 --- a/drivers/soc/fsl/guts.c +++ b/drivers/soc/fsl/guts.c @@ -126,6 +126,10 @@ static u64 fsl_guts_get_soc_uid(const char *compat, unsigned int offset) return 0; sfp_base = of_iomap(np, 0); + if (!sfp_base) { + of_node_put(np); + return 0; + } uid = ioread32(sfp_base + offset); uid <<= 32; -- cgit v1.2.3 From 7d0221fb59125181a31ef3a561306c70fb238bc7 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Fri, 1 Jul 2022 09:37:00 +0200 Subject: soc/qcom: Make QCOM_RPMPD select PM_GENERIC_DOMAINS/_OF The driver uses generic genpd OF APIs and with a very minimal config where nothing else selects them, this driver will not probe, as of_genpd_add_provider_onecell will return -EOPNOTSUPP. Make sure to select these in Kconfig to prevent that. Signed-off-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220701073700.17124-1-konrad.dybcio@somainline.org --- drivers/soc/qcom/Kconfig | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index e718b8735444..a750714d5714 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -130,6 +130,8 @@ config QCOM_RPMHPD config QCOM_RPMPD tristate "Qualcomm RPM Power domain driver" depends on QCOM_SMD_RPM + select PM_GENERIC_DOMAINS + select PM_GENERIC_DOMAINS_OF help QCOM RPM Power domain driver to support power-domains with performance states. The driver communicates a performance state -- cgit v1.2.3 From db1bfbd60e325abddc824c49b2fd45a39a10b2f5 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Fri, 1 Jul 2022 22:25:20 -0500 Subject: soc: sunxi: mbus: Only build the driver on ARM/ARM64 This driver exists as a workaround for old devicetrees which are missing interconnects properties, so it is only useful for those specific platforms, which all happen to be ARM or ARM64. This solves the issue that the driver fails to build on RISC-V, where PHYS_OFFSET is not defined. Signed-off-by: Samuel Holland Reviewed-by: Jernej Skrabec Signed-off-by: Jernej Skrabec Link: https://lore.kernel.org/r/20220702032520.22129-1-samuel@sholland.org --- drivers/soc/sunxi/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/soc') diff --git a/drivers/soc/sunxi/Kconfig b/drivers/soc/sunxi/Kconfig index 1fef0e711056..8aecbc9b1976 100644 --- a/drivers/soc/sunxi/Kconfig +++ b/drivers/soc/sunxi/Kconfig @@ -6,6 +6,7 @@ config SUNXI_MBUS bool default ARCH_SUNXI + depends on ARM || ARM64 help Say y to enable the fixups needed to support the Allwinner MBUS DMA quirks. -- cgit v1.2.3 From b9c2ae6cac403dee3195fda9eb28d8ee733b225b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 4 Jul 2022 14:17:28 +0200 Subject: soc: qcom: icc-bwmon: Add bandwidth monitoring driver Bandwidth monitoring (BWMON) sits between various subsytems like CPU, GPU, Last Level caches and memory subsystem. The BWMON can be configured to monitor the data throuhput between memory and other subsytems. The throughput is measured within specified sampling window and is used to vote for corresponding interconnect bandwidth. Current implementation brings support for BWMON v4, used for example on SDM845 to measure bandwidth between CPU (gladiator_noc) and Last Level Cache (memnoc). Usage of this BWMON allows to remove fixed bandwidth votes from cpufreq (CPU nodes) thus achieve high memory throughput even with lower CPU frequencies. The driver was tested on SDM845. Co-developed-by: Thara Gopinath Signed-off-by: Thara Gopinath Signed-off-by: Krzysztof Kozlowski Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220704121730.127925-3-krzysztof.kozlowski@linaro.org --- MAINTAINERS | 7 + drivers/soc/qcom/Kconfig | 15 ++ drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/icc-bwmon.c | 421 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 444 insertions(+) create mode 100644 drivers/soc/qcom/icc-bwmon.c (limited to 'drivers/soc') diff --git a/MAINTAINERS b/MAINTAINERS index a6d3bd9d2a8d..6c5a34815fc9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16523,6 +16523,13 @@ S: Maintained F: Documentation/devicetree/bindings/i2c/i2c-qcom-cci.txt F: drivers/i2c/busses/i2c-qcom-cci.c +QUALCOMM INTERCONNECT BWMON DRIVER +M: Krzysztof Kozlowski +L: linux-arm-msm@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml +F: drivers/soc/qcom/icc-bwmon.c + QUALCOMM IOMMU M: Rob Clark L: iommu@lists.linux-foundation.org diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index a750714d5714..c0a46633bed9 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -230,4 +230,19 @@ config QCOM_APR application processor and QDSP6. APR is used by audio driver to configure QDSP6 ASM, ADM and AFE modules. + +config QCOM_ICC_BWMON + tristate "QCOM Interconnect Bandwidth Monitor driver" + depends on ARCH_QCOM || COMPILE_TEST + select PM_OPP + help + Sets up driver monitoring bandwidth on various interconnects and + based on that voting for interconnect bandwidth, adjusting their + speed to current demand. + Current implementation brings support for BWMON v4, used for example + on SDM845 to measure bandwidth between CPU (gladiator_noc) and Last + Level Cache (memnoc). Usage of this BWMON allows to remove some of + the fixed bandwidth votes from cpufreq (CPU nodes) thus achieve high + memory throughput even with lower CPU frequencies. + endmenu diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 70d5de69fd7b..d66604aff2b0 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -28,3 +28,4 @@ obj-$(CONFIG_QCOM_LLCC) += llcc-qcom.o obj-$(CONFIG_QCOM_RPMHPD) += rpmhpd.o obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o obj-$(CONFIG_QCOM_KRYO_L2_ACCESSORS) += kryo-l2-accessors.o +obj-$(CONFIG_QCOM_ICC_BWMON) += icc-bwmon.o diff --git a/drivers/soc/qcom/icc-bwmon.c b/drivers/soc/qcom/icc-bwmon.c new file mode 100644 index 000000000000..bea3ea1de7a4 --- /dev/null +++ b/drivers/soc/qcom/icc-bwmon.c @@ -0,0 +1,421 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. + * Copyright (C) 2021-2022 Linaro Ltd + * Author: Krzysztof Kozlowski , based on + * previous work of Thara Gopinath and msm-4.9 downstream sources. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The BWMON samples data throughput within 'sample_ms' time. With three + * configurable thresholds (Low, Medium and High) gives four windows (called + * zones) of current bandwidth: + * + * Zone 0: byte count < THRES_LO + * Zone 1: THRES_LO < byte count < THRES_MED + * Zone 2: THRES_MED < byte count < THRES_HIGH + * Zone 3: THRES_HIGH < byte count + * + * Zones 0 and 2 are not used by this driver. + */ + +/* Internal sampling clock frequency */ +#define HW_TIMER_HZ 19200000 + +#define BWMON_GLOBAL_IRQ_STATUS 0x0 +#define BWMON_GLOBAL_IRQ_CLEAR 0x8 +#define BWMON_GLOBAL_IRQ_ENABLE 0xc +#define BWMON_GLOBAL_IRQ_ENABLE_ENABLE BIT(0) + +#define BWMON_IRQ_STATUS 0x100 +#define BWMON_IRQ_STATUS_ZONE_SHIFT 4 +#define BWMON_IRQ_CLEAR 0x108 +#define BWMON_IRQ_ENABLE 0x10c +#define BWMON_IRQ_ENABLE_ZONE1_SHIFT 5 +#define BWMON_IRQ_ENABLE_ZONE2_SHIFT 6 +#define BWMON_IRQ_ENABLE_ZONE3_SHIFT 7 +#define BWMON_IRQ_ENABLE_MASK (BIT(BWMON_IRQ_ENABLE_ZONE1_SHIFT) | \ + BIT(BWMON_IRQ_ENABLE_ZONE3_SHIFT)) + +#define BWMON_ENABLE 0x2a0 +#define BWMON_ENABLE_ENABLE BIT(0) + +#define BWMON_CLEAR 0x2a4 +#define BWMON_CLEAR_CLEAR BIT(0) + +#define BWMON_SAMPLE_WINDOW 0x2a8 +#define BWMON_THRESHOLD_HIGH 0x2ac +#define BWMON_THRESHOLD_MED 0x2b0 +#define BWMON_THRESHOLD_LOW 0x2b4 + +#define BWMON_ZONE_ACTIONS 0x2b8 +/* + * Actions to perform on some zone 'z' when current zone hits the threshold: + * Increment counter of zone 'z' + */ +#define BWMON_ZONE_ACTIONS_INCREMENT(z) (0x2 << ((z) * 2)) +/* Clear counter of zone 'z' */ +#define BWMON_ZONE_ACTIONS_CLEAR(z) (0x1 << ((z) * 2)) + +/* Zone 0 threshold hit: Clear zone count */ +#define BWMON_ZONE_ACTIONS_ZONE0 (BWMON_ZONE_ACTIONS_CLEAR(0)) + +/* Zone 1 threshold hit: Increment zone count & clear lower zones */ +#define BWMON_ZONE_ACTIONS_ZONE1 (BWMON_ZONE_ACTIONS_INCREMENT(1) | \ + BWMON_ZONE_ACTIONS_CLEAR(0)) + +/* Zone 2 threshold hit: Increment zone count & clear lower zones */ +#define BWMON_ZONE_ACTIONS_ZONE2 (BWMON_ZONE_ACTIONS_INCREMENT(2) | \ + BWMON_ZONE_ACTIONS_CLEAR(1) | \ + BWMON_ZONE_ACTIONS_CLEAR(0)) + +/* Zone 3 threshold hit: Increment zone count & clear lower zones */ +#define BWMON_ZONE_ACTIONS_ZONE3 (BWMON_ZONE_ACTIONS_INCREMENT(3) | \ + BWMON_ZONE_ACTIONS_CLEAR(2) | \ + BWMON_ZONE_ACTIONS_CLEAR(1) | \ + BWMON_ZONE_ACTIONS_CLEAR(0)) +/* Value for BWMON_ZONE_ACTIONS */ +#define BWMON_ZONE_ACTIONS_DEFAULT (BWMON_ZONE_ACTIONS_ZONE0 | \ + BWMON_ZONE_ACTIONS_ZONE1 << 8 | \ + BWMON_ZONE_ACTIONS_ZONE2 << 16 | \ + BWMON_ZONE_ACTIONS_ZONE3 << 24) + +/* + * There is no clear documentation/explanation of BWMON_THRESHOLD_COUNT + * register. Based on observations, this is number of times one threshold has to + * be reached, to trigger interrupt in given zone. + * + * 0xff are maximum values meant to ignore the zones 0 and 2. + */ +#define BWMON_THRESHOLD_COUNT 0x2bc +#define BWMON_THRESHOLD_COUNT_ZONE1_SHIFT 8 +#define BWMON_THRESHOLD_COUNT_ZONE2_SHIFT 16 +#define BWMON_THRESHOLD_COUNT_ZONE3_SHIFT 24 +#define BWMON_THRESHOLD_COUNT_ZONE0_DEFAULT 0xff +#define BWMON_THRESHOLD_COUNT_ZONE2_DEFAULT 0xff + +/* BWMONv4 count registers use count unit of 64 kB */ +#define BWMON_COUNT_UNIT_KB 64 +#define BWMON_ZONE_COUNT 0x2d8 +#define BWMON_ZONE_MAX(zone) (0x2e0 + 4 * (zone)) + +struct icc_bwmon_data { + unsigned int sample_ms; + unsigned int default_highbw_kbps; + unsigned int default_medbw_kbps; + unsigned int default_lowbw_kbps; + u8 zone1_thres_count; + u8 zone3_thres_count; +}; + +struct icc_bwmon { + struct device *dev; + void __iomem *base; + int irq; + + unsigned int default_lowbw_kbps; + unsigned int sample_ms; + unsigned int max_bw_kbps; + unsigned int min_bw_kbps; + unsigned int target_kbps; + unsigned int current_kbps; +}; + +static void bwmon_clear_counters(struct icc_bwmon *bwmon) +{ + /* + * Clear counters. The order and barriers are + * important. Quoting downstream Qualcomm msm-4.9 tree: + * + * The counter clear and IRQ clear bits are not in the same 4KB + * region. So, we need to make sure the counter clear is completed + * before we try to clear the IRQ or do any other counter operations. + */ + writel(BWMON_CLEAR_CLEAR, bwmon->base + BWMON_CLEAR); +} + +static void bwmon_clear_irq(struct icc_bwmon *bwmon) +{ + /* + * Clear zone and global interrupts. The order and barriers are + * important. Quoting downstream Qualcomm msm-4.9 tree: + * + * Synchronize the local interrupt clear in mon_irq_clear() + * with the global interrupt clear here. Otherwise, the CPU + * may reorder the two writes and clear the global interrupt + * before the local interrupt, causing the global interrupt + * to be retriggered by the local interrupt still being high. + * + * Similarly, because the global registers are in a different + * region than the local registers, we need to ensure any register + * writes to enable the monitor after this call are ordered with the + * clearing here so that local writes don't happen before the + * interrupt is cleared. + */ + writel(BWMON_IRQ_ENABLE_MASK, bwmon->base + BWMON_IRQ_CLEAR); + writel(BIT(0), bwmon->base + BWMON_GLOBAL_IRQ_CLEAR); +} + +static void bwmon_disable(struct icc_bwmon *bwmon) +{ + /* Disable interrupts. Strict ordering, see bwmon_clear_irq(). */ + writel(0x0, bwmon->base + BWMON_GLOBAL_IRQ_ENABLE); + writel(0x0, bwmon->base + BWMON_IRQ_ENABLE); + + /* + * Disable bwmon. Must happen before bwmon_clear_irq() to avoid spurious + * IRQ. + */ + writel(0x0, bwmon->base + BWMON_ENABLE); +} + +static void bwmon_enable(struct icc_bwmon *bwmon, unsigned int irq_enable) +{ + /* Enable interrupts */ + writel(BWMON_GLOBAL_IRQ_ENABLE_ENABLE, + bwmon->base + BWMON_GLOBAL_IRQ_ENABLE); + writel(irq_enable, bwmon->base + BWMON_IRQ_ENABLE); + + /* Enable bwmon */ + writel(BWMON_ENABLE_ENABLE, bwmon->base + BWMON_ENABLE); +} + +static unsigned int bwmon_kbps_to_count(unsigned int kbps) +{ + return kbps / BWMON_COUNT_UNIT_KB; +} + +static void bwmon_set_threshold(struct icc_bwmon *bwmon, unsigned int reg, + unsigned int kbps) +{ + unsigned int thres; + + thres = mult_frac(bwmon_kbps_to_count(kbps), bwmon->sample_ms, + MSEC_PER_SEC); + writel_relaxed(thres, bwmon->base + reg); +} + +static void bwmon_start(struct icc_bwmon *bwmon, + const struct icc_bwmon_data *data) +{ + unsigned int thres_count; + int window; + + bwmon_clear_counters(bwmon); + + window = mult_frac(bwmon->sample_ms, HW_TIMER_HZ, MSEC_PER_SEC); + /* Maximum sampling window: 0xfffff */ + writel_relaxed(window, bwmon->base + BWMON_SAMPLE_WINDOW); + + bwmon_set_threshold(bwmon, BWMON_THRESHOLD_HIGH, + data->default_highbw_kbps); + bwmon_set_threshold(bwmon, BWMON_THRESHOLD_MED, + data->default_medbw_kbps); + bwmon_set_threshold(bwmon, BWMON_THRESHOLD_LOW, + data->default_lowbw_kbps); + + thres_count = data->zone3_thres_count << BWMON_THRESHOLD_COUNT_ZONE3_SHIFT | + BWMON_THRESHOLD_COUNT_ZONE2_DEFAULT << BWMON_THRESHOLD_COUNT_ZONE2_SHIFT | + data->zone1_thres_count << BWMON_THRESHOLD_COUNT_ZONE1_SHIFT | + BWMON_THRESHOLD_COUNT_ZONE0_DEFAULT; + writel_relaxed(thres_count, bwmon->base + BWMON_THRESHOLD_COUNT); + writel_relaxed(BWMON_ZONE_ACTIONS_DEFAULT, + bwmon->base + BWMON_ZONE_ACTIONS); + /* Write barriers in bwmon_clear_irq() */ + + bwmon_clear_irq(bwmon); + bwmon_enable(bwmon, BWMON_IRQ_ENABLE_MASK); +} + +static irqreturn_t bwmon_intr(int irq, void *dev_id) +{ + struct icc_bwmon *bwmon = dev_id; + unsigned int status, max; + int zone; + + status = readl(bwmon->base + BWMON_IRQ_STATUS); + status &= BWMON_IRQ_ENABLE_MASK; + if (!status) { + /* + * Only zone 1 and zone 3 interrupts are enabled but zone 2 + * threshold could be hit and trigger interrupt even if not + * enabled. + * Such spurious interrupt might come with valuable max count or + * not, so solution would be to always check all + * BWMON_ZONE_MAX() registers to find the highest value. + * Such case is currently ignored. + */ + return IRQ_NONE; + } + + bwmon_disable(bwmon); + + zone = get_bitmask_order(status >> BWMON_IRQ_STATUS_ZONE_SHIFT) - 1; + /* + * Zone max bytes count register returns count units within sampling + * window. Downstream kernel for BWMONv4 (called BWMON type 2 in + * downstream) always increments the max bytes count by one. + */ + max = readl(bwmon->base + BWMON_ZONE_MAX(zone)) + 1; + max *= BWMON_COUNT_UNIT_KB; + bwmon->target_kbps = mult_frac(max, MSEC_PER_SEC, bwmon->sample_ms); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t bwmon_intr_thread(int irq, void *dev_id) +{ + struct icc_bwmon *bwmon = dev_id; + unsigned int irq_enable = 0; + struct dev_pm_opp *opp, *target_opp; + unsigned int bw_kbps, up_kbps, down_kbps; + + bw_kbps = bwmon->target_kbps; + + target_opp = dev_pm_opp_find_bw_ceil(bwmon->dev, &bw_kbps, 0); + if (IS_ERR(target_opp) && PTR_ERR(target_opp) == -ERANGE) + target_opp = dev_pm_opp_find_bw_floor(bwmon->dev, &bw_kbps, 0); + + bwmon->target_kbps = bw_kbps; + + bw_kbps--; + opp = dev_pm_opp_find_bw_floor(bwmon->dev, &bw_kbps, 0); + if (IS_ERR(opp) && PTR_ERR(opp) == -ERANGE) + down_kbps = bwmon->target_kbps; + else + down_kbps = bw_kbps; + + up_kbps = bwmon->target_kbps + 1; + + if (bwmon->target_kbps >= bwmon->max_bw_kbps) + irq_enable = BIT(BWMON_IRQ_ENABLE_ZONE1_SHIFT); + else if (bwmon->target_kbps <= bwmon->min_bw_kbps) + irq_enable = BIT(BWMON_IRQ_ENABLE_ZONE3_SHIFT); + else + irq_enable = BWMON_IRQ_ENABLE_MASK; + + bwmon_set_threshold(bwmon, BWMON_THRESHOLD_HIGH, up_kbps); + bwmon_set_threshold(bwmon, BWMON_THRESHOLD_MED, down_kbps); + /* Write barriers in bwmon_clear_counters() */ + bwmon_clear_counters(bwmon); + bwmon_clear_irq(bwmon); + bwmon_enable(bwmon, irq_enable); + + if (bwmon->target_kbps == bwmon->current_kbps) + goto out; + + dev_pm_opp_set_opp(bwmon->dev, target_opp); + bwmon->current_kbps = bwmon->target_kbps; + +out: + dev_pm_opp_put(target_opp); + if (!IS_ERR(opp)) + dev_pm_opp_put(opp); + + return IRQ_HANDLED; +} + +static int bwmon_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dev_pm_opp *opp; + struct icc_bwmon *bwmon; + const struct icc_bwmon_data *data; + int ret; + + bwmon = devm_kzalloc(dev, sizeof(*bwmon), GFP_KERNEL); + if (!bwmon) + return -ENOMEM; + + data = of_device_get_match_data(dev); + + bwmon->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(bwmon->base)) { + dev_err(dev, "failed to map bwmon registers\n"); + return PTR_ERR(bwmon->base); + } + + bwmon->irq = platform_get_irq(pdev, 0); + if (bwmon->irq < 0) { + dev_err(dev, "failed to acquire bwmon IRQ\n"); + return bwmon->irq; + } + + ret = devm_pm_opp_of_add_table(dev); + if (ret) + return dev_err_probe(dev, ret, "failed to add OPP table\n"); + + bwmon->max_bw_kbps = UINT_MAX; + opp = dev_pm_opp_find_bw_floor(dev, &bwmon->max_bw_kbps, 0); + if (IS_ERR(opp)) + return dev_err_probe(dev, ret, "failed to find max peak bandwidth\n"); + + bwmon->min_bw_kbps = 0; + opp = dev_pm_opp_find_bw_ceil(dev, &bwmon->min_bw_kbps, 0); + if (IS_ERR(opp)) + return dev_err_probe(dev, ret, "failed to find min peak bandwidth\n"); + + bwmon->sample_ms = data->sample_ms; + bwmon->default_lowbw_kbps = data->default_lowbw_kbps; + bwmon->dev = dev; + + bwmon_disable(bwmon); + ret = devm_request_threaded_irq(dev, bwmon->irq, bwmon_intr, + bwmon_intr_thread, + IRQF_ONESHOT, dev_name(dev), bwmon); + if (ret) + return dev_err_probe(dev, ret, "failed to request IRQ\n"); + + platform_set_drvdata(pdev, bwmon); + bwmon_start(bwmon, data); + + return 0; +} + +static int bwmon_remove(struct platform_device *pdev) +{ + struct icc_bwmon *bwmon = platform_get_drvdata(pdev); + + bwmon_disable(bwmon); + + return 0; +} + +/* BWMON v4 */ +static const struct icc_bwmon_data msm8998_bwmon_data = { + .sample_ms = 4, + .default_highbw_kbps = 4800 * 1024, /* 4.8 GBps */ + .default_medbw_kbps = 512 * 1024, /* 512 MBps */ + .default_lowbw_kbps = 0, + .zone1_thres_count = 16, + .zone3_thres_count = 1, +}; + +static const struct of_device_id bwmon_of_match[] = { + { .compatible = "qcom,msm8998-bwmon", .data = &msm8998_bwmon_data }, + {} +}; +MODULE_DEVICE_TABLE(of, bwmon_of_match); + +static struct platform_driver bwmon_driver = { + .probe = bwmon_probe, + .remove = bwmon_remove, + .driver = { + .name = "qcom-bwmon", + .of_match_table = bwmon_of_match, + }, +}; +module_platform_driver(bwmon_driver); + +MODULE_AUTHOR("Krzysztof Kozlowski "); +MODULE_DESCRIPTION("QCOM BWMON driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From f16afe238a7ff3c71a943cf74392538974c29b22 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 2 Jun 2022 17:36:13 +0530 Subject: soc: ti: pruss: Enable support for PRUSS-M subsystem on K3 AM62x SoCs The K3 AM62x family of SoC has one PRUSS-M instance and it has two Programmable Real-Time Units (PRU0 and PRU1). This does not support Industrial Communications Subsystem features like Ethernet. The existing pruss platform driver has been updated to support this through a new AM62x specific compatible. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Nishanth Menon Link: https://lore.kernel.org/r/20220602120613.2175-4-kishon@ti.com --- drivers/soc/ti/pruss.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/soc') diff --git a/drivers/soc/ti/pruss.c b/drivers/soc/ti/pruss.c index 0e4ba0f89533..6882c86b3ce5 100644 --- a/drivers/soc/ti/pruss.c +++ b/drivers/soc/ti/pruss.c @@ -338,6 +338,7 @@ static const struct of_device_id pruss_of_match[] = { { .compatible = "ti,am654-icssg", .data = &am65x_j721e_pruss_data, }, { .compatible = "ti,j721e-icssg", .data = &am65x_j721e_pruss_data, }, { .compatible = "ti,am642-icssg", .data = &am65x_j721e_pruss_data, }, + { .compatible = "ti,am625-pruss", .data = &am65x_j721e_pruss_data, }, {}, }; MODULE_DEVICE_TABLE(of, pruss_of_match); -- cgit v1.2.3 From 13bde169c6fe0ff7a527927a838eb732d6b14f37 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 23 Jun 2022 14:38:49 +0200 Subject: soc: mediatek: mtk-pm-domains: Allow probing vreg supply on two MFGs MediaTek SoCs have multiple MFG power-domains, exclusively used for the GPU which, in turn, requires external power supplies: make sure to have the MTK_SCPD_DOMAIN_SUPPLY cap on the two topmost MFGs to allow voting for regulators on/off upon usage of these power domains. This also ensures that the SRAM is actually powered and that we're not relying on the bootloader leaving this supply on when performing the first (and latter) poweron sequence for these domains' sram. Signed-off-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20220623123850.110225-2-angelogioacchino.delregno@collabora.com Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mt8183-pm-domains.h | 1 + drivers/soc/mediatek/mt8186-pm-domains.h | 2 +- drivers/soc/mediatek/mt8192-pm-domains.h | 2 ++ drivers/soc/mediatek/mt8195-pm-domains.h | 2 +- 4 files changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mt8183-pm-domains.h b/drivers/soc/mediatek/mt8183-pm-domains.h index 71b8757e552d..99de67fe5de8 100644 --- a/drivers/soc/mediatek/mt8183-pm-domains.h +++ b/drivers/soc/mediatek/mt8183-pm-domains.h @@ -41,6 +41,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8183[] = { .pwr_sta2nd_offs = 0x0184, .sram_pdn_bits = 0, .sram_pdn_ack_bits = 0, + .caps = MTK_SCPD_DOMAIN_SUPPLY, }, [MT8183_POWER_DOMAIN_MFG] = { .name = "mfg", diff --git a/drivers/soc/mediatek/mt8186-pm-domains.h b/drivers/soc/mediatek/mt8186-pm-domains.h index bf2dd0cdc3a8..108af61854a3 100644 --- a/drivers/soc/mediatek/mt8186-pm-domains.h +++ b/drivers/soc/mediatek/mt8186-pm-domains.h @@ -51,7 +51,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8186[] = { MT8186_TOP_AXI_PROT_EN_1_CLR, MT8186_TOP_AXI_PROT_EN_1_STA), }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY, }, [MT8186_POWER_DOMAIN_MFG2] = { .name = "mfg2", diff --git a/drivers/soc/mediatek/mt8192-pm-domains.h b/drivers/soc/mediatek/mt8192-pm-domains.h index 558c4ee4784a..b97b2051920f 100644 --- a/drivers/soc/mediatek/mt8192-pm-domains.h +++ b/drivers/soc/mediatek/mt8192-pm-domains.h @@ -58,6 +58,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8192[] = { .pwr_sta2nd_offs = 0x0170, .sram_pdn_bits = GENMASK(8, 8), .sram_pdn_ack_bits = GENMASK(12, 12), + .caps = MTK_SCPD_DOMAIN_SUPPLY, }, [MT8192_POWER_DOMAIN_MFG1] = { .name = "mfg1", @@ -85,6 +86,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8192[] = { MT8192_TOP_AXI_PROT_EN_2_CLR, MT8192_TOP_AXI_PROT_EN_2_STA1), }, + .caps = MTK_SCPD_DOMAIN_SUPPLY, }, [MT8192_POWER_DOMAIN_MFG2] = { .name = "mfg2", diff --git a/drivers/soc/mediatek/mt8195-pm-domains.h b/drivers/soc/mediatek/mt8195-pm-domains.h index 0529d130b675..d7387ea1b9c9 100644 --- a/drivers/soc/mediatek/mt8195-pm-domains.h +++ b/drivers/soc/mediatek/mt8195-pm-domains.h @@ -162,7 +162,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8195[] = { MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_CLR, MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_STA1), }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY, }, [MT8195_POWER_DOMAIN_MFG2] = { .name = "mfg2", -- cgit v1.2.3 From c749d676a33d99ee4c40d69ac2bf280270d890ad Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Wed, 22 Jun 2022 10:56:49 -0700 Subject: soc: mediatek: SVS: Use DEFINE_SIMPLE_DEV_PM_OPS for svs_pm_ops When building this driver for an architecture that does not support CONFIG_PM_SLEEP, such as hexagon, the following warnings occur: drivers/soc/mediatek/mtk-svs.c:1481:12: error: unused function 'svs_suspend' [-Werror,-Wunused-function] static int svs_suspend(struct device *dev) ^ drivers/soc/mediatek/mtk-svs.c:1515:12: error: unused function 'svs_resume' [-Werror,-Wunused-function] static int svs_resume(struct device *dev) ^ 2 errors generated. This happens because SIMPLE_DEV_PM_OPS uses SET_SYSTEM_SLEEP_PM_OPS, which evaluates to nothing when CONFIG_PM_SLEEP is not set, leaving the functions unused in the eyes of the compiler. This problem was rectified in commit 1a3c7bb08826 ("PM: core: Add new *_PM_OPS macros, deprecate old ones") with new macros. Use DEFINE_SIMPLE_DEV_PM_OPS to fix the warning while not changing svs_pm_ops when CONFIG_PM_SLEEP is set. Fixes: 681a02e95000 ("soc: mediatek: SVS: introduce MTK SVS engine") Signed-off-by: Nathan Chancellor Link: https://lore.kernel.org/r/20220622175649.1856337-1-nathan@kernel.org Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-svs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-svs.c b/drivers/soc/mediatek/mtk-svs.c index 87e05ab51552..dee8664a12fd 100644 --- a/drivers/soc/mediatek/mtk-svs.c +++ b/drivers/soc/mediatek/mtk-svs.c @@ -2385,7 +2385,7 @@ svs_probe_free_resource: return ret; } -static SIMPLE_DEV_PM_OPS(svs_pm_ops, svs_suspend, svs_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(svs_pm_ops, svs_suspend, svs_resume); static struct platform_driver svs_driver = { .probe = svs_probe, -- cgit v1.2.3 From 2b5764fb7bf8e22c9e853eb58865df9d74f8f20a Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 3 May 2022 16:14:41 +0200 Subject: soc: mediatek: pm-domains: Add support for Helio X10 MT6795 Add pm-domains (mtcmos) data for MediaTek Helio X10 MT6795 SoC. Signed-off-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20220503141441.125852-3-angelogioacchino.delregno@collabora.com Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mt6795-pm-domains.h | 112 +++++++++++++++++++++++++++++++ drivers/soc/mediatek/mtk-pm-domains.c | 5 ++ 2 files changed, 117 insertions(+) create mode 100644 drivers/soc/mediatek/mt6795-pm-domains.h (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mt6795-pm-domains.h b/drivers/soc/mediatek/mt6795-pm-domains.h new file mode 100644 index 000000000000..ef07c9dfdd9b --- /dev/null +++ b/drivers/soc/mediatek/mt6795-pm-domains.h @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SOC_MEDIATEK_MT6795_PM_DOMAINS_H +#define __SOC_MEDIATEK_MT6795_PM_DOMAINS_H + +#include "mtk-pm-domains.h" +#include + +/* + * MT6795 power domain support + */ + +static const struct scpsys_domain_data scpsys_domain_data_mt6795[] = { + [MT6795_POWER_DOMAIN_VDEC] = { + .name = "vdec", + .sta_mask = PWR_STATUS_VDEC, + .ctl_offs = SPM_VDE_PWR_CON, + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + }, + [MT6795_POWER_DOMAIN_VENC] = { + .name = "venc", + .sta_mask = PWR_STATUS_VENC, + .ctl_offs = SPM_VEN_PWR_CON, + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + }, + [MT6795_POWER_DOMAIN_ISP] = { + .name = "isp", + .sta_mask = PWR_STATUS_ISP, + .ctl_offs = SPM_ISP_PWR_CON, + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(13, 12), + }, + [MT6795_POWER_DOMAIN_MM] = { + .name = "mm", + .sta_mask = PWR_STATUS_DISP, + .ctl_offs = SPM_DIS_PWR_CON, + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_UPDATE_TOPAXI(MT8173_TOP_AXI_PROT_EN_MM_M0 | + MT8173_TOP_AXI_PROT_EN_MM_M1), + }, + }, + [MT6795_POWER_DOMAIN_MJC] = { + .name = "mjc", + .sta_mask = BIT(20), + .ctl_offs = 0x298, + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + }, + [MT6795_POWER_DOMAIN_AUDIO] = { + .name = "audio", + .sta_mask = PWR_STATUS_AUDIO, + .ctl_offs = SPM_AUDIO_PWR_CON, + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + }, + [MT6795_POWER_DOMAIN_MFG_ASYNC] = { + .name = "mfg_async", + .sta_mask = PWR_STATUS_MFG_ASYNC, + .ctl_offs = SPM_MFG_ASYNC_PWR_CON, + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = 0, + }, + [MT6795_POWER_DOMAIN_MFG_2D] = { + .name = "mfg_2d", + .sta_mask = PWR_STATUS_MFG_2D, + .ctl_offs = SPM_MFG_2D_PWR_CON, + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(13, 12), + }, + [MT6795_POWER_DOMAIN_MFG] = { + .name = "mfg", + .sta_mask = PWR_STATUS_MFG, + .ctl_offs = SPM_MFG_PWR_CON, + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND, + .sram_pdn_bits = GENMASK(13, 8), + .sram_pdn_ack_bits = GENMASK(21, 16), + .bp_infracfg = { + BUS_PROT_UPDATE_TOPAXI(MT8173_TOP_AXI_PROT_EN_MFG_S | + MT8173_TOP_AXI_PROT_EN_MFG_M0 | + MT8173_TOP_AXI_PROT_EN_MFG_M1 | + MT8173_TOP_AXI_PROT_EN_MFG_SNOOP_OUT), + }, + }, +}; + +static const struct scpsys_soc_data mt6795_scpsys_data = { + .domains_data = scpsys_domain_data_mt6795, + .num_domains = ARRAY_SIZE(scpsys_domain_data_mt6795), +}; + +#endif /* __SOC_MEDIATEK_MT6795_PM_DOMAINS_H */ diff --git a/drivers/soc/mediatek/mtk-pm-domains.c b/drivers/soc/mediatek/mtk-pm-domains.c index a3dae391a38a..9734f1091c69 100644 --- a/drivers/soc/mediatek/mtk-pm-domains.c +++ b/drivers/soc/mediatek/mtk-pm-domains.c @@ -16,6 +16,7 @@ #include #include +#include "mt6795-pm-domains.h" #include "mt8167-pm-domains.h" #include "mt8173-pm-domains.h" #include "mt8183-pm-domains.h" @@ -558,6 +559,10 @@ static void scpsys_domain_cleanup(struct scpsys *scpsys) } static const struct of_device_id scpsys_of_match[] = { + { + .compatible = "mediatek,mt6795-power-controller", + .data = &mt6795_scpsys_data, + }, { .compatible = "mediatek,mt8167-power-controller", .data = &mt8167_scpsys_data, -- cgit v1.2.3 From 4d3ddc9bce4b2bfc167d8c2880f34de29b4587c6 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 23 Jun 2022 14:17:01 +0200 Subject: soc: mediatek: mutex: Simplify with devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. While at it, also remove a redundant call to of_address_to_resource(), as this was getting the address of the same resource index 0: we can as well get the iostart from the 'regs' pointer. Signed-off-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20220623121701.107512-1-angelogioacchino.delregno@collabora.com Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-mutex.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-mutex.c b/drivers/soc/mediatek/mtk-mutex.c index 2fd8318c3bdc..5ea43de4e410 100644 --- a/drivers/soc/mediatek/mtk-mutex.c +++ b/drivers/soc/mediatek/mtk-mutex.c @@ -750,7 +750,7 @@ static int mtk_mutex_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct mtk_mutex_ctx *mtx; - struct resource *regs, addr; + struct resource *regs; int i; #if IS_REACHABLE(CONFIG_MTK_CMDQ) int ret; @@ -774,11 +774,12 @@ static int mtk_mutex_probe(struct platform_device *pdev) } } - if (of_address_to_resource(dev->of_node, 0, &addr) < 0) { - dev_err(dev, "Failed to get addr\n"); - return -EINVAL; + mtx->regs = devm_platform_get_and_ioremap_resource(pdev, 0, ®s); + if (IS_ERR(mtx->regs)) { + dev_err(dev, "Failed to map mutex registers\n"); + return PTR_ERR(mtx->regs); } - mtx->addr = addr.start; + mtx->addr = regs->start; #if IS_REACHABLE(CONFIG_MTK_CMDQ) ret = cmdq_dev_get_client_reg(dev, &mtx->cmdq_reg, 0); @@ -786,13 +787,6 @@ static int mtk_mutex_probe(struct platform_device *pdev) dev_dbg(dev, "No mediatek,gce-client-reg!\n"); #endif - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mtx->regs = devm_ioremap_resource(dev, regs); - if (IS_ERR(mtx->regs)) { - dev_err(dev, "Failed to map mutex registers\n"); - return PTR_ERR(mtx->regs); - } - platform_set_drvdata(pdev, mtx); return 0; -- cgit v1.2.3 From a6232f2aa99ce470799992e99e0012945bb5308f Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Thu, 7 Jul 2022 23:21:58 +0200 Subject: soc: qcom: Make QCOM_RPMPD depend on PM QCOM_RPMPD requires PM_GENERIC_DOMAINS/_OF, which in turns requires CONFIG_PM. I forgot about the latter in my earlier patch (it's still in -next as of the time of committing, hence no Fixes: tag). Fix it. Reported-by: kernel test robot Signed-off-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220707212158.32684-1-konrad.dybcio@somainline.org --- drivers/soc/qcom/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index c0a46633bed9..e0d7a5459562 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -129,6 +129,7 @@ config QCOM_RPMHPD config QCOM_RPMPD tristate "Qualcomm RPM Power domain driver" + depends on PM depends on QCOM_SMD_RPM select PM_GENERIC_DOMAINS select PM_GENERIC_DOMAINS_OF -- cgit v1.2.3 From 5bed21af0005cc7d8bb05d2c4a63afbcede23382 Mon Sep 17 00:00:00 2001 From: Parikshit Pareek Date: Mon, 11 Jul 2022 14:09:57 +0530 Subject: soc: qcom: socinfo: Fix the id of SA8540P SoC Change the id of SA8540P to its correct value, i.e., 461. Also, map the id 460 to its correct values, i.e. SA8295P. Fixes: 76ee15ae1b13 ("soc: qcom: socinfo: Add some more PMICs and SoCs") Signed-off-by: Parikshit Pareek Reviewed-by: Eric Chanudet Tested-by: Eric Chanudet Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220711083957.12091-1-quic_ppareek@quicinc.com --- drivers/soc/qcom/socinfo.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index c2c879ccc6c0..4554fb8655d3 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -328,7 +328,8 @@ static const struct soc_id soc_id[] = { { 455, "QRB5165" }, { 457, "SM8450" }, { 459, "SM7225" }, - { 460, "SA8540P" }, + { 460, "SA8295P" }, + { 461, "SA8540P" }, { 480, "SM8450" }, { 482, "SM8450" }, { 487, "SC7280" }, -- cgit v1.2.3 From 64f89dfaa4f155e3797191286aac6e60f404725f Mon Sep 17 00:00:00 2001 From: Hitomi Hasegawa Date: Fri, 20 May 2022 16:41:19 +0900 Subject: soc: fujitsu: Add A64FX diagnostic interrupt driver Register the NMI/IRQ corresponding to the A64FX's device definition dedicated to diagnostic interrupts, so that when this interrupt is sent using the BMC, it causes a panic. This can be used to obtain a kernel dump. Signed-off-by: Hitomi Hasegawa Link: https://lore.kernel.org/r/20220520074119.3574753-2-hasegawa-hitomi@fujitsu.com' Signed-off-by: Arnd Bergmann --- MAINTAINERS | 5 ++ drivers/soc/Kconfig | 1 + drivers/soc/Makefile | 1 + drivers/soc/fujitsu/Kconfig | 16 ++++ drivers/soc/fujitsu/Makefile | 3 + drivers/soc/fujitsu/a64fx-diag.c | 154 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 180 insertions(+) create mode 100644 drivers/soc/fujitsu/Kconfig create mode 100644 drivers/soc/fujitsu/Makefile create mode 100644 drivers/soc/fujitsu/a64fx-diag.c (limited to 'drivers/soc') diff --git a/MAINTAINERS b/MAINTAINERS index 2ef5b11cd813..49cb2d39c421 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -242,6 +242,11 @@ F: include/trace/events/9p.h F: include/uapi/linux/virtio_9p.h F: net/9p/ +A64FX DIAG DRIVER +M: Hitomi Hasegawa +S: Supported +F: drivers/soc/fujitsu/a64fx-diag.c + A8293 MEDIA DRIVER M: Antti Palosaari L: linux-media@vger.kernel.org diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index 86ccf5970bc1..e461c071189b 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -9,6 +9,7 @@ source "drivers/soc/atmel/Kconfig" source "drivers/soc/bcm/Kconfig" source "drivers/soc/canaan/Kconfig" source "drivers/soc/fsl/Kconfig" +source "drivers/soc/fujitsu/Kconfig" source "drivers/soc/imx/Kconfig" source "drivers/soc/ixp4xx/Kconfig" source "drivers/soc/litex/Kconfig" diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 919716e0e700..69ba6508cf2c 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_SOC_CANAAN) += canaan/ obj-$(CONFIG_ARCH_DOVE) += dove/ obj-$(CONFIG_MACH_DOVE) += dove/ obj-y += fsl/ +obj-y += fujitsu/ obj-$(CONFIG_ARCH_GEMINI) += gemini/ obj-y += imx/ obj-y += ixp4xx/ diff --git a/drivers/soc/fujitsu/Kconfig b/drivers/soc/fujitsu/Kconfig new file mode 100644 index 000000000000..56275b44b293 --- /dev/null +++ b/drivers/soc/fujitsu/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only +menu "fujitsu SoC drivers" + +config A64FX_DIAG + tristate "A64FX diag driver" + depends on ARM64 + depends on ACPI + help + Say Y here if you want to enable diag interrupt on Fujitsu A64FX. + This driver enables BMC's diagnostic requests and enables + A64FX-specific interrupts. This allows administrators to obtain + kernel dumps via diagnostic requests using ipmitool, etc. + + If unsure, say N. + +endmenu diff --git a/drivers/soc/fujitsu/Makefile b/drivers/soc/fujitsu/Makefile new file mode 100644 index 000000000000..945bc1c14ad0 --- /dev/null +++ b/drivers/soc/fujitsu/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_A64FX_DIAG) += a64fx-diag.o diff --git a/drivers/soc/fujitsu/a64fx-diag.c b/drivers/soc/fujitsu/a64fx-diag.c new file mode 100644 index 000000000000..d87f348427bf --- /dev/null +++ b/drivers/soc/fujitsu/a64fx-diag.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * A64FX diag driver. + * Copyright (c) 2022 Fujitsu Ltd. + */ + +#include +#include +#include +#include +#include + +#define A64FX_DIAG_IRQ 1 +#define BMC_DIAG_INTERRUPT_ENABLE 0x40 +#define BMC_DIAG_INTERRUPT_STATUS 0x44 +#define BMC_DIAG_INTERRUPT_MASK BIT(31) + +struct a64fx_diag_priv { + void __iomem *mmsc_reg_base; + int irq; + bool has_nmi; +}; + +static irqreturn_t a64fx_diag_handler_nmi(int irq, void *dev_id) +{ + nmi_panic(NULL, "a64fx_diag: interrupt received\n"); + + return IRQ_HANDLED; +} + +static irqreturn_t a64fx_diag_handler_irq(int irq, void *dev_id) +{ + panic("a64fx_diag: interrupt received\n"); + + return IRQ_HANDLED; +} + +static void a64fx_diag_interrupt_clear(struct a64fx_diag_priv *priv) +{ + void __iomem *diag_status_reg_addr; + u32 mmsc; + + diag_status_reg_addr = priv->mmsc_reg_base + BMC_DIAG_INTERRUPT_STATUS; + mmsc = readl(diag_status_reg_addr); + if (mmsc & BMC_DIAG_INTERRUPT_MASK) + writel(BMC_DIAG_INTERRUPT_MASK, diag_status_reg_addr); +} + +static void a64fx_diag_interrupt_enable(struct a64fx_diag_priv *priv) +{ + void __iomem *diag_enable_reg_addr; + u32 mmsc; + + diag_enable_reg_addr = priv->mmsc_reg_base + BMC_DIAG_INTERRUPT_ENABLE; + mmsc = readl(diag_enable_reg_addr); + if (!(mmsc & BMC_DIAG_INTERRUPT_MASK)) { + mmsc |= BMC_DIAG_INTERRUPT_MASK; + writel(mmsc, diag_enable_reg_addr); + } +} + +static void a64fx_diag_interrupt_disable(struct a64fx_diag_priv *priv) +{ + void __iomem *diag_enable_reg_addr; + u32 mmsc; + + diag_enable_reg_addr = priv->mmsc_reg_base + BMC_DIAG_INTERRUPT_ENABLE; + mmsc = readl(diag_enable_reg_addr); + if (mmsc & BMC_DIAG_INTERRUPT_MASK) { + mmsc &= ~BMC_DIAG_INTERRUPT_MASK; + writel(mmsc, diag_enable_reg_addr); + } +} + +static int a64fx_diag_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct a64fx_diag_priv *priv; + unsigned long irq_flags; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + priv->mmsc_reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->mmsc_reg_base)) + return PTR_ERR(priv->mmsc_reg_base); + + priv->irq = platform_get_irq(pdev, A64FX_DIAG_IRQ); + if (priv->irq < 0) + return priv->irq; + + platform_set_drvdata(pdev, priv); + + irq_flags = IRQF_PERCPU | IRQF_NOBALANCING | IRQF_NO_AUTOEN | + IRQF_NO_THREAD; + ret = request_nmi(priv->irq, &a64fx_diag_handler_nmi, irq_flags, + "a64fx_diag_nmi", NULL); + if (ret) { + ret = request_irq(priv->irq, &a64fx_diag_handler_irq, + irq_flags, "a64fx_diag_irq", NULL); + if (ret) { + dev_err(dev, "cannot register IRQ %d\n", ret); + return ret; + } + enable_irq(priv->irq); + } else { + enable_nmi(priv->irq); + priv->has_nmi = true; + } + + a64fx_diag_interrupt_clear(priv); + a64fx_diag_interrupt_enable(priv); + + return 0; +} + +static int a64fx_diag_remove(struct platform_device *pdev) +{ + struct a64fx_diag_priv *priv = platform_get_drvdata(pdev); + + a64fx_diag_interrupt_disable(priv); + a64fx_diag_interrupt_clear(priv); + + if (priv->has_nmi) + free_nmi(priv->irq, NULL); + else + free_irq(priv->irq, NULL); + + return 0; +} + +static const struct acpi_device_id a64fx_diag_acpi_match[] = { + { "FUJI2007", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, a64fx_diag_acpi_match); + + +static struct platform_driver a64fx_diag_driver = { + .driver = { + .name = "a64fx_diag_driver", + .acpi_match_table = ACPI_PTR(a64fx_diag_acpi_match), + }, + .probe = a64fx_diag_probe, + .remove = a64fx_diag_remove, +}; + +module_platform_driver(a64fx_diag_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Hitomi Hasegawa "); +MODULE_DESCRIPTION("A64FX diag driver"); -- cgit v1.2.3 From 947bb0d16576bdac27f3355f268f51acfe29bfa7 Mon Sep 17 00:00:00 2001 From: Yang Li Date: Thu, 14 Jul 2022 15:55:32 +0800 Subject: soc: qcom: icc-bwmon: Remove unnecessary print function dev_err() Eliminate the follow coccicheck warning: ./drivers/soc/qcom/icc-bwmon.c:349:2-9: line 349 is redundant because platform_get_irq() already prints an error Fixes: b9c2ae6cac40 ("soc: qcom: icc-bwmon: Add bandwidth monitoring driver") Signed-off-by: Yang Li Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220714075532.104665-1-yang.lee@linux.alibaba.com --- drivers/soc/qcom/icc-bwmon.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/icc-bwmon.c b/drivers/soc/qcom/icc-bwmon.c index bea3ea1de7a4..7f8aca533cd3 100644 --- a/drivers/soc/qcom/icc-bwmon.c +++ b/drivers/soc/qcom/icc-bwmon.c @@ -345,10 +345,8 @@ static int bwmon_probe(struct platform_device *pdev) } bwmon->irq = platform_get_irq(pdev, 0); - if (bwmon->irq < 0) { - dev_err(dev, "failed to acquire bwmon IRQ\n"); + if (bwmon->irq < 0) return bwmon->irq; - } ret = devm_pm_opp_of_add_table(dev); if (ret) -- cgit v1.2.3 From 73579f2a99a63c24339d93e39c6fbf0d76e1c770 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Tue, 5 Jul 2022 16:35:17 +0200 Subject: soc: qcom: smd-rpm: Add compatible for MSM8909 Add the new "qcom,rpm-msm8909" compatible to the driver so the interface to the Resource Power Manager (RPM) is initialized correctly on MSM8909. Signed-off-by: Stephan Gerhold Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220705143523.3390944-3-stephan.gerhold@kernkonzept.com --- drivers/soc/qcom/smd-rpm.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/smd-rpm.c b/drivers/soc/qcom/smd-rpm.c index 30dda1af63c8..413f9f4ae9cd 100644 --- a/drivers/soc/qcom/smd-rpm.c +++ b/drivers/soc/qcom/smd-rpm.c @@ -234,6 +234,7 @@ static const struct of_device_id qcom_smd_rpm_of_match[] = { { .compatible = "qcom,rpm-apq8084" }, { .compatible = "qcom,rpm-ipq6018" }, { .compatible = "qcom,rpm-msm8226" }, + { .compatible = "qcom,rpm-msm8909" }, { .compatible = "qcom,rpm-msm8916" }, { .compatible = "qcom,rpm-msm8936" }, { .compatible = "qcom,rpm-msm8953" }, -- cgit v1.2.3 From 488f1d96c35421c8cc09887b7cdf7504c757283b Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Tue, 5 Jul 2022 16:35:19 +0200 Subject: soc: qcom: rpmpd: Add compatible for MSM8909 MSM8909 has the same power domains as MSM8916, so just define another compatible for the existing definition. Signed-off-by: Stephan Gerhold Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220705143523.3390944-5-stephan.gerhold@kernkonzept.com --- drivers/soc/qcom/rpmpd.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmpd.c b/drivers/soc/qcom/rpmpd.c index 3b5b91621532..5803038c744e 100644 --- a/drivers/soc/qcom/rpmpd.c +++ b/drivers/soc/qcom/rpmpd.c @@ -453,6 +453,7 @@ static const struct rpmpd_desc qcm2290_desc = { static const struct of_device_id rpmpd_match_table[] = { { .compatible = "qcom,mdm9607-rpmpd", .data = &mdm9607_desc }, { .compatible = "qcom,msm8226-rpmpd", .data = &msm8226_desc }, + { .compatible = "qcom,msm8909-rpmpd", .data = &msm8916_desc }, { .compatible = "qcom,msm8916-rpmpd", .data = &msm8916_desc }, { .compatible = "qcom,msm8939-rpmpd", .data = &msm8939_desc }, { .compatible = "qcom,msm8953-rpmpd", .data = &msm8953_desc }, -- cgit v1.2.3 From fddb663de9ebcece42ad1add175fbe0d6b2ae7e8 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Tue, 5 Jul 2022 16:35:21 +0200 Subject: soc: qcom: spm: Add CPU data for MSM8909 Given the lack of public documentation for the SPM, the configuration data is taken without modification from Qualcomm's msm-3.10 release [1]. It is pretty much identical to the one for MSM8916, except that 0x3B is missing in the sequence for standalone power collapse for some reason. [1]: https://git.codelinaro.org/clo/la/kernel/msm-3.10/-/blob/LA.BR.1.2.3-00910-8x09.0/arch/arm/boot/dts/qcom/msm8909-pm8909-pm.dtsi Signed-off-by: Stephan Gerhold Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220705143523.3390944-7-stephan.gerhold@kernkonzept.com --- drivers/soc/qcom/spm.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c index f831420b7fd4..484b42b7454e 100644 --- a/drivers/soc/qcom/spm.c +++ b/drivers/soc/qcom/spm.c @@ -74,6 +74,18 @@ static const u16 spm_reg_offset_v3_0[SPM_REG_NR] = { [SPM_REG_SEQ_ENTRY] = 0x400, }; +/* SPM register data for 8909 */ +static const struct spm_reg_data spm_reg_8909_cpu = { + .reg_offset = spm_reg_offset_v3_0, + .spm_cfg = 0x1, + .spm_dly = 0x3C102800, + .seq = { 0x60, 0x03, 0x60, 0x0B, 0x0F, 0x20, 0x10, 0x80, 0x30, 0x90, + 0x5B, 0x60, 0x03, 0x60, 0x76, 0x76, 0x0B, 0x94, 0x5B, 0x80, + 0x10, 0x26, 0x30, 0x0F }, + .start_index[PM_SLEEP_MODE_STBY] = 0, + .start_index[PM_SLEEP_MODE_SPC] = 5, +}; + /* SPM register data for 8916 */ static const struct spm_reg_data spm_reg_8916_cpu = { .reg_offset = spm_reg_offset_v3_0, @@ -195,6 +207,8 @@ static const struct of_device_id spm_match_table[] = { .data = &spm_reg_660_silver_l2 }, { .compatible = "qcom,msm8226-saw2-v2.1-cpu", .data = &spm_reg_8226_cpu }, + { .compatible = "qcom,msm8909-saw2-v3.0-cpu", + .data = &spm_reg_8909_cpu }, { .compatible = "qcom,msm8916-saw2-v3.0-cpu", .data = &spm_reg_8916_cpu }, { .compatible = "qcom,msm8974-saw2-v2.1-cpu", -- cgit v1.2.3 From f740949d41a7ff85aa23ce62c29d095066e5e6d4 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 20 Jul 2022 17:58:18 +0200 Subject: soc: a64fx-diag: disable modular build The NMI infrastructure cannot currently be used in loadable modules: ERROR: modpost: "free_nmi" [drivers/soc/fujitsu/a64fx-diag.ko] undefined! ERROR: modpost: "request_nmi" [drivers/soc/fujitsu/a64fx-diag.ko] undefined! ERROR: modpost: "enable_nmi" [drivers/soc/fujitsu/a64fx-diag.ko] undefined! Disable this for now to make allmodconfig build again. We may revisit this and export those symbols instead in the future. Reported-by: Sudip Mukherjee Signed-off-by: Arnd Bergmann --- drivers/soc/fujitsu/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/fujitsu/Kconfig b/drivers/soc/fujitsu/Kconfig index 56275b44b293..987731e80612 100644 --- a/drivers/soc/fujitsu/Kconfig +++ b/drivers/soc/fujitsu/Kconfig @@ -2,7 +2,7 @@ menu "fujitsu SoC drivers" config A64FX_DIAG - tristate "A64FX diag driver" + bool "A64FX diag driver" depends on ARM64 depends on ACPI help -- cgit v1.2.3