From cd2e9be89a246046d42939b8c0e0ea0d08530d30 Mon Sep 17 00:00:00 2001 From: Cristian Birsan Date: Thu, 6 Jul 2017 11:35:27 +0300 Subject: drivers: soc: atmel: Add basic support for new sama5d2 SiPs Add basic support for new sama5d2 System in a Package chips. Signed-off-by: Cristian Birsan [claudiu.beznea@microchip.com: use MiB instead of MB] Signed-off-by: Claudiu Beznea Acked-by: Nicolas Ferre Acked-by: Ludovic Desroches Signed-off-by: Alexandre Belloni --- drivers/soc/atmel/soc.c | 8 ++++++++ drivers/soc/atmel/soc.h | 4 ++++ 2 files changed, 12 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/atmel/soc.c b/drivers/soc/atmel/soc.c index c1363c83c352..4dd03b099c89 100644 --- a/drivers/soc/atmel/soc.c +++ b/drivers/soc/atmel/soc.c @@ -72,6 +72,8 @@ static const struct at91_soc __initconst socs[] = { "sama5d21", "sama5d2"), AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D22CU_EXID_MATCH, "sama5d22", "sama5d2"), + AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D225C_D1M_EXID_MATCH, + "sama5d225c 16MiB SiP", "sama5d2"), AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D23CU_EXID_MATCH, "sama5d23", "sama5d2"), AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D24CX_EXID_MATCH, @@ -84,10 +86,16 @@ static const struct at91_soc __initconst socs[] = { "sama5d27", "sama5d2"), AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D27CN_EXID_MATCH, "sama5d27", "sama5d2"), + AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D27C_D1G_EXID_MATCH, + "sama5d27c 128MiB SiP", "sama5d2"), + AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D27C_D5M_EXID_MATCH, + "sama5d27c 64MiB SiP", "sama5d2"), AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D28CU_EXID_MATCH, "sama5d28", "sama5d2"), AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D28CN_EXID_MATCH, "sama5d28", "sama5d2"), + AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D28C_D1G_EXID_MATCH, + "sama5d28c 128MiB SiP", "sama5d2"), AT91_SOC(SAMA5D3_CIDR_MATCH, SAMA5D31_EXID_MATCH, "sama5d31", "sama5d3"), AT91_SOC(SAMA5D3_CIDR_MATCH, SAMA5D33_EXID_MATCH, diff --git a/drivers/soc/atmel/soc.h b/drivers/soc/atmel/soc.h index a90bd5b0ef8f..94cd5d1ab502 100644 --- a/drivers/soc/atmel/soc.h +++ b/drivers/soc/atmel/soc.h @@ -64,14 +64,18 @@ at91_soc_init(const struct at91_soc *socs); #define SAMA5D2_CIDR_MATCH 0x0a5c08c0 #define SAMA5D21CU_EXID_MATCH 0x0000005a +#define SAMA5D225C_D1M_EXID_MATCH 0x00000053 #define SAMA5D22CU_EXID_MATCH 0x00000059 #define SAMA5D22CN_EXID_MATCH 0x00000069 #define SAMA5D23CU_EXID_MATCH 0x00000058 #define SAMA5D24CX_EXID_MATCH 0x00000004 #define SAMA5D24CU_EXID_MATCH 0x00000014 #define SAMA5D26CU_EXID_MATCH 0x00000012 +#define SAMA5D27C_D1G_EXID_MATCH 0x00000033 +#define SAMA5D27C_D5M_EXID_MATCH 0x00000032 #define SAMA5D27CU_EXID_MATCH 0x00000011 #define SAMA5D27CN_EXID_MATCH 0x00000021 +#define SAMA5D28C_D1G_EXID_MATCH 0x00000013 #define SAMA5D28CU_EXID_MATCH 0x00000010 #define SAMA5D28CN_EXID_MATCH 0x00000020 -- cgit From 17760376ae31e06f66b3c3b8981f5978d4c53150 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 12 Sep 2017 23:37:18 +0300 Subject: soc: renesas: rcar-rst: add R8A77970 support Add support for R-Car V3M (R8A77970) to the R-Car RST driver -- this driver is needed for the clock driver to work. Signed-off-by: Sergei Shtylyov Reviewed-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- drivers/soc/renesas/Kconfig | 3 ++- drivers/soc/renesas/rcar-rst.c | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 567414cb42ba..f0d562a7c4d3 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -3,7 +3,8 @@ config SOC_RENESAS default y if ARCH_RENESAS select SOC_BUS select RST_RCAR if ARCH_RCAR_GEN1 || ARCH_RCAR_GEN2 || \ - ARCH_R8A7795 || ARCH_R8A7796 || ARCH_R8A77995 + ARCH_R8A7795 || ARCH_R8A7796 || ARCH_R8A77970 || \ + ARCH_R8A77995 select SYSC_R8A7743 if ARCH_R8A7743 select SYSC_R8A7745 if ARCH_R8A7745 select SYSC_R8A7779 if ARCH_R8A7779 diff --git a/drivers/soc/renesas/rcar-rst.c b/drivers/soc/renesas/rcar-rst.c index baa47014e96b..3316b028f231 100644 --- a/drivers/soc/renesas/rcar-rst.c +++ b/drivers/soc/renesas/rcar-rst.c @@ -41,6 +41,7 @@ static const struct of_device_id rcar_rst_matches[] __initconst = { /* R-Car Gen3 is handled like R-Car Gen2 */ { .compatible = "renesas,r8a7795-rst", .data = &rcar_rst_gen2 }, { .compatible = "renesas,r8a7796-rst", .data = &rcar_rst_gen2 }, + { .compatible = "renesas,r8a77970-rst", .data = &rcar_rst_gen2 }, { .compatible = "renesas,r8a77995-rst", .data = &rcar_rst_gen2 }, { /* sentinel */ } }; -- cgit From bb0030752f288a2e2d27cc110d6d4139fe7a948d Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 12 Sep 2017 23:37:17 +0300 Subject: soc: renesas: identify R-Car V3M Add support for identifying the R-Car V3M (R8A77970) SoC. Signed-off-by: Sergei Shtylyov Reviewed-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- drivers/soc/renesas/renesas-soc.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c index 90d6b7a4340a..9f4ee2567c72 100644 --- a/drivers/soc/renesas/renesas-soc.c +++ b/drivers/soc/renesas/renesas-soc.c @@ -144,6 +144,11 @@ static const struct renesas_soc soc_rcar_m3_w __initconst __maybe_unused = { .id = 0x52, }; +static const struct renesas_soc soc_rcar_v3m __initconst __maybe_unused = { + .family = &fam_rcar_gen3, + .id = 0x54, +}; + static const struct renesas_soc soc_rcar_d3 __initconst __maybe_unused = { .family = &fam_rcar_gen3, .id = 0x58, @@ -204,6 +209,9 @@ static const struct of_device_id renesas_socs[] __initconst = { #ifdef CONFIG_ARCH_R8A7796 { .compatible = "renesas,r8a7796", .data = &soc_rcar_m3_w }, #endif +#ifdef CONFIG_ARCH_R8A77970 + { .compatible = "renesas,r8a77970", .data = &soc_rcar_v3m }, +#endif #ifdef CONFIG_ARCH_R8A77995 { .compatible = "renesas,r8a77995", .data = &soc_rcar_d3 }, #endif -- cgit From bab9b2a74fe9da96e895e0919f625679a0a8c964 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 12 Sep 2017 23:37:20 +0300 Subject: soc: renesas: rcar-sysc: add R8A77970 support Add support for R-Car V3M (R8A77970) SoC power areas to the R-Car SYSC driver. Based on the original (and large) patch by Daisuke Matsushita . Signed-off-by: Vladimir Barinov Signed-off-by: Sergei Shtylyov Reviewed-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- drivers/soc/renesas/Kconfig | 5 +++++ drivers/soc/renesas/Makefile | 1 + drivers/soc/renesas/r8a77970-sysc.c | 39 +++++++++++++++++++++++++++++++++++++ drivers/soc/renesas/rcar-sysc.c | 3 +++ drivers/soc/renesas/rcar-sysc.h | 1 + 5 files changed, 49 insertions(+) create mode 100644 drivers/soc/renesas/r8a77970-sysc.c (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index f0d562a7c4d3..09550b1da56d 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -14,6 +14,7 @@ config SOC_RENESAS select SYSC_R8A7794 if ARCH_R8A7794 select SYSC_R8A7795 if ARCH_R8A7795 select SYSC_R8A7796 if ARCH_R8A7796 + select SYSC_R8A77970 if ARCH_R8A77970 select SYSC_R8A77995 if ARCH_R8A77995 if SOC_RENESAS @@ -55,6 +56,10 @@ config SYSC_R8A7796 bool "R-Car M3-W System Controller support" if COMPILE_TEST select SYSC_RCAR +config SYSC_R8A77970 + bool "R-Car V3M System Controller support" if COMPILE_TEST + select SYSC_RCAR + config SYSC_R8A77995 bool "R-Car D3 System Controller support" if COMPILE_TEST select SYSC_RCAR diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile index 6b6e7f16104c..8334af1d231b 100644 --- a/drivers/soc/renesas/Makefile +++ b/drivers/soc/renesas/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_SYSC_R8A7792) += r8a7792-sysc.o obj-$(CONFIG_SYSC_R8A7794) += r8a7794-sysc.o obj-$(CONFIG_SYSC_R8A7795) += r8a7795-sysc.o obj-$(CONFIG_SYSC_R8A7796) += r8a7796-sysc.o +obj-$(CONFIG_SYSC_R8A77970) += r8a77970-sysc.o obj-$(CONFIG_SYSC_R8A77995) += r8a77995-sysc.o # Family diff --git a/drivers/soc/renesas/r8a77970-sysc.c b/drivers/soc/renesas/r8a77970-sysc.c new file mode 100644 index 000000000000..8c614164718e --- /dev/null +++ b/drivers/soc/renesas/r8a77970-sysc.c @@ -0,0 +1,39 @@ +/* + * Renesas R-Car V3M System Controller + * + * Copyright (C) 2017 Cogent Embedded Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + +#include + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a77970_areas[] __initconst = { + { "always-on", 0, 0, R8A77970_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca53-scu", 0x140, 0, R8A77970_PD_CA53_SCU, R8A77970_PD_ALWAYS_ON, + PD_SCU }, + { "ca53-cpu0", 0x200, 0, R8A77970_PD_CA53_CPU0, R8A77970_PD_CA53_SCU, + PD_CPU_NOCR }, + { "ca53-cpu1", 0x200, 1, R8A77970_PD_CA53_CPU1, R8A77970_PD_CA53_SCU, + PD_CPU_NOCR }, + { "cr7", 0x240, 0, R8A77970_PD_CR7, R8A77970_PD_ALWAYS_ON }, + { "a3ir", 0x180, 0, R8A77970_PD_A3IR, R8A77970_PD_ALWAYS_ON }, + { "a2ir0", 0x400, 0, R8A77970_PD_A2IR0, R8A77970_PD_ALWAYS_ON }, + { "a2ir1", 0x400, 1, R8A77970_PD_A2IR1, R8A77970_PD_A2IR0 }, + { "a2ir2", 0x400, 2, R8A77970_PD_A2IR2, R8A77970_PD_A2IR0 }, + { "a2ir3", 0x400, 3, R8A77970_PD_A2IR3, R8A77970_PD_A2IR0 }, + { "a2sc0", 0x400, 4, R8A77970_PD_A2SC0, R8A77970_PD_ALWAYS_ON }, + { "a2sc1", 0x400, 5, R8A77970_PD_A2SC1, R8A77970_PD_A2SC0 }, +}; + +const struct rcar_sysc_info r8a77970_sysc_info __initconst = { + .areas = r8a77970_areas, + .num_areas = ARRAY_SIZE(r8a77970_areas), +}; diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c index c8406e81640f..55a47e509e49 100644 --- a/drivers/soc/renesas/rcar-sysc.c +++ b/drivers/soc/renesas/rcar-sysc.c @@ -284,6 +284,9 @@ static const struct of_device_id rcar_sysc_matches[] = { #ifdef CONFIG_SYSC_R8A7796 { .compatible = "renesas,r8a7796-sysc", .data = &r8a7796_sysc_info }, #endif +#ifdef CONFIG_SYSC_R8A77970 + { .compatible = "renesas,r8a77970-sysc", .data = &r8a77970_sysc_info }, +#endif #ifdef CONFIG_SYSC_R8A77995 { .compatible = "renesas,r8a77995-sysc", .data = &r8a77995_sysc_info }, #endif diff --git a/drivers/soc/renesas/rcar-sysc.h b/drivers/soc/renesas/rcar-sysc.h index 2f524922c4d2..9d9daf9eb91b 100644 --- a/drivers/soc/renesas/rcar-sysc.h +++ b/drivers/soc/renesas/rcar-sysc.h @@ -58,6 +58,7 @@ extern const struct rcar_sysc_info r8a7792_sysc_info; extern const struct rcar_sysc_info r8a7794_sysc_info; extern const struct rcar_sysc_info r8a7795_sysc_info; extern const struct rcar_sysc_info r8a7796_sysc_info; +extern const struct rcar_sysc_info r8a77970_sysc_info; extern const struct rcar_sysc_info r8a77995_sysc_info; -- cgit From 7ce2e934178b1af66c99ffefd831ab0de7244747 Mon Sep 17 00:00:00 2001 From: Karim Eshapa Date: Fri, 5 May 2017 07:45:18 +0200 Subject: soc/fsl/qman: Sleep instead of stuck hacking jiffies Use msleep() instead of stucking with long delay will be more efficient. Signed-off-by: Karim Eshapa Acked-by: Scott Wood Signed-off-by: Li Yang --- drivers/soc/fsl/qbman/qman.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/qbman/qman.c b/drivers/soc/fsl/qbman/qman.c index 18eefc3f1abe..da68f887ed82 100644 --- a/drivers/soc/fsl/qbman/qman.c +++ b/drivers/soc/fsl/qbman/qman.c @@ -1084,11 +1084,7 @@ loop: * entries well before the ring has been fully consumed, so * we're being *really* paranoid here. */ - u64 now, then = jiffies; - - do { - now = jiffies; - } while ((then + 10000) > now); + msleep(1); msg = qm_mr_current(p); if (!msg) return 0; -- cgit From b4964680834708da00fd82e84d8cfc716bf829d1 Mon Sep 17 00:00:00 2001 From: ashish kumar Date: Mon, 21 Aug 2017 11:18:56 +0530 Subject: soc/fsl/guts: Add compatible string for LS1088 Adding compatible string "ls1088a-dcfg" so that guts driver can be init for ls1088 Signed-off-by: Ashish Kumar Signed-off-by: Amrita Kumari Signed-off-by: Li Yang --- drivers/soc/fsl/guts.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c index 6af7a11f09a5..d89a6a80c8ef 100644 --- a/drivers/soc/fsl/guts.c +++ b/drivers/soc/fsl/guts.c @@ -213,6 +213,7 @@ static const struct of_device_id fsl_guts_of_match[] = { { .compatible = "fsl,ls1021a-dcfg", }, { .compatible = "fsl,ls1043a-dcfg", }, { .compatible = "fsl,ls2080a-dcfg", }, + { .compatible = "fsl,ls1088a-dcfg", }, {} }; MODULE_DEVICE_TABLE(of, fsl_guts_of_match); -- cgit From 42d0349784c74d8a1e92cfbec120b9a6868b0c25 Mon Sep 17 00:00:00 2001 From: Roy Pledge Date: Mon, 18 Sep 2017 16:39:36 -0400 Subject: soc/fsl/qbman: Add common routine for QBMan private allocations The QBMan device uses several memory regions to manage frame queues and buffers. Add a common routine for extracting and initializing these reserved memory areas. Signed-off-by: Roy Pledge Reviewed-by: Catalin Marinas Signed-off-by: Li Yang --- drivers/soc/fsl/qbman/Makefile | 2 +- drivers/soc/fsl/qbman/dpaa_sys.c | 78 ++++++++++++++++++++++++++++++++++++++++ drivers/soc/fsl/qbman/dpaa_sys.h | 4 +++ 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 drivers/soc/fsl/qbman/dpaa_sys.c (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/qbman/Makefile b/drivers/soc/fsl/qbman/Makefile index 7ae199f1664e..3cbd08aeab59 100644 --- a/drivers/soc/fsl/qbman/Makefile +++ b/drivers/soc/fsl/qbman/Makefile @@ -1,6 +1,6 @@ obj-$(CONFIG_FSL_DPAA) += bman_ccsr.o qman_ccsr.o \ bman_portal.o qman_portal.o \ - bman.o qman.o + bman.o qman.o dpaa_sys.o obj-$(CONFIG_FSL_BMAN_TEST) += bman-test.o bman-test-y = bman_test.o diff --git a/drivers/soc/fsl/qbman/dpaa_sys.c b/drivers/soc/fsl/qbman/dpaa_sys.c new file mode 100644 index 000000000000..9436aa83ff1b --- /dev/null +++ b/drivers/soc/fsl/qbman/dpaa_sys.c @@ -0,0 +1,78 @@ +/* Copyright 2017 NXP Semiconductor, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of NXP Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY NXP Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NXP Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "dpaa_sys.h" + +/* + * Initialize a devices private memory region + */ +int qbman_init_private_mem(struct device *dev, int idx, dma_addr_t *addr, + size_t *size) +{ + int ret; + struct device_node *mem_node; + u64 size64; + + ret = of_reserved_mem_device_init_by_idx(dev, dev->of_node, idx); + if (ret) { + dev_err(dev, + "of_reserved_mem_device_init_by_idx(%d) failed 0x%x\n", + idx, ret); + return -ENODEV; + } + mem_node = of_parse_phandle(dev->of_node, "memory-region", 0); + if (mem_node) { + ret = of_property_read_u64(mem_node, "size", &size64); + if (ret) { + dev_err(dev, "of_address_to_resource fails 0x%x\n", + ret); + return -ENODEV; + } + *size = size64; + } else { + dev_err(dev, "No memory-region found for index %d\n", idx); + return -ENODEV; + } + + if (!dma_zalloc_coherent(dev, *size, addr, 0)) { + dev_err(dev, "DMA Alloc memory failed\n"); + return -ENODEV; + } + + /* + * Disassociate the reserved memory area from the device + * because a device can only have one DMA memory area. This + * should be fine since the memory is allocated and initialized + * and only ever accessed by the QBMan device from now on + */ + of_reserved_mem_device_release(dev); + return 0; +} diff --git a/drivers/soc/fsl/qbman/dpaa_sys.h b/drivers/soc/fsl/qbman/dpaa_sys.h index 2ce394aa4c95..676af829cea0 100644 --- a/drivers/soc/fsl/qbman/dpaa_sys.h +++ b/drivers/soc/fsl/qbman/dpaa_sys.h @@ -102,4 +102,8 @@ static inline u8 dpaa_cyc_diff(u8 ringsize, u8 first, u8 last) /* Offset applied to genalloc pools due to zero being an error return */ #define DPAA_GENALLOC_OFF 0x80000000 +/* Initialize the devices private memory region */ +int qbman_init_private_mem(struct device *dev, int idx, dma_addr_t *addr, + size_t *size); + #endif /* __DPAA_SYS_H */ -- cgit From 5ae783c6a1e5441250da8e015141bff6b29f54fc Mon Sep 17 00:00:00 2001 From: Roy Pledge Date: Mon, 18 Sep 2017 16:39:37 -0400 Subject: soc/fsl/qbman: Use shared-dma-pool for BMan private memory allocations Use the shared-memory-pool mechanism for free buffer proxy record area allocation. Signed-off-by: Roy Pledge Reviewed-by: Catalin Marinas Signed-off-by: Li Yang --- drivers/soc/fsl/qbman/bman_ccsr.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/qbman/bman_ccsr.c b/drivers/soc/fsl/qbman/bman_ccsr.c index eaa9585c7347..05c42235dd41 100644 --- a/drivers/soc/fsl/qbman/bman_ccsr.c +++ b/drivers/soc/fsl/qbman/bman_ccsr.c @@ -201,6 +201,21 @@ static int fsl_bman_probe(struct platform_device *pdev) return -ENODEV; } + /* + * If FBPR memory wasn't defined using the qbman compatible string + * try using the of_reserved_mem_device method + */ + if (!fbpr_a) { + ret = qbman_init_private_mem(dev, 0, &fbpr_a, &fbpr_sz); + if (ret) { + dev_err(dev, "qbman_init_private_mem() failed 0x%x\n", + ret); + return -ENODEV; + } + } + + dev_dbg(dev, "Allocated FBPR 0x%llx 0x%zx\n", fbpr_a, fbpr_sz); + bm_set_memory(fbpr_a, fbpr_sz); err_irq = platform_get_irq(pdev, 0); -- cgit From 07f86917a450810107a010cc96e6777b2f400efe Mon Sep 17 00:00:00 2001 From: Roy Pledge Date: Mon, 18 Sep 2017 16:39:38 -0400 Subject: soc/fsl/qbman: Use shared-dma-pool for QMan private memory allocations Use the shared-memory-pool mechanism for frame queue descriptor and packed frame descriptor record area allocations. Signed-off-by: Roy Pledge Reviewed-by: Catalin Marinas Signed-off-by: Li Yang --- drivers/soc/fsl/qbman/qman_ccsr.c | 93 ++++++++++++++++++++++++++------------- drivers/soc/fsl/qbman/qman_priv.h | 2 - drivers/soc/fsl/qbman/qman_test.h | 2 - 3 files changed, 63 insertions(+), 34 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/qbman/qman_ccsr.c b/drivers/soc/fsl/qbman/qman_ccsr.c index 835ce947ffca..607355b91d45 100644 --- a/drivers/soc/fsl/qbman/qman_ccsr.c +++ b/drivers/soc/fsl/qbman/qman_ccsr.c @@ -401,21 +401,42 @@ static int qm_init_pfdr(struct device *dev, u32 pfdr_start, u32 num) } /* - * Ideally we would use the DMA API to turn rmem->base into a DMA address - * (especially if iommu translations ever get involved). Unfortunately, the - * DMA API currently does not allow mapping anything that is not backed with - * a struct page. + * QMan needs two global memory areas initialized at boot time: + * 1) FQD: Frame Queue Descriptors used to manage frame queues + * 2) PFDR: Packed Frame Queue Descriptor Records used to store frames + * Both areas are reserved using the device tree reserved memory framework + * and the addresses and sizes are initialized when the QMan device is probed */ static dma_addr_t fqd_a, pfdr_a; static size_t fqd_sz, pfdr_sz; +#ifdef CONFIG_PPC +/* + * Support for PPC Device Tree backward compatibility when compatible + * string is set to fsl-qman-fqd and fsl-qman-pfdr + */ +static int zero_priv_mem(phys_addr_t addr, size_t sz) +{ + /* map as cacheable, non-guarded */ + void __iomem *tmpp = ioremap_prot(addr, sz, 0); + + if (!tmpp) + return -ENOMEM; + + memset_io(tmpp, 0, sz); + flush_dcache_range((unsigned long)tmpp, + (unsigned long)tmpp + sz); + iounmap(tmpp); + + return 0; +} + static int qman_fqd(struct reserved_mem *rmem) { fqd_a = rmem->base; fqd_sz = rmem->size; WARN_ON(!(fqd_a && fqd_sz)); - return 0; } RESERVEDMEM_OF_DECLARE(qman_fqd, "fsl,qman-fqd", qman_fqd); @@ -431,32 +452,13 @@ static int qman_pfdr(struct reserved_mem *rmem) } RESERVEDMEM_OF_DECLARE(qman_pfdr, "fsl,qman-pfdr", qman_pfdr); +#endif + static unsigned int qm_get_fqid_maxcnt(void) { return fqd_sz / 64; } -/* - * Flush this memory range from data cache so that QMAN originated - * transactions for this memory region could be marked non-coherent. - */ -static int zero_priv_mem(struct device *dev, struct device_node *node, - phys_addr_t addr, size_t sz) -{ - /* map as cacheable, non-guarded */ - void __iomem *tmpp = ioremap_prot(addr, sz, 0); - - if (!tmpp) - return -ENOMEM; - - memset_io(tmpp, 0, sz); - flush_dcache_range((unsigned long)tmpp, - (unsigned long)tmpp + sz); - iounmap(tmpp); - - return 0; -} - static void log_edata_bits(struct device *dev, u32 bit_count) { u32 i, j, mask = 0xffffffff; @@ -727,10 +729,41 @@ static int fsl_qman_probe(struct platform_device *pdev) qm_channel_caam = QMAN_CHANNEL_CAAM_REV3; } - ret = zero_priv_mem(dev, node, fqd_a, fqd_sz); - WARN_ON(ret); - if (ret) - return -ENODEV; + if (fqd_a) { +#ifdef CONFIG_PPC + /* + * For PPC backward DT compatibility + * FQD memory MUST be zero'd by software + */ + zero_priv_mem(fqd_a, fqd_sz); +#else + WARN(1, "Unexpected architecture using non shared-dma-mem reservations"); +#endif + } else { + /* + * Order of memory regions is assumed as FQD followed by PFDR + * in order to ensure allocations from the correct regions the + * driver initializes then allocates each piece in order + */ + ret = qbman_init_private_mem(dev, 0, &fqd_a, &fqd_sz); + if (ret) { + dev_err(dev, "qbman_init_private_mem() for FQD failed 0x%x\n", + ret); + return -ENODEV; + } + } + dev_dbg(dev, "Allocated FQD 0x%llx 0x%zx\n", fqd_a, fqd_sz); + + if (!pfdr_a) { + /* Setup PFDR memory */ + ret = qbman_init_private_mem(dev, 1, &pfdr_a, &pfdr_sz); + if (ret) { + dev_err(dev, "qbman_init_private_mem() for PFDR failed 0x%x\n", + ret); + return -ENODEV; + } + } + dev_dbg(dev, "Allocated PFDR 0x%llx 0x%zx\n", pfdr_a, pfdr_sz); ret = qman_init_ccsr(dev); if (ret) { diff --git a/drivers/soc/fsl/qbman/qman_priv.h b/drivers/soc/fsl/qbman/qman_priv.h index 5fe9faf6232e..b1e2cbfd449b 100644 --- a/drivers/soc/fsl/qbman/qman_priv.h +++ b/drivers/soc/fsl/qbman/qman_priv.h @@ -28,8 +28,6 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include "dpaa_sys.h" #include diff --git a/drivers/soc/fsl/qbman/qman_test.h b/drivers/soc/fsl/qbman/qman_test.h index d5f8cb2260dc..41bdbc48cade 100644 --- a/drivers/soc/fsl/qbman/qman_test.h +++ b/drivers/soc/fsl/qbman/qman_test.h @@ -30,7 +30,5 @@ #include "qman_priv.h" -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - int qman_test_stash(void); int qman_test_api(void); -- cgit From f5bd22996038d5f45b158d5c14fd45209d7fbb5d Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Mon, 18 Sep 2017 16:39:40 -0400 Subject: soc/fsl/qbman: Drop set/clear_bits usage Replace PPC specific set/clear_bits API with standard bit twiddling so driver is portalable outside PPC. Signed-off-by: Madalin Bucur Signed-off-by: Claudiu Manoil Signed-off-by: Roy Pledge Reviewed-by: Catalin Marinas Signed-off-by: Li Yang --- drivers/soc/fsl/qbman/bman.c | 2 +- drivers/soc/fsl/qbman/qman.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/qbman/bman.c b/drivers/soc/fsl/qbman/bman.c index a3d6d7cfa929..3acded1ccc37 100644 --- a/drivers/soc/fsl/qbman/bman.c +++ b/drivers/soc/fsl/qbman/bman.c @@ -607,7 +607,7 @@ int bman_p_irqsource_add(struct bman_portal *p, u32 bits) unsigned long irqflags; local_irq_save(irqflags); - set_bits(bits & BM_PIRQ_VISIBLE, &p->irq_sources); + p->irq_sources |= bits & BM_PIRQ_VISIBLE; bm_out(&p->p, BM_REG_IER, p->irq_sources); local_irq_restore(irqflags); return 0; diff --git a/drivers/soc/fsl/qbman/qman.c b/drivers/soc/fsl/qbman/qman.c index da68f887ed82..8829bbd31580 100644 --- a/drivers/soc/fsl/qbman/qman.c +++ b/drivers/soc/fsl/qbman/qman.c @@ -909,12 +909,12 @@ static inline int qm_mc_result_timeout(struct qm_portal *portal, static inline void fq_set(struct qman_fq *fq, u32 mask) { - set_bits(mask, &fq->flags); + fq->flags |= mask; } static inline void fq_clear(struct qman_fq *fq, u32 mask) { - clear_bits(mask, &fq->flags); + fq->flags &= ~mask; } static inline int fq_isset(struct qman_fq *fq, u32 mask) @@ -1562,7 +1562,7 @@ void qman_p_irqsource_add(struct qman_portal *p, u32 bits) unsigned long irqflags; local_irq_save(irqflags); - set_bits(bits & QM_PIRQ_VISIBLE, &p->irq_sources); + p->irq_sources |= bits & QM_PIRQ_VISIBLE; qm_out(&p->p, QM_REG_IER, p->irq_sources); local_irq_restore(irqflags); } @@ -1585,7 +1585,7 @@ void qman_p_irqsource_remove(struct qman_portal *p, u32 bits) */ local_irq_save(irqflags); bits &= QM_PIRQ_VISIBLE; - clear_bits(bits, &p->irq_sources); + p->irq_sources &= ~bits; qm_out(&p->p, QM_REG_IER, p->irq_sources); ier = qm_in(&p->p, QM_REG_IER); /* -- cgit From 6111d198362b9b3ffc1c253c74119cca9afd3968 Mon Sep 17 00:00:00 2001 From: Claudiu Manoil Date: Mon, 18 Sep 2017 16:39:41 -0400 Subject: soc/fsl/qbman: Drop L1_CACHE_BYTES compile time check Not relevant and arch dependent. Overkill for PPC. Signed-off-by: Claudiu Manoil Signed-off-by: Roy Pledge Reviewed-by: Catalin Marinas Signed-off-by: Li Yang --- drivers/soc/fsl/qbman/dpaa_sys.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/qbman/dpaa_sys.h b/drivers/soc/fsl/qbman/dpaa_sys.h index 676af829cea0..4b1a4674d5c4 100644 --- a/drivers/soc/fsl/qbman/dpaa_sys.h +++ b/drivers/soc/fsl/qbman/dpaa_sys.h @@ -49,10 +49,6 @@ #define DPAA_PORTAL_CE 0 #define DPAA_PORTAL_CI 1 -#if (L1_CACHE_BYTES != 32) && (L1_CACHE_BYTES != 64) -#error "Unsupported Cacheline Size" -#endif - static inline void dpaa_flush(void *p) { #ifdef CONFIG_PPC -- cgit From 219e8e0580cc441504b75e291401cf108e1a78f3 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Mon, 18 Sep 2017 16:39:42 -0400 Subject: soc/fsl/qbman: Fix ARM32 typo The Kconfig symbol for 32bit ARM is 'ARM', not 'ARM32'. Signed-off-by: Valentin Rothberg Signed-off-by: Claudiu Manoil Signed-off-by: Roy Pledge Reviewed-by: Catalin Marinas Signed-off-by: Li Yang --- drivers/soc/fsl/qbman/dpaa_sys.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/qbman/dpaa_sys.h b/drivers/soc/fsl/qbman/dpaa_sys.h index 4b1a4674d5c4..61cfdb396fe5 100644 --- a/drivers/soc/fsl/qbman/dpaa_sys.h +++ b/drivers/soc/fsl/qbman/dpaa_sys.h @@ -53,7 +53,7 @@ static inline void dpaa_flush(void *p) { #ifdef CONFIG_PPC flush_dcache_range((unsigned long)p, (unsigned long)p+64); -#elif defined(CONFIG_ARM32) +#elif defined(CONFIG_ARM) __cpuc_flush_dcache_area(p, 64); #elif defined(CONFIG_ARM64) __flush_dcache_area(p, 64); -- cgit From e6e2df69c8321fcb5497a409a1a2a3139b15c3d8 Mon Sep 17 00:00:00 2001 From: Roy Pledge Date: Mon, 18 Sep 2017 16:39:43 -0400 Subject: soc/fsl/qbman: Rework portal mapping calls for ARM/PPC Rework portal mapping for PPC and ARM. The PPC devices require a cacheable coherent mapping while ARM will work with a non-cachable/write combine mapping. This also eliminates the need for manual cache flushes on ARM. This also fixes the code so sparse checking is clean. Signed-off-by: Roy Pledge Reviewed-by: Catalin Marinas Signed-off-by: Li Yang --- drivers/soc/fsl/qbman/bman.c | 18 ++++++++++-------- drivers/soc/fsl/qbman/bman_portal.c | 23 ++++++++++------------- drivers/soc/fsl/qbman/bman_priv.h | 8 +++----- drivers/soc/fsl/qbman/dpaa_sys.h | 15 +++++++++++---- drivers/soc/fsl/qbman/qman.c | 31 +++++++++++++------------------ drivers/soc/fsl/qbman/qman_portal.c | 23 ++++++++++------------- drivers/soc/fsl/qbman/qman_priv.h | 8 +++----- 7 files changed, 60 insertions(+), 66 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/qbman/bman.c b/drivers/soc/fsl/qbman/bman.c index 3acded1ccc37..1b4ff5ae3733 100644 --- a/drivers/soc/fsl/qbman/bman.c +++ b/drivers/soc/fsl/qbman/bman.c @@ -154,7 +154,8 @@ struct bm_mc { }; struct bm_addr { - void __iomem *ce; /* cache-enabled */ + void *ce; /* cache-enabled */ + __be32 *ce_be; /* Same as above but for direct access */ void __iomem *ci; /* cache-inhibited */ }; @@ -167,12 +168,12 @@ struct bm_portal { /* Cache-inhibited register access. */ static inline u32 bm_in(struct bm_portal *p, u32 offset) { - return be32_to_cpu(__raw_readl(p->addr.ci + offset)); + return ioread32be(p->addr.ci + offset); } static inline void bm_out(struct bm_portal *p, u32 offset, u32 val) { - __raw_writel(cpu_to_be32(val), p->addr.ci + offset); + iowrite32be(val, p->addr.ci + offset); } /* Cache Enabled Portal Access */ @@ -188,7 +189,7 @@ static inline void bm_cl_touch_ro(struct bm_portal *p, u32 offset) static inline u32 bm_ce_in(struct bm_portal *p, u32 offset) { - return be32_to_cpu(__raw_readl(p->addr.ce + offset)); + return be32_to_cpu(*(p->addr.ce_be + (offset/4))); } struct bman_portal { @@ -408,7 +409,7 @@ static int bm_mc_init(struct bm_portal *portal) mc->cr = portal->addr.ce + BM_CL_CR; mc->rr = portal->addr.ce + BM_CL_RR0; - mc->rridx = (__raw_readb(&mc->cr->_ncw_verb) & BM_MCC_VERB_VBIT) ? + mc->rridx = (mc->cr->_ncw_verb & BM_MCC_VERB_VBIT) ? 0 : 1; mc->vbit = mc->rridx ? BM_MCC_VERB_VBIT : 0; #ifdef CONFIG_FSL_DPAA_CHECKING @@ -466,7 +467,7 @@ static inline union bm_mc_result *bm_mc_result(struct bm_portal *portal) * its command is submitted and completed. This includes the valid-bit, * in case you were wondering... */ - if (!__raw_readb(&rr->verb)) { + if (!rr->verb) { dpaa_invalidate_touch_ro(rr); return NULL; } @@ -512,8 +513,9 @@ static int bman_create_portal(struct bman_portal *portal, * config, everything that follows depends on it and "config" is more * for (de)reference... */ - p->addr.ce = c->addr_virt[DPAA_PORTAL_CE]; - p->addr.ci = c->addr_virt[DPAA_PORTAL_CI]; + p->addr.ce = c->addr_virt_ce; + p->addr.ce_be = c->addr_virt_ce; + p->addr.ci = c->addr_virt_ci; if (bm_rcr_init(p, bm_rcr_pvb, bm_rcr_cce)) { dev_err(c->dev, "RCR initialisation failed\n"); goto fail_rcr; diff --git a/drivers/soc/fsl/qbman/bman_portal.c b/drivers/soc/fsl/qbman/bman_portal.c index 39b39c8f1399..2f71f7df3465 100644 --- a/drivers/soc/fsl/qbman/bman_portal.c +++ b/drivers/soc/fsl/qbman/bman_portal.c @@ -91,7 +91,6 @@ static int bman_portal_probe(struct platform_device *pdev) struct device_node *node = dev->of_node; struct bm_portal_config *pcfg; struct resource *addr_phys[2]; - void __iomem *va; int irq, cpu; pcfg = devm_kmalloc(dev, sizeof(*pcfg), GFP_KERNEL); @@ -123,23 +122,21 @@ static int bman_portal_probe(struct platform_device *pdev) } pcfg->irq = irq; - va = ioremap_prot(addr_phys[0]->start, resource_size(addr_phys[0]), 0); - if (!va) { - dev_err(dev, "ioremap::CE failed\n"); + pcfg->addr_virt_ce = memremap(addr_phys[0]->start, + resource_size(addr_phys[0]), + QBMAN_MEMREMAP_ATTR); + if (!pcfg->addr_virt_ce) { + dev_err(dev, "memremap::CE failed\n"); goto err_ioremap1; } - pcfg->addr_virt[DPAA_PORTAL_CE] = va; - - va = ioremap_prot(addr_phys[1]->start, resource_size(addr_phys[1]), - _PAGE_GUARDED | _PAGE_NO_CACHE); - if (!va) { + pcfg->addr_virt_ci = ioremap(addr_phys[1]->start, + resource_size(addr_phys[1])); + if (!pcfg->addr_virt_ci) { dev_err(dev, "ioremap::CI failed\n"); goto err_ioremap2; } - pcfg->addr_virt[DPAA_PORTAL_CI] = va; - spin_lock(&bman_lock); cpu = cpumask_next_zero(-1, &portal_cpus); if (cpu >= nr_cpu_ids) { @@ -164,9 +161,9 @@ static int bman_portal_probe(struct platform_device *pdev) return 0; err_portal_init: - iounmap(pcfg->addr_virt[DPAA_PORTAL_CI]); + iounmap(pcfg->addr_virt_ci); err_ioremap2: - iounmap(pcfg->addr_virt[DPAA_PORTAL_CE]); + memunmap(pcfg->addr_virt_ce); err_ioremap1: return -ENXIO; } diff --git a/drivers/soc/fsl/qbman/bman_priv.h b/drivers/soc/fsl/qbman/bman_priv.h index f6896a2f6d90..751ce90383b7 100644 --- a/drivers/soc/fsl/qbman/bman_priv.h +++ b/drivers/soc/fsl/qbman/bman_priv.h @@ -46,11 +46,9 @@ extern u16 bman_ip_rev; /* 0 if uninitialised, otherwise BMAN_REVx */ extern struct gen_pool *bm_bpalloc; struct bm_portal_config { - /* - * Corenet portal addresses; - * [0]==cache-enabled, [1]==cache-inhibited. - */ - void __iomem *addr_virt[2]; + /* Portal addresses */ + void *addr_virt_ce; + void __iomem *addr_virt_ci; /* Allow these to be joined in lists */ struct list_head list; struct device *dev; diff --git a/drivers/soc/fsl/qbman/dpaa_sys.h b/drivers/soc/fsl/qbman/dpaa_sys.h index 61cfdb396fe5..5a2c0af2489e 100644 --- a/drivers/soc/fsl/qbman/dpaa_sys.h +++ b/drivers/soc/fsl/qbman/dpaa_sys.h @@ -51,12 +51,12 @@ static inline void dpaa_flush(void *p) { + /* + * Only PPC needs to flush the cache currently - on ARM the mapping + * is non cacheable + */ #ifdef CONFIG_PPC flush_dcache_range((unsigned long)p, (unsigned long)p+64); -#elif defined(CONFIG_ARM) - __cpuc_flush_dcache_area(p, 64); -#elif defined(CONFIG_ARM64) - __flush_dcache_area(p, 64); #endif } @@ -102,4 +102,11 @@ static inline u8 dpaa_cyc_diff(u8 ringsize, u8 first, u8 last) int qbman_init_private_mem(struct device *dev, int idx, dma_addr_t *addr, size_t *size); +/* memremap() attributes for different platforms */ +#ifdef CONFIG_PPC +#define QBMAN_MEMREMAP_ATTR MEMREMAP_WB +#else +#define QBMAN_MEMREMAP_ATTR MEMREMAP_WC +#endif + #endif /* __DPAA_SYS_H */ diff --git a/drivers/soc/fsl/qbman/qman.c b/drivers/soc/fsl/qbman/qman.c index 8829bbd31580..23fcc0b2498f 100644 --- a/drivers/soc/fsl/qbman/qman.c +++ b/drivers/soc/fsl/qbman/qman.c @@ -300,7 +300,8 @@ struct qm_mc { }; struct qm_addr { - void __iomem *ce; /* cache-enabled */ + void *ce; /* cache-enabled */ + __be32 *ce_be; /* same value as above but for direct access */ void __iomem *ci; /* cache-inhibited */ }; @@ -321,12 +322,12 @@ struct qm_portal { /* Cache-inhibited register access. */ static inline u32 qm_in(struct qm_portal *p, u32 offset) { - return be32_to_cpu(__raw_readl(p->addr.ci + offset)); + return ioread32be(p->addr.ci + offset); } static inline void qm_out(struct qm_portal *p, u32 offset, u32 val) { - __raw_writel(cpu_to_be32(val), p->addr.ci + offset); + iowrite32be(val, p->addr.ci + offset); } /* Cache Enabled Portal Access */ @@ -342,7 +343,7 @@ static inline void qm_cl_touch_ro(struct qm_portal *p, u32 offset) static inline u32 qm_ce_in(struct qm_portal *p, u32 offset) { - return be32_to_cpu(__raw_readl(p->addr.ce + offset)); + return be32_to_cpu(*(p->addr.ce_be + (offset/4))); } /* --- EQCR API --- */ @@ -646,11 +647,7 @@ static inline void qm_dqrr_pvb_update(struct qm_portal *portal) */ dpaa_invalidate_touch_ro(res); #endif - /* - * when accessing 'verb', use __raw_readb() to ensure that compiler - * inlining doesn't try to optimise out "excess reads". - */ - if ((__raw_readb(&res->verb) & QM_DQRR_VERB_VBIT) == dqrr->vbit) { + if ((res->verb & QM_DQRR_VERB_VBIT) == dqrr->vbit) { dqrr->pi = (dqrr->pi + 1) & (QM_DQRR_SIZE - 1); if (!dqrr->pi) dqrr->vbit ^= QM_DQRR_VERB_VBIT; @@ -777,11 +774,8 @@ static inline void qm_mr_pvb_update(struct qm_portal *portal) union qm_mr_entry *res = qm_cl(mr->ring, mr->pi); DPAA_ASSERT(mr->pmode == qm_mr_pvb); - /* - * when accessing 'verb', use __raw_readb() to ensure that compiler - * inlining doesn't try to optimise out "excess reads". - */ - if ((__raw_readb(&res->verb) & QM_MR_VERB_VBIT) == mr->vbit) { + + if ((res->verb & QM_MR_VERB_VBIT) == mr->vbit) { mr->pi = (mr->pi + 1) & (QM_MR_SIZE - 1); if (!mr->pi) mr->vbit ^= QM_MR_VERB_VBIT; @@ -822,7 +816,7 @@ static inline int qm_mc_init(struct qm_portal *portal) mc->cr = portal->addr.ce + QM_CL_CR; mc->rr = portal->addr.ce + QM_CL_RR0; - mc->rridx = (__raw_readb(&mc->cr->_ncw_verb) & QM_MCC_VERB_VBIT) + mc->rridx = (mc->cr->_ncw_verb & QM_MCC_VERB_VBIT) ? 0 : 1; mc->vbit = mc->rridx ? QM_MCC_VERB_VBIT : 0; #ifdef CONFIG_FSL_DPAA_CHECKING @@ -880,7 +874,7 @@ static inline union qm_mc_result *qm_mc_result(struct qm_portal *portal) * its command is submitted and completed. This includes the valid-bit, * in case you were wondering... */ - if (!__raw_readb(&rr->verb)) { + if (!rr->verb) { dpaa_invalidate_touch_ro(rr); return NULL; } @@ -1120,8 +1114,9 @@ static int qman_create_portal(struct qman_portal *portal, * config, everything that follows depends on it and "config" is more * for (de)reference */ - p->addr.ce = c->addr_virt[DPAA_PORTAL_CE]; - p->addr.ci = c->addr_virt[DPAA_PORTAL_CI]; + p->addr.ce = c->addr_virt_ce; + p->addr.ce_be = c->addr_virt_ce; + p->addr.ci = c->addr_virt_ci; /* * If CI-stashing is used, the current defaults use a threshold of 3, * and stash with high-than-DQRR priority. diff --git a/drivers/soc/fsl/qbman/qman_portal.c b/drivers/soc/fsl/qbman/qman_portal.c index cbacdf4f98ed..a120002b630e 100644 --- a/drivers/soc/fsl/qbman/qman_portal.c +++ b/drivers/soc/fsl/qbman/qman_portal.c @@ -224,7 +224,6 @@ static int qman_portal_probe(struct platform_device *pdev) struct device_node *node = dev->of_node; struct qm_portal_config *pcfg; struct resource *addr_phys[2]; - void __iomem *va; int irq, cpu, err; u32 val; @@ -262,23 +261,21 @@ static int qman_portal_probe(struct platform_device *pdev) } pcfg->irq = irq; - va = ioremap_prot(addr_phys[0]->start, resource_size(addr_phys[0]), 0); - if (!va) { - dev_err(dev, "ioremap::CE failed\n"); + pcfg->addr_virt_ce = memremap(addr_phys[0]->start, + resource_size(addr_phys[0]), + QBMAN_MEMREMAP_ATTR); + if (!pcfg->addr_virt_ce) { + dev_err(dev, "memremap::CE failed\n"); goto err_ioremap1; } - pcfg->addr_virt[DPAA_PORTAL_CE] = va; - - va = ioremap_prot(addr_phys[1]->start, resource_size(addr_phys[1]), - _PAGE_GUARDED | _PAGE_NO_CACHE); - if (!va) { + pcfg->addr_virt_ci = ioremap(addr_phys[1]->start, + resource_size(addr_phys[1])); + if (!pcfg->addr_virt_ci) { dev_err(dev, "ioremap::CI failed\n"); goto err_ioremap2; } - pcfg->addr_virt[DPAA_PORTAL_CI] = va; - pcfg->pools = qm_get_pools_sdqcr(); spin_lock(&qman_lock); @@ -310,9 +307,9 @@ static int qman_portal_probe(struct platform_device *pdev) return 0; err_portal_init: - iounmap(pcfg->addr_virt[DPAA_PORTAL_CI]); + iounmap(pcfg->addr_virt_ci); err_ioremap2: - iounmap(pcfg->addr_virt[DPAA_PORTAL_CE]); + memunmap(pcfg->addr_virt_ce); err_ioremap1: return -ENXIO; } diff --git a/drivers/soc/fsl/qbman/qman_priv.h b/drivers/soc/fsl/qbman/qman_priv.h index b1e2cbfd449b..9407d2e72c02 100644 --- a/drivers/soc/fsl/qbman/qman_priv.h +++ b/drivers/soc/fsl/qbman/qman_priv.h @@ -153,11 +153,9 @@ static inline void qman_cgrs_xor(struct qman_cgrs *dest, void qman_init_cgr_all(void); struct qm_portal_config { - /* - * Corenet portal addresses; - * [0]==cache-enabled, [1]==cache-inhibited. - */ - void __iomem *addr_virt[2]; + /* Portal addresses */ + void *addr_virt_ce; + void __iomem *addr_virt_ci; struct device *dev; struct iommu_domain *iommu_domain; /* Allow these to be joined in lists */ -- cgit From 2c62f8b6fbd0431ccfb31b730a299d864a6bb8c9 Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Mon, 18 Sep 2017 16:39:44 -0400 Subject: soc/fsl/qbman: add QMAN_REV32 Add revision 3.2 of the QBMan block. This is the version for LS1043A and LS1046A SoCs. Signed-off-by: Madalin Bucur Signed-off-by: Roy Pledge Signed-off-by: Li Yang --- drivers/soc/fsl/qbman/qman_ccsr.c | 2 ++ drivers/soc/fsl/qbman/qman_priv.h | 1 + 2 files changed, 3 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/qbman/qman_ccsr.c b/drivers/soc/fsl/qbman/qman_ccsr.c index 607355b91d45..79cba58387a5 100644 --- a/drivers/soc/fsl/qbman/qman_ccsr.c +++ b/drivers/soc/fsl/qbman/qman_ccsr.c @@ -719,6 +719,8 @@ static int fsl_qman_probe(struct platform_device *pdev) qman_ip_rev = QMAN_REV30; else if (major == 3 && minor == 1) qman_ip_rev = QMAN_REV31; + else if (major == 3 && minor == 2) + qman_ip_rev = QMAN_REV32; else { dev_err(dev, "Unknown QMan version\n"); return -ENODEV; diff --git a/drivers/soc/fsl/qbman/qman_priv.h b/drivers/soc/fsl/qbman/qman_priv.h index 9407d2e72c02..75a8f905f8f7 100644 --- a/drivers/soc/fsl/qbman/qman_priv.h +++ b/drivers/soc/fsl/qbman/qman_priv.h @@ -183,6 +183,7 @@ struct qm_portal_config { #define QMAN_REV20 0x0200 #define QMAN_REV30 0x0300 #define QMAN_REV31 0x0301 +#define QMAN_REV32 0x0302 extern u16 qman_ip_rev; /* 0 if uninitialised, otherwise QMAN_REVx */ #define QM_FQID_RANGE_START 1 /* FQID 0 reserved for internal use */ -- cgit From 21772c4355707fd1c919033adea67f29d7689f89 Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Mon, 18 Sep 2017 16:39:45 -0400 Subject: soc/fsl/qbman: different register offsets on ARM Signed-off-by: Madalin Bucur Signed-off-by: Claudiu Manoil Signed-off-by: Roy Pledge Signed-off-by: Li Yang --- drivers/soc/fsl/qbman/bman.c | 22 ++++++++++++++++++++++ drivers/soc/fsl/qbman/qman.c | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/qbman/bman.c b/drivers/soc/fsl/qbman/bman.c index 1b4ff5ae3733..f9485cedc648 100644 --- a/drivers/soc/fsl/qbman/bman.c +++ b/drivers/soc/fsl/qbman/bman.c @@ -35,6 +35,27 @@ /* Portal register assists */ +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) +/* Cache-inhibited register offsets */ +#define BM_REG_RCR_PI_CINH 0x3000 +#define BM_REG_RCR_CI_CINH 0x3100 +#define BM_REG_RCR_ITR 0x3200 +#define BM_REG_CFG 0x3300 +#define BM_REG_SCN(n) (0x3400 + ((n) << 6)) +#define BM_REG_ISR 0x3e00 +#define BM_REG_IER 0x3e40 +#define BM_REG_ISDR 0x3e80 +#define BM_REG_IIR 0x3ec0 + +/* Cache-enabled register offsets */ +#define BM_CL_CR 0x0000 +#define BM_CL_RR0 0x0100 +#define BM_CL_RR1 0x0140 +#define BM_CL_RCR 0x1000 +#define BM_CL_RCR_PI_CENA 0x3000 +#define BM_CL_RCR_CI_CENA 0x3100 + +#else /* Cache-inhibited register offsets */ #define BM_REG_RCR_PI_CINH 0x0000 #define BM_REG_RCR_CI_CINH 0x0004 @@ -53,6 +74,7 @@ #define BM_CL_RCR 0x1000 #define BM_CL_RCR_PI_CENA 0x3000 #define BM_CL_RCR_CI_CENA 0x3100 +#endif /* * Portal modes. diff --git a/drivers/soc/fsl/qbman/qman.c b/drivers/soc/fsl/qbman/qman.c index 23fcc0b2498f..e4f5bb056fd2 100644 --- a/drivers/soc/fsl/qbman/qman.c +++ b/drivers/soc/fsl/qbman/qman.c @@ -41,6 +41,43 @@ /* Portal register assists */ +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) +/* Cache-inhibited register offsets */ +#define QM_REG_EQCR_PI_CINH 0x3000 +#define QM_REG_EQCR_CI_CINH 0x3040 +#define QM_REG_EQCR_ITR 0x3080 +#define QM_REG_DQRR_PI_CINH 0x3100 +#define QM_REG_DQRR_CI_CINH 0x3140 +#define QM_REG_DQRR_ITR 0x3180 +#define QM_REG_DQRR_DCAP 0x31C0 +#define QM_REG_DQRR_SDQCR 0x3200 +#define QM_REG_DQRR_VDQCR 0x3240 +#define QM_REG_DQRR_PDQCR 0x3280 +#define QM_REG_MR_PI_CINH 0x3300 +#define QM_REG_MR_CI_CINH 0x3340 +#define QM_REG_MR_ITR 0x3380 +#define QM_REG_CFG 0x3500 +#define QM_REG_ISR 0x3600 +#define QM_REG_IER 0x3640 +#define QM_REG_ISDR 0x3680 +#define QM_REG_IIR 0x36C0 +#define QM_REG_ITPR 0x3740 + +/* Cache-enabled register offsets */ +#define QM_CL_EQCR 0x0000 +#define QM_CL_DQRR 0x1000 +#define QM_CL_MR 0x2000 +#define QM_CL_EQCR_PI_CENA 0x3000 +#define QM_CL_EQCR_CI_CENA 0x3040 +#define QM_CL_DQRR_PI_CENA 0x3100 +#define QM_CL_DQRR_CI_CENA 0x3140 +#define QM_CL_MR_PI_CENA 0x3300 +#define QM_CL_MR_CI_CENA 0x3340 +#define QM_CL_CR 0x3800 +#define QM_CL_RR0 0x3900 +#define QM_CL_RR1 0x3940 + +#else /* Cache-inhibited register offsets */ #define QM_REG_EQCR_PI_CINH 0x0000 #define QM_REG_EQCR_CI_CINH 0x0004 @@ -75,6 +112,7 @@ #define QM_CL_CR 0x3800 #define QM_CL_RR0 0x3900 #define QM_CL_RR1 0x3940 +#endif /* * BTW, the drivers (and h/w programming model) already obtain the required -- cgit From e6b909fe84ba891cc523a65a6b6af07f4e977c5f Mon Sep 17 00:00:00 2001 From: Claudiu Manoil Date: Mon, 18 Sep 2017 16:39:46 -0400 Subject: soc/fsl/qbman: Add missing headers on ARM Unlike PPC builds, ARM builds need following headers explicitly: +#include for ioread32be() +#include for udelay() Signed-off-by: Claudiu Manoil Signed-off-by: Roy Pledge Signed-off-by: Li Yang --- drivers/soc/fsl/qbman/dpaa_sys.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/qbman/dpaa_sys.h b/drivers/soc/fsl/qbman/dpaa_sys.h index 5a2c0af2489e..9f379000da85 100644 --- a/drivers/soc/fsl/qbman/dpaa_sys.h +++ b/drivers/soc/fsl/qbman/dpaa_sys.h @@ -44,6 +44,8 @@ #include #include #include +#include +#include /* For 2-element tables related to cache-inhibited and cache-enabled mappings */ #define DPAA_PORTAL_CE 0 -- cgit From e868adf21c0a25634d5dfa5b1e6dbf839306d8fa Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Mon, 18 Sep 2017 16:39:47 -0400 Subject: soc/fsl/qbman: Enable FSL_LAYERSCAPE config on ARM Signed-off-by: Madalin Bucur Signed-off-by: Claudiu Manoil [Stuart: changed to use ARCH_LAYERSCAPE] Signed-off-by: Stuart Yoder Signed-off-by: Roy Pledge Signed-off-by: Li Yang --- drivers/soc/fsl/qbman/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/qbman/Kconfig b/drivers/soc/fsl/qbman/Kconfig index 757033c0586c..fb4e6bf0a0c4 100644 --- a/drivers/soc/fsl/qbman/Kconfig +++ b/drivers/soc/fsl/qbman/Kconfig @@ -1,6 +1,6 @@ menuconfig FSL_DPAA bool "Freescale DPAA 1.x support" - depends on FSL_SOC_BOOKE + depends on (FSL_SOC_BOOKE || ARCH_LAYERSCAPE) select GENERIC_ALLOCATOR help The Freescale Data Path Acceleration Architecture (DPAA) is a set of -- cgit From 0b741b8234c86065fb6954d32d427b3f7e14756f Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Thu, 15 Jun 2017 12:37:31 -0700 Subject: soc: bcm: brcmstb: Add support for S2/S3/S5 suspend states (ARM) This commit adds support for the Broadcom STB S2/S3/S5 suspend states on ARM based SoCs. This requires quite a lot of code in order to deal with the different HW blocks that need to be quiesced during suspend: - DDR PHY SHIM - DDR memory controller and sequencer - control processor The final steps of the suspend execute in an on-chip SRAM and there is a little bit of assembly code in order to shut down the DDR PHY PLL and then go into a wfi loop until a wake-up even occurs. Conversely the resume part involves waiting for the DDR PHY PLL to come back up and resume executions where we left. For S3, because of our memory hashing (actual hashing code not included for simplicity, and is bypassed) we need to relocate the writable variables (stack) into SRAM shortly before suspending in order to leave the DRAM untouched and create a reliable hash of its contents. This code has been contributed by Brian Norris initially and has been incrementally fixed and updated to support new chips by a lot of people. Signed-off-by: Brian Norris Signed-off-by: Markus Mayer Signed-off-by: Justin Chen Signed-off-by: Gareth Powell Signed-off-by: Doug Berger Signed-off-by: Florian Fainelli --- drivers/soc/bcm/Kconfig | 2 + drivers/soc/bcm/brcmstb/Kconfig | 10 + drivers/soc/bcm/brcmstb/Makefile | 1 + drivers/soc/bcm/brcmstb/pm/Makefile | 2 + drivers/soc/bcm/brcmstb/pm/aon_defs.h | 113 +++++ drivers/soc/bcm/brcmstb/pm/pm-arm.c | 822 ++++++++++++++++++++++++++++++++++ drivers/soc/bcm/brcmstb/pm/pm.h | 78 ++++ drivers/soc/bcm/brcmstb/pm/s2-arm.S | 76 ++++ 8 files changed, 1104 insertions(+) create mode 100644 drivers/soc/bcm/brcmstb/Kconfig create mode 100644 drivers/soc/bcm/brcmstb/pm/Makefile create mode 100644 drivers/soc/bcm/brcmstb/pm/aon_defs.h create mode 100644 drivers/soc/bcm/brcmstb/pm/pm-arm.c create mode 100644 drivers/soc/bcm/brcmstb/pm/pm.h create mode 100644 drivers/soc/bcm/brcmstb/pm/s2-arm.S (limited to 'drivers/soc') diff --git a/drivers/soc/bcm/Kconfig b/drivers/soc/bcm/Kconfig index 49f1e2a75d61..055a845ed979 100644 --- a/drivers/soc/bcm/Kconfig +++ b/drivers/soc/bcm/Kconfig @@ -20,4 +20,6 @@ config SOC_BRCMSTB If unsure, say N. +source "drivers/soc/bcm/brcmstb/Kconfig" + endmenu diff --git a/drivers/soc/bcm/brcmstb/Kconfig b/drivers/soc/bcm/brcmstb/Kconfig new file mode 100644 index 000000000000..4425430119fe --- /dev/null +++ b/drivers/soc/bcm/brcmstb/Kconfig @@ -0,0 +1,10 @@ +if SOC_BRCMSTB + +config BRCMSTB_PM + bool "Support suspend/resume for STB platforms" + default y + depends on PM + depends on ARCH_BRCMSTB + select ARM_CPU_SUSPEND if ARM + +endif # SOC_BRCMSTB diff --git a/drivers/soc/bcm/brcmstb/Makefile b/drivers/soc/bcm/brcmstb/Makefile index 9120b2715d3e..01687c26535b 100644 --- a/drivers/soc/bcm/brcmstb/Makefile +++ b/drivers/soc/bcm/brcmstb/Makefile @@ -1 +1,2 @@ obj-y += common.o biuctrl.o +obj-$(CONFIG_BRCMSTB_PM) += pm/ diff --git a/drivers/soc/bcm/brcmstb/pm/Makefile b/drivers/soc/bcm/brcmstb/pm/Makefile new file mode 100644 index 000000000000..7c3d20135b7c --- /dev/null +++ b/drivers/soc/bcm/brcmstb/pm/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_ARM) += s2-arm.o pm-arm.o +AFLAGS_s2-arm.o := -march=armv7-a diff --git a/drivers/soc/bcm/brcmstb/pm/aon_defs.h b/drivers/soc/bcm/brcmstb/pm/aon_defs.h new file mode 100644 index 000000000000..fb936abd847d --- /dev/null +++ b/drivers/soc/bcm/brcmstb/pm/aon_defs.h @@ -0,0 +1,113 @@ +/* + * Always ON (AON) register interface between bootloader and Linux + * + * Copyright © 2014-2017 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __BRCMSTB_AON_DEFS_H__ +#define __BRCMSTB_AON_DEFS_H__ + +#include + +/* Magic number in upper 16-bits */ +#define BRCMSTB_S3_MAGIC_MASK 0xffff0000 +#define BRCMSTB_S3_MAGIC_SHORT 0x5AFE0000 + +enum { + /* Restore random key for AES memory verification (off = fixed key) */ + S3_FLAG_LOAD_RANDKEY = (1 << 0), + + /* Scratch buffer page table is present */ + S3_FLAG_SCRATCH_BUFFER_TABLE = (1 << 1), + + /* Skip all memory verification */ + S3_FLAG_NO_MEM_VERIFY = (1 << 2), + + /* + * Modification of this bit reserved for bootloader only. + * 1=PSCI started Linux, 0=Direct jump to Linux. + */ + S3_FLAG_PSCI_BOOT = (1 << 3), + + /* + * Modification of this bit reserved for bootloader only. + * 1=64 bit boot, 0=32 bit boot. + */ + S3_FLAG_BOOTED64 = (1 << 4), +}; + +#define BRCMSTB_HASH_LEN (128 / 8) /* 128-bit hash */ + +#define AON_REG_MAGIC_FLAGS 0x00 +#define AON_REG_CONTROL_LOW 0x04 +#define AON_REG_CONTROL_HIGH 0x08 +#define AON_REG_S3_HASH 0x0c /* hash of S3 params */ +#define AON_REG_CONTROL_HASH_LEN 0x1c +#define AON_REG_PANIC 0x20 + +#define BRCMSTB_S3_MAGIC 0x5AFEB007 +#define BRCMSTB_PANIC_MAGIC 0x512E115E +#define BOOTLOADER_SCRATCH_SIZE 64 +#define BRCMSTB_DTU_STATE_MAP_ENTRIES (8*1024) +#define BRCMSTB_DTU_CONFIG_ENTRIES (512) +#define BRCMSTB_DTU_COUNT (2) + +#define IMAGE_DESCRIPTORS_BUFSIZE (2 * 1024) +#define S3_BOOTLOADER_RESERVED (S3_FLAG_PSCI_BOOT | S3_FLAG_BOOTED64) + +struct brcmstb_bootloader_dtu_table { + uint32_t dtu_state_map[BRCMSTB_DTU_STATE_MAP_ENTRIES]; + uint32_t dtu_config[BRCMSTB_DTU_CONFIG_ENTRIES]; +}; + +/* + * Bootloader utilizes a custom parameter block left in DRAM for handling S3 + * warm resume + */ +struct brcmstb_s3_params { + /* scratch memory for bootloader */ + uint8_t scratch[BOOTLOADER_SCRATCH_SIZE]; + + uint32_t magic; /* BRCMSTB_S3_MAGIC */ + uint64_t reentry; /* PA */ + + /* descriptors */ + uint32_t hash[BRCMSTB_HASH_LEN / 4]; + + /* + * If 0, then ignore this parameter (there is only one set of + * descriptors) + * + * If non-0, then a second set of descriptors is stored at: + * + * descriptors + desc_offset_2 + * + * The MAC result of both descriptors is XOR'd and stored in @hash + */ + uint32_t desc_offset_2; + + /* + * (Physical) address of a brcmstb_bootloader_scratch_table, for + * providing a large DRAM buffer to the bootloader + */ + uint64_t buffer_table; + + uint32_t spare[70]; + + uint8_t descriptors[IMAGE_DESCRIPTORS_BUFSIZE]; + /* + * Must be last member of struct. See brcmstb_pm_s3_finish() for reason. + */ + struct brcmstb_bootloader_dtu_table dtu[BRCMSTB_DTU_COUNT]; +} __packed; + +#endif /* __BRCMSTB_AON_DEFS_H__ */ diff --git a/drivers/soc/bcm/brcmstb/pm/pm-arm.c b/drivers/soc/bcm/brcmstb/pm/pm-arm.c new file mode 100644 index 000000000000..dcf8c8065508 --- /dev/null +++ b/drivers/soc/bcm/brcmstb/pm/pm-arm.c @@ -0,0 +1,822 @@ +/* + * ARM-specific support for Broadcom STB S2/S3/S5 power management + * + * S2: clock gate CPUs and as many peripherals as possible + * S3: power off all of the chip except the Always ON (AON) island; keep DDR is + * self-refresh + * S5: (a.k.a. S3 cold boot) much like S3, except DDR is powered down, so we + * treat this mode like a soft power-off, with wakeup allowed from AON + * + * Copyright © 2014-2017 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "brcmstb-pm: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "pm.h" +#include "aon_defs.h" + +#define SHIMPHY_DDR_PAD_CNTRL 0x8c + +/* Method #0 */ +#define SHIMPHY_PAD_PLL_SEQUENCE BIT(8) +#define SHIMPHY_PAD_GATE_PLL_S3 BIT(9) + +/* Method #1 */ +#define PWRDWN_SEQ_NO_SEQUENCING 0 +#define PWRDWN_SEQ_HOLD_CHANNEL 1 +#define PWRDWN_SEQ_RESET_PLL 2 +#define PWRDWN_SEQ_POWERDOWN_PLL 3 + +#define SHIMPHY_PAD_S3_PWRDWN_SEQ_MASK 0x00f00000 +#define SHIMPHY_PAD_S3_PWRDWN_SEQ_SHIFT 20 + +#define DDR_FORCE_CKE_RST_N BIT(3) +#define DDR_PHY_RST_N BIT(2) +#define DDR_PHY_CKE BIT(1) + +#define DDR_PHY_NO_CHANNEL 0xffffffff + +#define MAX_NUM_MEMC 3 + +struct brcmstb_memc { + void __iomem *ddr_phy_base; + void __iomem *ddr_shimphy_base; + void __iomem *ddr_ctrl; +}; + +struct brcmstb_pm_control { + void __iomem *aon_ctrl_base; + void __iomem *aon_sram; + struct brcmstb_memc memcs[MAX_NUM_MEMC]; + + void __iomem *boot_sram; + size_t boot_sram_len; + + bool support_warm_boot; + size_t pll_status_offset; + int num_memc; + + struct brcmstb_s3_params *s3_params; + dma_addr_t s3_params_pa; + int s3entry_method; + u32 warm_boot_offset; + u32 phy_a_standby_ctrl_offs; + u32 phy_b_standby_ctrl_offs; + bool needs_ddr_pad; + struct platform_device *pdev; +}; + +enum bsp_initiate_command { + BSP_CLOCK_STOP = 0x00, + BSP_GEN_RANDOM_KEY = 0x4A, + BSP_RESTORE_RANDOM_KEY = 0x55, + BSP_GEN_FIXED_KEY = 0x63, +}; + +#define PM_INITIATE 0x01 +#define PM_INITIATE_SUCCESS 0x00 +#define PM_INITIATE_FAIL 0xfe + +static struct brcmstb_pm_control ctrl; + +static int (*brcmstb_pm_do_s2_sram)(void __iomem *aon_ctrl_base, + void __iomem *ddr_phy_pll_status); + +static int brcmstb_init_sram(struct device_node *dn) +{ + void __iomem *sram; + struct resource res; + int ret; + + ret = of_address_to_resource(dn, 0, &res); + if (ret) + return ret; + + /* Uncached, executable remapping of SRAM */ + sram = __arm_ioremap_exec(res.start, resource_size(&res), false); + if (!sram) + return -ENOMEM; + + ctrl.boot_sram = sram; + ctrl.boot_sram_len = resource_size(&res); + + return 0; +} + +static const struct of_device_id sram_dt_ids[] = { + { .compatible = "mmio-sram" }, + { /* sentinel */ } +}; + +static int do_bsp_initiate_command(enum bsp_initiate_command cmd) +{ + void __iomem *base = ctrl.aon_ctrl_base; + int ret; + int timeo = 1000 * 1000; /* 1 second */ + + writel_relaxed(0, base + AON_CTRL_PM_INITIATE); + (void)readl_relaxed(base + AON_CTRL_PM_INITIATE); + + /* Go! */ + writel_relaxed((cmd << 1) | PM_INITIATE, base + AON_CTRL_PM_INITIATE); + + /* + * If firmware doesn't support the 'ack', then just assume it's done + * after 10ms. Note that this only works for command 0, BSP_CLOCK_STOP + */ + if (of_machine_is_compatible("brcm,bcm74371a0")) { + (void)readl_relaxed(base + AON_CTRL_PM_INITIATE); + mdelay(10); + return 0; + } + + for (;;) { + ret = readl_relaxed(base + AON_CTRL_PM_INITIATE); + if (!(ret & PM_INITIATE)) + break; + if (timeo <= 0) { + pr_err("error: timeout waiting for BSP (%x)\n", ret); + break; + } + timeo -= 50; + udelay(50); + } + + return (ret & 0xff) != PM_INITIATE_SUCCESS; +} + +static int brcmstb_pm_handshake(void) +{ + void __iomem *base = ctrl.aon_ctrl_base; + u32 tmp; + int ret; + + /* BSP power handshake, v1 */ + tmp = readl_relaxed(base + AON_CTRL_HOST_MISC_CMDS); + tmp &= ~1UL; + writel_relaxed(tmp, base + AON_CTRL_HOST_MISC_CMDS); + (void)readl_relaxed(base + AON_CTRL_HOST_MISC_CMDS); + + ret = do_bsp_initiate_command(BSP_CLOCK_STOP); + if (ret) + pr_err("BSP handshake failed\n"); + + /* + * HACK: BSP may have internal race on the CLOCK_STOP command. + * Avoid touching the BSP for a few milliseconds. + */ + mdelay(3); + + return ret; +} + +static inline void shimphy_set(u32 value, u32 mask) +{ + int i; + + if (!ctrl.needs_ddr_pad) + return; + + for (i = 0; i < ctrl.num_memc; i++) { + u32 tmp; + + tmp = readl_relaxed(ctrl.memcs[i].ddr_shimphy_base + + SHIMPHY_DDR_PAD_CNTRL); + tmp = value | (tmp & mask); + writel_relaxed(tmp, ctrl.memcs[i].ddr_shimphy_base + + SHIMPHY_DDR_PAD_CNTRL); + } + wmb(); /* Complete sequence in order. */ +} + +static inline void ddr_ctrl_set(bool warmboot) +{ + int i; + + for (i = 0; i < ctrl.num_memc; i++) { + u32 tmp; + + tmp = readl_relaxed(ctrl.memcs[i].ddr_ctrl + + ctrl.warm_boot_offset); + if (warmboot) + tmp |= 1; + else + tmp &= ~1; /* Cold boot */ + writel_relaxed(tmp, ctrl.memcs[i].ddr_ctrl + + ctrl.warm_boot_offset); + } + /* Complete sequence in order */ + wmb(); +} + +static inline void s3entry_method0(void) +{ + shimphy_set(SHIMPHY_PAD_GATE_PLL_S3 | SHIMPHY_PAD_PLL_SEQUENCE, + 0xffffffff); +} + +static inline void s3entry_method1(void) +{ + /* + * S3 Entry Sequence + * ----------------- + * Step 1: SHIMPHY_ADDR_CNTL_0_DDR_PAD_CNTRL [ S3_PWRDWN_SEQ ] = 3 + * Step 2: MEMC_DDR_0_WARM_BOOT [ WARM_BOOT ] = 1 + */ + shimphy_set((PWRDWN_SEQ_POWERDOWN_PLL << + SHIMPHY_PAD_S3_PWRDWN_SEQ_SHIFT), + ~SHIMPHY_PAD_S3_PWRDWN_SEQ_MASK); + + ddr_ctrl_set(true); +} + +static inline void s5entry_method1(void) +{ + int i; + + /* + * S5 Entry Sequence + * ----------------- + * Step 1: SHIMPHY_ADDR_CNTL_0_DDR_PAD_CNTRL [ S3_PWRDWN_SEQ ] = 3 + * Step 2: MEMC_DDR_0_WARM_BOOT [ WARM_BOOT ] = 0 + * Step 3: DDR_PHY_CONTROL_REGS_[AB]_0_STANDBY_CONTROL[ CKE ] = 0 + * DDR_PHY_CONTROL_REGS_[AB]_0_STANDBY_CONTROL[ RST_N ] = 0 + */ + shimphy_set((PWRDWN_SEQ_POWERDOWN_PLL << + SHIMPHY_PAD_S3_PWRDWN_SEQ_SHIFT), + ~SHIMPHY_PAD_S3_PWRDWN_SEQ_MASK); + + ddr_ctrl_set(false); + + for (i = 0; i < ctrl.num_memc; i++) { + u32 tmp; + + /* Step 3: Channel A (RST_N = CKE = 0) */ + tmp = readl_relaxed(ctrl.memcs[i].ddr_phy_base + + ctrl.phy_a_standby_ctrl_offs); + tmp &= ~(DDR_PHY_RST_N | DDR_PHY_RST_N); + writel_relaxed(tmp, ctrl.memcs[i].ddr_phy_base + + ctrl.phy_a_standby_ctrl_offs); + + /* Step 3: Channel B? */ + if (ctrl.phy_b_standby_ctrl_offs != DDR_PHY_NO_CHANNEL) { + tmp = readl_relaxed(ctrl.memcs[i].ddr_phy_base + + ctrl.phy_b_standby_ctrl_offs); + tmp &= ~(DDR_PHY_RST_N | DDR_PHY_RST_N); + writel_relaxed(tmp, ctrl.memcs[i].ddr_phy_base + + ctrl.phy_b_standby_ctrl_offs); + } + } + /* Must complete */ + wmb(); +} + +/* + * Run a Power Management State Machine (PMSM) shutdown command and put the CPU + * into a low-power mode + */ +static void brcmstb_do_pmsm_power_down(unsigned long base_cmd, bool onewrite) +{ + void __iomem *base = ctrl.aon_ctrl_base; + + if ((ctrl.s3entry_method == 1) && (base_cmd == PM_COLD_CONFIG)) + s5entry_method1(); + + /* pm_start_pwrdn transition 0->1 */ + writel_relaxed(base_cmd, base + AON_CTRL_PM_CTRL); + + if (!onewrite) { + (void)readl_relaxed(base + AON_CTRL_PM_CTRL); + + writel_relaxed(base_cmd | PM_PWR_DOWN, base + AON_CTRL_PM_CTRL); + (void)readl_relaxed(base + AON_CTRL_PM_CTRL); + } + wfi(); +} + +/* Support S5 cold boot out of "poweroff" */ +static void brcmstb_pm_poweroff(void) +{ + brcmstb_pm_handshake(); + + /* Clear magic S3 warm-boot value */ + writel_relaxed(0, ctrl.aon_sram + AON_REG_MAGIC_FLAGS); + (void)readl_relaxed(ctrl.aon_sram + AON_REG_MAGIC_FLAGS); + + /* Skip wait-for-interrupt signal; just use a countdown */ + writel_relaxed(0x10, ctrl.aon_ctrl_base + AON_CTRL_PM_CPU_WAIT_COUNT); + (void)readl_relaxed(ctrl.aon_ctrl_base + AON_CTRL_PM_CPU_WAIT_COUNT); + + if (ctrl.s3entry_method == 1) { + shimphy_set((PWRDWN_SEQ_POWERDOWN_PLL << + SHIMPHY_PAD_S3_PWRDWN_SEQ_SHIFT), + ~SHIMPHY_PAD_S3_PWRDWN_SEQ_MASK); + ddr_ctrl_set(false); + brcmstb_do_pmsm_power_down(M1_PM_COLD_CONFIG, true); + return; /* We should never actually get here */ + } + + brcmstb_do_pmsm_power_down(PM_COLD_CONFIG, false); +} + +static void *brcmstb_pm_copy_to_sram(void *fn, size_t len) +{ + unsigned int size = ALIGN(len, FNCPY_ALIGN); + + if (ctrl.boot_sram_len < size) { + pr_err("standby code will not fit in SRAM\n"); + return NULL; + } + + return fncpy(ctrl.boot_sram, fn, size); +} + +/* + * S2 suspend/resume picks up where we left off, so we must execute carefully + * from SRAM, in order to allow DDR to come back up safely before we continue. + */ +static int brcmstb_pm_s2(void) +{ + /* A previous S3 can set a value hazardous to S2, so make sure. */ + if (ctrl.s3entry_method == 1) { + shimphy_set((PWRDWN_SEQ_NO_SEQUENCING << + SHIMPHY_PAD_S3_PWRDWN_SEQ_SHIFT), + ~SHIMPHY_PAD_S3_PWRDWN_SEQ_MASK); + ddr_ctrl_set(false); + } + + brcmstb_pm_do_s2_sram = brcmstb_pm_copy_to_sram(&brcmstb_pm_do_s2, + brcmstb_pm_do_s2_sz); + if (!brcmstb_pm_do_s2_sram) + return -EINVAL; + + return brcmstb_pm_do_s2_sram(ctrl.aon_ctrl_base, + ctrl.memcs[0].ddr_phy_base + + ctrl.pll_status_offset); +} + +/* + * This function is called on a new stack, so don't allow inlining (which will + * generate stack references on the old stack). It cannot be made static because + * it is referenced from brcmstb_pm_s3() + */ +noinline int brcmstb_pm_s3_finish(void) +{ + struct brcmstb_s3_params *params = ctrl.s3_params; + dma_addr_t params_pa = ctrl.s3_params_pa; + phys_addr_t reentry = virt_to_phys(&cpu_resume); + enum bsp_initiate_command cmd; + u32 flags; + + /* + * Clear parameter structure, but not DTU area, which has already been + * filled in. We know DTU is a the end, so we can just subtract its + * size. + */ + memset(params, 0, sizeof(*params) - sizeof(params->dtu)); + + flags = readl_relaxed(ctrl.aon_sram + AON_REG_MAGIC_FLAGS); + + flags &= S3_BOOTLOADER_RESERVED; + flags |= S3_FLAG_NO_MEM_VERIFY; + flags |= S3_FLAG_LOAD_RANDKEY; + + /* Load random / fixed key */ + if (flags & S3_FLAG_LOAD_RANDKEY) + cmd = BSP_GEN_RANDOM_KEY; + else + cmd = BSP_GEN_FIXED_KEY; + if (do_bsp_initiate_command(cmd)) { + pr_info("key loading failed\n"); + return -EIO; + } + + params->magic = BRCMSTB_S3_MAGIC; + params->reentry = reentry; + + /* No more writes to DRAM */ + flush_cache_all(); + + flags |= BRCMSTB_S3_MAGIC_SHORT; + + writel_relaxed(flags, ctrl.aon_sram + AON_REG_MAGIC_FLAGS); + writel_relaxed(lower_32_bits(params_pa), + ctrl.aon_sram + AON_REG_CONTROL_LOW); + writel_relaxed(upper_32_bits(params_pa), + ctrl.aon_sram + AON_REG_CONTROL_HIGH); + + switch (ctrl.s3entry_method) { + case 0: + s3entry_method0(); + brcmstb_do_pmsm_power_down(PM_WARM_CONFIG, false); + break; + case 1: + s3entry_method1(); + brcmstb_do_pmsm_power_down(M1_PM_WARM_CONFIG, true); + break; + default: + return -EINVAL; + } + + /* Must have been interrupted from wfi()? */ + return -EINTR; +} + +static int brcmstb_pm_do_s3(unsigned long sp) +{ + unsigned long save_sp; + int ret; + + asm volatile ( + "mov %[save], sp\n" + "mov sp, %[new]\n" + "bl brcmstb_pm_s3_finish\n" + "mov %[ret], r0\n" + "mov %[new], sp\n" + "mov sp, %[save]\n" + : [save] "=&r" (save_sp), [ret] "=&r" (ret) + : [new] "r" (sp) + ); + + return ret; +} + +static int brcmstb_pm_s3(void) +{ + void __iomem *sp = ctrl.boot_sram + ctrl.boot_sram_len; + + return cpu_suspend((unsigned long)sp, brcmstb_pm_do_s3); +} + +static int brcmstb_pm_standby(bool deep_standby) +{ + int ret; + + if (brcmstb_pm_handshake()) + return -EIO; + + if (deep_standby) + ret = brcmstb_pm_s3(); + else + ret = brcmstb_pm_s2(); + if (ret) + pr_err("%s: standby failed\n", __func__); + + return ret; +} + +static int brcmstb_pm_enter(suspend_state_t state) +{ + int ret = -EINVAL; + + switch (state) { + case PM_SUSPEND_STANDBY: + ret = brcmstb_pm_standby(false); + break; + case PM_SUSPEND_MEM: + ret = brcmstb_pm_standby(true); + break; + } + + return ret; +} + +static int brcmstb_pm_valid(suspend_state_t state) +{ + switch (state) { + case PM_SUSPEND_STANDBY: + return true; + case PM_SUSPEND_MEM: + return ctrl.support_warm_boot; + default: + return false; + } +} + +static const struct platform_suspend_ops brcmstb_pm_ops = { + .enter = brcmstb_pm_enter, + .valid = brcmstb_pm_valid, +}; + +static const struct of_device_id aon_ctrl_dt_ids[] = { + { .compatible = "brcm,brcmstb-aon-ctrl" }, + {} +}; + +struct ddr_phy_ofdata { + bool supports_warm_boot; + size_t pll_status_offset; + int s3entry_method; + u32 warm_boot_offset; + u32 phy_a_standby_ctrl_offs; + u32 phy_b_standby_ctrl_offs; +}; + +static struct ddr_phy_ofdata ddr_phy_71_1 = { + .supports_warm_boot = true, + .pll_status_offset = 0x0c, + .s3entry_method = 1, + .warm_boot_offset = 0x2c, + .phy_a_standby_ctrl_offs = 0x198, + .phy_b_standby_ctrl_offs = DDR_PHY_NO_CHANNEL +}; + +static struct ddr_phy_ofdata ddr_phy_72_0 = { + .supports_warm_boot = true, + .pll_status_offset = 0x10, + .s3entry_method = 1, + .warm_boot_offset = 0x40, + .phy_a_standby_ctrl_offs = 0x2a4, + .phy_b_standby_ctrl_offs = 0x8a4 +}; + +static struct ddr_phy_ofdata ddr_phy_225_1 = { + .supports_warm_boot = false, + .pll_status_offset = 0x4, + .s3entry_method = 0 +}; + +static struct ddr_phy_ofdata ddr_phy_240_1 = { + .supports_warm_boot = true, + .pll_status_offset = 0x4, + .s3entry_method = 0 +}; + +static const struct of_device_id ddr_phy_dt_ids[] = { + { + .compatible = "brcm,brcmstb-ddr-phy-v71.1", + .data = &ddr_phy_71_1, + }, + { + .compatible = "brcm,brcmstb-ddr-phy-v72.0", + .data = &ddr_phy_72_0, + }, + { + .compatible = "brcm,brcmstb-ddr-phy-v225.1", + .data = &ddr_phy_225_1, + }, + { + .compatible = "brcm,brcmstb-ddr-phy-v240.1", + .data = &ddr_phy_240_1, + }, + { + /* Same as v240.1, for the registers we care about */ + .compatible = "brcm,brcmstb-ddr-phy-v240.2", + .data = &ddr_phy_240_1, + }, + {} +}; + +struct ddr_seq_ofdata { + bool needs_ddr_pad; + u32 warm_boot_offset; +}; + +static const struct ddr_seq_ofdata ddr_seq_b22 = { + .needs_ddr_pad = false, + .warm_boot_offset = 0x2c, +}; + +static const struct ddr_seq_ofdata ddr_seq = { + .needs_ddr_pad = true, +}; + +static const struct of_device_id ddr_shimphy_dt_ids[] = { + { .compatible = "brcm,brcmstb-ddr-shimphy-v1.0" }, + {} +}; + +static const struct of_device_id brcmstb_memc_of_match[] = { + { + .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.2", + .data = &ddr_seq_b22, + }, + { + .compatible = "brcm,brcmstb-memc-ddr", + .data = &ddr_seq, + }, + {}, +}; + +static void __iomem *brcmstb_ioremap_match(const struct of_device_id *matches, + int index, const void **ofdata) +{ + struct device_node *dn; + const struct of_device_id *match; + + dn = of_find_matching_node_and_match(NULL, matches, &match); + if (!dn) + return ERR_PTR(-EINVAL); + + if (ofdata) + *ofdata = match->data; + + return of_io_request_and_map(dn, index, dn->full_name); +} + +static int brcmstb_pm_panic_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + writel_relaxed(BRCMSTB_PANIC_MAGIC, ctrl.aon_sram + AON_REG_PANIC); + + return NOTIFY_DONE; +} + +static struct notifier_block brcmstb_pm_panic_nb = { + .notifier_call = brcmstb_pm_panic_notify, +}; + +static int brcmstb_pm_probe(struct platform_device *pdev) +{ + const struct ddr_phy_ofdata *ddr_phy_data; + const struct ddr_seq_ofdata *ddr_seq_data; + const struct of_device_id *of_id = NULL; + struct device_node *dn; + void __iomem *base; + int ret, i; + + /* AON ctrl registers */ + base = brcmstb_ioremap_match(aon_ctrl_dt_ids, 0, NULL); + if (IS_ERR(base)) { + pr_err("error mapping AON_CTRL\n"); + return PTR_ERR(base); + } + ctrl.aon_ctrl_base = base; + + /* AON SRAM registers */ + base = brcmstb_ioremap_match(aon_ctrl_dt_ids, 1, NULL); + if (IS_ERR(base)) { + /* Assume standard offset */ + ctrl.aon_sram = ctrl.aon_ctrl_base + + AON_CTRL_SYSTEM_DATA_RAM_OFS; + } else { + ctrl.aon_sram = base; + } + + writel_relaxed(0, ctrl.aon_sram + AON_REG_PANIC); + + /* DDR PHY registers */ + base = brcmstb_ioremap_match(ddr_phy_dt_ids, 0, + (const void **)&ddr_phy_data); + if (IS_ERR(base)) { + pr_err("error mapping DDR PHY\n"); + return PTR_ERR(base); + } + ctrl.support_warm_boot = ddr_phy_data->supports_warm_boot; + ctrl.pll_status_offset = ddr_phy_data->pll_status_offset; + /* Only need DDR PHY 0 for now? */ + ctrl.memcs[0].ddr_phy_base = base; + ctrl.s3entry_method = ddr_phy_data->s3entry_method; + ctrl.phy_a_standby_ctrl_offs = ddr_phy_data->phy_a_standby_ctrl_offs; + ctrl.phy_b_standby_ctrl_offs = ddr_phy_data->phy_b_standby_ctrl_offs; + /* + * Slightly grosss to use the phy ver to get a memc, + * offset but that is the only versioned things so far + * we can test for. + */ + ctrl.warm_boot_offset = ddr_phy_data->warm_boot_offset; + + /* DDR SHIM-PHY registers */ + for_each_matching_node(dn, ddr_shimphy_dt_ids) { + i = ctrl.num_memc; + if (i >= MAX_NUM_MEMC) { + pr_warn("too many MEMCs (max %d)\n", MAX_NUM_MEMC); + break; + } + + base = of_io_request_and_map(dn, 0, dn->full_name); + if (IS_ERR(base)) { + if (!ctrl.support_warm_boot) + break; + + pr_err("error mapping DDR SHIMPHY %d\n", i); + return PTR_ERR(base); + } + ctrl.memcs[i].ddr_shimphy_base = base; + ctrl.num_memc++; + } + + /* Sequencer DRAM Param and Control Registers */ + i = 0; + for_each_matching_node(dn, brcmstb_memc_of_match) { + base = of_iomap(dn, 0); + if (!base) { + pr_err("error mapping DDR Sequencer %d\n", i); + return -ENOMEM; + } + + of_id = of_match_node(brcmstb_memc_of_match, dn); + if (!of_id) { + iounmap(base); + return -EINVAL; + } + + ddr_seq_data = of_id->data; + ctrl.needs_ddr_pad = ddr_seq_data->needs_ddr_pad; + /* Adjust warm boot offset based on the DDR sequencer */ + if (ddr_seq_data->warm_boot_offset) + ctrl.warm_boot_offset = ddr_seq_data->warm_boot_offset; + + ctrl.memcs[i].ddr_ctrl = base; + i++; + } + + pr_debug("PM: supports warm boot:%d, method:%d, wboffs:%x\n", + ctrl.support_warm_boot, ctrl.s3entry_method, + ctrl.warm_boot_offset); + + dn = of_find_matching_node(NULL, sram_dt_ids); + if (!dn) { + pr_err("SRAM not found\n"); + return -EINVAL; + } + + ret = brcmstb_init_sram(dn); + if (ret) { + pr_err("error setting up SRAM for PM\n"); + return ret; + } + + ctrl.pdev = pdev; + + ctrl.s3_params = kmalloc(sizeof(*ctrl.s3_params), GFP_KERNEL); + if (!ctrl.s3_params) + return -ENOMEM; + ctrl.s3_params_pa = dma_map_single(&pdev->dev, ctrl.s3_params, + sizeof(*ctrl.s3_params), + DMA_TO_DEVICE); + if (dma_mapping_error(&pdev->dev, ctrl.s3_params_pa)) { + pr_err("error mapping DMA memory\n"); + ret = -ENOMEM; + goto out; + } + + atomic_notifier_chain_register(&panic_notifier_list, + &brcmstb_pm_panic_nb); + + pm_power_off = brcmstb_pm_poweroff; + suspend_set_ops(&brcmstb_pm_ops); + + return 0; + +out: + kfree(ctrl.s3_params); + + pr_warn("PM: initialization failed with code %d\n", ret); + + return ret; +} + +static struct platform_driver brcmstb_pm_driver = { + .driver = { + .name = "brcmstb-pm", + .of_match_table = aon_ctrl_dt_ids, + }, +}; + +static int __init brcmstb_pm_init(void) +{ + return platform_driver_probe(&brcmstb_pm_driver, + brcmstb_pm_probe); +} +module_init(brcmstb_pm_init); diff --git a/drivers/soc/bcm/brcmstb/pm/pm.h b/drivers/soc/bcm/brcmstb/pm/pm.h new file mode 100644 index 000000000000..142519fdb8f8 --- /dev/null +++ b/drivers/soc/bcm/brcmstb/pm/pm.h @@ -0,0 +1,78 @@ +/* + * Definitions for Broadcom STB power management / Always ON (AON) block + * + * Copyright © 2016-2017 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __BRCMSTB_PM_H__ +#define __BRCMSTB_PM_H__ + +#define AON_CTRL_RESET_CTRL 0x00 +#define AON_CTRL_PM_CTRL 0x04 +#define AON_CTRL_PM_STATUS 0x08 +#define AON_CTRL_PM_CPU_WAIT_COUNT 0x10 +#define AON_CTRL_PM_INITIATE 0x88 +#define AON_CTRL_HOST_MISC_CMDS 0x8c +#define AON_CTRL_SYSTEM_DATA_RAM_OFS 0x200 + +/* MIPS PM constants */ +/* MEMC0 offsets */ +#define DDR40_PHY_CONTROL_REGS_0_PLL_STATUS 0x10 +#define DDR40_PHY_CONTROL_REGS_0_STANDBY_CTRL 0xa4 + +/* TIMER offsets */ +#define TIMER_TIMER1_CTRL 0x0c +#define TIMER_TIMER1_STAT 0x1c + +/* TIMER defines */ +#define RESET_TIMER 0x0 +#define START_TIMER 0xbfffffff +#define TIMER_MASK 0x3fffffff + +/* PM_CTRL bitfield (Method #0) */ +#define PM_FAST_PWRDOWN (1 << 6) +#define PM_WARM_BOOT (1 << 5) +#define PM_DEEP_STANDBY (1 << 4) +#define PM_CPU_PWR (1 << 3) +#define PM_USE_CPU_RDY (1 << 2) +#define PM_PLL_PWRDOWN (1 << 1) +#define PM_PWR_DOWN (1 << 0) + +/* PM_CTRL bitfield (Method #1) */ +#define PM_DPHY_STANDBY_CLEAR (1 << 20) +#define PM_MIN_S3_WIDTH_TIMER_BYPASS (1 << 7) + +#define PM_S2_COMMAND (PM_PLL_PWRDOWN | PM_USE_CPU_RDY | PM_PWR_DOWN) + +/* Method 0 bitmasks */ +#define PM_COLD_CONFIG (PM_PLL_PWRDOWN | PM_DEEP_STANDBY) +#define PM_WARM_CONFIG (PM_COLD_CONFIG | PM_USE_CPU_RDY | PM_WARM_BOOT) + +/* Method 1 bitmask */ +#define M1_PM_WARM_CONFIG (PM_DPHY_STANDBY_CLEAR | \ + PM_MIN_S3_WIDTH_TIMER_BYPASS | \ + PM_WARM_BOOT | PM_DEEP_STANDBY | \ + PM_PLL_PWRDOWN | PM_PWR_DOWN) + +#define M1_PM_COLD_CONFIG (PM_DPHY_STANDBY_CLEAR | \ + PM_MIN_S3_WIDTH_TIMER_BYPASS | \ + PM_DEEP_STANDBY | \ + PM_PLL_PWRDOWN | PM_PWR_DOWN) + +#ifndef __ASSEMBLY__ + +extern const unsigned long brcmstb_pm_do_s2_sz; +extern asmlinkage int brcmstb_pm_do_s2(void __iomem *aon_ctrl_base, + void __iomem *ddr_phy_pll_status); +#endif + +#endif /* __BRCMSTB_PM_H__ */ diff --git a/drivers/soc/bcm/brcmstb/pm/s2-arm.S b/drivers/soc/bcm/brcmstb/pm/s2-arm.S new file mode 100644 index 000000000000..1d472d564638 --- /dev/null +++ b/drivers/soc/bcm/brcmstb/pm/s2-arm.S @@ -0,0 +1,76 @@ +/* + * Copyright © 2014-2017 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +#include "pm.h" + + .text + .align 3 + +#define AON_CTRL_REG r10 +#define DDR_PHY_STATUS_REG r11 + +/* + * r0: AON_CTRL base address + * r1: DDRY PHY PLL status register address + */ +ENTRY(brcmstb_pm_do_s2) + stmfd sp!, {r4-r11, lr} + mov AON_CTRL_REG, r0 + mov DDR_PHY_STATUS_REG, r1 + + /* Flush memory transactions */ + dsb + + /* Cache DDR_PHY_STATUS_REG translation */ + ldr r0, [DDR_PHY_STATUS_REG] + + /* power down request */ + ldr r0, =PM_S2_COMMAND + ldr r1, =0 + str r1, [AON_CTRL_REG, #AON_CTRL_PM_CTRL] + ldr r1, [AON_CTRL_REG, #AON_CTRL_PM_CTRL] + str r0, [AON_CTRL_REG, #AON_CTRL_PM_CTRL] + ldr r0, [AON_CTRL_REG, #AON_CTRL_PM_CTRL] + + /* Wait for interrupt */ + wfi + nop + + /* Bring MEMC back up */ +1: ldr r0, [DDR_PHY_STATUS_REG] + ands r0, #1 + beq 1b + + /* Power-up handshake */ + ldr r0, =1 + str r0, [AON_CTRL_REG, #AON_CTRL_HOST_MISC_CMDS] + ldr r0, [AON_CTRL_REG, #AON_CTRL_HOST_MISC_CMDS] + + ldr r0, =0 + str r0, [AON_CTRL_REG, #AON_CTRL_PM_CTRL] + ldr r0, [AON_CTRL_REG, #AON_CTRL_PM_CTRL] + + /* Return to caller */ + ldr r0, =0 + ldmfd sp!, {r4-r11, pc} + + ENDPROC(brcmstb_pm_do_s2) + + /* Place literal pool here */ + .ltorg + +ENTRY(brcmstb_pm_do_s2_sz) + .word . - brcmstb_pm_do_s2 -- cgit From 0e9b11413262ef3d64273e15f7631d9836c53c1a Mon Sep 17 00:00:00 2001 From: Justin Chen Date: Thu, 15 Jun 2017 15:20:31 -0700 Subject: soc bcm: brcmstb: Add support for S2/S3/S5 suspend states (MIPS) This commit adds support for the Broadcom STB S2/S3/S5 suspend states on MIPS based SoCs. This requires quite a lot of code in order to deal with the different HW blocks that need to be quiesced during suspend: - DDR PHY - DDR memory controller and arbiter - control processor The final steps of the suspend execute in cache and there is is a little bit of assembly code in order to shut down the DDR PHY PLL and then go into a wait loop until a wake-up even occurs. Conversely the resume part involves waiting for the DDR PHY PLL to come back up and resume executions where we left. Signed-off-by: Justin Chen Signed-off-by: Florian Fainelli --- drivers/soc/bcm/brcmstb/Kconfig | 2 +- drivers/soc/bcm/brcmstb/pm/Makefile | 1 + drivers/soc/bcm/brcmstb/pm/pm-mips.c | 461 +++++++++++++++++++++++++++++++++++ drivers/soc/bcm/brcmstb/pm/pm.h | 13 +- drivers/soc/bcm/brcmstb/pm/s2-mips.S | 200 +++++++++++++++ drivers/soc/bcm/brcmstb/pm/s3-mips.S | 146 +++++++++++ 6 files changed, 821 insertions(+), 2 deletions(-) create mode 100644 drivers/soc/bcm/brcmstb/pm/pm-mips.c create mode 100644 drivers/soc/bcm/brcmstb/pm/s2-mips.S create mode 100644 drivers/soc/bcm/brcmstb/pm/s3-mips.S (limited to 'drivers/soc') diff --git a/drivers/soc/bcm/brcmstb/Kconfig b/drivers/soc/bcm/brcmstb/Kconfig index 4425430119fe..d36f6e03c1a6 100644 --- a/drivers/soc/bcm/brcmstb/Kconfig +++ b/drivers/soc/bcm/brcmstb/Kconfig @@ -4,7 +4,7 @@ config BRCMSTB_PM bool "Support suspend/resume for STB platforms" default y depends on PM - depends on ARCH_BRCMSTB + depends on ARCH_BRCMSTB || BMIPS_GENERIC select ARM_CPU_SUSPEND if ARM endif # SOC_BRCMSTB diff --git a/drivers/soc/bcm/brcmstb/pm/Makefile b/drivers/soc/bcm/brcmstb/pm/Makefile index 7c3d20135b7c..08bbd244ef11 100644 --- a/drivers/soc/bcm/brcmstb/pm/Makefile +++ b/drivers/soc/bcm/brcmstb/pm/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_ARM) += s2-arm.o pm-arm.o AFLAGS_s2-arm.o := -march=armv7-a +obj-$(CONFIG_BMIPS_GENERIC) += s2-mips.o s3-mips.o pm-mips.o diff --git a/drivers/soc/bcm/brcmstb/pm/pm-mips.c b/drivers/soc/bcm/brcmstb/pm/pm-mips.c new file mode 100644 index 000000000000..9300b5f62e56 --- /dev/null +++ b/drivers/soc/bcm/brcmstb/pm/pm-mips.c @@ -0,0 +1,461 @@ +/* + * MIPS-specific support for Broadcom STB S2/S3/S5 power management + * + * Copyright (C) 2016-2017 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pm.h" + +#define S2_NUM_PARAMS 6 +#define MAX_NUM_MEMC 3 + +/* S3 constants */ +#define MAX_GP_REGS 16 +#define MAX_CP0_REGS 32 +#define NUM_MEMC_CLIENTS 128 +#define AON_CTRL_RAM_SIZE 128 +#define BRCMSTB_S3_MAGIC 0x5AFEB007 + +#define CLEAR_RESET_MASK 0x01 + +/* Index each CP0 register that needs to be saved */ +#define CONTEXT 0 +#define USER_LOCAL 1 +#define PGMK 2 +#define HWRENA 3 +#define COMPARE 4 +#define STATUS 5 +#define CONFIG 6 +#define MODE 7 +#define EDSP 8 +#define BOOT_VEC 9 +#define EBASE 10 + +struct brcmstb_memc { + void __iomem *ddr_phy_base; + void __iomem *arb_base; +}; + +struct brcmstb_pm_control { + void __iomem *aon_ctrl_base; + void __iomem *aon_sram_base; + void __iomem *timers_base; + struct brcmstb_memc memcs[MAX_NUM_MEMC]; + int num_memc; +}; + +struct brcm_pm_s3_context { + u32 cp0_regs[MAX_CP0_REGS]; + u32 memc0_rts[NUM_MEMC_CLIENTS]; + u32 sc_boot_vec; +}; + +struct brcmstb_mem_transfer; + +struct brcmstb_mem_transfer { + struct brcmstb_mem_transfer *next; + void *src; + void *dst; + dma_addr_t pa_src; + dma_addr_t pa_dst; + u32 len; + u8 key; + u8 mode; + u8 src_remapped; + u8 dst_remapped; + u8 src_dst_remapped; +}; + +#define AON_SAVE_SRAM(base, idx, val) \ + __raw_writel(val, base + (idx << 2)) + +/* Used for saving registers in asm */ +u32 gp_regs[MAX_GP_REGS]; + +#define BSP_CLOCK_STOP 0x00 +#define PM_INITIATE 0x01 + +static struct brcmstb_pm_control ctrl; + +static void brcm_pm_save_cp0_context(struct brcm_pm_s3_context *ctx) +{ + /* Generic MIPS */ + ctx->cp0_regs[CONTEXT] = read_c0_context(); + ctx->cp0_regs[USER_LOCAL] = read_c0_userlocal(); + ctx->cp0_regs[PGMK] = read_c0_pagemask(); + ctx->cp0_regs[HWRENA] = read_c0_cache(); + ctx->cp0_regs[COMPARE] = read_c0_compare(); + ctx->cp0_regs[STATUS] = read_c0_status(); + + /* Broadcom specific */ + ctx->cp0_regs[CONFIG] = read_c0_brcm_config(); + ctx->cp0_regs[MODE] = read_c0_brcm_mode(); + ctx->cp0_regs[EDSP] = read_c0_brcm_edsp(); + ctx->cp0_regs[BOOT_VEC] = read_c0_brcm_bootvec(); + ctx->cp0_regs[EBASE] = read_c0_ebase(); + + ctx->sc_boot_vec = bmips_read_zscm_reg(0xa0); +} + +static void brcm_pm_restore_cp0_context(struct brcm_pm_s3_context *ctx) +{ + /* Restore cp0 state */ + bmips_write_zscm_reg(0xa0, ctx->sc_boot_vec); + + /* Generic MIPS */ + write_c0_context(ctx->cp0_regs[CONTEXT]); + write_c0_userlocal(ctx->cp0_regs[USER_LOCAL]); + write_c0_pagemask(ctx->cp0_regs[PGMK]); + write_c0_cache(ctx->cp0_regs[HWRENA]); + write_c0_compare(ctx->cp0_regs[COMPARE]); + write_c0_status(ctx->cp0_regs[STATUS]); + + /* Broadcom specific */ + write_c0_brcm_config(ctx->cp0_regs[CONFIG]); + write_c0_brcm_mode(ctx->cp0_regs[MODE]); + write_c0_brcm_edsp(ctx->cp0_regs[EDSP]); + write_c0_brcm_bootvec(ctx->cp0_regs[BOOT_VEC]); + write_c0_ebase(ctx->cp0_regs[EBASE]); +} + +static void brcmstb_pm_handshake(void) +{ + void __iomem *base = ctrl.aon_ctrl_base; + u32 tmp; + + /* BSP power handshake, v1 */ + tmp = __raw_readl(base + AON_CTRL_HOST_MISC_CMDS); + tmp &= ~1UL; + __raw_writel(tmp, base + AON_CTRL_HOST_MISC_CMDS); + (void)__raw_readl(base + AON_CTRL_HOST_MISC_CMDS); + + __raw_writel(0, base + AON_CTRL_PM_INITIATE); + (void)__raw_readl(base + AON_CTRL_PM_INITIATE); + __raw_writel(BSP_CLOCK_STOP | PM_INITIATE, + base + AON_CTRL_PM_INITIATE); + /* + * HACK: BSP may have internal race on the CLOCK_STOP command. + * Avoid touching the BSP for a few milliseconds. + */ + mdelay(3); +} + +static void brcmstb_pm_s5(void) +{ + void __iomem *base = ctrl.aon_ctrl_base; + + brcmstb_pm_handshake(); + + /* Clear magic s3 warm-boot value */ + AON_SAVE_SRAM(ctrl.aon_sram_base, 0, 0); + + /* Set the countdown */ + __raw_writel(0x10, base + AON_CTRL_PM_CPU_WAIT_COUNT); + (void)__raw_readl(base + AON_CTRL_PM_CPU_WAIT_COUNT); + + /* Prepare to S5 cold boot */ + __raw_writel(PM_COLD_CONFIG, base + AON_CTRL_PM_CTRL); + (void)__raw_readl(base + AON_CTRL_PM_CTRL); + + __raw_writel((PM_COLD_CONFIG | PM_PWR_DOWN), base + + AON_CTRL_PM_CTRL); + (void)__raw_readl(base + AON_CTRL_PM_CTRL); + + __asm__ __volatile__( + " wait\n" + : : : "memory"); +} + +static int brcmstb_pm_s3(void) +{ + struct brcm_pm_s3_context s3_context; + void __iomem *memc_arb_base; + unsigned long flags; + u32 tmp; + int i; + + /* Prepare for s3 */ + AON_SAVE_SRAM(ctrl.aon_sram_base, 0, BRCMSTB_S3_MAGIC); + AON_SAVE_SRAM(ctrl.aon_sram_base, 1, (u32)&s3_reentry); + AON_SAVE_SRAM(ctrl.aon_sram_base, 2, 0); + + /* Clear RESET_HISTORY */ + tmp = __raw_readl(ctrl.aon_ctrl_base + AON_CTRL_RESET_CTRL); + tmp &= ~CLEAR_RESET_MASK; + __raw_writel(tmp, ctrl.aon_ctrl_base + AON_CTRL_RESET_CTRL); + + local_irq_save(flags); + + /* Inhibit DDR_RSTb pulse for both MMCs*/ + for (i = 0; i < ctrl.num_memc; i++) { + tmp = __raw_readl(ctrl.memcs[i].ddr_phy_base + + DDR40_PHY_CONTROL_REGS_0_STANDBY_CTRL); + + tmp &= ~0x0f; + __raw_writel(tmp, ctrl.memcs[i].ddr_phy_base + + DDR40_PHY_CONTROL_REGS_0_STANDBY_CTRL); + tmp |= (0x05 | BIT(5)); + __raw_writel(tmp, ctrl.memcs[i].ddr_phy_base + + DDR40_PHY_CONTROL_REGS_0_STANDBY_CTRL); + } + + /* Save CP0 context */ + brcm_pm_save_cp0_context(&s3_context); + + /* Save RTS(skip debug register) */ + memc_arb_base = ctrl.memcs[0].arb_base + 4; + for (i = 0; i < NUM_MEMC_CLIENTS; i++) { + s3_context.memc0_rts[i] = __raw_readl(memc_arb_base); + memc_arb_base += 4; + } + + /* Save I/O context */ + local_flush_tlb_all(); + _dma_cache_wback_inv(0, ~0); + + brcm_pm_do_s3(ctrl.aon_ctrl_base, current_cpu_data.dcache.linesz); + + /* CPU reconfiguration */ + local_flush_tlb_all(); + bmips_cpu_setup(); + cpumask_clear(&bmips_booted_mask); + + /* Restore RTS (skip debug register) */ + memc_arb_base = ctrl.memcs[0].arb_base + 4; + for (i = 0; i < NUM_MEMC_CLIENTS; i++) { + __raw_writel(s3_context.memc0_rts[i], memc_arb_base); + memc_arb_base += 4; + } + + /* restore CP0 context */ + brcm_pm_restore_cp0_context(&s3_context); + + local_irq_restore(flags); + + return 0; +} + +static int brcmstb_pm_s2(void) +{ + /* + * We need to pass 6 arguments to an assembly function. Lets avoid the + * stack and pass arguments in a explicit 4 byte array. The assembly + * code assumes all arguments are 4 bytes and arguments are ordered + * like so: + * + * 0: AON_CTRl base register + * 1: DDR_PHY base register + * 2: TIMERS base resgister + * 3: I-Cache line size + * 4: Restart vector address + * 5: Restart vector size + */ + u32 s2_params[6]; + + /* Prepare s2 parameters */ + s2_params[0] = (u32)ctrl.aon_ctrl_base; + s2_params[1] = (u32)ctrl.memcs[0].ddr_phy_base; + s2_params[2] = (u32)ctrl.timers_base; + s2_params[3] = (u32)current_cpu_data.icache.linesz; + s2_params[4] = (u32)BMIPS_WARM_RESTART_VEC; + s2_params[5] = (u32)(bmips_smp_int_vec_end - + bmips_smp_int_vec); + + /* Drop to standby */ + brcm_pm_do_s2(s2_params); + + return 0; +} + +static int brcmstb_pm_standby(bool deep_standby) +{ + brcmstb_pm_handshake(); + + /* Send IRQs to BMIPS_WARM_RESTART_VEC */ + clear_c0_cause(CAUSEF_IV); + irq_disable_hazard(); + set_c0_status(ST0_BEV); + irq_disable_hazard(); + + if (deep_standby) + brcmstb_pm_s3(); + else + brcmstb_pm_s2(); + + /* Send IRQs to normal runtime vectors */ + clear_c0_status(ST0_BEV); + irq_disable_hazard(); + set_c0_cause(CAUSEF_IV); + irq_disable_hazard(); + + return 0; +} + +static int brcmstb_pm_enter(suspend_state_t state) +{ + int ret = -EINVAL; + + switch (state) { + case PM_SUSPEND_STANDBY: + ret = brcmstb_pm_standby(false); + break; + case PM_SUSPEND_MEM: + ret = brcmstb_pm_standby(true); + break; + } + + return ret; +} + +static int brcmstb_pm_valid(suspend_state_t state) +{ + switch (state) { + case PM_SUSPEND_STANDBY: + return true; + case PM_SUSPEND_MEM: + return true; + default: + return false; + } +} + +static const struct platform_suspend_ops brcmstb_pm_ops = { + .enter = brcmstb_pm_enter, + .valid = brcmstb_pm_valid, +}; + +static const struct of_device_id aon_ctrl_dt_ids[] = { + { .compatible = "brcm,brcmstb-aon-ctrl" }, + { /* sentinel */ } +}; + +static const struct of_device_id ddr_phy_dt_ids[] = { + { .compatible = "brcm,brcmstb-ddr-phy" }, + { /* sentinel */ } +}; + +static const struct of_device_id arb_dt_ids[] = { + { .compatible = "brcm,brcmstb-memc-arb" }, + { /* sentinel */ } +}; + +static const struct of_device_id timers_ids[] = { + { .compatible = "brcm,brcmstb-timers" }, + { /* sentinel */ } +}; + +static inline void __iomem *brcmstb_ioremap_node(struct device_node *dn, + int index) +{ + return of_io_request_and_map(dn, index, dn->full_name); +} + +static void __iomem *brcmstb_ioremap_match(const struct of_device_id *matches, + int index, const void **ofdata) +{ + struct device_node *dn; + const struct of_device_id *match; + + dn = of_find_matching_node_and_match(NULL, matches, &match); + if (!dn) + return ERR_PTR(-EINVAL); + + if (ofdata) + *ofdata = match->data; + + return brcmstb_ioremap_node(dn, index); +} + +static int brcmstb_pm_init(void) +{ + struct device_node *dn; + void __iomem *base; + int i; + + /* AON ctrl registers */ + base = brcmstb_ioremap_match(aon_ctrl_dt_ids, 0, NULL); + if (IS_ERR(base)) { + pr_err("error mapping AON_CTRL\n"); + goto aon_err; + } + ctrl.aon_ctrl_base = base; + + /* AON SRAM registers */ + base = brcmstb_ioremap_match(aon_ctrl_dt_ids, 1, NULL); + if (IS_ERR(base)) { + pr_err("error mapping AON_SRAM\n"); + goto sram_err; + } + ctrl.aon_sram_base = base; + + ctrl.num_memc = 0; + /* Map MEMC DDR PHY registers */ + for_each_matching_node(dn, ddr_phy_dt_ids) { + i = ctrl.num_memc; + if (i >= MAX_NUM_MEMC) { + pr_warn("Too many MEMCs (max %d)\n", MAX_NUM_MEMC); + break; + } + base = brcmstb_ioremap_node(dn, 0); + if (IS_ERR(base)) + goto ddr_err; + + ctrl.memcs[i].ddr_phy_base = base; + ctrl.num_memc++; + } + + /* MEMC ARB registers */ + base = brcmstb_ioremap_match(arb_dt_ids, 0, NULL); + if (IS_ERR(base)) { + pr_err("error mapping MEMC ARB\n"); + goto ddr_err; + } + ctrl.memcs[0].arb_base = base; + + /* Timer registers */ + base = brcmstb_ioremap_match(timers_ids, 0, NULL); + if (IS_ERR(base)) { + pr_err("error mapping timers\n"); + goto tmr_err; + } + ctrl.timers_base = base; + + /* s3 cold boot aka s5 */ + pm_power_off = brcmstb_pm_s5; + + suspend_set_ops(&brcmstb_pm_ops); + + return 0; + +tmr_err: + iounmap(ctrl.memcs[0].arb_base); +ddr_err: + for (i = 0; i < ctrl.num_memc; i++) + iounmap(ctrl.memcs[i].ddr_phy_base); + + iounmap(ctrl.aon_sram_base); +sram_err: + iounmap(ctrl.aon_ctrl_base); +aon_err: + return PTR_ERR(base); +} +arch_initcall(brcmstb_pm_init); diff --git a/drivers/soc/bcm/brcmstb/pm/pm.h b/drivers/soc/bcm/brcmstb/pm/pm.h index 142519fdb8f8..b7d35ac70e60 100644 --- a/drivers/soc/bcm/brcmstb/pm/pm.h +++ b/drivers/soc/bcm/brcmstb/pm/pm.h @@ -70,9 +70,20 @@ #ifndef __ASSEMBLY__ +#ifndef CONFIG_MIPS extern const unsigned long brcmstb_pm_do_s2_sz; extern asmlinkage int brcmstb_pm_do_s2(void __iomem *aon_ctrl_base, void __iomem *ddr_phy_pll_status); -#endif +#else +/* s2 asm */ +extern asmlinkage int brcm_pm_do_s2(u32 *s2_params); + +/* s3 asm */ +extern asmlinkage int brcm_pm_do_s3(void __iomem *aon_ctrl_base, + int dcache_linesz); +extern int s3_reentry; +#endif /* CONFIG_MIPS */ + +#endif #endif /* __BRCMSTB_PM_H__ */ diff --git a/drivers/soc/bcm/brcmstb/pm/s2-mips.S b/drivers/soc/bcm/brcmstb/pm/s2-mips.S new file mode 100644 index 000000000000..27a14bc46043 --- /dev/null +++ b/drivers/soc/bcm/brcmstb/pm/s2-mips.S @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2016 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + +#include "pm.h" + + .text + .set noreorder + .align 5 + +/* + * a0: u32 params array + */ +LEAF(brcm_pm_do_s2) + + subu sp, 64 + sw ra, 0(sp) + sw s0, 4(sp) + sw s1, 8(sp) + sw s2, 12(sp) + sw s3, 16(sp) + sw s4, 20(sp) + sw s5, 24(sp) + sw s6, 28(sp) + sw s7, 32(sp) + + /* + * Dereference the params array + * s0: AON_CTRL base register + * s1: DDR_PHY base register + * s2: TIMERS base register + * s3: I-Cache line size + * s4: Restart vector address + * s5: Restart vector size + */ + move t0, a0 + + lw s0, 0(t0) + lw s1, 4(t0) + lw s2, 8(t0) + lw s3, 12(t0) + lw s4, 16(t0) + lw s5, 20(t0) + + /* Lock this asm section into the I-cache */ + addiu t1, s3, -1 + not t1 + + la t0, brcm_pm_do_s2 + and t0, t1 + + la t2, asm_end + and t2, t1 + +1: cache 0x1c, 0(t0) + bne t0, t2, 1b + addu t0, s3 + + /* Lock the interrupt vector into the I-cache */ + move t0, zero + +2: move t1, s4 + cache 0x1c, 0(t1) + addu t1, s3 + addu t0, s3 + ble t0, s5, 2b + nop + + sync + + /* Power down request */ + li t0, PM_S2_COMMAND + sw zero, AON_CTRL_PM_CTRL(s0) + lw zero, AON_CTRL_PM_CTRL(s0) + sw t0, AON_CTRL_PM_CTRL(s0) + lw t0, AON_CTRL_PM_CTRL(s0) + + /* Enable CP0 interrupt 2 and wait for interrupt */ + mfc0 t0, CP0_STATUS + /* Save cp0 sr for restoring later */ + move s6, t0 + + li t1, ~(ST0_IM | ST0_IE) + and t0, t1 + ori t0, STATUSF_IP2 + mtc0 t0, CP0_STATUS + nop + nop + nop + ori t0, ST0_IE + mtc0 t0, CP0_STATUS + + /* Wait for interrupt */ + wait + nop + + /* Wait for memc0 */ +1: lw t0, DDR40_PHY_CONTROL_REGS_0_PLL_STATUS(s1) + andi t0, 1 + beqz t0, 1b + nop + + /* 1ms delay needed for stable recovery */ + /* Use TIMER1 to count 1 ms */ + li t0, RESET_TIMER + sw t0, TIMER_TIMER1_CTRL(s2) + lw t0, TIMER_TIMER1_CTRL(s2) + + li t0, START_TIMER + sw t0, TIMER_TIMER1_CTRL(s2) + lw t0, TIMER_TIMER1_CTRL(s2) + + /* Prepare delay */ + li t0, TIMER_MASK + lw t1, TIMER_TIMER1_STAT(s2) + and t1, t0 + /* 1ms delay */ + addi t1, 27000 + + /* Wait for the timer value to exceed t1 */ +1: lw t0, TIMER_TIMER1_STAT(s2) + sgtu t2, t1, t0 + bnez t2, 1b + nop + + /* Power back up */ + li t1, 1 + sw t1, AON_CTRL_HOST_MISC_CMDS(s0) + lw t1, AON_CTRL_HOST_MISC_CMDS(s0) + + sw zero, AON_CTRL_PM_CTRL(s0) + lw zero, AON_CTRL_PM_CTRL(s0) + + /* Unlock I-cache */ + addiu t1, s3, -1 + not t1 + + la t0, brcm_pm_do_s2 + and t0, t1 + + la t2, asm_end + and t2, t1 + +1: cache 0x00, 0(t0) + bne t0, t2, 1b + addu t0, s3 + + /* Unlock interrupt vector */ + move t0, zero + +2: move t1, s4 + cache 0x00, 0(t1) + addu t1, s3 + addu t0, s3 + ble t0, s5, 2b + nop + + /* Restore cp0 sr */ + sync + nop + mtc0 s6, CP0_STATUS + nop + + /* Set return value to success */ + li v0, 0 + + /* Return to caller */ + lw s7, 32(sp) + lw s6, 28(sp) + lw s5, 24(sp) + lw s4, 20(sp) + lw s3, 16(sp) + lw s2, 12(sp) + lw s1, 8(sp) + lw s0, 4(sp) + lw ra, 0(sp) + addiu sp, 64 + + jr ra + nop +END(brcm_pm_do_s2) + + .globl asm_end +asm_end: + nop + diff --git a/drivers/soc/bcm/brcmstb/pm/s3-mips.S b/drivers/soc/bcm/brcmstb/pm/s3-mips.S new file mode 100644 index 000000000000..1242308a8868 --- /dev/null +++ b/drivers/soc/bcm/brcmstb/pm/s3-mips.S @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2016 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + +#include "pm.h" + + .text + .set noreorder + .align 5 + .global s3_reentry + +/* + * a0: AON_CTRL base register + * a1: D-Cache line size + */ +LEAF(brcm_pm_do_s3) + + /* Get the address of s3_context */ + la t0, gp_regs + sw ra, 0(t0) + sw s0, 4(t0) + sw s1, 8(t0) + sw s2, 12(t0) + sw s3, 16(t0) + sw s4, 20(t0) + sw s5, 24(t0) + sw s6, 28(t0) + sw s7, 32(t0) + sw gp, 36(t0) + sw sp, 40(t0) + sw fp, 44(t0) + + /* Save CP0 Status */ + mfc0 t1, CP0_STATUS + sw t1, 48(t0) + + /* Write-back gp registers - cache will be gone */ + addiu t1, a1, -1 + not t1 + and t0, t1 + + /* Flush at least 64 bytes */ + addiu t2, t0, 64 + and t2, t1 + +1: cache 0x17, 0(t0) + bne t0, t2, 1b + addu t0, a1 + + /* Drop to deep standby */ + li t1, PM_WARM_CONFIG + sw zero, AON_CTRL_PM_CTRL(a0) + lw zero, AON_CTRL_PM_CTRL(a0) + sw t1, AON_CTRL_PM_CTRL(a0) + lw t1, AON_CTRL_PM_CTRL(a0) + + li t1, (PM_WARM_CONFIG | PM_PWR_DOWN) + sw t1, AON_CTRL_PM_CTRL(a0) + lw t1, AON_CTRL_PM_CTRL(a0) + + /* Enable CP0 interrupt 2 and wait for interrupt */ + mfc0 t0, CP0_STATUS + + li t1, ~(ST0_IM | ST0_IE) + and t0, t1 + ori t0, STATUSF_IP2 + mtc0 t0, CP0_STATUS + nop + nop + nop + ori t0, ST0_IE + mtc0 t0, CP0_STATUS + + /* Wait for interrupt */ + wait + nop + +s3_reentry: + + /* Clear call/return stack */ + li t0, (0x06 << 16) + mtc0 t0, $22, 2 + ssnop + ssnop + ssnop + + /* Clear jump target buffer */ + li t0, (0x04 << 16) + mtc0 t0, $22, 2 + ssnop + ssnop + ssnop + + sync + nop + + /* Setup mmu defaults */ + mtc0 zero, CP0_WIRED + mtc0 zero, CP0_ENTRYHI + li k0, PM_DEFAULT_MASK + mtc0 k0, CP0_PAGEMASK + + li sp, BMIPS_WARM_RESTART_VEC + la k0, plat_wired_tlb_setup + jalr k0 + nop + + /* Restore general purpose registers */ + la t0, gp_regs + lw fp, 44(t0) + lw sp, 40(t0) + lw gp, 36(t0) + lw s7, 32(t0) + lw s6, 28(t0) + lw s5, 24(t0) + lw s4, 20(t0) + lw s3, 16(t0) + lw s2, 12(t0) + lw s1, 8(t0) + lw s0, 4(t0) + lw ra, 0(t0) + + /* Restore CP0 status */ + lw t1, 48(t0) + mtc0 t1, CP0_STATUS + + /* Return to caller */ + li v0, 0 + jr ra + nop + +END(brcm_pm_do_s3) -- cgit From 5e68c0fc8df8a588b15cd469b27b8b5dbfadc6c3 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sat, 23 Sep 2017 16:14:02 +0200 Subject: soc: amlogic: Add Meson6/Meson8/Meson8b/Meson8m2 SoC Information driver Amlogic SoCs have an information register which contains the SoC type and revision information. This patchs adds support for decoding those registers and exposing the resulting information via the SoC bus infrastructure. Signed-off-by: Martin Blumenstingl Signed-off-by: Kevin Hilman --- drivers/soc/amlogic/Kconfig | 10 ++ drivers/soc/amlogic/Makefile | 1 + drivers/soc/amlogic/meson-mx-socinfo.c | 175 +++++++++++++++++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100644 drivers/soc/amlogic/meson-mx-socinfo.c (limited to 'drivers/soc') diff --git a/drivers/soc/amlogic/Kconfig b/drivers/soc/amlogic/Kconfig index 22acf064531f..ef0b8f6c53e0 100644 --- a/drivers/soc/amlogic/Kconfig +++ b/drivers/soc/amlogic/Kconfig @@ -9,4 +9,14 @@ config MESON_GX_SOCINFO Say yes to support decoding of Amlogic Meson GX SoC family information about the type, package and version. +config MESON_MX_SOCINFO + bool "Amlogic Meson MX SoC Information driver" + depends on ARCH_MESON || COMPILE_TEST + default ARCH_MESON + select SOC_BUS + help + Say yes to support decoding of Amlogic Meson6, Meson8, + Meson8b and Meson8m2 SoC family information about the type + and version. + endmenu diff --git a/drivers/soc/amlogic/Makefile b/drivers/soc/amlogic/Makefile index 3e85fc462c21..1f5df50ebd73 100644 --- a/drivers/soc/amlogic/Makefile +++ b/drivers/soc/amlogic/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_MESON_GX_SOCINFO) += meson-gx-socinfo.o +obj-$(CONFIG_MESON_MX_SOCINFO) += meson-mx-socinfo.o diff --git a/drivers/soc/amlogic/meson-mx-socinfo.c b/drivers/soc/amlogic/meson-mx-socinfo.c new file mode 100644 index 000000000000..7bfff5ff22a2 --- /dev/null +++ b/drivers/soc/amlogic/meson-mx-socinfo.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2017 Martin Blumenstingl + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MESON_SOCINFO_MAJOR_VER_MESON6 0x16 +#define MESON_SOCINFO_MAJOR_VER_MESON8 0x19 +#define MESON_SOCINFO_MAJOR_VER_MESON8B 0x1b + +#define MESON_MX_ASSIST_HW_REV 0x14c + +#define MESON_MX_ANALOG_TOP_METAL_REVISION 0x0 + +#define MESON_MX_BOOTROM_MISC_VER 0x4 + +static const char *meson_mx_socinfo_revision(unsigned int major_ver, + unsigned int misc_ver, + unsigned int metal_rev) +{ + unsigned int minor_ver; + + switch (major_ver) { + case MESON_SOCINFO_MAJOR_VER_MESON6: + minor_ver = 0xa; + break; + + case MESON_SOCINFO_MAJOR_VER_MESON8: + if (metal_rev == 0x11111112) + major_ver = 0x1d; + + if (metal_rev == 0x11111111 || metal_rev == 0x11111112) + minor_ver = 0xa; + else if (metal_rev == 0x11111113) + minor_ver = 0xb; + else if (metal_rev == 0x11111133) + minor_ver = 0xc; + else + minor_ver = 0xd; + + break; + + case MESON_SOCINFO_MAJOR_VER_MESON8B: + if (metal_rev == 0x11111111) + minor_ver = 0xa; + else + minor_ver = 0xb; + + break; + + default: + minor_ver = 0x0; + break; + } + + return kasprintf(GFP_KERNEL, "Rev%X (%x - 0:%X)", minor_ver, major_ver, + misc_ver); +} + +static const char *meson_mx_socinfo_soc_id(unsigned int major_ver, + unsigned int metal_rev) +{ + const char *soc_id; + + switch (major_ver) { + case MESON_SOCINFO_MAJOR_VER_MESON6: + soc_id = "Meson6 (AML8726-MX)"; + break; + + case MESON_SOCINFO_MAJOR_VER_MESON8: + if (metal_rev == 0x11111112) + soc_id = "Meson8m2 (S812)"; + else + soc_id = "Meson8 (S802)"; + + break; + + case MESON_SOCINFO_MAJOR_VER_MESON8B: + soc_id = "Meson8b (S805)"; + break; + + default: + soc_id = "Unknown"; + break; + } + + return kstrdup_const(soc_id, GFP_KERNEL); +} + +static const struct of_device_id meson_mx_socinfo_analog_top_ids[] = { + { .compatible = "amlogic,meson8-analog-top", }, + { .compatible = "amlogic,meson8b-analog-top", }, + { /* sentinel */ } +}; + +int __init meson_mx_socinfo_init(void) +{ + struct soc_device_attribute *soc_dev_attr; + struct soc_device *soc_dev; + struct device_node *np; + struct regmap *assist_regmap, *bootrom_regmap, *analog_top_regmap; + unsigned int major_ver, misc_ver, metal_rev = 0; + int ret; + + assist_regmap = + syscon_regmap_lookup_by_compatible("amlogic,meson-mx-assist"); + if (IS_ERR(assist_regmap)) + return PTR_ERR(assist_regmap); + + bootrom_regmap = + syscon_regmap_lookup_by_compatible("amlogic,meson-mx-bootrom"); + if (IS_ERR(bootrom_regmap)) + return PTR_ERR(bootrom_regmap); + + np = of_find_matching_node(NULL, meson_mx_socinfo_analog_top_ids); + if (np) { + analog_top_regmap = syscon_node_to_regmap(np); + if (IS_ERR(analog_top_regmap)) + return PTR_ERR(analog_top_regmap); + + ret = regmap_read(analog_top_regmap, + MESON_MX_ANALOG_TOP_METAL_REVISION, + &metal_rev); + if (ret) + return ret; + } + + ret = regmap_read(assist_regmap, MESON_MX_ASSIST_HW_REV, &major_ver); + if (ret < 0) + return ret; + + ret = regmap_read(bootrom_regmap, MESON_MX_BOOTROM_MISC_VER, + &misc_ver); + if (ret < 0) + return ret; + + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + return -ENODEV; + + soc_dev_attr->family = "Amlogic Meson"; + + np = of_find_node_by_path("/"); + of_property_read_string(np, "model", &soc_dev_attr->machine); + of_node_put(np); + + soc_dev_attr->revision = meson_mx_socinfo_revision(major_ver, misc_ver, + metal_rev); + soc_dev_attr->soc_id = meson_mx_socinfo_soc_id(major_ver, metal_rev); + + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR(soc_dev)) { + kfree_const(soc_dev_attr->revision); + kfree_const(soc_dev_attr->soc_id); + kfree(soc_dev_attr); + return PTR_ERR(soc_dev); + } + + dev_info(soc_device_to_device(soc_dev), "Amlogic %s %s detected\n", + soc_dev_attr->soc_id, soc_dev_attr->revision); + + return 0; +} +device_initcall(meson_mx_socinfo_init); -- cgit From c40610198f35e8264f9175dafe74db6288a07eda Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 4 Oct 2017 08:38:23 +0200 Subject: soc: samsung: Remove Exynos4212 related dead code Support for Exynos4212 SoCs has been removed by commit bca9085e0ae9 ("ARM: dts: exynos: remove Exynos4212 support (dead code)"), so there is no need to keep remaining dead code related to this SoC version. Signed-off-by: Marek Szyprowski Signed-off-by: Krzysztof Kozlowski --- drivers/soc/samsung/exynos-pmu.c | 9 --------- drivers/soc/samsung/exynos-pmu.h | 2 -- drivers/soc/samsung/exynos4-pmu.c | 13 ++----------- 3 files changed, 2 insertions(+), 22 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c index bd4a76f27bc2..938f8ccfcb74 100644 --- a/drivers/soc/samsung/exynos-pmu.c +++ b/drivers/soc/samsung/exynos-pmu.c @@ -60,12 +60,6 @@ void exynos_sys_powerdown_conf(enum sys_powerdown mode) if (pmu_data->powerdown_conf_extra) pmu_data->powerdown_conf_extra(mode); - - if (pmu_data->pmu_config_extra) { - for (i = 0; pmu_data->pmu_config_extra[i].offset != PMU_TABLE_END; i++) - pmu_raw_writel(pmu_data->pmu_config_extra[i].val[mode], - pmu_data->pmu_config_extra[i].offset); - } } /* @@ -88,9 +82,6 @@ static const struct of_device_id exynos_pmu_of_device_ids[] = { }, { .compatible = "samsung,exynos4210-pmu", .data = exynos_pmu_data_arm_ptr(exynos4210_pmu_data), - }, { - .compatible = "samsung,exynos4212-pmu", - .data = exynos_pmu_data_arm_ptr(exynos4212_pmu_data), }, { .compatible = "samsung,exynos4412-pmu", .data = exynos_pmu_data_arm_ptr(exynos4412_pmu_data), diff --git a/drivers/soc/samsung/exynos-pmu.h b/drivers/soc/samsung/exynos-pmu.h index 40d4229abfb5..86b3f2f8966d 100644 --- a/drivers/soc/samsung/exynos-pmu.h +++ b/drivers/soc/samsung/exynos-pmu.h @@ -23,7 +23,6 @@ struct exynos_pmu_conf { struct exynos_pmu_data { const struct exynos_pmu_conf *pmu_config; - const struct exynos_pmu_conf *pmu_config_extra; void (*pmu_init)(void); void (*powerdown_conf)(enum sys_powerdown); @@ -36,7 +35,6 @@ extern void __iomem *pmu_base_addr; /* list of all exported SoC specific data */ extern const struct exynos_pmu_data exynos3250_pmu_data; extern const struct exynos_pmu_data exynos4210_pmu_data; -extern const struct exynos_pmu_data exynos4212_pmu_data; extern const struct exynos_pmu_data exynos4412_pmu_data; extern const struct exynos_pmu_data exynos5250_pmu_data; extern const struct exynos_pmu_data exynos5420_pmu_data; diff --git a/drivers/soc/samsung/exynos4-pmu.c b/drivers/soc/samsung/exynos4-pmu.c index bc4fa73bed11..5dbfe4e31f4c 100644 --- a/drivers/soc/samsung/exynos4-pmu.c +++ b/drivers/soc/samsung/exynos4-pmu.c @@ -90,7 +90,7 @@ static const struct exynos_pmu_conf exynos4210_pmu_config[] = { { PMU_TABLE_END,}, }; -static const struct exynos_pmu_conf exynos4x12_pmu_config[] = { +static const struct exynos_pmu_conf exynos4412_pmu_config[] = { { S5P_ARM_CORE0_LOWPWR, { 0x0, 0x0, 0x2 } }, { S5P_DIS_IRQ_CORE0, { 0x0, 0x0, 0x0 } }, { S5P_DIS_IRQ_CENTRAL0, { 0x0, 0x0, 0x0 } }, @@ -195,10 +195,6 @@ static const struct exynos_pmu_conf exynos4x12_pmu_config[] = { { S5P_GPS_ALIVE_LOWPWR, { 0x7, 0x0, 0x0 } }, { S5P_CMU_SYSCLK_ISP_LOWPWR, { 0x1, 0x0, 0x0 } }, { S5P_CMU_SYSCLK_GPS_LOWPWR, { 0x1, 0x0, 0x0 } }, - { PMU_TABLE_END,}, -}; - -static const struct exynos_pmu_conf exynos4412_pmu_config[] = { { S5P_ARM_CORE2_LOWPWR, { 0x0, 0x0, 0x2 } }, { S5P_DIS_IRQ_CORE2, { 0x0, 0x0, 0x0 } }, { S5P_DIS_IRQ_CENTRAL2, { 0x0, 0x0, 0x0 } }, @@ -212,11 +208,6 @@ const struct exynos_pmu_data exynos4210_pmu_data = { .pmu_config = exynos4210_pmu_config, }; -const struct exynos_pmu_data exynos4212_pmu_data = { - .pmu_config = exynos4x12_pmu_config, -}; - const struct exynos_pmu_data exynos4412_pmu_data = { - .pmu_config = exynos4x12_pmu_config, - .pmu_config_extra = exynos4412_pmu_config, + .pmu_config = exynos4412_pmu_config, }; -- cgit From cbd71f0aaa60a2108f6d88152e5a7274865dbfa9 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Thu, 21 Sep 2017 16:26:57 +0800 Subject: soc: mediatek: pwrap: fixup warnings from coding style fixup those warnings such as lines over 80 words and parenthesis alignment which would be complained by checkpatch.pl. Signed-off-by: Sean Wang Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-pmic-wrap.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c index c2048382830f..f095faac1e04 100644 --- a/drivers/soc/mediatek/mtk-pmic-wrap.c +++ b/drivers/soc/mediatek/mtk-pmic-wrap.c @@ -827,7 +827,8 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp) /* wait for cipher data ready@PMIC */ ret = pwrap_wait_for_state(wrp, pwrap_is_pmic_cipher_ready); if (ret) { - dev_err(wrp->dev, "timeout waiting for cipher data ready@PMIC\n"); + dev_err(wrp->dev, + "timeout waiting for cipher data ready@PMIC\n"); return ret; } @@ -1159,23 +1160,27 @@ static int pwrap_probe(struct platform_device *pdev) if (IS_ERR(wrp->bridge_base)) return PTR_ERR(wrp->bridge_base); - wrp->rstc_bridge = devm_reset_control_get(wrp->dev, "pwrap-bridge"); + wrp->rstc_bridge = devm_reset_control_get(wrp->dev, + "pwrap-bridge"); if (IS_ERR(wrp->rstc_bridge)) { ret = PTR_ERR(wrp->rstc_bridge); - dev_dbg(wrp->dev, "cannot get pwrap-bridge reset: %d\n", ret); + dev_dbg(wrp->dev, + "cannot get pwrap-bridge reset: %d\n", ret); return ret; } } wrp->clk_spi = devm_clk_get(wrp->dev, "spi"); if (IS_ERR(wrp->clk_spi)) { - dev_dbg(wrp->dev, "failed to get clock: %ld\n", PTR_ERR(wrp->clk_spi)); + dev_dbg(wrp->dev, "failed to get clock: %ld\n", + PTR_ERR(wrp->clk_spi)); return PTR_ERR(wrp->clk_spi); } wrp->clk_wrap = devm_clk_get(wrp->dev, "wrap"); if (IS_ERR(wrp->clk_wrap)) { - dev_dbg(wrp->dev, "failed to get clock: %ld\n", PTR_ERR(wrp->clk_wrap)); + dev_dbg(wrp->dev, "failed to get clock: %ld\n", + PTR_ERR(wrp->clk_wrap)); return PTR_ERR(wrp->clk_wrap); } @@ -1220,8 +1225,9 @@ static int pwrap_probe(struct platform_device *pdev) pwrap_writel(wrp, wrp->master->int_en_all, PWRAP_INT_EN); irq = platform_get_irq(pdev, 0); - ret = devm_request_irq(wrp->dev, irq, pwrap_interrupt, IRQF_TRIGGER_HIGH, - "mt-pmic-pwrap", wrp); + ret = devm_request_irq(wrp->dev, irq, pwrap_interrupt, + IRQF_TRIGGER_HIGH, + "mt-pmic-pwrap", wrp); if (ret) goto err_out2; -- cgit From 01f141544413aa5de45d3979b8aafa9aaf6f9b9f Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Wed, 4 Oct 2017 20:11:27 -0700 Subject: soc: qcom: smem: Rename "uncached" accessors In preparation for adding accessors for "cached" entries rename the "uncached" accessors. Also rename "first" cached entry to "last", as the cached list grows backwards. Signed-off-by: Bjorn Andersson Reviewed-by: Stephen Boyd Signed-off-by: Andy Gross --- drivers/soc/qcom/smem.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index 18ec52f2078a..b451dbc4aa39 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -245,14 +245,14 @@ struct qcom_smem { }; static struct smem_private_entry * -phdr_to_last_private_entry(struct smem_partition_header *phdr) +phdr_to_last_uncached_entry(struct smem_partition_header *phdr) { void *p = phdr; return p + le32_to_cpu(phdr->offset_free_uncached); } -static void *phdr_to_first_cached_entry(struct smem_partition_header *phdr) +static void *phdr_to_last_cached_entry(struct smem_partition_header *phdr) { void *p = phdr; @@ -260,7 +260,7 @@ static void *phdr_to_first_cached_entry(struct smem_partition_header *phdr) } static struct smem_private_entry * -phdr_to_first_private_entry(struct smem_partition_header *phdr) +phdr_to_first_uncached_entry(struct smem_partition_header *phdr) { void *p = phdr; @@ -268,7 +268,7 @@ phdr_to_first_private_entry(struct smem_partition_header *phdr) } static struct smem_private_entry * -private_entry_next(struct smem_private_entry *e) +uncached_entry_next(struct smem_private_entry *e) { void *p = e; @@ -276,7 +276,7 @@ private_entry_next(struct smem_private_entry *e) le32_to_cpu(e->size); } -static void *entry_to_item(struct smem_private_entry *e) +static void *uncached_entry_to_item(struct smem_private_entry *e) { void *p = e; @@ -300,9 +300,9 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem, void *cached; phdr = smem->partitions[host]; - hdr = phdr_to_first_private_entry(phdr); - end = phdr_to_last_private_entry(phdr); - cached = phdr_to_first_cached_entry(phdr); + hdr = phdr_to_first_uncached_entry(phdr); + end = phdr_to_last_uncached_entry(phdr); + cached = phdr_to_last_cached_entry(phdr); while (hdr < end) { if (hdr->canary != SMEM_PRIVATE_CANARY) { @@ -315,7 +315,7 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem, if (le16_to_cpu(hdr->item) == item) return -EEXIST; - hdr = private_entry_next(hdr); + hdr = uncached_entry_next(hdr); } /* Check that we don't grow into the cached region */ @@ -460,8 +460,8 @@ static void *qcom_smem_get_private(struct qcom_smem *smem, struct smem_private_entry *e, *end; phdr = smem->partitions[host]; - e = phdr_to_first_private_entry(phdr); - end = phdr_to_last_private_entry(phdr); + e = phdr_to_first_uncached_entry(phdr); + end = phdr_to_last_uncached_entry(phdr); while (e < end) { if (e->canary != SMEM_PRIVATE_CANARY) { @@ -476,10 +476,10 @@ static void *qcom_smem_get_private(struct qcom_smem *smem, *size = le32_to_cpu(e->size) - le16_to_cpu(e->padding_data); - return entry_to_item(e); + return uncached_entry_to_item(e); } - e = private_entry_next(e); + e = uncached_entry_next(e); } return ERR_PTR(-ENOENT); -- cgit From c7c1dc35871378ede5f117dc9c2a7ce1cecd4449 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Wed, 4 Oct 2017 20:11:28 -0700 Subject: soc: qcom: smem: Support getting cached entries On msm8996 cached SMEM items are used for storing the GLINK FIFOs, so for items not found in the uncached list we need to also search the cased list for these items. Signed-off-by: Bjorn Andersson Reviewed-by: Stephen Boyd Signed-off-by: Andy Gross --- drivers/soc/qcom/smem.c | 69 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 8 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index b451dbc4aa39..c28275be0038 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -52,7 +52,8 @@ * * Items in the non-cached region are allocated from the start of the partition * while items in the cached region are allocated from the end. The free area - * is hence the region between the cached and non-cached offsets. + * is hence the region between the cached and non-cached offsets. The header of + * cached items comes after the data. * * * To synchronize allocations in the shared memory heaps a remote spinlock must @@ -140,6 +141,7 @@ struct smem_header { * @flags: flags for the partition (currently unused) * @host0: first processor/host with access to this partition * @host1: second processor/host with access to this partition + * @cacheline: alignment for "cached" entries * @reserved: reserved entries for later use */ struct smem_ptable_entry { @@ -148,7 +150,8 @@ struct smem_ptable_entry { __le32 flags; __le16 host0; __le16 host1; - __le32 reserved[8]; + __le32 cacheline; + __le32 reserved[7]; }; /** @@ -230,6 +233,7 @@ struct smem_region { * @hwlock: reference to a hwspinlock * @partitions: list of pointers to partitions affecting the current * processor/host + * @cacheline: list of cacheline sizes for each host * @num_regions: number of @regions * @regions: list of the memory regions defining the shared memory */ @@ -239,6 +243,7 @@ struct qcom_smem { struct hwspinlock *hwlock; struct smem_partition_header *partitions[SMEM_HOST_COUNT]; + size_t cacheline[SMEM_HOST_COUNT]; unsigned num_regions; struct smem_region regions[0]; @@ -252,6 +257,14 @@ phdr_to_last_uncached_entry(struct smem_partition_header *phdr) return p + le32_to_cpu(phdr->offset_free_uncached); } +static void *phdr_to_first_cached_entry(struct smem_partition_header *phdr, + size_t cacheline) +{ + void *p = phdr; + + return p + le32_to_cpu(phdr->size) - ALIGN(sizeof(*phdr), cacheline); +} + static void *phdr_to_last_cached_entry(struct smem_partition_header *phdr) { void *p = phdr; @@ -276,6 +289,14 @@ uncached_entry_next(struct smem_private_entry *e) le32_to_cpu(e->size); } +static struct smem_private_entry * +cached_entry_next(struct smem_private_entry *e, size_t cacheline) +{ + void *p = e; + + return p - le32_to_cpu(e->size) - ALIGN(sizeof(*e), cacheline); +} + static void *uncached_entry_to_item(struct smem_private_entry *e) { void *p = e; @@ -283,6 +304,13 @@ static void *uncached_entry_to_item(struct smem_private_entry *e) return p + sizeof(*e) + le16_to_cpu(e->padding_hdr); } +static void *cached_entry_to_item(struct smem_private_entry *e) +{ + void *p = e; + + return p - le32_to_cpu(e->size); +} + /* Pointer to the one and only smem handle */ static struct qcom_smem *__smem; @@ -458,18 +486,17 @@ static void *qcom_smem_get_private(struct qcom_smem *smem, { struct smem_partition_header *phdr; struct smem_private_entry *e, *end; + size_t cacheline; phdr = smem->partitions[host]; + cacheline = smem->cacheline[host]; + e = phdr_to_first_uncached_entry(phdr); end = phdr_to_last_uncached_entry(phdr); while (e < end) { - if (e->canary != SMEM_PRIVATE_CANARY) { - dev_err(smem->dev, - "Found invalid canary in host %d partition\n", - host); - return ERR_PTR(-EINVAL); - } + if (e->canary != SMEM_PRIVATE_CANARY) + goto invalid_canary; if (le16_to_cpu(e->item) == item) { if (size != NULL) @@ -482,7 +509,32 @@ static void *qcom_smem_get_private(struct qcom_smem *smem, e = uncached_entry_next(e); } + /* Item was not found in the uncached list, search the cached list */ + + e = phdr_to_first_cached_entry(phdr, cacheline); + end = phdr_to_last_cached_entry(phdr); + + while (e > end) { + if (e->canary != SMEM_PRIVATE_CANARY) + goto invalid_canary; + + if (le16_to_cpu(e->item) == item) { + if (size != NULL) + *size = le32_to_cpu(e->size) - + le16_to_cpu(e->padding_data); + + return cached_entry_to_item(e); + } + + e = cached_entry_next(e, cacheline); + } + return ERR_PTR(-ENOENT); + +invalid_canary: + dev_err(smem->dev, "Found invalid canary in host %d partition\n", host); + + return ERR_PTR(-EINVAL); } /** @@ -659,6 +711,7 @@ static int qcom_smem_enumerate_partitions(struct qcom_smem *smem, } smem->partitions[remote_host] = header; + smem->cacheline[remote_host] = le32_to_cpu(entry->cacheline); } return 0; -- cgit From a216000f0140f415cec96129f777b5234c9d142f Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Wed, 11 Oct 2017 14:29:06 -0700 Subject: soc: qcom: smem: Use le32_to_cpu for comparison Endianness can vary in the system, add le32_to_cpu when comparing partition sizes from smem. Signed-off-by: Chris Lew Acked-by: Bjorn Andersson Signed-off-by: Andy Gross --- drivers/soc/qcom/smem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index c28275be0038..db04c45d4132 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -698,7 +698,7 @@ static int qcom_smem_enumerate_partitions(struct qcom_smem *smem, return -EINVAL; } - if (header->size != entry->size) { + if (le32_to_cpu(header->size) != le32_to_cpu(entry->size)) { dev_err(smem->dev, "Partition %d has invalid size\n", i); return -EINVAL; -- cgit From dcc0967dd776cefe04856726a39195c1c5edbd2c Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Wed, 11 Oct 2017 14:29:07 -0700 Subject: soc: qcom: smem: Read version from the smem header The SMEM header structure includes the version information. Read the version directly from the header instead of getting an item from the global heap. Signed-off-by: Chris Lew Acked-by: Bjorn Andersson Signed-off-by: Andy Gross --- drivers/soc/qcom/smem.c | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index db04c45d4132..540322ae409e 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -63,13 +63,12 @@ */ /* - * Item 3 of the global heap contains an array of versions for the various - * software components in the SoC. We verify that the boot loader version is - * what the expected version (SMEM_EXPECTED_VERSION) as a sanity check. + * The version member of the smem header contains an array of versions for the + * various software components in the SoC. We verify that the boot loader + * version is a valid version as a sanity check. */ -#define SMEM_ITEM_VERSION 3 -#define SMEM_MASTER_SBL_VERSION_INDEX 7 -#define SMEM_EXPECTED_VERSION 11 +#define SMEM_MASTER_SBL_VERSION_INDEX 7 +#define SMEM_EXPECTED_VERSION 11 /* * The first 8 items are only to be allocated by the boot loader while @@ -604,19 +603,11 @@ EXPORT_SYMBOL(qcom_smem_get_free_space); static int qcom_smem_get_sbl_version(struct qcom_smem *smem) { + struct smem_header *header; __le32 *versions; - size_t size; - - versions = qcom_smem_get_global(smem, SMEM_ITEM_VERSION, &size); - if (IS_ERR(versions)) { - dev_err(smem->dev, "Unable to read the version item\n"); - return -ENOENT; - } - if (size < sizeof(unsigned) * SMEM_MASTER_SBL_VERSION_INDEX) { - dev_err(smem->dev, "Version item is too small\n"); - return -EINVAL; - } + header = smem->regions[0].virt_base; + versions = header->version; return le32_to_cpu(versions[SMEM_MASTER_SBL_VERSION_INDEX]); } -- cgit From d52e404874369f10d20519f4095478d9cb4d6aad Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Wed, 11 Oct 2017 14:29:08 -0700 Subject: soc: qcom: smem: Support global partition SMEM V12 creates a global partition to allocate global smem items from instead of a global heap. The global partition has the same structure as a private partition. Signed-off-by: Chris Lew Acked-by: Bjorn Andersson Signed-off-by: Andy Gross --- drivers/soc/qcom/smem.c | 172 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 142 insertions(+), 30 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index 540322ae409e..6a3134e9c591 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -55,6 +55,10 @@ * is hence the region between the cached and non-cached offsets. The header of * cached items comes after the data. * + * Version 12 (SMEM_GLOBAL_PART_VERSION) changes the item alloc/get procedure + * for the global heap. A new global partition is created from the global heap + * region with partition type (SMEM_GLOBAL_HOST) and the max smem item count is + * set by the bootloader. * * To synchronize allocations in the shared memory heaps a remote spinlock must * be held - currently lock number 3 of the sfpb or tcsr is used for this on all @@ -68,7 +72,8 @@ * version is a valid version as a sanity check. */ #define SMEM_MASTER_SBL_VERSION_INDEX 7 -#define SMEM_EXPECTED_VERSION 11 +#define SMEM_GLOBAL_HEAP_VERSION 11 +#define SMEM_GLOBAL_PART_VERSION 12 /* * The first 8 items are only to be allocated by the boot loader while @@ -82,6 +87,9 @@ /* Processor/host identifier for the application processor */ #define SMEM_HOST_APPS 0 +/* Processor/host identifier for the global partition */ +#define SMEM_GLOBAL_HOST 0xfffe + /* Max number of processors/hosts in a system */ #define SMEM_HOST_COUNT 9 @@ -230,6 +238,8 @@ struct smem_region { * struct qcom_smem - device data for the smem device * @dev: device pointer * @hwlock: reference to a hwspinlock + * @global_partition: pointer to global partition when in use + * @global_cacheline: cacheline size for global partition * @partitions: list of pointers to partitions affecting the current * processor/host * @cacheline: list of cacheline sizes for each host @@ -241,6 +251,8 @@ struct qcom_smem { struct hwspinlock *hwlock; + struct smem_partition_header *global_partition; + size_t global_cacheline; struct smem_partition_header *partitions[SMEM_HOST_COUNT]; size_t cacheline[SMEM_HOST_COUNT]; @@ -317,16 +329,14 @@ static struct qcom_smem *__smem; #define HWSPINLOCK_TIMEOUT 1000 static int qcom_smem_alloc_private(struct qcom_smem *smem, - unsigned host, + struct smem_partition_header *phdr, unsigned item, size_t size) { - struct smem_partition_header *phdr; struct smem_private_entry *hdr, *end; size_t alloc_size; void *cached; - phdr = smem->partitions[host]; hdr = phdr_to_first_uncached_entry(phdr); end = phdr_to_last_uncached_entry(phdr); cached = phdr_to_last_cached_entry(phdr); @@ -334,8 +344,8 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem, while (hdr < end) { if (hdr->canary != SMEM_PRIVATE_CANARY) { dev_err(smem->dev, - "Found invalid canary in host %d partition\n", - host); + "Found invalid canary in hosts %d:%d partition\n", + phdr->host0, phdr->host1); return -EINVAL; } @@ -373,8 +383,8 @@ static int qcom_smem_alloc_global(struct qcom_smem *smem, unsigned item, size_t size) { - struct smem_header *header; struct smem_global_entry *entry; + struct smem_header *header; if (WARN_ON(item >= SMEM_ITEM_COUNT)) return -EINVAL; @@ -416,6 +426,7 @@ static int qcom_smem_alloc_global(struct qcom_smem *smem, */ int qcom_smem_alloc(unsigned host, unsigned item, size_t size) { + struct smem_partition_header *phdr; unsigned long flags; int ret; @@ -434,10 +445,15 @@ int qcom_smem_alloc(unsigned host, unsigned item, size_t size) if (ret) return ret; - if (host < SMEM_HOST_COUNT && __smem->partitions[host]) - ret = qcom_smem_alloc_private(__smem, host, item, size); - else + if (host < SMEM_HOST_COUNT && __smem->partitions[host]) { + phdr = __smem->partitions[host]; + ret = qcom_smem_alloc_private(__smem, phdr, item, size); + } else if (__smem->global_partition) { + phdr = __smem->global_partition; + ret = qcom_smem_alloc_private(__smem, phdr, item, size); + } else { ret = qcom_smem_alloc_global(__smem, item, size); + } hwspin_unlock_irqrestore(__smem->hwlock, &flags); @@ -479,16 +495,12 @@ static void *qcom_smem_get_global(struct qcom_smem *smem, } static void *qcom_smem_get_private(struct qcom_smem *smem, - unsigned host, + struct smem_partition_header *phdr, + size_t cacheline, unsigned item, size_t *size) { - struct smem_partition_header *phdr; struct smem_private_entry *e, *end; - size_t cacheline; - - phdr = smem->partitions[host]; - cacheline = smem->cacheline[host]; e = phdr_to_first_uncached_entry(phdr); end = phdr_to_last_uncached_entry(phdr); @@ -531,7 +543,8 @@ static void *qcom_smem_get_private(struct qcom_smem *smem, return ERR_PTR(-ENOENT); invalid_canary: - dev_err(smem->dev, "Found invalid canary in host %d partition\n", host); + dev_err(smem->dev, "Found invalid canary in hosts %d:%d partition\n", + phdr->host0, phdr->host1); return ERR_PTR(-EINVAL); } @@ -547,7 +560,9 @@ invalid_canary: */ void *qcom_smem_get(unsigned host, unsigned item, size_t *size) { + struct smem_partition_header *phdr; unsigned long flags; + size_t cacheln; int ret; void *ptr = ERR_PTR(-EPROBE_DEFER); @@ -560,10 +575,17 @@ void *qcom_smem_get(unsigned host, unsigned item, size_t *size) if (ret) return ERR_PTR(ret); - if (host < SMEM_HOST_COUNT && __smem->partitions[host]) - ptr = qcom_smem_get_private(__smem, host, item, size); - else + if (host < SMEM_HOST_COUNT && __smem->partitions[host]) { + phdr = __smem->partitions[host]; + cacheln = __smem->cacheline[host]; + ptr = qcom_smem_get_private(__smem, phdr, cacheln, item, size); + } else if (__smem->global_partition) { + phdr = __smem->global_partition; + cacheln = __smem->global_cacheline; + ptr = qcom_smem_get_private(__smem, phdr, cacheln, item, size); + } else { ptr = qcom_smem_get_global(__smem, item, size); + } hwspin_unlock_irqrestore(__smem->hwlock, &flags); @@ -592,6 +614,10 @@ int qcom_smem_get_free_space(unsigned host) phdr = __smem->partitions[host]; ret = le32_to_cpu(phdr->offset_free_cached) - le32_to_cpu(phdr->offset_free_uncached); + } else if (__smem->global_partition) { + phdr = __smem->global_partition; + ret = le32_to_cpu(phdr->offset_free_cached) - + le32_to_cpu(phdr->offset_free_uncached); } else { header = __smem->regions[0].virt_base; ret = le32_to_cpu(header->available); @@ -612,27 +638,106 @@ static int qcom_smem_get_sbl_version(struct qcom_smem *smem) return le32_to_cpu(versions[SMEM_MASTER_SBL_VERSION_INDEX]); } -static int qcom_smem_enumerate_partitions(struct qcom_smem *smem, - unsigned local_host) +static struct smem_ptable *qcom_smem_get_ptable(struct qcom_smem *smem) { - struct smem_partition_header *header; - struct smem_ptable_entry *entry; struct smem_ptable *ptable; - unsigned remote_host; - u32 version, host0, host1; - int i; + u32 version; ptable = smem->regions[0].virt_base + smem->regions[0].size - SZ_4K; if (memcmp(ptable->magic, SMEM_PTABLE_MAGIC, sizeof(ptable->magic))) - return 0; + return ERR_PTR(-ENOENT); version = le32_to_cpu(ptable->version); if (version != 1) { dev_err(smem->dev, "Unsupported partition header version %d\n", version); + return ERR_PTR(-EINVAL); + } + return ptable; +} + +static int qcom_smem_set_global_partition(struct qcom_smem *smem) +{ + struct smem_partition_header *header; + struct smem_ptable_entry *entry = NULL; + struct smem_ptable *ptable; + u32 host0, host1, size; + int i; + + ptable = qcom_smem_get_ptable(smem); + if (IS_ERR(ptable)) + return PTR_ERR(ptable); + + for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) { + entry = &ptable->entry[i]; + host0 = le16_to_cpu(entry->host0); + host1 = le16_to_cpu(entry->host1); + + if (host0 == SMEM_GLOBAL_HOST && host0 == host1) + break; + } + + if (!entry) { + dev_err(smem->dev, "Missing entry for global partition\n"); + return -EINVAL; + } + + if (!le32_to_cpu(entry->offset) || !le32_to_cpu(entry->size)) { + dev_err(smem->dev, "Invalid entry for global partition\n"); + return -EINVAL; + } + + if (smem->global_partition) { + dev_err(smem->dev, "Already found the global partition\n"); + return -EINVAL; + } + + header = smem->regions[0].virt_base + le32_to_cpu(entry->offset); + host0 = le16_to_cpu(header->host0); + host1 = le16_to_cpu(header->host1); + + if (memcmp(header->magic, SMEM_PART_MAGIC, sizeof(header->magic))) { + dev_err(smem->dev, "Global partition has invalid magic\n"); + return -EINVAL; + } + + if (host0 != SMEM_GLOBAL_HOST && host1 != SMEM_GLOBAL_HOST) { + dev_err(smem->dev, "Global partition hosts are invalid\n"); + return -EINVAL; + } + + if (le32_to_cpu(header->size) != le32_to_cpu(entry->size)) { + dev_err(smem->dev, "Global partition has invalid size\n"); return -EINVAL; } + size = le32_to_cpu(header->offset_free_uncached); + if (size > le32_to_cpu(header->size)) { + dev_err(smem->dev, + "Global partition has invalid free pointer\n"); + return -EINVAL; + } + + smem->global_partition = header; + smem->global_cacheline = le32_to_cpu(entry->cacheline); + + return 0; +} + +static int qcom_smem_enumerate_partitions(struct qcom_smem *smem, + unsigned int local_host) +{ + struct smem_partition_header *header; + struct smem_ptable_entry *entry; + struct smem_ptable *ptable; + unsigned int remote_host; + u32 host0, host1; + int i; + + ptable = qcom_smem_get_ptable(smem); + if (IS_ERR(ptable)) + return PTR_ERR(ptable); + for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) { entry = &ptable->entry[i]; host0 = le16_to_cpu(entry->host0); @@ -773,13 +878,20 @@ static int qcom_smem_probe(struct platform_device *pdev) } version = qcom_smem_get_sbl_version(smem); - if (version >> 16 != SMEM_EXPECTED_VERSION) { + switch (version >> 16) { + case SMEM_GLOBAL_PART_VERSION: + ret = qcom_smem_set_global_partition(smem); + if (ret < 0) + return ret; + case SMEM_GLOBAL_HEAP_VERSION: + break; + default: dev_err(&pdev->dev, "Unsupported SMEM version 0x%x\n", version); return -EINVAL; } ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS); - if (ret < 0) + if (ret < 0 && ret != -ENOENT) return ret; hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0); -- cgit From 5b3940676107dd65165b5eaea58fa60442d29a1f Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Wed, 11 Oct 2017 14:29:09 -0700 Subject: soc: qcom: smem: Support dynamic item limit In V12 SMEM, SBL writes SMEM parameter information after the TOC. Use the SBL provided item count as the max item number. Signed-off-by: Chris Lew Acked-by: Bjorn Andersson Signed-off-by: Andy Gross --- drivers/soc/qcom/smem.c | 51 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 6 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index 6a3134e9c591..19704baa65f4 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -222,6 +222,24 @@ struct smem_private_entry { }; #define SMEM_PRIVATE_CANARY 0xa5a5 +/** + * struct smem_info - smem region info located after the table of contents + * @magic: magic number, must be SMEM_INFO_MAGIC + * @size: size of the smem region + * @base_addr: base address of the smem region + * @reserved: for now reserved entry + * @num_items: highest accepted item number + */ +struct smem_info { + u8 magic[4]; + __le32 size; + __le32 base_addr; + __le32 reserved; + __le16 num_items; +}; + +static const u8 SMEM_INFO_MAGIC[] = { 0x53, 0x49, 0x49, 0x49 }; /* SIII */ + /** * struct smem_region - representation of a chunk of memory used for smem * @aux_base: identifier of aux_mem base @@ -243,6 +261,7 @@ struct smem_region { * @partitions: list of pointers to partitions affecting the current * processor/host * @cacheline: list of cacheline sizes for each host + * @item_count: max accepted item number * @num_regions: number of @regions * @regions: list of the memory regions defining the shared memory */ @@ -255,6 +274,7 @@ struct qcom_smem { size_t global_cacheline; struct smem_partition_header *partitions[SMEM_HOST_COUNT]; size_t cacheline[SMEM_HOST_COUNT]; + u32 item_count; unsigned num_regions; struct smem_region regions[0]; @@ -386,9 +406,6 @@ static int qcom_smem_alloc_global(struct qcom_smem *smem, struct smem_global_entry *entry; struct smem_header *header; - if (WARN_ON(item >= SMEM_ITEM_COUNT)) - return -EINVAL; - header = smem->regions[0].virt_base; entry = &header->toc[item]; if (entry->allocated) @@ -439,6 +456,9 @@ int qcom_smem_alloc(unsigned host, unsigned item, size_t size) return -EINVAL; } + if (WARN_ON(item >= __smem->item_count)) + return -EINVAL; + ret = hwspin_lock_timeout_irqsave(__smem->hwlock, HWSPINLOCK_TIMEOUT, &flags); @@ -471,9 +491,6 @@ static void *qcom_smem_get_global(struct qcom_smem *smem, u32 aux_base; unsigned i; - if (WARN_ON(item >= SMEM_ITEM_COUNT)) - return ERR_PTR(-EINVAL); - header = smem->regions[0].virt_base; entry = &header->toc[item]; if (!entry->allocated) @@ -569,6 +586,9 @@ void *qcom_smem_get(unsigned host, unsigned item, size_t *size) if (!__smem) return ptr; + if (WARN_ON(item >= __smem->item_count)) + return ERR_PTR(-EINVAL); + ret = hwspin_lock_timeout_irqsave(__smem->hwlock, HWSPINLOCK_TIMEOUT, &flags); @@ -656,6 +676,22 @@ static struct smem_ptable *qcom_smem_get_ptable(struct qcom_smem *smem) return ptable; } +static u32 qcom_smem_get_item_count(struct qcom_smem *smem) +{ + struct smem_ptable *ptable; + struct smem_info *info; + + ptable = qcom_smem_get_ptable(smem); + if (IS_ERR_OR_NULL(ptable)) + return SMEM_ITEM_COUNT; + + info = (struct smem_info *)&ptable->entry[ptable->num_entries]; + if (memcmp(info->magic, SMEM_INFO_MAGIC, sizeof(info->magic))) + return SMEM_ITEM_COUNT; + + return le16_to_cpu(info->num_items); +} + static int qcom_smem_set_global_partition(struct qcom_smem *smem) { struct smem_partition_header *header; @@ -883,7 +919,10 @@ static int qcom_smem_probe(struct platform_device *pdev) ret = qcom_smem_set_global_partition(smem); if (ret < 0) return ret; + smem->item_count = qcom_smem_get_item_count(smem); + break; case SMEM_GLOBAL_HEAP_VERSION: + smem->item_count = SMEM_ITEM_COUNT; break; default: dev_err(&pdev->dev, "Unsupported SMEM version 0x%x\n", version); -- cgit From e691b48dcc7553b423bcbc7ddcd9f51698d093b1 Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Wed, 11 Oct 2017 14:29:10 -0700 Subject: soc: qcom: smem: Increase the number of hosts Increase the maximum number of hosts in a system to 10. Signed-off-by: Chris Lew Acked-by: Bjorn Andersson Signed-off-by: Andy Gross --- drivers/soc/qcom/smem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index 19704baa65f4..0b94d62fad2b 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -91,7 +91,7 @@ #define SMEM_GLOBAL_HOST 0xfffe /* Max number of processors/hosts in a system */ -#define SMEM_HOST_COUNT 9 +#define SMEM_HOST_COUNT 10 /** * struct smem_proc_comm - proc_comm communication struct (legacy) -- cgit From 775dba87f891e99c2f46df17edd5f5359d0883ad Mon Sep 17 00:00:00 2001 From: Timo Alho Date: Thu, 7 Sep 2017 12:31:04 +0300 Subject: soc/tegra: bpmp: Check BPMP response return code Add checks for the return code in BPMP response messages. Signed-off-by: Timo Alho Acked-by: Jon Hunter Signed-off-by: Thierry Reding --- drivers/soc/tegra/powergate-bpmp.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/powergate-bpmp.c b/drivers/soc/tegra/powergate-bpmp.c index 8fc356039401..82c7e27cd8bb 100644 --- a/drivers/soc/tegra/powergate-bpmp.c +++ b/drivers/soc/tegra/powergate-bpmp.c @@ -42,6 +42,7 @@ static int tegra_bpmp_powergate_set_state(struct tegra_bpmp *bpmp, { struct mrq_pg_request request; struct tegra_bpmp_message msg; + int err; memset(&request, 0, sizeof(request)); request.cmd = CMD_PG_SET_STATE; @@ -53,7 +54,13 @@ static int tegra_bpmp_powergate_set_state(struct tegra_bpmp *bpmp, msg.tx.data = &request; msg.tx.size = sizeof(request); - return tegra_bpmp_transfer(bpmp, &msg); + err = tegra_bpmp_transfer(bpmp, &msg); + if (err < 0) + return err; + else if (msg.rx.ret < 0) + return -EINVAL; + + return 0; } static int tegra_bpmp_powergate_get_state(struct tegra_bpmp *bpmp, @@ -80,6 +87,8 @@ static int tegra_bpmp_powergate_get_state(struct tegra_bpmp *bpmp, err = tegra_bpmp_transfer(bpmp, &msg); if (err < 0) return PG_STATE_OFF; + else if (msg.rx.ret < 0) + return -EINVAL; return response.get_state.state; } @@ -106,6 +115,8 @@ static int tegra_bpmp_powergate_get_max_id(struct tegra_bpmp *bpmp) err = tegra_bpmp_transfer(bpmp, &msg); if (err < 0) return err; + else if (msg.rx.ret < 0) + return -EINVAL; return response.get_max_id.max_id; } @@ -132,7 +143,7 @@ static char *tegra_bpmp_powergate_get_name(struct tegra_bpmp *bpmp, msg.rx.size = sizeof(response); err = tegra_bpmp_transfer(bpmp, &msg); - if (err < 0) + if (err < 0 || msg.rx.ret < 0) return NULL; return kstrdup(response.get_name.name, GFP_KERNEL); -- cgit From ec83368635ac153d58517bd286afc1fe773a1272 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Wed, 18 Oct 2017 16:28:43 +0800 Subject: soc: mediatek: pwrap: add pwrap_read32 for reading in 32-bit mode Some regulators such as MediaTek MT6380 has to be read in 32-bit mode. So the patch adds pwrap_read32, rename old pwrap_read into pwrap_read16 and one function pointer is introduced for increasing flexibility allowing the determination which mode is used by the pwrap slave detection through device tree. Signed-off-by: Chenglin Xu Signed-off-by: Chen Zhong Signed-off-by: Sean Wang Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-pmic-wrap.c | 55 +++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c index f095faac1e04..06930e2ebe4c 100644 --- a/drivers/soc/mediatek/mtk-pmic-wrap.c +++ b/drivers/soc/mediatek/mtk-pmic-wrap.c @@ -487,6 +487,7 @@ static int mt8135_regs[] = { enum pmic_type { PMIC_MT6323, + PMIC_MT6380, PMIC_MT6397, }; @@ -496,9 +497,16 @@ enum pwrap_type { PWRAP_MT8173, }; +struct pmic_wrapper; struct pwrap_slv_type { const u32 *dew_regs; enum pmic_type type; + /* + * pwrap operations are highly associated with the PMIC types, + * so the pointers added increases flexibility allowing determination + * which type is used by the detection through device tree. + */ + int (*pwrap_read)(struct pmic_wrapper *wrp, u32 adr, u32 *rdata); }; struct pmic_wrapper { @@ -609,7 +617,7 @@ static int pwrap_write(struct pmic_wrapper *wrp, u32 adr, u32 wdata) return 0; } -static int pwrap_read(struct pmic_wrapper *wrp, u32 adr, u32 *rdata) +static int pwrap_read16(struct pmic_wrapper *wrp, u32 adr, u32 *rdata) { int ret; @@ -632,6 +640,39 @@ static int pwrap_read(struct pmic_wrapper *wrp, u32 adr, u32 *rdata) return 0; } +static int pwrap_read32(struct pmic_wrapper *wrp, u32 adr, u32 *rdata) +{ + int ret, msb; + + *rdata = 0; + for (msb = 0; msb < 2; msb++) { + ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle); + if (ret) { + pwrap_leave_fsm_vldclr(wrp); + return ret; + } + + pwrap_writel(wrp, ((msb << 30) | (adr << 16)), + PWRAP_WACS2_CMD); + + ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_vldclr); + if (ret) + return ret; + + *rdata += (PWRAP_GET_WACS_RDATA(pwrap_readl(wrp, + PWRAP_WACS2_RDATA)) << (16 * msb)); + + pwrap_writel(wrp, 1, PWRAP_WACS2_VLDCLR); + } + + return 0; +} + +static int pwrap_read(struct pmic_wrapper *wrp, u32 adr, u32 *rdata) +{ + return wrp->slave->pwrap_read(wrp, adr, rdata); +} + static int pwrap_regmap_read(void *context, u32 adr, u32 *rdata) { return pwrap_read(context, adr, rdata); @@ -752,6 +793,8 @@ static int pwrap_mt2701_init_reg_clock(struct pmic_wrapper *wrp) pwrap_writel(wrp, 0x2, PWRAP_CSLEXT_START); pwrap_writel(wrp, 0x2, PWRAP_CSLEXT_END); break; + default: + break; } return 0; @@ -815,6 +858,8 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp) pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_CIPHER_EN], 0x1); break; + default: + break; } /* wait for cipher data ready@AP */ @@ -1036,11 +1081,19 @@ static const struct regmap_config pwrap_regmap_config = { static const struct pwrap_slv_type pmic_mt6323 = { .dew_regs = mt6323_regs, .type = PMIC_MT6323, + .pwrap_read = pwrap_read16, +}; + +static const struct pwrap_slv_type pmic_mt6380 = { + .dew_regs = NULL, + .type = PMIC_MT6380, + .pwrap_read = pwrap_read32, }; static const struct pwrap_slv_type pmic_mt6397 = { .dew_regs = mt6397_regs, .type = PMIC_MT6397, + .pwrap_read = pwrap_read16, }; static const struct of_device_id of_slave_match_tbl[] = { -- cgit From a10616f05307caf31bb45f413c1a3c48c4b130ea Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Wed, 18 Oct 2017 16:28:44 +0800 Subject: soc: mediatek: pwrap: add pwrap_write32 for writing in 32-bit mode Some regulators such as MediaTek MT6380 also has to be written in 32-bit mode. So the patch adds pwrap_write32, rename old pwrap_write into pwrap_write16 and one additional function pointer is introduced for increasing flexibility allowing the determination which mode is used by the pwrap slave detection through device tree. Signed-off-by: Chenglin Xu Signed-off-by: Chen Zhong Signed-off-by: Sean Wang Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-pmic-wrap.c | 70 +++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 16 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c index 06930e2ebe4c..2d3a8faae124 100644 --- a/drivers/soc/mediatek/mtk-pmic-wrap.c +++ b/drivers/soc/mediatek/mtk-pmic-wrap.c @@ -507,6 +507,7 @@ struct pwrap_slv_type { * which type is used by the detection through device tree. */ int (*pwrap_read)(struct pmic_wrapper *wrp, u32 adr, u32 *rdata); + int (*pwrap_write)(struct pmic_wrapper *wrp, u32 adr, u32 wdata); }; struct pmic_wrapper { @@ -601,22 +602,6 @@ static int pwrap_wait_for_state(struct pmic_wrapper *wrp, } while (1); } -static int pwrap_write(struct pmic_wrapper *wrp, u32 adr, u32 wdata) -{ - int ret; - - ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle); - if (ret) { - pwrap_leave_fsm_vldclr(wrp); - return ret; - } - - pwrap_writel(wrp, (1 << 31) | ((adr >> 1) << 16) | wdata, - PWRAP_WACS2_CMD); - - return 0; -} - static int pwrap_read16(struct pmic_wrapper *wrp, u32 adr, u32 *rdata) { int ret; @@ -673,6 +658,56 @@ static int pwrap_read(struct pmic_wrapper *wrp, u32 adr, u32 *rdata) return wrp->slave->pwrap_read(wrp, adr, rdata); } +static int pwrap_write16(struct pmic_wrapper *wrp, u32 adr, u32 wdata) +{ + int ret; + + ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle); + if (ret) { + pwrap_leave_fsm_vldclr(wrp); + return ret; + } + + pwrap_writel(wrp, (1 << 31) | ((adr >> 1) << 16) | wdata, + PWRAP_WACS2_CMD); + + return 0; +} + +static int pwrap_write32(struct pmic_wrapper *wrp, u32 adr, u32 wdata) +{ + int ret, msb, rdata; + + for (msb = 0; msb < 2; msb++) { + ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle); + if (ret) { + pwrap_leave_fsm_vldclr(wrp); + return ret; + } + + pwrap_writel(wrp, (1 << 31) | (msb << 30) | (adr << 16) | + ((wdata >> (msb * 16)) & 0xffff), + PWRAP_WACS2_CMD); + + /* + * The pwrap_read operation is the requirement of hardware used + * for the synchronization between two successive 16-bit + * pwrap_writel operations composing one 32-bit bus writing. + * Otherwise, we'll find the result fails on the lower 16-bit + * pwrap writing. + */ + if (!msb) + pwrap_read(wrp, adr, &rdata); + } + + return 0; +} + +static int pwrap_write(struct pmic_wrapper *wrp, u32 adr, u32 wdata) +{ + return wrp->slave->pwrap_write(wrp, adr, wdata); +} + static int pwrap_regmap_read(void *context, u32 adr, u32 *rdata) { return pwrap_read(context, adr, rdata); @@ -1082,18 +1117,21 @@ static const struct pwrap_slv_type pmic_mt6323 = { .dew_regs = mt6323_regs, .type = PMIC_MT6323, .pwrap_read = pwrap_read16, + .pwrap_write = pwrap_write16, }; static const struct pwrap_slv_type pmic_mt6380 = { .dew_regs = NULL, .type = PMIC_MT6380, .pwrap_read = pwrap_read32, + .pwrap_write = pwrap_write32, }; static const struct pwrap_slv_type pmic_mt6397 = { .dew_regs = mt6397_regs, .type = PMIC_MT6397, .pwrap_read = pwrap_read16, + .pwrap_write = pwrap_write16, }; static const struct of_device_id of_slave_match_tbl[] = { -- cgit From d56b31e2eabfce7e41c3beb67be91f00a03d42ed Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Wed, 18 Oct 2017 16:28:45 +0800 Subject: soc: mediatek: pwrap: refactor pwrap_init for the various PMIC types pwrap initialization is highly associated with the base SoC and the target PMICs, so slight refactorization is made here for allowing pwrap_init to run on those PMICs with different capability from the previous MediaTek PMICs and the determination for the enablement of the pwrap capability depending on PMIC type. Apart from this, the patch makes the driver more extensible especially when more PMICs join into the pwrap driver. Signed-off-by: Chenglin Xu Signed-off-by: Sean Wang Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-pmic-wrap.c | 130 ++++++++++++++++++++++++----------- 1 file changed, 90 insertions(+), 40 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c index 2d3a8faae124..e3398e37a7a6 100644 --- a/drivers/soc/mediatek/mtk-pmic-wrap.c +++ b/drivers/soc/mediatek/mtk-pmic-wrap.c @@ -70,6 +70,12 @@ PWRAP_WDT_SRC_EN_HARB_STAUPD_DLE | \ PWRAP_WDT_SRC_EN_HARB_STAUPD_ALE) +/* Group of bits used for shown slave capability */ +#define PWRAP_SLV_CAP_SPI BIT(0) +#define PWRAP_SLV_CAP_DUALIO BIT(1) +#define PWRAP_SLV_CAP_SECURITY BIT(2) +#define HAS_CAP(_c, _x) (((_c) & (_x)) == (_x)) + /* defines for slave device wrapper registers */ enum dew_regs { PWRAP_DEW_BASE, @@ -501,6 +507,8 @@ struct pmic_wrapper; struct pwrap_slv_type { const u32 *dew_regs; enum pmic_type type; + /* Flags indicating the capability for the target slave */ + u32 caps; /* * pwrap operations are highly associated with the PMIC types, * so the pointers added increases flexibility allowing determination @@ -787,6 +795,37 @@ static int pwrap_init_sidly(struct pmic_wrapper *wrp) return 0; } +static int pwrap_init_dual_io(struct pmic_wrapper *wrp) +{ + int ret; + u32 rdata; + + /* Enable dual IO mode */ + pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_DIO_EN], 1); + + /* Check IDLE & INIT_DONE in advance */ + ret = pwrap_wait_for_state(wrp, + pwrap_is_fsm_idle_and_sync_idle); + if (ret) { + dev_err(wrp->dev, "%s fail, ret=%d\n", __func__, ret); + return ret; + } + + pwrap_writel(wrp, 1, PWRAP_DIO_EN); + + /* Read Test */ + pwrap_read(wrp, + wrp->slave->dew_regs[PWRAP_DEW_READ_TEST], &rdata); + if (rdata != PWRAP_DEW_READ_TEST_VAL) { + dev_err(wrp->dev, + "Read failed on DIO mode: 0x%04x!=0x%04x\n", + PWRAP_DEW_READ_TEST_VAL, rdata); + return -EFAULT; + } + + return 0; +} + static int pwrap_mt8135_init_reg_clock(struct pmic_wrapper *wrp) { pwrap_writel(wrp, 0x4, PWRAP_CSHEXT); @@ -935,6 +974,30 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp) return 0; } +static int pwrap_init_security(struct pmic_wrapper *wrp) +{ + int ret; + + /* Enable encryption */ + ret = pwrap_init_cipher(wrp); + if (ret) + return ret; + + /* Signature checking - using CRC */ + if (pwrap_write(wrp, + wrp->slave->dew_regs[PWRAP_DEW_CRC_EN], 0x1)) + return -EFAULT; + + pwrap_writel(wrp, 0x1, PWRAP_CRC_EN); + pwrap_writel(wrp, 0x0, PWRAP_SIG_MODE); + pwrap_writel(wrp, wrp->slave->dew_regs[PWRAP_DEW_CRC_VAL], + PWRAP_SIG_ADR); + pwrap_writel(wrp, + wrp->master->arb_en_all, PWRAP_HIPRIO_ARB_EN); + + return 0; +} + static int pwrap_mt8135_init_soc_specific(struct pmic_wrapper *wrp) { /* enable pwrap events and pwrap bridge in AP side */ @@ -995,7 +1058,6 @@ static int pwrap_mt2701_init_soc_specific(struct pmic_wrapper *wrp) static int pwrap_init(struct pmic_wrapper *wrp) { int ret; - u32 rdata; reset_control_reset(wrp->rstc); if (wrp->rstc_bridge) @@ -1007,10 +1069,12 @@ static int pwrap_init(struct pmic_wrapper *wrp) pwrap_writel(wrp, 0, PWRAP_DCM_DBC_PRD); } - /* Reset SPI slave */ - ret = pwrap_reset_spislave(wrp); - if (ret) - return ret; + if (HAS_CAP(wrp->slave->caps, PWRAP_SLV_CAP_SPI)) { + /* Reset SPI slave */ + ret = pwrap_reset_spislave(wrp); + if (ret) + return ret; + } pwrap_writel(wrp, 1, PWRAP_WRAP_EN); @@ -1022,45 +1086,26 @@ static int pwrap_init(struct pmic_wrapper *wrp) if (ret) return ret; - /* Setup serial input delay */ - ret = pwrap_init_sidly(wrp); - if (ret) - return ret; - - /* Enable dual IO mode */ - pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_DIO_EN], 1); - - /* Check IDLE & INIT_DONE in advance */ - ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle_and_sync_idle); - if (ret) { - dev_err(wrp->dev, "%s fail, ret=%d\n", __func__, ret); - return ret; + if (HAS_CAP(wrp->slave->caps, PWRAP_SLV_CAP_SPI)) { + /* Setup serial input delay */ + ret = pwrap_init_sidly(wrp); + if (ret) + return ret; } - pwrap_writel(wrp, 1, PWRAP_DIO_EN); - - /* Read Test */ - pwrap_read(wrp, wrp->slave->dew_regs[PWRAP_DEW_READ_TEST], &rdata); - if (rdata != PWRAP_DEW_READ_TEST_VAL) { - dev_err(wrp->dev, "Read test failed after switch to DIO mode: 0x%04x != 0x%04x\n", - PWRAP_DEW_READ_TEST_VAL, rdata); - return -EFAULT; + if (HAS_CAP(wrp->slave->caps, PWRAP_SLV_CAP_DUALIO)) { + /* Enable dual I/O mode */ + ret = pwrap_init_dual_io(wrp); + if (ret) + return ret; } - /* Enable encryption */ - ret = pwrap_init_cipher(wrp); - if (ret) - return ret; - - /* Signature checking - using CRC */ - if (pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_CRC_EN], 0x1)) - return -EFAULT; - - pwrap_writel(wrp, 0x1, PWRAP_CRC_EN); - pwrap_writel(wrp, 0x0, PWRAP_SIG_MODE); - pwrap_writel(wrp, wrp->slave->dew_regs[PWRAP_DEW_CRC_VAL], - PWRAP_SIG_ADR); - pwrap_writel(wrp, wrp->master->arb_en_all, PWRAP_HIPRIO_ARB_EN); + if (HAS_CAP(wrp->slave->caps, PWRAP_SLV_CAP_SECURITY)) { + /* Enable security on bus */ + ret = pwrap_init_security(wrp); + if (ret) + return ret; + } if (wrp->master->type == PWRAP_MT8135) pwrap_writel(wrp, 0x7, PWRAP_RRARB_EN); @@ -1116,6 +1161,8 @@ static const struct regmap_config pwrap_regmap_config = { static const struct pwrap_slv_type pmic_mt6323 = { .dew_regs = mt6323_regs, .type = PMIC_MT6323, + .caps = PWRAP_SLV_CAP_SPI | PWRAP_SLV_CAP_DUALIO | + PWRAP_SLV_CAP_SECURITY, .pwrap_read = pwrap_read16, .pwrap_write = pwrap_write16, }; @@ -1123,6 +1170,7 @@ static const struct pwrap_slv_type pmic_mt6323 = { static const struct pwrap_slv_type pmic_mt6380 = { .dew_regs = NULL, .type = PMIC_MT6380, + .caps = 0, .pwrap_read = pwrap_read32, .pwrap_write = pwrap_write32, }; @@ -1130,6 +1178,8 @@ static const struct pwrap_slv_type pmic_mt6380 = { static const struct pwrap_slv_type pmic_mt6397 = { .dew_regs = mt6397_regs, .type = PMIC_MT6397, + .caps = PWRAP_SLV_CAP_SPI | PWRAP_SLV_CAP_DUALIO | + PWRAP_SLV_CAP_SECURITY, .pwrap_read = pwrap_read16, .pwrap_write = pwrap_write16, }; -- cgit From 7f80f1007881f1d5c7c37010ecd435946b65061d Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Wed, 18 Oct 2017 16:28:46 +0800 Subject: soc: mediatek: pwrap: add MediaTek MT6380 as one slave of pwrap Add MediaTek MT6380 regulator becoming one of PMIC wrapper slave and also add extra new regmap_config of 32-bit mode for MT6380 since old regmap_config of 16-bit mode can't be fit into the need. Signed-off-by: Chenglin Xu Signed-off-by: Chen Zhong Signed-off-by: Sean Wang Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-pmic-wrap.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c index e3398e37a7a6..45c3e44d8f40 100644 --- a/drivers/soc/mediatek/mtk-pmic-wrap.c +++ b/drivers/soc/mediatek/mtk-pmic-wrap.c @@ -507,6 +507,7 @@ struct pmic_wrapper; struct pwrap_slv_type { const u32 *dew_regs; enum pmic_type type; + const struct regmap_config *regmap; /* Flags indicating the capability for the target slave */ u32 caps; /* @@ -1149,7 +1150,7 @@ static irqreturn_t pwrap_interrupt(int irqno, void *dev_id) return IRQ_HANDLED; } -static const struct regmap_config pwrap_regmap_config = { +static const struct regmap_config pwrap_regmap_config16 = { .reg_bits = 16, .val_bits = 16, .reg_stride = 2, @@ -1158,9 +1159,19 @@ static const struct regmap_config pwrap_regmap_config = { .max_register = 0xffff, }; +static const struct regmap_config pwrap_regmap_config32 = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .reg_read = pwrap_regmap_read, + .reg_write = pwrap_regmap_write, + .max_register = 0xffff, +}; + static const struct pwrap_slv_type pmic_mt6323 = { .dew_regs = mt6323_regs, .type = PMIC_MT6323, + .regmap = &pwrap_regmap_config16, .caps = PWRAP_SLV_CAP_SPI | PWRAP_SLV_CAP_DUALIO | PWRAP_SLV_CAP_SECURITY, .pwrap_read = pwrap_read16, @@ -1170,6 +1181,7 @@ static const struct pwrap_slv_type pmic_mt6323 = { static const struct pwrap_slv_type pmic_mt6380 = { .dew_regs = NULL, .type = PMIC_MT6380, + .regmap = &pwrap_regmap_config32, .caps = 0, .pwrap_read = pwrap_read32, .pwrap_write = pwrap_write32, @@ -1178,6 +1190,7 @@ static const struct pwrap_slv_type pmic_mt6380 = { static const struct pwrap_slv_type pmic_mt6397 = { .dew_regs = mt6397_regs, .type = PMIC_MT6397, + .regmap = &pwrap_regmap_config16, .caps = PWRAP_SLV_CAP_SPI | PWRAP_SLV_CAP_DUALIO | PWRAP_SLV_CAP_SECURITY, .pwrap_read = pwrap_read16, @@ -1188,10 +1201,15 @@ static const struct of_device_id of_slave_match_tbl[] = { { .compatible = "mediatek,mt6323", .data = &pmic_mt6323, + }, { + /* The MT6380 PMIC only implements a regulator, so we bind it + * directly instead of using a MFD. + */ + .compatible = "mediatek,mt6380-regulator", + .data = &pmic_mt6380, }, { .compatible = "mediatek,mt6397", .data = &pmic_mt6397, - }, { /* sentinel */ } }; @@ -1372,7 +1390,7 @@ static int pwrap_probe(struct platform_device *pdev) if (ret) goto err_out2; - wrp->regmap = devm_regmap_init(wrp->dev, NULL, wrp, &pwrap_regmap_config); + wrp->regmap = devm_regmap_init(wrp->dev, NULL, wrp, wrp->slave->regmap); if (IS_ERR(wrp->regmap)) { ret = PTR_ERR(wrp->regmap); goto err_out2; -- cgit From 68189ed59d34bf430790c026465b5489c7edad66 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Wed, 18 Oct 2017 16:28:47 +0800 Subject: soc: mediatek: pwrap: add common way for setup CS timing extenstion Multiple platforms would always use their own way handling CS timing extension on the bus which leads to a little bit code duplication. Therefore, the patch groups the similar logic to handle CS timing extension into the common function which allows the following SoCs have more reusability for configing CS timing. Signed-off-by: Chenglin Xu Signed-off-by: Sean Wang Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-pmic-wrap.c | 59 ++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 22 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c index 45c3e44d8f40..cbc3f0e82337 100644 --- a/drivers/soc/mediatek/mtk-pmic-wrap.c +++ b/drivers/soc/mediatek/mtk-pmic-wrap.c @@ -827,23 +827,44 @@ static int pwrap_init_dual_io(struct pmic_wrapper *wrp) return 0; } -static int pwrap_mt8135_init_reg_clock(struct pmic_wrapper *wrp) +/* + * pwrap_init_chip_select_ext is used to configure CS extension time for each + * phase during data transactions on the pwrap bus. + */ +static void pwrap_init_chip_select_ext(struct pmic_wrapper *wrp, u8 hext_write, + u8 hext_read, u8 lext_start, + u8 lext_end) { - pwrap_writel(wrp, 0x4, PWRAP_CSHEXT); - pwrap_writel(wrp, 0x0, PWRAP_CSHEXT_WRITE); - pwrap_writel(wrp, 0x4, PWRAP_CSHEXT_READ); - pwrap_writel(wrp, 0x0, PWRAP_CSLEXT_START); - pwrap_writel(wrp, 0x0, PWRAP_CSLEXT_END); + /* + * After finishing a write and read transaction, extends CS high time + * to be at least xT of BUS CLK as hext_write and hext_read specifies + * respectively. + */ + pwrap_writel(wrp, hext_write, PWRAP_CSHEXT_WRITE); + pwrap_writel(wrp, hext_read, PWRAP_CSHEXT_READ); - return 0; + /* + * Extends CS low time after CSL and before CSH command to be at + * least xT of BUS CLK as lext_start and lext_end specifies + * respectively. + */ + pwrap_writel(wrp, lext_start, PWRAP_CSLEXT_START); + pwrap_writel(wrp, lext_end, PWRAP_CSLEXT_END); } -static int pwrap_mt8173_init_reg_clock(struct pmic_wrapper *wrp) +static int pwrap_common_init_reg_clock(struct pmic_wrapper *wrp) { - pwrap_writel(wrp, 0x0, PWRAP_CSHEXT_WRITE); - pwrap_writel(wrp, 0x4, PWRAP_CSHEXT_READ); - pwrap_writel(wrp, 0x2, PWRAP_CSLEXT_START); - pwrap_writel(wrp, 0x2, PWRAP_CSLEXT_END); + switch (wrp->master->type) { + case PWRAP_MT8173: + pwrap_init_chip_select_ext(wrp, 0, 4, 2, 2); + break; + case PWRAP_MT8135: + pwrap_writel(wrp, 0x4, PWRAP_CSHEXT); + pwrap_init_chip_select_ext(wrp, 0, 4, 0, 0); + break; + default: + break; + } return 0; } @@ -853,20 +874,14 @@ static int pwrap_mt2701_init_reg_clock(struct pmic_wrapper *wrp) switch (wrp->slave->type) { case PMIC_MT6397: pwrap_writel(wrp, 0xc, PWRAP_RDDMY); - pwrap_writel(wrp, 0x4, PWRAP_CSHEXT_WRITE); - pwrap_writel(wrp, 0x0, PWRAP_CSHEXT_READ); - pwrap_writel(wrp, 0x2, PWRAP_CSLEXT_START); - pwrap_writel(wrp, 0x2, PWRAP_CSLEXT_END); + pwrap_init_chip_select_ext(wrp, 4, 0, 2, 2); break; case PMIC_MT6323: pwrap_writel(wrp, 0x8, PWRAP_RDDMY); pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_RDDMY_NO], 0x8); - pwrap_writel(wrp, 0x5, PWRAP_CSHEXT_WRITE); - pwrap_writel(wrp, 0x0, PWRAP_CSHEXT_READ); - pwrap_writel(wrp, 0x2, PWRAP_CSLEXT_START); - pwrap_writel(wrp, 0x2, PWRAP_CSLEXT_END); + pwrap_init_chip_select_ext(wrp, 5, 0, 2, 2); break; default: break; @@ -1235,7 +1250,7 @@ static const struct pmic_wrapper_type pwrap_mt8135 = { .spi_w = PWRAP_MAN_CMD_SPI_WRITE, .wdt_src = PWRAP_WDT_SRC_MASK_ALL, .has_bridge = 1, - .init_reg_clock = pwrap_mt8135_init_reg_clock, + .init_reg_clock = pwrap_common_init_reg_clock, .init_soc_specific = pwrap_mt8135_init_soc_specific, }; @@ -1247,7 +1262,7 @@ static const struct pmic_wrapper_type pwrap_mt8173 = { .spi_w = PWRAP_MAN_CMD_SPI_WRITE, .wdt_src = PWRAP_WDT_SRC_MASK_NO_STAUPD, .has_bridge = 0, - .init_reg_clock = pwrap_mt8173_init_reg_clock, + .init_reg_clock = pwrap_common_init_reg_clock, .init_soc_specific = pwrap_mt8173_init_soc_specific, }; -- cgit From 69d0c060ac0cf61f25b35fb2c222c78168d04658 Mon Sep 17 00:00:00 2001 From: Chenglin Xu Date: Wed, 18 Oct 2017 16:28:48 +0800 Subject: soc: mediatek: pwrap: add support for MT7622 SoC Add the registers, callbacks and data structures required to make the PMIC wrapper work on MT7622. Signed-off-by: Chenglin Xu Signed-off-by: Chen Zhong Signed-off-by: Sean Wang Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-pmic-wrap.c | 170 +++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c index cbc3f0e82337..5d61d127e1d7 100644 --- a/drivers/soc/mediatek/mtk-pmic-wrap.c +++ b/drivers/soc/mediatek/mtk-pmic-wrap.c @@ -214,6 +214,36 @@ enum pwrap_regs { PWRAP_ADC_RDATA_ADDR1, PWRAP_ADC_RDATA_ADDR2, + /* MT7622 only regs */ + PWRAP_EINT_STA0_ADR, + PWRAP_EINT_STA1_ADR, + PWRAP_STA, + PWRAP_CLR, + PWRAP_DVFS_ADR8, + PWRAP_DVFS_WDATA8, + PWRAP_DVFS_ADR9, + PWRAP_DVFS_WDATA9, + PWRAP_DVFS_ADR10, + PWRAP_DVFS_WDATA10, + PWRAP_DVFS_ADR11, + PWRAP_DVFS_WDATA11, + PWRAP_DVFS_ADR12, + PWRAP_DVFS_WDATA12, + PWRAP_DVFS_ADR13, + PWRAP_DVFS_WDATA13, + PWRAP_DVFS_ADR14, + PWRAP_DVFS_WDATA14, + PWRAP_DVFS_ADR15, + PWRAP_DVFS_WDATA15, + PWRAP_EXT_CK, + PWRAP_ADC_RDATA_ADDR, + PWRAP_GPS_STA, + PWRAP_SW_RST, + PWRAP_DVFS_STEP_CTRL0, + PWRAP_DVFS_STEP_CTRL1, + PWRAP_DVFS_STEP_CTRL2, + PWRAP_SPI2_CTRL, + /* MT8135 only regs */ PWRAP_CSHEXT, PWRAP_EVENT_IN_EN, @@ -336,6 +366,118 @@ static int mt2701_regs[] = { [PWRAP_ADC_RDATA_ADDR2] = 0x154, }; +static int mt7622_regs[] = { + [PWRAP_MUX_SEL] = 0x0, + [PWRAP_WRAP_EN] = 0x4, + [PWRAP_DIO_EN] = 0x8, + [PWRAP_SIDLY] = 0xC, + [PWRAP_RDDMY] = 0x10, + [PWRAP_SI_CK_CON] = 0x14, + [PWRAP_CSHEXT_WRITE] = 0x18, + [PWRAP_CSHEXT_READ] = 0x1C, + [PWRAP_CSLEXT_START] = 0x20, + [PWRAP_CSLEXT_END] = 0x24, + [PWRAP_STAUPD_PRD] = 0x28, + [PWRAP_STAUPD_GRPEN] = 0x2C, + [PWRAP_EINT_STA0_ADR] = 0x30, + [PWRAP_EINT_STA1_ADR] = 0x34, + [PWRAP_STA] = 0x38, + [PWRAP_CLR] = 0x3C, + [PWRAP_STAUPD_MAN_TRIG] = 0x40, + [PWRAP_STAUPD_STA] = 0x44, + [PWRAP_WRAP_STA] = 0x48, + [PWRAP_HARB_INIT] = 0x4C, + [PWRAP_HARB_HPRIO] = 0x50, + [PWRAP_HIPRIO_ARB_EN] = 0x54, + [PWRAP_HARB_STA0] = 0x58, + [PWRAP_HARB_STA1] = 0x5C, + [PWRAP_MAN_EN] = 0x60, + [PWRAP_MAN_CMD] = 0x64, + [PWRAP_MAN_RDATA] = 0x68, + [PWRAP_MAN_VLDCLR] = 0x6C, + [PWRAP_WACS0_EN] = 0x70, + [PWRAP_INIT_DONE0] = 0x74, + [PWRAP_WACS0_CMD] = 0x78, + [PWRAP_WACS0_RDATA] = 0x7C, + [PWRAP_WACS0_VLDCLR] = 0x80, + [PWRAP_WACS1_EN] = 0x84, + [PWRAP_INIT_DONE1] = 0x88, + [PWRAP_WACS1_CMD] = 0x8C, + [PWRAP_WACS1_RDATA] = 0x90, + [PWRAP_WACS1_VLDCLR] = 0x94, + [PWRAP_WACS2_EN] = 0x98, + [PWRAP_INIT_DONE2] = 0x9C, + [PWRAP_WACS2_CMD] = 0xA0, + [PWRAP_WACS2_RDATA] = 0xA4, + [PWRAP_WACS2_VLDCLR] = 0xA8, + [PWRAP_INT_EN] = 0xAC, + [PWRAP_INT_FLG_RAW] = 0xB0, + [PWRAP_INT_FLG] = 0xB4, + [PWRAP_INT_CLR] = 0xB8, + [PWRAP_SIG_ADR] = 0xBC, + [PWRAP_SIG_MODE] = 0xC0, + [PWRAP_SIG_VALUE] = 0xC4, + [PWRAP_SIG_ERRVAL] = 0xC8, + [PWRAP_CRC_EN] = 0xCC, + [PWRAP_TIMER_EN] = 0xD0, + [PWRAP_TIMER_STA] = 0xD4, + [PWRAP_WDT_UNIT] = 0xD8, + [PWRAP_WDT_SRC_EN] = 0xDC, + [PWRAP_WDT_FLG] = 0xE0, + [PWRAP_DEBUG_INT_SEL] = 0xE4, + [PWRAP_DVFS_ADR0] = 0xE8, + [PWRAP_DVFS_WDATA0] = 0xEC, + [PWRAP_DVFS_ADR1] = 0xF0, + [PWRAP_DVFS_WDATA1] = 0xF4, + [PWRAP_DVFS_ADR2] = 0xF8, + [PWRAP_DVFS_WDATA2] = 0xFC, + [PWRAP_DVFS_ADR3] = 0x100, + [PWRAP_DVFS_WDATA3] = 0x104, + [PWRAP_DVFS_ADR4] = 0x108, + [PWRAP_DVFS_WDATA4] = 0x10C, + [PWRAP_DVFS_ADR5] = 0x110, + [PWRAP_DVFS_WDATA5] = 0x114, + [PWRAP_DVFS_ADR6] = 0x118, + [PWRAP_DVFS_WDATA6] = 0x11C, + [PWRAP_DVFS_ADR7] = 0x120, + [PWRAP_DVFS_WDATA7] = 0x124, + [PWRAP_DVFS_ADR8] = 0x128, + [PWRAP_DVFS_WDATA8] = 0x12C, + [PWRAP_DVFS_ADR9] = 0x130, + [PWRAP_DVFS_WDATA9] = 0x134, + [PWRAP_DVFS_ADR10] = 0x138, + [PWRAP_DVFS_WDATA10] = 0x13C, + [PWRAP_DVFS_ADR11] = 0x140, + [PWRAP_DVFS_WDATA11] = 0x144, + [PWRAP_DVFS_ADR12] = 0x148, + [PWRAP_DVFS_WDATA12] = 0x14C, + [PWRAP_DVFS_ADR13] = 0x150, + [PWRAP_DVFS_WDATA13] = 0x154, + [PWRAP_DVFS_ADR14] = 0x158, + [PWRAP_DVFS_WDATA14] = 0x15C, + [PWRAP_DVFS_ADR15] = 0x160, + [PWRAP_DVFS_WDATA15] = 0x164, + [PWRAP_SPMINF_STA] = 0x168, + [PWRAP_CIPHER_KEY_SEL] = 0x16C, + [PWRAP_CIPHER_IV_SEL] = 0x170, + [PWRAP_CIPHER_EN] = 0x174, + [PWRAP_CIPHER_RDY] = 0x178, + [PWRAP_CIPHER_MODE] = 0x17C, + [PWRAP_CIPHER_SWRST] = 0x180, + [PWRAP_DCM_EN] = 0x184, + [PWRAP_DCM_DBC_PRD] = 0x188, + [PWRAP_EXT_CK] = 0x18C, + [PWRAP_ADC_CMD_ADDR] = 0x190, + [PWRAP_PWRAP_ADC_CMD] = 0x194, + [PWRAP_ADC_RDATA_ADDR] = 0x198, + [PWRAP_GPS_STA] = 0x19C, + [PWRAP_SW_RST] = 0x1A0, + [PWRAP_DVFS_STEP_CTRL0] = 0x238, + [PWRAP_DVFS_STEP_CTRL1] = 0x23C, + [PWRAP_DVFS_STEP_CTRL2] = 0x240, + [PWRAP_SPI2_CTRL] = 0x244, +}; + static int mt8173_regs[] = { [PWRAP_MUX_SEL] = 0x0, [PWRAP_WRAP_EN] = 0x4, @@ -499,6 +641,7 @@ enum pmic_type { enum pwrap_type { PWRAP_MT2701, + PWRAP_MT7622, PWRAP_MT8135, PWRAP_MT8173, }; @@ -927,6 +1070,9 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp) case PWRAP_MT8173: pwrap_writel(wrp, 1, PWRAP_CIPHER_EN); break; + case PWRAP_MT7622: + pwrap_writel(wrp, 0, PWRAP_CIPHER_EN); + break; } /* Config cipher mode @PMIC */ @@ -1071,6 +1217,15 @@ static int pwrap_mt2701_init_soc_specific(struct pmic_wrapper *wrp) return 0; } +static int pwrap_mt7622_init_soc_specific(struct pmic_wrapper *wrp) +{ + pwrap_writel(wrp, 0, PWRAP_STAUPD_PRD); + /* enable 2wire SPI master */ + pwrap_writel(wrp, 0x8000000, PWRAP_SPI2_CTRL); + + return 0; +} + static int pwrap_init(struct pmic_wrapper *wrp) { int ret; @@ -1242,6 +1397,18 @@ static const struct pmic_wrapper_type pwrap_mt2701 = { .init_soc_specific = pwrap_mt2701_init_soc_specific, }; +static const struct pmic_wrapper_type pwrap_mt7622 = { + .regs = mt7622_regs, + .type = PWRAP_MT7622, + .arb_en_all = 0xff, + .int_en_all = ~(u32)BIT(31), + .spi_w = PWRAP_MAN_CMD_SPI_WRITE, + .wdt_src = PWRAP_WDT_SRC_MASK_ALL, + .has_bridge = 0, + .init_reg_clock = pwrap_common_init_reg_clock, + .init_soc_specific = pwrap_mt7622_init_soc_specific, +}; + static const struct pmic_wrapper_type pwrap_mt8135 = { .regs = mt8135_regs, .type = PWRAP_MT8135, @@ -1270,6 +1437,9 @@ static const struct of_device_id of_pwrap_match_tbl[] = { { .compatible = "mediatek,mt2701-pwrap", .data = &pwrap_mt2701, + }, { + .compatible = "mediatek,mt7622-pwrap", + .data = &pwrap_mt7622, }, { .compatible = "mediatek,mt8135-pwrap", .data = &pwrap_mt8135, -- cgit From 31d7b359a94b8c96e54c6e3b438e6c7b65b9acb1 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Thu, 5 Oct 2017 11:17:49 +0800 Subject: soc: mediatek: place Kconfig for all SoC drivers under menu Add cleanup for placing all Kconfig for all MediaTek SoC drivers under the independent menu as other SoCs vendor usually did. Since the menu would be shown depending on "ARCH_MEDIATEK || COMPILE_TEST" selected and MTK_PMIC_WRAP is still safe compiling with the case of "COMPILE_TEST" only, the superfluous dependency for those items under the menu also is also being removed for the sake of simplicity. Signed-off-by: Sean Wang Reviewed-by: Jean Delvare Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/Kconfig | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig index 609bb3424c14..a7d0667338f2 100644 --- a/drivers/soc/mediatek/Kconfig +++ b/drivers/soc/mediatek/Kconfig @@ -1,9 +1,11 @@ # # MediaTek SoC drivers # +menu "MediaTek SoC drivers" + depends on ARCH_MEDIATEK || COMPILE_TEST + config MTK_INFRACFG bool "MediaTek INFRACFG Support" - depends on ARCH_MEDIATEK || COMPILE_TEST select REGMAP help Say yes here to add support for the MediaTek INFRACFG controller. The @@ -12,7 +14,6 @@ config MTK_INFRACFG config MTK_PMIC_WRAP tristate "MediaTek PMIC Wrapper Support" - depends on ARCH_MEDIATEK depends on RESET_CONTROLLER select REGMAP help @@ -22,7 +23,6 @@ config MTK_PMIC_WRAP config MTK_SCPSYS bool "MediaTek SCPSYS Support" - depends on ARCH_MEDIATEK || COMPILE_TEST default ARCH_MEDIATEK select REGMAP select MTK_INFRACFG @@ -30,3 +30,5 @@ config MTK_SCPSYS help Say yes here to add support for the MediaTek SCPSYS power domain driver. + +endmenu -- cgit From ab86ba2f8c3ab075833357c8ddc1b1ecd6b599a6 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 5 Oct 2017 11:17:43 +0200 Subject: soc: Allow test-building of MediaTek drivers The COMPILE_TEST alternative dependency allows test-building the drivers but only as long as the build system itself will look into the directory where the drivers reside. Signed-off-by: Jean Delvare Cc: Sean Wang Cc: Matthias Brugger Acked-by: Sean Wang Signed-off-by: Matthias Brugger --- drivers/soc/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 2fcaff864584..1d920a9e94d0 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -10,7 +10,7 @@ obj-$(CONFIG_MACH_DOVE) += dove/ obj-y += fsl/ obj-$(CONFIG_ARCH_MXC) += imx/ obj-$(CONFIG_SOC_XWAY) += lantiq/ -obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ +obj-y += mediatek/ obj-$(CONFIG_ARCH_MESON) += amlogic/ obj-$(CONFIG_ARCH_QCOM) += qcom/ obj-y += renesas/ -- cgit From fb2c1934f30577756e55e24e8870b45c78da3bc2 Mon Sep 17 00:00:00 2001 From: Matthias Brugger Date: Sat, 21 Oct 2017 10:17:47 +0200 Subject: soc: mediatek: pwrap: fix compiler errors When compiling using sparse, we got the following error: drivers/soc/mediatek/mtk-pmic-wrap.c:686:25: error: dubious one-bit signed bitfield Changing the data type to unsigned fixes this. Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-pmic-wrap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c index 5d61d127e1d7..912edf93c192 100644 --- a/drivers/soc/mediatek/mtk-pmic-wrap.c +++ b/drivers/soc/mediatek/mtk-pmic-wrap.c @@ -683,7 +683,7 @@ struct pmic_wrapper_type { u32 int_en_all; u32 spi_w; u32 wdt_src; - int has_bridge:1; + unsigned int has_bridge:1; int (*init_reg_clock)(struct pmic_wrapper *wrp); int (*init_soc_specific)(struct pmic_wrapper *wrp); }; -- cgit From f32fbbadaf4470e8bac27f6d87bd27441df7167f Mon Sep 17 00:00:00 2001 From: Matthias Brugger Date: Sat, 21 Oct 2017 10:18:20 +0200 Subject: soc: mediatek: pwrap: fix fatal compiler error When adding the MT6380 compatible, the sentinel for of_device_id was deleted, which leades to the following compiler error: FATAL: drivers/soc/mediatek/mtk-pmic-wrap: struct of_device_id is not terminated with a NULL entry! Fix this by adding the sentinel again. Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-pmic-wrap.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c index 912edf93c192..e9e054a15b7d 100644 --- a/drivers/soc/mediatek/mtk-pmic-wrap.c +++ b/drivers/soc/mediatek/mtk-pmic-wrap.c @@ -1380,6 +1380,7 @@ static const struct of_device_id of_slave_match_tbl[] = { }, { .compatible = "mediatek,mt6397", .data = &pmic_mt6397, + }, { /* sentinel */ } }; -- cgit From d1de6d6c639b7827c42c4750b8101ed1049e1c72 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Mon, 16 Oct 2017 13:17:08 -0500 Subject: soc: qcom: Remote filesystem memory driver The Qualcomm remote file system protocol is used by certain remoteprocs, in particular the modem, to read and write persistent storage in platforms where only the application CPU has physical storage access. The protocol is based on a set of QMI-encoded control-messages and a shared memory buffer for exchaning the data. This driver implements the latter, providing the user space service access to the carved out chunk of memory. Acked-by: Rob Herring Signed-off-by: Bjorn Andersson Signed-off-by: Andy Gross --- drivers/soc/qcom/Kconfig | 11 ++ drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/rmtfs_mem.c | 271 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 283 insertions(+) create mode 100644 drivers/soc/qcom/rmtfs_mem.c (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index b00bccddcd3b..b81374bb6713 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -35,6 +35,17 @@ config QCOM_PM modes. It interface with various system drivers to put the cores in low power modes. +config QCOM_RMTFS_MEM + tristate "Qualcomm Remote Filesystem memory driver" + depends on ARCH_QCOM + help + The Qualcomm remote filesystem memory driver is used for allocating + and exposing regions of shared memory with remote processors for the + purpose of exchanging sector-data between the remote filesystem + service and its clients. + + Say y here if you intend to boot the modem remoteproc. + config QCOM_SMEM tristate "Qualcomm Shared Memory Manager (SMEM)" depends on ARCH_QCOM diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index f151de41eb93..250407faa7d9 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_QCOM_GLINK_SSR) += glink_ssr.o obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o obj-$(CONFIG_QCOM_PM) += spm.o +obj-$(CONFIG_QCOM_RMTFS_MEM) += rmtfs_mem.o obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o obj-$(CONFIG_QCOM_SMEM) += smem.o obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o diff --git a/drivers/soc/qcom/rmtfs_mem.c b/drivers/soc/qcom/rmtfs_mem.c new file mode 100644 index 000000000000..f6f2f0cb3b3a --- /dev/null +++ b/drivers/soc/qcom/rmtfs_mem.c @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define QCOM_RMTFS_MEM_DEV_MAX (MINORMASK + 1) + +static dev_t qcom_rmtfs_mem_major; + +struct qcom_rmtfs_mem { + struct device dev; + struct cdev cdev; + + void *base; + phys_addr_t addr; + phys_addr_t size; + + unsigned int client_id; +}; + +static ssize_t qcom_rmtfs_mem_show(struct device *dev, + struct device_attribute *attr, + char *buf); + +static DEVICE_ATTR(phys_addr, 0400, qcom_rmtfs_mem_show, NULL); +static DEVICE_ATTR(size, 0400, qcom_rmtfs_mem_show, NULL); +static DEVICE_ATTR(client_id, 0400, qcom_rmtfs_mem_show, NULL); + +static ssize_t qcom_rmtfs_mem_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct qcom_rmtfs_mem *rmtfs_mem = container_of(dev, + struct qcom_rmtfs_mem, + dev); + + if (attr == &dev_attr_phys_addr) + return sprintf(buf, "%pa\n", &rmtfs_mem->addr); + if (attr == &dev_attr_size) + return sprintf(buf, "%pa\n", &rmtfs_mem->size); + if (attr == &dev_attr_client_id) + return sprintf(buf, "%d\n", rmtfs_mem->client_id); + + return -EINVAL; +} + +static struct attribute *qcom_rmtfs_mem_attrs[] = { + &dev_attr_phys_addr.attr, + &dev_attr_size.attr, + &dev_attr_client_id.attr, + NULL +}; +ATTRIBUTE_GROUPS(qcom_rmtfs_mem); + +static int qcom_rmtfs_mem_open(struct inode *inode, struct file *filp) +{ + struct qcom_rmtfs_mem *rmtfs_mem = container_of(inode->i_cdev, + struct qcom_rmtfs_mem, + cdev); + + get_device(&rmtfs_mem->dev); + filp->private_data = rmtfs_mem; + + return 0; +} +static ssize_t qcom_rmtfs_mem_read(struct file *filp, + char __user *buf, size_t count, loff_t *f_pos) +{ + struct qcom_rmtfs_mem *rmtfs_mem = filp->private_data; + + if (*f_pos >= rmtfs_mem->size) + return 0; + + if (*f_pos + count >= rmtfs_mem->size) + count = rmtfs_mem->size - *f_pos; + + if (copy_to_user(buf, rmtfs_mem->base + *f_pos, count)) + return -EFAULT; + + *f_pos += count; + return count; +} + +static ssize_t qcom_rmtfs_mem_write(struct file *filp, + const char __user *buf, size_t count, + loff_t *f_pos) +{ + struct qcom_rmtfs_mem *rmtfs_mem = filp->private_data; + + if (*f_pos >= rmtfs_mem->size) + return 0; + + if (*f_pos + count >= rmtfs_mem->size) + count = rmtfs_mem->size - *f_pos; + + if (copy_from_user(rmtfs_mem->base + *f_pos, buf, count)) + return -EFAULT; + + *f_pos += count; + return count; +} + +static int qcom_rmtfs_mem_release(struct inode *inode, struct file *filp) +{ + struct qcom_rmtfs_mem *rmtfs_mem = filp->private_data; + + put_device(&rmtfs_mem->dev); + + return 0; +} + +static const struct file_operations qcom_rmtfs_mem_fops = { + .owner = THIS_MODULE, + .open = qcom_rmtfs_mem_open, + .read = qcom_rmtfs_mem_read, + .write = qcom_rmtfs_mem_write, + .release = qcom_rmtfs_mem_release, + .llseek = default_llseek, +}; + +static void qcom_rmtfs_mem_release_device(struct device *dev) +{ + struct qcom_rmtfs_mem *rmtfs_mem = container_of(dev, + struct qcom_rmtfs_mem, + dev); + + kfree(rmtfs_mem); +} + +static int qcom_rmtfs_mem_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct reserved_mem *rmem; + struct qcom_rmtfs_mem *rmtfs_mem; + u32 client_id; + int ret; + + rmem = of_reserved_mem_lookup(node); + if (!rmem) { + dev_err(&pdev->dev, "failed to acquire memory region\n"); + return -EINVAL; + } + + ret = of_property_read_u32(node, "qcom,client-id", &client_id); + if (ret) { + dev_err(&pdev->dev, "failed to parse \"qcom,client-id\"\n"); + return ret; + + } + + rmtfs_mem = kzalloc(sizeof(*rmtfs_mem), GFP_KERNEL); + if (!rmtfs_mem) + return -ENOMEM; + + rmtfs_mem->addr = rmem->base; + rmtfs_mem->client_id = client_id; + rmtfs_mem->size = rmem->size; + + device_initialize(&rmtfs_mem->dev); + rmtfs_mem->dev.parent = &pdev->dev; + rmtfs_mem->dev.groups = qcom_rmtfs_mem_groups; + + rmtfs_mem->base = devm_memremap(&rmtfs_mem->dev, rmtfs_mem->addr, + rmtfs_mem->size, MEMREMAP_WC); + if (IS_ERR(rmtfs_mem->base)) { + dev_err(&pdev->dev, "failed to remap rmtfs_mem region\n"); + ret = PTR_ERR(rmtfs_mem->base); + goto put_device; + } + + cdev_init(&rmtfs_mem->cdev, &qcom_rmtfs_mem_fops); + rmtfs_mem->cdev.owner = THIS_MODULE; + + dev_set_name(&rmtfs_mem->dev, "qcom_rmtfs_mem%d", client_id); + rmtfs_mem->dev.id = client_id; + rmtfs_mem->dev.devt = MKDEV(MAJOR(qcom_rmtfs_mem_major), client_id); + + ret = cdev_device_add(&rmtfs_mem->cdev, &rmtfs_mem->dev); + if (ret) { + dev_err(&pdev->dev, "failed to add cdev: %d\n", ret); + goto put_device; + } + + rmtfs_mem->dev.release = qcom_rmtfs_mem_release_device; + + dev_set_drvdata(&pdev->dev, rmtfs_mem); + + return 0; + +remove_cdev: + cdev_device_del(&rmtfs_mem->cdev, &rmtfs_mem->dev); +put_device: + put_device(&rmtfs_mem->dev); + + return ret; +} + +static int qcom_rmtfs_mem_remove(struct platform_device *pdev) +{ + struct qcom_rmtfs_mem *rmtfs_mem = dev_get_drvdata(&pdev->dev); + + cdev_device_del(&rmtfs_mem->cdev, &rmtfs_mem->dev); + put_device(&rmtfs_mem->dev); + + return 0; +} + +static const struct of_device_id qcom_rmtfs_mem_of_match[] = { + { .compatible = "qcom,rmtfs-mem" }, + {} +}; +MODULE_DEVICE_TABLE(of, qcom_rmtfs_mem_of_match); + +static struct platform_driver qcom_rmtfs_mem_driver = { + .probe = qcom_rmtfs_mem_probe, + .remove = qcom_rmtfs_mem_remove, + .driver = { + .name = "qcom_rmtfs_mem", + .of_match_table = qcom_rmtfs_mem_of_match, + }, +}; + +static int qcom_rmtfs_mem_init(void) +{ + int ret; + + ret = alloc_chrdev_region(&qcom_rmtfs_mem_major, 0, + QCOM_RMTFS_MEM_DEV_MAX, "qcom_rmtfs_mem"); + if (ret < 0) { + pr_err("qcom_rmtfs_mem: failed to allocate char dev region\n"); + return ret; + } + + ret = platform_driver_register(&qcom_rmtfs_mem_driver); + if (ret < 0) { + pr_err("qcom_rmtfs_mem: failed to register rmtfs_mem driver\n"); + unregister_chrdev_region(qcom_rmtfs_mem_major, + QCOM_RMTFS_MEM_DEV_MAX); + } + + return ret; +} +module_init(qcom_rmtfs_mem_init); + +static void qcom_rmtfs_mem_exit(void) +{ + platform_driver_unregister(&qcom_rmtfs_mem_driver); + unregister_chrdev_region(qcom_rmtfs_mem_major, QCOM_RMTFS_MEM_DEV_MAX); +} +module_exit(qcom_rmtfs_mem_exit); -- cgit From 75fcb5ca4b465580565e63cd9ca807b37169c798 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Sat, 28 Oct 2017 18:40:16 +0200 Subject: soc: amlogic: add Meson GX VPU Domains driver The Video Processing Unit needs a specific Power Domain powering scheme this driver handles this as a PM Power Domain driver. Signed-off-by: Neil Armstrong Signed-off-by: Kevin Hilman --- drivers/soc/amlogic/Kconfig | 10 ++ drivers/soc/amlogic/Makefile | 1 + drivers/soc/amlogic/meson-gx-pwrc-vpu.c | 234 ++++++++++++++++++++++++++++++++ 3 files changed, 245 insertions(+) create mode 100644 drivers/soc/amlogic/meson-gx-pwrc-vpu.c (limited to 'drivers/soc') diff --git a/drivers/soc/amlogic/Kconfig b/drivers/soc/amlogic/Kconfig index ef0b8f6c53e0..1621bb25a1ea 100644 --- a/drivers/soc/amlogic/Kconfig +++ b/drivers/soc/amlogic/Kconfig @@ -9,6 +9,16 @@ config MESON_GX_SOCINFO Say yes to support decoding of Amlogic Meson GX SoC family information about the type, package and version. +config MESON_GX_PM_DOMAINS + bool "Amlogic Meson GX Power Domains driver" + depends on ARCH_MESON || COMPILE_TEST + default ARCH_MESON + select PM_GENERIC_DOMAINS + select PM_GENERIC_DOMAINS_OF + help + Say yes to expose Amlogic Meson GX Power Domains as + Generic Power Domains. + config MESON_MX_SOCINFO bool "Amlogic Meson MX SoC Information driver" depends on ARCH_MESON || COMPILE_TEST diff --git a/drivers/soc/amlogic/Makefile b/drivers/soc/amlogic/Makefile index 1f5df50ebd73..8fa321893928 100644 --- a/drivers/soc/amlogic/Makefile +++ b/drivers/soc/amlogic/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_MESON_GX_SOCINFO) += meson-gx-socinfo.o +obj-$(CONFIG_MESON_GX_PM_DOMAINS) += meson-gx-pwrc-vpu.o obj-$(CONFIG_MESON_MX_SOCINFO) += meson-mx-socinfo.o diff --git a/drivers/soc/amlogic/meson-gx-pwrc-vpu.c b/drivers/soc/amlogic/meson-gx-pwrc-vpu.c new file mode 100644 index 000000000000..bf5190b65ad9 --- /dev/null +++ b/drivers/soc/amlogic/meson-gx-pwrc-vpu.c @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2017 BayLibre, SAS + * Author: Neil Armstrong + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* AO Offsets */ + +#define AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2) + +#define GEN_PWR_VPU_HDMI BIT(8) +#define GEN_PWR_VPU_HDMI_ISO BIT(9) + +/* HHI Offsets */ + +#define HHI_MEM_PD_REG0 (0x40 << 2) +#define HHI_VPU_MEM_PD_REG0 (0x41 << 2) +#define HHI_VPU_MEM_PD_REG1 (0x42 << 2) + +struct meson_gx_pwrc_vpu { + struct generic_pm_domain genpd; + struct regmap *regmap_ao; + struct regmap *regmap_hhi; + struct reset_control *rstc; + struct clk *vpu_clk; + struct clk *vapb_clk; + bool powered; +}; + +static inline +struct meson_gx_pwrc_vpu *genpd_to_pd(struct generic_pm_domain *d) +{ + return container_of(d, struct meson_gx_pwrc_vpu, genpd); +} + +static int meson_gx_pwrc_vpu_power_off(struct generic_pm_domain *genpd) +{ + struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); + int i; + + regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO); + udelay(20); + + /* Power Down Memories */ + for (i = 0; i < 32; i += 2) { + regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, + 0x2 << i, 0x3 << i); + udelay(5); + } + for (i = 0; i < 32; i += 2) { + regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, + 0x2 << i, 0x3 << i); + udelay(5); + } + for (i = 8; i < 16; i++) { + regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, + BIT(i), BIT(i)); + udelay(5); + } + udelay(20); + + regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI); + + msleep(20); + + clk_disable_unprepare(pd->vpu_clk); + clk_disable_unprepare(pd->vapb_clk); + + pd->powered = false; + + return 0; +} + +static int meson_gx_pwrc_vpu_setup_clk(struct meson_gx_pwrc_vpu *pd) +{ + int ret; + + ret = clk_prepare_enable(pd->vpu_clk); + if (ret) + return ret; + + return clk_prepare_enable(pd->vapb_clk); +} + +static int meson_gx_pwrc_vpu_power_on(struct generic_pm_domain *genpd) +{ + struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); + int ret; + int i; + + regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VPU_HDMI, 0); + udelay(20); + + /* Power Up Memories */ + for (i = 0; i < 32; i += 2) { + regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, + 0x2 << i, 0); + udelay(5); + } + + for (i = 0; i < 32; i += 2) { + regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, + 0x2 << i, 0); + udelay(5); + } + + for (i = 8; i < 16; i++) { + regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, + BIT(i), 0); + udelay(5); + } + udelay(20); + + ret = reset_control_assert(pd->rstc); + if (ret) + return ret; + + regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VPU_HDMI_ISO, 0); + + ret = reset_control_deassert(pd->rstc); + if (ret) + return ret; + + ret = meson_gx_pwrc_vpu_setup_clk(pd); + if (ret) + return ret; + + pd->powered = true; + + return 0; +} + +static bool meson_gx_pwrc_vpu_get_power(struct meson_gx_pwrc_vpu *pd) +{ + u32 reg; + + regmap_read(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, ®); + + return (reg & GEN_PWR_VPU_HDMI); +} + +static struct meson_gx_pwrc_vpu vpu_hdmi_pd = { + .genpd = { + .name = "vpu_hdmi", + .power_off = meson_gx_pwrc_vpu_power_off, + .power_on = meson_gx_pwrc_vpu_power_on, + }, +}; + +static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev) +{ + struct regmap *regmap_ao, *regmap_hhi; + struct reset_control *rstc; + struct clk *vpu_clk; + struct clk *vapb_clk; + + regmap_ao = syscon_node_to_regmap(of_get_parent(pdev->dev.of_node)); + if (IS_ERR(regmap_ao)) { + dev_err(&pdev->dev, "failed to get regmap\n"); + return PTR_ERR(regmap_ao); + } + + regmap_hhi = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "amlogic,hhi-sysctrl"); + if (IS_ERR(regmap_hhi)) { + dev_err(&pdev->dev, "failed to get HHI regmap\n"); + return PTR_ERR(regmap_hhi); + } + + rstc = devm_reset_control_array_get(&pdev->dev, false, false); + if (IS_ERR(rstc)) { + dev_err(&pdev->dev, "failed to get reset lines\n"); + return PTR_ERR(rstc); + } + + vpu_clk = devm_clk_get(&pdev->dev, "vpu"); + if (IS_ERR(vpu_clk)) { + dev_err(&pdev->dev, "vpu clock request failed\n"); + return PTR_ERR(vpu_clk); + } + + vapb_clk = devm_clk_get(&pdev->dev, "vapb"); + if (IS_ERR(vapb_clk)) { + dev_err(&pdev->dev, "vapb clock request failed\n"); + return PTR_ERR(vapb_clk); + } + + vpu_hdmi_pd.regmap_ao = regmap_ao; + vpu_hdmi_pd.regmap_hhi = regmap_hhi; + vpu_hdmi_pd.rstc = rstc; + vpu_hdmi_pd.vpu_clk = vpu_clk; + vpu_hdmi_pd.vapb_clk = vapb_clk; + + pm_genpd_init(&vpu_hdmi_pd.genpd, &simple_qos_governor, + meson_gx_pwrc_vpu_get_power(&vpu_hdmi_pd)); + + return of_genpd_add_provider_simple(pdev->dev.of_node, + &vpu_hdmi_pd.genpd); +} + +static void meson_gx_pwrc_vpu_shutdown(struct platform_device *pdev) +{ + if (vpu_hdmi_pd.powered) + meson_gx_pwrc_vpu_power_off(&vpu_hdmi_pd.genpd); +} + +static const struct of_device_id meson_gx_pwrc_vpu_match_table[] = { + { .compatible = "amlogic,meson-gx-pwrc-vpu" }, + { /* sentinel */ } +}; + +static struct platform_driver meson_gx_pwrc_vpu_driver = { + .probe = meson_gx_pwrc_vpu_probe, + .shutdown = meson_gx_pwrc_vpu_shutdown, + .driver = { + .name = "meson_gx_pwrc_vpu", + .of_match_table = meson_gx_pwrc_vpu_match_table, + }, +}; +builtin_platform_driver(meson_gx_pwrc_vpu_driver); -- cgit From 30f3b0678ce6fc46bcdde61ca35821032d0f25b5 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 2 Nov 2017 23:39:54 +0100 Subject: soc: amlogic: gx pm domain: add PM and OF dependencies The new driver introduces harmless warnings: warning: (PM_RMOBILE && ARCH_RCAR_GEN1 && ARCH_RCAR_GEN2 && ARCH_R7S72100 && MESON_GX_PM_DOMAINS) selects PM_GENERIC_DOMAINS which has unmet direct dependencies (PM) warning: (MESON_GX_PM_DOMAINS) selects PM_GENERIC_DOMAINS_OF which has unmet direct dependencies (PM_GENERIC_DOMAINS && OF) This adds CONFIG_OF and CONFIG_PM dependencies to ensure it will only be enabled in valid configurations. Fixes: 75fcb5ca4b46 ("soc: amlogic: add Meson GX VPU Domains driver") Signed-off-by: Arnd Bergmann --- drivers/soc/amlogic/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/soc') diff --git a/drivers/soc/amlogic/Kconfig b/drivers/soc/amlogic/Kconfig index 1621bb25a1ea..b04f6e4aedbc 100644 --- a/drivers/soc/amlogic/Kconfig +++ b/drivers/soc/amlogic/Kconfig @@ -12,6 +12,7 @@ config MESON_GX_SOCINFO config MESON_GX_PM_DOMAINS bool "Amlogic Meson GX Power Domains driver" depends on ARCH_MESON || COMPILE_TEST + depends on PM && OF default ARCH_MESON select PM_GENERIC_DOMAINS select PM_GENERIC_DOMAINS_OF -- cgit From d771d62e3139986cc62d2067b37ebbe33a6d3b08 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 18 Oct 2017 10:04:28 +0200 Subject: soc: qcom: remove unused label The newly added driver comes with a harmless warning: drivers/soc/qcom/rmtfs_mem.c: In function 'qcom_rmtfs_mem_probe': drivers/soc/qcom/rmtfs_mem.c:211:1: error: label 'remove_cdev' defined but not used [-Werror=unused-label] This removes the unused label to avoid the warning. Fixes: 702baebb8e00 ("soc: qcom: Remote filesystem memory driver") Signed-off-by: Arnd Bergmann --- drivers/soc/qcom/rmtfs_mem.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rmtfs_mem.c b/drivers/soc/qcom/rmtfs_mem.c index f6f2f0cb3b3a..ce35ff748adf 100644 --- a/drivers/soc/qcom/rmtfs_mem.c +++ b/drivers/soc/qcom/rmtfs_mem.c @@ -208,8 +208,6 @@ static int qcom_rmtfs_mem_probe(struct platform_device *pdev) return 0; -remove_cdev: - cdev_device_del(&rmtfs_mem->cdev, &rmtfs_mem->dev); put_device: put_device(&rmtfs_mem->dev); -- cgit From 339cd0ea082287ea8e2b7e7159a5a33665a2cbe3 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Fri, 3 Nov 2017 16:43:24 +0100 Subject: soc: amlogic: meson-gx-pwrc-vpu: fix power-off when powered by bootloader In the case the VPU power domain has been powered on by the bootloader and no driver are attached to this power domain, the genpd will power it off after a certain amount of time, but the clocks hasn't been enabled by the kernel itself and the power-off will trigger some faults. This patch enable the clocks to have a coherent state for an eventual poweroff and switches to the pm_domain_always_on_gov governor. Fixes: 75fcb5ca4b46 ("soc: amlogic: add Meson GX VPU Domains driver") Signed-off-by: Neil Armstrong Tested-by: Kevin Hilman Signed-off-by: Arnd Bergmann --- drivers/soc/amlogic/meson-gx-pwrc-vpu.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/amlogic/meson-gx-pwrc-vpu.c b/drivers/soc/amlogic/meson-gx-pwrc-vpu.c index bf5190b65ad9..2bdeebc48901 100644 --- a/drivers/soc/amlogic/meson-gx-pwrc-vpu.c +++ b/drivers/soc/amlogic/meson-gx-pwrc-vpu.c @@ -34,7 +34,6 @@ struct meson_gx_pwrc_vpu { struct reset_control *rstc; struct clk *vpu_clk; struct clk *vapb_clk; - bool powered; }; static inline @@ -78,8 +77,6 @@ static int meson_gx_pwrc_vpu_power_off(struct generic_pm_domain *genpd) clk_disable_unprepare(pd->vpu_clk); clk_disable_unprepare(pd->vapb_clk); - pd->powered = false; - return 0; } @@ -91,7 +88,11 @@ static int meson_gx_pwrc_vpu_setup_clk(struct meson_gx_pwrc_vpu *pd) if (ret) return ret; - return clk_prepare_enable(pd->vapb_clk); + ret = clk_prepare_enable(pd->vapb_clk); + if (ret) + clk_disable_unprepare(pd->vpu_clk); + + return ret; } static int meson_gx_pwrc_vpu_power_on(struct generic_pm_domain *genpd) @@ -139,8 +140,6 @@ static int meson_gx_pwrc_vpu_power_on(struct generic_pm_domain *genpd) if (ret) return ret; - pd->powered = true; - return 0; } @@ -167,6 +166,8 @@ static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev) struct reset_control *rstc; struct clk *vpu_clk; struct clk *vapb_clk; + bool powered_off; + int ret; regmap_ao = syscon_node_to_regmap(of_get_parent(pdev->dev.of_node)); if (IS_ERR(regmap_ao)) { @@ -205,8 +206,17 @@ static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev) vpu_hdmi_pd.vpu_clk = vpu_clk; vpu_hdmi_pd.vapb_clk = vapb_clk; - pm_genpd_init(&vpu_hdmi_pd.genpd, &simple_qos_governor, - meson_gx_pwrc_vpu_get_power(&vpu_hdmi_pd)); + powered_off = meson_gx_pwrc_vpu_get_power(&vpu_hdmi_pd); + + /* If already powered, sync the clock states */ + if (!powered_off) { + ret = meson_gx_pwrc_vpu_setup_clk(&vpu_hdmi_pd); + if (ret) + return ret; + } + + pm_genpd_init(&vpu_hdmi_pd.genpd, &pm_domain_always_on_gov, + powered_off); return of_genpd_add_provider_simple(pdev->dev.of_node, &vpu_hdmi_pd.genpd); @@ -214,8 +224,7 @@ static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev) static void meson_gx_pwrc_vpu_shutdown(struct platform_device *pdev) { - if (vpu_hdmi_pd.powered) - meson_gx_pwrc_vpu_power_off(&vpu_hdmi_pd.genpd); + meson_gx_pwrc_vpu_power_off(&vpu_hdmi_pd.genpd); } static const struct of_device_id meson_gx_pwrc_vpu_match_table[] = { -- cgit