From 13032709e2328553970f0002df5edce6aac69425 Mon Sep 17 00:00:00 2001 From: Matthias Brugger Date: Wed, 25 Mar 2020 18:31:21 +0100 Subject: clk / soc: mediatek: Move mt8173 MMSYS to platform driver There is no strong reason for this to use CLK_OF_DECLARE instead of being a platform driver. Plus, MMSYS provides clocks but also a shared register space for the mediatek-drm and the mediatek-mdp driver. So move the MMSYS clocks to a new platform driver and also create a new MMSYS platform driver in drivers/soc/mediatek that instantiates the clock driver. Signed-off-by: Matthias Brugger Signed-off-by: Enric Balletbo i Serra Reviewed-by: CK Hu Reviewed-by: Stephen Boyd Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/Kconfig | 8 +++++++ drivers/soc/mediatek/Makefile | 1 + drivers/soc/mediatek/mtk-mmsys.c | 50 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 drivers/soc/mediatek/mtk-mmsys.c (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig index 2114b563478c..e84513318725 100644 --- a/drivers/soc/mediatek/Kconfig +++ b/drivers/soc/mediatek/Kconfig @@ -44,4 +44,12 @@ config MTK_SCPSYS Say yes here to add support for the MediaTek SCPSYS power domain driver. +config MTK_MMSYS + bool "MediaTek MMSYS Support" + depends on COMMON_CLK_MT8173_MMSYS + default COMMON_CLK_MT8173_MMSYS + help + Say yes here to add support for the MediaTek Multimedia + Subsystem (MMSYS). + endmenu diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile index b01733074ad6..01f9f873634a 100644 --- a/drivers/soc/mediatek/Makefile +++ b/drivers/soc/mediatek/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_MTK_CMDQ) += mtk-cmdq-helper.o obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o obj-$(CONFIG_MTK_PMIC_WRAP) += mtk-pmic-wrap.o obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o +obj-$(CONFIG_MTK_MMSYS) += mtk-mmsys.o diff --git a/drivers/soc/mediatek/mtk-mmsys.c b/drivers/soc/mediatek/mtk-mmsys.c new file mode 100644 index 000000000000..dbdfedd302fa --- /dev/null +++ b/drivers/soc/mediatek/mtk-mmsys.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao + */ + +#include +#include +#include + +struct mtk_mmsys_driver_data { + const char *clk_driver; +}; + +static const struct mtk_mmsys_driver_data mt8173_mmsys_driver_data = { + .clk_driver = "clk-mt8173-mm", +}; + +static int mtk_mmsys_probe(struct platform_device *pdev) +{ + const struct mtk_mmsys_driver_data *data; + struct platform_device *clks; + + data = of_device_get_match_data(&pdev->dev); + + clks = platform_device_register_data(&pdev->dev, data->clk_driver, + PLATFORM_DEVID_AUTO, NULL, 0); + if (IS_ERR(clks)) + return PTR_ERR(clks); + + return 0; +} + +static const struct of_device_id of_match_mtk_mmsys[] = { + { + .compatible = "mediatek,mt8173-mmsys", + .data = &mt8173_mmsys_driver_data, + }, + { } +}; + +static struct platform_driver mtk_mmsys_drv = { + .driver = { + .name = "mtk-mmsys", + .of_match_table = of_match_mtk_mmsys, + }, + .probe = mtk_mmsys_probe, +}; + +builtin_platform_driver(mtk_mmsys_drv); -- cgit From 2c758e301ed95aefde68f98584204811d55c9bb8 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Wed, 25 Mar 2020 18:31:22 +0100 Subject: soc / drm: mediatek: Move routing control to mmsys device Provide a mtk_mmsys_ddp_connect() and mtk_mmsys_disconnect() functions to replace mtk_ddp_add_comp_to_path() and mtk_ddp_remove_comp_from_path(). Those functions will allow DRM driver and others to control the data path routing. Signed-off-by: Enric Balletbo i Serra Reviewed-by: Matthias Brugger Reviewed-by: CK Hu Acked-by: CK Hu Tested-by: Anders Roxell Reviewed-by: Chun-Kuang Hu Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-mmsys.c | 281 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 281 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-mmsys.c b/drivers/soc/mediatek/mtk-mmsys.c index dbdfedd302fa..703064f45616 100644 --- a/drivers/soc/mediatek/mtk-mmsys.c +++ b/drivers/soc/mediatek/mtk-mmsys.c @@ -5,8 +5,76 @@ */ #include +#include #include #include +#include + +#include "../../gpu/drm/mediatek/mtk_drm_ddp.h" +#include "../../gpu/drm/mediatek/mtk_drm_ddp_comp.h" + +#define DISP_REG_CONFIG_DISP_OVL0_MOUT_EN 0x040 +#define DISP_REG_CONFIG_DISP_OVL1_MOUT_EN 0x044 +#define DISP_REG_CONFIG_DISP_OD_MOUT_EN 0x048 +#define DISP_REG_CONFIG_DISP_GAMMA_MOUT_EN 0x04c +#define DISP_REG_CONFIG_DISP_UFOE_MOUT_EN 0x050 +#define DISP_REG_CONFIG_DISP_COLOR0_SEL_IN 0x084 +#define DISP_REG_CONFIG_DISP_COLOR1_SEL_IN 0x088 +#define DISP_REG_CONFIG_DSIE_SEL_IN 0x0a4 +#define DISP_REG_CONFIG_DSIO_SEL_IN 0x0a8 +#define DISP_REG_CONFIG_DPI_SEL_IN 0x0ac +#define DISP_REG_CONFIG_DISP_RDMA2_SOUT 0x0b8 +#define DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN 0x0c4 +#define DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN 0x0c8 +#define DISP_REG_CONFIG_MMSYS_CG_CON0 0x100 + +#define DISP_REG_CONFIG_DISP_OVL_MOUT_EN 0x030 +#define DISP_REG_CONFIG_OUT_SEL 0x04c +#define DISP_REG_CONFIG_DSI_SEL 0x050 +#define DISP_REG_CONFIG_DPI_SEL 0x064 + +#define OVL0_MOUT_EN_COLOR0 0x1 +#define OD_MOUT_EN_RDMA0 0x1 +#define OD1_MOUT_EN_RDMA1 BIT(16) +#define UFOE_MOUT_EN_DSI0 0x1 +#define COLOR0_SEL_IN_OVL0 0x1 +#define OVL1_MOUT_EN_COLOR1 0x1 +#define GAMMA_MOUT_EN_RDMA1 0x1 +#define RDMA0_SOUT_DPI0 0x2 +#define RDMA0_SOUT_DPI1 0x3 +#define RDMA0_SOUT_DSI1 0x1 +#define RDMA0_SOUT_DSI2 0x4 +#define RDMA0_SOUT_DSI3 0x5 +#define RDMA1_SOUT_DPI0 0x2 +#define RDMA1_SOUT_DPI1 0x3 +#define RDMA1_SOUT_DSI1 0x1 +#define RDMA1_SOUT_DSI2 0x4 +#define RDMA1_SOUT_DSI3 0x5 +#define RDMA2_SOUT_DPI0 0x2 +#define RDMA2_SOUT_DPI1 0x3 +#define RDMA2_SOUT_DSI1 0x1 +#define RDMA2_SOUT_DSI2 0x4 +#define RDMA2_SOUT_DSI3 0x5 +#define DPI0_SEL_IN_RDMA1 0x1 +#define DPI0_SEL_IN_RDMA2 0x3 +#define DPI1_SEL_IN_RDMA1 (0x1 << 8) +#define DPI1_SEL_IN_RDMA2 (0x3 << 8) +#define DSI0_SEL_IN_RDMA1 0x1 +#define DSI0_SEL_IN_RDMA2 0x4 +#define DSI1_SEL_IN_RDMA1 0x1 +#define DSI1_SEL_IN_RDMA2 0x4 +#define DSI2_SEL_IN_RDMA1 (0x1 << 16) +#define DSI2_SEL_IN_RDMA2 (0x4 << 16) +#define DSI3_SEL_IN_RDMA1 (0x1 << 16) +#define DSI3_SEL_IN_RDMA2 (0x4 << 16) +#define COLOR1_SEL_IN_OVL1 0x1 + +#define OVL_MOUT_EN_RDMA 0x1 +#define BLS_TO_DSI_RDMA1_TO_DPI1 0x8 +#define BLS_TO_DPI_RDMA1_TO_DSI 0x2 +#define DSI_SEL_IN_BLS 0x0 +#define DPI_SEL_IN_BLS 0x0 +#define DSI_SEL_IN_RDMA 0x1 struct mtk_mmsys_driver_data { const char *clk_driver; @@ -16,10 +84,223 @@ static const struct mtk_mmsys_driver_data mt8173_mmsys_driver_data = { .clk_driver = "clk-mt8173-mm", }; +static unsigned int mtk_mmsys_ddp_mout_en(enum mtk_ddp_comp_id cur, + enum mtk_ddp_comp_id next, + unsigned int *addr) +{ + unsigned int value; + + if (cur == DDP_COMPONENT_OVL0 && next == DDP_COMPONENT_COLOR0) { + *addr = DISP_REG_CONFIG_DISP_OVL0_MOUT_EN; + value = OVL0_MOUT_EN_COLOR0; + } else if (cur == DDP_COMPONENT_OVL0 && next == DDP_COMPONENT_RDMA0) { + *addr = DISP_REG_CONFIG_DISP_OVL_MOUT_EN; + value = OVL_MOUT_EN_RDMA; + } else if (cur == DDP_COMPONENT_OD0 && next == DDP_COMPONENT_RDMA0) { + *addr = DISP_REG_CONFIG_DISP_OD_MOUT_EN; + value = OD_MOUT_EN_RDMA0; + } else if (cur == DDP_COMPONENT_UFOE && next == DDP_COMPONENT_DSI0) { + *addr = DISP_REG_CONFIG_DISP_UFOE_MOUT_EN; + value = UFOE_MOUT_EN_DSI0; + } else if (cur == DDP_COMPONENT_OVL1 && next == DDP_COMPONENT_COLOR1) { + *addr = DISP_REG_CONFIG_DISP_OVL1_MOUT_EN; + value = OVL1_MOUT_EN_COLOR1; + } else if (cur == DDP_COMPONENT_GAMMA && next == DDP_COMPONENT_RDMA1) { + *addr = DISP_REG_CONFIG_DISP_GAMMA_MOUT_EN; + value = GAMMA_MOUT_EN_RDMA1; + } else if (cur == DDP_COMPONENT_OD1 && next == DDP_COMPONENT_RDMA1) { + *addr = DISP_REG_CONFIG_DISP_OD_MOUT_EN; + value = OD1_MOUT_EN_RDMA1; + } else if (cur == DDP_COMPONENT_RDMA0 && next == DDP_COMPONENT_DPI0) { + *addr = DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN; + value = RDMA0_SOUT_DPI0; + } else if (cur == DDP_COMPONENT_RDMA0 && next == DDP_COMPONENT_DPI1) { + *addr = DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN; + value = RDMA0_SOUT_DPI1; + } else if (cur == DDP_COMPONENT_RDMA0 && next == DDP_COMPONENT_DSI1) { + *addr = DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN; + value = RDMA0_SOUT_DSI1; + } else if (cur == DDP_COMPONENT_RDMA0 && next == DDP_COMPONENT_DSI2) { + *addr = DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN; + value = RDMA0_SOUT_DSI2; + } else if (cur == DDP_COMPONENT_RDMA0 && next == DDP_COMPONENT_DSI3) { + *addr = DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN; + value = RDMA0_SOUT_DSI3; + } else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DSI1) { + *addr = DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN; + value = RDMA1_SOUT_DSI1; + } else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DSI2) { + *addr = DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN; + value = RDMA1_SOUT_DSI2; + } else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DSI3) { + *addr = DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN; + value = RDMA1_SOUT_DSI3; + } else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DPI0) { + *addr = DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN; + value = RDMA1_SOUT_DPI0; + } else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DPI1) { + *addr = DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN; + value = RDMA1_SOUT_DPI1; + } else if (cur == DDP_COMPONENT_RDMA2 && next == DDP_COMPONENT_DPI0) { + *addr = DISP_REG_CONFIG_DISP_RDMA2_SOUT; + value = RDMA2_SOUT_DPI0; + } else if (cur == DDP_COMPONENT_RDMA2 && next == DDP_COMPONENT_DPI1) { + *addr = DISP_REG_CONFIG_DISP_RDMA2_SOUT; + value = RDMA2_SOUT_DPI1; + } else if (cur == DDP_COMPONENT_RDMA2 && next == DDP_COMPONENT_DSI1) { + *addr = DISP_REG_CONFIG_DISP_RDMA2_SOUT; + value = RDMA2_SOUT_DSI1; + } else if (cur == DDP_COMPONENT_RDMA2 && next == DDP_COMPONENT_DSI2) { + *addr = DISP_REG_CONFIG_DISP_RDMA2_SOUT; + value = RDMA2_SOUT_DSI2; + } else if (cur == DDP_COMPONENT_RDMA2 && next == DDP_COMPONENT_DSI3) { + *addr = DISP_REG_CONFIG_DISP_RDMA2_SOUT; + value = RDMA2_SOUT_DSI3; + } else { + value = 0; + } + + return value; +} + +static unsigned int mtk_mmsys_ddp_sel_in(enum mtk_ddp_comp_id cur, + enum mtk_ddp_comp_id next, + unsigned int *addr) +{ + unsigned int value; + + if (cur == DDP_COMPONENT_OVL0 && next == DDP_COMPONENT_COLOR0) { + *addr = DISP_REG_CONFIG_DISP_COLOR0_SEL_IN; + value = COLOR0_SEL_IN_OVL0; + } else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DPI0) { + *addr = DISP_REG_CONFIG_DPI_SEL_IN; + value = DPI0_SEL_IN_RDMA1; + } else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DPI1) { + *addr = DISP_REG_CONFIG_DPI_SEL_IN; + value = DPI1_SEL_IN_RDMA1; + } else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DSI0) { + *addr = DISP_REG_CONFIG_DSIE_SEL_IN; + value = DSI0_SEL_IN_RDMA1; + } else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DSI1) { + *addr = DISP_REG_CONFIG_DSIO_SEL_IN; + value = DSI1_SEL_IN_RDMA1; + } else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DSI2) { + *addr = DISP_REG_CONFIG_DSIE_SEL_IN; + value = DSI2_SEL_IN_RDMA1; + } else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DSI3) { + *addr = DISP_REG_CONFIG_DSIO_SEL_IN; + value = DSI3_SEL_IN_RDMA1; + } else if (cur == DDP_COMPONENT_RDMA2 && next == DDP_COMPONENT_DPI0) { + *addr = DISP_REG_CONFIG_DPI_SEL_IN; + value = DPI0_SEL_IN_RDMA2; + } else if (cur == DDP_COMPONENT_RDMA2 && next == DDP_COMPONENT_DPI1) { + *addr = DISP_REG_CONFIG_DPI_SEL_IN; + value = DPI1_SEL_IN_RDMA2; + } else if (cur == DDP_COMPONENT_RDMA2 && next == DDP_COMPONENT_DSI0) { + *addr = DISP_REG_CONFIG_DSIE_SEL_IN; + value = DSI0_SEL_IN_RDMA2; + } else if (cur == DDP_COMPONENT_RDMA2 && next == DDP_COMPONENT_DSI1) { + *addr = DISP_REG_CONFIG_DSIO_SEL_IN; + value = DSI1_SEL_IN_RDMA2; + } else if (cur == DDP_COMPONENT_RDMA2 && next == DDP_COMPONENT_DSI2) { + *addr = DISP_REG_CONFIG_DSIE_SEL_IN; + value = DSI2_SEL_IN_RDMA2; + } else if (cur == DDP_COMPONENT_RDMA2 && next == DDP_COMPONENT_DSI3) { + *addr = DISP_REG_CONFIG_DSIE_SEL_IN; + value = DSI3_SEL_IN_RDMA2; + } else if (cur == DDP_COMPONENT_OVL1 && next == DDP_COMPONENT_COLOR1) { + *addr = DISP_REG_CONFIG_DISP_COLOR1_SEL_IN; + value = COLOR1_SEL_IN_OVL1; + } else if (cur == DDP_COMPONENT_BLS && next == DDP_COMPONENT_DSI0) { + *addr = DISP_REG_CONFIG_DSI_SEL; + value = DSI_SEL_IN_BLS; + } else { + value = 0; + } + + return value; +} + +static void mtk_mmsys_ddp_sout_sel(void __iomem *config_regs, + enum mtk_ddp_comp_id cur, + enum mtk_ddp_comp_id next) +{ + if (cur == DDP_COMPONENT_BLS && next == DDP_COMPONENT_DSI0) { + writel_relaxed(BLS_TO_DSI_RDMA1_TO_DPI1, + config_regs + DISP_REG_CONFIG_OUT_SEL); + } else if (cur == DDP_COMPONENT_BLS && next == DDP_COMPONENT_DPI0) { + writel_relaxed(BLS_TO_DPI_RDMA1_TO_DSI, + config_regs + DISP_REG_CONFIG_OUT_SEL); + writel_relaxed(DSI_SEL_IN_RDMA, + config_regs + DISP_REG_CONFIG_DSI_SEL); + writel_relaxed(DPI_SEL_IN_BLS, + config_regs + DISP_REG_CONFIG_DPI_SEL); + } +} + +void mtk_mmsys_ddp_connect(struct device *dev, + enum mtk_ddp_comp_id cur, + enum mtk_ddp_comp_id next) +{ + void __iomem *config_regs = dev_get_drvdata(dev); + unsigned int addr, value, reg; + + value = mtk_mmsys_ddp_mout_en(cur, next, &addr); + if (value) { + reg = readl_relaxed(config_regs + addr) | value; + writel_relaxed(reg, config_regs + addr); + } + + mtk_mmsys_ddp_sout_sel(config_regs, cur, next); + + value = mtk_mmsys_ddp_sel_in(cur, next, &addr); + if (value) { + reg = readl_relaxed(config_regs + addr) | value; + writel_relaxed(reg, config_regs + addr); + } +} +EXPORT_SYMBOL_GPL(mtk_mmsys_ddp_connect); + +void mtk_mmsys_ddp_disconnect(struct device *dev, + enum mtk_ddp_comp_id cur, + enum mtk_ddp_comp_id next) +{ + void __iomem *config_regs = dev_get_drvdata(dev); + unsigned int addr, value, reg; + + value = mtk_mmsys_ddp_mout_en(cur, next, &addr); + if (value) { + reg = readl_relaxed(config_regs + addr) & ~value; + writel_relaxed(reg, config_regs + addr); + } + + value = mtk_mmsys_ddp_sel_in(cur, next, &addr); + if (value) { + reg = readl_relaxed(config_regs + addr) & ~value; + writel_relaxed(reg, config_regs + addr); + } +} +EXPORT_SYMBOL_GPL(mtk_mmsys_ddp_disconnect); + static int mtk_mmsys_probe(struct platform_device *pdev) { const struct mtk_mmsys_driver_data *data; + struct device *dev = &pdev->dev; struct platform_device *clks; + void __iomem *config_regs; + struct resource *mem; + int ret; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + config_regs = devm_ioremap_resource(dev, mem); + if (IS_ERR(config_regs)) { + ret = PTR_ERR(config_regs); + dev_err(dev, "Failed to ioremap mmsys-config resource: %d\n", + ret); + return ret; + } + + platform_set_drvdata(pdev, config_regs); data = of_device_get_match_data(&pdev->dev); -- cgit From 667c769246b01c53ad0925d603d2a2531abd3ef2 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Wed, 25 Mar 2020 18:31:23 +0100 Subject: soc / drm: mediatek: Fix mediatek-drm device probing In the actual implementation the same compatible string "mediatek,-mmsys" is used to bind the clock drivers (drivers/soc/mediatek) as well as to the gpu driver (drivers/gpu/drm/mediatek/mtk_drm_drv.c). This ends with the problem that the only probed driver is the clock driver and there is no display at all. In any case having the same compatible string for two drivers is not correct and should be fixed. To fix this, and maintain backward compatibility, we can consider that the mmsys driver is the top-level entry point for the multimedia subsystem, so is not a pure clock controller but a system controller, and the drm driver is instantiated by that MMSYS driver. Signed-off-by: Enric Balletbo i Serra Reviewed-by: CK Hu Acked-by: CK Hu Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-mmsys.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-mmsys.c b/drivers/soc/mediatek/mtk-mmsys.c index 703064f45616..05e322c9c301 100644 --- a/drivers/soc/mediatek/mtk-mmsys.c +++ b/drivers/soc/mediatek/mtk-mmsys.c @@ -287,6 +287,7 @@ static int mtk_mmsys_probe(struct platform_device *pdev) const struct mtk_mmsys_driver_data *data; struct device *dev = &pdev->dev; struct platform_device *clks; + struct platform_device *drm; void __iomem *config_regs; struct resource *mem; int ret; @@ -309,6 +310,11 @@ static int mtk_mmsys_probe(struct platform_device *pdev) if (IS_ERR(clks)) return PTR_ERR(clks); + drm = platform_device_register_data(&pdev->dev, "mediatek-drm", + PLATFORM_DEVID_AUTO, NULL, 0); + if (IS_ERR(drm)) + return PTR_ERR(drm); + return 0; } -- cgit From 27a344139c186889d742764d3c2a62b395949cef Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 19 Mar 2020 12:14:18 +0000 Subject: soc: qcom: socinfo: add missing soc_id sysfs entry Looks like SoC ID is not exported to sysfs for some reason. This patch adds it! This is mostly used by userspace libraries like Snapdragon Neural Processing Engine (SNPE) SDK for checking supported SoC info. Fixes: efb448d0a3fc ("soc: qcom: Add socinfo driver") Reviewed-by: Stephen Boyd Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200319121418.5180-1-srinivas.kandagatla@linaro.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/socinfo.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index ebb49aee179b..08a4b8ae1764 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -430,6 +430,8 @@ static int qcom_socinfo_probe(struct platform_device *pdev) qs->attr.family = "Snapdragon"; qs->attr.machine = socinfo_machine(&pdev->dev, le32_to_cpu(info->id)); + qs->attr.soc_id = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%u", + le32_to_cpu(info->id)); qs->attr.revision = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%u.%u", SOCINFO_MAJOR(le32_to_cpu(info->ver)), SOCINFO_MINOR(le32_to_cpu(info->ver))); -- cgit From d6815c5c43d4f9d18e557d27fd27ae8d9cfd450c Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 9 Mar 2020 11:57:04 -0700 Subject: soc: qcom: cmd-db: Add debugfs dumping file It's useful for kernel devs to understand what resources and data is stored inside command db. Add a file in debugufs called 'cmd-db' to dump the memory contents and strings for resources along with their addresses. E.g. Command DB DUMP Slave ARC (v16.0) ------------------------- 0x00030000: cx.lvl [00 00 10 00 40 00 80 00 c0 00 00 01 80 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00] 0x00030004: cx.tmr 0x00030010: mx.lvl [00 00 10 00 00 01 80 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00] 0x00030014: mx.tmr Cc: Lina Iyer Cc: Maulik Shah Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20200309185704.2491-1-swboyd@chromium.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/cmd-db.c | 78 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 2 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c index f6c3d17b05c7..8b2b7357b6da 100644 --- a/drivers/soc/qcom/cmd-db.c +++ b/drivers/soc/qcom/cmd-db.c @@ -1,12 +1,13 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. */ +#include #include #include #include -#include #include #include +#include #include #include @@ -236,6 +237,77 @@ enum cmd_db_hw_type cmd_db_read_slave_id(const char *id) } EXPORT_SYMBOL(cmd_db_read_slave_id); +#ifdef CONFIG_DEBUG_FS +static int cmd_db_debugfs_dump(struct seq_file *seq, void *p) +{ + int i, j; + const struct rsc_hdr *rsc; + const struct entry_header *ent; + const char *name; + u16 len, version; + u8 major, minor; + + seq_puts(seq, "Command DB DUMP\n"); + + for (i = 0; i < MAX_SLV_ID; i++) { + rsc = &cmd_db_header->header[i]; + if (!rsc->slv_id) + break; + + switch (rsc->slv_id) { + case CMD_DB_HW_ARC: + name = "ARC"; + break; + case CMD_DB_HW_VRM: + name = "VRM"; + break; + case CMD_DB_HW_BCM: + name = "BCM"; + break; + default: + name = "Unknown"; + break; + } + + version = le16_to_cpu(rsc->version); + major = version >> 8; + minor = version; + + seq_printf(seq, "Slave %s (v%u.%u)\n", name, major, minor); + seq_puts(seq, "-------------------------\n"); + + ent = rsc_to_entry_header(rsc); + for (j = 0; j < le16_to_cpu(rsc->cnt); j++, ent++) { + seq_printf(seq, "0x%08x: %*pEp", le32_to_cpu(ent->addr), + sizeof(ent->id), ent->id); + + len = le16_to_cpu(ent->len); + if (len) { + seq_printf(seq, " [%*ph]", + len, rsc_offset(rsc, ent)); + } + seq_putc(seq, '\n'); + } + } + + return 0; +} + +static int open_cmd_db_debugfs(struct inode *inode, struct file *file) +{ + return single_open(file, cmd_db_debugfs_dump, inode->i_private); +} +#endif + +static const struct file_operations cmd_db_debugfs_ops = { +#ifdef CONFIG_DEBUG_FS + .open = open_cmd_db_debugfs, +#endif + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static int cmd_db_dev_probe(struct platform_device *pdev) { struct reserved_mem *rmem; @@ -259,12 +331,14 @@ static int cmd_db_dev_probe(struct platform_device *pdev) return -EINVAL; } + debugfs_create_file("cmd-db", 0400, NULL, NULL, &cmd_db_debugfs_ops); + return 0; } static const struct of_device_id cmd_db_match_table[] = { { .compatible = "qcom,cmd-db" }, - { }, + { } }; static struct platform_driver cmd_db_dev_driver = { -- cgit From 1790c97125dd29d58d2404192c542966b0056309 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sun, 5 Apr 2020 18:08:13 +0200 Subject: soc: qcom: smp2p: Delete an error message in qcom_smp2p_probe() The function platform_get_irq() can log an error already. Thus omit a redundant message for the exception handling in the calling function. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Link: https://lore.kernel.org/r/eb92fcfb-6181-1f9d-2601-61e5231bd892@web.de Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/smp2p.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/smp2p.c b/drivers/soc/qcom/smp2p.c index c7300d54e444..07183d731d74 100644 --- a/drivers/soc/qcom/smp2p.c +++ b/drivers/soc/qcom/smp2p.c @@ -474,10 +474,8 @@ static int qcom_smp2p_probe(struct platform_device *pdev) goto report_read_failure; irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "unable to acquire smp2p interrupt\n"); + if (irq < 0) return irq; - } smp2p->mbox_client.dev = &pdev->dev; smp2p->mbox_client.knows_txdone = true; -- cgit From bb7000677a1b287206c8d4327c62442fa3050a8f Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Sun, 12 Apr 2020 20:20:00 +0530 Subject: soc: qcom: rpmh: Update dirty flag only when data changes Currently rpmh ctrlr dirty flag is set for all cases regardless of data is really changed or not. Add changes to update dirty flag when data is changed to newer values. Update dirty flag everytime when data in batch cache is updated since rpmh_flush() may get invoked from any CPU instead of only last CPU going to low power mode. Also move dirty flag updates to happen from within cache_lock and remove unnecessary INIT_LIST_HEAD() call and a default case from switch. Fixes: 600513dfeef3 ("drivers: qcom: rpmh: cache sleep/wake state requests") Signed-off-by: Maulik Shah Reviewed-by: Srinivas Rao L Reviewed-by: Evan Green Reviewed-by: Douglas Anderson Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/1586703004-13674-3-git-send-email-mkshah@codeaurora.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index eb0ded059d2e..03630aeb4fef 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -119,6 +119,7 @@ static struct cache_req *cache_rpm_request(struct rpmh_ctrlr *ctrlr, { struct cache_req *req; unsigned long flags; + u32 old_sleep_val, old_wake_val; spin_lock_irqsave(&ctrlr->cache_lock, flags); req = __find_req(ctrlr, cmd->addr); @@ -133,26 +134,27 @@ static struct cache_req *cache_rpm_request(struct rpmh_ctrlr *ctrlr, req->addr = cmd->addr; req->sleep_val = req->wake_val = UINT_MAX; - INIT_LIST_HEAD(&req->list); list_add_tail(&req->list, &ctrlr->cache); existing: + old_sleep_val = req->sleep_val; + old_wake_val = req->wake_val; + switch (state) { case RPMH_ACTIVE_ONLY_STATE: - if (req->sleep_val != UINT_MAX) - req->wake_val = cmd->data; - break; case RPMH_WAKE_ONLY_STATE: req->wake_val = cmd->data; break; case RPMH_SLEEP_STATE: req->sleep_val = cmd->data; break; - default: - break; } - ctrlr->dirty = true; + ctrlr->dirty = (req->sleep_val != old_sleep_val || + req->wake_val != old_wake_val) && + req->sleep_val != UINT_MAX && + req->wake_val != UINT_MAX; + unlock: spin_unlock_irqrestore(&ctrlr->cache_lock, flags); @@ -287,6 +289,7 @@ static void cache_batch(struct rpmh_ctrlr *ctrlr, struct batch_cache_req *req) spin_lock_irqsave(&ctrlr->cache_lock, flags); list_add_tail(&req->list, &ctrlr->batch_cache); + ctrlr->dirty = true; spin_unlock_irqrestore(&ctrlr->cache_lock, flags); } @@ -323,6 +326,7 @@ static void invalidate_batch(struct rpmh_ctrlr *ctrlr) list_for_each_entry_safe(req, tmp, &ctrlr->batch_cache, list) kfree(req); INIT_LIST_HEAD(&ctrlr->batch_cache); + ctrlr->dirty = true; spin_unlock_irqrestore(&ctrlr->cache_lock, flags); } @@ -507,7 +511,6 @@ int rpmh_invalidate(const struct device *dev) int ret; invalidate_batch(ctrlr); - ctrlr->dirty = true; do { ret = rpmh_rsc_invalidate(ctrlr_to_drv(ctrlr)); -- cgit From f5ac95f9ca2f439179a5baf48e1c0f22f83d936e Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Sun, 12 Apr 2020 20:20:01 +0530 Subject: soc: qcom: rpmh: Invalidate SLEEP and WAKE TCSes before flushing new data TCSes have previously programmed data when rpmh_flush() is called. This can cause old data to trigger along with newly flushed. Fix this by cleaning SLEEP and WAKE TCSes before new data is flushed. With this there is no need to invoke rpmh_rsc_invalidate() call from rpmh_invalidate(). Simplify rpmh_invalidate() by moving invalidate_batch() inside. Fixes: 600513dfeef3 ("drivers: qcom: rpmh: cache sleep/wake state requests") Signed-off-by: Maulik Shah Reviewed-by: Douglas Anderson Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/1586703004-13674-4-git-send-email-mkshah@codeaurora.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh.c | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index 03630aeb4fef..a75f3df97742 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -317,19 +317,6 @@ static int flush_batch(struct rpmh_ctrlr *ctrlr) return ret; } -static void invalidate_batch(struct rpmh_ctrlr *ctrlr) -{ - struct batch_cache_req *req, *tmp; - unsigned long flags; - - spin_lock_irqsave(&ctrlr->cache_lock, flags); - list_for_each_entry_safe(req, tmp, &ctrlr->batch_cache, list) - kfree(req); - INIT_LIST_HEAD(&ctrlr->batch_cache); - ctrlr->dirty = true; - spin_unlock_irqrestore(&ctrlr->cache_lock, flags); -} - /** * rpmh_write_batch: Write multiple sets of RPMH commands and wait for the * batch to finish. @@ -467,6 +454,13 @@ int rpmh_flush(struct rpmh_ctrlr *ctrlr) return 0; } + /* Invalidate the TCSes first to avoid stale data */ + do { + ret = rpmh_rsc_invalidate(ctrlr_to_drv(ctrlr)); + } while (ret == -EAGAIN); + if (ret) + return ret; + /* First flush the cached batch requests */ ret = flush_batch(ctrlr); if (ret) @@ -498,24 +492,25 @@ int rpmh_flush(struct rpmh_ctrlr *ctrlr) } /** - * rpmh_invalidate: Invalidate all sleep and active sets - * sets. + * rpmh_invalidate: Invalidate sleep and wake sets in batch_cache * * @dev: The device making the request * - * Invalidate the sleep and active values in the TCS blocks. + * Invalidate the sleep and wake values in batch_cache. */ int rpmh_invalidate(const struct device *dev) { struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); - int ret; - - invalidate_batch(ctrlr); + struct batch_cache_req *req, *tmp; + unsigned long flags; - do { - ret = rpmh_rsc_invalidate(ctrlr_to_drv(ctrlr)); - } while (ret == -EAGAIN); + spin_lock_irqsave(&ctrlr->cache_lock, flags); + list_for_each_entry_safe(req, tmp, &ctrlr->batch_cache, list) + kfree(req); + INIT_LIST_HEAD(&ctrlr->batch_cache); + ctrlr->dirty = true; + spin_unlock_irqrestore(&ctrlr->cache_lock, flags); - return ret; + return 0; } EXPORT_SYMBOL(rpmh_invalidate); -- cgit From 985427f997b6a31155cce841eb395d43c64771c5 Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Sun, 12 Apr 2020 20:20:02 +0530 Subject: soc: qcom: rpmh: Invoke rpmh_flush() for dirty caches Add changes to invoke rpmh flush() from CPU PM notification. This is done when the last the cpu is entering deep CPU idle states and controller is not busy. Controllers that have 'HW solver' mode like display RSC do not need to register for CPU PM notification. They may be in autonomous mode executing low power mode and do not require rpmh_flush() to happen from CPU PM notification. Signed-off-by: Maulik Shah Reviewed-by: Douglas Anderson Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/1586703004-13674-5-git-send-email-mkshah@codeaurora.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-internal.h | 25 +++++--- drivers/soc/qcom/rpmh-rsc.c | 122 +++++++++++++++++++++++++++++++++++---- drivers/soc/qcom/rpmh.c | 29 ++++------ 3 files changed, 139 insertions(+), 37 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h index 6eec32b97f83..e9a90cb7773e 100644 --- a/drivers/soc/qcom/rpmh-internal.h +++ b/drivers/soc/qcom/rpmh-internal.h @@ -84,23 +84,32 @@ struct rpmh_ctrlr { * struct rsc_drv: the Direct Resource Voter (DRV) of the * Resource State Coordinator controller (RSC) * - * @name: controller identifier - * @tcs_base: start address of the TCS registers in this controller - * @id: instance id in the controller (Direct Resource Voter) - * @num_tcs: number of TCSes in this DRV - * @tcs: TCS groups - * @tcs_in_use: s/w state of the TCS - * @lock: synchronize state of the controller - * @client: handle to the DRV's client. + * @name: Controller identifier + * @tcs_base: Start address of the TCS registers in this controller + * @id: Instance id in the controller (Direct Resource Voter) + * @num_tcs: Number of TCSes in this DRV + * @rsc_pm: CPU PM notifier for controller + * Used when solver mode is not present + * @cpus_entered_pm: CPU mask for cpus in idle power collapse + * Used when solver mode is not present + * @tcs: TCS groups + * @tcs_in_use: S/W state of the TCS + * @lock: Synchronize state of the controller + * @pm_lock: Synchronize during PM notifications + * Used when solver mode is not present + * @client: Handle to the DRV's client. */ struct rsc_drv { const char *name; void __iomem *tcs_base; int id; int num_tcs; + struct notifier_block rsc_pm; + struct cpumask cpus_entered_pm; struct tcs_group tcs[TCS_TYPE_NR]; DECLARE_BITMAP(tcs_in_use, MAX_TCS_NR); spinlock_t lock; + spinlock_t pm_lock; struct rpmh_ctrlr client; }; diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index b71822131f59..892c82b7e3fb 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -6,6 +6,7 @@ #define pr_fmt(fmt) "%s " fmt, KBUILD_MODNAME #include +#include #include #include #include @@ -30,7 +31,12 @@ #define RSC_DRV_TCS_OFFSET 672 #define RSC_DRV_CMD_OFFSET 20 -/* DRV Configuration Information Register */ +/* DRV HW Solver Configuration Information Register */ +#define DRV_SOLVER_CONFIG 0x04 +#define DRV_HW_SOLVER_MASK 1 +#define DRV_HW_SOLVER_SHIFT 24 + +/* DRV TCS Configuration Information Register */ #define DRV_PRNT_CHLD_CONFIG 0x0C #define DRV_NUM_TCS_MASK 0x3F #define DRV_NUM_TCS_SHIFT 6 @@ -521,8 +527,85 @@ int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg) return tcs_ctrl_write(drv, msg); } +/** + * rpmh_rsc_ctrlr_is_busy() - Check if any of the AMCs are busy. + * + * @drv: The controller + * + * Checks if any of the AMCs are busy in handling ACTIVE sets. + * This is called from the last cpu powering down before flushing + * SLEEP and WAKE sets. If AMCs are busy, controller can not enter + * power collapse, so deny from the last cpu's pm notification. + * + * Return: + * * False - AMCs are idle + * * True - AMCs are busy + */ +static bool rpmh_rsc_ctrlr_is_busy(struct rsc_drv *drv) +{ + int m; + struct tcs_group *tcs = get_tcs_of_type(drv, ACTIVE_TCS); + + /* + * If we made an active request on a RSC that does not have a + * dedicated TCS for active state use, then re-purposed wake TCSes + * should be checked for not busy, because we used wake TCSes for + * active requests in this case. + * + * Since this is called from the last cpu, need not take drv or tcs + * lock before checking tcs_is_free(). + */ + if (!tcs->num_tcs) + tcs = get_tcs_of_type(drv, WAKE_TCS); + + for (m = tcs->offset; m < tcs->offset + tcs->num_tcs; m++) { + if (!tcs_is_free(drv, m)) + return true; + } + + return false; +} + +static int rpmh_rsc_cpu_pm_callback(struct notifier_block *nfb, + unsigned long action, void *v) +{ + struct rsc_drv *drv = container_of(nfb, struct rsc_drv, rsc_pm); + int ret = NOTIFY_OK; + + spin_lock(&drv->pm_lock); + + switch (action) { + case CPU_PM_ENTER: + cpumask_set_cpu(smp_processor_id(), &drv->cpus_entered_pm); + + if (!cpumask_equal(&drv->cpus_entered_pm, cpu_online_mask)) + goto exit; + break; + case CPU_PM_ENTER_FAILED: + case CPU_PM_EXIT: + cpumask_clear_cpu(smp_processor_id(), &drv->cpus_entered_pm); + goto exit; + } + + ret = rpmh_rsc_ctrlr_is_busy(drv); + if (ret) { + ret = NOTIFY_BAD; + goto exit; + } + + ret = rpmh_flush(&drv->client); + if (ret) + ret = NOTIFY_BAD; + else + ret = NOTIFY_OK; + +exit: + spin_unlock(&drv->pm_lock); + return ret; +} + static int rpmh_probe_tcs_config(struct platform_device *pdev, - struct rsc_drv *drv) + struct rsc_drv *drv, void __iomem *base) { struct tcs_type_config { u32 type; @@ -532,15 +615,6 @@ static int rpmh_probe_tcs_config(struct platform_device *pdev, u32 config, max_tcs, ncpt, offset; int i, ret, n, st = 0; struct tcs_group *tcs; - struct resource *res; - void __iomem *base; - char drv_id[10] = {0}; - - snprintf(drv_id, ARRAY_SIZE(drv_id), "drv-%d", drv->id); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, drv_id); - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); ret = of_property_read_u32(dn, "qcom,tcs-offset", &offset); if (ret) @@ -620,7 +694,11 @@ static int rpmh_rsc_probe(struct platform_device *pdev) { struct device_node *dn = pdev->dev.of_node; struct rsc_drv *drv; + struct resource *res; + char drv_id[10] = {0}; int ret, irq; + u32 solver_config; + void __iomem *base; /* * Even though RPMh doesn't directly use cmd-db, all of its children @@ -646,7 +724,13 @@ static int rpmh_rsc_probe(struct platform_device *pdev) if (!drv->name) drv->name = dev_name(&pdev->dev); - ret = rpmh_probe_tcs_config(pdev, drv); + snprintf(drv_id, ARRAY_SIZE(drv_id), "drv-%d", drv->id); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, drv_id); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + ret = rpmh_probe_tcs_config(pdev, drv, base); if (ret) return ret; @@ -663,6 +747,20 @@ static int rpmh_rsc_probe(struct platform_device *pdev) if (ret) return ret; + /* + * CPU PM notification are not required for controllers that support + * 'HW solver' mode where they can be in autonomous mode executing low + * power mode to power down. + */ + solver_config = readl_relaxed(base + DRV_SOLVER_CONFIG); + solver_config &= DRV_HW_SOLVER_MASK << DRV_HW_SOLVER_SHIFT; + solver_config = solver_config >> DRV_HW_SOLVER_SHIFT; + if (!solver_config) { + drv->rsc_pm.notifier_call = rpmh_rsc_cpu_pm_callback; + spin_lock_init(&drv->pm_lock); + cpu_pm_register_notifier(&drv->rsc_pm); + } + /* Enable the active TCS to send requests immediately */ write_tcs_reg(drv, RSC_DRV_IRQ_ENABLE, 0, drv->tcs[ACTIVE_TCS].mask); diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index a75f3df97742..be5e89d73526 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -297,12 +298,10 @@ static int flush_batch(struct rpmh_ctrlr *ctrlr) { struct batch_cache_req *req; const struct rpmh_request *rpm_msg; - unsigned long flags; int ret = 0; int i; /* Send Sleep/Wake requests to the controller, expect no response */ - spin_lock_irqsave(&ctrlr->cache_lock, flags); list_for_each_entry(req, &ctrlr->batch_cache, list) { for (i = 0; i < req->count; i++) { rpm_msg = req->rpm_msgs + i; @@ -312,7 +311,6 @@ static int flush_batch(struct rpmh_ctrlr *ctrlr) break; } } - spin_unlock_irqrestore(&ctrlr->cache_lock, flags); return ret; } @@ -433,31 +431,32 @@ static int send_single(struct rpmh_ctrlr *ctrlr, enum rpmh_state state, } /** - * rpmh_flush: Flushes the buffered active and sleep sets to TCS + * rpmh_flush() - Flushes the buffered sleep and wake sets to TCSes * - * @ctrlr: controller making request to flush cached data + * @ctrlr: Controller making request to flush cached data * - * Return: -EBUSY if the controller is busy, probably waiting on a response - * to a RPMH request sent earlier. + * This function is called from sleep code on the last CPU + * (thus no spinlock needed). * - * This function is always called from the sleep code from the last CPU - * that is powering down the entire system. Since no other RPMH API would be - * executing at this time, it is safe to run lockless. + * Return: + * * 0 - Success + * * -EAGAIN - Retry again + * * Error code - Otherwise */ int rpmh_flush(struct rpmh_ctrlr *ctrlr) { struct cache_req *p; int ret; + lockdep_assert_irqs_disabled(); + if (!ctrlr->dirty) { pr_debug("Skipping flush, TCS has latest data.\n"); return 0; } /* Invalidate the TCSes first to avoid stale data */ - do { - ret = rpmh_rsc_invalidate(ctrlr_to_drv(ctrlr)); - } while (ret == -EAGAIN); + ret = rpmh_rsc_invalidate(ctrlr_to_drv(ctrlr)); if (ret) return ret; @@ -466,10 +465,6 @@ int rpmh_flush(struct rpmh_ctrlr *ctrlr) if (ret) return ret; - /* - * Nobody else should be calling this function other than system PM, - * hence we can run without locks. - */ list_for_each_entry(p, &ctrlr->cache, list) { if (!is_req_valid(p)) { pr_debug("%s: skipping RPMH req: a:%#x s:%#x w:%#x", -- cgit From 15b3bf61b8d48f8e0ccd9d7f1bcb468b543da396 Mon Sep 17 00:00:00 2001 From: "Raju P.L.S.S.S.N" Date: Sun, 12 Apr 2020 20:20:03 +0530 Subject: soc: qcom: rpmh-rsc: Clear active mode configuration for wake TCS For RSCs that have sleep & wake TCS but no dedicated active TCS, wake TCS can be re-purposed to send active requests. Once the active requests are sent and response is received, the active mode configuration needs to be cleared so that controller can use wake TCS for sending wake requests. Introduce enable_tcs_irq() to enable completion IRQ for repurposed TCSes. Fixes: 2de4b8d33eab (drivers: qcom: rpmh-rsc: allow active requests from wake TCS) Signed-off-by: Raju P.L.S.S.S.N [mkshah: call enable_tcs_irq() within drv->lock, update commit message] Signed-off-by: Maulik Shah Reviewed-by: Douglas Anderson Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/1586703004-13674-6-git-send-email-mkshah@codeaurora.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 77 +++++++++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 23 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 892c82b7e3fb..80e8a9485b6b 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -207,6 +207,42 @@ static const struct tcs_request *get_req_from_tcs(struct rsc_drv *drv, return NULL; } +static void __tcs_set_trigger(struct rsc_drv *drv, int tcs_id, bool trigger) +{ + u32 enable; + + /* + * HW req: Clear the DRV_CONTROL and enable TCS again + * While clearing ensure that the AMC mode trigger is cleared + * and then the mode enable is cleared. + */ + enable = read_tcs_reg(drv, RSC_DRV_CONTROL, tcs_id, 0); + enable &= ~TCS_AMC_MODE_TRIGGER; + write_tcs_reg_sync(drv, RSC_DRV_CONTROL, tcs_id, enable); + enable &= ~TCS_AMC_MODE_ENABLE; + write_tcs_reg_sync(drv, RSC_DRV_CONTROL, tcs_id, enable); + + if (trigger) { + /* Enable the AMC mode on the TCS and then trigger the TCS */ + enable = TCS_AMC_MODE_ENABLE; + write_tcs_reg_sync(drv, RSC_DRV_CONTROL, tcs_id, enable); + enable |= TCS_AMC_MODE_TRIGGER; + write_tcs_reg_sync(drv, RSC_DRV_CONTROL, tcs_id, enable); + } +} + +static void enable_tcs_irq(struct rsc_drv *drv, int tcs_id, bool enable) +{ + u32 data; + + data = read_tcs_reg(drv, RSC_DRV_IRQ_ENABLE, 0, 0); + if (enable) + data |= BIT(tcs_id); + else + data &= ~BIT(tcs_id); + write_tcs_reg(drv, RSC_DRV_IRQ_ENABLE, 0, data); +} + /** * tcs_tx_done: TX Done interrupt handler */ @@ -243,6 +279,14 @@ static irqreturn_t tcs_tx_done(int irq, void *p) } trace_rpmh_tx_done(drv, i, req, err); + + /* + * If wake tcs was re-purposed for sending active + * votes, clear AMC trigger & enable modes and + * disable interrupt for this TCS + */ + if (!drv->tcs[ACTIVE_TCS].num_tcs) + __tcs_set_trigger(drv, i, false); skip: /* Reclaim the TCS */ write_tcs_reg(drv, RSC_DRV_CMD_ENABLE, i, 0); @@ -250,6 +294,13 @@ skip: write_tcs_reg(drv, RSC_DRV_IRQ_CLEAR, 0, BIT(i)); spin_lock(&drv->lock); clear_bit(i, drv->tcs_in_use); + /* + * Disable interrupt for WAKE TCS to avoid being + * spammed with interrupts coming when the solver + * sends its wake votes. + */ + if (!drv->tcs[ACTIVE_TCS].num_tcs) + enable_tcs_irq(drv, i, false); spin_unlock(&drv->lock); if (req) rpmh_tx_done(req, err); @@ -291,28 +342,6 @@ static void __tcs_buffer_write(struct rsc_drv *drv, int tcs_id, int cmd_id, write_tcs_reg(drv, RSC_DRV_CMD_ENABLE, tcs_id, cmd_enable); } -static void __tcs_trigger(struct rsc_drv *drv, int tcs_id) -{ - u32 enable; - - /* - * HW req: Clear the DRV_CONTROL and enable TCS again - * While clearing ensure that the AMC mode trigger is cleared - * and then the mode enable is cleared. - */ - enable = read_tcs_reg(drv, RSC_DRV_CONTROL, tcs_id, 0); - enable &= ~TCS_AMC_MODE_TRIGGER; - write_tcs_reg_sync(drv, RSC_DRV_CONTROL, tcs_id, enable); - enable &= ~TCS_AMC_MODE_ENABLE; - write_tcs_reg_sync(drv, RSC_DRV_CONTROL, tcs_id, enable); - - /* Enable the AMC mode on the TCS and then trigger the TCS */ - enable = TCS_AMC_MODE_ENABLE; - write_tcs_reg_sync(drv, RSC_DRV_CONTROL, tcs_id, enable); - enable |= TCS_AMC_MODE_TRIGGER; - write_tcs_reg_sync(drv, RSC_DRV_CONTROL, tcs_id, enable); -} - static int check_for_req_inflight(struct rsc_drv *drv, struct tcs_group *tcs, const struct tcs_request *msg) { @@ -383,10 +412,12 @@ static int tcs_write(struct rsc_drv *drv, const struct tcs_request *msg) tcs->req[tcs_id - tcs->offset] = msg; set_bit(tcs_id, drv->tcs_in_use); + if (msg->state == RPMH_ACTIVE_ONLY_STATE && tcs->type != ACTIVE_TCS) + enable_tcs_irq(drv, tcs_id, true); spin_unlock(&drv->lock); __tcs_buffer_write(drv, tcs_id, 0, msg); - __tcs_trigger(drv, tcs_id); + __tcs_set_trigger(drv, tcs_id, true); done_write: spin_unlock_irqrestore(&tcs->lock, flags); -- cgit From 38427e5a47bf83299da930bd474c6cb2632ad810 Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Sun, 12 Apr 2020 20:20:04 +0530 Subject: soc: qcom: rpmh-rsc: Allow using free WAKE TCS for active request When there are more than one WAKE TCS available and there is no dedicated ACTIVE TCS available, invalidating all WAKE TCSes and waiting for current transfer to complete in first WAKE TCS blocks using another free WAKE TCS to complete current request. Remove rpmh_rsc_invalidate() to happen from tcs_write() when WAKE TCSes is re-purposed to be used for Active mode. Clear only currently used WAKE TCS's register configuration. Fixes: 2de4b8d33eab (drivers: qcom: rpmh-rsc: allow active requests from wake TCS) Signed-off-by: Maulik Shah Reviewed-by: Douglas Anderson Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/1586703004-13674-7-git-send-email-mkshah@codeaurora.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 80e8a9485b6b..cc1293cb15a5 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -154,7 +154,7 @@ int rpmh_rsc_invalidate(struct rsc_drv *drv) static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv, const struct tcs_request *msg) { - int type, ret; + int type; struct tcs_group *tcs; switch (msg->state) { @@ -175,19 +175,10 @@ static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv, * If we are making an active request on a RSC that does not have a * dedicated TCS for active state use, then re-purpose a wake TCS to * send active votes. - * NOTE: The driver must be aware that this RSC does not have a - * dedicated AMC, and therefore would invalidate the sleep and wake - * TCSes before making an active state request. */ tcs = get_tcs_of_type(drv, type); - if (msg->state == RPMH_ACTIVE_ONLY_STATE && !tcs->num_tcs) { + if (msg->state == RPMH_ACTIVE_ONLY_STATE && !tcs->num_tcs) tcs = get_tcs_of_type(drv, WAKE_TCS); - if (tcs->num_tcs) { - ret = rpmh_rsc_invalidate(drv); - if (ret) - return ERR_PTR(ret); - } - } return tcs; } @@ -412,8 +403,16 @@ static int tcs_write(struct rsc_drv *drv, const struct tcs_request *msg) tcs->req[tcs_id - tcs->offset] = msg; set_bit(tcs_id, drv->tcs_in_use); - if (msg->state == RPMH_ACTIVE_ONLY_STATE && tcs->type != ACTIVE_TCS) + if (msg->state == RPMH_ACTIVE_ONLY_STATE && tcs->type != ACTIVE_TCS) { + /* + * Clear previously programmed WAKE commands in selected + * repurposed TCS to avoid triggering them. tcs->slots will be + * cleaned from rpmh_flush() by invoking rpmh_rsc_invalidate() + */ + write_tcs_reg_sync(drv, RSC_DRV_CMD_ENABLE, tcs_id, 0); + write_tcs_reg_sync(drv, RSC_DRV_CMD_WAIT_FOR_CMPL, tcs_id, 0); enable_tcs_irq(drv, tcs_id, true); + } spin_unlock(&drv->lock); __tcs_buffer_write(drv, tcs_id, 0, msg); -- cgit From 3b5e3d50f83a3706126ade45ba8d44ca829b0803 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 13 Apr 2020 10:04:06 -0700 Subject: soc: qcom: rpmh-rsc: Clean code reading/writing TCS regs/cmds This patch makes two changes, both of which should be no-ops: 1. Make read_tcs_reg() / read_tcs_cmd() symmetric to write_tcs_reg() / write_tcs_cmd(). 2. Change the order of operations in the above functions to make it more obvious to me what the math is doing. Specifically first you want to find the right TCS, then the right register, and then multiply by the command ID if necessary. Signed-off-by: Douglas Anderson Reviewed-by: Maulik Shah Reviewed-by: Stephen Boyd Tested-by: Maulik Shah Link: https://lore.kernel.org/r/20200413100321.v4.1.I1b754137e8089e46cf33fc2ea270734ec3847ec4@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index cc1293cb15a5..91fb5a6d68a2 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -67,28 +67,33 @@ #define CMD_STATUS_ISSUED BIT(8) #define CMD_STATUS_COMPL BIT(16) -static u32 read_tcs_reg(struct rsc_drv *drv, int reg, int tcs_id, int cmd_id) +static u32 read_tcs_cmd(struct rsc_drv *drv, int reg, int tcs_id, int cmd_id) { - return readl_relaxed(drv->tcs_base + reg + RSC_DRV_TCS_OFFSET * tcs_id + + return readl_relaxed(drv->tcs_base + RSC_DRV_TCS_OFFSET * tcs_id + reg + RSC_DRV_CMD_OFFSET * cmd_id); } +static u32 read_tcs_reg(struct rsc_drv *drv, int reg, int tcs_id) +{ + return readl_relaxed(drv->tcs_base + RSC_DRV_TCS_OFFSET * tcs_id + reg); +} + static void write_tcs_cmd(struct rsc_drv *drv, int reg, int tcs_id, int cmd_id, u32 data) { - writel_relaxed(data, drv->tcs_base + reg + RSC_DRV_TCS_OFFSET * tcs_id + + writel_relaxed(data, drv->tcs_base + RSC_DRV_TCS_OFFSET * tcs_id + reg + RSC_DRV_CMD_OFFSET * cmd_id); } static void write_tcs_reg(struct rsc_drv *drv, int reg, int tcs_id, u32 data) { - writel_relaxed(data, drv->tcs_base + reg + RSC_DRV_TCS_OFFSET * tcs_id); + writel_relaxed(data, drv->tcs_base + RSC_DRV_TCS_OFFSET * tcs_id + reg); } static void write_tcs_reg_sync(struct rsc_drv *drv, int reg, int tcs_id, u32 data) { - writel(data, drv->tcs_base + reg + RSC_DRV_TCS_OFFSET * tcs_id); + writel(data, drv->tcs_base + RSC_DRV_TCS_OFFSET * tcs_id + reg); for (;;) { if (data == readl(drv->tcs_base + reg + RSC_DRV_TCS_OFFSET * tcs_id)) @@ -100,7 +105,7 @@ static void write_tcs_reg_sync(struct rsc_drv *drv, int reg, int tcs_id, static bool tcs_is_free(struct rsc_drv *drv, int tcs_id) { return !test_bit(tcs_id, drv->tcs_in_use) && - read_tcs_reg(drv, RSC_DRV_STATUS, tcs_id, 0); + read_tcs_reg(drv, RSC_DRV_STATUS, tcs_id); } static struct tcs_group *get_tcs_of_type(struct rsc_drv *drv, int type) @@ -207,7 +212,7 @@ static void __tcs_set_trigger(struct rsc_drv *drv, int tcs_id, bool trigger) * While clearing ensure that the AMC mode trigger is cleared * and then the mode enable is cleared. */ - enable = read_tcs_reg(drv, RSC_DRV_CONTROL, tcs_id, 0); + enable = read_tcs_reg(drv, RSC_DRV_CONTROL, tcs_id); enable &= ~TCS_AMC_MODE_TRIGGER; write_tcs_reg_sync(drv, RSC_DRV_CONTROL, tcs_id, enable); enable &= ~TCS_AMC_MODE_ENABLE; @@ -226,7 +231,7 @@ static void enable_tcs_irq(struct rsc_drv *drv, int tcs_id, bool enable) { u32 data; - data = read_tcs_reg(drv, RSC_DRV_IRQ_ENABLE, 0, 0); + data = read_tcs_reg(drv, RSC_DRV_IRQ_ENABLE, 0); if (enable) data |= BIT(tcs_id); else @@ -245,7 +250,7 @@ static irqreturn_t tcs_tx_done(int irq, void *p) const struct tcs_request *req; struct tcs_cmd *cmd; - irq_status = read_tcs_reg(drv, RSC_DRV_IRQ_STATUS, 0, 0); + irq_status = read_tcs_reg(drv, RSC_DRV_IRQ_STATUS, 0); for_each_set_bit(i, &irq_status, BITS_PER_LONG) { req = get_req_from_tcs(drv, i); @@ -259,7 +264,7 @@ static irqreturn_t tcs_tx_done(int irq, void *p) u32 sts; cmd = &req->cmds[j]; - sts = read_tcs_reg(drv, RSC_DRV_CMD_STATUS, i, j); + sts = read_tcs_cmd(drv, RSC_DRV_CMD_STATUS, i, j); if (!(sts & CMD_STATUS_ISSUED) || ((req->wait_for_compl || cmd->wait) && !(sts & CMD_STATUS_COMPL))) { @@ -313,7 +318,7 @@ static void __tcs_buffer_write(struct rsc_drv *drv, int tcs_id, int cmd_id, cmd_msgid |= msg->wait_for_compl ? CMD_MSGID_RESP_REQ : 0; cmd_msgid |= CMD_MSGID_WRITE; - cmd_complete = read_tcs_reg(drv, RSC_DRV_CMD_WAIT_FOR_CMPL, tcs_id, 0); + cmd_complete = read_tcs_reg(drv, RSC_DRV_CMD_WAIT_FOR_CMPL, tcs_id); for (i = 0, j = cmd_id; i < msg->num_cmds; i++, j++) { cmd = &msg->cmds[i]; @@ -329,7 +334,7 @@ static void __tcs_buffer_write(struct rsc_drv *drv, int tcs_id, int cmd_id, } write_tcs_reg(drv, RSC_DRV_CMD_WAIT_FOR_CMPL, tcs_id, cmd_complete); - cmd_enable |= read_tcs_reg(drv, RSC_DRV_CMD_ENABLE, tcs_id, 0); + cmd_enable |= read_tcs_reg(drv, RSC_DRV_CMD_ENABLE, tcs_id); write_tcs_reg(drv, RSC_DRV_CMD_ENABLE, tcs_id, cmd_enable); } @@ -345,10 +350,10 @@ static int check_for_req_inflight(struct rsc_drv *drv, struct tcs_group *tcs, if (tcs_is_free(drv, tcs_id)) continue; - curr_enabled = read_tcs_reg(drv, RSC_DRV_CMD_ENABLE, tcs_id, 0); + curr_enabled = read_tcs_reg(drv, RSC_DRV_CMD_ENABLE, tcs_id); for_each_set_bit(j, &curr_enabled, MAX_CMDS_PER_TCS) { - addr = read_tcs_reg(drv, RSC_DRV_CMD_ADDR, tcs_id, j); + addr = read_tcs_cmd(drv, RSC_DRV_CMD_ADDR, tcs_id, j); for (k = 0; k < msg->num_cmds; k++) { if (addr == msg->cmds[k].addr) return -EBUSY; -- cgit From 1f7dbeb51a4f555db4105dc7927be6c77f0b60fd Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 13 Apr 2020 10:04:07 -0700 Subject: soc: qcom: rpmh-rsc: Document the register layout better Perhaps it's just me, it took a really long time to understand what the register layout of rpmh-rsc was just from the #defines. Let's add a bunch of comments describing which blocks are part of other blocks. Signed-off-by: Douglas Anderson Reviewed-by: Maulik Shah Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20200413100321.v4.2.Iaddc29b72772e6ea381238a0ee85b82d3903e5f2@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 79 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 5 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 91fb5a6d68a2..439a0eadabf1 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -43,14 +43,29 @@ #define DRV_NCPT_MASK 0x1F #define DRV_NCPT_SHIFT 27 -/* Register offsets */ +/* Offsets for common TCS Registers, one bit per TCS */ #define RSC_DRV_IRQ_ENABLE 0x00 #define RSC_DRV_IRQ_STATUS 0x04 -#define RSC_DRV_IRQ_CLEAR 0x08 -#define RSC_DRV_CMD_WAIT_FOR_CMPL 0x10 +#define RSC_DRV_IRQ_CLEAR 0x08 /* w/o; write 1 to clear */ + +/* + * Offsets for per TCS Registers. + * + * TCSes start at 0x10 from tcs_base and are stored one after another. + * Multiply tcs_id by RSC_DRV_TCS_OFFSET to find a given TCS and add one + * of the below to find a register. + */ +#define RSC_DRV_CMD_WAIT_FOR_CMPL 0x10 /* 1 bit per command */ #define RSC_DRV_CONTROL 0x14 -#define RSC_DRV_STATUS 0x18 -#define RSC_DRV_CMD_ENABLE 0x1C +#define RSC_DRV_STATUS 0x18 /* zero if tcs is busy */ +#define RSC_DRV_CMD_ENABLE 0x1C /* 1 bit per command */ + +/* + * Offsets for per command in a TCS. + * + * Commands (up to 16) start at 0x30 in a TCS; multiply command index + * by RSC_DRV_CMD_OFFSET and add one of the below to find a register. + */ #define RSC_DRV_CMD_MSGID 0x30 #define RSC_DRV_CMD_ADDR 0x34 #define RSC_DRV_CMD_DATA 0x38 @@ -67,6 +82,60 @@ #define CMD_STATUS_ISSUED BIT(8) #define CMD_STATUS_COMPL BIT(16) +/* + * Here's a high level overview of how all the registers in RPMH work + * together: + * + * - The main rpmh-rsc address is the base of a register space that can + * be used to find overall configuration of the hardware + * (DRV_PRNT_CHLD_CONFIG). Also found within the rpmh-rsc register + * space are all the TCS blocks. The offset of the TCS blocks is + * specified in the device tree by "qcom,tcs-offset" and used to + * compute tcs_base. + * - TCS blocks come one after another. Type, count, and order are + * specified by the device tree as "qcom,tcs-config". + * - Each TCS block has some registers, then space for up to 16 commands. + * Note that though address space is reserved for 16 commands, fewer + * might be present. See ncpt (num cmds per TCS). + * + * Here's a picture: + * + * +---------------------------------------------------+ + * |RSC | + * | ctrl | + * | | + * | Drvs: | + * | +-----------------------------------------------+ | + * | |DRV0 | | + * | | ctrl/config | | + * | | IRQ | | + * | | | | + * | | TCSes: | | + * | | +------------------------------------------+ | | + * | | |TCS0 | | | | | | | | | | | | | | | + * | | | ctrl | 0| 1| 2| 3| 4| 5| .| .| .| .|14|15| | | + * | | | | | | | | | | | | | | | | | | + * | | +------------------------------------------+ | | + * | | +------------------------------------------+ | | + * | | |TCS1 | | | | | | | | | | | | | | | + * | | | ctrl | 0| 1| 2| 3| 4| 5| .| .| .| .|14|15| | | + * | | | | | | | | | | | | | | | | | | + * | | +------------------------------------------+ | | + * | | +------------------------------------------+ | | + * | | |TCS2 | | | | | | | | | | | | | | | + * | | | ctrl | 0| 1| 2| 3| 4| 5| .| .| .| .|14|15| | | + * | | | | | | | | | | | | | | | | | | + * | | +------------------------------------------+ | | + * | | ...... | | + * | +-----------------------------------------------+ | + * | +-----------------------------------------------+ | + * | |DRV1 | | + * | | (same as DRV0) | | + * | +-----------------------------------------------+ | + * | ...... | + * +---------------------------------------------------+ + */ + static u32 read_tcs_cmd(struct rsc_drv *drv, int reg, int tcs_id, int cmd_id) { return readl_relaxed(drv->tcs_base + RSC_DRV_TCS_OFFSET * tcs_id + reg + -- cgit From 427ef4f72bba5c8d3fb7dce758c3afe99d9db9c6 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 13 Apr 2020 10:04:08 -0700 Subject: soc: qcom: rpmh-rsc: Fold tcs_ctrl_write() into its single caller I was trying to write documentation for the functions in rpmh-rsc and I got to tcs_ctrl_write(). The documentation for the function would have been: "This is the core of rpmh_rsc_write_ctrl_data(); all the caller does is error-check and then call this". Having the error checks in a separate function doesn't help for anything since: - There are no other callers that need to bypass the error checks. - It's less documenting. When I read tcs_ctrl_write() I kept wondering if I need to handle cases other than ACTIVE_ONLY or cases with more commands than could fit in a TCS. This is obvious when the error checks and code are together. - The function just isn't that long, so there's no problem understanding the combined function. Things were even more confusing because the two functions names didn't make obvious (at least to me) their relationship. Simplify by folding one function into the other. Signed-off-by: Douglas Anderson Reviewed-by: Maulik Shah Reviewed-by: Stephen Boyd Tested-by: Maulik Shah Link: https://lore.kernel.org/r/20200413100321.v4.3.Ie88ce5ccfc0c6055903ccca5286ae28ed3b85ed3@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 439a0eadabf1..d9177324c6a2 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -587,27 +587,6 @@ copy_data: return 0; } -static int tcs_ctrl_write(struct rsc_drv *drv, const struct tcs_request *msg) -{ - struct tcs_group *tcs; - int tcs_id = 0, cmd_id = 0; - unsigned long flags; - int ret; - - tcs = get_tcs_for_msg(drv, msg); - if (IS_ERR(tcs)) - return PTR_ERR(tcs); - - spin_lock_irqsave(&tcs->lock, flags); - /* find the TCS id and the command in the TCS to write to */ - ret = find_slots(tcs, msg, &tcs_id, &cmd_id); - if (!ret) - __tcs_buffer_write(drv, tcs_id, cmd_id, msg); - spin_unlock_irqrestore(&tcs->lock, flags); - - return ret; -} - /** * rpmh_rsc_write_ctrl_data: Write request to the controller * @@ -618,6 +597,11 @@ static int tcs_ctrl_write(struct rsc_drv *drv, const struct tcs_request *msg) */ int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg) { + struct tcs_group *tcs; + int tcs_id = 0, cmd_id = 0; + unsigned long flags; + int ret; + if (!msg || !msg->cmds || !msg->num_cmds || msg->num_cmds > MAX_RPMH_PAYLOAD) { pr_err("Payload error\n"); @@ -628,7 +612,18 @@ int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg) if (msg->state == RPMH_ACTIVE_ONLY_STATE) return -EINVAL; - return tcs_ctrl_write(drv, msg); + tcs = get_tcs_for_msg(drv, msg); + if (IS_ERR(tcs)) + return PTR_ERR(tcs); + + spin_lock_irqsave(&tcs->lock, flags); + /* find the TCS id and the command in the TCS to write to */ + ret = find_slots(tcs, msg, &tcs_id, &cmd_id); + if (!ret) + __tcs_buffer_write(drv, tcs_id, cmd_id, msg); + spin_unlock_irqrestore(&tcs->lock, flags); + + return ret; } /** -- cgit From 53d49fe1ff49196712c416ddb987634483423934 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 13 Apr 2020 10:04:09 -0700 Subject: soc: qcom: rpmh-rsc: Remove get_tcs_of_type() abstraction The get_tcs_of_type() function doesn't provide any value. It's not conceptually difficult to access a value in an array, even if that value is in a structure and we want a pointer to the value. Having the function in there makes me feel like it's doing something fancier like looping or searching. Remove it. Signed-off-by: Douglas Anderson Reviewed-by: Maulik Shah Reviewed-by: Stephen Boyd Tested-by: Maulik Shah Link: https://lore.kernel.org/r/20200413100321.v4.4.Ia348ade7c6ed1d0d952ff2245bc854e5834c8d9a@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index d9177324c6a2..d0c187c17ce1 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -177,17 +177,10 @@ static bool tcs_is_free(struct rsc_drv *drv, int tcs_id) read_tcs_reg(drv, RSC_DRV_STATUS, tcs_id); } -static struct tcs_group *get_tcs_of_type(struct rsc_drv *drv, int type) -{ - return &drv->tcs[type]; -} - static int tcs_invalidate(struct rsc_drv *drv, int type) { int m; - struct tcs_group *tcs; - - tcs = get_tcs_of_type(drv, type); + struct tcs_group *tcs = &drv->tcs[type]; spin_lock(&tcs->lock); if (bitmap_empty(tcs->slots, MAX_TCS_SLOTS)) { @@ -250,9 +243,9 @@ static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv, * dedicated TCS for active state use, then re-purpose a wake TCS to * send active votes. */ - tcs = get_tcs_of_type(drv, type); + tcs = &drv->tcs[type]; if (msg->state == RPMH_ACTIVE_ONLY_STATE && !tcs->num_tcs) - tcs = get_tcs_of_type(drv, WAKE_TCS); + tcs = &drv->tcs[WAKE_TCS]; return tcs; } @@ -643,7 +636,7 @@ int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg) static bool rpmh_rsc_ctrlr_is_busy(struct rsc_drv *drv) { int m; - struct tcs_group *tcs = get_tcs_of_type(drv, ACTIVE_TCS); + struct tcs_group *tcs = &drv->tcs[ACTIVE_TCS]; /* * If we made an active request on a RSC that does not have a @@ -655,7 +648,7 @@ static bool rpmh_rsc_ctrlr_is_busy(struct rsc_drv *drv) * lock before checking tcs_is_free(). */ if (!tcs->num_tcs) - tcs = get_tcs_of_type(drv, WAKE_TCS); + tcs = &drv->tcs[WAKE_TCS]; for (m = tcs->offset; m < tcs->offset + tcs->num_tcs; m++) { if (!tcs_is_free(drv, m)) -- cgit From 1bc92a933f19e2415353a4892f7df4617f691b6c Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 13 Apr 2020 10:04:10 -0700 Subject: soc: qcom: rpmh-rsc: Kill cmd_cache and find_match() with fire The "cmd_cache" in RPMH wasn't terribly sensible. Specifically: - The current code doesn't really detect "conflicts" properly any case where the sequence being checked has more than one entry. One simple way to see this in the current code is that if cmd[0].addr isn't found then cmd[1].addr is never checked. - The code attempted to use the "cmd_cache" to update an existing message in a sleep/wake TCS with new data. The goal appeared to be to update part of a TCS while leaving the rest of the TCS alone. We never actually do this. We always fully invalidate and re-write everything. - If/when we try to optimize things to not fully invalidate / re-write every time we update the TCSes we'll need to think it through very carefully. Specifically requirement of find_match() that the new sequence of addrs must match exactly the old sequence of addrs seems inflexible. It's also not documented in rpmh_write() and rpmh_write_batch(). In any case, if we do decide to require updates to keep the exact same sequence and length then presumably the API and data structures should be updated to understand groups more properly. The current algorithm doesn't really keep track of the length of the old sequence and there are several boundary-condition bugs because of that. Said another way: if we decide to do something like this in the future we should start from scratch and thus find_match() isn't useful to keep around. This patch isn't quite a no-op. Specifically: - It should be a slight performance boost of not searching through so many arrays. - The old code would have done something useful in one case: it would allow someone calling rpmh_write() to override the data that came from rpmh_write_batch(). I don't believe that actually happens in reality. Signed-off-by: Douglas Anderson Reviewed-by: Maulik Shah Reviewed-by: Stephen Boyd Tested-by: Maulik Shah Link: https://lore.kernel.org/r/20200413100321.v4.5.I6d3d0a3ec810dc72ff1df3cbf97deefdcdeb8eef@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-internal.h | 2 -- drivers/soc/qcom/rpmh-rsc.c | 47 ---------------------------------------- 2 files changed, 49 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h index e9a90cb7773e..6a6d776ccca9 100644 --- a/drivers/soc/qcom/rpmh-internal.h +++ b/drivers/soc/qcom/rpmh-internal.h @@ -30,7 +30,6 @@ struct rsc_drv; * @ncpt: number of commands in each TCS * @lock: lock for synchronizing this TCS writes * @req: requests that are sent from the TCS - * @cmd_cache: flattened cache of cmds in sleep/wake TCS * @slots: indicates which of @cmd_addr are occupied */ struct tcs_group { @@ -42,7 +41,6 @@ struct tcs_group { int ncpt; spinlock_t lock; const struct tcs_request *req[MAX_TCS_PER_TYPE]; - u32 *cmd_cache; DECLARE_BITMAP(slots, MAX_TCS_SLOTS); }; diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index d0c187c17ce1..c9e5cddbc099 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -522,42 +522,12 @@ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) return ret; } -static int find_match(const struct tcs_group *tcs, const struct tcs_cmd *cmd, - int len) -{ - int i, j; - - /* Check for already cached commands */ - for_each_set_bit(i, tcs->slots, MAX_TCS_SLOTS) { - if (tcs->cmd_cache[i] != cmd[0].addr) - continue; - if (i + len >= tcs->num_tcs * tcs->ncpt) - goto seq_err; - for (j = 0; j < len; j++) { - if (tcs->cmd_cache[i + j] != cmd[j].addr) - goto seq_err; - } - return i; - } - - return -ENODATA; - -seq_err: - WARN(1, "Message does not match previous sequence.\n"); - return -EINVAL; -} - static int find_slots(struct tcs_group *tcs, const struct tcs_request *msg, int *tcs_id, int *cmd_id) { int slot, offset; int i = 0; - /* Find if we already have the msg in our TCS */ - slot = find_match(tcs, msg->cmds, msg->num_cmds); - if (slot >= 0) - goto copy_data; - /* Do over, until we can fit the full payload in a TCS */ do { slot = bitmap_find_next_zero_area(tcs->slots, MAX_TCS_SLOTS, @@ -567,11 +537,7 @@ static int find_slots(struct tcs_group *tcs, const struct tcs_request *msg, i += tcs->ncpt; } while (slot + msg->num_cmds - 1 >= i); -copy_data: bitmap_set(tcs->slots, slot, msg->num_cmds); - /* Copy the addresses of the resources over to the slots */ - for (i = 0; i < msg->num_cmds; i++) - tcs->cmd_cache[slot + i] = msg->cmds[i].addr; offset = slot / tcs->ncpt; *tcs_id = offset + tcs->offset; @@ -762,19 +728,6 @@ static int rpmh_probe_tcs_config(struct platform_device *pdev, tcs->mask = ((1 << tcs->num_tcs) - 1) << st; tcs->offset = st; st += tcs->num_tcs; - - /* - * Allocate memory to cache sleep and wake requests to - * avoid reading TCS register memory. - */ - if (tcs->type == ACTIVE_TCS) - continue; - - tcs->cmd_cache = devm_kcalloc(&pdev->dev, - tcs->num_tcs * ncpt, sizeof(u32), - GFP_KERNEL); - if (!tcs->cmd_cache) - return -ENOMEM; } drv->num_tcs = st; -- cgit From e40b0c1628f27986dd90f94c43464df5aa8968cf Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 13 Apr 2020 10:04:11 -0700 Subject: soc: qcom: rpmh-rsc: A lot of comments I've been pouring through the rpmh-rsc code and trying to understand it. Document everything to the best of my ability. All documentation here is strictly from code analysis--no actual knowledge of the hardware was used. If something is wrong in here I either misunderstood the code, had a typo, or the code has a bug in it leading to my incorrect understanding. In a few places here I have documented things that don't make tons of sense. A future patch will try to address this. While this means I'm adding comments / todos and then later fixing them in the series, it seemed more urgent to get things documented first so that people could understand the later patches. Any comments I adjusted I also tried to make match kernel-doc better. Specifically: - kernel-doc says do not leave a blank line between the function description and the arguments - kernel-doc examples always have things starting w/ a capital and ending with a period. This should be a no-op. It's just comment changes. Signed-off-by: Douglas Anderson Reviewed-by: Maulik Shah Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20200413100321.v4.6.I52653eb85d7dc8981ee0dafcd0b6cc0f273e9425@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-internal.h | 62 +++++++---- drivers/soc/qcom/rpmh-rsc.c | 222 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 247 insertions(+), 37 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h index 6a6d776ccca9..f06350cbc9a2 100644 --- a/drivers/soc/qcom/rpmh-internal.h +++ b/drivers/soc/qcom/rpmh-internal.h @@ -22,15 +22,24 @@ struct rsc_drv; * struct tcs_group: group of Trigger Command Sets (TCS) to send state requests * to the controller * - * @drv: the controller - * @type: type of the TCS in this group - active, sleep, wake - * @mask: mask of the TCSes relative to all the TCSes in the RSC - * @offset: start of the TCS group relative to the TCSes in the RSC - * @num_tcs: number of TCSes in this type - * @ncpt: number of commands in each TCS - * @lock: lock for synchronizing this TCS writes - * @req: requests that are sent from the TCS - * @slots: indicates which of @cmd_addr are occupied + * @drv: The controller. + * @type: Type of the TCS in this group - active, sleep, wake. + * @mask: Mask of the TCSes relative to all the TCSes in the RSC. + * @offset: Start of the TCS group relative to the TCSes in the RSC. + * @num_tcs: Number of TCSes in this type. + * @ncpt: Number of commands in each TCS. + * @lock: Lock for synchronizing this TCS writes. + * @req: Requests that are sent from the TCS; only used for ACTIVE_ONLY + * transfers (could be on a wake/sleep TCS if we are borrowing for + * an ACTIVE_ONLY transfer). + * Start: grab drv->lock, set req, set tcs_in_use, drop drv->lock, + * trigger + * End: get irq, access req, + * grab drv->lock, clear tcs_in_use, drop drv->lock + * @slots: Indicates which of @cmd_addr are occupied; only used for + * SLEEP / WAKE TCSs. Things are tightly packed in the + * case that (ncpt < MAX_CMDS_PER_TCS). That is if ncpt = 2 and + * MAX_CMDS_PER_TCS = 16 then bit[2] = the first bit in 2nd TCS. */ struct tcs_group { struct rsc_drv *drv; @@ -82,19 +91,28 @@ struct rpmh_ctrlr { * struct rsc_drv: the Direct Resource Voter (DRV) of the * Resource State Coordinator controller (RSC) * - * @name: Controller identifier - * @tcs_base: Start address of the TCS registers in this controller - * @id: Instance id in the controller (Direct Resource Voter) - * @num_tcs: Number of TCSes in this DRV - * @rsc_pm: CPU PM notifier for controller - * Used when solver mode is not present - * @cpus_entered_pm: CPU mask for cpus in idle power collapse - * Used when solver mode is not present - * @tcs: TCS groups - * @tcs_in_use: S/W state of the TCS - * @lock: Synchronize state of the controller - * @pm_lock: Synchronize during PM notifications - * Used when solver mode is not present + * @name: Controller identifier. + * @tcs_base: Start address of the TCS registers in this controller. + * @id: Instance id in the controller (Direct Resource Voter). + * @num_tcs: Number of TCSes in this DRV. + * @rsc_pm: CPU PM notifier for controller. + * Used when solver mode is not present. + * @cpus_entered_pm: CPU mask for cpus in idle power collapse. + * Used when solver mode is not present. + * @tcs: TCS groups. + * @tcs_in_use: S/W state of the TCS; only set for ACTIVE_ONLY + * transfers, but might show a sleep/wake TCS in use if + * it was borrowed for an active_only transfer. You + * must hold both the lock in this struct and the + * tcs_lock for the TCS in order to mark a TCS as + * in-use, but you only need the lock in this structure + * (aka the drv->lock) to mark one freed. + * @lock: Synchronize state of the controller. If you will be + * grabbing this lock and a tcs_lock at the same time, + * grab the tcs_lock first so we always have a + * consistent lock ordering. + * @pm_lock: Synchronize during PM notifications. + * Used when solver mode is not present. * @client: Handle to the DRV's client. */ struct rsc_drv { diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index c9e5cddbc099..78fe9344ecd3 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -171,12 +171,39 @@ static void write_tcs_reg_sync(struct rsc_drv *drv, int reg, int tcs_id, } } +/** + * tcs_is_free() - Return if a TCS is totally free. + * @drv: The RSC controller. + * @tcs_id: The global ID of this TCS. + * + * Returns true if nobody has claimed this TCS (by setting tcs_in_use). + * If the TCS looks free, checks that the hardware agrees. + * + * Context: Must be called with the drv->lock held or the tcs_lock for the TCS + * being tested. If only the tcs_lock is held then it is possible that + * this function will return that a tcs is still busy when it has been + * recently been freed but it will never return free when a TCS is + * actually in use. + * + * Return: true if the given TCS is free. + */ static bool tcs_is_free(struct rsc_drv *drv, int tcs_id) { return !test_bit(tcs_id, drv->tcs_in_use) && read_tcs_reg(drv, RSC_DRV_STATUS, tcs_id); } +/** + * tcs_invalidate() - Invalidate all TCSes of the given type (sleep or wake). + * @drv: The RSC controller. + * @type: SLEEP_TCS or WAKE_TCS + * + * This will clear the "slots" variable of the given tcs_group and also + * tell the hardware to forget about all entries. + * + * Return: 0 if no problem, or -EAGAIN if the caller should try again in a + * bit. Caller should make sure to enable interrupts between tries. + */ static int tcs_invalidate(struct rsc_drv *drv, int type) { int m; @@ -203,9 +230,11 @@ static int tcs_invalidate(struct rsc_drv *drv, int type) } /** - * rpmh_rsc_invalidate - Invalidate sleep and wake TCSes + * rpmh_rsc_invalidate() - Invalidate sleep and wake TCSes. + * @drv: The RSC controller. * - * @drv: the RSC controller + * Return: 0 if no problem, or -EAGAIN if the caller should try again in a + * bit. Caller should make sure to enable interrupts between tries. */ int rpmh_rsc_invalidate(struct rsc_drv *drv) { @@ -218,6 +247,18 @@ int rpmh_rsc_invalidate(struct rsc_drv *drv) return ret; } +/** + * get_tcs_for_msg() - Get the tcs_group used to send the given message. + * @drv: The RSC controller. + * @msg: The message we want to send. + * + * This is normally pretty straightforward except if we are trying to send + * an ACTIVE_ONLY message but don't have any active_only TCSes. + * + * Called without drv->lock held and with no tcs_lock locks held. + * + * Return: A pointer to a tcs_group or an ERR_PTR. + */ static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv, const struct tcs_request *msg) { @@ -241,7 +282,9 @@ static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv, /* * If we are making an active request on a RSC that does not have a * dedicated TCS for active state use, then re-purpose a wake TCS to - * send active votes. + * send active votes. This is safe because we ensure any active-only + * transfers have finished before we use it (maybe by running from + * the last CPU in PM code). */ tcs = &drv->tcs[type]; if (msg->state == RPMH_ACTIVE_ONLY_STATE && !tcs->num_tcs) @@ -250,6 +293,22 @@ static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv, return tcs; } +/** + * get_req_from_tcs() - Get a stashed request that was xfering on the given TCS. + * @drv: The RSC controller. + * @tcs_id: The global ID of this TCS. + * + * For ACTIVE_ONLY transfers we want to call back into the client when the + * transfer finishes. To do this we need the "request" that the client + * originally provided us. This function grabs the request that we stashed + * when we started the transfer. + * + * This only makes sense for ACTIVE_ONLY transfers since those are the only + * ones we track sending (the only ones we enable interrupts for and the only + * ones we call back to the client for). + * + * Return: The stashed request. + */ static const struct tcs_request *get_req_from_tcs(struct rsc_drv *drv, int tcs_id) { @@ -265,6 +324,23 @@ static const struct tcs_request *get_req_from_tcs(struct rsc_drv *drv, return NULL; } +/** + * __tcs_set_trigger() - Start xfer on a TCS or unset trigger on a borrowed TCS + * @drv: The controller. + * @tcs_id: The global ID of this TCS. + * @trigger: If true then untrigger/retrigger. If false then just untrigger. + * + * In the normal case we only ever call with "trigger=true" to start a + * transfer. That will un-trigger/disable the TCS from the last transfer + * then trigger/enable for this transfer. + * + * If we borrowed a wake TCS for an active-only transfer we'll also call + * this function with "trigger=false" to just do the un-trigger/disable + * before using the TCS for wake purposes again. + * + * Note that the AP is only in charge of triggering active-only transfers. + * The AP never triggers sleep/wake values using this function. + */ static void __tcs_set_trigger(struct rsc_drv *drv, int tcs_id, bool trigger) { u32 enable; @@ -289,6 +365,15 @@ static void __tcs_set_trigger(struct rsc_drv *drv, int tcs_id, bool trigger) } } +/** + * enable_tcs_irq() - Enable or disable interrupts on the given TCS. + * @drv: The controller. + * @tcs_id: The global ID of this TCS. + * @enable: If true then enable; if false then disable + * + * We only ever call this when we borrow a wake TCS for an active-only + * transfer. For active-only TCSes interrupts are always left enabled. + */ static void enable_tcs_irq(struct rsc_drv *drv, int tcs_id, bool enable) { u32 data; @@ -302,7 +387,14 @@ static void enable_tcs_irq(struct rsc_drv *drv, int tcs_id, bool enable) } /** - * tcs_tx_done: TX Done interrupt handler + * tcs_tx_done() - TX Done interrupt handler. + * @irq: The IRQ number (ignored). + * @p: Pointer to "struct rsc_drv". + * + * Called for ACTIVE_ONLY transfers (those are the only ones we enable the + * IRQ for) when a transfer is done. + * + * Return: IRQ_HANDLED */ static irqreturn_t tcs_tx_done(int irq, void *p) { @@ -367,6 +459,16 @@ skip: return IRQ_HANDLED; } +/** + * __tcs_buffer_write() - Write to TCS hardware from a request; don't trigger. + * @drv: The controller. + * @tcs_id: The global ID of this TCS. + * @cmd_id: The index within the TCS to start writing. + * @msg: The message we want to send, which will contain several addr/data + * pairs to program (but few enough that they all fit in one TCS). + * + * This is used for all types of transfers (active, sleep, and wake). + */ static void __tcs_buffer_write(struct rsc_drv *drv, int tcs_id, int cmd_id, const struct tcs_request *msg) { @@ -400,6 +502,26 @@ static void __tcs_buffer_write(struct rsc_drv *drv, int tcs_id, int cmd_id, write_tcs_reg(drv, RSC_DRV_CMD_ENABLE, tcs_id, cmd_enable); } +/** + * check_for_req_inflight() - Look to see if conflicting cmds are in flight. + * @drv: The controller. + * @tcs: A pointer to the tcs_group used for ACTIVE_ONLY transfers. + * @msg: The message we want to send, which will contain several addr/data + * pairs to program (but few enough that they all fit in one TCS). + * + * This will walk through the TCSes in the group and check if any of them + * appear to be sending to addresses referenced in the message. If it finds + * one it'll return -EBUSY. + * + * Only for use for active-only transfers. + * + * Must be called with the drv->lock held since that protects tcs_in_use. + * + * Return: 0 if nothing in flight or -EBUSY if we should try again later. + * The caller must re-enable interrupts between tries since that's + * the only way tcs_is_free() will ever return true and the only way + * RSC_DRV_CMD_ENABLE will ever be cleared. + */ static int check_for_req_inflight(struct rsc_drv *drv, struct tcs_group *tcs, const struct tcs_request *msg) { @@ -426,6 +548,15 @@ static int check_for_req_inflight(struct rsc_drv *drv, struct tcs_group *tcs, return 0; } +/** + * find_free_tcs() - Find free tcs in the given tcs_group; only for active. + * @tcs: A pointer to the active-only tcs_group (or the wake tcs_group if + * we borrowed it because there are zero active-only ones). + * + * Must be called with the drv->lock held since that protects tcs_in_use. + * + * Return: The first tcs that's free. + */ static int find_free_tcs(struct tcs_group *tcs) { int i; @@ -438,6 +569,20 @@ static int find_free_tcs(struct tcs_group *tcs) return -EBUSY; } +/** + * tcs_write() - Store messages into a TCS right now, or return -EBUSY. + * @drv: The controller. + * @msg: The data to be sent. + * + * Grabs a TCS for ACTIVE_ONLY transfers and writes the messages to it. + * + * If there are no free TCSes for ACTIVE_ONLY transfers or if a command for + * the same address is already transferring returns -EBUSY which means the + * client should retry shortly. + * + * Return: 0 on success, -EBUSY if client should retry, or an error. + * Client should have interrupts enabled for a bit before retrying. + */ static int tcs_write(struct rsc_drv *drv, const struct tcs_request *msg) { struct tcs_group *tcs; @@ -491,14 +636,26 @@ done_write: } /** - * rpmh_rsc_send_data: Validate the incoming message and write to the - * appropriate TCS block. + * rpmh_rsc_send_data() - Validate the incoming message + write to TCS block. + * @drv: The controller. + * @msg: The data to be sent. * - * @drv: the controller - * @msg: the data to be sent + * NOTES: + * - This is only used for "ACTIVE_ONLY" since the limitations of this + * function don't make sense for sleep/wake cases. + * - To do the transfer, we will grab a whole TCS for ourselves--we don't + * try to share. If there are none available we'll wait indefinitely + * for a free one. + * - This function will not wait for the commands to be finished, only for + * data to be programmed into the RPMh. See rpmh_tx_done() which will + * be called when the transfer is fully complete. + * - This function must be called with interrupts enabled. If the hardware + * is busy doing someone else's transfer we need that transfer to fully + * finish so that we can have the hardware, and to fully finish it needs + * the interrupt handler to run. If the interrupts is set to run on the + * active CPU this can never happen if interrupts are disabled. * * Return: 0 on success, -EINVAL on error. - * Note: This call blocks until a valid data is written to the TCS. */ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) { @@ -522,13 +679,30 @@ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) return ret; } +/** + * find_slots() - Find a place to write the given message. + * @tcs: The tcs group to search. + * @msg: The message we want to find room for. + * @tcs_id: If we return 0 from the function, we return the global ID of the + * TCS to write to here. + * @cmd_id: If we return 0 from the function, we return the index of + * the command array of the returned TCS where the client should + * start writing the message. + * + * Only for use on sleep/wake TCSes since those are the only ones we maintain + * tcs->slots for. + * + * Must be called with the tcs_lock for the group held. + * + * Return: -ENOMEM if there was no room, else 0. + */ static int find_slots(struct tcs_group *tcs, const struct tcs_request *msg, int *tcs_id, int *cmd_id) { int slot, offset; int i = 0; - /* Do over, until we can fit the full payload in a TCS */ + /* Do over, until we can fit the full payload in a single TCS */ do { slot = bitmap_find_next_zero_area(tcs->slots, MAX_TCS_SLOTS, i, msg->num_cmds, 0); @@ -547,12 +721,14 @@ static int find_slots(struct tcs_group *tcs, const struct tcs_request *msg, } /** - * rpmh_rsc_write_ctrl_data: Write request to the controller + * rpmh_rsc_write_ctrl_data() - Write request to controller but don't trigger. + * @drv: The controller. + * @msg: The data to be written to the controller. * - * @drv: the controller - * @msg: the data to be written to the controller + * This should only be called for for sleep/wake state, never active-only + * state. * - * There is no response returned for writing the request to the controller. + * Return: 0 if no error; else -error. */ int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg) { @@ -587,7 +763,6 @@ int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg) /** * rpmh_rsc_ctrlr_is_busy() - Check if any of the AMCs are busy. - * * @drv: The controller * * Checks if any of the AMCs are busy in handling ACTIVE sets. @@ -624,6 +799,23 @@ static bool rpmh_rsc_ctrlr_is_busy(struct rsc_drv *drv) return false; } +/** + * rpmh_rsc_cpu_pm_callback() - Check if any of the AMCs are busy. + * @nfb: Pointer to the notifier block in struct rsc_drv. + * @action: CPU_PM_ENTER, CPU_PM_ENTER_FAILED, or CPU_PM_EXIT. + * @v: Unused + * + * This function is given to cpu_pm_register_notifier so we can be informed + * about when CPUs go down. When all CPUs go down we know no more active + * transfers will be started so we write sleep/wake sets. This function gets + * called from cpuidle code paths and also at system suspend time. + * + * If its last CPU going down and AMCs are not busy then writes cached sleep + * and wake messages to TCSes. The firmware then takes care of triggering + * them when entering deepest low power modes. + * + * Return: See cpu_pm_register_notifier() + */ static int rpmh_rsc_cpu_pm_callback(struct notifier_block *nfb, unsigned long action, void *v) { -- cgit From ff304ea34d2e2d7ef7a13aefb4e62f456cf78e99 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 13 Apr 2020 10:04:12 -0700 Subject: soc: qcom: rpmh-rsc: tcs_is_free() can just check tcs_in_use tcs_is_free() had two checks in it: does the software think that the TCS is free and does the hardware think that the TCS is free. I couldn't figure out in which case the hardware could think that a TCS was in-use but software thought it was free. Apparently there is no case and the extra check can be removed. This apparently has already been done in a downstream patch. Suggested-by: Maulik Shah Signed-off-by: Douglas Anderson Reviewed-by: Maulik Shah Reviewed-by: Stephen Boyd Tested-by: Maulik Shah Link: https://lore.kernel.org/r/20200413100321.v4.7.Icf2213131ea652087f100129359052c83601f8b0@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 78fe9344ecd3..dc4bad01c000 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -177,7 +177,6 @@ static void write_tcs_reg_sync(struct rsc_drv *drv, int reg, int tcs_id, * @tcs_id: The global ID of this TCS. * * Returns true if nobody has claimed this TCS (by setting tcs_in_use). - * If the TCS looks free, checks that the hardware agrees. * * Context: Must be called with the drv->lock held or the tcs_lock for the TCS * being tested. If only the tcs_lock is held then it is possible that @@ -189,8 +188,7 @@ static void write_tcs_reg_sync(struct rsc_drv *drv, int reg, int tcs_id, */ static bool tcs_is_free(struct rsc_drv *drv, int tcs_id) { - return !test_bit(tcs_id, drv->tcs_in_use) && - read_tcs_reg(drv, RSC_DRV_STATUS, tcs_id); + return !test_bit(tcs_id, drv->tcs_in_use); } /** -- cgit From dded0317f510352ee622e526e113b9478ca406da Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 13 Apr 2020 10:04:13 -0700 Subject: soc: qcom: rpmh-rsc: Don't double-check rpmh payload The calls rpmh_rsc_write_ctrl_data() and rpmh_rsc_send_data() are only ever called from rpmh.c. We know that rpmh.c already error checked the message. There's no reason to do it again in rpmh-rsc. Suggested-by: Maulik Shah Signed-off-by: Douglas Anderson Reviewed-by: Maulik Shah Reviewed-by: Stephen Boyd Tested-by: Maulik Shah Link: https://lore.kernel.org/r/20200413100321.v4.8.I8e187cdfb7a31f5bb7724f1f937f2862ee464a35@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index dc4bad01c000..59ff4d17ddf5 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -634,7 +634,7 @@ done_write: } /** - * rpmh_rsc_send_data() - Validate the incoming message + write to TCS block. + * rpmh_rsc_send_data() - Write / trigger active-only message. * @drv: The controller. * @msg: The data to be sent. * @@ -659,12 +659,6 @@ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) { int ret; - if (!msg || !msg->cmds || !msg->num_cmds || - msg->num_cmds > MAX_RPMH_PAYLOAD) { - WARN_ON(1); - return -EINVAL; - } - do { ret = tcs_write(drv, msg); if (ret == -EBUSY) { @@ -735,16 +729,6 @@ int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg) unsigned long flags; int ret; - if (!msg || !msg->cmds || !msg->num_cmds || - msg->num_cmds > MAX_RPMH_PAYLOAD) { - pr_err("Payload error\n"); - return -EINVAL; - } - - /* Data sent to this API will not be sent immediately */ - if (msg->state == RPMH_ACTIVE_ONLY_STATE) - return -EINVAL; - tcs = get_tcs_for_msg(drv, msg); if (IS_ERR(tcs)) return PTR_ERR(tcs); -- cgit From 881808d0bbf336d333981ad86bde62ef2165e437 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 13 Apr 2020 10:04:14 -0700 Subject: soc: qcom: rpmh-rsc: Caller handles tcs_invalidate() exclusivity Auditing tcs_invalidate() made me worried. Specifically I saw that it used spin_lock(), not spin_lock_irqsave(). That always worries me unless I can trace for sure that I'm in the interrupt handler or that someone else already disabled interrupts. Looking more at it, there is actually no reason for these locks anyway. Specifically the only reason you'd ever call rpmh_rsc_invalidate() is if you cared that the sleep/wake TCSes were empty. That means that they need to continue to be empty even after rpmh_rsc_invalidate() returns. The only way that can happen is if the caller already has done something to keep all other RPMH users out. It should be noted that even though the caller is only worried about making sleep/wake TCSes empty, they also need to worry about stopping active-only transfers if they need to handle the case where active-only transfers might borrow the wake TCS. At the moment rpmh_rsc_invalidate() is only called in PM code from the last CPU. If that later changes the caller will still need to solve the above problems themselves, so these locks will never be useful. Continuing to audit tcs_invalidate(), I found a bug. The function didn't properly check for a borrowed TCS if we hadn't recently written anything into the TCS. Specifically, if we've never written to the WAKE_TCS (or we've flushed it recently) then tcs->slots is empty. We'll early-out and we'll never call tcs_is_free(). I thought about fixing this bug by either deleting the early check for bitmap_empty() or possibly only doing it if we knew we weren't on a TCS that could be borrowed. However, I think it's better to just delete the checks. As argued above it's up to the caller to make sure that all other users of RPMH are quiet before tcs_invalidate() is called. Since callers need to handle the zero-active-TCS case anyway that means they need to make sure that the active-only transfers are quiet before calling too. The one way tcs_invalidate() gets called today is through rpmh_rsc_cpu_pm_callback() which calls rpmh_rsc_ctrlr_is_busy() to handle this. When we have another path to get to tcs_invalidate() it will also need to come up with something similar and it won't need this extra check either. If we later find some code path that actually needs this check back in (and somehow manages to be race free) we can always add it back in. Signed-off-by: Douglas Anderson Reviewed-by: Maulik Shah Reviewed-by: Stephen Boyd Tested-by: Maulik Shah Link: https://lore.kernel.org/r/20200413100321.v4.9.I07c1f70e0e8f2dc0004bd38970b4e258acdc773e@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-internal.h | 2 +- drivers/soc/qcom/rpmh-rsc.c | 38 +++++++++++++------------------------- drivers/soc/qcom/rpmh.c | 5 +---- 3 files changed, 15 insertions(+), 30 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h index f06350cbc9a2..dba8510c0669 100644 --- a/drivers/soc/qcom/rpmh-internal.h +++ b/drivers/soc/qcom/rpmh-internal.h @@ -132,7 +132,7 @@ struct rsc_drv { int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg); int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg); -int rpmh_rsc_invalidate(struct rsc_drv *drv); +void rpmh_rsc_invalidate(struct rsc_drv *drv); void rpmh_tx_done(const struct tcs_request *msg, int r); int rpmh_flush(struct rpmh_ctrlr *ctrlr); diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 59ff4d17ddf5..5303837bb866 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -199,50 +199,38 @@ static bool tcs_is_free(struct rsc_drv *drv, int tcs_id) * This will clear the "slots" variable of the given tcs_group and also * tell the hardware to forget about all entries. * - * Return: 0 if no problem, or -EAGAIN if the caller should try again in a - * bit. Caller should make sure to enable interrupts between tries. + * The caller must ensure that no other RPMH actions are happening when this + * function is called, since otherwise the device may immediately become + * used again even before this function exits. */ -static int tcs_invalidate(struct rsc_drv *drv, int type) +static void tcs_invalidate(struct rsc_drv *drv, int type) { int m; struct tcs_group *tcs = &drv->tcs[type]; - spin_lock(&tcs->lock); - if (bitmap_empty(tcs->slots, MAX_TCS_SLOTS)) { - spin_unlock(&tcs->lock); - return 0; - } + /* Caller ensures nobody else is running so no lock */ + if (bitmap_empty(tcs->slots, MAX_TCS_SLOTS)) + return; for (m = tcs->offset; m < tcs->offset + tcs->num_tcs; m++) { - if (!tcs_is_free(drv, m)) { - spin_unlock(&tcs->lock); - return -EAGAIN; - } write_tcs_reg_sync(drv, RSC_DRV_CMD_ENABLE, m, 0); write_tcs_reg_sync(drv, RSC_DRV_CMD_WAIT_FOR_CMPL, m, 0); } bitmap_zero(tcs->slots, MAX_TCS_SLOTS); - spin_unlock(&tcs->lock); - - return 0; } /** * rpmh_rsc_invalidate() - Invalidate sleep and wake TCSes. * @drv: The RSC controller. * - * Return: 0 if no problem, or -EAGAIN if the caller should try again in a - * bit. Caller should make sure to enable interrupts between tries. + * The caller must ensure that no other RPMH actions are happening when this + * function is called, since otherwise the device may immediately become + * used again even before this function exits. */ -int rpmh_rsc_invalidate(struct rsc_drv *drv) +void rpmh_rsc_invalidate(struct rsc_drv *drv) { - int ret; - - ret = tcs_invalidate(drv, SLEEP_TCS); - if (!ret) - ret = tcs_invalidate(drv, WAKE_TCS); - - return ret; + tcs_invalidate(drv, SLEEP_TCS); + tcs_invalidate(drv, WAKE_TCS); } /** diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index be5e89d73526..3abbb08cd6e1 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -440,7 +440,6 @@ static int send_single(struct rpmh_ctrlr *ctrlr, enum rpmh_state state, * * Return: * * 0 - Success - * * -EAGAIN - Retry again * * Error code - Otherwise */ int rpmh_flush(struct rpmh_ctrlr *ctrlr) @@ -456,9 +455,7 @@ int rpmh_flush(struct rpmh_ctrlr *ctrlr) } /* Invalidate the TCSes first to avoid stale data */ - ret = rpmh_rsc_invalidate(ctrlr_to_drv(ctrlr)); - if (ret) - return ret; + rpmh_rsc_invalidate(ctrlr_to_drv(ctrlr)); /* First flush the cached batch requests */ ret = flush_batch(ctrlr); -- cgit From 032c692ae588324f66ac07c0357d2e681a9d0e1e Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 13 Apr 2020 10:04:15 -0700 Subject: soc: qcom: rpmh-rsc: read_tcs_reg()/write_tcs_reg() are not for IRQ The RSC_DRV_IRQ_ENABLE, RSC_DRV_IRQ_STATUS, and RSC_DRV_IRQ_CLEAR registers are not part of TCS 0. Let's not pretend that they are by using read_tcs_reg() and write_tcs_reg() and passing a bogus tcs_id of 0. We could introduce a new wrapper for these registers but it wouldn't buy us much. Let's just read/write directly. Signed-off-by: Douglas Anderson Reviewed-by: Maulik Shah Reviewed-by: Stephen Boyd Tested-by: Maulik Shah Link: https://lore.kernel.org/r/20200413100321.v4.10.I2adf93809c692d0b673e1a86ea97c45644aa8d97@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 5303837bb866..732316bb67dc 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -364,12 +364,12 @@ static void enable_tcs_irq(struct rsc_drv *drv, int tcs_id, bool enable) { u32 data; - data = read_tcs_reg(drv, RSC_DRV_IRQ_ENABLE, 0); + data = readl_relaxed(drv->tcs_base + RSC_DRV_IRQ_ENABLE); if (enable) data |= BIT(tcs_id); else data &= ~BIT(tcs_id); - write_tcs_reg(drv, RSC_DRV_IRQ_ENABLE, 0, data); + writel_relaxed(data, drv->tcs_base + RSC_DRV_IRQ_ENABLE); } /** @@ -390,7 +390,7 @@ static irqreturn_t tcs_tx_done(int irq, void *p) const struct tcs_request *req; struct tcs_cmd *cmd; - irq_status = read_tcs_reg(drv, RSC_DRV_IRQ_STATUS, 0); + irq_status = readl_relaxed(drv->tcs_base + RSC_DRV_IRQ_STATUS); for_each_set_bit(i, &irq_status, BITS_PER_LONG) { req = get_req_from_tcs(drv, i); @@ -427,7 +427,7 @@ skip: /* Reclaim the TCS */ write_tcs_reg(drv, RSC_DRV_CMD_ENABLE, i, 0); write_tcs_reg(drv, RSC_DRV_CMD_WAIT_FOR_CMPL, i, 0); - write_tcs_reg(drv, RSC_DRV_IRQ_CLEAR, 0, BIT(i)); + writel_relaxed(BIT(i), drv->tcs_base + RSC_DRV_IRQ_CLEAR); spin_lock(&drv->lock); clear_bit(i, drv->tcs_in_use); /* @@ -969,7 +969,8 @@ static int rpmh_rsc_probe(struct platform_device *pdev) } /* Enable the active TCS to send requests immediately */ - write_tcs_reg(drv, RSC_DRV_IRQ_ENABLE, 0, drv->tcs[ACTIVE_TCS].mask); + writel_relaxed(drv->tcs[ACTIVE_TCS].mask, + drv->tcs_base + RSC_DRV_IRQ_ENABLE); spin_lock_init(&drv->client.cache_lock); INIT_LIST_HEAD(&drv->client.cache); -- cgit From 04df45fba52177f796a20642b92fdc44e489e174 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Tue, 17 Mar 2020 09:37:33 +0800 Subject: soc: imx8m: No need to put node when of_find_compatible_node() failed No need to put node when of_find_compatible_node() failed, return immediately to simplify the code. Signed-off-by: Anson Huang Signed-off-by: Shawn Guo --- drivers/soc/imx/soc-imx8m.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/imx/soc-imx8m.c b/drivers/soc/imx/soc-imx8m.c index 719e1f189ebf..7b0759adb47d 100644 --- a/drivers/soc/imx/soc-imx8m.c +++ b/drivers/soc/imx/soc-imx8m.c @@ -53,11 +53,11 @@ static u32 __init imx8mq_soc_revision(void) struct device_node *np; void __iomem *ocotp_base; u32 magic; - u32 rev = 0; + u32 rev; np = of_find_compatible_node(NULL, NULL, "fsl,imx8mq-ocotp"); if (!np) - goto out; + return 0; ocotp_base = of_iomap(np, 0); WARN_ON(!ocotp_base); @@ -78,9 +78,8 @@ static u32 __init imx8mq_soc_revision(void) soc_uid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW); iounmap(ocotp_base); - -out: of_node_put(np); + return rev; } -- cgit From 1d3c6f86fd3f8b88c707f56d8c3f94e014b40e83 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Thu, 26 Mar 2020 22:44:58 +0000 Subject: soc: qcom: rpmh: Allow RPMH driver to be loaded as a module This patch allow the rpmh driver to be loaded as a permenent module. Meaning it can be loaded from a module, but then cannot be unloaded. Ideally, it would include a remove hook and related logic, but the rpmh driver is fairly core to the system, so once its loaded with almost anythign else to get the system to go, the dependencies are not likely to ever also be removed. So making it a permenent module at least improves things slightly over requiring it to be a built in driver. Acked-by: Saravana Kannan Cc: Todd Kjos Cc: Saravana Kannan Cc: Andy Gross Cc: Bjorn Andersson Cc: Rajendra Nayak Cc: linux-arm-msm@vger.kernel.org Tested-by: Bjorn Andersson Reviewed-by: Bjorn Andersson Signed-off-by: John Stultz Link: https://lore.kernel.org/r/20200326224459.105170-3-john.stultz@linaro.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/Kconfig | 2 +- drivers/soc/qcom/rpmh-rsc.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index bf42a17a45de..70d97147d87e 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -107,7 +107,7 @@ config QCOM_RMTFS_MEM Say y here if you intend to boot the modem remoteproc. config QCOM_RPMH - bool "Qualcomm RPM-Hardened (RPMH) Communication" + tristate "Qualcomm RPM-Hardened (RPMH) Communication" depends on ARCH_QCOM && ARM64 || COMPILE_TEST help Support for communication with the hardened-RPM blocks in diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 732316bb67dc..a9e15699f55f 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -985,6 +986,7 @@ static const struct of_device_id rpmh_drv_match[] = { { .compatible = "qcom,rpmh-rsc", }, { } }; +MODULE_DEVICE_TABLE(of, rpmh_drv_match); static struct platform_driver rpmh_driver = { .probe = rpmh_rsc_probe, @@ -999,3 +1001,6 @@ static int __init rpmh_driver_init(void) return platform_driver_register(&rpmh_driver); } arch_initcall(rpmh_driver_init); + +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. RPMh Driver"); +MODULE_LICENSE("GPL v2"); -- cgit From d4889ec1fc6ac6321cc1e8b35bb656f970926a09 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Thu, 26 Mar 2020 22:44:59 +0000 Subject: soc: qcom: rpmhpd: Allow RPMHPD driver to be loaded as a module This patch allow the rpmhpd driver to be loaded as a permenent module. Meaning it can be loaded from a module, but then cannot be unloaded. Ideally, it would include a remove hook and related logic, but apparently the genpd code isn't able to track usage and cleaning things up? So making it a permenent module at least improves things slightly over requiring it to be a built in driver. Cc: Todd Kjos Cc: Saravana Kannan Cc: Andy Gross Cc: Bjorn Andersson Cc: Rajendra Nayak Cc: linux-arm-msm@vger.kernel.org Acked-by: Saravana Kannan Tested-by: Bjorn Andersson Reviewed-by: Bjorn Andersson Signed-off-by: John Stultz Link: https://lore.kernel.org/r/20200326224459.105170-4-john.stultz@linaro.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/Kconfig | 2 +- drivers/soc/qcom/rpmhpd.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 70d97147d87e..c2a8fcdb24b9 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -117,7 +117,7 @@ config QCOM_RPMH help apply the aggregated state on the resource. config QCOM_RPMHPD - bool "Qualcomm RPMh Power domain driver" + tristate "Qualcomm RPMh Power domain driver" depends on QCOM_RPMH && QCOM_COMMAND_DB help QCOM RPMh Power domain driver to support power-domains with diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c index 4d264d0672c4..0bb12d5870a7 100644 --- a/drivers/soc/qcom/rpmhpd.c +++ b/drivers/soc/qcom/rpmhpd.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -189,6 +190,7 @@ static const struct of_device_id rpmhpd_match_table[] = { { .compatible = "qcom,sm8150-rpmhpd", .data = &sm8150_desc }, { } }; +MODULE_DEVICE_TABLE(of, rpmhpd_match_table); static int rpmhpd_send_corner(struct rpmhpd *pd, int state, unsigned int corner, bool sync) @@ -460,3 +462,6 @@ static int __init rpmhpd_init(void) return platform_driver_register(&rpmhpd_driver); } core_initcall(rpmhpd_init); + +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. RPMh Power Domain Driver"); +MODULE_LICENSE("GPL v2"); -- cgit From f29808b2fb85a7ff2d4830aa1cb736c8c9b986f4 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Thu, 26 Mar 2020 22:44:57 +0000 Subject: soc: qcom: rpmpd: Allow RPMPD driver to be loaded as a module This patch allow the rpmpd driver to be loaded as a permenent module. Meaning it can be loaded from a module, but then cannot be unloaded. Ideally, it would include a remove hook and related logic, but apparently the genpd code isn't able to track usage and cleaning things up? (See: https://lkml.org/lkml/2019/1/24/38) So making it a permenent module at least improves things slightly over requiring it to be a built in driver. Cc: Todd Kjos Cc: Saravana Kannan Cc: Andy Gross Cc: Bjorn Andersson Cc: Rajendra Nayak Cc: linux-arm-msm@vger.kernel.org Acked-by: Saravana Kannan Tested-by: Bjorn Andersson Reviewed-by: Bjorn Andersson Signed-off-by: John Stultz Link: https://lore.kernel.org/r/20200326224459.105170-2-john.stultz@linaro.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/Kconfig | 4 ++-- drivers/soc/qcom/rpmpd.c | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index c2a8fcdb24b9..0d0123f6ec0a 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -126,8 +126,8 @@ config QCOM_RPMHPD for the voltage rail. config QCOM_RPMPD - bool "Qualcomm RPM Power domain driver" - depends on QCOM_SMD_RPM=y + tristate "Qualcomm RPM Power domain driver" + depends on QCOM_SMD_RPM help QCOM RPM Power domain driver to support power-domains with performance states. The driver communicates a performance state diff --git a/drivers/soc/qcom/rpmpd.c b/drivers/soc/qcom/rpmpd.c index 2b1834c5609a..f2168e4259b2 100644 --- a/drivers/soc/qcom/rpmpd.c +++ b/drivers/soc/qcom/rpmpd.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -226,6 +227,7 @@ static const struct of_device_id rpmpd_match_table[] = { { .compatible = "qcom,qcs404-rpmpd", .data = &qcs404_desc }, { } }; +MODULE_DEVICE_TABLE(of, rpmpd_match_table); static int rpmpd_send_enable(struct rpmpd *pd, bool enable) { @@ -422,3 +424,6 @@ static int __init rpmpd_init(void) return platform_driver_register(&rpmpd_driver); } core_initcall(rpmpd_init); + +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. RPM Power Domain Driver"); +MODULE_LICENSE("GPL v2"); -- cgit From 9d6ba921acf43bf1894564eb3d51ced2145e0147 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 14 Apr 2020 23:20:33 -0700 Subject: soc: qcom: cmd-db: Cast sizeof() to int to silence field width warning We pass the result of sizeof() here to tell the printk format specifier how many bytes to print. That expects an int though and sizeof() isn't that type. Cast to int to silence this warning: drivers/soc/qcom/cmd-db.c: In function 'cmd_db_debugfs_dump': drivers/soc/qcom/cmd-db.c:281:30: warning: field width specifier '*' expects argument of type 'int', but argument 4 has type 'long unsigned int' [-Wformat=] Reviewed-by: Guenter Roeck Fixes: d6815c5c43d4 ("soc: qcom: cmd-db: Add debugfs dumping file") Reported-by: Stephen Rothwell Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20200415062033.66406-1-swboyd@chromium.org Signed-off-by: Bjorn Andersson --- 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 8b2b7357b6da..57382b64f1da 100644 --- a/drivers/soc/qcom/cmd-db.c +++ b/drivers/soc/qcom/cmd-db.c @@ -279,7 +279,7 @@ static int cmd_db_debugfs_dump(struct seq_file *seq, void *p) ent = rsc_to_entry_header(rsc); for (j = 0; j < le16_to_cpu(rsc->cnt); j++, ent++) { seq_printf(seq, "0x%08x: %*pEp", le32_to_cpu(ent->addr), - sizeof(ent->id), ent->id); + (int)sizeof(ent->id), ent->id); len = le16_to_cpu(ent->len); if (len) { -- cgit From 3adaf26e7b01691eeee4086b7ac70c7750ff126e Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 15 Apr 2020 12:29:16 -0700 Subject: soc: qcom: cmd-db: Use 5 digits for printing address The top few bits aren't relevant to pad out because they're always zero. Let's just print 5 digits instead of 8 so that it's a little shorter and more readable. Reviewed-by: Lina Iyer Suggested-by: Lina Iyer Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20200415192916.78339-1-swboyd@chromium.org Signed-off-by: Bjorn Andersson --- 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 57382b64f1da..4f8b739c6485 100644 --- a/drivers/soc/qcom/cmd-db.c +++ b/drivers/soc/qcom/cmd-db.c @@ -278,7 +278,7 @@ static int cmd_db_debugfs_dump(struct seq_file *seq, void *p) ent = rsc_to_entry_header(rsc); for (j = 0; j < le16_to_cpu(rsc->cnt); j++, ent++) { - seq_printf(seq, "0x%08x: %*pEp", le32_to_cpu(ent->addr), + seq_printf(seq, "0x%05x: %*pEp", le32_to_cpu(ent->addr), (int)sizeof(ent->id), ent->id); len = le16_to_cpu(ent->len); -- cgit From 704887278b3fb6e72ef767e56fbae4129e567c48 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 16 Apr 2020 17:06:45 -0700 Subject: soc: qcom: cmd-db: Properly endian swap the slv_id for debugfs Read the slv_id properly by making sure the 16-bit number is endian swapped from little endian to CPU native before we read it to figure out what to print for the human readable name. Otherwise we may just show that all the elements in the cmd-db are "Unknown" which isn't right. Reviewed-by: Guenter Roeck Reported-by: kbuild test robot Cc: Lina Iyer Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20200417000645.234693-1-swboyd@chromium.org Signed-off-by: Bjorn Andersson --- 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 4f8b739c6485..fc5610603b17 100644 --- a/drivers/soc/qcom/cmd-db.c +++ b/drivers/soc/qcom/cmd-db.c @@ -254,7 +254,7 @@ static int cmd_db_debugfs_dump(struct seq_file *seq, void *p) if (!rsc->slv_id) break; - switch (rsc->slv_id) { + switch (le16_to_cpu(rsc->slv_id)) { case CMD_DB_HW_ARC: name = "ARC"; break; -- cgit From 64016bb88e8519d0f8512b9861837779c1239c0d Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Tue, 14 Apr 2020 23:21:53 -0700 Subject: soc: qcom: rpmhpd: Add SM8250 power domains Tested-by: Vinod Koul Reviewed-by: Vinod Koul Acked-by: Rob Herring Link: https://lore.kernel.org/r/20200415062154.741179-2-bjorn.andersson@linaro.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmhpd.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c index 0bb12d5870a7..e72426221a69 100644 --- a/drivers/soc/qcom/rpmhpd.c +++ b/drivers/soc/qcom/rpmhpd.c @@ -167,6 +167,24 @@ static const struct rpmhpd_desc sm8150_desc = { .num_pds = ARRAY_SIZE(sm8150_rpmhpds), }; +static struct rpmhpd *sm8250_rpmhpds[] = { + [SM8250_CX] = &sdm845_cx, + [SM8250_CX_AO] = &sdm845_cx_ao, + [SM8250_EBI] = &sdm845_ebi, + [SM8250_GFX] = &sdm845_gfx, + [SM8250_LCX] = &sdm845_lcx, + [SM8250_LMX] = &sdm845_lmx, + [SM8250_MMCX] = &sm8150_mmcx, + [SM8250_MMCX_AO] = &sm8150_mmcx_ao, + [SM8250_MX] = &sdm845_mx, + [SM8250_MX_AO] = &sdm845_mx_ao, +}; + +static const struct rpmhpd_desc sm8250_desc = { + .rpmhpds = sm8250_rpmhpds, + .num_pds = ARRAY_SIZE(sm8250_rpmhpds), +}; + /* SC7180 RPMH powerdomains */ static struct rpmhpd *sc7180_rpmhpds[] = { [SC7180_CX] = &sdm845_cx, @@ -188,6 +206,7 @@ static const struct of_device_id rpmhpd_match_table[] = { { .compatible = "qcom,sc7180-rpmhpd", .data = &sc7180_desc }, { .compatible = "qcom,sdm845-rpmhpd", .data = &sdm845_desc }, { .compatible = "qcom,sm8150-rpmhpd", .data = &sm8150_desc }, + { .compatible = "qcom,sm8250-rpmhpd", .data = &sm8250_desc }, { } }; MODULE_DEVICE_TABLE(of, rpmhpd_match_table); -- cgit From 35bb4b22f606c0cc8eedf567313adc18161b1af4 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Fri, 17 Apr 2020 14:15:47 -0700 Subject: soc: qcom: rpmh: Dirt can only make you dirtier, not cleaner Adding an item into the cache should never be able to make the cache cleaner. Use "|=" rather than "=" to update the dirty flag. Reviewed-by: Matthias Kaehlcke Reviewed-by: Maulik Shah Thanks, Maulik Reviewed-by: Bjorn Andersson Fixes: bb7000677a1b ("soc: qcom: rpmh: Update dirty flag only when data changes") Reported-by: Stephen Boyd Signed-off-by: Douglas Anderson Link: https://lore.kernel.org/r/20200417141531.1.Ia4b74158497213eabad7c3d474c50bfccb3f342e@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index 3abbb08cd6e1..d1626a1328d7 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -151,10 +151,10 @@ existing: break; } - ctrlr->dirty = (req->sleep_val != old_sleep_val || - req->wake_val != old_wake_val) && - req->sleep_val != UINT_MAX && - req->wake_val != UINT_MAX; + ctrlr->dirty |= (req->sleep_val != old_sleep_val || + req->wake_val != old_wake_val) && + req->sleep_val != UINT_MAX && + req->wake_val != UINT_MAX; unlock: spin_unlock_irqrestore(&ctrlr->cache_lock, flags); -- cgit From 02d8ecc18b8f392389ac9e7b785b0230ecb80833 Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Wed, 15 Apr 2020 11:59:55 +0530 Subject: soc: qcom: pdr: Remove impossible error condition The patch fbe639b44a82: "soc: qcom: Introduce Protection Domain Restart helpers" leads to the following static checker warning: drivers/soc/qcom/pdr_interface.c:158 pdr_register_listener() '(resp.curr_state < (-((~0 >> 1)) - 1)) => (s32min-s32max < s32min)' These are casted to int so they can't be outside of int range. Fixes: fbe639b44a82 ("soc: qcom: Introduce Protection Domain Restart helpers") Reported-by: Dan Carpenter Signed-off-by: Sibi Sankar Link: https://lore.kernel.org/r/20200415062955.21439-1-sibis@codeaurora.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/pdr_interface.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/pdr_interface.c b/drivers/soc/qcom/pdr_interface.c index 17ad3b8698e1..bdcf16f88a97 100644 --- a/drivers/soc/qcom/pdr_interface.c +++ b/drivers/soc/qcom/pdr_interface.c @@ -155,10 +155,6 @@ static int pdr_register_listener(struct pdr_handle *pdr, return ret; } - if ((int)resp.curr_state < INT_MIN || (int)resp.curr_state > INT_MAX) - pr_err("PDR: %s notification state invalid: 0x%x\n", - pds->service_path, resp.curr_state); - pds->state = resp.curr_state; return 0; -- cgit From c78cf9956f19a7db7199310802ec514266653931 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 17 Apr 2020 19:11:16 +0200 Subject: soc/tegra: pmc: Enable PMIC wake event on Tegra186 The PMIC wake event can be used to bring the system out of suspend based on certain events happening on the PMIC (such as an RTC alarm). Signed-off-by: Thierry Reding Acked-by: Jon Hunter Tested-by: Jon Hunter --- drivers/soc/tegra/pmc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 1c533a969f54..72cfda7305d5 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -3193,6 +3193,7 @@ static void tegra186_pmc_setup_irq_polarity(struct tegra_pmc *pmc, } static const struct tegra_wake_event tegra186_wake_events[] = { + TEGRA_WAKE_IRQ("pmu", 24, 209), TEGRA_WAKE_GPIO("power", 29, 1, TEGRA186_AON_GPIO(FF, 0)), TEGRA_WAKE_IRQ("rtc", 73, 10), }; -- cgit From 379ac9eb1fe94cc40173b83fda1ee8d611a756e9 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Fri, 17 Apr 2020 13:39:48 +0100 Subject: soc/tegra: fuse: Add custom SoC attributes Add a custom SoC attribute for Tegra to expose the HIDREV register fields to userspace via the sysfs. This register provides additional details about the type of device (eg, silicon, FPGA, etc) as well as revision. Exposing this information is useful for identifying the exact device revision and device type. For Tegra devices up until Tegra186, the majorrev and minorrev fields of the HIDREV register are used to determine the device revision and device type. For Tegra194, the majorrev and minorrev fields only determine the revision. Starting with Tegra194, there is an additional field, pre_si_platform (which occupies bits 20-23), that now determines device type. Therefore, for all Tegra devices, add a custom SoC attribute for the majorrev and minorrev fields and for Tegra194 add an additional attribute for the pre_si_platform field. Signed-off-by: Jon Hunter Signed-off-by: Thierry Reding --- drivers/soc/tegra/fuse/fuse-tegra.c | 54 ++++++++++++++++++++++++++++++++++ drivers/soc/tegra/fuse/fuse-tegra20.c | 1 + drivers/soc/tegra/fuse/fuse-tegra30.c | 6 ++++ drivers/soc/tegra/fuse/fuse.h | 8 +++++ drivers/soc/tegra/fuse/tegra-apbmisc.c | 10 +++++++ 5 files changed, 79 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c index 802717b9f6a3..db65ddb6a5d2 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra.c +++ b/drivers/soc/tegra/fuse/fuse-tegra.c @@ -300,6 +300,59 @@ static void tegra_enable_fuse_clk(void __iomem *base) writel(reg, base + 0x14); } +static ssize_t major_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", tegra_get_major_rev()); +} + +static DEVICE_ATTR_RO(major); + +static ssize_t minor_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", tegra_get_minor_rev()); +} + +static DEVICE_ATTR_RO(minor); + +static struct attribute *tegra_soc_attr[] = { + &dev_attr_major.attr, + &dev_attr_minor.attr, + NULL, +}; + +const struct attribute_group tegra_soc_attr_group = { + .attrs = tegra_soc_attr, +}; + +#ifdef CONFIG_ARCH_TEGRA_194_SOC +static ssize_t platform_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + /* + * Displays the value in the 'pre_si_platform' field of the HIDREV + * register for Tegra194 devices. A value of 0 indicates that the + * platform type is silicon and all other non-zero values indicate + * the type of simulation platform is being used. + */ + return sprintf(buf, "%d\n", (tegra_read_chipid() >> 20) & 0xf); +} + +static DEVICE_ATTR_RO(platform); + +static struct attribute *tegra194_soc_attr[] = { + &dev_attr_major.attr, + &dev_attr_minor.attr, + &dev_attr_platform.attr, + NULL, +}; + +const struct attribute_group tegra194_soc_attr_group = { + .attrs = tegra194_soc_attr, +}; +#endif + struct device * __init tegra_soc_device_register(void) { struct soc_device_attribute *attr; @@ -312,6 +365,7 @@ struct device * __init tegra_soc_device_register(void) attr->family = kasprintf(GFP_KERNEL, "Tegra"); attr->revision = kasprintf(GFP_KERNEL, "%d", tegra_sku_info.revision); attr->soc_id = kasprintf(GFP_KERNEL, "%u", tegra_get_chip_id()); + attr->custom_attr_group = fuse->soc->soc_attr_group; dev = soc_device_register(attr); if (IS_ERR(dev)) { diff --git a/drivers/soc/tegra/fuse/fuse-tegra20.c b/drivers/soc/tegra/fuse/fuse-tegra20.c index d4aef9c4a94c..16aaa28573ac 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra20.c +++ b/drivers/soc/tegra/fuse/fuse-tegra20.c @@ -164,4 +164,5 @@ const struct tegra_fuse_soc tegra20_fuse_soc = { .speedo_init = tegra20_init_speedo_data, .probe = tegra20_fuse_probe, .info = &tegra20_fuse_info, + .soc_attr_group = &tegra_soc_attr_group, }; diff --git a/drivers/soc/tegra/fuse/fuse-tegra30.c b/drivers/soc/tegra/fuse/fuse-tegra30.c index e6037f900fb7..85accef41fa1 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra30.c +++ b/drivers/soc/tegra/fuse/fuse-tegra30.c @@ -111,6 +111,7 @@ const struct tegra_fuse_soc tegra30_fuse_soc = { .init = tegra30_fuse_init, .speedo_init = tegra30_init_speedo_data, .info = &tegra30_fuse_info, + .soc_attr_group = &tegra_soc_attr_group, }; #endif @@ -125,6 +126,7 @@ const struct tegra_fuse_soc tegra114_fuse_soc = { .init = tegra30_fuse_init, .speedo_init = tegra114_init_speedo_data, .info = &tegra114_fuse_info, + .soc_attr_group = &tegra_soc_attr_group, }; #endif @@ -205,6 +207,7 @@ const struct tegra_fuse_soc tegra124_fuse_soc = { .info = &tegra124_fuse_info, .lookups = tegra124_fuse_lookups, .num_lookups = ARRAY_SIZE(tegra124_fuse_lookups), + .soc_attr_group = &tegra_soc_attr_group, }; #endif @@ -290,6 +293,7 @@ const struct tegra_fuse_soc tegra210_fuse_soc = { .info = &tegra210_fuse_info, .lookups = tegra210_fuse_lookups, .num_lookups = ARRAY_SIZE(tegra210_fuse_lookups), + .soc_attr_group = &tegra_soc_attr_group, }; #endif @@ -319,6 +323,7 @@ const struct tegra_fuse_soc tegra186_fuse_soc = { .info = &tegra186_fuse_info, .lookups = tegra186_fuse_lookups, .num_lookups = ARRAY_SIZE(tegra186_fuse_lookups), + .soc_attr_group = &tegra_soc_attr_group, }; #endif @@ -348,5 +353,6 @@ const struct tegra_fuse_soc tegra194_fuse_soc = { .info = &tegra194_fuse_info, .lookups = tegra194_fuse_lookups, .num_lookups = ARRAY_SIZE(tegra194_fuse_lookups), + .soc_attr_group = &tegra194_soc_attr_group, }; #endif diff --git a/drivers/soc/tegra/fuse/fuse.h b/drivers/soc/tegra/fuse/fuse.h index 94a059e577a1..9d4fc315a007 100644 --- a/drivers/soc/tegra/fuse/fuse.h +++ b/drivers/soc/tegra/fuse/fuse.h @@ -32,6 +32,8 @@ struct tegra_fuse_soc { const struct nvmem_cell_lookup *lookups; unsigned int num_lookups; + + const struct attribute_group *soc_attr_group; }; struct tegra_fuse { @@ -64,6 +66,11 @@ void tegra_init_apbmisc(void); bool __init tegra_fuse_read_spare(unsigned int spare); u32 __init tegra_fuse_read_early(unsigned int offset); +u8 tegra_get_major_rev(void); +u8 tegra_get_minor_rev(void); + +extern const struct attribute_group tegra_soc_attr_group; + #ifdef CONFIG_ARCH_TEGRA_2x_SOC void tegra20_init_speedo_data(struct tegra_sku_info *sku_info); #endif @@ -110,6 +117,7 @@ extern const struct tegra_fuse_soc tegra186_fuse_soc; #ifdef CONFIG_ARCH_TEGRA_194_SOC extern const struct tegra_fuse_soc tegra194_fuse_soc; +extern const struct attribute_group tegra194_soc_attr_group; #endif #endif diff --git a/drivers/soc/tegra/fuse/tegra-apbmisc.c b/drivers/soc/tegra/fuse/tegra-apbmisc.c index 089d9340564b..44a154ca16b5 100644 --- a/drivers/soc/tegra/fuse/tegra-apbmisc.c +++ b/drivers/soc/tegra/fuse/tegra-apbmisc.c @@ -37,6 +37,16 @@ u8 tegra_get_chip_id(void) return (tegra_read_chipid() >> 8) & 0xff; } +u8 tegra_get_major_rev(void) +{ + return (tegra_read_chipid() >> 4) & 0xf; +} + +u8 tegra_get_minor_rev(void) +{ + return (tegra_read_chipid() >> 16) & 0xf; +} + u32 tegra_read_straps(void) { WARN(!chipid, "Tegra ABP MISC not yet available\n"); -- cgit From d08a4095abd8174e13c23c733584da9d0de556e7 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Fri, 17 Apr 2020 13:39:49 +0100 Subject: soc/tegra: fuse: Trivial clean-up of tegra_init_revision() Clean-up the tegra_init_revision() function by removing the 'rev' variable which is not needed and use the newly added helper function tegra_get_minor_rev() to get the minor revision. Signed-off-by: Jon Hunter Signed-off-by: Thierry Reding --- drivers/soc/tegra/fuse/tegra-apbmisc.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/fuse/tegra-apbmisc.c b/drivers/soc/tegra/fuse/tegra-apbmisc.c index 44a154ca16b5..3cdd69d1bd4d 100644 --- a/drivers/soc/tegra/fuse/tegra-apbmisc.c +++ b/drivers/soc/tegra/fuse/tegra-apbmisc.c @@ -75,36 +75,32 @@ static const struct of_device_id apbmisc_match[] __initconst = { void __init tegra_init_revision(void) { - u32 id, chip_id, minor_rev; - int rev; + u8 chip_id, minor_rev; - id = tegra_read_chipid(); - chip_id = (id >> 8) & 0xff; - minor_rev = (id >> 16) & 0xf; + chip_id = tegra_get_chip_id(); + minor_rev = tegra_get_minor_rev(); switch (minor_rev) { case 1: - rev = TEGRA_REVISION_A01; + tegra_sku_info.revision = TEGRA_REVISION_A01; break; case 2: - rev = TEGRA_REVISION_A02; + tegra_sku_info.revision = TEGRA_REVISION_A02; break; case 3: if (chip_id == TEGRA20 && (tegra_fuse_read_spare(18) || tegra_fuse_read_spare(19))) - rev = TEGRA_REVISION_A03p; + tegra_sku_info.revision = TEGRA_REVISION_A03p; else - rev = TEGRA_REVISION_A03; + tegra_sku_info.revision = TEGRA_REVISION_A03; break; case 4: - rev = TEGRA_REVISION_A04; + tegra_sku_info.revision = TEGRA_REVISION_A04; break; default: - rev = TEGRA_REVISION_UNKNOWN; + tegra_sku_info.revision = TEGRA_REVISION_UNKNOWN; } - tegra_sku_info.revision = rev; - tegra_sku_info.sku_id = tegra_fuse_read_early(FUSE_SKU_INFO); } -- cgit From 37558ac85c197a5ff776b7b2f53046877f2bd4ee Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Fri, 17 Apr 2020 13:40:46 +0100 Subject: soc/tegra: fuse: Update the SoC revision attribute to display a name Currently the SoC revision attribute for Tegra devices displays the value of the enum associated with a particular revision. This is not very useful because to obtain the actual revision you need to use the tegra_revision enumeration to translate the value. It is more meaningful to display a name for the revision, such as 'A01', than the enumarated value and therefore, update the revision attribute to display a name. This change does alter the ABI, which is unfortunate, but this is more meaningful and maintable. Signed-off-by: Jon Hunter Signed-off-by: Thierry Reding --- drivers/soc/tegra/fuse/fuse-tegra.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c index db65ddb6a5d2..d1f8dd0289e6 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra.c +++ b/drivers/soc/tegra/fuse/fuse-tegra.c @@ -363,7 +363,8 @@ struct device * __init tegra_soc_device_register(void) return NULL; attr->family = kasprintf(GFP_KERNEL, "Tegra"); - attr->revision = kasprintf(GFP_KERNEL, "%d", tegra_sku_info.revision); + attr->revision = kasprintf(GFP_KERNEL, "%s", + tegra_revision_name[tegra_sku_info.revision]); attr->soc_id = kasprintf(GFP_KERNEL, "%u", tegra_get_chip_id()); attr->custom_attr_group = fuse->soc->soc_attr_group; -- cgit From 5b9fa9cbe654ed0a138fd2dd6b79a19125f3c3e2 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Thu, 23 Apr 2020 22:40:43 +0100 Subject: soc: renesas: rcar-sysc: Add R8A7742 support Add support for RZ/G1H (R8A7742) SoC power areas to the R-Car SYSC driver. Signed-off-by: Lad Prabhakar Reviewed-by: Marian-Cristian Rotariu Link: https://lore.kernel.org/r/1587678050-23468-4-git-send-email-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/soc/renesas/Kconfig | 4 ++++ drivers/soc/renesas/Makefile | 1 + drivers/soc/renesas/r8a7742-sysc.c | 42 ++++++++++++++++++++++++++++++++++++++ drivers/soc/renesas/rcar-sysc.c | 3 +++ drivers/soc/renesas/rcar-sysc.h | 1 + 5 files changed, 51 insertions(+) create mode 100644 drivers/soc/renesas/r8a7742-sysc.c (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 1982c7fb45fa..1eff82c3b934 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -261,6 +261,10 @@ config ARCH_R8A77995 endif # ARM64 # SoC +config SYSC_R8A7742 + bool "RZ/G1H System Controller support" if COMPILE_TEST + select SYSC_RCAR + config SYSC_R8A7743 bool "RZ/G1M System Controller support" if COMPILE_TEST select SYSC_RCAR diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile index e595c3c3bd10..08296d78e2ad 100644 --- a/drivers/soc/renesas/Makefile +++ b/drivers/soc/renesas/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_SOC_RENESAS) += renesas-soc.o # SoC +obj-$(CONFIG_SYSC_R8A7742) += r8a7742-sysc.o obj-$(CONFIG_SYSC_R8A7743) += r8a7743-sysc.o obj-$(CONFIG_SYSC_R8A7745) += r8a7745-sysc.o obj-$(CONFIG_SYSC_R8A77470) += r8a77470-sysc.o diff --git a/drivers/soc/renesas/r8a7742-sysc.c b/drivers/soc/renesas/r8a7742-sysc.c new file mode 100644 index 000000000000..219a675f83f4 --- /dev/null +++ b/drivers/soc/renesas/r8a7742-sysc.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RZ/G1H System Controller + * + * Copyright (C) 2020 Renesas Electronics Corp. + */ + +#include + +#include + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a7742_areas[] __initconst = { + { "always-on", 0, 0, R8A7742_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca15-scu", 0x180, 0, R8A7742_PD_CA15_SCU, R8A7742_PD_ALWAYS_ON, + PD_SCU }, + { "ca15-cpu0", 0x40, 0, R8A7742_PD_CA15_CPU0, R8A7742_PD_CA15_SCU, + PD_CPU_NOCR }, + { "ca15-cpu1", 0x40, 1, R8A7742_PD_CA15_CPU1, R8A7742_PD_CA15_SCU, + PD_CPU_NOCR }, + { "ca15-cpu2", 0x40, 2, R8A7742_PD_CA15_CPU2, R8A7742_PD_CA15_SCU, + PD_CPU_NOCR }, + { "ca15-cpu3", 0x40, 3, R8A7742_PD_CA15_CPU3, R8A7742_PD_CA15_SCU, + PD_CPU_NOCR }, + { "ca7-scu", 0x100, 0, R8A7742_PD_CA7_SCU, R8A7742_PD_ALWAYS_ON, + PD_SCU }, + { "ca7-cpu0", 0x1c0, 0, R8A7742_PD_CA7_CPU0, R8A7742_PD_CA7_SCU, + PD_CPU_NOCR }, + { "ca7-cpu1", 0x1c0, 1, R8A7742_PD_CA7_CPU1, R8A7742_PD_CA7_SCU, + PD_CPU_NOCR }, + { "ca7-cpu2", 0x1c0, 2, R8A7742_PD_CA7_CPU2, R8A7742_PD_CA7_SCU, + PD_CPU_NOCR }, + { "ca7-cpu3", 0x1c0, 3, R8A7742_PD_CA7_CPU3, R8A7742_PD_CA7_SCU, + PD_CPU_NOCR }, + { "rgx", 0xc0, 0, R8A7742_PD_RGX, R8A7742_PD_ALWAYS_ON }, +}; + +const struct rcar_sysc_info r8a7742_sysc_info __initconst = { + .areas = r8a7742_areas, + .num_areas = ARRAY_SIZE(r8a7742_areas), +}; diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c index f0b291e02b8a..04ea87a188f1 100644 --- a/drivers/soc/renesas/rcar-sysc.c +++ b/drivers/soc/renesas/rcar-sysc.c @@ -273,6 +273,9 @@ finalize: } static const struct of_device_id rcar_sysc_matches[] __initconst = { +#ifdef CONFIG_SYSC_R8A7742 + { .compatible = "renesas,r8a7742-sysc", .data = &r8a7742_sysc_info }, +#endif #ifdef CONFIG_SYSC_R8A7743 { .compatible = "renesas,r8a7743-sysc", .data = &r8a7743_sysc_info }, /* RZ/G1N is identical to RZ/G2M w.r.t. power domains. */ diff --git a/drivers/soc/renesas/rcar-sysc.h b/drivers/soc/renesas/rcar-sysc.h index 0fc3b119930a..e417f26fe155 100644 --- a/drivers/soc/renesas/rcar-sysc.h +++ b/drivers/soc/renesas/rcar-sysc.h @@ -49,6 +49,7 @@ struct rcar_sysc_info { u32 extmask_val; /* SYSCEXTMASK register mask value */ }; +extern const struct rcar_sysc_info r8a7742_sysc_info; extern const struct rcar_sysc_info r8a7743_sysc_info; extern const struct rcar_sysc_info r8a7745_sysc_info; extern const struct rcar_sysc_info r8a77470_sysc_info; -- cgit From 2f71832755a9422f5a62a13ea3e805df7b173837 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Thu, 23 Apr 2020 22:40:45 +0100 Subject: soc: renesas: rcar-rst: Add support for RZ/G1H Add support for RZ/G1H (R8A7742) to the R-Car RST driver. Signed-off-by: Lad Prabhakar Reviewed-by: Marian-Cristian Rotariu Link: https://lore.kernel.org/r/1587678050-23468-6-git-send-email-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/soc/renesas/rcar-rst.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/rcar-rst.c b/drivers/soc/renesas/rcar-rst.c index 2af2e0dd83fe..a2b2b1768768 100644 --- a/drivers/soc/renesas/rcar-rst.c +++ b/drivers/soc/renesas/rcar-rst.c @@ -39,6 +39,7 @@ static const struct rst_config rcar_rst_gen3 __initconst = { static const struct of_device_id rcar_rst_matches[] __initconst = { /* RZ/G1 is handled like R-Car Gen2 */ + { .compatible = "renesas,r8a7742-rst", .data = &rcar_rst_gen2 }, { .compatible = "renesas,r8a7743-rst", .data = &rcar_rst_gen2 }, { .compatible = "renesas,r8a7744-rst", .data = &rcar_rst_gen2 }, { .compatible = "renesas,r8a7745-rst", .data = &rcar_rst_gen2 }, -- cgit From cdc8abe763c99a6c2b854d7096eaf1ea21017a42 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 29 Apr 2020 22:56:38 +0100 Subject: soc: renesas: Add Renesas R8A7742 config option Add configuration option for the RZ/G1H (R8A77420) SoC. Signed-off-by: Lad Prabhakar Reviewed-by: Marian-Cristian Rotariu Link: https://lore.kernel.org/r/1588197415-13747-2-git-send-email-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/soc/renesas/Kconfig | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 1eff82c3b934..53cd8d2d0cd2 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -83,6 +83,13 @@ config ARCH_R8A7740 select ARM_ERRATA_754322 select RENESAS_INTC_IRQPIN +config ARCH_R8A7742 + bool "RZ/G1H (R8A77420)" + select ARCH_RCAR_GEN2 + select ARM_ERRATA_798181 if SMP + select ARM_ERRATA_814220 + select SYSC_R8A7742 + config ARCH_R8A7743 bool "RZ/G1M (R8A77430)" select ARCH_RCAR_GEN2 -- cgit From 5098e2b95e8e6f56266c2d5c180c75917090082a Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Wed, 18 Mar 2020 15:25:08 +0000 Subject: soc/tegra: pmc: Select GENERIC_PINCONF I have hit the following build error: armv7a-hardfloat-linux-gnueabi-ld: drivers/soc/tegra/pmc.o: in function `pinconf_generic_dt_node_to_map_pin': pmc.c:(.text+0x500): undefined reference to `pinconf_generic_dt_node_to_map' armv7a-hardfloat-linux-gnueabi-ld: drivers/soc/tegra/pmc.o:(.rodata+0x1f88): undefined reference to `pinconf_generic_dt_free_map' So SOC_TEGRA_PMC should select GENERIC_PINCONF. Fixes: 4a37f11c8f57 ("soc/tegra: pmc: Implement pad configuration via pinctrl") Cc: stable Signed-off-by: Corentin Labbe Signed-off-by: Thierry Reding --- drivers/soc/tegra/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig index 3693532949b8..6bc603d0b9d9 100644 --- a/drivers/soc/tegra/Kconfig +++ b/drivers/soc/tegra/Kconfig @@ -133,6 +133,7 @@ config SOC_TEGRA_FLOWCTRL config SOC_TEGRA_PMC bool + select GENERIC_PINCONF config SOC_TEGRA_POWERGATE_BPMP def_bool y -- cgit From 09701895aa93df32cc708eab032ae8704feda333 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Thu, 7 May 2020 11:13:48 +0100 Subject: soc/tegra: pmc: Enable PMIC wake event on Tegra194 The PMIC wake event can be used to bring the system out of suspend based on certain events happening on the PMIC (such as an RTC alarm). Signed-off-by: Jon Hunter Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 72cfda7305d5..8169737c3252 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -3326,6 +3326,7 @@ static const char * const tegra194_reset_sources[] = { }; static const struct tegra_wake_event tegra194_wake_events[] = { + TEGRA_WAKE_IRQ("pmu", 24, 209), TEGRA_WAKE_GPIO("power", 29, 1, TEGRA194_AON_GPIO(EE, 4)), TEGRA_WAKE_IRQ("rtc", 73, 10), }; -- cgit From ce187859cea2004d9520de76948d6fc3e2f4b4bb Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Sun, 26 Apr 2020 22:42:02 -0700 Subject: soc: qcom: aoss: Add SM8250 compatible Add SM8250 compatible to the qcom_aoss binding and driver. Acked-by: Rob Herring Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20200427054202.2822144-1-bjorn.andersson@linaro.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/qcom_aoss.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c index f43a2e07ee83..ed2c687c16b3 100644 --- a/drivers/soc/qcom/qcom_aoss.c +++ b/drivers/soc/qcom/qcom_aoss.c @@ -599,6 +599,7 @@ static const struct of_device_id qmp_dt_match[] = { { .compatible = "qcom,sc7180-aoss-qmp", }, { .compatible = "qcom,sdm845-aoss-qmp", }, { .compatible = "qcom,sm8150-aoss-qmp", }, + { .compatible = "qcom,sm8250-aoss-qmp", }, {} }; MODULE_DEVICE_TABLE(of, qmp_dt_match); -- cgit From 8f09210d89e7a6feb07699b4e73a7c34f39554e4 Mon Sep 17 00:00:00 2001 From: Vincent Knecht Date: Mon, 11 May 2020 23:27:33 +0200 Subject: soc: qcom: socinfo: add msm8936/39 and apq8036/39 soc ids This patch adds missing SoC IDs for MSM8936/39 and their APQ variants. Signed-off-by: Vincent Knecht Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20200511212733.214464-1-konradybcio@gmail.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/socinfo.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 08a4b8ae1764..5983c6ffb078 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -188,6 +188,10 @@ static const struct soc_id soc_id[] = { { 216, "MSM8674PRO" }, { 217, "MSM8974-AA" }, { 218, "MSM8974-AB" }, + { 233, "MSM8936" }, + { 239, "MSM8939" }, + { 240, "APQ8036" }, + { 241, "APQ8039" }, { 246, "MSM8996" }, { 247, "APQ8016" }, { 248, "MSM8216" }, -- cgit From faa0c1f106efd6b7f61e9df95e27cc748a7123ec Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Wed, 15 Apr 2020 10:00:27 -0700 Subject: soc: qcom: rpmh-rsc: Factor "tcs_reg_addr" and "tcs_cmd_addr" calculation We can make some of the register access functions more readable by factoring out the calculations a little bit. Suggested-by: Joe Perches Signed-off-by: Douglas Anderson Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20200415095953.v3.1.Ic70288f256ff0be65cac6a600367212dfe39f6c9@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index a9e15699f55f..ce39d8399312 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -137,36 +137,47 @@ * +---------------------------------------------------+ */ -static u32 read_tcs_cmd(struct rsc_drv *drv, int reg, int tcs_id, int cmd_id) +static inline void __iomem * +tcs_reg_addr(const struct rsc_drv *drv, int reg, int tcs_id) { - return readl_relaxed(drv->tcs_base + RSC_DRV_TCS_OFFSET * tcs_id + reg + - RSC_DRV_CMD_OFFSET * cmd_id); + return drv->tcs_base + RSC_DRV_TCS_OFFSET * tcs_id + reg; } -static u32 read_tcs_reg(struct rsc_drv *drv, int reg, int tcs_id) +static inline void __iomem * +tcs_cmd_addr(const struct rsc_drv *drv, int reg, int tcs_id, int cmd_id) { - return readl_relaxed(drv->tcs_base + RSC_DRV_TCS_OFFSET * tcs_id + reg); + return tcs_reg_addr(drv, reg, tcs_id) + RSC_DRV_CMD_OFFSET * cmd_id; } -static void write_tcs_cmd(struct rsc_drv *drv, int reg, int tcs_id, int cmd_id, - u32 data) +static u32 read_tcs_cmd(const struct rsc_drv *drv, int reg, int tcs_id, + int cmd_id) +{ + return readl_relaxed(tcs_cmd_addr(drv, reg, tcs_id, cmd_id)); +} + +static u32 read_tcs_reg(const struct rsc_drv *drv, int reg, int tcs_id) { - writel_relaxed(data, drv->tcs_base + RSC_DRV_TCS_OFFSET * tcs_id + reg + - RSC_DRV_CMD_OFFSET * cmd_id); + return readl_relaxed(tcs_reg_addr(drv, reg, tcs_id)); } -static void write_tcs_reg(struct rsc_drv *drv, int reg, int tcs_id, u32 data) +static void write_tcs_cmd(const struct rsc_drv *drv, int reg, int tcs_id, + int cmd_id, u32 data) +{ + writel_relaxed(data, tcs_cmd_addr(drv, reg, tcs_id, cmd_id)); +} + +static void write_tcs_reg(const struct rsc_drv *drv, int reg, int tcs_id, + u32 data) { - writel_relaxed(data, drv->tcs_base + RSC_DRV_TCS_OFFSET * tcs_id + reg); + writel_relaxed(data, tcs_reg_addr(drv, reg, tcs_id)); } -static void write_tcs_reg_sync(struct rsc_drv *drv, int reg, int tcs_id, +static void write_tcs_reg_sync(const struct rsc_drv *drv, int reg, int tcs_id, u32 data) { - writel(data, drv->tcs_base + RSC_DRV_TCS_OFFSET * tcs_id + reg); + writel(data, tcs_reg_addr(drv, reg, tcs_id)); for (;;) { - if (data == readl(drv->tcs_base + reg + - RSC_DRV_TCS_OFFSET * tcs_id)) + if (data == readl(tcs_reg_addr(drv, reg, tcs_id))) break; udelay(1); } -- cgit From 91160150aba03d0c173b3f5c859a795cc701bb8d Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Wed, 15 Apr 2020 10:00:28 -0700 Subject: soc: qcom: rpmh-rsc: Timeout after 1 second in write_tcs_reg_sync() If our data still isn't there after 1 second, shout and give up. Reported-by: Joe Perches Signed-off-by: Douglas Anderson Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20200415095953.v3.2.I8550512081c89ec7a545018a7d2d9418a27c1a7a@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index ce39d8399312..e09d1ada0cd2 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -175,12 +176,13 @@ static void write_tcs_reg(const struct rsc_drv *drv, int reg, int tcs_id, static void write_tcs_reg_sync(const struct rsc_drv *drv, int reg, int tcs_id, u32 data) { + u32 new_data; + writel(data, tcs_reg_addr(drv, reg, tcs_id)); - for (;;) { - if (data == readl(tcs_reg_addr(drv, reg, tcs_id))) - break; - udelay(1); - } + if (readl_poll_timeout_atomic(tcs_reg_addr(drv, reg, tcs_id), new_data, + new_data == data, 1, USEC_PER_SEC)) + pr_err("%s: error writing %#x to %d:%#x\n", drv->name, + data, tcs_id, reg); } /** -- cgit From 1a6e55ca8f3684d7e1fe646396303233bf1c1ed7 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 6 May 2020 14:02:04 +0200 Subject: soc: mediatek: mmsys: Drop After the split, the mt8173 MMSYS driver is no longer a clock provider, and thus does not need to include . Fixes: 13032709e2328553 ("clk / soc: mediatek: Move mt8173 MMSYS to platform driver") Signed-off-by: Geert Uytterhoeven Reviewed-by: Enric Balletbo i Serra Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20200506120204.31422-1-geert+renesas@glider.be Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-mmsys.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-mmsys.c b/drivers/soc/mediatek/mtk-mmsys.c index 05e322c9c301..bb5ccd5b0386 100644 --- a/drivers/soc/mediatek/mtk-mmsys.c +++ b/drivers/soc/mediatek/mtk-mmsys.c @@ -4,7 +4,6 @@ * Author: James Liao */ -#include #include #include #include -- cgit From ff34e17cf9bce8b6d01c6a1e387720dc3393f45b Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 6 May 2020 14:13:17 +0000 Subject: soc: mediatek: Missing platform_device_unregister() on error in mtk_mmsys_probe() Add the missing platform_device_unregister() before return from mtk_mmsys_probe() in the error handling case. Fixes: 667c769246b0 ("soc / drm: mediatek: Fix mediatek-drm device probing") Signed-off-by: Wei Yongjun Reviewed-by: Enric Balletbo i Serra Link: https://lore.kernel.org/r/20200506141317.119537-1-weiyongjun1@huawei.com Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-mmsys.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-mmsys.c b/drivers/soc/mediatek/mtk-mmsys.c index bb5ccd5b0386..b0bb08a66b01 100644 --- a/drivers/soc/mediatek/mtk-mmsys.c +++ b/drivers/soc/mediatek/mtk-mmsys.c @@ -311,8 +311,10 @@ static int mtk_mmsys_probe(struct platform_device *pdev) drm = platform_device_register_data(&pdev->dev, "mediatek-drm", PLATFORM_DEVID_AUTO, NULL, 0); - if (IS_ERR(drm)) + if (IS_ERR(drm)) { + platform_device_unregister(clks); return PTR_ERR(drm); + } return 0; } -- cgit From 1143c36656b8dccf0ece93053502a147d3c60961 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 4 May 2020 10:50:15 -0700 Subject: soc: qcom: rpmh-rsc: Correctly ignore CPU_CLUSTER_PM notifications Our switch statement doesn't have entries for CPU_CLUSTER_PM_ENTER, CPU_CLUSTER_PM_ENTER_FAILED, and CPU_CLUSTER_PM_EXIT and doesn't have a default. This means that we'll try to do a flush in those cases but we won't necessarily be the last CPU down. That's not so ideal since our (lack of) locking assumes we're on the last CPU. Luckily this isn't as big a problem as you'd think since (at least on the SoC I tested) we don't get these notifications except on full system suspend. ...and on full system suspend we get them on the last CPU down. That means that the worst problem we hit is flushing twice. Still, it's good to make it correct. Reviewed-by: Stephen Boyd Fixes: 985427f997b6 ("soc: qcom: rpmh: Invoke rpmh_flush() for dirty caches") Reported-by: Stephen Boyd Signed-off-by: Douglas Anderson Link: https://lore.kernel.org/r/20200504104917.v6.1.Ic7096b3b9b7828cdd41cd5469a6dee5eb6abf549@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index e09d1ada0cd2..5a2659df98da 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -819,6 +819,9 @@ static int rpmh_rsc_cpu_pm_callback(struct notifier_block *nfb, case CPU_PM_EXIT: cpumask_clear_cpu(smp_processor_id(), &drv->cpus_entered_pm); goto exit; + default: + ret = NOTIFY_DONE; + goto exit; } ret = rpmh_rsc_ctrlr_is_busy(drv); -- cgit From c45def5d804abaa48c205f8ba5cd52bfbeeae70c Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 4 May 2020 10:50:16 -0700 Subject: soc: qcom: rpmh-rsc: We aren't notified of our own failure w/ NOTIFY_BAD When a PM Notifier returns NOTIFY_BAD it doesn't get called with CPU_PM_ENTER_FAILED. It only get called for CPU_PM_ENTER_FAILED if someone else (further down the notifier chain) returns NOTIFY_BAD. Handle this case by taking our CPU out of the list of ones that have entered PM. Without this it's possible we could detect that the last CPU went down (and we would flush) even if some CPU was alive. That's not good since our flushing routines currently assume they're running on the last CPU for mutual exclusion. Fixes: 985427f997b6 ("soc: qcom: rpmh: Invoke rpmh_flush() for dirty caches") Signed-off-by: Douglas Anderson Reviewed-by: Maulik Shah Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20200504104917.v6.2.I1927d1bca2569a27b2d04986baf285027f0818a2@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 5a2659df98da..a5659a7306e4 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -837,6 +837,10 @@ static int rpmh_rsc_cpu_pm_callback(struct notifier_block *nfb, ret = NOTIFY_OK; exit: + if (ret == NOTIFY_BAD) + /* We won't be called w/ CPU_PM_ENTER_FAILED */ + cpumask_clear_cpu(smp_processor_id(), &drv->cpus_entered_pm); + spin_unlock(&drv->pm_lock); return ret; } -- cgit From 555701a45f146673c8961f084b6880c637d41129 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 4 May 2020 10:50:18 -0700 Subject: soc: qcom: rpmh-rsc: Simplify locking by eliminating the per-TCS lock The rpmh-rsc code had both a driver-level lock (sometimes referred to in comments as drv->lock) and a lock per-TCS. The idea was supposed to be that there would be times where you could get by with just locking a TCS lock and therefor other RPMH users wouldn't be blocked. The above didn't work out so well. Looking at tcs_write() the bigger drv->lock was held for most of the function anyway. Only the __tcs_buffer_write() and __tcs_set_trigger() calls were called without holding the drv->lock. It actually turns out that in tcs_write() we don't need to hold the drv->lock for those function calls anyway even if the per-TCS lock isn't there anymore. From the newly added comments in the code, this is because: - We marked "tcs_in_use" under lock. - Once "tcs_in_use" has been marked nobody else could be writing to these registers until the interrupt goes off. - The interrupt can't go off until we trigger w/ the last line of __tcs_set_trigger(). Thus, from a tcs_write() point of view, the per-TCS lock was useless. Looking at rpmh_rsc_write_ctrl_data(), only the per-TCS lock was held. It turns out, though, that this function already needs to be called with the equivalent of the drv->lock held anyway (we either need to hold drv->lock as we will in a future patch or we need to know no other CPUs could be running as happens today). Specifically rpmh_rsc_write_ctrl_data() might be writing to a TCS that has been borrowed for writing an active transation but it never checks this. Let's eliminate this extra overhead and avoid possible AB BA locking headaches. Suggested-by: Maulik Shah Signed-off-by: Douglas Anderson Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20200504104917.v6.4.Ib8dccfdb10bf6b1fb1d600ca1c21d9c0db1ef746@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-internal.h | 13 +++------- drivers/soc/qcom/rpmh-rsc.c | 55 ++++++++++++++++++---------------------- 2 files changed, 28 insertions(+), 40 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h index dba8510c0669..1f2857b3f38e 100644 --- a/drivers/soc/qcom/rpmh-internal.h +++ b/drivers/soc/qcom/rpmh-internal.h @@ -28,7 +28,6 @@ struct rsc_drv; * @offset: Start of the TCS group relative to the TCSes in the RSC. * @num_tcs: Number of TCSes in this type. * @ncpt: Number of commands in each TCS. - * @lock: Lock for synchronizing this TCS writes. * @req: Requests that are sent from the TCS; only used for ACTIVE_ONLY * transfers (could be on a wake/sleep TCS if we are borrowing for * an ACTIVE_ONLY transfer). @@ -48,7 +47,6 @@ struct tcs_group { u32 offset; int num_tcs; int ncpt; - spinlock_t lock; const struct tcs_request *req[MAX_TCS_PER_TYPE]; DECLARE_BITMAP(slots, MAX_TCS_SLOTS); }; @@ -103,14 +101,9 @@ struct rpmh_ctrlr { * @tcs_in_use: S/W state of the TCS; only set for ACTIVE_ONLY * transfers, but might show a sleep/wake TCS in use if * it was borrowed for an active_only transfer. You - * must hold both the lock in this struct and the - * tcs_lock for the TCS in order to mark a TCS as - * in-use, but you only need the lock in this structure - * (aka the drv->lock) to mark one freed. - * @lock: Synchronize state of the controller. If you will be - * grabbing this lock and a tcs_lock at the same time, - * grab the tcs_lock first so we always have a - * consistent lock ordering. + * must hold the lock in this struct (AKA drv->lock) in + * order to update this. + * @lock: Synchronize state of the controller. * @pm_lock: Synchronize during PM notifications. * Used when solver mode is not present. * @client: Handle to the DRV's client. diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index a5659a7306e4..fb142dfbb237 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -192,11 +192,7 @@ static void write_tcs_reg_sync(const struct rsc_drv *drv, int reg, int tcs_id, * * Returns true if nobody has claimed this TCS (by setting tcs_in_use). * - * Context: Must be called with the drv->lock held or the tcs_lock for the TCS - * being tested. If only the tcs_lock is held then it is possible that - * this function will return that a tcs is still busy when it has been - * recently been freed but it will never return free when a TCS is - * actually in use. + * Context: Must be called with the drv->lock held. * * Return: true if the given TCS is free. */ @@ -255,8 +251,6 @@ void rpmh_rsc_invalidate(struct rsc_drv *drv) * This is normally pretty straightforward except if we are trying to send * an ACTIVE_ONLY message but don't have any active_only TCSes. * - * Called without drv->lock held and with no tcs_lock locks held. - * * Return: A pointer to a tcs_group or an ERR_PTR. */ static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv, @@ -594,24 +588,19 @@ static int tcs_write(struct rsc_drv *drv, const struct tcs_request *msg) if (IS_ERR(tcs)) return PTR_ERR(tcs); - spin_lock_irqsave(&tcs->lock, flags); - spin_lock(&drv->lock); + spin_lock_irqsave(&drv->lock, flags); /* * The h/w does not like if we send a request to the same address, * when one is already in-flight or being processed. */ ret = check_for_req_inflight(drv, tcs, msg); - if (ret) { - spin_unlock(&drv->lock); - goto done_write; - } + if (ret) + goto unlock; - tcs_id = find_free_tcs(tcs); - if (tcs_id < 0) { - ret = tcs_id; - spin_unlock(&drv->lock); - goto done_write; - } + ret = find_free_tcs(tcs); + if (ret < 0) + goto unlock; + tcs_id = ret; tcs->req[tcs_id - tcs->offset] = msg; set_bit(tcs_id, drv->tcs_in_use); @@ -625,13 +614,22 @@ static int tcs_write(struct rsc_drv *drv, const struct tcs_request *msg) write_tcs_reg_sync(drv, RSC_DRV_CMD_WAIT_FOR_CMPL, tcs_id, 0); enable_tcs_irq(drv, tcs_id, true); } - spin_unlock(&drv->lock); + spin_unlock_irqrestore(&drv->lock, flags); + /* + * These two can be done after the lock is released because: + * - We marked "tcs_in_use" under lock. + * - Once "tcs_in_use" has been marked nobody else could be writing + * to these registers until the interrupt goes off. + * - The interrupt can't go off until we trigger w/ the last line + * of __tcs_set_trigger() below. + */ __tcs_buffer_write(drv, tcs_id, 0, msg); __tcs_set_trigger(drv, tcs_id, true); -done_write: - spin_unlock_irqrestore(&tcs->lock, flags); + return 0; +unlock: + spin_unlock_irqrestore(&drv->lock, flags); return ret; } @@ -686,8 +684,6 @@ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) * Only for use on sleep/wake TCSes since those are the only ones we maintain * tcs->slots for. * - * Must be called with the tcs_lock for the group held. - * * Return: -ENOMEM if there was no room, else 0. */ static int find_slots(struct tcs_group *tcs, const struct tcs_request *msg, @@ -722,25 +718,25 @@ static int find_slots(struct tcs_group *tcs, const struct tcs_request *msg, * This should only be called for for sleep/wake state, never active-only * state. * + * The caller must ensure that no other RPMH actions are happening and the + * controller is idle when this function is called since it runs lockless. + * * Return: 0 if no error; else -error. */ int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg) { struct tcs_group *tcs; int tcs_id = 0, cmd_id = 0; - unsigned long flags; int ret; tcs = get_tcs_for_msg(drv, msg); if (IS_ERR(tcs)) return PTR_ERR(tcs); - spin_lock_irqsave(&tcs->lock, flags); /* find the TCS id and the command in the TCS to write to */ ret = find_slots(tcs, msg, &tcs_id, &cmd_id); if (!ret) __tcs_buffer_write(drv, tcs_id, cmd_id, msg); - spin_unlock_irqrestore(&tcs->lock, flags); return ret; } @@ -769,8 +765,8 @@ static bool rpmh_rsc_ctrlr_is_busy(struct rsc_drv *drv) * should be checked for not busy, because we used wake TCSes for * active requests in this case. * - * Since this is called from the last cpu, need not take drv or tcs - * lock before checking tcs_is_free(). + * Since this is called from the last cpu, need not take drv->lock + * before checking tcs_is_free(). */ if (!tcs->num_tcs) tcs = &drv->tcs[WAKE_TCS]; @@ -899,7 +895,6 @@ static int rpmh_probe_tcs_config(struct platform_device *pdev, tcs->type = tcs_cfg[i].type; tcs->num_tcs = tcs_cfg[i].n; tcs->ncpt = ncpt; - spin_lock_init(&tcs->lock); if (!tcs->num_tcs || tcs->type == CONTROL_TCS) continue; -- cgit From d2a8cfc6f320263b90ca523590a339661d0f4fae Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 4 May 2020 10:50:19 -0700 Subject: soc: qcom: rpmh-rsc: Remove the pm_lock It has been postulated that the pm_lock is bad for performance because a CPU currently running rpmh_flush() could block other CPUs from coming out of idle. Similarly CPUs coming out of / going into idle all need to contend with each other for the spinlock just to update the variable tracking who's in PM. Let's optimize this a bit. Specifically: - Use a count rather than a bitmask. This is faster to access and also means we can use the atomic_inc_return() function to really detect who the last one to enter PM was. - Accept that it's OK if we race and are doing the flush (because we think we're last) while another CPU is coming out of idle. As long as we block that CPU if/when it tries to do an active-only transfer we're OK. Signed-off-by: Douglas Anderson Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20200504104917.v6.5.I295cb72bc5334a2af80313cbe97cb5c9dcb1442c@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-internal.h | 11 +++--- drivers/soc/qcom/rpmh-rsc.c | 75 ++++++++++++++++++++++++---------------- drivers/soc/qcom/rpmh.c | 25 +++++++++----- 3 files changed, 67 insertions(+), 44 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h index 1f2857b3f38e..ef60e790a750 100644 --- a/drivers/soc/qcom/rpmh-internal.h +++ b/drivers/soc/qcom/rpmh-internal.h @@ -95,7 +95,7 @@ struct rpmh_ctrlr { * @num_tcs: Number of TCSes in this DRV. * @rsc_pm: CPU PM notifier for controller. * Used when solver mode is not present. - * @cpus_entered_pm: CPU mask for cpus in idle power collapse. + * @cpus_in_pm: Number of CPUs not in idle power collapse. * Used when solver mode is not present. * @tcs: TCS groups. * @tcs_in_use: S/W state of the TCS; only set for ACTIVE_ONLY @@ -103,9 +103,9 @@ struct rpmh_ctrlr { * it was borrowed for an active_only transfer. You * must hold the lock in this struct (AKA drv->lock) in * order to update this. - * @lock: Synchronize state of the controller. - * @pm_lock: Synchronize during PM notifications. - * Used when solver mode is not present. + * @lock: Synchronize state of the controller. If RPMH's cache + * lock will also be held, the order is: drv->lock then + * cache_lock. * @client: Handle to the DRV's client. */ struct rsc_drv { @@ -114,11 +114,10 @@ struct rsc_drv { int id; int num_tcs; struct notifier_block rsc_pm; - struct cpumask cpus_entered_pm; + atomic_t cpus_in_pm; struct tcs_group tcs[TCS_TYPE_NR]; DECLARE_BITMAP(tcs_in_use, MAX_TCS_NR); spinlock_t lock; - spinlock_t pm_lock; struct rpmh_ctrlr client; }; diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index fb142dfbb237..237d7d5cc8a8 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -750,6 +750,8 @@ int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg) * SLEEP and WAKE sets. If AMCs are busy, controller can not enter * power collapse, so deny from the last cpu's pm notification. * + * Context: Must be called with the drv->lock held. + * * Return: * * False - AMCs are idle * * True - AMCs are busy @@ -764,9 +766,6 @@ static bool rpmh_rsc_ctrlr_is_busy(struct rsc_drv *drv) * dedicated TCS for active state use, then re-purposed wake TCSes * should be checked for not busy, because we used wake TCSes for * active requests in this case. - * - * Since this is called from the last cpu, need not take drv->lock - * before checking tcs_is_free(). */ if (!tcs->num_tcs) tcs = &drv->tcs[WAKE_TCS]; @@ -801,43 +800,62 @@ static int rpmh_rsc_cpu_pm_callback(struct notifier_block *nfb, { struct rsc_drv *drv = container_of(nfb, struct rsc_drv, rsc_pm); int ret = NOTIFY_OK; - - spin_lock(&drv->pm_lock); + int cpus_in_pm; switch (action) { case CPU_PM_ENTER: - cpumask_set_cpu(smp_processor_id(), &drv->cpus_entered_pm); - - if (!cpumask_equal(&drv->cpus_entered_pm, cpu_online_mask)) - goto exit; + cpus_in_pm = atomic_inc_return(&drv->cpus_in_pm); + /* + * NOTE: comments for num_online_cpus() point out that it's + * only a snapshot so we need to be careful. It should be OK + * for us to use, though. It's important for us not to miss + * if we're the last CPU going down so it would only be a + * problem if a CPU went offline right after we did the check + * AND that CPU was not idle AND that CPU was the last non-idle + * CPU. That can't happen. CPUs would have to come out of idle + * before the CPU could go offline. + */ + if (cpus_in_pm < num_online_cpus()) + return NOTIFY_OK; break; case CPU_PM_ENTER_FAILED: case CPU_PM_EXIT: - cpumask_clear_cpu(smp_processor_id(), &drv->cpus_entered_pm); - goto exit; + atomic_dec(&drv->cpus_in_pm); + return NOTIFY_OK; default: - ret = NOTIFY_DONE; - goto exit; + return NOTIFY_DONE; } - ret = rpmh_rsc_ctrlr_is_busy(drv); - if (ret) { - ret = NOTIFY_BAD; - goto exit; + /* + * It's likely we're on the last CPU. Grab the drv->lock and write + * out the sleep/wake commands to RPMH hardware. Grabbing the lock + * means that if we race with another CPU coming up we are still + * guaranteed to be safe. If another CPU came up just after we checked + * and has grabbed the lock or started an active transfer then we'll + * notice we're busy and abort. If another CPU comes up after we start + * flushing it will be blocked from starting an active transfer until + * we're done flushing. If another CPU starts an active transfer after + * we release the lock we're still OK because we're no longer the last + * CPU. + */ + if (spin_trylock(&drv->lock)) { + if (rpmh_rsc_ctrlr_is_busy(drv) || rpmh_flush(&drv->client)) + ret = NOTIFY_BAD; + spin_unlock(&drv->lock); + } else { + /* Another CPU must be up */ + return NOTIFY_OK; } - ret = rpmh_flush(&drv->client); - if (ret) - ret = NOTIFY_BAD; - else - ret = NOTIFY_OK; - -exit: - if (ret == NOTIFY_BAD) - /* We won't be called w/ CPU_PM_ENTER_FAILED */ - cpumask_clear_cpu(smp_processor_id(), &drv->cpus_entered_pm); + if (ret == NOTIFY_BAD) { + /* Double-check if we're here because someone else is up */ + if (cpus_in_pm < num_online_cpus()) + ret = NOTIFY_OK; + else + /* We won't be called w/ CPU_PM_ENTER_FAILED */ + atomic_dec(&drv->cpus_in_pm); + } - spin_unlock(&drv->pm_lock); return ret; } @@ -980,7 +998,6 @@ static int rpmh_rsc_probe(struct platform_device *pdev) solver_config = solver_config >> DRV_HW_SOLVER_SHIFT; if (!solver_config) { drv->rsc_pm.notifier_call = rpmh_rsc_cpu_pm_callback; - spin_lock_init(&drv->pm_lock); cpu_pm_register_notifier(&drv->rsc_pm); } diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index d1626a1328d7..f2b5b46ccd1f 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -435,9 +435,6 @@ static int send_single(struct rpmh_ctrlr *ctrlr, enum rpmh_state state, * * @ctrlr: Controller making request to flush cached data * - * This function is called from sleep code on the last CPU - * (thus no spinlock needed). - * * Return: * * 0 - Success * * Error code - Otherwise @@ -445,13 +442,21 @@ static int send_single(struct rpmh_ctrlr *ctrlr, enum rpmh_state state, int rpmh_flush(struct rpmh_ctrlr *ctrlr) { struct cache_req *p; - int ret; + int ret = 0; lockdep_assert_irqs_disabled(); + /* + * Currently rpmh_flush() is only called when we think we're running + * on the last processor. If the lock is busy it means another + * processor is up and it's better to abort than spin. + */ + if (!spin_trylock(&ctrlr->cache_lock)) + return -EBUSY; + if (!ctrlr->dirty) { pr_debug("Skipping flush, TCS has latest data.\n"); - return 0; + goto exit; } /* Invalidate the TCSes first to avoid stale data */ @@ -460,7 +465,7 @@ int rpmh_flush(struct rpmh_ctrlr *ctrlr) /* First flush the cached batch requests */ ret = flush_batch(ctrlr); if (ret) - return ret; + goto exit; list_for_each_entry(p, &ctrlr->cache, list) { if (!is_req_valid(p)) { @@ -471,16 +476,18 @@ int rpmh_flush(struct rpmh_ctrlr *ctrlr) ret = send_single(ctrlr, RPMH_SLEEP_STATE, p->addr, p->sleep_val); if (ret) - return ret; + goto exit; ret = send_single(ctrlr, RPMH_WAKE_ONLY_STATE, p->addr, p->wake_val); if (ret) - return ret; + goto exit; } ctrlr->dirty = false; - return 0; +exit: + spin_unlock(&ctrlr->cache_lock); + return ret; } /** -- cgit From 1f7a3eb785e4a4e196729cd3d5ec97bd5f9f2940 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Sun, 17 May 2020 23:10:41 -0700 Subject: Revert "soc: qcom: rpmh: Allow RPMH driver to be loaded as a module" Attempting to compile rpmh-rsc.c as a module with TRACING enabled causes a build error as no _rcuidle function is generated for tracepoints when CONFIG_MODULE is set. Attempts has been made, but no resolution has been agreed upon, so lets revert this commit for now. This reverts commit 1d3c6f86fd3f8b88c707f56d8c3f94e014b40e83. Reported-by: Stephen Rothwell Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/Kconfig | 2 +- drivers/soc/qcom/rpmh-rsc.c | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 0d0123f6ec0a..19332ea40234 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -107,7 +107,7 @@ config QCOM_RMTFS_MEM Say y here if you intend to boot the modem remoteproc. config QCOM_RPMH - tristate "Qualcomm RPM-Hardened (RPMH) Communication" + bool "Qualcomm RPM-Hardened (RPMH) Communication" depends on ARCH_QCOM && ARM64 || COMPILE_TEST help Support for communication with the hardened-RPM blocks in diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 237d7d5cc8a8..076fd27f3081 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -1018,7 +1017,6 @@ static const struct of_device_id rpmh_drv_match[] = { { .compatible = "qcom,rpmh-rsc", }, { } }; -MODULE_DEVICE_TABLE(of, rpmh_drv_match); static struct platform_driver rpmh_driver = { .probe = rpmh_rsc_probe, @@ -1033,6 +1031,3 @@ static int __init rpmh_driver_init(void) return platform_driver_register(&rpmh_driver); } arch_initcall(rpmh_driver_init); - -MODULE_DESCRIPTION("Qualcomm Technologies, Inc. RPMh Driver"); -MODULE_LICENSE("GPL v2"); -- cgit From 34217df28db7cddb52120da57fd1d469e896af3d Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Fri, 15 May 2020 22:47:08 +0200 Subject: soc: amlogic: meson-ee-pwrc: add support for Meson8/Meson8b/Meson8m2 This adds support for the power domains on Meson8/Meson8b/Meson8m2. Meson8 doesn't use any reset lines while Meson8b and Meson8m2 use the same set of reset lines (which is different from the newer SoCs). Add dedicated compatible strings for Meson8, Meson8b and Meson8m2 to support these differences. Notable differences between Meson8 and G12A are: - there is no HHI_VPU_MEM_PD_REG2 on the 32-bit SoCs - the Meson8b datasheet describes an "audio DSP memory" power domain which is used for the hardware audio decoder - the "amlogic,ao-sysctrl" only includes the power management related registers on the 32-bit SoCs, meaning the for example the AO_RTI_GEN_PWR_SLEEP0 register is at offset (0x2 << 2) rather than (0x3a << 2). As result of this (0x38 << 2) is subtracted from the register offsets, which is the start of the power management related registers. Signed-off-by: Martin Blumenstingl Signed-off-by: Kevin Hilman Link: https://lore.kernel.org/r/20200515204709.1505498-4-martin.blumenstingl@googlemail.com --- drivers/soc/amlogic/meson-ee-pwrc.c | 86 +++++++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 9 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/amlogic/meson-ee-pwrc.c b/drivers/soc/amlogic/meson-ee-pwrc.c index 3f0261d53ad9..390eb0dd0a79 100644 --- a/drivers/soc/amlogic/meson-ee-pwrc.c +++ b/drivers/soc/amlogic/meson-ee-pwrc.c @@ -14,13 +14,22 @@ #include #include #include +#include #include #include /* AO Offsets */ -#define AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2) -#define AO_RTI_GEN_PWR_ISO0 (0x3b << 2) +#define GX_AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2) +#define GX_AO_RTI_GEN_PWR_ISO0 (0x3b << 2) + +/* + * Meson8/Meson8b/Meson8m2 only expose the power management registers of the + * AO-bus as syscon. 0x3a from GX translates to 0x02, 0x3b translates to 0x03 + * and so on. + */ +#define MESON8_AO_RTI_GEN_PWR_SLEEP0 (0x02 << 2) +#define MESON8_AO_RTI_GEN_PWR_ISO0 (0x03 << 2) /* HHI Offsets */ @@ -67,17 +76,24 @@ struct meson_ee_pwrc_domain_data { /* TOP Power Domains */ static struct meson_ee_pwrc_top_domain g12a_pwrc_vpu = { - .sleep_reg = AO_RTI_GEN_PWR_SLEEP0, + .sleep_reg = GX_AO_RTI_GEN_PWR_SLEEP0, + .sleep_mask = BIT(8), + .iso_reg = GX_AO_RTI_GEN_PWR_SLEEP0, + .iso_mask = BIT(9), +}; + +static struct meson_ee_pwrc_top_domain meson8_pwrc_vpu = { + .sleep_reg = MESON8_AO_RTI_GEN_PWR_SLEEP0, .sleep_mask = BIT(8), - .iso_reg = AO_RTI_GEN_PWR_SLEEP0, + .iso_reg = MESON8_AO_RTI_GEN_PWR_SLEEP0, .iso_mask = BIT(9), }; #define SM1_EE_PD(__bit) \ { \ - .sleep_reg = AO_RTI_GEN_PWR_SLEEP0, \ + .sleep_reg = GX_AO_RTI_GEN_PWR_SLEEP0, \ .sleep_mask = BIT(__bit), \ - .iso_reg = AO_RTI_GEN_PWR_ISO0, \ + .iso_reg = GX_AO_RTI_GEN_PWR_ISO0, \ .iso_mask = BIT(__bit), \ } @@ -124,10 +140,20 @@ static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_vpu[] = { VPU_HHI_MEMPD(HHI_MEM_PD_REG0), }; -static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_eth[] = { +static struct meson_ee_pwrc_mem_domain meson_pwrc_mem_eth[] = { { HHI_MEM_PD_REG0, GENMASK(3, 2) }, }; +static struct meson_ee_pwrc_mem_domain meson8_pwrc_audio_dsp_mem[] = { + { HHI_MEM_PD_REG0, GENMASK(1, 0) }, +}; + +static struct meson_ee_pwrc_mem_domain meson8_pwrc_mem_vpu[] = { + VPU_MEMPD(HHI_VPU_MEM_PD_REG0), + VPU_MEMPD(HHI_VPU_MEM_PD_REG1), + VPU_HHI_MEMPD(HHI_MEM_PD_REG0), +}; + static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_vpu[] = { VPU_MEMPD(HHI_VPU_MEM_PD_REG0), VPU_MEMPD(HHI_VPU_MEM_PD_REG1), @@ -201,7 +227,27 @@ static bool pwrc_ee_get_power(struct meson_ee_pwrc_domain *pwrc_domain); static struct meson_ee_pwrc_domain_desc g12a_pwrc_domains[] = { [PWRC_G12A_VPU_ID] = VPU_PD("VPU", &g12a_pwrc_vpu, g12a_pwrc_mem_vpu, pwrc_ee_get_power, 11, 2), - [PWRC_G12A_ETH_ID] = MEM_PD("ETH", g12a_pwrc_mem_eth), + [PWRC_G12A_ETH_ID] = MEM_PD("ETH", meson_pwrc_mem_eth), +}; + +static struct meson_ee_pwrc_domain_desc meson8_pwrc_domains[] = { + [PWRC_MESON8_VPU_ID] = VPU_PD("VPU", &meson8_pwrc_vpu, + meson8_pwrc_mem_vpu, pwrc_ee_get_power, + 0, 1), + [PWRC_MESON8_ETHERNET_MEM_ID] = MEM_PD("ETHERNET_MEM", + meson_pwrc_mem_eth), + [PWRC_MESON8_AUDIO_DSP_MEM_ID] = MEM_PD("AUDIO_DSP_MEM", + meson8_pwrc_audio_dsp_mem), +}; + +static struct meson_ee_pwrc_domain_desc meson8b_pwrc_domains[] = { + [PWRC_MESON8_VPU_ID] = VPU_PD("VPU", &meson8_pwrc_vpu, + meson8_pwrc_mem_vpu, pwrc_ee_get_power, + 11, 1), + [PWRC_MESON8_ETHERNET_MEM_ID] = MEM_PD("ETHERNET_MEM", + meson_pwrc_mem_eth), + [PWRC_MESON8_AUDIO_DSP_MEM_ID] = MEM_PD("AUDIO_DSP_MEM", + meson8_pwrc_audio_dsp_mem), }; static struct meson_ee_pwrc_domain_desc sm1_pwrc_domains[] = { @@ -216,7 +262,7 @@ static struct meson_ee_pwrc_domain_desc sm1_pwrc_domains[] = { [PWRC_SM1_GE2D_ID] = TOP_PD("GE2D", &sm1_pwrc_ge2d, sm1_pwrc_mem_ge2d, pwrc_ee_get_power), [PWRC_SM1_AUDIO_ID] = MEM_PD("AUDIO", sm1_pwrc_mem_audio), - [PWRC_SM1_ETH_ID] = MEM_PD("ETH", g12a_pwrc_mem_eth), + [PWRC_SM1_ETH_ID] = MEM_PD("ETH", meson_pwrc_mem_eth), }; struct meson_ee_pwrc_domain { @@ -470,12 +516,34 @@ static struct meson_ee_pwrc_domain_data meson_ee_g12a_pwrc_data = { .domains = g12a_pwrc_domains, }; +static struct meson_ee_pwrc_domain_data meson_ee_m8_pwrc_data = { + .count = ARRAY_SIZE(meson8_pwrc_domains), + .domains = meson8_pwrc_domains, +}; + +static struct meson_ee_pwrc_domain_data meson_ee_m8b_pwrc_data = { + .count = ARRAY_SIZE(meson8b_pwrc_domains), + .domains = meson8b_pwrc_domains, +}; + static struct meson_ee_pwrc_domain_data meson_ee_sm1_pwrc_data = { .count = ARRAY_SIZE(sm1_pwrc_domains), .domains = sm1_pwrc_domains, }; static const struct of_device_id meson_ee_pwrc_match_table[] = { + { + .compatible = "amlogic,meson8-pwrc", + .data = &meson_ee_m8_pwrc_data, + }, + { + .compatible = "amlogic,meson8b-pwrc", + .data = &meson_ee_m8b_pwrc_data, + }, + { + .compatible = "amlogic,meson8m2-pwrc", + .data = &meson_ee_m8b_pwrc_data, + }, { .compatible = "amlogic,meson-g12a-pwrc", .data = &meson_ee_g12a_pwrc_data, -- cgit From 53773f2dfd9c847304b184d5617e36aeafdf5d87 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Fri, 15 May 2020 22:47:09 +0200 Subject: soc: amlogic: meson-ee-pwrc: add support for the Meson GX SoCs Add support for the Meson GX SoCs to the meson-ee-pwrc driver. The power domains on the GX SoCs are very similar to G12A. The only known differences so far are: - The GX SoCs do not have the HHI_VPU_MEM_PD_REG2 register (for the VPU power-domain) - The GX SoCs have an additional reset line called "dvin" Signed-off-by: Martin Blumenstingl Signed-off-by: Kevin Hilman Reviewed-by: Neil Armstrong Link: https://lore.kernel.org/r/20200515204709.1505498-5-martin.blumenstingl@googlemail.com --- drivers/soc/amlogic/meson-ee-pwrc.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/amlogic/meson-ee-pwrc.c b/drivers/soc/amlogic/meson-ee-pwrc.c index 390eb0dd0a79..43665b77aa9e 100644 --- a/drivers/soc/amlogic/meson-ee-pwrc.c +++ b/drivers/soc/amlogic/meson-ee-pwrc.c @@ -16,6 +16,7 @@ #include #include #include +#include #include /* AO Offsets */ @@ -75,7 +76,7 @@ struct meson_ee_pwrc_domain_data { /* TOP Power Domains */ -static struct meson_ee_pwrc_top_domain g12a_pwrc_vpu = { +static struct meson_ee_pwrc_top_domain gx_pwrc_vpu = { .sleep_reg = GX_AO_RTI_GEN_PWR_SLEEP0, .sleep_mask = BIT(8), .iso_reg = GX_AO_RTI_GEN_PWR_SLEEP0, @@ -140,6 +141,12 @@ static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_vpu[] = { VPU_HHI_MEMPD(HHI_MEM_PD_REG0), }; +static struct meson_ee_pwrc_mem_domain gxbb_pwrc_mem_vpu[] = { + VPU_MEMPD(HHI_VPU_MEM_PD_REG0), + VPU_MEMPD(HHI_VPU_MEM_PD_REG1), + VPU_HHI_MEMPD(HHI_MEM_PD_REG0), +}; + static struct meson_ee_pwrc_mem_domain meson_pwrc_mem_eth[] = { { HHI_MEM_PD_REG0, GENMASK(3, 2) }, }; @@ -225,11 +232,17 @@ static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_audio[] = { static bool pwrc_ee_get_power(struct meson_ee_pwrc_domain *pwrc_domain); static struct meson_ee_pwrc_domain_desc g12a_pwrc_domains[] = { - [PWRC_G12A_VPU_ID] = VPU_PD("VPU", &g12a_pwrc_vpu, g12a_pwrc_mem_vpu, + [PWRC_G12A_VPU_ID] = VPU_PD("VPU", &gx_pwrc_vpu, g12a_pwrc_mem_vpu, pwrc_ee_get_power, 11, 2), [PWRC_G12A_ETH_ID] = MEM_PD("ETH", meson_pwrc_mem_eth), }; +static struct meson_ee_pwrc_domain_desc gxbb_pwrc_domains[] = { + [PWRC_GXBB_VPU_ID] = VPU_PD("VPU", &gx_pwrc_vpu, gxbb_pwrc_mem_vpu, + pwrc_ee_get_power, 12, 2), + [PWRC_GXBB_ETHERNET_MEM_ID] = MEM_PD("ETH", meson_pwrc_mem_eth), +}; + static struct meson_ee_pwrc_domain_desc meson8_pwrc_domains[] = { [PWRC_MESON8_VPU_ID] = VPU_PD("VPU", &meson8_pwrc_vpu, meson8_pwrc_mem_vpu, pwrc_ee_get_power, @@ -516,6 +529,11 @@ static struct meson_ee_pwrc_domain_data meson_ee_g12a_pwrc_data = { .domains = g12a_pwrc_domains, }; +static struct meson_ee_pwrc_domain_data meson_ee_gxbb_pwrc_data = { + .count = ARRAY_SIZE(gxbb_pwrc_domains), + .domains = gxbb_pwrc_domains, +}; + static struct meson_ee_pwrc_domain_data meson_ee_m8_pwrc_data = { .count = ARRAY_SIZE(meson8_pwrc_domains), .domains = meson8_pwrc_domains, @@ -544,6 +562,10 @@ static const struct of_device_id meson_ee_pwrc_match_table[] = { .compatible = "amlogic,meson8m2-pwrc", .data = &meson_ee_m8b_pwrc_data, }, + { + .compatible = "amlogic,meson-gxbb-pwrc", + .data = &meson_ee_gxbb_pwrc_data, + }, { .compatible = "amlogic,meson-g12a-pwrc", .data = &meson_ee_g12a_pwrc_data, -- cgit From 52660e568518b07daf15d117ef5a704909ea2513 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Wed, 1 Apr 2020 22:17:33 +0200 Subject: soc: mediatek: Enable mmsys driver by default if Mediatek arch is selected The mmsys driver supports only MT8173 device for now, but like other system controllers is an important piece for other Mediatek devices. Actually it depends on the mt8173 clock specific driver but that dependency is not real as it can build without the clock driver. Instead of depends on a specific model, make the driver depends on the generic ARCH_MEDIATEK and enable by default so other Mediatek devices can start using it without flood the Kconfig. Signed-off-by: Enric Balletbo i Serra Tested-by: Hsin-Yi Wang Link: https://lore.kernel.org/r/20200401201736.2980433-1-enric.balletbo@collabora.com Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/Kconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig index e84513318725..59a56cd790ec 100644 --- a/drivers/soc/mediatek/Kconfig +++ b/drivers/soc/mediatek/Kconfig @@ -46,8 +46,7 @@ config MTK_SCPSYS config MTK_MMSYS bool "MediaTek MMSYS Support" - depends on COMMON_CLK_MT8173_MMSYS - default COMMON_CLK_MT8173_MMSYS + default ARCH_MEDIATEK help Say yes here to add support for the MediaTek Multimedia Subsystem (MMSYS). -- cgit From 9c5a0a3a8f0403c911ed4f02d4f4a81fe3c22889 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Wed, 1 Apr 2020 22:17:34 +0200 Subject: clk / soc: mediatek: Bind clock and gpu driver for mt2712 Now that the mmsys driver is the top-level entry point for the multimedia subsystem, we could bind the clock and the gpu driver on those devices that is expected to work, so the drm driver is intantiated by the mmsys driver and display, hopefully, working again on those devices. Signed-off-by: Enric Balletbo i Serra Reviewed-by: Chun-Kuang Hu Acked-by: Stephen Boyd Link: https://lore.kernel.org/r/20200401201736.2980433-2-enric.balletbo@collabora.com Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-mmsys.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-mmsys.c b/drivers/soc/mediatek/mtk-mmsys.c index b0bb08a66b01..da9039185aac 100644 --- a/drivers/soc/mediatek/mtk-mmsys.c +++ b/drivers/soc/mediatek/mtk-mmsys.c @@ -79,6 +79,10 @@ struct mtk_mmsys_driver_data { const char *clk_driver; }; +static const struct mtk_mmsys_driver_data mt2712_mmsys_driver_data = { + .clk_driver = "clk-mt2712-mm", +}; + static const struct mtk_mmsys_driver_data mt8173_mmsys_driver_data = { .clk_driver = "clk-mt8173-mm", }; @@ -320,6 +324,10 @@ static int mtk_mmsys_probe(struct platform_device *pdev) } static const struct of_device_id of_match_mtk_mmsys[] = { + { + .compatible = "mediatek,mt2712-mmsys", + .data = &mt2712_mmsys_driver_data, + }, { .compatible = "mediatek,mt8173-mmsys", .data = &mt8173_mmsys_driver_data, -- cgit From c292b133b58b49d9c5d6a838a8de7831fd97f58d Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Wed, 1 Apr 2020 22:17:35 +0200 Subject: clk / soc: mediatek: Bind clock and gpu driver for mt2701 Now that the mmsys driver is the top-level entry point for the multimedia subsystem, we could bind the clock and the gpu driver on those devices that is expected to work, so the drm driver is intantiated by the mmsys driver and display, hopefully, working again. Signed-off-by: Enric Balletbo i Serra Reviewed-by: Chun-Kuang Hu Acked-by: Stephen Boyd Link: https://lore.kernel.org/r/20200401201736.2980433-3-enric.balletbo@collabora.com Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-mmsys.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-mmsys.c b/drivers/soc/mediatek/mtk-mmsys.c index da9039185aac..e1084ef26825 100644 --- a/drivers/soc/mediatek/mtk-mmsys.c +++ b/drivers/soc/mediatek/mtk-mmsys.c @@ -79,6 +79,10 @@ struct mtk_mmsys_driver_data { const char *clk_driver; }; +static const struct mtk_mmsys_driver_data mt2701_mmsys_driver_data = { + .clk_driver = "clk-mt2701-mm", +}; + static const struct mtk_mmsys_driver_data mt2712_mmsys_driver_data = { .clk_driver = "clk-mt2712-mm", }; @@ -324,6 +328,10 @@ static int mtk_mmsys_probe(struct platform_device *pdev) } static const struct of_device_id of_match_mtk_mmsys[] = { + { + .compatible = "mediatek,mt2701-mmsys", + .data = &mt2701_mmsys_driver_data, + }, { .compatible = "mediatek,mt2712-mmsys", .data = &mt2712_mmsys_driver_data, -- cgit From 1f9adbc72824ff07bbffd776d8b51f91f5a82c18 Mon Sep 17 00:00:00 2001 From: Matthias Brugger Date: Mon, 18 May 2020 13:31:53 +0200 Subject: clk/soc: mediatek: mt8183: Bind clock driver from platform device The mmsys driver is now the top level entry point for the multimedia system (mmsys), we bind the clock driver by creating a platform device. We also bind the MediaTek DRM driver which is not yet implement and therefor will errror out for now. Signed-off-by: Matthias Brugger Reviewed-by: Chun-Kuang Hu Acked-by: Stephen Boyd Link: https://lore.kernel.org/r/20200518113156.25009-1-matthias.bgg@kernel.org Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-mmsys.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-mmsys.c b/drivers/soc/mediatek/mtk-mmsys.c index e1084ef26825..5042851ca92d 100644 --- a/drivers/soc/mediatek/mtk-mmsys.c +++ b/drivers/soc/mediatek/mtk-mmsys.c @@ -91,6 +91,10 @@ static const struct mtk_mmsys_driver_data mt8173_mmsys_driver_data = { .clk_driver = "clk-mt8173-mm", }; +static const struct mtk_mmsys_driver_data mt8183_mmsys_driver_data = { + .clk_driver = "clk-mt8183-mm", +}; + static unsigned int mtk_mmsys_ddp_mout_en(enum mtk_ddp_comp_id cur, enum mtk_ddp_comp_id next, unsigned int *addr) @@ -340,6 +344,10 @@ static const struct of_device_id of_match_mtk_mmsys[] = { .compatible = "mediatek,mt8173-mmsys", .data = &mt8173_mmsys_driver_data, }, + { + .compatible = "mediatek,mt8183-mmsys", + .data = &mt8183_mmsys_driver_data, + }, { } }; -- cgit From cad4e3790a987867c3779b59521f19ff75b8d8b6 Mon Sep 17 00:00:00 2001 From: Matthias Brugger Date: Mon, 18 May 2020 13:31:54 +0200 Subject: clk/soc: mediatek: mt6797: Bind clock driver from platform device The mmsys driver is now the top level entry point for the multimedia system (mmsys), we bind the clock driver by creating a platform device. We also bind the MediaTek DRM driver which is not yet implement and therefor will errror out for now. Signed-off-by: Matthias Brugger Reviewed-by: Chun-Kuang Hu Acked-by: Stephen Boyd Link: https://lore.kernel.org/r/20200518113156.25009-2-matthias.bgg@kernel.org Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-mmsys.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-mmsys.c b/drivers/soc/mediatek/mtk-mmsys.c index 5042851ca92d..e13dfda8b43b 100644 --- a/drivers/soc/mediatek/mtk-mmsys.c +++ b/drivers/soc/mediatek/mtk-mmsys.c @@ -87,6 +87,10 @@ static const struct mtk_mmsys_driver_data mt2712_mmsys_driver_data = { .clk_driver = "clk-mt2712-mm", }; +static const struct mtk_mmsys_driver_data mt6797_mmsys_driver_data = { + .clk_driver = "clk-mt6797-mm", +}; + static const struct mtk_mmsys_driver_data mt8173_mmsys_driver_data = { .clk_driver = "clk-mt8173-mm", }; @@ -340,6 +344,10 @@ static const struct of_device_id of_match_mtk_mmsys[] = { .compatible = "mediatek,mt2712-mmsys", .data = &mt2712_mmsys_driver_data, }, + { + .compatible = "mediatek,mt6797-mmsys", + .data = &mt6797_mmsys_driver_data, + }, { .compatible = "mediatek,mt8173-mmsys", .data = &mt8173_mmsys_driver_data, -- cgit From 32956dda97577f0960eb3d9d9aff7338d0cf4cc4 Mon Sep 17 00:00:00 2001 From: Matthias Brugger Date: Mon, 18 May 2020 13:31:55 +0200 Subject: clk/soc: mediatek: mt6779: Bind clock driver from platform device The mmsys driver is now the top level entry point for the multimedia system (mmsys), we bind the clock driver by creating a platform device. We also bind the MediaTek DRM driver which is not yet implement and therefor will errror out for now. Signed-off-by: Matthias Brugger Reviewed-by: Chun-Kuang Hu Acked-by: Stephen Boyd Link: https://lore.kernel.org/r/20200518113156.25009-3-matthias.bgg@kernel.org Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-mmsys.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-mmsys.c b/drivers/soc/mediatek/mtk-mmsys.c index e13dfda8b43b..a55f25511173 100644 --- a/drivers/soc/mediatek/mtk-mmsys.c +++ b/drivers/soc/mediatek/mtk-mmsys.c @@ -87,6 +87,10 @@ static const struct mtk_mmsys_driver_data mt2712_mmsys_driver_data = { .clk_driver = "clk-mt2712-mm", }; +static const struct mtk_mmsys_driver_data mt6779_mmsys_driver_data = { + .clk_driver = "clk-mt6779-mm", +}; + static const struct mtk_mmsys_driver_data mt6797_mmsys_driver_data = { .clk_driver = "clk-mt6797-mm", }; @@ -344,6 +348,10 @@ static const struct of_device_id of_match_mtk_mmsys[] = { .compatible = "mediatek,mt2712-mmsys", .data = &mt2712_mmsys_driver_data, }, + { + .compatible = "mediatek,mt6779-mmsys", + .data = &mt6779_mmsys_driver_data, + }, { .compatible = "mediatek,mt6797-mmsys", .data = &mt6797_mmsys_driver_data, -- cgit From df701a76a6419e66b566457a5b3fcdd314e48fd9 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Wed, 20 May 2020 16:13:17 +0100 Subject: soc/tegra: pmc: Enable PMIC wake event on Tegra210 The PMIC wake event can be used to bring the system out of suspend based on certain events happening on the PMIC (such as an RTC alarm). Signed-off-by: Jon Hunter Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 8169737c3252..42cf37a0556b 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -3063,6 +3063,7 @@ static const struct pinctrl_pin_desc tegra210_pin_descs[] = { static const struct tegra_wake_event tegra210_wake_events[] = { TEGRA_WAKE_IRQ("rtc", 16, 2), + TEGRA_WAKE_IRQ("pmu", 51, 86), }; static const struct tegra_pmc_soc tegra210_pmc_soc = { -- cgit From 661ea25e5319d0ceaeba80dbc2e083245d91f57a Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 18 May 2020 17:19:04 -0500 Subject: soc: fsl: qe: Replace one-element array and use struct_size() helper The current codebase makes use of one-element arrays in the following form: struct something { int length; u8 data[1]; }; struct something *instance; instance = kmalloc(sizeof(*instance) + size, GFP_KERNEL); instance->length = size; memcpy(instance->data, source, size); but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. So, replace the one-element array with a flexible-array member. Also, make use of the new struct_size() helper to properly calculate the size of struct qe_firmware. This issue was found with the help of Coccinelle and, audited and fixed _manually_. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Reviewed-by: Qiang Zhao Signed-off-by: Li Yang --- drivers/soc/fsl/qe/qe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/qe/qe.c b/drivers/soc/fsl/qe/qe.c index 447146861c2c..2df20d6f85fa 100644 --- a/drivers/soc/fsl/qe/qe.c +++ b/drivers/soc/fsl/qe/qe.c @@ -448,7 +448,7 @@ int qe_upload_firmware(const struct qe_firmware *firmware) unsigned int i; unsigned int j; u32 crc; - size_t calc_size = sizeof(struct qe_firmware); + size_t calc_size; size_t length; const struct qe_header *hdr; @@ -480,7 +480,7 @@ int qe_upload_firmware(const struct qe_firmware *firmware) } /* Validate the length and check if there's a CRC */ - calc_size += (firmware->count - 1) * sizeof(struct qe_microcode); + calc_size = struct_size(firmware, microcode, firmware->count); for (i = 0; i < firmware->count; i++) /* -- cgit From 162b323c3d0820617289bd67d1895d5e7541cc3a Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 8 May 2020 22:08:46 +0800 Subject: soc: fsl: qbman: Remove unused inline function qm_eqcr_get_ci_stashing There's no callers in-tree anymore. Signed-off-by: YueHaibing Signed-off-by: Li Yang --- drivers/soc/fsl/qbman/qman.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/qbman/qman.c b/drivers/soc/fsl/qbman/qman.c index 1e164e03410a..9888a7061873 100644 --- a/drivers/soc/fsl/qbman/qman.c +++ b/drivers/soc/fsl/qbman/qman.c @@ -449,11 +449,6 @@ static inline int qm_eqcr_init(struct qm_portal *portal, return 0; } -static inline unsigned int qm_eqcr_get_ci_stashing(struct qm_portal *portal) -{ - return (qm_in(portal, QM_REG_CFG) >> 28) & 0x7; -} - static inline void qm_eqcr_finish(struct qm_portal *portal) { struct qm_eqcr *eqcr = &portal->eqcr; -- cgit From d0bab301a0462ae46f65fea0af01b938d709aba7 Mon Sep 17 00:00:00 2001 From: Roy Pledge Date: Fri, 24 Apr 2020 14:51:12 +0300 Subject: soc: fsl: dpio: Prefer the CPU affine DPIO Use the cpu affine DPIO unless there isn't one which can happen if less DPIOs than cores are assign to the kernel. Signed-off-by: Roy Pledge Signed-off-by: Ioana Ciornei Signed-off-by: Li Yang --- drivers/soc/fsl/dpio/dpio-service.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/dpio/dpio-service.c b/drivers/soc/fsl/dpio/dpio-service.c index cd4f6410e8c2..f1080c7a3fe1 100644 --- a/drivers/soc/fsl/dpio/dpio-service.c +++ b/drivers/soc/fsl/dpio/dpio-service.c @@ -58,7 +58,7 @@ static inline struct dpaa2_io *service_select_by_cpu(struct dpaa2_io *d, * If cpu == -1, choose the current cpu, with no guarantees about * potentially being migrated away. */ - if (unlikely(cpu < 0)) + if (cpu < 0) cpu = smp_processor_id(); /* If a specific cpu was requested, pick it up immediately */ @@ -70,6 +70,10 @@ static inline struct dpaa2_io *service_select(struct dpaa2_io *d) if (d) return d; + d = service_select_by_cpu(d, -1); + if (d) + return d; + spin_lock(&dpio_list_lock); d = list_entry(dpio_list.next, struct dpaa2_io, node); list_del(&d->node); -- cgit From b25511ef134af3b2926d5c79141c9dbabe416f02 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 27 Mar 2020 16:13:49 +0000 Subject: soc: fsl: qe: clean up an indentation issue There is a statement that not indented correctly, remove the extraneous space. Signed-off-by: Colin Ian King Signed-off-by: Li Yang --- drivers/soc/fsl/qe/ucc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/qe/ucc.c b/drivers/soc/fsl/qe/ucc.c index d6c93970df4d..cac0fb7693a0 100644 --- a/drivers/soc/fsl/qe/ucc.c +++ b/drivers/soc/fsl/qe/ucc.c @@ -519,7 +519,7 @@ int ucc_set_tdm_rxtx_clk(u32 tdm_num, enum qe_clock clock, int clock_bits; u32 shift; struct qe_mux __iomem *qe_mux_reg; - __be32 __iomem *cmxs1cr; + __be32 __iomem *cmxs1cr; qe_mux_reg = &qe_immr->qmx; -- cgit From e9e4ef9116b12951eaee3f8447ba9bbb40ab3620 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 8 May 2020 22:09:47 +0800 Subject: soc: fsl: dpio: Remove unused inline function qbman_write_eqcr_am_rt_register There's no callers in-tree anymore since commit 3b2abda7d28c ("soc: fsl: dpio: Replace QMAN array mode with ring mode enqueue") Signed-off-by: YueHaibing Signed-off-by: Li Yang --- drivers/soc/fsl/dpio/qbman-portal.c | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/dpio/qbman-portal.c b/drivers/soc/fsl/dpio/qbman-portal.c index d1f49caa5b13..053086b99e25 100644 --- a/drivers/soc/fsl/dpio/qbman-portal.c +++ b/drivers/soc/fsl/dpio/qbman-portal.c @@ -572,18 +572,6 @@ void qbman_eq_desc_set_qd(struct qbman_eq_desc *d, u32 qdid, #define EQAR_VB(eqar) ((eqar) & 0x80) #define EQAR_SUCCESS(eqar) ((eqar) & 0x100) -static inline void qbman_write_eqcr_am_rt_register(struct qbman_swp *p, - u8 idx) -{ - if (idx < 16) - qbman_write_register(p, QBMAN_CINH_SWP_EQCR_AM_RT + idx * 4, - QMAN_RT_MODE); - else - qbman_write_register(p, QBMAN_CINH_SWP_EQCR_AM_RT2 + - (idx - 16) * 4, - QMAN_RT_MODE); -} - #define QB_RT_BIT ((u32)0x100) /** * qbman_swp_enqueue_direct() - Issue an enqueue command -- cgit From 907a2b7e2fc746ac3b57b7eb64c220b81d74fc42 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Wed, 27 May 2020 20:39:14 -0700 Subject: soc: ti: add k3 platforms chipid module driver The Texas Instruments K3 Multicore SoC platforms have chipid module which is represented by CTRLMMR_xxx_JTAGID register and contains information about SoC id and revision. Bits: 31-28 VARIANT Device variant 27-12 PARTNO Part number 11-1 MFG Indicates TI as manufacturer (0x17) 1 Always 1 This patch adds corresponding driver to identify the TI K3 SoC family and revision, and registers this information with the SoC bus. It is available under /sys/devices/soc0/ for user space, and can be checked, where needed, in Kernel using soc_device_match(). Identification is done by: - checking MFG to be TI ID - retrieving Device variant (revision) - retrieving Part number and convert it to the family - retrieving machine from DT "/model" Example J721E: # cat /sys/devices/soc0/{machine,family,revision} Texas Instruments K3 J721E SoC J721E SR1.0 Example AM65x: # cat /sys/devices/soc0/{machine,family,revision} Texas Instruments AM654 Base Board AM65X SR1.0 Cc: Arnd Bergmann Signed-off-by: Grygorii Strashko Reviewed-by: Lokesh Vutla Reviewed-by: Tero Kristo Signed-off-by: Santosh Shilimkar --- drivers/soc/ti/Kconfig | 10 +++ drivers/soc/ti/Makefile | 1 + drivers/soc/ti/k3-socinfo.c | 152 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 drivers/soc/ti/k3-socinfo.c (limited to 'drivers/soc') diff --git a/drivers/soc/ti/Kconfig b/drivers/soc/ti/Kconfig index 4486e055794c..e192fb788836 100644 --- a/drivers/soc/ti/Kconfig +++ b/drivers/soc/ti/Kconfig @@ -91,6 +91,16 @@ config TI_K3_RINGACC and a consumer. There is one RINGACC module per NAVSS on TI AM65x SoCs If unsure, say N. +config TI_K3_SOCINFO + bool + depends on ARCH_K3 || COMPILE_TEST + select SOC_BUS + select MFD_SYSCON + help + Include support for the SoC bus socinfo for the TI K3 Multicore SoC + platforms to provide information about the SoC family and + variant to user space. + endif # SOC_TI config TI_SCI_INTA_MSI_DOMAIN diff --git a/drivers/soc/ti/Makefile b/drivers/soc/ti/Makefile index bec827937a5f..1110e5c98685 100644 --- a/drivers/soc/ti/Makefile +++ b/drivers/soc/ti/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_WKUP_M3_IPC) += wkup_m3_ipc.o obj-$(CONFIG_TI_SCI_PM_DOMAINS) += ti_sci_pm_domains.o obj-$(CONFIG_TI_SCI_INTA_MSI_DOMAIN) += ti_sci_inta_msi.o obj-$(CONFIG_TI_K3_RINGACC) += k3-ringacc.o +obj-$(CONFIG_TI_K3_SOCINFO) += k3-socinfo.o diff --git a/drivers/soc/ti/k3-socinfo.c b/drivers/soc/ti/k3-socinfo.c new file mode 100644 index 000000000000..af0ba5288e58 --- /dev/null +++ b/drivers/soc/ti/k3-socinfo.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TI K3 SoC info driver + * + * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define CTRLMMR_WKUP_JTAGID_REG 0 +/* + * Bits: + * 31-28 VARIANT Device variant + * 27-12 PARTNO Part number + * 11-1 MFG Indicates TI as manufacturer (0x17) + * 1 Always 1 + */ +#define CTRLMMR_WKUP_JTAGID_VARIANT_SHIFT (28) +#define CTRLMMR_WKUP_JTAGID_VARIANT_MASK GENMASK(31, 28) + +#define CTRLMMR_WKUP_JTAGID_PARTNO_SHIFT (12) +#define CTRLMMR_WKUP_JTAGID_PARTNO_MASK GENMASK(27, 12) + +#define CTRLMMR_WKUP_JTAGID_MFG_SHIFT (1) +#define CTRLMMR_WKUP_JTAGID_MFG_MASK GENMASK(11, 1) + +#define CTRLMMR_WKUP_JTAGID_MFG_TI 0x17 + +static const struct k3_soc_id { + unsigned int id; + const char *family_name; +} k3_soc_ids[] = { + { 0xBB5A, "AM65X" }, + { 0xBB64, "J721E" }, +}; + +static int +k3_chipinfo_partno_to_names(unsigned int partno, + struct soc_device_attribute *soc_dev_attr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(k3_soc_ids); i++) + if (partno == k3_soc_ids[i].id) { + soc_dev_attr->family = k3_soc_ids[i].family_name; + return 0; + } + + return -EINVAL; +} + +static int k3_chipinfo_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct soc_device_attribute *soc_dev_attr; + struct device *dev = &pdev->dev; + struct soc_device *soc_dev; + struct regmap *regmap; + u32 partno_id; + u32 variant; + u32 jtag_id; + u32 mfg; + int ret; + + regmap = device_node_to_regmap(node); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + ret = regmap_read(regmap, CTRLMMR_WKUP_JTAGID_REG, &jtag_id); + if (ret < 0) + return ret; + + mfg = (jtag_id & CTRLMMR_WKUP_JTAGID_MFG_MASK) >> + CTRLMMR_WKUP_JTAGID_MFG_SHIFT; + + if (mfg != CTRLMMR_WKUP_JTAGID_MFG_TI) { + dev_err(dev, "Invalid MFG SoC\n"); + return -ENODEV; + } + + variant = (jtag_id & CTRLMMR_WKUP_JTAGID_VARIANT_MASK) >> + CTRLMMR_WKUP_JTAGID_VARIANT_SHIFT; + variant++; + + partno_id = (jtag_id & CTRLMMR_WKUP_JTAGID_PARTNO_MASK) >> + CTRLMMR_WKUP_JTAGID_PARTNO_SHIFT; + + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + return -ENOMEM; + + soc_dev_attr->revision = kasprintf(GFP_KERNEL, "SR%x.0", variant); + if (!soc_dev_attr->revision) { + ret = -ENOMEM; + goto err; + } + + ret = k3_chipinfo_partno_to_names(partno_id, soc_dev_attr); + if (ret) { + dev_err(dev, "Unknown SoC JTAGID[0x%08X]\n", jtag_id); + ret = -ENODEV; + goto err_free_rev; + } + + node = of_find_node_by_path("/"); + of_property_read_string(node, "model", &soc_dev_attr->machine); + of_node_put(node); + + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR(soc_dev)) { + ret = PTR_ERR(soc_dev); + goto err_free_rev; + } + + dev_info(dev, "Family:%s rev:%s JTAGID[0x%08x] Detected\n", + soc_dev_attr->family, + soc_dev_attr->revision, jtag_id); + + return 0; + +err_free_rev: + kfree(soc_dev_attr->revision); +err: + kfree(soc_dev_attr); + return ret; +} + +static const struct of_device_id k3_chipinfo_of_match[] = { + { .compatible = "ti,am654-chipid", }, + { /* sentinel */ }, +}; + +static struct platform_driver k3_chipinfo_driver = { + .driver = { + .name = "k3-chipinfo", + .of_match_table = k3_chipinfo_of_match, + }, + .probe = k3_chipinfo_probe, +}; + +static int __init k3_chipinfo_init(void) +{ + return platform_driver_register(&k3_chipinfo_driver); +} +subsys_initcall(k3_chipinfo_init); -- cgit From b8b38a8e3cae100f292d756e32c78ab288db8a7d Mon Sep 17 00:00:00 2001 From: Samuel Zou Date: Wed, 27 May 2020 20:39:14 -0700 Subject: drivers: soc: ti: knav_qmss_queue: Make knav_gp_range_ops static Fix the following sparse warning: drivers/soc/ti/knav_qmss_queue.c:412:23: warning: symbol 'knav_gp_range_ops' was not declared. The knav_acc_firmwares has only call site within knav_qmss_queue.c It should be static Fixes: 41f93af900a2 ("soc: ti: add Keystone Navigator QMSS driver") Reported-by: Hulk Robot Signed-off-by: Samuel Zou Signed-off-by: Santosh Shilimkar --- drivers/soc/ti/knav_qmss_queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/ti/knav_qmss_queue.c b/drivers/soc/ti/knav_qmss_queue.c index 37f3db6c041c..aa071d96ef36 100644 --- a/drivers/soc/ti/knav_qmss_queue.c +++ b/drivers/soc/ti/knav_qmss_queue.c @@ -409,7 +409,7 @@ static int knav_gp_close_queue(struct knav_range_info *range, return 0; } -struct knav_range_ops knav_gp_range_ops = { +static struct knav_range_ops knav_gp_range_ops = { .set_notify = knav_gp_set_notify, .open_queue = knav_gp_open_queue, .close_queue = knav_gp_close_queue, -- cgit