From 4c7924fb905b02323ff6d9d20f370892615dccfa Mon Sep 17 00:00:00 2001 From: Julien Massot Date: Fri, 22 Oct 2021 14:21:01 +0200 Subject: [PATCH 01/94] soc: renesas: rcar-rst: Add support to set rproc boot address R-Car Gen3 SoC series has a realtime processor, the boot address of this processor can be set thanks to CR7BAR register of the reset module. Export this function so that it's possible to set the boot address from a remoteproc driver. Also drop the __initdata qualifier on rcar_rst_base, since we will use this address later than init time. Signed-off-by: Julien Massot Link: https://lore.kernel.org/r/20211022122101.66998-1-julien.massot@iot.bzh Signed-off-by: Geert Uytterhoeven --- drivers/soc/renesas/rcar-rst.c | 43 ++++++++++++++++++++++++++-- include/linux/soc/renesas/rcar-rst.h | 2 ++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/drivers/soc/renesas/rcar-rst.c b/drivers/soc/renesas/rcar-rst.c index 8a1e402ea799..1ad54e0c32e1 100644 --- a/drivers/soc/renesas/rcar-rst.c +++ b/drivers/soc/renesas/rcar-rst.c @@ -13,15 +13,43 @@ #define WDTRSTCR_RESET 0xA55A0002 #define WDTRSTCR 0x0054 +#define CR7BAR 0x0070 +#define CR7BAREN BIT(4) +#define CR7BAR_MASK 0xFFFC0000 + +static void __iomem *rcar_rst_base; +static u32 saved_mode __initdata; +static int (*rcar_rst_set_rproc_boot_addr_func)(u64 boot_addr); + static int rcar_rst_enable_wdt_reset(void __iomem *base) { iowrite32(WDTRSTCR_RESET, base + WDTRSTCR); return 0; } +/* + * Most of the R-Car Gen3 SoCs have an ARM Realtime Core. + * Firmware boot address has to be set in CR7BAR before + * starting the realtime core. + * Boot address must be aligned on a 256k boundary. + */ +static int rcar_rst_set_gen3_rproc_boot_addr(u64 boot_addr) +{ + if (boot_addr & ~(u64)CR7BAR_MASK) { + pr_err("Invalid boot address got %llx\n", boot_addr); + return -EINVAL; + } + + iowrite32(boot_addr, rcar_rst_base + CR7BAR); + iowrite32(boot_addr | CR7BAREN, rcar_rst_base + CR7BAR); + + return 0; +} + struct rst_config { unsigned int modemr; /* Mode Monitoring Register Offset */ int (*configure)(void __iomem *base); /* Platform specific config */ + int (*set_rproc_boot_addr)(u64 boot_addr); }; static const struct rst_config rcar_rst_gen1 __initconst = { @@ -35,6 +63,7 @@ static const struct rst_config rcar_rst_gen2 __initconst = { static const struct rst_config rcar_rst_gen3 __initconst = { .modemr = 0x60, + .set_rproc_boot_addr = rcar_rst_set_gen3_rproc_boot_addr, }; static const struct rst_config rcar_rst_r8a779a0 __initconst = { @@ -76,9 +105,6 @@ static const struct of_device_id rcar_rst_matches[] __initconst = { { /* sentinel */ } }; -static void __iomem *rcar_rst_base __initdata; -static u32 saved_mode __initdata; - static int __init rcar_rst_init(void) { const struct of_device_id *match; @@ -100,6 +126,8 @@ static int __init rcar_rst_init(void) rcar_rst_base = base; cfg = match->data; + rcar_rst_set_rproc_boot_addr_func = cfg->set_rproc_boot_addr; + saved_mode = ioread32(base + cfg->modemr); if (cfg->configure) { error = cfg->configure(base); @@ -130,3 +158,12 @@ int __init rcar_rst_read_mode_pins(u32 *mode) *mode = saved_mode; return 0; } + +int rcar_rst_set_rproc_boot_addr(u64 boot_addr) +{ + if (!rcar_rst_set_rproc_boot_addr_func) + return -EIO; + + return rcar_rst_set_rproc_boot_addr_func(boot_addr); +} +EXPORT_SYMBOL_GPL(rcar_rst_set_rproc_boot_addr); diff --git a/include/linux/soc/renesas/rcar-rst.h b/include/linux/soc/renesas/rcar-rst.h index 7899a5b8c247..1f1fe8bfaa76 100644 --- a/include/linux/soc/renesas/rcar-rst.h +++ b/include/linux/soc/renesas/rcar-rst.h @@ -4,8 +4,10 @@ #ifdef CONFIG_RST_RCAR int rcar_rst_read_mode_pins(u32 *mode); +int rcar_rst_set_rproc_boot_addr(u64 boot_addr); #else static inline int rcar_rst_read_mode_pins(u32 *mode) { return -ENODEV; } +static inline int rcar_rst_set_rproc_boot_addr(u64 boot_addr) { return -ENODEV; } #endif #endif /* __LINUX_SOC_RENESAS_RCAR_RST_H__ */ From 569e45a1135497d8dddc647bc615e26c49b070a8 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 31 Oct 2021 21:52:12 +0100 Subject: [PATCH 02/94] soc: samsung: exynos-chipid: describe which SoCs go with compatibles The Exynos ChipID driver, like most of the Exynos drivers, uses one compatible for entire family of compatible devices using one devicetree "compatible". The compatibility is here described by programming interface (register layout), not by actual values, so the product ID register on one family of devices has different values for different SoCs. Describe which SoC goes with which compatible for documentation purposes, if the DTS is not available in mainline. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20211031205212.59505-1-krzysztof.kozlowski@canonical.com --- drivers/soc/samsung/exynos-chipid.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soc/samsung/exynos-chipid.c b/drivers/soc/samsung/exynos-chipid.c index a28053ec7e6a..baf3afc69d79 100644 --- a/drivers/soc/samsung/exynos-chipid.c +++ b/drivers/soc/samsung/exynos-chipid.c @@ -42,6 +42,7 @@ static const struct exynos_soc_id { unsigned int id; } soc_ids[] = { /* List ordered by SoC name */ + /* Compatible with: samsung,exynos4210-chipid */ { "EXYNOS3250", 0xE3472000 }, { "EXYNOS4210", 0x43200000 }, /* EVT0 revision */ { "EXYNOS4210", 0x43210000 }, @@ -55,6 +56,7 @@ static const struct exynos_soc_id { { "EXYNOS5440", 0xE5440000 }, { "EXYNOS5800", 0xE5422000 }, { "EXYNOS7420", 0xE7420000 }, + /* Compatible with: samsung,exynos850-chipid */ { "EXYNOS850", 0xE3830000 }, { "EXYNOSAUTOV9", 0xAAA80000 }, }; From 214f78060713fe13e3ba3701dff51e46d9576a9c Mon Sep 17 00:00:00 2001 From: David Virag Date: Mon, 1 Nov 2021 01:48:53 +0100 Subject: [PATCH 03/94] soc: samsung: exynos-chipid: add Exynos7885 SoC support Exynos 7885 has product ID "0xE7885000". Add this ID to the IDs with the name. Signed-off-by: David Virag Link: https://lore.kernel.org/r/20211101004853.55810-1-virag.david003@gmail.com Signed-off-by: Krzysztof Kozlowski --- drivers/soc/samsung/exynos-chipid.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/samsung/exynos-chipid.c b/drivers/soc/samsung/exynos-chipid.c index baf3afc69d79..2746d05936d3 100644 --- a/drivers/soc/samsung/exynos-chipid.c +++ b/drivers/soc/samsung/exynos-chipid.c @@ -57,6 +57,7 @@ static const struct exynos_soc_id { { "EXYNOS5800", 0xE5422000 }, { "EXYNOS7420", 0xE7420000 }, /* Compatible with: samsung,exynos850-chipid */ + { "EXYNOS7885", 0xE7885000 }, { "EXYNOS850", 0xE3830000 }, { "EXYNOSAUTOV9", 0xAAA80000 }, }; From a67cce83945170994fcef241b934699b6018b707 Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Thu, 28 Oct 2021 17:43:12 +0300 Subject: [PATCH 04/94] dt-bindings: samsung: pmu: Document Exynos850 Exynos850 SoC can reuse PMU driver functionality. Add corresponding compatible string to PMU bindings documentation. Signed-off-by: Sam Protsenko Acked-by: Rob Herring Link: https://lore.kernel.org/r/20211028144313.9444-1-semen.protsenko@linaro.org Signed-off-by: Krzysztof Kozlowski --- Documentation/devicetree/bindings/arm/samsung/pmu.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/samsung/pmu.yaml b/Documentation/devicetree/bindings/arm/samsung/pmu.yaml index 17678d9686c1..0bbd89265b28 100644 --- a/Documentation/devicetree/bindings/arm/samsung/pmu.yaml +++ b/Documentation/devicetree/bindings/arm/samsung/pmu.yaml @@ -24,6 +24,7 @@ select: - samsung,exynos5420-pmu - samsung,exynos5433-pmu - samsung,exynos7-pmu + - samsung,exynos850-pmu - samsung-s5pv210-pmu required: - compatible @@ -41,6 +42,7 @@ properties: - samsung,exynos5420-pmu - samsung,exynos5433-pmu - samsung,exynos7-pmu + - samsung,exynos850-pmu - samsung-s5pv210-pmu - const: syscon From f5dc0140d483fcc6417d1d655481f0ff1de390b0 Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Thu, 28 Oct 2021 17:43:13 +0300 Subject: [PATCH 05/94] soc: samsung: exynos-pmu: Add Exynos850 support Exynos850 SoC can reuse PMU driver functionality. Add corresponding compatible string. Signed-off-by: Sam Protsenko Link: https://lore.kernel.org/r/20211028144313.9444-2-semen.protsenko@linaro.org Signed-off-by: Krzysztof Kozlowski --- drivers/soc/samsung/exynos-pmu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c index a18c93a4646c..732c86ce2be8 100644 --- a/drivers/soc/samsung/exynos-pmu.c +++ b/drivers/soc/samsung/exynos-pmu.c @@ -94,6 +94,8 @@ static const struct of_device_id exynos_pmu_of_device_ids[] = { .compatible = "samsung,exynos5433-pmu", }, { .compatible = "samsung,exynos7-pmu", + }, { + .compatible = "samsung,exynos850-pmu", }, { /*sentinel*/ }, }; From c271aa1f73515bcb35f977f30825832d41a2f504 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Mon, 25 Oct 2021 21:56:25 +0100 Subject: [PATCH 06/94] dt-bindings: memory: renesas,rpc-if: Add support for the R9A07G044 SPI Multi I/O Bus Controller on RZ/G2L SoC is almost identical to the RPC-IF interface found on R-Car Gen3 SoC's. This patch adds a new compatible string to identify the RZ/G2L family so that the timing values on RZ/G2L can be adjusted. Signed-off-by: Lad Prabhakar Reviewed-by: Biju Das Reviewed-by: Rob Herring Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20211025205631.21151-2-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Krzysztof Kozlowski --- .../memory-controllers/renesas,rpc-if.yaml | 53 +++++++++++++------ 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.yaml b/Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.yaml index 9da80e8f2444..43391a2d5bc5 100644 --- a/Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.yaml @@ -24,17 +24,23 @@ allOf: properties: compatible: - items: - - enum: - - renesas,r8a774a1-rpc-if # RZ/G2M - - renesas,r8a774b1-rpc-if # RZ/G2N - - renesas,r8a774c0-rpc-if # RZ/G2E - - renesas,r8a774e1-rpc-if # RZ/G2H - - renesas,r8a77970-rpc-if # R-Car V3M - - renesas,r8a77980-rpc-if # R-Car V3H - - renesas,r8a77995-rpc-if # R-Car D3 - - renesas,r8a779a0-rpc-if # R-Car V3U - - const: renesas,rcar-gen3-rpc-if # a generic R-Car gen3 or RZ/G2 device + oneOf: + - items: + - enum: + - renesas,r8a774a1-rpc-if # RZ/G2M + - renesas,r8a774b1-rpc-if # RZ/G2N + - renesas,r8a774c0-rpc-if # RZ/G2E + - renesas,r8a774e1-rpc-if # RZ/G2H + - renesas,r8a77970-rpc-if # R-Car V3M + - renesas,r8a77980-rpc-if # R-Car V3H + - renesas,r8a77995-rpc-if # R-Car D3 + - renesas,r8a779a0-rpc-if # R-Car V3U + - const: renesas,rcar-gen3-rpc-if # a generic R-Car gen3 or RZ/G2{E,H,M,N} device + + - items: + - enum: + - renesas,r9a07g044-rpc-if # RZ/G2{L,LC} + - const: renesas,rzg2l-rpc-if # RZ/G2L family reg: items: @@ -48,8 +54,7 @@ properties: - const: dirmap - const: wbuf - clocks: - maxItems: 1 + clocks: true power-domains: maxItems: 1 @@ -67,8 +72,6 @@ patternProperties: - cfi-flash - jedec,spi-nor -unevaluatedProperties: false - required: - compatible - reg @@ -79,6 +82,26 @@ required: - '#address-cells' - '#size-cells' +if: + properties: + compatible: + contains: + enum: + - renesas,rzg2l-rpc-if +then: + properties: + clocks: + items: + - description: SPI Multi IO Register access clock (SPI_CLK2) + - description: SPI Multi IO Main clock (SPI_CLK). + +else: + properties: + clocks: + maxItems: 1 + +unevaluatedProperties: false + examples: - | #include From 4b5a231ff617d5cdc9e99f39a7268ad491400195 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Mon, 25 Oct 2021 21:56:26 +0100 Subject: [PATCH 07/94] dt-bindings: memory: renesas,rpc-if: Add optional interrupts property For completeness add optional interrupts property. Signed-off-by: Lad Prabhakar Reviewed-by: Biju Das Reviewed-by: Wolfram Sang Reviewed-by: Rob Herring Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20211025205631.21151-3-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Krzysztof Kozlowski --- .../devicetree/bindings/memory-controllers/renesas,rpc-if.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.yaml b/Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.yaml index 43391a2d5bc5..294f1036420d 100644 --- a/Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.yaml @@ -56,6 +56,9 @@ properties: clocks: true + interrupts: + maxItems: 1 + power-domains: maxItems: 1 From 818fdfa89baac77a8df5a2c30f4fb798cc937aa0 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Mon, 25 Oct 2021 21:56:29 +0100 Subject: [PATCH 08/94] memory: renesas-rpc-if: Return error in case devm_ioremap_resource() fails Make sure we return error in case devm_ioremap_resource() fails for dirmap resource. Fixes: ca7d8b980b67 ("memory: add Renesas RPC-IF driver") Signed-off-by: Lad Prabhakar Reviewed-by: Biju Das Reviewed-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20211025205631.21151-6-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/renesas-rpc-if.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/memory/renesas-rpc-if.c b/drivers/memory/renesas-rpc-if.c index 7435baad0007..ff8bcbccac63 100644 --- a/drivers/memory/renesas-rpc-if.c +++ b/drivers/memory/renesas-rpc-if.c @@ -243,7 +243,7 @@ int rpcif_sw_init(struct rpcif *rpc, struct device *dev) res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dirmap"); rpc->dirmap = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(rpc->dirmap)) - rpc->dirmap = NULL; + return PTR_ERR(rpc->dirmap); rpc->size = resource_size(res); rpc->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); From 5da9b59b23d8112709034a07338e03dcc65fa11f Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Mon, 25 Oct 2021 21:56:30 +0100 Subject: [PATCH 09/94] memory: renesas-rpc-if: Drop usage of RPCIF_DIRMAP_SIZE macro RPCIF_DIRMAP_SIZE may differ on various SoC's. Instead of using RPCIF_DIRMAP_SIZE macro use resource size to get dirmap size which is already part of struct rpcif. Signed-off-by: Lad Prabhakar Reviewed-by: Biju Das Reviewed-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20211025205631.21151-7-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/renesas-rpc-if.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/memory/renesas-rpc-if.c b/drivers/memory/renesas-rpc-if.c index ff8bcbccac63..0c56decc91f2 100644 --- a/drivers/memory/renesas-rpc-if.c +++ b/drivers/memory/renesas-rpc-if.c @@ -147,8 +147,6 @@ #define RPCIF_PHYINT 0x0088 /* R/W */ #define RPCIF_PHYINT_WPVAL BIT(1) -#define RPCIF_DIRMAP_SIZE 0x4000000 - static const struct regmap_range rpcif_volatile_ranges[] = { regmap_reg_range(RPCIF_SMRDR0, RPCIF_SMRDR1), regmap_reg_range(RPCIF_SMWDR0, RPCIF_SMWDR1), @@ -588,8 +586,8 @@ static void memcpy_fromio_readw(void *to, ssize_t rpcif_dirmap_read(struct rpcif *rpc, u64 offs, size_t len, void *buf) { - loff_t from = offs & (RPCIF_DIRMAP_SIZE - 1); - size_t size = RPCIF_DIRMAP_SIZE - from; + loff_t from = offs & (rpc->size - 1); + size_t size = rpc->size - from; if (len > size) len = size; From b04cc0d912eb80d3c438b11d96ca847c3e77e8ab Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Mon, 25 Oct 2021 21:56:31 +0100 Subject: [PATCH 10/94] memory: renesas-rpc-if: Add support for RZ/G2L SPI Multi I/O Bus Controller on RZ/G2L SoC is almost identical to the RPC-IF interface found on R-Car Gen3 SoC's. This patch adds a new compatible string for the RZ/G2L family so that the timing values on RZ/G2L can be adjusted. Signed-off-by: Lad Prabhakar Reviewed-by: Biju Das Reviewed-by: Wolfram Sang Link: https://lore.kernel.org/r/20211025205631.21151-8-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/renesas-rpc-if.c | 72 ++++++++++++++++++++++++++++----- drivers/mtd/hyperbus/rpc-if.c | 4 +- drivers/spi/spi-rpc-if.c | 4 +- include/memory/renesas-rpc-if.h | 8 +++- 4 files changed, 75 insertions(+), 13 deletions(-) diff --git a/drivers/memory/renesas-rpc-if.c b/drivers/memory/renesas-rpc-if.c index 0c56decc91f2..8c51145c0f5c 100644 --- a/drivers/memory/renesas-rpc-if.c +++ b/drivers/memory/renesas-rpc-if.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -27,8 +28,8 @@ #define RPCIF_CMNCR_MOIIO_HIZ (RPCIF_CMNCR_MOIIO0(3) | \ RPCIF_CMNCR_MOIIO1(3) | \ RPCIF_CMNCR_MOIIO2(3) | RPCIF_CMNCR_MOIIO3(3)) -#define RPCIF_CMNCR_IO3FV(val) (((val) & 0x3) << 14) /* undocumented */ -#define RPCIF_CMNCR_IO2FV(val) (((val) & 0x3) << 12) /* undocumented */ +#define RPCIF_CMNCR_IO3FV(val) (((val) & 0x3) << 14) /* documented for RZ/G2L */ +#define RPCIF_CMNCR_IO2FV(val) (((val) & 0x3) << 12) /* documented for RZ/G2L */ #define RPCIF_CMNCR_IO0FV(val) (((val) & 0x3) << 8) #define RPCIF_CMNCR_IOFV_HIZ (RPCIF_CMNCR_IO0FV(3) | RPCIF_CMNCR_IO2FV(3) | \ RPCIF_CMNCR_IO3FV(3)) @@ -126,6 +127,9 @@ #define RPCIF_SMDRENR_OPDRE BIT(4) #define RPCIF_SMDRENR_SPIDRE BIT(0) +#define RPCIF_PHYADD 0x0070 /* R/W available on R-Car E3/D3/V3M and RZ/G2{E,L} */ +#define RPCIF_PHYWR 0x0074 /* R/W available on R-Car E3/D3/V3M and RZ/G2{E,L} */ + #define RPCIF_PHYCNT 0x007C /* R/W */ #define RPCIF_PHYCNT_CAL BIT(31) #define RPCIF_PHYCNT_OCTA(v) (((v) & 0x3) << 22) @@ -133,10 +137,12 @@ #define RPCIF_PHYCNT_OCT BIT(20) #define RPCIF_PHYCNT_DDRCAL BIT(19) #define RPCIF_PHYCNT_HS BIT(18) -#define RPCIF_PHYCNT_STRTIM(v) (((v) & 0x7) << 15) +#define RPCIF_PHYCNT_CKSEL(v) (((v) & 0x3) << 16) /* valid only for RZ/G2L */ +#define RPCIF_PHYCNT_STRTIM(v) (((v) & 0x7) << 15) /* valid for R-Car and RZ/G2{E,H,M,N} */ #define RPCIF_PHYCNT_WBUF2 BIT(4) #define RPCIF_PHYCNT_WBUF BIT(2) #define RPCIF_PHYCNT_PHYMEM(v) (((v) & 0x3) << 0) +#define RPCIF_PHYCNT_PHYMEM_MASK GENMASK(1, 0) #define RPCIF_PHYOFFSET1 0x0080 /* R/W */ #define RPCIF_PHYOFFSET1_DDRTMG(v) (((v) & 0x3) << 28) @@ -244,18 +250,46 @@ int rpcif_sw_init(struct rpcif *rpc, struct device *dev) return PTR_ERR(rpc->dirmap); rpc->size = resource_size(res); + rpc->type = (enum rpcif_type)of_device_get_match_data(dev); rpc->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); return PTR_ERR_OR_ZERO(rpc->rstc); } EXPORT_SYMBOL(rpcif_sw_init); -void rpcif_hw_init(struct rpcif *rpc, bool hyperflash) +static void rpcif_rzg2l_timing_adjust_sdr(struct rpcif *rpc) +{ + u32 data; + + regmap_write(rpc->regmap, RPCIF_PHYWR, 0xa5390000); + regmap_write(rpc->regmap, RPCIF_PHYADD, 0x80000000); + regmap_write(rpc->regmap, RPCIF_PHYWR, 0x00008080); + regmap_write(rpc->regmap, RPCIF_PHYADD, 0x80000022); + regmap_write(rpc->regmap, RPCIF_PHYWR, 0x00008080); + regmap_write(rpc->regmap, RPCIF_PHYADD, 0x80000024); + + regmap_read(rpc->regmap, RPCIF_PHYCNT, &data); + regmap_write(rpc->regmap, RPCIF_PHYCNT, data | RPCIF_PHYCNT_CKSEL(3)); + regmap_write(rpc->regmap, RPCIF_PHYWR, 0x00000030); + regmap_write(rpc->regmap, RPCIF_PHYADD, 0x80000032); +} + +int rpcif_hw_init(struct rpcif *rpc, bool hyperflash) { u32 dummy; pm_runtime_get_sync(rpc->dev); + if (rpc->type == RPCIF_RZ_G2L) { + int ret; + + ret = reset_control_reset(rpc->rstc); + if (ret) + return ret; + usleep_range(200, 300); + rpcif_rzg2l_timing_adjust_sdr(rpc); + } + /* * NOTE: The 0x260 are undocumented bits, but they must be set. * RPCIF_PHYCNT_STRTIM is strobe timing adjustment bits, @@ -264,8 +298,15 @@ void rpcif_hw_init(struct rpcif *rpc, bool hyperflash) * On H3 ES1.x, the value should be 0, while on others, * the value should be 7. */ - regmap_write(rpc->regmap, RPCIF_PHYCNT, RPCIF_PHYCNT_STRTIM(7) | - RPCIF_PHYCNT_PHYMEM(hyperflash ? 3 : 0) | 0x260); + if (rpc->type == RPCIF_RCAR_GEN3) { + regmap_write(rpc->regmap, RPCIF_PHYCNT, RPCIF_PHYCNT_STRTIM(7) | + RPCIF_PHYCNT_PHYMEM(hyperflash ? 3 : 0) | 0x260); + } else { + regmap_read(rpc->regmap, RPCIF_PHYCNT, &dummy); + dummy &= ~RPCIF_PHYCNT_PHYMEM_MASK; + dummy |= RPCIF_PHYCNT_PHYMEM(hyperflash ? 3 : 0) | 0x260; + regmap_write(rpc->regmap, RPCIF_PHYCNT, dummy); + } /* * NOTE: The 0x1511144 are undocumented bits, but they must be set @@ -282,9 +323,17 @@ void rpcif_hw_init(struct rpcif *rpc, bool hyperflash) regmap_update_bits(rpc->regmap, RPCIF_PHYINT, RPCIF_PHYINT_WPVAL, 0); - regmap_write(rpc->regmap, RPCIF_CMNCR, RPCIF_CMNCR_SFDE | - RPCIF_CMNCR_MOIIO_HIZ | RPCIF_CMNCR_IOFV_HIZ | - RPCIF_CMNCR_BSZ(hyperflash ? 1 : 0)); + if (rpc->type == RPCIF_RCAR_GEN3) + regmap_write(rpc->regmap, RPCIF_CMNCR, RPCIF_CMNCR_SFDE | + RPCIF_CMNCR_MOIIO_HIZ | RPCIF_CMNCR_IOFV_HIZ | + RPCIF_CMNCR_BSZ(hyperflash ? 1 : 0)); + else + regmap_write(rpc->regmap, RPCIF_CMNCR, RPCIF_CMNCR_SFDE | + RPCIF_CMNCR_MOIIO3(1) | RPCIF_CMNCR_MOIIO2(1) | + RPCIF_CMNCR_MOIIO1(1) | RPCIF_CMNCR_MOIIO0(1) | + RPCIF_CMNCR_IO3FV(2) | RPCIF_CMNCR_IO2FV(2) | + RPCIF_CMNCR_IO0FV(2) | RPCIF_CMNCR_BSZ(hyperflash ? 1 : 0)); + /* Set RCF after BSZ update */ regmap_write(rpc->regmap, RPCIF_DRCR, RPCIF_DRCR_RCF); /* Dummy read according to spec */ @@ -295,6 +344,8 @@ void rpcif_hw_init(struct rpcif *rpc, bool hyperflash) pm_runtime_put(rpc->dev); rpc->bus_size = hyperflash ? 2 : 1; + + return 0; } EXPORT_SYMBOL(rpcif_hw_init); @@ -657,7 +708,8 @@ static int rpcif_remove(struct platform_device *pdev) } static const struct of_device_id rpcif_of_match[] = { - { .compatible = "renesas,rcar-gen3-rpc-if", }, + { .compatible = "renesas,rcar-gen3-rpc-if", .data = (void *)RPCIF_RCAR_GEN3 }, + { .compatible = "renesas,rzg2l-rpc-if", .data = (void *)RPCIF_RZ_G2L }, {}, }; MODULE_DEVICE_TABLE(of, rpcif_of_match); diff --git a/drivers/mtd/hyperbus/rpc-if.c b/drivers/mtd/hyperbus/rpc-if.c index ecb050ba95cd..8daa296f6eb6 100644 --- a/drivers/mtd/hyperbus/rpc-if.c +++ b/drivers/mtd/hyperbus/rpc-if.c @@ -130,7 +130,9 @@ static int rpcif_hb_probe(struct platform_device *pdev) rpcif_enable_rpm(&hyperbus->rpc); - rpcif_hw_init(&hyperbus->rpc, true); + error = rpcif_hw_init(&hyperbus->rpc, true); + if (error) + return error; hyperbus->hbdev.map.size = hyperbus->rpc.size; hyperbus->hbdev.map.virt = hyperbus->rpc.dirmap; diff --git a/drivers/spi/spi-rpc-if.c b/drivers/spi/spi-rpc-if.c index 83796a4ead34..fe82f3575df4 100644 --- a/drivers/spi/spi-rpc-if.c +++ b/drivers/spi/spi-rpc-if.c @@ -156,7 +156,9 @@ static int rpcif_spi_probe(struct platform_device *pdev) ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_TX_QUAD | SPI_RX_QUAD; ctlr->flags = SPI_CONTROLLER_HALF_DUPLEX; - rpcif_hw_init(rpc, false); + error = rpcif_hw_init(rpc, false); + if (error) + return error; error = spi_register_controller(ctlr); if (error) { diff --git a/include/memory/renesas-rpc-if.h b/include/memory/renesas-rpc-if.h index 77c694a19149..7c93f5177532 100644 --- a/include/memory/renesas-rpc-if.h +++ b/include/memory/renesas-rpc-if.h @@ -57,6 +57,11 @@ struct rpcif_op { } data; }; +enum rpcif_type { + RPCIF_RCAR_GEN3, + RPCIF_RZ_G2L, +}; + struct rpcif { struct device *dev; void __iomem *base; @@ -64,6 +69,7 @@ struct rpcif { struct regmap *regmap; struct reset_control *rstc; size_t size; + enum rpcif_type type; enum rpcif_data_dir dir; u8 bus_size; void *buffer; @@ -78,7 +84,7 @@ struct rpcif { }; int rpcif_sw_init(struct rpcif *rpc, struct device *dev); -void rpcif_hw_init(struct rpcif *rpc, bool hyperflash); +int rpcif_hw_init(struct rpcif *rpc, bool hyperflash); void rpcif_prepare(struct rpcif *rpc, const struct rpcif_op *op, u64 *offs, size_t *len); int rpcif_manual_xfer(struct rpcif *rpc); From 63d5bc420f460162cc5318136c2bddba87cfbb29 Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Tue, 15 Jun 2021 22:23:44 +0200 Subject: [PATCH 11/94] docs: staging/tee.rst: add a section on OP-TEE notifications Adds a section on notifications used by OP-TEE, synchronous and asynchronous. Reviewed-by: Sumit Garg Acked-by: Marc Zyngier Signed-off-by: Jens Wiklander --- Documentation/staging/tee.rst | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/Documentation/staging/tee.rst b/Documentation/staging/tee.rst index 4d4b5f889603..3c63d8dcd61e 100644 --- a/Documentation/staging/tee.rst +++ b/Documentation/staging/tee.rst @@ -184,6 +184,36 @@ order to support device enumeration. In other words, OP-TEE driver invokes this application to retrieve a list of Trusted Applications which can be registered as devices on the TEE bus. +OP-TEE notifications +-------------------- + +There are two kinds of notifications that secure world can use to make +normal world aware of some event. + +1. Synchronous notifications delivered with ``OPTEE_RPC_CMD_NOTIFICATION`` + using the ``OPTEE_RPC_NOTIFICATION_SEND`` parameter. +2. Asynchronous notifications delivered with a combination of a non-secure + edge-triggered interrupt and a fast call from the non-secure interrupt + handler. + +Synchronous notifications are limited by depending on RPC for delivery, +this is only usable when secure world is entered with a yielding call via +``OPTEE_SMC_CALL_WITH_ARG``. This excludes such notifications from secure +world interrupt handlers. + +An asynchronous notification is delivered via a non-secure edge-triggered +interrupt to an interrupt handler registered in the OP-TEE driver. The +actual notification value are retrieved with the fast call +``OPTEE_SMC_GET_ASYNC_NOTIF_VALUE``. Note that one interrupt can represent +multiple notifications. + +One notification value ``OPTEE_SMC_ASYNC_NOTIF_VALUE_DO_BOTTOM_HALF`` has a +special meaning. When this value is received it means that normal world is +supposed to make a yielding call ``OPTEE_MSG_CMD_DO_BOTTOM_HALF``. This +call is done from the thread assisting the interrupt handler. This is a +building block for OP-TEE OS in secure world to implement the top half and +bottom half style of device drivers. + AMD-TEE driver ============== From ff5fdc34d0aed9931c01e6d50f7f873ecf8df8d3 Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Tue, 15 Jun 2021 22:23:48 +0200 Subject: [PATCH 12/94] dt-bindings: arm: optee: add interrupt property Adds an optional interrupt property to the optee binding. Reviewed-by: Sumit Garg Reviewed-by: Rob Herring Acked-by: Marc Zyngier Signed-off-by: Jens Wiklander --- .../devicetree/bindings/arm/firmware/linaro,optee-tz.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/firmware/linaro,optee-tz.yaml b/Documentation/devicetree/bindings/arm/firmware/linaro,optee-tz.yaml index c24047c1fdd5..9a426110a14a 100644 --- a/Documentation/devicetree/bindings/arm/firmware/linaro,optee-tz.yaml +++ b/Documentation/devicetree/bindings/arm/firmware/linaro,optee-tz.yaml @@ -24,6 +24,12 @@ properties: compatible: const: linaro,optee-tz + interrupts: + maxItems: 1 + description: | + This interrupt which is used to signal an event by the secure world + software is expected to be edge-triggered. + method: enum: [smc, hvc] description: | @@ -42,10 +48,12 @@ additionalProperties: false examples: - | + #include firmware { optee { compatible = "linaro,optee-tz"; method = "smc"; + interrupts = ; }; }; From f18397ab3ae23e8e43bba9986e66af6d4497f2ad Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Tue, 15 Jun 2021 22:23:50 +0200 Subject: [PATCH 13/94] tee: fix put order in teedev_close_context() Prior to this patch was teedev_close_context() calling tee_device_put() before teedev_ctx_put() leading to teedev_ctx_release() accessing ctx->teedev just after the reference counter was decreased on the teedev. Fix this by calling teedev_ctx_put() before tee_device_put(). Fixes: 217e0250cccb ("tee: use reference counting for tee_context") Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- drivers/tee/tee_core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 2b37bc408fc3..85102d12d716 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -98,8 +98,10 @@ void teedev_ctx_put(struct tee_context *ctx) static void teedev_close_context(struct tee_context *ctx) { - tee_device_put(ctx->teedev); + struct tee_device *teedev = ctx->teedev; + teedev_ctx_put(ctx); + tee_device_put(teedev); } static int tee_open(struct inode *inode, struct file *filp) From 1e2c3ef0496e72ba9001da5fd1b7ed56ccb30597 Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Mon, 4 Oct 2021 16:11:52 +0200 Subject: [PATCH 14/94] tee: export teedev_open() and teedev_close_context() Exports the two functions teedev_open() and teedev_close_context() in order to make it easier to create a driver internal struct tee_context. Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- drivers/tee/tee_core.c | 6 ++++-- include/linux/tee_drv.h | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 85102d12d716..3fc426dad2df 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -43,7 +43,7 @@ static DEFINE_SPINLOCK(driver_lock); static struct class *tee_class; static dev_t tee_devt; -static struct tee_context *teedev_open(struct tee_device *teedev) +struct tee_context *teedev_open(struct tee_device *teedev) { int rc; struct tee_context *ctx; @@ -70,6 +70,7 @@ static struct tee_context *teedev_open(struct tee_device *teedev) return ERR_PTR(rc); } +EXPORT_SYMBOL_GPL(teedev_open); void teedev_ctx_get(struct tee_context *ctx) { @@ -96,13 +97,14 @@ void teedev_ctx_put(struct tee_context *ctx) kref_put(&ctx->refcount, teedev_ctx_release); } -static void teedev_close_context(struct tee_context *ctx) +void teedev_close_context(struct tee_context *ctx) { struct tee_device *teedev = ctx->teedev; teedev_ctx_put(ctx); tee_device_put(teedev); } +EXPORT_SYMBOL_GPL(teedev_close_context); static int tee_open(struct inode *inode, struct file *filp) { diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index a1f03461369b..468a7d83dc6c 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -587,4 +587,18 @@ struct tee_client_driver { #define to_tee_client_driver(d) \ container_of(d, struct tee_client_driver, driver) +/** + * teedev_open() - Open a struct tee_device + * @teedev: Device to open + * + * @return a pointer to struct tee_context on success or an ERR_PTR on failure. + */ +struct tee_context *teedev_open(struct tee_device *teedev); + +/** + * teedev_close_context() - closes a struct tee_context + * @ctx: The struct tee_context to close + */ +void teedev_close_context(struct tee_context *ctx); + #endif /*__TEE_DRV_H*/ From 787c80cc7b22804aa370f04a19f9fe0fa98b1e49 Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Tue, 15 Jun 2021 22:23:53 +0200 Subject: [PATCH 15/94] optee: separate notification functions Renames struct optee_wait_queue to struct optee_notif and all related functions to optee_notif_*(). The implementation is changed to allow sending a notification from an atomic state, that is from the top half of an interrupt handler. Waiting for keys is currently only used when secure world is waiting for a mutex or condition variable. The old implementation could handle any 32-bit key while this new implementation is restricted to only 8 bits or the maximum value 255. A upper value is needed since a bitmap is allocated to allow an interrupt handler to only set a bit in case the waiter hasn't had the time yet to allocate and register a completion. The keys are currently only representing secure world threads which number usually are never even close to 255 so it should be safe for now. In future ABI updates the maximum value of the key will be communicated while the driver is initializing. Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- drivers/tee/optee/Makefile | 1 + drivers/tee/optee/core.c | 2 +- drivers/tee/optee/ffa_abi.c | 6 +- drivers/tee/optee/notif.c | 125 ++++++++++++++++++++++++++++++ drivers/tee/optee/optee_private.h | 26 +++++-- drivers/tee/optee/optee_rpc_cmd.h | 31 ++++---- drivers/tee/optee/rpc.c | 71 ++--------------- drivers/tee/optee/smc_abi.c | 10 ++- 8 files changed, 181 insertions(+), 91 deletions(-) create mode 100644 drivers/tee/optee/notif.c diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile index 66b8a17f14c4..a6eff388d300 100644 --- a/drivers/tee/optee/Makefile +++ b/drivers/tee/optee/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_OPTEE) += optee.o optee-objs += core.o optee-objs += call.o +optee-objs += notif.o optee-objs += rpc.o optee-objs += supp.o optee-objs += device.o diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index ab2edfcc6c70..ba7300ca9ddf 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -159,6 +159,7 @@ void optee_remove_common(struct optee *optee) /* Unregister OP-TEE specific client devices on TEE bus */ optee_unregister_devices(); + optee_notif_uninit(optee); /* * The two devices have to be unregistered before we can free the * other resources. @@ -167,7 +168,6 @@ void optee_remove_common(struct optee *optee) tee_device_unregister(optee->teedev); tee_shm_pool_free(optee->pool); - optee_wait_queue_exit(&optee->wait_queue); optee_supp_uninit(&optee->supp); mutex_destroy(&optee->call_queue.mutex); } diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c index 45424824e0f9..3577781e5df7 100644 --- a/drivers/tee/optee/ffa_abi.c +++ b/drivers/tee/optee/ffa_abi.c @@ -856,9 +856,13 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev) mutex_init(&optee->ffa.mutex); mutex_init(&optee->call_queue.mutex); INIT_LIST_HEAD(&optee->call_queue.waiters); - optee_wait_queue_init(&optee->wait_queue); optee_supp_init(&optee->supp); ffa_dev_set_drvdata(ffa_dev, optee); + rc = optee_notif_init(optee, OPTEE_DEFAULT_MAX_NOTIF_VALUE); + if (rc) { + optee_ffa_remove(ffa_dev); + return rc; + } rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES); if (rc) { diff --git a/drivers/tee/optee/notif.c b/drivers/tee/optee/notif.c new file mode 100644 index 000000000000..a28fa03dcd0e --- /dev/null +++ b/drivers/tee/optee/notif.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015-2021, Linaro Limited + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include "optee_private.h" + +struct notif_entry { + struct list_head link; + struct completion c; + u_int key; +}; + +static bool have_key(struct optee *optee, u_int key) +{ + struct notif_entry *entry; + + list_for_each_entry(entry, &optee->notif.db, link) + if (entry->key == key) + return true; + + return false; +} + +int optee_notif_wait(struct optee *optee, u_int key) +{ + unsigned long flags; + struct notif_entry *entry; + int rc = 0; + + if (key > optee->notif.max_key) + return -EINVAL; + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + init_completion(&entry->c); + entry->key = key; + + spin_lock_irqsave(&optee->notif.lock, flags); + + /* + * If the bit is already set it means that the key has already + * been posted and we must not wait. + */ + if (test_bit(key, optee->notif.bitmap)) { + clear_bit(key, optee->notif.bitmap); + goto out; + } + + /* + * Check if someone is already waiting for this key. If there is + * it's a programming error. + */ + if (have_key(optee, key)) { + rc = -EBUSY; + goto out; + } + + list_add_tail(&entry->link, &optee->notif.db); + + /* + * Unlock temporarily and wait for completion. + */ + spin_unlock_irqrestore(&optee->notif.lock, flags); + wait_for_completion(&entry->c); + spin_lock_irqsave(&optee->notif.lock, flags); + + list_del(&entry->link); +out: + spin_unlock_irqrestore(&optee->notif.lock, flags); + + kfree(entry); + + return rc; +} + +int optee_notif_send(struct optee *optee, u_int key) +{ + unsigned long flags; + struct notif_entry *entry; + + if (key > optee->notif.max_key) + return -EINVAL; + + spin_lock_irqsave(&optee->notif.lock, flags); + + list_for_each_entry(entry, &optee->notif.db, link) + if (entry->key == key) { + complete(&entry->c); + goto out; + } + + /* Only set the bit in case there where nobody waiting */ + set_bit(key, optee->notif.bitmap); +out: + spin_unlock_irqrestore(&optee->notif.lock, flags); + + return 0; +} + +int optee_notif_init(struct optee *optee, u_int max_key) +{ + spin_lock_init(&optee->notif.lock); + INIT_LIST_HEAD(&optee->notif.db); + optee->notif.bitmap = bitmap_zalloc(max_key, GFP_KERNEL); + if (!optee->notif.bitmap) + return -ENOMEM; + + optee->notif.max_key = max_key; + + return 0; +} + +void optee_notif_uninit(struct optee *optee) +{ + kfree(optee->notif.bitmap); +} diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index 6660e05298db..68fd28f8c6e9 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -28,6 +28,13 @@ #define TEEC_ORIGIN_COMMS 0x00000002 +/* + * This value should be larger than the number threads in secure world to + * meet the need from secure world. The number of threads in secure world + * are usually not even close to 255 so we should be safe for now. + */ +#define OPTEE_DEFAULT_MAX_NOTIF_VALUE 255 + typedef void (optee_invoke_fn)(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, @@ -44,10 +51,12 @@ struct optee_call_queue { struct list_head waiters; }; -struct optee_wait_queue { - /* Serializes access to this struct */ - struct mutex mu; +struct optee_notif { + u_int max_key; + /* Serializes access to the elements below in this struct */ + spinlock_t lock; struct list_head db; + u_long *bitmap; }; /** @@ -129,8 +138,7 @@ struct optee_ops { * @smc: specific to SMC ABI * @ffa: specific to FF-A ABI * @call_queue: queue of threads waiting to call @invoke_fn - * @wait_queue: queue of threads from secure world waiting for a - * secure world sync object + * @notif: notification synchronization struct * @supp: supplicant synchronization struct for RPC to supplicant * @pool: shared memory pool * @rpc_arg_count: If > 0 number of RPC parameters to make room for @@ -147,7 +155,7 @@ struct optee { struct optee_ffa ffa; }; struct optee_call_queue call_queue; - struct optee_wait_queue wait_queue; + struct optee_notif notif; struct optee_supp supp; struct tee_shm_pool *pool; unsigned int rpc_arg_count; @@ -185,8 +193,10 @@ struct optee_call_ctx { size_t num_entries; }; -void optee_wait_queue_init(struct optee_wait_queue *wq); -void optee_wait_queue_exit(struct optee_wait_queue *wq); +int optee_notif_init(struct optee *optee, u_int max_key); +void optee_notif_uninit(struct optee *optee); +int optee_notif_wait(struct optee *optee, u_int key); +int optee_notif_send(struct optee *optee, u_int key); u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params, struct tee_param *param); diff --git a/drivers/tee/optee/optee_rpc_cmd.h b/drivers/tee/optee/optee_rpc_cmd.h index b8275140cef8..f3f06e0994a7 100644 --- a/drivers/tee/optee/optee_rpc_cmd.h +++ b/drivers/tee/optee/optee_rpc_cmd.h @@ -28,24 +28,27 @@ #define OPTEE_RPC_CMD_GET_TIME 3 /* - * Wait queue primitive, helper for secure world to implement a wait queue. + * Notification from/to secure world. * - * If secure world needs to wait for a secure world mutex it issues a sleep - * request instead of spinning in secure world. Conversely is a wakeup - * request issued when a secure world mutex with a thread waiting thread is - * unlocked. + * If secure world needs to wait for something, for instance a mutex, it + * does a notification wait request instead of spinning in secure world. + * Conversely can a synchronous notification can be sent when a secure + * world mutex with a thread waiting thread is unlocked. * - * Waiting on a key - * [in] value[0].a OPTEE_RPC_WAIT_QUEUE_SLEEP - * [in] value[0].b Wait key + * This interface can also be used to wait for a asynchronous notification + * which instead is sent via a non-secure interrupt. * - * Waking up a key - * [in] value[0].a OPTEE_RPC_WAIT_QUEUE_WAKEUP - * [in] value[0].b Wakeup key + * Waiting on notification + * [in] value[0].a OPTEE_RPC_NOTIFICATION_WAIT + * [in] value[0].b notification value + * + * Sending a synchronous notification + * [in] value[0].a OPTEE_RPC_NOTIFICATION_SEND + * [in] value[0].b notification value */ -#define OPTEE_RPC_CMD_WAIT_QUEUE 4 -#define OPTEE_RPC_WAIT_QUEUE_SLEEP 0 -#define OPTEE_RPC_WAIT_QUEUE_WAKEUP 1 +#define OPTEE_RPC_CMD_NOTIFICATION 4 +#define OPTEE_RPC_NOTIFICATION_WAIT 0 +#define OPTEE_RPC_NOTIFICATION_SEND 1 /* * Suspend execution diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c index cd642e340eaf..e69bc6380683 100644 --- a/drivers/tee/optee/rpc.c +++ b/drivers/tee/optee/rpc.c @@ -12,23 +12,6 @@ #include "optee_private.h" #include "optee_rpc_cmd.h" -struct wq_entry { - struct list_head link; - struct completion c; - u32 key; -}; - -void optee_wait_queue_init(struct optee_wait_queue *priv) -{ - mutex_init(&priv->mu); - INIT_LIST_HEAD(&priv->db); -} - -void optee_wait_queue_exit(struct optee_wait_queue *priv) -{ - mutex_destroy(&priv->mu); -} - static void handle_rpc_func_cmd_get_time(struct optee_msg_arg *arg) { struct timespec64 ts; @@ -144,48 +127,6 @@ static void handle_rpc_func_cmd_i2c_transfer(struct tee_context *ctx, } #endif -static struct wq_entry *wq_entry_get(struct optee_wait_queue *wq, u32 key) -{ - struct wq_entry *w; - - mutex_lock(&wq->mu); - - list_for_each_entry(w, &wq->db, link) - if (w->key == key) - goto out; - - w = kmalloc(sizeof(*w), GFP_KERNEL); - if (w) { - init_completion(&w->c); - w->key = key; - list_add_tail(&w->link, &wq->db); - } -out: - mutex_unlock(&wq->mu); - return w; -} - -static void wq_sleep(struct optee_wait_queue *wq, u32 key) -{ - struct wq_entry *w = wq_entry_get(wq, key); - - if (w) { - wait_for_completion(&w->c); - mutex_lock(&wq->mu); - list_del(&w->link); - mutex_unlock(&wq->mu); - kfree(w); - } -} - -static void wq_wakeup(struct optee_wait_queue *wq, u32 key) -{ - struct wq_entry *w = wq_entry_get(wq, key); - - if (w) - complete(&w->c); -} - static void handle_rpc_func_cmd_wq(struct optee *optee, struct optee_msg_arg *arg) { @@ -197,11 +138,13 @@ static void handle_rpc_func_cmd_wq(struct optee *optee, goto bad; switch (arg->params[0].u.value.a) { - case OPTEE_RPC_WAIT_QUEUE_SLEEP: - wq_sleep(&optee->wait_queue, arg->params[0].u.value.b); + case OPTEE_RPC_NOTIFICATION_WAIT: + if (optee_notif_wait(optee, arg->params[0].u.value.b)) + goto bad; break; - case OPTEE_RPC_WAIT_QUEUE_WAKEUP: - wq_wakeup(&optee->wait_queue, arg->params[0].u.value.b); + case OPTEE_RPC_NOTIFICATION_SEND: + if (optee_notif_send(optee, arg->params[0].u.value.b)) + goto bad; break; default: goto bad; @@ -319,7 +262,7 @@ void optee_rpc_cmd(struct tee_context *ctx, struct optee *optee, case OPTEE_RPC_CMD_GET_TIME: handle_rpc_func_cmd_get_time(arg); break; - case OPTEE_RPC_CMD_WAIT_QUEUE: + case OPTEE_RPC_CMD_NOTIFICATION: handle_rpc_func_cmd_wq(optee, arg); break; case OPTEE_RPC_CMD_SUSPEND: diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c index 6196d7c3888f..00a7ff00a7c0 100644 --- a/drivers/tee/optee/smc_abi.c +++ b/drivers/tee/optee/smc_abi.c @@ -1288,11 +1288,17 @@ static int optee_probe(struct platform_device *pdev) mutex_init(&optee->call_queue.mutex); INIT_LIST_HEAD(&optee->call_queue.waiters); - optee_wait_queue_init(&optee->wait_queue); optee_supp_init(&optee->supp); optee->smc.memremaped_shm = memremaped_shm; optee->pool = pool; + platform_set_drvdata(pdev, optee); + rc = optee_notif_init(optee, OPTEE_DEFAULT_MAX_NOTIF_VALUE); + if (rc) { + optee_remove(pdev); + return rc; + } + /* * Ensure that there are no pre-existing shm objects before enabling * the shm cache so that there's no chance of receiving an invalid @@ -1307,8 +1313,6 @@ static int optee_probe(struct platform_device *pdev) if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) pr_info("dynamic shared memory is enabled\n"); - platform_set_drvdata(pdev, optee); - rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES); if (rc) { optee_smc_remove(pdev); From 6749e69c4dadd352bdfdb306513aa48cc15fbdd6 Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Tue, 15 Jun 2021 22:23:54 +0200 Subject: [PATCH 16/94] optee: add asynchronous notifications Adds support for asynchronous notifications from secure world to normal world. This allows a design with a top half and bottom half type of driver where the top half runs in secure interrupt context and a notifications tells normal world to schedule a yielding call to do the bottom half processing. The protocol is defined in optee_msg.h optee_rpc_cmd.h and optee_smc.h. A notification consists of a 32-bit value which normal world can retrieve using a fastcall into secure world. The value OPTEE_SMC_ASYNC_NOTIF_VALUE_DO_BOTTOM_HALF (0) has a special meaning. When this value is sent it means that normal world is supposed to make a yielding call OPTEE_MSG_CMD_DO_BOTTOM_HALF. Notification capability is negotiated while the driver is initialized. If both sides supports these notifications then they are enabled. An interrupt is used to notify the driver that there are asynchronous notifications pending. The maximum needed notification value is communicated at this stage. This allows scaling up when needed. Reviewed-by: Sumit Garg Acked-by: Ard Biesheuvel Signed-off-by: Jens Wiklander --- drivers/tee/optee/optee_msg.h | 9 ++ drivers/tee/optee/optee_private.h | 2 + drivers/tee/optee/optee_smc.h | 75 +++++++++- drivers/tee/optee/smc_abi.c | 239 +++++++++++++++++++++++++----- 4 files changed, 289 insertions(+), 36 deletions(-) diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h index 2422e185d400..70e9cc2ee96b 100644 --- a/drivers/tee/optee/optee_msg.h +++ b/drivers/tee/optee/optee_msg.h @@ -318,6 +318,13 @@ struct optee_msg_arg { * [in] param[0].u.rmem.shm_ref holds shared memory reference * [in] param[0].u.rmem.offs 0 * [in] param[0].u.rmem.size 0 + * + * OPTEE_MSG_CMD_DO_BOTTOM_HALF does the scheduled bottom half processing + * of a driver. + * + * OPTEE_MSG_CMD_STOP_ASYNC_NOTIF informs secure world that from now is + * normal world unable to process asynchronous notifications. Typically + * used when the driver is shut down. */ #define OPTEE_MSG_CMD_OPEN_SESSION 0 #define OPTEE_MSG_CMD_INVOKE_COMMAND 1 @@ -325,6 +332,8 @@ struct optee_msg_arg { #define OPTEE_MSG_CMD_CANCEL 3 #define OPTEE_MSG_CMD_REGISTER_SHM 4 #define OPTEE_MSG_CMD_UNREGISTER_SHM 5 +#define OPTEE_MSG_CMD_DO_BOTTOM_HALF 6 +#define OPTEE_MSG_CMD_STOP_ASYNC_NOTIF 7 #define OPTEE_MSG_FUNCID_CALL_WITH_ARG 0x0004 #endif /* _OPTEE_MSG_H */ diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index 68fd28f8c6e9..46f74ab07c7e 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -53,6 +53,7 @@ struct optee_call_queue { struct optee_notif { u_int max_key; + struct tee_context *ctx; /* Serializes access to the elements below in this struct */ spinlock_t lock; struct list_head db; @@ -88,6 +89,7 @@ struct optee_smc { optee_invoke_fn *invoke_fn; void *memremaped_shm; u32 sec_caps; + unsigned int notif_irq; }; /** diff --git a/drivers/tee/optee/optee_smc.h b/drivers/tee/optee/optee_smc.h index 80eb763a8a80..d44a6ae994f8 100644 --- a/drivers/tee/optee/optee_smc.h +++ b/drivers/tee/optee/optee_smc.h @@ -107,6 +107,12 @@ struct optee_smc_call_get_os_revision_result { /* * Call with struct optee_msg_arg as argument * + * When calling this function normal world has a few responsibilities: + * 1. It must be able to handle eventual RPCs + * 2. Non-secure interrupts should not be masked + * 3. If asynchronous notifications has been negotiated successfully, then + * asynchronous notifications should be unmasked during this call. + * * Call register usage: * a0 SMC Function ID, OPTEE_SMC*CALL_WITH_ARG * a1 Upper 32 bits of a 64-bit physical pointer to a struct optee_msg_arg @@ -195,7 +201,8 @@ struct optee_smc_get_shm_config_result { * Normal return register usage: * a0 OPTEE_SMC_RETURN_OK * a1 bitfield of secure world capabilities OPTEE_SMC_SEC_CAP_* - * a2-7 Preserved + * a2 The maximum secure world notification number + * a3-7 Preserved * * Error return register usage: * a0 OPTEE_SMC_RETURN_ENOTAVAIL, can't use the capabilities from normal world @@ -218,6 +225,8 @@ struct optee_smc_get_shm_config_result { #define OPTEE_SMC_SEC_CAP_VIRTUALIZATION BIT(3) /* Secure world supports Shared Memory with a NULL reference */ #define OPTEE_SMC_SEC_CAP_MEMREF_NULL BIT(4) +/* Secure world supports asynchronous notification of normal world */ +#define OPTEE_SMC_SEC_CAP_ASYNC_NOTIF BIT(5) #define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES 9 #define OPTEE_SMC_EXCHANGE_CAPABILITIES \ @@ -226,8 +235,8 @@ struct optee_smc_get_shm_config_result { struct optee_smc_exchange_capabilities_result { unsigned long status; unsigned long capabilities; + unsigned long max_notif_value; unsigned long reserved0; - unsigned long reserved1; }; /* @@ -319,6 +328,68 @@ struct optee_smc_disable_shm_cache_result { #define OPTEE_SMC_GET_THREAD_COUNT \ OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_THREAD_COUNT) +/* + * Inform OP-TEE that normal world is able to receive asynchronous + * notifications. + * + * Call requests usage: + * a0 SMC Function ID, OPTEE_SMC_ENABLE_ASYNC_NOTIF + * a1-6 Not used + * a7 Hypervisor Client ID register + * + * Normal return register usage: + * a0 OPTEE_SMC_RETURN_OK + * a1-7 Preserved + * + * Not supported return register usage: + * a0 OPTEE_SMC_RETURN_ENOTAVAIL + * a1-7 Preserved + */ +#define OPTEE_SMC_FUNCID_ENABLE_ASYNC_NOTIF 16 +#define OPTEE_SMC_ENABLE_ASYNC_NOTIF \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_ENABLE_ASYNC_NOTIF) + +/* + * Retrieve a value of notifications pending since the last call of this + * function. + * + * OP-TEE keeps a record of all posted values. When an interrupt is + * received which indicates that there are posted values this function + * should be called until all pended values have been retrieved. When a + * value is retrieved, it's cleared from the record in secure world. + * + * Call requests usage: + * a0 SMC Function ID, OPTEE_SMC_GET_ASYNC_NOTIF_VALUE + * a1-6 Not used + * a7 Hypervisor Client ID register + * + * Normal return register usage: + * a0 OPTEE_SMC_RETURN_OK + * a1 value + * a2 Bit[0]: OPTEE_SMC_ASYNC_NOTIF_VALUE_VALID if the value in a1 is + * valid, else 0 if no values where pending + * a2 Bit[1]: OPTEE_SMC_ASYNC_NOTIF_VALUE_PENDING if another value is + * pending, else 0. + * Bit[31:2]: MBZ + * a3-7 Preserved + * + * Not supported return register usage: + * a0 OPTEE_SMC_RETURN_ENOTAVAIL + * a1-7 Preserved + */ +#define OPTEE_SMC_ASYNC_NOTIF_VALUE_VALID BIT(0) +#define OPTEE_SMC_ASYNC_NOTIF_VALUE_PENDING BIT(1) + +/* + * Notification that OP-TEE expects a yielding call to do some bottom half + * work in a driver. + */ +#define OPTEE_SMC_ASYNC_NOTIF_VALUE_DO_BOTTOM_HALF 0 + +#define OPTEE_SMC_FUNCID_GET_ASYNC_NOTIF_VALUE 17 +#define OPTEE_SMC_GET_ASYNC_NOTIF_VALUE \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_ASYNC_NOTIF_VALUE) + /* * Resume from RPC (for example after processing a foreign interrupt) * diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c index 00a7ff00a7c0..92759d70bfce 100644 --- a/drivers/tee/optee/smc_abi.c +++ b/drivers/tee/optee/smc_abi.c @@ -8,13 +8,16 @@ #include #include +#include #include -#include +#include #include #include #include +#include #include #include +#include #include #include #include @@ -34,7 +37,8 @@ * 2. Low level support functions to register shared memory in secure world * 3. Dynamic shared memory pool based on alloc_pages() * 4. Do a normal scheduled call into secure world - * 5. Driver initialization. + * 5. Asynchronous notification + * 6. Driver initialization. */ #define OPTEE_SHM_NUM_PRIV_PAGES CONFIG_OPTEE_SHM_NUM_PRIV_PAGES @@ -875,10 +879,137 @@ static int optee_smc_do_call_with_arg(struct tee_context *ctx, return rc; } +static int simple_call_with_arg(struct tee_context *ctx, u32 cmd) +{ + struct optee_msg_arg *msg_arg; + struct tee_shm *shm; + + shm = optee_get_msg_arg(ctx, 0, &msg_arg); + if (IS_ERR(shm)) + return PTR_ERR(shm); + + msg_arg->cmd = cmd; + optee_smc_do_call_with_arg(ctx, shm); + + tee_shm_free(shm); + return 0; +} + +static int optee_smc_do_bottom_half(struct tee_context *ctx) +{ + return simple_call_with_arg(ctx, OPTEE_MSG_CMD_DO_BOTTOM_HALF); +} + +static int optee_smc_stop_async_notif(struct tee_context *ctx) +{ + return simple_call_with_arg(ctx, OPTEE_MSG_CMD_STOP_ASYNC_NOTIF); +} + /* - * 5. Driver initialization + * 5. Asynchronous notification + */ + +static u32 get_async_notif_value(optee_invoke_fn *invoke_fn, bool *value_valid, + bool *value_pending) +{ + struct arm_smccc_res res; + + invoke_fn(OPTEE_SMC_GET_ASYNC_NOTIF_VALUE, 0, 0, 0, 0, 0, 0, 0, &res); + + if (res.a0) + return 0; + *value_valid = (res.a2 & OPTEE_SMC_ASYNC_NOTIF_VALUE_VALID); + *value_pending = (res.a2 & OPTEE_SMC_ASYNC_NOTIF_VALUE_PENDING); + return res.a1; +} + +static irqreturn_t notif_irq_handler(int irq, void *dev_id) +{ + struct optee *optee = dev_id; + bool do_bottom_half = false; + bool value_valid; + bool value_pending; + u32 value; + + do { + value = get_async_notif_value(optee->smc.invoke_fn, + &value_valid, &value_pending); + if (!value_valid) + break; + + if (value == OPTEE_SMC_ASYNC_NOTIF_VALUE_DO_BOTTOM_HALF) + do_bottom_half = true; + else + optee_notif_send(optee, value); + } while (value_pending); + + if (do_bottom_half) + return IRQ_WAKE_THREAD; + return IRQ_HANDLED; +} + +static irqreturn_t notif_irq_thread_fn(int irq, void *dev_id) +{ + struct optee *optee = dev_id; + + optee_smc_do_bottom_half(optee->notif.ctx); + + return IRQ_HANDLED; +} + +static int optee_smc_notif_init_irq(struct optee *optee, u_int irq) +{ + struct tee_context *ctx; + int rc; + + ctx = teedev_open(optee->teedev); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + optee->notif.ctx = ctx; + rc = request_threaded_irq(irq, notif_irq_handler, + notif_irq_thread_fn, + 0, "optee_notification", optee); + if (rc) + goto err_close_ctx; + + optee->smc.notif_irq = irq; + + return 0; + +err_close_ctx: + teedev_close_context(optee->notif.ctx); + optee->notif.ctx = NULL; + + return rc; +} + +static void optee_smc_notif_uninit_irq(struct optee *optee) +{ + if (optee->notif.ctx) { + optee_smc_stop_async_notif(optee->notif.ctx); + if (optee->smc.notif_irq) { + free_irq(optee->smc.notif_irq, optee); + irq_dispose_mapping(optee->smc.notif_irq); + } + + /* + * The thread normally working with optee->notif.ctx was + * stopped with free_irq() above. + * + * Note we're not using teedev_close_context() or + * tee_client_close_context() since we have already called + * tee_device_put() while initializing to avoid a circular + * reference counting. + */ + teedev_close_context(optee->notif.ctx); + } +} + +/* + * 6. Driver initialization * - * During driver inititialization is secure world probed to find out which + * During driver initialization is secure world probed to find out which * features it supports so the driver can be initialized with a matching * configuration. This involves for instance support for dynamic shared * memory instead of a static memory carvout. @@ -950,6 +1081,17 @@ static const struct optee_ops optee_ops = { .from_msg_param = optee_from_msg_param, }; +static int enable_async_notif(optee_invoke_fn *invoke_fn) +{ + struct arm_smccc_res res; + + invoke_fn(OPTEE_SMC_ENABLE_ASYNC_NOTIF, 0, 0, 0, 0, 0, 0, 0, &res); + + if (res.a0) + return -EINVAL; + return 0; +} + static bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn) { struct arm_smccc_res res; @@ -999,7 +1141,7 @@ static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn) } static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, - u32 *sec_caps) + u32 *sec_caps, u32 *max_notif_value) { union { struct arm_smccc_res smccc; @@ -1022,6 +1164,11 @@ static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, return false; *sec_caps = res.result.capabilities; + if (*sec_caps & OPTEE_SMC_SEC_CAP_ASYNC_NOTIF) + *max_notif_value = res.result.max_notif_value; + else + *max_notif_value = OPTEE_DEFAULT_MAX_NOTIF_VALUE; + return true; } @@ -1186,6 +1333,8 @@ static int optee_smc_remove(struct platform_device *pdev) */ optee_disable_shm_cache(optee); + optee_smc_notif_uninit_irq(optee); + optee_remove_common(optee); if (optee->smc.memremaped_shm) @@ -1215,6 +1364,7 @@ static int optee_probe(struct platform_device *pdev) struct optee *optee = NULL; void *memremaped_shm = NULL; struct tee_device *teedev; + u32 max_notif_value; u32 sec_caps; int rc; @@ -1234,7 +1384,8 @@ static int optee_probe(struct platform_device *pdev) return -EINVAL; } - if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps)) { + if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps, + &max_notif_value)) { pr_warn("capabilities mismatch\n"); return -EINVAL; } @@ -1257,7 +1408,7 @@ static int optee_probe(struct platform_device *pdev) optee = kzalloc(sizeof(*optee), GFP_KERNEL); if (!optee) { rc = -ENOMEM; - goto err; + goto err_free_pool; } optee->ops = &optee_ops; @@ -1267,24 +1418,24 @@ static int optee_probe(struct platform_device *pdev) teedev = tee_device_alloc(&optee_clnt_desc, NULL, pool, optee); if (IS_ERR(teedev)) { rc = PTR_ERR(teedev); - goto err; + goto err_free_optee; } optee->teedev = teedev; teedev = tee_device_alloc(&optee_supp_desc, NULL, pool, optee); if (IS_ERR(teedev)) { rc = PTR_ERR(teedev); - goto err; + goto err_unreg_teedev; } optee->supp_teedev = teedev; rc = tee_device_register(optee->teedev); if (rc) - goto err; + goto err_unreg_supp_teedev; rc = tee_device_register(optee->supp_teedev); if (rc) - goto err; + goto err_unreg_supp_teedev; mutex_init(&optee->call_queue.mutex); INIT_LIST_HEAD(&optee->call_queue.waiters); @@ -1293,10 +1444,27 @@ static int optee_probe(struct platform_device *pdev) optee->pool = pool; platform_set_drvdata(pdev, optee); - rc = optee_notif_init(optee, OPTEE_DEFAULT_MAX_NOTIF_VALUE); - if (rc) { - optee_remove(pdev); - return rc; + rc = optee_notif_init(optee, max_notif_value); + if (rc) + goto err_supp_uninit; + + if (sec_caps & OPTEE_SMC_SEC_CAP_ASYNC_NOTIF) { + unsigned int irq; + + rc = platform_get_irq(pdev, 0); + if (rc < 0) { + pr_err("platform_get_irq: ret %d\n", rc); + goto err_notif_uninit; + } + irq = rc; + + rc = optee_smc_notif_init_irq(optee, irq); + if (rc) { + irq_dispose_mapping(irq); + goto err_notif_uninit; + } + enable_async_notif(optee->smc.invoke_fn); + pr_info("Asynchronous notifications enabled\n"); } /* @@ -1314,28 +1482,31 @@ static int optee_probe(struct platform_device *pdev) pr_info("dynamic shared memory is enabled\n"); rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES); - if (rc) { - optee_smc_remove(pdev); - return rc; - } + if (rc) + goto err_disable_shm_cache; pr_info("initialized driver\n"); return 0; -err: - if (optee) { - /* - * tee_device_unregister() is safe to call even if the - * devices hasn't been registered with - * tee_device_register() yet. - */ - tee_device_unregister(optee->supp_teedev); - tee_device_unregister(optee->teedev); - kfree(optee); - } - if (pool) - tee_shm_pool_free(pool); - if (memremaped_shm) - memunmap(memremaped_shm); + +err_disable_shm_cache: + optee_disable_shm_cache(optee); + optee_smc_notif_uninit_irq(optee); + optee_unregister_devices(); +err_notif_uninit: + optee_notif_uninit(optee); +err_supp_uninit: + optee_supp_uninit(&optee->supp); + mutex_destroy(&optee->call_queue.mutex); +err_unreg_supp_teedev: + tee_device_unregister(optee->supp_teedev); +err_unreg_teedev: + tee_device_unregister(optee->teedev); +err_free_optee: + kfree(optee); +err_free_pool: + tee_shm_pool_free(pool); + if (optee->smc.memremaped_shm) + memunmap(optee->smc.memremaped_shm); return rc; } From bd9ccaec6ac998196baa11380ad4651a7df62ef0 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Sat, 13 Nov 2021 13:53:58 +0800 Subject: [PATCH 17/94] soc: qcom: qmi: Fix a typo in a comment The double word `client' in a comment is repeated, thus one of them should be removed. Signed-off-by: Jason Wang Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211113055358.206533-1-wangborong@cdjrlc.com --- drivers/soc/qcom/qmi_interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/qcom/qmi_interface.c b/drivers/soc/qcom/qmi_interface.c index 1a03eaa38c46..c8c4c730b135 100644 --- a/drivers/soc/qcom/qmi_interface.c +++ b/drivers/soc/qcom/qmi_interface.c @@ -96,7 +96,7 @@ static void qmi_recv_del_server(struct qmi_handle *qmi, * @node: id of the dying node * * Signals the client that all previously registered services on this node are - * now gone and then calls the bye callback to allow the client client further + * now gone and then calls the bye callback to allow the client further * cleaning up resources associated with this remote. */ static void qmi_recv_bye(struct qmi_handle *qmi, From 7ba9dd0d04a8af868bc0785d79bde4ac0240328f Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 8 Nov 2021 21:44:40 +0800 Subject: [PATCH 18/94] soc: qcom: rpmpd: Drop unused res_name from struct rpmpd The res_name field in struct rpmpd is unused. Drop it. Signed-off-by: Shawn Guo Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211108134442.30051-2-shawn.guo@linaro.org --- drivers/soc/qcom/rpmpd.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/soc/qcom/rpmpd.c b/drivers/soc/qcom/rpmpd.c index 4f69fb9b2e0e..3e7905854eb9 100644 --- a/drivers/soc/qcom/rpmpd.c +++ b/drivers/soc/qcom/rpmpd.c @@ -102,7 +102,6 @@ struct rpmpd { const bool active_only; unsigned int corner; bool enabled; - const char *res_name; const int res_type; const int res_id; struct qcom_smd_rpm *rpm; From 2475fcfbe4e383d586c5a58711e436d83a2bdfe6 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 8 Nov 2021 21:44:41 +0800 Subject: [PATCH 19/94] dt-bindings: power: rpmpd: Add QCM2290 support Add compatible and constants for the power domains exposed by the QCM2290 RPM. Signed-off-by: Shawn Guo Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211108134442.30051-3-shawn.guo@linaro.org --- .../devicetree/bindings/power/qcom,rpmpd.yaml | 1 + include/dt-bindings/power/qcom-rpmpd.h | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml index e810480e3eb7..a6d51d51d2c7 100644 --- a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml +++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml @@ -24,6 +24,7 @@ properties: - qcom,msm8994-rpmpd - qcom,msm8996-rpmpd - qcom,msm8998-rpmpd + - qcom,qcm2290-rpmpd - qcom,qcs404-rpmpd - qcom,sdm660-rpmpd - qcom,sc7180-rpmhpd diff --git a/include/dt-bindings/power/qcom-rpmpd.h b/include/dt-bindings/power/qcom-rpmpd.h index 960f7976a807..340b0ffe5eb8 100644 --- a/include/dt-bindings/power/qcom-rpmpd.h +++ b/include/dt-bindings/power/qcom-rpmpd.h @@ -219,6 +219,16 @@ #define SM6115_VDD_LPI_CX 6 #define SM6115_VDD_LPI_MX 7 +/* QCM2290 Power Domains */ +#define QCM2290_VDDCX 0 +#define QCM2290_VDDCX_AO 1 +#define QCM2290_VDDCX_VFL 2 +#define QCM2290_VDDMX 3 +#define QCM2290_VDDMX_AO 4 +#define QCM2290_VDDMX_VFL 5 +#define QCM2290_VDD_LPI_CX 6 +#define QCM2290_VDD_LPI_MX 7 + /* RPM SMD Power Domain performance levels */ #define RPM_SMD_LEVEL_RETENTION 16 #define RPM_SMD_LEVEL_RETENTION_PLUS 32 From 1cd3921aa95ebf4a6d1f93fb736449441c530aa1 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 8 Nov 2021 21:44:42 +0800 Subject: [PATCH 20/94] soc: qcom: rpmpd: Add QCM2290 support QCM2290 has the same RPM power domains as SM6115. Add QCM2290 support by reusing SM6115 power domains. Signed-off-by: Shawn Guo Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211108134442.30051-4-shawn.guo@linaro.org --- drivers/soc/qcom/rpmpd.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/soc/qcom/rpmpd.c b/drivers/soc/qcom/rpmpd.c index 3e7905854eb9..36b2381039e6 100644 --- a/drivers/soc/qcom/rpmpd.c +++ b/drivers/soc/qcom/rpmpd.c @@ -395,6 +395,23 @@ static const struct rpmpd_desc sm6115_desc = { .max_state = RPM_SMD_LEVEL_TURBO_NO_CPR, }; +static struct rpmpd *qcm2290_rpmpds[] = { + [QCM2290_VDDCX] = &sm6115_vddcx, + [QCM2290_VDDCX_AO] = &sm6115_vddcx_ao, + [QCM2290_VDDCX_VFL] = &sm6115_vddcx_vfl, + [QCM2290_VDDMX] = &sm6115_vddmx, + [QCM2290_VDDMX_AO] = &sm6115_vddmx_ao, + [QCM2290_VDDMX_VFL] = &sm6115_vddmx_vfl, + [QCM2290_VDD_LPI_CX] = &sm6115_vdd_lpi_cx, + [QCM2290_VDD_LPI_MX] = &sm6115_vdd_lpi_mx, +}; + +static const struct rpmpd_desc qcm2290_desc = { + .rpmpds = qcm2290_rpmpds, + .num_pds = ARRAY_SIZE(qcm2290_rpmpds), + .max_state = RPM_SMD_LEVEL_TURBO_NO_CPR, +}; + static const struct of_device_id rpmpd_match_table[] = { { .compatible = "qcom,mdm9607-rpmpd", .data = &mdm9607_desc }, { .compatible = "qcom,msm8916-rpmpd", .data = &msm8916_desc }, @@ -404,6 +421,7 @@ static const struct of_device_id rpmpd_match_table[] = { { .compatible = "qcom,msm8994-rpmpd", .data = &msm8994_desc }, { .compatible = "qcom,msm8996-rpmpd", .data = &msm8996_desc }, { .compatible = "qcom,msm8998-rpmpd", .data = &msm8998_desc }, + { .compatible = "qcom,qcm2290-rpmpd", .data = &qcm2290_desc }, { .compatible = "qcom,qcs404-rpmpd", .data = &qcs404_desc }, { .compatible = "qcom,sdm660-rpmpd", .data = &sdm660_desc }, { .compatible = "qcom,sm6115-rpmpd", .data = &sm6115_desc }, From f756f435f7dd823f2d4bd593ce1bf3168def1308 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 16 Oct 2021 23:05:47 +0200 Subject: [PATCH 21/94] soc: imx: gpcv2: Synchronously suspend MIX domains In case the following power domain sequence happens, iMX8M Mini always hangs: gpumix:on -> gpu:on -> gpu:off -> gpu:on This is likely due to another quirk of the GPC block. This situation can be prevented by always synchronously powering off both the domain and MIX domain. Make it so. This turns the aforementioned sequence into: gpumix:on -> gpu:on -> gpu:off -> gpumix:off -> gpumix:on -> gpu:on Signed-off-by: Marek Vasut Cc: Frieder Schrempf Cc: Lucas Stach Cc: NXP Linux Team Cc: Peng Fan Cc: Shawn Guo Acked-by: Lucas Stach Signed-off-by: Shawn Guo --- drivers/soc/imx/gpcv2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c index b8d52d8d29db..e757044ab751 100644 --- a/drivers/soc/imx/gpcv2.c +++ b/drivers/soc/imx/gpcv2.c @@ -377,7 +377,7 @@ static int imx_pgc_power_down(struct generic_pm_domain *genpd) } } - pm_runtime_put(domain->dev); + pm_runtime_put_sync_suspend(domain->dev); return 0; From 2602dc10f9d930bcc537467d13de4cfbfaa2126d Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Sun, 21 Nov 2021 18:01:55 +0000 Subject: [PATCH 22/94] memory: renesas-rpc-if: Silence clang warning This patch silences the following clang warning: | drivers/memory/renesas-rpc-if.c:253:14: warning: cast to smaller integer | type 'enum rpcif_type' from 'const void *' [-Wvoid-pointer-to-enum-cast] | rpc->type = (enum rpcif_type)of_device_get_match_data(dev); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Fixes: b04cc0d912eb8 ("memory: renesas-rpc-if: Add support for RZ/G2L") Reported-by: kernel test robot Signed-off-by: Lad Prabhakar Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20211121180155.9062-1-prabhakar.mahadev-lad.rj@bp.renesas.com [krzysztof: drop enum rpcif_type cast] Signed-off-by: Krzysztof Kozlowski --- drivers/memory/renesas-rpc-if.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/memory/renesas-rpc-if.c b/drivers/memory/renesas-rpc-if.c index 8c51145c0f5c..77c53cd4e1d5 100644 --- a/drivers/memory/renesas-rpc-if.c +++ b/drivers/memory/renesas-rpc-if.c @@ -250,7 +250,7 @@ int rpcif_sw_init(struct rpcif *rpc, struct device *dev) return PTR_ERR(rpc->dirmap); rpc->size = resource_size(res); - rpc->type = (enum rpcif_type)of_device_get_match_data(dev); + rpc->type = (uintptr_t)of_device_get_match_data(dev); rpc->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); return PTR_ERR_OR_ZERO(rpc->rstc); From 2db468d6fda4c33d26ba9dde7c72e081b2b86ad1 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 17 Nov 2021 11:29:02 +0100 Subject: [PATCH 23/94] memory: renesas-rpc-if: simplify register update No need to open code regmap_update_bits(). Signed-off-by: Wolfram Sang Tested-by: Lad Prabhakar Link: https://lore.kernel.org/r/20211117102902.20062-1-wsa+renesas@sang-engineering.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/renesas-rpc-if.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/memory/renesas-rpc-if.c b/drivers/memory/renesas-rpc-if.c index 77c53cd4e1d5..edd7eb4486e1 100644 --- a/drivers/memory/renesas-rpc-if.c +++ b/drivers/memory/renesas-rpc-if.c @@ -259,17 +259,14 @@ EXPORT_SYMBOL(rpcif_sw_init); static void rpcif_rzg2l_timing_adjust_sdr(struct rpcif *rpc) { - u32 data; - regmap_write(rpc->regmap, RPCIF_PHYWR, 0xa5390000); regmap_write(rpc->regmap, RPCIF_PHYADD, 0x80000000); regmap_write(rpc->regmap, RPCIF_PHYWR, 0x00008080); regmap_write(rpc->regmap, RPCIF_PHYADD, 0x80000022); regmap_write(rpc->regmap, RPCIF_PHYWR, 0x00008080); regmap_write(rpc->regmap, RPCIF_PHYADD, 0x80000024); - - regmap_read(rpc->regmap, RPCIF_PHYCNT, &data); - regmap_write(rpc->regmap, RPCIF_PHYCNT, data | RPCIF_PHYCNT_CKSEL(3)); + regmap_update_bits(rpc->regmap, RPCIF_PHYCNT, RPCIF_PHYCNT_CKSEL(3), + RPCIF_PHYCNT_CKSEL(3)); regmap_write(rpc->regmap, RPCIF_PHYWR, 0x00000030); regmap_write(rpc->regmap, RPCIF_PHYADD, 0x80000032); } From 57ea9daad51f7707f61a602a743decf10cf9fea9 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 17 Nov 2021 10:37:10 +0100 Subject: [PATCH 24/94] memory: renesas-rpc-if: avoid use of undocumented bits Instead of writing fixed values with undocumented bits which happen to be set on some SoCs, better switch to read-modify-write operations changing only bits which are documented. This is way more future-proof as we don't know yet how these bits may be on upcoming SoCs. Signed-off-by: Wolfram Sang Tested-by: Lad Prabhakar Reviewed-by: Lad Prabhakar Link: https://lore.kernel.org/r/20211117093710.14430-1-wsa+renesas@sang-engineering.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/renesas-rpc-if.c | 58 +++++++++++++-------------------- 1 file changed, 22 insertions(+), 36 deletions(-) diff --git a/drivers/memory/renesas-rpc-if.c b/drivers/memory/renesas-rpc-if.c index edd7eb4486e1..ccd662ee2acb 100644 --- a/drivers/memory/renesas-rpc-if.c +++ b/drivers/memory/renesas-rpc-if.c @@ -20,7 +20,6 @@ #define RPCIF_CMNCR 0x0000 /* R/W */ #define RPCIF_CMNCR_MD BIT(31) -#define RPCIF_CMNCR_SFDE BIT(24) /* undocumented but must be set */ #define RPCIF_CMNCR_MOIIO3(val) (((val) & 0x3) << 22) #define RPCIF_CMNCR_MOIIO2(val) (((val) & 0x3) << 20) #define RPCIF_CMNCR_MOIIO1(val) (((val) & 0x3) << 18) @@ -287,49 +286,36 @@ int rpcif_hw_init(struct rpcif *rpc, bool hyperflash) rpcif_rzg2l_timing_adjust_sdr(rpc); } - /* - * NOTE: The 0x260 are undocumented bits, but they must be set. - * RPCIF_PHYCNT_STRTIM is strobe timing adjustment bits, - * 0x0 : the delay is biggest, - * 0x1 : the delay is 2nd biggest, - * On H3 ES1.x, the value should be 0, while on others, - * the value should be 7. - */ - if (rpc->type == RPCIF_RCAR_GEN3) { - regmap_write(rpc->regmap, RPCIF_PHYCNT, RPCIF_PHYCNT_STRTIM(7) | - RPCIF_PHYCNT_PHYMEM(hyperflash ? 3 : 0) | 0x260); - } else { - regmap_read(rpc->regmap, RPCIF_PHYCNT, &dummy); - dummy &= ~RPCIF_PHYCNT_PHYMEM_MASK; - dummy |= RPCIF_PHYCNT_PHYMEM(hyperflash ? 3 : 0) | 0x260; - regmap_write(rpc->regmap, RPCIF_PHYCNT, dummy); - } + regmap_update_bits(rpc->regmap, RPCIF_PHYCNT, RPCIF_PHYCNT_PHYMEM_MASK, + RPCIF_PHYCNT_PHYMEM(hyperflash ? 3 : 0)); - /* - * NOTE: The 0x1511144 are undocumented bits, but they must be set - * for RPCIF_PHYOFFSET1. - * The 0x31 are undocumented bits, but they must be set - * for RPCIF_PHYOFFSET2. - */ - regmap_write(rpc->regmap, RPCIF_PHYOFFSET1, 0x1511144 | - RPCIF_PHYOFFSET1_DDRTMG(3)); - regmap_write(rpc->regmap, RPCIF_PHYOFFSET2, 0x31 | - RPCIF_PHYOFFSET2_OCTTMG(4)); + if (rpc->type == RPCIF_RCAR_GEN3) + regmap_update_bits(rpc->regmap, RPCIF_PHYCNT, + RPCIF_PHYCNT_STRTIM(7), RPCIF_PHYCNT_STRTIM(7)); + + regmap_update_bits(rpc->regmap, RPCIF_PHYOFFSET1, RPCIF_PHYOFFSET1_DDRTMG(3), + RPCIF_PHYOFFSET1_DDRTMG(3)); + regmap_update_bits(rpc->regmap, RPCIF_PHYOFFSET2, RPCIF_PHYOFFSET2_OCTTMG(7), + RPCIF_PHYOFFSET2_OCTTMG(4)); if (hyperflash) regmap_update_bits(rpc->regmap, RPCIF_PHYINT, RPCIF_PHYINT_WPVAL, 0); if (rpc->type == RPCIF_RCAR_GEN3) - regmap_write(rpc->regmap, RPCIF_CMNCR, RPCIF_CMNCR_SFDE | - RPCIF_CMNCR_MOIIO_HIZ | RPCIF_CMNCR_IOFV_HIZ | - RPCIF_CMNCR_BSZ(hyperflash ? 1 : 0)); + regmap_update_bits(rpc->regmap, RPCIF_CMNCR, + RPCIF_CMNCR_MOIIO_HIZ | RPCIF_CMNCR_BSZ(3), + RPCIF_CMNCR_MOIIO_HIZ | + RPCIF_CMNCR_BSZ(hyperflash ? 1 : 0)); else - regmap_write(rpc->regmap, RPCIF_CMNCR, RPCIF_CMNCR_SFDE | - RPCIF_CMNCR_MOIIO3(1) | RPCIF_CMNCR_MOIIO2(1) | - RPCIF_CMNCR_MOIIO1(1) | RPCIF_CMNCR_MOIIO0(1) | - RPCIF_CMNCR_IO3FV(2) | RPCIF_CMNCR_IO2FV(2) | - RPCIF_CMNCR_IO0FV(2) | RPCIF_CMNCR_BSZ(hyperflash ? 1 : 0)); + regmap_update_bits(rpc->regmap, RPCIF_CMNCR, + RPCIF_CMNCR_MOIIO_HIZ | RPCIF_CMNCR_IOFV_HIZ | + RPCIF_CMNCR_BSZ(3), + RPCIF_CMNCR_MOIIO3(1) | RPCIF_CMNCR_MOIIO2(1) | + RPCIF_CMNCR_MOIIO1(1) | RPCIF_CMNCR_MOIIO0(1) | + RPCIF_CMNCR_IO3FV(2) | RPCIF_CMNCR_IO2FV(2) | + RPCIF_CMNCR_IO0FV(2) | + RPCIF_CMNCR_BSZ(hyperflash ? 1 : 0)); /* Set RCF after BSZ update */ regmap_write(rpc->regmap, RPCIF_DRCR, RPCIF_DRCR_RCF); From 3542de6a5b159fac0e7ca84d77a57ea99125d6b1 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 19 Nov 2021 12:04:42 +0100 Subject: [PATCH 25/94] memory: renesas-rpc-if: refactor MOIIO and IOFV macros Don't use _HIZ macros but also provide a val. This is more consistent with the other macros and, thus, easier to read. Also shorter. Signed-off-by: Wolfram Sang Link: https://lore.kernel.org/r/20211119110442.4946-1-wsa+renesas@sang-engineering.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/renesas-rpc-if.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/memory/renesas-rpc-if.c b/drivers/memory/renesas-rpc-if.c index ccd662ee2acb..e4cc64f56019 100644 --- a/drivers/memory/renesas-rpc-if.c +++ b/drivers/memory/renesas-rpc-if.c @@ -24,14 +24,13 @@ #define RPCIF_CMNCR_MOIIO2(val) (((val) & 0x3) << 20) #define RPCIF_CMNCR_MOIIO1(val) (((val) & 0x3) << 18) #define RPCIF_CMNCR_MOIIO0(val) (((val) & 0x3) << 16) -#define RPCIF_CMNCR_MOIIO_HIZ (RPCIF_CMNCR_MOIIO0(3) | \ - RPCIF_CMNCR_MOIIO1(3) | \ - RPCIF_CMNCR_MOIIO2(3) | RPCIF_CMNCR_MOIIO3(3)) +#define RPCIF_CMNCR_MOIIO(val) (RPCIF_CMNCR_MOIIO0(val) | RPCIF_CMNCR_MOIIO1(val) | \ + RPCIF_CMNCR_MOIIO2(val) | RPCIF_CMNCR_MOIIO3(val)) #define RPCIF_CMNCR_IO3FV(val) (((val) & 0x3) << 14) /* documented for RZ/G2L */ #define RPCIF_CMNCR_IO2FV(val) (((val) & 0x3) << 12) /* documented for RZ/G2L */ #define RPCIF_CMNCR_IO0FV(val) (((val) & 0x3) << 8) -#define RPCIF_CMNCR_IOFV_HIZ (RPCIF_CMNCR_IO0FV(3) | RPCIF_CMNCR_IO2FV(3) | \ - RPCIF_CMNCR_IO3FV(3)) +#define RPCIF_CMNCR_IOFV(val) (RPCIF_CMNCR_IO0FV(val) | RPCIF_CMNCR_IO2FV(val) | \ + RPCIF_CMNCR_IO3FV(val)) #define RPCIF_CMNCR_BSZ(val) (((val) & 0x3) << 0) #define RPCIF_SSLDR 0x0004 /* R/W */ @@ -304,17 +303,14 @@ int rpcif_hw_init(struct rpcif *rpc, bool hyperflash) if (rpc->type == RPCIF_RCAR_GEN3) regmap_update_bits(rpc->regmap, RPCIF_CMNCR, - RPCIF_CMNCR_MOIIO_HIZ | RPCIF_CMNCR_BSZ(3), - RPCIF_CMNCR_MOIIO_HIZ | + RPCIF_CMNCR_MOIIO(3) | RPCIF_CMNCR_BSZ(3), + RPCIF_CMNCR_MOIIO(3) | RPCIF_CMNCR_BSZ(hyperflash ? 1 : 0)); else regmap_update_bits(rpc->regmap, RPCIF_CMNCR, - RPCIF_CMNCR_MOIIO_HIZ | RPCIF_CMNCR_IOFV_HIZ | + RPCIF_CMNCR_MOIIO(3) | RPCIF_CMNCR_IOFV(3) | RPCIF_CMNCR_BSZ(3), - RPCIF_CMNCR_MOIIO3(1) | RPCIF_CMNCR_MOIIO2(1) | - RPCIF_CMNCR_MOIIO1(1) | RPCIF_CMNCR_MOIIO0(1) | - RPCIF_CMNCR_IO3FV(2) | RPCIF_CMNCR_IO2FV(2) | - RPCIF_CMNCR_IO0FV(2) | + RPCIF_CMNCR_MOIIO(1) | RPCIF_CMNCR_IOFV(2) | RPCIF_CMNCR_BSZ(hyperflash ? 1 : 0)); /* Set RCF after BSZ update */ From 8aa35e0bb5eaa42bac415ad0847985daa7b4890c Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 21 Jun 2021 20:08:28 +0200 Subject: [PATCH 26/94] soc: ti: pruss: fix referenced node in error message So far, "(null)" is reported for the node that is missing clocks. Signed-off-by: Jan Kiszka Acked-by: Suman Anna Signed-off-by: Nishanth Menon Link: https://lore.kernel.org/r/d6e24953-ea89-fd1c-6e16-7a0142118054@siemens.com --- drivers/soc/ti/pruss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/ti/pruss.c b/drivers/soc/ti/pruss.c index 49da387d7749..b36779309e49 100644 --- a/drivers/soc/ti/pruss.c +++ b/drivers/soc/ti/pruss.c @@ -129,7 +129,7 @@ static int pruss_clk_init(struct pruss *pruss, struct device_node *cfg_node) clks_np = of_get_child_by_name(cfg_node, "clocks"); if (!clks_np) { - dev_err(dev, "%pOF is missing its 'clocks' node\n", clks_np); + dev_err(dev, "%pOF is missing its 'clocks' node\n", cfg_node); return -ENODEV; } From 7c1c1d36e83073aab18d3dd596b735bcc6ea7984 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Sun, 14 Nov 2021 13:53:33 +0800 Subject: [PATCH 27/94] firmware: ti_sci: rm: remove unneeded semicolon The semicolon after `}' is unneeded. So, just remove it. Signed-off-by: Jason Wang Signed-off-by: Nishanth Menon Link: https://lore.kernel.org/r/20211114055333.217919-1-wangborong@cdjrlc.com --- drivers/firmware/ti_sci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index 235c7e7869aa..5ae2040b8b02 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -1759,7 +1759,7 @@ static int ti_sci_get_resource_range(const struct ti_sci_handle *handle, desc->num = resp->range_num; desc->start_sec = resp->range_start_sec; desc->num_sec = resp->range_num_sec; - }; + } fail: ti_sci_put_one_xfer(&info->minfo, xfer); From 8361b8b29f9389084b679db854cf733375c64763 Mon Sep 17 00:00:00 2001 From: Adam Ford Date: Sat, 20 Nov 2021 13:39:16 -0600 Subject: [PATCH 28/94] soc: imx: gpcv2: keep i.MX8MM VPU-H1 bus clock active Enable the vpu-h1 clock when the domain is active because reading or writing to the VPU-H1 IP block cause the system to hang. Fixes: 656ade7aa42a ("soc: imx: gpcv2: keep i.MX8M* bus clocks enabled") Signed-off-by: Adam Ford Reviewed-by: Fabio Estevam Signed-off-by: Shawn Guo --- drivers/soc/imx/gpcv2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c index e757044ab751..8176380b02e6 100644 --- a/drivers/soc/imx/gpcv2.c +++ b/drivers/soc/imx/gpcv2.c @@ -734,6 +734,7 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = { .map = IMX8MM_VPUH1_A53_DOMAIN, }, .pgc = BIT(IMX8MM_PGC_VPUH1), + .keep_clocks = true, }, [IMX8MM_POWER_DOMAIN_DISPMIX] = { From b98aee466d194788bd651cb375b0e0f7e0e69865 Mon Sep 17 00:00:00 2001 From: Yang Li Date: Mon, 22 Nov 2021 10:20:50 +0800 Subject: [PATCH 29/94] optee: Fix NULL but dereferenced coccicheck error Eliminate the following coccicheck warning: ./drivers/tee/optee/smc_abi.c:1508:12-15: ERROR: optee is NULL but dereferenced. Reported-by: Abaci Robot Fixes: 6749e69c4dad ("optee: add asynchronous notifications") Signed-off-by: Yang Li Signed-off-by: Jens Wiklander --- drivers/tee/optee/smc_abi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c index 92759d70bfce..d7c8235c1c42 100644 --- a/drivers/tee/optee/smc_abi.c +++ b/drivers/tee/optee/smc_abi.c @@ -1505,8 +1505,8 @@ static int optee_probe(struct platform_device *pdev) kfree(optee); err_free_pool: tee_shm_pool_free(pool); - if (optee->smc.memremaped_shm) - memunmap(optee->smc.memremaped_shm); + if (memremaped_shm) + memunmap(memremaped_shm); return rc; } From 05b22caa7490e4f4c94bbde33c61cf72d187b8f7 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 10 Nov 2021 20:00:52 +0100 Subject: [PATCH 30/94] soc: renesas: Consolidate product register handling Currently renesas_soc_init() scans the whole device tree up to three times, to find a device node describing a product register. Furthermore, the product register handling for the different variants is very similar, with the major difference being the location of the product bitfield inside the product register. Reduce scanning to a single pass using of_find_matching_node_and_match() instead. Switch to a common handling of product registers, by storing the intrinsics of each product register type in the data field of the corresponding match entry. Signed-off-by: Geert Uytterhoeven Tested-by: Lad Prabhakar Link: https://lore.kernel.org/r/057721f46c7499de4133135488f0f3da7fb39265.1636570669.git.geert+renesas@glider.be --- drivers/soc/renesas/renesas-soc.c | 117 +++++++++++++++--------------- 1 file changed, 57 insertions(+), 60 deletions(-) diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c index 7961b0be1850..dff90f504963 100644 --- a/drivers/soc/renesas/renesas-soc.c +++ b/drivers/soc/renesas/renesas-soc.c @@ -328,94 +328,92 @@ static const struct of_device_id renesas_socs[] __initconst = { { /* sentinel */ } }; +struct renesas_id { + unsigned int offset; + u32 mask; +}; + +static const struct renesas_id id_bsid __initconst = { + .offset = 0, + .mask = 0xff0000, + /* + * TODO: Upper 4 bits of BSID are for chip version, but the format is + * not known at this time so we don't know how to specify eshi and eslo + */ +}; + +static const struct renesas_id id_rzg2l __initconst = { + .offset = 0xa04, + .mask = 0xfffffff, +}; + +static const struct renesas_id id_prr __initconst = { + .offset = 0, + .mask = 0xff00, +}; + +static const struct of_device_id renesas_ids[] __initconst = { + { .compatible = "renesas,bsid", .data = &id_bsid }, + { .compatible = "renesas,r9a07g044-sysc", .data = &id_rzg2l }, + { .compatible = "renesas,prr", .data = &id_prr }, + { /* sentinel */ } +}; + static int __init renesas_soc_init(void) { struct soc_device_attribute *soc_dev_attr; + unsigned int product, eshi = 0, eslo; const struct renesas_family *family; const struct of_device_id *match; const struct renesas_soc *soc; + const struct renesas_id *id; void __iomem *chipid = NULL; struct soc_device *soc_dev; struct device_node *np; - unsigned int product, eshi = 0, eslo; + const char *soc_id; match = of_match_node(renesas_socs, of_root); if (!match) return -ENODEV; + soc_id = strchr(match->compatible, ',') + 1; soc = match->data; family = soc->family; - np = of_find_compatible_node(NULL, NULL, "renesas,bsid"); - if (np) { - chipid = of_iomap(np, 0); - of_node_put(np); - - if (chipid) { - product = readl(chipid); - iounmap(chipid); - - if (soc->id && ((product >> 16) & 0xff) != soc->id) { - pr_warn("SoC mismatch (product = 0x%x)\n", - product); - return -ENODEV; - } - } - - /* - * TODO: Upper 4 bits of BSID are for chip version, but the - * format is not known at this time so we don't know how to - * specify eshi and eslo - */ - - goto done; - } - - np = of_find_compatible_node(NULL, NULL, "renesas,r9a07g044-sysc"); - if (np) { - chipid = of_iomap(np, 0); - of_node_put(np); - - if (chipid) { - product = readl(chipid + 0x0a04); - iounmap(chipid); - - if (soc->id && (product & 0xfffffff) != soc->id) { - pr_warn("SoC mismatch (product = 0x%x)\n", - product); - return -ENODEV; - } - } - - goto done; - } - - /* Try PRR first, then hardcoded fallback */ - np = of_find_compatible_node(NULL, NULL, "renesas,prr"); + np = of_find_matching_node_and_match(NULL, renesas_ids, &match); if (np) { + id = match->data; chipid = of_iomap(np, 0); of_node_put(np); } else if (soc->id && family->reg) { + /* Try hardcoded CCCR/PRR fallback */ + id = &id_prr; chipid = ioremap(family->reg, 4); } + if (chipid) { - product = readl(chipid); + product = readl(chipid + id->offset); iounmap(chipid); - /* R-Car M3-W ES1.1 incorrectly identifies as ES2.0 */ - if ((product & 0x7fff) == 0x5210) - product ^= 0x11; - /* R-Car M3-W ES1.3 incorrectly identifies as ES2.1 */ - if ((product & 0x7fff) == 0x5211) - product ^= 0x12; - if (soc->id && ((product >> 8) & 0xff) != soc->id) { + + if (id == &id_prr) { + /* R-Car M3-W ES1.1 incorrectly identifies as ES2.0 */ + if ((product & 0x7fff) == 0x5210) + product ^= 0x11; + /* R-Car M3-W ES1.3 incorrectly identifies as ES2.1 */ + if ((product & 0x7fff) == 0x5211) + product ^= 0x12; + + eshi = ((product >> 4) & 0x0f) + 1; + eslo = product & 0xf; + } + + if (soc->id && + ((product & id->mask) >> __ffs(id->mask)) != soc->id) { pr_warn("SoC mismatch (product = 0x%x)\n", product); return -ENODEV; } - eshi = ((product >> 4) & 0x0f) + 1; - eslo = product & 0xf; } -done: soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); if (!soc_dev_attr) return -ENOMEM; @@ -425,8 +423,7 @@ static int __init renesas_soc_init(void) of_node_put(np); soc_dev_attr->family = kstrdup_const(family->name, GFP_KERNEL); - soc_dev_attr->soc_id = kstrdup_const(strchr(match->compatible, ',') + 1, - GFP_KERNEL); + soc_dev_attr->soc_id = kstrdup_const(soc_id, GFP_KERNEL); if (eshi) soc_dev_attr->revision = kasprintf(GFP_KERNEL, "ES%u.%u", eshi, eslo); From 6df9d38f9146e83b473f0c9e57fb5fdf3fcc93e2 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Wed, 24 Nov 2021 16:34:18 +0900 Subject: [PATCH 31/94] soc: apple: Add driver for Apple PMGR power state controls Implements genpd and reset providers for downstream devices. Each instance of the driver binds to a single register and represents a single SoC power domain. The driver does not currently implement all features (clockgate-only state, misc flags), but we declare the respective registers for documentation purposes. These features will be added as they become useful for downstream devices. This also creates the apple/soc tree and Kconfig submenu. Acked-by: Linus Walleij Signed-off-by: Hector Martin --- drivers/soc/Kconfig | 1 + drivers/soc/Makefile | 1 + drivers/soc/apple/Kconfig | 22 ++ drivers/soc/apple/Makefile | 2 + drivers/soc/apple/apple-pmgr-pwrstate.c | 317 ++++++++++++++++++++++++ 5 files changed, 343 insertions(+) create mode 100644 drivers/soc/apple/Kconfig create mode 100644 drivers/soc/apple/Makefile create mode 100644 drivers/soc/apple/apple-pmgr-pwrstate.c diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index e8a30c4c5aec..a8562678c437 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -3,6 +3,7 @@ menu "SOC (System On Chip) specific Drivers" source "drivers/soc/actions/Kconfig" source "drivers/soc/amlogic/Kconfig" +source "drivers/soc/apple/Kconfig" source "drivers/soc/aspeed/Kconfig" source "drivers/soc/atmel/Kconfig" source "drivers/soc/bcm/Kconfig" diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index a05e9fbcd3e0..adb30c2d4fea 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -4,6 +4,7 @@ # obj-$(CONFIG_ARCH_ACTIONS) += actions/ +obj-$(CONFIG_ARCH_APPLE) += apple/ obj-y += aspeed/ obj-$(CONFIG_ARCH_AT91) += atmel/ obj-y += bcm/ diff --git a/drivers/soc/apple/Kconfig b/drivers/soc/apple/Kconfig new file mode 100644 index 000000000000..3eb14abf8d33 --- /dev/null +++ b/drivers/soc/apple/Kconfig @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0-only + +if ARCH_APPLE || COMPILE_TEST + +menu "Apple SoC drivers" + +config APPLE_PMGR_PWRSTATE + tristate "Apple SoC PMGR power state control" + depends on PM + select REGMAP + select MFD_SYSCON + select PM_GENERIC_DOMAINS + select RESET_CONTROLLER + default ARCH_APPLE + help + The PMGR block in Apple SoCs provides high-level power state + controls for SoC devices. This driver manages them through the + generic power domain framework, and also provides reset support. + +endmenu + +endif diff --git a/drivers/soc/apple/Makefile b/drivers/soc/apple/Makefile new file mode 100644 index 000000000000..c114e84667e4 --- /dev/null +++ b/drivers/soc/apple/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_APPLE_PMGR_PWRSTATE) += apple-pmgr-pwrstate.o diff --git a/drivers/soc/apple/apple-pmgr-pwrstate.c b/drivers/soc/apple/apple-pmgr-pwrstate.c new file mode 100644 index 000000000000..8ad9d5fdefbf --- /dev/null +++ b/drivers/soc/apple/apple-pmgr-pwrstate.c @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple SoC PMGR device power state driver + * + * Copyright The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define APPLE_PMGR_RESET BIT(31) +#define APPLE_PMGR_AUTO_ENABLE BIT(28) +#define APPLE_PMGR_PS_AUTO GENMASK(27, 24) +#define APPLE_PMGR_PARENT_OFF BIT(11) +#define APPLE_PMGR_DEV_DISABLE BIT(10) +#define APPLE_PMGR_WAS_CLKGATED BIT(9) +#define APPLE_PMGR_WAS_PWRGATED BIT(8) +#define APPLE_PMGR_PS_ACTUAL GENMASK(7, 4) +#define APPLE_PMGR_PS_TARGET GENMASK(3, 0) + +#define APPLE_PMGR_FLAGS (APPLE_PMGR_WAS_CLKGATED | APPLE_PMGR_WAS_PWRGATED) + +#define APPLE_PMGR_PS_ACTIVE 0xf +#define APPLE_PMGR_PS_CLKGATE 0x4 +#define APPLE_PMGR_PS_PWRGATE 0x0 + +#define APPLE_PMGR_PS_SET_TIMEOUT 100 +#define APPLE_PMGR_RESET_TIME 1 + +struct apple_pmgr_ps { + struct device *dev; + struct generic_pm_domain genpd; + struct reset_controller_dev rcdev; + struct regmap *regmap; + u32 offset; +}; + +#define genpd_to_apple_pmgr_ps(_genpd) container_of(_genpd, struct apple_pmgr_ps, genpd) +#define rcdev_to_apple_pmgr_ps(_rcdev) container_of(_rcdev, struct apple_pmgr_ps, rcdev) + +static int apple_pmgr_ps_set(struct generic_pm_domain *genpd, u32 pstate, bool auto_enable) +{ + int ret; + struct apple_pmgr_ps *ps = genpd_to_apple_pmgr_ps(genpd); + u32 reg; + + ret = regmap_read(ps->regmap, ps->offset, ®); + if (ret < 0) + return ret; + + /* Resets are synchronous, and only work if the device is powered and clocked. */ + if (reg & APPLE_PMGR_RESET && pstate != APPLE_PMGR_PS_ACTIVE) + dev_err(ps->dev, "PS %s: powering off with RESET active\n", + genpd->name); + + reg &= ~(APPLE_PMGR_AUTO_ENABLE | APPLE_PMGR_FLAGS | APPLE_PMGR_PS_TARGET); + reg |= FIELD_PREP(APPLE_PMGR_PS_TARGET, pstate); + + dev_dbg(ps->dev, "PS %s: pwrstate = 0x%x: 0x%x\n", genpd->name, pstate, reg); + + regmap_write(ps->regmap, ps->offset, reg); + + ret = regmap_read_poll_timeout_atomic( + ps->regmap, ps->offset, reg, + (FIELD_GET(APPLE_PMGR_PS_ACTUAL, reg) == pstate), 1, + APPLE_PMGR_PS_SET_TIMEOUT); + if (ret < 0) + dev_err(ps->dev, "PS %s: Failed to reach power state 0x%x (now: 0x%x)\n", + genpd->name, pstate, reg); + + if (auto_enable) { + /* Not all devices implement this; this is a no-op where not implemented. */ + reg &= ~APPLE_PMGR_FLAGS; + reg |= APPLE_PMGR_AUTO_ENABLE; + regmap_write(ps->regmap, ps->offset, reg); + } + + return ret; +} + +static bool apple_pmgr_ps_is_active(struct apple_pmgr_ps *ps) +{ + u32 reg = 0; + + regmap_read(ps->regmap, ps->offset, ®); + /* + * We consider domains as active if they are actually on, or if they have auto-PM + * enabled and the intended target is on. + */ + return (FIELD_GET(APPLE_PMGR_PS_ACTUAL, reg) == APPLE_PMGR_PS_ACTIVE || + (FIELD_GET(APPLE_PMGR_PS_TARGET, reg) == APPLE_PMGR_PS_ACTIVE && + reg & APPLE_PMGR_AUTO_ENABLE)); +} + +static int apple_pmgr_ps_power_on(struct generic_pm_domain *genpd) +{ + return apple_pmgr_ps_set(genpd, APPLE_PMGR_PS_ACTIVE, true); +} + +static int apple_pmgr_ps_power_off(struct generic_pm_domain *genpd) +{ + return apple_pmgr_ps_set(genpd, APPLE_PMGR_PS_PWRGATE, false); +} + +static int apple_pmgr_reset_assert(struct reset_controller_dev *rcdev, unsigned long id) +{ + struct apple_pmgr_ps *ps = rcdev_to_apple_pmgr_ps(rcdev); + + mutex_lock(&ps->genpd.mlock); + + if (ps->genpd.status == GENPD_STATE_OFF) + dev_err(ps->dev, "PS 0x%x: asserting RESET while powered down\n", ps->offset); + + dev_dbg(ps->dev, "PS 0x%x: assert reset\n", ps->offset); + /* Quiesce device before asserting reset */ + regmap_update_bits(ps->regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_DEV_DISABLE, + APPLE_PMGR_DEV_DISABLE); + regmap_update_bits(ps->regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_RESET, + APPLE_PMGR_RESET); + + mutex_unlock(&ps->genpd.mlock); + + return 0; +} + +static int apple_pmgr_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id) +{ + struct apple_pmgr_ps *ps = rcdev_to_apple_pmgr_ps(rcdev); + + mutex_lock(&ps->genpd.mlock); + + dev_dbg(ps->dev, "PS 0x%x: deassert reset\n", ps->offset); + regmap_update_bits(ps->regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_RESET, 0); + regmap_update_bits(ps->regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_DEV_DISABLE, 0); + + if (ps->genpd.status == GENPD_STATE_OFF) + dev_err(ps->dev, "PS 0x%x: RESET was deasserted while powered down\n", ps->offset); + + mutex_unlock(&ps->genpd.mlock); + + return 0; +} + +static int apple_pmgr_reset_reset(struct reset_controller_dev *rcdev, unsigned long id) +{ + int ret; + + ret = apple_pmgr_reset_assert(rcdev, id); + if (ret) + return ret; + + usleep_range(APPLE_PMGR_RESET_TIME, 2 * APPLE_PMGR_RESET_TIME); + + return apple_pmgr_reset_deassert(rcdev, id); +} + +static int apple_pmgr_reset_status(struct reset_controller_dev *rcdev, unsigned long id) +{ + struct apple_pmgr_ps *ps = rcdev_to_apple_pmgr_ps(rcdev); + u32 reg = 0; + + regmap_read(ps->regmap, ps->offset, ®); + + return !!(reg & APPLE_PMGR_RESET); +} + +const struct reset_control_ops apple_pmgr_reset_ops = { + .assert = apple_pmgr_reset_assert, + .deassert = apple_pmgr_reset_deassert, + .reset = apple_pmgr_reset_reset, + .status = apple_pmgr_reset_status, +}; + +static int apple_pmgr_reset_xlate(struct reset_controller_dev *rcdev, + const struct of_phandle_args *reset_spec) +{ + return 0; +} + +static int apple_pmgr_ps_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct apple_pmgr_ps *ps; + struct regmap *regmap; + struct of_phandle_iterator it; + int ret; + const char *name; + bool active; + + regmap = syscon_node_to_regmap(node->parent); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + ps = devm_kzalloc(dev, sizeof(*ps), GFP_KERNEL); + if (!ps) + return -ENOMEM; + + ps->dev = dev; + ps->regmap = regmap; + + ret = of_property_read_string(node, "label", &name); + if (ret < 0) { + dev_err(dev, "missing label property\n"); + return ret; + } + + ret = of_property_read_u32(node, "reg", &ps->offset); + if (ret < 0) { + dev_err(dev, "missing reg property\n"); + return ret; + } + + ps->genpd.name = name; + ps->genpd.power_on = apple_pmgr_ps_power_on; + ps->genpd.power_off = apple_pmgr_ps_power_off; + + active = apple_pmgr_ps_is_active(ps); + if (of_property_read_bool(node, "apple,always-on")) { + ps->genpd.flags |= GENPD_FLAG_ALWAYS_ON; + if (!active) { + dev_warn(dev, "always-on domain %s is not on at boot\n", name); + /* Turn it on so pm_genpd_init does not fail */ + active = apple_pmgr_ps_power_on(&ps->genpd) == 0; + } + } + + /* Turn on auto-PM if the domain is already on */ + if (active) + regmap_update_bits(regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_AUTO_ENABLE, + APPLE_PMGR_AUTO_ENABLE); + + ret = pm_genpd_init(&ps->genpd, NULL, !active); + if (ret < 0) { + dev_err(dev, "pm_genpd_init failed\n"); + return ret; + } + + ret = of_genpd_add_provider_simple(node, &ps->genpd); + if (ret < 0) { + dev_err(dev, "of_genpd_add_provider_simple failed\n"); + return ret; + } + + of_for_each_phandle(&it, ret, node, "power-domains", "#power-domain-cells", -1) { + struct of_phandle_args parent, child; + + parent.np = it.node; + parent.args_count = of_phandle_iterator_args(&it, parent.args, MAX_PHANDLE_ARGS); + child.np = node; + child.args_count = 0; + ret = of_genpd_add_subdomain(&parent, &child); + + if (ret == -EPROBE_DEFER) { + of_node_put(parent.np); + goto err_remove; + } else if (ret < 0) { + dev_err(dev, "failed to add to parent domain: %d (%s -> %s)\n", + ret, it.node->name, node->name); + of_node_put(parent.np); + goto err_remove; + } + } + + /* + * Do not participate in regular PM; parent power domains are handled via the + * genpd hierarchy. + */ + pm_genpd_remove_device(dev); + + ps->rcdev.owner = THIS_MODULE; + ps->rcdev.nr_resets = 1; + ps->rcdev.ops = &apple_pmgr_reset_ops; + ps->rcdev.of_node = dev->of_node; + ps->rcdev.of_reset_n_cells = 0; + ps->rcdev.of_xlate = apple_pmgr_reset_xlate; + + ret = devm_reset_controller_register(dev, &ps->rcdev); + if (ret < 0) + goto err_remove; + + return 0; +err_remove: + of_genpd_del_provider(node); + pm_genpd_remove(&ps->genpd); + return ret; +} + +static const struct of_device_id apple_pmgr_ps_of_match[] = { + { .compatible = "apple,pmgr-pwrstate" }, + {} +}; + +MODULE_DEVICE_TABLE(of, apple_pmgr_ps_of_match); + +static struct platform_driver apple_pmgr_ps_driver = { + .probe = apple_pmgr_ps_probe, + .driver = { + .name = "apple-pmgr-pwrstate", + .of_match_table = apple_pmgr_ps_of_match, + }, +}; + +MODULE_AUTHOR("Hector Martin "); +MODULE_DESCRIPTION("PMGR power state driver for Apple SoCs"); +MODULE_LICENSE("GPL v2"); + +module_platform_driver(apple_pmgr_ps_driver); From 500daa0e6be292edcf635ba6b090b89da80e90a8 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 1 Dec 2021 16:32:56 +0900 Subject: [PATCH 32/94] dt-bindings: power: Add r8a779f0 SYSC power domain definitions Add power domain indices for R-Car S4-8 (r8a779f0). Signed-off-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20211201073308.1003945-3-yoshihiro.shimoda.uh@renesas.com Signed-off-by: Geert Uytterhoeven --- include/dt-bindings/power/r8a779f0-sysc.h | 30 +++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 include/dt-bindings/power/r8a779f0-sysc.h diff --git a/include/dt-bindings/power/r8a779f0-sysc.h b/include/dt-bindings/power/r8a779f0-sysc.h new file mode 100644 index 000000000000..0ec8ad727ed9 --- /dev/null +++ b/include/dt-bindings/power/r8a779f0-sysc.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: (GPL-2.0 or MIT) */ +/* + * Copyright (C) 2021 Renesas Electronics Corp. + */ +#ifndef __DT_BINDINGS_POWER_R8A779F0_SYSC_H__ +#define __DT_BINDINGS_POWER_R8A779F0_SYSC_H__ + +/* + * These power domain indices match the Power Domain Register Numbers (PDR) + */ + +#define R8A779F0_PD_A1E0D0C0 0 +#define R8A779F0_PD_A1E0D0C1 1 +#define R8A779F0_PD_A1E0D1C0 2 +#define R8A779F0_PD_A1E0D1C1 3 +#define R8A779F0_PD_A1E1D0C0 4 +#define R8A779F0_PD_A1E1D0C1 5 +#define R8A779F0_PD_A1E1D1C0 6 +#define R8A779F0_PD_A1E1D1C1 7 +#define R8A779F0_PD_A2E0D0 16 +#define R8A779F0_PD_A2E0D1 17 +#define R8A779F0_PD_A2E1D0 18 +#define R8A779F0_PD_A2E1D1 19 +#define R8A779F0_PD_A3E0 20 +#define R8A779F0_PD_A3E1 21 + +/* Always-on power area */ +#define R8A779F0_PD_ALWAYS_ON 64 + +#endif /* __DT_BINDINGS_POWER_R8A779A0_SYSC_H__*/ From 81c1655823237eaec74b2c175ae8e13adfccdaf7 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 1 Dec 2021 16:32:57 +0900 Subject: [PATCH 33/94] dt-bindings: clock: Add r8a779f0 CPG Core Clock Definitions Add all Clock Pulse Generator Core Clock Outputs for the Renesas R-Car S4-8 (R8A779F0) SoC. Signed-off-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20211201073308.1003945-4-yoshihiro.shimoda.uh@renesas.com Signed-off-by: Geert Uytterhoeven --- include/dt-bindings/clock/r8a779f0-cpg-mssr.h | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 include/dt-bindings/clock/r8a779f0-cpg-mssr.h diff --git a/include/dt-bindings/clock/r8a779f0-cpg-mssr.h b/include/dt-bindings/clock/r8a779f0-cpg-mssr.h new file mode 100644 index 000000000000..f2ae1c6a82dd --- /dev/null +++ b/include/dt-bindings/clock/r8a779f0-cpg-mssr.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: (GPL-2.0 or MIT) */ +/* + * Copyright (C) 2021 Renesas Electronics Corp. + */ +#ifndef __DT_BINDINGS_CLOCK_R8A779F0_CPG_MSSR_H__ +#define __DT_BINDINGS_CLOCK_R8A779F0_CPG_MSSR_H__ + +#include + +/* r8a779f0 CPG Core Clocks */ + +#define R8A779F0_CLK_ZX 0 +#define R8A779F0_CLK_ZS 1 +#define R8A779F0_CLK_ZT 2 +#define R8A779F0_CLK_ZTR 3 +#define R8A779F0_CLK_S0D2 4 +#define R8A779F0_CLK_S0D3 5 +#define R8A779F0_CLK_S0D4 6 +#define R8A779F0_CLK_S0D2_MM 7 +#define R8A779F0_CLK_S0D3_MM 8 +#define R8A779F0_CLK_S0D4_MM 9 +#define R8A779F0_CLK_S0D2_RT 10 +#define R8A779F0_CLK_S0D3_RT 11 +#define R8A779F0_CLK_S0D4_RT 12 +#define R8A779F0_CLK_S0D6_RT 13 +#define R8A779F0_CLK_S0D3_PER 14 +#define R8A779F0_CLK_S0D6_PER 15 +#define R8A779F0_CLK_S0D12_PER 16 +#define R8A779F0_CLK_S0D24_PER 17 +#define R8A779F0_CLK_S0D2_HSC 18 +#define R8A779F0_CLK_S0D3_HSC 19 +#define R8A779F0_CLK_S0D4_HSC 20 +#define R8A779F0_CLK_S0D6_HSC 21 +#define R8A779F0_CLK_S0D12_HSC 22 +#define R8A779F0_CLK_S0D2_CC 23 +#define R8A779F0_CLK_CL 24 +#define R8A779F0_CLK_CL16M 25 +#define R8A779F0_CLK_CL16M_MM 26 +#define R8A779F0_CLK_CL16M_RT 27 +#define R8A779F0_CLK_CL16M_PER 28 +#define R8A779F0_CLK_CL16M_HSC 29 +#define R8A779F0_CLK_Z0 30 +#define R8A779F0_CLK_Z1 31 +#define R8A779F0_CLK_ZB3 32 +#define R8A779F0_CLK_ZB3D2 33 +#define R8A779F0_CLK_ZB3D4 34 +#define R8A779F0_CLK_SD0H 35 +#define R8A779F0_CLK_SD0 36 +#define R8A779F0_CLK_RPC 37 +#define R8A779F0_CLK_RPCD2 38 +#define R8A779F0_CLK_MSO 39 +#define R8A779F0_CLK_SASYNCRT 40 +#define R8A779F0_CLK_SASYNCPERD1 41 +#define R8A779F0_CLK_SASYNCPERD2 42 +#define R8A779F0_CLK_SASYNCPERD4 43 +#define R8A779F0_CLK_DBGSOC_HSC 44 +#define R8A779F0_CLK_RSW2 45 +#define R8A779F0_CLK_OSC 46 +#define R8A779F0_CLK_ZR 47 +#define R8A779F0_CLK_CPEX 48 +#define R8A779F0_CLK_CBFUSA 49 +#define R8A779F0_CLK_R 50 + +#endif /* __DT_BINDINGS_CLOCK_R8A779F0_CPG_MSSR_H__ */ From e62906d6315f652b80640df0f980086462a4953b Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 1 Dec 2021 16:32:59 +0900 Subject: [PATCH 34/94] soc: renesas: rcar-gen4-sysc: Introduce R-Car Gen4 SYSC driver According to the official website [1], the R-Car V3U SoC is based on the R-Car Gen4 architecture. So, introduce R-Car Gen4 SYSC driver. [1] https://www.renesas.com/us/en/products/automotive-products/automotive-system-chips-socs/r-car-v3u-best-class-r-car-v3u-asil-d-system-chip-automated-driving Signed-off-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20211201073308.1003945-6-yoshihiro.shimoda.uh@renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/soc/renesas/Kconfig | 4 + drivers/soc/renesas/Makefile | 1 + drivers/soc/renesas/r8a779a0-sysc.c | 380 +-------------------------- drivers/soc/renesas/rcar-gen4-sysc.c | 373 ++++++++++++++++++++++++++ drivers/soc/renesas/rcar-gen4-sysc.h | 42 +++ 5 files changed, 423 insertions(+), 377 deletions(-) create mode 100644 drivers/soc/renesas/rcar-gen4-sysc.c create mode 100644 drivers/soc/renesas/rcar-gen4-sysc.h diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index ce16ef5c939c..2b09d6cf0d66 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -297,6 +297,9 @@ config RST_RCAR config SYSC_RCAR bool "System Controller support for R-Car" if COMPILE_TEST +config SYSC_RCAR_GEN4 + bool "System Controller support for R-Car Gen4" if COMPILE_TEST + config SYSC_R8A77995 bool "System Controller support for R-Car D3" if COMPILE_TEST select SYSC_RCAR @@ -351,6 +354,7 @@ config SYSC_R8A77970 config SYSC_R8A779A0 bool "System Controller support for R-Car V3U" if COMPILE_TEST + select SYSC_RCAR_GEN4 config SYSC_RMOBILE bool "System Controller support for R-Mobile" if COMPILE_TEST diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile index 9b29bed2a597..db90f3b06007 100644 --- a/drivers/soc/renesas/Makefile +++ b/drivers/soc/renesas/Makefile @@ -32,4 +32,5 @@ endif # Family obj-$(CONFIG_RST_RCAR) += rcar-rst.o obj-$(CONFIG_SYSC_RCAR) += rcar-sysc.o +obj-$(CONFIG_SYSC_RCAR_GEN4) += rcar-gen4-sysc.o obj-$(CONFIG_SYSC_RMOBILE) += rmobile-sysc.o diff --git a/drivers/soc/renesas/r8a779a0-sysc.c b/drivers/soc/renesas/r8a779a0-sysc.c index 7410b9fa9846..fdfc857df334 100644 --- a/drivers/soc/renesas/r8a779a0-sysc.c +++ b/drivers/soc/renesas/r8a779a0-sysc.c @@ -21,35 +21,9 @@ #include -/* - * Power Domain flags - */ -#define PD_CPU BIT(0) /* Area contains main CPU core */ -#define PD_SCU BIT(1) /* Area contains SCU and L2 cache */ -#define PD_NO_CR BIT(2) /* Area lacks PWR{ON,OFF}CR registers */ +#include "rcar-gen4-sysc.h" -#define PD_CPU_NOCR PD_CPU | PD_NO_CR /* CPU area lacks CR */ -#define PD_ALWAYS_ON PD_NO_CR /* Always-on area */ - -/* - * Description of a Power Area - */ -struct r8a779a0_sysc_area { - const char *name; - u8 pdr; /* PDRn */ - int parent; /* -1 if none */ - unsigned int flags; /* See PD_* */ -}; - -/* - * SoC-specific Power Area Description - */ -struct r8a779a0_sysc_info { - const struct r8a779a0_sysc_area *areas; - unsigned int num_areas; -}; - -static struct r8a779a0_sysc_area r8a779a0_areas[] __initdata = { +static struct rcar_gen4_sysc_area r8a779a0_areas[] __initdata = { { "always-on", R8A779A0_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, { "a3e0", R8A779A0_PD_A3E0, R8A779A0_PD_ALWAYS_ON, PD_SCU }, { "a3e1", R8A779A0_PD_A3E1, R8A779A0_PD_ALWAYS_ON, PD_SCU }, @@ -96,355 +70,7 @@ static struct r8a779a0_sysc_area r8a779a0_areas[] __initdata = { { "a1dsp1", R8A779A0_PD_A1DSP1, R8A779A0_PD_A2CN1 }, }; -static const struct r8a779a0_sysc_info r8a779a0_sysc_info __initconst = { +const struct rcar_gen4_sysc_info r8a779a0_sysc_info __initconst = { .areas = r8a779a0_areas, .num_areas = ARRAY_SIZE(r8a779a0_areas), }; - -/* SYSC Common */ -#define SYSCSR 0x000 /* SYSC Status Register */ -#define SYSCPONSR(x) (0x800 + ((x) * 0x4)) /* Power-ON Status Register 0 */ -#define SYSCPOFFSR(x) (0x808 + ((x) * 0x4)) /* Power-OFF Status Register */ -#define SYSCISCR(x) (0x810 + ((x) * 0x4)) /* Interrupt Status/Clear Register */ -#define SYSCIER(x) (0x820 + ((x) * 0x4)) /* Interrupt Enable Register */ -#define SYSCIMR(x) (0x830 + ((x) * 0x4)) /* Interrupt Mask Register */ - -/* Power Domain Registers */ -#define PDRSR(n) (0x1000 + ((n) * 0x40)) -#define PDRONCR(n) (0x1004 + ((n) * 0x40)) -#define PDROFFCR(n) (0x1008 + ((n) * 0x40)) -#define PDRESR(n) (0x100C + ((n) * 0x40)) - -/* PWRON/PWROFF */ -#define PWRON_PWROFF BIT(0) /* Power-ON/OFF request */ - -/* PDRESR */ -#define PDRESR_ERR BIT(0) - -/* PDRSR */ -#define PDRSR_OFF BIT(0) /* Power-OFF state */ -#define PDRSR_ON BIT(4) /* Power-ON state */ -#define PDRSR_OFF_STATE BIT(8) /* Processing Power-OFF sequence */ -#define PDRSR_ON_STATE BIT(12) /* Processing Power-ON sequence */ - -#define SYSCSR_BUSY GENMASK(1, 0) /* All bit sets is not busy */ - -#define SYSCSR_TIMEOUT 10000 -#define SYSCSR_DELAY_US 10 - -#define PDRESR_RETRIES 1000 -#define PDRESR_DELAY_US 10 - -#define SYSCISR_TIMEOUT 10000 -#define SYSCISR_DELAY_US 10 - -#define NUM_DOMAINS_EACH_REG BITS_PER_TYPE(u32) - -static void __iomem *r8a779a0_sysc_base; -static DEFINE_SPINLOCK(r8a779a0_sysc_lock); /* SMP CPUs + I/O devices */ - -static int r8a779a0_sysc_pwr_on_off(u8 pdr, bool on) -{ - unsigned int reg_offs; - u32 val; - int ret; - - if (on) - reg_offs = PDRONCR(pdr); - else - reg_offs = PDROFFCR(pdr); - - /* Wait until SYSC is ready to accept a power request */ - ret = readl_poll_timeout_atomic(r8a779a0_sysc_base + SYSCSR, val, - (val & SYSCSR_BUSY) == SYSCSR_BUSY, - SYSCSR_DELAY_US, SYSCSR_TIMEOUT); - if (ret < 0) - return -EAGAIN; - - /* Submit power shutoff or power resume request */ - iowrite32(PWRON_PWROFF, r8a779a0_sysc_base + reg_offs); - - return 0; -} - -static int clear_irq_flags(unsigned int reg_idx, unsigned int isr_mask) -{ - u32 val; - int ret; - - iowrite32(isr_mask, r8a779a0_sysc_base + SYSCISCR(reg_idx)); - - ret = readl_poll_timeout_atomic(r8a779a0_sysc_base + SYSCISCR(reg_idx), - val, !(val & isr_mask), - SYSCISR_DELAY_US, SYSCISR_TIMEOUT); - if (ret < 0) { - pr_err("\n %s : Can not clear IRQ flags in SYSCISCR", __func__); - return -EIO; - } - - return 0; -} - -static int r8a779a0_sysc_power(u8 pdr, bool on) -{ - unsigned int isr_mask; - unsigned int reg_idx, bit_idx; - unsigned int status; - unsigned long flags; - int ret = 0; - u32 val; - int k; - - spin_lock_irqsave(&r8a779a0_sysc_lock, flags); - - reg_idx = pdr / NUM_DOMAINS_EACH_REG; - bit_idx = pdr % NUM_DOMAINS_EACH_REG; - - isr_mask = BIT(bit_idx); - - /* - * The interrupt source needs to be enabled, but masked, to prevent the - * CPU from receiving it. - */ - iowrite32(ioread32(r8a779a0_sysc_base + SYSCIER(reg_idx)) | isr_mask, - r8a779a0_sysc_base + SYSCIER(reg_idx)); - iowrite32(ioread32(r8a779a0_sysc_base + SYSCIMR(reg_idx)) | isr_mask, - r8a779a0_sysc_base + SYSCIMR(reg_idx)); - - ret = clear_irq_flags(reg_idx, isr_mask); - if (ret) - goto out; - - /* Submit power shutoff or resume request until it was accepted */ - for (k = 0; k < PDRESR_RETRIES; k++) { - ret = r8a779a0_sysc_pwr_on_off(pdr, on); - if (ret) - goto out; - - status = ioread32(r8a779a0_sysc_base + PDRESR(pdr)); - if (!(status & PDRESR_ERR)) - break; - - udelay(PDRESR_DELAY_US); - } - - if (k == PDRESR_RETRIES) { - ret = -EIO; - goto out; - } - - /* Wait until the power shutoff or resume request has completed * */ - ret = readl_poll_timeout_atomic(r8a779a0_sysc_base + SYSCISCR(reg_idx), - val, (val & isr_mask), - SYSCISR_DELAY_US, SYSCISR_TIMEOUT); - if (ret < 0) { - ret = -EIO; - goto out; - } - - /* Clear interrupt flags */ - ret = clear_irq_flags(reg_idx, isr_mask); - if (ret) - goto out; - - out: - spin_unlock_irqrestore(&r8a779a0_sysc_lock, flags); - - pr_debug("sysc power %s domain %d: %08x -> %d\n", on ? "on" : "off", - pdr, ioread32(r8a779a0_sysc_base + SYSCISCR(reg_idx)), ret); - return ret; -} - -static bool r8a779a0_sysc_power_is_off(u8 pdr) -{ - unsigned int st; - - st = ioread32(r8a779a0_sysc_base + PDRSR(pdr)); - - if (st & PDRSR_OFF) - return true; - - return false; -} - -struct r8a779a0_sysc_pd { - struct generic_pm_domain genpd; - u8 pdr; - unsigned int flags; - char name[]; -}; - -static inline struct r8a779a0_sysc_pd *to_r8a779a0_pd(struct generic_pm_domain *d) -{ - return container_of(d, struct r8a779a0_sysc_pd, genpd); -} - -static int r8a779a0_sysc_pd_power_off(struct generic_pm_domain *genpd) -{ - struct r8a779a0_sysc_pd *pd = to_r8a779a0_pd(genpd); - - pr_debug("%s: %s\n", __func__, genpd->name); - return r8a779a0_sysc_power(pd->pdr, false); -} - -static int r8a779a0_sysc_pd_power_on(struct generic_pm_domain *genpd) -{ - struct r8a779a0_sysc_pd *pd = to_r8a779a0_pd(genpd); - - pr_debug("%s: %s\n", __func__, genpd->name); - return r8a779a0_sysc_power(pd->pdr, true); -} - -static int __init r8a779a0_sysc_pd_setup(struct r8a779a0_sysc_pd *pd) -{ - struct generic_pm_domain *genpd = &pd->genpd; - const char *name = pd->genpd.name; - int error; - - if (pd->flags & PD_CPU) { - /* - * This domain contains a CPU core and therefore it should - * only be turned off if the CPU is not in use. - */ - pr_debug("PM domain %s contains %s\n", name, "CPU"); - genpd->flags |= GENPD_FLAG_ALWAYS_ON; - } else if (pd->flags & PD_SCU) { - /* - * This domain contains an SCU and cache-controller, and - * therefore it should only be turned off if the CPU cores are - * not in use. - */ - pr_debug("PM domain %s contains %s\n", name, "SCU"); - genpd->flags |= GENPD_FLAG_ALWAYS_ON; - } else if (pd->flags & PD_NO_CR) { - /* - * This domain cannot be turned off. - */ - genpd->flags |= GENPD_FLAG_ALWAYS_ON; - } - - if (!(pd->flags & (PD_CPU | PD_SCU))) { - /* Enable Clock Domain for I/O devices */ - genpd->flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP; - genpd->attach_dev = cpg_mssr_attach_dev; - genpd->detach_dev = cpg_mssr_detach_dev; - } - - genpd->power_off = r8a779a0_sysc_pd_power_off; - genpd->power_on = r8a779a0_sysc_pd_power_on; - - if (pd->flags & (PD_CPU | PD_NO_CR)) { - /* Skip CPUs (handled by SMP code) and areas without control */ - pr_debug("%s: Not touching %s\n", __func__, genpd->name); - goto finalize; - } - - if (!r8a779a0_sysc_power_is_off(pd->pdr)) { - pr_debug("%s: %s is already powered\n", __func__, genpd->name); - goto finalize; - } - - r8a779a0_sysc_power(pd->pdr, true); - -finalize: - error = pm_genpd_init(genpd, &simple_qos_governor, false); - if (error) - pr_err("Failed to init PM domain %s: %d\n", name, error); - - return error; -} - -static const struct of_device_id r8a779a0_sysc_matches[] __initconst = { - { .compatible = "renesas,r8a779a0-sysc", .data = &r8a779a0_sysc_info }, - { /* sentinel */ } -}; - -struct r8a779a0_pm_domains { - struct genpd_onecell_data onecell_data; - struct generic_pm_domain *domains[R8A779A0_PD_ALWAYS_ON + 1]; -}; - -static struct genpd_onecell_data *r8a779a0_sysc_onecell_data; - -static int __init r8a779a0_sysc_pd_init(void) -{ - const struct r8a779a0_sysc_info *info; - const struct of_device_id *match; - struct r8a779a0_pm_domains *domains; - struct device_node *np; - void __iomem *base; - unsigned int i; - int error; - - np = of_find_matching_node_and_match(NULL, r8a779a0_sysc_matches, &match); - if (!np) - return -ENODEV; - - info = match->data; - - base = of_iomap(np, 0); - if (!base) { - pr_warn("%pOF: Cannot map regs\n", np); - error = -ENOMEM; - goto out_put; - } - - r8a779a0_sysc_base = base; - - domains = kzalloc(sizeof(*domains), GFP_KERNEL); - if (!domains) { - error = -ENOMEM; - goto out_put; - } - - domains->onecell_data.domains = domains->domains; - domains->onecell_data.num_domains = ARRAY_SIZE(domains->domains); - r8a779a0_sysc_onecell_data = &domains->onecell_data; - - for (i = 0; i < info->num_areas; i++) { - const struct r8a779a0_sysc_area *area = &info->areas[i]; - struct r8a779a0_sysc_pd *pd; - size_t n; - - if (!area->name) { - /* Skip NULLified area */ - continue; - } - - n = strlen(area->name) + 1; - pd = kzalloc(sizeof(*pd) + n, GFP_KERNEL); - if (!pd) { - error = -ENOMEM; - goto out_put; - } - - memcpy(pd->name, area->name, n); - pd->genpd.name = pd->name; - pd->pdr = area->pdr; - pd->flags = area->flags; - - error = r8a779a0_sysc_pd_setup(pd); - if (error) - goto out_put; - - domains->domains[area->pdr] = &pd->genpd; - - if (area->parent < 0) - continue; - - error = pm_genpd_add_subdomain(domains->domains[area->parent], - &pd->genpd); - if (error) { - pr_warn("Failed to add PM subdomain %s to parent %u\n", - area->name, area->parent); - goto out_put; - } - } - - error = of_genpd_add_provider_onecell(np, &domains->onecell_data); - -out_put: - of_node_put(np); - return error; -} -early_initcall(r8a779a0_sysc_pd_init); diff --git a/drivers/soc/renesas/rcar-gen4-sysc.c b/drivers/soc/renesas/rcar-gen4-sysc.c new file mode 100644 index 000000000000..d9a0929f5f1d --- /dev/null +++ b/drivers/soc/renesas/rcar-gen4-sysc.c @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * R-Car Gen4 SYSC Power management support + * + * Copyright (C) 2021 Renesas Electronics Corp. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rcar-gen4-sysc.h" + +/* SYSC Common */ +#define SYSCSR 0x000 /* SYSC Status Register */ +#define SYSCPONSR(x) (0x800 + ((x) * 0x4)) /* Power-ON Status Register 0 */ +#define SYSCPOFFSR(x) (0x808 + ((x) * 0x4)) /* Power-OFF Status Register */ +#define SYSCISCR(x) (0x810 + ((x) * 0x4)) /* Interrupt Status/Clear Register */ +#define SYSCIER(x) (0x820 + ((x) * 0x4)) /* Interrupt Enable Register */ +#define SYSCIMR(x) (0x830 + ((x) * 0x4)) /* Interrupt Mask Register */ + +/* Power Domain Registers */ +#define PDRSR(n) (0x1000 + ((n) * 0x40)) +#define PDRONCR(n) (0x1004 + ((n) * 0x40)) +#define PDROFFCR(n) (0x1008 + ((n) * 0x40)) +#define PDRESR(n) (0x100C + ((n) * 0x40)) + +/* PWRON/PWROFF */ +#define PWRON_PWROFF BIT(0) /* Power-ON/OFF request */ + +/* PDRESR */ +#define PDRESR_ERR BIT(0) + +/* PDRSR */ +#define PDRSR_OFF BIT(0) /* Power-OFF state */ +#define PDRSR_ON BIT(4) /* Power-ON state */ +#define PDRSR_OFF_STATE BIT(8) /* Processing Power-OFF sequence */ +#define PDRSR_ON_STATE BIT(12) /* Processing Power-ON sequence */ + +#define SYSCSR_BUSY GENMASK(1, 0) /* All bit sets is not busy */ + +#define SYSCSR_TIMEOUT 10000 +#define SYSCSR_DELAY_US 10 + +#define PDRESR_RETRIES 1000 +#define PDRESR_DELAY_US 10 + +#define SYSCISR_TIMEOUT 10000 +#define SYSCISR_DELAY_US 10 + +#define RCAR_GEN4_PD_ALWAYS_ON 64 +#define NUM_DOMAINS_EACH_REG BITS_PER_TYPE(u32) + +static void __iomem *rcar_gen4_sysc_base; +static DEFINE_SPINLOCK(rcar_gen4_sysc_lock); /* SMP CPUs + I/O devices */ + +static int rcar_gen4_sysc_pwr_on_off(u8 pdr, bool on) +{ + unsigned int reg_offs; + u32 val; + int ret; + + if (on) + reg_offs = PDRONCR(pdr); + else + reg_offs = PDROFFCR(pdr); + + /* Wait until SYSC is ready to accept a power request */ + ret = readl_poll_timeout_atomic(rcar_gen4_sysc_base + SYSCSR, val, + (val & SYSCSR_BUSY) == SYSCSR_BUSY, + SYSCSR_DELAY_US, SYSCSR_TIMEOUT); + if (ret < 0) + return -EAGAIN; + + /* Submit power shutoff or power resume request */ + iowrite32(PWRON_PWROFF, rcar_gen4_sysc_base + reg_offs); + + return 0; +} + +static int clear_irq_flags(unsigned int reg_idx, unsigned int isr_mask) +{ + u32 val; + int ret; + + iowrite32(isr_mask, rcar_gen4_sysc_base + SYSCISCR(reg_idx)); + + ret = readl_poll_timeout_atomic(rcar_gen4_sysc_base + SYSCISCR(reg_idx), + val, !(val & isr_mask), + SYSCISR_DELAY_US, SYSCISR_TIMEOUT); + if (ret < 0) { + pr_err("\n %s : Can not clear IRQ flags in SYSCISCR", __func__); + return -EIO; + } + + return 0; +} + +static int rcar_gen4_sysc_power(u8 pdr, bool on) +{ + unsigned int isr_mask; + unsigned int reg_idx, bit_idx; + unsigned int status; + unsigned long flags; + int ret = 0; + u32 val; + int k; + + spin_lock_irqsave(&rcar_gen4_sysc_lock, flags); + + reg_idx = pdr / NUM_DOMAINS_EACH_REG; + bit_idx = pdr % NUM_DOMAINS_EACH_REG; + + isr_mask = BIT(bit_idx); + + /* + * The interrupt source needs to be enabled, but masked, to prevent the + * CPU from receiving it. + */ + iowrite32(ioread32(rcar_gen4_sysc_base + SYSCIER(reg_idx)) | isr_mask, + rcar_gen4_sysc_base + SYSCIER(reg_idx)); + iowrite32(ioread32(rcar_gen4_sysc_base + SYSCIMR(reg_idx)) | isr_mask, + rcar_gen4_sysc_base + SYSCIMR(reg_idx)); + + ret = clear_irq_flags(reg_idx, isr_mask); + if (ret) + goto out; + + /* Submit power shutoff or resume request until it was accepted */ + for (k = 0; k < PDRESR_RETRIES; k++) { + ret = rcar_gen4_sysc_pwr_on_off(pdr, on); + if (ret) + goto out; + + status = ioread32(rcar_gen4_sysc_base + PDRESR(pdr)); + if (!(status & PDRESR_ERR)) + break; + + udelay(PDRESR_DELAY_US); + } + + if (k == PDRESR_RETRIES) { + ret = -EIO; + goto out; + } + + /* Wait until the power shutoff or resume request has completed * */ + ret = readl_poll_timeout_atomic(rcar_gen4_sysc_base + SYSCISCR(reg_idx), + val, (val & isr_mask), + SYSCISR_DELAY_US, SYSCISR_TIMEOUT); + if (ret < 0) { + ret = -EIO; + goto out; + } + + /* Clear interrupt flags */ + ret = clear_irq_flags(reg_idx, isr_mask); + if (ret) + goto out; + + out: + spin_unlock_irqrestore(&rcar_gen4_sysc_lock, flags); + + pr_debug("sysc power %s domain %d: %08x -> %d\n", on ? "on" : "off", + pdr, ioread32(rcar_gen4_sysc_base + SYSCISCR(reg_idx)), ret); + return ret; +} + +static bool rcar_gen4_sysc_power_is_off(u8 pdr) +{ + unsigned int st; + + st = ioread32(rcar_gen4_sysc_base + PDRSR(pdr)); + + if (st & PDRSR_OFF) + return true; + + return false; +} + +struct rcar_gen4_sysc_pd { + struct generic_pm_domain genpd; + u8 pdr; + unsigned int flags; + char name[]; +}; + +static inline struct rcar_gen4_sysc_pd *to_rcar_gen4_pd(struct generic_pm_domain *d) +{ + return container_of(d, struct rcar_gen4_sysc_pd, genpd); +} + +static int rcar_gen4_sysc_pd_power_off(struct generic_pm_domain *genpd) +{ + struct rcar_gen4_sysc_pd *pd = to_rcar_gen4_pd(genpd); + + pr_debug("%s: %s\n", __func__, genpd->name); + return rcar_gen4_sysc_power(pd->pdr, false); +} + +static int rcar_gen4_sysc_pd_power_on(struct generic_pm_domain *genpd) +{ + struct rcar_gen4_sysc_pd *pd = to_rcar_gen4_pd(genpd); + + pr_debug("%s: %s\n", __func__, genpd->name); + return rcar_gen4_sysc_power(pd->pdr, true); +} + +static int __init rcar_gen4_sysc_pd_setup(struct rcar_gen4_sysc_pd *pd) +{ + struct generic_pm_domain *genpd = &pd->genpd; + const char *name = pd->genpd.name; + int error; + + if (pd->flags & PD_CPU) { + /* + * This domain contains a CPU core and therefore it should + * only be turned off if the CPU is not in use. + */ + pr_debug("PM domain %s contains %s\n", name, "CPU"); + genpd->flags |= GENPD_FLAG_ALWAYS_ON; + } else if (pd->flags & PD_SCU) { + /* + * This domain contains an SCU and cache-controller, and + * therefore it should only be turned off if the CPU cores are + * not in use. + */ + pr_debug("PM domain %s contains %s\n", name, "SCU"); + genpd->flags |= GENPD_FLAG_ALWAYS_ON; + } else if (pd->flags & PD_NO_CR) { + /* + * This domain cannot be turned off. + */ + genpd->flags |= GENPD_FLAG_ALWAYS_ON; + } + + if (!(pd->flags & (PD_CPU | PD_SCU))) { + /* Enable Clock Domain for I/O devices */ + genpd->flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP; + genpd->attach_dev = cpg_mssr_attach_dev; + genpd->detach_dev = cpg_mssr_detach_dev; + } + + genpd->power_off = rcar_gen4_sysc_pd_power_off; + genpd->power_on = rcar_gen4_sysc_pd_power_on; + + if (pd->flags & (PD_CPU | PD_NO_CR)) { + /* Skip CPUs (handled by SMP code) and areas without control */ + pr_debug("%s: Not touching %s\n", __func__, genpd->name); + goto finalize; + } + + if (!rcar_gen4_sysc_power_is_off(pd->pdr)) { + pr_debug("%s: %s is already powered\n", __func__, genpd->name); + goto finalize; + } + + rcar_gen4_sysc_power(pd->pdr, true); + +finalize: + error = pm_genpd_init(genpd, &simple_qos_governor, false); + if (error) + pr_err("Failed to init PM domain %s: %d\n", name, error); + + return error; +} + +static const struct of_device_id rcar_gen4_sysc_matches[] __initconst = { +#ifdef CONFIG_SYSC_R8A779A0 + { .compatible = "renesas,r8a779a0-sysc", .data = &r8a779a0_sysc_info }, +#endif + { /* sentinel */ } +}; + +struct rcar_gen4_pm_domains { + struct genpd_onecell_data onecell_data; + struct generic_pm_domain *domains[RCAR_GEN4_PD_ALWAYS_ON + 1]; +}; + +static struct genpd_onecell_data *rcar_gen4_sysc_onecell_data; + +static int __init rcar_gen4_sysc_pd_init(void) +{ + const struct rcar_gen4_sysc_info *info; + const struct of_device_id *match; + struct rcar_gen4_pm_domains *domains; + struct device_node *np; + void __iomem *base; + unsigned int i; + int error; + + np = of_find_matching_node_and_match(NULL, rcar_gen4_sysc_matches, &match); + if (!np) + return -ENODEV; + + info = match->data; + + base = of_iomap(np, 0); + if (!base) { + pr_warn("%pOF: Cannot map regs\n", np); + error = -ENOMEM; + goto out_put; + } + + rcar_gen4_sysc_base = base; + + domains = kzalloc(sizeof(*domains), GFP_KERNEL); + if (!domains) { + error = -ENOMEM; + goto out_put; + } + + domains->onecell_data.domains = domains->domains; + domains->onecell_data.num_domains = ARRAY_SIZE(domains->domains); + rcar_gen4_sysc_onecell_data = &domains->onecell_data; + + for (i = 0; i < info->num_areas; i++) { + const struct rcar_gen4_sysc_area *area = &info->areas[i]; + struct rcar_gen4_sysc_pd *pd; + size_t n; + + if (!area->name) { + /* Skip NULLified area */ + continue; + } + + n = strlen(area->name) + 1; + pd = kzalloc(sizeof(*pd) + n, GFP_KERNEL); + if (!pd) { + error = -ENOMEM; + goto out_put; + } + + memcpy(pd->name, area->name, n); + pd->genpd.name = pd->name; + pd->pdr = area->pdr; + pd->flags = area->flags; + + error = rcar_gen4_sysc_pd_setup(pd); + if (error) + goto out_put; + + domains->domains[area->pdr] = &pd->genpd; + + if (area->parent < 0) + continue; + + error = pm_genpd_add_subdomain(domains->domains[area->parent], + &pd->genpd); + if (error) { + pr_warn("Failed to add PM subdomain %s to parent %u\n", + area->name, area->parent); + goto out_put; + } + } + + error = of_genpd_add_provider_onecell(np, &domains->onecell_data); + +out_put: + of_node_put(np); + return error; +} +early_initcall(rcar_gen4_sysc_pd_init); diff --git a/drivers/soc/renesas/rcar-gen4-sysc.h b/drivers/soc/renesas/rcar-gen4-sysc.h new file mode 100644 index 000000000000..3470a9e2f5c7 --- /dev/null +++ b/drivers/soc/renesas/rcar-gen4-sysc.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * R-Car Gen4 System Controller + * + * Copyright (C) 2021 Renesas Electronics Corp. + */ +#ifndef __SOC_RENESAS_RCAR_GEN4_SYSC_H__ +#define __SOC_RENESAS_RCAR_GEN4_SYSC_H__ + +#include + +/* + * Power Domain flags + */ +#define PD_CPU BIT(0) /* Area contains main CPU core */ +#define PD_SCU BIT(1) /* Area contains SCU and L2 cache */ +#define PD_NO_CR BIT(2) /* Area lacks PWR{ON,OFF}CR registers */ + +#define PD_CPU_NOCR (PD_CPU | PD_NO_CR) /* CPU area lacks CR */ +#define PD_ALWAYS_ON PD_NO_CR /* Always-on area */ + +/* + * Description of a Power Area + */ +struct rcar_gen4_sysc_area { + const char *name; + u8 pdr; /* PDRn */ + int parent; /* -1 if none */ + unsigned int flags; /* See PD_* */ +}; + +/* + * SoC-specific Power Area Description + */ +struct rcar_gen4_sysc_info { + const struct rcar_gen4_sysc_area *areas; + unsigned int num_areas; +}; + +extern const struct rcar_gen4_sysc_info r8a779a0_sysc_info; + +#endif /* __SOC_RENESAS_RCAR_GEN4_SYSC_H__ */ From 654d5fdb8923994f5e79a7f0faf722958d1d759f Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 1 Dec 2021 16:33:00 +0900 Subject: [PATCH 35/94] soc: renesas: r8a779f0-sysc: Add r8a779f0 support Add support for R-Car S4-8 (R8A779F0) SoC power areas and register access. Signed-off-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20211201073308.1003945-7-yoshihiro.shimoda.uh@renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/soc/renesas/Kconfig | 4 +++ drivers/soc/renesas/Makefile | 1 + drivers/soc/renesas/r8a779f0-sysc.c | 47 ++++++++++++++++++++++++++++ drivers/soc/renesas/rcar-gen4-sysc.c | 3 ++ drivers/soc/renesas/rcar-gen4-sysc.h | 1 + 5 files changed, 56 insertions(+) create mode 100644 drivers/soc/renesas/r8a779f0-sysc.c diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 2b09d6cf0d66..59a4106a66e9 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -340,6 +340,10 @@ config SYSC_R8A77961 bool "System Controller support for R-Car M3-W+" if COMPILE_TEST select SYSC_RCAR +config SYSC_R8A779F0 + bool "System Controller support for R-Car S4-8" if COMPILE_TEST + select SYSC_RCAR_GEN4 + config SYSC_R8A7792 bool "System Controller support for R-Car V2H" if COMPILE_TEST select SYSC_RCAR diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile index db90f3b06007..deeb41f84f01 100644 --- a/drivers/soc/renesas/Makefile +++ b/drivers/soc/renesas/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_SYSC_R8A77980) += r8a77980-sysc.o obj-$(CONFIG_SYSC_R8A77990) += r8a77990-sysc.o obj-$(CONFIG_SYSC_R8A77995) += r8a77995-sysc.o obj-$(CONFIG_SYSC_R8A779A0) += r8a779a0-sysc.o +obj-$(CONFIG_SYSC_R8A779F0) += r8a779f0-sysc.o ifdef CONFIG_SMP obj-$(CONFIG_ARCH_R9A06G032) += r9a06g032-smp.o endif diff --git a/drivers/soc/renesas/r8a779f0-sysc.c b/drivers/soc/renesas/r8a779f0-sysc.c new file mode 100644 index 000000000000..5602aa6bd7ed --- /dev/null +++ b/drivers/soc/renesas/r8a779f0-sysc.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas R-Car S4-8 System Controller + * + * Copyright (C) 2021 Renesas Electronics Corp. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "rcar-gen4-sysc.h" + +static struct rcar_gen4_sysc_area r8a779f0_areas[] __initdata = { + { "always-on", R8A779F0_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "a3e0", R8A779F0_PD_A3E0, R8A779F0_PD_ALWAYS_ON, PD_SCU }, + { "a3e1", R8A779F0_PD_A3E1, R8A779F0_PD_ALWAYS_ON, PD_SCU }, + { "a2e0d0", R8A779F0_PD_A2E0D0, R8A779F0_PD_A3E0, PD_SCU }, + { "a2e0d1", R8A779F0_PD_A2E0D1, R8A779F0_PD_A3E0, PD_SCU }, + { "a2e1d0", R8A779F0_PD_A2E1D0, R8A779F0_PD_A3E1, PD_SCU }, + { "a2e1d1", R8A779F0_PD_A2E1D1, R8A779F0_PD_A3E1, PD_SCU }, + { "a1e0d0c0", R8A779F0_PD_A1E0D0C0, R8A779F0_PD_A2E0D0, PD_CPU_NOCR }, + { "a1e0d0c1", R8A779F0_PD_A1E0D0C1, R8A779F0_PD_A2E0D0, PD_CPU_NOCR }, + { "a1e0d1c0", R8A779F0_PD_A1E0D1C0, R8A779F0_PD_A2E0D1, PD_CPU_NOCR }, + { "a1e0d1c1", R8A779F0_PD_A1E0D1C1, R8A779F0_PD_A2E0D1, PD_CPU_NOCR }, + { "a1e1d0c0", R8A779F0_PD_A1E1D0C0, R8A779F0_PD_A2E1D0, PD_CPU_NOCR }, + { "a1e1d0c1", R8A779F0_PD_A1E1D0C1, R8A779F0_PD_A2E1D0, PD_CPU_NOCR }, + { "a1e1d1c0", R8A779F0_PD_A1E1D1C0, R8A779F0_PD_A2E1D1, PD_CPU_NOCR }, + { "a1e1d1c1", R8A779F0_PD_A1E1D1C1, R8A779F0_PD_A2E1D1, PD_CPU_NOCR }, +}; + +const struct rcar_gen4_sysc_info r8a779f0_sysc_info __initconst = { + .areas = r8a779f0_areas, + .num_areas = ARRAY_SIZE(r8a779f0_areas), +}; diff --git a/drivers/soc/renesas/rcar-gen4-sysc.c b/drivers/soc/renesas/rcar-gen4-sysc.c index d9a0929f5f1d..831162a57f9a 100644 --- a/drivers/soc/renesas/rcar-gen4-sysc.c +++ b/drivers/soc/renesas/rcar-gen4-sysc.c @@ -278,6 +278,9 @@ static int __init rcar_gen4_sysc_pd_setup(struct rcar_gen4_sysc_pd *pd) static const struct of_device_id rcar_gen4_sysc_matches[] __initconst = { #ifdef CONFIG_SYSC_R8A779A0 { .compatible = "renesas,r8a779a0-sysc", .data = &r8a779a0_sysc_info }, +#endif +#ifdef CONFIG_SYSC_R8A779F0 + { .compatible = "renesas,r8a779f0-sysc", .data = &r8a779f0_sysc_info }, #endif { /* sentinel */ } }; diff --git a/drivers/soc/renesas/rcar-gen4-sysc.h b/drivers/soc/renesas/rcar-gen4-sysc.h index 3470a9e2f5c7..0e0bd102b1f9 100644 --- a/drivers/soc/renesas/rcar-gen4-sysc.h +++ b/drivers/soc/renesas/rcar-gen4-sysc.h @@ -38,5 +38,6 @@ struct rcar_gen4_sysc_info { }; extern const struct rcar_gen4_sysc_info r8a779a0_sysc_info; +extern const struct rcar_gen4_sysc_info r8a779f0_sysc_info; #endif /* __SOC_RENESAS_RCAR_GEN4_SYSC_H__ */ From 9711633587f4fb4376265781c75e39c2a7b21d6f Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 1 Dec 2021 16:33:01 +0900 Subject: [PATCH 36/94] soc: renesas: Identify R-Car S4-8 Add support for identifying the R-Car S4-8 (R8A779F0) SoC. Signed-off-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20211201073308.1003945-8-yoshihiro.shimoda.uh@renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/soc/renesas/Kconfig | 7 +++++++ drivers/soc/renesas/renesas-soc.c | 12 ++++++++++++ 2 files changed, 19 insertions(+) diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 59a4106a66e9..2cbd03db2cc7 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -235,6 +235,13 @@ config ARCH_R8A77961 This enables support for the Renesas R-Car M3-W+ SoC. This includes different gradings like R-Car M3e and M3e-2G. +config ARCH_R8A779F0 + bool "ARM64 Platform support for R-Car S4-8" + select ARCH_RCAR_GEN3 + select SYSC_R8A779F0 + help + This enables support for the Renesas R-Car S4-8 SoC. + config ARCH_R8A77980 bool "ARM64 Platform support for R-Car V3H" select ARCH_RCAR_GEN3 diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c index dff90f504963..62540ffc581a 100644 --- a/drivers/soc/renesas/renesas-soc.c +++ b/drivers/soc/renesas/renesas-soc.c @@ -33,6 +33,10 @@ static const struct renesas_family fam_rcar_gen3 __initconst __maybe_unused = { .reg = 0xfff00044, /* PRR (Product Register) */ }; +static const struct renesas_family fam_rcar_gen4 __initconst __maybe_unused = { + .name = "R-Car Gen4", +}; + static const struct renesas_family fam_rmobile __initconst __maybe_unused = { .name = "R-Mobile", .reg = 0xe600101c, /* CCCR (Common Chip Code Register) */ @@ -214,6 +218,11 @@ static const struct renesas_soc soc_rcar_v3u __initconst __maybe_unused = { .id = 0x59, }; +static const struct renesas_soc soc_rcar_s4 __initconst __maybe_unused = { + .family = &fam_rcar_gen4, + .id = 0x5a, +}; + static const struct renesas_soc soc_shmobile_ag5 __initconst __maybe_unused = { .family = &fam_shmobile, .id = 0x37, @@ -319,6 +328,9 @@ static const struct of_device_id renesas_socs[] __initconst = { #ifdef CONFIG_ARCH_R8A779A0 { .compatible = "renesas,r8a779a0", .data = &soc_rcar_v3u }, #endif +#ifdef CONFIG_ARCH_R8A779F0 + { .compatible = "renesas,r8a779f0", .data = &soc_rcar_s4 }, +#endif #if defined(CONFIG_ARCH_R9A07G044) { .compatible = "renesas,r9a07g044", .data = &soc_rz_g2l }, #endif From 363b41dd25397f553bebf87594eca9b2d6445b55 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 1 Dec 2021 16:33:04 +0900 Subject: [PATCH 37/94] soc: renesas: rcar-rst: Add support for R-Car S4-8 Add support for R-Car S4-8 (R8A779F0) to the R-Car RST driver. The register map of R-Car S4-8 is the same as R-Car V3U so that renames "V3U" and "r8a779a0" to "Gen4". Signed-off-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20211201073308.1003945-11-yoshihiro.shimoda.uh@renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/soc/renesas/rcar-rst.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/soc/renesas/rcar-rst.c b/drivers/soc/renesas/rcar-rst.c index 1ad54e0c32e1..4d293eb2d8f3 100644 --- a/drivers/soc/renesas/rcar-rst.c +++ b/drivers/soc/renesas/rcar-rst.c @@ -66,7 +66,7 @@ static const struct rst_config rcar_rst_gen3 __initconst = { .set_rproc_boot_addr = rcar_rst_set_gen3_rproc_boot_addr, }; -static const struct rst_config rcar_rst_r8a779a0 __initconst = { +static const struct rst_config rcar_rst_gen4 __initconst = { .modemr = 0x00, /* MODEMR0 and it has CPG related bits */ }; @@ -100,8 +100,9 @@ static const struct of_device_id rcar_rst_matches[] __initconst = { { .compatible = "renesas,r8a77980-rst", .data = &rcar_rst_gen3 }, { .compatible = "renesas,r8a77990-rst", .data = &rcar_rst_gen3 }, { .compatible = "renesas,r8a77995-rst", .data = &rcar_rst_gen3 }, - /* R-Car V3U */ - { .compatible = "renesas,r8a779a0-rst", .data = &rcar_rst_r8a779a0 }, + /* R-Car Gen4 */ + { .compatible = "renesas,r8a779a0-rst", .data = &rcar_rst_gen4 }, + { .compatible = "renesas,r8a779f0-rst", .data = &rcar_rst_gen4 }, { /* sentinel */ } }; From 7fd890b89dea55eb5866640eb8befad26d558161 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Wed, 25 Aug 2021 17:03:10 +0200 Subject: [PATCH 38/94] soc: xilinx: move PM_INIT_FINALIZE to zynqmp_pm_domains driver PM_INIT_FINALIZE tells the PMU FW that Linux is able to handle the power management nodes that are provided by the PMU FW. Nodes that are not requested are shut down after this call. Calling PM_INIT_FINALIZE from the zynqmp_power driver is wrong. The PM node request mechanism is implemented in the zynqmp_pm_domains driver, which must also call PM_INIT_FINALIZE. Due to the behavior of the PMU FW, all devices must be powered up before PM_INIT_FINALIZE is called, because otherwise the devices might misbehave. Calling PM_INIT_FINALIZE from the sync_state device callback ensures that all users probed successfully before the PMU FW is allowed to power off unused domains. Signed-off-by: Michael Tretter Acked-by: Michal Simek Acked-by: Rajan Vaja Link: https://lore.kernel.org/r/20210825150313.4033156-2-m.tretter@pengutronix.de Signed-off-by: Michal Simek --- drivers/soc/xilinx/zynqmp_pm_domains.c | 16 ++++++++++++++++ drivers/soc/xilinx/zynqmp_power.c | 1 - 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/soc/xilinx/zynqmp_pm_domains.c b/drivers/soc/xilinx/zynqmp_pm_domains.c index 226d343f0a6a..81e8e10f1092 100644 --- a/drivers/soc/xilinx/zynqmp_pm_domains.c +++ b/drivers/soc/xilinx/zynqmp_pm_domains.c @@ -152,11 +152,17 @@ static int zynqmp_gpd_power_off(struct generic_pm_domain *domain) static int zynqmp_gpd_attach_dev(struct generic_pm_domain *domain, struct device *dev) { + struct device_link *link; int ret; struct zynqmp_pm_domain *pd; pd = container_of(domain, struct zynqmp_pm_domain, gpd); + link = device_link_add(dev, &domain->dev, DL_FLAG_SYNC_STATE_ONLY); + if (!link) + dev_dbg(&domain->dev, "failed to create device link for %s\n", + dev_name(dev)); + /* If this is not the first device to attach there is nothing to do */ if (domain->device_count) return 0; @@ -299,9 +305,19 @@ static int zynqmp_gpd_remove(struct platform_device *pdev) return 0; } +static void zynqmp_gpd_sync_state(struct device *dev) +{ + int ret; + + ret = zynqmp_pm_init_finalize(); + if (ret) + dev_warn(dev, "failed to release power management to firmware\n"); +} + static struct platform_driver zynqmp_power_domain_driver = { .driver = { .name = "zynqmp_power_controller", + .sync_state = zynqmp_gpd_sync_state, }, .probe = zynqmp_gpd_probe, .remove = zynqmp_gpd_remove, diff --git a/drivers/soc/xilinx/zynqmp_power.c b/drivers/soc/xilinx/zynqmp_power.c index c556623dae02..f8c301984d4f 100644 --- a/drivers/soc/xilinx/zynqmp_power.c +++ b/drivers/soc/xilinx/zynqmp_power.c @@ -178,7 +178,6 @@ static int zynqmp_pm_probe(struct platform_device *pdev) u32 pm_api_version; struct mbox_client *client; - zynqmp_pm_init_finalize(); zynqmp_pm_get_api_version(&pm_api_version); /* Check PM API version number */ From c4245100f7461e0dd1f16ef0e0fcc19bcd826de3 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Wed, 25 Aug 2021 17:03:11 +0200 Subject: [PATCH 39/94] soc: xilinx: cleanup debug and error messages Use dev_err/dev_dbg instead of pr_err/pr_debug. Add the PM node ids to supplement the (arbitrary) power domain names to include information which PM nodes are requested by the driver. Drop function names from the messages, because they can easily be added with dynamic debug. Remove comments explaining that error messages are printed on errors. Signed-off-by: Michael Tretter Acked-by: Michal Simek Acked-by: Rajan Vaja Link: https://lore.kernel.org/r/20210825150313.4033156-3-m.tretter@pengutronix.de Signed-off-by: Michal Simek --- drivers/soc/xilinx/zynqmp_pm_domains.c | 45 +++++++++++++------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/drivers/soc/xilinx/zynqmp_pm_domains.c b/drivers/soc/xilinx/zynqmp_pm_domains.c index 81e8e10f1092..27c46c68e5cc 100644 --- a/drivers/soc/xilinx/zynqmp_pm_domains.c +++ b/drivers/soc/xilinx/zynqmp_pm_domains.c @@ -80,12 +80,15 @@ static int zynqmp_gpd_power_on(struct generic_pm_domain *domain) ZYNQMP_PM_MAX_QOS, ZYNQMP_PM_REQUEST_ACK_BLOCKING); if (ret) { - pr_err("%s() %s set requirement for node %d failed: %d\n", - __func__, domain->name, pd->node_id, ret); + dev_err(&domain->dev, + "failed to set requirement to 0x%x for PM node id %d: %d\n", + ZYNQMP_PM_CAPABILITY_ACCESS, pd->node_id, ret); return ret; } - pr_debug("%s() Powered on %s domain\n", __func__, domain->name); + dev_dbg(&domain->dev, "set requirement to 0x%x for PM node id %d\n", + ZYNQMP_PM_CAPABILITY_ACCESS, pd->node_id); + return 0; } @@ -110,8 +113,8 @@ static int zynqmp_gpd_power_off(struct generic_pm_domain *domain) /* If domain is already released there is nothing to be done */ if (!(pd->flags & ZYNQMP_PM_DOMAIN_REQUESTED)) { - pr_debug("%s() %s domain is already released\n", - __func__, domain->name); + dev_dbg(&domain->dev, "PM node id %d is already released\n", + pd->node_id); return 0; } @@ -128,17 +131,16 @@ static int zynqmp_gpd_power_off(struct generic_pm_domain *domain) ret = zynqmp_pm_set_requirement(pd->node_id, capabilities, 0, ZYNQMP_PM_REQUEST_ACK_NO); - /** - * If powering down of any node inside this domain fails, - * report and return the error - */ if (ret) { - pr_err("%s() %s set requirement for node %d failed: %d\n", - __func__, domain->name, pd->node_id, ret); + dev_err(&domain->dev, + "failed to set requirement to 0x%x for PM node id %d: %d\n", + capabilities, pd->node_id, ret); return ret; } - pr_debug("%s() Powered off %s domain\n", __func__, domain->name); + dev_dbg(&domain->dev, "set requirement to 0x%x for PM node id %d\n", + capabilities, pd->node_id); + return 0; } @@ -169,17 +171,17 @@ static int zynqmp_gpd_attach_dev(struct generic_pm_domain *domain, ret = zynqmp_pm_request_node(pd->node_id, 0, 0, ZYNQMP_PM_REQUEST_ACK_BLOCKING); - /* If requesting a node fails print and return the error */ if (ret) { - pr_err("%s() %s request failed for node %d: %d\n", - __func__, domain->name, pd->node_id, ret); + dev_err(&domain->dev, "%s request failed for node %d: %d\n", + domain->name, pd->node_id, ret); return ret; } pd->flags |= ZYNQMP_PM_DOMAIN_REQUESTED; - pr_debug("%s() %s attached to %s domain\n", __func__, - dev_name(dev), domain->name); + dev_dbg(&domain->dev, "%s requested PM node id %d\n", + dev_name(dev), pd->node_id); + return 0; } @@ -201,17 +203,16 @@ static void zynqmp_gpd_detach_dev(struct generic_pm_domain *domain, return; ret = zynqmp_pm_release_node(pd->node_id); - /* If releasing a node fails print the error and return */ if (ret) { - pr_err("%s() %s release failed for node %d: %d\n", - __func__, domain->name, pd->node_id, ret); + dev_err(&domain->dev, "failed to release PM node id %d: %d\n", + pd->node_id, ret); return; } pd->flags &= ~ZYNQMP_PM_DOMAIN_REQUESTED; - pr_debug("%s() %s detached from %s domain\n", __func__, - dev_name(dev), domain->name); + dev_dbg(&domain->dev, "%s released PM node id %d\n", + dev_name(dev), pd->node_id); } static struct generic_pm_domain *zynqmp_gpd_xlate From e7a9106c32c0527735a01ae00d5d9edbdb573b40 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Wed, 25 Aug 2021 17:03:12 +0200 Subject: [PATCH 40/94] soc: xilinx: use a properly named field instead of flags Instead of defining a flags field and a single bit in this field to signal that a PM node has been requested, use a boolean field with a descriptive name. No functional change, but using a proper name instead of flags makes the code easier to read. Signed-off-by: Michael Tretter Acked-by: Michal Simek Acked-by: Rajan Vaja Link: https://lore.kernel.org/r/20210825150313.4033156-4-m.tretter@pengutronix.de Signed-off-by: Michal Simek --- drivers/soc/xilinx/zynqmp_pm_domains.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/soc/xilinx/zynqmp_pm_domains.c b/drivers/soc/xilinx/zynqmp_pm_domains.c index 27c46c68e5cc..53b0041f9fe0 100644 --- a/drivers/soc/xilinx/zynqmp_pm_domains.c +++ b/drivers/soc/xilinx/zynqmp_pm_domains.c @@ -20,8 +20,6 @@ #include #define ZYNQMP_NUM_DOMAINS (100) -/* Flag stating if PM nodes mapped to the PM domain has been requested */ -#define ZYNQMP_PM_DOMAIN_REQUESTED BIT(0) static int min_capability; @@ -29,12 +27,12 @@ static int min_capability; * struct zynqmp_pm_domain - Wrapper around struct generic_pm_domain * @gpd: Generic power domain * @node_id: PM node ID corresponding to device inside PM domain - * @flags: ZynqMP PM domain flags + * @requested: The PM node mapped to the PM domain has been requested */ struct zynqmp_pm_domain { struct generic_pm_domain gpd; u32 node_id; - u8 flags; + bool requested; }; /** @@ -112,7 +110,7 @@ static int zynqmp_gpd_power_off(struct generic_pm_domain *domain) pd = container_of(domain, struct zynqmp_pm_domain, gpd); /* If domain is already released there is nothing to be done */ - if (!(pd->flags & ZYNQMP_PM_DOMAIN_REQUESTED)) { + if (!pd->requested) { dev_dbg(&domain->dev, "PM node id %d is already released\n", pd->node_id); return 0; @@ -177,7 +175,7 @@ static int zynqmp_gpd_attach_dev(struct generic_pm_domain *domain, return ret; } - pd->flags |= ZYNQMP_PM_DOMAIN_REQUESTED; + pd->requested = true; dev_dbg(&domain->dev, "%s requested PM node id %d\n", dev_name(dev), pd->node_id); @@ -209,7 +207,7 @@ static void zynqmp_gpd_detach_dev(struct generic_pm_domain *domain, return; } - pd->flags &= ~ZYNQMP_PM_DOMAIN_REQUESTED; + pd->requested = false; dev_dbg(&domain->dev, "%s released PM node id %d\n", dev_name(dev), pd->node_id); From 628e8ba1d331397d580e65a61c0fb4b5ae8e5ccb Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Wed, 25 Aug 2021 17:03:13 +0200 Subject: [PATCH 41/94] soc: xilinx: add a to_zynqmp_pm_domain macro Replace container_of for converting a generic_pm_domain to a zynqmp_pm_domain with a macro definition to simplify the code. Signed-off-by: Michael Tretter Acked-by: Michal Simek Acked-by: Rajan Vaja Link: https://lore.kernel.org/r/20210825150313.4033156-5-m.tretter@pengutronix.de Signed-off-by: Michal Simek --- drivers/soc/xilinx/zynqmp_pm_domains.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/soc/xilinx/zynqmp_pm_domains.c b/drivers/soc/xilinx/zynqmp_pm_domains.c index 53b0041f9fe0..fcce2433bd6d 100644 --- a/drivers/soc/xilinx/zynqmp_pm_domains.c +++ b/drivers/soc/xilinx/zynqmp_pm_domains.c @@ -35,6 +35,9 @@ struct zynqmp_pm_domain { bool requested; }; +#define to_zynqmp_pm_domain(pm_domain) \ + container_of(pm_domain, struct zynqmp_pm_domain, gpd) + /** * zynqmp_gpd_is_active_wakeup_path() - Check if device is in wakeup source * path @@ -69,10 +72,9 @@ static int zynqmp_gpd_is_active_wakeup_path(struct device *dev, void *not_used) */ static int zynqmp_gpd_power_on(struct generic_pm_domain *domain) { + struct zynqmp_pm_domain *pd = to_zynqmp_pm_domain(domain); int ret; - struct zynqmp_pm_domain *pd; - pd = container_of(domain, struct zynqmp_pm_domain, gpd); ret = zynqmp_pm_set_requirement(pd->node_id, ZYNQMP_PM_CAPABILITY_ACCESS, ZYNQMP_PM_MAX_QOS, @@ -101,14 +103,12 @@ static int zynqmp_gpd_power_on(struct generic_pm_domain *domain) */ static int zynqmp_gpd_power_off(struct generic_pm_domain *domain) { + struct zynqmp_pm_domain *pd = to_zynqmp_pm_domain(domain); int ret; struct pm_domain_data *pdd, *tmp; - struct zynqmp_pm_domain *pd; u32 capabilities = min_capability; bool may_wakeup; - pd = container_of(domain, struct zynqmp_pm_domain, gpd); - /* If domain is already released there is nothing to be done */ if (!pd->requested) { dev_dbg(&domain->dev, "PM node id %d is already released\n", @@ -152,11 +152,9 @@ static int zynqmp_gpd_power_off(struct generic_pm_domain *domain) static int zynqmp_gpd_attach_dev(struct generic_pm_domain *domain, struct device *dev) { + struct zynqmp_pm_domain *pd = to_zynqmp_pm_domain(domain); struct device_link *link; int ret; - struct zynqmp_pm_domain *pd; - - pd = container_of(domain, struct zynqmp_pm_domain, gpd); link = device_link_add(dev, &domain->dev, DL_FLAG_SYNC_STATE_ONLY); if (!link) @@ -191,10 +189,8 @@ static int zynqmp_gpd_attach_dev(struct generic_pm_domain *domain, static void zynqmp_gpd_detach_dev(struct generic_pm_domain *domain, struct device *dev) { + struct zynqmp_pm_domain *pd = to_zynqmp_pm_domain(domain); int ret; - struct zynqmp_pm_domain *pd; - - pd = container_of(domain, struct zynqmp_pm_domain, gpd); /* If this is not the last device to detach there is nothing to do */ if (domain->device_count) @@ -220,7 +216,7 @@ static struct generic_pm_domain *zynqmp_gpd_xlate unsigned int i, idx = genpdspec->args[0]; struct zynqmp_pm_domain *pd; - pd = container_of(genpd_data->domains[0], struct zynqmp_pm_domain, gpd); + pd = to_zynqmp_pm_domain(genpd_data->domains[0]); if (genpdspec->args_count != 1) return ERR_PTR(-EINVAL); From 168e05c131cd15932f95364b103b8cb7dbf19546 Mon Sep 17 00:00:00 2001 From: Rajan Vaja Date: Wed, 6 Oct 2021 01:43:55 -0700 Subject: [PATCH 42/94] firmware: xilinx: check return value of zynqmp_pm_get_api_version() Currently return value of zynqmp_pm_get_api_version() is ignored. Because of that, API version is checked in case of error also. So add check for return value of zynqmp_pm_get_api_version(). Signed-off-by: Rajan Vaja Reviewed-by: Michal Simek Link: https://lore.kernel.org/r/1633509835-31949-1-git-send-email-rajan.vaja@xilinx.com Signed-off-by: Michal Simek --- drivers/firmware/xilinx/zynqmp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index 3dd45a7420dc..0dd117860b63 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -1434,7 +1434,10 @@ static int zynqmp_firmware_probe(struct platform_device *pdev) return ret; /* Check PM API version number */ - zynqmp_pm_get_api_version(&pm_api_version); + ret = zynqmp_pm_get_api_version(&pm_api_version); + if (ret) + return ret; + if (pm_api_version < ZYNQMP_PM_VERSION) { panic("%s Platform Management API version error. Expected: v%d.%d - Found: v%d.%d\n", __func__, From bb29e4091079dd7ef5bd488bd7625965cdfd2c12 Mon Sep 17 00:00:00 2001 From: Wan Jiabing Date: Thu, 14 Oct 2021 04:45:55 -0400 Subject: [PATCH 43/94] ARM: at91: pm: Add of_node_put() before goto Fix following coccicheck warning: ./arch/arm/mach-at91/pm.c:643:1-33: WARNING: Function for_each_matching_node_and_match should have of_node_put() before goto Early exits from for_each_matching_node_and_match should decrement the node reference counter. Signed-off-by: Wan Jiabing Signed-off-by: Nicolas Ferre Link: https://lore.kernel.org/r/20211014084555.21422-1-wanjiabing@vivo.com --- arch/arm/mach-at91/pm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c index 8711d6824c1f..dd6f4ce3f766 100644 --- a/arch/arm/mach-at91/pm.c +++ b/arch/arm/mach-at91/pm.c @@ -645,6 +645,7 @@ static __init int at91_dt_ramc(bool phy_mandatory) if (!soc_pm.data.ramc[idx]) { pr_err("unable to map ramc[%d] cpu registers\n", idx); ret = -ENOMEM; + of_node_put(np); goto unmap_ramc; } @@ -670,6 +671,7 @@ static __init int at91_dt_ramc(bool phy_mandatory) if (!soc_pm.data.ramc_phy) { pr_err("unable to map ramc phy cpu registers\n"); ret = -ENOMEM; + of_node_put(np); goto unmap_ramc; } } From cc1fe1e54ba565aaec0006796db65b7c3b1ccd69 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 9 Dec 2021 13:45:00 +0900 Subject: [PATCH 44/94] soc: apple: apple-pmgr-pwrstate: Add auto-PM min level support This is seemingly required for DCP/DCPEXT, without which they refuse to boot properly. They need to be set to minimum state 4 (clock gated). Reviewed-by: Sven Peter Signed-off-by: Hector Martin --- drivers/soc/apple/apple-pmgr-pwrstate.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/soc/apple/apple-pmgr-pwrstate.c b/drivers/soc/apple/apple-pmgr-pwrstate.c index 8ad9d5fdefbf..e1122288409a 100644 --- a/drivers/soc/apple/apple-pmgr-pwrstate.c +++ b/drivers/soc/apple/apple-pmgr-pwrstate.c @@ -20,6 +20,7 @@ #define APPLE_PMGR_RESET BIT(31) #define APPLE_PMGR_AUTO_ENABLE BIT(28) #define APPLE_PMGR_PS_AUTO GENMASK(27, 24) +#define APPLE_PMGR_PS_MIN GENMASK(19, 16) #define APPLE_PMGR_PARENT_OFF BIT(11) #define APPLE_PMGR_DEV_DISABLE BIT(10) #define APPLE_PMGR_WAS_CLKGATED BIT(9) @@ -42,6 +43,7 @@ struct apple_pmgr_ps { struct reset_controller_dev rcdev; struct regmap *regmap; u32 offset; + u32 min_state; }; #define genpd_to_apple_pmgr_ps(_genpd) container_of(_genpd, struct apple_pmgr_ps, genpd) @@ -224,6 +226,11 @@ static int apple_pmgr_ps_probe(struct platform_device *pdev) ps->genpd.power_on = apple_pmgr_ps_power_on; ps->genpd.power_off = apple_pmgr_ps_power_off; + ret = of_property_read_u32(node, "apple,min-state", &ps->min_state); + if (ret == 0 && ps->min_state <= APPLE_PMGR_PS_ACTIVE) + regmap_update_bits(regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_PS_MIN, + FIELD_PREP(APPLE_PMGR_PS_MIN, ps->min_state)); + active = apple_pmgr_ps_is_active(ps); if (of_property_read_bool(node, "apple,always-on")) { ps->genpd.flags |= GENPD_FLAG_ALWAYS_ON; From e522ae91b8ff7bf89d22d9322308aba1a6326996 Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Sat, 4 Dec 2021 21:57:53 +0200 Subject: [PATCH 45/94] dt-bindings: soc: samsung: Add Exynos USI bindings Add constants for choosing USIv2 configuration mode in device tree. Those are further used in USI driver to figure out which value to write into SW_CONF register. Also document USIv2 IP-core bindings. Signed-off-by: Sam Protsenko Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211204195757.8600-2-semen.protsenko@linaro.org Signed-off-by: Krzysztof Kozlowski --- .../bindings/soc/samsung/exynos-usi.yaml | 159 ++++++++++++++++++ include/dt-bindings/soc/samsung,exynos-usi.h | 17 ++ 2 files changed, 176 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/samsung/exynos-usi.yaml create mode 100644 include/dt-bindings/soc/samsung,exynos-usi.h diff --git a/Documentation/devicetree/bindings/soc/samsung/exynos-usi.yaml b/Documentation/devicetree/bindings/soc/samsung/exynos-usi.yaml new file mode 100644 index 000000000000..0af4821fae5e --- /dev/null +++ b/Documentation/devicetree/bindings/soc/samsung/exynos-usi.yaml @@ -0,0 +1,159 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/samsung/exynos-usi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Samsung's Exynos USI (Universal Serial Interface) binding + +maintainers: + - Sam Protsenko + - Krzysztof Kozlowski + +description: | + USI IP-core provides selectable serial protocol (UART, SPI or High-Speed I2C). + USI shares almost all internal circuits within each protocol, so only one + protocol can be chosen at a time. USI is modeled as a node with zero or more + child nodes, each representing a serial sub-node device. The mode setting + selects which particular function will be used. + + Refer to next bindings documentation for information on protocol subnodes that + can exist under USI node: + + [1] Documentation/devicetree/bindings/serial/samsung_uart.yaml + [2] Documentation/devicetree/bindings/i2c/i2c-exynos5.txt + [3] Documentation/devicetree/bindings/spi/spi-samsung.txt + +properties: + $nodename: + pattern: "^usi@[0-9a-f]+$" + + compatible: + enum: + - samsung,exynos850-usi # for USIv2 (Exynos850, ExynosAutoV9) + + reg: true + + clocks: true + + clock-names: true + + ranges: true + + "#address-cells": + const: 1 + + "#size-cells": + const: 1 + + samsung,sysreg: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: + Should be phandle/offset pair. The phandle to System Register syscon node + (for the same domain where this USI controller resides) and the offset + of SW_CONF register for this USI controller. + + samsung,mode: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Selects USI function (which serial protocol to use). Refer to + for valid USI mode values. + + samsung,clkreq-on: + type: boolean + description: + Enable this property if underlying protocol requires the clock to be + continuously provided without automatic gating. As suggested by SoC + manual, it should be set in case of SPI/I2C slave, UART Rx and I2C + multi-master mode. Usually this property is needed if USI mode is set + to "UART". + + This property is optional. + +patternProperties: + # All other properties should be child nodes + "^(serial|spi|i2c)@[0-9a-f]+$": + type: object + description: Child node describing underlying USI serial protocol + +required: + - compatible + - ranges + - "#address-cells" + - "#size-cells" + - samsung,sysreg + - samsung,mode + +if: + properties: + compatible: + contains: + enum: + - samsung,exynos850-usi + +then: + properties: + reg: + maxItems: 1 + + clocks: + items: + - description: Bus (APB) clock + - description: Operating clock for UART/SPI/I2C protocol + + clock-names: + items: + - const: pclk + - const: ipclk + + required: + - reg + - clocks + - clock-names + +else: + properties: + reg: false + clocks: false + clock-names: false + samsung,clkreq-on: false + +additionalProperties: false + +examples: + - | + #include + #include + + usi0: usi@138200c0 { + compatible = "samsung,exynos850-usi"; + reg = <0x138200c0 0x20>; + samsung,sysreg = <&sysreg_peri 0x1010>; + samsung,mode = ; + samsung,clkreq-on; /* needed for UART mode */ + #address-cells = <1>; + #size-cells = <1>; + ranges; + clocks = <&cmu_peri 32>, <&cmu_peri 31>; + clock-names = "pclk", "ipclk"; + + serial_0: serial@13820000 { + compatible = "samsung,exynos850-uart"; + reg = <0x13820000 0xc0>; + interrupts = ; + clocks = <&cmu_peri 32>, <&cmu_peri 31>; + clock-names = "uart", "clk_uart_baud0"; + status = "disabled"; + }; + + hsi2c_0: i2c@13820000 { + compatible = "samsung,exynosautov9-hsi2c"; + reg = <0x13820000 0xc0>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&cmu_peri 32>, <&cmu_peri 31>; + clock-names = "hsi2c_pclk", "hsi2c"; + status = "disabled"; + }; + }; diff --git a/include/dt-bindings/soc/samsung,exynos-usi.h b/include/dt-bindings/soc/samsung,exynos-usi.h new file mode 100644 index 000000000000..a01af169d249 --- /dev/null +++ b/include/dt-bindings/soc/samsung,exynos-usi.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) 2021 Linaro Ltd. + * Author: Sam Protsenko + * + * Device Tree bindings for Samsung Exynos USI (Universal Serial Interface). + */ + +#ifndef __DT_BINDINGS_SAMSUNG_EXYNOS_USI_H +#define __DT_BINDINGS_SAMSUNG_EXYNOS_USI_H + +#define USI_V2_NONE 0 +#define USI_V2_UART 1 +#define USI_V2_SPI 2 +#define USI_V2_I2C 3 + +#endif /* __DT_BINDINGS_SAMSUNG_EXYNOS_USI_H */ From a34ff76a161583d24d29816a6cce85232ea7d9d0 Mon Sep 17 00:00:00 2001 From: Aswath Govindraju Date: Fri, 3 Dec 2021 17:39:13 +0530 Subject: [PATCH 46/94] soc: ti: k3-socinfo: Add entry for J721S2 SoC family J721S2 SoC's JTAG PARTNO is 0xBB75. Signed-off-by: Aswath Govindraju Signed-off-by: Nishanth Menon Reviewed-by: Kishon Vijay Abraham I Link: https://lore.kernel.org/r/20211203120913.14737-1-a-govindraju@ti.com --- drivers/soc/ti/k3-socinfo.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/soc/ti/k3-socinfo.c b/drivers/soc/ti/k3-socinfo.c index fd91129de6e5..b6b2150aca4e 100644 --- a/drivers/soc/ti/k3-socinfo.c +++ b/drivers/soc/ti/k3-socinfo.c @@ -40,7 +40,8 @@ static const struct k3_soc_id { { 0xBB5A, "AM65X" }, { 0xBB64, "J721E" }, { 0xBB6D, "J7200" }, - { 0xBB38, "AM64X" } + { 0xBB38, "AM64X" }, + { 0xBB75, "J721S2"}, }; static int From 7b983da38417fa8c36514d6a98997b3cad11b6a9 Mon Sep 17 00:00:00 2001 From: Ivan Bornyakov Date: Thu, 2 Dec 2021 08:57:23 +0300 Subject: [PATCH 47/94] bus: imx-weim: optionally enable continuous burst clock To enable continuous burst clock, add "fsl,continuous-burst-clk" along with "fsl,burst-clk-enable" property to the weim bus's devicetree node. Example: weim: weim@21b8000 { compatible = "fsl,imx6ul-weim", "fsl,imx6q-weim"; reg = <0x021b8000 0x4000>; clocks = <&clks 143>; #address-cells = <2>; #size-cells = <1>; ranges = <0 0 0x50000000 0x08000000>; fsl,weim-cs-gpr = <&gpr>; fsl,burst-clk-enable; fsl,continuous-burst-clk; client-device@0 { ... }; }; Signed-off-by: Ivan Bornyakov Reviewed-by: Fabio Estevam Signed-off-by: Shawn Guo --- drivers/bus/imx-weim.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c index 28bb65a5613f..bccb275b65ba 100644 --- a/drivers/bus/imx-weim.c +++ b/drivers/bus/imx-weim.c @@ -21,6 +21,7 @@ struct imx_weim_devtype { unsigned int cs_stride; unsigned int wcr_offset; unsigned int wcr_bcm; + unsigned int wcr_cont_bclk; }; static const struct imx_weim_devtype imx1_weim_devtype = { @@ -41,6 +42,7 @@ static const struct imx_weim_devtype imx50_weim_devtype = { .cs_stride = 0x18, .wcr_offset = 0x90, .wcr_bcm = BIT(0), + .wcr_cont_bclk = BIT(3), }; static const struct imx_weim_devtype imx51_weim_devtype = { @@ -206,8 +208,20 @@ static int weim_parse_dt(struct platform_device *pdev, void __iomem *base) if (of_property_read_bool(pdev->dev.of_node, "fsl,burst-clk-enable")) { if (devtype->wcr_bcm) { reg = readl(base + devtype->wcr_offset); - writel(reg | devtype->wcr_bcm, - base + devtype->wcr_offset); + reg |= devtype->wcr_bcm; + + if (of_property_read_bool(pdev->dev.of_node, + "fsl,continuous-burst-clk")) { + if (devtype->wcr_cont_bclk) { + reg |= devtype->wcr_cont_bclk; + } else { + dev_err(&pdev->dev, + "continuous burst clk not supported.\n"); + return -EINVAL; + } + } + + writel(reg, base + devtype->wcr_offset); } else { dev_err(&pdev->dev, "burst clk mode not supported.\n"); return -EINVAL; From 8e136c5ea43ae08c5d672deb1c3f494782a4392a Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Wed, 15 Dec 2021 20:27:54 +0900 Subject: [PATCH 48/94] soc: apple: apple-pmgr-pwrstate: Do not build as a module This doesn't make any sense as a module since it is a critical device, and it turns out of_phandle_iterator_args was not exported so the module version doesn't build anyway. Fixes: 6df9d38f9146 ("soc: apple: Add driver for Apple PMGR power state controls") Reported-by: kernel test robot Reviewed-by: Sven Peter Signed-off-by: Hector Martin --- drivers/soc/apple/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/apple/Kconfig b/drivers/soc/apple/Kconfig index 3eb14abf8d33..9b8de31d6a8f 100644 --- a/drivers/soc/apple/Kconfig +++ b/drivers/soc/apple/Kconfig @@ -5,7 +5,7 @@ if ARCH_APPLE || COMPILE_TEST menu "Apple SoC drivers" config APPLE_PMGR_PWRSTATE - tristate "Apple SoC PMGR power state control" + bool "Apple SoC PMGR power state control" depends on PM select REGMAP select MFD_SYSCON From 1bb0b8b195d821d009bae61248da14f2b17bd44a Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Tue, 14 Dec 2021 01:55:44 +0000 Subject: [PATCH 49/94] soc: ti: knav_dma: Fix NULL vs IS_ERR() checking in dma_init Since devm_ioremap_resource() function return error pointers. The pktdma_get_regs() function does not return NULL, It return error pointers too. Using IS_ERR() to check the return value to fix this. Signed-off-by: Miaoqian Lin Signed-off-by: Nishanth Menon Link: https://lore.kernel.org/r/20211214015544.7270-1-linmq006@gmail.com --- drivers/soc/ti/knav_dma.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/soc/ti/knav_dma.c b/drivers/soc/ti/knav_dma.c index 591d14ebcb11..700d8eecd8c4 100644 --- a/drivers/soc/ti/knav_dma.c +++ b/drivers/soc/ti/knav_dma.c @@ -646,31 +646,31 @@ static int dma_init(struct device_node *cloud, struct device_node *dma_node) } dma->reg_global = pktdma_get_regs(dma, node, 0, &size); - if (!dma->reg_global) - return -ENODEV; + if (IS_ERR(dma->reg_global)) + return PTR_ERR(dma->reg_global); if (size < sizeof(struct reg_global)) { dev_err(kdev->dev, "bad size %pa for global regs\n", &size); return -ENODEV; } dma->reg_tx_chan = pktdma_get_regs(dma, node, 1, &size); - if (!dma->reg_tx_chan) - return -ENODEV; + if (IS_ERR(dma->reg_tx_chan)) + return PTR_ERR(dma->reg_tx_chan); max_tx_chan = size / sizeof(struct reg_chan); dma->reg_rx_chan = pktdma_get_regs(dma, node, 2, &size); - if (!dma->reg_rx_chan) - return -ENODEV; + if (IS_ERR(dma->reg_rx_chan)) + return PTR_ERR(dma->reg_rx_chan); max_rx_chan = size / sizeof(struct reg_chan); dma->reg_tx_sched = pktdma_get_regs(dma, node, 3, &size); - if (!dma->reg_tx_sched) - return -ENODEV; + if (IS_ERR(dma->reg_tx_sched)) + return PTR_ERR(dma->reg_tx_sched); max_tx_sched = size / sizeof(struct reg_tx_sched); dma->reg_rx_flow = pktdma_get_regs(dma, node, 4, &size); - if (!dma->reg_rx_flow) - return -ENODEV; + if (IS_ERR(dma->reg_rx_flow)) + return PTR_ERR(dma->reg_rx_flow); max_rx_flow = size / sizeof(struct reg_rx_flow); dma->rx_priority = DMA_PRIO_DEFAULT; From 66209e6fbd564f1bb3c69248423e4b761904a943 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 1 Dec 2021 02:23:10 +0300 Subject: [PATCH 50/94] soc/tegra: Don't print error message when OPPs not available Previously we assumed that devm_tegra_core_dev_init_opp_table() will be used only by drivers that will always have device with OPP table, but this is not true anymore. For example now Tegra30 will have OPP table for PWM, but Tegra20 not and both use the same driver. Hence let's not print the error message about missing OPP table in the common helper, we can print it elsewhere. Reviewed-by: Ulf Hansson Signed-off-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/soc/tegra/common.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/soc/tegra/common.c b/drivers/soc/tegra/common.c index 35c882da55fc..32c346b72635 100644 --- a/drivers/soc/tegra/common.c +++ b/drivers/soc/tegra/common.c @@ -136,9 +136,7 @@ int devm_tegra_core_dev_init_opp_table(struct device *dev, */ err = devm_pm_opp_of_add_table(dev); if (err) { - if (err == -ENODEV) - dev_err_once(dev, "OPP table not found, please update device-tree\n"); - else + if (err != -ENODEV) dev_err(dev, "failed to add OPP table: %d\n", err); return err; From 765d95f8ac54af611c804bb956b79391cfc1d27b Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 30 Nov 2021 11:44:06 +0000 Subject: [PATCH 51/94] soc/tegra: pmc: Add reboot notifier The Tegra PMC driver implements a restart handler that supports Tegra specific reboot commands such as placing the device into 'recovery' mode in order to reprogram the platform. This is accomplished by setting the appropriate bit in the PMC scratch0 register prior to rebooting the platform. For Tegra platforms that support PSCI or EFI, the default Tegra restart handler is not called and the PSCI or EFI restart handler is called instead. Hence, for Tegra platforms that support PSCI or EFI, the Tegra specific reboot commands do not currently work. Fix this by moving the code that programs the PMC scratch0 register into a separate reboot notifier that will always be called on reboot. Signed-off-by: Jon Hunter Reviewed-by: Dmitry Osipenko Tested-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 575d6d5b4294..6db0e31ac6fa 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -1064,10 +1064,8 @@ int tegra_pmc_cpu_remove_clamping(unsigned int cpuid) return tegra_powergate_remove_clamping(id); } -static int tegra_pmc_restart_notify(struct notifier_block *this, - unsigned long action, void *data) +static void tegra_pmc_program_reboot_reason(const char *cmd) { - const char *cmd = data; u32 value; value = tegra_pmc_scratch_readl(pmc, pmc->soc->regs->scratch0); @@ -1085,6 +1083,25 @@ static int tegra_pmc_restart_notify(struct notifier_block *this, } tegra_pmc_scratch_writel(pmc, value, pmc->soc->regs->scratch0); +} + +static int tegra_pmc_reboot_notify(struct notifier_block *this, + unsigned long action, void *data) +{ + if (action == SYS_RESTART) + tegra_pmc_program_reboot_reason(data); + + return NOTIFY_DONE; +} + +static struct notifier_block tegra_pmc_reboot_notifier = { + .notifier_call = tegra_pmc_reboot_notify, +}; + +static int tegra_pmc_restart_notify(struct notifier_block *this, + unsigned long action, void *data) +{ + u32 value; /* reset everything but PMC_SCRATCH0 and PMC_RST_STATUS */ value = tegra_pmc_readl(pmc, PMC_CNTRL); @@ -2890,6 +2907,14 @@ static int tegra_pmc_probe(struct platform_device *pdev) goto cleanup_sysfs; } + err = devm_register_reboot_notifier(&pdev->dev, + &tegra_pmc_reboot_notifier); + if (err) { + dev_err(&pdev->dev, "unable to register reboot notifier, %d\n", + err); + goto cleanup_debugfs; + } + err = register_restart_handler(&tegra_pmc_restart_handler); if (err) { dev_err(&pdev->dev, "unable to register restart handler, %d\n", From aeecc50ace0480e6df565093e578b129902918db Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 1 Dec 2021 02:23:35 +0300 Subject: [PATCH 52/94] soc/tegra: fuse: Reset hardware The FUSE controller is enabled at a boot time. Reset it in order to put hardware and clock into clean and disabled state. Reviewed-by: Ulf Hansson Signed-off-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/soc/tegra/fuse/fuse-tegra.c | 25 +++++++++++++++++++++++++ drivers/soc/tegra/fuse/fuse.h | 1 + 2 files changed, 26 insertions(+) diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c index f2151815db58..cc032729a143 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra.c +++ b/drivers/soc/tegra/fuse/fuse-tegra.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -243,6 +244,30 @@ static int tegra_fuse_probe(struct platform_device *pdev) goto restore; } + fuse->rst = devm_reset_control_get_optional(&pdev->dev, "fuse"); + if (IS_ERR(fuse->rst)) { + err = PTR_ERR(fuse->rst); + dev_err(&pdev->dev, "failed to get FUSE reset: %pe\n", + fuse->rst); + goto restore; + } + + /* + * FUSE clock is enabled at a boot time, hence this resume/suspend + * disables the clock besides the h/w resetting. + */ + err = pm_runtime_resume_and_get(&pdev->dev); + if (err) + goto restore; + + err = reset_control_reset(fuse->rst); + pm_runtime_put(&pdev->dev); + + if (err < 0) { + dev_err(&pdev->dev, "failed to reset FUSE: %d\n", err); + goto restore; + } + /* release the early I/O memory mapping */ iounmap(base); diff --git a/drivers/soc/tegra/fuse/fuse.h b/drivers/soc/tegra/fuse/fuse.h index de58feba0435..1b719d85bd04 100644 --- a/drivers/soc/tegra/fuse/fuse.h +++ b/drivers/soc/tegra/fuse/fuse.h @@ -43,6 +43,7 @@ struct tegra_fuse { void __iomem *base; phys_addr_t phys; struct clk *clk; + struct reset_control *rst; u32 (*read_early)(struct tegra_fuse *fuse, unsigned int offset); u32 (*read)(struct tegra_fuse *fuse, unsigned int offset); From 88724b78a84c85350c21ac8d4432f2cf5a77a10a Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 1 Dec 2021 02:23:36 +0300 Subject: [PATCH 53/94] soc/tegra: fuse: Use resource-managed helpers Use resource-managed helpers to make code cleaner and more correct, properly releasing all resources in case of driver probe error. Signed-off-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/soc/tegra/fuse/fuse-tegra.c | 32 ++++++++++++++------------ drivers/soc/tegra/fuse/fuse-tegra20.c | 33 ++++++++++++++++++++++++--- 2 files changed, 48 insertions(+), 17 deletions(-) diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c index cc032729a143..fe4f935ce73a 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra.c +++ b/drivers/soc/tegra/fuse/fuse-tegra.c @@ -182,6 +182,12 @@ static const struct nvmem_cell_info tegra_fuse_cells[] = { }, }; +static void tegra_fuse_restore(void *base) +{ + fuse->clk = NULL; + fuse->base = base; +} + static int tegra_fuse_probe(struct platform_device *pdev) { void __iomem *base = fuse->base; @@ -189,13 +195,16 @@ static int tegra_fuse_probe(struct platform_device *pdev) struct resource *res; int err; + err = devm_add_action(&pdev->dev, tegra_fuse_restore, base); + if (err) + return err; + /* take over the memory region from the early initialization */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); fuse->phys = res->start; fuse->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(fuse->base)) { err = PTR_ERR(fuse->base); - fuse->base = base; return err; } @@ -205,19 +214,20 @@ static int tegra_fuse_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to get FUSE clock: %ld", PTR_ERR(fuse->clk)); - fuse->base = base; return PTR_ERR(fuse->clk); } platform_set_drvdata(pdev, fuse); fuse->dev = &pdev->dev; - pm_runtime_enable(&pdev->dev); + err = devm_pm_runtime_enable(&pdev->dev); + if (err) + return err; if (fuse->soc->probe) { err = fuse->soc->probe(fuse); if (err < 0) - goto restore; + return err; } memset(&nvmem, 0, sizeof(nvmem)); @@ -241,7 +251,7 @@ static int tegra_fuse_probe(struct platform_device *pdev) err = PTR_ERR(fuse->nvmem); dev_err(&pdev->dev, "failed to register NVMEM device: %d\n", err); - goto restore; + return err; } fuse->rst = devm_reset_control_get_optional(&pdev->dev, "fuse"); @@ -249,7 +259,7 @@ static int tegra_fuse_probe(struct platform_device *pdev) err = PTR_ERR(fuse->rst); dev_err(&pdev->dev, "failed to get FUSE reset: %pe\n", fuse->rst); - goto restore; + return err; } /* @@ -258,26 +268,20 @@ static int tegra_fuse_probe(struct platform_device *pdev) */ err = pm_runtime_resume_and_get(&pdev->dev); if (err) - goto restore; + return err; err = reset_control_reset(fuse->rst); pm_runtime_put(&pdev->dev); if (err < 0) { dev_err(&pdev->dev, "failed to reset FUSE: %d\n", err); - goto restore; + return err; } /* release the early I/O memory mapping */ iounmap(base); return 0; - -restore: - fuse->clk = NULL; - fuse->base = base; - pm_runtime_disable(&pdev->dev); - return err; } static int __maybe_unused tegra_fuse_runtime_resume(struct device *dev) diff --git a/drivers/soc/tegra/fuse/fuse-tegra20.c b/drivers/soc/tegra/fuse/fuse-tegra20.c index 8ec9fc5e5e4b..12503f563e36 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra20.c +++ b/drivers/soc/tegra/fuse/fuse-tegra20.c @@ -94,9 +94,28 @@ static bool dma_filter(struct dma_chan *chan, void *filter_param) return of_device_is_compatible(np, "nvidia,tegra20-apbdma"); } +static void tegra20_fuse_release_channel(void *data) +{ + struct tegra_fuse *fuse = data; + + dma_release_channel(fuse->apbdma.chan); + fuse->apbdma.chan = NULL; +} + +static void tegra20_fuse_free_coherent(void *data) +{ + struct tegra_fuse *fuse = data; + + dma_free_coherent(fuse->dev, sizeof(u32), fuse->apbdma.virt, + fuse->apbdma.phys); + fuse->apbdma.virt = NULL; + fuse->apbdma.phys = 0x0; +} + static int tegra20_fuse_probe(struct tegra_fuse *fuse) { dma_cap_mask_t mask; + int err; dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); @@ -105,13 +124,21 @@ static int tegra20_fuse_probe(struct tegra_fuse *fuse) if (!fuse->apbdma.chan) return -EPROBE_DEFER; + err = devm_add_action_or_reset(fuse->dev, tegra20_fuse_release_channel, + fuse); + if (err) + return err; + fuse->apbdma.virt = dma_alloc_coherent(fuse->dev, sizeof(u32), &fuse->apbdma.phys, GFP_KERNEL); - if (!fuse->apbdma.virt) { - dma_release_channel(fuse->apbdma.chan); + if (!fuse->apbdma.virt) return -ENOMEM; - } + + err = devm_add_action_or_reset(fuse->dev, tegra20_fuse_free_coherent, + fuse); + if (err) + return err; fuse->apbdma.config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; fuse->apbdma.config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; From 80ef351c98714bb95bafd30b3faa6f11d8d58eb7 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 1 Dec 2021 02:23:37 +0300 Subject: [PATCH 54/94] soc/tegra: regulators: Prepare for suspend Depending on hardware version, Tegra SoC may require a higher voltages during resume from system suspend, otherwise hardware will crash. Set SoC voltages to a nominal levels during suspend. Link: https://lore.kernel.org/all/a8280b5b-7347-8995-c97b-10b798cdf057@gmail.com/ Reviewed-by: Ulf Hansson Signed-off-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/soc/tegra/regulators-tegra20.c | 99 ++++++++++++++++++++ drivers/soc/tegra/regulators-tegra30.c | 122 +++++++++++++++++++++++++ 2 files changed, 221 insertions(+) diff --git a/drivers/soc/tegra/regulators-tegra20.c b/drivers/soc/tegra/regulators-tegra20.c index b8ce9fd0650d..6a2f90ab9d3e 100644 --- a/drivers/soc/tegra/regulators-tegra20.c +++ b/drivers/soc/tegra/regulators-tegra20.c @@ -16,7 +16,9 @@ #include #include #include +#include +#include #include struct tegra_regulator_coupler { @@ -25,9 +27,12 @@ struct tegra_regulator_coupler { struct regulator_dev *cpu_rdev; struct regulator_dev *rtc_rdev; struct notifier_block reboot_notifier; + struct notifier_block suspend_notifier; int core_min_uV, cpu_min_uV; bool sys_reboot_mode_req; bool sys_reboot_mode; + bool sys_suspend_mode_req; + bool sys_suspend_mode; }; static inline struct tegra_regulator_coupler * @@ -105,6 +110,28 @@ static int tegra20_core_rtc_max_spread(struct regulator_dev *core_rdev, return 150000; } +static int tegra20_cpu_nominal_uV(void) +{ + switch (tegra_sku_info.soc_speedo_id) { + case 0: + return 1100000; + case 1: + return 1025000; + default: + return 1125000; + } +} + +static int tegra20_core_nominal_uV(void) +{ + switch (tegra_sku_info.soc_speedo_id) { + default: + return 1225000; + case 2: + return 1300000; + } +} + static int tegra20_core_rtc_update(struct tegra_regulator_coupler *tegra, struct regulator_dev *core_rdev, struct regulator_dev *rtc_rdev, @@ -144,6 +171,11 @@ static int tegra20_core_rtc_update(struct tegra_regulator_coupler *tegra, if (err) return err; + /* prepare voltage level for suspend */ + if (tegra->sys_suspend_mode) + core_min_uV = clamp(tegra20_core_nominal_uV(), + core_min_uV, core_max_uV); + core_uV = regulator_get_voltage_rdev(core_rdev); if (core_uV < 0) return core_uV; @@ -279,6 +311,11 @@ static int tegra20_cpu_voltage_update(struct tegra_regulator_coupler *tegra, if (tegra->sys_reboot_mode) cpu_min_uV = max(cpu_min_uV, tegra->cpu_min_uV); + /* prepare voltage level for suspend */ + if (tegra->sys_suspend_mode) + cpu_min_uV = clamp(tegra20_cpu_nominal_uV(), + cpu_min_uV, cpu_max_uV); + if (cpu_min_uV > cpu_uV) { err = tegra20_core_rtc_update(tegra, core_rdev, rtc_rdev, cpu_uV, cpu_min_uV); @@ -320,6 +357,7 @@ static int tegra20_regulator_balance_voltage(struct regulator_coupler *coupler, } tegra->sys_reboot_mode = READ_ONCE(tegra->sys_reboot_mode_req); + tegra->sys_suspend_mode = READ_ONCE(tegra->sys_suspend_mode_req); if (rdev == cpu_rdev) return tegra20_cpu_voltage_update(tegra, cpu_rdev, @@ -334,6 +372,63 @@ static int tegra20_regulator_balance_voltage(struct regulator_coupler *coupler, return -EPERM; } +static int tegra20_regulator_prepare_suspend(struct tegra_regulator_coupler *tegra, + bool sys_suspend_mode) +{ + int err; + + if (!tegra->core_rdev || !tegra->rtc_rdev || !tegra->cpu_rdev) + return 0; + + /* + * All power domains are enabled early during resume from suspend + * by GENPD core. Domains like VENC may require a higher voltage + * when enabled during resume from suspend. This also prepares + * hardware for resuming from LP0. + */ + + WRITE_ONCE(tegra->sys_suspend_mode_req, sys_suspend_mode); + + err = regulator_sync_voltage_rdev(tegra->cpu_rdev); + if (err) + return err; + + err = regulator_sync_voltage_rdev(tegra->core_rdev); + if (err) + return err; + + return 0; +} + +static int tegra20_regulator_suspend(struct notifier_block *notifier, + unsigned long mode, void *arg) +{ + struct tegra_regulator_coupler *tegra; + int ret = 0; + + tegra = container_of(notifier, struct tegra_regulator_coupler, + suspend_notifier); + + switch (mode) { + case PM_HIBERNATION_PREPARE: + case PM_RESTORE_PREPARE: + case PM_SUSPEND_PREPARE: + ret = tegra20_regulator_prepare_suspend(tegra, true); + break; + + case PM_POST_HIBERNATION: + case PM_POST_RESTORE: + case PM_POST_SUSPEND: + ret = tegra20_regulator_prepare_suspend(tegra, false); + break; + } + + if (ret) + pr_err("failed to prepare regulators: %d\n", ret); + + return notifier_from_errno(ret); +} + static int tegra20_regulator_prepare_reboot(struct tegra_regulator_coupler *tegra, bool sys_reboot_mode) { @@ -444,6 +539,7 @@ static struct tegra_regulator_coupler tegra20_coupler = { .balance_voltage = tegra20_regulator_balance_voltage, }, .reboot_notifier.notifier_call = tegra20_regulator_reboot, + .suspend_notifier.notifier_call = tegra20_regulator_suspend, }; static int __init tegra_regulator_coupler_init(void) @@ -456,6 +552,9 @@ static int __init tegra_regulator_coupler_init(void) err = register_reboot_notifier(&tegra20_coupler.reboot_notifier); WARN_ON(err); + err = register_pm_notifier(&tegra20_coupler.suspend_notifier); + WARN_ON(err); + return regulator_coupler_register(&tegra20_coupler.coupler); } arch_initcall(tegra_regulator_coupler_init); diff --git a/drivers/soc/tegra/regulators-tegra30.c b/drivers/soc/tegra/regulators-tegra30.c index e74bbc9c7859..8fd43c689134 100644 --- a/drivers/soc/tegra/regulators-tegra30.c +++ b/drivers/soc/tegra/regulators-tegra30.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -25,9 +26,12 @@ struct tegra_regulator_coupler { struct regulator_dev *core_rdev; struct regulator_dev *cpu_rdev; struct notifier_block reboot_notifier; + struct notifier_block suspend_notifier; int core_min_uV, cpu_min_uV; bool sys_reboot_mode_req; bool sys_reboot_mode; + bool sys_suspend_mode_req; + bool sys_suspend_mode; }; static inline struct tegra_regulator_coupler * @@ -113,6 +117,52 @@ static int tegra30_core_cpu_limit(int cpu_uV) return -EINVAL; } +static int tegra30_cpu_nominal_uV(void) +{ + switch (tegra_sku_info.cpu_speedo_id) { + case 10 ... 11: + return 850000; + + case 9: + return 912000; + + case 1 ... 3: + case 7 ... 8: + return 1050000; + + default: + return 1125000; + + case 4 ... 6: + case 12 ... 13: + return 1237000; + } +} + +static int tegra30_core_nominal_uV(void) +{ + switch (tegra_sku_info.soc_speedo_id) { + case 0: + return 1200000; + + case 1: + if (tegra_sku_info.cpu_speedo_id != 7 && + tegra_sku_info.cpu_speedo_id != 8) + return 1200000; + + fallthrough; + + case 2: + if (tegra_sku_info.cpu_speedo_id != 13) + return 1300000; + + return 1350000; + + default: + return 1250000; + } +} + static int tegra30_voltage_update(struct tegra_regulator_coupler *tegra, struct regulator_dev *cpu_rdev, struct regulator_dev *core_rdev) @@ -168,6 +218,11 @@ static int tegra30_voltage_update(struct tegra_regulator_coupler *tegra, if (err) return err; + /* prepare voltage level for suspend */ + if (tegra->sys_suspend_mode) + core_min_uV = clamp(tegra30_core_nominal_uV(), + core_min_uV, core_max_uV); + core_uV = regulator_get_voltage_rdev(core_rdev); if (core_uV < 0) return core_uV; @@ -223,6 +278,11 @@ static int tegra30_voltage_update(struct tegra_regulator_coupler *tegra, if (tegra->sys_reboot_mode) cpu_min_uV = max(cpu_min_uV, tegra->cpu_min_uV); + /* prepare voltage level for suspend */ + if (tegra->sys_suspend_mode) + cpu_min_uV = clamp(tegra30_cpu_nominal_uV(), + cpu_min_uV, cpu_max_uV); + if (core_min_limited_uV > core_uV) { pr_err("core voltage constraint violated: %d %d %d\n", core_uV, core_min_limited_uV, cpu_uV); @@ -292,10 +352,68 @@ static int tegra30_regulator_balance_voltage(struct regulator_coupler *coupler, } tegra->sys_reboot_mode = READ_ONCE(tegra->sys_reboot_mode_req); + tegra->sys_suspend_mode = READ_ONCE(tegra->sys_suspend_mode_req); return tegra30_voltage_update(tegra, cpu_rdev, core_rdev); } +static int tegra30_regulator_prepare_suspend(struct tegra_regulator_coupler *tegra, + bool sys_suspend_mode) +{ + int err; + + if (!tegra->core_rdev || !tegra->cpu_rdev) + return 0; + + /* + * All power domains are enabled early during resume from suspend + * by GENPD core. Domains like VENC may require a higher voltage + * when enabled during resume from suspend. This also prepares + * hardware for resuming from LP0. + */ + + WRITE_ONCE(tegra->sys_suspend_mode_req, sys_suspend_mode); + + err = regulator_sync_voltage_rdev(tegra->cpu_rdev); + if (err) + return err; + + err = regulator_sync_voltage_rdev(tegra->core_rdev); + if (err) + return err; + + return 0; +} + +static int tegra30_regulator_suspend(struct notifier_block *notifier, + unsigned long mode, void *arg) +{ + struct tegra_regulator_coupler *tegra; + int ret = 0; + + tegra = container_of(notifier, struct tegra_regulator_coupler, + suspend_notifier); + + switch (mode) { + case PM_HIBERNATION_PREPARE: + case PM_RESTORE_PREPARE: + case PM_SUSPEND_PREPARE: + ret = tegra30_regulator_prepare_suspend(tegra, true); + break; + + case PM_POST_HIBERNATION: + case PM_POST_RESTORE: + case PM_POST_SUSPEND: + ret = tegra30_regulator_prepare_suspend(tegra, false); + break; + } + + if (ret) + pr_err("failed to prepare regulators: %d\n", ret); + + return notifier_from_errno(ret); +} + static int tegra30_regulator_prepare_reboot(struct tegra_regulator_coupler *tegra, bool sys_reboot_mode) { @@ -395,6 +513,7 @@ static struct tegra_regulator_coupler tegra30_coupler = { .balance_voltage = tegra30_regulator_balance_voltage, }, .reboot_notifier.notifier_call = tegra30_regulator_reboot, + .suspend_notifier.notifier_call = tegra30_regulator_suspend, }; static int __init tegra_regulator_coupler_init(void) @@ -407,6 +526,9 @@ static int __init tegra_regulator_coupler_init(void) err = register_reboot_notifier(&tegra30_coupler.reboot_notifier); WARN_ON(err); + err = register_pm_notifier(&tegra30_coupler.suspend_notifier); + WARN_ON(err); + return regulator_coupler_register(&tegra30_coupler.coupler); } arch_initcall(tegra_regulator_coupler_init); From 006da96c840ff60a1764fc38ee7adc3dc4191d79 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 1 Dec 2021 02:23:08 +0300 Subject: [PATCH 55/94] soc/tegra: Enable runtime PM during OPP state-syncing GENPD core now can set up domain's performance state properly while device is RPM-suspended. Runtime PM of a device must be enabled during setup because GENPD checks whether device is suspended and check doesn't work while RPM is disabled. Instead of replicating the boilerplate RPM-enable code around OPP helper for each driver, let's make OPP helper to take care of enabling it. Reviewed-by: Ulf Hansson Signed-off-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/soc/tegra/common.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/soc/tegra/common.c b/drivers/soc/tegra/common.c index cd33e99249c3..35c882da55fc 100644 --- a/drivers/soc/tegra/common.c +++ b/drivers/soc/tegra/common.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -43,6 +44,7 @@ static int tegra_core_dev_init_opp_state(struct device *dev) { unsigned long rate; struct clk *clk; + bool rpm_enabled; int err; clk = devm_clk_get(dev, NULL); @@ -57,8 +59,31 @@ static int tegra_core_dev_init_opp_state(struct device *dev) return -EINVAL; } + /* + * Runtime PM of the device must be enabled in order to set up + * GENPD's performance properly because GENPD core checks whether + * device is suspended and this check doesn't work while RPM is + * disabled. This makes sure the OPP vote below gets cached in + * GENPD for the device. Instead, the vote is done the next time + * the device gets runtime resumed. + */ + rpm_enabled = pm_runtime_enabled(dev); + if (!rpm_enabled) + pm_runtime_enable(dev); + + /* should never happen in practice */ + if (!pm_runtime_enabled(dev)) { + dev_WARN(dev, "failed to enable runtime PM\n"); + pm_runtime_disable(dev); + return -EINVAL; + } + /* first dummy rate-setting initializes voltage vote */ err = dev_pm_opp_set_rate(dev, rate); + + if (!rpm_enabled) + pm_runtime_disable(dev); + if (err) { dev_err(dev, "failed to initialize OPP clock: %d\n", err); return err; From 8d1a3411da0c90f506c5e2f69529f4ba2954d3a3 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 1 Dec 2021 02:23:38 +0300 Subject: [PATCH 56/94] soc/tegra: pmc: Rename 3d power domains Device-tree schema doesn't allow domain name to start with a number. We don't use 3d domain yet in device-trees, so rename it to the name used by Tegra TRMs: TD, TD2. Reported-by: David Heidelberg Reviewed-by: Ulf Hansson Signed-off-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 6db0e31ac6fa..cc8c22327311 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -2988,7 +2988,7 @@ static SIMPLE_DEV_PM_OPS(tegra_pmc_pm_ops, tegra_pmc_suspend, tegra_pmc_resume); static const char * const tegra20_powergates[] = { [TEGRA_POWERGATE_CPU] = "cpu", - [TEGRA_POWERGATE_3D] = "3d", + [TEGRA_POWERGATE_3D] = "td", [TEGRA_POWERGATE_VENC] = "venc", [TEGRA_POWERGATE_VDEC] = "vdec", [TEGRA_POWERGATE_PCIE] = "pcie", @@ -3096,7 +3096,7 @@ static const struct tegra_pmc_soc tegra20_pmc_soc = { static const char * const tegra30_powergates[] = { [TEGRA_POWERGATE_CPU] = "cpu0", - [TEGRA_POWERGATE_3D] = "3d0", + [TEGRA_POWERGATE_3D] = "td", [TEGRA_POWERGATE_VENC] = "venc", [TEGRA_POWERGATE_VDEC] = "vdec", [TEGRA_POWERGATE_PCIE] = "pcie", @@ -3108,7 +3108,7 @@ static const char * const tegra30_powergates[] = { [TEGRA_POWERGATE_CPU2] = "cpu2", [TEGRA_POWERGATE_CPU3] = "cpu3", [TEGRA_POWERGATE_CELP] = "celp", - [TEGRA_POWERGATE_3D1] = "3d1", + [TEGRA_POWERGATE_3D1] = "td2", }; static const u8 tegra30_cpu_powergates[] = { @@ -3157,7 +3157,7 @@ static const struct tegra_pmc_soc tegra30_pmc_soc = { static const char * const tegra114_powergates[] = { [TEGRA_POWERGATE_CPU] = "crail", - [TEGRA_POWERGATE_3D] = "3d", + [TEGRA_POWERGATE_3D] = "td", [TEGRA_POWERGATE_VENC] = "venc", [TEGRA_POWERGATE_VDEC] = "vdec", [TEGRA_POWERGATE_MPE] = "mpe", From 9131c6331726514f0d0364c41f8733cc5eec11ab Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 1 Dec 2021 02:23:09 +0300 Subject: [PATCH 57/94] soc/tegra: Add devm_tegra_core_dev_init_opp_table_common() Only couple drivers need to get the -ENODEV error code and majority of drivers need to explicitly initialize the performance state. Add new common helper which sets up OPP table for these drivers. Reviewed-by: Ulf Hansson Signed-off-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- include/soc/tegra/common.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/include/soc/tegra/common.h b/include/soc/tegra/common.h index af41ad80ec21..8ec1ac07fc85 100644 --- a/include/soc/tegra/common.h +++ b/include/soc/tegra/common.h @@ -39,4 +39,19 @@ devm_tegra_core_dev_init_opp_table(struct device *dev, } #endif +static inline int +devm_tegra_core_dev_init_opp_table_common(struct device *dev) +{ + struct tegra_core_opp_params opp_params = {}; + int err; + + opp_params.init_state = true; + + err = devm_tegra_core_dev_init_opp_table(dev, &opp_params); + if (err != -ENODEV) + return err; + + return 0; +} + #endif /* __SOC_TEGRA_COMMON_H__ */ From 81c4c86c66650f61c6d7a712737d43a3e4d072bf Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 1 Dec 2021 02:23:39 +0300 Subject: [PATCH 58/94] soc/tegra: pmc: Rename core power domain CORE power domain uses name of device-tree node, which is inconsistent with the names of PMC domains. Set the name to "core" to make it consistent. Reviewed-by: Ulf Hansson Signed-off-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index cc8c22327311..5aceacbd8ce0 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -1370,7 +1370,7 @@ static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np) if (!genpd) return -ENOMEM; - genpd->name = np->name; + genpd->name = "core"; genpd->set_performance_state = tegra_pmc_core_pd_set_performance_state; genpd->opp_to_performance_state = tegra_pmc_core_pd_opp_to_performance_state; From 8b85e11c1a7a75007e027efd1dbc5add689aa5a0 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 1 Dec 2021 02:23:26 +0300 Subject: [PATCH 59/94] usb: chipidea: tegra: Add runtime PM and OPP support The Tegra USB controller belongs to the core power domain and we're going to enable GENPD support for the core domain. Now USB controller must be resumed using runtime PM API in order to initialize the USB power state. We already support runtime PM for the CI device, but CI's PM is separated from the RPM managed by tegra-usb driver. Add runtime PM and OPP support to the driver. Acked-by: Peter Chen Reviewed-by: Ulf Hansson Signed-off-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/usb/chipidea/ci_hdrc_tegra.c | 53 ++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c index 60361141ac04..a72a9474afea 100644 --- a/drivers/usb/chipidea/ci_hdrc_tegra.c +++ b/drivers/usb/chipidea/ci_hdrc_tegra.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -15,6 +16,8 @@ #include #include +#include + #include "../host/ehci.h" #include "ci.h" @@ -278,6 +281,8 @@ static int tegra_usb_probe(struct platform_device *pdev) if (!usb) return -ENOMEM; + platform_set_drvdata(pdev, usb); + soc = of_device_get_match_data(&pdev->dev); if (!soc) { dev_err(&pdev->dev, "failed to match OF data\n"); @@ -296,11 +301,14 @@ static int tegra_usb_probe(struct platform_device *pdev) return err; } - err = clk_prepare_enable(usb->clk); - if (err < 0) { - dev_err(&pdev->dev, "failed to enable clock: %d\n", err); + err = devm_tegra_core_dev_init_opp_table_common(&pdev->dev); + if (err) + return err; + + pm_runtime_enable(&pdev->dev); + err = pm_runtime_resume_and_get(&pdev->dev); + if (err) return err; - } if (device_property_present(&pdev->dev, "nvidia,needs-double-reset")) usb->needs_double_reset = true; @@ -320,8 +328,6 @@ static int tegra_usb_probe(struct platform_device *pdev) if (err) goto fail_power_off; - platform_set_drvdata(pdev, usb); - /* setup and register ChipIdea HDRC device */ usb->soc = soc; usb->data.name = "tegra-usb"; @@ -350,7 +356,9 @@ static int tegra_usb_probe(struct platform_device *pdev) phy_shutdown: usb_phy_shutdown(usb->phy); fail_power_off: - clk_disable_unprepare(usb->clk); + pm_runtime_put_sync_suspend(&pdev->dev); + pm_runtime_force_suspend(&pdev->dev); + return err; } @@ -360,15 +368,46 @@ static int tegra_usb_remove(struct platform_device *pdev) ci_hdrc_remove_device(usb->dev); usb_phy_shutdown(usb->phy); + + pm_runtime_put_sync_suspend(&pdev->dev); + pm_runtime_force_suspend(&pdev->dev); + + return 0; +} + +static int __maybe_unused tegra_usb_runtime_resume(struct device *dev) +{ + struct tegra_usb *usb = dev_get_drvdata(dev); + int err; + + err = clk_prepare_enable(usb->clk); + if (err < 0) { + dev_err(dev, "failed to enable clock: %d\n", err); + return err; + } + + return 0; +} + +static int __maybe_unused tegra_usb_runtime_suspend(struct device *dev) +{ + struct tegra_usb *usb = dev_get_drvdata(dev); + clk_disable_unprepare(usb->clk); return 0; } +static const struct dev_pm_ops tegra_usb_pm = { + SET_RUNTIME_PM_OPS(tegra_usb_runtime_suspend, tegra_usb_runtime_resume, + NULL) +}; + static struct platform_driver tegra_usb_driver = { .driver = { .name = "tegra-usb", .of_match_table = tegra_usb_of_match, + .pm = &tegra_usb_pm, }, .probe = tegra_usb_probe, .remove = tegra_usb_remove, From 59caf73284d11188d5e6a4ad25ab4678c05ef639 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 1 Dec 2021 02:23:27 +0300 Subject: [PATCH 60/94] bus: tegra-gmi: Add runtime PM and OPP support The GMI bus on Tegra belongs to the core power domain and we're going to enable GENPD support for the core domain. Now GMI must be resumed using runtime PM API in order to initialize the GMI power state. Add runtime PM and OPP support to the GMI driver. Reviewed-by: Ulf Hansson Signed-off-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/bus/tegra-gmi.c | 50 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/drivers/bus/tegra-gmi.c b/drivers/bus/tegra-gmi.c index a6570789f7af..35b59f92fa66 100644 --- a/drivers/bus/tegra-gmi.c +++ b/drivers/bus/tegra-gmi.c @@ -13,8 +13,11 @@ #include #include #include +#include #include +#include + #define TEGRA_GMI_CONFIG 0x00 #define TEGRA_GMI_CONFIG_GO BIT(31) #define TEGRA_GMI_BUS_WIDTH_32BIT BIT(30) @@ -54,9 +57,10 @@ static int tegra_gmi_enable(struct tegra_gmi *gmi) { int err; - err = clk_prepare_enable(gmi->clk); - if (err < 0) { - dev_err(gmi->dev, "failed to enable clock: %d\n", err); + pm_runtime_enable(gmi->dev); + err = pm_runtime_resume_and_get(gmi->dev); + if (err) { + pm_runtime_disable(gmi->dev); return err; } @@ -83,7 +87,9 @@ static void tegra_gmi_disable(struct tegra_gmi *gmi) writel(config, gmi->base + TEGRA_GMI_CONFIG); reset_control_assert(gmi->rst); - clk_disable_unprepare(gmi->clk); + + pm_runtime_put_sync_suspend(gmi->dev); + pm_runtime_force_suspend(gmi->dev); } static int tegra_gmi_parse_dt(struct tegra_gmi *gmi) @@ -213,6 +219,7 @@ static int tegra_gmi_probe(struct platform_device *pdev) if (!gmi) return -ENOMEM; + platform_set_drvdata(pdev, gmi); gmi->dev = dev; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -232,6 +239,10 @@ static int tegra_gmi_probe(struct platform_device *pdev) return PTR_ERR(gmi->rst); } + err = devm_tegra_core_dev_init_opp_table_common(&pdev->dev); + if (err) + return err; + err = tegra_gmi_parse_dt(gmi); if (err) return err; @@ -247,8 +258,6 @@ static int tegra_gmi_probe(struct platform_device *pdev) return err; } - platform_set_drvdata(pdev, gmi); - return 0; } @@ -262,6 +271,34 @@ static int tegra_gmi_remove(struct platform_device *pdev) return 0; } +static int __maybe_unused tegra_gmi_runtime_resume(struct device *dev) +{ + struct tegra_gmi *gmi = dev_get_drvdata(dev); + int err; + + err = clk_prepare_enable(gmi->clk); + if (err < 0) { + dev_err(gmi->dev, "failed to enable clock: %d\n", err); + return err; + } + + return 0; +} + +static int __maybe_unused tegra_gmi_runtime_suspend(struct device *dev) +{ + struct tegra_gmi *gmi = dev_get_drvdata(dev); + + clk_disable_unprepare(gmi->clk); + + return 0; +} + +static const struct dev_pm_ops tegra_gmi_pm = { + SET_RUNTIME_PM_OPS(tegra_gmi_runtime_suspend, tegra_gmi_runtime_resume, + NULL) +}; + static const struct of_device_id tegra_gmi_id_table[] = { { .compatible = "nvidia,tegra20-gmi", }, { .compatible = "nvidia,tegra30-gmi", }, @@ -275,6 +312,7 @@ static struct platform_driver tegra_gmi_driver = { .driver = { .name = "tegra-gmi", .of_match_table = tegra_gmi_id_table, + .pm = &tegra_gmi_pm, }, }; module_platform_driver(tegra_gmi_driver); From 3da9b0feaa1617061eddfd0eedcc24164c71528a Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 1 Dec 2021 02:23:28 +0300 Subject: [PATCH 61/94] pwm: tegra: Add runtime PM and OPP support The PWM on Tegra belongs to the core power domain and we're going to enable GENPD support for the core domain. Now PWM must be resumed using runtime PM API in order to initialize the PWM power state. The PWM clock rate must be changed using OPP API that will reconfigure the power domain performance state in accordance to the rate. Add runtime PM and OPP support to the PWM driver. Reviewed-by: Ulf Hansson Signed-off-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tegra.c | 82 ++++++++++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 18 deletions(-) diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c index 11a10b575ace..18cf974ac776 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -42,12 +42,16 @@ #include #include #include +#include #include #include #include +#include #include #include +#include + #define PWM_ENABLE (1 << 31) #define PWM_DUTY_WIDTH 8 #define PWM_DUTY_SHIFT 16 @@ -145,7 +149,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, required_clk_rate = (NSEC_PER_SEC / period_ns) << PWM_DUTY_WIDTH; - err = clk_set_rate(pc->clk, required_clk_rate); + err = dev_pm_opp_set_rate(pc->dev, required_clk_rate); if (err < 0) return -EINVAL; @@ -181,8 +185,8 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, * before writing the register. Otherwise, keep it enabled. */ if (!pwm_is_enabled(pwm)) { - err = clk_prepare_enable(pc->clk); - if (err < 0) + err = pm_runtime_resume_and_get(pc->dev); + if (err) return err; } else val |= PWM_ENABLE; @@ -193,7 +197,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, * If the PWM is not enabled, turn the clock off again to save power. */ if (!pwm_is_enabled(pwm)) - clk_disable_unprepare(pc->clk); + pm_runtime_put(pc->dev); return 0; } @@ -204,8 +208,8 @@ static int tegra_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) int rc = 0; u32 val; - rc = clk_prepare_enable(pc->clk); - if (rc < 0) + rc = pm_runtime_resume_and_get(pc->dev); + if (rc) return rc; val = pwm_readl(pc, pwm->hwpwm); @@ -224,7 +228,7 @@ static void tegra_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) val &= ~PWM_ENABLE; pwm_writel(pc, pwm->hwpwm, val); - clk_disable_unprepare(pc->clk); + pm_runtime_put_sync(pc->dev); } static const struct pwm_ops tegra_pwm_ops = { @@ -256,11 +260,20 @@ static int tegra_pwm_probe(struct platform_device *pdev) if (IS_ERR(pwm->clk)) return PTR_ERR(pwm->clk); + ret = devm_tegra_core_dev_init_opp_table_common(&pdev->dev); + if (ret) + return ret; + + pm_runtime_enable(&pdev->dev); + ret = pm_runtime_resume_and_get(&pdev->dev); + if (ret) + return ret; + /* Set maximum frequency of the IP */ - ret = clk_set_rate(pwm->clk, pwm->soc->max_frequency); + ret = dev_pm_opp_set_rate(pwm->dev, pwm->soc->max_frequency); if (ret < 0) { dev_err(&pdev->dev, "Failed to set max frequency: %d\n", ret); - return ret; + goto put_pm; } /* @@ -278,7 +291,7 @@ static int tegra_pwm_probe(struct platform_device *pdev) if (IS_ERR(pwm->rst)) { ret = PTR_ERR(pwm->rst); dev_err(&pdev->dev, "Reset control is not found: %d\n", ret); - return ret; + goto put_pm; } reset_control_deassert(pwm->rst); @@ -291,10 +304,16 @@ static int tegra_pwm_probe(struct platform_device *pdev) if (ret < 0) { dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); reset_control_assert(pwm->rst); - return ret; + goto put_pm; } + pm_runtime_put(&pdev->dev); + return 0; +put_pm: + pm_runtime_put_sync_suspend(&pdev->dev); + pm_runtime_force_suspend(&pdev->dev); + return ret; } static int tegra_pwm_remove(struct platform_device *pdev) @@ -305,20 +324,44 @@ static int tegra_pwm_remove(struct platform_device *pdev) reset_control_assert(pc->rst); + pm_runtime_force_suspend(&pdev->dev); + return 0; } -#ifdef CONFIG_PM_SLEEP -static int tegra_pwm_suspend(struct device *dev) +static int __maybe_unused tegra_pwm_runtime_suspend(struct device *dev) { - return pinctrl_pm_select_sleep_state(dev); + struct tegra_pwm_chip *pc = dev_get_drvdata(dev); + int err; + + clk_disable_unprepare(pc->clk); + + err = pinctrl_pm_select_sleep_state(dev); + if (err) { + clk_prepare_enable(pc->clk); + return err; + } + + return 0; } -static int tegra_pwm_resume(struct device *dev) +static int __maybe_unused tegra_pwm_runtime_resume(struct device *dev) { - return pinctrl_pm_select_default_state(dev); + struct tegra_pwm_chip *pc = dev_get_drvdata(dev); + int err; + + err = pinctrl_pm_select_default_state(dev); + if (err) + return err; + + err = clk_prepare_enable(pc->clk); + if (err) { + pinctrl_pm_select_sleep_state(dev); + return err; + } + + return 0; } -#endif static const struct tegra_pwm_soc tegra20_pwm_soc = { .num_channels = 4, @@ -344,7 +387,10 @@ static const struct of_device_id tegra_pwm_of_match[] = { MODULE_DEVICE_TABLE(of, tegra_pwm_of_match); static const struct dev_pm_ops tegra_pwm_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(tegra_pwm_suspend, tegra_pwm_resume) + SET_RUNTIME_PM_OPS(tegra_pwm_runtime_suspend, tegra_pwm_runtime_resume, + NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) }; static struct platform_driver tegra_pwm_driver = { From d618978dd4d33ec3920aeca8efbfb5f59fbe7df0 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 1 Dec 2021 02:23:29 +0300 Subject: [PATCH 62/94] mmc: sdhci-tegra: Add runtime PM and OPP support The SDHCI on Tegra belongs to the core power domain and we're going to enable GENPD support for the core domain. Now SDHCI must be resumed using runtime PM API in order to initialize the SDHCI power state. The SDHCI clock rate must be changed using OPP API that will reconfigure the power domain performance state in accordance to the rate. Add runtime PM and OPP support to the SDHCI driver. Reviewed-by: Ulf Hansson Signed-off-by: Dmitry Osipenko Acked-by: Adrian Hunter Signed-off-by: Thierry Reding --- drivers/mmc/host/sdhci-tegra.c | 81 +++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 16 deletions(-) diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index a5001875876b..6435a75142a6 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include #include @@ -24,6 +26,8 @@ #include #include +#include + #include "sdhci-pltfm.h" #include "cqhci.h" @@ -760,7 +764,9 @@ static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); + struct device *dev = mmc_dev(host->mmc); unsigned long host_clk; + int err; if (!clock) return sdhci_set_clock(host, clock); @@ -778,7 +784,12 @@ static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) * from clk_get_rate() is used. */ host_clk = tegra_host->ddr_signaling ? clock * 2 : clock; - clk_set_rate(pltfm_host->clk, host_clk); + + err = dev_pm_opp_set_rate(dev, host_clk); + if (err) + dev_err(dev, "failed to set clk rate to %luHz: %d\n", + host_clk, err); + tegra_host->curr_clk_rate = host_clk; if (tegra_host->ddr_signaling) host->max_clk = host_clk; @@ -1705,7 +1716,6 @@ static int sdhci_tegra_probe(struct platform_device *pdev) "failed to get clock\n"); goto err_clk_get; } - clk_prepare_enable(clk); pltfm_host->clk = clk; tegra_host->rst = devm_reset_control_get_exclusive(&pdev->dev, @@ -1716,15 +1726,24 @@ static int sdhci_tegra_probe(struct platform_device *pdev) goto err_rst_get; } - rc = reset_control_assert(tegra_host->rst); + rc = devm_tegra_core_dev_init_opp_table_common(&pdev->dev); if (rc) goto err_rst_get; + pm_runtime_enable(&pdev->dev); + rc = pm_runtime_resume_and_get(&pdev->dev); + if (rc) + goto err_pm_get; + + rc = reset_control_assert(tegra_host->rst); + if (rc) + goto err_rst_assert; + usleep_range(2000, 4000); rc = reset_control_deassert(tegra_host->rst); if (rc) - goto err_rst_get; + goto err_rst_assert; usleep_range(2000, 4000); @@ -1736,8 +1755,11 @@ static int sdhci_tegra_probe(struct platform_device *pdev) err_add_host: reset_control_assert(tegra_host->rst); +err_rst_assert: + pm_runtime_put_sync_suspend(&pdev->dev); +err_pm_get: + pm_runtime_disable(&pdev->dev); err_rst_get: - clk_disable_unprepare(pltfm_host->clk); err_clk_get: clk_disable_unprepare(tegra_host->tmclk); err_power_req: @@ -1756,19 +1778,38 @@ static int sdhci_tegra_remove(struct platform_device *pdev) reset_control_assert(tegra_host->rst); usleep_range(2000, 4000); - clk_disable_unprepare(pltfm_host->clk); - clk_disable_unprepare(tegra_host->tmclk); + pm_runtime_put_sync_suspend(&pdev->dev); + pm_runtime_force_suspend(&pdev->dev); + + clk_disable_unprepare(tegra_host->tmclk); sdhci_pltfm_free(pdev); return 0; } -#ifdef CONFIG_PM_SLEEP -static int __maybe_unused sdhci_tegra_suspend(struct device *dev) +static int __maybe_unused sdhci_tegra_runtime_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + clk_disable_unprepare(pltfm_host->clk); + + return 0; +} + +static int __maybe_unused sdhci_tegra_runtime_resume(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + return clk_prepare_enable(pltfm_host->clk); +} + +#ifdef CONFIG_PM_SLEEP +static int sdhci_tegra_suspend(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); int ret; if (host->mmc->caps2 & MMC_CAP2_CQE) { @@ -1783,17 +1824,22 @@ static int __maybe_unused sdhci_tegra_suspend(struct device *dev) return ret; } - clk_disable_unprepare(pltfm_host->clk); + ret = pm_runtime_force_suspend(dev); + if (ret) { + sdhci_resume_host(host); + cqhci_resume(host->mmc); + return ret; + } + return 0; } -static int __maybe_unused sdhci_tegra_resume(struct device *dev) +static int sdhci_tegra_resume(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); - struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); int ret; - ret = clk_prepare_enable(pltfm_host->clk); + ret = pm_runtime_force_resume(dev); if (ret) return ret; @@ -1812,13 +1858,16 @@ static int __maybe_unused sdhci_tegra_resume(struct device *dev) suspend_host: sdhci_suspend_host(host); disable_clk: - clk_disable_unprepare(pltfm_host->clk); + pm_runtime_force_suspend(dev); return ret; } #endif -static SIMPLE_DEV_PM_OPS(sdhci_tegra_dev_pm_ops, sdhci_tegra_suspend, - sdhci_tegra_resume); +static const struct dev_pm_ops sdhci_tegra_dev_pm_ops = { + SET_RUNTIME_PM_OPS(sdhci_tegra_runtime_suspend, sdhci_tegra_runtime_resume, + NULL) + SET_SYSTEM_SLEEP_PM_OPS(sdhci_tegra_suspend, sdhci_tegra_resume) +}; static struct platform_driver sdhci_tegra_driver = { .driver = { From 6902dc2fd57c9d72418f82e00a2af6d2ee7c7e0d Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 1 Dec 2021 02:23:30 +0300 Subject: [PATCH 63/94] mtd: rawnand: tegra: Add runtime PM and OPP support The NAND on Tegra belongs to the core power domain and we're going to enable GENPD support for the core domain. Now NAND must be resumed using runtime PM API in order to initialize the NAND power state. Add runtime PM and OPP support to the NAND driver. Reviewed-by: Ulf Hansson Acked-by: Miquel Raynal Signed-off-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/mtd/nand/raw/tegra_nand.c | 58 ++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/nand/raw/tegra_nand.c b/drivers/mtd/nand/raw/tegra_nand.c index 32431bbe69b8..b36e5260ae27 100644 --- a/drivers/mtd/nand/raw/tegra_nand.c +++ b/drivers/mtd/nand/raw/tegra_nand.c @@ -17,8 +17,11 @@ #include #include #include +#include #include +#include + #define COMMAND 0x00 #define COMMAND_GO BIT(31) #define COMMAND_CLE BIT(30) @@ -1151,6 +1154,7 @@ static int tegra_nand_probe(struct platform_device *pdev) return -ENOMEM; ctrl->dev = &pdev->dev; + platform_set_drvdata(pdev, ctrl); nand_controller_init(&ctrl->controller); ctrl->controller.ops = &tegra_nand_controller_ops; @@ -1166,14 +1170,23 @@ static int tegra_nand_probe(struct platform_device *pdev) if (IS_ERR(ctrl->clk)) return PTR_ERR(ctrl->clk); - err = clk_prepare_enable(ctrl->clk); + err = devm_tegra_core_dev_init_opp_table_common(&pdev->dev); + if (err) + return err; + + /* + * This driver doesn't support active power management yet, + * so we will simply keep device resumed. + */ + pm_runtime_enable(&pdev->dev); + err = pm_runtime_resume_and_get(&pdev->dev); if (err) return err; err = reset_control_reset(rst); if (err) { dev_err(ctrl->dev, "Failed to reset HW: %d\n", err); - goto err_disable_clk; + goto err_put_pm; } writel_relaxed(HWSTATUS_CMD_DEFAULT, ctrl->regs + HWSTATUS_CMD); @@ -1188,21 +1201,20 @@ static int tegra_nand_probe(struct platform_device *pdev) dev_name(&pdev->dev), ctrl); if (err) { dev_err(ctrl->dev, "Failed to get IRQ: %d\n", err); - goto err_disable_clk; + goto err_put_pm; } writel_relaxed(DMA_MST_CTRL_IS_DONE, ctrl->regs + DMA_MST_CTRL); err = tegra_nand_chips_init(ctrl->dev, ctrl); if (err) - goto err_disable_clk; - - platform_set_drvdata(pdev, ctrl); + goto err_put_pm; return 0; -err_disable_clk: - clk_disable_unprepare(ctrl->clk); +err_put_pm: + pm_runtime_put_sync_suspend(ctrl->dev); + pm_runtime_force_suspend(ctrl->dev); return err; } @@ -1219,11 +1231,40 @@ static int tegra_nand_remove(struct platform_device *pdev) nand_cleanup(chip); + pm_runtime_put_sync_suspend(ctrl->dev); + pm_runtime_force_suspend(ctrl->dev); + + return 0; +} + +static int __maybe_unused tegra_nand_runtime_resume(struct device *dev) +{ + struct tegra_nand_controller *ctrl = dev_get_drvdata(dev); + int err; + + err = clk_prepare_enable(ctrl->clk); + if (err) { + dev_err(dev, "Failed to enable clock: %d\n", err); + return err; + } + + return 0; +} + +static int __maybe_unused tegra_nand_runtime_suspend(struct device *dev) +{ + struct tegra_nand_controller *ctrl = dev_get_drvdata(dev); + clk_disable_unprepare(ctrl->clk); return 0; } +static const struct dev_pm_ops tegra_nand_pm = { + SET_RUNTIME_PM_OPS(tegra_nand_runtime_suspend, tegra_nand_runtime_resume, + NULL) +}; + static const struct of_device_id tegra_nand_of_match[] = { { .compatible = "nvidia,tegra20-nand" }, { /* sentinel */ } @@ -1234,6 +1275,7 @@ static struct platform_driver tegra_nand_driver = { .driver = { .name = "tegra-nand", .of_match_table = tegra_nand_of_match, + .pm = &tegra_nand_pm, }, .probe = tegra_nand_probe, .remove = tegra_nand_remove, From 07f837554bb59c17729951c12d3564d05d0ff4bb Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 1 Dec 2021 02:23:31 +0300 Subject: [PATCH 64/94] spi: tegra20-slink: Add OPP support The SPI on Tegra belongs to the core power domain and we're going to enable GENPD support for the core domain. Now SPI driver must use OPP API for driving the controller's clock rate because OPP API takes care of reconfiguring the domain's performance state in accordance to the rate. Add OPP support to the driver. Acked-by: Mark Brown Reviewed-by: Ulf Hansson Signed-off-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/spi/spi-tegra20-slink.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index e8204e155484..2a03739a0c60 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -18,12 +18,15 @@ #include #include #include +#include #include #include #include #include #include +#include + #define SLINK_COMMAND 0x000 #define SLINK_BIT_LENGTH(x) (((x) & 0x1f) << 0) #define SLINK_WORD_SIZE(x) (((x) & 0x1f) << 5) @@ -680,7 +683,7 @@ static int tegra_slink_start_transfer_one(struct spi_device *spi, bits_per_word = t->bits_per_word; speed = t->speed_hz; if (speed != tspi->cur_speed) { - clk_set_rate(tspi->clk, speed * 4); + dev_pm_opp_set_rate(tspi->dev, speed * 4); tspi->cur_speed = speed; } @@ -1066,6 +1069,10 @@ static int tegra_slink_probe(struct platform_device *pdev) goto exit_free_master; } + ret = devm_tegra_core_dev_init_opp_table_common(&pdev->dev); + if (ret) + goto exit_free_master; + tspi->max_buf_size = SLINK_FIFO_DEPTH << 2; tspi->dma_buf_size = DEFAULT_SPI_DMA_BUF_LEN; From e0abae19535504cc012a32c98619c07ace8a91aa Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 1 Dec 2021 02:23:34 +0300 Subject: [PATCH 65/94] media: staging: tegra-vde: Support generic power domain Currently driver supports legacy power domain API, this patch adds generic power domain support. This allows us to utilize a modern GENPD API for newer device-trees. Reviewed-by: Ulf Hansson Tested-by: Peter Geis # Ouya T30 Tested-by: Paul Fertser # PAZ00 T20 Tested-by: Nicolas Chauvet # PAZ00 T20 and TK1 T124 Tested-by: Matt Merhar # Ouya T30 Acked-by: Hans Verkuil Signed-off-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/staging/media/tegra-vde/vde.c | 63 ++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 11 deletions(-) diff --git a/drivers/staging/media/tegra-vde/vde.c b/drivers/staging/media/tegra-vde/vde.c index ed4c1250b303..859f60a70904 100644 --- a/drivers/staging/media/tegra-vde/vde.c +++ b/drivers/staging/media/tegra-vde/vde.c @@ -20,6 +20,7 @@ #include #include +#include #include #include "uapi.h" @@ -920,13 +921,17 @@ static __maybe_unused int tegra_vde_runtime_suspend(struct device *dev) struct tegra_vde *vde = dev_get_drvdata(dev); int err; - err = tegra_powergate_power_off(TEGRA_POWERGATE_VDEC); - if (err) { - dev_err(dev, "Failed to power down HW: %d\n", err); - return err; + if (!dev->pm_domain) { + err = tegra_powergate_power_off(TEGRA_POWERGATE_VDEC); + if (err) { + dev_err(dev, "Failed to power down HW: %d\n", err); + return err; + } } clk_disable_unprepare(vde->clk); + reset_control_release(vde->rst); + reset_control_release(vde->rst_mc); return 0; } @@ -936,14 +941,45 @@ static __maybe_unused int tegra_vde_runtime_resume(struct device *dev) struct tegra_vde *vde = dev_get_drvdata(dev); int err; - err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_VDEC, - vde->clk, vde->rst); + err = reset_control_acquire(vde->rst_mc); if (err) { - dev_err(dev, "Failed to power up HW : %d\n", err); + dev_err(dev, "Failed to acquire mc reset: %d\n", err); return err; } + err = reset_control_acquire(vde->rst); + if (err) { + dev_err(dev, "Failed to acquire reset: %d\n", err); + goto release_mc_reset; + } + + if (!dev->pm_domain) { + err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_VDEC, + vde->clk, vde->rst); + if (err) { + dev_err(dev, "Failed to power up HW : %d\n", err); + goto release_reset; + } + } else { + /* + * tegra_powergate_sequence_power_up() leaves clocks enabled, + * while GENPD not. + */ + err = clk_prepare_enable(vde->clk); + if (err) { + dev_err(dev, "Failed to enable clock: %d\n", err); + goto release_reset; + } + } + return 0; + +release_reset: + reset_control_release(vde->rst); +release_mc_reset: + reset_control_release(vde->rst_mc); + + return err; } static int tegra_vde_probe(struct platform_device *pdev) @@ -1001,14 +1037,14 @@ static int tegra_vde_probe(struct platform_device *pdev) return err; } - vde->rst = devm_reset_control_get(dev, NULL); + vde->rst = devm_reset_control_get_exclusive_released(dev, NULL); if (IS_ERR(vde->rst)) { err = PTR_ERR(vde->rst); dev_err(dev, "Could not get VDE reset %d\n", err); return err; } - vde->rst_mc = devm_reset_control_get_optional(dev, "mc"); + vde->rst_mc = devm_reset_control_get_optional_exclusive_released(dev, "mc"); if (IS_ERR(vde->rst_mc)) { err = PTR_ERR(vde->rst_mc); dev_err(dev, "Could not get MC reset %d\n", err); @@ -1026,6 +1062,12 @@ static int tegra_vde_probe(struct platform_device *pdev) return err; } + err = devm_tegra_core_dev_init_opp_table_common(dev); + if (err) { + dev_err(dev, "Could initialize OPP table %d\n", err); + return err; + } + vde->iram_pool = of_gen_pool_get(dev->of_node, "iram", 0); if (!vde->iram_pool) { dev_err(dev, "Could not get IRAM pool\n"); @@ -1133,8 +1175,7 @@ static void tegra_vde_shutdown(struct platform_device *pdev) * On some devices bootloader isn't ready to a power-gated VDE on * a warm-reboot, machine will hang in that case. */ - if (pm_runtime_status_suspended(&pdev->dev)) - tegra_vde_runtime_resume(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); } static __maybe_unused int tegra_vde_pm_suspend(struct device *dev) From e2a6d22f3b48dffcab1dbcb162572ef5a923f24d Mon Sep 17 00:00:00 2001 From: Adam Ford Date: Tue, 14 Dec 2021 18:46:18 -0600 Subject: [PATCH 66/94] soc: imx: gpcv2: keep i.MX8MN gpumix bus clock enabled Like the i.MX8MM, keep the gpumix clocks running when the domain is active. Signed-off-by: Adam Ford Reviewed-by: Lucas Stach Signed-off-by: Shawn Guo --- drivers/soc/imx/gpcv2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c index 8176380b02e6..a0eab9f41a71 100644 --- a/drivers/soc/imx/gpcv2.c +++ b/drivers/soc/imx/gpcv2.c @@ -841,6 +841,7 @@ static const struct imx_pgc_domain imx8mn_pgc_domains[] = { .hskack = IMX8MN_GPUMIX_HSK_PWRDNACKN, }, .pgc = BIT(IMX8MN_PGC_GPUMIX), + .keep_clocks = true, }, }; From a0ec8a3a4c8122c1abae04e90c66e79e77eed357 Mon Sep 17 00:00:00 2001 From: Adam Ford Date: Tue, 14 Dec 2021 18:46:19 -0600 Subject: [PATCH 67/94] soc: imx: gpcv2: Add dispmix and mipi domains to imx8mn The dispmix will be needed for the blkctl driver, so add it to the gpcv2. Signed-off-by: Adam Ford Reviewed-by: Lucas Stach Signed-off-by: Shawn Guo --- drivers/soc/imx/gpcv2.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c index a0eab9f41a71..3e59d479d001 100644 --- a/drivers/soc/imx/gpcv2.c +++ b/drivers/soc/imx/gpcv2.c @@ -843,6 +843,31 @@ static const struct imx_pgc_domain imx8mn_pgc_domains[] = { .pgc = BIT(IMX8MN_PGC_GPUMIX), .keep_clocks = true, }, + + [IMX8MN_POWER_DOMAIN_DISPMIX] = { + .genpd = { + .name = "dispmix", + }, + .bits = { + .pxx = IMX8MN_DISPMIX_SW_Pxx_REQ, + .map = IMX8MN_DISPMIX_A53_DOMAIN, + .hskreq = IMX8MN_DISPMIX_HSK_PWRDNREQN, + .hskack = IMX8MN_DISPMIX_HSK_PWRDNACKN, + }, + .pgc = BIT(IMX8MN_PGC_DISPMIX), + .keep_clocks = true, + }, + + [IMX8MN_POWER_DOMAIN_MIPI] = { + .genpd = { + .name = "mipi", + }, + .bits = { + .pxx = IMX8MN_MIPI_SW_Pxx_REQ, + .map = IMX8MN_MIPI_A53_DOMAIN, + }, + .pgc = BIT(IMX8MN_PGC_MIPI), + }, }; static const struct regmap_range imx8mn_yes_ranges[] = { From b77beaaee1be62a4623dd7142868c2e1180d8288 Mon Sep 17 00:00:00 2001 From: Adam Ford Date: Tue, 14 Dec 2021 18:46:20 -0600 Subject: [PATCH 68/94] dt-bindings: power: imx8mn: add defines for DISP blk-ctrl domains This adds the defines for the power domains provided by the DISP blk-ctrl. Signed-off-by: Adam Ford Acked-by: Rob Herring Reviewed-by: Lucas Stach Signed-off-by: Shawn Guo --- include/dt-bindings/power/imx8mn-power.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/dt-bindings/power/imx8mn-power.h b/include/dt-bindings/power/imx8mn-power.h index 102ee85a9b62..eedd0e581939 100644 --- a/include/dt-bindings/power/imx8mn-power.h +++ b/include/dt-bindings/power/imx8mn-power.h @@ -12,4 +12,9 @@ #define IMX8MN_POWER_DOMAIN_DISPMIX 3 #define IMX8MN_POWER_DOMAIN_MIPI 4 +#define IMX8MN_DISPBLK_PD_MIPI_DSI 0 +#define IMX8MN_DISPBLK_PD_MIPI_CSI 1 +#define IMX8MN_DISPBLK_PD_LCDIF 2 +#define IMX8MN_DISPBLK_PD_ISI 3 + #endif From 7f511d514e8c34b6dd0c17b5496e48b2a6a2626c Mon Sep 17 00:00:00 2001 From: Adam Ford Date: Tue, 14 Dec 2021 18:46:22 -0600 Subject: [PATCH 69/94] soc: imx: imx8m-blk-ctrl: add i.MX8MN DISP blk-ctrl This adds the description for the i.MX8MN disp blk-ctrl. Signed-off-by: Adam Ford Reviewed-by: Lucas Stach Signed-off-by: Shawn Guo --- drivers/soc/imx/imx8m-blk-ctrl.c | 77 +++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/drivers/soc/imx/imx8m-blk-ctrl.c b/drivers/soc/imx/imx8m-blk-ctrl.c index 519b3651d1d9..58a7f11e7e08 100644 --- a/drivers/soc/imx/imx8m-blk-ctrl.c +++ b/drivers/soc/imx/imx8m-blk-ctrl.c @@ -14,6 +14,7 @@ #include #include +#include #define BLK_SFT_RSTN 0x0 #define BLK_CLK_EN 0x4 @@ -498,6 +499,77 @@ static const struct imx8m_blk_ctrl_data imx8mm_disp_blk_ctl_dev_data = { .num_domains = ARRAY_SIZE(imx8mm_disp_blk_ctl_domain_data), }; + +static int imx8mn_disp_power_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl, + power_nb); + + if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF) + return NOTIFY_OK; + + /* Enable bus clock and deassert bus reset */ + regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(8)); + regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(8)); + + /* + * On power up we have no software backchannel to the GPC to + * wait for the ADB handshake to happen, so we just delay for a + * bit. On power down the GPC driver waits for the handshake. + */ + if (action == GENPD_NOTIFY_ON) + udelay(5); + + + return NOTIFY_OK; +} + +static const struct imx8m_blk_ctrl_domain_data imx8mn_disp_blk_ctl_domain_data[] = { + [IMX8MN_DISPBLK_PD_MIPI_DSI] = { + .name = "dispblk-mipi-dsi", + .clk_names = (const char *[]){ "dsi-pclk", "dsi-ref", }, + .num_clks = 2, + .gpc_name = "mipi-dsi", + .rst_mask = BIT(0) | BIT(1), + .clk_mask = BIT(0) | BIT(1), + .mipi_phy_rst_mask = BIT(17), + }, + [IMX8MN_DISPBLK_PD_MIPI_CSI] = { + .name = "dispblk-mipi-csi", + .clk_names = (const char *[]){ "csi-aclk", "csi-pclk" }, + .num_clks = 2, + .gpc_name = "mipi-csi", + .rst_mask = BIT(2) | BIT(3), + .clk_mask = BIT(2) | BIT(3), + .mipi_phy_rst_mask = BIT(16), + }, + [IMX8MN_DISPBLK_PD_LCDIF] = { + .name = "dispblk-lcdif", + .clk_names = (const char *[]){ "lcdif-axi", "lcdif-apb", "lcdif-pix", }, + .num_clks = 3, + .gpc_name = "lcdif", + .rst_mask = BIT(4) | BIT(5), + .clk_mask = BIT(4) | BIT(5), + }, + [IMX8MN_DISPBLK_PD_ISI] = { + .name = "dispblk-isi", + .clk_names = (const char *[]){ "disp_axi", "disp_apb", "disp_axi_root", + "disp_apb_root"}, + .num_clks = 4, + .gpc_name = "isi", + .rst_mask = BIT(6) | BIT(7), + .clk_mask = BIT(6) | BIT(7), + }, +}; + +static const struct imx8m_blk_ctrl_data imx8mn_disp_blk_ctl_dev_data = { + .max_reg = 0x84, + .power_notifier_fn = imx8mn_disp_power_notifier, + .domains = imx8mn_disp_blk_ctl_domain_data, + .num_domains = ARRAY_SIZE(imx8mn_disp_blk_ctl_domain_data), +}; + static const struct of_device_id imx8m_blk_ctrl_of_match[] = { { .compatible = "fsl,imx8mm-vpu-blk-ctrl", @@ -505,7 +577,10 @@ static const struct of_device_id imx8m_blk_ctrl_of_match[] = { }, { .compatible = "fsl,imx8mm-disp-blk-ctrl", .data = &imx8mm_disp_blk_ctl_dev_data - } ,{ + }, { + .compatible = "fsl,imx8mn-disp-blk-ctrl", + .data = &imx8mn_disp_blk_ctl_dev_data + }, { /* Sentinel */ } }; From b603377e408fb41dbb0971635c57e123edbb5bf8 Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Sat, 4 Dec 2021 21:57:54 +0200 Subject: [PATCH 70/94] soc: samsung: Add USI driver USIv2 IP-core is found on modern ARM64 Exynos SoCs (like Exynos850) and provides selectable serial protocol (one of: UART, SPI, I2C). USIv2 registers usually reside in the same register map as a particular underlying protocol it implements, but have some particular offset. E.g. on Exynos850 the USI_UART has 0x13820000 base address, where UART registers have 0x00..0x40 offsets, and USI registers have 0xc0..0xdc offsets. Desired protocol can be chosen via SW_CONF register from System Register block of the same domain as USI. Before starting to use a particular protocol, USIv2 must be configured properly: 1. Select protocol to be used via System Register 2. Clear "reset" flag in USI_CON 3. Configure HWACG behavior (e.g. for UART Rx the HWACG must be disabled, so that the IP clock is not gated automatically); this is done using USI_OPTION register 4. Keep both USI clocks (PCLK and IPCLK) running during USI registers modification This driver implements the above behavior. Of course, USIv2 driver should be probed before UART/I2C/SPI drivers. It can be achieved by embedding UART/I2C/SPI nodes inside of the USI node (in Device Tree); driver then walks underlying nodes and instantiates those. Driver also handles USI configuration on PM resume, as register contents can be lost during CPU suspend. This driver is designed with different USI versions in mind. So it should be relatively easy to add new USI revisions to it later. Signed-off-by: Sam Protsenko Link: https://lore.kernel.org/r/20211204195757.8600-3-semen.protsenko@linaro.org Tested-by: Chanho Park Signed-off-by: Krzysztof Kozlowski --- drivers/soc/samsung/Kconfig | 14 ++ drivers/soc/samsung/Makefile | 2 + drivers/soc/samsung/exynos-usi.c | 285 +++++++++++++++++++++++++++++++ 3 files changed, 301 insertions(+) create mode 100644 drivers/soc/samsung/exynos-usi.c diff --git a/drivers/soc/samsung/Kconfig b/drivers/soc/samsung/Kconfig index e2cedef1e8d1..a9f8b224322e 100644 --- a/drivers/soc/samsung/Kconfig +++ b/drivers/soc/samsung/Kconfig @@ -23,6 +23,20 @@ config EXYNOS_CHIPID Support for Samsung Exynos SoC ChipID and Adaptive Supply Voltage. This driver can also be built as module (exynos_chipid). +config EXYNOS_USI + tristate "Exynos USI (Universal Serial Interface) driver" + default ARCH_EXYNOS && ARM64 + depends on ARCH_EXYNOS || COMPILE_TEST + select MFD_SYSCON + help + Enable support for USI block. USI (Universal Serial Interface) is an + IP-core found in modern Samsung Exynos SoCs, like Exynos850 and + ExynosAutoV0. USI block can be configured to provide one of the + following serial protocols: UART, SPI or High Speed I2C. + + This driver allows one to configure USI for desired protocol, which + is usually done in USI node in Device Tree. + config EXYNOS_PMU bool "Exynos PMU controller driver" if COMPILE_TEST depends on ARCH_EXYNOS || ((ARM || ARM64) && COMPILE_TEST) diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile index 2ae4bea804cf..9f59d1905ab0 100644 --- a/drivers/soc/samsung/Makefile +++ b/drivers/soc/samsung/Makefile @@ -4,6 +4,8 @@ obj-$(CONFIG_EXYNOS_ASV_ARM) += exynos5422-asv.o obj-$(CONFIG_EXYNOS_CHIPID) += exynos_chipid.o exynos_chipid-y += exynos-chipid.o exynos-asv.o +obj-$(CONFIG_EXYNOS_USI) += exynos-usi.o + obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o obj-$(CONFIG_EXYNOS_PMU_ARM_DRIVERS) += exynos3250-pmu.o exynos4-pmu.o \ diff --git a/drivers/soc/samsung/exynos-usi.c b/drivers/soc/samsung/exynos-usi.c new file mode 100644 index 000000000000..114352695ac2 --- /dev/null +++ b/drivers/soc/samsung/exynos-usi.c @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 Linaro Ltd. + * Author: Sam Protsenko + * + * Samsung Exynos USI driver (Universal Serial Interface). + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* USIv2: System Register: SW_CONF register bits */ +#define USI_V2_SW_CONF_NONE 0x0 +#define USI_V2_SW_CONF_UART BIT(0) +#define USI_V2_SW_CONF_SPI BIT(1) +#define USI_V2_SW_CONF_I2C BIT(2) +#define USI_V2_SW_CONF_MASK (USI_V2_SW_CONF_UART | USI_V2_SW_CONF_SPI | \ + USI_V2_SW_CONF_I2C) + +/* USIv2: USI register offsets */ +#define USI_CON 0x04 +#define USI_OPTION 0x08 + +/* USIv2: USI register bits */ +#define USI_CON_RESET BIT(0) +#define USI_OPTION_CLKREQ_ON BIT(1) +#define USI_OPTION_CLKSTOP_ON BIT(2) + +enum exynos_usi_ver { + USI_VER2 = 2, +}; + +struct exynos_usi_variant { + enum exynos_usi_ver ver; /* USI IP-core version */ + unsigned int sw_conf_mask; /* SW_CONF mask for all protocols */ + size_t min_mode; /* first index in exynos_usi_modes[] */ + size_t max_mode; /* last index in exynos_usi_modes[] */ + size_t num_clks; /* number of clocks to assert */ + const char * const *clk_names; /* clock names to assert */ +}; + +struct exynos_usi { + struct device *dev; + void __iomem *regs; /* USI register map */ + struct clk_bulk_data *clks; /* USI clocks */ + + size_t mode; /* current USI SW_CONF mode index */ + bool clkreq_on; /* always provide clock to IP */ + + /* System Register */ + struct regmap *sysreg; /* System Register map */ + unsigned int sw_conf; /* SW_CONF register offset in sysreg */ + + const struct exynos_usi_variant *data; +}; + +struct exynos_usi_mode { + const char *name; /* mode name */ + unsigned int val; /* mode register value */ +}; + +static const struct exynos_usi_mode exynos_usi_modes[] = { + [USI_V2_NONE] = { .name = "none", .val = USI_V2_SW_CONF_NONE }, + [USI_V2_UART] = { .name = "uart", .val = USI_V2_SW_CONF_UART }, + [USI_V2_SPI] = { .name = "spi", .val = USI_V2_SW_CONF_SPI }, + [USI_V2_I2C] = { .name = "i2c", .val = USI_V2_SW_CONF_I2C }, +}; + +static const char * const exynos850_usi_clk_names[] = { "pclk", "ipclk" }; +static const struct exynos_usi_variant exynos850_usi_data = { + .ver = USI_VER2, + .sw_conf_mask = USI_V2_SW_CONF_MASK, + .min_mode = USI_V2_NONE, + .max_mode = USI_V2_I2C, + .num_clks = ARRAY_SIZE(exynos850_usi_clk_names), + .clk_names = exynos850_usi_clk_names, +}; + +static const struct of_device_id exynos_usi_dt_match[] = { + { + .compatible = "samsung,exynos850-usi", + .data = &exynos850_usi_data, + }, + { } /* sentinel */ +}; +MODULE_DEVICE_TABLE(of, exynos_usi_dt_match); + +/** + * exynos_usi_set_sw_conf - Set USI block configuration mode + * @usi: USI driver object + * @mode: Mode index + * + * Select underlying serial protocol (UART/SPI/I2C) in USI IP-core. + * + * Return: 0 on success, or negative error code on failure. + */ +static int exynos_usi_set_sw_conf(struct exynos_usi *usi, size_t mode) +{ + unsigned int val; + int ret; + + if (mode < usi->data->min_mode || mode > usi->data->max_mode) + return -EINVAL; + + val = exynos_usi_modes[mode].val; + ret = regmap_update_bits(usi->sysreg, usi->sw_conf, + usi->data->sw_conf_mask, val); + if (ret) + return ret; + + usi->mode = mode; + dev_dbg(usi->dev, "protocol: %s\n", exynos_usi_modes[usi->mode].name); + + return 0; +} + +/** + * exynos_usi_enable - Initialize USI block + * @usi: USI driver object + * + * USI IP-core start state is "reset" (on startup and after CPU resume). This + * routine enables the USI block by clearing the reset flag. It also configures + * HWACG behavior (needed e.g. for UART Rx). It should be performed before + * underlying protocol becomes functional. + * + * Return: 0 on success, or negative error code on failure. + */ +static int exynos_usi_enable(const struct exynos_usi *usi) +{ + u32 val; + int ret; + + ret = clk_bulk_prepare_enable(usi->data->num_clks, usi->clks); + if (ret) + return ret; + + /* Enable USI block */ + val = readl(usi->regs + USI_CON); + val &= ~USI_CON_RESET; + writel(val, usi->regs + USI_CON); + udelay(1); + + /* Continuously provide the clock to USI IP w/o gating */ + if (usi->clkreq_on) { + val = readl(usi->regs + USI_OPTION); + val &= ~USI_OPTION_CLKSTOP_ON; + val |= USI_OPTION_CLKREQ_ON; + writel(val, usi->regs + USI_OPTION); + } + + clk_bulk_disable_unprepare(usi->data->num_clks, usi->clks); + + return ret; +} + +static int exynos_usi_configure(struct exynos_usi *usi) +{ + int ret; + + ret = exynos_usi_set_sw_conf(usi, usi->mode); + if (ret) + return ret; + + if (usi->data->ver == USI_VER2) + return exynos_usi_enable(usi); + + return 0; +} + +static int exynos_usi_parse_dt(struct device_node *np, struct exynos_usi *usi) +{ + int ret; + u32 mode; + + ret = of_property_read_u32(np, "samsung,mode", &mode); + if (ret) + return ret; + if (mode < usi->data->min_mode || mode > usi->data->max_mode) + return -EINVAL; + usi->mode = mode; + + usi->sysreg = syscon_regmap_lookup_by_phandle(np, "samsung,sysreg"); + if (IS_ERR(usi->sysreg)) + return PTR_ERR(usi->sysreg); + + ret = of_property_read_u32_index(np, "samsung,sysreg", 1, + &usi->sw_conf); + if (ret) + return ret; + + usi->clkreq_on = of_property_read_bool(np, "samsung,clkreq-on"); + + return 0; +} + +static int exynos_usi_get_clocks(struct exynos_usi *usi) +{ + const size_t num = usi->data->num_clks; + struct device *dev = usi->dev; + size_t i; + + if (num == 0) + return 0; + + usi->clks = devm_kcalloc(dev, num, sizeof(*usi->clks), GFP_KERNEL); + if (!usi->clks) + return -ENOMEM; + + for (i = 0; i < num; ++i) + usi->clks[i].id = usi->data->clk_names[i]; + + return devm_clk_bulk_get(dev, num, usi->clks); +} + +static int exynos_usi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct exynos_usi *usi; + int ret; + + usi = devm_kzalloc(dev, sizeof(*usi), GFP_KERNEL); + if (!usi) + return -ENOMEM; + + usi->dev = dev; + platform_set_drvdata(pdev, usi); + + usi->data = of_device_get_match_data(dev); + if (!usi->data) + return -EINVAL; + + ret = exynos_usi_parse_dt(np, usi); + if (ret) + return ret; + + ret = exynos_usi_get_clocks(usi); + if (ret) + return ret; + + if (usi->data->ver == USI_VER2) { + usi->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(usi->regs)) + return PTR_ERR(usi->regs); + } + + ret = exynos_usi_configure(usi); + if (ret) + return ret; + + /* Make it possible to embed protocol nodes into USI np */ + return of_platform_populate(np, NULL, NULL, dev); +} + +static int __maybe_unused exynos_usi_resume_noirq(struct device *dev) +{ + struct exynos_usi *usi = dev_get_drvdata(dev); + + return exynos_usi_configure(usi); +} + +static const struct dev_pm_ops exynos_usi_pm = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, exynos_usi_resume_noirq) +}; + +static struct platform_driver exynos_usi_driver = { + .driver = { + .name = "exynos-usi", + .pm = &exynos_usi_pm, + .of_match_table = exynos_usi_dt_match, + }, + .probe = exynos_usi_probe, +}; +module_platform_driver(exynos_usi_driver); + +MODULE_DESCRIPTION("Samsung USI driver"); +MODULE_AUTHOR("Sam Protsenko "); +MODULE_LICENSE("GPL"); From 96c8bddb6cdefb029cc7e411a5ee2183477368ea Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 13 Dec 2021 12:20:57 +0100 Subject: [PATCH 71/94] dt-bindings: soc: samsung: keep SoC driver bindings together Recently added Samsung Exynos USI driver devicetree bindings were added under ../bindings/soc/samsung/exynos-usi.yaml, so move there also two other bindings for Exynos SoC drivers: the PMU and ChipID. Update Samsung Exynos MAINTAINERS entry to include this new path. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Sam Protsenko Link: https://lore.kernel.org/r/20211213112057.16709-1-krzysztof.kozlowski@canonical.com --- .../devicetree/bindings/{arm => soc}/samsung/exynos-chipid.yaml | 2 +- .../{arm/samsung/pmu.yaml => soc/samsung/exynos-pmu.yaml} | 2 +- MAINTAINERS | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) rename Documentation/devicetree/bindings/{arm => soc}/samsung/exynos-chipid.yaml (93%) rename Documentation/devicetree/bindings/{arm/samsung/pmu.yaml => soc/samsung/exynos-pmu.yaml} (97%) diff --git a/Documentation/devicetree/bindings/arm/samsung/exynos-chipid.yaml b/Documentation/devicetree/bindings/soc/samsung/exynos-chipid.yaml similarity index 93% rename from Documentation/devicetree/bindings/arm/samsung/exynos-chipid.yaml rename to Documentation/devicetree/bindings/soc/samsung/exynos-chipid.yaml index bfc352a2fdd6..4bb8efb83ac1 100644 --- a/Documentation/devicetree/bindings/arm/samsung/exynos-chipid.yaml +++ b/Documentation/devicetree/bindings/soc/samsung/exynos-chipid.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: http://devicetree.org/schemas/arm/samsung/exynos-chipid.yaml# +$id: http://devicetree.org/schemas/soc/samsung/exynos-chipid.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Samsung Exynos SoC series Chipid driver diff --git a/Documentation/devicetree/bindings/arm/samsung/pmu.yaml b/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml similarity index 97% rename from Documentation/devicetree/bindings/arm/samsung/pmu.yaml rename to Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml index 0bbd89265b28..c30a6437030d 100644 --- a/Documentation/devicetree/bindings/arm/samsung/pmu.yaml +++ b/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: http://devicetree.org/schemas/arm/samsung/pmu.yaml# +$id: http://devicetree.org/schemas/soc/samsung/exynos-pmu.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Samsung Exynos SoC series Power Management Unit (PMU) diff --git a/MAINTAINERS b/MAINTAINERS index 7a2345ce8521..bdab70cad197 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2542,6 +2542,7 @@ Q: https://patchwork.kernel.org/project/linux-samsung-soc/list/ F: Documentation/arm/samsung/ F: Documentation/devicetree/bindings/arm/samsung/ F: Documentation/devicetree/bindings/power/pd-samsung.yaml +F: Documentation/devicetree/bindings/soc/samsung/ F: arch/arm/boot/dts/exynos* F: arch/arm/boot/dts/s3c* F: arch/arm/boot/dts/s5p* From 2e8f2d3a691e521fa49f825010759192e86cf102 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Fri, 19 Nov 2021 22:39:51 +0100 Subject: [PATCH 72/94] dt-bindings: soc: qcom: stats: Document compatibles with fixed offset Document additional compatibles that can be used similarly to qcom,rpm-stats for older RPM firmware versions that have the sleep stats at a fixed offset rather than a dynamic one. The exact offset might vary depending on the SoC so use SoC-specific compatible names to avoid confusion. Cc: Maulik Shah Signed-off-by: Stephan Gerhold Acked-by: Rob Herring Reviewed-by: Bjorn Andersson Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211119213953.31970-2-stephan@gerhold.net --- Documentation/devicetree/bindings/soc/qcom/qcom-stats.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom-stats.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom-stats.yaml index 99dff7d73b7e..473adca4e973 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom-stats.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom-stats.yaml @@ -21,6 +21,11 @@ properties: enum: - qcom,rpmh-stats - qcom,rpm-stats + # For older RPM firmware versions with fixed offset for the sleep stats + - qcom,apq8084-rpm-stats + - qcom,msm8226-rpm-stats + - qcom,msm8916-rpm-stats + - qcom,msm8974-rpm-stats reg: maxItems: 1 From 708dbf4490c8ddf55d79b9f3855219b92d108760 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Fri, 19 Nov 2021 22:39:52 +0100 Subject: [PATCH 73/94] soc: qcom: stats: Add fixed sleep stats offset for older RPM firmwares Not all RPM firmware versions have the dynamic sleep stats offset available. Most older versions use a fixed offset of 0xdba0. Add support for this using new SoC-specific compatibles for APQ8084, MSM8226, MSM8916 and MSM8974. Even older SoCs seem to use a different offset and stats format. If needed those could be supported in the future by adding separate compatibles for those with a different stats_config. Cc: Maulik Shah Signed-off-by: Stephan Gerhold Reviewed-by: Bjorn Andersson Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211119213953.31970-3-stephan@gerhold.net --- drivers/soc/qcom/qcom_stats.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/soc/qcom/qcom_stats.c b/drivers/soc/qcom/qcom_stats.c index 131d24caabf8..d6bfd1bbdc2a 100644 --- a/drivers/soc/qcom/qcom_stats.c +++ b/drivers/soc/qcom/qcom_stats.c @@ -237,6 +237,15 @@ static const struct stats_config rpm_data = { .subsystem_stats_in_smem = false, }; +/* Older RPM firmwares have the stats at a fixed offset instead */ +static const struct stats_config rpm_data_dba0 = { + .stats_offset = 0xdba0, + .num_records = 2, + .appended_stats_avail = true, + .dynamic_offset = false, + .subsystem_stats_in_smem = false, +}; + static const struct stats_config rpmh_data = { .stats_offset = 0x48, .num_records = 3, @@ -246,6 +255,10 @@ static const struct stats_config rpmh_data = { }; static const struct of_device_id qcom_stats_table[] = { + { .compatible = "qcom,apq8084-rpm-stats", .data = &rpm_data_dba0 }, + { .compatible = "qcom,msm8226-rpm-stats", .data = &rpm_data_dba0 }, + { .compatible = "qcom,msm8916-rpm-stats", .data = &rpm_data_dba0 }, + { .compatible = "qcom,msm8974-rpm-stats", .data = &rpm_data_dba0 }, { .compatible = "qcom,rpm-stats", .data = &rpm_data }, { .compatible = "qcom,rpmh-stats", .data = &rpmh_data }, { } From 6fc61c39ee1adb5f4115d288c876772fcd8b6979 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Sun, 21 Nov 2021 01:20:46 +0100 Subject: [PATCH 74/94] soc: qcom: llcc: Add configuration data for SM8350 Add LLCC configuration data for SM8350 SoC. Signed-off-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211121002050.36977-2-konrad.dybcio@somainline.org --- drivers/soc/qcom/llcc-qcom.c | 28 ++++++++++++++++++++++++++++ include/linux/soc/qcom/llcc-qcom.h | 3 +++ 2 files changed, 31 insertions(+) diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index 6bf2f1d1f2c5..ec52f29c8867 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -195,6 +195,28 @@ static const struct llcc_slice_config sm8250_data[] = { { LLCC_WRCACHE, 31, 256, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 }, }; +static const struct llcc_slice_config sm8350_data[] = { + { LLCC_CPUSS, 1, 3072, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 1 }, + { LLCC_VIDSC0, 2, 512, 3, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 }, + { LLCC_AUDIO, 6, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 0, 0 }, + { LLCC_MDMHPGRW, 7, 1024, 3, 0, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 }, + { LLCC_MODHW, 9, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 }, + { LLCC_CMPT, 10, 3072, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 }, + { LLCC_GPUHTW, 11, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 }, + { LLCC_GPU, 12, 1024, 1, 0, 0xfff, 0x0, 0, 0, 0, 1, 1, 0 }, + { LLCC_MMUHWT, 13, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 0, 1 }, + { LLCC_DISP, 16, 3072, 2, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 }, + { LLCC_MDMPNG, 21, 1024, 0, 1, 0xf, 0x0, 0, 0, 0, 0, 1, 0 }, + { LLCC_AUDHW, 22, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 }, + { LLCC_CVP, 28, 512, 3, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 }, + { LLCC_MODPE, 29, 256, 1, 1, 0xf, 0x0, 0, 0, 0, 0, 1, 0 }, + { LLCC_APTCM, 30, 1024, 3, 1, 0x0, 0x1, 1, 0, 0, 0, 1, 0 }, + { LLCC_WRCACHE, 31, 512, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 0, 1 }, + { LLCC_CVPFW, 17, 512, 1, 0, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 }, + { LLCC_CPUSS1, 3, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 }, + { LLCC_CPUHWT, 5, 512, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 0, 1 }, +}; + static const struct qcom_llcc_config sc7180_cfg = { .sct_data = sc7180_data, .size = ARRAY_SIZE(sc7180_data), @@ -228,6 +250,11 @@ static const struct qcom_llcc_config sm8250_cfg = { .size = ARRAY_SIZE(sm8250_data), }; +static const struct qcom_llcc_config sm8350_cfg = { + .sct_data = sm8350_data, + .size = ARRAY_SIZE(sm8350_data), +}; + static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER; /** @@ -644,6 +671,7 @@ static const struct of_device_id qcom_llcc_of_match[] = { { .compatible = "qcom,sm6350-llcc", .data = &sm6350_cfg }, { .compatible = "qcom,sm8150-llcc", .data = &sm8150_cfg }, { .compatible = "qcom,sm8250-llcc", .data = &sm8250_cfg }, + { .compatible = "qcom,sm8350-llcc", .data = &sm8350_cfg }, { } }; diff --git a/include/linux/soc/qcom/llcc-qcom.h b/include/linux/soc/qcom/llcc-qcom.h index 437c9df13229..9e8fd92c96b7 100644 --- a/include/linux/soc/qcom/llcc-qcom.h +++ b/include/linux/soc/qcom/llcc-qcom.h @@ -33,6 +33,9 @@ #define LLCC_MODPE 29 #define LLCC_APTCM 30 #define LLCC_WRCACHE 31 +#define LLCC_CVPFW 32 +#define LLCC_CPUSS1 33 +#define LLCC_CPUHWT 36 /** * struct llcc_slice_desc - Cache slice descriptor From 92c550f9ffd2884bb5def52b5c0485a35e452784 Mon Sep 17 00:00:00 2001 From: Changcheng Deng Date: Thu, 25 Nov 2021 01:43:11 +0000 Subject: [PATCH 75/94] PM: AVS: qcom-cpr: Use div64_ul instead of do_div do_div() does a 64-by-32 division. Here the divisor is an unsigned long which on some platforms is 64 bit wide. So use div64_ul instead of do_div to avoid a possible truncation. Reported-by: Zeal Robot Signed-off-by: Changcheng Deng Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211125014311.45942-1-deng.changcheng@zte.com.cn --- drivers/soc/qcom/cpr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/qcom/cpr.c b/drivers/soc/qcom/cpr.c index 1d818a8ba208..e9b854ed1bdf 100644 --- a/drivers/soc/qcom/cpr.c +++ b/drivers/soc/qcom/cpr.c @@ -1010,7 +1010,7 @@ static int cpr_interpolate(const struct corner *corner, int step_volt, return corner->uV; temp = f_diff * (uV_high - uV_low); - do_div(temp, f_high - f_low); + temp = div64_ul(temp, f_high - f_low); /* * max_volt_scale has units of uV/MHz while freq values From 3925b909f75800e69bd1a65130a0440191a9e60a Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Sun, 28 Nov 2021 22:03:17 +0100 Subject: [PATCH 76/94] soc: qcom: aoss: constify static struct thermal_cooling_device_ops The only usage of qmp_cooling_device_ops is to pass its address to devm_thermal_of_cooling_device_register() which takes a pointer to const struct thermal_cooling_device_ops as argument. Make it const to allow the compiler to put it in read-only memory. Signed-off-by: Rikard Falkeborn Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211128210317.25504-1-rikard.falkeborn@gmail.com --- drivers/soc/qcom/qcom_aoss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c index 34acf58bbb0d..cbe5e39fdaeb 100644 --- a/drivers/soc/qcom/qcom_aoss.c +++ b/drivers/soc/qcom/qcom_aoss.c @@ -352,7 +352,7 @@ static int qmp_cdev_set_cur_state(struct thermal_cooling_device *cdev, return ret; } -static struct thermal_cooling_device_ops qmp_cooling_device_ops = { +static const struct thermal_cooling_device_ops qmp_cooling_device_ops = { .get_max_state = qmp_cdev_get_max_state, .get_cur_state = qmp_cdev_get_cur_state, .set_cur_state = qmp_cdev_set_cur_state, From 8712107740ad272bd89c873ec850146ac3d8832a Mon Sep 17 00:00:00 2001 From: Martin Botka Date: Tue, 30 Nov 2021 22:23:29 +0100 Subject: [PATCH 77/94] dt-bindings: qcom-rpmpd: Add sm6125 power domains Add dt-bindings for sm6125 SoC RPM Power Domains Signed-off-by: Martin Botka [bjorn: Added compatible to binding as well] Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211130212332.25401-1-martin.botka@somainline.org --- Documentation/devicetree/bindings/power/qcom,rpmpd.yaml | 1 + include/dt-bindings/power/qcom-rpmpd.h | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml index a6d51d51d2c7..8b5d641104b1 100644 --- a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml +++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml @@ -33,6 +33,7 @@ properties: - qcom,sdm845-rpmhpd - qcom,sdx55-rpmhpd - qcom,sm6115-rpmpd + - qcom,sm6125-rpmpd - qcom,sm6350-rpmhpd - qcom,sm8150-rpmhpd - qcom,sm8250-rpmhpd diff --git a/include/dt-bindings/power/qcom-rpmpd.h b/include/dt-bindings/power/qcom-rpmpd.h index 340b0ffe5eb8..5b64059ef43e 100644 --- a/include/dt-bindings/power/qcom-rpmpd.h +++ b/include/dt-bindings/power/qcom-rpmpd.h @@ -219,6 +219,14 @@ #define SM6115_VDD_LPI_CX 6 #define SM6115_VDD_LPI_MX 7 +/* SM6125 Power Domains */ +#define SM6125_VDDCX 0 +#define SM6125_VDDCX_AO 1 +#define SM6125_VDDCX_VFL 2 +#define SM6125_VDDMX 3 +#define SM6125_VDDMX_AO 4 +#define SM6125_VDDMX_VFL 5 + /* QCM2290 Power Domains */ #define QCM2290_VDDCX 0 #define QCM2290_VDDCX_AO 1 From 82c6bf7585cdb5e1bc8f8b1a2925ad692585e1f4 Mon Sep 17 00:00:00 2001 From: Martin Botka Date: Tue, 30 Nov 2021 22:23:30 +0100 Subject: [PATCH 78/94] soc: qcom: rpmpd: Add support for sm6125 Add RPM power domains located in Qualcomm SM6125 SoC. Signed-off-by: Martin Botka Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211130212332.25401-2-martin.botka@somainline.org --- drivers/soc/qcom/rpmpd.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/soc/qcom/rpmpd.c b/drivers/soc/qcom/rpmpd.c index 36b2381039e6..0a8d8d24bfb7 100644 --- a/drivers/soc/qcom/rpmpd.c +++ b/drivers/soc/qcom/rpmpd.c @@ -395,6 +395,28 @@ static const struct rpmpd_desc sm6115_desc = { .max_state = RPM_SMD_LEVEL_TURBO_NO_CPR, }; +/* sm6125 RPM Power domains */ +DEFINE_RPMPD_PAIR(sm6125, vddcx, vddcx_ao, RWCX, LEVEL, 0); +DEFINE_RPMPD_VFL(sm6125, vddcx_vfl, RWCX, 0); + +DEFINE_RPMPD_PAIR(sm6125, vddmx, vddmx_ao, RWMX, LEVEL, 0); +DEFINE_RPMPD_VFL(sm6125, vddmx_vfl, RWMX, 0); + +static struct rpmpd *sm6125_rpmpds[] = { + [SM6125_VDDCX] = &sm6125_vddcx, + [SM6125_VDDCX_AO] = &sm6125_vddcx_ao, + [SM6125_VDDCX_VFL] = &sm6125_vddcx_vfl, + [SM6125_VDDMX] = &sm6125_vddmx, + [SM6125_VDDMX_AO] = &sm6125_vddmx_ao, + [SM6125_VDDMX_VFL] = &sm6125_vddmx_vfl, +}; + +static const struct rpmpd_desc sm6125_desc = { + .rpmpds = sm6125_rpmpds, + .num_pds = ARRAY_SIZE(sm6125_rpmpds), + .max_state = RPM_SMD_LEVEL_BINNING, +}; + static struct rpmpd *qcm2290_rpmpds[] = { [QCM2290_VDDCX] = &sm6115_vddcx, [QCM2290_VDDCX_AO] = &sm6115_vddcx_ao, @@ -425,6 +447,7 @@ static const struct of_device_id rpmpd_match_table[] = { { .compatible = "qcom,qcs404-rpmpd", .data = &qcs404_desc }, { .compatible = "qcom,sdm660-rpmpd", .data = &sdm660_desc }, { .compatible = "qcom,sm6115-rpmpd", .data = &sm6115_desc }, + { .compatible = "qcom,sm6125-rpmpd", .data = &sm6125_desc }, { } }; MODULE_DEVICE_TABLE(of, rpmpd_match_table); From 71ca61c4d009ef914bab95b875ef4411c6cb7b2f Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 1 Dec 2021 12:57:39 +0530 Subject: [PATCH 79/94] dt-bindings: arm: cpus: Add kryo780 compatible Kryo780 is found in SM8450, so add it to the list of cpu compatibles Signed-off-by: Vinod Koul Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211201072745.3969077-2-vkoul@kernel.org --- Documentation/devicetree/bindings/arm/cpus.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/arm/cpus.yaml b/Documentation/devicetree/bindings/arm/cpus.yaml index f2ab6423b4af..dfa28e3525cb 100644 --- a/Documentation/devicetree/bindings/arm/cpus.yaml +++ b/Documentation/devicetree/bindings/arm/cpus.yaml @@ -174,6 +174,7 @@ properties: - qcom,kryo560 - qcom,kryo570 - qcom,kryo685 + - qcom,kryo780 - qcom,scorpion enable-method: From 028e4c664906331fb6623982b8486cd86efa14b3 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 1 Dec 2021 12:57:40 +0530 Subject: [PATCH 80/94] dt-bindings: firmware: scm: Add SM8450 compatible Add compatible for SM8450 SoCs. Signed-off-by: Vinod Koul Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211201072745.3969077-3-vkoul@kernel.org --- Documentation/devicetree/bindings/firmware/qcom,scm.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/firmware/qcom,scm.txt b/Documentation/devicetree/bindings/firmware/qcom,scm.txt index d7e3cda8924e..548733388497 100644 --- a/Documentation/devicetree/bindings/firmware/qcom,scm.txt +++ b/Documentation/devicetree/bindings/firmware/qcom,scm.txt @@ -29,6 +29,7 @@ Required properties: * "qcom,scm-sm8150" * "qcom,scm-sm8250" * "qcom,scm-sm8350" + * "qcom,scm-sm8450" and: * "qcom,scm" - clocks: Specifies clocks needed by the SCM interface, if any: From 0e57fe4d11e59a9625b30be18b84aa69e32f0c37 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 1 Dec 2021 12:57:41 +0530 Subject: [PATCH 81/94] dt-bindings: arm: qcom: Document SM8450 SoC and boards Document the SM8450 SoC binding and also the boards using it. Signed-off-by: Vinod Koul Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211201072745.3969077-4-vkoul@kernel.org --- Documentation/devicetree/bindings/arm/qcom.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/qcom.yaml b/Documentation/devicetree/bindings/arm/qcom.yaml index c8808e0f9e64..29a0bd3adac2 100644 --- a/Documentation/devicetree/bindings/arm/qcom.yaml +++ b/Documentation/devicetree/bindings/arm/qcom.yaml @@ -50,6 +50,7 @@ description: | sm8150 sm8250 sm8350 + sm8450 The 'board' element must be one of the following strings: @@ -257,6 +258,11 @@ properties: - qcom,sm8350-mtp - const: qcom,sm8350 + - items: + - enum: + - qcom,sm8450-qrd + - const: qcom,sm8450 + additionalProperties: true ... From aa9fc2c7e5777ae44a2f03e041d3f4b3e4a02860 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 1 Dec 2021 12:57:42 +0530 Subject: [PATCH 82/94] soc: qcom: smem: Update max processor count Update max processor count to reflect the number of co-processors on SM8450 SoCs. Signed-off-by: Dmitry Baryshkov Signed-off-by: Vinod Koul Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211201072745.3969077-5-vkoul@kernel.org --- drivers/soc/qcom/smem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index c7e519bfdc8a..e2057d8f1eff 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -85,7 +85,7 @@ #define SMEM_GLOBAL_HOST 0xfffe /* Max number of processors/hosts in a system */ -#define SMEM_HOST_COUNT 14 +#define SMEM_HOST_COUNT 15 /** * struct smem_proc_comm - proc_comm communication struct (legacy) From 22c755708c2395c58eb7670357baa110ba1ca2e0 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 1 Dec 2021 12:57:43 +0530 Subject: [PATCH 83/94] dt-bindings: power: rpmpd: Add SM8450 to rpmpd binding Add compatible and constants for the power domains exposed by the RPMH in the Qualcomm SM8450 platform. Signed-off-by: Dmitry Baryshkov Signed-off-by: Vinod Koul Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211201072745.3969077-6-vkoul@kernel.org --- .../devicetree/bindings/power/qcom,rpmpd.yaml | 1 + include/dt-bindings/power/qcom-rpmpd.h | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml index 8b5d641104b1..f48bc41d81ec 100644 --- a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml +++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml @@ -38,6 +38,7 @@ properties: - qcom,sm8150-rpmhpd - qcom,sm8250-rpmhpd - qcom,sm8350-rpmhpd + - qcom,sm8450-rpmhpd '#power-domain-cells': const: 1 diff --git a/include/dt-bindings/power/qcom-rpmpd.h b/include/dt-bindings/power/qcom-rpmpd.h index 5b64059ef43e..edfc1ff2acb3 100644 --- a/include/dt-bindings/power/qcom-rpmpd.h +++ b/include/dt-bindings/power/qcom-rpmpd.h @@ -68,6 +68,21 @@ #define SM8350_MXC_AO 11 #define SM8350_MSS 12 +/* SM8450 Power Domain Indexes */ +#define SM8450_CX 0 +#define SM8450_CX_AO 1 +#define SM8450_EBI 2 +#define SM8450_GFX 3 +#define SM8450_LCX 4 +#define SM8450_LMX 5 +#define SM8450_MMCX 6 +#define SM8450_MMCX_AO 7 +#define SM8450_MX 8 +#define SM8450_MX_AO 9 +#define SM8450_MXC 10 +#define SM8450_MXC_AO 11 +#define SM8450_MSS 12 + /* SC7180 Power Domain Indexes */ #define SC7180_CX 0 #define SC7180_CX_AO 1 From 5d12289516d943dd6f348826a9f56f84199af0b1 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 1 Dec 2021 12:57:44 +0530 Subject: [PATCH 84/94] soc: qcom: rpmhpd: Add SM8450 power domains Add the power domains exposed by RPMH in the Qualcomm SM8450 platform. Unlike previous generations CX domain is not a child of MX domain. Signed-off-by: Dmitry Baryshkov Signed-off-by: Vinod Koul Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211201072745.3969077-7-vkoul@kernel.org --- drivers/soc/qcom/rpmhpd.c | 52 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c index 1118345d8824..e7e150ce1b1a 100644 --- a/drivers/soc/qcom/rpmhpd.c +++ b/drivers/soc/qcom/rpmhpd.c @@ -253,6 +253,57 @@ static const struct rpmhpd_desc sm8350_desc = { .num_pds = ARRAY_SIZE(sm8350_rpmhpds), }; +/* SM8450 RPMH powerdomains */ +static struct rpmhpd sm8450_cx_ao; +static struct rpmhpd sm8450_cx = { + .pd = { .name = "cx", }, + .peer = &sm8450_cx_ao, + .res_name = "cx.lvl", +}; + +static struct rpmhpd sm8450_cx_ao = { + .pd = { .name = "cx_ao", }, + .active_only = true, + .res_name = "cx.lvl", +}; + +static struct rpmhpd sm8450_mmcx_ao; +static struct rpmhpd sm8450_mmcx = { + .pd = { .name = "mmcx", }, + .peer = &sm8450_mmcx_ao, + .parent = &sm8450_cx.pd, + .res_name = "mmcx.lvl", +}; + +static struct rpmhpd sm8450_mmcx_ao = { + .pd = { .name = "mmcx_ao", }, + .active_only = true, + .peer = &sm8450_mmcx, + .parent = &sm8450_cx_ao.pd, + .res_name = "mmcx.lvl", +}; + +static struct rpmhpd *sm8450_rpmhpds[] = { + [SM8450_CX] = &sm8450_cx, + [SM8450_CX_AO] = &sm8450_cx_ao, + [SM8450_EBI] = &sdm845_ebi, + [SM8450_GFX] = &sdm845_gfx, + [SM8450_LCX] = &sdm845_lcx, + [SM8450_LMX] = &sdm845_lmx, + [SM8450_MMCX] = &sm8450_mmcx, + [SM8450_MMCX_AO] = &sm8450_mmcx_ao, + [SM8450_MX] = &sdm845_mx, + [SM8450_MX_AO] = &sdm845_mx_ao, + [SM8450_MXC] = &sm8350_mxc, + [SM8450_MXC_AO] = &sm8350_mxc_ao, + [SM8450_MSS] = &sdm845_mss, +}; + +static const struct rpmhpd_desc sm8450_desc = { + .rpmhpds = sm8450_rpmhpds, + .num_pds = ARRAY_SIZE(sm8450_rpmhpds), +}; + /* SC7180 RPMH powerdomains */ static struct rpmhpd *sc7180_rpmhpds[] = { [SC7180_CX] = &sdm845_cx, @@ -318,6 +369,7 @@ static const struct of_device_id rpmhpd_match_table[] = { { .compatible = "qcom,sm8150-rpmhpd", .data = &sm8150_desc }, { .compatible = "qcom,sm8250-rpmhpd", .data = &sm8250_desc }, { .compatible = "qcom,sm8350-rpmhpd", .data = &sm8350_desc }, + { .compatible = "qcom,sm8450-rpmhpd", .data = &sm8450_desc }, { } }; MODULE_DEVICE_TABLE(of, rpmhpd_match_table); From 9e4cdb4ca7e1fc4cca021cd8fefc4488c6c63bfa Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 1 Dec 2021 12:57:45 +0530 Subject: [PATCH 85/94] soc: qcom: socinfo: add SM8450 ID Add the ID for the Qualcomm SM8450 SoC. Signed-off-by: Dmitry Baryshkov Signed-off-by: Vinod Koul Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211201072745.3969077-8-vkoul@kernel.org --- drivers/soc/qcom/socinfo.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 9a0eb59405e8..113dc57b74c3 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -315,6 +315,7 @@ static const struct soc_id soc_id[] = { { 425, "SC7180" }, { 453, "IPQ6005" }, { 455, "QRB5165" }, + { 457, "SM8450" }, }; static const char *socinfo_machine(struct device *dev, unsigned int id) From 84e3b09292a481df5afe8a6b6fff8ded746506e3 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Thu, 9 Dec 2021 21:01:54 +0530 Subject: [PATCH 86/94] soc: qcom: rpmhpd: sm8450: Add the missing .peer for sm8450_cx_ao sm8450_cx and sm8450_cx_ao should be peers of each other, add the missing .peer entry for sm8450_cx_ao Cc: Dmitry Baryshkov Cc: Vinod Koul Signed-off-by: Rajendra Nayak Reviewed-by: Dmitry Baryshkov Reviewed-by: Bjorn Andersson Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/1639063917-9011-2-git-send-email-quic_rjendra@quicinc.com --- drivers/soc/qcom/rpmhpd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c index e7e150ce1b1a..2032cf355e52 100644 --- a/drivers/soc/qcom/rpmhpd.c +++ b/drivers/soc/qcom/rpmhpd.c @@ -264,6 +264,7 @@ static struct rpmhpd sm8450_cx = { static struct rpmhpd sm8450_cx_ao = { .pd = { .name = "cx_ao", }, .active_only = true, + .peer = &sm8450_cx, .res_name = "cx.lvl", }; From 09bb67c104b5d27718a7191eff057ee7a45fc035 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Thu, 9 Dec 2021 21:01:55 +0530 Subject: [PATCH 87/94] soc: qcom: rpmhpd: Rename rpmhpd struct names The rpmhpd structs were named with a SoC-name prefix, but then they got reused across multiple SoC families making things confusing. Rename all the struct names to remove SoC-name prefixes. While we do this we end up with some power-domains without parents, and some with and at times different parents across SoCs. use a _w__parent suffix for such cases. No functional change as part of this patch. Signed-off-by: Rajendra Nayak Reviewed-by: Matthias Kaehlcke Reviewed-by: Bjorn Andersson Reviewed-by: Stephen Boyd Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/1639063917-9011-3-git-send-email-quic_rjendra@quicinc.com --- drivers/soc/qcom/rpmhpd.c | 341 +++++++++++++++++++------------------- 1 file changed, 171 insertions(+), 170 deletions(-) diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c index 2032cf355e52..a12db32433c5 100644 --- a/drivers/soc/qcom/rpmhpd.c +++ b/drivers/soc/qcom/rpmhpd.c @@ -63,73 +63,132 @@ struct rpmhpd_desc { static DEFINE_MUTEX(rpmhpd_lock); -/* SDM845 RPMH powerdomains */ +/* RPMH powerdomains */ -static struct rpmhpd sdm845_ebi = { +static struct rpmhpd ebi = { .pd = { .name = "ebi", }, .res_name = "ebi.lvl", }; -static struct rpmhpd sdm845_lmx = { +static struct rpmhpd lmx = { .pd = { .name = "lmx", }, .res_name = "lmx.lvl", }; -static struct rpmhpd sdm845_lcx = { +static struct rpmhpd lcx = { .pd = { .name = "lcx", }, .res_name = "lcx.lvl", }; -static struct rpmhpd sdm845_gfx = { +static struct rpmhpd gfx = { .pd = { .name = "gfx", }, .res_name = "gfx.lvl", }; -static struct rpmhpd sdm845_mss = { +static struct rpmhpd mss = { .pd = { .name = "mss", }, .res_name = "mss.lvl", }; -static struct rpmhpd sdm845_mx_ao; -static struct rpmhpd sdm845_mx = { +static struct rpmhpd mx_ao; +static struct rpmhpd mx = { .pd = { .name = "mx", }, - .peer = &sdm845_mx_ao, + .peer = &mx_ao, .res_name = "mx.lvl", }; -static struct rpmhpd sdm845_mx_ao = { +static struct rpmhpd mx_ao = { .pd = { .name = "mx_ao", }, .active_only = true, - .peer = &sdm845_mx, + .peer = &mx, .res_name = "mx.lvl", }; -static struct rpmhpd sdm845_cx_ao; -static struct rpmhpd sdm845_cx = { +static struct rpmhpd cx_ao; +static struct rpmhpd cx = { .pd = { .name = "cx", }, - .peer = &sdm845_cx_ao, - .parent = &sdm845_mx.pd, + .peer = &cx_ao, .res_name = "cx.lvl", }; -static struct rpmhpd sdm845_cx_ao = { +static struct rpmhpd cx_ao = { .pd = { .name = "cx_ao", }, .active_only = true, - .peer = &sdm845_cx, - .parent = &sdm845_mx_ao.pd, + .peer = &cx, .res_name = "cx.lvl", }; +static struct rpmhpd cx_ao_w_mx_parent; +static struct rpmhpd cx_w_mx_parent = { + .pd = { .name = "cx", }, + .peer = &cx_ao_w_mx_parent, + .parent = &mx.pd, + .res_name = "cx.lvl", +}; + +static struct rpmhpd cx_ao_w_mx_parent = { + .pd = { .name = "cx_ao", }, + .active_only = true, + .peer = &cx_w_mx_parent, + .parent = &mx_ao.pd, + .res_name = "cx.lvl", +}; + +static struct rpmhpd mmcx_ao; +static struct rpmhpd mmcx = { + .pd = { .name = "mmcx", }, + .peer = &mmcx_ao, + .res_name = "mmcx.lvl", +}; + +static struct rpmhpd mmcx_ao = { + .pd = { .name = "mmcx_ao", }, + .active_only = true, + .peer = &mmcx, + .res_name = "mmcx.lvl", +}; + +static struct rpmhpd mmcx_ao_w_cx_parent; +static struct rpmhpd mmcx_w_cx_parent = { + .pd = { .name = "mmcx", }, + .peer = &mmcx_ao_w_cx_parent, + .parent = &cx.pd, + .res_name = "mmcx.lvl", +}; + +static struct rpmhpd mmcx_ao_w_cx_parent = { + .pd = { .name = "mmcx_ao", }, + .active_only = true, + .peer = &mmcx_w_cx_parent, + .parent = &cx_ao.pd, + .res_name = "mmcx.lvl", +}; + +static struct rpmhpd mxc_ao; +static struct rpmhpd mxc = { + .pd = { .name = "mxc", }, + .peer = &mxc_ao, + .res_name = "mxc.lvl", +}; + +static struct rpmhpd mxc_ao = { + .pd = { .name = "mxc_ao", }, + .active_only = true, + .peer = &mxc, + .res_name = "mxc.lvl", +}; + +/* SDM845 RPMH powerdomains */ static struct rpmhpd *sdm845_rpmhpds[] = { - [SDM845_EBI] = &sdm845_ebi, - [SDM845_MX] = &sdm845_mx, - [SDM845_MX_AO] = &sdm845_mx_ao, - [SDM845_CX] = &sdm845_cx, - [SDM845_CX_AO] = &sdm845_cx_ao, - [SDM845_LMX] = &sdm845_lmx, - [SDM845_LCX] = &sdm845_lcx, - [SDM845_GFX] = &sdm845_gfx, - [SDM845_MSS] = &sdm845_mss, + [SDM845_EBI] = &ebi, + [SDM845_MX] = &mx, + [SDM845_MX_AO] = &mx_ao, + [SDM845_CX] = &cx_w_mx_parent, + [SDM845_CX_AO] = &cx_ao_w_mx_parent, + [SDM845_LMX] = &lmx, + [SDM845_LCX] = &lcx, + [SDM845_GFX] = &gfx, + [SDM845_MSS] = &mss, }; static const struct rpmhpd_desc sdm845_desc = { @@ -139,9 +198,9 @@ static const struct rpmhpd_desc sdm845_desc = { /* SDX55 RPMH powerdomains */ static struct rpmhpd *sdx55_rpmhpds[] = { - [SDX55_MSS] = &sdm845_mss, - [SDX55_MX] = &sdm845_mx, - [SDX55_CX] = &sdm845_cx, + [SDX55_MSS] = &mss, + [SDX55_MX] = &mx, + [SDX55_CX] = &cx_w_mx_parent, }; static const struct rpmhpd_desc sdx55_desc = { @@ -151,12 +210,12 @@ static const struct rpmhpd_desc sdx55_desc = { /* SM6350 RPMH powerdomains */ static struct rpmhpd *sm6350_rpmhpds[] = { - [SM6350_CX] = &sdm845_cx, - [SM6350_GFX] = &sdm845_gfx, - [SM6350_LCX] = &sdm845_lcx, - [SM6350_LMX] = &sdm845_lmx, - [SM6350_MSS] = &sdm845_mss, - [SM6350_MX] = &sdm845_mx, + [SM6350_CX] = &cx_w_mx_parent, + [SM6350_GFX] = &gfx, + [SM6350_LCX] = &lcx, + [SM6350_LMX] = &lmx, + [SM6350_MSS] = &mss, + [SM6350_MX] = &mx, }; static const struct rpmhpd_desc sm6350_desc = { @@ -165,33 +224,18 @@ static const struct rpmhpd_desc sm6350_desc = { }; /* SM8150 RPMH powerdomains */ - -static struct rpmhpd sm8150_mmcx_ao; -static struct rpmhpd sm8150_mmcx = { - .pd = { .name = "mmcx", }, - .peer = &sm8150_mmcx_ao, - .res_name = "mmcx.lvl", -}; - -static struct rpmhpd sm8150_mmcx_ao = { - .pd = { .name = "mmcx_ao", }, - .active_only = true, - .peer = &sm8150_mmcx, - .res_name = "mmcx.lvl", -}; - static struct rpmhpd *sm8150_rpmhpds[] = { - [SM8150_MSS] = &sdm845_mss, - [SM8150_EBI] = &sdm845_ebi, - [SM8150_LMX] = &sdm845_lmx, - [SM8150_LCX] = &sdm845_lcx, - [SM8150_GFX] = &sdm845_gfx, - [SM8150_MX] = &sdm845_mx, - [SM8150_MX_AO] = &sdm845_mx_ao, - [SM8150_CX] = &sdm845_cx, - [SM8150_CX_AO] = &sdm845_cx_ao, - [SM8150_MMCX] = &sm8150_mmcx, - [SM8150_MMCX_AO] = &sm8150_mmcx_ao, + [SM8150_MSS] = &mss, + [SM8150_EBI] = &ebi, + [SM8150_LMX] = &lmx, + [SM8150_LCX] = &lcx, + [SM8150_GFX] = &gfx, + [SM8150_MX] = &mx, + [SM8150_MX_AO] = &mx_ao, + [SM8150_CX] = &cx_w_mx_parent, + [SM8150_CX_AO] = &cx_ao_w_mx_parent, + [SM8150_MMCX] = &mmcx, + [SM8150_MMCX_AO] = &mmcx_ao, }; static const struct rpmhpd_desc sm8150_desc = { @@ -199,17 +243,18 @@ static const struct rpmhpd_desc sm8150_desc = { .num_pds = ARRAY_SIZE(sm8150_rpmhpds), }; +/* SM8250 RPMH powerdomains */ 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, + [SM8250_CX] = &cx_w_mx_parent, + [SM8250_CX_AO] = &cx_ao_w_mx_parent, + [SM8250_EBI] = &ebi, + [SM8250_GFX] = &gfx, + [SM8250_LCX] = &lcx, + [SM8250_LMX] = &lmx, + [SM8250_MMCX] = &mmcx, + [SM8250_MMCX_AO] = &mmcx_ao, + [SM8250_MX] = &mx, + [SM8250_MX_AO] = &mx_ao, }; static const struct rpmhpd_desc sm8250_desc = { @@ -218,34 +263,20 @@ static const struct rpmhpd_desc sm8250_desc = { }; /* SM8350 Power domains */ -static struct rpmhpd sm8350_mxc_ao; -static struct rpmhpd sm8350_mxc = { - .pd = { .name = "mxc", }, - .peer = &sm8350_mxc_ao, - .res_name = "mxc.lvl", -}; - -static struct rpmhpd sm8350_mxc_ao = { - .pd = { .name = "mxc_ao", }, - .active_only = true, - .peer = &sm8350_mxc, - .res_name = "mxc.lvl", -}; - static struct rpmhpd *sm8350_rpmhpds[] = { - [SM8350_CX] = &sdm845_cx, - [SM8350_CX_AO] = &sdm845_cx_ao, - [SM8350_EBI] = &sdm845_ebi, - [SM8350_GFX] = &sdm845_gfx, - [SM8350_LCX] = &sdm845_lcx, - [SM8350_LMX] = &sdm845_lmx, - [SM8350_MMCX] = &sm8150_mmcx, - [SM8350_MMCX_AO] = &sm8150_mmcx_ao, - [SM8350_MX] = &sdm845_mx, - [SM8350_MX_AO] = &sdm845_mx_ao, - [SM8350_MXC] = &sm8350_mxc, - [SM8350_MXC_AO] = &sm8350_mxc_ao, - [SM8350_MSS] = &sdm845_mss, + [SM8350_CX] = &cx_w_mx_parent, + [SM8350_CX_AO] = &cx_ao_w_mx_parent, + [SM8350_EBI] = &ebi, + [SM8350_GFX] = &gfx, + [SM8350_LCX] = &lcx, + [SM8350_LMX] = &lmx, + [SM8350_MMCX] = &mmcx, + [SM8350_MMCX_AO] = &mmcx_ao, + [SM8350_MX] = &mx, + [SM8350_MX_AO] = &mx_ao, + [SM8350_MXC] = &mxc, + [SM8350_MXC_AO] = &mxc_ao, + [SM8350_MSS] = &mss, }; static const struct rpmhpd_desc sm8350_desc = { @@ -254,50 +285,20 @@ static const struct rpmhpd_desc sm8350_desc = { }; /* SM8450 RPMH powerdomains */ -static struct rpmhpd sm8450_cx_ao; -static struct rpmhpd sm8450_cx = { - .pd = { .name = "cx", }, - .peer = &sm8450_cx_ao, - .res_name = "cx.lvl", -}; - -static struct rpmhpd sm8450_cx_ao = { - .pd = { .name = "cx_ao", }, - .active_only = true, - .peer = &sm8450_cx, - .res_name = "cx.lvl", -}; - -static struct rpmhpd sm8450_mmcx_ao; -static struct rpmhpd sm8450_mmcx = { - .pd = { .name = "mmcx", }, - .peer = &sm8450_mmcx_ao, - .parent = &sm8450_cx.pd, - .res_name = "mmcx.lvl", -}; - -static struct rpmhpd sm8450_mmcx_ao = { - .pd = { .name = "mmcx_ao", }, - .active_only = true, - .peer = &sm8450_mmcx, - .parent = &sm8450_cx_ao.pd, - .res_name = "mmcx.lvl", -}; - static struct rpmhpd *sm8450_rpmhpds[] = { - [SM8450_CX] = &sm8450_cx, - [SM8450_CX_AO] = &sm8450_cx_ao, - [SM8450_EBI] = &sdm845_ebi, - [SM8450_GFX] = &sdm845_gfx, - [SM8450_LCX] = &sdm845_lcx, - [SM8450_LMX] = &sdm845_lmx, - [SM8450_MMCX] = &sm8450_mmcx, - [SM8450_MMCX_AO] = &sm8450_mmcx_ao, - [SM8450_MX] = &sdm845_mx, - [SM8450_MX_AO] = &sdm845_mx_ao, - [SM8450_MXC] = &sm8350_mxc, - [SM8450_MXC_AO] = &sm8350_mxc_ao, - [SM8450_MSS] = &sdm845_mss, + [SM8450_CX] = &cx, + [SM8450_CX_AO] = &cx_ao, + [SM8450_EBI] = &ebi, + [SM8450_GFX] = &gfx, + [SM8450_LCX] = &lcx, + [SM8450_LMX] = &lmx, + [SM8450_MMCX] = &mmcx_w_cx_parent, + [SM8450_MMCX_AO] = &mmcx_ao_w_cx_parent, + [SM8450_MX] = &mx, + [SM8450_MX_AO] = &mx_ao, + [SM8450_MXC] = &mxc, + [SM8450_MXC_AO] = &mxc_ao, + [SM8450_MSS] = &mss, }; static const struct rpmhpd_desc sm8450_desc = { @@ -307,14 +308,14 @@ static const struct rpmhpd_desc sm8450_desc = { /* SC7180 RPMH powerdomains */ static struct rpmhpd *sc7180_rpmhpds[] = { - [SC7180_CX] = &sdm845_cx, - [SC7180_CX_AO] = &sdm845_cx_ao, - [SC7180_GFX] = &sdm845_gfx, - [SC7180_MX] = &sdm845_mx, - [SC7180_MX_AO] = &sdm845_mx_ao, - [SC7180_LMX] = &sdm845_lmx, - [SC7180_LCX] = &sdm845_lcx, - [SC7180_MSS] = &sdm845_mss, + [SC7180_CX] = &cx_w_mx_parent, + [SC7180_CX_AO] = &cx_ao_w_mx_parent, + [SC7180_GFX] = &gfx, + [SC7180_MX] = &mx, + [SC7180_MX_AO] = &mx_ao, + [SC7180_LMX] = &lmx, + [SC7180_LCX] = &lcx, + [SC7180_MSS] = &mss, }; static const struct rpmhpd_desc sc7180_desc = { @@ -324,15 +325,15 @@ static const struct rpmhpd_desc sc7180_desc = { /* SC7280 RPMH powerdomains */ static struct rpmhpd *sc7280_rpmhpds[] = { - [SC7280_CX] = &sdm845_cx, - [SC7280_CX_AO] = &sdm845_cx_ao, - [SC7280_EBI] = &sdm845_ebi, - [SC7280_GFX] = &sdm845_gfx, - [SC7280_MX] = &sdm845_mx, - [SC7280_MX_AO] = &sdm845_mx_ao, - [SC7280_LMX] = &sdm845_lmx, - [SC7280_LCX] = &sdm845_lcx, - [SC7280_MSS] = &sdm845_mss, + [SC7280_CX] = &cx_w_mx_parent, + [SC7280_CX_AO] = &cx_ao_w_mx_parent, + [SC7280_EBI] = &ebi, + [SC7280_GFX] = &gfx, + [SC7280_MX] = &mx, + [SC7280_MX_AO] = &mx_ao, + [SC7280_LMX] = &lmx, + [SC7280_LCX] = &lcx, + [SC7280_MSS] = &mss, }; static const struct rpmhpd_desc sc7280_desc = { @@ -342,17 +343,17 @@ static const struct rpmhpd_desc sc7280_desc = { /* SC8180x RPMH powerdomains */ static struct rpmhpd *sc8180x_rpmhpds[] = { - [SC8180X_CX] = &sdm845_cx, - [SC8180X_CX_AO] = &sdm845_cx_ao, - [SC8180X_EBI] = &sdm845_ebi, - [SC8180X_GFX] = &sdm845_gfx, - [SC8180X_LCX] = &sdm845_lcx, - [SC8180X_LMX] = &sdm845_lmx, - [SC8180X_MMCX] = &sm8150_mmcx, - [SC8180X_MMCX_AO] = &sm8150_mmcx_ao, - [SC8180X_MSS] = &sdm845_mss, - [SC8180X_MX] = &sdm845_mx, - [SC8180X_MX_AO] = &sdm845_mx_ao, + [SC8180X_CX] = &cx_w_mx_parent, + [SC8180X_CX_AO] = &cx_ao_w_mx_parent, + [SC8180X_EBI] = &ebi, + [SC8180X_GFX] = &gfx, + [SC8180X_LCX] = &lcx, + [SC8180X_LMX] = &lmx, + [SC8180X_MMCX] = &mmcx, + [SC8180X_MMCX_AO] = &mmcx_ao, + [SC8180X_MSS] = &mss, + [SC8180X_MX] = &mx, + [SC8180X_MX_AO] = &mx_ao, }; static const struct rpmhpd_desc sc8180x_desc = { From 7d6a0a4dcf148681bb7760f9aad100ee3d3ce836 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Thu, 9 Dec 2021 21:01:56 +0530 Subject: [PATCH 88/94] soc: qcom: rpmhpd: Remove mx/cx relationship on sc7280 The requirement to specify the active + sleep and active-only MX power-domains as the parents of the corresponding CX power domains is not applicable on sc7280. Fix it by using the cx/cx_ao structs for sc7280 instead of the _w_mx_parent ones. Signed-off-by: Rajendra Nayak Reviewed-by: Bjorn Andersson Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/1639063917-9011-4-git-send-email-quic_rjendra@quicinc.com --- drivers/soc/qcom/rpmhpd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c index a12db32433c5..63602049eaa0 100644 --- a/drivers/soc/qcom/rpmhpd.c +++ b/drivers/soc/qcom/rpmhpd.c @@ -325,8 +325,8 @@ static const struct rpmhpd_desc sc7180_desc = { /* SC7280 RPMH powerdomains */ static struct rpmhpd *sc7280_rpmhpds[] = { - [SC7280_CX] = &cx_w_mx_parent, - [SC7280_CX_AO] = &cx_ao_w_mx_parent, + [SC7280_CX] = &cx, + [SC7280_CX_AO] = &cx_ao, [SC7280_EBI] = &ebi, [SC7280_GFX] = &gfx, [SC7280_MX] = &mx, From 90c74c1c2574e9454fb3a7ae2cddc9433aa89d49 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Thu, 9 Dec 2021 21:01:57 +0530 Subject: [PATCH 89/94] soc: qcom: rpmhpd: Sort power-domain definitions and lists Sort all power-domain defines and the SoC specific lists in alphabetical order for better readability. No functional changes. Signed-off-by: Rajendra Nayak Reviewed-by: Bjorn Andersson Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/1639063917-9011-5-git-send-email-quic_rjendra@quicinc.com --- drivers/soc/qcom/rpmhpd.c | 122 +++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 60 deletions(-) diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c index 63602049eaa0..58f1dc9b9cb7 100644 --- a/drivers/soc/qcom/rpmhpd.c +++ b/drivers/soc/qcom/rpmhpd.c @@ -65,46 +65,9 @@ static DEFINE_MUTEX(rpmhpd_lock); /* RPMH powerdomains */ -static struct rpmhpd ebi = { - .pd = { .name = "ebi", }, - .res_name = "ebi.lvl", -}; - -static struct rpmhpd lmx = { - .pd = { .name = "lmx", }, - .res_name = "lmx.lvl", -}; - -static struct rpmhpd lcx = { - .pd = { .name = "lcx", }, - .res_name = "lcx.lvl", -}; - -static struct rpmhpd gfx = { - .pd = { .name = "gfx", }, - .res_name = "gfx.lvl", -}; - -static struct rpmhpd mss = { - .pd = { .name = "mss", }, - .res_name = "mss.lvl", -}; - -static struct rpmhpd mx_ao; -static struct rpmhpd mx = { - .pd = { .name = "mx", }, - .peer = &mx_ao, - .res_name = "mx.lvl", -}; - -static struct rpmhpd mx_ao = { - .pd = { .name = "mx_ao", }, - .active_only = true, - .peer = &mx, - .res_name = "mx.lvl", -}; - static struct rpmhpd cx_ao; +static struct rpmhpd mx; +static struct rpmhpd mx_ao; static struct rpmhpd cx = { .pd = { .name = "cx", }, .peer = &cx_ao, @@ -134,6 +97,26 @@ static struct rpmhpd cx_ao_w_mx_parent = { .res_name = "cx.lvl", }; +static struct rpmhpd ebi = { + .pd = { .name = "ebi", }, + .res_name = "ebi.lvl", +}; + +static struct rpmhpd gfx = { + .pd = { .name = "gfx", }, + .res_name = "gfx.lvl", +}; + +static struct rpmhpd lcx = { + .pd = { .name = "lcx", }, + .res_name = "lcx.lvl", +}; + +static struct rpmhpd lmx = { + .pd = { .name = "lmx", }, + .res_name = "lmx.lvl", +}; + static struct rpmhpd mmcx_ao; static struct rpmhpd mmcx = { .pd = { .name = "mmcx", }, @@ -164,6 +147,25 @@ static struct rpmhpd mmcx_ao_w_cx_parent = { .res_name = "mmcx.lvl", }; +static struct rpmhpd mss = { + .pd = { .name = "mss", }, + .res_name = "mss.lvl", +}; + +static struct rpmhpd mx_ao; +static struct rpmhpd mx = { + .pd = { .name = "mx", }, + .peer = &mx_ao, + .res_name = "mx.lvl", +}; + +static struct rpmhpd mx_ao = { + .pd = { .name = "mx_ao", }, + .active_only = true, + .peer = &mx, + .res_name = "mx.lvl", +}; + static struct rpmhpd mxc_ao; static struct rpmhpd mxc = { .pd = { .name = "mxc", }, @@ -180,15 +182,15 @@ static struct rpmhpd mxc_ao = { /* SDM845 RPMH powerdomains */ static struct rpmhpd *sdm845_rpmhpds[] = { - [SDM845_EBI] = &ebi, - [SDM845_MX] = &mx, - [SDM845_MX_AO] = &mx_ao, [SDM845_CX] = &cx_w_mx_parent, [SDM845_CX_AO] = &cx_ao_w_mx_parent, - [SDM845_LMX] = &lmx, - [SDM845_LCX] = &lcx, + [SDM845_EBI] = &ebi, [SDM845_GFX] = &gfx, + [SDM845_LCX] = &lcx, + [SDM845_LMX] = &lmx, [SDM845_MSS] = &mss, + [SDM845_MX] = &mx, + [SDM845_MX_AO] = &mx_ao, }; static const struct rpmhpd_desc sdm845_desc = { @@ -198,9 +200,9 @@ static const struct rpmhpd_desc sdm845_desc = { /* SDX55 RPMH powerdomains */ static struct rpmhpd *sdx55_rpmhpds[] = { + [SDX55_CX] = &cx_w_mx_parent, [SDX55_MSS] = &mss, [SDX55_MX] = &mx, - [SDX55_CX] = &cx_w_mx_parent, }; static const struct rpmhpd_desc sdx55_desc = { @@ -225,17 +227,17 @@ static const struct rpmhpd_desc sm6350_desc = { /* SM8150 RPMH powerdomains */ static struct rpmhpd *sm8150_rpmhpds[] = { - [SM8150_MSS] = &mss, - [SM8150_EBI] = &ebi, - [SM8150_LMX] = &lmx, - [SM8150_LCX] = &lcx, - [SM8150_GFX] = &gfx, - [SM8150_MX] = &mx, - [SM8150_MX_AO] = &mx_ao, [SM8150_CX] = &cx_w_mx_parent, [SM8150_CX_AO] = &cx_ao_w_mx_parent, + [SM8150_EBI] = &ebi, + [SM8150_GFX] = &gfx, + [SM8150_LCX] = &lcx, + [SM8150_LMX] = &lmx, [SM8150_MMCX] = &mmcx, [SM8150_MMCX_AO] = &mmcx_ao, + [SM8150_MSS] = &mss, + [SM8150_MX] = &mx, + [SM8150_MX_AO] = &mx_ao, }; static const struct rpmhpd_desc sm8150_desc = { @@ -272,11 +274,11 @@ static struct rpmhpd *sm8350_rpmhpds[] = { [SM8350_LMX] = &lmx, [SM8350_MMCX] = &mmcx, [SM8350_MMCX_AO] = &mmcx_ao, + [SM8350_MSS] = &mss, [SM8350_MX] = &mx, [SM8350_MX_AO] = &mx_ao, [SM8350_MXC] = &mxc, [SM8350_MXC_AO] = &mxc_ao, - [SM8350_MSS] = &mss, }; static const struct rpmhpd_desc sm8350_desc = { @@ -294,11 +296,11 @@ static struct rpmhpd *sm8450_rpmhpds[] = { [SM8450_LMX] = &lmx, [SM8450_MMCX] = &mmcx_w_cx_parent, [SM8450_MMCX_AO] = &mmcx_ao_w_cx_parent, + [SM8450_MSS] = &mss, [SM8450_MX] = &mx, [SM8450_MX_AO] = &mx_ao, [SM8450_MXC] = &mxc, [SM8450_MXC_AO] = &mxc_ao, - [SM8450_MSS] = &mss, }; static const struct rpmhpd_desc sm8450_desc = { @@ -311,11 +313,11 @@ static struct rpmhpd *sc7180_rpmhpds[] = { [SC7180_CX] = &cx_w_mx_parent, [SC7180_CX_AO] = &cx_ao_w_mx_parent, [SC7180_GFX] = &gfx, + [SC7180_LCX] = &lcx, + [SC7180_LMX] = &lmx, + [SC7180_MSS] = &mss, [SC7180_MX] = &mx, [SC7180_MX_AO] = &mx_ao, - [SC7180_LMX] = &lmx, - [SC7180_LCX] = &lcx, - [SC7180_MSS] = &mss, }; static const struct rpmhpd_desc sc7180_desc = { @@ -329,11 +331,11 @@ static struct rpmhpd *sc7280_rpmhpds[] = { [SC7280_CX_AO] = &cx_ao, [SC7280_EBI] = &ebi, [SC7280_GFX] = &gfx, + [SC7280_LCX] = &lcx, + [SC7280_LMX] = &lmx, + [SC7280_MSS] = &mss, [SC7280_MX] = &mx, [SC7280_MX_AO] = &mx_ao, - [SC7280_LMX] = &lmx, - [SC7280_LCX] = &lcx, - [SC7280_MSS] = &mss, }; static const struct rpmhpd_desc sc7280_desc = { From 492c995ab1ed1a149d5e8808953ce8837e310c06 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Mon, 13 Dec 2021 09:26:02 +0100 Subject: [PATCH 90/94] dt-bindings: arm: msm: Add LLCC for SM6350 Add LLCC compatible for SM6350 SoC. Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Konrad Dybcio Acked-by: Rob Herring Signed-off-by: Luca Weiss Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211213082614.22651-2-luca.weiss@fairphone.com --- Documentation/devicetree/bindings/arm/msm/qcom,llcc.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,llcc.yaml b/Documentation/devicetree/bindings/arm/msm/qcom,llcc.yaml index 62fcbd883392..e12fc7999356 100644 --- a/Documentation/devicetree/bindings/arm/msm/qcom,llcc.yaml +++ b/Documentation/devicetree/bindings/arm/msm/qcom,llcc.yaml @@ -24,6 +24,7 @@ properties: - qcom,sc7180-llcc - qcom,sc7280-llcc - qcom,sdm845-llcc + - qcom,sm6350-llcc - qcom,sm8150-llcc - qcom,sm8250-llcc From bd0d04d4144d3db5b461b21d528607fe994d9afa Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Mon, 13 Dec 2021 09:26:03 +0100 Subject: [PATCH 91/94] dt-bindings: firmware: scm: Add SM6350 compatible Add devicetree compatible for SCM on SM6350 SoC. Signed-off-by: Luca Weiss Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211213082614.22651-3-luca.weiss@fairphone.com --- Documentation/devicetree/bindings/firmware/qcom,scm.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/firmware/qcom,scm.txt b/Documentation/devicetree/bindings/firmware/qcom,scm.txt index 548733388497..b1cd4ad1889a 100644 --- a/Documentation/devicetree/bindings/firmware/qcom,scm.txt +++ b/Documentation/devicetree/bindings/firmware/qcom,scm.txt @@ -26,6 +26,7 @@ Required properties: * "qcom,scm-sc7280" * "qcom,scm-sdm845" * "qcom,scm-sdx55" + * "qcom,scm-sm6350" * "qcom,scm-sm8150" * "qcom,scm-sm8250" * "qcom,scm-sm8350" From 1e20b28d2e0e3e2996841df92f3af9050303f864 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Mon, 13 Dec 2021 09:26:09 +0100 Subject: [PATCH 92/94] dt-bindings: arm: msm: Don't mark LLCC interrupt as required Newer SoCs like SM6350 or SM8250 don't provide an interrupt for LLCC. Signed-off-by: Luca Weiss Acked-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211213082614.22651-9-luca.weiss@fairphone.com --- Documentation/devicetree/bindings/arm/msm/qcom,llcc.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,llcc.yaml b/Documentation/devicetree/bindings/arm/msm/qcom,llcc.yaml index e12fc7999356..03882aac8d2d 100644 --- a/Documentation/devicetree/bindings/arm/msm/qcom,llcc.yaml +++ b/Documentation/devicetree/bindings/arm/msm/qcom,llcc.yaml @@ -45,7 +45,6 @@ required: - compatible - reg - reg-names - - interrupts additionalProperties: false From d39cec003ab0d6b91f9709c59daa6f0480d232e2 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Mon, 13 Dec 2021 09:11:11 +0100 Subject: [PATCH 93/94] soc: qcom: socinfo: Add SM6350 and SM7225 Both SoCs are known as 'lagoon' downstream. Add their ids to the socinfo driver. Signed-off-by: Luca Weiss Reviewed-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211213081111.20217-1-luca.weiss@fairphone.com --- drivers/soc/qcom/socinfo.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 113dc57b74c3..6dc0f39c0ec3 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -313,9 +313,11 @@ static const struct soc_id soc_id[] = { { 421, "IPQ6000" }, { 422, "IPQ6010" }, { 425, "SC7180" }, + { 434, "SM6350" }, { 453, "IPQ6005" }, { 455, "QRB5165" }, { 457, "SM8450" }, + { 459, "SM7225" }, }; static const char *socinfo_machine(struct device *dev, unsigned int id) From e395f021ccebc239e4fec02fec47f5f0eef44ebd Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Sat, 11 Dec 2021 17:06:26 +0800 Subject: [PATCH 94/94] soc: qcom: rpmh-rsc: Fix typo in a comment The double `for' in the comment in line 694 is repeated. Remove one of them from the comment. Signed-off-by: Jason Wang Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211211090626.248801-1-wangborong@cdjrlc.com --- drivers/soc/qcom/rpmh-rsc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 3a12a482f6b2..01c2f50cb97e 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -691,7 +691,7 @@ static int find_slots(struct tcs_group *tcs, const struct tcs_request *msg, * @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 + * This should only be called for sleep/wake state, never active-only * state. * * The caller must ensure that no other RPMH actions are happening and the