From 2f89bef90de4740be33b2cb4ba95e0107df0d25e Mon Sep 17 00:00:00 2001 From: Biju Das Date: Tue, 15 Mar 2022 14:26:39 +0000 Subject: soc: renesas: Identify RZ/G2UL SoC Add support for identifying the RZ/G2UL SoC. Signed-off-by: Biju Das Reviewed-by: Lad Prabhakar Link: https://lore.kernel.org/r/20220315142644.17660-3-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/soc/renesas/Kconfig | 6 ++++++ drivers/soc/renesas/renesas-soc.c | 13 +++++++++++++ 2 files changed, 19 insertions(+) diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index fdc99a05a7e0..63477f05fbaf 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -296,6 +296,12 @@ config ARCH_R8A774B1 help This enables support for the Renesas RZ/G2N SoC. +config ARCH_R9A07G043 + bool "ARM64 Platform support for RZ/G2UL" + select ARCH_RZG2L + help + This enables support for the Renesas RZ/G2UL SoC variants. + config ARCH_R9A07G044 bool "ARM64 Platform support for RZ/G2L" select ARCH_RZG2L diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c index 92c7b42250ee..30192a099d38 100644 --- a/drivers/soc/renesas/renesas-soc.c +++ b/drivers/soc/renesas/renesas-soc.c @@ -64,6 +64,10 @@ static const struct renesas_family fam_rzg2l __initconst __maybe_unused = { .name = "RZ/G2L", }; +static const struct renesas_family fam_rzg2ul __initconst __maybe_unused = { + .name = "RZ/G2UL", +}; + static const struct renesas_family fam_rzv2l __initconst __maybe_unused = { .name = "RZ/V2L", }; @@ -148,6 +152,11 @@ static const struct renesas_soc soc_rz_g2l __initconst __maybe_unused = { .id = 0x841c447, }; +static const struct renesas_soc soc_rz_g2ul __initconst __maybe_unused = { + .family = &fam_rzg2ul, + .id = 0x8450447, +}; + static const struct renesas_soc soc_rz_v2l __initconst __maybe_unused = { .family = &fam_rzv2l, .id = 0x8447447, @@ -340,6 +349,9 @@ static const struct of_device_id renesas_socs[] __initconst = { #ifdef CONFIG_ARCH_R8A779F0 { .compatible = "renesas,r8a779f0", .data = &soc_rcar_s4 }, #endif +#if defined(CONFIG_ARCH_R9A07G043) + { .compatible = "renesas,r9a07g043", .data = &soc_rz_g2ul }, +#endif #if defined(CONFIG_ARCH_R9A07G044) { .compatible = "renesas,r9a07g044", .data = &soc_rz_g2l }, #endif @@ -378,6 +390,7 @@ static const struct renesas_id id_prr __initconst = { static const struct of_device_id renesas_ids[] __initconst = { { .compatible = "renesas,bsid", .data = &id_bsid }, + { .compatible = "renesas,r9a07g043-sysc", .data = &id_rzg2l }, { .compatible = "renesas,r9a07g044-sysc", .data = &id_rzg2l }, { .compatible = "renesas,r9a07g054-sysc", .data = &id_rzg2l }, { .compatible = "renesas,prr", .data = &id_prr }, -- cgit v1.2.3 From 56653827f0d7bc7c2d8bac0e119fd1521fa9990a Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 20 Mar 2022 08:10:30 +0100 Subject: memory: samsung: exynos5422-dmc: Avoid some over memory allocation 'dmc->counter' is a 'struct devfreq_event_dev **', so there is some over memory allocation. 'counters_size' should be computed with 'sizeof(struct devfreq_event_dev *)'. Use 'sizeof(*dmc->counter)' instead to fix it. While at it, use devm_kcalloc() instead of devm_kzalloc()+open coded multiplication. Fixes: 6e7674c3c6df ("memory: Add DMC driver for Exynos5422") Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/69d7e69346986e2fdb994d4382954c932f9f0993.1647760213.git.christophe.jaillet@wanadoo.fr Signed-off-by: Krzysztof Kozlowski --- drivers/memory/samsung/exynos5422-dmc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/memory/samsung/exynos5422-dmc.c b/drivers/memory/samsung/exynos5422-dmc.c index 9c8318923ed0..4733e7898ffe 100644 --- a/drivers/memory/samsung/exynos5422-dmc.c +++ b/drivers/memory/samsung/exynos5422-dmc.c @@ -1322,7 +1322,6 @@ static int exynos5_dmc_init_clks(struct exynos5_dmc *dmc) */ static int exynos5_performance_counters_init(struct exynos5_dmc *dmc) { - int counters_size; int ret, i; dmc->num_counters = devfreq_event_get_edev_count(dmc->dev, @@ -1332,8 +1331,8 @@ static int exynos5_performance_counters_init(struct exynos5_dmc *dmc) return dmc->num_counters; } - counters_size = sizeof(struct devfreq_event_dev) * dmc->num_counters; - dmc->counter = devm_kzalloc(dmc->dev, counters_size, GFP_KERNEL); + dmc->counter = devm_kcalloc(dmc->dev, dmc->num_counters, + sizeof(*dmc->counter), GFP_KERNEL); if (!dmc->counter) return -ENOMEM; -- cgit v1.2.3 From a4b75fe8e1c15c96c4eb083e211ccbbfd56599f9 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 28 Dec 2021 20:57:37 +0100 Subject: tee: combine "config" and "menu" for TEE's menuconfig Don't let TEE occupy two lines in menuconfig when practically no other (sub)menu does either. Signed-off-by: Jan Engelhardt Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- drivers/tee/Kconfig | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/tee/Kconfig b/drivers/tee/Kconfig index e99d840c2511..73a147202e88 100644 --- a/drivers/tee/Kconfig +++ b/drivers/tee/Kconfig @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only # Generic Trusted Execution Environment Configuration -config TEE +menuconfig TEE tristate "Trusted Execution Environment support" depends on HAVE_ARM_SMCCC || COMPILE_TEST || CPU_SUP_AMD select CRYPTO @@ -13,10 +13,7 @@ config TEE if TEE -menu "TEE drivers" - source "drivers/tee/optee/Kconfig" source "drivers/tee/amdtee/Kconfig" -endmenu endif -- cgit v1.2.3 From 3e47235eaee09488c7e467b9aaccb7c93c862c6b Mon Sep 17 00:00:00 2001 From: Phil Chang Date: Fri, 25 Feb 2022 23:20:40 +0800 Subject: tee: make tee_shm_register_kernel_buf vmalloc supported In some low-memory devices, it's hard to aquire large-orders pages, this patch allowed user using scatter pages to register shm. Signed-off-by: Phil Chang Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- drivers/tee/optee/call.c | 2 +- drivers/tee/tee_shm.c | 35 +++++++++++++++++++++++++---------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index bd49ec934060..2082e632adff 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -362,7 +362,7 @@ int optee_check_mem_type(unsigned long start, size_t num_pages) * Allow kernel address to register with OP-TEE as kernel * pages are configured as normal memory only. */ - if (virt_addr_valid(start)) + if (virt_addr_valid(start) || is_vmalloc_addr((void *)start)) return 0; mmap_read_lock(mm); diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c index f31e29e8f1ca..836872467dc6 100644 --- a/drivers/tee/tee_shm.c +++ b/drivers/tee/tee_shm.c @@ -23,21 +23,36 @@ static void shm_put_kernel_pages(struct page **pages, size_t page_count) static int shm_get_kernel_pages(unsigned long start, size_t page_count, struct page **pages) { - struct kvec *kiov; size_t n; int rc; - kiov = kcalloc(page_count, sizeof(*kiov), GFP_KERNEL); - if (!kiov) - return -ENOMEM; + if (is_vmalloc_addr((void *)start)) { + struct page *page; - for (n = 0; n < page_count; n++) { - kiov[n].iov_base = (void *)(start + n * PAGE_SIZE); - kiov[n].iov_len = PAGE_SIZE; - } + for (n = 0; n < page_count; n++) { + page = vmalloc_to_page((void *)(start + PAGE_SIZE * n)); + if (!page) + return -ENOMEM; + + get_page(page); + pages[n] = page; + } + rc = page_count; + } else { + struct kvec *kiov; + + kiov = kcalloc(page_count, sizeof(*kiov), GFP_KERNEL); + if (!kiov) + return -ENOMEM; - rc = get_kernel_pages(kiov, page_count, 0, pages); - kfree(kiov); + for (n = 0; n < page_count; n++) { + kiov[n].iov_base = (void *)(start + n * PAGE_SIZE); + kiov[n].iov_len = PAGE_SIZE; + } + + rc = get_kernel_pages(kiov, page_count, 0, pages); + kfree(kiov); + } return rc; } -- cgit v1.2.3 From 942501661fa65f13bf9b5582e8248e92770b2d93 Mon Sep 17 00:00:00 2001 From: Sagar Kamble Date: Thu, 24 Mar 2022 17:38:48 +0530 Subject: soc/tegra: fuse: Add nvmem cell lookup entries for Tegra194 Add nvmem cell lookup entries for below FUSE cells: - gcplex-config-fuse - pdi0 - pdi1 Also include the device name prefix "gpu-" in the names of the GPU FUSE cells in nvmem_cell_info. Signed-off-by: Sagar Kamble Reviewed-by: Jon Hunter Signed-off-by: Thierry Reding --- drivers/soc/tegra/fuse/fuse-tegra.c | 8 ++++---- drivers/soc/tegra/fuse/fuse-tegra30.c | 17 ++++++++++++++++- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c index aa94fda282f4..b0a8405dbdb1 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra.c +++ b/drivers/soc/tegra/fuse/fuse-tegra.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2013-2021, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2013-2022, NVIDIA CORPORATION. All rights reserved. */ #include @@ -162,7 +162,7 @@ static const struct nvmem_cell_info tegra_fuse_cells[] = { .bit_offset = 0, .nbits = 32, }, { - .name = "gcplex-config-fuse", + .name = "gpu-gcplex-config-fuse", .offset = 0x1c8, .bytes = 4, .bit_offset = 0, @@ -186,13 +186,13 @@ static const struct nvmem_cell_info tegra_fuse_cells[] = { .bit_offset = 0, .nbits = 32, }, { - .name = "pdi0", + .name = "gpu-pdi0", .offset = 0x300, .bytes = 4, .bit_offset = 0, .nbits = 32, }, { - .name = "pdi1", + .name = "gpu-pdi1", .offset = 0x304, .bytes = 4, .bit_offset = 0, diff --git a/drivers/soc/tegra/fuse/fuse-tegra30.c b/drivers/soc/tegra/fuse/fuse-tegra30.c index b071d433d74f..f01d8a2547b6 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra30.c +++ b/drivers/soc/tegra/fuse/fuse-tegra30.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2013-2014, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2013-2022, NVIDIA CORPORATION. All rights reserved. */ #include @@ -344,6 +344,21 @@ static const struct nvmem_cell_lookup tegra194_fuse_lookups[] = { .cell_name = "xusb-pad-calibration-ext", .dev_id = "3520000.padctl", .con_id = "calibration-ext", + }, { + .nvmem_name = "fuse", + .cell_name = "gpu-gcplex-config-fuse", + .dev_id = "17000000.gpu", + .con_id = "gcplex-config-fuse", + }, { + .nvmem_name = "fuse", + .cell_name = "gpu-pdi0", + .dev_id = "17000000.gpu", + .con_id = "pdi0", + }, { + .nvmem_name = "fuse", + .cell_name = "gpu-pdi1", + .dev_id = "17000000.gpu", + .con_id = "pdi1", }, }; -- cgit v1.2.3 From d3ed7526543db9d26341b9df97064b332262b626 Mon Sep 17 00:00:00 2001 From: Sandipan Patra Date: Fri, 1 Apr 2022 20:03:43 +0530 Subject: soc/tegra: pmc: Update Tegra234 reset sources Update the tegra234_reset_sources array to contain all reset sources for Tegra234 and NULL out the entries that do not actually exist. Signed-off-by: Sandipan Patra Reviewed-by: Andy Shevchenko Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index fdf508e03400..c01db5309b5e 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -3766,7 +3766,7 @@ static const struct tegra_pmc_regs tegra234_pmc_regs = { }; static const char * const tegra234_reset_sources[] = { - "SYS_RESET_N", + "SYS_RESET_N", /* 0x0 */ "AOWDT", "BCCPLEXWDT", "BPMPWDT", @@ -3774,19 +3774,36 @@ static const char * const tegra234_reset_sources[] = { "SPEWDT", "APEWDT", "LCCPLEXWDT", - "SENSOR", - "AOTAG", - "VFSENSOR", + "SENSOR", /* 0x8 */ + NULL, + NULL, "MAINSWRST", "SC7", "HSM", - "CSITE", + NULL, "RCEWDT", - "PVA0WDT", - "PVA1WDT", - "L1A_ASYNC", + NULL, /* 0x10 */ + NULL, + NULL, "BPMPBOOT", "FUSECRC", + "DCEWDT", + "PSCWDT", + "PSC", + "CSITE_SW", /* 0x18 */ + "POD", + "SCPM", + "VREFRO_POWERBAD", + "VMON", + "FMON", + "FSI_R5WDT", + "FSI_THERM", + "FSI_R52C0WDT", /* 0x20 */ + "FSI_R52C1WDT", + "FSI_R52C2WDT", + "FSI_R52C3WDT", + "FSI_FMON", + "FSI_VMON", /* 0x25 */ }; static const struct tegra_wake_event tegra234_wake_events[] = { -- cgit v1.2.3 From db85474594609fc6f6a37fe5d5001fa9341971ed Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 6 Apr 2022 17:40:50 +0200 Subject: dt-bindings: soc: samsung: usi: refer to dtschema for SPI After adding DT schema for Samsung SPI controller, the Samsung USI bindings can reference it directly for proper schema validation. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Sam Protsenko Acked-by: Rob Herring Link: https://lore.kernel.org/r/20220324134106.103588-1-krzk@kernel.org --- Documentation/devicetree/bindings/soc/samsung/exynos-usi.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/soc/samsung/exynos-usi.yaml b/Documentation/devicetree/bindings/soc/samsung/exynos-usi.yaml index a98ed66d092e..55417a649970 100644 --- a/Documentation/devicetree/bindings/soc/samsung/exynos-usi.yaml +++ b/Documentation/devicetree/bindings/soc/samsung/exynos-usi.yaml @@ -77,7 +77,7 @@ patternProperties: description: Child node describing underlying UART/serial "^spi@[0-9a-f]+$": - type: object + $ref: /schemas/spi/samsung,spi.yaml description: Child node describing underlying SPI required: -- cgit v1.2.3 From 6ded3d7471d8bb01eb87ac2d506db7e8a839986a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 6 Apr 2022 18:00:07 +0200 Subject: memory: emif: remove unneeded ENOMEM error messages Memory subsystem already prints message about failed memory allocation, there is no need to do it in the drivers. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220304082339.230938-1-krzysztof.kozlowski@canonical.com --- drivers/memory/emif.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/memory/emif.c b/drivers/memory/emif.c index ecc78d6f89ed..edf3ba7447ed 100644 --- a/drivers/memory/emif.c +++ b/drivers/memory/emif.c @@ -1025,10 +1025,8 @@ static struct emif_data *__init_or_module get_device_details( temp = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); dev_info = devm_kzalloc(dev, sizeof(*dev_info), GFP_KERNEL); - if (!emif || !temp || !dev_info) { - dev_err(dev, "%s:%d: allocation error\n", __func__, __LINE__); + if (!emif || !temp || !dev_info) goto error; - } memcpy(temp, pd, sizeof(*pd)); pd = temp; @@ -1067,9 +1065,6 @@ static struct emif_data *__init_or_module get_device_details( temp = devm_kzalloc(dev, sizeof(*cust_cfgs), GFP_KERNEL); if (temp) memcpy(temp, cust_cfgs, sizeof(*cust_cfgs)); - else - dev_warn(dev, "%s:%d: allocation error\n", __func__, - __LINE__); pd->custom_configs = temp; } @@ -1084,8 +1079,6 @@ static struct emif_data *__init_or_module get_device_details( memcpy(temp, pd->timings, size); pd->timings = temp; } else { - dev_warn(dev, "%s:%d: allocation error\n", __func__, - __LINE__); get_default_timings(emif); } } else { @@ -1098,8 +1091,6 @@ static struct emif_data *__init_or_module get_device_details( memcpy(temp, pd->min_tck, sizeof(*pd->min_tck)); pd->min_tck = temp; } else { - dev_warn(dev, "%s:%d: allocation error\n", __func__, - __LINE__); pd->min_tck = &lpddr2_jedec_min_tck; } } else { -- cgit v1.2.3 From 4a77579c73413deb4aa562b6e781e3c65ff7abb2 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 21 Mar 2022 16:15:55 -0700 Subject: soc: qcom: socinfo: add SC7280 entry to soc_id array Add an entry for SC7280 SoC. Signed-off-by: Douglas Anderson Reviewed-by: Sai Prakash Ranjan Reviewed-by: Stephen Boyd Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220321161546.1.Ifc4270fbe9bad536f08a47696e00cca5a0714abd@changeid --- drivers/soc/qcom/socinfo.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 8b38d134720a..dbdbad5db3e5 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -330,6 +330,7 @@ static const struct soc_id soc_id[] = { { 459, "SM7225" }, { 460, "SA8540P" }, { 480, "SM8450" }, + { 487, "SC7280" }, }; static const char *socinfo_machine(struct device *dev, unsigned int id) -- cgit v1.2.3 From 54c2e9256a5688a98225207734688b4b7c0f40c9 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Mon, 28 Feb 2022 22:00:13 +0300 Subject: soc: qcom: socinfo: Add another ID for SM8450 Add another id for SM8450. Signed-off-by: Dmitry Baryshkov Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220228190013.1790617-1-dmitry.baryshkov@linaro.org --- drivers/soc/qcom/socinfo.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index dbdbad5db3e5..146ff934fdc8 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -330,6 +330,7 @@ static const struct soc_id soc_id[] = { { 459, "SM7225" }, { 460, "SA8540P" }, { 480, "SM8450" }, + { 482, "SM8450" }, { 487, "SC7280" }, }; -- cgit v1.2.3 From 8fd3f18ea31a398ecce4a6d3804433658678b0a3 Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Tue, 8 Mar 2022 07:19:42 +0000 Subject: soc: qcom: smp2p: Fix missing of_node_put() in smp2p_parse_ipc The device_node pointer is returned by of_parse_phandle() with refcount incremented. We should use of_node_put() on it when done. Fixes: 50e99641413e ("soc: qcom: smp2p: Qualcomm Shared Memory Point to Point") Signed-off-by: Miaoqian Lin Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220308071942.22942-1-linmq006@gmail.com --- drivers/soc/qcom/smp2p.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/qcom/smp2p.c b/drivers/soc/qcom/smp2p.c index 4a157240f419..59dbf4b61e6c 100644 --- a/drivers/soc/qcom/smp2p.c +++ b/drivers/soc/qcom/smp2p.c @@ -493,6 +493,7 @@ static int smp2p_parse_ipc(struct qcom_smp2p *smp2p) } smp2p->ipc_regmap = syscon_node_to_regmap(syscon); + of_node_put(syscon); if (IS_ERR(smp2p->ipc_regmap)) return PTR_ERR(smp2p->ipc_regmap); -- cgit v1.2.3 From aad66a3c78da668f4506356c2fdb70b7a19ecc76 Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Tue, 8 Mar 2022 07:36:48 +0000 Subject: soc: qcom: smsm: Fix missing of_node_put() in smsm_parse_ipc The device_node pointer is returned by of_parse_phandle() with refcount incremented. We should use of_node_put() on it when done. Fixes: c97c4090ff72 ("soc: qcom: smsm: Add driver for Qualcomm SMSM") Signed-off-by: Miaoqian Lin Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220308073648.24634-1-linmq006@gmail.com --- drivers/soc/qcom/smsm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/qcom/smsm.c b/drivers/soc/qcom/smsm.c index ef15d014c03a..9df9bba242f3 100644 --- a/drivers/soc/qcom/smsm.c +++ b/drivers/soc/qcom/smsm.c @@ -374,6 +374,7 @@ static int smsm_parse_ipc(struct qcom_smsm *smsm, unsigned host_id) return 0; host->ipc_regmap = syscon_node_to_regmap(syscon); + of_node_put(syscon); if (IS_ERR(host->ipc_regmap)) return PTR_ERR(host->ipc_regmap); -- cgit v1.2.3 From ccc706353d1b89d8ff1726e575dd4a10f8550fb7 Mon Sep 17 00:00:00 2001 From: Rohit Agarwal Date: Wed, 16 Mar 2022 11:37:14 +0530 Subject: dt-bindings: power: Add rpm power domain bindings for SDX65 Add RPM power domain bindings for the SDX65 SoC. Signed-off-by: Rohit Agarwal Acked-by: Krzysztof Kozlowski Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/1647410837-22537-4-git-send-email-quic_rohiagar@quicinc.com --- Documentation/devicetree/bindings/power/qcom,rpmpd.yaml | 1 + include/dt-bindings/power/qcom-rpmpd.h | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml index 940e97b565bf..b90c4427beec 100644 --- a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml +++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml @@ -33,6 +33,7 @@ properties: - qcom,sc8180x-rpmhpd - qcom,sdm845-rpmhpd - qcom,sdx55-rpmhpd + - qcom,sdx65-rpmhpd - qcom,sm6115-rpmpd - qcom,sm6125-rpmpd - qcom,sm6350-rpmhpd diff --git a/include/dt-bindings/power/qcom-rpmpd.h b/include/dt-bindings/power/qcom-rpmpd.h index c002cc6ddf55..0a8978d0c78c 100644 --- a/include/dt-bindings/power/qcom-rpmpd.h +++ b/include/dt-bindings/power/qcom-rpmpd.h @@ -20,6 +20,14 @@ #define SDX55_MX 1 #define SDX55_CX 2 +/* SDX65 Power Domain Indexes */ +#define SDX65_MSS 0 +#define SDX65_MX 1 +#define SDX65_MX_AO 2 +#define SDX65_CX 3 +#define SDX65_CX_AO 4 +#define SDX65_MXC 5 + /* SM6350 Power Domain Indexes */ #define SM6350_CX 0 #define SM6350_GFX 1 -- cgit v1.2.3 From 3edff6262c48fe73feedc7277a3a6ebc3ed35a37 Mon Sep 17 00:00:00 2001 From: Rohit Agarwal Date: Wed, 16 Mar 2022 11:37:15 +0530 Subject: soc: qcom: rpmhpd: Add SDX65 power domains Add power domains found in Qualcomm SDX65 SoC. Signed-off-by: Rohit Agarwal Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/1647410837-22537-5-git-send-email-quic_rohiagar@quicinc.com --- drivers/soc/qcom/rpmhpd.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c index 58f1dc9b9cb7..11102ac47769 100644 --- a/drivers/soc/qcom/rpmhpd.c +++ b/drivers/soc/qcom/rpmhpd.c @@ -210,6 +210,21 @@ static const struct rpmhpd_desc sdx55_desc = { .num_pds = ARRAY_SIZE(sdx55_rpmhpds), }; +/* SDX65 RPMH powerdomains */ +static struct rpmhpd *sdx65_rpmhpds[] = { + [SDX65_CX] = &cx_w_mx_parent, + [SDX65_CX_AO] = &cx_ao_w_mx_parent, + [SDX65_MSS] = &mss, + [SDX65_MX] = &mx, + [SDX65_MX_AO] = &mx_ao, + [SDX65_MXC] = &mxc, +}; + +static const struct rpmhpd_desc sdx65_desc = { + .rpmhpds = sdx65_rpmhpds, + .num_pds = ARRAY_SIZE(sdx65_rpmhpds), +}; + /* SM6350 RPMH powerdomains */ static struct rpmhpd *sm6350_rpmhpds[] = { [SM6350_CX] = &cx_w_mx_parent, @@ -369,6 +384,7 @@ static const struct of_device_id rpmhpd_match_table[] = { { .compatible = "qcom,sc8180x-rpmhpd", .data = &sc8180x_desc }, { .compatible = "qcom,sdm845-rpmhpd", .data = &sdm845_desc }, { .compatible = "qcom,sdx55-rpmhpd", .data = &sdx55_desc}, + { .compatible = "qcom,sdx65-rpmhpd", .data = &sdx65_desc}, { .compatible = "qcom,sm6350-rpmhpd", .data = &sm6350_desc }, { .compatible = "qcom,sm8150-rpmhpd", .data = &sm8150_desc }, { .compatible = "qcom,sm8250-rpmhpd", .data = &sm8250_desc }, -- cgit v1.2.3 From 3be066542ec400e7cdbcef2a6ac1d36c5366a6f6 Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Thu, 24 Mar 2022 08:24:35 +0100 Subject: soc: qcom: pdr: replace usage of found with dedicated list iterator variable To move the list iterator variable into the list_for_each_entry_*() macro in the future it should be avoided to use the list iterator variable after the loop body. To *never* use the list iterator variable after the loop it was concluded to use a separate iterator variable instead of a found boolean [1]. This removes the need to use a found variable and simply checking if the variable was set, can determine if the break/goto was hit. Link: https://lore.kernel.org/all/CAHk-=wgRr_D8CB-D9Kg-c=EHreAsk5SqXPwr9Y7k9sA6cWXJ6w@mail.gmail.com/ Signed-off-by: Jakob Koschel Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220324072435.63070-1-jakobkoschel@gmail.com --- drivers/soc/qcom/pdr_interface.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/soc/qcom/pdr_interface.c b/drivers/soc/qcom/pdr_interface.c index fc580a3c4336..0034af927b48 100644 --- a/drivers/soc/qcom/pdr_interface.c +++ b/drivers/soc/qcom/pdr_interface.c @@ -304,24 +304,23 @@ static void pdr_indication_cb(struct qmi_handle *qmi, notifier_hdl); const struct servreg_state_updated_ind *ind_msg = data; struct pdr_list_node *ind; - struct pdr_service *pds; - bool found = false; + struct pdr_service *pds = NULL, *iter; if (!ind_msg || !ind_msg->service_path[0] || strlen(ind_msg->service_path) > SERVREG_NAME_LENGTH) return; mutex_lock(&pdr->list_lock); - list_for_each_entry(pds, &pdr->lookups, node) { - if (strcmp(pds->service_path, ind_msg->service_path)) + list_for_each_entry(iter, &pdr->lookups, node) { + if (strcmp(iter->service_path, ind_msg->service_path)) continue; - found = true; + pds = iter; break; } mutex_unlock(&pdr->list_lock); - if (!found) + if (!pds) return; pr_info("PDR: Indication received from %s, state: 0x%x, trans-id: %d\n", -- cgit v1.2.3 From d9651b99dd65e861b1c525d0e8ab8da0d37642ca Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Wed, 30 Mar 2022 12:46:16 +0200 Subject: soc: imx: gpcv2: add PGC control register indirection The PGC control registers in the shared (not per-PGC) region of the GPC address space have different offsets on i.MX8MP to make space for additional interrupt control registers. Signed-off-by: Lucas Stach Reviewed-by: Laurent Pinchart Tested-by: Peng Fan Signed-off-by: Shawn Guo --- drivers/soc/imx/gpcv2.c | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c index 3cb123016b3e..677c5c581521 100644 --- a/drivers/soc/imx/gpcv2.c +++ b/drivers/soc/imx/gpcv2.c @@ -184,9 +184,17 @@ #define GPC_PGC_CTRL_PCR BIT(0) +struct imx_pgc_regs { + u16 map; + u16 pup; + u16 pdn; + u16 hsk; +}; + struct imx_pgc_domain { struct generic_pm_domain genpd; struct regmap *regmap; + const struct imx_pgc_regs *regs; struct regulator *regulator; struct reset_control *reset; struct clk_bulk_data *clks; @@ -210,6 +218,7 @@ struct imx_pgc_domain_data { const struct imx_pgc_domain *domains; size_t domains_num; const struct regmap_access_table *reg_access_table; + const struct imx_pgc_regs *pgc_regs; }; static inline struct imx_pgc_domain * @@ -249,14 +258,14 @@ static int imx_pgc_power_up(struct generic_pm_domain *genpd) if (domain->bits.pxx) { /* request the domain to power up */ - regmap_update_bits(domain->regmap, GPC_PU_PGC_SW_PUP_REQ, + regmap_update_bits(domain->regmap, domain->regs->pup, domain->bits.pxx, domain->bits.pxx); /* * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait * for PUP_REQ/PDN_REQ bit to be cleared */ ret = regmap_read_poll_timeout(domain->regmap, - GPC_PU_PGC_SW_PUP_REQ, reg_val, + domain->regs->pup, reg_val, !(reg_val & domain->bits.pxx), 0, USEC_PER_MSEC); if (ret) { @@ -278,11 +287,11 @@ static int imx_pgc_power_up(struct generic_pm_domain *genpd) /* request the ADB400 to power up */ if (domain->bits.hskreq) { - regmap_update_bits(domain->regmap, GPC_PU_PWRHSK, + regmap_update_bits(domain->regmap, domain->regs->hsk, domain->bits.hskreq, domain->bits.hskreq); /* - * ret = regmap_read_poll_timeout(domain->regmap, GPC_PU_PWRHSK, reg_val, + * ret = regmap_read_poll_timeout(domain->regmap, domain->regs->hsk, reg_val, * (reg_val & domain->bits.hskack), 0, * USEC_PER_MSEC); * Technically we need the commented code to wait handshake. But that needs @@ -329,10 +338,10 @@ static int imx_pgc_power_down(struct generic_pm_domain *genpd) /* request the ADB400 to power down */ if (domain->bits.hskreq) { - regmap_clear_bits(domain->regmap, GPC_PU_PWRHSK, + regmap_clear_bits(domain->regmap, domain->regs->hsk, domain->bits.hskreq); - ret = regmap_read_poll_timeout(domain->regmap, GPC_PU_PWRHSK, + ret = regmap_read_poll_timeout(domain->regmap, domain->regs->hsk, reg_val, !(reg_val & domain->bits.hskack), 0, USEC_PER_MSEC); @@ -350,14 +359,14 @@ static int imx_pgc_power_down(struct generic_pm_domain *genpd) } /* request the domain to power down */ - regmap_update_bits(domain->regmap, GPC_PU_PGC_SW_PDN_REQ, + regmap_update_bits(domain->regmap, domain->regs->pdn, domain->bits.pxx, domain->bits.pxx); /* * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait * for PUP_REQ/PDN_REQ bit to be cleared */ ret = regmap_read_poll_timeout(domain->regmap, - GPC_PU_PGC_SW_PDN_REQ, reg_val, + domain->regs->pdn, reg_val, !(reg_val & domain->bits.pxx), 0, USEC_PER_MSEC); if (ret) { @@ -442,10 +451,18 @@ static const struct regmap_access_table imx7_access_table = { .n_yes_ranges = ARRAY_SIZE(imx7_yes_ranges), }; +static const struct imx_pgc_regs imx7_pgc_regs = { + .map = GPC_PGC_CPU_MAPPING, + .pup = GPC_PU_PGC_SW_PUP_REQ, + .pdn = GPC_PU_PGC_SW_PDN_REQ, + .hsk = GPC_PU_PWRHSK, +}; + static const struct imx_pgc_domain_data imx7_pgc_domain_data = { .domains = imx7_pgc_domains, .domains_num = ARRAY_SIZE(imx7_pgc_domains), .reg_access_table = &imx7_access_table, + .pgc_regs = &imx7_pgc_regs, }; static const struct imx_pgc_domain imx8m_pgc_domains[] = { @@ -614,6 +631,7 @@ static const struct imx_pgc_domain_data imx8m_pgc_domain_data = { .domains = imx8m_pgc_domains, .domains_num = ARRAY_SIZE(imx8m_pgc_domains), .reg_access_table = &imx8m_access_table, + .pgc_regs = &imx7_pgc_regs, }; static const struct imx_pgc_domain imx8mm_pgc_domains[] = { @@ -804,6 +822,7 @@ static const struct imx_pgc_domain_data imx8mm_pgc_domain_data = { .domains = imx8mm_pgc_domains, .domains_num = ARRAY_SIZE(imx8mm_pgc_domains), .reg_access_table = &imx8mm_access_table, + .pgc_regs = &imx7_pgc_regs, }; static const struct imx_pgc_domain imx8mn_pgc_domains[] = { @@ -895,6 +914,7 @@ static const struct imx_pgc_domain_data imx8mn_pgc_domain_data = { .domains = imx8mn_pgc_domains, .domains_num = ARRAY_SIZE(imx8mn_pgc_domains), .reg_access_table = &imx8mn_access_table, + .pgc_regs = &imx7_pgc_regs, }; static int imx_pgc_domain_probe(struct platform_device *pdev) @@ -927,7 +947,7 @@ static int imx_pgc_domain_probe(struct platform_device *pdev) pm_runtime_enable(domain->dev); if (domain->bits.map) - regmap_update_bits(domain->regmap, GPC_PGC_CPU_MAPPING, + regmap_update_bits(domain->regmap, domain->regs->map, domain->bits.map, domain->bits.map); ret = pm_genpd_init(&domain->genpd, NULL, true); @@ -953,7 +973,7 @@ out_genpd_remove: pm_genpd_remove(&domain->genpd); out_domain_unmap: if (domain->bits.map) - regmap_update_bits(domain->regmap, GPC_PGC_CPU_MAPPING, + regmap_update_bits(domain->regmap, domain->regs->map, domain->bits.map, 0); pm_runtime_disable(domain->dev); @@ -968,7 +988,7 @@ static int imx_pgc_domain_remove(struct platform_device *pdev) pm_genpd_remove(&domain->genpd); if (domain->bits.map) - regmap_update_bits(domain->regmap, GPC_PGC_CPU_MAPPING, + regmap_update_bits(domain->regmap, domain->regs->map, domain->bits.map, 0); pm_runtime_disable(domain->dev); @@ -1099,6 +1119,7 @@ static int imx_gpcv2_probe(struct platform_device *pdev) domain = pd_pdev->dev.platform_data; domain->regmap = regmap; + domain->regs = domain_data->pgc_regs; domain->genpd.power_on = imx_pgc_power_up; domain->genpd.power_off = imx_pgc_power_down; -- cgit v1.2.3 From a8a2c274dda4ae0d63ffbe9812e67dc2cf5dcdd9 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Wed, 30 Mar 2022 12:46:18 +0200 Subject: soc: imx: gpcv2: add support for i.MX8MP power domains This adds driver support for all the GPC power domains found on the i.MX8MP SoC. Signed-off-by: Lucas Stach Reviewed-by: Laurent Pinchart Tested-by: Laurent Pinchart Tested-by: Peng Fan Signed-off-by: Shawn Guo --- drivers/soc/imx/gpcv2.c | 387 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 386 insertions(+), 1 deletion(-) diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c index 677c5c581521..85aa86e1338a 100644 --- a/drivers/soc/imx/gpcv2.c +++ b/drivers/soc/imx/gpcv2.c @@ -21,10 +21,12 @@ #include #include #include +#include #define GPC_LPCR_A_CORE_BSC 0x000 #define GPC_PGC_CPU_MAPPING 0x0ec +#define IMX8MP_GPC_PGC_CPU_MAPPING 0x1cc #define IMX7_USB_HSIC_PHY_A_CORE_DOMAIN BIT(6) #define IMX7_USB_OTG2_PHY_A_CORE_DOMAIN BIT(5) @@ -65,6 +67,29 @@ #define IMX8MN_OTG1_A53_DOMAIN BIT(4) #define IMX8MN_MIPI_A53_DOMAIN BIT(2) +#define IMX8MP_MEDIA_ISPDWP_A53_DOMAIN BIT(20) +#define IMX8MP_HSIOMIX_A53_DOMAIN BIT(19) +#define IMX8MP_MIPI_PHY2_A53_DOMAIN BIT(18) +#define IMX8MP_HDMI_PHY_A53_DOMAIN BIT(17) +#define IMX8MP_HDMIMIX_A53_DOMAIN BIT(16) +#define IMX8MP_VPU_VC8000E_A53_DOMAIN BIT(15) +#define IMX8MP_VPU_G2_A53_DOMAIN BIT(14) +#define IMX8MP_VPU_G1_A53_DOMAIN BIT(13) +#define IMX8MP_MEDIAMIX_A53_DOMAIN BIT(12) +#define IMX8MP_GPU3D_A53_DOMAIN BIT(11) +#define IMX8MP_VPUMIX_A53_DOMAIN BIT(10) +#define IMX8MP_GPUMIX_A53_DOMAIN BIT(9) +#define IMX8MP_GPU2D_A53_DOMAIN BIT(8) +#define IMX8MP_AUDIOMIX_A53_DOMAIN BIT(7) +#define IMX8MP_MLMIX_A53_DOMAIN BIT(6) +#define IMX8MP_USB2_PHY_A53_DOMAIN BIT(5) +#define IMX8MP_USB1_PHY_A53_DOMAIN BIT(4) +#define IMX8MP_PCIE_PHY_A53_DOMAIN BIT(3) +#define IMX8MP_MIPI_PHY1_A53_DOMAIN BIT(2) + +#define IMX8MP_GPC_PU_PGC_SW_PUP_REQ 0x0d8 +#define IMX8MP_GPC_PU_PGC_SW_PDN_REQ 0x0e4 + #define GPC_PU_PGC_SW_PUP_REQ 0x0f8 #define GPC_PU_PGC_SW_PDN_REQ 0x104 @@ -107,8 +132,30 @@ #define IMX8MN_OTG1_SW_Pxx_REQ BIT(2) #define IMX8MN_MIPI_SW_Pxx_REQ BIT(0) +#define IMX8MP_DDRMIX_Pxx_REQ BIT(19) +#define IMX8MP_MEDIA_ISP_DWP_Pxx_REQ BIT(18) +#define IMX8MP_HSIOMIX_Pxx_REQ BIT(17) +#define IMX8MP_MIPI_PHY2_Pxx_REQ BIT(16) +#define IMX8MP_HDMI_PHY_Pxx_REQ BIT(15) +#define IMX8MP_HDMIMIX_Pxx_REQ BIT(14) +#define IMX8MP_VPU_VC8K_Pxx_REQ BIT(13) +#define IMX8MP_VPU_G2_Pxx_REQ BIT(12) +#define IMX8MP_VPU_G1_Pxx_REQ BIT(11) +#define IMX8MP_MEDIMIX_Pxx_REQ BIT(10) +#define IMX8MP_GPU_3D_Pxx_REQ BIT(9) +#define IMX8MP_VPU_MIX_SHARE_LOGIC_Pxx_REQ BIT(8) +#define IMX8MP_GPU_SHARE_LOGIC_Pxx_REQ BIT(7) +#define IMX8MP_GPU_2D_Pxx_REQ BIT(6) +#define IMX8MP_AUDIOMIX_Pxx_REQ BIT(5) +#define IMX8MP_MLMIX_Pxx_REQ BIT(4) +#define IMX8MP_USB2_PHY_Pxx_REQ BIT(3) +#define IMX8MP_USB1_PHY_Pxx_REQ BIT(2) +#define IMX8MP_PCIE_PHY_SW_Pxx_REQ BIT(1) +#define IMX8MP_MIPI_PHY1_SW_Pxx_REQ BIT(0) + #define GPC_M4_PU_PDN_FLG 0x1bc +#define IMX8MP_GPC_PU_PWRHSK 0x190 #define GPC_PU_PWRHSK 0x1fc #define IMX8M_GPU_HSK_PWRDNACKN BIT(26) @@ -118,7 +165,6 @@ #define IMX8M_VPU_HSK_PWRDNREQN BIT(5) #define IMX8M_DISP_HSK_PWRDNREQN BIT(4) - #define IMX8MM_GPUMIX_HSK_PWRDNACKN BIT(29) #define IMX8MM_GPU_HSK_PWRDNACKN (BIT(27) | BIT(28)) #define IMX8MM_VPUMIX_HSK_PWRDNACKN BIT(26) @@ -137,6 +183,21 @@ #define IMX8MN_DISPMIX_HSK_PWRDNREQN BIT(7) #define IMX8MN_HSIO_HSK_PWRDNREQN BIT(5) +#define IMX8MP_MEDIAMIX_PWRDNACKN BIT(30) +#define IMX8MP_HDMIMIX_PWRDNACKN BIT(29) +#define IMX8MP_HSIOMIX_PWRDNACKN BIT(28) +#define IMX8MP_VPUMIX_PWRDNACKN BIT(26) +#define IMX8MP_GPUMIX_PWRDNACKN BIT(25) +#define IMX8MP_MLMIX_PWRDNACKN (BIT(23) | BIT(24)) +#define IMX8MP_AUDIOMIX_PWRDNACKN (BIT(20) | BIT(31)) +#define IMX8MP_MEDIAMIX_PWRDNREQN BIT(14) +#define IMX8MP_HDMIMIX_PWRDNREQN BIT(13) +#define IMX8MP_HSIOMIX_PWRDNREQN BIT(12) +#define IMX8MP_VPUMIX_PWRDNREQN BIT(10) +#define IMX8MP_GPUMIX_PWRDNREQN BIT(9) +#define IMX8MP_MLMIX_PWRDNREQN (BIT(7) | BIT(8)) +#define IMX8MP_AUDIOMIX_PWRDNREQN (BIT(4) | BIT(15)) + /* * The PGC offset values in Reference Manual * (Rev. 1, 01/2018 and the older ones) GPC chapter's @@ -179,6 +240,28 @@ #define IMX8MN_PGC_GPUMIX 23 #define IMX8MN_PGC_DISPMIX 26 +#define IMX8MP_PGC_NOC 9 +#define IMX8MP_PGC_MIPI1 12 +#define IMX8MP_PGC_PCIE 13 +#define IMX8MP_PGC_USB1 14 +#define IMX8MP_PGC_USB2 15 +#define IMX8MP_PGC_MLMIX 16 +#define IMX8MP_PGC_AUDIOMIX 17 +#define IMX8MP_PGC_GPU2D 18 +#define IMX8MP_PGC_GPUMIX 19 +#define IMX8MP_PGC_VPUMIX 20 +#define IMX8MP_PGC_GPU3D 21 +#define IMX8MP_PGC_MEDIAMIX 22 +#define IMX8MP_PGC_VPU_G1 23 +#define IMX8MP_PGC_VPU_G2 24 +#define IMX8MP_PGC_VPU_VC8000E 25 +#define IMX8MP_PGC_HDMIMIX 26 +#define IMX8MP_PGC_HDMI 27 +#define IMX8MP_PGC_MIPI2 28 +#define IMX8MP_PGC_HSIOMIX 29 +#define IMX8MP_PGC_MEDIA_ISP_DWP 30 +#define IMX8MP_PGC_DDRMIX 31 + #define GPC_PGC_CTRL(n) (0x800 + (n) * 0x40) #define GPC_PGC_SR(n) (GPC_PGC_CTRL(n) + 0xc) @@ -212,6 +295,9 @@ struct imx_pgc_domain { const int voltage; const bool keep_clocks; struct device *dev; + + unsigned int pgc_sw_pup_reg; + unsigned int pgc_sw_pdn_reg; }; struct imx_pgc_domain_data { @@ -825,6 +911,303 @@ static const struct imx_pgc_domain_data imx8mm_pgc_domain_data = { .pgc_regs = &imx7_pgc_regs, }; +static const struct imx_pgc_domain imx8mp_pgc_domains[] = { + [IMX8MP_POWER_DOMAIN_MIPI_PHY1] = { + .genpd = { + .name = "mipi-phy1", + }, + .bits = { + .pxx = IMX8MP_MIPI_PHY1_SW_Pxx_REQ, + .map = IMX8MP_MIPI_PHY1_A53_DOMAIN, + }, + .pgc = BIT(IMX8MP_PGC_MIPI1), + }, + + [IMX8MP_POWER_DOMAIN_PCIE_PHY] = { + .genpd = { + .name = "pcie-phy1", + }, + .bits = { + .pxx = IMX8MP_PCIE_PHY_SW_Pxx_REQ, + .map = IMX8MP_PCIE_PHY_A53_DOMAIN, + }, + .pgc = BIT(IMX8MP_PGC_PCIE), + }, + + [IMX8MP_POWER_DOMAIN_USB1_PHY] = { + .genpd = { + .name = "usb-otg1", + }, + .bits = { + .pxx = IMX8MP_USB1_PHY_Pxx_REQ, + .map = IMX8MP_USB1_PHY_A53_DOMAIN, + }, + .pgc = BIT(IMX8MP_PGC_USB1), + }, + + [IMX8MP_POWER_DOMAIN_USB2_PHY] = { + .genpd = { + .name = "usb-otg2", + }, + .bits = { + .pxx = IMX8MP_USB2_PHY_Pxx_REQ, + .map = IMX8MP_USB2_PHY_A53_DOMAIN, + }, + .pgc = BIT(IMX8MP_PGC_USB2), + }, + + [IMX8MP_POWER_DOMAIN_MLMIX] = { + .genpd = { + .name = "mlmix", + }, + .bits = { + .pxx = IMX8MP_MLMIX_Pxx_REQ, + .map = IMX8MP_MLMIX_A53_DOMAIN, + .hskreq = IMX8MP_MLMIX_PWRDNREQN, + .hskack = IMX8MP_MLMIX_PWRDNACKN, + }, + .pgc = BIT(IMX8MP_PGC_MLMIX), + .keep_clocks = true, + }, + + [IMX8MP_POWER_DOMAIN_AUDIOMIX] = { + .genpd = { + .name = "audiomix", + }, + .bits = { + .pxx = IMX8MP_AUDIOMIX_Pxx_REQ, + .map = IMX8MP_AUDIOMIX_A53_DOMAIN, + .hskreq = IMX8MP_AUDIOMIX_PWRDNREQN, + .hskack = IMX8MP_AUDIOMIX_PWRDNACKN, + }, + .pgc = BIT(IMX8MP_PGC_AUDIOMIX), + .keep_clocks = true, + }, + + [IMX8MP_POWER_DOMAIN_GPU2D] = { + .genpd = { + .name = "gpu2d", + }, + .bits = { + .pxx = IMX8MP_GPU_2D_Pxx_REQ, + .map = IMX8MP_GPU2D_A53_DOMAIN, + }, + .pgc = BIT(IMX8MP_PGC_GPU2D), + }, + + [IMX8MP_POWER_DOMAIN_GPUMIX] = { + .genpd = { + .name = "gpumix", + }, + .bits = { + .pxx = IMX8MP_GPU_SHARE_LOGIC_Pxx_REQ, + .map = IMX8MP_GPUMIX_A53_DOMAIN, + .hskreq = IMX8MP_GPUMIX_PWRDNREQN, + .hskack = IMX8MP_GPUMIX_PWRDNACKN, + }, + .pgc = BIT(IMX8MP_PGC_GPUMIX), + .keep_clocks = true, + }, + + [IMX8MP_POWER_DOMAIN_VPUMIX] = { + .genpd = { + .name = "vpumix", + }, + .bits = { + .pxx = IMX8MP_VPU_MIX_SHARE_LOGIC_Pxx_REQ, + .map = IMX8MP_VPUMIX_A53_DOMAIN, + .hskreq = IMX8MP_VPUMIX_PWRDNREQN, + .hskack = IMX8MP_VPUMIX_PWRDNACKN, + }, + .pgc = BIT(IMX8MP_PGC_VPUMIX), + .keep_clocks = true, + }, + + [IMX8MP_POWER_DOMAIN_GPU3D] = { + .genpd = { + .name = "gpu3d", + }, + .bits = { + .pxx = IMX8MP_GPU_3D_Pxx_REQ, + .map = IMX8MP_GPU3D_A53_DOMAIN, + }, + .pgc = BIT(IMX8MP_PGC_GPU3D), + }, + + [IMX8MP_POWER_DOMAIN_MEDIAMIX] = { + .genpd = { + .name = "mediamix", + }, + .bits = { + .pxx = IMX8MP_MEDIMIX_Pxx_REQ, + .map = IMX8MP_MEDIAMIX_A53_DOMAIN, + .hskreq = IMX8MP_MEDIAMIX_PWRDNREQN, + .hskack = IMX8MP_MEDIAMIX_PWRDNACKN, + }, + .pgc = BIT(IMX8MP_PGC_MEDIAMIX), + .keep_clocks = true, + }, + + [IMX8MP_POWER_DOMAIN_VPU_G1] = { + .genpd = { + .name = "vpu-g1", + }, + .bits = { + .pxx = IMX8MP_VPU_G1_Pxx_REQ, + .map = IMX8MP_VPU_G1_A53_DOMAIN, + }, + .pgc = BIT(IMX8MP_PGC_VPU_G1), + }, + + [IMX8MP_POWER_DOMAIN_VPU_G2] = { + .genpd = { + .name = "vpu-g2", + }, + .bits = { + .pxx = IMX8MP_VPU_G2_Pxx_REQ, + .map = IMX8MP_VPU_G2_A53_DOMAIN + }, + .pgc = BIT(IMX8MP_PGC_VPU_G2), + }, + + [IMX8MP_POWER_DOMAIN_VPU_VC8000E] = { + .genpd = { + .name = "vpu-h1", + }, + .bits = { + .pxx = IMX8MP_VPU_VC8K_Pxx_REQ, + .map = IMX8MP_VPU_VC8000E_A53_DOMAIN, + }, + .pgc = BIT(IMX8MP_PGC_VPU_VC8000E), + }, + + [IMX8MP_POWER_DOMAIN_HDMIMIX] = { + .genpd = { + .name = "hdmimix", + }, + .bits = { + .pxx = IMX8MP_HDMIMIX_Pxx_REQ, + .map = IMX8MP_HDMIMIX_A53_DOMAIN, + .hskreq = IMX8MP_HDMIMIX_PWRDNREQN, + .hskack = IMX8MP_HDMIMIX_PWRDNACKN, + }, + .pgc = BIT(IMX8MP_PGC_HDMIMIX), + .keep_clocks = true, + }, + + [IMX8MP_POWER_DOMAIN_HDMI_PHY] = { + .genpd = { + .name = "hdmi-phy", + }, + .bits = { + .pxx = IMX8MP_HDMI_PHY_Pxx_REQ, + .map = IMX8MP_HDMI_PHY_A53_DOMAIN, + }, + .pgc = BIT(IMX8MP_PGC_HDMI), + }, + + [IMX8MP_POWER_DOMAIN_MIPI_PHY2] = { + .genpd = { + .name = "mipi-phy2", + }, + .bits = { + .pxx = IMX8MP_MIPI_PHY2_Pxx_REQ, + .map = IMX8MP_MIPI_PHY2_A53_DOMAIN, + }, + .pgc = BIT(IMX8MP_PGC_MIPI2), + }, + + [IMX8MP_POWER_DOMAIN_HSIOMIX] = { + .genpd = { + .name = "hsiomix", + }, + .bits = { + .pxx = IMX8MP_HSIOMIX_Pxx_REQ, + .map = IMX8MP_HSIOMIX_A53_DOMAIN, + .hskreq = IMX8MP_HSIOMIX_PWRDNREQN, + .hskack = IMX8MP_HSIOMIX_PWRDNACKN, + }, + .pgc = BIT(IMX8MP_PGC_HSIOMIX), + .keep_clocks = true, + }, + + [IMX8MP_POWER_DOMAIN_MEDIAMIX_ISPDWP] = { + .genpd = { + .name = "mediamix-isp-dwp", + }, + .bits = { + .pxx = IMX8MP_MEDIA_ISP_DWP_Pxx_REQ, + .map = IMX8MP_MEDIA_ISPDWP_A53_DOMAIN, + }, + .pgc = BIT(IMX8MP_PGC_MEDIA_ISP_DWP), + }, +}; + +static const struct regmap_range imx8mp_yes_ranges[] = { + regmap_reg_range(GPC_LPCR_A_CORE_BSC, + IMX8MP_GPC_PGC_CPU_MAPPING), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_NOC), + GPC_PGC_SR(IMX8MP_PGC_NOC)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_MIPI1), + GPC_PGC_SR(IMX8MP_PGC_MIPI1)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_PCIE), + GPC_PGC_SR(IMX8MP_PGC_PCIE)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_USB1), + GPC_PGC_SR(IMX8MP_PGC_USB1)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_USB2), + GPC_PGC_SR(IMX8MP_PGC_USB2)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_MLMIX), + GPC_PGC_SR(IMX8MP_PGC_MLMIX)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_AUDIOMIX), + GPC_PGC_SR(IMX8MP_PGC_AUDIOMIX)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_GPU2D), + GPC_PGC_SR(IMX8MP_PGC_GPU2D)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_GPUMIX), + GPC_PGC_SR(IMX8MP_PGC_GPUMIX)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_VPUMIX), + GPC_PGC_SR(IMX8MP_PGC_VPUMIX)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_GPU3D), + GPC_PGC_SR(IMX8MP_PGC_GPU3D)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_MEDIAMIX), + GPC_PGC_SR(IMX8MP_PGC_MEDIAMIX)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_VPU_G1), + GPC_PGC_SR(IMX8MP_PGC_VPU_G1)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_VPU_G2), + GPC_PGC_SR(IMX8MP_PGC_VPU_G2)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_VPU_VC8000E), + GPC_PGC_SR(IMX8MP_PGC_VPU_VC8000E)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_HDMIMIX), + GPC_PGC_SR(IMX8MP_PGC_HDMIMIX)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_HDMI), + GPC_PGC_SR(IMX8MP_PGC_HDMI)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_MIPI2), + GPC_PGC_SR(IMX8MP_PGC_MIPI2)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_HSIOMIX), + GPC_PGC_SR(IMX8MP_PGC_HSIOMIX)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_MEDIA_ISP_DWP), + GPC_PGC_SR(IMX8MP_PGC_MEDIA_ISP_DWP)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_DDRMIX), + GPC_PGC_SR(IMX8MP_PGC_DDRMIX)), +}; + +static const struct regmap_access_table imx8mp_access_table = { + .yes_ranges = imx8mp_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(imx8mp_yes_ranges), +}; + +static const struct imx_pgc_regs imx8mp_pgc_regs = { + .map = IMX8MP_GPC_PGC_CPU_MAPPING, + .pup = IMX8MP_GPC_PU_PGC_SW_PUP_REQ, + .pdn = IMX8MP_GPC_PU_PGC_SW_PDN_REQ, + .hsk = IMX8MP_GPC_PU_PWRHSK, +}; +static const struct imx_pgc_domain_data imx8mp_pgc_domain_data = { + .domains = imx8mp_pgc_domains, + .domains_num = ARRAY_SIZE(imx8mp_pgc_domains), + .reg_access_table = &imx8mp_access_table, + .pgc_regs = &imx8mp_pgc_regs, +}; + static const struct imx_pgc_domain imx8mn_pgc_domains[] = { [IMX8MN_POWER_DOMAIN_HSIOMIX] = { .genpd = { @@ -1120,6 +1503,7 @@ static int imx_gpcv2_probe(struct platform_device *pdev) domain = pd_pdev->dev.platform_data; domain->regmap = regmap; domain->regs = domain_data->pgc_regs; + domain->genpd.power_on = imx_pgc_power_up; domain->genpd.power_off = imx_pgc_power_down; @@ -1141,6 +1525,7 @@ static const struct of_device_id imx_gpcv2_dt_ids[] = { { .compatible = "fsl,imx7d-gpc", .data = &imx7_pgc_domain_data, }, { .compatible = "fsl,imx8mm-gpc", .data = &imx8mm_pgc_domain_data, }, { .compatible = "fsl,imx8mn-gpc", .data = &imx8mn_pgc_domain_data, }, + { .compatible = "fsl,imx8mp-gpc", .data = &imx8mp_pgc_domain_data, }, { .compatible = "fsl,imx8mq-gpc", .data = &imx8m_pgc_domain_data, }, { } }; -- cgit v1.2.3 From 8f0e3af81711bf72b9c6138b0138bdc330d8c388 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 29 Mar 2022 11:38:03 +0200 Subject: dt-bindings: memory: renesas,rpc-if: Document R-Car H3/M3/E3 support Document support for the SPI Multi I/O Bus Controller (RPC-IF) in the R-Car H3, M3-W, M3-W+, M3-N, and E3 SoCs. Signed-off-by: Geert Uytterhoeven Reviewed-by: Wolfram Sang Acked-by: Rob Herring Link: https://lore.kernel.org/r/3784b6cb76a008fb56d6cb4ba228d78c77e710fa.1648546583.git.geert+renesas@glider.be Signed-off-by: Krzysztof Kozlowski --- .../devicetree/bindings/memory-controllers/renesas,rpc-if.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.yaml b/Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.yaml index 9a6dbf54b540..ae31dc97524c 100644 --- a/Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.yaml @@ -31,8 +31,13 @@ properties: - renesas,r8a774b1-rpc-if # RZ/G2N - renesas,r8a774c0-rpc-if # RZ/G2E - renesas,r8a774e1-rpc-if # RZ/G2H + - renesas,r8a7795-rpc-if # R-Car H3 + - renesas,r8a7796-rpc-if # R-Car M3-W + - renesas,r8a77961-rpc-if # R-Car M3-W+ + - renesas,r8a77965-rpc-if # R-Car M3-N - renesas,r8a77970-rpc-if # R-Car V3M - renesas,r8a77980-rpc-if # R-Car V3H + - renesas,r8a77990-rpc-if # R-Car E3 - renesas,r8a77995-rpc-if # R-Car D3 - renesas,r8a779a0-rpc-if # R-Car V3U - const: renesas,rcar-gen3-rpc-if # a generic R-Car gen3 or RZ/G2{E,H,M,N} device -- cgit v1.2.3 From a1793920b6647447e4757aec4434eb919840acb8 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Thu, 24 Feb 2022 19:54:15 -0800 Subject: dt-bindings: arm: qcom: Document sa8540p, sc8180x and sc8280xp Add compatibles for the sa8540p automotive, as well as sc8180x and sc8280xp compute platforms. Also add compatibles for the ADP devboard, the Lenovo Flex 5G, Microsoft Surface Pro X and the sc8280xp QRD. Signed-off-by: Bjorn Andersson Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220225035415.2294821-1-bjorn.andersson@linaro.org --- Documentation/devicetree/bindings/arm/qcom.yaml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/qcom.yaml b/Documentation/devicetree/bindings/arm/qcom.yaml index 129cdd246223..93e4e4256e73 100644 --- a/Documentation/devicetree/bindings/arm/qcom.yaml +++ b/Documentation/devicetree/bindings/arm/qcom.yaml @@ -39,8 +39,11 @@ description: | msm8994 msm8996 sa8155p + sa8540p sc7180 sc7280 + sc8180x + sc8280xp sdm630 sdm632 sdm660 @@ -225,6 +228,18 @@ properties: - google,senor - const: qcom,sc7280 + - items: + - enum: + - lenovo,flex-5g + - microsoft,surface-prox + - qcom,sc8180x-primus + - const: qcom,sc8180x + + - items: + - enum: + - qcom,sc8280xp-qrd + - const: qcom,sc8280xp + - items: - enum: - fairphone,fp3 @@ -258,6 +273,11 @@ properties: - qcom,sa8155p-adp - const: qcom,sa8155p + - items: + - enum: + - qcom,sa8295p-adp + - const: qcom,sa8540p + - items: - enum: - fairphone,fp4 -- cgit v1.2.3 From 8a1b63b14fe7df811439235606c146faa15faa52 Mon Sep 17 00:00:00 2001 From: Johan Jonker Date: Thu, 24 Mar 2022 13:01:22 +0100 Subject: dt-bindings: soc: rockchip: add naneng combo phy register compatible Add Naneng combo phy register compatible. Signed-off-by: Johan Jonker Signed-off-by: Yifeng Zhao Acked-by: Rob Herring Link: https://lore.kernel.org/r/20220324120122.1339-1-jbx6244@gmail.com Signed-off-by: Heiko Stuebner --- Documentation/devicetree/bindings/soc/rockchip/grf.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/soc/rockchip/grf.yaml b/Documentation/devicetree/bindings/soc/rockchip/grf.yaml index b2ba7bed89b2..5079e9d24af6 100644 --- a/Documentation/devicetree/bindings/soc/rockchip/grf.yaml +++ b/Documentation/devicetree/bindings/soc/rockchip/grf.yaml @@ -15,6 +15,8 @@ properties: - items: - enum: - rockchip,rk3288-sgrf + - rockchip,rk3568-pipe-grf + - rockchip,rk3568-pipe-phy-grf - rockchip,rk3568-usb2phy-grf - rockchip,rv1108-usbgrf - const: syscon -- cgit v1.2.3 From 07905844f4880df27a3d80adb07342ec152fd2d7 Mon Sep 17 00:00:00 2001 From: Sebastian Fricke Date: Sat, 26 Mar 2022 19:34:50 +0100 Subject: soc: rockchip: pm_domains: Fix typo in comment s/eject devices form power domain/eject devices from the power domain/ Signed-off-by: Sebastian Fricke Link: https://lore.kernel.org/r/20220326183451.66115-1-sebastian.fricke@collabora.com Signed-off-by: Heiko Stuebner --- drivers/soc/rockchip/pm_domains.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/soc/rockchip/pm_domains.c b/drivers/soc/rockchip/pm_domains.c index 0868b7d406fb..1b029e494274 100644 --- a/drivers/soc/rockchip/pm_domains.c +++ b/drivers/soc/rockchip/pm_domains.c @@ -1068,9 +1068,9 @@ static struct platform_driver rockchip_pm_domain_driver = { .name = "rockchip-pm-domain", .of_match_table = rockchip_pm_domain_dt_match, /* - * We can't forcibly eject devices form power domain, - * so we can't really remove power domains once they - * were added. + * We can't forcibly eject devices from the power + * domain, so we can't really remove power domains + * once they were added. */ .suppress_bind_attrs = true, }, -- cgit v1.2.3 From 2a872dd86eeb349f169df0a204668afa578a94b2 Mon Sep 17 00:00:00 2001 From: Peter Geis Date: Fri, 8 Apr 2022 11:12:33 -0400 Subject: dt-bindings: soc: rockchip: add rk3566-pipe-grf compatible The rk3566 requires special handling for the dwc3-otg clock in order for the port to function correctly. Add a binding for the rk3566-pipe-grf so we can handle setup with the grf driver. Signed-off-by: Peter Geis Acked-by: Rob Herring Link: https://lore.kernel.org/r/20220408151237.3165046-2-pgwipeout@gmail.com Signed-off-by: Heiko Stuebner --- Documentation/devicetree/bindings/soc/rockchip/grf.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/soc/rockchip/grf.yaml b/Documentation/devicetree/bindings/soc/rockchip/grf.yaml index 5079e9d24af6..75a2b8bb25fb 100644 --- a/Documentation/devicetree/bindings/soc/rockchip/grf.yaml +++ b/Documentation/devicetree/bindings/soc/rockchip/grf.yaml @@ -15,6 +15,7 @@ properties: - items: - enum: - rockchip,rk3288-sgrf + - rockchip,rk3566-pipe-grf - rockchip,rk3568-pipe-grf - rockchip,rk3568-pipe-phy-grf - rockchip,rk3568-usb2phy-grf -- cgit v1.2.3 From 5c0bb71138770d85ea840acd379edc6471b867ee Mon Sep 17 00:00:00 2001 From: Peter Geis Date: Fri, 8 Apr 2022 11:12:34 -0400 Subject: soc: rockchip: set dwc3 clock for rk3566 The rk3566 dwc3 otg port clock is unavailable at boot, as it defaults to the combophy as the clock source. As combophy0 doesn't exist on rk3566, we need to set the clock source to the usb2 phy instead. Add handling to the grf driver to handle this on boot. Signed-off-by: Peter Geis Link: https://lore.kernel.org/r/20220408151237.3165046-3-pgwipeout@gmail.com Signed-off-by: Heiko Stuebner --- drivers/soc/rockchip/grf.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/soc/rockchip/grf.c b/drivers/soc/rockchip/grf.c index 494cf2b5bf7b..384461b70684 100644 --- a/drivers/soc/rockchip/grf.c +++ b/drivers/soc/rockchip/grf.c @@ -108,6 +108,20 @@ static const struct rockchip_grf_info rk3399_grf __initconst = { .num_values = ARRAY_SIZE(rk3399_defaults), }; +#define RK3566_GRF_USB3OTG0_CON1 0x0104 + +static const struct rockchip_grf_value rk3566_defaults[] __initconst = { + { "usb3otg port switch", RK3566_GRF_USB3OTG0_CON1, HIWORD_UPDATE(0, 1, 12) }, + { "usb3otg clock switch", RK3566_GRF_USB3OTG0_CON1, HIWORD_UPDATE(1, 1, 7) }, + { "usb3otg disable usb3", RK3566_GRF_USB3OTG0_CON1, HIWORD_UPDATE(1, 1, 0) }, +}; + +static const struct rockchip_grf_info rk3566_pipegrf __initconst = { + .values = rk3566_defaults, + .num_values = ARRAY_SIZE(rk3566_defaults), +}; + + static const struct of_device_id rockchip_grf_dt_match[] __initconst = { { .compatible = "rockchip,rk3036-grf", @@ -130,6 +144,9 @@ static const struct of_device_id rockchip_grf_dt_match[] __initconst = { }, { .compatible = "rockchip,rk3399-grf", .data = (void *)&rk3399_grf, + }, { + .compatible = "rockchip,rk3566-pipe-grf", + .data = (void *)&rk3566_pipegrf, }, { /* sentinel */ }, }; -- cgit v1.2.3 From c2b0390132edffad1b52e4e84f797343b10f5ef2 Mon Sep 17 00:00:00 2001 From: Yihao Han Date: Thu, 3 Mar 2022 04:44:44 -0800 Subject: soc: ti: wkup_m3_ipc: fix platform_get_irq.cocci warning Remove dev_err() messages after platform_get_irq*() failures. platform_get_irq() already prints an error. Generated by: scripts/coccinelle/api/platform_get_irq.cocci Signed-off-by: Yihao Han Signed-off-by: Nishanth Menon Link: https://lore.kernel.org/r/20220303124444.3373-1-hanyihao@vivo.com --- drivers/soc/ti/wkup_m3_ipc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/soc/ti/wkup_m3_ipc.c b/drivers/soc/ti/wkup_m3_ipc.c index 2f03ced0f411..f145e65041fd 100644 --- a/drivers/soc/ti/wkup_m3_ipc.c +++ b/drivers/soc/ti/wkup_m3_ipc.c @@ -450,10 +450,8 @@ static int wkup_m3_ipc_probe(struct platform_device *pdev) return PTR_ERR(m3_ipc->ipc_mem_base); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "no irq resource\n"); + if (irq < 0) return irq; - } ret = devm_request_irq(dev, irq, wkup_m3_txev_handler, 0, "wkup_m3_txev", m3_ipc); -- cgit v1.2.3 From d281a982c2693c6a7bffaa1ae1ff3b400d6e6c74 Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Thu, 24 Mar 2022 08:25:03 +0100 Subject: soc: ti: replace usage of found with dedicated list iterator variable To move the list iterator variable into the list_for_each_entry_*() macro in the future it should be avoided to use the list iterator variable after the loop body. To *never* use the list iterator variable after the loop it was concluded to use a separate iterator variable instead of a found boolean [1]. This removes the need to use a found variable and simply checking if the variable was set, can determine if the break/goto was hit. Signed-off-by: Jakob Koschel Signed-off-by: Nishanth Menon Reviewed-by: Vignesh Raghavendra Link: https://lore.kernel.org/all/CAHk-=wgRr_D8CB-D9Kg-c=EHreAsk5SqXPwr9Y7k9sA6cWXJ6w@mail.gmail.com/ Link: https://lore.kernel.org/r/20220324072503.63244-1-jakobkoschel@gmail.com --- drivers/soc/ti/knav_dma.c | 26 ++++++++++++-------------- drivers/soc/ti/knav_qmss_queue.c | 16 +++++++--------- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/drivers/soc/ti/knav_dma.c b/drivers/soc/ti/knav_dma.c index 700d8eecd8c4..7e126a73e56e 100644 --- a/drivers/soc/ti/knav_dma.c +++ b/drivers/soc/ti/knav_dma.c @@ -415,9 +415,8 @@ static int of_channel_match_helper(struct device_node *np, const char *name, void *knav_dma_open_channel(struct device *dev, const char *name, struct knav_dma_cfg *config) { - struct knav_dma_chan *chan; - struct knav_dma_device *dma; - bool found = false; + struct knav_dma_device *dma = NULL, *iter1; + struct knav_dma_chan *chan = NULL, *iter2; int chan_num = -1; const char *instance; @@ -444,33 +443,32 @@ void *knav_dma_open_channel(struct device *dev, const char *name, } /* Look for correct dma instance */ - list_for_each_entry(dma, &kdev->list, list) { - if (!strcmp(dma->name, instance)) { - found = true; + list_for_each_entry(iter1, &kdev->list, list) { + if (!strcmp(iter1->name, instance)) { + dma = iter1; break; } } - if (!found) { + if (!dma) { dev_err(kdev->dev, "No DMA instance with name %s\n", instance); return (void *)-EINVAL; } /* Look for correct dma channel from dma instance */ - found = false; - list_for_each_entry(chan, &dma->chan_list, list) { + list_for_each_entry(iter2, &dma->chan_list, list) { if (config->direction == DMA_MEM_TO_DEV) { - if (chan->channel == chan_num) { - found = true; + if (iter2->channel == chan_num) { + chan = iter2; break; } } else { - if (chan->flow == chan_num) { - found = true; + if (iter2->flow == chan_num) { + chan = iter2; break; } } } - if (!found) { + if (!chan) { dev_err(kdev->dev, "channel %d is not in DMA %s\n", chan_num, instance); return (void *)-EINVAL; diff --git a/drivers/soc/ti/knav_qmss_queue.c b/drivers/soc/ti/knav_qmss_queue.c index 2ac3856b8d42..4dbaa8c3636c 100644 --- a/drivers/soc/ti/knav_qmss_queue.c +++ b/drivers/soc/ti/knav_qmss_queue.c @@ -758,10 +758,9 @@ void *knav_pool_create(const char *name, int num_desc, int region_id) { struct knav_region *reg_itr, *region = NULL; - struct knav_pool *pool, *pi; + struct knav_pool *pool, *pi = NULL, *iter; struct list_head *node; unsigned last_offset; - bool slot_found; int ret; if (!kdev) @@ -816,18 +815,17 @@ void *knav_pool_create(const char *name, * the request */ last_offset = 0; - slot_found = false; node = ®ion->pools; - list_for_each_entry(pi, ®ion->pools, region_inst) { - if ((pi->region_offset - last_offset) >= num_desc) { - slot_found = true; + list_for_each_entry(iter, ®ion->pools, region_inst) { + if ((iter->region_offset - last_offset) >= num_desc) { + pi = iter; break; } - last_offset = pi->region_offset + pi->num_desc; + last_offset = iter->region_offset + iter->num_desc; } - node = &pi->region_inst; - if (slot_found) { + if (pi) { + node = &pi->region_inst; pool->region = region; pool->num_desc = num_desc; pool->region_offset = last_offset; -- cgit v1.2.3 From f25d2b2b554133b935e72c61deb40d82287a6f75 Mon Sep 17 00:00:00 2001 From: Minghao Chi Date: Fri, 8 Apr 2022 08:08:53 +0000 Subject: soc: ti: pruss: using pm_runtime_resume_and_get instead of pm_runtime_get_sync Using pm_runtime_resume_and_get is more appropriate for simplifing code Reported-by: Zeal Robot Signed-off-by: Minghao Chi Signed-off-by: Nishanth Menon Reviewed-by: Dave Gerlach Link: https://lore.kernel.org/r/20220408080853.2494292-1-chi.minghao@zte.com.cn --- drivers/soc/ti/pruss.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/soc/ti/pruss.c b/drivers/soc/ti/pruss.c index b36779309e49..0e4ba0f89533 100644 --- a/drivers/soc/ti/pruss.c +++ b/drivers/soc/ti/pruss.c @@ -279,10 +279,9 @@ static int pruss_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pruss); pm_runtime_enable(dev); - ret = pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); if (ret < 0) { dev_err(dev, "couldn't enable module\n"); - pm_runtime_put_noidle(dev); goto rpm_disable; } -- cgit v1.2.3 From 5334a3b12a7233b31788de60d61bfd890059d783 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Fri, 8 Apr 2022 14:33:36 -0700 Subject: soc: qcom: llcc: Add MODULE_DEVICE_TABLE() The llcc-qcom driver can be compiled as a module, but lacks MODULE_DEVICE_TABLE() and will therefore not be loaded automatically. Fix this. Fixes: a3134fb09e0b ("drivers: soc: Add LLCC driver") Signed-off-by: Bjorn Andersson Reviewed-by: Sai Prakash Ranjan Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20220408213336.581661-3-bjorn.andersson@linaro.org --- drivers/soc/qcom/llcc-qcom.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index eecafeded56f..85ba8209b182 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -749,6 +749,7 @@ static const struct of_device_id qcom_llcc_of_match[] = { { .compatible = "qcom,sm8450-llcc", .data = &sm8450_cfg }, { } }; +MODULE_DEVICE_TABLE(of, qcom_llcc_of_match); static struct platform_driver qcom_llcc_driver = { .driver = { -- cgit v1.2.3 From d00004c4563f6c00a79f7228524966ac7a1aa29c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 5 Apr 2022 08:55:12 +0200 Subject: dt-bindings: soc: qcom,smsm: convert to dtschema Convert the Qualcomm Shared Memory State Machine to DT schema. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220405065512.26521-1-krzysztof.kozlowski@linaro.org --- .../devicetree/bindings/soc/qcom/qcom,smsm.txt | 104 ---------------- .../devicetree/bindings/soc/qcom/qcom,smsm.yaml | 138 +++++++++++++++++++++ 2 files changed, 138 insertions(+), 104 deletions(-) delete mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,smsm.txt create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,smsm.yaml diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smsm.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,smsm.txt deleted file mode 100644 index 2993b5a97dd6..000000000000 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,smsm.txt +++ /dev/null @@ -1,104 +0,0 @@ -Qualcomm Shared Memory State Machine - -The Shared Memory State Machine facilitates broadcasting of single bit state -information between the processors in a Qualcomm SoC. Each processor is -assigned 32 bits of state that can be modified. A processor can through a -matrix of bitmaps signal subscription of notifications upon changes to a -certain bit owned by a certain remote processor. - -- compatible: - Usage: required - Value type: - Definition: must be one of: - "qcom,smsm" - -- qcom,ipc-N: - Usage: required - Value type: - Definition: three entries specifying the outgoing ipc bit used for - signaling the N:th remote processor - - phandle to a syscon node representing the apcs registers - - u32 representing offset to the register within the syscon - - u32 representing the ipc bit within the register - -- qcom,local-host: - Usage: optional - Value type: - Definition: identifier of the local processor in the list of hosts, or - in other words specifier of the column in the subscription - matrix representing the local processor - defaults to host 0 - -- #address-cells: - Usage: required - Value type: - Definition: must be 1 - -- #size-cells: - Usage: required - Value type: - Definition: must be 0 - -= SUBNODES -Each processor's state bits are described by a subnode of the smsm device node. -Nodes can either be flagged as an interrupt-controller to denote a remote -processor's state bits or the local processors bits. The node names are not -important. - -- reg: - Usage: required - Value type: - Definition: specifies the offset, in words, of the first bit for this - entry - -- #qcom,smem-state-cells: - Usage: required for local entry - Value type: - Definition: must be 1 - denotes bit number - -- interrupt-controller: - Usage: required for remote entries - Value type: - Definition: marks the entry as a interrupt-controller and the state bits - to belong to a remote processor - -- #interrupt-cells: - Usage: required for remote entries - Value type: - Definition: must be 2 - denotes bit number and IRQ flags - -- interrupts: - Usage: required for remote entries - Value type: - Definition: one entry specifying remote IRQ used by the remote processor - to signal changes of its state bits - - -= EXAMPLE -The following example shows the SMEM setup for controlling properties of the -wireless processor, defined from the 8974 apps processor's point-of-view. It -encompasses one outbound entry and the outgoing interrupt for the wireless -processor. - -smsm { - compatible = "qcom,smsm"; - - #address-cells = <1>; - #size-cells = <0>; - - qcom,ipc-3 = <&apcs 8 19>; - - apps_smsm: apps@0 { - reg = <0>; - - #qcom,smem-state-cells = <1>; - }; - - wcnss_smsm: wcnss@7 { - reg = <7>; - interrupts = <0 144 1>; - - interrupt-controller; - #interrupt-cells = <2>; - }; -}; diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smsm.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,smsm.yaml new file mode 100644 index 000000000000..db67cf043256 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smsm.yaml @@ -0,0 +1,138 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/qcom/qcom,smsm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Shared Memory State Machine + +maintainers: + - Andy Gross + - Bjorn Andersson + - Krzysztof Kozlowski + +description: + The Shared Memory State Machine facilitates broadcasting of single bit state + information between the processors in a Qualcomm SoC. Each processor is + assigned 32 bits of state that can be modified. A processor can through a + matrix of bitmaps signal subscription of notifications upon changes to a + certain bit owned by a certain remote processor. + +properties: + compatible: + const: qcom,smsm + + '#address-cells': + const: 1 + + qcom,local-host: + $ref: /schemas/types.yaml#/definitions/uint32 + default: 0 + description: + Identifier of the local processor in the list of hosts, or in other words + specifier of the column in the subscription matrix representing the local + processor. + + '#size-cells': + const: 0 + +patternProperties: + "^qcom,ipc-[1-4]$": + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to a syscon node representing the APCS registers + - description: u32 representing offset to the register within the syscon + - description: u32 representing the ipc bit within the register + description: + Three entries specifying the outgoing ipc bit used for signaling the N:th + remote processor. + + "@[0-9a-f]$": + type: object + description: + Each processor's state bits are described by a subnode of the SMSM device + node. Nodes can either be flagged as an interrupt-controller to denote a + remote processor's state bits or the local processors bits. The node + names are not important. + + properties: + reg: + maxItems: 1 + + interrupt-controller: + description: + Marks the entry as a interrupt-controller and the state bits to + belong to a remote processor. + + '#interrupt-cells': + const: 2 + + interrupts: + maxItems: 1 + description: + One entry specifying remote IRQ used by the remote processor to + signal changes of its state bits. + + '#qcom,smem-state-cells': + $ref: /schemas/types.yaml#/definitions/uint32 + const: 1 + description: + Required for local entry. Denotes bit number. + + required: + - reg + + oneOf: + - required: + - '#qcom,smem-state-cells' + - required: + - interrupt-controller + - '#interrupt-cells' + - interrupts + + additionalProperties: false + +required: + - compatible + - '#address-cells' + - '#size-cells' + +anyOf: + - required: + - qcom,ipc-1 + - required: + - qcom,ipc-2 + - required: + - qcom,ipc-3 + - required: + - qcom,ipc-4 + +additionalProperties: false + +examples: + # The following example shows the SMEM setup for controlling properties of + # the wireless processor, defined from the 8974 apps processor's + # point-of-view. It encompasses one outbound entry and the outgoing interrupt + # for the wireless processor. + - | + #include + + shared-memory { + compatible = "qcom,smsm"; + #address-cells = <1>; + #size-cells = <0>; + qcom,ipc-3 = <&apcs 8 19>; + + apps_smsm: apps@0 { + reg = <0>; + #qcom,smem-state-cells = <1>; + }; + + wcnss_smsm: wcnss@7 { + reg = <7>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; -- cgit v1.2.3 From a5ffbc04ad0327919157a643cc56de513a4c6928 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 5 Apr 2022 08:57:52 +0200 Subject: dt-bindings: soc: qcom,wcnss: convert to dtschema Convert the Qualcomm WCNSS bindings to DT Schema. Changes against original bindings: fix qcom,smd-channels and qcom,smem-states names (how they are used in DTS and driver), enforce child node naming of bluetooth and wifi. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220405065752.27389-3-krzysztof.kozlowski@linaro.org --- .../devicetree/bindings/soc/qcom/qcom,wcnss.txt | 131 -------------------- .../devicetree/bindings/soc/qcom/qcom,wcnss.yaml | 137 +++++++++++++++++++++ 2 files changed, 137 insertions(+), 131 deletions(-) delete mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,wcnss.txt create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,wcnss.yaml diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,wcnss.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,wcnss.txt deleted file mode 100644 index 1382b64e1381..000000000000 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,wcnss.txt +++ /dev/null @@ -1,131 +0,0 @@ -Qualcomm WCNSS Binding - -This binding describes the Qualcomm WCNSS hardware. It consists of control -block and a BT, WiFi and FM radio block, all using SMD as command channels. - -- compatible: - Usage: required - Value type: - Definition: must be: "qcom,wcnss", - -- qcom,smd-channel: - Usage: required - Value type: - Definition: standard SMD property specifying the SMD channel used for - communication with the WiFi firmware. - Should be "WCNSS_CTRL". - -- qcom,mmio: - Usage: required - Value type: - Definition: reference to a node specifying the wcnss "ccu" and "dxe" - register blocks. The node must be compatible with one of - the following: - "qcom,riva", - "qcom,pronto" - -- firmware-name: - Usage: optional - Value type: - Definition: specifies the relative firmware image path for the WLAN NV - blob. Defaults to "wlan/prima/WCNSS_qcom_wlan_nv.bin" if - not specified. - -= SUBNODES -The subnodes of the wcnss node are optional and describe the individual blocks in -the WCNSS. - -== Bluetooth -The following properties are defined to the bluetooth node: - -- compatible: - Usage: required - Value type: - Definition: must be: - "qcom,wcnss-bt" - -- local-bd-address: - Usage: optional - Value type: - Definition: see Documentation/devicetree/bindings/net/bluetooth.txt - -== WiFi -The following properties are defined to the WiFi node: - -- compatible: - Usage: required - Value type: - Definition: must be one of: - "qcom,wcnss-wlan", - -- interrupts: - Usage: required - Value type: - Definition: should specify the "rx" and "tx" interrupts - -- interrupt-names: - Usage: required - Value type: - Definition: must contain "rx" and "tx" - -- qcom,smem-state: - Usage: required - Value type: - Definition: should reference the tx-enable and tx-rings-empty SMEM states - -- qcom,smem-state-names: - Usage: required - Value type: - Definition: must contain "tx-enable" and "tx-rings-empty" - -= EXAMPLE -The following example represents a SMD node, with one edge representing the -"pronto" subsystem, with the wcnss device and its wcn3680 BT and WiFi blocks -described; as found on the 8974 platform. - -smd { - compatible = "qcom,smd"; - - pronto-edge { - interrupts = <0 142 1>; - - qcom,ipc = <&apcs 8 17>; - qcom,smd-edge = <6>; - - wcnss { - compatible = "qcom,wcnss"; - qcom,smd-channels = "WCNSS_CTRL"; - - #address-cells = <1>; - #size-cells = <1>; - - qcom,mmio = <&pronto>; - - bt { - compatible = "qcom,wcnss-bt"; - - /* BD address 00:11:22:33:44:55 */ - local-bd-address = [ 55 44 33 22 11 00 ]; - }; - - wlan { - compatible = "qcom,wcnss-wlan"; - - interrupts = <0 145 0>, <0 146 0>; - interrupt-names = "tx", "rx"; - - qcom,smem-state = <&apps_smsm 10>, <&apps_smsm 9>; - qcom,smem-state-names = "tx-enable", "tx-rings-empty"; - }; - }; - }; -}; - -soc { - pronto: pronto { - compatible = "qcom,pronto"; - - reg = <0xfb204000 0x2000>, <0xfb202000 0x1000>, <0xfb21b000 0x3000>; - reg-names = "ccu", "dxe", "pmu"; - }; -}; diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,wcnss.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,wcnss.yaml new file mode 100644 index 000000000000..d891ecfb2691 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,wcnss.yaml @@ -0,0 +1,137 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/qcom/qcom,wcnss.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm WCNSS + +maintainers: + - Andy Gross + - Bjorn Andersson + +description: + The Qualcomm WCNSS hardware consists of control block and a BT, WiFi and FM + radio block, all using SMD as command channels. + +properties: + compatible: + const: qcom,wcnss + + firmware-name: + $ref: /schemas/types.yaml#/definitions/string + default: "wlan/prima/WCNSS_qcom_wlan_nv.bin" + description: + Relative firmware image path for the WLAN NV blob. + + qcom,mmio: + $ref: /schemas/types.yaml#/definitions/phandle + description: | + Reference to a node specifying the wcnss "ccu" and "dxe" register blocks. + The node must be compatible with one of the following:: + - qcom,riva" + - qcom,pronto" + + qcom,smd-channels: + $ref: /schemas/types.yaml#/definitions/string + const: WCNSS_CTRL + description: + Standard SMD property specifying the SMD channel used for communication + with the WiFi firmware. + + bluetooth: + type: object + additionalProperties: false + properties: + compatible: + const: qcom,wcnss-bt + + local-bd-address: + $ref: /schemas/types.yaml#/definitions/uint8-array + maxItems: 6 + description: + See Documentation/devicetree/bindings/net/bluetooth.txt + + required: + - compatible + + wifi: + additionalProperties: false + type: object + properties: + compatible: + const: qcom,wcnss-wlan + + interrupts: + maxItems: 2 + + interrupt-names: + items: + - const: tx + - const: rx + + qcom,smem-states: + $ref: /schemas/types.yaml#/definitions/phandle-array + maxItems: 2 + description: + Should reference the tx-enable and tx-rings-empty SMEM states. + + qcom,smem-state-names: + $ref: /schemas/types.yaml#/definitions/string-array + items: + - const: tx-enable + - const: tx-rings-empty + description: + Names of SMEM states. + + required: + - compatible + - interrupts + - interrupt-names + - qcom,smem-states + - qcom,smem-state-names + +required: + - compatible + - qcom,mmio + - qcom,smd-channels + +additionalProperties: false + +examples: + - | + #include + + smd-edge { + interrupts = ; + + qcom,ipc = <&apcs 8 17>; + qcom,smd-edge = <6>; + qcom,remote-pid = <4>; + + label = "pronto"; + + wcnss { + compatible = "qcom,wcnss"; + qcom,smd-channels = "WCNSS_CTRL"; + + qcom,mmio = <&pronto>; + + bluetooth { + compatible = "qcom,wcnss-bt"; + /* BD address 00:11:22:33:44:55 */ + local-bd-address = [ 55 44 33 22 11 00 ]; + }; + + wifi { + compatible = "qcom,wcnss-wlan"; + + interrupts = , + ; + interrupt-names = "tx", "rx"; + + qcom,smem-states = <&apps_smsm 10>, <&apps_smsm 9>; + qcom,smem-state-names = "tx-enable", "tx-rings-empty"; + }; + }; + }; -- cgit v1.2.3 From 1dcf37365e7de6214af55587c69f4cced06262de Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 5 Apr 2022 08:34:51 +0200 Subject: dt-bindings: qcom: qcom,gsbi: convert to dtschema Convert the Qualcomm General Serial Bus Interface (GSBI) to DT Schema. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220405063451.12011-10-krzysztof.kozlowski@linaro.org --- .../devicetree/bindings/soc/qcom/qcom,gsbi.txt | 87 -------------- .../devicetree/bindings/soc/qcom/qcom,gsbi.yaml | 132 +++++++++++++++++++++ 2 files changed, 132 insertions(+), 87 deletions(-) delete mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.yaml diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt deleted file mode 100644 index fe1855f09dcc..000000000000 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt +++ /dev/null @@ -1,87 +0,0 @@ -QCOM GSBI (General Serial Bus Interface) Driver - -The GSBI controller is modeled as a node with zero or more child nodes, each -representing a serial sub-node device that is mux'd as part of the GSBI -configuration settings. The mode setting will govern the input/output mode of -the 4 GSBI IOs. - -Required properties: -- compatible: Should contain "qcom,gsbi-v1.0.0" -- cell-index: Should contain the GSBI index -- reg: Address range for GSBI registers -- clocks: required clock -- clock-names: must contain "iface" entry -- qcom,mode : indicates MUX value for configuration of the serial interface. - Please reference dt-bindings/soc/qcom,gsbi.h for valid mux values. - -Optional properties: -- qcom,crci : indicates CRCI MUX value for QUP CRCI ports. Please reference - dt-bindings/soc/qcom,gsbi.h for valid CRCI mux values. -- syscon-tcsr: indicates phandle of TCSR syscon node. Required if child uses - dma. - -Required properties if child node exists: -- #address-cells: Must be 1 -- #size-cells: Must be 1 -- ranges: Must be present - -Properties for children: - -A GSBI controller node can contain 0 or more child nodes representing serial -devices. These serial devices can be a QCOM UART, I2C controller, spi -controller, or some combination of aforementioned devices. - -See the following for child node definitions: -Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt -Documentation/devicetree/bindings/spi/qcom,spi-qup.txt -Documentation/devicetree/bindings/serial/qcom,msm-uartdm.txt - -Example for APQ8064: - -#include - - gsbi4@16300000 { - compatible = "qcom,gsbi-v1.0.0"; - cell-index = <4>; - reg = <0x16300000 0x100>; - clocks = <&gcc GSBI4_H_CLK>; - clock-names = "iface"; - #address-cells = <1>; - #size-cells = <1>; - ranges; - qcom,mode = ; - qcom,crci = ; - - syscon-tcsr = <&tcsr>; - - /* child nodes go under here */ - - i2c_qup4: i2c@16380000 { - compatible = "qcom,i2c-qup-v1.1.1"; - reg = <0x16380000 0x1000>; - interrupts = <0 153 0>; - - clocks = <&gcc GSBI4_QUP_CLK>, <&gcc GSBI4_H_CLK>; - clock-names = "core", "iface"; - - clock-frequency = <200000>; - - #address-cells = <1>; - #size-cells = <0>; - - }; - - uart4: serial@16340000 { - compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; - reg = <0x16340000 0x1000>, - <0x16300000 0x1000>; - interrupts = <0 152 0x0>; - clocks = <&gcc GSBI4_UART_CLK>, <&gcc GSBI4_H_CLK>; - clock-names = "core", "iface"; - }; - }; - - tcsr: syscon@1a400000 { - compatible = "qcom,apq8064-tcsr", "syscon"; - reg = <0x1a400000 0x100>; - }; diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.yaml new file mode 100644 index 000000000000..c33704333e49 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.yaml @@ -0,0 +1,132 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/qcom/qcom,gsbi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm General Serial Bus Interface (GSBI) + +maintainers: + - Andy Gross + - Bjorn Andersson + - Krzysztof Kozlowski + +description: + The GSBI controller is modeled as a node with zero or more child nodes, each + representing a serial sub-node device that is mux'd as part of the GSBI + configuration settings. The mode setting will govern the input/output mode + of the 4 GSBI IOs. + + A GSBI controller node can contain 0 or more child nodes representing serial + devices. These serial devices can be a QCOM UART, I2C controller, spi + controller, or some combination of aforementioned devices. + +properties: + compatible: + const: qcom,gsbi-v1.0.0 + + '#address-cells': + const: 1 + + cell-index: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + The GSBI index. + + clocks: + maxItems: 1 + + clock-names: + const: iface + + qcom,crci: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + CRCI MUX value for QUP CRCI ports. Please reference + include/dt-bindings/soc/qcom,gsbi.h for valid CRCI mux values. + + qcom,mode: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + MUX value for configuration of the serial interface. Please reference + include/dt-bindings/soc/qcom,gsbi.h for valid mux values. + + '#size-cells': + const: 1 + + syscon-tcsr: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle of TCSR syscon node.Required if child uses dma. + + ranges: true + + reg: + maxItems: 1 + +patternProperties: + "spi@[0-9a-f]+$": + type: object + $ref: /schemas/spi/qcom,spi-qup.yaml# + + "i2c@[0-9a-f]+$": + type: object + $ref: /schemas/i2c/qcom,i2c-qup.yaml# + + "serial@[0-9a-f]+$": + type: object + $ref: /schemas/serial/qcom,msm-uartdm.yaml# + +required: + - compatible + - cell-index + - clocks + - clock-names + - qcom,mode + - reg + +additionalProperties: false + +examples: + - | + #include + #include + #include + + gsbi@12440000 { + compatible = "qcom,gsbi-v1.0.0"; + reg = <0x12440000 0x100>; + cell-index = <1>; + clocks = <&gcc GSBI1_H_CLK>; + clock-names = "iface"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + syscon-tcsr = <&tcsr>; + qcom,mode = ; + + serial@12450000 { + compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; + reg = <0x12450000 0x100>, + <0x12400000 0x03>; + interrupts = <0 193 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&gcc GSBI1_UART_CLK>, <&gcc GSBI1_H_CLK>; + clock-names = "core", "iface"; + }; + + i2c@12460000 { + compatible = "qcom,i2c-qup-v1.1.1"; + reg = <0x12460000 0x1000>; + pinctrl-0 = <&i2c1_pins>; + pinctrl-1 = <&i2c1_pins_sleep>; + pinctrl-names = "default", "sleep"; + interrupts = <0 194 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&gcc GSBI1_QUP_CLK>, <&gcc GSBI1_H_CLK>; + clock-names = "core", "iface"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; /* UART chosen */ + }; + }; -- cgit v1.2.3 From ca1666463eec6a80724701bf58f33530f5642a2b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 1 Apr 2022 22:27:59 +0200 Subject: dt-bindings: qcom,smd-rpm: update maintainers (drop Kathiravan) Kathiravan's email bounces (585: Recipient address rejected: undeliverable address: No such user here) so switch maintainers to Andy and Bjorn (as Qualcomm platform maintainers). Signed-off-by: Krzysztof Kozlowski Acked-by: Mark Brown Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220401202759.191560-1-krzysztof.kozlowski@linaro.org --- .../devicetree/bindings/regulator/qcom,smd-rpm-regulator.yaml | 3 ++- Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.yaml index f052e03be402..8069d32cf899 100644 --- a/Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.yaml @@ -69,7 +69,8 @@ description: l12, l13, l14, l15, l16, l17, l18, l19, l20, l21, l22 maintainers: - - Kathiravan T + - Andy Gross + - Bjorn Andersson properties: compatible: diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml index b32457c2fc0b..d91b62eed5c6 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml @@ -25,7 +25,8 @@ description: | rpm_requests. maintainers: - - Kathiravan T + - Andy Gross + - Bjorn Andersson properties: compatible: -- cgit v1.2.3 From 20bb6c9de1b7e13f11d2ffe73686f4449c426807 Mon Sep 17 00:00:00 2001 From: Deepak Kumar Singh Date: Tue, 1 Mar 2022 20:48:32 +0530 Subject: soc: qcom: smem: map only partitions used by local HOST SMEM driver is IO mapping complete region and CPU is doing a speculative read into a partition where local HOST does not have permission resulting in a NOC error. Map only those partitions which are accessibly to local HOST. Signed-off-by: Deepak Kumar Singh Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/1646147913-15791-1-git-send-email-quic_deesin@quicinc.com --- drivers/soc/qcom/smem.c | 226 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 164 insertions(+), 62 deletions(-) diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index e2057d8f1eff..fcf8e40436e3 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -195,6 +195,20 @@ struct smem_partition_header { __le32 reserved[3]; }; +/** + * struct smem_partition - describes smem partition + * @virt_base: starting virtual address of partition + * @phys_base: starting physical address of partition + * @cacheline: alignment for "cached" entries + * @size: size of partition + */ +struct smem_partition { + void __iomem *virt_base; + phys_addr_t phys_base; + size_t cacheline; + size_t size; +}; + static const u8 SMEM_PART_MAGIC[] = { 0x24, 0x50, 0x52, 0x54 }; /** @@ -250,11 +264,9 @@ 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 + * @ptable: virtual base of partition table + * @global_partition: describes for global partition when in use + * @partitions: list of partitions of current processor/host * @item_count: max accepted item number * @socinfo: platform device pointer * @num_regions: number of @regions @@ -265,12 +277,11 @@ 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]; u32 item_count; struct platform_device *socinfo; + struct smem_ptable *ptable; + struct smem_partition global_partition; + struct smem_partition partitions[SMEM_HOST_COUNT]; unsigned num_regions; struct smem_region regions[]; @@ -348,14 +359,17 @@ static struct qcom_smem *__smem; #define HWSPINLOCK_TIMEOUT 1000 static int qcom_smem_alloc_private(struct qcom_smem *smem, - struct smem_partition_header *phdr, + struct smem_partition *part, unsigned item, size_t size) { struct smem_private_entry *hdr, *end; + struct smem_partition_header *phdr; size_t alloc_size; void *cached; + phdr = (struct smem_partition_header __force *)part->virt_base; + hdr = phdr_to_first_uncached_entry(phdr); end = phdr_to_last_uncached_entry(phdr); cached = phdr_to_last_cached_entry(phdr); @@ -442,7 +456,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; + struct smem_partition *part; unsigned long flags; int ret; @@ -464,12 +478,12 @@ int qcom_smem_alloc(unsigned host, unsigned item, size_t size) if (ret) return ret; - 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); + if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) { + part = &__smem->partitions[host]; + ret = qcom_smem_alloc_private(__smem, part, item, size); + } else if (__smem->global_partition.virt_base) { + part = &__smem->global_partition; + ret = qcom_smem_alloc_private(__smem, part, item, size); } else { ret = qcom_smem_alloc_global(__smem, item, size); } @@ -511,12 +525,14 @@ static void *qcom_smem_get_global(struct qcom_smem *smem, } static void *qcom_smem_get_private(struct qcom_smem *smem, - struct smem_partition_header *phdr, - size_t cacheline, + struct smem_partition *part, unsigned item, size_t *size) { struct smem_private_entry *e, *end; + struct smem_partition_header *phdr; + + phdr = (struct smem_partition_header __force *)part->virt_base; e = phdr_to_first_uncached_entry(phdr); end = phdr_to_last_uncached_entry(phdr); @@ -538,7 +554,7 @@ static void *qcom_smem_get_private(struct qcom_smem *smem, /* Item was not found in the uncached list, search the cached list */ - e = phdr_to_first_cached_entry(phdr, cacheline); + e = phdr_to_first_cached_entry(phdr, part->cacheline); end = phdr_to_last_cached_entry(phdr); while (e > end) { @@ -553,7 +569,7 @@ static void *qcom_smem_get_private(struct qcom_smem *smem, return cached_entry_to_item(e); } - e = cached_entry_next(e, cacheline); + e = cached_entry_next(e, part->cacheline); } return ERR_PTR(-ENOENT); @@ -576,9 +592,8 @@ invalid_canary: */ void *qcom_smem_get(unsigned host, unsigned item, size_t *size) { - struct smem_partition_header *phdr; + struct smem_partition *part; unsigned long flags; - size_t cacheln; int ret; void *ptr = ERR_PTR(-EPROBE_DEFER); @@ -594,14 +609,12 @@ 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]) { - 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); + if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) { + part = &__smem->partitions[host]; + ptr = qcom_smem_get_private(__smem, part, item, size); + } else if (__smem->global_partition.virt_base) { + part = &__smem->global_partition; + ptr = qcom_smem_get_private(__smem, part, item, size); } else { ptr = qcom_smem_get_global(__smem, item, size); } @@ -622,6 +635,7 @@ EXPORT_SYMBOL(qcom_smem_get); */ int qcom_smem_get_free_space(unsigned host) { + struct smem_partition *part; struct smem_partition_header *phdr; struct smem_header *header; unsigned ret; @@ -629,12 +643,14 @@ int qcom_smem_get_free_space(unsigned host) if (!__smem) return -EPROBE_DEFER; - if (host < SMEM_HOST_COUNT && __smem->partitions[host]) { - phdr = __smem->partitions[host]; + if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) { + part = &__smem->partitions[host]; + phdr = part->virt_base; ret = le32_to_cpu(phdr->offset_free_cached) - le32_to_cpu(phdr->offset_free_uncached); - } else if (__smem->global_partition) { - phdr = __smem->global_partition; + } else if (__smem->global_partition.virt_base) { + part = &__smem->global_partition; + phdr = part->virt_base; ret = le32_to_cpu(phdr->offset_free_cached) - le32_to_cpu(phdr->offset_free_uncached); } else { @@ -646,6 +662,11 @@ int qcom_smem_get_free_space(unsigned host) } EXPORT_SYMBOL(qcom_smem_get_free_space); +static bool addr_in_range(void __iomem *base, size_t size, void *addr) +{ + return base && (addr >= base && addr < base + size); +} + /** * qcom_smem_virt_to_phys() - return the physical address associated * with an smem item pointer (previously returned by qcom_smem_get() @@ -655,17 +676,36 @@ EXPORT_SYMBOL(qcom_smem_get_free_space); */ phys_addr_t qcom_smem_virt_to_phys(void *p) { - unsigned i; + struct smem_partition *part; + struct smem_region *area; + u64 offset; + u32 i; + + for (i = 0; i < SMEM_HOST_COUNT; i++) { + part = &__smem->partitions[i]; + + if (addr_in_range(part->virt_base, part->size, p)) { + offset = p - part->virt_base; + + return (phys_addr_t)part->phys_base + offset; + } + } + + part = &__smem->global_partition; + + if (addr_in_range(part->virt_base, part->size, p)) { + offset = p - part->virt_base; + + return (phys_addr_t)part->phys_base + offset; + } for (i = 0; i < __smem->num_regions; i++) { - struct smem_region *region = &__smem->regions[i]; + area = &__smem->regions[i]; - if (p < region->virt_base) - continue; - if (p < region->virt_base + region->size) { - u64 offset = p - region->virt_base; + if (addr_in_range(area->virt_base, area->size, p)) { + offset = p - area->virt_base; - return region->aux_base + offset; + return (phys_addr_t)area->aux_base + offset; } } @@ -689,7 +729,7 @@ static struct smem_ptable *qcom_smem_get_ptable(struct qcom_smem *smem) struct smem_ptable *ptable; u32 version; - ptable = smem->regions[0].virt_base + smem->regions[0].size - SZ_4K; + ptable = smem->ptable; if (memcmp(ptable->magic, SMEM_PTABLE_MAGIC, sizeof(ptable->magic))) return ERR_PTR(-ENOENT); @@ -728,9 +768,14 @@ qcom_smem_partition_header(struct qcom_smem *smem, struct smem_ptable_entry *entry, u16 host0, u16 host1) { struct smem_partition_header *header; + u32 phys_addr; u32 size; - header = smem->regions[0].virt_base + le32_to_cpu(entry->offset); + phys_addr = smem->regions[0].aux_base + le32_to_cpu(entry->offset); + header = devm_ioremap_wc(smem->dev, phys_addr, le32_to_cpu(entry->size)); + + if (!header) + return NULL; if (memcmp(header->magic, SMEM_PART_MAGIC, sizeof(header->magic))) { dev_err(smem->dev, "bad partition magic %4ph\n", header->magic); @@ -772,7 +817,7 @@ static int qcom_smem_set_global_partition(struct qcom_smem *smem) bool found = false; int i; - if (smem->global_partition) { + if (smem->global_partition.virt_base) { dev_err(smem->dev, "Already found the global partition\n"); return -EINVAL; } @@ -807,8 +852,11 @@ static int qcom_smem_set_global_partition(struct qcom_smem *smem) if (!header) return -EINVAL; - smem->global_partition = header; - smem->global_cacheline = le32_to_cpu(entry->cacheline); + smem->global_partition.virt_base = (void __iomem *)header; + smem->global_partition.phys_base = smem->regions[0].aux_base + + le32_to_cpu(entry->offset); + smem->global_partition.size = le32_to_cpu(entry->size); + smem->global_partition.cacheline = le32_to_cpu(entry->cacheline); return 0; } @@ -848,7 +896,7 @@ qcom_smem_enumerate_partitions(struct qcom_smem *smem, u16 local_host) return -EINVAL; } - if (smem->partitions[remote_host]) { + if (smem->partitions[remote_host].virt_base) { dev_err(smem->dev, "duplicate host %hu\n", remote_host); return -EINVAL; } @@ -857,13 +905,47 @@ qcom_smem_enumerate_partitions(struct qcom_smem *smem, u16 local_host) if (!header) return -EINVAL; - smem->partitions[remote_host] = header; - smem->cacheline[remote_host] = le32_to_cpu(entry->cacheline); + smem->partitions[remote_host].virt_base = (void __iomem *)header; + smem->partitions[remote_host].phys_base = smem->regions[0].aux_base + + le32_to_cpu(entry->offset); + smem->partitions[remote_host].size = le32_to_cpu(entry->size); + smem->partitions[remote_host].cacheline = le32_to_cpu(entry->cacheline); } return 0; } +static int qcom_smem_map_toc(struct qcom_smem *smem, struct smem_region *region) +{ + u32 ptable_start; + + /* map starting 4K for smem header */ + region->virt_base = devm_ioremap_wc(smem->dev, region->aux_base, SZ_4K); + ptable_start = region->aux_base + region->size - SZ_4K; + /* map last 4k for toc */ + smem->ptable = devm_ioremap_wc(smem->dev, ptable_start, SZ_4K); + + if (!region->virt_base || !smem->ptable) + return -ENOMEM; + + return 0; +} + +static int qcom_smem_map_global(struct qcom_smem *smem, u32 size) +{ + u32 phys_addr; + + phys_addr = smem->regions[0].aux_base; + + smem->regions[0].size = size; + smem->regions[0].virt_base = devm_ioremap_wc(smem->dev, phys_addr, size); + + if (!smem->regions[0].virt_base) + return -ENOMEM; + + return 0; +} + static int qcom_smem_resolve_mem(struct qcom_smem *smem, const char *name, struct smem_region *region) { @@ -894,10 +976,12 @@ static int qcom_smem_probe(struct platform_device *pdev) struct smem_header *header; struct reserved_mem *rmem; struct qcom_smem *smem; + unsigned long flags; size_t array_size; int num_regions; int hwlock_id; u32 version; + u32 size; int ret; int i; @@ -933,7 +1017,12 @@ static int qcom_smem_probe(struct platform_device *pdev) return ret; } - for (i = 0; i < num_regions; i++) { + + ret = qcom_smem_map_toc(smem, &smem->regions[0]); + if (ret) + return ret; + + for (i = 1; i < num_regions; i++) { smem->regions[i].virt_base = devm_ioremap_wc(&pdev->dev, smem->regions[i].aux_base, smem->regions[i].size); @@ -950,7 +1039,30 @@ static int qcom_smem_probe(struct platform_device *pdev) return -EINVAL; } + hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0); + if (hwlock_id < 0) { + if (hwlock_id != -EPROBE_DEFER) + dev_err(&pdev->dev, "failed to retrieve hwlock\n"); + return hwlock_id; + } + + smem->hwlock = hwspin_lock_request_specific(hwlock_id); + if (!smem->hwlock) + return -ENXIO; + + ret = hwspin_lock_timeout_irqsave(smem->hwlock, HWSPINLOCK_TIMEOUT, &flags); + if (ret) + return ret; + size = readl_relaxed(&header->available) + readl_relaxed(&header->free_offset); + hwspin_unlock_irqrestore(smem->hwlock, &flags); + version = qcom_smem_get_sbl_version(smem); + /* + * smem header mapping is required only in heap version scheme, so unmap + * it here. It will be remapped in qcom_smem_map_global() when whole + * partition is mapped again. + */ + devm_iounmap(smem->dev, smem->regions[0].virt_base); switch (version >> 16) { case SMEM_GLOBAL_PART_VERSION: ret = qcom_smem_set_global_partition(smem); @@ -959,6 +1071,7 @@ static int qcom_smem_probe(struct platform_device *pdev) smem->item_count = qcom_smem_get_item_count(smem); break; case SMEM_GLOBAL_HEAP_VERSION: + qcom_smem_map_global(smem, size); smem->item_count = SMEM_ITEM_COUNT; break; default: @@ -971,17 +1084,6 @@ static int qcom_smem_probe(struct platform_device *pdev) if (ret < 0 && ret != -ENOENT) return ret; - hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0); - if (hwlock_id < 0) { - if (hwlock_id != -EPROBE_DEFER) - dev_err(&pdev->dev, "failed to retrieve hwlock\n"); - return hwlock_id; - } - - smem->hwlock = hwspin_lock_request_specific(hwlock_id); - if (!smem->hwlock) - return -ENXIO; - __smem = smem; smem->socinfo = platform_device_register_data(&pdev->dev, "qcom-socinfo", -- cgit v1.2.3 From f1383348ad0fe0d568dfd37f2552b0b4f044dbe5 Mon Sep 17 00:00:00 2001 From: Deepak Kumar Singh Date: Tue, 1 Mar 2022 20:48:33 +0530 Subject: soc: qcom: smem: validate fields of shared structures Structures in shared memory that can be modified by remote processors may have untrusted values, they should be validated before use. Adding proper validation before using fields of shared structures. Signed-off-by: Deepak Kumar Singh Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/1646147913-15791-2-git-send-email-quic_deesin@quicinc.com --- drivers/soc/qcom/smem.c | 79 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 10 deletions(-) diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index fcf8e40436e3..3e95835653ea 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -367,13 +367,18 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem, struct smem_partition_header *phdr; size_t alloc_size; void *cached; + void *p_end; phdr = (struct smem_partition_header __force *)part->virt_base; + p_end = (void *)phdr + part->size; hdr = phdr_to_first_uncached_entry(phdr); end = phdr_to_last_uncached_entry(phdr); cached = phdr_to_last_cached_entry(phdr); + if (WARN_ON((void *)end > p_end || cached > p_end)) + return -EINVAL; + while (hdr < end) { if (hdr->canary != SMEM_PRIVATE_CANARY) goto bad_canary; @@ -383,6 +388,9 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem, hdr = uncached_entry_next(hdr); } + if (WARN_ON((void *)hdr > p_end)) + return -EINVAL; + /* Check that we don't grow into the cached region */ alloc_size = sizeof(*hdr) + ALIGN(size, 8); if ((void *)hdr + alloc_size > cached) { @@ -501,6 +509,8 @@ static void *qcom_smem_get_global(struct qcom_smem *smem, struct smem_header *header; struct smem_region *region; struct smem_global_entry *entry; + u64 entry_offset; + u32 e_size; u32 aux_base; unsigned i; @@ -515,9 +525,16 @@ static void *qcom_smem_get_global(struct qcom_smem *smem, region = &smem->regions[i]; if ((u32)region->aux_base == aux_base || !aux_base) { + e_size = le32_to_cpu(entry->size); + entry_offset = le32_to_cpu(entry->offset); + + if (WARN_ON(e_size + entry_offset > region->size)) + return ERR_PTR(-EINVAL); + if (size != NULL) - *size = le32_to_cpu(entry->size); - return region->virt_base + le32_to_cpu(entry->offset); + *size = e_size; + + return region->virt_base + entry_offset; } } @@ -531,8 +548,12 @@ static void *qcom_smem_get_private(struct qcom_smem *smem, { struct smem_private_entry *e, *end; struct smem_partition_header *phdr; + void *item_ptr, *p_end; + u32 padding_data; + u32 e_size; phdr = (struct smem_partition_header __force *)part->virt_base; + p_end = (void *)phdr + part->size; e = phdr_to_first_uncached_entry(phdr); end = phdr_to_last_uncached_entry(phdr); @@ -542,36 +563,65 @@ static void *qcom_smem_get_private(struct qcom_smem *smem, 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); + if (size != NULL) { + e_size = le32_to_cpu(e->size); + padding_data = le16_to_cpu(e->padding_data); + + if (WARN_ON(e_size > part->size || padding_data > e_size)) + return ERR_PTR(-EINVAL); + + *size = e_size - padding_data; + } + + item_ptr = uncached_entry_to_item(e); + if (WARN_ON(item_ptr > p_end)) + return ERR_PTR(-EINVAL); - return uncached_entry_to_item(e); + return item_ptr; } e = uncached_entry_next(e); } + if (WARN_ON((void *)e > p_end)) + return ERR_PTR(-EINVAL); + /* Item was not found in the uncached list, search the cached list */ e = phdr_to_first_cached_entry(phdr, part->cacheline); end = phdr_to_last_cached_entry(phdr); + if (WARN_ON((void *)e < (void *)phdr || (void *)end > p_end)) + return ERR_PTR(-EINVAL); + 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); + if (size != NULL) { + e_size = le32_to_cpu(e->size); + padding_data = le16_to_cpu(e->padding_data); + + if (WARN_ON(e_size > part->size || padding_data > e_size)) + return ERR_PTR(-EINVAL); + + *size = e_size - padding_data; + } - return cached_entry_to_item(e); + item_ptr = cached_entry_to_item(e); + if (WARN_ON(item_ptr < (void *)phdr)) + return ERR_PTR(-EINVAL); + + return item_ptr; } e = cached_entry_next(e, part->cacheline); } + if (WARN_ON((void *)e < (void *)phdr)) + return ERR_PTR(-EINVAL); + return ERR_PTR(-ENOENT); invalid_canary: @@ -648,14 +698,23 @@ int qcom_smem_get_free_space(unsigned host) phdr = part->virt_base; ret = le32_to_cpu(phdr->offset_free_cached) - le32_to_cpu(phdr->offset_free_uncached); + + if (ret > le32_to_cpu(part->size)) + return -EINVAL; } else if (__smem->global_partition.virt_base) { part = &__smem->global_partition; phdr = part->virt_base; ret = le32_to_cpu(phdr->offset_free_cached) - le32_to_cpu(phdr->offset_free_uncached); + + if (ret > le32_to_cpu(part->size)) + return -EINVAL; } else { header = __smem->regions[0].virt_base; ret = le32_to_cpu(header->available); + + if (ret > __smem->regions[0].size) + return -EINVAL; } return ret; -- cgit v1.2.3 From a175c6faaa36b1a4c7d945bd3fb0c354c9323623 Mon Sep 17 00:00:00 2001 From: Adam Skladowski Date: Mon, 7 Mar 2022 20:17:24 +0100 Subject: dt-bindings: firmware: qcom-scm: Document msm8976 bindings SCM driver on MSM8976 requires 3 clocks. Signed-off-by: Adam Skladowski Acked-by: Krzysztof Kozlowski Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220307191726.10869-4-a39.skl@gmail.com --- Documentation/devicetree/bindings/firmware/qcom,scm.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/firmware/qcom,scm.txt b/Documentation/devicetree/bindings/firmware/qcom,scm.txt index b1cd4ad1889a..0f4e5ab26477 100644 --- a/Documentation/devicetree/bindings/firmware/qcom,scm.txt +++ b/Documentation/devicetree/bindings/firmware/qcom,scm.txt @@ -19,6 +19,7 @@ Required properties: * "qcom,scm-msm8953" * "qcom,scm-msm8960" * "qcom,scm-msm8974" + * "qcom,scm-msm8976" * "qcom,scm-msm8994" * "qcom,scm-msm8996" * "qcom,scm-msm8998" @@ -37,7 +38,7 @@ Required properties: * core clock required for "qcom,scm-apq8064", "qcom,scm-msm8660" and "qcom,scm-msm8960" * core, iface and bus clocks required for "qcom,scm-apq8084", - "qcom,scm-msm8916", "qcom,scm-msm8953" and "qcom,scm-msm8974" + "qcom,scm-msm8916", "qcom,scm-msm8953", "qcom,scm-msm8974" and "qcom,scm-msm8976" - clock-names: Must contain "core" for the core clock, "iface" for the interface clock and "bus" for the bus clock per the requirements of the compatible. - qcom,dload-mode: phandle to the TCSR hardware block and offset of the -- cgit v1.2.3 From 34128350b838840b17a1770269158dc0430177d3 Mon Sep 17 00:00:00 2001 From: Adam Skladowski Date: Mon, 7 Mar 2022 20:17:25 +0100 Subject: firmware: qcom_scm: Add compatible for MSM8976 SoC Add compatible for SCM on MSM8976. Signed-off-by: Adam Skladowski Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220307191726.10869-5-a39.skl@gmail.com --- drivers/firmware/qcom_scm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 491bbf70c94a..3163660fa8e2 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -1379,6 +1379,10 @@ static const struct of_device_id qcom_scm_dt_match[] = { SCM_HAS_IFACE_CLK | SCM_HAS_BUS_CLK) }, + { .compatible = "qcom,scm-msm8976", .data = (void *)(SCM_HAS_CORE_CLK | + SCM_HAS_IFACE_CLK | + SCM_HAS_BUS_CLK) + }, { .compatible = "qcom,scm-msm8994" }, { .compatible = "qcom,scm-msm8996" }, { .compatible = "qcom,scm" }, -- cgit v1.2.3 From 375eed5f51a84dee4cf933a2ec0710bab9a8df69 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 1 Apr 2022 22:10:33 +0200 Subject: dt-bindings: soc: qcom,smd: convert to dtschema Convert the Qualcomm Shared Memory Driver bindings to DT Schema. Changes against original bindings: enforce only specific names of child nodes, instead of any names. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220401201035.189106-9-krzysztof.kozlowski@linaro.org --- .../bindings/regulator/qcom,smd-rpm-regulator.yaml | 2 +- .../devicetree/bindings/remoteproc/qcom,q6v5.txt | 2 +- .../bindings/remoteproc/qcom,wcnss-pil.txt | 2 +- .../devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml | 4 +- .../devicetree/bindings/soc/qcom/qcom,smd.txt | 98 --------------- .../devicetree/bindings/soc/qcom/qcom,smd.yaml | 137 +++++++++++++++++++++ 6 files changed, 142 insertions(+), 103 deletions(-) delete mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,smd.txt create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,smd.yaml diff --git a/Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.yaml index 8069d32cf899..6a9a7eed466f 100644 --- a/Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.yaml @@ -12,7 +12,7 @@ description: resides as a subnode of the SMD. As such, the SMD-RPM regulator requires that the SMD and RPM nodes be present. - Please refer to Documentation/devicetree/bindings/soc/qcom/qcom,smd.txt for + Please refer to Documentation/devicetree/bindings/soc/qcom/qcom,smd.yaml for information pertaining to the SMD node. Please refer to Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt index 8f1507052afd..b677900b3aae 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt +++ b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt @@ -250,7 +250,7 @@ the memory regions used by the Hexagon firmware. Each sub-node must contain: The Hexagon node may also have an subnode named either "smd-edge" or "glink-edge" that describes the communication edge, channels and devices -related to the Hexagon. See ../soc/qcom/qcom,smd.txt and +related to the Hexagon. See ../soc/qcom/qcom,smd.yaml and ../soc/qcom/qcom,glink.txt for details on how to describe these. = EXAMPLE diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,wcnss-pil.txt b/Documentation/devicetree/bindings/remoteproc/qcom,wcnss-pil.txt index a83080b8905c..ac423f4c3f1b 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,wcnss-pil.txt +++ b/Documentation/devicetree/bindings/remoteproc/qcom,wcnss-pil.txt @@ -111,7 +111,7 @@ and its resource dependencies. It is described by the following properties: The wcnss node can also have an subnode named "smd-edge" that describes the SMD edge, channels and devices related to the WCNSS. -See ../soc/qcom/qcom,smd.txt for details on how to describe the SMD edge. +See ../soc/qcom/qcom,smd.yaml for details on how to describe the SMD edge. = EXAMPLE The following example describes the resources needed to boot control the WCNSS, diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml index d91b62eed5c6..c4ca384be196 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml @@ -12,7 +12,7 @@ description: | to vote for state of the system resources, such as clocks, regulators and bus frequencies. - The SMD information for the RPM edge should be filled out. See qcom,smd.txt + The SMD information for the RPM edge should be filled out. See qcom,smd.yaml for the required edge properties. All SMD related properties will reside within the RPM node itself. @@ -83,7 +83,7 @@ examples: qcom,ipc = <&apcs 8 0>; qcom,smd-edge = <15>; - rpm_requests { + rpm-requests { compatible = "qcom,rpm-msm8974"; qcom,smd-channels = "rpm_requests"; diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smd.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,smd.txt deleted file mode 100644 index 234ae2256501..000000000000 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,smd.txt +++ /dev/null @@ -1,98 +0,0 @@ -Qualcomm Shared Memory Driver (SMD) binding - -This binding describes the Qualcomm Shared Memory Driver, a fifo based -communication channel for sending data between the various subsystems in -Qualcomm platforms. - -- compatible: - Usage: required - Value type: - Definition: must be "qcom,smd" - -= EDGES - -Each subnode of the SMD node represents a remote subsystem or a remote -processor of some sort - or in SMD language an "edge". The name of the edges -are not important. -The edge is described by the following properties: - -- interrupts: - Usage: required - Value type: - Definition: should specify the IRQ used by the remote processor to - signal this processor about communication related updates - -- mboxes: - Usage: required - Value type: - Definition: reference to the associated doorbell in APCS, as described - in mailbox/mailbox.txt - -- qcom,ipc: - Usage: required, unless mboxes is specified - Value type: - Definition: three entries specifying the outgoing ipc bit used for - signaling the remote processor: - - phandle to a syscon node representing the apcs registers - - u32 representing offset to the register within the syscon - - u32 representing the ipc bit within the register - -- qcom,smd-edge: - Usage: required - Value type: - Definition: the identifier of the remote processor in the smd channel - allocation table - -- qcom,remote-pid: - Usage: optional - Value type: - Definition: the identifier for the remote processor as known by the rest - of the system. - -- label: - Usage: optional - Value type: - Definition: name of the edge, used for debugging and identification - purposes. The node name will be used if this is not - present. - -= SMD DEVICES - -In turn, subnodes of the "edges" represent devices tied to SMD channels on that -"edge". The names of the devices are not important. The properties of these -nodes are defined by the individual bindings for the SMD devices - but must -contain the following property: - -- qcom,smd-channels: - Usage: required - Value type: - Definition: a list of channels tied to this device, used for matching - the device to channels - -= EXAMPLE - -The following example represents a smd node, with one edge representing the -"rpm" subsystem. For the "rpm" subsystem we have a device tied to the -"rpm_request" channel. - - apcs: syscon@f9011000 { - compatible = "syscon"; - reg = <0xf9011000 0x1000>; - }; - - smd { - compatible = "qcom,smd"; - - rpm { - interrupts = <0 168 1>; - qcom,ipc = <&apcs 8 0>; - qcom,smd-edge = <15>; - - rpm_requests { - compatible = "qcom,rpm-msm8974"; - qcom,smd-channels = "rpm_requests"; - - ... - }; - }; - }; diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smd.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,smd.yaml new file mode 100644 index 000000000000..e6f9ffa1c0ea --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smd.yaml @@ -0,0 +1,137 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/qcom/qcom,smd.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Shared Memory Driver + +maintainers: + - Andy Gross + - Bjorn Andersson + - Krzysztof Kozlowski + +description: + The Qualcomm Shared Memory Driver is a FIFO based communication channel for + sending data between the various subsystems in Qualcomm platforms. + +properties: + compatible: + const: qcom,smd + +patternProperties: + "^.*-edge|rpm$": + type: object + description: + Each subnode of the SMD node represents a remote subsystem or a remote + processor of some sort - or in SMD language an "edge". The name of the + edges are not important. + + properties: + interrupts: + maxItems: 1 + + label: + $ref: /schemas/types.yaml#/definitions/string + description: + Name of the edge, used for debugging and identification purposes. The + node name will be used if this is not present. + + mboxes: + maxItems: 1 + description: + Reference to the mailbox representing the outgoing doorbell in APCS for + this client. + + qcom,ipc: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to a syscon node representing the APCS registers + - description: u32 representing offset to the register within the syscon + - description: u32 representing the ipc bit within the register + description: + Three entries specifying the outgoing ipc bit used for signaling the + remote processor. + + qcom,smd-edge: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + The identifier of the remote processor in the smd channel allocation + table. + + qcom,remote-pid: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + The identifier for the remote processor as known by the rest of the + system. + + # Binding for edge subnodes is not complete + patternProperties: + "^rpm-requests$": + type: object + description: + In turn, subnodes of the "edges" represent devices tied to SMD + channels on that "edge". The names of the devices are not + important. The properties of these nodes are defined by the + individual bindings for the SMD devices. + + properties: + qcom,smd-channels: + $ref: /schemas/types.yaml#/definitions/string-array + minItems: 1 + maxItems: 32 + description: + A list of channels tied to this device, used for matching the + device to channels. + + required: + - compatible + - qcom,smd-channels + + additionalProperties: true + + required: + - interrupts + - qcom,smd-edge + + oneOf: + - required: + - mboxes + - required: + - qcom,ipc + + additionalProperties: false + +required: + - compatible + +additionalProperties: false + +examples: + # The following example represents a smd node, with one edge representing the + # "rpm" subsystem. For the "rpm" subsystem we have a device tied to the + # "rpm_request" channel. + - | + #include + + shared-memory { + compatible = "qcom,smd"; + + rpm { + interrupts = ; + qcom,ipc = <&apcs 8 0>; + qcom,smd-edge = <15>; + + rpm-requests { + compatible = "qcom,rpm-msm8974"; + qcom,smd-channels = "rpm_requests"; + + clock-controller { + compatible = "qcom,rpmcc-msm8974", "qcom,rpmcc"; + #clock-cells = <1>; + }; + + }; + }; + }; -- cgit v1.2.3 From 7b5d44212076e7474bc8d86bcf38829992a14cc2 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 4 Apr 2022 08:40:17 +0200 Subject: dt-bindings: qcom: qcom,geni-se: refer to dtschema for SPI After adding DT schema for the SPI controller, the Qualcomm GENI Serial Engine QUP Wrapper Controller bindings can reference it directly for full schema validation. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Kuldeep Singh Reviewed-by: Rob Herring Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220404064017.68634-2-krzysztof.kozlowski@linaro.org --- .../devicetree/bindings/soc/qcom/qcom,geni-se.yaml | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.yaml index 95fcb43675d6..1ca081b1664e 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.yaml @@ -103,27 +103,7 @@ patternProperties: supports up to 50MHz, up to four chip selects, programmable data path from 4 bits to 32 bits and numerous protocol variants. - $ref: /schemas/spi/spi-controller.yaml# - - properties: - compatible: - enum: - - qcom,geni-spi - - interrupts: - maxItems: 1 - - "#address-cells": - const: 1 - - "#size-cells": - const: 0 - - required: - - compatible - - interrupts - - "#address-cells" - - "#size-cells" + $ref: /schemas/spi/qcom,spi-geni-qcom.yaml# "i2c@[0-9a-f]+$": type: object -- cgit v1.2.3 From 30c375a7f6bd2b65243b58cf0aa39791978f2b0b Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Wed, 16 Mar 2022 13:28:31 +0100 Subject: optee: rename rpc_arg_count to rpc_param_count Renames the field rpc_arg_count in struct optee to rpc_param_count. Function parameter names and local variables are also renamed to match. Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- drivers/tee/optee/call.c | 6 +++--- drivers/tee/optee/ffa_abi.c | 10 +++++----- drivers/tee/optee/optee_private.h | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index bd49ec934060..a9a237d20c61 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -113,12 +113,12 @@ struct tee_shm *optee_get_msg_arg(struct tee_context *ctx, size_t num_params, struct optee_msg_arg *ma; /* - * rpc_arg_count is set to the number of allocated parameters in + * rpc_param_count is set to the number of allocated parameters in * the RPC argument struct if a second MSG arg struct is expected. * The second arg struct will then be used for RPC. */ - if (optee->rpc_arg_count) - sz += OPTEE_MSG_GET_ARG_SIZE(optee->rpc_arg_count); + if (optee->rpc_param_count) + sz += OPTEE_MSG_GET_ARG_SIZE(optee->rpc_param_count); shm = tee_shm_alloc_priv_buf(ctx, sz); if (IS_ERR(shm)) diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c index a5eb4ef46971..7686f7020616 100644 --- a/drivers/tee/optee/ffa_abi.c +++ b/drivers/tee/optee/ffa_abi.c @@ -678,7 +678,7 @@ static bool optee_ffa_api_is_compatbile(struct ffa_device *ffa_dev, static bool optee_ffa_exchange_caps(struct ffa_device *ffa_dev, const struct ffa_dev_ops *ops, - unsigned int *rpc_arg_count) + unsigned int *rpc_param_count) { struct ffa_send_direct_data data = { OPTEE_FFA_EXCHANGE_CAPABILITIES }; int rc; @@ -693,7 +693,7 @@ static bool optee_ffa_exchange_caps(struct ffa_device *ffa_dev, return false; } - *rpc_arg_count = (u8)data.data1; + *rpc_param_count = (u8)data.data1; return true; } @@ -772,7 +772,7 @@ static void optee_ffa_remove(struct ffa_device *ffa_dev) static int optee_ffa_probe(struct ffa_device *ffa_dev) { const struct ffa_dev_ops *ffa_ops; - unsigned int rpc_arg_count; + unsigned int rpc_param_count; struct tee_shm_pool *pool; struct tee_device *teedev; struct tee_context *ctx; @@ -788,7 +788,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev) if (!optee_ffa_api_is_compatbile(ffa_dev, ffa_ops)) return -EINVAL; - if (!optee_ffa_exchange_caps(ffa_dev, ffa_ops, &rpc_arg_count)) + if (!optee_ffa_exchange_caps(ffa_dev, ffa_ops, &rpc_param_count)) return -EINVAL; optee = kzalloc(sizeof(*optee), GFP_KERNEL); @@ -805,7 +805,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev) optee->ops = &optee_ffa_ops; optee->ffa.ffa_dev = ffa_dev; optee->ffa.ffa_ops = ffa_ops; - optee->rpc_arg_count = rpc_arg_count; + optee->rpc_param_count = rpc_param_count; teedev = tee_device_alloc(&optee_ffa_clnt_desc, NULL, optee->pool, optee); diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index e77765c78878..e80c5d9b62ec 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -143,7 +143,7 @@ struct optee_ops { * @notif: notification synchronization struct * @supp: supplicant synchronization struct for RPC to supplicant * @pool: shared memory pool - * @rpc_arg_count: If > 0 number of RPC parameters to make room for + * @rpc_param_count: If > 0 number of RPC parameters to make room for * @scan_bus_done flag if device registation was already done. * @scan_bus_wq workqueue to scan optee bus and register optee drivers * @scan_bus_work workq to scan optee bus and register optee drivers @@ -161,7 +161,7 @@ struct optee { struct optee_notif notif; struct optee_supp supp; struct tee_shm_pool *pool; - unsigned int rpc_arg_count; + unsigned int rpc_param_count; bool scan_bus_done; struct workqueue_struct *scan_bus_wq; struct work_struct scan_bus_work; -- cgit v1.2.3 From ed8faf6c8f8c1f7e9ee88342a915ce5f09ca9736 Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Wed, 3 Nov 2021 11:42:30 +0100 Subject: optee: add OPTEE_SMC_CALL_WITH_RPC_ARG and OPTEE_SMC_CALL_WITH_REGD_ARG Adds OPTEE_SMC_CALL_WITH_RPC_ARG and OPTEE_SMC_CALL_WITH_REGD_ARG where the struct optee_msg_arg to be used for RPC is appended in the memory following the normal argument struct optee_msg_arg. This is an optimization to avoid caching the RPC argument struct while still maintaining similar performance as if it was cached. OPTEE_SMC_CALL_WITH_REGD_ARG optimized one step further by using a registered shared memory object instead. It's in other aspects identical to OPTEE_SMC_CALL_WITH_RPC_ARG. The presence of OPTEE_SMC_CALL_WITH_RPC_ARG and OPTEE_SMC_CALL_WITH_REGD_ARG is indicated by the new OPTEE_SMC_SEC_CAP_RPC_ARG bit returned by OPTEE_SMC_EXCHANGE_CAPABILITIES. OPTEE_SMC_EXCHANGE_CAPABILITIES also reports the number of arguments that the RPC argument struct must have room for. OPTEE_SMC_CALL_WITH_RPC_ARG and OPTEE_SMC_CALL_WITH_ARG can be used interleaved with difference that when OPTEE_SMC_CALL_WITH_RPC_ARG is used the RPC argument struct to be used is the one appended to the normal argument struct. The same is true for OPTEE_SMC_CALL_WITH_REGD_ARG. Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- drivers/tee/optee/call.c | 2 +- drivers/tee/optee/optee_smc.h | 48 ++++++++++++++++++--- drivers/tee/optee/smc_abi.c | 99 ++++++++++++++++++++++++++++++++----------- 3 files changed, 117 insertions(+), 32 deletions(-) diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index a9a237d20c61..58ac15c02818 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -130,7 +130,7 @@ struct tee_shm *optee_get_msg_arg(struct tee_context *ctx, size_t num_params, return (void *)ma; } - memset(ma, 0, OPTEE_MSG_GET_ARG_SIZE(num_params)); + memset(ma, 0, sz); ma->num_params = num_params; *msg_arg = ma; diff --git a/drivers/tee/optee/optee_smc.h b/drivers/tee/optee/optee_smc.h index d44a6ae994f8..c60896cf71cb 100644 --- a/drivers/tee/optee/optee_smc.h +++ b/drivers/tee/optee/optee_smc.h @@ -107,14 +107,22 @@ struct optee_smc_call_get_os_revision_result { /* * Call with struct optee_msg_arg as argument * - * When calling this function normal world has a few responsibilities: + * When called with OPTEE_SMC_CALL_WITH_RPC_ARG or + * OPTEE_SMC_CALL_WITH_REGD_ARG in a0 there is one RPC struct optee_msg_arg + * following after the first struct optee_msg_arg. The RPC struct + * optee_msg_arg has reserved space for the number of RPC parameters as + * returned by OPTEE_SMC_EXCHANGE_CAPABILITIES. + * + * When calling these functions, normal world has a few responsibilities: * 1. It must be able to handle eventual RPCs * 2. Non-secure interrupts should not be masked * 3. If asynchronous notifications has been negotiated successfully, then - * asynchronous notifications should be unmasked during this call. + * the interrupt for asynchronous notifications should be unmasked + * during this call. * - * Call register usage: - * a0 SMC Function ID, OPTEE_SMC*CALL_WITH_ARG + * Call register usage, OPTEE_SMC_CALL_WITH_ARG and + * OPTEE_SMC_CALL_WITH_RPC_ARG: + * a0 SMC Function ID, OPTEE_SMC_CALL_WITH_ARG or OPTEE_SMC_CALL_WITH_RPC_ARG * a1 Upper 32 bits of a 64-bit physical pointer to a struct optee_msg_arg * a2 Lower 32 bits of a 64-bit physical pointer to a struct optee_msg_arg * a3 Cache settings, not used if physical pointer is in a predefined shared @@ -122,6 +130,15 @@ struct optee_smc_call_get_os_revision_result { * a4-6 Not used * a7 Hypervisor Client ID register * + * Call register usage, OPTEE_SMC_CALL_WITH_REGD_ARG: + * a0 SMC Function ID, OPTEE_SMC_CALL_WITH_REGD_ARG + * a1 Upper 32 bits of a 64-bit shared memory cookie + * a2 Lower 32 bits of a 64-bit shared memory cookie + * a3 Offset of the struct optee_msg_arg in the shared memory with the + * supplied cookie + * a4-6 Not used + * a7 Hypervisor Client ID register + * * Normal return register usage: * a0 Return value, OPTEE_SMC_RETURN_* * a1-3 Not used @@ -154,6 +171,10 @@ struct optee_smc_call_get_os_revision_result { #define OPTEE_SMC_FUNCID_CALL_WITH_ARG OPTEE_MSG_FUNCID_CALL_WITH_ARG #define OPTEE_SMC_CALL_WITH_ARG \ OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_CALL_WITH_ARG) +#define OPTEE_SMC_CALL_WITH_RPC_ARG \ + OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_CALL_WITH_RPC_ARG) +#define OPTEE_SMC_CALL_WITH_REGD_ARG \ + OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_CALL_WITH_REGD_ARG) /* * Get Shared Memory Config @@ -202,7 +223,11 @@ struct optee_smc_get_shm_config_result { * a0 OPTEE_SMC_RETURN_OK * a1 bitfield of secure world capabilities OPTEE_SMC_SEC_CAP_* * a2 The maximum secure world notification number - * a3-7 Preserved + * a3 Bit[7:0]: Number of parameters needed for RPC to be supplied + * as the second MSG arg struct for + * OPTEE_SMC_CALL_WITH_ARG + * Bit[31:8]: Reserved (MBZ) + * a4-7 Preserved * * Error return register usage: * a0 OPTEE_SMC_RETURN_ENOTAVAIL, can't use the capabilities from normal world @@ -227,6 +252,8 @@ struct optee_smc_get_shm_config_result { #define OPTEE_SMC_SEC_CAP_MEMREF_NULL BIT(4) /* Secure world supports asynchronous notification of normal world */ #define OPTEE_SMC_SEC_CAP_ASYNC_NOTIF BIT(5) +/* Secure world supports pre-allocating RPC arg struct */ +#define OPTEE_SMC_SEC_CAP_RPC_ARG BIT(6) #define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES 9 #define OPTEE_SMC_EXCHANGE_CAPABILITIES \ @@ -236,7 +263,7 @@ struct optee_smc_exchange_capabilities_result { unsigned long status; unsigned long capabilities; unsigned long max_notif_value; - unsigned long reserved0; + unsigned long data; }; /* @@ -358,6 +385,9 @@ struct optee_smc_disable_shm_cache_result { * should be called until all pended values have been retrieved. When a * value is retrieved, it's cleared from the record in secure world. * + * It is expected that this function is called from an interrupt handler + * in normal world. + * * Call requests usage: * a0 SMC Function ID, OPTEE_SMC_GET_ASYNC_NOTIF_VALUE * a1-6 Not used @@ -390,6 +420,12 @@ struct optee_smc_disable_shm_cache_result { #define OPTEE_SMC_GET_ASYNC_NOTIF_VALUE \ OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_ASYNC_NOTIF_VALUE) +/* See OPTEE_SMC_CALL_WITH_RPC_ARG above */ +#define OPTEE_SMC_FUNCID_CALL_WITH_RPC_ARG 18 + +/* See OPTEE_SMC_CALL_WITH_REGD_ARG above */ +#define OPTEE_SMC_FUNCID_CALL_WITH_REGD_ARG 19 + /* * Resume from RPC (for example after processing a foreign interrupt) * diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c index 67b7f7d2ff27..b258d7306042 100644 --- a/drivers/tee/optee/smc_abi.c +++ b/drivers/tee/optee/smc_abi.c @@ -732,16 +732,9 @@ static void optee_rpc_finalize_call(struct optee_call_ctx *call_ctx) } static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee, - struct tee_shm *shm, + struct optee_msg_arg *arg, struct optee_call_ctx *call_ctx) { - struct optee_msg_arg *arg; - - arg = tee_shm_get_va(shm, 0); - if (IS_ERR(arg)) { - pr_err("%s: tee_shm_get_va %p failed\n", __func__, shm); - return; - } switch (arg->cmd) { case OPTEE_RPC_CMD_SHM_ALLOC: @@ -765,11 +758,13 @@ static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee, * Result of RPC is written back into @param. */ static void optee_handle_rpc(struct tee_context *ctx, + struct optee_msg_arg *rpc_arg, struct optee_rpc_param *param, struct optee_call_ctx *call_ctx) { struct tee_device *teedev = ctx->teedev; struct optee *optee = tee_get_drvdata(teedev); + struct optee_msg_arg *arg; struct tee_shm *shm; phys_addr_t pa; @@ -801,8 +796,19 @@ static void optee_handle_rpc(struct tee_context *ctx, */ break; case OPTEE_SMC_RPC_FUNC_CMD: - shm = reg_pair_to_ptr(param->a1, param->a2); - handle_rpc_func_cmd(ctx, optee, shm, call_ctx); + if (rpc_arg) { + arg = rpc_arg; + } else { + shm = reg_pair_to_ptr(param->a1, param->a2); + arg = tee_shm_get_va(shm, 0); + if (IS_ERR(arg)) { + pr_err("%s: tee_shm_get_va %p failed\n", + __func__, shm); + break; + } + } + + handle_rpc_func_cmd(ctx, optee, arg, call_ctx); break; default: pr_warn("Unknown RPC func 0x%x\n", @@ -816,7 +822,7 @@ static void optee_handle_rpc(struct tee_context *ctx, /** * optee_smc_do_call_with_arg() - Do an SMC to OP-TEE in secure world * @ctx: calling context - * @arg: shared memory holding the message to pass to secure world + * @shm: shared memory holding the message to pass to secure world * * Does and SMC to OP-TEE in secure world and handles eventual resulting * Remote Procedure Calls (RPC) from OP-TEE. @@ -824,21 +830,46 @@ static void optee_handle_rpc(struct tee_context *ctx, * Returns return code from secure world, 0 is OK */ static int optee_smc_do_call_with_arg(struct tee_context *ctx, - struct tee_shm *arg) + struct tee_shm *shm) { struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_call_waiter w; struct optee_rpc_param param = { }; struct optee_call_ctx call_ctx = { }; - phys_addr_t parg; + struct optee_msg_arg *rpc_arg = NULL; int rc; - rc = tee_shm_get_pa(arg, 0, &parg); - if (rc) - return rc; + if (optee->rpc_param_count) { + struct optee_msg_arg *arg; + unsigned int rpc_arg_offs; + + arg = tee_shm_get_va(shm, 0); + if (IS_ERR(arg)) + return PTR_ERR(arg); - param.a0 = OPTEE_SMC_CALL_WITH_ARG; - reg_pair_from_64(¶m.a1, ¶m.a2, parg); + rpc_arg_offs = OPTEE_MSG_GET_ARG_SIZE(arg->num_params); + rpc_arg = tee_shm_get_va(shm, rpc_arg_offs); + if (IS_ERR(arg)) + return PTR_ERR(arg); + } + + if (rpc_arg && tee_shm_is_dynamic(shm)) { + param.a0 = OPTEE_SMC_CALL_WITH_REGD_ARG; + reg_pair_from_64(¶m.a1, ¶m.a2, (u_long)shm); + param.a3 = 0; + } else { + phys_addr_t parg; + + rc = tee_shm_get_pa(shm, 0, &parg); + if (rc) + return rc; + + if (rpc_arg) + param.a0 = OPTEE_SMC_CALL_WITH_RPC_ARG; + else + param.a0 = OPTEE_SMC_CALL_WITH_ARG; + reg_pair_from_64(¶m.a1, ¶m.a2, parg); + } /* Initialize waiter */ optee_cq_wait_init(&optee->call_queue, &w); while (true) { @@ -862,7 +893,7 @@ static int optee_smc_do_call_with_arg(struct tee_context *ctx, param.a1 = res.a1; param.a2 = res.a2; param.a3 = res.a3; - optee_handle_rpc(ctx, ¶m, &call_ctx); + optee_handle_rpc(ctx, rpc_arg, ¶m, &call_ctx); } else { rc = res.a0; break; @@ -1118,7 +1149,8 @@ static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn) } static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, - u32 *sec_caps, u32 *max_notif_value) + u32 *sec_caps, u32 *max_notif_value, + unsigned int *rpc_param_count) { union { struct arm_smccc_res smccc; @@ -1145,6 +1177,10 @@ static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, *max_notif_value = res.result.max_notif_value; else *max_notif_value = OPTEE_DEFAULT_MAX_NOTIF_VALUE; + if (*sec_caps & OPTEE_SMC_SEC_CAP_RPC_ARG) + *rpc_param_count = (u8)res.result.data; + else + *rpc_param_count = 0; return true; } @@ -1251,7 +1287,8 @@ static int optee_smc_remove(struct platform_device *pdev) * reference counters and also avoid wild pointers in secure world * into the old shared memory range. */ - optee_disable_shm_cache(optee); + if (!optee->rpc_param_count) + optee_disable_shm_cache(optee); optee_smc_notif_uninit_irq(optee); @@ -1274,7 +1311,10 @@ static int optee_smc_remove(struct platform_device *pdev) */ static void optee_shutdown(struct platform_device *pdev) { - optee_disable_shm_cache(platform_get_drvdata(pdev)); + struct optee *optee = platform_get_drvdata(pdev); + + if (!optee->rpc_param_count) + optee_disable_shm_cache(optee); } static int optee_probe(struct platform_device *pdev) @@ -1283,6 +1323,7 @@ static int optee_probe(struct platform_device *pdev) struct tee_shm_pool *pool = ERR_PTR(-EINVAL); struct optee *optee = NULL; void *memremaped_shm = NULL; + unsigned int rpc_param_count; struct tee_device *teedev; struct tee_context *ctx; u32 max_notif_value; @@ -1306,7 +1347,8 @@ static int optee_probe(struct platform_device *pdev) } if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps, - &max_notif_value)) { + &max_notif_value, + &rpc_param_count)) { pr_warn("capabilities mismatch\n"); return -EINVAL; } @@ -1335,6 +1377,7 @@ static int optee_probe(struct platform_device *pdev) optee->ops = &optee_ops; optee->smc.invoke_fn = invoke_fn; optee->smc.sec_caps = sec_caps; + optee->rpc_param_count = rpc_param_count; teedev = tee_device_alloc(&optee_clnt_desc, NULL, pool, optee); if (IS_ERR(teedev)) { @@ -1403,7 +1446,12 @@ static int optee_probe(struct platform_device *pdev) */ optee_disable_unmapped_shm_cache(optee); - optee_enable_shm_cache(optee); + /* + * Only enable the shm cache in case we're not able to pass the RPC + * arg struct right after the normal arg struct. + */ + if (!optee->rpc_param_count) + optee_enable_shm_cache(optee); if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) pr_info("dynamic shared memory is enabled\n"); @@ -1416,7 +1464,8 @@ static int optee_probe(struct platform_device *pdev) return 0; err_disable_shm_cache: - optee_disable_shm_cache(optee); + if (!optee->rpc_param_count) + optee_disable_shm_cache(optee); optee_smc_notif_uninit_irq(optee); optee_unregister_devices(); err_notif_uninit: -- cgit v1.2.3 From a639b2b18a240d497109631d48016b620fb354f9 Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Wed, 12 Jan 2022 17:06:32 +0100 Subject: optee: add FF-A capability OPTEE_FFA_SEC_CAP_ARG_OFFSET Adds the secure capability OPTEE_FFA_SEC_CAP_ARG_OFFSET to indicate that OP-TEE with FF-A can support an argument struct at a non-zero offset into a passed shared memory object. Signed-off-by: Jens Wiklander --- drivers/tee/optee/ffa_abi.c | 17 +++++++++++++++-- drivers/tee/optee/optee_ffa.h | 12 +++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c index 7686f7020616..cc863aaefcd9 100644 --- a/drivers/tee/optee/ffa_abi.c +++ b/drivers/tee/optee/ffa_abi.c @@ -615,12 +615,21 @@ static int optee_ffa_do_call_with_arg(struct tee_context *ctx, .data0 = OPTEE_FFA_YIELDING_CALL_WITH_ARG, .data1 = (u32)shm->sec_world_id, .data2 = (u32)(shm->sec_world_id >> 32), - .data3 = shm->offset, + .data3 = 0, }; struct optee_msg_arg *arg; unsigned int rpc_arg_offs; struct optee_msg_arg *rpc_arg; + /* + * The shared memory object has to start on a page when passed as + * an argument struct. This is also what the shm pool allocator + * returns, but check this before calling secure world to catch + * eventual errors early in case something changes. + */ + if (shm->offset) + return -EINVAL; + arg = tee_shm_get_va(shm, 0); if (IS_ERR(arg)) return PTR_ERR(arg); @@ -678,6 +687,7 @@ static bool optee_ffa_api_is_compatbile(struct ffa_device *ffa_dev, static bool optee_ffa_exchange_caps(struct ffa_device *ffa_dev, const struct ffa_dev_ops *ops, + u32 *sec_caps, unsigned int *rpc_param_count) { struct ffa_send_direct_data data = { OPTEE_FFA_EXCHANGE_CAPABILITIES }; @@ -694,6 +704,7 @@ static bool optee_ffa_exchange_caps(struct ffa_device *ffa_dev, } *rpc_param_count = (u8)data.data1; + *sec_caps = data.data2; return true; } @@ -777,6 +788,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev) struct tee_device *teedev; struct tee_context *ctx; struct optee *optee; + u32 sec_caps; int rc; ffa_ops = ffa_dev_ops_get(ffa_dev); @@ -788,7 +800,8 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev) if (!optee_ffa_api_is_compatbile(ffa_dev, ffa_ops)) return -EINVAL; - if (!optee_ffa_exchange_caps(ffa_dev, ffa_ops, &rpc_param_count)) + if (!optee_ffa_exchange_caps(ffa_dev, ffa_ops, &sec_caps, + &rpc_param_count)) return -EINVAL; optee = kzalloc(sizeof(*optee), GFP_KERNEL); diff --git a/drivers/tee/optee/optee_ffa.h b/drivers/tee/optee/optee_ffa.h index ee3a03fc392c..97266243deaa 100644 --- a/drivers/tee/optee/optee_ffa.h +++ b/drivers/tee/optee/optee_ffa.h @@ -81,8 +81,16 @@ * as the second MSG arg struct for * OPTEE_FFA_YIELDING_CALL_WITH_ARG. * Bit[31:8]: Reserved (MBZ) - * w5-w7: Note used (MBZ) + * w5: Bitfield of secure world capabilities OPTEE_FFA_SEC_CAP_* below, + * unused bits MBZ. + * w6-w7: Not used (MBZ) + */ +/* + * Secure world supports giving an offset into the argument shared memory + * object, see also OPTEE_FFA_YIELDING_CALL_WITH_ARG */ +#define OPTEE_FFA_SEC_CAP_ARG_OFFSET BIT(0) + #define OPTEE_FFA_EXCHANGE_CAPABILITIES OPTEE_FFA_BLOCKING_CALL(2) /* @@ -112,6 +120,8 @@ * OPTEE_MSG_GET_ARG_SIZE(num_params) follows a struct optee_msg_arg * for RPC, this struct has reserved space for the number of RPC * parameters as returned by OPTEE_FFA_EXCHANGE_CAPABILITIES. + * MBZ unless the bit OPTEE_FFA_SEC_CAP_ARG_OFFSET is received with + * OPTEE_FFA_EXCHANGE_CAPABILITIES. * w7: Not used (MBZ) * Resume from RPC. Register usage: * w3: Service ID, OPTEE_FFA_YIELDING_CALL_RESUME -- cgit v1.2.3 From 1f26a60b55aa654c73b5b9eb9eab8a7d687d429d Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 13 Apr 2022 15:49:21 +0200 Subject: memory: renesas-rpc-if: Simplify single/double data register access For manual write and read, factor out the common access to the first data register by keeping track of the current data pointer. Signed-off-by: Geert Uytterhoeven Tested-by: Wolfram Sang Reviewed-by: Wolfram Sang Link: https://lore.kernel.org/r/c3b2a8d1a69f1b1e8d1a460148406cfb83e52eb4.1649857740.git.geert+renesas@glider.be Signed-off-by: Krzysztof Kozlowski --- drivers/memory/renesas-rpc-if.c | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/drivers/memory/renesas-rpc-if.c b/drivers/memory/renesas-rpc-if.c index e4cc64f56019..f946b77d4875 100644 --- a/drivers/memory/renesas-rpc-if.c +++ b/drivers/memory/renesas-rpc-if.c @@ -458,7 +458,7 @@ int rpcif_manual_xfer(struct rpcif *rpc) case RPCIF_DATA_OUT: while (pos < rpc->xferlen) { u32 bytes_left = rpc->xferlen - pos; - u32 nbytes, data[2]; + u32 nbytes, data[2], *p = data; smcr = rpc->smcr | RPCIF_SMCR_SPIE; @@ -471,15 +471,9 @@ int rpcif_manual_xfer(struct rpcif *rpc) regmap_write(rpc->regmap, RPCIF_SMENR, smenr); memcpy(data, rpc->buffer + pos, nbytes); - if (nbytes == 8) { - regmap_write(rpc->regmap, RPCIF_SMWDR1, - data[0]); - regmap_write(rpc->regmap, RPCIF_SMWDR0, - data[1]); - } else { - regmap_write(rpc->regmap, RPCIF_SMWDR0, - data[0]); - } + if (nbytes == 8) + regmap_write(rpc->regmap, RPCIF_SMWDR1, *p++); + regmap_write(rpc->regmap, RPCIF_SMWDR0, *p); regmap_write(rpc->regmap, RPCIF_SMCR, smcr); ret = wait_msg_xfer_end(rpc); @@ -521,7 +515,7 @@ int rpcif_manual_xfer(struct rpcif *rpc) } while (pos < rpc->xferlen) { u32 bytes_left = rpc->xferlen - pos; - u32 nbytes, data[2]; + u32 nbytes, data[2], *p = data; /* nbytes may only be 1, 2, 4, or 8 */ nbytes = bytes_left >= max ? max : (1 << ilog2(bytes_left)); @@ -537,15 +531,9 @@ int rpcif_manual_xfer(struct rpcif *rpc) if (ret) goto err_out; - if (nbytes == 8) { - regmap_read(rpc->regmap, RPCIF_SMRDR1, - &data[0]); - regmap_read(rpc->regmap, RPCIF_SMRDR0, - &data[1]); - } else { - regmap_read(rpc->regmap, RPCIF_SMRDR0, - &data[0]); - } + if (nbytes == 8) + regmap_read(rpc->regmap, RPCIF_SMRDR1, p++); + regmap_read(rpc->regmap, RPCIF_SMRDR0, p); memcpy(rpc->buffer + pos, data, nbytes); pos += nbytes; -- cgit v1.2.3 From cabfa5b46573b4e5ded52b4296a831745c6d32b5 Mon Sep 17 00:00:00 2001 From: "Minghao Chi (CGEL ZTE)" Date: Mon, 7 Mar 2022 03:37:36 +0000 Subject: soc: ti: omap_prm: Use of_device_get_match_data() Since omap_prm_id_table all have (and expected to have) data entries, use of_device_get_match_data() to simplify the code. Reported-by: Zeal Robot Signed-off-by: Minghao Chi (CGEL ZTE) Signed-off-by: Nishanth Menon Link: https://lore.kernel.org/r/20220307033736.2075221-1-chi.minghao@zte.com.cn --- drivers/soc/ti/omap_prm.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/soc/ti/omap_prm.c b/drivers/soc/ti/omap_prm.c index f32e1cbbe8c5..913b964374a4 100644 --- a/drivers/soc/ti/omap_prm.c +++ b/drivers/soc/ti/omap_prm.c @@ -941,23 +941,20 @@ static int omap_prm_probe(struct platform_device *pdev) struct resource *res; const struct omap_prm_data *data; struct omap_prm *prm; - const struct of_device_id *match; int ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENODEV; - match = of_match_device(omap_prm_id_table, &pdev->dev); - if (!match) + data = of_device_get_match_data(&pdev->dev); + if (!data) return -ENOTSUPP; prm = devm_kzalloc(&pdev->dev, sizeof(*prm), GFP_KERNEL); if (!prm) return -ENOMEM; - data = match->data; - while (data->base != res->start) { if (!data->base) return -EINVAL; -- cgit v1.2.3 From ba56291e297d28aa6eb82c5c1964fae2d7594746 Mon Sep 17 00:00:00 2001 From: QintaoShen Date: Thu, 24 Mar 2022 15:44:03 +0800 Subject: soc: ti: ti_sci_pm_domains: Check for null return of devm_kcalloc The allocation funciton devm_kcalloc may fail and return a null pointer, which would cause a null-pointer dereference later. It might be better to check it and directly return -ENOMEM just like the usage of devm_kcalloc in previous code. Signed-off-by: QintaoShen Signed-off-by: Nishanth Menon Link: https://lore.kernel.org/r/1648107843-29077-1-git-send-email-unSimple1993@163.com --- drivers/soc/ti/ti_sci_pm_domains.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soc/ti/ti_sci_pm_domains.c b/drivers/soc/ti/ti_sci_pm_domains.c index 8afb3f45d263..a33ec7eaf23d 100644 --- a/drivers/soc/ti/ti_sci_pm_domains.c +++ b/drivers/soc/ti/ti_sci_pm_domains.c @@ -183,6 +183,8 @@ static int ti_sci_pm_domain_probe(struct platform_device *pdev) devm_kcalloc(dev, max_id + 1, sizeof(*pd_provider->data.domains), GFP_KERNEL); + if (!pd_provider->data.domains) + return -ENOMEM; pd_provider->data.num_domains = max_id + 1; pd_provider->data.xlate = ti_sci_pd_xlate; -- cgit v1.2.3 From a6af504184c981efd253f986e6fc54db57b1d39f Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 4 Apr 2022 11:45:00 +0200 Subject: reset: ti-sci: Allow building under COMPILE_TEST Since commit 043cfff99a18 ("firmware: ti_sci: Fix compilation failure when CONFIG_TI_SCI_PROTOCOL is not defined") it is possible to build reset-ti-sci under CONFIG_COMPILE_TEST. Signed-off-by: Philipp Zabel Signed-off-by: Nishanth Menon Link: https://lore.kernel.org/r/20220404094500.2708816-1-p.zabel@pengutronix.de --- drivers/reset/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index b496028b6bfa..d6cb52a1dce7 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -240,7 +240,7 @@ config RESET_SUNXI config RESET_TI_SCI tristate "TI System Control Interface (TI-SCI) reset driver" - depends on TI_SCI_PROTOCOL + depends on TI_SCI_PROTOCOL || COMPILE_TEST help This enables the reset driver support over TI System Control Interface available on some new TI's SoCs. If you wish to use reset resources -- cgit v1.2.3 From d3e3116f253591a473873fab8363ecb998ddde13 Mon Sep 17 00:00:00 2001 From: Minghao Chi Date: Tue, 12 Apr 2022 08:29:23 +0000 Subject: soc: ti: knav_dma: Use pm_runtime_resume_and_get instead of pm_runtime_get_sync Using pm_runtime_resume_and_get is more appropriate for simplifying code. Reported-by: Zeal Robot Signed-off-by: Minghao Chi Signed-off-by: Nishanth Menon Reviewed-by: Grygorii Strashko Link: https://lore.kernel.org/r/20220412082923.2532649-1-chi.minghao@zte.com.cn --- drivers/soc/ti/knav_dma.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/soc/ti/knav_dma.c b/drivers/soc/ti/knav_dma.c index 7e126a73e56e..d756591de973 100644 --- a/drivers/soc/ti/knav_dma.c +++ b/drivers/soc/ti/knav_dma.c @@ -745,9 +745,8 @@ static int knav_dma_probe(struct platform_device *pdev) INIT_LIST_HEAD(&kdev->list); pm_runtime_enable(kdev->dev); - ret = pm_runtime_get_sync(kdev->dev); + ret = pm_runtime_resume_and_get(kdev->dev); if (ret < 0) { - pm_runtime_put_noidle(kdev->dev); dev_err(kdev->dev, "unable to enable pktdma, err %d\n", ret); goto err_pm_disable; } -- cgit v1.2.3 From d37b07897e5024088b2170b8e6e1c68d567b9be6 Mon Sep 17 00:00:00 2001 From: Lv Ruyi Date: Mon, 18 Apr 2022 02:01:47 +0000 Subject: memory: fsl-corenet-cf: Use helper function devm_platform_ioremap_resource() Use the devm_platform_ioremap_resource() helper instead of calling platform_get_resource() and devm_ioremap_resource() separately.Make the code simpler without functional changes. Signed-off-by: Lv Ruyi Link: https://lore.kernel.org/r/20220418020147.2556925-1-lv.ruyi@zte.com.cn Signed-off-by: Krzysztof Kozlowski --- drivers/memory/fsl-corenet-cf.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/memory/fsl-corenet-cf.c b/drivers/memory/fsl-corenet-cf.c index f8ea592c9cb5..7fc9f57ae278 100644 --- a/drivers/memory/fsl-corenet-cf.c +++ b/drivers/memory/fsl-corenet-cf.c @@ -172,7 +172,6 @@ out: static int ccf_probe(struct platform_device *pdev) { struct ccf_private *ccf; - struct resource *r; const struct of_device_id *match; u32 errinten; int ret, irq; @@ -185,13 +184,7 @@ static int ccf_probe(struct platform_device *pdev) if (!ccf) return -ENOMEM; - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) { - dev_err(&pdev->dev, "%s: no mem resource\n", __func__); - return -ENXIO; - } - - ccf->regs = devm_ioremap_resource(&pdev->dev, r); + ccf->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ccf->regs)) return PTR_ERR(ccf->regs); -- cgit v1.2.3 From 0b9fe9b7792f31674fbc8eb165c3b22a32d0a434 Mon Sep 17 00:00:00 2001 From: Michael Srba Date: Mon, 11 Apr 2022 09:21:54 +0200 Subject: dt-bindings: bus: add device tree bindings for qcom,ssc-block-bus Adds bindings for the AHB bus which exposes the SSC block in the global address space. This bus (and the SSC block itself) is present on certain qcom SoCs. In typical configuration, this bus (as some of the clocks and registers that we need to manipulate) is not accessible to the OS, and the resources on this bus are indirectly accessed by communicating with a hexagon CPU core residing in the SSC block. In this configuration, the hypervisor is the one performing the bus initialization for the purposes of bringing the haxagon CPU core out of reset. However, it is possible to change the configuration, in which case this binding serves to allow the OS to initialize the bus. Signed-off-by: Michael Srba Reviewed-by: Rob Herring Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220411072156.24451-4-michael.srba@seznam.cz --- .../bindings/bus/qcom,ssc-block-bus.yaml | 147 +++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 Documentation/devicetree/bindings/bus/qcom,ssc-block-bus.yaml diff --git a/Documentation/devicetree/bindings/bus/qcom,ssc-block-bus.yaml b/Documentation/devicetree/bindings/bus/qcom,ssc-block-bus.yaml new file mode 100644 index 000000000000..5b9705079015 --- /dev/null +++ b/Documentation/devicetree/bindings/bus/qcom,ssc-block-bus.yaml @@ -0,0 +1,147 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/bus/qcom,ssc-block-bus.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: The AHB Bus Providing a Global View of the SSC Block on (some) qcom SoCs + +maintainers: + - Michael Srba + +description: | + This binding describes the dependencies (clocks, resets, power domains) which + need to be turned on in a sequence before communication over the AHB bus + becomes possible. + + Additionally, the reg property is used to pass to the driver the location of + two sadly undocumented registers which need to be poked as part of the sequence. + + The SSC (Snapdragon Sensor Core) block contains a gpio controller, i2c/spi/uart + controllers, a hexagon core, and a clock controller which provides clocks for + the above. + +properties: + compatible: + items: + - const: qcom,msm8998-ssc-block-bus + - const: qcom,ssc-block-bus + + reg: + description: | + Shall contain the addresses of the SSCAON_CONFIG0 and SSCAON_CONFIG1 + registers + minItems: 2 + maxItems: 2 + + reg-names: + items: + - const: mpm_sscaon_config0 + - const: mpm_sscaon_config1 + + '#address-cells': + enum: [ 1, 2 ] + + '#size-cells': + enum: [ 1, 2 ] + + ranges: true + + clocks: + minItems: 6 + maxItems: 6 + + clock-names: + items: + - const: xo + - const: aggre2 + - const: gcc_im_sleep + - const: aggre2_north + - const: ssc_xo + - const: ssc_ahbs + + power-domains: + description: Power domain phandles for the ssc_cx and ssc_mx power domains + minItems: 2 + maxItems: 2 + + power-domain-names: + items: + - const: ssc_cx + - const: ssc_mx + + resets: + description: | + Reset phandles for the ssc_reset and ssc_bcr resets (note: ssc_bcr is the + branch control register associated with the ssc_xo and ssc_ahbs clocks) + minItems: 2 + maxItems: 2 + + reset-names: + items: + - const: ssc_reset + - const: ssc_bcr + + qcom,halt-regs: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: describes how to locate the ssc AXI halt register + items: + - items: + - description: Phandle reference to a syscon representing TCSR + - description: offset for the ssc AXI halt register + +required: + - compatible + - reg + - reg-names + - '#address-cells' + - '#size-cells' + - ranges + - clocks + - clock-names + - power-domains + - power-domain-names + - resets + - reset-names + - qcom,halt-regs + +additionalProperties: + type: object + +examples: + - | + #include + #include + #include + + soc { + #address-cells = <1>; + #size-cells = <1>; + + // devices under this node are physically located in the SSC block, connected to an ssc-internal bus; + ssc_ahb_slave: bus@10ac008 { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + compatible = "qcom,msm8998-ssc-block-bus", "qcom,ssc-block-bus"; + reg = <0x10ac008 0x4>, <0x10ac010 0x4>; + reg-names = "mpm_sscaon_config0", "mpm_sscaon_config1"; + + clocks = <&xo>, + <&rpmcc RPM_SMD_AGGR2_NOC_CLK>, + <&gcc GCC_IM_SLEEP>, + <&gcc AGGRE2_SNOC_NORTH_AXI>, + <&gcc SSC_XO>, + <&gcc SSC_CNOC_AHBS_CLK>; + clock-names = "xo", "aggre2", "gcc_im_sleep", "aggre2_north", "ssc_xo", "ssc_ahbs"; + + resets = <&gcc GCC_SSC_RESET>, <&gcc GCC_SSC_BCR>; + reset-names = "ssc_reset", "ssc_bcr"; + + power-domains = <&rpmpd MSM8998_SSCCX>, <&rpmpd MSM8998_SSCMX>; + power-domain-names = "ssc_cx", "ssc_mx"; + + qcom,halt-regs = <&tcsr_mutex_regs 0x26000>; + }; + }; -- cgit v1.2.3 From 97d485edc1d9902c9ae3b2242144abdf85edd4c1 Mon Sep 17 00:00:00 2001 From: Michael Srba Date: Mon, 11 Apr 2022 09:21:55 +0200 Subject: bus: add driver for initializing the SSC bus on (some) qcom SoCs Add bindings for the AHB bus which exposes the SSC (Snapdragon Sensor Core) block in the global address space. This bus (and the SSC block itself) is present on certain qcom SoCs. In typical configuration, this bus (as some of the clocks and registers that we need to manipulate) is not accessible to Linux, and the resources on this bus are indirectly accessed by communicating with a hexagon CPU core residing in the SSC block. In this configuration, the hypervisor is the one performing the bus initialization for the purposes of bringing the hexagon CPU core out of reset. However, it is possible to change the configuration, in which case this driver will initialize the bus. In combination with drivers for resources on the SSC bus, this driver can aid in debugging, and for example with a TLMM driver can be used to directly access SSC-dedicated GPIO pins, removing the need to commit to a particular usecase during hw design. Finally, until open firmware for the hexagon core is available, this approach allows for using sensors hooked up to SSC-dedicated GPIO pins on mainline Linux simply by utilizing the existing in-tree drivers for these sensors. Signed-off-by: Michael Srba Reviewed-by: Jeffrey Hugo Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220411072156.24451-5-michael.srba@seznam.cz --- drivers/bus/Kconfig | 11 ++ drivers/bus/Makefile | 1 + drivers/bus/qcom-ssc-block-bus.c | 389 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 401 insertions(+) create mode 100644 drivers/bus/qcom-ssc-block-bus.c diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 3c68e174a113..7bfe998f3514 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -152,6 +152,17 @@ config QCOM_EBI2 Interface 2, which can be used to connect things like NAND Flash, SRAM, ethernet adapters, FPGAs and LCD displays. +config QCOM_SSC_BLOCK_BUS + bool "Qualcomm SSC Block Bus Init Driver" + depends on ARCH_QCOM + help + Say y here to enable support for initializing the bus that connects + the SSC block's internal bus to the cNoC (configurantion NoC) on + (some) qcom SoCs. + The SSC (Snapdragon Sensor Core) block contains a gpio controller, + i2c/spi/uart controllers, a hexagon core, and a clock controller + which provides clocks for the above. + config SUN50I_DE2_BUS bool "Allwinner A64 DE2 Bus Driver" default ARM64 diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index 16da51130d1a..d90eed189a65 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o obj-$(CONFIG_QCOM_EBI2) += qcom-ebi2.o +obj-$(CONFIG_QCOM_SSC_BLOCK_BUS) += qcom-ssc-block-bus.o obj-$(CONFIG_SUN50I_DE2_BUS) += sun50i-de2.o obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o obj-$(CONFIG_OF) += simple-pm-bus.o diff --git a/drivers/bus/qcom-ssc-block-bus.c b/drivers/bus/qcom-ssc-block-bus.c new file mode 100644 index 000000000000..eedeb29a5ff3 --- /dev/null +++ b/drivers/bus/qcom-ssc-block-bus.c @@ -0,0 +1,389 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2021, Michael Srba + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* AXI Halt Register Offsets */ +#define AXI_HALTREQ_REG 0x0 +#define AXI_HALTACK_REG 0x4 +#define AXI_IDLE_REG 0x8 + +#define SSCAON_CONFIG0_CLAMP_EN_OVRD BIT(4) +#define SSCAON_CONFIG0_CLAMP_EN_OVRD_VAL BIT(5) + +static const char *const qcom_ssc_block_pd_names[] = { + "ssc_cx", + "ssc_mx" +}; + +struct qcom_ssc_block_bus_data { + const char *const *pd_names; + struct device *pds[ARRAY_SIZE(qcom_ssc_block_pd_names)]; + char __iomem *reg_mpm_sscaon_config0; + char __iomem *reg_mpm_sscaon_config1; + struct regmap *halt_map; + struct clk *xo_clk; + struct clk *aggre2_clk; + struct clk *gcc_im_sleep_clk; + struct clk *aggre2_north_clk; + struct clk *ssc_xo_clk; + struct clk *ssc_ahbs_clk; + struct reset_control *ssc_bcr; + struct reset_control *ssc_reset; + u32 ssc_axi_halt; + int num_pds; +}; + +static void reg32_set_bits(char __iomem *reg, u32 value) +{ + u32 tmp = ioread32(reg); + + iowrite32(tmp | value, reg); +} + +static void reg32_clear_bits(char __iomem *reg, u32 value) +{ + u32 tmp = ioread32(reg); + + iowrite32(tmp & (~value), reg); +} + +static int qcom_ssc_block_bus_init(struct device *dev) +{ + int ret; + + struct qcom_ssc_block_bus_data *data = dev_get_drvdata(dev); + + ret = clk_prepare_enable(data->xo_clk); + if (ret) { + dev_err(dev, "error enabling xo_clk: %d\n", ret); + goto err_xo_clk; + } + + ret = clk_prepare_enable(data->aggre2_clk); + if (ret) { + dev_err(dev, "error enabling aggre2_clk: %d\n", ret); + goto err_aggre2_clk; + } + + ret = clk_prepare_enable(data->gcc_im_sleep_clk); + if (ret) { + dev_err(dev, "error enabling gcc_im_sleep_clk: %d\n", ret); + goto err_gcc_im_sleep_clk; + } + + /* + * We need to intervene here because the HW logic driving these signals cannot handle + * initialization after power collapse by itself. + */ + reg32_clear_bits(data->reg_mpm_sscaon_config0, + SSCAON_CONFIG0_CLAMP_EN_OVRD | SSCAON_CONFIG0_CLAMP_EN_OVRD_VAL); + /* override few_ack/rest_ack */ + reg32_clear_bits(data->reg_mpm_sscaon_config1, BIT(31)); + + ret = clk_prepare_enable(data->aggre2_north_clk); + if (ret) { + dev_err(dev, "error enabling aggre2_north_clk: %d\n", ret); + goto err_aggre2_north_clk; + } + + ret = reset_control_deassert(data->ssc_reset); + if (ret) { + dev_err(dev, "error deasserting ssc_reset: %d\n", ret); + goto err_ssc_reset; + } + + ret = reset_control_deassert(data->ssc_bcr); + if (ret) { + dev_err(dev, "error deasserting ssc_bcr: %d\n", ret); + goto err_ssc_bcr; + } + + regmap_write(data->halt_map, data->ssc_axi_halt + AXI_HALTREQ_REG, 0); + + ret = clk_prepare_enable(data->ssc_xo_clk); + if (ret) { + dev_err(dev, "error deasserting ssc_xo_clk: %d\n", ret); + goto err_ssc_xo_clk; + } + + ret = clk_prepare_enable(data->ssc_ahbs_clk); + if (ret) { + dev_err(dev, "error deasserting ssc_ahbs_clk: %d\n", ret); + goto err_ssc_ahbs_clk; + } + + return 0; + +err_ssc_ahbs_clk: + clk_disable(data->ssc_xo_clk); + +err_ssc_xo_clk: + regmap_write(data->halt_map, data->ssc_axi_halt + AXI_HALTREQ_REG, 1); + + reset_control_assert(data->ssc_bcr); + +err_ssc_bcr: + reset_control_assert(data->ssc_reset); + +err_ssc_reset: + clk_disable(data->aggre2_north_clk); + +err_aggre2_north_clk: + reg32_set_bits(data->reg_mpm_sscaon_config0, BIT(4) | BIT(5)); + reg32_set_bits(data->reg_mpm_sscaon_config1, BIT(31)); + + clk_disable(data->gcc_im_sleep_clk); + +err_gcc_im_sleep_clk: + clk_disable(data->aggre2_clk); + +err_aggre2_clk: + clk_disable(data->xo_clk); + +err_xo_clk: + return ret; +} + +static void qcom_ssc_block_bus_deinit(struct device *dev) +{ + int ret; + + struct qcom_ssc_block_bus_data *data = dev_get_drvdata(dev); + + clk_disable(data->ssc_xo_clk); + clk_disable(data->ssc_ahbs_clk); + + ret = reset_control_assert(data->ssc_bcr); + if (ret) + dev_err(dev, "error asserting ssc_bcr: %d\n", ret); + + regmap_write(data->halt_map, data->ssc_axi_halt + AXI_HALTREQ_REG, 1); + + reg32_set_bits(data->reg_mpm_sscaon_config1, BIT(31)); + reg32_set_bits(data->reg_mpm_sscaon_config0, BIT(4) | BIT(5)); + + ret = reset_control_assert(data->ssc_reset); + if (ret) + dev_err(dev, "error asserting ssc_reset: %d\n", ret); + + clk_disable(data->gcc_im_sleep_clk); + + clk_disable(data->aggre2_north_clk); + + clk_disable(data->aggre2_clk); + clk_disable(data->xo_clk); +} + +static int qcom_ssc_block_bus_pds_attach(struct device *dev, struct device **pds, + const char *const *pd_names, size_t num_pds) +{ + int ret; + int i; + + for (i = 0; i < num_pds; i++) { + pds[i] = dev_pm_domain_attach_by_name(dev, pd_names[i]); + if (IS_ERR_OR_NULL(pds[i])) { + ret = PTR_ERR(pds[i]) ? : -ENODATA; + goto unroll_attach; + } + } + + return num_pds; + +unroll_attach: + for (i--; i >= 0; i--) + dev_pm_domain_detach(pds[i], false); + + return ret; +}; + +static void qcom_ssc_block_bus_pds_detach(struct device *dev, struct device **pds, size_t num_pds) +{ + int i; + + for (i = 0; i < num_pds; i++) + dev_pm_domain_detach(pds[i], false); +} + +static int qcom_ssc_block_bus_pds_enable(struct device **pds, size_t num_pds) +{ + int ret; + int i; + + for (i = 0; i < num_pds; i++) { + dev_pm_genpd_set_performance_state(pds[i], INT_MAX); + ret = pm_runtime_get_sync(pds[i]); + if (ret < 0) + goto unroll_pd_votes; + } + + return 0; + +unroll_pd_votes: + for (i--; i >= 0; i--) { + dev_pm_genpd_set_performance_state(pds[i], 0); + pm_runtime_put(pds[i]); + } + + return ret; +}; + +static void qcom_ssc_block_bus_pds_disable(struct device **pds, size_t num_pds) +{ + int i; + + for (i = 0; i < num_pds; i++) { + dev_pm_genpd_set_performance_state(pds[i], 0); + pm_runtime_put(pds[i]); + } +} + +static int qcom_ssc_block_bus_probe(struct platform_device *pdev) +{ + struct qcom_ssc_block_bus_data *data; + struct device_node *np = pdev->dev.of_node; + struct of_phandle_args halt_args; + struct resource *res; + int ret; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + platform_set_drvdata(pdev, data); + + data->pd_names = qcom_ssc_block_pd_names; + data->num_pds = ARRAY_SIZE(qcom_ssc_block_pd_names); + + /* power domains */ + ret = qcom_ssc_block_bus_pds_attach(&pdev->dev, data->pds, data->pd_names, data->num_pds); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "error when attaching power domains\n"); + + ret = qcom_ssc_block_bus_pds_enable(data->pds, data->num_pds); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "error when enabling power domains\n"); + + /* low level overrides for when the HW logic doesn't "just work" */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpm_sscaon_config0"); + data->reg_mpm_sscaon_config0 = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->reg_mpm_sscaon_config0)) + return dev_err_probe(&pdev->dev, PTR_ERR(data->reg_mpm_sscaon_config0), + "Failed to ioremap mpm_sscaon_config0\n"); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpm_sscaon_config1"); + data->reg_mpm_sscaon_config1 = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->reg_mpm_sscaon_config1)) + return dev_err_probe(&pdev->dev, PTR_ERR(data->reg_mpm_sscaon_config1), + "Failed to ioremap mpm_sscaon_config1\n"); + + /* resets */ + data->ssc_bcr = devm_reset_control_get_exclusive(&pdev->dev, "ssc_bcr"); + if (IS_ERR(data->ssc_bcr)) + return dev_err_probe(&pdev->dev, PTR_ERR(data->ssc_bcr), + "Failed to acquire reset: scc_bcr\n"); + + data->ssc_reset = devm_reset_control_get_exclusive(&pdev->dev, "ssc_reset"); + if (IS_ERR(data->ssc_reset)) + return dev_err_probe(&pdev->dev, PTR_ERR(data->ssc_reset), + "Failed to acquire reset: ssc_reset:\n"); + + /* clocks */ + data->xo_clk = devm_clk_get(&pdev->dev, "xo"); + if (IS_ERR(data->xo_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(data->xo_clk), + "Failed to get clock: xo\n"); + + data->aggre2_clk = devm_clk_get(&pdev->dev, "aggre2"); + if (IS_ERR(data->aggre2_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(data->aggre2_clk), + "Failed to get clock: aggre2\n"); + + data->gcc_im_sleep_clk = devm_clk_get(&pdev->dev, "gcc_im_sleep"); + if (IS_ERR(data->gcc_im_sleep_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(data->gcc_im_sleep_clk), + "Failed to get clock: gcc_im_sleep\n"); + + data->aggre2_north_clk = devm_clk_get(&pdev->dev, "aggre2_north"); + if (IS_ERR(data->aggre2_north_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(data->aggre2_north_clk), + "Failed to get clock: aggre2_north\n"); + + data->ssc_xo_clk = devm_clk_get(&pdev->dev, "ssc_xo"); + if (IS_ERR(data->ssc_xo_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(data->ssc_xo_clk), + "Failed to get clock: ssc_xo\n"); + + data->ssc_ahbs_clk = devm_clk_get(&pdev->dev, "ssc_ahbs"); + if (IS_ERR(data->ssc_ahbs_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(data->ssc_ahbs_clk), + "Failed to get clock: ssc_ahbs\n"); + + ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, "qcom,halt-regs", 1, 0, + &halt_args); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "Failed to parse qcom,halt-regs\n"); + + data->halt_map = syscon_node_to_regmap(halt_args.np); + of_node_put(halt_args.np); + if (IS_ERR(data->halt_map)) + return PTR_ERR(data->halt_map); + + data->ssc_axi_halt = halt_args.args[0]; + + qcom_ssc_block_bus_init(&pdev->dev); + + of_platform_populate(np, NULL, NULL, &pdev->dev); + + return 0; +} + +static int qcom_ssc_block_bus_remove(struct platform_device *pdev) +{ + struct qcom_ssc_block_bus_data *data = platform_get_drvdata(pdev); + + qcom_ssc_block_bus_deinit(&pdev->dev); + + iounmap(data->reg_mpm_sscaon_config0); + iounmap(data->reg_mpm_sscaon_config1); + + qcom_ssc_block_bus_pds_disable(data->pds, data->num_pds); + qcom_ssc_block_bus_pds_detach(&pdev->dev, data->pds, data->num_pds); + pm_runtime_disable(&pdev->dev); + pm_clk_destroy(&pdev->dev); + + return 0; +} + +static const struct of_device_id qcom_ssc_block_bus_of_match[] = { + { .compatible = "qcom,ssc-block-bus", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, qcom_ssc_block_bus_of_match); + +static struct platform_driver qcom_ssc_block_bus_driver = { + .probe = qcom_ssc_block_bus_probe, + .remove = qcom_ssc_block_bus_remove, + .driver = { + .name = "qcom-ssc-block-bus", + .of_match_table = qcom_ssc_block_bus_of_match, + }, +}; + +module_platform_driver(qcom_ssc_block_bus_driver); + +MODULE_DESCRIPTION("A driver for handling the init sequence needed for accessing the SSC block on (some) qcom SoCs over AHB"); +MODULE_AUTHOR("Michael Srba "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 38d46b0f41ec57c734f1a647055644c2131c3d8f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 11 Apr 2022 10:59:35 +0200 Subject: dt-bindings: soc: qcom,rpmh-rsc: convert to dtschema Convert the Qualcomm RPMH RSC bindings to DT Schema. Changes against original bindings: 1. Add qcom,tcs-offset as a property instead of one of reg (not used that way). 2. Add limits to interrupts. 3. Add child nodes (bcm-voter, clock-controller, power-controller). 4. Extend the example with more complex one. The device description and DTS examples were copied from existing sources, so keep the license as GPL-2.0-only. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220411085935.130072-4-krzysztof.kozlowski@linaro.org --- .../bindings/interconnect/qcom,bcm-voter.yaml | 8 +- .../bindings/soc/qcom/qcom,rpmh-rsc.yaml | 272 +++++++++++++++++++++ .../devicetree/bindings/soc/qcom/rpmh-rsc.txt | 137 ----------- 3 files changed, 276 insertions(+), 141 deletions(-) create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,rpmh-rsc.yaml delete mode 100644 Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt diff --git a/Documentation/devicetree/bindings/interconnect/qcom,bcm-voter.yaml b/Documentation/devicetree/bindings/interconnect/qcom,bcm-voter.yaml index e23df4836c6f..eec987640b37 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,bcm-voter.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,bcm-voter.yaml @@ -45,20 +45,20 @@ additionalProperties: false examples: # Example 1: apps bcm_voter on SDM845 SoC should be defined inside &apps_rsc node - # as defined in Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt + # as defined in Documentation/devicetree/bindings/soc/qcom/qcom,rpmh-rsc.yaml - | - apps_bcm_voter: bcm_voter { + apps_bcm_voter: bcm-voter { compatible = "qcom,bcm-voter"; }; # Example 2: disp bcm_voter on SDM845 should be defined inside &disp_rsc node - # as defined in Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt + # as defined in Documentation/devicetree/bindings/soc/qcom/qcom,rpmh-rsc.yaml - | #include - disp_bcm_voter: bcm_voter { + disp_bcm_voter: bcm-voter { compatible = "qcom,bcm-voter"; qcom,tcs-wait = ; }; diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,rpmh-rsc.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,rpmh-rsc.yaml new file mode 100644 index 000000000000..f5ecf4a8c377 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,rpmh-rsc.yaml @@ -0,0 +1,272 @@ +# SPDX-License-Identifier: GPL-2.0-only +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/qcom/qcom,rpmh-rsc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm RPMH RSC + +maintainers: + - Bjorn Andersson + +description: | + Resource Power Manager Hardened (RPMH) is the mechanism for communicating + with the hardened resource accelerators on Qualcomm SoCs. Requests to the + resources can be written to the Trigger Command Set (TCS) registers and + using a (addr, val) pair and triggered. Messages in the TCS are then sent in + sequence over an internal bus. + + The hardware block (Direct Resource Voter or DRV) is a part of the h/w entity + (Resource State Coordinator a.k.a RSC) that can handle multiple sleep and + active/wake resource requests. Multiple such DRVs can exist in a SoC and can + be written to from Linux. The structure of each DRV follows the same template + with a few variations that are captured by the properties here. + + A TCS may be triggered from Linux or triggered by the F/W after all the CPUs + have powered off to facilitate idle power saving. TCS could be classified as:: + ACTIVE - Triggered by Linux + SLEEP - Triggered by F/W + WAKE - Triggered by F/W + CONTROL - Triggered by F/W + See also:: + + The order in which they are described in the DT, should match the hardware + configuration. + + Requests can be made for the state of a resource, when the subsystem is + active or idle. When all subsystems like Modem, GPU, CPU are idle, the + resource state will be an aggregate of the sleep votes from each of those + subsystems. Clients may request a sleep value for their shared resources in + addition to the active mode requests. + + Drivers that want to use the RSC to communicate with RPMH must specify their + bindings as child nodes of the RSC controllers they wish to communicate with. + +properties: + compatible: + const: qcom,rpmh-rsc + + interrupts: + minItems: 1 + maxItems: 4 + description: + The interrupt that trips when a message complete/response is received for + this DRV from the accelerators. + Number of interrupts must match number of DRV blocks. + + label: + description: + Name for the RSC. The name would be used in trace logs. + + qcom,drv-id: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + The ID of the DRV in the RSC block that will be used by this controller. + + qcom,tcs-config: + $ref: /schemas/types.yaml#/definitions/uint32-matrix + items: + - items: + - description: TCS type + enum: [ 0, 1, 2, 3 ] + - description: Number of TCS + - items: + - description: TCS type + enum: [ 0, 1, 2, 3 ] + - description: Number of TCS + - items: + - description: TCS type + enum: [ 0, 1, 2, 3] + - description: Numbe r of TCS + - items: + - description: TCS type + enum: [ 0, 1, 2, 3 ] + - description: Number of TCS + description: | + The tuple defining the configuration of TCS. Must have two cells which + describe each TCS type. The order of the TCS must match the hardware + configuration. + Cell 1 (TCS Type):: TCS types to be specified:: + - ACTIVE_TCS + - SLEEP_TCS + - WAKE_TCS + - CONTROL_TCS + Cell 2 (Number of TCS):: + + qcom,tcs-offset: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + The offset of the TCS blocks. + + reg: + minItems: 1 + maxItems: 4 + + reg-names: + minItems: 1 + items: + - const: drv-0 + - const: drv-1 + - const: drv-2 + - const: drv-3 + + bcm-voter: + $ref: /schemas/interconnect/qcom,bcm-voter.yaml# + + clock-controller: + $ref: /schemas/clock/qcom,rpmhcc.yaml# + + power-controller: + $ref: /schemas/power/qcom,rpmpd.yaml# + +patternProperties: + '-regulators$': + $ref: /schemas/regulator/qcom,rpmh-regulator.yaml# + +required: + - compatible + - interrupts + - qcom,drv-id + - qcom,tcs-config + - qcom,tcs-offset + - reg + - reg-names + +additionalProperties: false + +examples: + - | + // For a TCS whose RSC base address is 0x179C0000 and is at a DRV id of + // 2, the register offsets for DRV2 start at 0D00, the register + // calculations are like this:: + // DRV0: 0x179C0000 + // DRV2: 0x179C0000 + 0x10000 = 0x179D0000 + // DRV2: 0x179C0000 + 0x10000 * 2 = 0x179E0000 + // TCS-OFFSET: 0xD00 + #include + #include + + rsc@179c0000 { + compatible = "qcom,rpmh-rsc"; + reg = <0x179c0000 0x10000>, + <0x179d0000 0x10000>, + <0x179e0000 0x10000>; + reg-names = "drv-0", "drv-1", "drv-2"; + interrupts = , + , + ; + label = "apps_rsc"; + qcom,tcs-offset = <0xd00>; + qcom,drv-id = <2>; + qcom,tcs-config = , + , + , + ; + }; + + - | + // For a TCS whose RSC base address is 0xAF20000 and is at DRV id of 0, the + // register offsets for DRV0 start at 01C00, the register calculations are + // like this:: + // DRV0: 0xAF20000 + // TCS-OFFSET: 0x1C00 + #include + #include + + rsc@af20000 { + compatible = "qcom,rpmh-rsc"; + reg = <0xaf20000 0x10000>; + reg-names = "drv-0"; + interrupts = ; + label = "disp_rsc"; + qcom,tcs-offset = <0x1c00>; + qcom,drv-id = <0>; + qcom,tcs-config = , + , + , + ; + }; + + - | + #include + #include + #include + + rsc@18200000 { + compatible = "qcom,rpmh-rsc"; + reg = <0x18200000 0x10000>, + <0x18210000 0x10000>, + <0x18220000 0x10000>; + reg-names = "drv-0", "drv-1", "drv-2"; + interrupts = , + , + ; + label = "apps_rsc"; + qcom,tcs-offset = <0xd00>; + qcom,drv-id = <2>; + qcom,tcs-config = , + , + , + ; + + clock-controller { + compatible = "qcom,sm8350-rpmh-clk"; + #clock-cells = <1>; + clock-names = "xo"; + clocks = <&xo_board>; + }; + + power-controller { + compatible = "qcom,sm8350-rpmhpd"; + #power-domain-cells = <1>; + operating-points-v2 = <&rpmhpd_opp_table>; + + rpmhpd_opp_table: opp-table { + compatible = "operating-points-v2"; + + rpmhpd_opp_ret: opp1 { + opp-level = ; + }; + + rpmhpd_opp_min_svs: opp2 { + opp-level = ; + }; + + rpmhpd_opp_low_svs: opp3 { + opp-level = ; + }; + + rpmhpd_opp_svs: opp4 { + opp-level = ; + }; + + rpmhpd_opp_svs_l1: opp5 { + opp-level = ; + }; + + rpmhpd_opp_nom: opp6 { + opp-level = ; + }; + + rpmhpd_opp_nom_l1: opp7 { + opp-level = ; + }; + + rpmhpd_opp_nom_l2: opp8 { + opp-level = ; + }; + + rpmhpd_opp_turbo: opp9 { + opp-level = ; + }; + + rpmhpd_opp_turbo_l1: opp10 { + opp-level = ; + }; + }; + }; + + bcm-voter { + compatible = "qcom,bcm-voter"; + }; + }; diff --git a/Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt b/Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt deleted file mode 100644 index 9b86d1eff219..000000000000 --- a/Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt +++ /dev/null @@ -1,137 +0,0 @@ -RPMH RSC: ------------- - -Resource Power Manager Hardened (RPMH) is the mechanism for communicating with -the hardened resource accelerators on Qualcomm SoCs. Requests to the resources -can be written to the Trigger Command Set (TCS) registers and using a (addr, -val) pair and triggered. Messages in the TCS are then sent in sequence over an -internal bus. - -The hardware block (Direct Resource Voter or DRV) is a part of the h/w entity -(Resource State Coordinator a.k.a RSC) that can handle multiple sleep and -active/wake resource requests. Multiple such DRVs can exist in a SoC and can -be written to from Linux. The structure of each DRV follows the same template -with a few variations that are captured by the properties here. - -A TCS may be triggered from Linux or triggered by the F/W after all the CPUs -have powered off to facilitate idle power saving. TCS could be classified as - - - ACTIVE /* Triggered by Linux */ - SLEEP /* Triggered by F/W */ - WAKE /* Triggered by F/W */ - CONTROL /* Triggered by F/W */ - -The order in which they are described in the DT, should match the hardware -configuration. - -Requests can be made for the state of a resource, when the subsystem is active -or idle. When all subsystems like Modem, GPU, CPU are idle, the resource state -will be an aggregate of the sleep votes from each of those subsystems. Clients -may request a sleep value for their shared resources in addition to the active -mode requests. - -Properties: - -- compatible: - Usage: required - Value type: - Definition: Should be "qcom,rpmh-rsc". - -- reg: - Usage: required - Value type: - Definition: The first register specifies the base address of the - DRV(s). The number of DRVs in the dependent on the RSC. - The tcs-offset specifies the start address of the - TCS in the DRVs. - -- reg-names: - Usage: required - Value type: - Definition: Maps the register specified in the reg property. Must be - "drv-0", "drv-1", "drv-2" etc and "tcs-offset". The - -- interrupts: - Usage: required - Value type: - Definition: The interrupt that trips when a message complete/response - is received for this DRV from the accelerators. - -- qcom,drv-id: - Usage: required - Value type: - Definition: The id of the DRV in the RSC block that will be used by - this controller. - -- qcom,tcs-config: - Usage: required - Value type: - Definition: The tuple defining the configuration of TCS. - Must have 2 cells which describe each TCS type. - . - The order of the TCS must match the hardware - configuration. - - Cell #1 (TCS Type): TCS types to be specified - - ACTIVE_TCS - SLEEP_TCS - WAKE_TCS - CONTROL_TCS - - Cell #2 (Number of TCS): - -- label: - Usage: optional - Value type: - Definition: Name for the RSC. The name would be used in trace logs. - -Drivers that want to use the RSC to communicate with RPMH must specify their -bindings as child nodes of the RSC controllers they wish to communicate with. - -Example 1: - -For a TCS whose RSC base address is is 0x179C0000 and is at a DRV id of 2, the -register offsets for DRV2 start at 0D00, the register calculations are like -this - -DRV0: 0x179C0000 -DRV2: 0x179C0000 + 0x10000 = 0x179D0000 -DRV2: 0x179C0000 + 0x10000 * 2 = 0x179E0000 -TCS-OFFSET: 0xD00 - - apps_rsc: rsc@179c0000 { - label = "apps_rsc"; - compatible = "qcom,rpmh-rsc"; - reg = <0x179c0000 0x10000>, - <0x179d0000 0x10000>, - <0x179e0000 0x10000>; - reg-names = "drv-0", "drv-1", "drv-2"; - interrupts = , - , - ; - qcom,tcs-offset = <0xd00>; - qcom,drv-id = <2>; - qcom,tcs-config = , - , - , - ; - }; - -Example 2: - -For a TCS whose RSC base address is 0xAF20000 and is at DRV id of 0, the -register offsets for DRV0 start at 01C00, the register calculations are like -this - -DRV0: 0xAF20000 -TCS-OFFSET: 0x1C00 - - disp_rsc: rsc@af20000 { - label = "disp_rsc"; - compatible = "qcom,rpmh-rsc"; - reg = <0xaf20000 0x10000>; - reg-names = "drv-0"; - interrupts = ; - qcom,tcs-offset = <0x1c00>; - qcom,drv-id = <0>; - qcom,tcs-config = , - , - , - ; - }; -- cgit v1.2.3 From fcf2c0f7cbcad29f1decb88567e54c2451b60ba5 Mon Sep 17 00:00:00 2001 From: Kuldeep Singh Date: Mon, 4 Apr 2022 23:59:35 +0530 Subject: dt-bindings: qcom: geni-se: Update I2C schema reference We now have geni based QUP I2C controller binding in place as dt-bindigs/i2c/qcom,i2c-geni-qcom.yaml similar to other controllers, update reference in parent schema and while at it, also remove properties defined for the controller from common wrapper. Signed-off-by: Kuldeep Singh Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220404182938.29492-3-singh.kuldeep87k@gmail.com --- .../devicetree/bindings/soc/qcom/qcom,geni-se.yaml | 26 +--------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.yaml index 1ca081b1664e..d3c89a6dd9f3 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.yaml @@ -108,31 +108,7 @@ patternProperties: "i2c@[0-9a-f]+$": type: object description: GENI serial engine based I2C controller. - $ref: /schemas/i2c/i2c-controller.yaml# - - properties: - compatible: - enum: - - qcom,geni-i2c - - interrupts: - maxItems: 1 - - "#address-cells": - const: 1 - - "#size-cells": - const: 0 - - clock-frequency: - description: Desired I2C bus clock frequency in Hz. - default: 100000 - - required: - - compatible - - interrupts - - "#address-cells" - - "#size-cells" + $ref: /schemas/i2c/qcom,i2c-geni-qcom.yaml# "serial@[0-9a-f]+$": type: object -- cgit v1.2.3 From 6579f39290bf056442850094b8f4b95163c17996 Mon Sep 17 00:00:00 2001 From: Kuldeep Singh Date: Mon, 4 Apr 2022 23:59:37 +0530 Subject: dt-bindings: qcom: geni-se: Update UART schema reference We now have geni based QUP UART controller individual binding, update reference in parent schema and remove properties from common wrapper. With removal of last child node schema, remove common properties of all the controllers as they have become obsolete now. Signed-off-by: Kuldeep Singh Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220404182938.29492-5-singh.kuldeep87k@gmail.com --- .../devicetree/bindings/soc/qcom/qcom,geni-se.yaml | 51 +--------------------- 1 file changed, 1 insertion(+), 50 deletions(-) diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.yaml index d3c89a6dd9f3..006e9f6f4dc5 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.yaml @@ -64,39 +64,6 @@ required: - ranges patternProperties: - "^.*@[0-9a-f]+$": - type: object - description: Common properties for GENI Serial Engine based I2C, SPI and - UART controller. - - properties: - reg: - description: GENI Serial Engine register address and length. - maxItems: 1 - - clock-names: - const: se - - clocks: - description: Serial engine core clock needed by the device. - maxItems: 1 - - interconnects: - minItems: 2 - maxItems: 3 - - interconnect-names: - minItems: 2 - items: - - const: qup-core - - const: qup-config - - const: qup-memory - - required: - - reg - - clock-names - - clocks - "spi@[0-9a-f]+$": type: object description: GENI serial engine based SPI controller. SPI in master mode @@ -113,23 +80,7 @@ patternProperties: "serial@[0-9a-f]+$": type: object description: GENI Serial Engine based UART Controller. - $ref: /schemas/serial.yaml# - - properties: - compatible: - enum: - - qcom,geni-uart - - qcom,geni-debug-uart - - interrupts: - minItems: 1 - items: - - description: UART core irq - - description: Wakeup irq (RX GPIO) - - required: - - compatible - - interrupts + $ref: /schemas/serial/qcom,serial-geni-qcom.yaml# additionalProperties: false -- cgit v1.2.3 From 5f9e72a4025b232ef0ddd5b6e4189e81091322fe Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 11 Apr 2022 17:55:28 +0200 Subject: dt-bindings: soc: qcom,smp2p: convert to dtschema Convert the Qualcomm Shared Memory Point 2 Point bindings to DT Schema. Changes against original bindings: enforce only specific names of child nodes, instead of any names. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220411155528.502889-1-krzysztof.kozlowski@linaro.org --- .../devicetree/bindings/soc/qcom/qcom,smp2p.txt | 110 ---------------- .../devicetree/bindings/soc/qcom/qcom,smp2p.yaml | 145 +++++++++++++++++++++ 2 files changed, 145 insertions(+), 110 deletions(-) delete mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.txt create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.yaml diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.txt deleted file mode 100644 index 49e1d72d3648..000000000000 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.txt +++ /dev/null @@ -1,110 +0,0 @@ -Qualcomm Shared Memory Point 2 Point binding - -The Shared Memory Point to Point (SMP2P) protocol facilitates communication of -a single 32-bit value between two processors. Each value has a single writer -(the local side) and a single reader (the remote side). Values are uniquely -identified in the system by the directed edge (local processor ID to remote -processor ID) and a string identifier. - -- compatible: - Usage: required - Value type: - Definition: must be one of: - "qcom,smp2p" - -- interrupts: - Usage: required - Value type: - Definition: one entry specifying the smp2p notification interrupt - -- mboxes: - Usage: required - Value type: - Definition: reference to the associated doorbell in APCS, as described - in mailbox/mailbox.txt - -- qcom,ipc: - Usage: required, unless mboxes is specified - Value type: - Definition: three entries specifying the outgoing ipc bit used for - signaling the remote end of the smp2p edge: - - phandle to a syscon node representing the apcs registers - - u32 representing offset to the register within the syscon - - u32 representing the ipc bit within the register - -- qcom,smem: - Usage: required - Value type: - Definition: two identifiers of the inbound and outbound smem items used - for this edge - -- qcom,local-pid: - Usage: required - Value type: - Definition: specifies the identifier of the local endpoint of this edge - -- qcom,remote-pid: - Usage: required - Value type: - Definition: specifies the identifier of the remote endpoint of this edge - -= SUBNODES -Each SMP2P pair contain a set of inbound and outbound entries, these are -described in subnodes of the smp2p device node. The node names are not -important. - -- qcom,entry-name: - Usage: required - Value type: - Definition: specifies the name of this entry, for inbound entries this - will be used to match against the remotely allocated entry - and for outbound entries this name is used for allocating - entries - -- interrupt-controller: - Usage: required for incoming entries - Value type: - Definition: marks the entry as inbound; the node should be specified - as a two cell interrupt-controller as defined in - "../interrupt-controller/interrupts.txt" - If not specified this node will denote the outgoing entry - -- #interrupt-cells: - Usage: required for incoming entries - Value type: - Definition: must be 2 - denoting the bit in the entry and IRQ flags - -- #qcom,smem-state-cells: - Usage: required for outgoing entries - Value type: - Definition: must be 1 - denoting the bit in the entry - -= EXAMPLE -The following example shows the SMP2P setup with the wireless processor, -defined from the 8974 apps processor's point-of-view. It encompasses one -inbound and one outbound entry: - -wcnss-smp2p { - compatible = "qcom,smp2p"; - qcom,smem = <431>, <451>; - - interrupts = <0 143 1>; - - qcom,ipc = <&apcs 8 18>; - - qcom,local-pid = <0>; - qcom,remote-pid = <4>; - - wcnss_smp2p_out: master-kernel { - qcom,entry-name = "master-kernel"; - - #qcom,smem-state-cells = <1>; - }; - - wcnss_smp2p_in: slave-kernel { - qcom,entry-name = "slave-kernel"; - - interrupt-controller; - #interrupt-cells = <2>; - }; -}; diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.yaml new file mode 100644 index 000000000000..795bd8cd4104 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.yaml @@ -0,0 +1,145 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/qcom/qcom,smp2p.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Shared Memory Point 2 Point + +maintainers: + - Andy Gross + - Bjorn Andersson + - Krzysztof Kozlowski + +description: + The Shared Memory Point to Point (SMP2P) protocol facilitates communication + of a single 32-bit value between two processors. Each value has a single + writer (the local side) and a single reader (the remote side). Values are + uniquely identified in the system by the directed edge (local processor ID to + remote processor ID) and a string identifier. + +properties: + compatible: + const: qcom,smp2p + + interrupts: + maxItems: 1 + + mboxes: + maxItems: 1 + description: + Reference to the mailbox representing the outgoing doorbell in APCS for + this client. + + qcom,ipc: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to a syscon node representing the APCS registers + - description: u32 representing offset to the register within the syscon + - description: u32 representing the ipc bit within the register + description: + Three entries specifying the outgoing ipc bit used for signaling the + remote end of the smp2p edge. + + qcom,local-pid: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + The identifier of the local endpoint of this edge. + + qcom,remote-pid: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + The identifier of the remote endpoint of this edge. + + qcom,smem: + $ref: /schemas/types.yaml#/definitions/uint32-array + items: + maxItems: 2 + description: + Two identifiers of the inbound and outbound smem items used for this edge. + +patternProperties: + "^master-kernel|slave-kernel|ipa-ap-to-modem|ipa-modem-to-ap$": + type: object + description: + Each SMP2P pair contain a set of inbound and outbound entries, these are + described in subnodes of the smp2p device node. The node names are not + important. + + properties: + interrupt-controller: + description: + Marks the entry as inbound; the node should be specified as a two + cell interrupt-controller. If not specified this node will denote + the outgoing entry. + + '#interrupt-cells': + const: 2 + + qcom,entry-name: + $ref: /schemas/types.yaml#/definitions/string + description: + The name of this entry, for inbound entries this will be used to + match against the remotely allocated entry and for outbound entries + this name is used for allocating entries. + + '#qcom,smem-state-cells': + $ref: /schemas/types.yaml#/definitions/uint32 + const: 1 + description: + Required for outgoing entries. + + required: + - qcom,entry-name + + oneOf: + - required: + - interrupt-controller + - '#interrupt-cells' + - required: + - '#qcom,smem-state-cells' + + additionalProperties: false + +required: + - compatible + - interrupts + - qcom,local-pid + - qcom,remote-pid + - qcom,smem + +oneOf: + - required: + - mboxes + - required: + - qcom,ipc + +additionalProperties: false + +examples: + # The following example shows the SMP2P setup with the wireless processor, + # defined from the 8974 apps processor's point-of-view. It encompasses one + # inbound and one outbound entry. + - | + #include + + shared-memory { + compatible = "qcom,smp2p"; + qcom,smem = <431>, <451>; + interrupts = ; + qcom,ipc = <&apcs 8 18>; + qcom,local-pid = <0>; + qcom,remote-pid = <4>; + + wcnss_smp2p_out: master-kernel { + qcom,entry-name = "master-kernel"; + #qcom,smem-state-cells = <1>; + }; + + wcnss_smp2p_in: slave-kernel { + qcom,entry-name = "slave-kernel"; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; -- cgit v1.2.3 From a555b382e7640b80b20816cf9179d091dd7716eb Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 15 Apr 2022 21:54:49 +0200 Subject: soc: qcom: socinfo: Sort out 8974PRO names MSM8974PRO is commonly used for referring to Snapdragon 801, compared to MSM8974 which is Snapdragon 800. The Snapdragon 801 has three variants with different clock speeds, with the -AA, -AB and -AC suffix. Adjust the names in socinfo to reflect this. Signed-off-by: Luca Weiss Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220415195449.617040-1-luca@z3ntu.xyz --- drivers/soc/qcom/socinfo.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 146ff934fdc8..cee579a267a6 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -236,24 +236,24 @@ static const struct soc_id soc_id[] = { { 184, "APQ8074" }, { 185, "MSM8274" }, { 186, "MSM8674" }, - { 194, "MSM8974PRO" }, + { 194, "MSM8974PRO-AC" }, { 198, "MSM8126" }, { 199, "APQ8026" }, { 200, "MSM8926" }, { 205, "MSM8326" }, { 206, "MSM8916" }, { 207, "MSM8994" }, - { 208, "APQ8074-AA" }, - { 209, "APQ8074-AB" }, - { 210, "APQ8074PRO" }, - { 211, "MSM8274-AA" }, - { 212, "MSM8274-AB" }, - { 213, "MSM8274PRO" }, - { 214, "MSM8674-AA" }, - { 215, "MSM8674-AB" }, - { 216, "MSM8674PRO" }, - { 217, "MSM8974-AA" }, - { 218, "MSM8974-AB" }, + { 208, "APQ8074PRO-AA" }, + { 209, "APQ8074PRO-AB" }, + { 210, "APQ8074PRO-AC" }, + { 211, "MSM8274PRO-AA" }, + { 212, "MSM8274PRO-AB" }, + { 213, "MSM8274PRO-AC" }, + { 214, "MSM8674PRO-AA" }, + { 215, "MSM8674PRO-AB" }, + { 216, "MSM8674PRO-AC" }, + { 217, "MSM8974PRO-AA" }, + { 218, "MSM8974PRO-AB" }, { 219, "APQ8028" }, { 220, "MSM8128" }, { 221, "MSM8228" }, -- cgit v1.2.3 From b6cd9be3d288249f52a1bcb10f7dbd445747ee59 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 3 Mar 2022 13:45:52 +0100 Subject: bus: ti-sysc: Drop commas after SoC match table sentinels It does not make sense to have a comma after a sentinel, as any new elements must be added before the sentinel. Signed-off-by: Geert Uytterhoeven Message-Id: <602f74030dc746eaa9f82f115acc46e62c6be165.1646311501.git.geert+renesas@glider.be> Signed-off-by: Tony Lindgren --- drivers/bus/ti-sysc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index 54c0ee6dda30..41d5e69063f5 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -3049,7 +3049,7 @@ static const struct soc_device_attribute sysc_soc_match[] = { SOC_FLAG("AM43*", SOC_AM4), SOC_FLAG("DRA7*", SOC_DRA7), - { /* sentinel */ }, + { /* sentinel */ } }; /* @@ -3070,7 +3070,7 @@ static const struct soc_device_attribute sysc_soc_feat_match[] = { SOC_FLAG("OMAP3615/AM3715", DIS_IVA), SOC_FLAG("OMAP3621", DIS_ISP), - { /* sentinel */ }, + { /* sentinel */ } }; static int sysc_add_disabled(unsigned long base) -- cgit v1.2.3 From 12eeb74925da70eb39d90abead9de9793be3d4c8 Mon Sep 17 00:00:00 2001 From: Minghao Chi Date: Mon, 18 Apr 2022 06:29:55 +0000 Subject: soc: ti: knav_qmss_queue: Use pm_runtime_resume_and_get instead of pm_runtime_get_sync Using pm_runtime_resume_and_get is more appropriate for simplifying code. Reported-by: Zeal Robot Signed-off-by: Minghao Chi Signed-off-by: Nishanth Menon Link: https://lore.kernel.org/r/20220418062955.2557949-1-chi.minghao@zte.com.cn --- drivers/soc/ti/knav_qmss_queue.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/soc/ti/knav_qmss_queue.c b/drivers/soc/ti/knav_qmss_queue.c index 4dbaa8c3636c..30612719e2f1 100644 --- a/drivers/soc/ti/knav_qmss_queue.c +++ b/drivers/soc/ti/knav_qmss_queue.c @@ -1783,9 +1783,8 @@ static int knav_queue_probe(struct platform_device *pdev) INIT_LIST_HEAD(&kdev->pdsps); pm_runtime_enable(&pdev->dev); - ret = pm_runtime_get_sync(&pdev->dev); + ret = pm_runtime_resume_and_get(&pdev->dev); if (ret < 0) { - pm_runtime_put_noidle(&pdev->dev); dev_err(dev, "Failed to enable QMSS\n"); return ret; } -- cgit v1.2.3 From f226041424cf87245d39a1b2dfae304308b36b6b Mon Sep 17 00:00:00 2001 From: Dave Gerlach Date: Sat, 9 Apr 2022 14:12:15 -0700 Subject: soc: ti: wkup_m3_ipc: Add support for toggling VTT regulator Some boards like the AM335x EVM-SK and AM437x GP EVM provide software control via a GPIO pin to toggle the DDR VTT regulator to reduce power consumption in low power states. The VTT regulator should be disabled after enabling self-refresh on suspend, and should be enabled before disabling self-refresh on resume. This is to allow proper self-refresh entry/exit commands to be transmitted to the memory. The "ti,vtt-gpio-pin" device tree property in the wkup_m3_ipc node specifies which GPIO pin to use. This property is communicated to the Wakeup Cortex M3 co-processor where the actual toggling of the GPIO pin happens in CM3 firmware [1]. Please note that the GPIO pin must be on the GPIO0 module as that module is in the wakeup power domain. [1] https://git.ti.com/cgit/processor-firmware/ti-amx3-cm3-pm-firmware/tree/src/pm_services/ddr.c?h=08.02.00.006#n190 Signed-off-by: Dave Gerlach Signed-off-by: Keerthy [dfustini: remove the unnecessary "ti,needs-vtt-toggle" property] Signed-off-by: Drew Fustini Signed-off-by: Nishanth Menon Link: https://lore.kernel.org/r/20220409211215.2529387-3-dfustini@baylibre.com --- drivers/soc/ti/wkup_m3_ipc.c | 26 ++++++++++++++++++++++++-- include/linux/wkup_m3_ipc.h | 1 + 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/drivers/soc/ti/wkup_m3_ipc.c b/drivers/soc/ti/wkup_m3_ipc.c index f145e65041fd..48659758228e 100644 --- a/drivers/soc/ti/wkup_m3_ipc.c +++ b/drivers/soc/ti/wkup_m3_ipc.c @@ -40,6 +40,13 @@ #define M3_FW_VERSION_MASK 0xffff #define M3_WAKE_SRC_MASK 0xff +#define IPC_MEM_TYPE_SHIFT (0x0) +#define IPC_MEM_TYPE_MASK (0x7 << 0) +#define IPC_VTT_STAT_SHIFT (0x3) +#define IPC_VTT_STAT_MASK (0x1 << 3) +#define IPC_VTT_GPIO_PIN_SHIFT (0x4) +#define IPC_VTT_GPIO_PIN_MASK (0x3f << 4) + #define M3_STATE_UNKNOWN 0 #define M3_STATE_RESET 1 #define M3_STATE_INITED 2 @@ -215,6 +222,12 @@ static int wkup_m3_is_available(struct wkup_m3_ipc *m3_ipc) (m3_ipc->state != M3_STATE_UNKNOWN)); } +static void wkup_m3_set_vtt_gpio(struct wkup_m3_ipc *m3_ipc, int gpio) +{ + m3_ipc->vtt_conf = (1 << IPC_VTT_STAT_SHIFT) | + (gpio << IPC_VTT_GPIO_PIN_SHIFT); +} + /* Public functions */ /** * wkup_m3_set_mem_type - Pass wkup_m3 which type of memory is in use @@ -294,7 +307,8 @@ static int wkup_m3_prepare_low_power(struct wkup_m3_ipc *m3_ipc, int state) /* Program each required IPC register then write defaults to others */ wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->resume_addr, 0); wkup_m3_ctrl_ipc_write(m3_ipc, m3_power_state, 1); - wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->mem_type, 4); + wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->mem_type | + m3_ipc->vtt_conf, 4); wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 2); wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 3); @@ -433,12 +447,13 @@ static int wkup_m3_rproc_boot_thread(void *arg) static int wkup_m3_ipc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - int irq, ret; + int irq, ret, temp; phandle rproc_phandle; struct rproc *m3_rproc; struct resource *res; struct task_struct *task; struct wkup_m3_ipc *m3_ipc; + struct device_node *np = dev->of_node; m3_ipc = devm_kzalloc(dev, sizeof(*m3_ipc), GFP_KERNEL); if (!m3_ipc) @@ -494,6 +509,13 @@ static int wkup_m3_ipc_probe(struct platform_device *pdev) m3_ipc->ops = &ipc_ops; + if (!of_property_read_u32(np, "ti,vtt-gpio-pin", &temp)) { + if (temp >= 0 && temp <= 31) + wkup_m3_set_vtt_gpio(m3_ipc, temp); + else + dev_warn(dev, "Invalid VTT GPIO(%d) pin\n", temp); + } + /* * Wait for firmware loading completion in a thread so we * can boot the wkup_m3 as soon as it's ready without holding diff --git a/include/linux/wkup_m3_ipc.h b/include/linux/wkup_m3_ipc.h index 3f496967b538..2bc52c6381d5 100644 --- a/include/linux/wkup_m3_ipc.h +++ b/include/linux/wkup_m3_ipc.h @@ -33,6 +33,7 @@ struct wkup_m3_ipc { int mem_type; unsigned long resume_addr; + int vtt_conf; int state; struct completion sync_complete; -- cgit v1.2.3 From 5fe580196dd9b7d8eb2a99629055bb4ffa00f262 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 7 Apr 2022 21:41:27 +0200 Subject: dt-bindings: timer: exynos4210-mct: describe known hardware and its interrupts Most of the Samsung Exynos SoCs use almost the same Multi-Core Timer block, so only two compatibles were used so far (for Exynos4210 and Exynos4412 flavors) with Exynos4210-one being used in most of the SoCs. However the Exynos4210 flavor actually differs by number of interrupts. Add new compatibles, maintaining backward compatibility with Exynos4210, and constraints for number of interrupts. This allows to exactly match the Exynos MCT hardware. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Alim Akhtar Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220407194127.19004-1-krzysztof.kozlowski@linaro.org --- .../bindings/timer/samsung,exynos4210-mct.yaml | 67 +++++++++++++++++++++- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/timer/samsung,exynos4210-mct.yaml b/Documentation/devicetree/bindings/timer/samsung,exynos4210-mct.yaml index f11cbc7ccc14..1584944c7ac4 100644 --- a/Documentation/devicetree/bindings/timer/samsung,exynos4210-mct.yaml +++ b/Documentation/devicetree/bindings/timer/samsung,exynos4210-mct.yaml @@ -19,9 +19,20 @@ description: |+ properties: compatible: - enum: - - samsung,exynos4210-mct - - samsung,exynos4412-mct + oneOf: + - enum: + - samsung,exynos4210-mct + - samsung,exynos4412-mct + - items: + - enum: + - samsung,exynos3250-mct + - samsung,exynos5250-mct + - samsung,exynos5260-mct + - samsung,exynos5420-mct + - samsung,exynos5433-mct + - samsung,exynos850-mct + - tesla,fsd-mct + - const: samsung,exynos4210-mct clocks: minItems: 2 @@ -63,6 +74,56 @@ required: - interrupts - reg +allOf: + - if: + properties: + compatible: + contains: + const: samsung,exynos3250-mct + then: + properties: + interrupts: + minItems: 8 + maxItems: 8 + + - if: + properties: + compatible: + contains: + const: samsung,exynos5250-mct + then: + properties: + interrupts: + minItems: 6 + maxItems: 6 + + - if: + properties: + compatible: + contains: + enum: + - samsung,exynos5260-mct + - samsung,exynos5420-mct + - samsung,exynos5433-mct + - samsung,exynos850-mct + then: + properties: + interrupts: + minItems: 12 + maxItems: 12 + + - if: + properties: + compatible: + contains: + enum: + - tesla,fsd-mct + then: + properties: + interrupts: + minItems: 16 + maxItems: 16 + additionalProperties: false examples: -- cgit v1.2.3 From 11a5ebb42ec2ae5a979769e09845e4788e728484 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 20 Apr 2022 17:42:49 +0900 Subject: soc: renesas: r8a779g0-sysc: Add r8a779g0 support Add support for R-Car V4H (R8A779G0) SoC power areas and register access. Signed-off-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20220420084255.375700-10-yoshihiro.shimoda.uh@renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/soc/renesas/Kconfig | 4 +++ drivers/soc/renesas/Makefile | 1 + drivers/soc/renesas/r8a779g0-sysc.c | 62 ++++++++++++++++++++++++++++++++++++ drivers/soc/renesas/rcar-gen4-sysc.c | 3 ++ drivers/soc/renesas/rcar-gen4-sysc.h | 1 + 5 files changed, 71 insertions(+) create mode 100644 drivers/soc/renesas/r8a779g0-sysc.c diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 63477f05fbaf..4bdb8d9f67d3 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -385,6 +385,10 @@ config SYSC_R8A779A0 bool "System Controller support for R-Car V3U" if COMPILE_TEST select SYSC_RCAR_GEN4 +config SYSC_R8A779G0 + bool "System Controller support for R-Car V4H" if COMPILE_TEST + select SYSC_RCAR_GEN4 + config SYSC_RMOBILE bool "System Controller support for R-Mobile" if COMPILE_TEST diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile index deeb41f84f01..535868c9c7e4 100644 --- a/drivers/soc/renesas/Makefile +++ b/drivers/soc/renesas/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_SYSC_R8A77990) += r8a77990-sysc.o obj-$(CONFIG_SYSC_R8A77995) += r8a77995-sysc.o obj-$(CONFIG_SYSC_R8A779A0) += r8a779a0-sysc.o obj-$(CONFIG_SYSC_R8A779F0) += r8a779f0-sysc.o +obj-$(CONFIG_SYSC_R8A779G0) += r8a779g0-sysc.o ifdef CONFIG_SMP obj-$(CONFIG_ARCH_R9A06G032) += r9a06g032-smp.o endif diff --git a/drivers/soc/renesas/r8a779g0-sysc.c b/drivers/soc/renesas/r8a779g0-sysc.c new file mode 100644 index 000000000000..a452709f066d --- /dev/null +++ b/drivers/soc/renesas/r8a779g0-sysc.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas R-Car V4H System Controller + * + * Copyright (C) 2022 Renesas Electronics Corp. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "rcar-gen4-sysc.h" + +static struct rcar_gen4_sysc_area r8a779g0_areas[] __initdata = { + { "always-on", R8A779G0_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "a3e0", R8A779G0_PD_A3E0, R8A779G0_PD_ALWAYS_ON, PD_SCU }, + { "a2e0d0", R8A779G0_PD_A2E0D0, R8A779G0_PD_A3E0, PD_SCU }, + { "a2e0d1", R8A779G0_PD_A2E0D1, R8A779G0_PD_A3E0, PD_SCU }, + { "a1e0d0c0", R8A779G0_PD_A1E0D0C0, R8A779G0_PD_A2E0D0, PD_CPU_NOCR }, + { "a1e0d0c1", R8A779G0_PD_A1E0D0C1, R8A779G0_PD_A2E0D0, PD_CPU_NOCR }, + { "a1e0d1c0", R8A779G0_PD_A1E0D1C0, R8A779G0_PD_A2E0D1, PD_CPU_NOCR }, + { "a1e0d1c1", R8A779G0_PD_A1E0D1C1, R8A779G0_PD_A2E0D1, PD_CPU_NOCR }, + { "a33dga", R8A779G0_PD_A33DGA, R8A779G0_PD_ALWAYS_ON }, + { "a23dgb", R8A779G0_PD_A23DGB, R8A779G0_PD_A33DGA }, + { "a3vip0", R8A779G0_PD_A3VIP0, R8A779G0_PD_ALWAYS_ON }, + { "a3vip1", R8A779G0_PD_A3VIP1, R8A779G0_PD_ALWAYS_ON }, + { "a3vip2", R8A779G0_PD_A3VIP2, R8A779G0_PD_ALWAYS_ON }, + { "a3isp0", R8A779G0_PD_A3ISP0, R8A779G0_PD_ALWAYS_ON }, + { "a3isp1", R8A779G0_PD_A3ISP1, R8A779G0_PD_ALWAYS_ON }, + { "a3ir", R8A779G0_PD_A3IR, R8A779G0_PD_ALWAYS_ON }, + { "a2cn0", R8A779G0_PD_A2CN0, R8A779G0_PD_A3IR }, + { "a1cnn0", R8A779G0_PD_A1CNN0, R8A779G0_PD_A2CN0 }, + { "a1dsp0", R8A779G0_PD_A1DSP0, R8A779G0_PD_A2CN0 }, + { "a1dsp1", R8A779G0_PD_A1DSP1, R8A779G0_PD_A2CN0 }, + { "a1dsp2", R8A779G0_PD_A1DSP2, R8A779G0_PD_A2CN0 }, + { "a1dsp3", R8A779G0_PD_A1DSP3, R8A779G0_PD_A2CN0 }, + { "a2imp01", R8A779G0_PD_A2IMP01, R8A779G0_PD_A3IR }, + { "a2imp23", R8A779G0_PD_A2IMP23, R8A779G0_PD_A3IR }, + { "a2psc", R8A779G0_PD_A2PSC, R8A779G0_PD_A3IR }, + { "a2dma", R8A779G0_PD_A2DMA, R8A779G0_PD_A3IR }, + { "a2cv0", R8A779G0_PD_A2CV0, R8A779G0_PD_A3IR }, + { "a2cv1", R8A779G0_PD_A2CV1, R8A779G0_PD_A3IR }, + { "a2cv2", R8A779G0_PD_A2CV2, R8A779G0_PD_A3IR }, + { "a2cv3", R8A779G0_PD_A2CV3, R8A779G0_PD_A3IR }, +}; + +const struct rcar_gen4_sysc_info r8a779g0_sysc_info __initconst = { + .areas = r8a779g0_areas, + .num_areas = ARRAY_SIZE(r8a779g0_areas), +}; diff --git a/drivers/soc/renesas/rcar-gen4-sysc.c b/drivers/soc/renesas/rcar-gen4-sysc.c index 831162a57f9a..9e5e6e077abc 100644 --- a/drivers/soc/renesas/rcar-gen4-sysc.c +++ b/drivers/soc/renesas/rcar-gen4-sysc.c @@ -281,6 +281,9 @@ static const struct of_device_id rcar_gen4_sysc_matches[] __initconst = { #endif #ifdef CONFIG_SYSC_R8A779F0 { .compatible = "renesas,r8a779f0-sysc", .data = &r8a779f0_sysc_info }, +#endif +#ifdef CONFIG_SYSC_R8A779G0 + { .compatible = "renesas,r8a779g0-sysc", .data = &r8a779g0_sysc_info }, #endif { /* sentinel */ } }; diff --git a/drivers/soc/renesas/rcar-gen4-sysc.h b/drivers/soc/renesas/rcar-gen4-sysc.h index 0e0bd102b1f9..fe2d98254754 100644 --- a/drivers/soc/renesas/rcar-gen4-sysc.h +++ b/drivers/soc/renesas/rcar-gen4-sysc.h @@ -39,5 +39,6 @@ struct rcar_gen4_sysc_info { extern const struct rcar_gen4_sysc_info r8a779a0_sysc_info; extern const struct rcar_gen4_sysc_info r8a779f0_sysc_info; +extern const struct rcar_gen4_sysc_info r8a779g0_sysc_info; #endif /* __SOC_RENESAS_RCAR_GEN4_SYSC_H__ */ -- cgit v1.2.3 From 87ab58e1fe3aa5a7f7afafe242d73a94a16d6e9c Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 20 Apr 2022 17:42:50 +0900 Subject: soc: renesas: Identify R-Car V4H Add support for identifying the R-Car V4H (R8A779G0) SoC. Signed-off-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20220420084255.375700-11-yoshihiro.shimoda.uh@renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/soc/renesas/Kconfig | 7 +++++++ drivers/soc/renesas/renesas-soc.c | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 4bdb8d9f67d3..4d89bf4dfdfd 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -268,6 +268,13 @@ config ARCH_R8A779A0 help This enables support for the Renesas R-Car V3U SoC. +config ARCH_R8A779G0 + bool "ARM64 Platform support for R-Car V4H" + select ARCH_RCAR_GEN3 + select SYSC_R8A779G0 + help + This enables support for the Renesas R-Car V4H SoC. + config ARCH_R8A774C0 bool "ARM64 Platform support for RZ/G2E" select ARCH_RCAR_GEN3 diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c index 30192a099d38..5b995952a747 100644 --- a/drivers/soc/renesas/renesas-soc.c +++ b/drivers/soc/renesas/renesas-soc.c @@ -241,6 +241,11 @@ static const struct renesas_soc soc_rcar_s4 __initconst __maybe_unused = { .id = 0x5a, }; +static const struct renesas_soc soc_rcar_v4h __initconst __maybe_unused = { + .family = &fam_rcar_gen4, + .id = 0x5c, +}; + static const struct renesas_soc soc_shmobile_ag5 __initconst __maybe_unused = { .family = &fam_shmobile, .id = 0x37, @@ -349,6 +354,9 @@ static const struct of_device_id renesas_socs[] __initconst = { #ifdef CONFIG_ARCH_R8A779F0 { .compatible = "renesas,r8a779f0", .data = &soc_rcar_s4 }, #endif +#ifdef CONFIG_ARCH_R8A779G0 + { .compatible = "renesas,r8a779g0", .data = &soc_rcar_v4h }, +#endif #if defined(CONFIG_ARCH_R9A07G043) { .compatible = "renesas,r9a07g043", .data = &soc_rz_g2ul }, #endif -- cgit v1.2.3 From d869d48ae7414cf1393636d30a1bfcf499d21ace Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 20 Apr 2022 17:42:51 +0900 Subject: soc: renesas: rcar-rst: Add support for R-Car V4H Add support for R-Car V4H (R8A779G0) to the R-Car RST driver. Signed-off-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20220420084255.375700-12-yoshihiro.shimoda.uh@renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/soc/renesas/rcar-rst.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/renesas/rcar-rst.c b/drivers/soc/renesas/rcar-rst.c index 4d293eb2d8f3..e1c7e91f5a86 100644 --- a/drivers/soc/renesas/rcar-rst.c +++ b/drivers/soc/renesas/rcar-rst.c @@ -103,6 +103,7 @@ static const struct of_device_id rcar_rst_matches[] __initconst = { /* R-Car Gen4 */ { .compatible = "renesas,r8a779a0-rst", .data = &rcar_rst_gen4 }, { .compatible = "renesas,r8a779f0-rst", .data = &rcar_rst_gen4 }, + { .compatible = "renesas,r8a779g0-rst", .data = &rcar_rst_gen4 }, { /* sentinel */ } }; -- cgit v1.2.3 From 5b4018b959149eb5b5f3004fc0339674af67516b Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Tue, 25 Jan 2022 21:26:42 +0100 Subject: optee: cache argument shared memory structs Implements a cache to handle shared memory used to pass the argument struct needed when doing a normal yielding call into secure world. Signed-off-by: Jens Wiklander --- drivers/tee/optee/call.c | 236 +++++++++++++++++++++++++++++++------- drivers/tee/optee/core.c | 1 + drivers/tee/optee/ffa_abi.c | 13 ++- drivers/tee/optee/optee_private.h | 27 ++++- drivers/tee/optee/smc_abi.c | 108 ++++++++++++++--- 5 files changed, 318 insertions(+), 67 deletions(-) diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index 58ac15c02818..608d5f4241de 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -11,6 +11,34 @@ #include #include "optee_private.h" +#define MAX_ARG_PARAM_COUNT 6 + +/* + * How much memory we allocate for each entry. This doesn't have to be a + * single page, but it makes sense to keep at least keep it as multiples of + * the page size. + */ +#define SHM_ENTRY_SIZE PAGE_SIZE + +/* + * We need to have a compile time constant to be able to determine the + * maximum needed size of the bit field. + */ +#define MIN_ARG_SIZE OPTEE_MSG_GET_ARG_SIZE(MAX_ARG_PARAM_COUNT) +#define MAX_ARG_COUNT_PER_ENTRY (SHM_ENTRY_SIZE / MIN_ARG_SIZE) + +/* + * Shared memory for argument structs are cached here. The number of + * arguments structs that can fit is determined at runtime depending on the + * needed RPC parameter count reported by secure world + * (optee->rpc_param_count). + */ +struct optee_shm_arg_entry { + struct list_head list_node; + struct tee_shm *shm; + DECLARE_BITMAP(map, MAX_ARG_COUNT_PER_ENTRY); +}; + void optee_cq_wait_init(struct optee_call_queue *cq, struct optee_call_waiter *w) { @@ -104,37 +132,149 @@ static struct optee_session *find_session(struct optee_context_data *ctxdata, return NULL; } -struct tee_shm *optee_get_msg_arg(struct tee_context *ctx, size_t num_params, - struct optee_msg_arg **msg_arg) +void optee_shm_arg_cache_init(struct optee *optee, u32 flags) +{ + INIT_LIST_HEAD(&optee->shm_arg_cache.shm_args); + mutex_init(&optee->shm_arg_cache.mutex); + optee->shm_arg_cache.flags = flags; +} + +void optee_shm_arg_cache_uninit(struct optee *optee) +{ + struct list_head *head = &optee->shm_arg_cache.shm_args; + struct optee_shm_arg_entry *entry; + + mutex_destroy(&optee->shm_arg_cache.mutex); + while (!list_empty(head)) { + entry = list_first_entry(head, struct optee_shm_arg_entry, + list_node); + list_del(&entry->list_node); + if (find_first_bit(entry->map, MAX_ARG_COUNT_PER_ENTRY) != + MAX_ARG_COUNT_PER_ENTRY) { + pr_err("Freeing non-free entry\n"); + } + tee_shm_free(entry->shm); + kfree(entry); + } +} + +size_t optee_msg_arg_size(size_t rpc_param_count) +{ + size_t sz = OPTEE_MSG_GET_ARG_SIZE(MAX_ARG_PARAM_COUNT); + + if (rpc_param_count) + sz += OPTEE_MSG_GET_ARG_SIZE(rpc_param_count); + + return sz; +} + +/** + * optee_get_msg_arg() - Provide shared memory for argument struct + * @ctx: Caller TEE context + * @num_params: Number of parameter to store + * @entry_ret: Entry pointer, needed when freeing the buffer + * @shm_ret: Shared memory buffer + * @offs_ret: Offset of argument strut in shared memory buffer + * + * @returns a pointer to the argument struct in memory, else an ERR_PTR + */ +struct optee_msg_arg *optee_get_msg_arg(struct tee_context *ctx, + size_t num_params, + struct optee_shm_arg_entry **entry_ret, + struct tee_shm **shm_ret, + u_int *offs_ret) { struct optee *optee = tee_get_drvdata(ctx->teedev); - size_t sz = OPTEE_MSG_GET_ARG_SIZE(num_params); - struct tee_shm *shm; + size_t sz = optee_msg_arg_size(optee->rpc_param_count); + struct optee_shm_arg_entry *entry; struct optee_msg_arg *ma; + size_t args_per_entry; + u_long bit; + u_int offs; + void *res; + + if (num_params > MAX_ARG_PARAM_COUNT) + return ERR_PTR(-EINVAL); + + if (optee->shm_arg_cache.flags & OPTEE_SHM_ARG_SHARED) + args_per_entry = SHM_ENTRY_SIZE / sz; + else + args_per_entry = 1; + + mutex_lock(&optee->shm_arg_cache.mutex); + list_for_each_entry(entry, &optee->shm_arg_cache.shm_args, list_node) { + bit = find_first_zero_bit(entry->map, MAX_ARG_COUNT_PER_ENTRY); + if (bit < args_per_entry) + goto have_entry; + } /* - * rpc_param_count is set to the number of allocated parameters in - * the RPC argument struct if a second MSG arg struct is expected. - * The second arg struct will then be used for RPC. + * No entry was found, let's allocate a new. */ - if (optee->rpc_param_count) - sz += OPTEE_MSG_GET_ARG_SIZE(optee->rpc_param_count); + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + res = ERR_PTR(-ENOMEM); + goto out; + } - shm = tee_shm_alloc_priv_buf(ctx, sz); - if (IS_ERR(shm)) - return shm; + if (optee->shm_arg_cache.flags & OPTEE_SHM_ARG_ALLOC_PRIV) + res = tee_shm_alloc_priv_buf(ctx, SHM_ENTRY_SIZE); + else + res = tee_shm_alloc_kernel_buf(ctx, SHM_ENTRY_SIZE); - ma = tee_shm_get_va(shm, 0); - if (IS_ERR(ma)) { - tee_shm_free(shm); - return (void *)ma; + if (IS_ERR(res)) { + kfree(entry); + goto out; } - + entry->shm = res; + list_add(&entry->list_node, &optee->shm_arg_cache.shm_args); + bit = 0; + +have_entry: + offs = bit * sz; + res = tee_shm_get_va(entry->shm, offs); + if (IS_ERR(res)) + goto out; + ma = res; + set_bit(bit, entry->map); memset(ma, 0, sz); ma->num_params = num_params; - *msg_arg = ma; + *entry_ret = entry; + *shm_ret = entry->shm; + *offs_ret = offs; +out: + mutex_unlock(&optee->shm_arg_cache.mutex); + return res; +} + +/** + * optee_free_msg_arg() - Free previsouly obtained shared memory + * @ctx: Caller TEE context + * @entry: Pointer returned when the shared memory was obtained + * @offs: Offset of shared memory buffer to free + * + * This function frees the shared memory obtained with optee_get_msg_arg(). + */ +void optee_free_msg_arg(struct tee_context *ctx, + struct optee_shm_arg_entry *entry, u_int offs) +{ + struct optee *optee = tee_get_drvdata(ctx->teedev); + size_t sz = optee_msg_arg_size(optee->rpc_param_count); + u_long bit; - return shm; + if (offs > SHM_ENTRY_SIZE || offs % sz) { + pr_err("Invalid offs %u\n", offs); + return; + } + bit = offs / sz; + + mutex_lock(&optee->shm_arg_cache.mutex); + + if (!test_bit(bit, entry->map)) + pr_err("Bit pos %lu is already free\n", bit); + clear_bit(bit, entry->map); + + mutex_unlock(&optee->shm_arg_cache.mutex); } int optee_open_session(struct tee_context *ctx, @@ -143,16 +283,19 @@ int optee_open_session(struct tee_context *ctx, { struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_context_data *ctxdata = ctx->data; - int rc; + struct optee_shm_arg_entry *entry; struct tee_shm *shm; struct optee_msg_arg *msg_arg; struct optee_session *sess = NULL; uuid_t client_uuid; + u_int offs; + int rc; /* +2 for the meta parameters added below */ - shm = optee_get_msg_arg(ctx, arg->num_params + 2, &msg_arg); - if (IS_ERR(shm)) - return PTR_ERR(shm); + msg_arg = optee_get_msg_arg(ctx, arg->num_params + 2, + &entry, &shm, &offs); + if (IS_ERR(msg_arg)) + return PTR_ERR(msg_arg); msg_arg->cmd = OPTEE_MSG_CMD_OPEN_SESSION; msg_arg->cancel_id = arg->cancel_id; @@ -185,7 +328,7 @@ int optee_open_session(struct tee_context *ctx, goto out; } - if (optee->ops->do_call_with_arg(ctx, shm)) { + if (optee->ops->do_call_with_arg(ctx, shm, offs)) { msg_arg->ret = TEEC_ERROR_COMMUNICATION; msg_arg->ret_origin = TEEC_ORIGIN_COMMS; } @@ -212,26 +355,28 @@ int optee_open_session(struct tee_context *ctx, arg->ret_origin = msg_arg->ret_origin; } out: - tee_shm_free(shm); + optee_free_msg_arg(ctx, entry, offs); return rc; } int optee_close_session_helper(struct tee_context *ctx, u32 session) { - struct tee_shm *shm; struct optee *optee = tee_get_drvdata(ctx->teedev); + struct optee_shm_arg_entry *entry; struct optee_msg_arg *msg_arg; + struct tee_shm *shm; + u_int offs; - shm = optee_get_msg_arg(ctx, 0, &msg_arg); - if (IS_ERR(shm)) - return PTR_ERR(shm); + msg_arg = optee_get_msg_arg(ctx, 0, &entry, &shm, &offs); + if (IS_ERR(msg_arg)) + return PTR_ERR(msg_arg); msg_arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION; msg_arg->session = session; - optee->ops->do_call_with_arg(ctx, shm); + optee->ops->do_call_with_arg(ctx, shm, offs); - tee_shm_free(shm); + optee_free_msg_arg(ctx, entry, offs); return 0; } @@ -259,9 +404,11 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, { struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_context_data *ctxdata = ctx->data; - struct tee_shm *shm; + struct optee_shm_arg_entry *entry; struct optee_msg_arg *msg_arg; struct optee_session *sess; + struct tee_shm *shm; + u_int offs; int rc; /* Check that the session is valid */ @@ -271,9 +418,10 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, if (!sess) return -EINVAL; - shm = optee_get_msg_arg(ctx, arg->num_params, &msg_arg); - if (IS_ERR(shm)) - return PTR_ERR(shm); + msg_arg = optee_get_msg_arg(ctx, arg->num_params, + &entry, &shm, &offs); + if (IS_ERR(msg_arg)) + return PTR_ERR(msg_arg); msg_arg->cmd = OPTEE_MSG_CMD_INVOKE_COMMAND; msg_arg->func = arg->func; msg_arg->session = arg->session; @@ -284,7 +432,7 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, if (rc) goto out; - if (optee->ops->do_call_with_arg(ctx, shm)) { + if (optee->ops->do_call_with_arg(ctx, shm, offs)) { msg_arg->ret = TEEC_ERROR_COMMUNICATION; msg_arg->ret_origin = TEEC_ORIGIN_COMMS; } @@ -298,7 +446,7 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, arg->ret = msg_arg->ret; arg->ret_origin = msg_arg->ret_origin; out: - tee_shm_free(shm); + optee_free_msg_arg(ctx, entry, offs); return rc; } @@ -306,9 +454,11 @@ int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session) { struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_context_data *ctxdata = ctx->data; - struct tee_shm *shm; + struct optee_shm_arg_entry *entry; struct optee_msg_arg *msg_arg; struct optee_session *sess; + struct tee_shm *shm; + u_int offs; /* Check that the session is valid */ mutex_lock(&ctxdata->mutex); @@ -317,16 +467,16 @@ int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session) if (!sess) return -EINVAL; - shm = optee_get_msg_arg(ctx, 0, &msg_arg); - if (IS_ERR(shm)) - return PTR_ERR(shm); + msg_arg = optee_get_msg_arg(ctx, 0, &entry, &shm, &offs); + if (IS_ERR(msg_arg)) + return PTR_ERR(msg_arg); msg_arg->cmd = OPTEE_MSG_CMD_CANCEL; msg_arg->session = session; msg_arg->cancel_id = cancel_id; - optee->ops->do_call_with_arg(ctx, shm); + optee->ops->do_call_with_arg(ctx, shm, offs); - tee_shm_free(shm); + optee_free_msg_arg(ctx, entry, offs); return 0; } diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index daf947e98d14..daf07737c4fd 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -171,6 +171,7 @@ void optee_remove_common(struct optee *optee) optee_unregister_devices(); optee_notif_uninit(optee); + optee_shm_arg_cache_uninit(optee); teedev_close_context(optee->ctx); /* * The two devices have to be unregistered before we can free the diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c index cc863aaefcd9..1552cd3f9d4e 100644 --- a/drivers/tee/optee/ffa_abi.c +++ b/drivers/tee/optee/ffa_abi.c @@ -601,6 +601,7 @@ done: * optee_ffa_do_call_with_arg() - Do a FF-A call to enter OP-TEE in secure world * @ctx: calling context * @shm: shared memory holding the message to pass to secure world + * @offs: offset of the message in @shm * * Does a FF-A call to OP-TEE in secure world and handles eventual resulting * Remote Procedure Calls (RPC) from OP-TEE. @@ -609,13 +610,13 @@ done: */ static int optee_ffa_do_call_with_arg(struct tee_context *ctx, - struct tee_shm *shm) + struct tee_shm *shm, u_int offs) { struct ffa_send_direct_data data = { .data0 = OPTEE_FFA_YIELDING_CALL_WITH_ARG, .data1 = (u32)shm->sec_world_id, .data2 = (u32)(shm->sec_world_id >> 32), - .data3 = 0, + .data3 = offs, }; struct optee_msg_arg *arg; unsigned int rpc_arg_offs; @@ -630,12 +631,12 @@ static int optee_ffa_do_call_with_arg(struct tee_context *ctx, if (shm->offset) return -EINVAL; - arg = tee_shm_get_va(shm, 0); + arg = tee_shm_get_va(shm, offs); if (IS_ERR(arg)) return PTR_ERR(arg); rpc_arg_offs = OPTEE_MSG_GET_ARG_SIZE(arg->num_params); - rpc_arg = tee_shm_get_va(shm, rpc_arg_offs); + rpc_arg = tee_shm_get_va(shm, offs + rpc_arg_offs); if (IS_ERR(rpc_arg)) return PTR_ERR(rpc_arg); @@ -787,6 +788,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev) struct tee_shm_pool *pool; struct tee_device *teedev; struct tee_context *ctx; + u32 arg_cache_flags = 0; struct optee *optee; u32 sec_caps; int rc; @@ -803,6 +805,8 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev) if (!optee_ffa_exchange_caps(ffa_dev, ffa_ops, &sec_caps, &rpc_param_count)) return -EINVAL; + if (sec_caps & OPTEE_FFA_SEC_CAP_ARG_OFFSET) + arg_cache_flags |= OPTEE_SHM_ARG_SHARED; optee = kzalloc(sizeof(*optee), GFP_KERNEL); if (!optee) @@ -851,6 +855,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev) mutex_init(&optee->call_queue.mutex); INIT_LIST_HEAD(&optee->call_queue.waiters); optee_supp_init(&optee->supp); + optee_shm_arg_cache_init(optee, arg_cache_flags); ffa_dev_set_drvdata(ffa_dev, optee); ctx = teedev_open(optee->teedev); if (IS_ERR(ctx)) { diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index e80c5d9b62ec..a33d98d17cfd 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -59,6 +59,16 @@ struct optee_notif { u_long *bitmap; }; +#define OPTEE_SHM_ARG_ALLOC_PRIV BIT(0) +#define OPTEE_SHM_ARG_SHARED BIT(1) +struct optee_shm_arg_entry; +struct optee_shm_arg_cache { + u32 flags; + /* Serializes access to this struct */ + struct mutex mutex; + struct list_head shm_args; +}; + /** * struct optee_supp - supplicant synchronization struct * @ctx the context of current connected supplicant. @@ -121,7 +131,7 @@ struct optee; */ struct optee_ops { int (*do_call_with_arg)(struct tee_context *ctx, - struct tee_shm *shm_arg); + struct tee_shm *shm_arg, u_int offs); int (*to_msg_param)(struct optee *optee, struct optee_msg_param *msg_params, size_t num_params, const struct tee_param *params); @@ -157,6 +167,7 @@ struct optee { struct optee_smc smc; struct optee_ffa ffa; }; + struct optee_shm_arg_cache shm_arg_cache; struct optee_call_queue call_queue; struct optee_notif notif; struct optee_supp supp; @@ -273,8 +284,18 @@ void optee_cq_wait_for_completion(struct optee_call_queue *cq, void optee_cq_wait_final(struct optee_call_queue *cq, struct optee_call_waiter *w); int optee_check_mem_type(unsigned long start, size_t num_pages); -struct tee_shm *optee_get_msg_arg(struct tee_context *ctx, size_t num_params, - struct optee_msg_arg **msg_arg); + +void optee_shm_arg_cache_init(struct optee *optee, u32 flags); +void optee_shm_arg_cache_uninit(struct optee *optee); +struct optee_msg_arg *optee_get_msg_arg(struct tee_context *ctx, + size_t num_params, + struct optee_shm_arg_entry **entry, + struct tee_shm **shm_ret, + u_int *offs); +void optee_free_msg_arg(struct tee_context *ctx, + struct optee_shm_arg_entry *entry, u_int offs); +size_t optee_msg_arg_size(size_t rpc_param_count); + struct tee_shm *optee_rpc_cmd_alloc_suppl(struct tee_context *ctx, size_t sz); void optee_rpc_cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm); diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c index b258d7306042..385cb0aee610 100644 --- a/drivers/tee/optee/smc_abi.c +++ b/drivers/tee/optee/smc_abi.c @@ -437,6 +437,7 @@ static int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm, struct optee_msg_arg *msg_arg; struct tee_shm *shm_arg; u64 *pages_list; + size_t sz; int rc; if (!num_pages) @@ -450,15 +451,30 @@ static int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm, if (!pages_list) return -ENOMEM; - shm_arg = optee_get_msg_arg(ctx, 1, &msg_arg); + /* + * We're about to register shared memory we can't register shared + * memory for this request or there's a catch-22. + * + * So in this we'll have to do the good old temporary private + * allocation instead of using optee_get_msg_arg(). + */ + sz = optee_msg_arg_size(optee->rpc_param_count); + shm_arg = tee_shm_alloc_priv_buf(ctx, sz); if (IS_ERR(shm_arg)) { rc = PTR_ERR(shm_arg); goto out; } + msg_arg = tee_shm_get_va(shm_arg, 0); + if (IS_ERR(msg_arg)) { + rc = PTR_ERR(msg_arg); + goto out; + } optee_fill_pages_list(pages_list, pages, num_pages, tee_shm_get_page_offset(shm)); + memset(msg_arg, 0, OPTEE_MSG_GET_ARG_SIZE(1)); + msg_arg->num_params = 1; msg_arg->cmd = OPTEE_MSG_CMD_REGISTER_SHM; msg_arg->params->attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT | OPTEE_MSG_ATTR_NONCONTIG; @@ -471,7 +487,7 @@ static int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm, msg_arg->params->u.tmem.buf_ptr = virt_to_phys(pages_list) | (tee_shm_get_page_offset(shm) & (OPTEE_MSG_NONCONTIG_PAGE_SIZE - 1)); - if (optee->ops->do_call_with_arg(ctx, shm_arg) || + if (optee->ops->do_call_with_arg(ctx, shm_arg, 0) || msg_arg->ret != TEEC_SUCCESS) rc = -EINVAL; @@ -487,19 +503,37 @@ static int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm) struct optee_msg_arg *msg_arg; struct tee_shm *shm_arg; int rc = 0; + size_t sz; - shm_arg = optee_get_msg_arg(ctx, 1, &msg_arg); + /* + * We're about to unregister shared memory and we may not be able + * register shared memory for this request in case we're called + * from optee_shm_arg_cache_uninit(). + * + * So in order to keep things simple in this function just as in + * optee_shm_register() we'll use temporary private allocation + * instead of using optee_get_msg_arg(). + */ + sz = optee_msg_arg_size(optee->rpc_param_count); + shm_arg = tee_shm_alloc_priv_buf(ctx, sz); if (IS_ERR(shm_arg)) return PTR_ERR(shm_arg); + msg_arg = tee_shm_get_va(shm_arg, 0); + if (IS_ERR(msg_arg)) { + rc = PTR_ERR(msg_arg); + goto out; + } + memset(msg_arg, 0, sz); + msg_arg->num_params = 1; msg_arg->cmd = OPTEE_MSG_CMD_UNREGISTER_SHM; - msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT; msg_arg->params[0].u.rmem.shm_ref = (unsigned long)shm; - if (optee->ops->do_call_with_arg(ctx, shm_arg) || + if (optee->ops->do_call_with_arg(ctx, shm_arg, 0) || msg_arg->ret != TEEC_SUCCESS) rc = -EINVAL; +out: tee_shm_free(shm_arg); return rc; } @@ -823,6 +857,7 @@ static void optee_handle_rpc(struct tee_context *ctx, * optee_smc_do_call_with_arg() - Do an SMC to OP-TEE in secure world * @ctx: calling context * @shm: shared memory holding the message to pass to secure world + * @offs: offset of the message in @shm * * Does and SMC to OP-TEE in secure world and handles eventual resulting * Remote Procedure Calls (RPC) from OP-TEE. @@ -830,7 +865,7 @@ static void optee_handle_rpc(struct tee_context *ctx, * Returns return code from secure world, 0 is OK */ static int optee_smc_do_call_with_arg(struct tee_context *ctx, - struct tee_shm *shm) + struct tee_shm *shm, u_int offs) { struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_call_waiter w; @@ -843,12 +878,12 @@ static int optee_smc_do_call_with_arg(struct tee_context *ctx, struct optee_msg_arg *arg; unsigned int rpc_arg_offs; - arg = tee_shm_get_va(shm, 0); + arg = tee_shm_get_va(shm, offs); if (IS_ERR(arg)) return PTR_ERR(arg); rpc_arg_offs = OPTEE_MSG_GET_ARG_SIZE(arg->num_params); - rpc_arg = tee_shm_get_va(shm, rpc_arg_offs); + rpc_arg = tee_shm_get_va(shm, offs + rpc_arg_offs); if (IS_ERR(arg)) return PTR_ERR(arg); } @@ -856,11 +891,11 @@ static int optee_smc_do_call_with_arg(struct tee_context *ctx, if (rpc_arg && tee_shm_is_dynamic(shm)) { param.a0 = OPTEE_SMC_CALL_WITH_REGD_ARG; reg_pair_from_64(¶m.a1, ¶m.a2, (u_long)shm); - param.a3 = 0; + param.a3 = offs; } else { phys_addr_t parg; - rc = tee_shm_get_pa(shm, 0, &parg); + rc = tee_shm_get_pa(shm, offs, &parg); if (rc) return rc; @@ -912,17 +947,19 @@ static int optee_smc_do_call_with_arg(struct tee_context *ctx, static int simple_call_with_arg(struct tee_context *ctx, u32 cmd) { + struct optee_shm_arg_entry *entry; struct optee_msg_arg *msg_arg; struct tee_shm *shm; + u_int offs; - shm = optee_get_msg_arg(ctx, 0, &msg_arg); - if (IS_ERR(shm)) - return PTR_ERR(shm); + msg_arg = optee_get_msg_arg(ctx, 0, &entry, &shm, &offs); + if (IS_ERR(msg_arg)) + return PTR_ERR(msg_arg); msg_arg->cmd = cmd; - optee_smc_do_call_with_arg(ctx, shm); + optee_smc_do_call_with_arg(ctx, shm, offs); - tee_shm_free(shm); + optee_free_msg_arg(ctx, entry, offs); return 0; } @@ -1327,6 +1364,7 @@ static int optee_probe(struct platform_device *pdev) struct tee_device *teedev; struct tee_context *ctx; u32 max_notif_value; + u32 arg_cache_flags; u32 sec_caps; int rc; @@ -1356,14 +1394,48 @@ static int optee_probe(struct platform_device *pdev) /* * Try to use dynamic shared memory if possible */ - if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) + if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) { + /* + * If we have OPTEE_SMC_SEC_CAP_RPC_ARG we can ask + * optee_get_msg_arg() to pre-register (by having + * OPTEE_SHM_ARG_ALLOC_PRIV cleared) the page used to pass + * an argument struct. + * + * With the page is pre-registered we can use a non-zero + * offset for argument struct, this is indicated with + * OPTEE_SHM_ARG_SHARED. + * + * This means that optee_smc_do_call_with_arg() will use + * OPTEE_SMC_CALL_WITH_REGD_ARG for pre-registered pages. + */ + if (sec_caps & OPTEE_SMC_SEC_CAP_RPC_ARG) + arg_cache_flags = OPTEE_SHM_ARG_SHARED; + else + arg_cache_flags = OPTEE_SHM_ARG_ALLOC_PRIV; + pool = optee_shm_pool_alloc_pages(); + } /* * If dynamic shared memory is not available or failed - try static one */ - if (IS_ERR(pool) && (sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM)) + if (IS_ERR(pool) && (sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM)) { + /* + * The static memory pool can use non-zero page offsets so + * let optee_get_msg_arg() know that with OPTEE_SHM_ARG_SHARED. + * + * optee_get_msg_arg() should not pre-register the + * allocated page used to pass an argument struct, this is + * indicated with OPTEE_SHM_ARG_ALLOC_PRIV. + * + * This means that optee_smc_do_call_with_arg() will use + * OPTEE_SMC_CALL_WITH_ARG if rpc_param_count is 0, else + * OPTEE_SMC_CALL_WITH_RPC_ARG. + */ + arg_cache_flags = OPTEE_SHM_ARG_SHARED | + OPTEE_SHM_ARG_ALLOC_PRIV; pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm); + } if (IS_ERR(pool)) return PTR_ERR(pool); @@ -1406,6 +1478,7 @@ static int optee_probe(struct platform_device *pdev) optee_supp_init(&optee->supp); optee->smc.memremaped_shm = memremaped_shm; optee->pool = pool; + optee_shm_arg_cache_init(optee, arg_cache_flags); platform_set_drvdata(pdev, optee); ctx = teedev_open(optee->teedev); @@ -1473,6 +1546,7 @@ err_notif_uninit: err_close_ctx: teedev_close_context(ctx); err_supp_uninit: + optee_shm_arg_cache_uninit(optee); optee_supp_uninit(&optee->supp); mutex_destroy(&optee->call_queue.mutex); err_unreg_supp_teedev: -- cgit v1.2.3 From 933713f5f49b816aa13a6441e41d98febef84dbe Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 19 Apr 2022 16:28:53 +0200 Subject: memory: da8xx-ddrctl: simplify platform_get_resource() Use devm_platform_get_and_ioremap_resource() instead of platform_get_resource() and devm_ioremap_resource(). Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220419142859.380566-1-krzysztof.kozlowski@linaro.org --- drivers/memory/da8xx-ddrctl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/memory/da8xx-ddrctl.c b/drivers/memory/da8xx-ddrctl.c index 872addd0ec60..b32005bf269c 100644 --- a/drivers/memory/da8xx-ddrctl.c +++ b/drivers/memory/da8xx-ddrctl.c @@ -115,8 +115,7 @@ static int da8xx_ddrctl_probe(struct platform_device *pdev) return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ddrctl = devm_ioremap_resource(dev, res); + ddrctl = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(ddrctl)) { dev_err(dev, "unable to map memory controller registers\n"); return PTR_ERR(ddrctl); -- cgit v1.2.3 From 734058b14de27682a176331ddd49fbdacdac1f46 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 19 Apr 2022 16:28:54 +0200 Subject: memory: emif: simplify platform_get_resource() Use devm_platform_ioremap_resource() instead of platform_get_resource() and devm_ioremap_resource(). Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220419142859.380566-2-krzysztof.kozlowski@linaro.org --- drivers/memory/emif.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/memory/emif.c b/drivers/memory/emif.c index edf3ba7447ed..6c2a421b86e3 100644 --- a/drivers/memory/emif.c +++ b/drivers/memory/emif.c @@ -1107,7 +1107,6 @@ error: static int __init_or_module emif_probe(struct platform_device *pdev) { struct emif_data *emif; - struct resource *res; int irq, ret; if (pdev->dev.of_node) @@ -1126,8 +1125,7 @@ static int __init_or_module emif_probe(struct platform_device *pdev) emif->dev = &pdev->dev; platform_set_drvdata(pdev, emif); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - emif->base = devm_ioremap_resource(emif->dev, res); + emif->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(emif->base)) goto error; -- cgit v1.2.3 From 083008defd83cb1ab6f9efaef6396bf4534ac6eb Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 19 Apr 2022 16:28:55 +0200 Subject: memory: ti-emif: simplify platform_get_resource() Use devm_platform_ioremap_resource() instead of platform_get_resource() and devm_ioremap_resource(). Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220419142859.380566-3-krzysztof.kozlowski@linaro.org --- drivers/memory/ti-aemif.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/memory/ti-aemif.c b/drivers/memory/ti-aemif.c index 51d20c2ccb75..f81e7df8798a 100644 --- a/drivers/memory/ti-aemif.c +++ b/drivers/memory/ti-aemif.c @@ -328,7 +328,6 @@ static int aemif_probe(struct platform_device *pdev) { int i; int ret = -ENODEV; - struct resource *res; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct device_node *child_np; @@ -362,8 +361,7 @@ static int aemif_probe(struct platform_device *pdev) else if (pdata) aemif->cs_offset = pdata->cs_offset; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - aemif->base = devm_ioremap_resource(dev, res); + aemif->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(aemif->base)) { ret = PTR_ERR(aemif->base); goto error; -- cgit v1.2.3 From 8e6a257a173378d0fb42d64865545286f1f84ef6 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 19 Apr 2022 16:28:56 +0200 Subject: memory: ti-emif-pm: simplify platform_get_resource() Use devm_platform_get_and_ioremap_resource() instead of platform_get_resource() and devm_ioremap_resource(). Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220419142859.380566-4-krzysztof.kozlowski@linaro.org --- drivers/memory/ti-emif-pm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/memory/ti-emif-pm.c b/drivers/memory/ti-emif-pm.c index 179fec2da56d..31d6266f008c 100644 --- a/drivers/memory/ti-emif-pm.c +++ b/drivers/memory/ti-emif-pm.c @@ -290,9 +290,9 @@ static int ti_emif_probe(struct platform_device *pdev) emif_data->pm_data.ti_emif_sram_config = (unsigned long)match->data; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - emif_data->pm_data.ti_emif_base_addr_virt = devm_ioremap_resource(dev, - res); + emif_data->pm_data.ti_emif_base_addr_virt = devm_platform_get_and_ioremap_resource(pdev, + 0, + &res); if (IS_ERR(emif_data->pm_data.ti_emif_base_addr_virt)) { ret = PTR_ERR(emif_data->pm_data.ti_emif_base_addr_virt); return ret; -- cgit v1.2.3 From dab022f22e3769260ef803eb7b70ec59df796a5a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 19 Apr 2022 16:28:57 +0200 Subject: memory: tegra: mc: simplify platform_get_resource() Use devm_platform_ioremap_resource() instead of platform_get_resource() and devm_ioremap_resource(). Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220419142859.380566-5-krzysztof.kozlowski@linaro.org --- drivers/memory/tegra/mc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index 44b4a4080920..6d22d1ee432a 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -716,7 +716,6 @@ del_provider: static int tegra_mc_probe(struct platform_device *pdev) { - struct resource *res; struct tegra_mc *mc; u64 mask; int err; @@ -741,8 +740,7 @@ static int tegra_mc_probe(struct platform_device *pdev) /* length of MC tick in nanoseconds */ mc->tick = 30; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mc->regs = devm_ioremap_resource(&pdev->dev, res); + mc->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(mc->regs)) return PTR_ERR(mc->regs); -- cgit v1.2.3 From ef231fefa47f9c694a8a5bbe16cb43b5db62d6d6 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 19 Apr 2022 16:28:58 +0200 Subject: memory: brcmstb_dpfe: simplify platform_get_resource_byname() Use devm_platform_ioremap_resource_byname() instead of platform_get_resource_byname() and devm_ioremap_resource(). Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220419142859.380566-6-krzysztof.kozlowski@linaro.org --- drivers/memory/brcmstb_dpfe.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/memory/brcmstb_dpfe.c b/drivers/memory/brcmstb_dpfe.c index 14412002775d..76c82e9c8fce 100644 --- a/drivers/memory/brcmstb_dpfe.c +++ b/drivers/memory/brcmstb_dpfe.c @@ -857,7 +857,6 @@ static int brcmstb_dpfe_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct brcmstb_dpfe_priv *priv; - struct resource *res; int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -869,22 +868,19 @@ static int brcmstb_dpfe_probe(struct platform_device *pdev) mutex_init(&priv->lock); platform_set_drvdata(pdev, priv); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-cpu"); - priv->regs = devm_ioremap_resource(dev, res); + priv->regs = devm_platform_ioremap_resource_byname(pdev, "dpfe-cpu"); if (IS_ERR(priv->regs)) { dev_err(dev, "couldn't map DCPU registers\n"); return -ENODEV; } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-dmem"); - priv->dmem = devm_ioremap_resource(dev, res); + priv->dmem = devm_platform_ioremap_resource_byname(pdev, "dpfe-dmem"); if (IS_ERR(priv->dmem)) { dev_err(dev, "Couldn't map DCPU data memory\n"); return -ENOENT; } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-imem"); - priv->imem = devm_ioremap_resource(dev, res); + priv->imem = devm_platform_ioremap_resource_byname(pdev, "dpfe-imem"); if (IS_ERR(priv->imem)) { dev_err(dev, "Couldn't map DCPU instruction memory\n"); return -ENOENT; -- cgit v1.2.3 From 2ca47b33a7794ce92ae881d6d62affea953814cd Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 19 Apr 2022 16:28:59 +0200 Subject: memory: renesas-rpc-if: simplify platform_get_resource_byname() Use devm_platform_ioremap_resource_byname() instead of platform_get_resource_byname() and devm_ioremap_resource(). Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220419142859.380566-7-krzysztof.kozlowski@linaro.org --- drivers/memory/renesas-rpc-if.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/memory/renesas-rpc-if.c b/drivers/memory/renesas-rpc-if.c index f946b77d4875..6f0ed2fcb7bd 100644 --- a/drivers/memory/renesas-rpc-if.c +++ b/drivers/memory/renesas-rpc-if.c @@ -229,8 +229,7 @@ int rpcif_sw_init(struct rpcif *rpc, struct device *dev) rpc->dev = dev; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); - rpc->base = devm_ioremap_resource(&pdev->dev, res); + rpc->base = devm_platform_ioremap_resource_byname(pdev, "regs"); if (IS_ERR(rpc->base)) return PTR_ERR(rpc->base); -- cgit v1.2.3 From 66eb6df79aefd6b3f7d2e749da7104e90cedc0ff Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Mon, 25 Apr 2022 09:16:16 -0500 Subject: tee: remove tee_shm_va2pa() and tee_shm_pa2va() We should not need to index into SHMs based on absolute VA/PA. These functions are not used and this kind of usage should not be encouraged anyway. Remove these functions. Signed-off-by: Andrew Davis Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- drivers/tee/tee_shm.c | 50 ------------------------------------------------- include/linux/tee_drv.h | 18 ------------------ 2 files changed, 68 deletions(-) diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c index f31e29e8f1ca..b0c6d553d3a7 100644 --- a/drivers/tee/tee_shm.c +++ b/drivers/tee/tee_shm.c @@ -414,56 +414,6 @@ void tee_shm_free(struct tee_shm *shm) } EXPORT_SYMBOL_GPL(tee_shm_free); -/** - * tee_shm_va2pa() - Get physical address of a virtual address - * @shm: Shared memory handle - * @va: Virtual address to tranlsate - * @pa: Returned physical address - * @returns 0 on success and < 0 on failure - */ -int tee_shm_va2pa(struct tee_shm *shm, void *va, phys_addr_t *pa) -{ - if (!shm->kaddr) - return -EINVAL; - /* Check that we're in the range of the shm */ - if ((char *)va < (char *)shm->kaddr) - return -EINVAL; - if ((char *)va >= ((char *)shm->kaddr + shm->size)) - return -EINVAL; - - return tee_shm_get_pa( - shm, (unsigned long)va - (unsigned long)shm->kaddr, pa); -} -EXPORT_SYMBOL_GPL(tee_shm_va2pa); - -/** - * tee_shm_pa2va() - Get virtual address of a physical address - * @shm: Shared memory handle - * @pa: Physical address to tranlsate - * @va: Returned virtual address - * @returns 0 on success and < 0 on failure - */ -int tee_shm_pa2va(struct tee_shm *shm, phys_addr_t pa, void **va) -{ - if (!shm->kaddr) - return -EINVAL; - /* Check that we're in the range of the shm */ - if (pa < shm->paddr) - return -EINVAL; - if (pa >= (shm->paddr + shm->size)) - return -EINVAL; - - if (va) { - void *v = tee_shm_get_va(shm, pa - shm->paddr); - - if (IS_ERR(v)) - return PTR_ERR(v); - *va = v; - } - return 0; -} -EXPORT_SYMBOL_GPL(tee_shm_pa2va); - /** * tee_shm_get_va() - Get virtual address of a shared memory plus an offset * @shm: Shared memory handle diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index 911cad324acc..17eb1c5205d3 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -298,24 +298,6 @@ void tee_shm_free(struct tee_shm *shm); */ void tee_shm_put(struct tee_shm *shm); -/** - * tee_shm_va2pa() - Get physical address of a virtual address - * @shm: Shared memory handle - * @va: Virtual address to tranlsate - * @pa: Returned physical address - * @returns 0 on success and < 0 on failure - */ -int tee_shm_va2pa(struct tee_shm *shm, void *va, phys_addr_t *pa); - -/** - * tee_shm_pa2va() - Get virtual address of a physical address - * @shm: Shared memory handle - * @pa: Physical address to tranlsate - * @va: Returned virtual address - * @returns 0 on success and < 0 on failure - */ -int tee_shm_pa2va(struct tee_shm *shm, phys_addr_t pa, void **va); - /** * tee_shm_get_va() - Get virtual address of a shared memory plus an offset * @shm: Shared memory handle -- cgit v1.2.3 From d8fc1c7c4c9b705ce5f5bba772ad66a0137c685d Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Mon, 25 Apr 2022 09:16:17 -0500 Subject: tee: remove flags TEE_IOCTL_SHM_MAPPED and TEE_IOCTL_SHM_DMA_BUF These look to be leftover from an early edition of this driver. Userspace does not need this information. Checking all users of this that I have access to I have verified no one is using them. They leak internal use flags out to userspace. Even more they are not correct anymore after a45ea4efa358. Lets drop these flags before someone does try to use them for something and they become ABI. Signed-off-by: Andrew Davis Acked-by: Sumit Garg Signed-off-by: Jens Wiklander --- drivers/tee/tee_core.c | 2 -- include/uapi/linux/tee.h | 4 ---- 2 files changed, 6 deletions(-) diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 8aa1a4836b92..af0f7c603fa4 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -302,7 +302,6 @@ static int tee_ioctl_shm_alloc(struct tee_context *ctx, return PTR_ERR(shm); data.id = shm->id; - data.flags = shm->flags; data.size = shm->size; if (copy_to_user(udata, &data, sizeof(data))) @@ -339,7 +338,6 @@ tee_ioctl_shm_register(struct tee_context *ctx, return PTR_ERR(shm); data.id = shm->id; - data.flags = shm->flags; data.length = shm->size; if (copy_to_user(udata, &data, sizeof(data))) diff --git a/include/uapi/linux/tee.h b/include/uapi/linux/tee.h index 25a6c534beb1..23e57164693c 100644 --- a/include/uapi/linux/tee.h +++ b/include/uapi/linux/tee.h @@ -42,10 +42,6 @@ #define TEE_IOC_MAGIC 0xa4 #define TEE_IOC_BASE 0 -/* Flags relating to shared memory */ -#define TEE_IOCTL_SHM_MAPPED 0x1 /* memory mapped in normal world */ -#define TEE_IOCTL_SHM_DMA_BUF 0x2 /* dma-buf handle on shared memory */ - #define TEE_MAX_ARG_SIZE 1024 #define TEE_GEN_CAP_GP (1 << 0)/* GlobalPlatform compliant TEE */ -- cgit v1.2.3 From 5bdcae1fe1c567c08e8bb7ef20fc70d0b70d5fc4 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 4 Apr 2022 08:40:16 +0200 Subject: spi: dt-bindings: qcom,spi-geni-qcom: convert to dtschema Convert the GENI based Qualcomm Universal Peripheral (QUP) Serial Peripheral Interface (SPI) bindings to DT Schema. The original bindings in TXT were not complete, so add during conversion properties already used in DTS and/or in the driver: dmas, interconnects, operating points and power-domains. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220404064017.68634-1-krzysztof.kozlowski@linaro.org --- .../devicetree/bindings/spi/qcom,spi-geni-qcom.txt | 39 ------- .../bindings/spi/qcom,spi-geni-qcom.yaml | 116 +++++++++++++++++++++ 2 files changed, 116 insertions(+), 39 deletions(-) delete mode 100644 Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.txt create mode 100644 Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.yaml diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.txt b/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.txt deleted file mode 100644 index c8c1e913f4e7..000000000000 --- a/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.txt +++ /dev/null @@ -1,39 +0,0 @@ -GENI based Qualcomm Universal Peripheral (QUP) Serial Peripheral Interface (SPI) - -The QUP v3 core is a GENI based AHB slave that provides a common data path -(an output FIFO and an input FIFO) for serial peripheral interface (SPI) -mini-core. - -SPI in master mode supports up to 50MHz, up to four chip selects, programmable -data path from 4 bits to 32 bits and numerous protocol variants. - -Required properties: -- compatible: Must contain "qcom,geni-spi". -- reg: Must contain SPI register location and length. -- interrupts: Must contain SPI controller interrupts. -- clock-names: Must contain "se". -- clocks: Serial engine core clock needed by the device. -- #address-cells: Must be <1> to define a chip select address on - the SPI bus. -- #size-cells: Must be <0>. - -SPI Controller nodes must be child of GENI based Qualcomm Universal -Peripharal. Please refer GENI based QUP wrapper controller node bindings -described in Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.yaml. - -SPI slave nodes must be children of the SPI master node and conform to SPI bus -binding as described in Documentation/devicetree/bindings/spi/spi-bus.txt. - -Example: - spi0: spi@a84000 { - compatible = "qcom,geni-spi"; - reg = <0xa84000 0x4000>; - interrupts = ; - clock-names = "se"; - clocks = <&clock_gcc GCC_QUPV3_WRAP0_S0_CLK>; - pinctrl-names = "default", "sleep"; - pinctrl-0 = <&qup_1_spi_2_active>; - pinctrl-1 = <&qup_1_spi_2_sleep>; - #address-cells = <1>; - #size-cells = <0>; - }; diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.yaml b/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.yaml new file mode 100644 index 000000000000..e2c7b934c50d --- /dev/null +++ b/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.yaml @@ -0,0 +1,116 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/qcom,spi-geni-qcom.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: GENI based Qualcomm Universal Peripheral (QUP) Serial Peripheral Interface (SPI) + +maintainers: + - Andy Gross + - Bjorn Andersson + - Krzysztof Kozlowski + +description: + The QUP v3 core is a GENI based AHB slave that provides a common data path + (an output FIFO and an input FIFO) for serial peripheral interface (SPI) + mini-core. + + SPI in master mode supports up to 50MHz, up to four chip selects, + programmable data path from 4 bits to 32 bits and numerous protocol variants. + + SPI Controller nodes must be child of GENI based Qualcomm Universal + Peripharal. Please refer GENI based QUP wrapper controller node bindings + described in Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.yaml. + +allOf: + - $ref: /schemas/spi/spi-controller.yaml# + +properties: + compatible: + const: qcom,geni-spi + + clocks: + maxItems: 1 + + clock-names: + const: se + + dmas: + maxItems: 2 + + dma-names: + items: + - const: tx + - const: rx + + interconnects: + maxItems: 2 + + interconnect-names: + items: + - const: qup-core + - const: qup-config + + interrupts: + maxItems: 1 + + operating-points-v2: true + + power-domains: + maxItems: 1 + + reg: + maxItems: 1 + +required: + - compatible + - clocks + - clock-names + - interrupts + - reg + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + #include + + spi@880000 { + compatible = "qcom,geni-spi"; + reg = <0x00880000 0x4000>; + clock-names = "se"; + clocks = <&gcc GCC_QUPV3_WRAP0_S0_CLK>; + pinctrl-names = "default"; + pinctrl-0 = <&qup_spi0_default>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + power-domains = <&rpmhpd SC7180_CX>; + operating-points-v2 = <&qup_opp_table>; + interconnects = <&qup_virt MASTER_QUP_CORE_0 0 &qup_virt SLAVE_QUP_CORE_0 0>, + <&gem_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_QUP_0 0>; + interconnect-names = "qup-core", "qup-config"; + }; + + - | + #include + + spi@884000 { + compatible = "qcom,geni-spi"; + reg = <0x00884000 0x4000>; + clock-names = "se"; + clocks = <&gcc GCC_QUPV3_WRAP0_S1_CLK>; + dmas = <&gpi_dma0 0 1 QCOM_GPI_SPI>, + <&gpi_dma0 1 1 QCOM_GPI_SPI>; + dma-names = "tx", "rx"; + pinctrl-names = "default"; + pinctrl-0 = <&qup_spi1_default>; + interrupts = ; + spi-max-frequency = <50000000>; + #address-cells = <1>; + #size-cells = <0>; + }; -- cgit v1.2.3 From b4bd2aafacce48db26b0a213d849818d940556dd Mon Sep 17 00:00:00 2001 From: QintaoShen Date: Thu, 24 Mar 2022 16:35:40 +0800 Subject: soc: bcm: Check for NULL return of devm_kzalloc() As the potential failure of allocation, devm_kzalloc() may return NULL. Then the 'pd->pmb' and the follow lines of code may bring null pointer dereference. Therefore, it is better to check the return value of devm_kzalloc() to avoid this confusion. Fixes: 8bcac4011ebe ("soc: bcm: add PM driver for Broadcom's PMB") Signed-off-by: QintaoShen Signed-off-by: Florian Fainelli --- drivers/soc/bcm/bcm63xx/bcm-pmb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/soc/bcm/bcm63xx/bcm-pmb.c b/drivers/soc/bcm/bcm63xx/bcm-pmb.c index 7bbe46ea5f94..9407cac47fdb 100644 --- a/drivers/soc/bcm/bcm63xx/bcm-pmb.c +++ b/drivers/soc/bcm/bcm63xx/bcm-pmb.c @@ -312,6 +312,9 @@ static int bcm_pmb_probe(struct platform_device *pdev) for (e = table; e->name; e++) { struct bcm_pmb_pm_domain *pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); + if (!pd) + return -ENOMEM; + pd->pmb = pmb; pd->data = e; pd->genpd.name = e->name; -- cgit v1.2.3 From 9235d5118fa4e31f4d71c45d788b6f08d18ab6e0 Mon Sep 17 00:00:00 2001 From: lizhe Date: Sat, 19 Mar 2022 23:45:29 -0700 Subject: bus: brcmstb_gisb: Remove the suppress_bind_attrs attribute of the driver Even if platform_driver does not set suppress_bind_attrs attribute, when registering with platform_driver_probe, the value of suppress_bind_attrs is still true, see __platform_driver_probe(). Signed-off-by: lizhe Signed-off-by: Florian Fainelli --- drivers/bus/brcmstb_gisb.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/bus/brcmstb_gisb.c b/drivers/bus/brcmstb_gisb.c index 183d5cc37d42..b0c3704777e9 100644 --- a/drivers/bus/brcmstb_gisb.c +++ b/drivers/bus/brcmstb_gisb.c @@ -536,7 +536,6 @@ static struct platform_driver brcmstb_gisb_arb_driver = { .name = "brcm-gisb-arb", .of_match_table = brcmstb_gisb_arb_of_match, .pm = &brcmstb_gisb_arb_pm_ops, - .suppress_bind_attrs = true, }, }; -- cgit v1.2.3 From 987756f67dee237ec35f3b249ab1ae25260c5340 Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Tue, 26 Apr 2022 13:12:19 +0100 Subject: firmware: arm_ffa: Fix handling of fragmented memory descriptors Fix the handling of MEM_FRAG_TX/RX SMCs when the full memory descriptor does not fit in a single innovation of a memory sharing request. The current implementation expects a FFA_MEM_SHARE/FFA_MEM_LEND call to always receive a FFA_SUCCESS response, however in the case where a full descriptor does not fit inside the partitions TX buffer, the call can instead complete with a FFA_MEM_FRAG_RX SMC to request the next part of the descriptor to be transmitted. Similarly a FFA_MEM_FRAG_TX call currently only expects FFA_MEM_FRAG_RX as a response, however once the full descriptor has been transmitted the FFA_SUCCESS ABI will be used to indicate successful transmission. Update the existing code to match the expected behaviour. Link: https://lore.kernel.org/r/20220426121219.1801601-1-marc.bonnici@arm.com Signed-off-by: Marc Bonnici Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/driver.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 14f900047ac0..ccccecae615f 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -398,11 +398,15 @@ static int ffa_mem_first_frag(u32 func_id, phys_addr_t buf, u32 buf_sz, if (ret.a0 == FFA_ERROR) return ffa_to_linux_errno((int)ret.a2); - if (ret.a0 != FFA_SUCCESS) + if (ret.a0 == FFA_SUCCESS) { + if (handle) + *handle = PACK_HANDLE(ret.a2, ret.a3); + } else if (ret.a0 == FFA_MEM_FRAG_RX) { + if (handle) + *handle = PACK_HANDLE(ret.a1, ret.a2); + } else { return -EOPNOTSUPP; - - if (handle) - *handle = PACK_HANDLE(ret.a2, ret.a3); + } return frag_len; } @@ -426,10 +430,12 @@ static int ffa_mem_next_frag(u64 handle, u32 frag_len) if (ret.a0 == FFA_ERROR) return ffa_to_linux_errno((int)ret.a2); - if (ret.a0 != FFA_MEM_FRAG_RX) - return -EOPNOTSUPP; + if (ret.a0 == FFA_MEM_FRAG_RX) + return ret.a3; + else if (ret.a0 == FFA_SUCCESS) + return 0; - return ret.a3; + return -EOPNOTSUPP; } static int -- cgit v1.2.3 From 4de1b36fae866c02f264f6bcda7a9a9e00ec5306 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 30 Mar 2022 16:05:31 +0100 Subject: firmware: arm_scmi: Make protocols initialisation fail on basic errors Bail out of protocol initialisation routine early when basic information about protocol version and attributes could not be retrieved. Failing to act this way can lead to a successfully initialized SCMI protocol which is in fact not fully functional. Link: https://lore.kernel.org/r/20220330150551.2573938-3-cristian.marussi@arm.com Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/base.c | 5 ++++- drivers/firmware/arm_scmi/clock.c | 8 ++++++-- drivers/firmware/arm_scmi/perf.c | 10 +++++++--- drivers/firmware/arm_scmi/power.c | 10 +++++++--- drivers/firmware/arm_scmi/reset.c | 10 +++++++--- drivers/firmware/arm_scmi/sensors.c | 4 +++- drivers/firmware/arm_scmi/system.c | 5 ++++- 7 files changed, 38 insertions(+), 14 deletions(-) diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c index f5219334fd3a..ebaef5d320af 100644 --- a/drivers/firmware/arm_scmi/base.c +++ b/drivers/firmware/arm_scmi/base.c @@ -359,7 +359,10 @@ static int scmi_base_protocol_init(const struct scmi_protocol_handle *ph) rev->minor_ver = PROTOCOL_REV_MINOR(version); ph->set_priv(ph, rev); - scmi_base_attributes_get(ph); + ret = scmi_base_attributes_get(ph); + if (ret) + return ret; + scmi_base_vendor_id_get(ph, false); scmi_base_vendor_id_get(ph, true); scmi_base_implementation_version_get(ph); diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 45600acc0f45..9f610206cbb8 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -361,7 +361,9 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph) int clkid, ret; struct clock_info *cinfo; - ph->xops->version_get(ph, &version); + ret = ph->xops->version_get(ph, &version); + if (ret) + return ret; dev_dbg(ph->dev, "Clock Version %d.%d\n", PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); @@ -370,7 +372,9 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph) if (!cinfo) return -ENOMEM; - scmi_clock_protocol_attributes_get(ph, cinfo); + ret = scmi_clock_protocol_attributes_get(ph, cinfo); + if (ret) + return ret; cinfo->clk = devm_kcalloc(ph->dev, cinfo->num_clocks, sizeof(*cinfo->clk), GFP_KERNEL); diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index f4cd5193b961..e9f68b91580c 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -873,11 +873,13 @@ static const struct scmi_protocol_events perf_protocol_events = { static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph) { - int domain; + int domain, ret; u32 version; struct scmi_perf_info *pinfo; - ph->xops->version_get(ph, &version); + ret = ph->xops->version_get(ph, &version); + if (ret) + return ret; dev_dbg(ph->dev, "Performance Version %d.%d\n", PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); @@ -886,7 +888,9 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph) if (!pinfo) return -ENOMEM; - scmi_perf_attributes_get(ph, pinfo); + ret = scmi_perf_attributes_get(ph, pinfo); + if (ret) + return ret; pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains, sizeof(*pinfo->dom_info), GFP_KERNEL); diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c index ad2ab080f344..0f0b94f0b624 100644 --- a/drivers/firmware/arm_scmi/power.c +++ b/drivers/firmware/arm_scmi/power.c @@ -280,11 +280,13 @@ static const struct scmi_protocol_events power_protocol_events = { static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph) { - int domain; + int domain, ret; u32 version; struct scmi_power_info *pinfo; - ph->xops->version_get(ph, &version); + ret = ph->xops->version_get(ph, &version); + if (ret) + return ret; dev_dbg(ph->dev, "Power Version %d.%d\n", PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); @@ -293,7 +295,9 @@ static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph) if (!pinfo) return -ENOMEM; - scmi_power_attributes_get(ph, pinfo); + ret = scmi_power_attributes_get(ph, pinfo); + if (ret) + return ret; pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains, sizeof(*pinfo->dom_info), GFP_KERNEL); diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c index 9bf2478ec6d1..9cdbd133db10 100644 --- a/drivers/firmware/arm_scmi/reset.c +++ b/drivers/firmware/arm_scmi/reset.c @@ -293,11 +293,13 @@ static const struct scmi_protocol_events reset_protocol_events = { static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph) { - int domain; + int domain, ret; u32 version; struct scmi_reset_info *pinfo; - ph->xops->version_get(ph, &version); + ret = ph->xops->version_get(ph, &version); + if (ret) + return ret; dev_dbg(ph->dev, "Reset Version %d.%d\n", PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); @@ -306,7 +308,9 @@ static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph) if (!pinfo) return -ENOMEM; - scmi_reset_attributes_get(ph, pinfo); + ret = scmi_reset_attributes_get(ph, pinfo); + if (ret) + return ret; pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains, sizeof(*pinfo->dom_info), GFP_KERNEL); diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c index cdbb287bd8bc..f37ac9824a87 100644 --- a/drivers/firmware/arm_scmi/sensors.c +++ b/drivers/firmware/arm_scmi/sensors.c @@ -966,7 +966,9 @@ static int scmi_sensors_protocol_init(const struct scmi_protocol_handle *ph) int ret; struct sensors_info *sinfo; - ph->xops->version_get(ph, &version); + ret = ph->xops->version_get(ph, &version); + if (ret) + return ret; dev_dbg(ph->dev, "Sensor Version %d.%d\n", PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); diff --git a/drivers/firmware/arm_scmi/system.c b/drivers/firmware/arm_scmi/system.c index e5175ef73b40..cbfc19f7a463 100644 --- a/drivers/firmware/arm_scmi/system.c +++ b/drivers/firmware/arm_scmi/system.c @@ -113,10 +113,13 @@ static const struct scmi_protocol_events system_protocol_events = { static int scmi_system_protocol_init(const struct scmi_protocol_handle *ph) { + int ret; u32 version; struct scmi_system_info *pinfo; - ph->xops->version_get(ph, &version); + ret = ph->xops->version_get(ph, &version); + if (ret) + return ret; dev_dbg(ph->dev, "System Power Version %d.%d\n", PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); -- cgit v1.2.3 From 8009120e0354a67068e920eb10dce532391361d0 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 30 Mar 2022 16:05:32 +0100 Subject: firmware: arm_scmi: Fix list protocols enumeration in the base protocol While enumerating protocols implemented by the SCMI platform using BASE_DISCOVER_LIST_PROTOCOLS, the number of returned protocols is currently validated in an improper way since the check employs a sum between unsigned integers that could overflow and cause the check itself to be silently bypassed if the returned value 'loop_num_ret' is big enough. Fix the validation avoiding the addition. Link: https://lore.kernel.org/r/20220330150551.2573938-4-cristian.marussi@arm.com Fixes: b6f20ff8bd94 ("firmware: arm_scmi: add common infrastructure and support for base protocol") Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c index ebaef5d320af..f279146f8110 100644 --- a/drivers/firmware/arm_scmi/base.c +++ b/drivers/firmware/arm_scmi/base.c @@ -197,7 +197,7 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph, break; loop_num_ret = le32_to_cpu(*num_ret); - if (tot_num_ret + loop_num_ret > MAX_PROTOCOLS_IMP) { + if (loop_num_ret > MAX_PROTOCOLS_IMP - tot_num_ret) { dev_err(dev, "No. of Protocol > MAX_PROTOCOLS_IMP"); break; } -- cgit v1.2.3 From 068eb5a9105bc43a60bdb20484840e5de945fe1d Mon Sep 17 00:00:00 2001 From: Phil Edworthy Date: Wed, 20 Apr 2022 21:43:07 +0100 Subject: soc: renesas: Add RZ/V2M (R9A09G011) config option Add a configuration option for the RZ/V2M SoC. Signed-off-by: Phil Edworthy Link: https://lore.kernel.org/r/20220420204307.87343-1-phil.edworthy@renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/soc/renesas/Kconfig | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 4d89bf4dfdfd..87857f5d122f 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -321,6 +321,13 @@ config ARCH_R9A07G054 help This enables support for the Renesas RZ/V2L SoC variants. +config ARCH_R9A09G011 + bool "ARM64 Platform support for RZ/V2M" + select PM + select PM_GENERIC_DOMAINS + help + This enables support for the Renesas RZ/V2M SoC. + endif # ARM64 config RST_RCAR -- cgit v1.2.3 From 14f11da778ff642142e9be18814815754c82d6c5 Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Fri, 22 Apr 2022 14:08:46 +0200 Subject: soc: renesas: rzn1: Select PM and PM_GENERIC_DOMAINS configs PM and PM_GENERIC_DOMAINS configs are required for RZ/N1 SOCs. Without these configs, the clocks used by the PCI bridge are not enabled and so accessing the devices leads to a kernel crash: Unhandled fault: external abort on non-linefetch (0x1008) at 0x90b5f848 Select PM and PM_GENERIC_DOMAINS for ARCH_RZN1 Signed-off-by: Herve Codina Link: https://lore.kernel.org/r/20220422120850.769480-5-herve.codina@bootlin.com Signed-off-by: Geert Uytterhoeven --- drivers/soc/renesas/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 87857f5d122f..c50a6ce1b99d 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -47,6 +47,8 @@ config ARCH_RZG2L config ARCH_RZN1 bool + select PM + select PM_GENERIC_DOMAINS select ARM_AMBA if ARM && ARCH_RENESAS -- cgit v1.2.3 From 3b0041f6e10e5bdbb646d98172be43e88734ed62 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 30 Mar 2022 16:05:33 +0100 Subject: firmware: arm_scmi: Validate BASE_DISCOVER_LIST_PROTOCOLS response Do not blindly trust SCMI platform response about list of implemented protocols, instead validate the reported length of the list of protocols against the real payload size of the message reply. Link: https://lore.kernel.org/r/20220330150551.2573938-5-cristian.marussi@arm.com Fixes: b6f20ff8bd94 ("firmware: arm_scmi: add common infrastructure and support for base protocol") Signed-off-by: Cristian Marussi [sudeep.holla: Added early break if loop_num_ret = 0 and simplified calc_list_sz calculation] Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/base.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c index f279146f8110..dc5b0c70a700 100644 --- a/drivers/firmware/arm_scmi/base.c +++ b/drivers/firmware/arm_scmi/base.c @@ -189,6 +189,9 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph, list = t->rx.buf + sizeof(*num_ret); do { + size_t real_list_sz; + u32 calc_list_sz; + /* Set the number of protocols to be skipped/already read */ *num_skip = cpu_to_le32(tot_num_ret); @@ -197,11 +200,32 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph, break; loop_num_ret = le32_to_cpu(*num_ret); + if (!loop_num_ret) + break; + if (loop_num_ret > MAX_PROTOCOLS_IMP - tot_num_ret) { dev_err(dev, "No. of Protocol > MAX_PROTOCOLS_IMP"); break; } + if (t->rx.len < (sizeof(u32) * 2)) { + dev_err(dev, "Truncated reply - rx.len:%zd\n", + t->rx.len); + ret = -EPROTO; + break; + } + + real_list_sz = t->rx.len - sizeof(u32); + calc_list_sz = (1 + (loop_num_ret - 1) / sizeof(u32)) * + sizeof(u32); + if (calc_list_sz != real_list_sz) { + dev_err(dev, + "Malformed reply - real_sz:%zd calc_sz:%u\n", + real_list_sz, calc_list_sz); + ret = -EPROTO; + break; + } + for (loop = 0; loop < loop_num_ret; loop++) protocols_imp[tot_num_ret + loop] = *(list + loop); -- cgit v1.2.3 From 776b6c8a25a36b5f46ed182ed6514c208e76720d Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 30 Mar 2022 16:05:34 +0100 Subject: firmware: arm_scmi: Dynamically allocate implemented protocols array Move away from a statically allocated array for holding the current set of protocols implemented by the platform in favour of allocating it dynamically based on the number of protocols effectively advertised by the platform via BASE protocol exchanges. While at that, rectify the BASE_DISCOVER_LIST_PROTOCOLS loop iterations to terminate only when a number of protocols equal to the advertised ones has been received, instead of looping till the platform returns no more protocols descriptors. This new behaviour is better compliant with the specification and it has been tested to work equally well against an SCMI stack running on top of an official SCP firmware on a JUNO board. Link: https://lore.kernel.org/r/20220330150551.2573938-6-cristian.marussi@arm.com Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/base.c | 17 ++++++++++------- drivers/firmware/arm_scmi/common.h | 1 - drivers/firmware/arm_scmi/driver.c | 6 ++++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c index dc5b0c70a700..20fba7370f4e 100644 --- a/drivers/firmware/arm_scmi/base.c +++ b/drivers/firmware/arm_scmi/base.c @@ -178,6 +178,7 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph, __le32 *num_skip, *num_ret; u32 tot_num_ret = 0, loop_num_ret; struct device *dev = ph->dev; + struct scmi_revision_info *rev = ph->get_priv(ph); ret = ph->xops->xfer_get_init(ph, BASE_DISCOVER_LIST_PROTOCOLS, sizeof(*num_skip), 0, &t); @@ -203,8 +204,9 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph, if (!loop_num_ret) break; - if (loop_num_ret > MAX_PROTOCOLS_IMP - tot_num_ret) { - dev_err(dev, "No. of Protocol > MAX_PROTOCOLS_IMP"); + if (loop_num_ret > rev->num_protocols - tot_num_ret) { + dev_err(dev, + "No. Returned protocols > Total protocols.\n"); break; } @@ -232,7 +234,7 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph, tot_num_ret += loop_num_ret; ph->xops->reset_rx_to_maxsz(ph, t); - } while (loop_num_ret); + } while (tot_num_ret < rev->num_protocols); ph->xops->xfer_put(ph, t); @@ -375,10 +377,6 @@ static int scmi_base_protocol_init(const struct scmi_protocol_handle *ph) if (ret) return ret; - prot_imp = devm_kcalloc(dev, MAX_PROTOCOLS_IMP, sizeof(u8), GFP_KERNEL); - if (!prot_imp) - return -ENOMEM; - rev->major_ver = PROTOCOL_REV_MAJOR(version), rev->minor_ver = PROTOCOL_REV_MINOR(version); ph->set_priv(ph, rev); @@ -387,6 +385,11 @@ static int scmi_base_protocol_init(const struct scmi_protocol_handle *ph) if (ret) return ret; + prot_imp = devm_kcalloc(dev, rev->num_protocols, sizeof(u8), + GFP_KERNEL); + if (!prot_imp) + return -ENOMEM; + scmi_base_vendor_id_get(ph, false); scmi_base_vendor_id_get(ph, true); scmi_base_implementation_version_get(ph); diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index 4fda84bfab42..6885348a505d 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -30,7 +30,6 @@ #define PROTOCOL_REV_MAJOR_MASK GENMASK(31, 16) #define PROTOCOL_REV_MAJOR(x) (u16)(FIELD_GET(PROTOCOL_REV_MAJOR_MASK, (x))) #define PROTOCOL_REV_MINOR(x) (u16)(FIELD_GET(PROTOCOL_REV_MINOR_MASK, (x))) -#define MAX_PROTOCOLS_IMP 16 #define MAX_OPPS 16 enum scmi_common_cmd { diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index e17c6568344d..a284a3fa85eb 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -128,7 +128,8 @@ struct scmi_protocol_instance { * usage. * @protocols_mtx: A mutex to protect protocols instances initialization. * @protocols_imp: List of protocols implemented, currently maximum of - * MAX_PROTOCOLS_IMP elements allocated by the base protocol + * scmi_revision_info.num_protocols elements allocated by the + * base protocol * @active_protocols: IDR storing device_nodes for protocols actually defined * in the DT and confirmed as implemented by fw. * @atomic_threshold: Optional system wide DT-configured threshold, expressed @@ -1310,11 +1311,12 @@ scmi_is_protocol_implemented(const struct scmi_handle *handle, u8 prot_id) { int i; struct scmi_info *info = handle_to_scmi_info(handle); + struct scmi_revision_info *rev = handle->version; if (!info->protocols_imp) return false; - for (i = 0; i < MAX_PROTOCOLS_IMP; i++) + for (i = 0; i < rev->num_protocols; i++) if (info->protocols_imp[i] == prot_id) return true; return false; -- cgit v1.2.3 From 992be5d3c818fcc277db246cb409659ca82abdbe Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 30 Mar 2022 16:05:35 +0100 Subject: firmware: arm_scmi: Make name_get operations return a const A few protocol operations are available that returns a pointer to an internal character array representing resource name. Make those functions return a const pointer to such array. Link: https://lore.kernel.org/r/20220330150551.2573938-7-cristian.marussi@arm.com Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/power.c | 5 +++-- drivers/firmware/arm_scmi/reset.c | 4 ++-- include/linux/scmi_protocol.h | 6 ++++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c index 0f0b94f0b624..5c04d63e7ff1 100644 --- a/drivers/firmware/arm_scmi/power.c +++ b/drivers/firmware/arm_scmi/power.c @@ -174,8 +174,9 @@ static int scmi_power_num_domains_get(const struct scmi_protocol_handle *ph) return pi->num_domains; } -static char *scmi_power_name_get(const struct scmi_protocol_handle *ph, - u32 domain) +static const char * +scmi_power_name_get(const struct scmi_protocol_handle *ph, + u32 domain) { struct scmi_power_info *pi = ph->get_priv(ph); struct power_dom_info *dom = pi->dom_info + domain; diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c index 9cdbd133db10..b823a9f64af9 100644 --- a/drivers/firmware/arm_scmi/reset.c +++ b/drivers/firmware/arm_scmi/reset.c @@ -126,8 +126,8 @@ static int scmi_reset_num_domains_get(const struct scmi_protocol_handle *ph) return pi->num_domains; } -static char *scmi_reset_name_get(const struct scmi_protocol_handle *ph, - u32 domain) +static const char * +scmi_reset_name_get(const struct scmi_protocol_handle *ph, u32 domain) { struct scmi_reset_info *pi = ph->get_priv(ph); diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index b87551f41f9f..ced37d1de1fe 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -146,7 +146,8 @@ struct scmi_perf_proto_ops { */ struct scmi_power_proto_ops { int (*num_domains_get)(const struct scmi_protocol_handle *ph); - char *(*name_get)(const struct scmi_protocol_handle *ph, u32 domain); + const char *(*name_get)(const struct scmi_protocol_handle *ph, + u32 domain); #define SCMI_POWER_STATE_TYPE_SHIFT 30 #define SCMI_POWER_STATE_ID_MASK (BIT(28) - 1) #define SCMI_POWER_STATE_PARAM(type, id) \ @@ -484,7 +485,8 @@ struct scmi_sensor_proto_ops { */ struct scmi_reset_proto_ops { int (*num_domains_get)(const struct scmi_protocol_handle *ph); - char *(*name_get)(const struct scmi_protocol_handle *ph, u32 domain); + const char *(*name_get)(const struct scmi_protocol_handle *ph, + u32 domain); int (*latency_get)(const struct scmi_protocol_handle *ph, u32 domain); int (*reset)(const struct scmi_protocol_handle *ph, u32 domain); int (*assert)(const struct scmi_protocol_handle *ph, u32 domain); -- cgit v1.2.3 From c7e223f5c736745967e6b6e5124489d81be111d2 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 30 Mar 2022 16:05:36 +0100 Subject: firmware: arm_scmi: Check CLOCK_RATE_SET_COMPLETE async response When CLOCK_RATE_SET command is issued in asynchronous mode the delayed response CLOCK_RATE_SET_COMPLETE comes back once the SCMI platform has effectively operated the requested change: such delayed response carries also the clock ID and the final clock rate that has been set. As an aid to debug issues, check that the clock ID in the delayed response matches the expected one and debug print the rate value. Link: https://lore.kernel.org/r/20220330150551.2573938-8-cristian.marussi@arm.com Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 9f610206cbb8..30d30ec8df94 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -68,6 +68,12 @@ struct scmi_clock_set_rate { __le32 value_high; }; +struct scmi_msg_resp_set_rate_complete { + __le32 id; + __le32 rate_low; + __le32 rate_high; +}; + struct clock_info { u32 version; int num_clocks; @@ -266,10 +272,22 @@ static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph, cfg->value_low = cpu_to_le32(rate & 0xffffffff); cfg->value_high = cpu_to_le32(rate >> 32); - if (flags & CLOCK_SET_ASYNC) + if (flags & CLOCK_SET_ASYNC) { ret = ph->xops->do_xfer_with_response(ph, t); - else + if (!ret) { + struct scmi_msg_resp_set_rate_complete *resp; + + resp = t->rx.buf; + if (le32_to_cpu(resp->id) == clk_id) + dev_dbg(ph->dev, + "Clk ID %d set async to %llu\n", clk_id, + get_unaligned_le64(&resp->rate_low)); + else + ret = -EPROTO; + } + } else { ret = ph->xops->do_xfer(ph, t); + } if (ci->max_async_req) atomic_dec(&ci->cur_async_req); -- cgit v1.2.3 From 91ebc56cbcfa4c746a84ab9ee6a854be739860e2 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 30 Mar 2022 16:05:37 +0100 Subject: firmware: arm_scmi: Remove unneeded NULL termination of clk name The string array 'name' inside struct scmi_clock_info holds the clock name which was successfully retrieved by querying the SCMI platform, unless the related underlying SCMI command failed. Anyway, such scmi_clock_info structure is allocated using devm_kcalloc() which in turn internally appends a __GFP_ZERO flag to its invocation: as a consequence the string 'name' field does not need to be zeroed when we fail to get the clock name via SCMI, it is already NULL terminated. Remove unneeded explicit NULL termination. Link: https://lore.kernel.org/r/20220330150551.2573938-9-cristian.marussi@arm.com Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 30d30ec8df94..9dff6a948674 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -129,8 +129,6 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph, if (t->rx.len == sizeof(*attr)) clk->enable_latency = le32_to_cpu(attr->clock_enable_latency); - } else { - clk->name[0] = '\0'; } ph->xops->xfer_put(ph, t); -- cgit v1.2.3 From 23136bff80a7edbe7b5df2b7c327d6584879930b Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 30 Mar 2022 16:05:38 +0100 Subject: firmware: arm_scmi: Split protocol specific definitions in a dedicated header Move some SCMI protocol specific definitions from common.h into a the new dedicated protocols.h header so that SCMI protocols core code can include only what it needs; this is going to be useful to avoid the risk of growing indefinitely the dimension of common.h, especially when introducing some common protocols helper functions. Header common.h will continue to be included by SCMI core and transport layers. Link: https://lore.kernel.org/r/20220330150551.2573938-10-cristian.marussi@arm.com Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 4 +- drivers/firmware/arm_scmi/common.h | 224 +----------------------------- drivers/firmware/arm_scmi/perf.c | 6 +- drivers/firmware/arm_scmi/power.c | 4 +- drivers/firmware/arm_scmi/protocols.h | 247 ++++++++++++++++++++++++++++++++++ drivers/firmware/arm_scmi/reset.c | 4 +- drivers/firmware/arm_scmi/sensors.c | 4 +- drivers/firmware/arm_scmi/system.c | 4 +- drivers/firmware/arm_scmi/voltage.c | 4 +- 9 files changed, 265 insertions(+), 236 deletions(-) create mode 100644 drivers/firmware/arm_scmi/protocols.h diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 9dff6a948674..40a6364e5578 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -2,13 +2,13 @@ /* * System Control and Management Interface (SCMI) Clock Protocol * - * Copyright (C) 2018-2021 ARM Ltd. + * Copyright (C) 2018-2022 ARM Ltd. */ #include #include -#include "common.h" +#include "protocols.h" enum scmi_clock_protocol_cmd { CLOCK_ATTRIBUTES = 0x3, diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index 6885348a505d..61aba7447c32 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -4,7 +4,7 @@ * driver common header file containing some definitions, structures * and function prototypes used in all the different SCMI protocols. * - * Copyright (C) 2018-2021 ARM Ltd. + * Copyright (C) 2018-2022 ARM Ltd. */ #ifndef _SCMI_COMMON_H #define _SCMI_COMMON_H @@ -24,37 +24,9 @@ #include +#include "protocols.h" #include "notify.h" -#define PROTOCOL_REV_MINOR_MASK GENMASK(15, 0) -#define PROTOCOL_REV_MAJOR_MASK GENMASK(31, 16) -#define PROTOCOL_REV_MAJOR(x) (u16)(FIELD_GET(PROTOCOL_REV_MAJOR_MASK, (x))) -#define PROTOCOL_REV_MINOR(x) (u16)(FIELD_GET(PROTOCOL_REV_MINOR_MASK, (x))) -#define MAX_OPPS 16 - -enum scmi_common_cmd { - PROTOCOL_VERSION = 0x0, - PROTOCOL_ATTRIBUTES = 0x1, - PROTOCOL_MESSAGE_ATTRIBUTES = 0x2, -}; - -/** - * struct scmi_msg_resp_prot_version - Response for a message - * - * @minor_version: Minor version of the ABI that firmware supports - * @major_version: Major version of the ABI that firmware supports - * - * In general, ABI version changes follow the rule that minor version increments - * are backward compatible. Major revision changes in ABI may not be - * backward compatible. - * - * Response to a generic message with message type SCMI_MSG_VERSION - */ -struct scmi_msg_resp_prot_version { - __le16 minor_version; - __le16 major_version; -}; - #define MSG_ID_MASK GENMASK(7, 0) #define MSG_XTRACT_ID(hdr) FIELD_GET(MSG_ID_MASK, (hdr)) #define MSG_TYPE_MASK GENMASK(9, 8) @@ -78,28 +50,6 @@ struct scmi_msg_resp_prot_version { */ #define SCMI_PENDING_XFERS_HT_ORDER_SZ 9 -/** - * struct scmi_msg_hdr - Message(Tx/Rx) header - * - * @id: The identifier of the message being sent - * @protocol_id: The identifier of the protocol used to send @id message - * @type: The SCMI type for this message - * @seq: The token to identify the message. When a message returns, the - * platform returns the whole message header unmodified including the - * token - * @status: Status of the transfer once it's complete - * @poll_completion: Indicate if the transfer needs to be polled for - * completion or interrupt mode is used - */ -struct scmi_msg_hdr { - u8 id; - u8 protocol_id; - u8 type; - u16 seq; - u32 status; - bool poll_completion; -}; - /** * pack_scmi_header() - packs and returns 32-bit header * @@ -129,72 +79,6 @@ static inline void unpack_scmi_header(u32 msg_hdr, struct scmi_msg_hdr *hdr) hdr->type = MSG_XTRACT_TYPE(msg_hdr); } -/** - * struct scmi_msg - Message(Tx/Rx) structure - * - * @buf: Buffer pointer - * @len: Length of data in the Buffer - */ -struct scmi_msg { - void *buf; - size_t len; -}; - -/** - * struct scmi_xfer - Structure representing a message flow - * - * @transfer_id: Unique ID for debug & profiling purpose - * @hdr: Transmit message header - * @tx: Transmit message - * @rx: Receive message, the buffer should be pre-allocated to store - * message. If request-ACK protocol is used, we can reuse the same - * buffer for the rx path as we use for the tx path. - * @done: command message transmit completion event - * @async_done: pointer to delayed response message received event completion - * @pending: True for xfers added to @pending_xfers hashtable - * @node: An hlist_node reference used to store this xfer, alternatively, on - * the free list @free_xfers or in the @pending_xfers hashtable - * @users: A refcount to track the active users for this xfer. - * This is meant to protect against the possibility that, when a command - * transaction times out concurrently with the reception of a valid - * response message, the xfer could be finally put on the TX path, and - * so vanish, while on the RX path scmi_rx_callback() is still - * processing it: in such a case this refcounting will ensure that, even - * though the timed-out transaction will anyway cause the command - * request to be reported as failed by time-out, the underlying xfer - * cannot be discarded and possibly reused until the last one user on - * the RX path has released it. - * @busy: An atomic flag to ensure exclusive write access to this xfer - * @state: The current state of this transfer, with states transitions deemed - * valid being: - * - SCMI_XFER_SENT_OK -> SCMI_XFER_RESP_OK [ -> SCMI_XFER_DRESP_OK ] - * - SCMI_XFER_SENT_OK -> SCMI_XFER_DRESP_OK - * (Missing synchronous response is assumed OK and ignored) - * @lock: A spinlock to protect state and busy fields. - * @priv: A pointer for transport private usage. - */ -struct scmi_xfer { - int transfer_id; - struct scmi_msg_hdr hdr; - struct scmi_msg tx; - struct scmi_msg rx; - struct completion done; - struct completion *async_done; - bool pending; - struct hlist_node node; - refcount_t users; -#define SCMI_XFER_FREE 0 -#define SCMI_XFER_BUSY 1 - atomic_t busy; -#define SCMI_XFER_SENT_OK 0 -#define SCMI_XFER_RESP_OK 1 -#define SCMI_XFER_DRESP_OK 2 - int state; - /* A lock to protect state and busy fields */ - spinlock_t lock; - void *priv; -}; - /* * An helper macro to lookup an xfer from the @pending_xfers hashtable * using the message sequence number token as a key. @@ -210,64 +94,6 @@ struct scmi_xfer { xfer_; \ }) -struct scmi_xfer_ops; - -/** - * struct scmi_protocol_handle - Reference to an initialized protocol instance - * - * @dev: A reference to the associated SCMI instance device (handle->dev). - * @xops: A reference to a struct holding refs to the core xfer operations that - * can be used by the protocol implementation to generate SCMI messages. - * @set_priv: A method to set protocol private data for this instance. - * @get_priv: A method to get protocol private data previously set. - * - * This structure represents a protocol initialized against specific SCMI - * instance and it will be used as follows: - * - as a parameter fed from the core to the protocol initialization code so - * that it can access the core xfer operations to build and generate SCMI - * messages exclusively for the specific underlying protocol instance. - * - as an opaque handle fed by an SCMI driver user when it tries to access - * this protocol through its own protocol operations. - * In this case this handle will be returned as an opaque object together - * with the related protocol operations when the SCMI driver tries to access - * the protocol. - */ -struct scmi_protocol_handle { - struct device *dev; - const struct scmi_xfer_ops *xops; - int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv); - void *(*get_priv)(const struct scmi_protocol_handle *ph); -}; - -/** - * struct scmi_xfer_ops - References to the core SCMI xfer operations. - * @version_get: Get this version protocol. - * @xfer_get_init: Initialize one struct xfer if any xfer slot is free. - * @reset_rx_to_maxsz: Reset rx size to max transport size. - * @do_xfer: Do the SCMI transfer. - * @do_xfer_with_response: Do the SCMI transfer waiting for a response. - * @xfer_put: Free the xfer slot. - * - * Note that all this operations expect a protocol handle as first parameter; - * they then internally use it to infer the underlying protocol number: this - * way is not possible for a protocol implementation to forge messages for - * another protocol. - */ -struct scmi_xfer_ops { - int (*version_get)(const struct scmi_protocol_handle *ph, u32 *version); - int (*xfer_get_init)(const struct scmi_protocol_handle *ph, u8 msg_id, - size_t tx_size, size_t rx_size, - struct scmi_xfer **p); - void (*reset_rx_to_maxsz)(const struct scmi_protocol_handle *ph, - struct scmi_xfer *xfer); - int (*do_xfer)(const struct scmi_protocol_handle *ph, - struct scmi_xfer *xfer); - int (*do_xfer_with_response)(const struct scmi_protocol_handle *ph, - struct scmi_xfer *xfer); - void (*xfer_put)(const struct scmi_protocol_handle *ph, - struct scmi_xfer *xfer); -}; - struct scmi_revision_info * scmi_revision_area_get(const struct scmi_protocol_handle *ph); int scmi_handle_put(const struct scmi_handle *handle); @@ -276,55 +102,9 @@ void scmi_set_handle(struct scmi_device *scmi_dev); void scmi_setup_protocol_implemented(const struct scmi_protocol_handle *ph, u8 *prot_imp); -typedef int (*scmi_prot_init_ph_fn_t)(const struct scmi_protocol_handle *); - -/** - * struct scmi_protocol - Protocol descriptor - * @id: Protocol ID. - * @owner: Module reference if any. - * @instance_init: Mandatory protocol initialization function. - * @instance_deinit: Optional protocol de-initialization function. - * @ops: Optional reference to the operations provided by the protocol and - * exposed in scmi_protocol.h. - * @events: An optional reference to the events supported by this protocol. - */ -struct scmi_protocol { - const u8 id; - struct module *owner; - const scmi_prot_init_ph_fn_t instance_init; - const scmi_prot_init_ph_fn_t instance_deinit; - const void *ops; - const struct scmi_protocol_events *events; -}; - int __init scmi_bus_init(void); void __exit scmi_bus_exit(void); -#define DECLARE_SCMI_REGISTER_UNREGISTER(func) \ - int __init scmi_##func##_register(void); \ - void __exit scmi_##func##_unregister(void) -DECLARE_SCMI_REGISTER_UNREGISTER(base); -DECLARE_SCMI_REGISTER_UNREGISTER(clock); -DECLARE_SCMI_REGISTER_UNREGISTER(perf); -DECLARE_SCMI_REGISTER_UNREGISTER(power); -DECLARE_SCMI_REGISTER_UNREGISTER(reset); -DECLARE_SCMI_REGISTER_UNREGISTER(sensors); -DECLARE_SCMI_REGISTER_UNREGISTER(voltage); -DECLARE_SCMI_REGISTER_UNREGISTER(system); - -#define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(name, proto) \ -static const struct scmi_protocol *__this_proto = &(proto); \ - \ -int __init scmi_##name##_register(void) \ -{ \ - return scmi_protocol_register(__this_proto); \ -} \ - \ -void __exit scmi_##name##_unregister(void) \ -{ \ - scmi_protocol_unregister(__this_proto); \ -} - const struct scmi_protocol *scmi_protocol_get(int protocol_id); void scmi_protocol_put(int protocol_id); diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index e9f68b91580c..0e9703310758 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -2,7 +2,7 @@ /* * System Control and Management Interface (SCMI) Performance Protocol * - * Copyright (C) 2018-2021 ARM Ltd. + * Copyright (C) 2018-2022 ARM Ltd. */ #define pr_fmt(fmt) "SCMI Notifications PERF - " fmt @@ -17,9 +17,11 @@ #include #include -#include "common.h" +#include "protocols.h" #include "notify.h" +#define MAX_OPPS 16 + enum scmi_performance_protocol_cmd { PERF_DOMAIN_ATTRIBUTES = 0x3, PERF_DESCRIBE_LEVELS = 0x4, diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c index 5c04d63e7ff1..e378a3eb0d07 100644 --- a/drivers/firmware/arm_scmi/power.c +++ b/drivers/firmware/arm_scmi/power.c @@ -2,7 +2,7 @@ /* * System Control and Management Interface (SCMI) Power Protocol * - * Copyright (C) 2018-2021 ARM Ltd. + * Copyright (C) 2018-2022 ARM Ltd. */ #define pr_fmt(fmt) "SCMI Notifications POWER - " fmt @@ -10,7 +10,7 @@ #include #include -#include "common.h" +#include "protocols.h" #include "notify.h" enum scmi_power_protocol_cmd { diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_scmi/protocols.h new file mode 100644 index 000000000000..b2a92d2b2986 --- /dev/null +++ b/drivers/firmware/arm_scmi/protocols.h @@ -0,0 +1,247 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * System Control and Management Interface (SCMI) Message Protocol + * protocols common header file containing some definitions, structures + * and function prototypes used in all the different SCMI protocols. + * + * Copyright (C) 2022 ARM Ltd. + */ +#ifndef _SCMI_PROTOCOLS_H +#define _SCMI_PROTOCOLS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PROTOCOL_REV_MINOR_MASK GENMASK(15, 0) +#define PROTOCOL_REV_MAJOR_MASK GENMASK(31, 16) +#define PROTOCOL_REV_MAJOR(x) ((u16)(FIELD_GET(PROTOCOL_REV_MAJOR_MASK, (x)))) +#define PROTOCOL_REV_MINOR(x) ((u16)(FIELD_GET(PROTOCOL_REV_MINOR_MASK, (x)))) + +enum scmi_common_cmd { + PROTOCOL_VERSION = 0x0, + PROTOCOL_ATTRIBUTES = 0x1, + PROTOCOL_MESSAGE_ATTRIBUTES = 0x2, +}; + +/** + * struct scmi_msg_resp_prot_version - Response for a message + * + * @minor_version: Minor version of the ABI that firmware supports + * @major_version: Major version of the ABI that firmware supports + * + * In general, ABI version changes follow the rule that minor version increments + * are backward compatible. Major revision changes in ABI may not be + * backward compatible. + * + * Response to a generic message with message type SCMI_MSG_VERSION + */ +struct scmi_msg_resp_prot_version { + __le16 minor_version; + __le16 major_version; +}; + +/** + * struct scmi_msg - Message(Tx/Rx) structure + * + * @buf: Buffer pointer + * @len: Length of data in the Buffer + */ +struct scmi_msg { + void *buf; + size_t len; +}; + +/** + * struct scmi_msg_hdr - Message(Tx/Rx) header + * + * @id: The identifier of the message being sent + * @protocol_id: The identifier of the protocol used to send @id message + * @type: The SCMI type for this message + * @seq: The token to identify the message. When a message returns, the + * platform returns the whole message header unmodified including the + * token + * @status: Status of the transfer once it's complete + * @poll_completion: Indicate if the transfer needs to be polled for + * completion or interrupt mode is used + */ +struct scmi_msg_hdr { + u8 id; + u8 protocol_id; + u8 type; + u16 seq; + u32 status; + bool poll_completion; +}; + +/** + * struct scmi_xfer - Structure representing a message flow + * + * @transfer_id: Unique ID for debug & profiling purpose + * @hdr: Transmit message header + * @tx: Transmit message + * @rx: Receive message, the buffer should be pre-allocated to store + * message. If request-ACK protocol is used, we can reuse the same + * buffer for the rx path as we use for the tx path. + * @done: command message transmit completion event + * @async_done: pointer to delayed response message received event completion + * @pending: True for xfers added to @pending_xfers hashtable + * @node: An hlist_node reference used to store this xfer, alternatively, on + * the free list @free_xfers or in the @pending_xfers hashtable + * @users: A refcount to track the active users for this xfer. + * This is meant to protect against the possibility that, when a command + * transaction times out concurrently with the reception of a valid + * response message, the xfer could be finally put on the TX path, and + * so vanish, while on the RX path scmi_rx_callback() is still + * processing it: in such a case this refcounting will ensure that, even + * though the timed-out transaction will anyway cause the command + * request to be reported as failed by time-out, the underlying xfer + * cannot be discarded and possibly reused until the last one user on + * the RX path has released it. + * @busy: An atomic flag to ensure exclusive write access to this xfer + * @state: The current state of this transfer, with states transitions deemed + * valid being: + * - SCMI_XFER_SENT_OK -> SCMI_XFER_RESP_OK [ -> SCMI_XFER_DRESP_OK ] + * - SCMI_XFER_SENT_OK -> SCMI_XFER_DRESP_OK + * (Missing synchronous response is assumed OK and ignored) + * @lock: A spinlock to protect state and busy fields. + * @priv: A pointer for transport private usage. + */ +struct scmi_xfer { + int transfer_id; + struct scmi_msg_hdr hdr; + struct scmi_msg tx; + struct scmi_msg rx; + struct completion done; + struct completion *async_done; + bool pending; + struct hlist_node node; + refcount_t users; +#define SCMI_XFER_FREE 0 +#define SCMI_XFER_BUSY 1 + atomic_t busy; +#define SCMI_XFER_SENT_OK 0 +#define SCMI_XFER_RESP_OK 1 +#define SCMI_XFER_DRESP_OK 2 + int state; + /* A lock to protect state and busy fields */ + spinlock_t lock; + void *priv; +}; + +struct scmi_xfer_ops; + +/** + * struct scmi_protocol_handle - Reference to an initialized protocol instance + * + * @dev: A reference to the associated SCMI instance device (handle->dev). + * @xops: A reference to a struct holding refs to the core xfer operations that + * can be used by the protocol implementation to generate SCMI messages. + * @set_priv: A method to set protocol private data for this instance. + * @get_priv: A method to get protocol private data previously set. + * + * This structure represents a protocol initialized against specific SCMI + * instance and it will be used as follows: + * - as a parameter fed from the core to the protocol initialization code so + * that it can access the core xfer operations to build and generate SCMI + * messages exclusively for the specific underlying protocol instance. + * - as an opaque handle fed by an SCMI driver user when it tries to access + * this protocol through its own protocol operations. + * In this case this handle will be returned as an opaque object together + * with the related protocol operations when the SCMI driver tries to access + * the protocol. + */ +struct scmi_protocol_handle { + struct device *dev; + const struct scmi_xfer_ops *xops; + int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv); + void *(*get_priv)(const struct scmi_protocol_handle *ph); +}; + +/** + * struct scmi_xfer_ops - References to the core SCMI xfer operations. + * @version_get: Get this version protocol. + * @xfer_get_init: Initialize one struct xfer if any xfer slot is free. + * @reset_rx_to_maxsz: Reset rx size to max transport size. + * @do_xfer: Do the SCMI transfer. + * @do_xfer_with_response: Do the SCMI transfer waiting for a response. + * @xfer_put: Free the xfer slot. + * + * Note that all this operations expect a protocol handle as first parameter; + * they then internally use it to infer the underlying protocol number: this + * way is not possible for a protocol implementation to forge messages for + * another protocol. + */ +struct scmi_xfer_ops { + int (*version_get)(const struct scmi_protocol_handle *ph, u32 *version); + int (*xfer_get_init)(const struct scmi_protocol_handle *ph, u8 msg_id, + size_t tx_size, size_t rx_size, + struct scmi_xfer **p); + void (*reset_rx_to_maxsz)(const struct scmi_protocol_handle *ph, + struct scmi_xfer *xfer); + int (*do_xfer)(const struct scmi_protocol_handle *ph, + struct scmi_xfer *xfer); + int (*do_xfer_with_response)(const struct scmi_protocol_handle *ph, + struct scmi_xfer *xfer); + void (*xfer_put)(const struct scmi_protocol_handle *ph, + struct scmi_xfer *xfer); +}; + +typedef int (*scmi_prot_init_ph_fn_t)(const struct scmi_protocol_handle *); + +/** + * struct scmi_protocol - Protocol descriptor + * @id: Protocol ID. + * @owner: Module reference if any. + * @instance_init: Mandatory protocol initialization function. + * @instance_deinit: Optional protocol de-initialization function. + * @ops: Optional reference to the operations provided by the protocol and + * exposed in scmi_protocol.h. + * @events: An optional reference to the events supported by this protocol. + */ +struct scmi_protocol { + const u8 id; + struct module *owner; + const scmi_prot_init_ph_fn_t instance_init; + const scmi_prot_init_ph_fn_t instance_deinit; + const void *ops; + const struct scmi_protocol_events *events; +}; + +#define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(name, proto) \ +static const struct scmi_protocol *__this_proto = &(proto); \ + \ +int __init scmi_##name##_register(void) \ +{ \ + return scmi_protocol_register(__this_proto); \ +} \ + \ +void __exit scmi_##name##_unregister(void) \ +{ \ + scmi_protocol_unregister(__this_proto); \ +} + +#define DECLARE_SCMI_REGISTER_UNREGISTER(func) \ + int __init scmi_##func##_register(void); \ + void __exit scmi_##func##_unregister(void) +DECLARE_SCMI_REGISTER_UNREGISTER(base); +DECLARE_SCMI_REGISTER_UNREGISTER(clock); +DECLARE_SCMI_REGISTER_UNREGISTER(perf); +DECLARE_SCMI_REGISTER_UNREGISTER(power); +DECLARE_SCMI_REGISTER_UNREGISTER(reset); +DECLARE_SCMI_REGISTER_UNREGISTER(sensors); +DECLARE_SCMI_REGISTER_UNREGISTER(voltage); +DECLARE_SCMI_REGISTER_UNREGISTER(system); + +#endif /* _SCMI_PROTOCOLS_H */ diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c index b823a9f64af9..cc465632aa1a 100644 --- a/drivers/firmware/arm_scmi/reset.c +++ b/drivers/firmware/arm_scmi/reset.c @@ -2,7 +2,7 @@ /* * System Control and Management Interface (SCMI) Reset Protocol * - * Copyright (C) 2019-2021 ARM Ltd. + * Copyright (C) 2019-2022 ARM Ltd. */ #define pr_fmt(fmt) "SCMI Notifications RESET - " fmt @@ -10,7 +10,7 @@ #include #include -#include "common.h" +#include "protocols.h" #include "notify.h" enum scmi_reset_protocol_cmd { diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c index f37ac9824a87..07c28e249f0c 100644 --- a/drivers/firmware/arm_scmi/sensors.c +++ b/drivers/firmware/arm_scmi/sensors.c @@ -2,7 +2,7 @@ /* * System Control and Management Interface (SCMI) Sensor Protocol * - * Copyright (C) 2018-2021 ARM Ltd. + * Copyright (C) 2018-2022 ARM Ltd. */ #define pr_fmt(fmt) "SCMI Notifications SENSOR - " fmt @@ -11,7 +11,7 @@ #include #include -#include "common.h" +#include "protocols.h" #include "notify.h" #define SCMI_MAX_NUM_SENSOR_AXIS 63 diff --git a/drivers/firmware/arm_scmi/system.c b/drivers/firmware/arm_scmi/system.c index cbfc19f7a463..220e399118ad 100644 --- a/drivers/firmware/arm_scmi/system.c +++ b/drivers/firmware/arm_scmi/system.c @@ -2,7 +2,7 @@ /* * System Control and Management Interface (SCMI) System Power Protocol * - * Copyright (C) 2020-2021 ARM Ltd. + * Copyright (C) 2020-2022 ARM Ltd. */ #define pr_fmt(fmt) "SCMI Notifications SYSTEM - " fmt @@ -10,7 +10,7 @@ #include #include -#include "common.h" +#include "protocols.h" #include "notify.h" #define SCMI_SYSTEM_NUM_SOURCES 1 diff --git a/drivers/firmware/arm_scmi/voltage.c b/drivers/firmware/arm_scmi/voltage.c index ac08e819088b..7aa887a7cbd2 100644 --- a/drivers/firmware/arm_scmi/voltage.c +++ b/drivers/firmware/arm_scmi/voltage.c @@ -2,13 +2,13 @@ /* * System Control and Management Interface (SCMI) Voltage Protocol * - * Copyright (C) 2020-2021 ARM Ltd. + * Copyright (C) 2020-2022 ARM Ltd. */ #include #include -#include "common.h" +#include "protocols.h" #define VOLTAGE_DOMS_NUM_MASK GENMASK(15, 0) #define REMAINING_LEVELS_MASK GENMASK(31, 16) -- cgit v1.2.3 From 5c873d120d8676b22e04ba1c7d9eba878b6c6484 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 30 Mar 2022 16:05:39 +0100 Subject: firmware: arm_scmi: Introduce a common SCMI v3.1 .extended_name_get helper Introduce a new set of common protocol operations bound to the protocol handle structure so that can be invoked by the protocol implementation code even when protocols are built as distinct loadable kernel module without the need of exporting new symbols, like already done with scmi_xfer_ops. Add at first, as new common protocol helper, an .extended_name_get helper which will ease implementation and will avoid code duplication when adding new SCMIv3.1 per-protocol _NAME_GET commands. Link: https://lore.kernel.org/r/20220330150551.2573938-11-cristian.marussi@arm.com Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 50 +++++++++++++++++++++++++++++++++++ drivers/firmware/arm_scmi/protocols.h | 15 +++++++++++ 2 files changed, 65 insertions(+) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index a284a3fa85eb..ae2fed4af133 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -1103,6 +1103,55 @@ static const struct scmi_xfer_ops xfer_ops = { .xfer_put = xfer_put, }; +struct scmi_msg_resp_domain_name_get { + __le32 flags; + u8 name[SCMI_MAX_STR_SIZE]; +}; + +/** + * scmi_common_extended_name_get - Common helper to get extended resources name + * @ph: A protocol handle reference. + * @cmd_id: The specific command ID to use. + * @res_id: The specific resource ID to use. + * @name: A pointer to the preallocated area where the retrieved name will be + * stored as a NULL terminated string. + * @len: The len in bytes of the @name char array. + * + * Return: 0 on Succcess + */ +static int scmi_common_extended_name_get(const struct scmi_protocol_handle *ph, + u8 cmd_id, u32 res_id, char *name, + size_t len) +{ + int ret; + struct scmi_xfer *t; + struct scmi_msg_resp_domain_name_get *resp; + + ret = ph->xops->xfer_get_init(ph, cmd_id, sizeof(res_id), + sizeof(*resp), &t); + if (ret) + goto out; + + put_unaligned_le32(res_id, t->tx.buf); + resp = t->rx.buf; + + ret = ph->xops->do_xfer(ph, t); + if (!ret) + strscpy(name, resp->name, len); + + ph->xops->xfer_put(ph, t); +out: + if (ret) + dev_warn(ph->dev, + "Failed to get extended name - id:%u (ret:%d). Using %s\n", + res_id, ret, name); + return ret; +} + +static const struct scmi_proto_helpers_ops helpers_ops = { + .extended_name_get = scmi_common_extended_name_get, +}; + /** * scmi_revision_area_get - Retrieve version memory area. * @@ -1163,6 +1212,7 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info, pi->handle = handle; pi->ph.dev = handle->dev; pi->ph.xops = &xfer_ops; + pi->ph.hops = &helpers_ops; pi->ph.set_priv = scmi_set_protocol_priv; pi->ph.get_priv = scmi_get_protocol_priv; refcount_set(&pi->users, 1); diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_scmi/protocols.h index b2a92d2b2986..5461fa333152 100644 --- a/drivers/firmware/arm_scmi/protocols.h +++ b/drivers/firmware/arm_scmi/protocols.h @@ -141,6 +141,7 @@ struct scmi_xfer { }; struct scmi_xfer_ops; +struct scmi_proto_helpers_ops; /** * struct scmi_protocol_handle - Reference to an initialized protocol instance @@ -165,10 +166,24 @@ struct scmi_xfer_ops; struct scmi_protocol_handle { struct device *dev; const struct scmi_xfer_ops *xops; + const struct scmi_proto_helpers_ops *hops; int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv); void *(*get_priv)(const struct scmi_protocol_handle *ph); }; +/** + * struct scmi_proto_helpers_ops - References to common protocol helpers + * @extended_name_get: A common helper function to retrieve extended naming + * for the specified resource using the specified command. + * Result is returned as a NULL terminated string in the + * pre-allocated area pointed to by @name with maximum + * capacity of @len bytes. + */ +struct scmi_proto_helpers_ops { + int (*extended_name_get)(const struct scmi_protocol_handle *ph, + u8 cmd_id, u32 res_id, char *name, size_t len); +}; + /** * struct scmi_xfer_ops - References to the core SCMI xfer operations. * @version_get: Get this version protocol. -- cgit v1.2.3 From b260fccaebdc2c838e62aaef24fedf497f181d10 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 30 Mar 2022 16:05:40 +0100 Subject: firmware: arm_scmi: Add SCMI v3.1 protocol extended names support Using the common protocol helper implementation add support for all new SCMIv3.1 extended names commands related to all protocols with the exception of SENSOR_AXIS_GET_NAME. Link: https://lore.kernel.org/r/20220330150551.2573938-12-cristian.marussi@arm.com Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 21 ++++++++++++++++++--- drivers/firmware/arm_scmi/perf.c | 22 ++++++++++++++++++---- drivers/firmware/arm_scmi/power.c | 25 ++++++++++++++++++++----- drivers/firmware/arm_scmi/protocols.h | 2 ++ drivers/firmware/arm_scmi/reset.c | 22 ++++++++++++++++++---- drivers/firmware/arm_scmi/sensors.c | 15 ++++++++++++++- drivers/firmware/arm_scmi/voltage.c | 14 +++++++++++++- include/linux/scmi_protocol.h | 2 +- 8 files changed, 104 insertions(+), 19 deletions(-) diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 40a6364e5578..7a031afff389 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -16,6 +16,7 @@ enum scmi_clock_protocol_cmd { CLOCK_RATE_SET = 0x5, CLOCK_RATE_GET = 0x6, CLOCK_CONFIG_SET = 0x7, + CLOCK_NAME_GET = 0x8, }; struct scmi_msg_resp_clock_protocol_attributes { @@ -27,7 +28,8 @@ struct scmi_msg_resp_clock_protocol_attributes { struct scmi_msg_resp_clock_attributes { __le32 attributes; #define CLOCK_ENABLE BIT(0) - u8 name[SCMI_MAX_STR_SIZE]; +#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(29)) + u8 name[SCMI_SHORT_NAME_MAX_SIZE]; __le32 clock_enable_latency; }; @@ -108,9 +110,11 @@ scmi_clock_protocol_attributes_get(const struct scmi_protocol_handle *ph, } static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph, - u32 clk_id, struct scmi_clock_info *clk) + u32 clk_id, struct scmi_clock_info *clk, + u32 version) { int ret; + u32 attributes; struct scmi_xfer *t; struct scmi_msg_resp_clock_attributes *attr; @@ -124,6 +128,7 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph, ret = ph->xops->do_xfer(ph, t); if (!ret) { + attributes = le32_to_cpu(attr->attributes); strlcpy(clk->name, attr->name, SCMI_MAX_STR_SIZE); /* Is optional field clock_enable_latency provided ? */ if (t->rx.len == sizeof(*attr)) @@ -132,6 +137,16 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph, } ph->xops->xfer_put(ph, t); + + /* + * If supported overwrite short name with the extended one; + * on error just carry on and use already provided short name. + */ + if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x2 && + SUPPORTS_EXTENDED_NAMES(attributes)) + ph->hops->extended_name_get(ph, CLOCK_NAME_GET, clk_id, + clk->name, SCMI_MAX_STR_SIZE); + return ret; } @@ -400,7 +415,7 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph) for (clkid = 0; clkid < cinfo->num_clocks; clkid++) { struct scmi_clock_info *clk = cinfo->clk + clkid; - ret = scmi_clock_attributes_get(ph, clkid, clk); + ret = scmi_clock_attributes_get(ph, clkid, clk, version); if (!ret) scmi_clock_describe_rates_get(ph, clkid, clk); } diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index 0e9703310758..9e046fd121b9 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -32,6 +32,7 @@ enum scmi_performance_protocol_cmd { PERF_NOTIFY_LIMITS = 0x9, PERF_NOTIFY_LEVEL = 0xa, PERF_DESCRIBE_FASTCHANNEL = 0xb, + PERF_DOMAIN_NAME_GET = 0xc, }; struct scmi_opp { @@ -56,10 +57,11 @@ struct scmi_msg_resp_perf_domain_attributes { #define SUPPORTS_PERF_LIMIT_NOTIFY(x) ((x) & BIT(29)) #define SUPPORTS_PERF_LEVEL_NOTIFY(x) ((x) & BIT(28)) #define SUPPORTS_PERF_FASTCHANNELS(x) ((x) & BIT(27)) +#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(26)) __le32 rate_limit_us; __le32 sustained_freq_khz; __le32 sustained_perf_level; - u8 name[SCMI_MAX_STR_SIZE]; + u8 name[SCMI_SHORT_NAME_MAX_SIZE]; }; struct scmi_msg_perf_describe_levels { @@ -209,9 +211,11 @@ static int scmi_perf_attributes_get(const struct scmi_protocol_handle *ph, static int scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph, - u32 domain, struct perf_dom_info *dom_info) + u32 domain, struct perf_dom_info *dom_info, + u32 version) { int ret; + u32 flags; struct scmi_xfer *t; struct scmi_msg_resp_perf_domain_attributes *attr; @@ -225,7 +229,7 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph, ret = ph->xops->do_xfer(ph, t); if (!ret) { - u32 flags = le32_to_cpu(attr->flags); + flags = le32_to_cpu(attr->flags); dom_info->set_limits = SUPPORTS_SET_LIMITS(flags); dom_info->set_perf = SUPPORTS_SET_PERF_LVL(flags); @@ -248,6 +252,16 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph, } ph->xops->xfer_put(ph, t); + + /* + * If supported overwrite short name with the extended one; + * on error just carry on and use already provided short name. + */ + if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 && + SUPPORTS_EXTENDED_NAMES(flags)) + ph->hops->extended_name_get(ph, PERF_DOMAIN_NAME_GET, domain, + dom_info->name, SCMI_MAX_STR_SIZE); + return ret; } @@ -902,7 +916,7 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph) for (domain = 0; domain < pinfo->num_domains; domain++) { struct perf_dom_info *dom = pinfo->dom_info + domain; - scmi_perf_domain_attributes_get(ph, domain, dom); + scmi_perf_domain_attributes_get(ph, domain, dom, version); scmi_perf_describe_levels_get(ph, domain, dom); if (dom->perf_fastchannels) diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c index e378a3eb0d07..964882cc8747 100644 --- a/drivers/firmware/arm_scmi/power.c +++ b/drivers/firmware/arm_scmi/power.c @@ -18,6 +18,7 @@ enum scmi_power_protocol_cmd { POWER_STATE_SET = 0x4, POWER_STATE_GET = 0x5, POWER_STATE_NOTIFY = 0x6, + POWER_DOMAIN_NAME_GET = 0x8, }; struct scmi_msg_resp_power_attributes { @@ -33,7 +34,8 @@ struct scmi_msg_resp_power_domain_attributes { #define SUPPORTS_STATE_SET_NOTIFY(x) ((x) & BIT(31)) #define SUPPORTS_STATE_SET_ASYNC(x) ((x) & BIT(30)) #define SUPPORTS_STATE_SET_SYNC(x) ((x) & BIT(29)) - u8 name[SCMI_MAX_STR_SIZE]; +#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(27)) + u8 name[SCMI_SHORT_NAME_MAX_SIZE]; }; struct scmi_power_set_state { @@ -97,9 +99,11 @@ static int scmi_power_attributes_get(const struct scmi_protocol_handle *ph, static int scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph, - u32 domain, struct power_dom_info *dom_info) + u32 domain, struct power_dom_info *dom_info, + u32 version) { int ret; + u32 flags; struct scmi_xfer *t; struct scmi_msg_resp_power_domain_attributes *attr; @@ -113,15 +117,26 @@ scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph, ret = ph->xops->do_xfer(ph, t); if (!ret) { - u32 flags = le32_to_cpu(attr->flags); + flags = le32_to_cpu(attr->flags); dom_info->state_set_notify = SUPPORTS_STATE_SET_NOTIFY(flags); dom_info->state_set_async = SUPPORTS_STATE_SET_ASYNC(flags); dom_info->state_set_sync = SUPPORTS_STATE_SET_SYNC(flags); strlcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE); } - ph->xops->xfer_put(ph, t); + + /* + * If supported overwrite short name with the extended one; + * on error just carry on and use already provided short name. + */ + if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 && + SUPPORTS_EXTENDED_NAMES(flags)) { + ph->hops->extended_name_get(ph, POWER_DOMAIN_NAME_GET, + domain, dom_info->name, + SCMI_MAX_STR_SIZE); + } + return ret; } @@ -308,7 +323,7 @@ static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph) for (domain = 0; domain < pinfo->num_domains; domain++) { struct power_dom_info *dom = pinfo->dom_info + domain; - scmi_power_domain_attributes_get(ph, domain, dom); + scmi_power_domain_attributes_get(ph, domain, dom, version); } pinfo->version = version; diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_scmi/protocols.h index 5461fa333152..60ea880b3855 100644 --- a/drivers/firmware/arm_scmi/protocols.h +++ b/drivers/firmware/arm_scmi/protocols.h @@ -24,6 +24,8 @@ #include +#define SCMI_SHORT_NAME_MAX_SIZE 16 + #define PROTOCOL_REV_MINOR_MASK GENMASK(15, 0) #define PROTOCOL_REV_MAJOR_MASK GENMASK(31, 16) #define PROTOCOL_REV_MAJOR(x) ((u16)(FIELD_GET(PROTOCOL_REV_MAJOR_MASK, (x)))) diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c index cc465632aa1a..a420a9102094 100644 --- a/drivers/firmware/arm_scmi/reset.c +++ b/drivers/firmware/arm_scmi/reset.c @@ -17,6 +17,7 @@ enum scmi_reset_protocol_cmd { RESET_DOMAIN_ATTRIBUTES = 0x3, RESET = 0x4, RESET_NOTIFY = 0x5, + RESET_DOMAIN_NAME_GET = 0x6, }; #define NUM_RESET_DOMAIN_MASK 0xffff @@ -26,8 +27,9 @@ struct scmi_msg_resp_reset_domain_attributes { __le32 attributes; #define SUPPORTS_ASYNC_RESET(x) ((x) & BIT(31)) #define SUPPORTS_NOTIFY_RESET(x) ((x) & BIT(30)) +#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(29)) __le32 latency; - u8 name[SCMI_MAX_STR_SIZE]; + u8 name[SCMI_SHORT_NAME_MAX_SIZE]; }; struct scmi_msg_reset_domain_reset { @@ -89,9 +91,11 @@ static int scmi_reset_attributes_get(const struct scmi_protocol_handle *ph, static int scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph, - u32 domain, struct reset_dom_info *dom_info) + u32 domain, struct reset_dom_info *dom_info, + u32 version) { int ret; + u32 attributes; struct scmi_xfer *t; struct scmi_msg_resp_reset_domain_attributes *attr; @@ -105,7 +109,7 @@ scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph, ret = ph->xops->do_xfer(ph, t); if (!ret) { - u32 attributes = le32_to_cpu(attr->attributes); + attributes = le32_to_cpu(attr->attributes); dom_info->async_reset = SUPPORTS_ASYNC_RESET(attributes); dom_info->reset_notify = SUPPORTS_NOTIFY_RESET(attributes); @@ -116,6 +120,16 @@ scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph, } ph->xops->xfer_put(ph, t); + + /* + * If supported overwrite short name with the extended one; + * on error just carry on and use already provided short name. + */ + if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 && + SUPPORTS_EXTENDED_NAMES(attributes)) + ph->hops->extended_name_get(ph, RESET_DOMAIN_NAME_GET, domain, + dom_info->name, SCMI_MAX_STR_SIZE); + return ret; } @@ -320,7 +334,7 @@ static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph) for (domain = 0; domain < pinfo->num_domains; domain++) { struct reset_dom_info *dom = pinfo->dom_info + domain; - scmi_reset_domain_attributes_get(ph, domain, dom); + scmi_reset_domain_attributes_get(ph, domain, dom, version); } pinfo->version = version; diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c index 07c28e249f0c..6fd8b3a874ea 100644 --- a/drivers/firmware/arm_scmi/sensors.c +++ b/drivers/firmware/arm_scmi/sensors.c @@ -27,6 +27,7 @@ enum scmi_sensor_protocol_cmd { SENSOR_CONFIG_GET = 0x9, SENSOR_CONFIG_SET = 0xA, SENSOR_CONTINUOUS_UPDATE_NOTIFY = 0xB, + SENSOR_NAME_GET = 0xC, }; struct scmi_msg_resp_sensor_attributes { @@ -71,6 +72,7 @@ struct scmi_msg_resp_sensor_description { __le32 attributes_low; /* Common attributes_low macros */ #define SUPPORTS_ASYNC_READ(x) FIELD_GET(BIT(31), (x)) +#define SUPPORTS_EXTENDED_NAMES(x) FIELD_GET(BIT(29), (x)) #define NUM_TRIP_POINTS(x) FIELD_GET(GENMASK(7, 0), (x)) __le32 attributes_high; /* Common attributes_high macros */ @@ -78,7 +80,7 @@ struct scmi_msg_resp_sensor_description { #define SENSOR_SCALE_SIGN BIT(4) #define SENSOR_SCALE_EXTEND GENMASK(31, 5) #define SENSOR_TYPE(x) FIELD_GET(GENMASK(7, 0), (x)) - u8 name[SCMI_MAX_STR_SIZE]; + u8 name[SCMI_SHORT_NAME_MAX_SIZE]; /* only for version > 2.0 */ __le32 power; __le32 resolution; @@ -519,6 +521,17 @@ static int scmi_sensor_description_get(const struct scmi_protocol_handle *ph, SCMI_MAX_NUM_SENSOR_AXIS); strlcpy(s->name, sdesc->name, SCMI_MAX_STR_SIZE); + /* + * If supported overwrite short name with the extended + * one; on error just carry on and use already provided + * short name. + */ + if (PROTOCOL_REV_MAJOR(si->version) >= 0x3 && + SUPPORTS_EXTENDED_NAMES(attrl)) + ph->hops->extended_name_get(ph, SENSOR_NAME_GET, + s->id, s->name, + SCMI_MAX_STR_SIZE); + if (s->extended_scalar_attrs) { s->sensor_power = le32_to_cpu(sdesc->power); dsize += sizeof(sdesc->power); diff --git a/drivers/firmware/arm_scmi/voltage.c b/drivers/firmware/arm_scmi/voltage.c index 7aa887a7cbd2..5d58ba724eeb 100644 --- a/drivers/firmware/arm_scmi/voltage.c +++ b/drivers/firmware/arm_scmi/voltage.c @@ -21,13 +21,15 @@ enum scmi_voltage_protocol_cmd { VOLTAGE_CONFIG_GET = 0x6, VOLTAGE_LEVEL_SET = 0x7, VOLTAGE_LEVEL_GET = 0x8, + VOLTAGE_DOMAIN_NAME_GET = 0x09, }; #define NUM_VOLTAGE_DOMAINS(x) ((u16)(FIELD_GET(VOLTAGE_DOMS_NUM_MASK, (x)))) struct scmi_msg_resp_domain_attributes { __le32 attr; - u8 name[SCMI_MAX_STR_SIZE]; +#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(30)) + u8 name[SCMI_SHORT_NAME_MAX_SIZE]; }; struct scmi_msg_cmd_describe_levels { @@ -149,6 +151,16 @@ static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph, v->attributes = le32_to_cpu(resp_dom->attr); strlcpy(v->name, resp_dom->name, SCMI_MAX_STR_SIZE); + /* + * If supported overwrite short name with the extended one; + * on error just carry on and use already provided short name. + */ + if (PROTOCOL_REV_MAJOR(vinfo->version) >= 0x2 && + SUPPORTS_EXTENDED_NAMES(v->attributes)) + ph->hops->extended_name_get(ph, VOLTAGE_DOMAIN_NAME_GET, + v->id, v->name, + SCMI_MAX_STR_SIZE); + cmd = tl->tx.buf; /* ...then retrieve domain levels descriptions */ do { diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index ced37d1de1fe..56e6f13355b8 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -13,7 +13,7 @@ #include #include -#define SCMI_MAX_STR_SIZE 16 +#define SCMI_MAX_STR_SIZE 64 #define SCMI_MAX_NUM_RATES 16 /** -- cgit v1.2.3 From 7ad6b6ccbafc1faa04ca9d26635bdd5e3e240a69 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 28 Apr 2022 13:29:13 +0100 Subject: firmware: arm_scmi: Set clock latency to U32_MAX if it is not supported As per the spec, the clock_enable_delay is the worst case latency incurred by the platform to enable the clock. The value of 0 indicates that the platform doesn't support the same and must be considered as maximum latency for practical purposes. Currently the value of 0 is assigned as is and is propogated to the clock framework which can assume that the clock can support atomic enable operation. Link: https://lore.kernel.org/r/20220428122913.1654821-1-sudeep.holla@arm.com Fixes: 18f295b758b2 ("firmware: arm_scmi: Add support for clock_enable_latency") Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 7a031afff389..81f739700893 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -6,6 +6,7 @@ */ #include +#include #include #include "protocols.h" @@ -128,12 +129,13 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph, ret = ph->xops->do_xfer(ph, t); if (!ret) { + u32 latency = 0; attributes = le32_to_cpu(attr->attributes); strlcpy(clk->name, attr->name, SCMI_MAX_STR_SIZE); /* Is optional field clock_enable_latency provided ? */ if (t->rx.len == sizeof(*attr)) - clk->enable_latency = - le32_to_cpu(attr->clock_enable_latency); + latency = le32_to_cpu(attr->clock_enable_latency); + clk->enable_latency = latency ? : U32_MAX; } ph->xops->xfer_put(ph, t); -- cgit v1.2.3 From df3576d14a4598534d1a28276ded5ba2545d5e4c Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 30 Mar 2022 16:05:41 +0100 Subject: firmware: arm_scmi: Parse clock_enable_latency conditionally The clock_enable_latency field in CLOCK_ATTRIBUTES response message has been added only since SCMI v3.1. Use the advertised SCMI clock protocol version as a proper condition check for parsing it, instead of the bare message length lookup. Link: https://lore.kernel.org/r/20220330150551.2573938-13-cristian.marussi@arm.com Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 81f739700893..cf26f18b85fc 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -132,8 +132,8 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph, u32 latency = 0; attributes = le32_to_cpu(attr->attributes); strlcpy(clk->name, attr->name, SCMI_MAX_STR_SIZE); - /* Is optional field clock_enable_latency provided ? */ - if (t->rx.len == sizeof(*attr)) + /* clock_enable_latency field is present only since SCMI v3.1 */ + if (PROTOCOL_REV_MAJOR(version) >= 0x2) latency = le32_to_cpu(attr->clock_enable_latency); clk->enable_latency = latency ? : U32_MAX; } -- cgit v1.2.3 From 36b6ea0fc6bcbc618fe20d33a3b529a6d0653d99 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 30 Mar 2022 16:05:42 +0100 Subject: firmware: arm_scmi: Add iterators for multi-part commands SCMI specification defines some commands as optionally issued over multiple messages in order to overcome possible limitations in payload size enforced by the configured underlyinng transport. Introduce some common protocol helpers to provide a unified solution for issuing such SCMI multi-part commands. Link: https://lore.kernel.org/r/20220330150551.2573938-14-cristian.marussi@arm.com Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 109 ++++++++++++++++++++++++++++++++++ drivers/firmware/arm_scmi/protocols.h | 54 +++++++++++++++++ 2 files changed, 163 insertions(+) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index ae2fed4af133..c4960fd3df75 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -1148,8 +1148,117 @@ out: return ret; } +/** + * struct scmi_iterator - Iterator descriptor + * @msg: A reference to the message TX buffer; filled by @prepare_message with + * a proper custom command payload for each multi-part command request. + * @resp: A reference to the response RX buffer; used by @update_state and + * @process_response to parse the multi-part replies. + * @t: A reference to the underlying xfer initialized and used transparently by + * the iterator internal routines. + * @ph: A reference to the associated protocol handle to be used. + * @ops: A reference to the custom provided iterator operations. + * @state: The current iterator state; used and updated in turn by the iterators + * internal routines and by the caller-provided @scmi_iterator_ops. + * @priv: A reference to optional private data as provided by the caller and + * passed back to the @@scmi_iterator_ops. + */ +struct scmi_iterator { + void *msg; + void *resp; + struct scmi_xfer *t; + const struct scmi_protocol_handle *ph; + struct scmi_iterator_ops *ops; + struct scmi_iterator_state state; + void *priv; +}; + +static void *scmi_iterator_init(const struct scmi_protocol_handle *ph, + struct scmi_iterator_ops *ops, + unsigned int max_resources, u8 msg_id, + size_t tx_size, void *priv) +{ + int ret; + struct scmi_iterator *i; + + i = devm_kzalloc(ph->dev, sizeof(*i), GFP_KERNEL); + if (!i) + return ERR_PTR(-ENOMEM); + + i->ph = ph; + i->ops = ops; + i->priv = priv; + + ret = ph->xops->xfer_get_init(ph, msg_id, tx_size, 0, &i->t); + if (ret) { + devm_kfree(ph->dev, i); + return ERR_PTR(ret); + } + + i->state.max_resources = max_resources; + i->msg = i->t->tx.buf; + i->resp = i->t->rx.buf; + + return i; +} + +static int scmi_iterator_run(void *iter) +{ + int ret = -EINVAL; + struct scmi_iterator *i = iter; + struct scmi_iterator_state *st = &i->state; + struct scmi_iterator_ops *iops = i->ops; + const struct scmi_protocol_handle *ph = i->ph; + const struct scmi_xfer_ops *xops = ph->xops; + + if (!i) + return ret; + + do { + iops->prepare_message(i->msg, st->desc_index, i->priv); + ret = xops->do_xfer(ph, i->t); + if (ret) + break; + + ret = iops->update_state(st, i->resp, i->priv); + if (ret) + break; + + if (st->num_returned > st->max_resources - st->desc_index) { + dev_err(ph->dev, + "No. of resources can't exceed %d\n", + st->max_resources); + ret = -EINVAL; + break; + } + + for (st->loop_idx = 0; st->loop_idx < st->num_returned; + st->loop_idx++) { + ret = iops->process_response(ph, i->resp, st, i->priv); + if (ret) + goto out; + } + + st->desc_index += st->num_returned; + xops->reset_rx_to_maxsz(ph, i->t); + /* + * check for both returned and remaining to avoid infinite + * loop due to buggy firmware + */ + } while (st->num_returned && st->num_remaining); + +out: + /* Finalize and destroy iterator */ + xops->xfer_put(ph, i->t); + devm_kfree(ph->dev, i); + + return ret; +} + static const struct scmi_proto_helpers_ops helpers_ops = { .extended_name_get = scmi_common_extended_name_get, + .iter_response_init = scmi_iterator_init, + .iter_response_run = scmi_iterator_run, }; /** diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_scmi/protocols.h index 60ea880b3855..73304af5ec4a 100644 --- a/drivers/firmware/arm_scmi/protocols.h +++ b/drivers/firmware/arm_scmi/protocols.h @@ -173,6 +173,47 @@ struct scmi_protocol_handle { void *(*get_priv)(const struct scmi_protocol_handle *ph); }; +/** + * struct scmi_iterator_state - Iterator current state descriptor + * @desc_index: Starting index for the current mulit-part request. + * @num_returned: Number of returned items in the last multi-part reply. + * @num_remaining: Number of remaining items in the multi-part message. + * @max_resources: Maximum acceptable number of items, configured by the caller + * depending on the underlying resources that it is querying. + * @loop_idx: The iterator loop index in the current multi-part reply. + * @priv: Optional pointer to some additional state-related private data setup + * by the caller during the iterations. + */ +struct scmi_iterator_state { + unsigned int desc_index; + unsigned int num_returned; + unsigned int num_remaining; + unsigned int max_resources; + unsigned int loop_idx; + void *priv; +}; + +/** + * struct scmi_iterator_ops - Custom iterator operations + * @prepare_message: An operation to provide the custom logic to fill in the + * SCMI command request pointed by @message. @desc_index is + * a reference to the next index to use in the multi-part + * request. + * @update_state: An operation to provide the custom logic to update the + * iterator state from the actual message response. + * @process_response: An operation to provide the custom logic needed to process + * each chunk of the multi-part message. + */ +struct scmi_iterator_ops { + void (*prepare_message)(void *message, unsigned int desc_index, + const void *priv); + int (*update_state)(struct scmi_iterator_state *st, + const void *response, void *priv); + int (*process_response)(const struct scmi_protocol_handle *ph, + const void *response, + struct scmi_iterator_state *st, void *priv); +}; + /** * struct scmi_proto_helpers_ops - References to common protocol helpers * @extended_name_get: A common helper function to retrieve extended naming @@ -180,10 +221,23 @@ struct scmi_protocol_handle { * Result is returned as a NULL terminated string in the * pre-allocated area pointed to by @name with maximum * capacity of @len bytes. + * @iter_response_init: A common helper to initialize a generic iterator to + * parse multi-message responses: when run the iterator + * will take care to send the initial command request as + * specified by @msg_id and @tx_size and then to parse the + * multi-part responses using the custom operations + * provided in @ops. + * @iter_response_run: A common helper to trigger the run of a previously + * initialized iterator. */ struct scmi_proto_helpers_ops { int (*extended_name_get)(const struct scmi_protocol_handle *ph, u8 cmd_id, u32 res_id, char *name, size_t len); + void *(*iter_response_init)(const struct scmi_protocol_handle *ph, + struct scmi_iterator_ops *ops, + unsigned int max_resources, u8 msg_id, + size_t tx_size, void *priv); + int (*iter_response_run)(void *iter); }; /** -- cgit v1.2.3 From 7cab537704ec03260208ed5f4ad54accb635164c Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 30 Mar 2022 16:05:43 +0100 Subject: firmware: arm_scmi: Use common iterators in the sensor protocol Make SCMI sensor protocol use the common iterator protocol helpers for issuing the multi-part commands. Link: https://lore.kernel.org/r/20220330150551.2573938-15-cristian.marussi@arm.com Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/sensors.c | 582 ++++++++++++++++++------------------ 1 file changed, 297 insertions(+), 285 deletions(-) diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c index 6fd8b3a874ea..e1a94463d7d8 100644 --- a/drivers/firmware/arm_scmi/sensors.c +++ b/drivers/firmware/arm_scmi/sensors.c @@ -64,6 +64,10 @@ struct scmi_msg_resp_attrs { __le32 max_range_high; }; +struct scmi_msg_sensor_description { + __le32 desc_index; +}; + struct scmi_msg_resp_sensor_description { __le16 num_returned; __le16 num_remaining; @@ -233,346 +237,354 @@ static int scmi_sensor_attributes_get(const struct scmi_protocol_handle *ph, } static inline void scmi_parse_range_attrs(struct scmi_range_attrs *out, - struct scmi_msg_resp_attrs *in) + const struct scmi_msg_resp_attrs *in) { out->min_range = get_unaligned_le64((void *)&in->min_range_low); out->max_range = get_unaligned_le64((void *)&in->max_range_low); } -static int scmi_sensor_update_intervals(const struct scmi_protocol_handle *ph, - struct scmi_sensor_info *s) -{ - int ret, cnt; - u32 desc_index = 0; - u16 num_returned, num_remaining; - struct scmi_xfer *ti; - struct scmi_msg_resp_sensor_list_update_intervals *buf; - struct scmi_msg_sensor_list_update_intervals *msg; - - ret = ph->xops->xfer_get_init(ph, SENSOR_LIST_UPDATE_INTERVALS, - sizeof(*msg), 0, &ti); - if (ret) - return ret; - - buf = ti->rx.buf; - do { - u32 flags; - - msg = ti->tx.buf; - /* Set the number of sensors to be skipped/already read */ - msg->id = cpu_to_le32(s->id); - msg->index = cpu_to_le32(desc_index); +struct scmi_sens_ipriv { + void *priv; + struct device *dev; +}; - ret = ph->xops->do_xfer(ph, ti); - if (ret) - break; +static void iter_intervals_prepare_message(void *message, + unsigned int desc_index, + const void *p) +{ + struct scmi_msg_sensor_list_update_intervals *msg = message; + const struct scmi_sensor_info *s; - flags = le32_to_cpu(buf->num_intervals_flags); - num_returned = NUM_INTERVALS_RETURNED(flags); - num_remaining = NUM_INTERVALS_REMAINING(flags); + s = ((const struct scmi_sens_ipriv *)p)->priv; + /* Set the number of sensors to be skipped/already read */ + msg->id = cpu_to_le32(s->id); + msg->index = cpu_to_le32(desc_index); +} - /* - * Max intervals is not declared previously anywhere so we - * assume it's returned+remaining. - */ - if (!s->intervals.count) { - s->intervals.segmented = SEGMENTED_INTVL_FORMAT(flags); - s->intervals.count = num_returned + num_remaining; - /* segmented intervals are reported in one triplet */ - if (s->intervals.segmented && - (num_remaining || num_returned != 3)) { - dev_err(ph->dev, - "Sensor ID:%d advertises an invalid segmented interval (%d)\n", - s->id, s->intervals.count); +static int iter_intervals_update_state(struct scmi_iterator_state *st, + const void *response, void *p) +{ + u32 flags; + struct scmi_sensor_info *s = ((struct scmi_sens_ipriv *)p)->priv; + struct device *dev = ((struct scmi_sens_ipriv *)p)->dev; + const struct scmi_msg_resp_sensor_list_update_intervals *r = response; + + flags = le32_to_cpu(r->num_intervals_flags); + st->num_returned = NUM_INTERVALS_RETURNED(flags); + st->num_remaining = NUM_INTERVALS_REMAINING(flags); + + /* + * Max intervals is not declared previously anywhere so we + * assume it's returned+remaining on first call. + */ + if (!st->max_resources) { + s->intervals.segmented = SEGMENTED_INTVL_FORMAT(flags); + s->intervals.count = st->num_returned + st->num_remaining; + /* segmented intervals are reported in one triplet */ + if (s->intervals.segmented && + (st->num_remaining || st->num_returned != 3)) { + dev_err(dev, + "Sensor ID:%d advertises an invalid segmented interval (%d)\n", + s->id, s->intervals.count); + s->intervals.segmented = false; + s->intervals.count = 0; + return -EINVAL; + } + /* Direct allocation when exceeding pre-allocated */ + if (s->intervals.count >= SCMI_MAX_PREALLOC_POOL) { + s->intervals.desc = + devm_kcalloc(dev, + s->intervals.count, + sizeof(*s->intervals.desc), + GFP_KERNEL); + if (!s->intervals.desc) { s->intervals.segmented = false; s->intervals.count = 0; - ret = -EINVAL; - break; - } - /* Direct allocation when exceeding pre-allocated */ - if (s->intervals.count >= SCMI_MAX_PREALLOC_POOL) { - s->intervals.desc = - devm_kcalloc(ph->dev, - s->intervals.count, - sizeof(*s->intervals.desc), - GFP_KERNEL); - if (!s->intervals.desc) { - s->intervals.segmented = false; - s->intervals.count = 0; - ret = -ENOMEM; - break; - } + return -ENOMEM; } - } else if (desc_index + num_returned > s->intervals.count) { - dev_err(ph->dev, - "No. of update intervals can't exceed %d\n", - s->intervals.count); - ret = -EINVAL; - break; } - for (cnt = 0; cnt < num_returned; cnt++) - s->intervals.desc[desc_index + cnt] = - le32_to_cpu(buf->intervals[cnt]); + st->max_resources = s->intervals.count; + } - desc_index += num_returned; + return 0; +} - ph->xops->reset_rx_to_maxsz(ph, ti); - /* - * check for both returned and remaining to avoid infinite - * loop due to buggy firmware - */ - } while (num_returned && num_remaining); +static int +iter_intervals_process_response(const struct scmi_protocol_handle *ph, + const void *response, + struct scmi_iterator_state *st, void *p) +{ + const struct scmi_msg_resp_sensor_list_update_intervals *r = response; + struct scmi_sensor_info *s = ((struct scmi_sens_ipriv *)p)->priv; - ph->xops->xfer_put(ph, ti); - return ret; + s->intervals.desc[st->desc_index + st->loop_idx] = + le32_to_cpu(r->intervals[st->loop_idx]); + + return 0; } -static int scmi_sensor_axis_description(const struct scmi_protocol_handle *ph, +static int scmi_sensor_update_intervals(const struct scmi_protocol_handle *ph, struct scmi_sensor_info *s) { - int ret, cnt; - u32 desc_index = 0; - u16 num_returned, num_remaining; - struct scmi_xfer *te; - struct scmi_msg_resp_sensor_axis_description *buf; - struct scmi_msg_sensor_axis_description_get *msg; + void *iter; + struct scmi_msg_sensor_list_update_intervals *msg; + struct scmi_iterator_ops ops = { + .prepare_message = iter_intervals_prepare_message, + .update_state = iter_intervals_update_state, + .process_response = iter_intervals_process_response, + }; + struct scmi_sens_ipriv upriv = { + .priv = s, + .dev = ph->dev, + }; + + iter = ph->hops->iter_response_init(ph, &ops, s->intervals.count, + SENSOR_LIST_UPDATE_INTERVALS, + sizeof(*msg), &upriv); + if (IS_ERR(iter)) + return PTR_ERR(iter); + + return ph->hops->iter_response_run(iter); +} - s->axis = devm_kcalloc(ph->dev, s->num_axis, - sizeof(*s->axis), GFP_KERNEL); - if (!s->axis) - return -ENOMEM; +static void iter_axes_desc_prepare_message(void *message, + const unsigned int desc_index, + const void *priv) +{ + struct scmi_msg_sensor_axis_description_get *msg = message; + const struct scmi_sensor_info *s = priv; - ret = ph->xops->xfer_get_init(ph, SENSOR_AXIS_DESCRIPTION_GET, - sizeof(*msg), 0, &te); - if (ret) - return ret; + /* Set the number of sensors to be skipped/already read */ + msg->id = cpu_to_le32(s->id); + msg->axis_desc_index = cpu_to_le32(desc_index); +} - buf = te->rx.buf; - do { - u32 flags; - struct scmi_axis_descriptor *adesc; +static int +iter_axes_desc_update_state(struct scmi_iterator_state *st, + const void *response, void *priv) +{ + u32 flags; + const struct scmi_msg_resp_sensor_axis_description *r = response; - msg = te->tx.buf; - /* Set the number of sensors to be skipped/already read */ - msg->id = cpu_to_le32(s->id); - msg->axis_desc_index = cpu_to_le32(desc_index); + flags = le32_to_cpu(r->num_axis_flags); + st->num_returned = NUM_AXIS_RETURNED(flags); + st->num_remaining = NUM_AXIS_REMAINING(flags); + st->priv = (void *)&r->desc[0]; - ret = ph->xops->do_xfer(ph, te); - if (ret) - break; + return 0; +} - flags = le32_to_cpu(buf->num_axis_flags); - num_returned = NUM_AXIS_RETURNED(flags); - num_remaining = NUM_AXIS_REMAINING(flags); +static int +iter_axes_desc_process_response(const struct scmi_protocol_handle *ph, + const void *response, + struct scmi_iterator_state *st, void *priv) +{ + u32 attrh, attrl; + struct scmi_sensor_axis_info *a; + size_t dsize = SCMI_MSG_RESP_AXIS_DESCR_BASE_SZ; + struct scmi_sensor_info *s = priv; + const struct scmi_axis_descriptor *adesc = st->priv; - if (desc_index + num_returned > s->num_axis) { - dev_err(ph->dev, "No. of axis can't exceed %d\n", - s->num_axis); - break; - } + attrl = le32_to_cpu(adesc->attributes_low); - adesc = &buf->desc[0]; - for (cnt = 0; cnt < num_returned; cnt++) { - u32 attrh, attrl; - struct scmi_sensor_axis_info *a; - size_t dsize = SCMI_MSG_RESP_AXIS_DESCR_BASE_SZ; + a = &s->axis[st->desc_index + st->loop_idx]; + a->id = le32_to_cpu(adesc->id); + a->extended_attrs = SUPPORTS_EXTEND_ATTRS(attrl); - attrl = le32_to_cpu(adesc->attributes_low); + attrh = le32_to_cpu(adesc->attributes_high); - a = &s->axis[desc_index + cnt]; + a->scale = S32_EXT(SENSOR_SCALE(attrh)); + a->type = SENSOR_TYPE(attrh); + strscpy(a->name, adesc->name, SCMI_MAX_STR_SIZE); - a->id = le32_to_cpu(adesc->id); - a->extended_attrs = SUPPORTS_EXTEND_ATTRS(attrl); + if (a->extended_attrs) { + unsigned int ares = le32_to_cpu(adesc->resolution); - attrh = le32_to_cpu(adesc->attributes_high); - a->scale = S32_EXT(SENSOR_SCALE(attrh)); - a->type = SENSOR_TYPE(attrh); - strlcpy(a->name, adesc->name, SCMI_MAX_STR_SIZE); + a->resolution = SENSOR_RES(ares); + a->exponent = S32_EXT(SENSOR_RES_EXP(ares)); + dsize += sizeof(adesc->resolution); - if (a->extended_attrs) { - unsigned int ares = - le32_to_cpu(adesc->resolution); + scmi_parse_range_attrs(&a->attrs, &adesc->attrs); + dsize += sizeof(adesc->attrs); + } - a->resolution = SENSOR_RES(ares); - a->exponent = - S32_EXT(SENSOR_RES_EXP(ares)); - dsize += sizeof(adesc->resolution); + st->priv = ((u8 *)adesc + dsize); - scmi_parse_range_attrs(&a->attrs, - &adesc->attrs); - dsize += sizeof(adesc->attrs); - } + return 0; +} - adesc = (typeof(adesc))((u8 *)adesc + dsize); - } +static int scmi_sensor_axis_description(const struct scmi_protocol_handle *ph, + struct scmi_sensor_info *s) +{ + void *iter; + struct scmi_msg_sensor_axis_description_get *msg; + struct scmi_iterator_ops ops = { + .prepare_message = iter_axes_desc_prepare_message, + .update_state = iter_axes_desc_update_state, + .process_response = iter_axes_desc_process_response, + }; - desc_index += num_returned; + s->axis = devm_kcalloc(ph->dev, s->num_axis, + sizeof(*s->axis), GFP_KERNEL); + if (!s->axis) + return -ENOMEM; - ph->xops->reset_rx_to_maxsz(ph, te); - /* - * check for both returned and remaining to avoid infinite - * loop due to buggy firmware - */ - } while (num_returned && num_remaining); + iter = ph->hops->iter_response_init(ph, &ops, s->num_axis, + SENSOR_AXIS_DESCRIPTION_GET, + sizeof(*msg), s); + if (IS_ERR(iter)) + return PTR_ERR(iter); - ph->xops->xfer_put(ph, te); - return ret; + return ph->hops->iter_response_run(iter); } -static int scmi_sensor_description_get(const struct scmi_protocol_handle *ph, - struct sensors_info *si) +static void iter_sens_descr_prepare_message(void *message, + unsigned int desc_index, + const void *priv) { - int ret, cnt; - u32 desc_index = 0; - u16 num_returned, num_remaining; - struct scmi_xfer *t; - struct scmi_msg_resp_sensor_description *buf; - - ret = ph->xops->xfer_get_init(ph, SENSOR_DESCRIPTION_GET, - sizeof(__le32), 0, &t); - if (ret) - return ret; - - buf = t->rx.buf; - - do { - struct scmi_sensor_descriptor *sdesc; + struct scmi_msg_sensor_description *msg = message; - /* Set the number of sensors to be skipped/already read */ - put_unaligned_le32(desc_index, t->tx.buf); + msg->desc_index = cpu_to_le32(desc_index); +} - ret = ph->xops->do_xfer(ph, t); - if (ret) - break; +static int iter_sens_descr_update_state(struct scmi_iterator_state *st, + const void *response, void *priv) +{ + const struct scmi_msg_resp_sensor_description *r = response; - num_returned = le16_to_cpu(buf->num_returned); - num_remaining = le16_to_cpu(buf->num_remaining); + st->num_returned = le16_to_cpu(r->num_returned); + st->num_remaining = le16_to_cpu(r->num_remaining); + st->priv = (void *)&r->desc[0]; - if (desc_index + num_returned > si->num_sensors) { - dev_err(ph->dev, "No. of sensors can't exceed %d", - si->num_sensors); - break; - } + return 0; +} - sdesc = &buf->desc[0]; - for (cnt = 0; cnt < num_returned; cnt++) { - u32 attrh, attrl; - struct scmi_sensor_info *s; - size_t dsize = SCMI_MSG_RESP_SENS_DESCR_BASE_SZ; - - s = &si->sensors[desc_index + cnt]; - s->id = le32_to_cpu(sdesc->id); - - attrl = le32_to_cpu(sdesc->attributes_low); - /* common bitfields parsing */ - s->async = SUPPORTS_ASYNC_READ(attrl); - s->num_trip_points = NUM_TRIP_POINTS(attrl); - /** - * only SCMIv3.0 specific bitfield below. - * Such bitfields are assumed to be zeroed on non - * relevant fw versions...assuming fw not buggy ! - */ - s->update = SUPPORTS_UPDATE_NOTIFY(attrl); - s->timestamped = SUPPORTS_TIMESTAMP(attrl); - if (s->timestamped) - s->tstamp_scale = - S32_EXT(SENSOR_TSTAMP_EXP(attrl)); - s->extended_scalar_attrs = - SUPPORTS_EXTEND_ATTRS(attrl); - - attrh = le32_to_cpu(sdesc->attributes_high); - /* common bitfields parsing */ - s->scale = S32_EXT(SENSOR_SCALE(attrh)); - s->type = SENSOR_TYPE(attrh); - /* Use pre-allocated pool wherever possible */ - s->intervals.desc = s->intervals.prealloc_pool; - if (si->version == SCMIv2_SENSOR_PROTOCOL) { - s->intervals.segmented = false; - s->intervals.count = 1; - /* - * Convert SCMIv2.0 update interval format to - * SCMIv3.0 to be used as the common exposed - * descriptor, accessible via common macros. - */ - s->intervals.desc[0] = - (SENSOR_UPDATE_BASE(attrh) << 5) | - SENSOR_UPDATE_SCALE(attrh); - } else { - /* - * From SCMIv3.0 update intervals are retrieved - * via a dedicated (optional) command. - * Since the command is optional, on error carry - * on without any update interval. - */ - if (scmi_sensor_update_intervals(ph, s)) - dev_dbg(ph->dev, - "Update Intervals not available for sensor ID:%d\n", - s->id); - } - /** - * only > SCMIv2.0 specific bitfield below. - * Such bitfields are assumed to be zeroed on non - * relevant fw versions...assuming fw not buggy ! - */ - s->num_axis = min_t(unsigned int, - SUPPORTS_AXIS(attrh) ? - SENSOR_AXIS_NUMBER(attrh) : 0, - SCMI_MAX_NUM_SENSOR_AXIS); - strlcpy(s->name, sdesc->name, SCMI_MAX_STR_SIZE); - - /* - * If supported overwrite short name with the extended - * one; on error just carry on and use already provided - * short name. - */ - if (PROTOCOL_REV_MAJOR(si->version) >= 0x3 && - SUPPORTS_EXTENDED_NAMES(attrl)) - ph->hops->extended_name_get(ph, SENSOR_NAME_GET, - s->id, s->name, - SCMI_MAX_STR_SIZE); - - if (s->extended_scalar_attrs) { - s->sensor_power = le32_to_cpu(sdesc->power); - dsize += sizeof(sdesc->power); - /* Only for sensors reporting scalar values */ - if (s->num_axis == 0) { - unsigned int sres = - le32_to_cpu(sdesc->resolution); - - s->resolution = SENSOR_RES(sres); - s->exponent = - S32_EXT(SENSOR_RES_EXP(sres)); - dsize += sizeof(sdesc->resolution); - - scmi_parse_range_attrs(&s->scalar_attrs, - &sdesc->scalar_attrs); - dsize += sizeof(sdesc->scalar_attrs); - } - } - if (s->num_axis > 0) { - ret = scmi_sensor_axis_description(ph, s); - if (ret) - goto out; - } +static int +iter_sens_descr_process_response(const struct scmi_protocol_handle *ph, + const void *response, + struct scmi_iterator_state *st, void *priv) - sdesc = (typeof(sdesc))((u8 *)sdesc + dsize); +{ + int ret = 0; + u32 attrh, attrl; + size_t dsize = SCMI_MSG_RESP_SENS_DESCR_BASE_SZ; + struct scmi_sensor_info *s; + struct sensors_info *si = priv; + const struct scmi_sensor_descriptor *sdesc = st->priv; + + s = &si->sensors[st->desc_index + st->loop_idx]; + s->id = le32_to_cpu(sdesc->id); + + attrl = le32_to_cpu(sdesc->attributes_low); + /* common bitfields parsing */ + s->async = SUPPORTS_ASYNC_READ(attrl); + s->num_trip_points = NUM_TRIP_POINTS(attrl); + /** + * only SCMIv3.0 specific bitfield below. + * Such bitfields are assumed to be zeroed on non + * relevant fw versions...assuming fw not buggy ! + */ + s->update = SUPPORTS_UPDATE_NOTIFY(attrl); + s->timestamped = SUPPORTS_TIMESTAMP(attrl); + if (s->timestamped) + s->tstamp_scale = S32_EXT(SENSOR_TSTAMP_EXP(attrl)); + s->extended_scalar_attrs = SUPPORTS_EXTEND_ATTRS(attrl); + + attrh = le32_to_cpu(sdesc->attributes_high); + /* common bitfields parsing */ + s->scale = S32_EXT(SENSOR_SCALE(attrh)); + s->type = SENSOR_TYPE(attrh); + /* Use pre-allocated pool wherever possible */ + s->intervals.desc = s->intervals.prealloc_pool; + if (si->version == SCMIv2_SENSOR_PROTOCOL) { + s->intervals.segmented = false; + s->intervals.count = 1; + /* + * Convert SCMIv2.0 update interval format to + * SCMIv3.0 to be used as the common exposed + * descriptor, accessible via common macros. + */ + s->intervals.desc[0] = (SENSOR_UPDATE_BASE(attrh) << 5) | + SENSOR_UPDATE_SCALE(attrh); + } else { + /* + * From SCMIv3.0 update intervals are retrieved + * via a dedicated (optional) command. + * Since the command is optional, on error carry + * on without any update interval. + */ + if (scmi_sensor_update_intervals(ph, s)) + dev_dbg(ph->dev, + "Update Intervals not available for sensor ID:%d\n", + s->id); + } + /** + * only > SCMIv2.0 specific bitfield below. + * Such bitfields are assumed to be zeroed on non + * relevant fw versions...assuming fw not buggy ! + */ + s->num_axis = min_t(unsigned int, + SUPPORTS_AXIS(attrh) ? + SENSOR_AXIS_NUMBER(attrh) : 0, + SCMI_MAX_NUM_SENSOR_AXIS); + strscpy(s->name, sdesc->name, SCMI_MAX_STR_SIZE); + + /* + * If supported overwrite short name with the extended + * one; on error just carry on and use already provided + * short name. + */ + if (PROTOCOL_REV_MAJOR(si->version) >= 0x3 && + SUPPORTS_EXTENDED_NAMES(attrl)) + ph->hops->extended_name_get(ph, SENSOR_NAME_GET, s->id, + s->name, SCMI_MAX_STR_SIZE); + + if (s->extended_scalar_attrs) { + s->sensor_power = le32_to_cpu(sdesc->power); + dsize += sizeof(sdesc->power); + + /* Only for sensors reporting scalar values */ + if (s->num_axis == 0) { + unsigned int sres = le32_to_cpu(sdesc->resolution); + + s->resolution = SENSOR_RES(sres); + s->exponent = S32_EXT(SENSOR_RES_EXP(sres)); + dsize += sizeof(sdesc->resolution); + + scmi_parse_range_attrs(&s->scalar_attrs, + &sdesc->scalar_attrs); + dsize += sizeof(sdesc->scalar_attrs); } + } - desc_index += num_returned; + if (s->num_axis > 0) + ret = scmi_sensor_axis_description(ph, s); - ph->xops->reset_rx_to_maxsz(ph, t); - /* - * check for both returned and remaining to avoid infinite - * loop due to buggy firmware - */ - } while (num_returned && num_remaining); + st->priv = ((u8 *)sdesc + dsize); -out: - ph->xops->xfer_put(ph, t); return ret; } +static int scmi_sensor_description_get(const struct scmi_protocol_handle *ph, + struct sensors_info *si) +{ + void *iter; + struct scmi_iterator_ops ops = { + .prepare_message = iter_sens_descr_prepare_message, + .update_state = iter_sens_descr_update_state, + .process_response = iter_sens_descr_process_response, + }; + + iter = ph->hops->iter_response_init(ph, &ops, si->num_sensors, + SENSOR_DESCRIPTION_GET, + sizeof(__le32), si); + if (IS_ERR(iter)) + return PTR_ERR(iter); + + return ph->hops->iter_response_run(iter); +} + static inline int scmi_sensor_request_notify(const struct scmi_protocol_handle *ph, u32 sensor_id, u8 message_id, bool enable) -- cgit v1.2.3 From 802b0bed011e598876c0975da2c41dadf01a3d03 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 30 Mar 2022 16:05:44 +0100 Subject: firmware: arm_scmi: Add SCMI v3.1 SENSOR_AXIS_NAME_GET support Add support for SCMI v3.1 SENSOR_AXIS_NAME_GET multi-part command using the common iterator protocol helpers. Link: https://lore.kernel.org/r/20220330150551.2573938-16-cristian.marussi@arm.com Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/sensors.c | 82 ++++++++++++++++++++++++++++++++++--- 1 file changed, 76 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c index e1a94463d7d8..21e0ce89b153 100644 --- a/drivers/firmware/arm_scmi/sensors.c +++ b/drivers/firmware/arm_scmi/sensors.c @@ -28,6 +28,7 @@ enum scmi_sensor_protocol_cmd { SENSOR_CONFIG_SET = 0xA, SENSOR_CONTINUOUS_UPDATE_NOTIFY = 0xB, SENSOR_NAME_GET = 0xC, + SENSOR_AXIS_NAME_GET = 0xD, }; struct scmi_msg_resp_sensor_attributes { @@ -117,13 +118,22 @@ struct scmi_msg_resp_sensor_axis_description { struct scmi_axis_descriptor { __le32 id; __le32 attributes_low; +#define SUPPORTS_EXTENDED_AXIS_NAMES(x) FIELD_GET(BIT(9), (x)) __le32 attributes_high; - u8 name[SCMI_MAX_STR_SIZE]; + u8 name[SCMI_SHORT_NAME_MAX_SIZE]; __le32 resolution; struct scmi_msg_resp_attrs attrs; } desc[]; }; +struct scmi_msg_resp_sensor_axis_names_description { + __le32 num_axis_flags; + struct scmi_sensor_axis_name_descriptor { + __le32 axis_id; + u8 name[SCMI_MAX_STR_SIZE]; + } desc[]; +}; + /* Base scmi_axis_descriptor size excluding extended attrs after name */ #define SCMI_MSG_RESP_AXIS_DESCR_BASE_SZ 28 @@ -393,7 +403,6 @@ iter_axes_desc_process_response(const struct scmi_protocol_handle *ph, a->extended_attrs = SUPPORTS_EXTEND_ATTRS(attrl); attrh = le32_to_cpu(adesc->attributes_high); - a->scale = S32_EXT(SENSOR_SCALE(attrh)); a->type = SENSOR_TYPE(attrh); strscpy(a->name, adesc->name, SCMI_MAX_STR_SIZE); @@ -408,15 +417,69 @@ iter_axes_desc_process_response(const struct scmi_protocol_handle *ph, scmi_parse_range_attrs(&a->attrs, &adesc->attrs); dsize += sizeof(adesc->attrs); } - st->priv = ((u8 *)adesc + dsize); return 0; } +static int +iter_axes_extended_name_update_state(struct scmi_iterator_state *st, + const void *response, void *priv) +{ + u32 flags; + const struct scmi_msg_resp_sensor_axis_names_description *r = response; + + flags = le32_to_cpu(r->num_axis_flags); + st->num_returned = NUM_AXIS_RETURNED(flags); + st->num_remaining = NUM_AXIS_REMAINING(flags); + st->priv = (void *)&r->desc[0]; + + return 0; +} + +static int +iter_axes_extended_name_process_response(const struct scmi_protocol_handle *ph, + const void *response, + struct scmi_iterator_state *st, + void *priv) +{ + struct scmi_sensor_axis_info *a; + const struct scmi_sensor_info *s = priv; + struct scmi_sensor_axis_name_descriptor *adesc = st->priv; + + a = &s->axis[st->desc_index + st->loop_idx]; + strscpy(a->name, adesc->name, SCMI_MAX_STR_SIZE); + st->priv = ++adesc; + + return 0; +} + +static int +scmi_sensor_axis_extended_names_get(const struct scmi_protocol_handle *ph, + struct scmi_sensor_info *s) +{ + void *iter; + struct scmi_msg_sensor_axis_description_get *msg; + struct scmi_iterator_ops ops = { + .prepare_message = iter_axes_desc_prepare_message, + .update_state = iter_axes_extended_name_update_state, + .process_response = iter_axes_extended_name_process_response, + }; + + iter = ph->hops->iter_response_init(ph, &ops, s->num_axis, + SENSOR_AXIS_NAME_GET, + sizeof(*msg), s); + if (IS_ERR(iter)) + return PTR_ERR(iter); + + return ph->hops->iter_response_run(iter); +} + static int scmi_sensor_axis_description(const struct scmi_protocol_handle *ph, - struct scmi_sensor_info *s) + struct scmi_sensor_info *s, + u32 version) { + int ret; void *iter; struct scmi_msg_sensor_axis_description_get *msg; struct scmi_iterator_ops ops = { @@ -436,7 +499,14 @@ static int scmi_sensor_axis_description(const struct scmi_protocol_handle *ph, if (IS_ERR(iter)) return PTR_ERR(iter); - return ph->hops->iter_response_run(iter); + ret = ph->hops->iter_response_run(iter); + if (ret) + return ret; + + if (PROTOCOL_REV_MAJOR(version) >= 0x3) + ret = scmi_sensor_axis_extended_names_get(ph, s); + + return ret; } static void iter_sens_descr_prepare_message(void *message, @@ -559,7 +629,7 @@ iter_sens_descr_process_response(const struct scmi_protocol_handle *ph, } if (s->num_axis > 0) - ret = scmi_sensor_axis_description(ph, s); + ret = scmi_sensor_axis_description(ph, s, si->version); st->priv = ((u8 *)sdesc + dsize); -- cgit v1.2.3 From 7bc7caafe6b1e5b882255a42bc1bf112fa87b69b Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 30 Mar 2022 16:05:45 +0100 Subject: firmware: arm_scmi: Use common iterators in the clock protocol Make SCMI clock protocol use the common iterator protocol helpers for issuing the multi-part commands. Link: https://lore.kernel.org/r/20220330150551.2573938-17-cristian.marussi@arm.com Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 150 +++++++++++++++++++++++--------------- 1 file changed, 90 insertions(+), 60 deletions(-) diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index cf26f18b85fc..5af14b0c8049 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -164,81 +164,111 @@ static int rate_cmp_func(const void *_r1, const void *_r2) return 1; } -static int -scmi_clock_describe_rates_get(const struct scmi_protocol_handle *ph, u32 clk_id, - struct scmi_clock_info *clk) -{ - u64 *rate = NULL; - int ret, cnt; - bool rate_discrete = false; - u32 tot_rate_cnt = 0, rates_flag; - u16 num_returned, num_remaining; - struct scmi_xfer *t; - struct scmi_msg_clock_describe_rates *clk_desc; - struct scmi_msg_resp_clock_describe_rates *rlist; +struct scmi_clk_ipriv { + u32 clk_id; + struct scmi_clock_info *clk; +}; - ret = ph->xops->xfer_get_init(ph, CLOCK_DESCRIBE_RATES, - sizeof(*clk_desc), 0, &t); - if (ret) - return ret; +static void iter_clk_describe_prepare_message(void *message, + const unsigned int desc_index, + const void *priv) +{ + struct scmi_msg_clock_describe_rates *msg = message; + const struct scmi_clk_ipriv *p = priv; - clk_desc = t->tx.buf; - rlist = t->rx.buf; + msg->id = cpu_to_le32(p->clk_id); + /* Set the number of rates to be skipped/already read */ + msg->rate_index = cpu_to_le32(desc_index); +} - do { - clk_desc->id = cpu_to_le32(clk_id); - /* Set the number of rates to be skipped/already read */ - clk_desc->rate_index = cpu_to_le32(tot_rate_cnt); +static int +iter_clk_describe_update_state(struct scmi_iterator_state *st, + const void *response, void *priv) +{ + u32 flags; + struct scmi_clk_ipriv *p = priv; + const struct scmi_msg_resp_clock_describe_rates *r = response; - ret = ph->xops->do_xfer(ph, t); - if (ret) - goto err; + flags = le32_to_cpu(r->num_rates_flags); + st->num_remaining = NUM_REMAINING(flags); + st->num_returned = NUM_RETURNED(flags); + p->clk->rate_discrete = RATE_DISCRETE(flags); - rates_flag = le32_to_cpu(rlist->num_rates_flags); - num_remaining = NUM_REMAINING(rates_flag); - rate_discrete = RATE_DISCRETE(rates_flag); - num_returned = NUM_RETURNED(rates_flag); + return 0; +} - if (tot_rate_cnt + num_returned > SCMI_MAX_NUM_RATES) { - dev_err(ph->dev, "No. of rates > MAX_NUM_RATES"); +static int +iter_clk_describe_process_response(const struct scmi_protocol_handle *ph, + const void *response, + struct scmi_iterator_state *st, void *priv) +{ + int ret = 0; + struct scmi_clk_ipriv *p = priv; + const struct scmi_msg_resp_clock_describe_rates *r = response; + + if (!p->clk->rate_discrete) { + switch (st->desc_index + st->loop_idx) { + case 0: + p->clk->range.min_rate = RATE_TO_U64(r->rate[0]); break; - } - - if (!rate_discrete) { - clk->range.min_rate = RATE_TO_U64(rlist->rate[0]); - clk->range.max_rate = RATE_TO_U64(rlist->rate[1]); - clk->range.step_size = RATE_TO_U64(rlist->rate[2]); - dev_dbg(ph->dev, "Min %llu Max %llu Step %llu Hz\n", - clk->range.min_rate, clk->range.max_rate, - clk->range.step_size); + case 1: + p->clk->range.max_rate = RATE_TO_U64(r->rate[1]); + break; + case 2: + p->clk->range.step_size = RATE_TO_U64(r->rate[2]); + break; + default: + ret = -EINVAL; break; } + } else { + u64 *rate = &p->clk->list.rates[st->desc_index + st->loop_idx]; - rate = &clk->list.rates[tot_rate_cnt]; - for (cnt = 0; cnt < num_returned; cnt++, rate++) { - *rate = RATE_TO_U64(rlist->rate[cnt]); - dev_dbg(ph->dev, "Rate %llu Hz\n", *rate); - } + *rate = RATE_TO_U64(r->rate[st->loop_idx]); + p->clk->list.num_rates++; + //XXX dev_dbg(ph->dev, "Rate %llu Hz\n", *rate); + } + + return ret; +} - tot_rate_cnt += num_returned; +static int +scmi_clock_describe_rates_get(const struct scmi_protocol_handle *ph, u32 clk_id, + struct scmi_clock_info *clk) +{ + int ret; - ph->xops->reset_rx_to_maxsz(ph, t); - /* - * check for both returned and remaining to avoid infinite - * loop due to buggy firmware - */ - } while (num_returned && num_remaining); + void *iter; + struct scmi_msg_clock_describe_rates *msg; + struct scmi_iterator_ops ops = { + .prepare_message = iter_clk_describe_prepare_message, + .update_state = iter_clk_describe_update_state, + .process_response = iter_clk_describe_process_response, + }; + struct scmi_clk_ipriv cpriv = { + .clk_id = clk_id, + .clk = clk, + }; + + iter = ph->hops->iter_response_init(ph, &ops, SCMI_MAX_NUM_RATES, + CLOCK_DESCRIBE_RATES, + sizeof(*msg), &cpriv); + if (IS_ERR(iter)) + return PTR_ERR(iter); + + ret = ph->hops->iter_response_run(iter); + if (ret) + return ret; - if (rate_discrete && rate) { - clk->list.num_rates = tot_rate_cnt; - sort(clk->list.rates, tot_rate_cnt, sizeof(*rate), - rate_cmp_func, NULL); + if (!clk->rate_discrete) { + dev_dbg(ph->dev, "Min %llu Max %llu Step %llu Hz\n", + clk->range.min_rate, clk->range.max_rate, + clk->range.step_size); + } else if (clk->list.num_rates) { + sort(clk->list.rates, clk->list.num_rates, + sizeof(clk->list.rates[0]), rate_cmp_func, NULL); } - clk->rate_discrete = rate_discrete; - -err: - ph->xops->xfer_put(ph, t); return ret; } -- cgit v1.2.3 From d8d7e91316746d130a9c5d09dc21d59eff5e3e43 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 30 Mar 2022 16:05:46 +0100 Subject: firmware: arm_scmi: Use common iterators in the voltage protocol Make SCMI voltage protocol use the common iterator protocol helpers for issuing the multi-part commands. Link: https://lore.kernel.org/r/20220330150551.2573938-18-cristian.marussi@arm.com Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/voltage.c | 161 +++++++++++++++++++++--------------- 1 file changed, 93 insertions(+), 68 deletions(-) diff --git a/drivers/firmware/arm_scmi/voltage.c b/drivers/firmware/arm_scmi/voltage.c index 5d58ba724eeb..e1bdce573c4f 100644 --- a/drivers/firmware/arm_scmi/voltage.c +++ b/drivers/firmware/arm_scmi/voltage.c @@ -112,14 +112,100 @@ static int scmi_init_voltage_levels(struct device *dev, return 0; } +struct scmi_volt_ipriv { + struct device *dev; + struct scmi_voltage_info *v; +}; + +static void iter_volt_levels_prepare_message(void *message, + unsigned int desc_index, + const void *priv) +{ + struct scmi_msg_cmd_describe_levels *msg = message; + const struct scmi_volt_ipriv *p = priv; + + msg->domain_id = cpu_to_le32(p->v->id); + msg->level_index = cpu_to_le32(desc_index); +} + +static int iter_volt_levels_update_state(struct scmi_iterator_state *st, + const void *response, void *priv) +{ + int ret = 0; + u32 flags; + const struct scmi_msg_resp_describe_levels *r = response; + struct scmi_volt_ipriv *p = priv; + + flags = le32_to_cpu(r->flags); + st->num_returned = NUM_RETURNED_LEVELS(flags); + st->num_remaining = NUM_REMAINING_LEVELS(flags); + + /* Allocate space for num_levels if not already done */ + if (!p->v->num_levels) { + ret = scmi_init_voltage_levels(p->dev, p->v, st->num_returned, + st->num_remaining, + SUPPORTS_SEGMENTED_LEVELS(flags)); + if (!ret) + st->max_resources = p->v->num_levels; + } + + return ret; +} + +static int +iter_volt_levels_process_response(const struct scmi_protocol_handle *ph, + const void *response, + struct scmi_iterator_state *st, void *priv) +{ + s32 val; + const struct scmi_msg_resp_describe_levels *r = response; + struct scmi_volt_ipriv *p = priv; + + val = (s32)le32_to_cpu(r->voltage[st->loop_idx]); + p->v->levels_uv[st->desc_index + st->loop_idx] = val; + if (val < 0) + p->v->negative_volts_allowed = true; + + return 0; +} + +static int scmi_voltage_levels_get(const struct scmi_protocol_handle *ph, + struct scmi_voltage_info *v) +{ + int ret; + void *iter; + struct scmi_msg_cmd_describe_levels *msg; + struct scmi_iterator_ops ops = { + .prepare_message = iter_volt_levels_prepare_message, + .update_state = iter_volt_levels_update_state, + .process_response = iter_volt_levels_process_response, + }; + struct scmi_volt_ipriv vpriv = { + .dev = ph->dev, + .v = v, + }; + + iter = ph->hops->iter_response_init(ph, &ops, v->num_levels, + VOLTAGE_DESCRIBE_LEVELS, + sizeof(*msg), &vpriv); + if (IS_ERR(iter)) + return PTR_ERR(iter); + + ret = ph->hops->iter_response_run(iter); + if (ret) { + v->num_levels = 0; + devm_kfree(ph->dev, v->levels_uv); + } + + return ret; +} + static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph, struct voltage_info *vinfo) { int ret, dom; - struct scmi_xfer *td, *tl; - struct device *dev = ph->dev; + struct scmi_xfer *td; struct scmi_msg_resp_domain_attributes *resp_dom; - struct scmi_msg_resp_describe_levels *resp_levels; ret = ph->xops->xfer_get_init(ph, VOLTAGE_DOMAIN_ATTRIBUTES, sizeof(__le32), sizeof(*resp_dom), &td); @@ -127,16 +213,7 @@ static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph, return ret; resp_dom = td->rx.buf; - ret = ph->xops->xfer_get_init(ph, VOLTAGE_DESCRIBE_LEVELS, - sizeof(__le64), 0, &tl); - if (ret) - goto outd; - resp_levels = tl->rx.buf; - for (dom = 0; dom < vinfo->num_domains; dom++) { - u32 desc_index = 0; - u16 num_returned = 0, num_remaining = 0; - struct scmi_msg_cmd_describe_levels *cmd; struct scmi_voltage_info *v; /* Retrieve domain attributes at first ... */ @@ -161,66 +238,14 @@ static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph, v->id, v->name, SCMI_MAX_STR_SIZE); - cmd = tl->tx.buf; - /* ...then retrieve domain levels descriptions */ - do { - u32 flags; - int cnt; - - cmd->domain_id = cpu_to_le32(v->id); - cmd->level_index = cpu_to_le32(desc_index); - ret = ph->xops->do_xfer(ph, tl); - if (ret) - break; - - flags = le32_to_cpu(resp_levels->flags); - num_returned = NUM_RETURNED_LEVELS(flags); - num_remaining = NUM_REMAINING_LEVELS(flags); - - /* Allocate space for num_levels if not already done */ - if (!v->num_levels) { - ret = scmi_init_voltage_levels(dev, v, - num_returned, - num_remaining, - SUPPORTS_SEGMENTED_LEVELS(flags)); - if (ret) - break; - } - - if (desc_index + num_returned > v->num_levels) { - dev_err(ph->dev, - "No. of voltage levels can't exceed %d\n", - v->num_levels); - ret = -EINVAL; - break; - } - - for (cnt = 0; cnt < num_returned; cnt++) { - s32 val; - - val = - (s32)le32_to_cpu(resp_levels->voltage[cnt]); - v->levels_uv[desc_index + cnt] = val; - if (val < 0) - v->negative_volts_allowed = true; - } - - desc_index += num_returned; - - ph->xops->reset_rx_to_maxsz(ph, tl); - /* check both to avoid infinite loop due to buggy fw */ - } while (num_returned && num_remaining); - - if (ret) { - v->num_levels = 0; - devm_kfree(dev, v->levels_uv); - } + ret = scmi_voltage_levels_get(ph, v); + /* Skip invalid voltage descriptors */ + if (ret) + continue; ph->xops->reset_rx_to_maxsz(ph, td); } - ph->xops->xfer_put(ph, tl); -outd: ph->xops->xfer_put(ph, td); return ret; -- cgit v1.2.3 From 79d2ea9244180bf76537c23406d838db955a57b5 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 30 Mar 2022 16:05:47 +0100 Subject: firmware: arm_scmi: Use common iterators in the perf protocol Make SCMI perf protocol use the common iterator protocol helpers for issuing the multi-part commands. Link: https://lore.kernel.org/r/20220330150551.2573938-19-cristian.marussi@arm.com Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/perf.c | 117 +++++++++++++++++++++++---------------- 1 file changed, 69 insertions(+), 48 deletions(-) diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index 9e046fd121b9..e1aa0ed67971 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -272,66 +272,87 @@ static int opp_cmp_func(const void *opp1, const void *opp2) return t1->perf - t2->perf; } -static int -scmi_perf_describe_levels_get(const struct scmi_protocol_handle *ph, u32 domain, - struct perf_dom_info *perf_dom) +struct scmi_perf_ipriv { + u32 domain; + struct perf_dom_info *perf_dom; +}; + +static void iter_perf_levels_prepare_message(void *message, + unsigned int desc_index, + const void *priv) { - int ret, cnt; - u32 tot_opp_cnt = 0; - u16 num_returned, num_remaining; - struct scmi_xfer *t; - struct scmi_opp *opp; - struct scmi_msg_perf_describe_levels *dom_info; - struct scmi_msg_resp_perf_describe_levels *level_info; + struct scmi_msg_perf_describe_levels *msg = message; + const struct scmi_perf_ipriv *p = priv; - ret = ph->xops->xfer_get_init(ph, PERF_DESCRIBE_LEVELS, - sizeof(*dom_info), 0, &t); - if (ret) - return ret; + msg->domain = cpu_to_le32(p->domain); + /* Set the number of OPPs to be skipped/already read */ + msg->level_index = cpu_to_le32(desc_index); +} - dom_info = t->tx.buf; - level_info = t->rx.buf; +static int iter_perf_levels_update_state(struct scmi_iterator_state *st, + const void *response, void *priv) +{ + const struct scmi_msg_resp_perf_describe_levels *r = response; - do { - dom_info->domain = cpu_to_le32(domain); - /* Set the number of OPPs to be skipped/already read */ - dom_info->level_index = cpu_to_le32(tot_opp_cnt); + st->num_returned = le16_to_cpu(r->num_returned); + st->num_remaining = le16_to_cpu(r->num_remaining); - ret = ph->xops->do_xfer(ph, t); - if (ret) - break; + return 0; +} - num_returned = le16_to_cpu(level_info->num_returned); - num_remaining = le16_to_cpu(level_info->num_remaining); - if (tot_opp_cnt + num_returned > MAX_OPPS) { - dev_err(ph->dev, "No. of OPPs exceeded MAX_OPPS"); - break; - } +static int +iter_perf_levels_process_response(const struct scmi_protocol_handle *ph, + const void *response, + struct scmi_iterator_state *st, void *priv) +{ + struct scmi_opp *opp; + const struct scmi_msg_resp_perf_describe_levels *r = response; + struct scmi_perf_ipriv *p = priv; - opp = &perf_dom->opp[tot_opp_cnt]; - for (cnt = 0; cnt < num_returned; cnt++, opp++) { - opp->perf = le32_to_cpu(level_info->opp[cnt].perf_val); - opp->power = le32_to_cpu(level_info->opp[cnt].power); - opp->trans_latency_us = le16_to_cpu - (level_info->opp[cnt].transition_latency_us); + opp = &p->perf_dom->opp[st->desc_index + st->loop_idx]; + opp->perf = le32_to_cpu(r->opp[st->loop_idx].perf_val); + opp->power = le32_to_cpu(r->opp[st->loop_idx].power); + opp->trans_latency_us = + le16_to_cpu(r->opp[st->loop_idx].transition_latency_us); + p->perf_dom->opp_count++; - dev_dbg(ph->dev, "Level %d Power %d Latency %dus\n", - opp->perf, opp->power, opp->trans_latency_us); - } + dev_dbg(ph->dev, "Level %d Power %d Latency %dus\n", + opp->perf, opp->power, opp->trans_latency_us); - tot_opp_cnt += num_returned; + return 0; +} - ph->xops->reset_rx_to_maxsz(ph, t); - /* - * check for both returned and remaining to avoid infinite - * loop due to buggy firmware - */ - } while (num_returned && num_remaining); +static int +scmi_perf_describe_levels_get(const struct scmi_protocol_handle *ph, u32 domain, + struct perf_dom_info *perf_dom) +{ + int ret; + void *iter; + struct scmi_msg_perf_describe_levels *msg; + struct scmi_iterator_ops ops = { + .prepare_message = iter_perf_levels_prepare_message, + .update_state = iter_perf_levels_update_state, + .process_response = iter_perf_levels_process_response, + }; + struct scmi_perf_ipriv ppriv = { + .domain = domain, + .perf_dom = perf_dom, + }; + + iter = ph->hops->iter_response_init(ph, &ops, MAX_OPPS, + PERF_DESCRIBE_LEVELS, + sizeof(*msg), &ppriv); + if (IS_ERR(iter)) + return PTR_ERR(iter); + + ret = ph->hops->iter_response_run(iter); + if (ret) + return ret; - perf_dom->opp_count = tot_opp_cnt; - ph->xops->xfer_put(ph, t); + if (perf_dom->opp_count) + sort(perf_dom->opp, perf_dom->opp_count, + sizeof(struct scmi_opp), opp_cmp_func, NULL); - sort(perf_dom->opp, tot_opp_cnt, sizeof(*opp), opp_cmp_func, NULL); return ret; } -- cgit v1.2.3 From 3630cd8130ce0b2a607db09a1995e5bf99053ae1 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 30 Mar 2022 16:05:50 +0100 Subject: firmware: arm_scmi: Add SCMI v3.1 perf power-cost in microwatts Add SCMI v3.1 internal support for parsing message attributes reporting the capability of a performance domain to report power-cost in microwatts. Link: https://lore.kernel.org/r/20220330150551.2573938-22-cristian.marussi@arm.com Cc: Lukasz Luba Reviewed-by: Lukasz Luba Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/perf.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index e1aa0ed67971..65ffda5495d6 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -45,6 +45,7 @@ struct scmi_msg_resp_perf_attributes { __le16 num_domains; __le16 flags; #define POWER_SCALE_IN_MILLIWATT(x) ((x) & BIT(0)) +#define POWER_SCALE_IN_MICROWATT(x) ((x) & BIT(1)) __le32 stats_addr_low; __le32 stats_addr_high; __le32 stats_size; @@ -170,6 +171,7 @@ struct scmi_perf_info { u32 version; int num_domains; bool power_scale_mw; + bool power_scale_uw; u64 stats_addr; u32 stats_size; struct perf_dom_info *dom_info; @@ -200,6 +202,8 @@ static int scmi_perf_attributes_get(const struct scmi_protocol_handle *ph, pi->num_domains = le16_to_cpu(attr->num_domains); pi->power_scale_mw = POWER_SCALE_IN_MILLIWATT(flags); + if (PROTOCOL_REV_MAJOR(pi->version) >= 0x3) + pi->power_scale_uw = POWER_SCALE_IN_MICROWATT(flags); pi->stats_addr = le32_to_cpu(attr->stats_addr_low) | (u64)le32_to_cpu(attr->stats_addr_high) << 32; pi->stats_size = le32_to_cpu(attr->stats_size); -- cgit v1.2.3 From 71bea05797b55d43c3cee8e2ee279ab510e0563c Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 30 Mar 2022 16:05:51 +0100 Subject: firmware: arm_scmi: Add checks for min/max limits in PERFORMANCE_LIMITS_SET Starting with SCMI v3.1, the PERFORMANCE_LIMITS_SET command allows a user to request only one between max and min ranges to be changed, while leaving the other untouched if set to zero in the request. Anyway SCMI v3.1 states also explicitly that you cannot leave both of those unchanged (zeroed) when issuing such command, so add a proper check for this condition. Link: https://lore.kernel.org/r/20220330150551.2573938-23-cristian.marussi@arm.com Signed-off-by: Cristian Marussi [sudeep.holla: Dropped check for v3.0 and above to make the check unconditional, updated the subject accordingly] Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/perf.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index 65ffda5495d6..8f4051aca220 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -423,6 +423,9 @@ static int scmi_perf_limits_set(const struct scmi_protocol_handle *ph, struct scmi_perf_info *pi = ph->get_priv(ph); struct perf_dom_info *dom = pi->dom_info + domain; + if (PROTOCOL_REV_MAJOR(pi->version) >= 0x3 && !max_perf && !min_perf) + return -EINVAL; + if (dom->fc_info && dom->fc_info->limit_set_addr) { iowrite32(max_perf, dom->fc_info->limit_set_addr); iowrite32(min_perf, dom->fc_info->limit_set_addr + 4); -- cgit v1.2.3 From 7aa75496ea1f38dfd99b93c66f8d9bc525d11efc Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 30 Mar 2022 16:05:48 +0100 Subject: firmware: arm_scmi: Add SCMI v3.1 clock notifications Add SCMI v3.1 clock pre and post notifications. Link: https://lore.kernel.org/r/20220330150551.2573938-20-cristian.marussi@arm.com Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 136 ++++++++++++++++++++++++++++++++++++-- include/linux/scmi_protocol.h | 11 +++ 2 files changed, 142 insertions(+), 5 deletions(-) diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 5af14b0c8049..4d36a9a133d1 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -10,6 +10,7 @@ #include #include "protocols.h" +#include "notify.h" enum scmi_clock_protocol_cmd { CLOCK_ATTRIBUTES = 0x3, @@ -18,6 +19,8 @@ enum scmi_clock_protocol_cmd { CLOCK_RATE_GET = 0x6, CLOCK_CONFIG_SET = 0x7, CLOCK_NAME_GET = 0x8, + CLOCK_RATE_NOTIFY = 0x9, + CLOCK_RATE_CHANGE_REQUESTED_NOTIFY = 0xA, }; struct scmi_msg_resp_clock_protocol_attributes { @@ -29,7 +32,9 @@ struct scmi_msg_resp_clock_protocol_attributes { struct scmi_msg_resp_clock_attributes { __le32 attributes; #define CLOCK_ENABLE BIT(0) -#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(29)) +#define SUPPORTS_RATE_CHANGED_NOTIF(x) ((x) & BIT(31)) +#define SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(x) ((x) & BIT(30)) +#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(29)) u8 name[SCMI_SHORT_NAME_MAX_SIZE]; __le32 clock_enable_latency; }; @@ -77,6 +82,18 @@ struct scmi_msg_resp_set_rate_complete { __le32 rate_high; }; +struct scmi_msg_clock_rate_notify { + __le32 clk_id; + __le32 notify_enable; +}; + +struct scmi_clock_rate_notify_payld { + __le32 agent_id; + __le32 clock_id; + __le32 rate_low; + __le32 rate_high; +}; + struct clock_info { u32 version; int num_clocks; @@ -85,6 +102,11 @@ struct clock_info { struct scmi_clock_info *clk; }; +static enum scmi_clock_protocol_cmd evt_2_cmd[] = { + CLOCK_RATE_NOTIFY, + CLOCK_RATE_CHANGE_REQUESTED_NOTIFY, +}; + static int scmi_clock_protocol_attributes_get(const struct scmi_protocol_handle *ph, struct clock_info *ci) @@ -144,10 +166,17 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph, * If supported overwrite short name with the extended one; * on error just carry on and use already provided short name. */ - if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x2 && - SUPPORTS_EXTENDED_NAMES(attributes)) - ph->hops->extended_name_get(ph, CLOCK_NAME_GET, clk_id, - clk->name, SCMI_MAX_STR_SIZE); + if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x2) { + if (SUPPORTS_EXTENDED_NAMES(attributes)) + ph->hops->extended_name_get(ph, CLOCK_NAME_GET, clk_id, + clk->name, + SCMI_MAX_STR_SIZE); + + if (SUPPORTS_RATE_CHANGED_NOTIF(attributes)) + clk->rate_changed_notifications = true; + if (SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(attributes)) + clk->rate_change_requested_notifications = true; + } return ret; } @@ -418,6 +447,102 @@ static const struct scmi_clk_proto_ops clk_proto_ops = { .disable_atomic = scmi_clock_disable_atomic, }; +static int scmi_clk_rate_notify(const struct scmi_protocol_handle *ph, + u32 clk_id, int message_id, bool enable) +{ + int ret; + struct scmi_xfer *t; + struct scmi_msg_clock_rate_notify *notify; + + ret = ph->xops->xfer_get_init(ph, message_id, sizeof(*notify), 0, &t); + if (ret) + return ret; + + notify = t->tx.buf; + notify->clk_id = cpu_to_le32(clk_id); + notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0; + + ret = ph->xops->do_xfer(ph, t); + + ph->xops->xfer_put(ph, t); + return ret; +} + +static int scmi_clk_set_notify_enabled(const struct scmi_protocol_handle *ph, + u8 evt_id, u32 src_id, bool enable) +{ + int ret, cmd_id; + + if (evt_id >= ARRAY_SIZE(evt_2_cmd)) + return -EINVAL; + + cmd_id = evt_2_cmd[evt_id]; + ret = scmi_clk_rate_notify(ph, src_id, cmd_id, enable); + if (ret) + pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n", + evt_id, src_id, ret); + + return ret; +} + +static void *scmi_clk_fill_custom_report(const struct scmi_protocol_handle *ph, + u8 evt_id, ktime_t timestamp, + const void *payld, size_t payld_sz, + void *report, u32 *src_id) +{ + const struct scmi_clock_rate_notify_payld *p = payld; + struct scmi_clock_rate_notif_report *r = report; + + if (sizeof(*p) != payld_sz || + (evt_id != SCMI_EVENT_CLOCK_RATE_CHANGED && + evt_id != SCMI_EVENT_CLOCK_RATE_CHANGE_REQUESTED)) + return NULL; + + r->timestamp = timestamp; + r->agent_id = le32_to_cpu(p->agent_id); + r->clock_id = le32_to_cpu(p->clock_id); + r->rate = get_unaligned_le64(&p->rate_low); + *src_id = r->clock_id; + + return r; +} + +static int scmi_clk_get_num_sources(const struct scmi_protocol_handle *ph) +{ + struct clock_info *ci = ph->get_priv(ph); + + if (!ci) + return -EINVAL; + + return ci->num_clocks; +} + +static const struct scmi_event clk_events[] = { + { + .id = SCMI_EVENT_CLOCK_RATE_CHANGED, + .max_payld_sz = sizeof(struct scmi_clock_rate_notify_payld), + .max_report_sz = sizeof(struct scmi_clock_rate_notif_report), + }, + { + .id = SCMI_EVENT_CLOCK_RATE_CHANGE_REQUESTED, + .max_payld_sz = sizeof(struct scmi_clock_rate_notify_payld), + .max_report_sz = sizeof(struct scmi_clock_rate_notif_report), + }, +}; + +static const struct scmi_event_ops clk_event_ops = { + .get_num_sources = scmi_clk_get_num_sources, + .set_notify_enabled = scmi_clk_set_notify_enabled, + .fill_custom_report = scmi_clk_fill_custom_report, +}; + +static const struct scmi_protocol_events clk_protocol_events = { + .queue_sz = SCMI_PROTO_QUEUE_SZ, + .ops = &clk_event_ops, + .evts = clk_events, + .num_events = ARRAY_SIZE(clk_events), +}; + static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph) { u32 version; @@ -461,6 +586,7 @@ static const struct scmi_protocol scmi_clock = { .owner = THIS_MODULE, .instance_init = &scmi_clock_protocol_init, .ops = &clk_proto_ops, + .events = &clk_protocol_events, }; DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(clock, scmi_clock) diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 56e6f13355b8..0e20acc80d50 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -44,6 +44,8 @@ struct scmi_clock_info { char name[SCMI_MAX_STR_SIZE]; unsigned int enable_latency; bool rate_discrete; + bool rate_changed_notifications; + bool rate_change_requested_notifications; union { struct { int num_rates; @@ -744,6 +746,8 @@ void scmi_protocol_unregister(const struct scmi_protocol *proto); /* SCMI Notification API - Custom Event Reports */ enum scmi_notification_events { SCMI_EVENT_POWER_STATE_CHANGED = 0x0, + SCMI_EVENT_CLOCK_RATE_CHANGED = 0x0, + SCMI_EVENT_CLOCK_RATE_CHANGE_REQUESTED = 0x1, SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED = 0x0, SCMI_EVENT_PERFORMANCE_LEVEL_CHANGED = 0x1, SCMI_EVENT_SENSOR_TRIP_POINT_EVENT = 0x0, @@ -760,6 +764,13 @@ struct scmi_power_state_changed_report { unsigned int power_state; }; +struct scmi_clock_rate_notif_report { + ktime_t timestamp; + unsigned int agent_id; + unsigned int clock_id; + unsigned long long rate; +}; + struct scmi_system_power_state_notifier_report { ktime_t timestamp; unsigned int agent_id; -- cgit v1.2.3 From 4c74701b1eb7636eb0cdd66b488b42920105122a Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 30 Mar 2022 16:05:49 +0100 Subject: firmware: arm_scmi: Add SCMI v3.1 VOLTAGE_LEVEL_SET_COMPLETE Add SCMI v3.1 voltage protocol support for asynchronous VOLTAGE_LEVEL_SET command. Note that, if a voltage domain is advertised to support the asynchronous version of VOLTAGE_LEVEL_SET, the command will be issued asynchronously unless explicitly requested to use the synchronous version by setting the mode to SCMI_VOLTAGE_LEVEL_SET_SYNC when calling voltage_ops->level_set. The SCMI regulator driver level_set invocation has been left unchanged so that it will transparently use the asynchronous version if available. Link: https://lore.kernel.org/r/20220330150551.2573938-21-cristian.marussi@arm.com Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/voltage.c | 51 ++++++++++++++++++++++++++++++------- include/linux/scmi_protocol.h | 12 ++++++--- 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/drivers/firmware/arm_scmi/voltage.c b/drivers/firmware/arm_scmi/voltage.c index e1bdce573c4f..9d195d8719ab 100644 --- a/drivers/firmware/arm_scmi/voltage.c +++ b/drivers/firmware/arm_scmi/voltage.c @@ -28,6 +28,7 @@ enum scmi_voltage_protocol_cmd { struct scmi_msg_resp_domain_attributes { __le32 attr; +#define SUPPORTS_ASYNC_LEVEL_SET(x) ((x) & BIT(31)) #define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(30)) u8 name[SCMI_SHORT_NAME_MAX_SIZE]; }; @@ -56,6 +57,11 @@ struct scmi_msg_cmd_level_set { __le32 voltage_level; }; +struct scmi_resp_voltage_level_set_complete { + __le32 domain_id; + __le32 voltage_level; +}; + struct voltage_info { unsigned int version; unsigned int num_domains; @@ -214,6 +220,7 @@ static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph, resp_dom = td->rx.buf; for (dom = 0; dom < vinfo->num_domains; dom++) { + u32 attributes; struct scmi_voltage_info *v; /* Retrieve domain attributes at first ... */ @@ -225,18 +232,22 @@ static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph, v = vinfo->domains + dom; v->id = dom; - v->attributes = le32_to_cpu(resp_dom->attr); + attributes = le32_to_cpu(resp_dom->attr); strlcpy(v->name, resp_dom->name, SCMI_MAX_STR_SIZE); /* * If supported overwrite short name with the extended one; * on error just carry on and use already provided short name. */ - if (PROTOCOL_REV_MAJOR(vinfo->version) >= 0x2 && - SUPPORTS_EXTENDED_NAMES(v->attributes)) - ph->hops->extended_name_get(ph, VOLTAGE_DOMAIN_NAME_GET, - v->id, v->name, - SCMI_MAX_STR_SIZE); + if (PROTOCOL_REV_MAJOR(vinfo->version) >= 0x2) { + if (SUPPORTS_EXTENDED_NAMES(attributes)) + ph->hops->extended_name_get(ph, + VOLTAGE_DOMAIN_NAME_GET, + v->id, v->name, + SCMI_MAX_STR_SIZE); + if (SUPPORTS_ASYNC_LEVEL_SET(attributes)) + v->async_level_set = true; + } ret = scmi_voltage_levels_get(ph, v); /* Skip invalid voltage descriptors */ @@ -308,12 +319,15 @@ static int scmi_voltage_config_get(const struct scmi_protocol_handle *ph, } static int scmi_voltage_level_set(const struct scmi_protocol_handle *ph, - u32 domain_id, u32 flags, s32 volt_uV) + u32 domain_id, + enum scmi_voltage_level_mode mode, + s32 volt_uV) { int ret; struct scmi_xfer *t; struct voltage_info *vinfo = ph->get_priv(ph); struct scmi_msg_cmd_level_set *cmd; + struct scmi_voltage_info *v; if (domain_id >= vinfo->num_domains) return -EINVAL; @@ -323,12 +337,31 @@ static int scmi_voltage_level_set(const struct scmi_protocol_handle *ph, if (ret) return ret; + v = vinfo->domains + domain_id; + cmd = t->tx.buf; cmd->domain_id = cpu_to_le32(domain_id); - cmd->flags = cpu_to_le32(flags); cmd->voltage_level = cpu_to_le32(volt_uV); - ret = ph->xops->do_xfer(ph, t); + if (!v->async_level_set || mode != SCMI_VOLTAGE_LEVEL_SET_AUTO) { + cmd->flags = cpu_to_le32(0x0); + ret = ph->xops->do_xfer(ph, t); + } else { + cmd->flags = cpu_to_le32(0x1); + ret = ph->xops->do_xfer_with_response(ph, t); + if (!ret) { + struct scmi_resp_voltage_level_set_complete *resp; + + resp = t->rx.buf; + if (le32_to_cpu(resp->domain_id) == domain_id) + dev_dbg(ph->dev, + "Voltage domain %d set async to %d\n", + v->id, + le32_to_cpu(resp->voltage_level)); + else + ret = -EPROTO; + } + } ph->xops->xfer_put(ph, t); return ret; diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 0e20acc80d50..1c58646ba381 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -495,6 +495,11 @@ struct scmi_reset_proto_ops { int (*deassert)(const struct scmi_protocol_handle *ph, u32 domain); }; +enum scmi_voltage_level_mode { + SCMI_VOLTAGE_LEVEL_SET_AUTO, + SCMI_VOLTAGE_LEVEL_SET_SYNC, +}; + /** * struct scmi_voltage_info - describe one available SCMI Voltage Domain * @@ -507,7 +512,8 @@ struct scmi_reset_proto_ops { * supported voltage level * @negative_volts_allowed: True if any of the entries of @levels_uv represent * a negative voltage. - * @attributes: represents Voltage Domain advertised attributes + * @async_level_set: True when the voltage domain supports asynchronous level + * set commands. * @name: name assigned to the Voltage Domain by platform * @num_levels: number of total entries in @levels_uv. * @levels_uv: array of entries describing the available voltage levels for @@ -517,7 +523,7 @@ struct scmi_voltage_info { unsigned int id; bool segmented; bool negative_volts_allowed; - unsigned int attributes; + bool async_level_set; char name[SCMI_MAX_STR_SIZE]; unsigned int num_levels; #define SCMI_VOLTAGE_SEGMENT_LOW 0 @@ -548,7 +554,7 @@ struct scmi_voltage_proto_ops { int (*config_get)(const struct scmi_protocol_handle *ph, u32 domain_id, u32 *config); int (*level_set)(const struct scmi_protocol_handle *ph, u32 domain_id, - u32 flags, s32 volt_uV); + enum scmi_voltage_level_mode mode, s32 volt_uV); int (*level_get)(const struct scmi_protocol_handle *ph, u32 domain_id, s32 *volt_uV); }; -- cgit v1.2.3 From 5e114ad984c220fcc5c08b6bf3b458ce95f46f43 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Mon, 25 Apr 2022 10:51:27 +0200 Subject: firmware: arm_scmi: Support optee shared memory in the optee transport Add support for TEE shared memory in optee scmi transport. When using tee shared memory, scmi optee transport manages SCMI messages using msg protocol(from msg.c) in shared memory, whereas smt(from shmem.c) protocol is used with static IOMEM based shared buffers. Link: https://lore.kernel.org/r/20220425085127.2009-1-etienne.carriere@linaro.org Reviewed-by: Cristian Marussi Signed-off-by: Etienne Carriere Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/Kconfig | 1 + drivers/firmware/arm_scmi/optee.c | 144 ++++++++++++++++++++++++++++---------- 2 files changed, 109 insertions(+), 36 deletions(-) diff --git a/drivers/firmware/arm_scmi/Kconfig b/drivers/firmware/arm_scmi/Kconfig index 7794bd41eaa0..1e7b7fec97d9 100644 --- a/drivers/firmware/arm_scmi/Kconfig +++ b/drivers/firmware/arm_scmi/Kconfig @@ -59,6 +59,7 @@ config ARM_SCMI_TRANSPORT_OPTEE depends on OPTEE=y || OPTEE=ARM_SCMI_PROTOCOL select ARM_SCMI_HAVE_TRANSPORT select ARM_SCMI_HAVE_SHMEM + select ARM_SCMI_HAVE_MSG default y help This enables the OP-TEE service based transport for SCMI. diff --git a/drivers/firmware/arm_scmi/optee.c b/drivers/firmware/arm_scmi/optee.c index 8302a2b4aeeb..b503c22cfd32 100644 --- a/drivers/firmware/arm_scmi/optee.c +++ b/drivers/firmware/arm_scmi/optee.c @@ -64,6 +64,22 @@ enum scmi_optee_pta_cmd { * [in] value[0].b: Requested capabilities mask (enum pta_scmi_caps) */ PTA_SCMI_CMD_GET_CHANNEL = 3, + + /* + * PTA_SCMI_CMD_PROCESS_MSG_CHANNEL - Process SCMI message in a MSG + * buffer pointed by memref parameters + * + * [in] value[0].a: Channel handle + * [in] memref[1]: Message buffer (MSG and SCMI payload) + * [out] memref[2]: Response buffer (MSG and SCMI payload) + * + * Shared memories used for SCMI message/response are MSG buffers + * referenced by param[1] and param[2]. MSG transport protocol + * uses a 32bit header to carry SCMI meta-data (protocol ID and + * protocol message ID) followed by the effective SCMI message + * payload. + */ + PTA_SCMI_CMD_PROCESS_MSG_CHANNEL = 4, }; /* @@ -72,9 +88,17 @@ enum scmi_optee_pta_cmd { * PTA_SCMI_CAPS_SMT_HEADER * When set, OP-TEE supports command using SMT header protocol (SCMI shmem) in * shared memory buffers to carry SCMI protocol synchronisation information. + * + * PTA_SCMI_CAPS_MSG_HEADER + * When set, OP-TEE supports command using MSG header protocol in an OP-TEE + * shared memory to carry SCMI protocol synchronisation information and SCMI + * message payload. */ #define PTA_SCMI_CAPS_NONE 0 #define PTA_SCMI_CAPS_SMT_HEADER BIT(0) +#define PTA_SCMI_CAPS_MSG_HEADER BIT(1) +#define PTA_SCMI_CAPS_MASK (PTA_SCMI_CAPS_SMT_HEADER | \ + PTA_SCMI_CAPS_MSG_HEADER) /** * struct scmi_optee_channel - Description of an OP-TEE SCMI channel @@ -85,7 +109,8 @@ enum scmi_optee_pta_cmd { * @mu: Mutex protection on channel access * @cinfo: SCMI channel information * @shmem: Virtual base address of the shared memory - * @tee_shm: Reference to TEE shared memory or NULL if using static shmem + * @req: Shared memory protocol handle for SCMI request and synchronous response + * @tee_shm: TEE shared memory handle @req or NULL if using IOMEM shmem * @link: Reference in agent's channel list */ struct scmi_optee_channel { @@ -94,7 +119,10 @@ struct scmi_optee_channel { u32 caps; struct mutex mu; struct scmi_chan_info *cinfo; - struct scmi_shared_mem __iomem *shmem; + union { + struct scmi_shared_mem __iomem *shmem; + struct scmi_msg_payld *msg; + } req; struct tee_shm *tee_shm; struct list_head link; }; @@ -178,8 +206,8 @@ static int get_capabilities(struct scmi_optee_agent *agent) caps = param[0].u.value.a; - if (!(caps & PTA_SCMI_CAPS_SMT_HEADER)) { - dev_err(agent->dev, "OP-TEE SCMI PTA doesn't support SMT\n"); + if (!(caps & (PTA_SCMI_CAPS_SMT_HEADER | PTA_SCMI_CAPS_MSG_HEADER))) { + dev_err(agent->dev, "OP-TEE SCMI PTA doesn't support SMT and MSG\n"); return -EOPNOTSUPP; } @@ -193,9 +221,14 @@ static int get_channel(struct scmi_optee_channel *channel) struct device *dev = scmi_optee_private->dev; struct tee_ioctl_invoke_arg arg = { }; struct tee_param param[1] = { }; - unsigned int caps = PTA_SCMI_CAPS_SMT_HEADER; + unsigned int caps = 0; int ret; + if (channel->tee_shm) + caps = PTA_SCMI_CAPS_MSG_HEADER; + else + caps = PTA_SCMI_CAPS_SMT_HEADER; + arg.func = PTA_SCMI_CMD_GET_CHANNEL; arg.session = channel->tee_session; arg.num_params = 1; @@ -220,25 +253,48 @@ static int get_channel(struct scmi_optee_channel *channel) static int invoke_process_smt_channel(struct scmi_optee_channel *channel) { - struct tee_ioctl_invoke_arg arg = { }; - struct tee_param param[2] = { }; + struct tee_ioctl_invoke_arg arg = { + .func = PTA_SCMI_CMD_PROCESS_SMT_CHANNEL, + .session = channel->tee_session, + .num_params = 1, + }; + struct tee_param param[1] = { }; int ret; - arg.session = channel->tee_session; param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; param[0].u.value.a = channel->channel_id; - if (channel->tee_shm) { - param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT; - param[1].u.memref.shm = channel->tee_shm; - param[1].u.memref.size = SCMI_OPTEE_MAX_MSG_SIZE; - arg.num_params = 2; - arg.func = PTA_SCMI_CMD_PROCESS_SMT_CHANNEL_MESSAGE; - } else { - arg.num_params = 1; - arg.func = PTA_SCMI_CMD_PROCESS_SMT_CHANNEL; + ret = tee_client_invoke_func(scmi_optee_private->tee_ctx, &arg, param); + if (ret < 0 || arg.ret) { + dev_err(scmi_optee_private->dev, "Can't invoke channel %u: %d / %#x\n", + channel->channel_id, ret, arg.ret); + return -EIO; } + return 0; +} + +static int invoke_process_msg_channel(struct scmi_optee_channel *channel, size_t msg_size) +{ + struct tee_ioctl_invoke_arg arg = { + .func = PTA_SCMI_CMD_PROCESS_MSG_CHANNEL, + .session = channel->tee_session, + .num_params = 3, + }; + struct tee_param param[3] = { }; + int ret; + + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; + param[0].u.value.a = channel->channel_id; + + param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; + param[1].u.memref.shm = channel->tee_shm; + param[1].u.memref.size = msg_size; + + param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT; + param[2].u.memref.shm = channel->tee_shm; + param[2].u.memref.size = SCMI_OPTEE_MAX_MSG_SIZE; + ret = tee_client_invoke_func(scmi_optee_private->tee_ctx, &arg, param); if (ret < 0 || arg.ret) { dev_err(scmi_optee_private->dev, "Can't invoke channel %u: %d / %#x\n", @@ -279,7 +335,26 @@ static void scmi_optee_clear_channel(struct scmi_chan_info *cinfo) { struct scmi_optee_channel *channel = cinfo->transport_info; - shmem_clear_channel(channel->shmem); + if (!channel->tee_shm) + shmem_clear_channel(channel->req.shmem); +} + +static int setup_dynamic_shmem(struct device *dev, struct scmi_optee_channel *channel) +{ + const size_t msg_size = SCMI_OPTEE_MAX_MSG_SIZE; + void *shbuf; + + channel->tee_shm = tee_shm_alloc_kernel_buf(scmi_optee_private->tee_ctx, msg_size); + if (IS_ERR(channel->tee_shm)) { + dev_err(channel->cinfo->dev, "shmem allocation failed\n"); + return -ENOMEM; + } + + shbuf = tee_shm_get_va(channel->tee_shm, 0); + memset(shbuf, 0, msg_size); + channel->req.msg = shbuf; + + return 0; } static int setup_static_shmem(struct device *dev, struct scmi_chan_info *cinfo, @@ -304,8 +379,8 @@ static int setup_static_shmem(struct device *dev, struct scmi_chan_info *cinfo, size = resource_size(&res); - channel->shmem = devm_ioremap(dev, res.start, size); - if (!channel->shmem) { + channel->req.shmem = devm_ioremap(dev, res.start, size); + if (!channel->req.shmem) { dev_err(dev, "Failed to ioremap SCMI Tx shared memory\n"); ret = -EADDRNOTAVAIL; goto out; @@ -325,7 +400,7 @@ static int setup_shmem(struct device *dev, struct scmi_chan_info *cinfo, if (of_find_property(cinfo->dev->of_node, "shmem", NULL)) return setup_static_shmem(dev, cinfo, channel); else - return -ENOMEM; + return setup_dynamic_shmem(dev, channel); } static int scmi_optee_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, bool tx) @@ -405,27 +480,22 @@ static int scmi_optee_chan_free(int id, void *p, void *data) return 0; } -static struct scmi_shared_mem __iomem * -get_channel_shm(struct scmi_optee_channel *chan, struct scmi_xfer *xfer) -{ - if (!chan) - return NULL; - - return chan->shmem; -} - - static int scmi_optee_send_message(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer) { struct scmi_optee_channel *channel = cinfo->transport_info; - struct scmi_shared_mem __iomem *shmem = get_channel_shm(channel, xfer); int ret; mutex_lock(&channel->mu); - shmem_tx_prepare(shmem, xfer); - ret = invoke_process_smt_channel(channel); + if (channel->tee_shm) { + msg_tx_prepare(channel->req.msg, xfer); + ret = invoke_process_msg_channel(channel, msg_command_size(xfer)); + } else { + shmem_tx_prepare(channel->req.shmem, xfer); + ret = invoke_process_smt_channel(channel); + } + if (ret) mutex_unlock(&channel->mu); @@ -436,9 +506,11 @@ static void scmi_optee_fetch_response(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer) { struct scmi_optee_channel *channel = cinfo->transport_info; - struct scmi_shared_mem __iomem *shmem = get_channel_shm(channel, xfer); - shmem_fetch_response(shmem, xfer); + if (channel->tee_shm) + msg_fetch_response(channel->req.msg, SCMI_OPTEE_MAX_MSG_SIZE, xfer); + else + shmem_fetch_response(channel->req.shmem, xfer); } static void scmi_optee_mark_txdone(struct scmi_chan_info *cinfo, int ret, -- cgit v1.2.3 From dbfb5f94e084287f0a3f23d14ef6692c43c98855 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Tue, 26 Apr 2022 16:35:06 -0700 Subject: dt-bindings: power: rpmpd: Add sc8280xp RPMh power-domains The sc8280xp has 13 power-domains controlled through the RPMh, document the compatible and provide definitions for the power-domains - and their active-only variants where applicable. The SA8540p differs slightly in the power domains exposed, so add a separate compatible for this, but reuse the constants to allow sharing the DeviceTree source. Signed-off-by: Bjorn Andersson Acked-by: Krzysztof Kozlowski Reviewed-by: Johan Hovold Link: https://lore.kernel.org/r/20220426233508.1762345-2-bjorn.andersson@linaro.org --- .../devicetree/bindings/power/qcom,rpmpd.yaml | 2 ++ include/dt-bindings/power/qcom-rpmpd.h | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml index b90c4427beec..ad77a6380f38 100644 --- a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml +++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml @@ -27,10 +27,12 @@ properties: - qcom,msm8998-rpmpd - qcom,qcm2290-rpmpd - qcom,qcs404-rpmpd + - qcom,sa8540p-rpmhpd - qcom,sdm660-rpmpd - qcom,sc7180-rpmhpd - qcom,sc7280-rpmhpd - qcom,sc8180x-rpmhpd + - qcom,sc8280xp-rpmhpd - qcom,sdm845-rpmhpd - qcom,sdx55-rpmhpd - qcom,sdx65-rpmhpd diff --git a/include/dt-bindings/power/qcom-rpmpd.h b/include/dt-bindings/power/qcom-rpmpd.h index 0a8978d0c78c..6cce5b7aa940 100644 --- a/include/dt-bindings/power/qcom-rpmpd.h +++ b/include/dt-bindings/power/qcom-rpmpd.h @@ -125,6 +125,24 @@ #define SC8180X_MX 9 #define SC8180X_MX_AO 10 +/* SC8280XP Power Domain Indexes */ +#define SC8280XP_CX 0 +#define SC8280XP_CX_AO 1 +#define SC8280XP_DDR 2 +#define SC8280XP_EBI 3 +#define SC8280XP_GFX 4 +#define SC8280XP_LCX 5 +#define SC8280XP_LMX 6 +#define SC8280XP_MMCX 7 +#define SC8280XP_MMCX_AO 8 +#define SC8280XP_MSS 9 +#define SC8280XP_MX 10 +#define SC8280XP_MXC 12 +#define SC8280XP_MX_AO 11 +#define SC8280XP_NSP 13 +#define SC8280XP_QPHY 14 +#define SC8280XP_XO 15 + /* SDM845 Power Domain performance levels */ #define RPMH_REGULATOR_LEVEL_RETENTION 16 #define RPMH_REGULATOR_LEVEL_MIN_SVS 48 -- cgit v1.2.3 From af77132e9d2bc8ca477003fef15e2c677ae958b1 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Tue, 26 Apr 2022 16:35:07 -0700 Subject: soc: qcom: rpmhpd: Don't warn about sparse rpmhpd arrays In some cases the DT binding will fully describe the set of available RPMh power-domains, but there is no reason for exposing them all in the implementation. Omitting individual data->domains is handle gracefully by of_genpd_add_provider_onecell(), so there's no reason for printing a warning when this occurs. Signed-off-by: Bjorn Andersson Reviewed-by: Johan Hovold Link: https://lore.kernel.org/r/20220426233508.1762345-3-bjorn.andersson@linaro.org --- drivers/soc/qcom/rpmhpd.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c index 11102ac47769..f8d28e902942 100644 --- a/drivers/soc/qcom/rpmhpd.c +++ b/drivers/soc/qcom/rpmhpd.c @@ -613,10 +613,8 @@ static int rpmhpd_probe(struct platform_device *pdev) data->num_domains = num_pds; for (i = 0; i < num_pds; i++) { - if (!rpmhpds[i]) { - dev_warn(dev, "rpmhpds[%d] is empty\n", i); + if (!rpmhpds[i]) continue; - } rpmhpds[i]->dev = dev; rpmhpds[i]->addr = cmd_db_read_addr(rpmhpds[i]->res_name); -- cgit v1.2.3 From f68f1cb3437d338ee88a9fc05acd19dacdb9aabd Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Tue, 26 Apr 2022 16:35:08 -0700 Subject: soc: qcom: rpmhpd: add sc8280xp & sa8540p rpmh power-domains The Qualcomm sc8280xp platform has 13 and the sa8540p platform has 11 power-domains. Add compatibles, the typically used ones power-domains and their relevant active-only variants, to the RPMh power-domain driver. Signed-off-by: Bjorn Andersson Reviewed-by: Johan Hovold Link: https://lore.kernel.org/r/20220426233508.1762345-4-bjorn.andersson@linaro.org --- drivers/soc/qcom/rpmhpd.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c index f8d28e902942..05fff8691ee3 100644 --- a/drivers/soc/qcom/rpmhpd.c +++ b/drivers/soc/qcom/rpmhpd.c @@ -180,6 +180,36 @@ static struct rpmhpd mxc_ao = { .res_name = "mxc.lvl", }; +static struct rpmhpd nsp = { + .pd = { .name = "nsp", }, + .res_name = "nsp.lvl", +}; + +static struct rpmhpd qphy = { + .pd = { .name = "qphy", }, + .res_name = "qphy.lvl", +}; + +/* SA8540P RPMH powerdomains */ +static struct rpmhpd *sa8540p_rpmhpds[] = { + [SC8280XP_CX] = &cx, + [SC8280XP_CX_AO] = &cx_ao, + [SC8280XP_EBI] = &ebi, + [SC8280XP_GFX] = &gfx, + [SC8280XP_LCX] = &lcx, + [SC8280XP_LMX] = &lmx, + [SC8280XP_MMCX] = &mmcx, + [SC8280XP_MMCX_AO] = &mmcx_ao, + [SC8280XP_MX] = &mx, + [SC8280XP_MX_AO] = &mx_ao, + [SC8280XP_NSP] = &nsp, +}; + +static const struct rpmhpd_desc sa8540p_desc = { + .rpmhpds = sa8540p_rpmhpds, + .num_pds = ARRAY_SIZE(sa8540p_rpmhpds), +}; + /* SDM845 RPMH powerdomains */ static struct rpmhpd *sdm845_rpmhpds[] = { [SDM845_CX] = &cx_w_mx_parent, @@ -378,10 +408,33 @@ static const struct rpmhpd_desc sc8180x_desc = { .num_pds = ARRAY_SIZE(sc8180x_rpmhpds), }; +/* SC8280xp RPMH powerdomains */ +static struct rpmhpd *sc8280xp_rpmhpds[] = { + [SC8280XP_CX] = &cx, + [SC8280XP_CX_AO] = &cx_ao, + [SC8280XP_EBI] = &ebi, + [SC8280XP_GFX] = &gfx, + [SC8280XP_LCX] = &lcx, + [SC8280XP_LMX] = &lmx, + [SC8280XP_MMCX] = &mmcx, + [SC8280XP_MMCX_AO] = &mmcx_ao, + [SC8280XP_MX] = &mx, + [SC8280XP_MX_AO] = &mx_ao, + [SC8280XP_NSP] = &nsp, + [SC8280XP_QPHY] = &qphy, +}; + +static const struct rpmhpd_desc sc8280xp_desc = { + .rpmhpds = sc8280xp_rpmhpds, + .num_pds = ARRAY_SIZE(sc8280xp_rpmhpds), +}; + static const struct of_device_id rpmhpd_match_table[] = { + { .compatible = "qcom,sa8540p-rpmhpd", .data = &sa8540p_desc }, { .compatible = "qcom,sc7180-rpmhpd", .data = &sc7180_desc }, { .compatible = "qcom,sc7280-rpmhpd", .data = &sc7280_desc }, { .compatible = "qcom,sc8180x-rpmhpd", .data = &sc8180x_desc }, + { .compatible = "qcom,sc8280xp-rpmhpd", .data = &sc8280xp_desc }, { .compatible = "qcom,sdm845-rpmhpd", .data = &sdm845_desc }, { .compatible = "qcom,sdx55-rpmhpd", .data = &sdx55_desc}, { .compatible = "qcom,sdx65-rpmhpd", .data = &sdx65_desc}, -- cgit v1.2.3 From be34f45f0d4aa91c6b83d140f9ace1ca40a5f9dc Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 26 Apr 2022 11:26:10 +0300 Subject: memory: omap-gpmc: Make OMAP_GPMC config visible and selectable So far for armv7 TI platforms, GPMC was being selected by arch/arm/mach-* architecture Kconfig files. For K3 platforms, GPMC is no longer required for basic boot and cannot be always enabled by default by mach- Kconfig. We need a way for user (or board defconfig) to enable it if required so make OMAP_GPMC Kconfig option always visible. Drop COMPILE_TEST as build fails if IRQ_DOMAIN is not enabled. Signed-off-by: Roger Quadros Link: https://lore.kernel.org/r/20220426082611.24427-2-rogerq@kernel.org Signed-off-by: Krzysztof Kozlowski --- drivers/memory/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 30bff6cb1b8d..da9634daad04 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -103,7 +103,7 @@ config TI_EMIF temperature changes config OMAP_GPMC - bool "Texas Instruments OMAP SoC GPMC driver" if COMPILE_TEST + bool "Texas Instruments OMAP SoC GPMC driver" depends on OF_ADDRESS select GPIOLIB help -- cgit v1.2.3 From 854fd9209b20837ab48c2e6714e5e44dd8ea33a2 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 26 Apr 2022 11:26:11 +0300 Subject: memory: omap-gpmc: Allow building as a module Allow OMAP_GPMC to be built as a module. When building this driver as a module, the symbol 'of_default_bus_match_table' will not be found as it is not being exported. The of_match_node() call is redundant anyways as of_platform_default_populate() already takes care of matching with 'of_default_bus_match_table'. So get rid of that call. This will also resolve the module build failure. Move compatible match table to the end where it is usually expected. Signed-off-by: Roger Quadros Link: https://lore.kernel.org/r/20220426082611.24427-3-rogerq@kernel.org Signed-off-by: Krzysztof Kozlowski --- drivers/memory/Kconfig | 2 +- drivers/memory/omap-gpmc.c | 43 +++++++++++++++++++++++-------------------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index da9634daad04..b7800b37af78 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -103,7 +103,7 @@ config TI_EMIF temperature changes config OMAP_GPMC - bool "Texas Instruments OMAP SoC GPMC driver" + tristate "Texas Instruments OMAP SoC GPMC driver" depends on OF_ADDRESS select GPIOLIB help diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index ed11887c1b7c..2351f2708da2 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -1889,16 +1890,6 @@ int gpmc_cs_program_settings(int cs, struct gpmc_settings *p) } #ifdef CONFIG_OF -static const struct of_device_id gpmc_dt_ids[] = { - { .compatible = "ti,omap2420-gpmc" }, - { .compatible = "ti,omap2430-gpmc" }, - { .compatible = "ti,omap3430-gpmc" }, /* omap3430 & omap3630 */ - { .compatible = "ti,omap4430-gpmc" }, /* omap4430 & omap4460 & omap543x */ - { .compatible = "ti,am3352-gpmc" }, /* am335x devices */ - { .compatible = "ti,am64-gpmc" }, - { } -}; - static void gpmc_cs_set_name(int cs, const char *name) { struct gpmc_cs_data *gpmc = &gpmc_cs[cs]; @@ -2257,11 +2248,9 @@ no_timings: if (!of_platform_device_create(child, NULL, &pdev->dev)) goto err_child_fail; - /* is child a common bus? */ - if (of_match_node(of_default_bus_match_table, child)) - /* create children and other common bus children */ - if (of_platform_default_populate(child, NULL, &pdev->dev)) - goto err_child_fail; + /* create children and other common bus children */ + if (of_platform_default_populate(child, NULL, &pdev->dev)) + goto err_child_fail; return 0; @@ -2278,6 +2267,8 @@ err: return ret; } +static const struct of_device_id gpmc_dt_ids[]; + static int gpmc_probe_dt(struct platform_device *pdev) { int ret; @@ -2644,6 +2635,19 @@ static int gpmc_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(gpmc_pm_ops, gpmc_suspend, gpmc_resume); +#ifdef CONFIG_OF +static const struct of_device_id gpmc_dt_ids[] = { + { .compatible = "ti,omap2420-gpmc" }, + { .compatible = "ti,omap2430-gpmc" }, + { .compatible = "ti,omap3430-gpmc" }, /* omap3430 & omap3630 */ + { .compatible = "ti,omap4430-gpmc" }, /* omap4430 & omap4460 & omap543x */ + { .compatible = "ti,am3352-gpmc" }, /* am335x devices */ + { .compatible = "ti,am64-gpmc" }, + { } +}; +MODULE_DEVICE_TABLE(of, gpmc_dt_ids); +#endif + static struct platform_driver gpmc_driver = { .probe = gpmc_probe, .remove = gpmc_remove, @@ -2654,8 +2658,7 @@ static struct platform_driver gpmc_driver = { }, }; -static __init int gpmc_init(void) -{ - return platform_driver_register(&gpmc_driver); -} -postcore_initcall(gpmc_init); +module_platform_driver(gpmc_driver); + +MODULE_DESCRIPTION("Texas Instruments GPMC driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From f3c45c045e25ed52461829d2ce07954f72b6ad15 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Fri, 29 Apr 2022 12:39:43 +0100 Subject: firmware: arm_ffa: Fix uuid parameter to ffa_partition_probe While we pass uuid_null intentionally to ffa_partition_probe in ffa_setup_partitions to get the count of the partitions, it must not be uuid_null in ffa_partition_info_get which is used by the ffa_drivers to fetch the specific partition info passing the UUID of the partition. Fix ffa_partition_info_get by passing the received uuid down to ffa_partition_probe so that the correct partition information is fetched. Link: https://lore.kernel.org/r/20220429113946.2087145-1-sudeep.holla@arm.com Fixes: d0c0bce83122 ("firmware: arm_ffa: Setup in-kernel users of FFA partitions") Reported-by: Arunachalam Ganapathy Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index ccccecae615f..6a913ac91e8e 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -588,7 +588,7 @@ static int ffa_partition_info_get(const char *uuid_str, return -ENODEV; } - count = ffa_partition_probe(&uuid_null, &pbuf); + count = ffa_partition_probe(&uuid, &pbuf); if (count <= 0) return -ENOENT; -- cgit v1.2.3 From 00512d2930b338fdd42bd90bbd1793fe212c2d31 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Fri, 29 Apr 2022 12:39:44 +0100 Subject: firmware: arm_ffa: Remove incorrect assignment of driver_data The ffa core driver currently assigns its own driver information to individual ffa device driver_data which is wrong. Firstly, it leaks this core driver information to individual ffa_device and hence to ffa_driver. Secondly the ffa_device driver_data is for use by individual ffa_driver and not for this core driver managing all those devices. Link: https://lore.kernel.org/r/20220429113946.2087145-2-sudeep.holla@arm.com Fixes: d0c0bce83122 ("firmware: arm_ffa: Setup in-kernel users of FFA partitions") Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/driver.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 6a913ac91e8e..ec731e9e942b 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -694,8 +694,6 @@ static void ffa_setup_partitions(void) __func__, tpbuf->id); continue; } - - ffa_dev_set_drvdata(ffa_dev, drv_info); } kfree(pbuf); } -- cgit v1.2.3 From 498af8d1678ae2351218337b47bbf3cb0fc16821 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Fri, 29 Apr 2022 12:39:45 +0100 Subject: firmware: arm_ffa: Add ffa_dev_get_drvdata helper function Add a helper function to fetch ffa_dev's driver_data using dev_get_drvdata. At the same time move existing ffa_dev_set_drvdata to use dev_set_drvdata. Link: https://lore.kernel.org/r/20220429113946.2087145-3-sudeep.holla@arm.com Suggested-by: Arunachalam Ganapathy Signed-off-by: Sudeep Holla --- include/linux/arm_ffa.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/linux/arm_ffa.h b/include/linux/arm_ffa.h index 85651e41ded8..e5c76c1ef9ed 100644 --- a/include/linux/arm_ffa.h +++ b/include/linux/arm_ffa.h @@ -38,7 +38,12 @@ struct ffa_driver { static inline void ffa_dev_set_drvdata(struct ffa_device *fdev, void *data) { - fdev->dev.driver_data = data; + dev_set_drvdata(&fdev->dev, data); +} + +static inline void *ffa_dev_get_drvdata(struct ffa_device *fdev) +{ + return dev_get_drvdata(&fdev->dev); } #if IS_REACHABLE(CONFIG_ARM_FFA_TRANSPORT) -- cgit v1.2.3 From f3f3bdbd58cea4fdd088075fdc8864fc47ecd419 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Fri, 29 Apr 2022 12:39:46 +0100 Subject: tee: optee: Use ffa_dev_get_drvdata to fetch driver_data Due to lack of an helper like ffa_dev_get_drvdata, this driver was fetching driver_data directly accessing the structure member. Now that we have added an helper, just use the same instead. Link: https://lore.kernel.org/r/20220429113946.2087145-4-sudeep.holla@arm.com Signed-off-by: Sudeep Holla --- drivers/tee/optee/ffa_abi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c index a5eb4ef46971..b819a65cf89d 100644 --- a/drivers/tee/optee/ffa_abi.c +++ b/drivers/tee/optee/ffa_abi.c @@ -759,7 +759,7 @@ static const struct optee_ops optee_ffa_ops = { static void optee_ffa_remove(struct ffa_device *ffa_dev) { - struct optee *optee = ffa_dev->dev.driver_data; + struct optee *optee = ffa_dev_get_drvdata(ffa_dev); optee_remove_common(optee); -- cgit v1.2.3 From 9be1a9996ebd8da27cafc947e4578b2d419dc07e Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 25 Apr 2022 18:45:42 -0700 Subject: soc: rockchip: Clean up Kconfig whitespace There are spaces instead of tabs, and other inconsistent indentation. Signed-off-by: Brian Norris Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20220426014545.628100-1-briannorris@chromium.org Signed-off-by: Heiko Stuebner --- drivers/soc/rockchip/Kconfig | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/soc/rockchip/Kconfig b/drivers/soc/rockchip/Kconfig index 156ac0e0c8fe..aff2f7e95237 100644 --- a/drivers/soc/rockchip/Kconfig +++ b/drivers/soc/rockchip/Kconfig @@ -23,23 +23,23 @@ config ROCKCHIP_IODOMAIN voltage supplied by the regulators. config ROCKCHIP_PM_DOMAINS - bool "Rockchip generic power domain" - depends on PM - select PM_GENERIC_DOMAINS - help - Say y here to enable power domain support. - In order to meet high performance and low power requirements, a power - management unit is designed or saving power when RK3288 in low power - mode. The RK3288 PMU is dedicated for managing the power of the whole chip. + bool "Rockchip generic power domain" + depends on PM + select PM_GENERIC_DOMAINS + help + Say y here to enable power domain support. + In order to meet high performance and low power requirements, a power + management unit is designed or saving power when RK3288 in low power + mode. The RK3288 PMU is dedicated for managing the power of the whole chip. - If unsure, say N. + If unsure, say N. config ROCKCHIP_DTPM tristate "Rockchip DTPM hierarchy" depends on DTPM && m help - Describe the hierarchy for the Dynamic Thermal Power - Management tree on this platform. That will create all the - power capping capable devices. + Describe the hierarchy for the Dynamic Thermal Power Management tree + on this platform. That will create all the power capping capable + devices. endif -- cgit v1.2.3 From 2ca9e472c70f15de768ab200571e2f6634f66394 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 25 Apr 2022 18:45:43 -0700 Subject: soc: rockchip: power-domain: Replace dsb() with smb() It's unclear if these are really needed at all, but seemingly their purpose is only as a write barrier. Use the general macro instead of the ARM-specific one. This driver is partially marked for COMPILE_TEST'ing, but it doesn't build under non-ARM architectures. Fix this up before *really* enabling it for COMPILE_TEST. Signed-off-by: Brian Norris Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20220426014545.628100-2-briannorris@chromium.org Signed-off-by: Heiko Stuebner --- drivers/soc/rockchip/pm_domains.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soc/rockchip/pm_domains.c b/drivers/soc/rockchip/pm_domains.c index 1b029e494274..cf16ff9b73b3 100644 --- a/drivers/soc/rockchip/pm_domains.c +++ b/drivers/soc/rockchip/pm_domains.c @@ -178,7 +178,7 @@ static int rockchip_pmu_set_idle_request(struct rockchip_pm_domain *pd, regmap_update_bits(pmu->regmap, pmu->info->req_offset, pd_info->req_mask, idle ? -1U : 0); - dsb(sy); + wmb(); /* Wait util idle_ack = 1 */ target_ack = idle ? pd_info->ack_mask : 0; @@ -285,7 +285,7 @@ static void rockchip_do_pmu_set_power_domain(struct rockchip_pm_domain *pd, regmap_update_bits(pmu->regmap, pmu->info->pwr_offset, pd->info->pwr_mask, on ? 0 : -1U); - dsb(sy); + wmb(); if (readx_poll_timeout_atomic(rockchip_pmu_domain_is_on, pd, is_on, is_on == on, 0, 10000)) { -- cgit v1.2.3 From add9f6f30e54b5c07e7a0260cda459ef1d9646b7 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 25 Apr 2022 18:45:44 -0700 Subject: soc: rockchip: Fix compile-testing SoC drivers Similar to commit 7f94b69ece51 ("ARM: ixp4xx: fix compile-testing soc drivers"). drivers/soc/rockchip/Kconfig makes plenty of provision for configuring drivers either for ARCH_ROCKCHIP or for COMPILE_TEST. But drivers/soc/Makefile pulls the rug out from under us, by refusing to build anything if we specified COMPILE_TEST but not ARCH_ROCKCHIP. Currently, I'm not aware of anything that breaks without this patch, but it certainly makes for confusing builds (CONFIG_ROCKCHIP_PM_DOMAINS=y, but we didn't actually compile the driver?) and leaves room for future error (I have pending patches that break confusingly with COMPILE_TEST=y even though their Kconfig dependencies seem correct [1]). Defer to drivers/soc/rockchip/{Makefile,Kconfig} to do the right thing. [1] e.g., https://lore.kernel.org/linux-rockchip/20220405184816.RFC.2.I2d73b403944f0b8b5871a77585b73f31ccc62999@changeid/ [RFC PATCH 2/2] PM / devfreq: rk3399_dmc: Block PMU during transitions Signed-off-by: Brian Norris Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20220425184503.v3.3.Id5f16dec920f620120c0a143a97a12e16d401760@changeid Signed-off-by: Heiko Stuebner --- drivers/soc/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 904eec2a7871..3c08514d66f1 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -22,7 +22,7 @@ obj-y += microchip/ obj-y += amlogic/ obj-y += qcom/ obj-y += renesas/ -obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ +obj-y += rockchip/ obj-$(CONFIG_SOC_SAMSUNG) += samsung/ obj-$(CONFIG_SOC_SIFIVE) += sifive/ obj-y += sunxi/ -- cgit v1.2.3 From cbb0f00131e48f38db9eda67e0f731504404567b Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Sun, 1 May 2022 16:55:05 +0200 Subject: soc: apple: Always include Makefile We want to allow the code inside drivers/soc/apple to be compiled with COMPILE_TEST but this will currently result in linking errors because ARCH_APPLE is not set and make will never recurse into drivers/soc/apple. Let's just unconditionally recurse into apple/ since all drivers in there are guarded by config options anyways. Signed-off-by: Sven Peter --- drivers/soc/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 904eec2a7871..6ad4177595f3 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -4,7 +4,7 @@ # obj-$(CONFIG_ARCH_ACTIONS) += actions/ -obj-$(CONFIG_ARCH_APPLE) += apple/ +obj-y += apple/ obj-y += aspeed/ obj-$(CONFIG_ARCH_AT91) += atmel/ obj-y += bcm/ -- cgit v1.2.3 From 9bd1d9a0d8bb1a549831fd98fcc3105960f7068b Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Sun, 1 May 2022 16:55:06 +0200 Subject: soc: apple: Add RTKit IPC library Apple SoCs such as the M1 come with multiple embedded co-processors running proprietary firmware. Communication with those is established over a simple mailbox using the RTKit IPC protocol. This cannot be implemented inside the mailbox subsystem since on top of communication over channels we also need support for starting, hibernating and resetting these co-processors. We also need to handle shared memory allocations differently depending on the co-processor and don't want to split that across multiple drivers. Reviewed-by: Arnd Bergmann Signed-off-by: Sven Peter --- drivers/soc/apple/Kconfig | 13 + drivers/soc/apple/Makefile | 3 + drivers/soc/apple/rtkit-crashlog.c | 154 ++++++ drivers/soc/apple/rtkit-internal.h | 62 +++ drivers/soc/apple/rtkit.c | 958 +++++++++++++++++++++++++++++++++++++ include/linux/soc/apple/rtkit.h | 155 ++++++ 6 files changed, 1345 insertions(+) create mode 100644 drivers/soc/apple/rtkit-crashlog.c create mode 100644 drivers/soc/apple/rtkit-internal.h create mode 100644 drivers/soc/apple/rtkit.c create mode 100644 include/linux/soc/apple/rtkit.h diff --git a/drivers/soc/apple/Kconfig b/drivers/soc/apple/Kconfig index 9b8de31d6a8f..1b09037584a0 100644 --- a/drivers/soc/apple/Kconfig +++ b/drivers/soc/apple/Kconfig @@ -17,6 +17,19 @@ config APPLE_PMGR_PWRSTATE controls for SoC devices. This driver manages them through the generic power domain framework, and also provides reset support. +config APPLE_RTKIT + tristate "Apple RTKit co-processor IPC protocol" + depends on MAILBOX + depends on ARCH_APPLE || COMPILE_TEST + default ARCH_APPLE + help + Apple SoCs such as the M1 come with various co-processors running + their proprietary RTKit operating system. This option enables support + for the protocol library used to communicate with those. It is used + by various client drivers. + + Say 'y' here if you have an Apple SoC. + endmenu endif diff --git a/drivers/soc/apple/Makefile b/drivers/soc/apple/Makefile index c114e84667e4..8aca79fca0dd 100644 --- a/drivers/soc/apple/Makefile +++ b/drivers/soc/apple/Makefile @@ -1,2 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_APPLE_PMGR_PWRSTATE) += apple-pmgr-pwrstate.o + +obj-$(CONFIG_APPLE_RTKIT) += apple-rtkit.o +apple-rtkit-y = rtkit.o rtkit-crashlog.o diff --git a/drivers/soc/apple/rtkit-crashlog.c b/drivers/soc/apple/rtkit-crashlog.c new file mode 100644 index 000000000000..732deed64660 --- /dev/null +++ b/drivers/soc/apple/rtkit-crashlog.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple RTKit IPC library + * Copyright (C) The Asahi Linux Contributors + */ +#include "rtkit-internal.h" + +#define FOURCC(a, b, c, d) \ + (((u32)(a) << 24) | ((u32)(b) << 16) | ((u32)(c) << 8) | ((u32)(d))) + +#define APPLE_RTKIT_CRASHLOG_HEADER FOURCC('C', 'L', 'H', 'E') +#define APPLE_RTKIT_CRASHLOG_STR FOURCC('C', 's', 't', 'r') +#define APPLE_RTKIT_CRASHLOG_VERSION FOURCC('C', 'v', 'e', 'r') +#define APPLE_RTKIT_CRASHLOG_MBOX FOURCC('C', 'm', 'b', 'x') +#define APPLE_RTKIT_CRASHLOG_TIME FOURCC('C', 't', 'i', 'm') + +struct apple_rtkit_crashlog_header { + u32 fourcc; + u32 version; + u32 size; + u32 flags; + u8 _unk[16]; +}; +static_assert(sizeof(struct apple_rtkit_crashlog_header) == 0x20); + +struct apple_rtkit_crashlog_mbox_entry { + u64 msg0; + u64 msg1; + u32 timestamp; + u8 _unk[4]; +}; +static_assert(sizeof(struct apple_rtkit_crashlog_mbox_entry) == 0x18); + +static void apple_rtkit_crashlog_dump_str(struct apple_rtkit *rtk, u8 *bfr, + size_t size) +{ + u32 idx; + u8 *ptr, *end; + + memcpy(&idx, bfr, 4); + + ptr = bfr + 4; + end = bfr + size; + while (ptr < end) { + u8 *newline = memchr(ptr, '\n', end - ptr); + + if (newline) { + u8 tmp = *newline; + *newline = '\0'; + dev_warn(rtk->dev, "RTKit: Message (id=%x): %s\n", idx, + ptr); + *newline = tmp; + ptr = newline + 1; + } else { + dev_warn(rtk->dev, "RTKit: Message (id=%x): %s", idx, + ptr); + break; + } + } +} + +static void apple_rtkit_crashlog_dump_version(struct apple_rtkit *rtk, u8 *bfr, + size_t size) +{ + dev_warn(rtk->dev, "RTKit: Version: %s", bfr + 16); +} + +static void apple_rtkit_crashlog_dump_time(struct apple_rtkit *rtk, u8 *bfr, + size_t size) +{ + u64 crash_time; + + memcpy(&crash_time, bfr, 8); + dev_warn(rtk->dev, "RTKit: Crash time: %lld", crash_time); +} + +static void apple_rtkit_crashlog_dump_mailbox(struct apple_rtkit *rtk, u8 *bfr, + size_t size) +{ + u32 type, index, i; + size_t n_messages; + struct apple_rtkit_crashlog_mbox_entry entry; + + memcpy(&type, bfr + 16, 4); + memcpy(&index, bfr + 24, 4); + n_messages = (size - 28) / sizeof(entry); + + dev_warn(rtk->dev, "RTKit: Mailbox history (type = %d, index = %d)", + type, index); + for (i = 0; i < n_messages; ++i) { + memcpy(&entry, bfr + 28 + i * sizeof(entry), sizeof(entry)); + dev_warn(rtk->dev, "RTKit: #%03d@%08x: %016llx %016llx", i, + entry.timestamp, entry.msg0, entry.msg1); + } +} + +void apple_rtkit_crashlog_dump(struct apple_rtkit *rtk, u8 *bfr, size_t size) +{ + size_t offset; + u32 section_fourcc, section_size; + struct apple_rtkit_crashlog_header header; + + memcpy(&header, bfr, sizeof(header)); + if (header.fourcc != APPLE_RTKIT_CRASHLOG_HEADER) { + dev_warn(rtk->dev, "RTKit: Expected crashlog header but got %x", + header.fourcc); + return; + } + + if (header.size > size) { + dev_warn(rtk->dev, "RTKit: Crashlog size (%x) is too large", + header.size); + return; + } + + size = header.size; + offset = sizeof(header); + + while (offset < size) { + memcpy(§ion_fourcc, bfr + offset, 4); + memcpy(§ion_size, bfr + offset + 12, 4); + + switch (section_fourcc) { + case APPLE_RTKIT_CRASHLOG_HEADER: + dev_dbg(rtk->dev, "RTKit: End of crashlog reached"); + return; + case APPLE_RTKIT_CRASHLOG_STR: + apple_rtkit_crashlog_dump_str(rtk, bfr + offset + 16, + section_size); + break; + case APPLE_RTKIT_CRASHLOG_VERSION: + apple_rtkit_crashlog_dump_version( + rtk, bfr + offset + 16, section_size); + break; + case APPLE_RTKIT_CRASHLOG_MBOX: + apple_rtkit_crashlog_dump_mailbox( + rtk, bfr + offset + 16, section_size); + break; + case APPLE_RTKIT_CRASHLOG_TIME: + apple_rtkit_crashlog_dump_time(rtk, bfr + offset + 16, + section_size); + break; + default: + dev_warn(rtk->dev, + "RTKit: Unknown crashlog section: %x", + section_fourcc); + } + + offset += section_size; + } + + dev_warn(rtk->dev, + "RTKit: End of crashlog reached but no footer present"); +} diff --git a/drivers/soc/apple/rtkit-internal.h b/drivers/soc/apple/rtkit-internal.h new file mode 100644 index 000000000000..24bd619ec5e4 --- /dev/null +++ b/drivers/soc/apple/rtkit-internal.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* + * Apple RTKit IPC library + * Copyright (C) The Asahi Linux Contributors + */ + +#ifndef _APPLE_RTKIT_INTERAL_H +#define _APPLE_RTKIT_INTERAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define APPLE_RTKIT_APP_ENDPOINT_START 0x20 +#define APPLE_RTKIT_MAX_ENDPOINTS 0x100 + +struct apple_rtkit { + void *cookie; + const struct apple_rtkit_ops *ops; + struct device *dev; + + const char *mbox_name; + int mbox_idx; + struct mbox_client mbox_cl; + struct mbox_chan *mbox_chan; + + struct completion epmap_completion; + struct completion iop_pwr_ack_completion; + struct completion ap_pwr_ack_completion; + + int boot_result; + int version; + + unsigned int iop_power_state; + unsigned int ap_power_state; + bool crashed; + + DECLARE_BITMAP(endpoints, APPLE_RTKIT_MAX_ENDPOINTS); + + struct apple_rtkit_shmem ioreport_buffer; + struct apple_rtkit_shmem crashlog_buffer; + + struct apple_rtkit_shmem syslog_buffer; + char *syslog_msg_buffer; + size_t syslog_n_entries; + size_t syslog_msg_size; + + struct workqueue_struct *wq; +}; + +void apple_rtkit_crashlog_dump(struct apple_rtkit *rtk, u8 *bfr, size_t size); + +#endif diff --git a/drivers/soc/apple/rtkit.c b/drivers/soc/apple/rtkit.c new file mode 100644 index 000000000000..cf1129e9f76b --- /dev/null +++ b/drivers/soc/apple/rtkit.c @@ -0,0 +1,958 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple RTKit IPC library + * Copyright (C) The Asahi Linux Contributors + */ + +#include "rtkit-internal.h" + +enum { + APPLE_RTKIT_PWR_STATE_OFF = 0x00, /* power off, cannot be restarted */ + APPLE_RTKIT_PWR_STATE_SLEEP = 0x01, /* sleeping, can be restarted */ + APPLE_RTKIT_PWR_STATE_QUIESCED = 0x10, /* running but no communication */ + APPLE_RTKIT_PWR_STATE_ON = 0x20, /* normal operating state */ +}; + +enum { + APPLE_RTKIT_EP_MGMT = 0, + APPLE_RTKIT_EP_CRASHLOG = 1, + APPLE_RTKIT_EP_SYSLOG = 2, + APPLE_RTKIT_EP_DEBUG = 3, + APPLE_RTKIT_EP_IOREPORT = 4, + APPLE_RTKIT_EP_OSLOG = 8, +}; + +#define APPLE_RTKIT_MGMT_TYPE GENMASK_ULL(59, 52) + +enum { + APPLE_RTKIT_MGMT_HELLO = 1, + APPLE_RTKIT_MGMT_HELLO_REPLY = 2, + APPLE_RTKIT_MGMT_STARTEP = 5, + APPLE_RTKIT_MGMT_SET_IOP_PWR_STATE = 6, + APPLE_RTKIT_MGMT_SET_IOP_PWR_STATE_ACK = 7, + APPLE_RTKIT_MGMT_EPMAP = 8, + APPLE_RTKIT_MGMT_EPMAP_REPLY = 8, + APPLE_RTKIT_MGMT_SET_AP_PWR_STATE = 0xb, + APPLE_RTKIT_MGMT_SET_AP_PWR_STATE_ACK = 0xb, +}; + +#define APPLE_RTKIT_MGMT_HELLO_MINVER GENMASK_ULL(15, 0) +#define APPLE_RTKIT_MGMT_HELLO_MAXVER GENMASK_ULL(31, 16) + +#define APPLE_RTKIT_MGMT_EPMAP_LAST BIT_ULL(51) +#define APPLE_RTKIT_MGMT_EPMAP_BASE GENMASK_ULL(34, 32) +#define APPLE_RTKIT_MGMT_EPMAP_BITMAP GENMASK_ULL(31, 0) + +#define APPLE_RTKIT_MGMT_EPMAP_REPLY_MORE BIT_ULL(0) + +#define APPLE_RTKIT_MGMT_STARTEP_EP GENMASK_ULL(39, 32) +#define APPLE_RTKIT_MGMT_STARTEP_FLAG BIT_ULL(1) + +#define APPLE_RTKIT_MGMT_PWR_STATE GENMASK_ULL(15, 0) + +#define APPLE_RTKIT_CRASHLOG_CRASH 1 + +#define APPLE_RTKIT_BUFFER_REQUEST 1 +#define APPLE_RTKIT_BUFFER_REQUEST_SIZE GENMASK_ULL(51, 44) +#define APPLE_RTKIT_BUFFER_REQUEST_IOVA GENMASK_ULL(41, 0) + +#define APPLE_RTKIT_SYSLOG_TYPE GENMASK_ULL(59, 52) + +#define APPLE_RTKIT_SYSLOG_LOG 5 + +#define APPLE_RTKIT_SYSLOG_INIT 8 +#define APPLE_RTKIT_SYSLOG_N_ENTRIES GENMASK_ULL(7, 0) +#define APPLE_RTKIT_SYSLOG_MSG_SIZE GENMASK_ULL(31, 24) + +#define APPLE_RTKIT_OSLOG_TYPE GENMASK_ULL(63, 56) +#define APPLE_RTKIT_OSLOG_INIT 1 +#define APPLE_RTKIT_OSLOG_ACK 3 + +#define APPLE_RTKIT_MIN_SUPPORTED_VERSION 11 +#define APPLE_RTKIT_MAX_SUPPORTED_VERSION 12 + +struct apple_rtkit_msg { + struct completion *completion; + struct apple_mbox_msg mbox_msg; +}; + +struct apple_rtkit_rx_work { + struct apple_rtkit *rtk; + u8 ep; + u64 msg; + struct work_struct work; +}; + +bool apple_rtkit_is_running(struct apple_rtkit *rtk) +{ + if (rtk->crashed) + return false; + if ((rtk->iop_power_state & 0xff) != APPLE_RTKIT_PWR_STATE_ON) + return false; + if ((rtk->ap_power_state & 0xff) != APPLE_RTKIT_PWR_STATE_ON) + return false; + return true; +} +EXPORT_SYMBOL_GPL(apple_rtkit_is_running); + +bool apple_rtkit_is_crashed(struct apple_rtkit *rtk) +{ + return rtk->crashed; +} +EXPORT_SYMBOL_GPL(apple_rtkit_is_crashed); + +static void apple_rtkit_management_send(struct apple_rtkit *rtk, u8 type, + u64 msg) +{ + msg &= ~APPLE_RTKIT_MGMT_TYPE; + msg |= FIELD_PREP(APPLE_RTKIT_MGMT_TYPE, type); + apple_rtkit_send_message(rtk, APPLE_RTKIT_EP_MGMT, msg, NULL, false); +} + +static void apple_rtkit_management_rx_hello(struct apple_rtkit *rtk, u64 msg) +{ + u64 reply; + + int min_ver = FIELD_GET(APPLE_RTKIT_MGMT_HELLO_MINVER, msg); + int max_ver = FIELD_GET(APPLE_RTKIT_MGMT_HELLO_MAXVER, msg); + int want_ver = min(APPLE_RTKIT_MAX_SUPPORTED_VERSION, max_ver); + + dev_dbg(rtk->dev, "RTKit: Min ver %d, max ver %d\n", min_ver, max_ver); + + if (min_ver > APPLE_RTKIT_MAX_SUPPORTED_VERSION) { + dev_err(rtk->dev, "RTKit: Firmware min version %d is too new\n", + min_ver); + goto abort_boot; + } + + if (max_ver < APPLE_RTKIT_MIN_SUPPORTED_VERSION) { + dev_err(rtk->dev, "RTKit: Firmware max version %d is too old\n", + max_ver); + goto abort_boot; + } + + dev_info(rtk->dev, "RTKit: Initializing (protocol version %d)\n", + want_ver); + rtk->version = want_ver; + + reply = FIELD_PREP(APPLE_RTKIT_MGMT_HELLO_MINVER, want_ver); + reply |= FIELD_PREP(APPLE_RTKIT_MGMT_HELLO_MAXVER, want_ver); + apple_rtkit_management_send(rtk, APPLE_RTKIT_MGMT_HELLO_REPLY, reply); + + return; + +abort_boot: + rtk->boot_result = -EINVAL; + complete_all(&rtk->epmap_completion); +} + +static void apple_rtkit_management_rx_epmap(struct apple_rtkit *rtk, u64 msg) +{ + int i, ep; + u64 reply; + unsigned long bitmap = FIELD_GET(APPLE_RTKIT_MGMT_EPMAP_BITMAP, msg); + u32 base = FIELD_GET(APPLE_RTKIT_MGMT_EPMAP_BASE, msg); + + dev_dbg(rtk->dev, + "RTKit: received endpoint bitmap 0x%lx with base 0x%x\n", + bitmap, base); + + for_each_set_bit(i, &bitmap, 32) { + ep = 32 * base + i; + dev_dbg(rtk->dev, "RTKit: Discovered endpoint 0x%02x\n", ep); + set_bit(ep, rtk->endpoints); + } + + reply = FIELD_PREP(APPLE_RTKIT_MGMT_EPMAP_BASE, base); + if (msg & APPLE_RTKIT_MGMT_EPMAP_LAST) + reply |= APPLE_RTKIT_MGMT_EPMAP_LAST; + else + reply |= APPLE_RTKIT_MGMT_EPMAP_REPLY_MORE; + + apple_rtkit_management_send(rtk, APPLE_RTKIT_MGMT_EPMAP_REPLY, reply); + + if (!(msg & APPLE_RTKIT_MGMT_EPMAP_LAST)) + return; + + for_each_set_bit(ep, rtk->endpoints, APPLE_RTKIT_APP_ENDPOINT_START) { + switch (ep) { + /* the management endpoint is started by default */ + case APPLE_RTKIT_EP_MGMT: + break; + + /* without starting these RTKit refuses to boot */ + case APPLE_RTKIT_EP_SYSLOG: + case APPLE_RTKIT_EP_CRASHLOG: + case APPLE_RTKIT_EP_DEBUG: + case APPLE_RTKIT_EP_IOREPORT: + case APPLE_RTKIT_EP_OSLOG: + dev_dbg(rtk->dev, + "RTKit: Starting system endpoint 0x%02x\n", ep); + apple_rtkit_start_ep(rtk, ep); + break; + + default: + dev_warn(rtk->dev, + "RTKit: Unknown system endpoint: 0x%02x\n", + ep); + } + } + + rtk->boot_result = 0; + complete_all(&rtk->epmap_completion); +} + +static void apple_rtkit_management_rx_iop_pwr_ack(struct apple_rtkit *rtk, + u64 msg) +{ + unsigned int new_state = FIELD_GET(APPLE_RTKIT_MGMT_PWR_STATE, msg); + + dev_dbg(rtk->dev, "RTKit: IOP power state transition: 0x%x -> 0x%x\n", + rtk->iop_power_state, new_state); + rtk->iop_power_state = new_state; + + complete_all(&rtk->iop_pwr_ack_completion); +} + +static void apple_rtkit_management_rx_ap_pwr_ack(struct apple_rtkit *rtk, + u64 msg) +{ + unsigned int new_state = FIELD_GET(APPLE_RTKIT_MGMT_PWR_STATE, msg); + + dev_dbg(rtk->dev, "RTKit: AP power state transition: 0x%x -> 0x%x\n", + rtk->ap_power_state, new_state); + rtk->ap_power_state = new_state; + + complete_all(&rtk->ap_pwr_ack_completion); +} + +static void apple_rtkit_management_rx(struct apple_rtkit *rtk, u64 msg) +{ + u8 type = FIELD_GET(APPLE_RTKIT_MGMT_TYPE, msg); + + switch (type) { + case APPLE_RTKIT_MGMT_HELLO: + apple_rtkit_management_rx_hello(rtk, msg); + break; + case APPLE_RTKIT_MGMT_EPMAP: + apple_rtkit_management_rx_epmap(rtk, msg); + break; + case APPLE_RTKIT_MGMT_SET_IOP_PWR_STATE_ACK: + apple_rtkit_management_rx_iop_pwr_ack(rtk, msg); + break; + case APPLE_RTKIT_MGMT_SET_AP_PWR_STATE_ACK: + apple_rtkit_management_rx_ap_pwr_ack(rtk, msg); + break; + default: + dev_warn( + rtk->dev, + "RTKit: unknown management message: 0x%llx (type: 0x%02x)\n", + msg, type); + } +} + +static int apple_rtkit_common_rx_get_buffer(struct apple_rtkit *rtk, + struct apple_rtkit_shmem *buffer, + u8 ep, u64 msg) +{ + size_t n_4kpages = FIELD_GET(APPLE_RTKIT_BUFFER_REQUEST_SIZE, msg); + u64 reply; + int err; + + buffer->buffer = NULL; + buffer->iomem = NULL; + buffer->is_mapped = false; + buffer->iova = FIELD_GET(APPLE_RTKIT_BUFFER_REQUEST_IOVA, msg); + buffer->size = n_4kpages << 12; + + dev_dbg(rtk->dev, "RTKit: buffer request for 0x%zx bytes at %pad\n", + buffer->size, &buffer->iova); + + if (buffer->iova && + (!rtk->ops->shmem_setup || !rtk->ops->shmem_destroy)) { + err = -EINVAL; + goto error; + } + + if (rtk->ops->shmem_setup) { + err = rtk->ops->shmem_setup(rtk->cookie, buffer); + if (err) + goto error; + } else { + buffer->buffer = dma_alloc_coherent(rtk->dev, buffer->size, + &buffer->iova, GFP_KERNEL); + if (!buffer->buffer) { + err = -ENOMEM; + goto error; + } + } + + if (!buffer->is_mapped) { + reply = FIELD_PREP(APPLE_RTKIT_SYSLOG_TYPE, + APPLE_RTKIT_BUFFER_REQUEST); + reply |= FIELD_PREP(APPLE_RTKIT_BUFFER_REQUEST_SIZE, n_4kpages); + reply |= FIELD_PREP(APPLE_RTKIT_BUFFER_REQUEST_IOVA, + buffer->iova); + apple_rtkit_send_message(rtk, ep, reply, NULL, false); + } + + return 0; + +error: + buffer->buffer = NULL; + buffer->iomem = NULL; + buffer->iova = 0; + buffer->size = 0; + buffer->is_mapped = false; + return err; +} + +static void apple_rtkit_free_buffer(struct apple_rtkit *rtk, + struct apple_rtkit_shmem *bfr) +{ + if (bfr->size == 0) + return; + + if (rtk->ops->shmem_destroy) + rtk->ops->shmem_destroy(rtk->cookie, bfr); + else if (bfr->buffer) + dma_free_coherent(rtk->dev, bfr->size, bfr->buffer, bfr->iova); + + bfr->buffer = NULL; + bfr->iomem = NULL; + bfr->iova = 0; + bfr->size = 0; + bfr->is_mapped = false; +} + +static void apple_rtkit_memcpy(struct apple_rtkit *rtk, void *dst, + struct apple_rtkit_shmem *bfr, size_t offset, + size_t len) +{ + if (bfr->iomem) + memcpy_fromio(dst, bfr->iomem + offset, len); + else + memcpy(dst, bfr->buffer + offset, len); +} + +static void apple_rtkit_crashlog_rx(struct apple_rtkit *rtk, u64 msg) +{ + u8 type = FIELD_GET(APPLE_RTKIT_SYSLOG_TYPE, msg); + u8 *bfr; + + if (type != APPLE_RTKIT_CRASHLOG_CRASH) { + dev_warn(rtk->dev, "RTKit: Unknown crashlog message: %llx\n", + msg); + return; + } + + if (!rtk->crashlog_buffer.size) { + apple_rtkit_common_rx_get_buffer(rtk, &rtk->crashlog_buffer, + APPLE_RTKIT_EP_CRASHLOG, msg); + return; + } + + dev_err(rtk->dev, "RTKit: co-processor has crashed\n"); + + /* + * create a shadow copy here to make sure the co-processor isn't able + * to change the log while we're dumping it. this also ensures + * the buffer is in normal memory and not iomem for e.g. the SMC + */ + bfr = kzalloc(rtk->crashlog_buffer.size, GFP_KERNEL); + if (bfr) { + apple_rtkit_memcpy(rtk, bfr, &rtk->crashlog_buffer, 0, + rtk->crashlog_buffer.size); + apple_rtkit_crashlog_dump(rtk, bfr, rtk->crashlog_buffer.size); + kfree(bfr); + } else { + dev_err(rtk->dev, + "RTKit: Couldn't allocate crashlog shadow buffer\n"); + } + + rtk->crashed = true; + if (rtk->ops->crashed) + rtk->ops->crashed(rtk->cookie); +} + +static void apple_rtkit_ioreport_rx(struct apple_rtkit *rtk, u64 msg) +{ + u8 type = FIELD_GET(APPLE_RTKIT_SYSLOG_TYPE, msg); + + switch (type) { + case APPLE_RTKIT_BUFFER_REQUEST: + apple_rtkit_common_rx_get_buffer(rtk, &rtk->ioreport_buffer, + APPLE_RTKIT_EP_IOREPORT, msg); + break; + /* unknown, must be ACKed or the co-processor will hang */ + case 0x8: + case 0xc: + apple_rtkit_send_message(rtk, APPLE_RTKIT_EP_IOREPORT, msg, + NULL, false); + break; + default: + dev_warn(rtk->dev, "RTKit: Unknown ioreport message: %llx\n", + msg); + } +} + +static void apple_rtkit_syslog_rx_init(struct apple_rtkit *rtk, u64 msg) +{ + rtk->syslog_n_entries = FIELD_GET(APPLE_RTKIT_SYSLOG_N_ENTRIES, msg); + rtk->syslog_msg_size = FIELD_GET(APPLE_RTKIT_SYSLOG_MSG_SIZE, msg); + + rtk->syslog_msg_buffer = kzalloc(rtk->syslog_msg_size, GFP_KERNEL); + + dev_dbg(rtk->dev, + "RTKit: syslog initialized: entries: %zd, msg_size: %zd\n", + rtk->syslog_n_entries, rtk->syslog_msg_size); +} + +static void apple_rtkit_syslog_rx_log(struct apple_rtkit *rtk, u64 msg) +{ + u8 idx = msg & 0xff; + char log_context[24]; + size_t entry_size = 0x20 + rtk->syslog_msg_size; + + if (!rtk->syslog_msg_buffer) { + dev_warn( + rtk->dev, + "RTKit: received syslog message but no syslog_msg_buffer\n"); + goto done; + } + if (!rtk->syslog_buffer.size) { + dev_warn( + rtk->dev, + "RTKit: received syslog message but syslog_buffer.size is zero\n"); + goto done; + } + if (!rtk->syslog_buffer.buffer && !rtk->syslog_buffer.iomem) { + dev_warn( + rtk->dev, + "RTKit: received syslog message but no syslog_buffer.buffer or syslog_buffer.iomem\n"); + goto done; + } + if (idx > rtk->syslog_n_entries) { + dev_warn(rtk->dev, "RTKit: syslog index %d out of range\n", + idx); + goto done; + } + + apple_rtkit_memcpy(rtk, log_context, &rtk->syslog_buffer, + idx * entry_size + 8, sizeof(log_context)); + apple_rtkit_memcpy(rtk, rtk->syslog_msg_buffer, &rtk->syslog_buffer, + idx * entry_size + 8 + sizeof(log_context), + rtk->syslog_msg_size); + + log_context[sizeof(log_context) - 1] = 0; + rtk->syslog_msg_buffer[rtk->syslog_msg_size - 1] = 0; + dev_info(rtk->dev, "RTKit: syslog message: %s: %s\n", log_context, + rtk->syslog_msg_buffer); + +done: + apple_rtkit_send_message(rtk, APPLE_RTKIT_EP_SYSLOG, msg, NULL, false); +} + +static void apple_rtkit_syslog_rx(struct apple_rtkit *rtk, u64 msg) +{ + u8 type = FIELD_GET(APPLE_RTKIT_SYSLOG_TYPE, msg); + + switch (type) { + case APPLE_RTKIT_BUFFER_REQUEST: + apple_rtkit_common_rx_get_buffer(rtk, &rtk->syslog_buffer, + APPLE_RTKIT_EP_SYSLOG, msg); + break; + case APPLE_RTKIT_SYSLOG_INIT: + apple_rtkit_syslog_rx_init(rtk, msg); + break; + case APPLE_RTKIT_SYSLOG_LOG: + apple_rtkit_syslog_rx_log(rtk, msg); + break; + default: + dev_warn(rtk->dev, "RTKit: Unknown syslog message: %llx\n", + msg); + } +} + +static void apple_rtkit_oslog_rx_init(struct apple_rtkit *rtk, u64 msg) +{ + u64 ack; + + dev_dbg(rtk->dev, "RTKit: oslog init: msg: 0x%llx\n", msg); + ack = FIELD_PREP(APPLE_RTKIT_OSLOG_TYPE, APPLE_RTKIT_OSLOG_ACK); + apple_rtkit_send_message(rtk, APPLE_RTKIT_EP_OSLOG, ack, NULL, false); +} + +static void apple_rtkit_oslog_rx(struct apple_rtkit *rtk, u64 msg) +{ + u8 type = FIELD_GET(APPLE_RTKIT_OSLOG_TYPE, msg); + + switch (type) { + case APPLE_RTKIT_OSLOG_INIT: + apple_rtkit_oslog_rx_init(rtk, msg); + break; + default: + dev_warn(rtk->dev, "RTKit: Unknown oslog message: %llx\n", msg); + } +} + +static void apple_rtkit_rx_work(struct work_struct *work) +{ + struct apple_rtkit_rx_work *rtk_work = + container_of(work, struct apple_rtkit_rx_work, work); + struct apple_rtkit *rtk = rtk_work->rtk; + + switch (rtk_work->ep) { + case APPLE_RTKIT_EP_MGMT: + apple_rtkit_management_rx(rtk, rtk_work->msg); + break; + case APPLE_RTKIT_EP_CRASHLOG: + apple_rtkit_crashlog_rx(rtk, rtk_work->msg); + break; + case APPLE_RTKIT_EP_SYSLOG: + apple_rtkit_syslog_rx(rtk, rtk_work->msg); + break; + case APPLE_RTKIT_EP_IOREPORT: + apple_rtkit_ioreport_rx(rtk, rtk_work->msg); + break; + case APPLE_RTKIT_EP_OSLOG: + apple_rtkit_oslog_rx(rtk, rtk_work->msg); + break; + case APPLE_RTKIT_APP_ENDPOINT_START ... 0xff: + if (rtk->ops->recv_message) + rtk->ops->recv_message(rtk->cookie, rtk_work->ep, + rtk_work->msg); + else + dev_warn( + rtk->dev, + "Received unexpected message to EP%02d: %llx\n", + rtk_work->ep, rtk_work->msg); + break; + default: + dev_warn(rtk->dev, + "RTKit: message to unknown endpoint %02x: %llx\n", + rtk_work->ep, rtk_work->msg); + } + + kfree(rtk_work); +} + +static void apple_rtkit_rx(struct mbox_client *cl, void *mssg) +{ + struct apple_rtkit *rtk = container_of(cl, struct apple_rtkit, mbox_cl); + struct apple_mbox_msg *msg = mssg; + struct apple_rtkit_rx_work *work; + u8 ep = msg->msg1; + + /* + * The message was read from a MMIO FIFO and we have to make + * sure all reads from buffers sent with that message happen + * afterwards. + */ + dma_rmb(); + + if (!test_bit(ep, rtk->endpoints)) + dev_warn(rtk->dev, + "RTKit: Message to undiscovered endpoint 0x%02x\n", + ep); + + if (ep >= APPLE_RTKIT_APP_ENDPOINT_START && + rtk->ops->recv_message_early && + rtk->ops->recv_message_early(rtk->cookie, ep, msg->msg0)) + return; + + work = kzalloc(sizeof(*work), GFP_ATOMIC); + if (!work) + return; + + work->rtk = rtk; + work->ep = ep; + work->msg = msg->msg0; + INIT_WORK(&work->work, apple_rtkit_rx_work); + queue_work(rtk->wq, &work->work); +} + +static void apple_rtkit_tx_done(struct mbox_client *cl, void *mssg, int r) +{ + struct apple_rtkit_msg *msg = + container_of(mssg, struct apple_rtkit_msg, mbox_msg); + + if (r == -ETIME) + return; + + if (msg->completion) + complete(msg->completion); + kfree(msg); +} + +int apple_rtkit_send_message(struct apple_rtkit *rtk, u8 ep, u64 message, + struct completion *completion, bool atomic) +{ + struct apple_rtkit_msg *msg; + int ret; + gfp_t flags; + + if (rtk->crashed) + return -EINVAL; + if (ep >= APPLE_RTKIT_APP_ENDPOINT_START && + !apple_rtkit_is_running(rtk)) + return -EINVAL; + + if (atomic) + flags = GFP_ATOMIC; + else + flags = GFP_KERNEL; + + msg = kzalloc(sizeof(*msg), flags); + if (!msg) + return -ENOMEM; + + msg->mbox_msg.msg0 = message; + msg->mbox_msg.msg1 = ep; + msg->completion = completion; + + /* + * The message will be sent with a MMIO write. We need the barrier + * here to ensure any previous writes to buffers are visible to the + * device before that MMIO write happens. + */ + dma_wmb(); + + ret = mbox_send_message(rtk->mbox_chan, &msg->mbox_msg); + if (ret < 0) { + kfree(msg); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(apple_rtkit_send_message); + +int apple_rtkit_send_message_wait(struct apple_rtkit *rtk, u8 ep, u64 message, + unsigned long timeout, bool atomic) +{ + DECLARE_COMPLETION_ONSTACK(completion); + int ret; + long t; + + ret = apple_rtkit_send_message(rtk, ep, message, &completion, atomic); + if (ret < 0) + return ret; + + if (atomic) { + ret = mbox_flush(rtk->mbox_chan, timeout); + if (ret < 0) + return ret; + + if (try_wait_for_completion(&completion)) + return 0; + + return -ETIME; + } else { + t = wait_for_completion_interruptible_timeout( + &completion, msecs_to_jiffies(timeout)); + if (t < 0) + return t; + else if (t == 0) + return -ETIME; + return 0; + } +} +EXPORT_SYMBOL_GPL(apple_rtkit_send_message_wait); + +int apple_rtkit_start_ep(struct apple_rtkit *rtk, u8 endpoint) +{ + u64 msg; + + if (!test_bit(endpoint, rtk->endpoints)) + return -EINVAL; + if (endpoint >= APPLE_RTKIT_APP_ENDPOINT_START && + !apple_rtkit_is_running(rtk)) + return -EINVAL; + + msg = FIELD_PREP(APPLE_RTKIT_MGMT_STARTEP_EP, endpoint); + msg |= APPLE_RTKIT_MGMT_STARTEP_FLAG; + apple_rtkit_management_send(rtk, APPLE_RTKIT_MGMT_STARTEP, msg); + + return 0; +} +EXPORT_SYMBOL_GPL(apple_rtkit_start_ep); + +static int apple_rtkit_request_mbox_chan(struct apple_rtkit *rtk) +{ + if (rtk->mbox_name) + rtk->mbox_chan = mbox_request_channel_byname(&rtk->mbox_cl, + rtk->mbox_name); + else + rtk->mbox_chan = + mbox_request_channel(&rtk->mbox_cl, rtk->mbox_idx); + + if (IS_ERR(rtk->mbox_chan)) + return PTR_ERR(rtk->mbox_chan); + return 0; +} + +static struct apple_rtkit *apple_rtkit_init(struct device *dev, void *cookie, + const char *mbox_name, int mbox_idx, + const struct apple_rtkit_ops *ops) +{ + struct apple_rtkit *rtk; + int ret; + + if (!ops) + return ERR_PTR(-EINVAL); + + rtk = kzalloc(sizeof(*rtk), GFP_KERNEL); + if (!rtk) + return ERR_PTR(-ENOMEM); + + rtk->dev = dev; + rtk->cookie = cookie; + rtk->ops = ops; + + init_completion(&rtk->epmap_completion); + init_completion(&rtk->iop_pwr_ack_completion); + init_completion(&rtk->ap_pwr_ack_completion); + + bitmap_zero(rtk->endpoints, APPLE_RTKIT_MAX_ENDPOINTS); + set_bit(APPLE_RTKIT_EP_MGMT, rtk->endpoints); + + rtk->mbox_name = mbox_name; + rtk->mbox_idx = mbox_idx; + rtk->mbox_cl.dev = dev; + rtk->mbox_cl.tx_block = false; + rtk->mbox_cl.knows_txdone = false; + rtk->mbox_cl.rx_callback = &apple_rtkit_rx; + rtk->mbox_cl.tx_done = &apple_rtkit_tx_done; + + rtk->wq = alloc_ordered_workqueue("rtkit-%s", WQ_MEM_RECLAIM, + dev_name(rtk->dev)); + if (!rtk->wq) { + ret = -ENOMEM; + goto free_rtk; + } + + ret = apple_rtkit_request_mbox_chan(rtk); + if (ret) + goto destroy_wq; + + return rtk; + +destroy_wq: + destroy_workqueue(rtk->wq); +free_rtk: + kfree(rtk); + return ERR_PTR(ret); +} + +static int apple_rtkit_wait_for_completion(struct completion *c) +{ + long t; + + t = wait_for_completion_interruptible_timeout(c, + msecs_to_jiffies(1000)); + if (t < 0) + return t; + else if (t == 0) + return -ETIME; + else + return 0; +} + +int apple_rtkit_reinit(struct apple_rtkit *rtk) +{ + /* make sure we don't handle any messages while reinitializing */ + mbox_free_channel(rtk->mbox_chan); + flush_workqueue(rtk->wq); + + apple_rtkit_free_buffer(rtk, &rtk->ioreport_buffer); + apple_rtkit_free_buffer(rtk, &rtk->crashlog_buffer); + apple_rtkit_free_buffer(rtk, &rtk->syslog_buffer); + + kfree(rtk->syslog_msg_buffer); + + rtk->syslog_msg_buffer = NULL; + rtk->syslog_n_entries = 0; + rtk->syslog_msg_size = 0; + + bitmap_zero(rtk->endpoints, APPLE_RTKIT_MAX_ENDPOINTS); + set_bit(APPLE_RTKIT_EP_MGMT, rtk->endpoints); + + reinit_completion(&rtk->epmap_completion); + reinit_completion(&rtk->iop_pwr_ack_completion); + reinit_completion(&rtk->ap_pwr_ack_completion); + + rtk->crashed = false; + rtk->iop_power_state = APPLE_RTKIT_PWR_STATE_OFF; + rtk->ap_power_state = APPLE_RTKIT_PWR_STATE_OFF; + + return apple_rtkit_request_mbox_chan(rtk); +} +EXPORT_SYMBOL_GPL(apple_rtkit_reinit); + +static int apple_rtkit_set_ap_power_state(struct apple_rtkit *rtk, + unsigned int state) +{ + u64 msg; + int ret; + + reinit_completion(&rtk->ap_pwr_ack_completion); + + msg = FIELD_PREP(APPLE_RTKIT_MGMT_PWR_STATE, state); + apple_rtkit_management_send(rtk, APPLE_RTKIT_MGMT_SET_AP_PWR_STATE, + msg); + + ret = apple_rtkit_wait_for_completion(&rtk->ap_pwr_ack_completion); + if (ret) + return ret; + + if (rtk->ap_power_state != state) + return -EINVAL; + return 0; +} + +static int apple_rtkit_set_iop_power_state(struct apple_rtkit *rtk, + unsigned int state) +{ + u64 msg; + int ret; + + reinit_completion(&rtk->iop_pwr_ack_completion); + + msg = FIELD_PREP(APPLE_RTKIT_MGMT_PWR_STATE, state); + apple_rtkit_management_send(rtk, APPLE_RTKIT_MGMT_SET_IOP_PWR_STATE, + msg); + + ret = apple_rtkit_wait_for_completion(&rtk->iop_pwr_ack_completion); + if (ret) + return ret; + + if (rtk->iop_power_state != state) + return -EINVAL; + return 0; +} + +int apple_rtkit_boot(struct apple_rtkit *rtk) +{ + int ret; + + if (apple_rtkit_is_running(rtk)) + return 0; + if (rtk->crashed) + return -EINVAL; + + dev_dbg(rtk->dev, "RTKit: waiting for boot to finish\n"); + ret = apple_rtkit_wait_for_completion(&rtk->epmap_completion); + if (ret) + return ret; + if (rtk->boot_result) + return rtk->boot_result; + + dev_dbg(rtk->dev, "RTKit: waiting for IOP power state ACK\n"); + ret = apple_rtkit_wait_for_completion(&rtk->iop_pwr_ack_completion); + if (ret) + return ret; + + return apple_rtkit_set_ap_power_state(rtk, APPLE_RTKIT_PWR_STATE_ON); +} +EXPORT_SYMBOL_GPL(apple_rtkit_boot); + +int apple_rtkit_shutdown(struct apple_rtkit *rtk) +{ + int ret; + + /* if OFF is used here the co-processor will not wake up again */ + ret = apple_rtkit_set_ap_power_state(rtk, + APPLE_RTKIT_PWR_STATE_QUIESCED); + if (ret) + return ret; + + ret = apple_rtkit_set_iop_power_state(rtk, APPLE_RTKIT_PWR_STATE_SLEEP); + if (ret) + return ret; + + return apple_rtkit_reinit(rtk); +} +EXPORT_SYMBOL_GPL(apple_rtkit_shutdown); + +int apple_rtkit_quiesce(struct apple_rtkit *rtk) +{ + int ret; + + ret = apple_rtkit_set_ap_power_state(rtk, + APPLE_RTKIT_PWR_STATE_QUIESCED); + if (ret) + return ret; + + ret = apple_rtkit_set_iop_power_state(rtk, + APPLE_RTKIT_PWR_STATE_QUIESCED); + if (ret) + return ret; + + ret = apple_rtkit_reinit(rtk); + if (ret) + return ret; + + rtk->iop_power_state = APPLE_RTKIT_PWR_STATE_QUIESCED; + rtk->ap_power_state = APPLE_RTKIT_PWR_STATE_QUIESCED; + return 0; +} +EXPORT_SYMBOL_GPL(apple_rtkit_quiesce); + +int apple_rtkit_wake(struct apple_rtkit *rtk) +{ + u64 msg; + + if (apple_rtkit_is_running(rtk)) + return -EINVAL; + + reinit_completion(&rtk->iop_pwr_ack_completion); + + /* + * Use open-coded apple_rtkit_set_iop_power_state since apple_rtkit_boot + * will wait for the completion anyway. + */ + msg = FIELD_PREP(APPLE_RTKIT_MGMT_PWR_STATE, APPLE_RTKIT_PWR_STATE_ON); + apple_rtkit_management_send(rtk, APPLE_RTKIT_MGMT_SET_IOP_PWR_STATE, + msg); + + return apple_rtkit_boot(rtk); +} +EXPORT_SYMBOL_GPL(apple_rtkit_wake); + +static void apple_rtkit_free(struct apple_rtkit *rtk) +{ + mbox_free_channel(rtk->mbox_chan); + destroy_workqueue(rtk->wq); + + apple_rtkit_free_buffer(rtk, &rtk->ioreport_buffer); + apple_rtkit_free_buffer(rtk, &rtk->crashlog_buffer); + apple_rtkit_free_buffer(rtk, &rtk->syslog_buffer); + + kfree(rtk->syslog_msg_buffer); + kfree(rtk); +} + +struct apple_rtkit *devm_apple_rtkit_init(struct device *dev, void *cookie, + const char *mbox_name, int mbox_idx, + const struct apple_rtkit_ops *ops) +{ + struct apple_rtkit *rtk; + int ret; + + rtk = apple_rtkit_init(dev, cookie, mbox_name, mbox_idx, ops); + if (IS_ERR(rtk)) + return rtk; + + ret = devm_add_action_or_reset(dev, (void (*)(void *))apple_rtkit_free, + rtk); + if (ret) + return ERR_PTR(ret); + + return rtk; +} +EXPORT_SYMBOL_GPL(devm_apple_rtkit_init); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_AUTHOR("Sven Peter "); +MODULE_DESCRIPTION("Apple RTKit driver"); diff --git a/include/linux/soc/apple/rtkit.h b/include/linux/soc/apple/rtkit.h new file mode 100644 index 000000000000..88eb832eac7b --- /dev/null +++ b/include/linux/soc/apple/rtkit.h @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* + * Apple RTKit IPC Library + * Copyright (C) The Asahi Linux Contributors + * + * Apple's SoCs come with various co-processors running their RTKit operating + * system. This protocol library is used by client drivers to use the + * features provided by them. + */ +#ifndef _LINUX_APPLE_RTKIT_H_ +#define _LINUX_APPLE_RTKIT_H_ + +#include +#include +#include + +/* + * Struct to represent implementation-specific RTKit operations. + * + * @buffer: Shared memory buffer allocated inside normal RAM. + * @iomem: Shared memory buffer controlled by the co-processors. + * @size: Size of the shared memory buffer. + * @iova: Device VA of shared memory buffer. + * @is_mapped: Shared memory buffer is managed by the co-processor. + */ + +struct apple_rtkit_shmem { + void *buffer; + void __iomem *iomem; + size_t size; + dma_addr_t iova; + bool is_mapped; +}; + +/* + * Struct to represent implementation-specific RTKit operations. + * + * @crashed: Called when the co-processor has crashed. Runs in process + * context. + * @recv_message: Function called when a message from RTKit is received + * on a non-system endpoint. Called from a worker thread. + * @recv_message_early: + * Like recv_message, but called from atomic context. It + * should return true if it handled the message. If it + * returns false, the message will be passed on to the + * worker thread. + * @shmem_setup: Setup shared memory buffer. If bfr.is_iomem is true the + * buffer is managed by the co-processor and needs to be mapped. + * Otherwise the buffer is managed by Linux and needs to be + * allocated. If not specified dma_alloc_coherent is used. + * Called in process context. + * @shmem_destroy: Undo the shared memory buffer setup in shmem_setup. If not + * specified dma_free_coherent is used. Called in process + * context. + */ +struct apple_rtkit_ops { + void (*crashed)(void *cookie); + void (*recv_message)(void *cookie, u8 endpoint, u64 message); + bool (*recv_message_early)(void *cookie, u8 endpoint, u64 message); + int (*shmem_setup)(void *cookie, struct apple_rtkit_shmem *bfr); + void (*shmem_destroy)(void *cookie, struct apple_rtkit_shmem *bfr); +}; + +struct apple_rtkit; + +/* + * Initializes the internal state required to handle RTKit. This + * should usually be called within _probe. + * + * @dev: Pointer to the device node this coprocessor is assocated with + * @cookie: opaque cookie passed to all functions defined in rtkit_ops + * @mbox_name: mailbox name used to communicate with the co-processor + * @mbox_idx: mailbox index to be used if mbox_name is NULL + * @ops: pointer to rtkit_ops to be used for this co-processor + */ +struct apple_rtkit *devm_apple_rtkit_init(struct device *dev, void *cookie, + const char *mbox_name, int mbox_idx, + const struct apple_rtkit_ops *ops); + +/* + * Reinitialize internal structures. Must only be called with the co-processor + * is held in reset. + */ +int apple_rtkit_reinit(struct apple_rtkit *rtk); + +/* + * Handle RTKit's boot process. Should be called after the CPU of the + * co-processor has been started. + */ +int apple_rtkit_boot(struct apple_rtkit *rtk); + +/* + * Quiesce the co-processor. + */ +int apple_rtkit_quiesce(struct apple_rtkit *rtk); + +/* + * Wake the co-processor up from hibernation mode. + */ +int apple_rtkit_wake(struct apple_rtkit *rtk); + +/* + * Shutdown the co-processor + */ +int apple_rtkit_shutdown(struct apple_rtkit *rtk); + +/* + * Checks if RTKit is running and ready to handle messages. + */ +bool apple_rtkit_is_running(struct apple_rtkit *rtk); + +/* + * Checks if RTKit has crashed. + */ +bool apple_rtkit_is_crashed(struct apple_rtkit *rtk); + +/* + * Starts an endpoint. Must be called after boot but before any messages can be + * sent or received from that endpoint. + */ +int apple_rtkit_start_ep(struct apple_rtkit *rtk, u8 endpoint); + +/* + * Send a message to the given endpoint. + * + * @rtk: RTKit reference + * @ep: target endpoint + * @message: message to be sent + * @completeion: will be completed once the message has been submitted + * to the hardware FIFO. Can be NULL. + * @atomic: if set to true this function can be called from atomic + * context. + */ +int apple_rtkit_send_message(struct apple_rtkit *rtk, u8 ep, u64 message, + struct completion *completion, bool atomic); + +/* + * Send a message to the given endpoint and wait until it has been submitted + * to the hardware FIFO. + * Will return zero on success and a negative error code on failure + * (e.g. -ETIME when the message couldn't be written within the given + * timeout) + * + * @rtk: RTKit reference + * @ep: target endpoint + * @message: message to be sent + * @timeout: timeout in milliseconds to allow the message transmission + * to be completed + * @atomic: if set to true this function can be called from atomic + * context. + */ +int apple_rtkit_send_message_wait(struct apple_rtkit *rtk, u8 ep, u64 message, + unsigned long timeout, bool atomic); + +#endif /* _LINUX_APPLE_RTKIT_H_ */ -- cgit v1.2.3 From a12d521f4a3d2088f23b75f063de68ea1dc6b5a3 Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Sun, 1 May 2022 16:55:07 +0200 Subject: dt-bindings: iommu: Add Apple SART DMA address filter Apple SoCs such as the M1 come with a simple DMA address filter called SART. Unlike a real IOMMU no pagetables can be configured but instead DMA transactions can be allowed for up to 16 paddr regions. The consumer also needs special support since not all DMA allocations have to be added to this filter. Reviewed-by: Rob Herring Signed-off-by: Sven Peter --- .../devicetree/bindings/iommu/apple,sart.yaml | 52 ++++++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 53 insertions(+) create mode 100644 Documentation/devicetree/bindings/iommu/apple,sart.yaml diff --git a/Documentation/devicetree/bindings/iommu/apple,sart.yaml b/Documentation/devicetree/bindings/iommu/apple,sart.yaml new file mode 100644 index 000000000000..1524fa3094ef --- /dev/null +++ b/Documentation/devicetree/bindings/iommu/apple,sart.yaml @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iommu/apple,sart.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Apple SART DMA address filter + +maintainers: + - Sven Peter + +description: + Apple SART is a simple address filter for DMA transactions. Regions of + physical memory must be added to the SART's allow list before any + DMA can target these. Unlike a proper IOMMU no remapping can be done and + special support in the consumer driver is required since not all DMA + transactions of a single device are subject to SART filtering. + + SART1 has first been used since at least the A11 (iPhone 8 and iPhone X) + and allows 36 bit of physical address space and filter entries with sizes + up to 24 bit. + + SART2, first seen in A14 and M1, allows 36 bit of physical address space + and filter entry size up to 36 bit. + + SART3, first seen in M1 Pro/Max, extends both the address space and filter + entry size to 42 bit. + +properties: + compatible: + enum: + - apple,t6000-sart + - apple,t8103-sart + + reg: + maxItems: 1 + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + iommu@7bc50000 { + compatible = "apple,t8103-sart"; + reg = <0x7bc50000 0x4000>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index fd768d43e048..4961da640e82 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1836,6 +1836,7 @@ F: Documentation/devicetree/bindings/arm/apple/* F: Documentation/devicetree/bindings/clock/apple,nco.yaml F: Documentation/devicetree/bindings/i2c/apple,i2c.yaml F: Documentation/devicetree/bindings/interrupt-controller/apple,* +F: Documentation/devicetree/bindings/iommu/apple,sart.yaml F: Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml F: Documentation/devicetree/bindings/pci/apple,pcie.yaml F: Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml -- cgit v1.2.3 From b170143ae1113882731666aec9b9105356f1fc17 Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Sun, 1 May 2022 16:55:08 +0200 Subject: soc: apple: Add SART driver The NVMe co-processor on the Apple M1 uses a DMA address filter called SART for some DMA transactions. This adds a simple driver used to configure the memory regions from which DMA transactions are allowed. Unlike a real IOMMU, SART does not support any pagetables and can't be implemented inside the IOMMU subsystem using iommu_ops. It also can't be implemented using dma_map_ops since not all DMA transactions of the NVMe controller are filtered by SART. Instead, most buffers have to be registered using the integrated NVMe IOMMU and we can't have two separate dma_map_ops implementations for a single device. Co-developed-by: Hector Martin Reviewed-by: Arnd Bergmann Signed-off-by: Hector Martin Signed-off-by: Sven Peter --- MAINTAINERS | 1 + drivers/soc/apple/Kconfig | 11 ++ drivers/soc/apple/Makefile | 3 + drivers/soc/apple/sart.c | 328 +++++++++++++++++++++++++++++++++++++++++ include/linux/soc/apple/sart.h | 53 +++++++ 5 files changed, 396 insertions(+) create mode 100644 drivers/soc/apple/sart.c create mode 100644 include/linux/soc/apple/sart.h diff --git a/MAINTAINERS b/MAINTAINERS index 4961da640e82..8dad61c380c3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1854,6 +1854,7 @@ F: drivers/watchdog/apple_wdt.c F: include/dt-bindings/interrupt-controller/apple-aic.h F: include/dt-bindings/pinctrl/apple.h F: include/linux/apple-mailbox.h +F: include/linux/soc/apple/* ARM/ARTPEC MACHINE SUPPORT M: Jesper Nilsson diff --git a/drivers/soc/apple/Kconfig b/drivers/soc/apple/Kconfig index 1b09037584a0..a1596fefacff 100644 --- a/drivers/soc/apple/Kconfig +++ b/drivers/soc/apple/Kconfig @@ -30,6 +30,17 @@ config APPLE_RTKIT Say 'y' here if you have an Apple SoC. +config APPLE_SART + tristate "Apple SART DMA address filter" + depends on ARCH_APPLE || COMPILE_TEST + default ARCH_APPLE + help + Apple SART is a simple DMA address filter used on Apple SoCs such + as the M1. It is usually required for the NVMe coprocessor which does + not use a proper IOMMU. + + Say 'y' here if you have an Apple SoC. + endmenu endif diff --git a/drivers/soc/apple/Makefile b/drivers/soc/apple/Makefile index 8aca79fca0dd..e293770cf66d 100644 --- a/drivers/soc/apple/Makefile +++ b/drivers/soc/apple/Makefile @@ -3,3 +3,6 @@ obj-$(CONFIG_APPLE_PMGR_PWRSTATE) += apple-pmgr-pwrstate.o obj-$(CONFIG_APPLE_RTKIT) += apple-rtkit.o apple-rtkit-y = rtkit.o rtkit-crashlog.o + +obj-$(CONFIG_APPLE_SART) += apple-sart.o +apple-sart-y = sart.o diff --git a/drivers/soc/apple/sart.c b/drivers/soc/apple/sart.c new file mode 100644 index 000000000000..83804b16ad03 --- /dev/null +++ b/drivers/soc/apple/sart.c @@ -0,0 +1,328 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple SART device driver + * Copyright (C) The Asahi Linux Contributors + * + * Apple SART is a simple address filter for some DMA transactions. + * Regions of physical memory must be added to the SART's allow + * list before any DMA can target these. Unlike a proper + * IOMMU no remapping can be done and special support in the + * consumer driver is required since not all DMA transactions of + * a single device are subject to SART filtering. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define APPLE_SART_MAX_ENTRIES 16 + +/* This is probably a bitfield but the exact meaning of each bit is unknown. */ +#define APPLE_SART_FLAGS_ALLOW 0xff + +/* SARTv2 registers */ +#define APPLE_SART2_CONFIG(idx) (0x00 + 4 * (idx)) +#define APPLE_SART2_CONFIG_FLAGS GENMASK(31, 24) +#define APPLE_SART2_CONFIG_SIZE GENMASK(23, 0) +#define APPLE_SART2_CONFIG_SIZE_SHIFT 12 +#define APPLE_SART2_CONFIG_SIZE_MAX GENMASK(23, 0) + +#define APPLE_SART2_PADDR(idx) (0x40 + 4 * (idx)) +#define APPLE_SART2_PADDR_SHIFT 12 + +/* SARTv3 registers */ +#define APPLE_SART3_CONFIG(idx) (0x00 + 4 * (idx)) + +#define APPLE_SART3_PADDR(idx) (0x40 + 4 * (idx)) +#define APPLE_SART3_PADDR_SHIFT 12 + +#define APPLE_SART3_SIZE(idx) (0x80 + 4 * (idx)) +#define APPLE_SART3_SIZE_SHIFT 12 +#define APPLE_SART3_SIZE_MAX GENMASK(29, 0) + +struct apple_sart_ops { + void (*get_entry)(struct apple_sart *sart, int index, u8 *flags, + phys_addr_t *paddr, size_t *size); + void (*set_entry)(struct apple_sart *sart, int index, u8 flags, + phys_addr_t paddr_shifted, size_t size_shifted); + unsigned int size_shift; + unsigned int paddr_shift; + size_t size_max; +}; + +struct apple_sart { + struct device *dev; + void __iomem *regs; + + const struct apple_sart_ops *ops; + + unsigned long protected_entries; + unsigned long used_entries; +}; + +static void sart2_get_entry(struct apple_sart *sart, int index, u8 *flags, + phys_addr_t *paddr, size_t *size) +{ + u32 cfg = readl(sart->regs + APPLE_SART2_CONFIG(index)); + phys_addr_t paddr_ = readl(sart->regs + APPLE_SART2_PADDR(index)); + size_t size_ = FIELD_GET(APPLE_SART2_CONFIG_SIZE, cfg); + + *flags = FIELD_GET(APPLE_SART2_CONFIG_FLAGS, cfg); + *size = size_ << APPLE_SART2_CONFIG_SIZE_SHIFT; + *paddr = paddr_ << APPLE_SART2_PADDR_SHIFT; +} + +static void sart2_set_entry(struct apple_sart *sart, int index, u8 flags, + phys_addr_t paddr_shifted, size_t size_shifted) +{ + u32 cfg; + + cfg = FIELD_PREP(APPLE_SART2_CONFIG_FLAGS, flags); + cfg |= FIELD_PREP(APPLE_SART2_CONFIG_SIZE, size_shifted); + + writel(paddr_shifted, sart->regs + APPLE_SART2_PADDR(index)); + writel(cfg, sart->regs + APPLE_SART2_CONFIG(index)); +} + +static struct apple_sart_ops sart_ops_v2 = { + .get_entry = sart2_get_entry, + .set_entry = sart2_set_entry, + .size_shift = APPLE_SART2_CONFIG_SIZE_SHIFT, + .paddr_shift = APPLE_SART2_PADDR_SHIFT, + .size_max = APPLE_SART2_CONFIG_SIZE_MAX, +}; + +static void sart3_get_entry(struct apple_sart *sart, int index, u8 *flags, + phys_addr_t *paddr, size_t *size) +{ + phys_addr_t paddr_ = readl(sart->regs + APPLE_SART3_PADDR(index)); + size_t size_ = readl(sart->regs + APPLE_SART3_SIZE(index)); + + *flags = readl(sart->regs + APPLE_SART3_CONFIG(index)); + *size = size_ << APPLE_SART3_SIZE_SHIFT; + *paddr = paddr_ << APPLE_SART3_PADDR_SHIFT; +} + +static void sart3_set_entry(struct apple_sart *sart, int index, u8 flags, + phys_addr_t paddr_shifted, size_t size_shifted) +{ + writel(paddr_shifted, sart->regs + APPLE_SART3_PADDR(index)); + writel(size_shifted, sart->regs + APPLE_SART3_SIZE(index)); + writel(flags, sart->regs + APPLE_SART3_CONFIG(index)); +} + +static struct apple_sart_ops sart_ops_v3 = { + .get_entry = sart3_get_entry, + .set_entry = sart3_set_entry, + .size_shift = APPLE_SART3_SIZE_SHIFT, + .paddr_shift = APPLE_SART3_PADDR_SHIFT, + .size_max = APPLE_SART3_SIZE_MAX, +}; + +static int apple_sart_probe(struct platform_device *pdev) +{ + int i; + struct apple_sart *sart; + struct device *dev = &pdev->dev; + + sart = devm_kzalloc(dev, sizeof(*sart), GFP_KERNEL); + if (!sart) + return -ENOMEM; + + sart->dev = dev; + sart->ops = of_device_get_match_data(dev); + + sart->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(sart->regs)) + return PTR_ERR(sart->regs); + + for (i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) { + u8 flags; + size_t size; + phys_addr_t paddr; + + sart->ops->get_entry(sart, i, &flags, &paddr, &size); + + if (!flags) + continue; + + dev_dbg(sart->dev, + "SART bootloader entry: index %02d; flags: 0x%02x; paddr: %pa; size: 0x%zx\n", + i, flags, &paddr, size); + set_bit(i, &sart->protected_entries); + } + + platform_set_drvdata(pdev, sart); + return 0; +} + +struct apple_sart *devm_apple_sart_get(struct device *dev) +{ + struct device_node *sart_node; + struct platform_device *sart_pdev; + struct apple_sart *sart; + int ret; + + sart_node = of_parse_phandle(dev->of_node, "apple,sart", 0); + if (!sart_node) + return ERR_PTR(-ENODEV); + + sart_pdev = of_find_device_by_node(sart_node); + of_node_put(sart_node); + + if (!sart_pdev) + return ERR_PTR(-ENODEV); + + sart = dev_get_drvdata(&sart_pdev->dev); + if (!sart) { + put_device(&sart_pdev->dev); + return ERR_PTR(-EPROBE_DEFER); + } + + ret = devm_add_action_or_reset(dev, (void (*)(void *))put_device, + &sart_pdev->dev); + if (ret) + return ERR_PTR(ret); + + device_link_add(dev, &sart_pdev->dev, + DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_SUPPLIER); + + return sart; +} +EXPORT_SYMBOL_GPL(devm_apple_sart_get); + +static int sart_set_entry(struct apple_sart *sart, int index, u8 flags, + phys_addr_t paddr, size_t size) +{ + if (size & ((1 << sart->ops->size_shift) - 1)) + return -EINVAL; + if (paddr & ((1 << sart->ops->paddr_shift) - 1)) + return -EINVAL; + + paddr >>= sart->ops->size_shift; + size >>= sart->ops->paddr_shift; + + if (size > sart->ops->size_max) + return -EINVAL; + + sart->ops->set_entry(sart, index, flags, paddr, size); + return 0; +} + +int apple_sart_add_allowed_region(struct apple_sart *sart, phys_addr_t paddr, + size_t size) +{ + int i, ret; + + for (i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) { + if (test_bit(i, &sart->protected_entries)) + continue; + if (test_and_set_bit(i, &sart->used_entries)) + continue; + + ret = sart_set_entry(sart, i, APPLE_SART_FLAGS_ALLOW, paddr, + size); + if (ret) { + dev_dbg(sart->dev, + "unable to set entry %d to [%pa, 0x%zx]\n", + i, &paddr, size); + clear_bit(i, &sart->used_entries); + return ret; + } + + dev_dbg(sart->dev, "wrote [%pa, 0x%zx] to %d\n", &paddr, size, + i); + return 0; + } + + dev_warn(sart->dev, + "no free entries left to add [paddr: 0x%pa, size: 0x%zx]\n", + &paddr, size); + + return -EBUSY; +} +EXPORT_SYMBOL_GPL(apple_sart_add_allowed_region); + +int apple_sart_remove_allowed_region(struct apple_sart *sart, phys_addr_t paddr, + size_t size) +{ + int i; + + dev_dbg(sart->dev, + "will remove [paddr: %pa, size: 0x%zx] from allowed regions\n", + &paddr, size); + + for (i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) { + u8 eflags; + size_t esize; + phys_addr_t epaddr; + + if (test_bit(i, &sart->protected_entries)) + continue; + + sart->ops->get_entry(sart, i, &eflags, &epaddr, &esize); + + if (epaddr != paddr || esize != size) + continue; + + sart->ops->set_entry(sart, i, 0, 0, 0); + + clear_bit(i, &sart->used_entries); + dev_dbg(sart->dev, "cleared entry %d\n", i); + return 0; + } + + dev_warn(sart->dev, "entry [paddr: 0x%pa, size: 0x%zx] not found\n", + &paddr, size); + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(apple_sart_remove_allowed_region); + +static void apple_sart_shutdown(struct platform_device *pdev) +{ + struct apple_sart *sart = dev_get_drvdata(&pdev->dev); + int i; + + for (i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) { + if (test_bit(i, &sart->protected_entries)) + continue; + + sart->ops->set_entry(sart, i, 0, 0, 0); + } +} + +static const struct of_device_id apple_sart_of_match[] = { + { + .compatible = "apple,t6000-sart", + .data = &sart_ops_v3, + }, + { + .compatible = "apple,t8103-sart", + .data = &sart_ops_v2, + }, + {} +}; +MODULE_DEVICE_TABLE(of, apple_sart_of_match); + +static struct platform_driver apple_sart_driver = { + .driver = { + .name = "apple-sart", + .of_match_table = apple_sart_of_match, + }, + .probe = apple_sart_probe, + .shutdown = apple_sart_shutdown, +}; +module_platform_driver(apple_sart_driver); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_AUTHOR("Sven Peter "); +MODULE_DESCRIPTION("Apple SART driver"); diff --git a/include/linux/soc/apple/sart.h b/include/linux/soc/apple/sart.h new file mode 100644 index 000000000000..2249bf6cde09 --- /dev/null +++ b/include/linux/soc/apple/sart.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* + * Apple SART device driver + * Copyright (C) The Asahi Linux Contributors + * + * Apple SART is a simple address filter for DMA transactions. + * Regions of physical memory must be added to the SART's allow + * list before any DMA can target these. Unlike a proper + * IOMMU no remapping can be done. + */ + +#ifndef _LINUX_SOC_APPLE_SART_H_ +#define _LINUX_SOC_APPLE_SART_H_ + +#include +#include +#include + +struct apple_sart; + +/* + * Get a reference to the SART attached to dev. + * + * Looks for the phandle reference in apple,sart and returns a pointer + * to the corresponding apple_sart struct to be used with + * apple_sart_add_allowed_region and apple_sart_remove_allowed_region. + */ +struct apple_sart *devm_apple_sart_get(struct device *dev); + +/* + * Adds the region [paddr, paddr+size] to the DMA allow list. + * + * @sart: SART reference + * @paddr: Start address of the region to be used for DMA + * @size: Size of the region to be used for DMA. + */ +int apple_sart_add_allowed_region(struct apple_sart *sart, phys_addr_t paddr, + size_t size); + +/* + * Removes the region [paddr, paddr+size] from the DMA allow list. + * + * Note that exact same paddr and size used for apple_sart_add_allowed_region + * have to be passed. + * + * @sart: SART reference + * @paddr: Start address of the region no longer used for DMA + * @size: Size of the region no longer used for DMA. + */ +int apple_sart_remove_allowed_region(struct apple_sart *sart, phys_addr_t paddr, + size_t size); + +#endif /* _LINUX_SOC_APPLE_SART_H_ */ -- cgit v1.2.3 From 82b96552f15a05ee9f31813178e024720aa5b9bc Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Sun, 1 May 2022 16:55:09 +0200 Subject: dt-bindings: nvme: Add Apple ANS NVMe Apple SoCs such as the M1 come with an embedded NVMe coprocessor called ANS2. Reviewed-by: Rob Herring Signed-off-by: Sven Peter --- .../devicetree/bindings/nvme/apple,nvme-ans.yaml | 111 +++++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 112 insertions(+) create mode 100644 Documentation/devicetree/bindings/nvme/apple,nvme-ans.yaml diff --git a/Documentation/devicetree/bindings/nvme/apple,nvme-ans.yaml b/Documentation/devicetree/bindings/nvme/apple,nvme-ans.yaml new file mode 100644 index 000000000000..ddff9233b159 --- /dev/null +++ b/Documentation/devicetree/bindings/nvme/apple,nvme-ans.yaml @@ -0,0 +1,111 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/nvme/apple,nvme-ans.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Apple ANS NVM Express host controller + +maintainers: + - Sven Peter + +properties: + compatible: + items: + - enum: + - apple,t8103-nvme-ans2 + - apple,t6000-nvme-ans2 + - const: apple,nvme-ans2 + + reg: + items: + - description: NVMe and NVMMU registers + - description: ANS2 co-processor control registers + + reg-names: + items: + - const: nvme + - const: ans + + resets: + maxItems: 1 + + power-domains: + # two domains for t8103, three for t6000 + minItems: 2 + items: + - description: power domain for the NVMe controller. + - description: power domain for the first PCIe bus connecting the NVMe + controller to the storage modules. + - description: optional power domain for the second PCIe bus + connecting the NVMe controller to the storage modules. + + power-domain-names: + minItems: 2 + items: + - const: ans + - const: apcie0 + - const: apcie1 + + mboxes: + maxItems: 1 + description: Mailbox of the ANS2 co-processor + + interrupts: + maxItems: 1 + + apple,sart: + maxItems: 1 + $ref: /schemas/types.yaml#/definitions/phandle + description: | + Reference to the SART address filter. + + The SART address filter is documented in iommu/apple,sart.yaml. + +if: + properties: + compatible: + contains: + const: apple,t8103-nvme-ans2 +then: + properties: + power-domains: + maxItems: 2 + power-domain-names: + maxItems: 2 +else: + properties: + power-domains: + minItems: 3 + power-domain-names: + minItems: 3 + +required: + - compatible + - reg + - reg-names + - resets + - power-domains + - power-domain-names + - mboxes + - interrupts + - apple,sart + +additionalProperties: false + +examples: + - | + #include + #include + + nvme@7bcc0000 { + compatible = "apple,t8103-nvme-ans2", "apple,nvme-ans2"; + reg = <0x7bcc0000 0x40000>, <0x77400000 0x4000>; + reg-names = "nvme", "ans"; + interrupts = ; + mboxes = <&ans>; + apple,sart = <&sart>; + power-domains = <&ps_ans2>, <&ps_apcie_st>; + power-domain-names = "ans", "apcie0"; + resets = <&ps_ans2>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 8dad61c380c3..24b94c386f33 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1838,6 +1838,7 @@ F: Documentation/devicetree/bindings/i2c/apple,i2c.yaml F: Documentation/devicetree/bindings/interrupt-controller/apple,* F: Documentation/devicetree/bindings/iommu/apple,sart.yaml F: Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml +F: Documentation/devicetree/bindings/nvme/apple,nvme-ans.yaml F: Documentation/devicetree/bindings/pci/apple,pcie.yaml F: Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml F: Documentation/devicetree/bindings/power/apple* -- cgit v1.2.3 From 5bd2927aceba181b84286e00aa2f56e117e699c3 Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Sun, 1 May 2022 16:55:12 +0200 Subject: nvme-apple: Add initial Apple SoC NVMe driver Apple SoCs such as the M1 come with an embedded NVMe controller that is not attached to any PCIe bus. Additionally, it doesn't conform to the NVMe specification and requires a bunch of changes to command submission and IOMMU configuration to work. Reviewed-by: Christoph Hellwig Reviewed-by: Arnd Bergmann Signed-off-by: Sven Peter --- MAINTAINERS | 1 + drivers/nvme/host/Kconfig | 13 + drivers/nvme/host/Makefile | 3 + drivers/nvme/host/apple.c | 1593 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1610 insertions(+) create mode 100644 drivers/nvme/host/apple.c diff --git a/MAINTAINERS b/MAINTAINERS index 24b94c386f33..438beb56e9b8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1849,6 +1849,7 @@ F: drivers/i2c/busses/i2c-pasemi-core.c F: drivers/i2c/busses/i2c-pasemi-platform.c F: drivers/irqchip/irq-apple-aic.c F: drivers/mailbox/apple-mailbox.c +F: drivers/nvme/host/apple.c F: drivers/pinctrl/pinctrl-apple-gpio.c F: drivers/soc/apple/* F: drivers/watchdog/apple_wdt.c diff --git a/drivers/nvme/host/Kconfig b/drivers/nvme/host/Kconfig index d6d056963c06..877d2ec4ea9f 100644 --- a/drivers/nvme/host/Kconfig +++ b/drivers/nvme/host/Kconfig @@ -91,3 +91,16 @@ config NVME_TCP from https://github.com/linux-nvme/nvme-cli. If unsure, say N. + +config NVME_APPLE + tristate "Apple ANS2 NVM Express host driver" + depends on OF && BLOCK + depends on APPLE_RTKIT && APPLE_SART + depends on ARCH_APPLE || COMPILE_TEST + select NVME_CORE + help + This provides support for the NVMe controller embedded in Apple SoCs + such as the M1. + + To compile this driver as a module, choose M here: the + module will be called nvme-apple. diff --git a/drivers/nvme/host/Makefile b/drivers/nvme/host/Makefile index 476c5c988496..a36ae1612059 100644 --- a/drivers/nvme/host/Makefile +++ b/drivers/nvme/host/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_NVME_FABRICS) += nvme-fabrics.o obj-$(CONFIG_NVME_RDMA) += nvme-rdma.o obj-$(CONFIG_NVME_FC) += nvme-fc.o obj-$(CONFIG_NVME_TCP) += nvme-tcp.o +obj-$(CONFIG_NVME_APPLE) += nvme-apple.o nvme-core-y := core.o ioctl.o constants.o nvme-core-$(CONFIG_TRACING) += trace.o @@ -25,3 +26,5 @@ nvme-rdma-y += rdma.o nvme-fc-y += fc.o nvme-tcp-y += tcp.o + +nvme-apple-y += apple.o diff --git a/drivers/nvme/host/apple.c b/drivers/nvme/host/apple.c new file mode 100644 index 000000000000..b321f5c0563f --- /dev/null +++ b/drivers/nvme/host/apple.c @@ -0,0 +1,1593 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Apple ANS NVM Express device driver + * Copyright The Asahi Linux Contributors + * + * Based on the pci.c NVM Express device driver + * Copyright (c) 2011-2014, Intel Corporation. + * and on the rdma.c NVMe over Fabrics RDMA host code. + * Copyright (c) 2015-2016 HGST, a Western Digital Company. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nvme.h" + +#define APPLE_ANS_BOOT_TIMEOUT USEC_PER_SEC +#define APPLE_ANS_MAX_QUEUE_DEPTH 64 + +#define APPLE_ANS_COPROC_CPU_CONTROL 0x44 +#define APPLE_ANS_COPROC_CPU_CONTROL_RUN BIT(4) + +#define APPLE_ANS_ACQ_DB 0x1004 +#define APPLE_ANS_IOCQ_DB 0x100c + +#define APPLE_ANS_MAX_PEND_CMDS_CTRL 0x1210 + +#define APPLE_ANS_BOOT_STATUS 0x1300 +#define APPLE_ANS_BOOT_STATUS_OK 0xde71ce55 + +#define APPLE_ANS_UNKNOWN_CTRL 0x24008 +#define APPLE_ANS_PRP_NULL_CHECK BIT(11) + +#define APPLE_ANS_LINEAR_SQ_CTRL 0x24908 +#define APPLE_ANS_LINEAR_SQ_EN BIT(0) + +#define APPLE_ANS_LINEAR_ASQ_DB 0x2490c +#define APPLE_ANS_LINEAR_IOSQ_DB 0x24910 + +#define APPLE_NVMMU_NUM_TCBS 0x28100 +#define APPLE_NVMMU_ASQ_TCB_BASE 0x28108 +#define APPLE_NVMMU_IOSQ_TCB_BASE 0x28110 +#define APPLE_NVMMU_TCB_INVAL 0x28118 +#define APPLE_NVMMU_TCB_STAT 0x28120 + +/* + * This controller is a bit weird in the way command tags works: Both the + * admin and the IO queue share the same tag space. Additionally, tags + * cannot be higher than 0x40 which effectively limits the combined + * queue depth to 0x40. Instead of wasting half of that on the admin queue + * which gets much less traffic we instead reduce its size here. + * The controller also doesn't support async event such that no space must + * be reserved for NVME_NR_AEN_COMMANDS. + */ +#define APPLE_NVME_AQ_DEPTH 2 +#define APPLE_NVME_AQ_MQ_TAG_DEPTH (APPLE_NVME_AQ_DEPTH - 1) + +/* + * These can be higher, but we need to ensure that any command doesn't + * require an sg allocation that needs more than a page of data. + */ +#define NVME_MAX_KB_SZ 4096 +#define NVME_MAX_SEGS 127 + +/* + * This controller comes with an embedded IOMMU known as NVMMU. + * The NVMMU is pointed to an array of TCBs indexed by the command tag. + * Each command must be configured inside this structure before it's allowed + * to execute, including commands that don't require DMA transfers. + * + * An exception to this are Apple's vendor-specific commands (opcode 0xD8 on the + * admin queue): Those commands must still be added to the NVMMU but the DMA + * buffers cannot be represented as PRPs and must instead be allowed using SART. + * + * Programming the PRPs to the same values as those in the submission queue + * looks rather silly at first. This hardware is however designed for a kernel + * that runs the NVMMU code in a higher exception level than the NVMe driver. + * In that setting the NVMe driver first programs the submission queue entry + * and then executes a hypercall to the code that is allowed to program the + * NVMMU. The NVMMU driver then creates a shadow copy of the PRPs while + * verifying that they don't point to kernel text, data, pagetables, or similar + * protected areas before programming the TCB to point to this shadow copy. + * Since Linux doesn't do any of that we may as well just point both the queue + * and the TCB PRP pointer to the same memory. + */ +struct apple_nvmmu_tcb { + u8 opcode; + +#define APPLE_ANS_TCB_DMA_FROM_DEVICE BIT(0) +#define APPLE_ANS_TCB_DMA_TO_DEVICE BIT(1) + u8 dma_flags; + + u8 command_id; + u8 _unk0; + u32 length; + u8 _unk1[16]; + u64 prp1; + u64 prp2; + u8 _unk2[16]; + u8 aes_iv[8]; + u8 _aes_unk[64]; +}; + +/* + * The Apple NVMe controller only supports a single admin and a single IO queue + * which are both limited to 64 entries and share a single interrupt. + * + * The completion queue works as usual. The submission "queue" instead is + * an array indexed by the command tag on this hardware. Commands must also be + * present in the NVMMU's tcb array. They are triggered by writing their tag to + * a MMIO register. + */ +struct apple_nvme_queue { + struct nvme_command *sqes; + struct nvme_completion *cqes; + struct apple_nvmmu_tcb *tcbs; + + dma_addr_t sq_dma_addr; + dma_addr_t cq_dma_addr; + dma_addr_t tcb_dma_addr; + + u32 __iomem *sq_db; + u32 __iomem *cq_db; + + u16 cq_head; + u8 cq_phase; + + bool is_adminq; + bool enabled; +}; + +/* + * The apple_nvme_iod describes the data in an I/O. + * + * The sg pointer contains the list of PRP chunk allocations in addition + * to the actual struct scatterlist. + */ +struct apple_nvme_iod { + struct nvme_request req; + struct nvme_command cmd; + struct apple_nvme_queue *q; + int npages; /* In the PRP list. 0 means small pool in use */ + int nents; /* Used in scatterlist */ + dma_addr_t first_dma; + unsigned int dma_len; /* length of single DMA segment mapping */ + struct scatterlist *sg; +}; + +struct apple_nvme { + struct device *dev; + + void __iomem *mmio_coproc; + void __iomem *mmio_nvme; + + struct device **pd_dev; + struct device_link **pd_link; + int pd_count; + + struct apple_sart *sart; + struct apple_rtkit *rtk; + struct reset_control *reset; + + struct dma_pool *prp_page_pool; + struct dma_pool *prp_small_pool; + mempool_t *iod_mempool; + + struct nvme_ctrl ctrl; + struct work_struct remove_work; + + struct apple_nvme_queue adminq; + struct apple_nvme_queue ioq; + + struct blk_mq_tag_set admin_tagset; + struct blk_mq_tag_set tagset; + + int irq; + spinlock_t lock; +}; + +static_assert(sizeof(struct nvme_command) == 64); +static_assert(sizeof(struct apple_nvmmu_tcb) == 128); + +static inline struct apple_nvme *ctrl_to_apple_nvme(struct nvme_ctrl *ctrl) +{ + return container_of(ctrl, struct apple_nvme, ctrl); +} + +static inline struct apple_nvme *queue_to_apple_nvme(struct apple_nvme_queue *q) +{ + if (q->is_adminq) + return container_of(q, struct apple_nvme, adminq); + else + return container_of(q, struct apple_nvme, ioq); +} + +static unsigned int apple_nvme_queue_depth(struct apple_nvme_queue *q) +{ + if (q->is_adminq) + return APPLE_NVME_AQ_DEPTH; + else + return APPLE_ANS_MAX_QUEUE_DEPTH; +} + +static void apple_nvme_rtkit_crashed(void *cookie) +{ + struct apple_nvme *anv = cookie; + + dev_warn(anv->dev, "RTKit crashed; unable to recover without a reboot"); + nvme_reset_ctrl(&anv->ctrl); +} + +static int apple_nvme_sart_dma_setup(void *cookie, + struct apple_rtkit_shmem *bfr) +{ + struct apple_nvme *anv = cookie; + int ret; + + if (bfr->iova) + return -EINVAL; + if (!bfr->size) + return -EINVAL; + + bfr->buffer = + dma_alloc_coherent(anv->dev, bfr->size, &bfr->iova, GFP_KERNEL); + if (!bfr->buffer) + return -ENOMEM; + + ret = apple_sart_add_allowed_region(anv->sart, bfr->iova, bfr->size); + if (ret) { + dma_free_coherent(anv->dev, bfr->size, bfr->buffer, bfr->iova); + bfr->buffer = NULL; + return -ENOMEM; + } + + return 0; +} + +static void apple_nvme_sart_dma_destroy(void *cookie, + struct apple_rtkit_shmem *bfr) +{ + struct apple_nvme *anv = cookie; + + apple_sart_remove_allowed_region(anv->sart, bfr->iova, bfr->size); + dma_free_coherent(anv->dev, bfr->size, bfr->buffer, bfr->iova); +} + +static const struct apple_rtkit_ops apple_nvme_rtkit_ops = { + .crashed = apple_nvme_rtkit_crashed, + .shmem_setup = apple_nvme_sart_dma_setup, + .shmem_destroy = apple_nvme_sart_dma_destroy, +}; + +static void apple_nvmmu_inval(struct apple_nvme_queue *q, unsigned int tag) +{ + struct apple_nvme *anv = queue_to_apple_nvme(q); + + writel(tag, anv->mmio_nvme + APPLE_NVMMU_TCB_INVAL); + if (readl(anv->mmio_nvme + APPLE_NVMMU_TCB_STAT)) + dev_warn_ratelimited(anv->dev, + "NVMMU TCB invalidation failed\n"); +} + +static void apple_nvme_submit_cmd(struct apple_nvme_queue *q, + struct nvme_command *cmd) +{ + struct apple_nvme *anv = queue_to_apple_nvme(q); + u32 tag = nvme_tag_from_cid(cmd->common.command_id); + struct apple_nvmmu_tcb *tcb = &q->tcbs[tag]; + + tcb->opcode = cmd->common.opcode; + tcb->prp1 = cmd->common.dptr.prp1; + tcb->prp2 = cmd->common.dptr.prp2; + tcb->length = cmd->rw.length; + tcb->command_id = tag; + + if (nvme_is_write(cmd)) + tcb->dma_flags = APPLE_ANS_TCB_DMA_TO_DEVICE; + else + tcb->dma_flags = APPLE_ANS_TCB_DMA_FROM_DEVICE; + + memcpy(&q->sqes[tag], cmd, sizeof(*cmd)); + + /* + * This lock here doesn't make much sense at a first glace but + * removing it will result in occasional missed completetion + * interrupts even though the commands still appear on the CQ. + * It's unclear why this happens but our best guess is that + * there is a bug in the firmware triggered when a new command + * is issued while we're inside the irq handler between the + * NVMMU invalidation (and making the tag available again) + * and the final CQ update. + */ + spin_lock_irq(&anv->lock); + writel(tag, q->sq_db); + spin_unlock_irq(&anv->lock); +} + +/* + * From pci.c: + * Will slightly overestimate the number of pages needed. This is OK + * as it only leads to a small amount of wasted memory for the lifetime of + * the I/O. + */ +static inline size_t apple_nvme_iod_alloc_size(void) +{ + const unsigned int nprps = DIV_ROUND_UP( + NVME_MAX_KB_SZ + NVME_CTRL_PAGE_SIZE, NVME_CTRL_PAGE_SIZE); + const int npages = DIV_ROUND_UP(8 * nprps, PAGE_SIZE - 8); + const size_t alloc_size = sizeof(__le64 *) * npages + + sizeof(struct scatterlist) * NVME_MAX_SEGS; + + return alloc_size; +} + +static void **apple_nvme_iod_list(struct request *req) +{ + struct apple_nvme_iod *iod = blk_mq_rq_to_pdu(req); + + return (void **)(iod->sg + blk_rq_nr_phys_segments(req)); +} + +static void apple_nvme_free_prps(struct apple_nvme *anv, struct request *req) +{ + const int last_prp = NVME_CTRL_PAGE_SIZE / sizeof(__le64) - 1; + struct apple_nvme_iod *iod = blk_mq_rq_to_pdu(req); + dma_addr_t dma_addr = iod->first_dma; + int i; + + for (i = 0; i < iod->npages; i++) { + __le64 *prp_list = apple_nvme_iod_list(req)[i]; + dma_addr_t next_dma_addr = prp_list[last_prp]; + + dma_pool_free(anv->prp_page_pool, prp_list, dma_addr); + dma_addr = next_dma_addr; + } +} + +static void apple_nvme_unmap_data(struct apple_nvme *anv, struct request *req) +{ + struct apple_nvme_iod *iod = blk_mq_rq_to_pdu(req); + + if (iod->dma_len) { + dma_unmap_page(anv->dev, iod->first_dma, iod->dma_len, + rq_dma_dir(req)); + return; + } + + WARN_ON_ONCE(!iod->nents); + + dma_unmap_sg(anv->dev, iod->sg, iod->nents, rq_dma_dir(req)); + if (iod->npages == 0) + dma_pool_free(anv->prp_small_pool, apple_nvme_iod_list(req)[0], + iod->first_dma); + else + apple_nvme_free_prps(anv, req); + mempool_free(iod->sg, anv->iod_mempool); +} + +static void apple_nvme_print_sgl(struct scatterlist *sgl, int nents) +{ + int i; + struct scatterlist *sg; + + for_each_sg(sgl, sg, nents, i) { + dma_addr_t phys = sg_phys(sg); + + pr_warn("sg[%d] phys_addr:%pad offset:%d length:%d dma_address:%pad dma_length:%d\n", + i, &phys, sg->offset, sg->length, &sg_dma_address(sg), + sg_dma_len(sg)); + } +} + +static blk_status_t apple_nvme_setup_prps(struct apple_nvme *anv, + struct request *req, + struct nvme_rw_command *cmnd) +{ + struct apple_nvme_iod *iod = blk_mq_rq_to_pdu(req); + struct dma_pool *pool; + int length = blk_rq_payload_bytes(req); + struct scatterlist *sg = iod->sg; + int dma_len = sg_dma_len(sg); + u64 dma_addr = sg_dma_address(sg); + int offset = dma_addr & (NVME_CTRL_PAGE_SIZE - 1); + __le64 *prp_list; + void **list = apple_nvme_iod_list(req); + dma_addr_t prp_dma; + int nprps, i; + + length -= (NVME_CTRL_PAGE_SIZE - offset); + if (length <= 0) { + iod->first_dma = 0; + goto done; + } + + dma_len -= (NVME_CTRL_PAGE_SIZE - offset); + if (dma_len) { + dma_addr += (NVME_CTRL_PAGE_SIZE - offset); + } else { + sg = sg_next(sg); + dma_addr = sg_dma_address(sg); + dma_len = sg_dma_len(sg); + } + + if (length <= NVME_CTRL_PAGE_SIZE) { + iod->first_dma = dma_addr; + goto done; + } + + nprps = DIV_ROUND_UP(length, NVME_CTRL_PAGE_SIZE); + if (nprps <= (256 / 8)) { + pool = anv->prp_small_pool; + iod->npages = 0; + } else { + pool = anv->prp_page_pool; + iod->npages = 1; + } + + prp_list = dma_pool_alloc(pool, GFP_ATOMIC, &prp_dma); + if (!prp_list) { + iod->first_dma = dma_addr; + iod->npages = -1; + return BLK_STS_RESOURCE; + } + list[0] = prp_list; + iod->first_dma = prp_dma; + i = 0; + for (;;) { + if (i == NVME_CTRL_PAGE_SIZE >> 3) { + __le64 *old_prp_list = prp_list; + + prp_list = dma_pool_alloc(pool, GFP_ATOMIC, &prp_dma); + if (!prp_list) + goto free_prps; + list[iod->npages++] = prp_list; + prp_list[0] = old_prp_list[i - 1]; + old_prp_list[i - 1] = prp_dma; + i = 1; + } + prp_list[i++] = dma_addr; + dma_len -= NVME_CTRL_PAGE_SIZE; + dma_addr += NVME_CTRL_PAGE_SIZE; + length -= NVME_CTRL_PAGE_SIZE; + if (length <= 0) + break; + if (dma_len > 0) + continue; + if (unlikely(dma_len < 0)) + goto bad_sgl; + sg = sg_next(sg); + dma_addr = sg_dma_address(sg); + dma_len = sg_dma_len(sg); + } +done: + cmnd->dptr.prp1 = sg_dma_address(iod->sg); + cmnd->dptr.prp2 = iod->first_dma; + return BLK_STS_OK; +free_prps: + apple_nvme_free_prps(anv, req); + return BLK_STS_RESOURCE; +bad_sgl: + WARN(DO_ONCE(apple_nvme_print_sgl, iod->sg, iod->nents), + "Invalid SGL for payload:%d nents:%d\n", blk_rq_payload_bytes(req), + iod->nents); + return BLK_STS_IOERR; +} + +static blk_status_t apple_nvme_setup_prp_simple(struct apple_nvme *anv, + struct request *req, + struct nvme_rw_command *cmnd, + struct bio_vec *bv) +{ + struct apple_nvme_iod *iod = blk_mq_rq_to_pdu(req); + unsigned int offset = bv->bv_offset & (NVME_CTRL_PAGE_SIZE - 1); + unsigned int first_prp_len = NVME_CTRL_PAGE_SIZE - offset; + + iod->first_dma = dma_map_bvec(anv->dev, bv, rq_dma_dir(req), 0); + if (dma_mapping_error(anv->dev, iod->first_dma)) + return BLK_STS_RESOURCE; + iod->dma_len = bv->bv_len; + + cmnd->dptr.prp1 = iod->first_dma; + if (bv->bv_len > first_prp_len) + cmnd->dptr.prp2 = iod->first_dma + first_prp_len; + return BLK_STS_OK; +} + +static blk_status_t apple_nvme_map_data(struct apple_nvme *anv, + struct request *req, + struct nvme_command *cmnd) +{ + struct apple_nvme_iod *iod = blk_mq_rq_to_pdu(req); + blk_status_t ret = BLK_STS_RESOURCE; + int nr_mapped; + + if (blk_rq_nr_phys_segments(req) == 1) { + struct bio_vec bv = req_bvec(req); + + if (bv.bv_offset + bv.bv_len <= NVME_CTRL_PAGE_SIZE * 2) + return apple_nvme_setup_prp_simple(anv, req, &cmnd->rw, + &bv); + } + + iod->dma_len = 0; + iod->sg = mempool_alloc(anv->iod_mempool, GFP_ATOMIC); + if (!iod->sg) + return BLK_STS_RESOURCE; + sg_init_table(iod->sg, blk_rq_nr_phys_segments(req)); + iod->nents = blk_rq_map_sg(req->q, req, iod->sg); + if (!iod->nents) + goto out_free_sg; + + nr_mapped = dma_map_sg_attrs(anv->dev, iod->sg, iod->nents, + rq_dma_dir(req), DMA_ATTR_NO_WARN); + if (!nr_mapped) + goto out_free_sg; + + ret = apple_nvme_setup_prps(anv, req, &cmnd->rw); + if (ret != BLK_STS_OK) + goto out_unmap_sg; + return BLK_STS_OK; + +out_unmap_sg: + dma_unmap_sg(anv->dev, iod->sg, iod->nents, rq_dma_dir(req)); +out_free_sg: + mempool_free(iod->sg, anv->iod_mempool); + return ret; +} + +static __always_inline void apple_nvme_unmap_rq(struct request *req) +{ + struct apple_nvme_iod *iod = blk_mq_rq_to_pdu(req); + struct apple_nvme *anv = queue_to_apple_nvme(iod->q); + + if (blk_rq_nr_phys_segments(req)) + apple_nvme_unmap_data(anv, req); +} + +static void apple_nvme_complete_rq(struct request *req) +{ + apple_nvme_unmap_rq(req); + nvme_complete_rq(req); +} + +static void apple_nvme_complete_batch(struct io_comp_batch *iob) +{ + nvme_complete_batch(iob, apple_nvme_unmap_rq); +} + +static inline bool apple_nvme_cqe_pending(struct apple_nvme_queue *q) +{ + struct nvme_completion *hcqe = &q->cqes[q->cq_head]; + + return (READ_ONCE(hcqe->status) & 1) == q->cq_phase; +} + +static inline struct blk_mq_tags * +apple_nvme_queue_tagset(struct apple_nvme *anv, struct apple_nvme_queue *q) +{ + if (q->is_adminq) + return anv->admin_tagset.tags[0]; + else + return anv->tagset.tags[0]; +} + +static inline void apple_nvme_handle_cqe(struct apple_nvme_queue *q, + struct io_comp_batch *iob, u16 idx) +{ + struct apple_nvme *anv = queue_to_apple_nvme(q); + struct nvme_completion *cqe = &q->cqes[idx]; + __u16 command_id = READ_ONCE(cqe->command_id); + struct request *req; + + apple_nvmmu_inval(q, command_id); + + req = nvme_find_rq(apple_nvme_queue_tagset(anv, q), command_id); + if (unlikely(!req)) { + dev_warn(anv->dev, "invalid id %d completed", command_id); + return; + } + + if (!nvme_try_complete_req(req, cqe->status, cqe->result) && + !blk_mq_add_to_batch(req, iob, nvme_req(req)->status, + apple_nvme_complete_batch)) + apple_nvme_complete_rq(req); +} + +static inline void apple_nvme_update_cq_head(struct apple_nvme_queue *q) +{ + u32 tmp = q->cq_head + 1; + + if (tmp == apple_nvme_queue_depth(q)) { + q->cq_head = 0; + q->cq_phase ^= 1; + } else { + q->cq_head = tmp; + } +} + +static bool apple_nvme_poll_cq(struct apple_nvme_queue *q, + struct io_comp_batch *iob) +{ + bool found = false; + + while (apple_nvme_cqe_pending(q)) { + found = true; + + /* + * load-load control dependency between phase and the rest of + * the cqe requires a full read memory barrier + */ + dma_rmb(); + apple_nvme_handle_cqe(q, iob, q->cq_head); + apple_nvme_update_cq_head(q); + } + + if (found) + writel(q->cq_head, q->cq_db); + + return found; +} + +static bool apple_nvme_handle_cq(struct apple_nvme_queue *q, bool force) +{ + bool found; + DEFINE_IO_COMP_BATCH(iob); + + if (!READ_ONCE(q->enabled) && !force) + return false; + + found = apple_nvme_poll_cq(q, &iob); + + if (!rq_list_empty(iob.req_list)) + apple_nvme_complete_batch(&iob); + + return found; +} + +static irqreturn_t apple_nvme_irq(int irq, void *data) +{ + struct apple_nvme *anv = data; + bool handled = false; + unsigned long flags; + + spin_lock_irqsave(&anv->lock, flags); + if (apple_nvme_handle_cq(&anv->ioq, false)) + handled = true; + if (apple_nvme_handle_cq(&anv->adminq, false)) + handled = true; + spin_unlock_irqrestore(&anv->lock, flags); + + if (handled) + return IRQ_HANDLED; + return IRQ_NONE; +} + +static int apple_nvme_create_cq(struct apple_nvme *anv) +{ + struct nvme_command c = {}; + + /* + * Note: we (ab)use the fact that the prp fields survive if no data + * is attached to the request. + */ + c.create_cq.opcode = nvme_admin_create_cq; + c.create_cq.prp1 = anv->ioq.cq_dma_addr; + c.create_cq.cqid = 1; + c.create_cq.qsize = APPLE_ANS_MAX_QUEUE_DEPTH - 1; + c.create_cq.cq_flags = NVME_QUEUE_PHYS_CONTIG | NVME_CQ_IRQ_ENABLED; + c.create_cq.irq_vector = 0; + + return nvme_submit_sync_cmd(anv->ctrl.admin_q, &c, NULL, 0); +} + +static int apple_nvme_remove_cq(struct apple_nvme *anv) +{ + struct nvme_command c = {}; + + c.delete_queue.opcode = nvme_admin_delete_cq; + c.delete_queue.qid = 1; + + return nvme_submit_sync_cmd(anv->ctrl.admin_q, &c, NULL, 0); +} + +static int apple_nvme_create_sq(struct apple_nvme *anv) +{ + struct nvme_command c = {}; + + /* + * Note: we (ab)use the fact that the prp fields survive if no data + * is attached to the request. + */ + c.create_sq.opcode = nvme_admin_create_sq; + c.create_sq.prp1 = anv->ioq.sq_dma_addr; + c.create_sq.sqid = 1; + c.create_sq.qsize = APPLE_ANS_MAX_QUEUE_DEPTH - 1; + c.create_sq.sq_flags = NVME_QUEUE_PHYS_CONTIG; + c.create_sq.cqid = 1; + + return nvme_submit_sync_cmd(anv->ctrl.admin_q, &c, NULL, 0); +} + +static int apple_nvme_remove_sq(struct apple_nvme *anv) +{ + struct nvme_command c = {}; + + c.delete_queue.opcode = nvme_admin_delete_sq; + c.delete_queue.qid = 1; + + return nvme_submit_sync_cmd(anv->ctrl.admin_q, &c, NULL, 0); +} + +static blk_status_t apple_nvme_queue_rq(struct blk_mq_hw_ctx *hctx, + const struct blk_mq_queue_data *bd) +{ + struct nvme_ns *ns = hctx->queue->queuedata; + struct apple_nvme_queue *q = hctx->driver_data; + struct apple_nvme *anv = queue_to_apple_nvme(q); + struct request *req = bd->rq; + struct apple_nvme_iod *iod = blk_mq_rq_to_pdu(req); + struct nvme_command *cmnd = &iod->cmd; + blk_status_t ret; + + iod->npages = -1; + iod->nents = 0; + + /* + * We should not need to do this, but we're still using this to + * ensure we can drain requests on a dying queue. + */ + if (unlikely(!READ_ONCE(q->enabled))) + return BLK_STS_IOERR; + + if (!nvme_check_ready(&anv->ctrl, req, true)) + return nvme_fail_nonready_command(&anv->ctrl, req); + + ret = nvme_setup_cmd(ns, req); + if (ret) + return ret; + + if (blk_rq_nr_phys_segments(req)) { + ret = apple_nvme_map_data(anv, req, cmnd); + if (ret) + goto out_free_cmd; + } + + blk_mq_start_request(req); + apple_nvme_submit_cmd(q, cmnd); + return BLK_STS_OK; + +out_free_cmd: + nvme_cleanup_cmd(req); + return ret; +} + +static int apple_nvme_init_hctx(struct blk_mq_hw_ctx *hctx, void *data, + unsigned int hctx_idx) +{ + hctx->driver_data = data; + return 0; +} + +static int apple_nvme_init_request(struct blk_mq_tag_set *set, + struct request *req, unsigned int hctx_idx, + unsigned int numa_node) +{ + struct apple_nvme_queue *q = set->driver_data; + struct apple_nvme *anv = queue_to_apple_nvme(q); + struct apple_nvme_iod *iod = blk_mq_rq_to_pdu(req); + struct nvme_request *nreq = nvme_req(req); + + iod->q = q; + nreq->ctrl = &anv->ctrl; + nreq->cmd = &iod->cmd; + + return 0; +} + +static void apple_nvme_disable(struct apple_nvme *anv, bool shutdown) +{ + u32 csts = readl(anv->mmio_nvme + NVME_REG_CSTS); + bool dead = false, freeze = false; + unsigned long flags; + + if (apple_rtkit_is_crashed(anv->rtk)) + dead = true; + if (!(csts & NVME_CSTS_RDY)) + dead = true; + if (csts & NVME_CSTS_CFS) + dead = true; + + if (anv->ctrl.state == NVME_CTRL_LIVE || + anv->ctrl.state == NVME_CTRL_RESETTING) { + freeze = true; + nvme_start_freeze(&anv->ctrl); + } + + /* + * Give the controller a chance to complete all entered requests if + * doing a safe shutdown. + */ + if (!dead && shutdown && freeze) + nvme_wait_freeze_timeout(&anv->ctrl, NVME_IO_TIMEOUT); + + nvme_stop_queues(&anv->ctrl); + + if (!dead) { + if (READ_ONCE(anv->ioq.enabled)) { + apple_nvme_remove_sq(anv); + apple_nvme_remove_cq(anv); + } + + if (shutdown) + nvme_shutdown_ctrl(&anv->ctrl); + nvme_disable_ctrl(&anv->ctrl); + } + + WRITE_ONCE(anv->ioq.enabled, false); + WRITE_ONCE(anv->adminq.enabled, false); + mb(); /* ensure that nvme_queue_rq() sees that enabled is cleared */ + nvme_stop_admin_queue(&anv->ctrl); + + /* last chance to complete any requests before nvme_cancel_request */ + spin_lock_irqsave(&anv->lock, flags); + apple_nvme_handle_cq(&anv->ioq, true); + apple_nvme_handle_cq(&anv->adminq, true); + spin_unlock_irqrestore(&anv->lock, flags); + + blk_mq_tagset_busy_iter(&anv->tagset, nvme_cancel_request, &anv->ctrl); + blk_mq_tagset_busy_iter(&anv->admin_tagset, nvme_cancel_request, + &anv->ctrl); + blk_mq_tagset_wait_completed_request(&anv->tagset); + blk_mq_tagset_wait_completed_request(&anv->admin_tagset); + + /* + * The driver will not be starting up queues again if shutting down so + * must flush all entered requests to their failed completion to avoid + * deadlocking blk-mq hot-cpu notifier. + */ + if (shutdown) { + nvme_start_queues(&anv->ctrl); + nvme_start_admin_queue(&anv->ctrl); + } +} + +static enum blk_eh_timer_return apple_nvme_timeout(struct request *req, + bool reserved) +{ + struct apple_nvme_iod *iod = blk_mq_rq_to_pdu(req); + struct apple_nvme_queue *q = iod->q; + struct apple_nvme *anv = queue_to_apple_nvme(q); + unsigned long flags; + u32 csts = readl(anv->mmio_nvme + NVME_REG_CSTS); + + if (anv->ctrl.state != NVME_CTRL_LIVE) { + /* + * From rdma.c: + * If we are resetting, connecting or deleting we should + * complete immediately because we may block controller + * teardown or setup sequence + * - ctrl disable/shutdown fabrics requests + * - connect requests + * - initialization admin requests + * - I/O requests that entered after unquiescing and + * the controller stopped responding + * + * All other requests should be cancelled by the error + * recovery work, so it's fine that we fail it here. + */ + dev_warn(anv->dev, + "I/O %d(aq:%d) timeout while not in live state\n", + req->tag, q->is_adminq); + if (blk_mq_request_started(req) && + !blk_mq_request_completed(req)) { + nvme_req(req)->status = NVME_SC_HOST_ABORTED_CMD; + nvme_req(req)->flags |= NVME_REQ_CANCELLED; + blk_mq_complete_request(req); + } + return BLK_EH_DONE; + } + + /* check if we just missed an interrupt if we're still alive */ + if (!apple_rtkit_is_crashed(anv->rtk) && !(csts & NVME_CSTS_CFS)) { + spin_lock_irqsave(&anv->lock, flags); + apple_nvme_handle_cq(q, false); + spin_unlock_irqrestore(&anv->lock, flags); + if (blk_mq_request_completed(req)) { + dev_warn(anv->dev, + "I/O %d(aq:%d) timeout: completion polled\n", + req->tag, q->is_adminq); + return BLK_EH_DONE; + } + } + + /* + * aborting commands isn't supported which leaves a full reset as our + * only option here + */ + dev_warn(anv->dev, "I/O %d(aq:%d) timeout: resetting controller\n", + req->tag, q->is_adminq); + nvme_req(req)->flags |= NVME_REQ_CANCELLED; + apple_nvme_disable(anv, false); + nvme_reset_ctrl(&anv->ctrl); + return BLK_EH_DONE; +} + +static int apple_nvme_poll(struct blk_mq_hw_ctx *hctx, + struct io_comp_batch *iob) +{ + struct apple_nvme_queue *q = hctx->driver_data; + struct apple_nvme *anv = queue_to_apple_nvme(q); + bool found; + unsigned long flags; + + spin_lock_irqsave(&anv->lock, flags); + found = apple_nvme_poll_cq(q, iob); + spin_unlock_irqrestore(&anv->lock, flags); + + return found; +} + +static const struct blk_mq_ops apple_nvme_mq_admin_ops = { + .queue_rq = apple_nvme_queue_rq, + .complete = apple_nvme_complete_rq, + .init_hctx = apple_nvme_init_hctx, + .init_request = apple_nvme_init_request, + .timeout = apple_nvme_timeout, +}; + +static const struct blk_mq_ops apple_nvme_mq_ops = { + .queue_rq = apple_nvme_queue_rq, + .complete = apple_nvme_complete_rq, + .init_hctx = apple_nvme_init_hctx, + .init_request = apple_nvme_init_request, + .timeout = apple_nvme_timeout, + .poll = apple_nvme_poll, +}; + +static void apple_nvme_init_queue(struct apple_nvme_queue *q) +{ + unsigned int depth = apple_nvme_queue_depth(q); + + q->cq_head = 0; + q->cq_phase = 1; + memset(q->tcbs, 0, + APPLE_ANS_MAX_QUEUE_DEPTH * sizeof(struct apple_nvmmu_tcb)); + memset(q->cqes, 0, depth * sizeof(struct nvme_completion)); + WRITE_ONCE(q->enabled, true); + wmb(); /* ensure the first interrupt sees the initialization */ +} + +static void apple_nvme_reset_work(struct work_struct *work) +{ + unsigned int nr_io_queues = 1; + int ret; + u32 boot_status, aqa; + struct apple_nvme *anv = + container_of(work, struct apple_nvme, ctrl.reset_work); + + if (anv->ctrl.state != NVME_CTRL_RESETTING) { + dev_warn(anv->dev, "ctrl state %d is not RESETTING\n", + anv->ctrl.state); + ret = -ENODEV; + goto out; + } + + /* there's unfortunately no known way to recover if RTKit crashed :( */ + if (apple_rtkit_is_crashed(anv->rtk)) { + dev_err(anv->dev, + "RTKit has crashed without any way to recover."); + ret = -EIO; + goto out; + } + + if (anv->ctrl.ctrl_config & NVME_CC_ENABLE) + apple_nvme_disable(anv, false); + + /* RTKit must be shut down cleanly for the (soft)-reset to work */ + if (apple_rtkit_is_running(anv->rtk)) { + dev_dbg(anv->dev, "Trying to shut down RTKit before reset."); + ret = apple_rtkit_shutdown(anv->rtk); + if (ret) + goto out; + } + + writel(0, anv->mmio_coproc + APPLE_ANS_COPROC_CPU_CONTROL); + + ret = reset_control_assert(anv->reset); + if (ret) + goto out; + + ret = apple_rtkit_reinit(anv->rtk); + if (ret) + goto out; + + ret = reset_control_deassert(anv->reset); + if (ret) + goto out; + + writel(APPLE_ANS_COPROC_CPU_CONTROL_RUN, + anv->mmio_coproc + APPLE_ANS_COPROC_CPU_CONTROL); + ret = apple_rtkit_boot(anv->rtk); + if (ret) { + dev_err(anv->dev, "ANS did not boot"); + goto out; + } + + ret = readl_poll_timeout(anv->mmio_nvme + APPLE_ANS_BOOT_STATUS, + boot_status, + boot_status == APPLE_ANS_BOOT_STATUS_OK, + USEC_PER_MSEC, APPLE_ANS_BOOT_TIMEOUT); + if (ret) { + dev_err(anv->dev, "ANS did not initialize"); + goto out; + } + + dev_dbg(anv->dev, "ANS booted successfully."); + + /* + * Limit the max command size to prevent iod->sg allocations going + * over a single page. + */ + anv->ctrl.max_hw_sectors = min_t(u32, NVME_MAX_KB_SZ << 1, + dma_max_mapping_size(anv->dev) >> 9); + anv->ctrl.max_segments = NVME_MAX_SEGS; + + /* + * Enable NVMMU and linear submission queues. + * While we could keep those disabled and pretend this is slightly + * more common NVMe controller we'd still need some quirks (e.g. + * sq entries will be 128 bytes) and Apple might drop support for + * that mode in the future. + */ + writel(APPLE_ANS_LINEAR_SQ_EN, + anv->mmio_nvme + APPLE_ANS_LINEAR_SQ_CTRL); + + /* Allow as many pending command as possible for both queues */ + writel(APPLE_ANS_MAX_QUEUE_DEPTH | (APPLE_ANS_MAX_QUEUE_DEPTH << 16), + anv->mmio_nvme + APPLE_ANS_MAX_PEND_CMDS_CTRL); + + /* Setup the NVMMU for the maximum admin and IO queue depth */ + writel(APPLE_ANS_MAX_QUEUE_DEPTH - 1, + anv->mmio_nvme + APPLE_NVMMU_NUM_TCBS); + + /* + * This is probably a chicken bit: without it all commands where any PRP + * is set to zero (including those that don't use that field) fail and + * the co-processor complains about "completed with err BAD_CMD-" or + * a "NULL_PRP_PTR_ERR" in the syslog + */ + writel(readl(anv->mmio_nvme + APPLE_ANS_UNKNOWN_CTRL) & + ~APPLE_ANS_PRP_NULL_CHECK, + anv->mmio_nvme + APPLE_ANS_UNKNOWN_CTRL); + + /* Setup the admin queue */ + aqa = APPLE_NVME_AQ_DEPTH - 1; + aqa |= aqa << 16; + writel(aqa, anv->mmio_nvme + NVME_REG_AQA); + writeq(anv->adminq.sq_dma_addr, anv->mmio_nvme + NVME_REG_ASQ); + writeq(anv->adminq.cq_dma_addr, anv->mmio_nvme + NVME_REG_ACQ); + + /* Setup NVMMU for both queues */ + writeq(anv->adminq.tcb_dma_addr, + anv->mmio_nvme + APPLE_NVMMU_ASQ_TCB_BASE); + writeq(anv->ioq.tcb_dma_addr, + anv->mmio_nvme + APPLE_NVMMU_IOSQ_TCB_BASE); + + anv->ctrl.sqsize = + APPLE_ANS_MAX_QUEUE_DEPTH - 1; /* 0's based queue depth */ + anv->ctrl.cap = readq(anv->mmio_nvme + NVME_REG_CAP); + + dev_dbg(anv->dev, "Enabling controller now"); + ret = nvme_enable_ctrl(&anv->ctrl); + if (ret) + goto out; + + dev_dbg(anv->dev, "Starting admin queue"); + apple_nvme_init_queue(&anv->adminq); + nvme_start_admin_queue(&anv->ctrl); + + if (!nvme_change_ctrl_state(&anv->ctrl, NVME_CTRL_CONNECTING)) { + dev_warn(anv->ctrl.device, + "failed to mark controller CONNECTING\n"); + ret = -ENODEV; + goto out; + } + + ret = nvme_init_ctrl_finish(&anv->ctrl); + if (ret) + goto out; + + dev_dbg(anv->dev, "Creating IOCQ"); + ret = apple_nvme_create_cq(anv); + if (ret) + goto out; + dev_dbg(anv->dev, "Creating IOSQ"); + ret = apple_nvme_create_sq(anv); + if (ret) + goto out_remove_cq; + + apple_nvme_init_queue(&anv->ioq); + nr_io_queues = 1; + ret = nvme_set_queue_count(&anv->ctrl, &nr_io_queues); + if (ret) + goto out_remove_sq; + if (nr_io_queues != 1) { + ret = -ENXIO; + goto out_remove_sq; + } + + anv->ctrl.queue_count = nr_io_queues + 1; + + nvme_start_queues(&anv->ctrl); + nvme_wait_freeze(&anv->ctrl); + blk_mq_update_nr_hw_queues(&anv->tagset, 1); + nvme_unfreeze(&anv->ctrl); + + if (!nvme_change_ctrl_state(&anv->ctrl, NVME_CTRL_LIVE)) { + dev_warn(anv->ctrl.device, + "failed to mark controller live state\n"); + ret = -ENODEV; + goto out_remove_sq; + } + + nvme_start_ctrl(&anv->ctrl); + + dev_dbg(anv->dev, "ANS boot and NVMe init completed."); + return; + +out_remove_sq: + apple_nvme_remove_sq(anv); +out_remove_cq: + apple_nvme_remove_cq(anv); +out: + dev_warn(anv->ctrl.device, "Reset failure status: %d\n", ret); + nvme_change_ctrl_state(&anv->ctrl, NVME_CTRL_DELETING); + nvme_get_ctrl(&anv->ctrl); + apple_nvme_disable(anv, false); + nvme_kill_queues(&anv->ctrl); + if (!queue_work(nvme_wq, &anv->remove_work)) + nvme_put_ctrl(&anv->ctrl); +} + +static void apple_nvme_remove_dead_ctrl_work(struct work_struct *work) +{ + struct apple_nvme *anv = + container_of(work, struct apple_nvme, remove_work); + + nvme_put_ctrl(&anv->ctrl); + device_release_driver(anv->dev); +} + +static int apple_nvme_reg_read32(struct nvme_ctrl *ctrl, u32 off, u32 *val) +{ + *val = readl(ctrl_to_apple_nvme(ctrl)->mmio_nvme + off); + return 0; +} + +static int apple_nvme_reg_write32(struct nvme_ctrl *ctrl, u32 off, u32 val) +{ + writel(val, ctrl_to_apple_nvme(ctrl)->mmio_nvme + off); + return 0; +} + +static int apple_nvme_reg_read64(struct nvme_ctrl *ctrl, u32 off, u64 *val) +{ + *val = readq(ctrl_to_apple_nvme(ctrl)->mmio_nvme + off); + return 0; +} + +static int apple_nvme_get_address(struct nvme_ctrl *ctrl, char *buf, int size) +{ + struct device *dev = ctrl_to_apple_nvme(ctrl)->dev; + + return snprintf(buf, size, "%s\n", dev_name(dev)); +} + +static void apple_nvme_free_ctrl(struct nvme_ctrl *ctrl) +{ + struct apple_nvme *anv = ctrl_to_apple_nvme(ctrl); + + if (anv->ctrl.admin_q) + blk_put_queue(anv->ctrl.admin_q); + put_device(anv->dev); +} + +static const struct nvme_ctrl_ops nvme_ctrl_ops = { + .name = "apple-nvme", + .module = THIS_MODULE, + .flags = 0, + .reg_read32 = apple_nvme_reg_read32, + .reg_write32 = apple_nvme_reg_write32, + .reg_read64 = apple_nvme_reg_read64, + .free_ctrl = apple_nvme_free_ctrl, + .get_address = apple_nvme_get_address, +}; + +static void apple_nvme_async_probe(void *data, async_cookie_t cookie) +{ + struct apple_nvme *anv = data; + + flush_work(&anv->ctrl.reset_work); + flush_work(&anv->ctrl.scan_work); + nvme_put_ctrl(&anv->ctrl); +} + +static int apple_nvme_alloc_tagsets(struct apple_nvme *anv) +{ + int ret; + + anv->admin_tagset.ops = &apple_nvme_mq_admin_ops; + anv->admin_tagset.nr_hw_queues = 1; + anv->admin_tagset.queue_depth = APPLE_NVME_AQ_MQ_TAG_DEPTH; + anv->admin_tagset.timeout = NVME_ADMIN_TIMEOUT; + anv->admin_tagset.numa_node = NUMA_NO_NODE; + anv->admin_tagset.cmd_size = sizeof(struct apple_nvme_iod); + anv->admin_tagset.flags = BLK_MQ_F_NO_SCHED; + anv->admin_tagset.driver_data = &anv->adminq; + + ret = blk_mq_alloc_tag_set(&anv->admin_tagset); + if (ret) + return ret; + ret = devm_add_action_or_reset(anv->dev, + (void (*)(void *))blk_mq_free_tag_set, + &anv->admin_tagset); + if (ret) + return ret; + + anv->tagset.ops = &apple_nvme_mq_ops; + anv->tagset.nr_hw_queues = 1; + anv->tagset.nr_maps = 1; + /* + * Tags are used as an index to the NVMMU and must be unique across + * both queues. The admin queue gets the first APPLE_NVME_AQ_DEPTH which + * must be marked as reserved in the IO queue. + */ + anv->tagset.reserved_tags = APPLE_NVME_AQ_DEPTH; + anv->tagset.queue_depth = APPLE_ANS_MAX_QUEUE_DEPTH - 1; + anv->tagset.timeout = NVME_IO_TIMEOUT; + anv->tagset.numa_node = NUMA_NO_NODE; + anv->tagset.cmd_size = sizeof(struct apple_nvme_iod); + anv->tagset.flags = BLK_MQ_F_SHOULD_MERGE; + anv->tagset.driver_data = &anv->ioq; + + ret = blk_mq_alloc_tag_set(&anv->tagset); + if (ret) + return ret; + ret = devm_add_action_or_reset( + anv->dev, (void (*)(void *))blk_mq_free_tag_set, &anv->tagset); + if (ret) + return ret; + + anv->ctrl.admin_tagset = &anv->admin_tagset; + anv->ctrl.tagset = &anv->tagset; + + return 0; +} + +static int apple_nvme_queue_alloc(struct apple_nvme *anv, + struct apple_nvme_queue *q) +{ + unsigned int depth = apple_nvme_queue_depth(q); + + q->cqes = dmam_alloc_coherent(anv->dev, + depth * sizeof(struct nvme_completion), + &q->cq_dma_addr, GFP_KERNEL); + if (!q->cqes) + return -ENOMEM; + + q->sqes = dmam_alloc_coherent(anv->dev, + depth * sizeof(struct nvme_command), + &q->sq_dma_addr, GFP_KERNEL); + if (!q->sqes) + return -ENOMEM; + + /* + * We need the maximum queue depth here because the NVMMU only has a + * single depth configuration shared between both queues. + */ + q->tcbs = dmam_alloc_coherent(anv->dev, + APPLE_ANS_MAX_QUEUE_DEPTH * + sizeof(struct apple_nvmmu_tcb), + &q->tcb_dma_addr, GFP_KERNEL); + if (!q->tcbs) + return -ENOMEM; + + /* + * initialize phase to make sure the allocated and empty memory + * doesn't look like a full cq already. + */ + q->cq_phase = 1; + return 0; +} + +static void apple_nvme_detach_genpd(struct apple_nvme *anv) +{ + int i; + + if (anv->pd_count <= 1) + return; + + for (i = anv->pd_count - 1; i >= 0; i--) { + if (anv->pd_link[i]) + device_link_del(anv->pd_link[i]); + if (!IS_ERR_OR_NULL(anv->pd_dev[i])) + dev_pm_domain_detach(anv->pd_dev[i], true); + } +} + +static int apple_nvme_attach_genpd(struct apple_nvme *anv) +{ + struct device *dev = anv->dev; + int i; + + anv->pd_count = of_count_phandle_with_args( + dev->of_node, "power-domains", "#power-domain-cells"); + if (anv->pd_count <= 1) + return 0; + + anv->pd_dev = devm_kcalloc(dev, anv->pd_count, sizeof(*anv->pd_dev), + GFP_KERNEL); + if (!anv->pd_dev) + return -ENOMEM; + + anv->pd_link = devm_kcalloc(dev, anv->pd_count, sizeof(*anv->pd_link), + GFP_KERNEL); + if (!anv->pd_link) + return -ENOMEM; + + for (i = 0; i < anv->pd_count; i++) { + anv->pd_dev[i] = dev_pm_domain_attach_by_id(dev, i); + if (IS_ERR(anv->pd_dev[i])) { + apple_nvme_detach_genpd(anv); + return PTR_ERR(anv->pd_dev[i]); + } + + anv->pd_link[i] = device_link_add(dev, anv->pd_dev[i], + DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE); + if (!anv->pd_link[i]) { + apple_nvme_detach_genpd(anv); + return -EINVAL; + } + } + + return 0; +} + +static int apple_nvme_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct apple_nvme *anv; + int ret; + + anv = devm_kzalloc(dev, sizeof(*anv), GFP_KERNEL); + if (!anv) + return -ENOMEM; + + anv->dev = get_device(dev); + anv->adminq.is_adminq = true; + platform_set_drvdata(pdev, anv); + + ret = apple_nvme_attach_genpd(anv); + if (ret < 0) { + dev_err_probe(dev, ret, "Failed to attach power domains"); + goto put_dev; + } + if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) { + ret = -ENXIO; + goto put_dev; + } + + anv->irq = platform_get_irq(pdev, 0); + if (anv->irq < 0) { + ret = anv->irq; + goto put_dev; + } + if (!anv->irq) { + ret = -ENXIO; + goto put_dev; + } + + anv->mmio_coproc = devm_platform_ioremap_resource_byname(pdev, "ans"); + if (IS_ERR(anv->mmio_coproc)) { + ret = PTR_ERR(anv->mmio_coproc); + goto put_dev; + } + anv->mmio_nvme = devm_platform_ioremap_resource_byname(pdev, "nvme"); + if (IS_ERR(anv->mmio_nvme)) { + ret = PTR_ERR(anv->mmio_nvme); + goto put_dev; + } + + anv->adminq.sq_db = anv->mmio_nvme + APPLE_ANS_LINEAR_ASQ_DB; + anv->adminq.cq_db = anv->mmio_nvme + APPLE_ANS_ACQ_DB; + anv->ioq.sq_db = anv->mmio_nvme + APPLE_ANS_LINEAR_IOSQ_DB; + anv->ioq.cq_db = anv->mmio_nvme + APPLE_ANS_IOCQ_DB; + + anv->sart = devm_apple_sart_get(dev); + if (IS_ERR(anv->sart)) { + ret = dev_err_probe(dev, PTR_ERR(anv->sart), + "Failed to initialize SART"); + goto put_dev; + } + + anv->reset = devm_reset_control_array_get_exclusive(anv->dev); + if (IS_ERR(anv->reset)) { + ret = dev_err_probe(dev, PTR_ERR(anv->reset), + "Failed to get reset control"); + goto put_dev; + } + + INIT_WORK(&anv->ctrl.reset_work, apple_nvme_reset_work); + INIT_WORK(&anv->remove_work, apple_nvme_remove_dead_ctrl_work); + spin_lock_init(&anv->lock); + + ret = apple_nvme_queue_alloc(anv, &anv->adminq); + if (ret) + goto put_dev; + ret = apple_nvme_queue_alloc(anv, &anv->ioq); + if (ret) + goto put_dev; + + anv->prp_page_pool = dmam_pool_create("prp list page", anv->dev, + NVME_CTRL_PAGE_SIZE, + NVME_CTRL_PAGE_SIZE, 0); + if (!anv->prp_page_pool) { + ret = -ENOMEM; + goto put_dev; + } + + anv->prp_small_pool = + dmam_pool_create("prp list 256", anv->dev, 256, 256, 0); + if (!anv->prp_small_pool) { + ret = -ENOMEM; + goto put_dev; + } + + WARN_ON_ONCE(apple_nvme_iod_alloc_size() > PAGE_SIZE); + anv->iod_mempool = + mempool_create_kmalloc_pool(1, apple_nvme_iod_alloc_size()); + if (!anv->iod_mempool) { + ret = -ENOMEM; + goto put_dev; + } + ret = devm_add_action_or_reset( + anv->dev, (void (*)(void *))mempool_destroy, anv->iod_mempool); + if (ret) + goto put_dev; + + ret = apple_nvme_alloc_tagsets(anv); + if (ret) + goto put_dev; + + ret = devm_request_irq(anv->dev, anv->irq, apple_nvme_irq, 0, + "nvme-apple", anv); + if (ret) { + dev_err_probe(dev, ret, "Failed to request IRQ"); + goto put_dev; + } + + anv->rtk = + devm_apple_rtkit_init(dev, anv, NULL, 0, &apple_nvme_rtkit_ops); + if (IS_ERR(anv->rtk)) { + ret = dev_err_probe(dev, PTR_ERR(anv->rtk), + "Failed to initialize RTKit"); + goto put_dev; + } + + ret = nvme_init_ctrl(&anv->ctrl, anv->dev, &nvme_ctrl_ops, + NVME_QUIRK_SKIP_CID_GEN); + if (ret) { + dev_err_probe(dev, ret, "Failed to initialize nvme_ctrl"); + goto put_dev; + } + + anv->ctrl.admin_q = blk_mq_init_queue(&anv->admin_tagset); + if (IS_ERR(anv->ctrl.admin_q)) { + ret = -ENOMEM; + goto put_dev; + } + + if (!blk_get_queue(anv->ctrl.admin_q)) { + nvme_start_admin_queue(&anv->ctrl); + blk_cleanup_queue(anv->ctrl.admin_q); + anv->ctrl.admin_q = NULL; + ret = -ENODEV; + goto put_dev; + } + + nvme_reset_ctrl(&anv->ctrl); + async_schedule(apple_nvme_async_probe, anv); + + return 0; + +put_dev: + put_device(anv->dev); + return ret; +} + +static int apple_nvme_remove(struct platform_device *pdev) +{ + struct apple_nvme *anv = platform_get_drvdata(pdev); + + nvme_change_ctrl_state(&anv->ctrl, NVME_CTRL_DELETING); + flush_work(&anv->ctrl.reset_work); + nvme_stop_ctrl(&anv->ctrl); + nvme_remove_namespaces(&anv->ctrl); + apple_nvme_disable(anv, true); + nvme_uninit_ctrl(&anv->ctrl); + + if (apple_rtkit_is_running(anv->rtk)) + apple_rtkit_shutdown(anv->rtk); + + apple_nvme_detach_genpd(anv); + + return 0; +} + +static void apple_nvme_shutdown(struct platform_device *pdev) +{ + struct apple_nvme *anv = platform_get_drvdata(pdev); + + apple_nvme_disable(anv, true); + if (apple_rtkit_is_running(anv->rtk)) + apple_rtkit_shutdown(anv->rtk); +} + +static int apple_nvme_resume(struct device *dev) +{ + struct apple_nvme *anv = dev_get_drvdata(dev); + + return nvme_reset_ctrl(&anv->ctrl); +} + +static int apple_nvme_suspend(struct device *dev) +{ + struct apple_nvme *anv = dev_get_drvdata(dev); + int ret = 0; + + apple_nvme_disable(anv, true); + + if (apple_rtkit_is_running(anv->rtk)) + ret = apple_rtkit_shutdown(anv->rtk); + + writel(0, anv->mmio_coproc + APPLE_ANS_COPROC_CPU_CONTROL); + + return ret; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(apple_nvme_pm_ops, apple_nvme_suspend, + apple_nvme_resume); + +static const struct of_device_id apple_nvme_of_match[] = { + { .compatible = "apple,nvme-ans2" }, + {}, +}; +MODULE_DEVICE_TABLE(of, apple_nvme_of_match); + +static struct platform_driver apple_nvme_driver = { + .driver = { + .name = "nvme-apple", + .of_match_table = apple_nvme_of_match, + .pm = pm_sleep_ptr(&apple_nvme_pm_ops), + }, + .probe = apple_nvme_probe, + .remove = apple_nvme_remove, + .shutdown = apple_nvme_shutdown, +}; +module_platform_driver(apple_nvme_driver); + +MODULE_AUTHOR("Sven Peter "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From b9e8a7d950ffed4cdd81e6457cfb8049227da2d1 Mon Sep 17 00:00:00 2001 From: Dave Gerlach Date: Tue, 12 Apr 2022 14:21:38 -0500 Subject: firmware: ti_sci: Switch transport to polled mode during system suspend During system suspend it is completely valid for devices to invoke TISCI commands during the noirq phase of the suspend path. Specifically this will always be seen for devices that define a power-domains DT property and make use of the ti_sci_pm_domains genpd implementation. The genpd_finish_suspend call will power off devices during the noirq phase, which will invoke TISCI. In order to support this, the ti_sci driver must switch to not use wait_for_completion_timeout during suspend, but instead rely on a manual check for if the completion is not yet done, and proceed only if this is the case. Signed-off-by: Dave Gerlach Signed-off-by: Nishanth Menon Link: https://lore.kernel.org/r/20220412192138.31189-1-d-gerlach@ti.com --- drivers/firmware/ti_sci.c | 61 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index 4697edc125b1..ebc32bbd9b83 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -2,7 +2,7 @@ /* * Texas Instruments System Control Interface Protocol Driver * - * Copyright (C) 2015-2016 Texas Instruments Incorporated - https://www.ti.com/ + * Copyright (C) 2015-2022 Texas Instruments Incorporated - https://www.ti.com/ * Nishanth Menon */ @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -96,6 +97,7 @@ struct ti_sci_desc { * @node: list head * @host_id: Host ID * @users: Number of users of this instance + * @is_suspending: Flag set to indicate in suspend path. */ struct ti_sci_info { struct device *dev; @@ -114,7 +116,7 @@ struct ti_sci_info { u8 host_id; /* protected by ti_sci_list_mutex */ int users; - + bool is_suspending; }; #define cl_to_ti_sci_info(c) container_of(c, struct ti_sci_info, cl) @@ -349,6 +351,8 @@ static struct ti_sci_xfer *ti_sci_get_one_xfer(struct ti_sci_info *info, hdr = (struct ti_sci_msg_hdr *)xfer->tx_message.buf; xfer->tx_message.len = tx_message_size; + xfer->tx_message.chan_rx = info->chan_rx; + xfer->tx_message.timeout_rx_ms = info->desc->max_rx_timeout_ms; xfer->rx_len = (u8)rx_message_size; reinit_completion(&xfer->done); @@ -406,6 +410,7 @@ static inline int ti_sci_do_xfer(struct ti_sci_info *info, int ret; int timeout; struct device *dev = info->dev; + bool done_state = true; ret = mbox_send_message(info->chan_tx, &xfer->tx_message); if (ret < 0) @@ -413,13 +418,27 @@ static inline int ti_sci_do_xfer(struct ti_sci_info *info, ret = 0; - /* And we wait for the response. */ - timeout = msecs_to_jiffies(info->desc->max_rx_timeout_ms); - if (!wait_for_completion_timeout(&xfer->done, timeout)) { + if (!info->is_suspending) { + /* And we wait for the response. */ + timeout = msecs_to_jiffies(info->desc->max_rx_timeout_ms); + if (!wait_for_completion_timeout(&xfer->done, timeout)) + ret = -ETIMEDOUT; + } else { + /* + * If we are suspending, we cannot use wait_for_completion_timeout + * during noirq phase, so we must manually poll the completion. + */ + ret = read_poll_timeout_atomic(try_wait_for_completion, done_state, + true, 1, + info->desc->max_rx_timeout_ms * 1000, + false, &xfer->done); + } + + if (ret == -ETIMEDOUT || !done_state) { dev_err(dev, "Mbox timedout in resp(caller: %pS)\n", (void *)_RET_IP_); - ret = -ETIMEDOUT; } + /* * NOTE: we might prefer not to need the mailbox ticker to manage the * transfer queueing since the protocol layer queues things by itself. @@ -3264,6 +3283,35 @@ static int tisci_reboot_handler(struct notifier_block *nb, unsigned long mode, return NOTIFY_BAD; } +static void ti_sci_set_is_suspending(struct ti_sci_info *info, bool is_suspending) +{ + info->is_suspending = is_suspending; +} + +static int ti_sci_suspend(struct device *dev) +{ + struct ti_sci_info *info = dev_get_drvdata(dev); + /* + * We must switch operation to polled mode now as drivers and the genpd + * layer may make late TI SCI calls to change clock and device states + * from the noirq phase of suspend. + */ + ti_sci_set_is_suspending(info, true); + + return 0; +} + +static int ti_sci_resume(struct device *dev) +{ + struct ti_sci_info *info = dev_get_drvdata(dev); + + ti_sci_set_is_suspending(info, false); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(ti_sci_pm_ops, ti_sci_suspend, ti_sci_resume); + /* Description for K2G */ static const struct ti_sci_desc ti_sci_pmmc_k2g_desc = { .default_host_id = 2, @@ -3472,6 +3520,7 @@ static struct platform_driver ti_sci_driver = { .driver = { .name = "ti-sci", .of_match_table = of_match_ptr(ti_sci_of_match), + .pm = &ti_sci_pm_ops, }, }; module_platform_driver(ti_sci_driver); -- cgit v1.2.3 From 2b7042500cab7952bdbf4fe4a84de8712b418c36 Mon Sep 17 00:00:00 2001 From: Minghao Chi Date: Mon, 18 Apr 2022 06:30:59 +0000 Subject: soc: ti: pm33xx: using pm_runtime_resume_and_get instead of pm_runtime_get_sync Using pm_runtime_resume_and_get is more appropriate for simplifing code Reported-by: Zeal Robot Signed-off-by: Minghao Chi Signed-off-by: Nishanth Menon Reviewed-by: Tony Lindgren Link: https://lore.kernel.org/r/20220418063059.2558074-1-chi.minghao@zte.com.cn --- drivers/soc/ti/pm33xx.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soc/ti/pm33xx.c b/drivers/soc/ti/pm33xx.c index 7bab4bbaf02d..ce09c42eaed2 100644 --- a/drivers/soc/ti/pm33xx.c +++ b/drivers/soc/ti/pm33xx.c @@ -555,11 +555,9 @@ static int am33xx_pm_probe(struct platform_device *pdev) #endif /* CONFIG_SUSPEND */ pm_runtime_enable(dev); - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - pm_runtime_put_noidle(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) goto err_pm_runtime_disable; - } ret = pm_ops->init(am33xx_do_sram_idle); if (ret) { -- cgit v1.2.3 From d4c41d32cf8af10e4c0a35a6d4995de253b54df6 Mon Sep 17 00:00:00 2001 From: Haowen Bai Date: Sun, 24 Apr 2022 10:05:43 +0800 Subject: soc: ti: knav_qmss_queue: Use IS_ERR instead of IS_ERR_OR_NULL when checking knav_queue_open() result As the usage of knav_queue_open(): * Returns a handle to the open hardware queue if successful. Use IS_ERR() * to check the returned value for error codes. It will only return error codes, not null. Signed-off-by: Haowen Bai Signed-off-by: Nishanth Menon Link: https://lore.kernel.org/r/1650765944-20170-1-git-send-email-baihaowen@meizu.com --- drivers/soc/ti/knav_qmss_queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/ti/knav_qmss_queue.c b/drivers/soc/ti/knav_qmss_queue.c index 30612719e2f1..92af7d1b6f5b 100644 --- a/drivers/soc/ti/knav_qmss_queue.c +++ b/drivers/soc/ti/knav_qmss_queue.c @@ -789,7 +789,7 @@ void *knav_pool_create(const char *name, } pool->queue = knav_queue_open(name, KNAV_QUEUE_GP, 0); - if (IS_ERR_OR_NULL(pool->queue)) { + if (IS_ERR(pool->queue)) { dev_err(kdev->dev, "failed to open queue for pool(%s), error %ld\n", name, PTR_ERR(pool->queue)); -- cgit v1.2.3 From 1dcbae86ee669bdb0338954cd0136863f5c96c0a Mon Sep 17 00:00:00 2001 From: Dave Gerlach Date: Thu, 14 Apr 2022 12:27:24 -0700 Subject: soc: ti: wkup_m3_ipc: Add support for IO Isolation AM43xx support isolation of the IOs so that control is taken from the peripheral they are connected to and overridden by values present in the CTRL_CONF_* registers for the pad in the control module. The actual toggling happens from the wkup_m3, so use a DT property from the wkup_m3_ipc node to allow the PM code to communicate the necessity for placing the IOs into isolation to the firmware. Signed-off-by: Dave Gerlach Signed-off-by: Keerthy Signed-off-by: Drew Fustini Signed-off-by: Nishanth Menon Link: https://lore.kernel.org/r/20220414192722.2978837-3-dfustini@baylibre.com --- drivers/soc/ti/wkup_m3_ipc.c | 14 ++++++++++++-- include/linux/wkup_m3_ipc.h | 1 + 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/soc/ti/wkup_m3_ipc.c b/drivers/soc/ti/wkup_m3_ipc.c index 48659758228e..2f2a0ab32aff 100644 --- a/drivers/soc/ti/wkup_m3_ipc.c +++ b/drivers/soc/ti/wkup_m3_ipc.c @@ -46,6 +46,8 @@ #define IPC_VTT_STAT_MASK (0x1 << 3) #define IPC_VTT_GPIO_PIN_SHIFT (0x4) #define IPC_VTT_GPIO_PIN_MASK (0x3f << 4) +#define IPC_IO_ISOLATION_STAT_SHIFT (10) +#define IPC_IO_ISOLATION_STAT_MASK (0x1 << 10) #define M3_STATE_UNKNOWN 0 #define M3_STATE_RESET 1 @@ -228,6 +230,11 @@ static void wkup_m3_set_vtt_gpio(struct wkup_m3_ipc *m3_ipc, int gpio) (gpio << IPC_VTT_GPIO_PIN_SHIFT); } +static void wkup_m3_set_io_isolation(struct wkup_m3_ipc *m3_ipc) +{ + m3_ipc->isolation_conf = (1 << IPC_IO_ISOLATION_STAT_SHIFT); +} + /* Public functions */ /** * wkup_m3_set_mem_type - Pass wkup_m3 which type of memory is in use @@ -308,8 +315,8 @@ static int wkup_m3_prepare_low_power(struct wkup_m3_ipc *m3_ipc, int state) wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->resume_addr, 0); wkup_m3_ctrl_ipc_write(m3_ipc, m3_power_state, 1); wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->mem_type | - m3_ipc->vtt_conf, 4); - + m3_ipc->vtt_conf | + m3_ipc->isolation_conf, 4); wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 2); wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 3); wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 5); @@ -516,6 +523,9 @@ static int wkup_m3_ipc_probe(struct platform_device *pdev) dev_warn(dev, "Invalid VTT GPIO(%d) pin\n", temp); } + if (of_find_property(np, "ti,set-io-isolation", NULL)) + wkup_m3_set_io_isolation(m3_ipc); + /* * Wait for firmware loading completion in a thread so we * can boot the wkup_m3 as soon as it's ready without holding diff --git a/include/linux/wkup_m3_ipc.h b/include/linux/wkup_m3_ipc.h index 2bc52c6381d5..b706eac58f92 100644 --- a/include/linux/wkup_m3_ipc.h +++ b/include/linux/wkup_m3_ipc.h @@ -34,6 +34,7 @@ struct wkup_m3_ipc { int mem_type; unsigned long resume_addr; int vtt_conf; + int isolation_conf; int state; struct completion sync_complete; -- cgit v1.2.3 From ea082040fe071d2ba1f8f73792743d7ca9fb218e Mon Sep 17 00:00:00 2001 From: Dave Gerlach Date: Tue, 26 Apr 2022 13:07:44 -0700 Subject: soc: ti: wkup_m3_ipc: Add support for i2c voltage scaling Allow loading of a binary containing i2c scaling sequences to be provided to the wkup_m3 firmware in order to properly scale voltage rails on the PMIC during low power modes like DeepSleep0. Proper binary format is determined by the FW in use. Code expects firmware to have 0x0C57 present as the first two bytes followed by one byte defining offset to sleep sequence followed by one byte defining offset to wake sequence and then lastly both sequences. Each sequence is a series of I2C transfers in the form: u8 length | u8 chip address | u8 byte0/reg address | u8 byte1 | u8 byteN .. The length indicates the number of bytes to transfer, including the register address. The length of each transfer is limited by the I2C buffer size of 32 bytes. Based on previous work by Russ Dill. [dfustini: replace FW_ACTION_HOTPLUG with FW_ACTION_UEVENT] Signed-off-by: Dave Gerlach Signed-off-by: Keerthy [dfustini: add NULL argument to rproc_da_to_va() call] Signed-off-by: Drew Fustini Signed-off-by: Nishanth Menon Link: https://lore.kernel.org/r/20220426200741.712842-3-dfustini@baylibre.com --- drivers/soc/ti/wkup_m3_ipc.c | 93 +++++++++++++++++++++++++++++++++++++++++++- include/linux/wkup_m3_ipc.h | 9 +++++ 2 files changed, 101 insertions(+), 1 deletion(-) diff --git a/drivers/soc/ti/wkup_m3_ipc.c b/drivers/soc/ti/wkup_m3_ipc.c index 2f2a0ab32aff..84e9534056f8 100644 --- a/drivers/soc/ti/wkup_m3_ipc.c +++ b/drivers/soc/ti/wkup_m3_ipc.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -55,6 +56,12 @@ #define M3_STATE_MSG_FOR_LP 3 #define M3_STATE_MSG_FOR_RESET 4 +#define WKUP_M3_SD_FW_MAGIC 0x570C + +#define WKUP_M3_DMEM_START 0x80000 +#define WKUP_M3_AUXDATA_OFFSET 0x1000 +#define WKUP_M3_AUXDATA_SIZE 0xFF + static struct wkup_m3_ipc *m3_ipc_state; static const struct wkup_m3_wakeup_src wakeups[] = { @@ -75,6 +82,81 @@ static const struct wkup_m3_wakeup_src wakeups[] = { {.irq_nr = 0, .src = "Unknown"}, }; +/** + * wkup_m3_copy_aux_data - Copy auxiliary data to special region of m3 dmem + * @data - pointer to data + * @sz - size of data to copy (limit 256 bytes) + * + * Copies any additional blob of data to the wkup_m3 dmem to be used by the + * firmware + */ +static unsigned long wkup_m3_copy_aux_data(struct wkup_m3_ipc *m3_ipc, + const void *data, int sz) +{ + unsigned long aux_data_dev_addr; + void *aux_data_addr; + + aux_data_dev_addr = WKUP_M3_DMEM_START + WKUP_M3_AUXDATA_OFFSET; + aux_data_addr = rproc_da_to_va(m3_ipc->rproc, + aux_data_dev_addr, + WKUP_M3_AUXDATA_SIZE, + NULL); + memcpy(aux_data_addr, data, sz); + + return WKUP_M3_AUXDATA_OFFSET; +} + +static void wkup_m3_scale_data_fw_cb(const struct firmware *fw, void *context) +{ + unsigned long val, aux_base; + struct wkup_m3_scale_data_header hdr; + struct wkup_m3_ipc *m3_ipc = context; + struct device *dev = m3_ipc->dev; + + if (!fw) { + dev_err(dev, "Voltage scale fw name given but file missing.\n"); + return; + } + + memcpy(&hdr, fw->data, sizeof(hdr)); + + if (hdr.magic != WKUP_M3_SD_FW_MAGIC) { + dev_err(dev, "PM: Voltage Scale Data binary does not appear valid.\n"); + goto release_sd_fw; + } + + aux_base = wkup_m3_copy_aux_data(m3_ipc, fw->data + sizeof(hdr), + fw->size - sizeof(hdr)); + + val = (aux_base + hdr.sleep_offset); + val |= ((aux_base + hdr.wake_offset) << 16); + + m3_ipc->volt_scale_offsets = val; + +release_sd_fw: + release_firmware(fw); +}; + +static int wkup_m3_init_scale_data(struct wkup_m3_ipc *m3_ipc, + struct device *dev) +{ + int ret = 0; + + /* + * If no name is provided, user has already been warned, pm will + * still work so return 0 + */ + + if (!m3_ipc->sd_fw_name) + return ret; + + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, + m3_ipc->sd_fw_name, dev, GFP_ATOMIC, + m3_ipc, wkup_m3_scale_data_fw_cb); + + return ret; +} + static void am33xx_txev_eoi(struct wkup_m3_ipc *m3_ipc) { writel(AM33XX_M3_TXEV_ACK, @@ -139,6 +221,7 @@ static irqreturn_t wkup_m3_txev_handler(int irq, void *ipc_data) } m3_ipc->state = M3_STATE_INITED; + wkup_m3_init_scale_data(m3_ipc, dev); complete(&m3_ipc->sync_complete); break; case M3_STATE_MSG_FOR_RESET: @@ -300,12 +383,15 @@ static int wkup_m3_prepare_low_power(struct wkup_m3_ipc *m3_ipc, int state) switch (state) { case WKUP_M3_DEEPSLEEP: m3_power_state = IPC_CMD_DS0; + wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->volt_scale_offsets, 5); break; case WKUP_M3_STANDBY: m3_power_state = IPC_CMD_STANDBY; + wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 5); break; case WKUP_M3_IDLE: m3_power_state = IPC_CMD_IDLE; + wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 5); break; default: return 1; @@ -319,7 +405,6 @@ static int wkup_m3_prepare_low_power(struct wkup_m3_ipc *m3_ipc, int state) m3_ipc->isolation_conf, 4); wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 2); wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 3); - wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 5); wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 6); wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 7); @@ -526,6 +611,12 @@ static int wkup_m3_ipc_probe(struct platform_device *pdev) if (of_find_property(np, "ti,set-io-isolation", NULL)) wkup_m3_set_io_isolation(m3_ipc); + ret = of_property_read_string(np, "firmware-name", + &m3_ipc->sd_fw_name); + if (ret) { + dev_dbg(dev, "Voltage scaling data blob not provided from DT.\n"); + }; + /* * Wait for firmware loading completion in a thread so we * can boot the wkup_m3 as soon as it's ready without holding diff --git a/include/linux/wkup_m3_ipc.h b/include/linux/wkup_m3_ipc.h index b706eac58f92..fef0fac60f8c 100644 --- a/include/linux/wkup_m3_ipc.h +++ b/include/linux/wkup_m3_ipc.h @@ -37,6 +37,9 @@ struct wkup_m3_ipc { int isolation_conf; int state; + unsigned long volt_scale_offsets; + const char *sd_fw_name; + struct completion sync_complete; struct mbox_client mbox_client; struct mbox_chan *mbox; @@ -50,6 +53,12 @@ struct wkup_m3_wakeup_src { char src[10]; }; +struct wkup_m3_scale_data_header { + u16 magic; + u8 sleep_offset; + u8 wake_offset; +} __packed; + struct wkup_m3_ipc_ops { void (*set_mem_type)(struct wkup_m3_ipc *m3_ipc, int mem_type); void (*set_resume_address)(struct wkup_m3_ipc *m3_ipc, void *addr); -- cgit v1.2.3 From 2a21f9e6d9a408dbd09a01caf5fff42c2f70fa82 Mon Sep 17 00:00:00 2001 From: Dave Gerlach Date: Sun, 1 May 2022 20:32:12 -0700 Subject: soc: ti: wkup_m3_ipc: Add debug option to halt m3 in suspend Add a debugfs option to allow configurable halting of the wkup_m3 during suspend at the last possible point before low power mode entry. This condition can only be resolved through JTAG and advancing beyond the while loop in a8_lp_ds0_handler [1]. Although this hangs the system it forces the system to remain active once it has been entirely configured for low power mode entry, allowing for register inspection through JTAG to help in debugging transition errors. Halt mode can be set using the enable_off_mode entry under wkup_m3_ipc in the debugfs. [1] https://git.ti.com/cgit/processor-firmware/ti-amx3-cm3-pm-firmware/tree/src/pm_services/pm_handlers.c?h=08.02.00.006#n141 Suggested-by: Brad Griffis Signed-off-by: Dave Gerlach [dfustini: add link for a8_lp_ds0_handler() in ti-amx3-cm3-pm-firmware] Signed-off-by: Drew Fustini Signed-off-by: Nishanth Menon Link: https://lore.kernel.org/r/20220502033211.1383158-1-dfustini@baylibre.com --- drivers/soc/ti/wkup_m3_ipc.c | 79 +++++++++++++++++++++++++++++++++++++++++++- include/linux/wkup_m3_ipc.h | 2 ++ 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/drivers/soc/ti/wkup_m3_ipc.c b/drivers/soc/ti/wkup_m3_ipc.c index 84e9534056f8..0076d467ff6b 100644 --- a/drivers/soc/ti/wkup_m3_ipc.c +++ b/drivers/soc/ti/wkup_m3_ipc.c @@ -7,6 +7,7 @@ * Dave Gerlach */ +#include #include #include #include @@ -50,6 +51,9 @@ #define IPC_IO_ISOLATION_STAT_SHIFT (10) #define IPC_IO_ISOLATION_STAT_MASK (0x1 << 10) +#define IPC_DBG_HALT_SHIFT (11) +#define IPC_DBG_HALT_MASK (0x1 << 11) + #define M3_STATE_UNKNOWN 0 #define M3_STATE_RESET 1 #define M3_STATE_INITED 2 @@ -157,6 +161,73 @@ static int wkup_m3_init_scale_data(struct wkup_m3_ipc *m3_ipc, return ret; } +#ifdef CONFIG_DEBUG_FS +static void wkup_m3_set_halt_late(bool enabled) +{ + if (enabled) + m3_ipc_state->halt = (1 << IPC_DBG_HALT_SHIFT); + else + m3_ipc_state->halt = 0; +} + +static int option_get(void *data, u64 *val) +{ + u32 *option = data; + + *val = *option; + + return 0; +} + +static int option_set(void *data, u64 val) +{ + u32 *option = data; + + *option = val; + + if (option == &m3_ipc_state->halt) { + if (val) + wkup_m3_set_halt_late(true); + else + wkup_m3_set_halt_late(false); + } + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(wkup_m3_ipc_option_fops, option_get, option_set, + "%llu\n"); + +static int wkup_m3_ipc_dbg_init(struct wkup_m3_ipc *m3_ipc) +{ + m3_ipc->dbg_path = debugfs_create_dir("wkup_m3_ipc", NULL); + + if (!m3_ipc->dbg_path) + return -EINVAL; + + (void)debugfs_create_file("enable_late_halt", 0644, + m3_ipc->dbg_path, + &m3_ipc->halt, + &wkup_m3_ipc_option_fops); + + return 0; +} + +static inline void wkup_m3_ipc_dbg_destroy(struct wkup_m3_ipc *m3_ipc) +{ + debugfs_remove_recursive(m3_ipc->dbg_path); +} +#else +static inline int wkup_m3_ipc_dbg_init(struct wkup_m3_ipc *m3_ipc) +{ + return 0; +} + +static inline void wkup_m3_ipc_dbg_destroy(struct wkup_m3_ipc *m3_ipc) +{ +} +#endif /* CONFIG_DEBUG_FS */ + static void am33xx_txev_eoi(struct wkup_m3_ipc *m3_ipc) { writel(AM33XX_M3_TXEV_ACK, @@ -402,7 +473,9 @@ static int wkup_m3_prepare_low_power(struct wkup_m3_ipc *m3_ipc, int state) wkup_m3_ctrl_ipc_write(m3_ipc, m3_power_state, 1); wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->mem_type | m3_ipc->vtt_conf | - m3_ipc->isolation_conf, 4); + m3_ipc->isolation_conf | + m3_ipc->halt, 4); + wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 2); wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 3); wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 6); @@ -631,6 +704,8 @@ static int wkup_m3_ipc_probe(struct platform_device *pdev) goto err_put_rproc; } + wkup_m3_ipc_dbg_init(m3_ipc); + return 0; err_put_rproc: @@ -642,6 +717,8 @@ err_free_mbox: static int wkup_m3_ipc_remove(struct platform_device *pdev) { + wkup_m3_ipc_dbg_destroy(m3_ipc_state); + mbox_free_channel(m3_ipc_state->mbox); rproc_shutdown(m3_ipc_state->rproc); diff --git a/include/linux/wkup_m3_ipc.h b/include/linux/wkup_m3_ipc.h index fef0fac60f8c..26d1eb058fa3 100644 --- a/include/linux/wkup_m3_ipc.h +++ b/include/linux/wkup_m3_ipc.h @@ -36,6 +36,7 @@ struct wkup_m3_ipc { int vtt_conf; int isolation_conf; int state; + u32 halt; unsigned long volt_scale_offsets; const char *sd_fw_name; @@ -46,6 +47,7 @@ struct wkup_m3_ipc { struct wkup_m3_ipc_ops *ops; int is_rtc_only; + struct dentry *dbg_path; }; struct wkup_m3_wakeup_src { -- cgit v1.2.3 From c7f8852d4216f7dc061f0bc02dda5af396048861 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Tue, 3 May 2022 13:10:47 +0100 Subject: firmware: arm_scmi: Fix late checks on pointer dereference A few dereferences could happen before the iterator pointer argument was checked for NULL, causing the following smatch warnings: drivers/firmware/arm_scmi/driver.c:1214 scmi_iterator_run() warn: variable dereferenced before check 'i' (see line 1210) Fix by moving the checks early and dropping some unneeded local references. No functional change. Link: https://lore.kernel.org/r/20220503121047.3590340-1-cristian.marussi@arm.com Reported-by: kernel test robot Reported-by: Dan Carpenter Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index c4960fd3df75..c1922bd650ae 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -1205,18 +1205,21 @@ static void *scmi_iterator_init(const struct scmi_protocol_handle *ph, static int scmi_iterator_run(void *iter) { int ret = -EINVAL; + struct scmi_iterator_ops *iops; + const struct scmi_protocol_handle *ph; + struct scmi_iterator_state *st; struct scmi_iterator *i = iter; - struct scmi_iterator_state *st = &i->state; - struct scmi_iterator_ops *iops = i->ops; - const struct scmi_protocol_handle *ph = i->ph; - const struct scmi_xfer_ops *xops = ph->xops; - if (!i) + if (!i || !i->ops || !i->ph) return ret; + iops = i->ops; + ph = i->ph; + st = &i->state; + do { iops->prepare_message(i->msg, st->desc_index, i->priv); - ret = xops->do_xfer(ph, i->t); + ret = ph->xops->do_xfer(ph, i->t); if (ret) break; @@ -1240,7 +1243,7 @@ static int scmi_iterator_run(void *iter) } st->desc_index += st->num_returned; - xops->reset_rx_to_maxsz(ph, i->t); + ph->xops->reset_rx_to_maxsz(ph, i->t); /* * check for both returned and remaining to avoid infinite * loop due to buggy firmware @@ -1249,7 +1252,7 @@ static int scmi_iterator_run(void *iter) out: /* Finalize and destroy iterator */ - xops->xfer_put(ph, i->t); + ph->xops->xfer_put(ph, i->t); devm_kfree(ph->dev, i); return ret; -- cgit v1.2.3 From 176cae38719196a43cd8ae08377413a3884a9f15 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Wed, 15 Dec 2021 10:38:28 +0100 Subject: reset: uniphier-glue: Use reset_control_bulk API This driver already uses the clk_bulk API. Simplify the driver by using the reset_control_bulk API as well. Signed-off-by: Philipp Zabel Reviewed-by: Kunihiko Hayashi Link: https://lore.kernel.org/r/20211215093829.3209416-1-p.zabel@pengutronix.de Signed-off-by: Philipp Zabel --- drivers/reset/reset-uniphier-glue.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/drivers/reset/reset-uniphier-glue.c b/drivers/reset/reset-uniphier-glue.c index 908c1d5bc41e..9b22c4a91d77 100644 --- a/drivers/reset/reset-uniphier-glue.c +++ b/drivers/reset/reset-uniphier-glue.c @@ -23,7 +23,7 @@ struct uniphier_glue_reset_soc_data { struct uniphier_glue_reset_priv { struct clk_bulk_data clk[MAX_CLKS]; - struct reset_control *rst[MAX_RSTS]; + struct reset_control_bulk_data rst[MAX_RSTS]; struct reset_simple_data rdata; const struct uniphier_glue_reset_soc_data *data; }; @@ -34,8 +34,7 @@ static int uniphier_glue_reset_probe(struct platform_device *pdev) struct uniphier_glue_reset_priv *priv; struct resource *res; resource_size_t size; - const char *name; - int i, ret, nr; + int i, ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -58,22 +57,20 @@ static int uniphier_glue_reset_probe(struct platform_device *pdev) if (ret) return ret; - for (i = 0; i < priv->data->nrsts; i++) { - name = priv->data->reset_names[i]; - priv->rst[i] = devm_reset_control_get_shared(dev, name); - if (IS_ERR(priv->rst[i])) - return PTR_ERR(priv->rst[i]); - } + for (i = 0; i < priv->data->nrsts; i++) + priv->rst[i].id = priv->data->reset_names[i]; + ret = devm_reset_control_bulk_get_shared(dev, priv->data->nrsts, + priv->rst); + if (ret) + return ret; ret = clk_bulk_prepare_enable(priv->data->nclks, priv->clk); if (ret) return ret; - for (nr = 0; nr < priv->data->nrsts; nr++) { - ret = reset_control_deassert(priv->rst[nr]); - if (ret) - goto out_rst_assert; - } + ret = reset_control_bulk_deassert(priv->data->nrsts, priv->rst); + if (ret) + goto out_clk_disable; spin_lock_init(&priv->rdata.lock); priv->rdata.rcdev.owner = THIS_MODULE; @@ -91,9 +88,9 @@ static int uniphier_glue_reset_probe(struct platform_device *pdev) return 0; out_rst_assert: - while (nr--) - reset_control_assert(priv->rst[nr]); + reset_control_bulk_assert(priv->data->nrsts, priv->rst); +out_clk_disable: clk_bulk_disable_unprepare(priv->data->nclks, priv->clk); return ret; @@ -102,10 +99,8 @@ out_rst_assert: static int uniphier_glue_reset_remove(struct platform_device *pdev) { struct uniphier_glue_reset_priv *priv = platform_get_drvdata(pdev); - int i; - for (i = 0; i < priv->data->nrsts; i++) - reset_control_assert(priv->rst[i]); + reset_control_bulk_assert(priv->data->nrsts, priv->rst); clk_bulk_disable_unprepare(priv->data->nclks, priv->clk); -- cgit v1.2.3 From 72bb7314e320a74b2ca02ceb1187b2c650f38311 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Wed, 15 Dec 2021 10:38:29 +0100 Subject: reset: uniphier-glue: Use devm_add_action_or_reset() Slightly simplify uniphier_glue_reset_probe() and drop uniphier_glue_reset_remove() by using devm_add_action_or_reset() for clock and reset cleanup. Signed-off-by: Philipp Zabel Reviewed-by: Kunihiko Hayashi Link: https://lore.kernel.org/r/20211215093829.3209416-2-p.zabel@pengutronix.de Signed-off-by: Philipp Zabel --- drivers/reset/reset-uniphier-glue.c | 50 ++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/drivers/reset/reset-uniphier-glue.c b/drivers/reset/reset-uniphier-glue.c index 9b22c4a91d77..146fd5d45e99 100644 --- a/drivers/reset/reset-uniphier-glue.c +++ b/drivers/reset/reset-uniphier-glue.c @@ -28,6 +28,20 @@ struct uniphier_glue_reset_priv { const struct uniphier_glue_reset_soc_data *data; }; +static void uniphier_clk_disable(void *_priv) +{ + struct uniphier_glue_reset_priv *priv = _priv; + + clk_bulk_disable_unprepare(priv->data->nclks, priv->clk); +} + +static void uniphier_rst_assert(void *_priv) +{ + struct uniphier_glue_reset_priv *priv = _priv; + + reset_control_bulk_assert(priv->data->nrsts, priv->rst); +} + static int uniphier_glue_reset_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -68,9 +82,17 @@ static int uniphier_glue_reset_probe(struct platform_device *pdev) if (ret) return ret; + ret = devm_add_action_or_reset(dev, uniphier_clk_disable, priv); + if (ret) + return ret; + ret = reset_control_bulk_deassert(priv->data->nrsts, priv->rst); if (ret) - goto out_clk_disable; + return ret; + + ret = devm_add_action_or_reset(dev, uniphier_rst_assert, priv); + if (ret) + return ret; spin_lock_init(&priv->rdata.lock); priv->rdata.rcdev.owner = THIS_MODULE; @@ -81,30 +103,7 @@ static int uniphier_glue_reset_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); - ret = devm_reset_controller_register(dev, &priv->rdata.rcdev); - if (ret) - goto out_rst_assert; - - return 0; - -out_rst_assert: - reset_control_bulk_assert(priv->data->nrsts, priv->rst); - -out_clk_disable: - clk_bulk_disable_unprepare(priv->data->nclks, priv->clk); - - return ret; -} - -static int uniphier_glue_reset_remove(struct platform_device *pdev) -{ - struct uniphier_glue_reset_priv *priv = platform_get_drvdata(pdev); - - reset_control_bulk_assert(priv->data->nrsts, priv->rst); - - clk_bulk_disable_unprepare(priv->data->nclks, priv->clk); - - return 0; + return devm_reset_controller_register(dev, &priv->rdata.rcdev); } static const char * const uniphier_pro4_clock_reset_names[] = { @@ -172,7 +171,6 @@ MODULE_DEVICE_TABLE(of, uniphier_glue_reset_match); static struct platform_driver uniphier_glue_reset_driver = { .probe = uniphier_glue_reset_probe, - .remove = uniphier_glue_reset_remove, .driver = { .name = "uniphier-glue-reset", .of_match_table = uniphier_glue_reset_match, -- cgit v1.2.3 From 55b6267efd10b5c332af9dc624a25070fbfe2fc0 Mon Sep 17 00:00:00 2001 From: Zelong Dong Date: Fri, 7 Jan 2022 10:39:29 +0800 Subject: dt-bindings: reset: Add compatible for Meson-S4 Reset Controller Add new compatible for Amlogic's Meson-S4 Reset Controller Signed-off-by: Zelong Dong Acked-by: Rob Herring Reviewed-by: Martin Blumenstingl Signed-off-by: Philipp Zabel Link: https://lore.kernel.org/r/20220107023931.13251-2-zelong.dong@amlogic.com --- Documentation/devicetree/bindings/reset/amlogic,meson-reset.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/reset/amlogic,meson-reset.yaml b/Documentation/devicetree/bindings/reset/amlogic,meson-reset.yaml index 92922d3afd14..494a454928ce 100644 --- a/Documentation/devicetree/bindings/reset/amlogic,meson-reset.yaml +++ b/Documentation/devicetree/bindings/reset/amlogic,meson-reset.yaml @@ -17,6 +17,7 @@ properties: - amlogic,meson-gxbb-reset # Reset Controller on GXBB and compatible SoCs - amlogic,meson-axg-reset # Reset Controller on AXG and compatible SoCs - amlogic,meson-a1-reset # Reset Controller on A1 and compatible SoCs + - amlogic,meson-s4-reset # Reset Controller on S4 and compatible SoCs reg: maxItems: 1 -- cgit v1.2.3 From 52f988d757c78cea06b1d5c75791a7fe1638ef86 Mon Sep 17 00:00:00 2001 From: Zelong Dong Date: Fri, 7 Jan 2022 10:39:30 +0800 Subject: dt-bindings: reset: add bindings for the Meson-S4 SoC Reset Controller Add DT bindings for the Meson-S4 SoC Reset Controller include file. Signed-off-by: Zelong Dong Reviewed-by: Martin Blumenstingl Signed-off-by: Philipp Zabel Link: https://lore.kernel.org/r/20220107023931.13251-3-zelong.dong@amlogic.com --- include/dt-bindings/reset/amlogic,meson-s4-reset.h | 125 +++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 include/dt-bindings/reset/amlogic,meson-s4-reset.h diff --git a/include/dt-bindings/reset/amlogic,meson-s4-reset.h b/include/dt-bindings/reset/amlogic,meson-s4-reset.h new file mode 100644 index 000000000000..eab428eb8ad6 --- /dev/null +++ b/include/dt-bindings/reset/amlogic,meson-s4-reset.h @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ +/* + * Copyright (c) 2021 Amlogic, Inc. All rights reserved. + * Author: Zelong Dong + * + */ + +#ifndef _DT_BINDINGS_AMLOGIC_MESON_S4_RESET_H +#define _DT_BINDINGS_AMLOGIC_MESON_S4_RESET_H + +/* RESET0 */ +#define RESET_USB_DDR0 0 +#define RESET_USB_DDR1 1 +#define RESET_USB_DDR2 2 +#define RESET_USB_DDR3 3 +#define RESET_USBCTRL 4 +/* 5-7 */ +#define RESET_USBPHY20 8 +#define RESET_USBPHY21 9 +/* 10-15 */ +#define RESET_HDMITX_APB 16 +#define RESET_BRG_VCBUS_DEC 17 +#define RESET_VCBUS 18 +#define RESET_VID_PLL_DIV 19 +#define RESET_VDI6 20 +#define RESET_GE2D 21 +#define RESET_HDMITXPHY 22 +#define RESET_VID_LOCK 23 +#define RESET_VENCL 24 +#define RESET_VDAC 25 +#define RESET_VENCP 26 +#define RESET_VENCI 27 +#define RESET_RDMA 28 +#define RESET_HDMI_TX 29 +#define RESET_VIU 30 +#define RESET_VENC 31 + +/* RESET1 */ +#define RESET_AUDIO 32 +#define RESET_MALI_APB 33 +#define RESET_MALI 34 +#define RESET_DDR_APB 35 +#define RESET_DDR 36 +#define RESET_DOS_APB 37 +#define RESET_DOS 38 +/* 39-47 */ +#define RESET_ETH 48 +/* 49-51 */ +#define RESET_DEMOD 52 +/* 53-63 */ + +/* RESET2 */ +#define RESET_ABUS_ARB 64 +#define RESET_IR_CTRL 65 +#define RESET_TEMPSENSOR_DDR 66 +#define RESET_TEMPSENSOR_PLL 67 +/* 68-71 */ +#define RESET_SMART_CARD 72 +#define RESET_SPICC0 73 +/* 74 */ +#define RESET_RSA 75 +/* 76-79 */ +#define RESET_MSR_CLK 80 +#define RESET_SPIFC 81 +#define RESET_SARADC 82 +/* 83-87 */ +#define RESET_ACODEC 88 +#define RESET_CEC 89 +#define RESET_AFIFO 90 +#define RESET_WATCHDOG 91 +/* 92-95 */ + +/* RESET3 */ +/* 96-127 */ + +/* RESET4 */ +/* 128-131 */ +#define RESET_PWM_AB 132 +#define RESET_PWM_CD 133 +#define RESET_PWM_EF 134 +#define RESET_PWM_GH 135 +#define RESET_PWM_IJ 136 +/* 137 */ +#define RESET_UART_A 138 +#define RESET_UART_B 139 +#define RESET_UART_C 140 +#define RESET_UART_D 141 +#define RESET_UART_E 142 +/* 143 */ +#define RESET_I2C_S_A 144 +#define RESET_I2C_M_A 145 +#define RESET_I2C_M_B 146 +#define RESET_I2C_M_C 147 +#define RESET_I2C_M_D 148 +#define RESET_I2C_M_E 149 +/* 150-151 */ +#define RESET_SD_EMMC_A 152 +#define RESET_SD_EMMC_B 153 +#define RESET_NAND_EMMC 154 +/* 155-159 */ + +/* RESET5 */ +#define RESET_BRG_VDEC_PIPL0 160 +#define RESET_BRG_HEVCF_PIPL0 161 +/* 162 */ +#define RESET_BRG_HCODEC_PIPL0 163 +#define RESET_BRG_GE2D_PIPL0 164 +#define RESET_BRG_VPU_PIPL0 165 +#define RESET_BRG_CPU_PIPL0 166 +#define RESET_BRG_MALI_PIPL0 167 +/* 168 */ +#define RESET_BRG_MALI_PIPL1 169 +/* 170-171 */ +#define RESET_BRG_HEVCF_PIPL1 172 +#define RESET_BRG_HEVCB_PIPL1 173 +/* 174-183 */ +#define RESET_RAMA 184 +/* 185-186 */ +#define RESET_BRG_NIC_VAPB 187 +#define RESET_BRG_NIC_DSU 188 +#define RESET_BRG_NIC_SYSCLK 189 +#define RESET_BRG_NIC_MAIN 190 +#define RESET_BRG_NIC_ALL 191 + +#endif -- cgit v1.2.3 From 636728d0893f1c73b31301f21ef8e5e26d2436e9 Mon Sep 17 00:00:00 2001 From: Zelong Dong Date: Fri, 7 Jan 2022 10:39:31 +0800 Subject: reset: reset-meson: add support for the Meson-S4 SoC Reset Controller Compared to the A1 SoCs the number of RESET registers is different and the offset for the level registers is the same. Add a new compatible string and struct meson_reset_param to add support for the reset controller on the S4 SoC. Signed-off-by: Zelong Dong Reviewed-by: Martin Blumenstingl Signed-off-by: Philipp Zabel Link: https://lore.kernel.org/r/20220107023931.13251-4-zelong.dong@amlogic.com --- drivers/reset/reset-meson.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/reset/reset-meson.c b/drivers/reset/reset-meson.c index c9bc325ad65a..26dc54778615 100644 --- a/drivers/reset/reset-meson.c +++ b/drivers/reset/reset-meson.c @@ -98,11 +98,17 @@ static const struct meson_reset_param meson_a1_param = { .level_offset = 0x40, }; +static const struct meson_reset_param meson_s4_param = { + .reg_count = 6, + .level_offset = 0x40, +}; + static const struct of_device_id meson_reset_dt_ids[] = { { .compatible = "amlogic,meson8b-reset", .data = &meson8b_param}, { .compatible = "amlogic,meson-gxbb-reset", .data = &meson8b_param}, { .compatible = "amlogic,meson-axg-reset", .data = &meson8b_param}, { .compatible = "amlogic,meson-a1-reset", .data = &meson_a1_param}, + { .compatible = "amlogic,meson-s4-reset", .data = &meson_s4_param}, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, meson_reset_dt_ids); -- cgit v1.2.3 From 77fb4e45260ac2e2f20616bea6aa1003f4cd168c Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Mon, 21 Feb 2022 17:56:50 +1030 Subject: reset: simple: Add AST2600 compatible Similar to the 2400 and 2500, the LPC controller has some simple reset lines that are used by the UART devices. This is already documented in LPC bindings document. Signed-off-by: Joel Stanley Signed-off-by: Philipp Zabel Link: https://lore.kernel.org/r/20220221072650.129348-1-joel@jms.id.au --- drivers/reset/reset-simple.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/reset/reset-simple.c b/drivers/reset/reset-simple.c index 4dda0daf2c6f..361a68314265 100644 --- a/drivers/reset/reset-simple.c +++ b/drivers/reset/reset-simple.c @@ -144,6 +144,7 @@ static const struct of_device_id reset_simple_dt_ids[] = { .data = &reset_simple_active_low }, { .compatible = "aspeed,ast2400-lpc-reset" }, { .compatible = "aspeed,ast2500-lpc-reset" }, + { .compatible = "aspeed,ast2600-lpc-reset" }, { .compatible = "bitmain,bm1880-reset", .data = &reset_simple_active_low }, { .compatible = "brcm,bcm4908-misc-pcie-reset", -- cgit v1.2.3 From 82816b4ffce96e7b0df3de583037490ba7dc8792 Mon Sep 17 00:00:00 2001 From: Krishna Yarlagadda Date: Mon, 7 Mar 2022 19:26:26 +0530 Subject: reset: ACPI reset support Some of the IO devices like I2C or SPI require reset at runtime to recover from an error condition without changing the power state of the system. Added check for ACPI handle and a call to method '__RST' if supported. Devices using device tree method are unaffected by this. Signed-off-by: Krishna Yarlagadda [p.zabel@pengutronix.de: wrap in #ifdef CONFIG_ACPI due to missing stubs] Signed-off-by: Philipp Zabel Link: https://lore.kernel.org/r/20220307135626.16673-1-kyarlagadda@nvidia.com --- drivers/reset/core.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 61e688882643..f0a076e94118 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -1100,13 +1101,25 @@ EXPORT_SYMBOL_GPL(__devm_reset_control_bulk_get); * * Convenience wrapper for __reset_control_get() and reset_control_reset(). * This is useful for the common case of devices with single, dedicated reset - * lines. + * lines. _RST firmware method will be called for devices with ACPI. */ int __device_reset(struct device *dev, bool optional) { struct reset_control *rstc; int ret; +#ifdef CONFIG_ACPI + acpi_handle handle = ACPI_HANDLE(dev); + + if (handle) { + if (!acpi_has_method(handle, "_RST")) + return optional ? 0 : -ENOENT; + if (ACPI_FAILURE(acpi_evaluate_object(handle, "_RST", NULL, + NULL))) + return -EIO; + } +#endif + rstc = __reset_control_get(dev, NULL, 0, 0, optional, true); if (IS_ERR(rstc)) return PTR_ERR(rstc); -- cgit v1.2.3 From 9fe7dd4e94f0bb76ec96224e8813695bdf019fa5 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 6 Apr 2022 08:16:47 +0100 Subject: reset: Kconfig: Make RESET_RZG2L_USBPHY_CTRL depend on ARCH_RZG2L The USBPHY Control block is identical on Renesas RZ/G2L and RZ/V2L SoC's, so instead of adding dependency for each SoC's add dependency on ARCH_RZG2L. The ARCH_RZG2L config option is already selected by ARCH_R9A07G044 and ARCH_R9A07G054. Signed-off-by: Lad Prabhakar Signed-off-by: Philipp Zabel Link: https://lore.kernel.org/r/20220406071647.14037-1-prabhakar.mahadev-lad.rj@bp.renesas.com --- drivers/reset/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index b496028b6bfa..e0fc80e041ea 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -183,7 +183,7 @@ config RESET_RASPBERRYPI config RESET_RZG2L_USBPHY_CTRL tristate "Renesas RZ/G2L USBPHY control driver" - depends on ARCH_R9A07G044 || COMPILE_TEST + depends on ARCH_RZG2L || COMPILE_TEST help Support for USBPHY Control found on RZ/G2L family. It mainly controls reset and power down of the USB/PHY. -- cgit v1.2.3 From edb9bd8f857e60be871e4e1cf0f96bfd77f1e604 Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Tue, 5 Apr 2022 16:56:18 +0900 Subject: dt-bindings: reset: uniphier-glue: Clean up clocks, resets, and their names using compatible string Instead of "oneOf:" choices, use "allOf:" and "if:" to define clocks, resets, and their names that can be taken by the compatible string. The order of clock-names and reset-names doesn't change here. Signed-off-by: Kunihiko Hayashi Reviewed-by: Rob Herring Signed-off-by: Philipp Zabel Link: https://lore.kernel.org/r/1649145378-30358-1-git-send-email-hayashi.kunihiko@socionext.com --- .../reset/socionext,uniphier-glue-reset.yaml | 52 ++++++++++++++++------ 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/Documentation/devicetree/bindings/reset/socionext,uniphier-glue-reset.yaml b/Documentation/devicetree/bindings/reset/socionext,uniphier-glue-reset.yaml index bfbd3e9b4186..0a2c13e1e230 100644 --- a/Documentation/devicetree/bindings/reset/socionext,uniphier-glue-reset.yaml +++ b/Documentation/devicetree/bindings/reset/socionext,uniphier-glue-reset.yaml @@ -38,25 +38,49 @@ properties: minItems: 1 maxItems: 2 - clock-names: - oneOf: - - items: # for Pro4, Pro5 - - const: gio - - const: link - - items: # for others - - const: link + clock-names: true resets: minItems: 1 maxItems: 2 - reset-names: - oneOf: - - items: # for Pro4, Pro5 - - const: gio - - const: link - - items: # for others - - const: link + reset-names: true + +allOf: + - if: + properties: + compatible: + contains: + enum: + - socionext,uniphier-pro4-usb3-reset + - socionext,uniphier-pro5-usb3-reset + - socionext,uniphier-pro4-ahci-reset + then: + properties: + clocks: + minItems: 2 + maxItems: 2 + clock-names: + items: + - const: gio + - const: link + resets: + minItems: 2 + maxItems: 2 + reset-names: + items: + - const: gio + - const: link + else: + properties: + clocks: + maxItems: 1 + clock-names: + const: link + resets: + maxItems: 1 + reset-names: + const: link additionalProperties: false -- cgit v1.2.3 From 5d814b2c3326c939c66bd52e472aabb9254bb2ce Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 7 Apr 2022 17:43:25 +0200 Subject: dt-bindings: reset: amlogic,meson-axg-audio-arb: Convert to yaml Convert the device tree bindings for the Amlogic audio memory arbiter controller to YAML schema to allow participating in DT validation. Signed-off-by: Philipp Zabel Cc: Kevin Hilman Cc: Jerome Brunet Cc: Martin Blumenstingl Reviewed-by: Martin Blumenstingl Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220407154338.4190674-1-p.zabel@pengutronix.de --- .../bindings/reset/amlogic,meson-axg-audio-arb.txt | 22 --------- .../reset/amlogic,meson-axg-audio-arb.yaml | 56 ++++++++++++++++++++++ 2 files changed, 56 insertions(+), 22 deletions(-) delete mode 100644 Documentation/devicetree/bindings/reset/amlogic,meson-axg-audio-arb.txt create mode 100644 Documentation/devicetree/bindings/reset/amlogic,meson-axg-audio-arb.yaml diff --git a/Documentation/devicetree/bindings/reset/amlogic,meson-axg-audio-arb.txt b/Documentation/devicetree/bindings/reset/amlogic,meson-axg-audio-arb.txt deleted file mode 100644 index 43e580ef64ba..000000000000 --- a/Documentation/devicetree/bindings/reset/amlogic,meson-axg-audio-arb.txt +++ /dev/null @@ -1,22 +0,0 @@ -* Amlogic audio memory arbiter controller - -The Amlogic Audio ARB is a simple device which enables or -disables the access of Audio FIFOs to DDR on AXG based SoC. - -Required properties: -- compatible: 'amlogic,meson-axg-audio-arb' or - 'amlogic,meson-sm1-audio-arb' -- reg: physical base address of the controller and length of memory - mapped region. -- clocks: phandle to the fifo peripheral clock provided by the audio - clock controller. -- #reset-cells: must be 1. - -Example on the A113 SoC: - -arb: reset-controller@280 { - compatible = "amlogic,meson-axg-audio-arb"; - reg = <0x0 0x280 0x0 0x4>; - #reset-cells = <1>; - clocks = <&clkc_audio AUD_CLKID_DDR_ARB>; -}; diff --git a/Documentation/devicetree/bindings/reset/amlogic,meson-axg-audio-arb.yaml b/Documentation/devicetree/bindings/reset/amlogic,meson-axg-audio-arb.yaml new file mode 100644 index 000000000000..704a502adc5d --- /dev/null +++ b/Documentation/devicetree/bindings/reset/amlogic,meson-axg-audio-arb.yaml @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2019 BayLibre, SAS +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/reset/amlogic,meson-axg-audio-arb.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Amlogic audio memory arbiter controller + +maintainers: + - Jerome Brunet + +description: The Amlogic Audio ARB is a simple device which enables or disables + the access of Audio FIFOs to DDR on AXG based SoC. + +properties: + compatible: + enum: + - amlogic,meson-axg-audio-arb + - amlogic,meson-sm1-audio-arb + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + description: | + phandle to the fifo peripheral clock provided by the audio clock + controller. + + "#reset-cells": + const: 1 + +required: + - compatible + - reg + - clocks + - "#reset-cells" + +additionalProperties: false + +examples: + - | + // on the A113 SoC: + #include + bus { + #address-cells = <2>; + #size-cells = <2>; + + arb: reset-controller@280 { + compatible = "amlogic,meson-axg-audio-arb"; + reg = <0x0 0x280 0x0 0x4>; + #reset-cells = <1>; + clocks = <&clkc_audio AUD_CLKID_DDR_ARB>; + }; + }; -- cgit v1.2.3 From 1b4efb23092b7503304c79f2307ae618c6c62d95 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 7 Apr 2022 17:43:26 +0200 Subject: dt-bindings: reset: ath79: Convert to yaml Convert the device tree bindings for the Qualcomm Atheros AR7xxx/AR9XXX reset controller to YAML schema to allow participating in DT validation. Signed-off-by: Philipp Zabel Cc: Alban Bedel Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220407154338.4190674-2-p.zabel@pengutronix.de --- .../devicetree/bindings/reset/ath79-reset.txt | 20 ----------- .../bindings/reset/qca,ar7100-reset.yaml | 40 ++++++++++++++++++++++ 2 files changed, 40 insertions(+), 20 deletions(-) delete mode 100644 Documentation/devicetree/bindings/reset/ath79-reset.txt create mode 100644 Documentation/devicetree/bindings/reset/qca,ar7100-reset.yaml diff --git a/Documentation/devicetree/bindings/reset/ath79-reset.txt b/Documentation/devicetree/bindings/reset/ath79-reset.txt deleted file mode 100644 index 4c56330bf398..000000000000 --- a/Documentation/devicetree/bindings/reset/ath79-reset.txt +++ /dev/null @@ -1,20 +0,0 @@ -Binding for Qualcomm Atheros AR7xxx/AR9XXX reset controller - -Please also refer to reset.txt in this directory for common reset -controller binding usage. - -Required Properties: -- compatible: has to be "qca,-reset", "qca,ar7100-reset" - as fallback -- reg: Base address and size of the controllers memory area -- #reset-cells : Specifies the number of cells needed to encode reset - line, should be 1 - -Example: - - reset-controller@1806001c { - compatible = "qca,ar9132-reset", "qca,ar7100-reset"; - reg = <0x1806001c 0x4>; - - #reset-cells = <1>; - }; diff --git a/Documentation/devicetree/bindings/reset/qca,ar7100-reset.yaml b/Documentation/devicetree/bindings/reset/qca,ar7100-reset.yaml new file mode 100644 index 000000000000..9be60e55cd71 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/qca,ar7100-reset.yaml @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2015 Alban Bedel +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/reset/qca,ar7100-reset.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Qualcomm Atheros AR7xxx/AR9XXX reset controller + +maintainers: + - Alban Bedel + +properties: + compatible: + items: + - enum: + - qca,ar9132-reset + - qca,ar9331-reset + - const: qca,ar7100-reset + + reg: + maxItems: 1 + + "#reset-cells": + const: 1 + +required: + - compatible + - reg + - "#reset-cells" + +additionalProperties: false + +examples: + - | + reset-controller@1806001c { + compatible = "qca,ar9132-reset", "qca,ar7100-reset"; + reg = <0x1806001c 0x4>; + #reset-cells = <1>; + }; -- cgit v1.2.3 From 1b6dc00703a2b8c312581ee0678f08468556ed6a Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 7 Apr 2022 17:43:27 +0200 Subject: dt-bindings: reset: berlin: Convert to yaml Convert the device tree bindings for the Marvell Berlin reset controller to YAML schema to allow participating in DT validation. Signed-off-by: Philipp Zabel Cc: Antoine Tenart Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220407154338.4190674-3-p.zabel@pengutronix.de --- .../devicetree/bindings/reset/berlin,reset.txt | 23 ------------- .../bindings/reset/marvell,berlin2-reset.yaml | 38 ++++++++++++++++++++++ 2 files changed, 38 insertions(+), 23 deletions(-) delete mode 100644 Documentation/devicetree/bindings/reset/berlin,reset.txt create mode 100644 Documentation/devicetree/bindings/reset/marvell,berlin2-reset.yaml diff --git a/Documentation/devicetree/bindings/reset/berlin,reset.txt b/Documentation/devicetree/bindings/reset/berlin,reset.txt deleted file mode 100644 index 514fee098b4b..000000000000 --- a/Documentation/devicetree/bindings/reset/berlin,reset.txt +++ /dev/null @@ -1,23 +0,0 @@ -Marvell Berlin reset controller -=============================== - -Please also refer to reset.txt in this directory for common reset -controller binding usage. - -The reset controller node must be a sub-node of the chip controller -node on Berlin SoCs. - -Required properties: -- compatible: should be "marvell,berlin2-reset" -- #reset-cells: must be set to 2 - -Example: - -chip_rst: reset { - compatible = "marvell,berlin2-reset"; - #reset-cells = <2>; -}; - -&usb_phy0 { - resets = <&chip_rst 0x104 12>; -}; diff --git a/Documentation/devicetree/bindings/reset/marvell,berlin2-reset.yaml b/Documentation/devicetree/bindings/reset/marvell,berlin2-reset.yaml new file mode 100644 index 000000000000..d71d0f0a13ee --- /dev/null +++ b/Documentation/devicetree/bindings/reset/marvell,berlin2-reset.yaml @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2015 Antoine Tenart +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/reset/marvell,berlin2-reset.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Marvell Berlin reset controller + +maintainers: + - Antoine Tenart + +description: The reset controller node must be a sub-node of the chip + controller node on Berlin SoCs. + +properties: + compatible: + const: marvell,berlin2-reset + + "#reset-cells": + const: 2 + +required: + - compatible + - "#reset-cells" + +additionalProperties: false + +examples: + - | + chip: chip-control@ea0000 { + reg = <0xea0000 0x400>; + + chip_rst: reset { + compatible = "marvell,berlin2-reset"; + #reset-cells = <2>; + }; + }; -- cgit v1.2.3 From e2ac01c199b4a9f2d12b55e1c614eba1f03e0eec Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 7 Apr 2022 17:43:28 +0200 Subject: dt-bindings: reset: bitmain,bm1880-reset: Convert to yaml Convert the device tree bindings for the Bitmain BM1880 reset controller to YAML schema to allow participating in DT validation. Signed-off-by: Philipp Zabel Cc: Manivannan Sadhasivam Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220407154338.4190674-4-p.zabel@pengutronix.de --- .../bindings/reset/bitmain,bm1880-reset.txt | 18 ----------- .../bindings/reset/bitmain,bm1880-reset.yaml | 36 ++++++++++++++++++++++ 2 files changed, 36 insertions(+), 18 deletions(-) delete mode 100644 Documentation/devicetree/bindings/reset/bitmain,bm1880-reset.txt create mode 100644 Documentation/devicetree/bindings/reset/bitmain,bm1880-reset.yaml diff --git a/Documentation/devicetree/bindings/reset/bitmain,bm1880-reset.txt b/Documentation/devicetree/bindings/reset/bitmain,bm1880-reset.txt deleted file mode 100644 index a6f8455ae6c4..000000000000 --- a/Documentation/devicetree/bindings/reset/bitmain,bm1880-reset.txt +++ /dev/null @@ -1,18 +0,0 @@ -Bitmain BM1880 SoC Reset Controller -=================================== - -Please also refer to reset.txt in this directory for common reset -controller binding usage. - -Required properties: -- compatible: Should be "bitmain,bm1880-reset" -- reg: Offset and length of reset controller space in SCTRL. -- #reset-cells: Must be 1. - -Example: - - rst: reset-controller@c00 { - compatible = "bitmain,bm1880-reset"; - reg = <0xc00 0x8>; - #reset-cells = <1>; - }; diff --git a/Documentation/devicetree/bindings/reset/bitmain,bm1880-reset.yaml b/Documentation/devicetree/bindings/reset/bitmain,bm1880-reset.yaml new file mode 100644 index 000000000000..f0aca744388c --- /dev/null +++ b/Documentation/devicetree/bindings/reset/bitmain,bm1880-reset.yaml @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2019 Manivannan Sadhasivam +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/reset/bitmain,bm1880-reset.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Bitmain BM1880 SoC Reset Controller + +maintainers: + - Manivannan Sadhasivam + +properties: + compatible: + const: bitmain,bm1880-reset + + reg: + maxItems: 1 + + "#reset-cells": + const: 1 + +required: + - compatible + - reg + - "#reset-cells" + +additionalProperties: false + +examples: + - | + rst: reset-controller@c00 { + compatible = "bitmain,bm1880-reset"; + reg = <0xc00 0x8>; + #reset-cells = <1>; + }; -- cgit v1.2.3 From 7809a619d41e6c1c43fdf15f0531681b36ad31c0 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 7 Apr 2022 17:43:29 +0200 Subject: dt-bindings: reset: lantiq,reset: Convert to yaml Convert the device tree bindings for the Lantiq XWAY SoC RCU reset controller to YAML schema to allow participating in DT validation. Signed-off-by: Philipp Zabel Cc: Martin Blumenstingl Reviewed-by: Martin Blumenstingl Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220407154338.4190674-5-p.zabel@pengutronix.de --- .../devicetree/bindings/reset/lantiq,reset.txt | 30 ------------- .../devicetree/bindings/reset/lantiq,reset.yaml | 49 ++++++++++++++++++++++ 2 files changed, 49 insertions(+), 30 deletions(-) delete mode 100644 Documentation/devicetree/bindings/reset/lantiq,reset.txt create mode 100644 Documentation/devicetree/bindings/reset/lantiq,reset.yaml diff --git a/Documentation/devicetree/bindings/reset/lantiq,reset.txt b/Documentation/devicetree/bindings/reset/lantiq,reset.txt deleted file mode 100644 index c6aef36b7d15..000000000000 --- a/Documentation/devicetree/bindings/reset/lantiq,reset.txt +++ /dev/null @@ -1,30 +0,0 @@ -Lantiq XWAY SoC RCU reset controller binding -============================================ - -This binding describes a reset-controller found on the RCU module on Lantiq -XWAY SoCs. - -This node has to be a sub node of the Lantiq RCU block. - -------------------------------------------------------------------------------- -Required properties: -- compatible : Should be one of - "lantiq,danube-reset" - "lantiq,xrx200-reset" -- reg : Defines the following sets of registers in the parent - syscon device - - Offset of the reset set register - - Offset of the reset status register -- #reset-cells : Specifies the number of cells needed to encode the - reset line, should be 2. - The first cell takes the reset set bit and the - second cell takes the status bit. - -------------------------------------------------------------------------------- -Example for the reset-controllers on the xRX200 SoCs: - reset0: reset-controller@10 { - compatible = "lantiq,xrx200-reset"; - reg <0x10 0x04>, <0x14 0x04>; - - #reset-cells = <2>; - }; diff --git a/Documentation/devicetree/bindings/reset/lantiq,reset.yaml b/Documentation/devicetree/bindings/reset/lantiq,reset.yaml new file mode 100644 index 000000000000..15d65a5dd631 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/lantiq,reset.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/reset/lantiq,reset.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Lantiq XWAY SoC RCU reset controller + +maintainers: + - Martin Blumenstingl + +description: | + This binding describes a reset-controller found on the RCU module on Lantiq + XWAY SoCs. This node has to be a sub node of the Lantiq RCU block. + +properties: + compatible: + enum: + - lantiq,danube-reset + - lantiq,xrx200-reset + + reg: + description: | + Defines the following sets of registers in the parent syscon device + Offset of the reset set register + Offset of the reset status register + maxItems: 2 + + '#reset-cells': + description: | + The first cell takes the reset set bit and the second cell takes the + status bit. + const: 2 + +required: + - compatible + - reg + - '#reset-cells' + +additionalProperties: false + +examples: + - | + // On the xRX200 SoCs: + reset0: reset-controller@10 { + compatible = "lantiq,xrx200-reset"; + reg = <0x10 0x04>, <0x14 0x04>; + #reset-cells = <2>; + }; -- cgit v1.2.3 From 5c9360ea998222abf1fba351fabbaba4a4c599fe Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 7 Apr 2022 17:43:30 +0200 Subject: dt-bindings: reset: nuvoton,npcm-reset: Convert to yaml Convert the device tree bindings for the Nuvoton NPCM reset controller to YAML schema to allow participating in DT validation. Signed-off-by: Philipp Zabel Cc: Avi Fishman Cc: Tomer Maimon Cc: Tali Perry Cc: Patrick Venture Cc: Nancy Yuen Cc: Benjamin Fair Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220407154338.4190674-6-p.zabel@pengutronix.de --- .../bindings/reset/nuvoton,npcm-reset.txt | 32 -------------- .../bindings/reset/nuvoton,npcm750-reset.yaml | 50 ++++++++++++++++++++++ 2 files changed, 50 insertions(+), 32 deletions(-) delete mode 100644 Documentation/devicetree/bindings/reset/nuvoton,npcm-reset.txt create mode 100644 Documentation/devicetree/bindings/reset/nuvoton,npcm750-reset.yaml diff --git a/Documentation/devicetree/bindings/reset/nuvoton,npcm-reset.txt b/Documentation/devicetree/bindings/reset/nuvoton,npcm-reset.txt deleted file mode 100644 index 17b7a6a43a29..000000000000 --- a/Documentation/devicetree/bindings/reset/nuvoton,npcm-reset.txt +++ /dev/null @@ -1,32 +0,0 @@ -Nuvoton NPCM Reset controller - -Required properties: -- compatible : "nuvoton,npcm750-reset" for NPCM7XX BMC -- reg : specifies physical base address and size of the register. -- #reset-cells: must be set to 2 - -Optional property: -- nuvoton,sw-reset-number - Contains the software reset number to restart the SoC. - NPCM7xx contain four software reset that represent numbers 1 to 4. - - If 'nuvoton,sw-reset-number' is not specified software reset is disabled. - -Example: - rstc: rstc@f0801000 { - compatible = "nuvoton,npcm750-reset"; - reg = <0xf0801000 0x70>; - #reset-cells = <2>; - nuvoton,sw-reset-number = <2>; - }; - -Specifying reset lines connected to IP NPCM7XX modules -====================================================== -example: - - spi0: spi@..... { - ... - resets = <&rstc NPCM7XX_RESET_IPSRST2 NPCM7XX_RESET_PSPI1>; - ... - }; - -The index could be found in . diff --git a/Documentation/devicetree/bindings/reset/nuvoton,npcm750-reset.yaml b/Documentation/devicetree/bindings/reset/nuvoton,npcm750-reset.yaml new file mode 100644 index 000000000000..fa5e4ea6400e --- /dev/null +++ b/Documentation/devicetree/bindings/reset/nuvoton,npcm750-reset.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/reset/nuvoton,npcm750-reset.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Nuvoton NPCM Reset controller + +maintainers: + - Tomer Maimon + +properties: + compatible: + const: nuvoton,npcm750-reset + + reg: + maxItems: 1 + + '#reset-cells': + const: 2 + + nuvoton,sw-reset-number: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 1 + maximum: 4 + description: | + Contains the software reset number to restart the SoC. + If not specified, software reset is disabled. + +required: + - compatible + - reg + - '#reset-cells' + +additionalProperties: false + +examples: + - | + #include + rstc: rstc@f0801000 { + compatible = "nuvoton,npcm750-reset"; + reg = <0xf0801000 0x70>; + #reset-cells = <2>; + nuvoton,sw-reset-number = <2>; + }; + + // Specifying reset lines connected to IP NPCM7XX modules + spi0: spi { + resets = <&rstc NPCM7XX_RESET_IPSRST2 NPCM7XX_RESET_PSPI1>; + }; -- cgit v1.2.3 From 820f722c05dd08706be1fed2787cd91ed38642e2 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 7 Apr 2022 17:43:32 +0200 Subject: dt-bindings: reset: snps,axs10x-reset: Convert to yaml Convert the device tree bindings for the AXS10x reset controller to YAML schema to allow participating in DT validation. Signed-off-by: Philipp Zabel Cc: Eugeniy Paltsev Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220407154338.4190674-8-p.zabel@pengutronix.de --- .../bindings/reset/snps,axs10x-reset.txt | 33 --------------- .../bindings/reset/snps,axs10x-reset.yaml | 48 ++++++++++++++++++++++ 2 files changed, 48 insertions(+), 33 deletions(-) delete mode 100644 Documentation/devicetree/bindings/reset/snps,axs10x-reset.txt create mode 100644 Documentation/devicetree/bindings/reset/snps,axs10x-reset.yaml diff --git a/Documentation/devicetree/bindings/reset/snps,axs10x-reset.txt b/Documentation/devicetree/bindings/reset/snps,axs10x-reset.txt deleted file mode 100644 index 32d8435a41df..000000000000 --- a/Documentation/devicetree/bindings/reset/snps,axs10x-reset.txt +++ /dev/null @@ -1,33 +0,0 @@ -Binding for the AXS10x reset controller - -This binding describes the ARC AXS10x boards custom IP-block which allows -to control reset signals of selected peripherals. For example DW GMAC, etc... -This block is controlled via memory-mapped register (AKA CREG) which -represents up-to 32 reset lines. - -As of today only the following lines are used: - - DW GMAC - line 5 - -This binding uses the common reset binding[1]. - -[1] Documentation/devicetree/bindings/reset/reset.txt - -Required properties: -- compatible: should be "snps,axs10x-reset". -- reg: should always contain pair address - length: for creg reset - bits register. -- #reset-cells: from common reset binding; Should always be set to 1. - -Example: - reset: reset-controller@11220 { - compatible = "snps,axs10x-reset"; - #reset-cells = <1>; - reg = <0x11220 0x4>; - }; - -Specifying reset lines connected to IP modules: - ethernet@.... { - .... - resets = <&reset 5>; - .... - }; diff --git a/Documentation/devicetree/bindings/reset/snps,axs10x-reset.yaml b/Documentation/devicetree/bindings/reset/snps,axs10x-reset.yaml new file mode 100644 index 000000000000..a75db3d405af --- /dev/null +++ b/Documentation/devicetree/bindings/reset/snps,axs10x-reset.yaml @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/reset/snps,axs10x-reset.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AXS10x reset controller + +maintainers: + - Eugeniy Paltsev + +description: | + This binding describes the ARC AXS10x boards custom IP-block which allows + to control reset signals of selected peripherals. For example DW GMAC, etc... + This block is controlled via memory-mapped register (AKA CREG) which + represents up-to 32 reset lines. + As of today only the following lines are used: + - DW GMAC - line 5 + +properties: + compatible: + const: snps,axs10x-reset + + reg: + maxItems: 1 + + '#reset-cells': + const: 1 + +required: + - compatible + - reg + - '#reset-cells' + +additionalProperties: false + +examples: + - | + reset: reset-controller@11220 { + compatible = "snps,axs10x-reset"; + #reset-cells = <1>; + reg = <0x11220 0x4>; + }; + + // Specifying reset lines connected to IP modules: + ethernet { + resets = <&reset 5>; + }; -- cgit v1.2.3 From e54bbcb02aeffda00a31c18372411f88b1615fa2 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 7 Apr 2022 17:43:35 +0200 Subject: dt-bindings: reset: socfpga: Convert to yaml Convert the device tree bindings for the Altera SOCFPGA reset manager to YAML schema to allow participating in DT validation. Signed-off-by: Philipp Zabel Cc: Dinh Nguyen Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220407154338.4190674-11-p.zabel@pengutronix.de --- .../devicetree/bindings/reset/altr,rst-mgr.yaml | 47 ++++++++++++++++++++++ .../devicetree/bindings/reset/socfpga-reset.txt | 16 -------- 2 files changed, 47 insertions(+), 16 deletions(-) create mode 100644 Documentation/devicetree/bindings/reset/altr,rst-mgr.yaml delete mode 100644 Documentation/devicetree/bindings/reset/socfpga-reset.txt diff --git a/Documentation/devicetree/bindings/reset/altr,rst-mgr.yaml b/Documentation/devicetree/bindings/reset/altr,rst-mgr.yaml new file mode 100644 index 000000000000..4379cec6b35a --- /dev/null +++ b/Documentation/devicetree/bindings/reset/altr,rst-mgr.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/reset/altr,rst-mgr.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Altera SOCFPGA Reset Manager + +maintainers: + - Dinh Nguyen + +properties: + compatible: + oneOf: + - description: Cyclone5/Arria5/Arria10 + const: altr,rst-mgr + - description: Stratix10 ARM64 SoC + items: + - const: altr,stratix10-rst-mgr + - const: altr,rst-mgr + + reg: + maxItems: 1 + + altr,modrst-offset: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Offset of the first modrst register + + '#reset-cells': + const: 1 + +required: + - compatible + - reg + - altr,modrst-offset + - '#reset-cells' + +additionalProperties: false + +examples: + - | + rstmgr@ffd05000 { + compatible = "altr,rst-mgr"; + reg = <0xffd05000 0x1000>; + altr,modrst-offset = <0x10>; + #reset-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/reset/socfpga-reset.txt b/Documentation/devicetree/bindings/reset/socfpga-reset.txt deleted file mode 100644 index 38fe34fd8b8a..000000000000 --- a/Documentation/devicetree/bindings/reset/socfpga-reset.txt +++ /dev/null @@ -1,16 +0,0 @@ -Altera SOCFPGA Reset Manager - -Required properties: -- compatible : "altr,rst-mgr" for (Cyclone5/Arria5/Arria10) - "altr,stratix10-rst-mgr","altr,rst-mgr" for Stratix10 ARM64 SoC -- reg : Should contain 1 register ranges(address and length) -- altr,modrst-offset : Should contain the offset of the first modrst register. -- #reset-cells: 1 - -Example: - rstmgr@ffd05000 { - #reset-cells = <1>; - compatible = "altr,rst-mgr"; - reg = <0xffd05000 0x1000>; - altr,modrst-offset = <0x10>; - }; -- cgit v1.2.3 From b550b8a451380965ecf2d0ae1e4b97f8047a8e9a Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 7 Apr 2022 17:43:36 +0200 Subject: dt-bindings: reset: st,sti-picophyreset: Convert to yaml Convert the device tree bindings for the STMicroelectronics STi SoftReset controller to YAML schema to allow participating in DT validation. Signed-off-by: Philipp Zabel Cc: Peter Griffin Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220407154338.4190674-12-p.zabel@pengutronix.de --- .../bindings/reset/st,sti-picophyreset.txt | 42 ------------------- .../bindings/reset/st,stih407-picophyreset.yaml | 47 ++++++++++++++++++++++ 2 files changed, 47 insertions(+), 42 deletions(-) delete mode 100644 Documentation/devicetree/bindings/reset/st,sti-picophyreset.txt create mode 100644 Documentation/devicetree/bindings/reset/st,stih407-picophyreset.yaml diff --git a/Documentation/devicetree/bindings/reset/st,sti-picophyreset.txt b/Documentation/devicetree/bindings/reset/st,sti-picophyreset.txt deleted file mode 100644 index 9ca27761f811..000000000000 --- a/Documentation/devicetree/bindings/reset/st,sti-picophyreset.txt +++ /dev/null @@ -1,42 +0,0 @@ -STMicroelectronics STi family Sysconfig Picophy SoftReset Controller -============================================================================= - -This binding describes a reset controller device that is used to enable and -disable on-chip PicoPHY USB2 phy(s) using "softreset" control bits found in -the STi family SoC system configuration registers. - -The actual action taken when softreset is asserted is hardware dependent. -However, when asserted it may not be possible to access the hardware's -registers and after an assert/deassert sequence the hardware's previous state -may no longer be valid. - -Please refer to Documentation/devicetree/bindings/reset/reset.txt -for common reset controller binding usage. - -Required properties: -- compatible: Should be "st,stih407-picophyreset" -- #reset-cells: 1, see below - -Example: - - picophyreset: picophyreset-controller { - compatible = "st,stih407-picophyreset"; - #reset-cells = <1>; - }; - -Specifying picophyreset control of devices -======================================= - -Device nodes should specify the reset channel required in their "resets" -property, containing a phandle to the picophyreset device node and an -index specifying which channel to use, as described in -Documentation/devicetree/bindings/reset/reset.txt. - -Example: - - usb2_picophy0: usbpicophy@0 { - resets = <&picophyreset STIH407_PICOPHY0_RESET>; - }; - -Macro definitions for the supported reset channels can be found in: -include/dt-bindings/reset/stih407-resets.h diff --git a/Documentation/devicetree/bindings/reset/st,stih407-picophyreset.yaml b/Documentation/devicetree/bindings/reset/st,stih407-picophyreset.yaml new file mode 100644 index 000000000000..329ae4ae1a10 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/st,stih407-picophyreset.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/reset/st,stih407-picophyreset.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: STMicroelectronics STi family Sysconfig Picophy SoftReset Controller + +maintainers: + - Peter Griffin + +description: | + This binding describes a reset controller device that is used to enable and + disable on-chip PicoPHY USB2 phy(s) using "softreset" control bits found in + the STi family SoC system configuration registers. + + The actual action taken when softreset is asserted is hardware dependent. + However, when asserted it may not be possible to access the hardware's + registers and after an assert/deassert sequence the hardware's previous state + may no longer be valid. + +properties: + compatible: + const: st,stih407-picophyreset + + '#reset-cells': + const: 1 + +required: + - compatible + - '#reset-cells' + +additionalProperties: false + +examples: + - | + #include + + picophyreset: picophyreset-controller { + compatible = "st,stih407-picophyreset"; + #reset-cells = <1>; + }; + + // Specifying picophyreset control of devices + usb2_picophy0: usbpicophy { + resets = <&picophyreset STIH407_PICOPHY0_RESET>; + }; -- cgit v1.2.3 From 2ca065dc9468fa1c60ada1e8cb9c1aa866367ec7 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 7 Apr 2022 17:43:37 +0200 Subject: dt-bindings: reset: st,sti-powerdown: Convert to yaml Convert the device tree bindings for the STMicroelectronics STi Peripheral Powerdown reset controller to YAML schema to allow participating in DT validation. Signed-off-by: Philipp Zabel Cc: Srinivas Kandagatla Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220407154338.4190674-13-p.zabel@pengutronix.de --- .../devicetree/bindings/reset/st,sti-powerdown.txt | 45 -------------------- .../bindings/reset/st,stih407-powerdown.yaml | 49 ++++++++++++++++++++++ 2 files changed, 49 insertions(+), 45 deletions(-) delete mode 100644 Documentation/devicetree/bindings/reset/st,sti-powerdown.txt create mode 100644 Documentation/devicetree/bindings/reset/st,stih407-powerdown.yaml diff --git a/Documentation/devicetree/bindings/reset/st,sti-powerdown.txt b/Documentation/devicetree/bindings/reset/st,sti-powerdown.txt deleted file mode 100644 index 92527138bc93..000000000000 --- a/Documentation/devicetree/bindings/reset/st,sti-powerdown.txt +++ /dev/null @@ -1,45 +0,0 @@ -STMicroelectronics STi family Sysconfig Peripheral Powerdown Reset Controller -============================================================================= - -This binding describes a reset controller device that is used to enable and -disable on-chip peripheral controllers such as USB and SATA, using -"powerdown" control bits found in the STi family SoC system configuration -registers. These have been grouped together into a single reset controller -device for convenience. - -The actual action taken when powerdown is asserted is hardware dependent. -However, when asserted it may not be possible to access the hardware's -registers and after an assert/deassert sequence the hardware's previous state -may no longer be valid. - -Please refer to reset.txt in this directory for common reset -controller binding usage. - -Required properties: -- compatible: Should be "st,stih407-powerdown" -- #reset-cells: 1, see below - -example: - - powerdown: powerdown-controller { - compatible = "st,stih407-powerdown"; - #reset-cells = <1>; - }; - - -Specifying powerdown control of devices -======================================= - -Device nodes should specify the reset channel required in their "resets" -property, containing a phandle to the powerdown device node and an -index specifying which channel to use, as described in reset.txt - -example: - - st_dwc3: dwc3@8f94000 { - resets = <&powerdown STIH407_USB3_POWERDOWN>, - }; - -Macro definitions for the supported reset channels can be found in: - -include/dt-bindings/reset/stih407-resets.h diff --git a/Documentation/devicetree/bindings/reset/st,stih407-powerdown.yaml b/Documentation/devicetree/bindings/reset/st,stih407-powerdown.yaml new file mode 100644 index 000000000000..d3790e602659 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/st,stih407-powerdown.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/reset/st,stih407-powerdown.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: STMicroelectronics STi family Sysconfig Peripheral Powerdown Reset Controller + +maintainers: + - Srinivas Kandagatla + +description: | + This binding describes a reset controller device that is used to enable and + disable on-chip peripheral controllers such as USB and SATA, using + "powerdown" control bits found in the STi family SoC system configuration + registers. These have been grouped together into a single reset controller + device for convenience. + + The actual action taken when powerdown is asserted is hardware dependent. + However, when asserted it may not be possible to access the hardware's + registers and after an assert/deassert sequence the hardware's previous state + may no longer be valid. + +properties: + compatible: + const: st,stih407-powerdown + + '#reset-cells': + const: 1 + +required: + - compatible + - '#reset-cells' + +additionalProperties: false + +examples: + - | + #include + + powerdown: powerdown-controller { + compatible = "st,stih407-powerdown"; + #reset-cells = <1>; + }; + + // Specifying powerdown control of devices: + st_dwc3: dwc3 { + resets = <&powerdown STIH407_USB3_POWERDOWN>; + }; -- cgit v1.2.3 From 9767d1be1529c40469d1bbdc457953f88fa1dca7 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 4 May 2022 11:08:14 +0200 Subject: soc/tegra: pmc: Select REGMAP Commit 9d5e7c3e36eb ("soc/tegra: pmc: Provide USB sleepwalk register map") introduced a dependency on the regmap infrastructure, so select the corresponding Kconfig entry. This avoids failures in random builds that may otherwise end up having SOC_TEGRA_PMC enabled but not REGMAP. Signed-off-by: Thierry Reding --- drivers/soc/tegra/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig index 8b53ed1cc67e..5725c8ef0406 100644 --- a/drivers/soc/tegra/Kconfig +++ b/drivers/soc/tegra/Kconfig @@ -146,6 +146,7 @@ config SOC_TEGRA_PMC select GENERIC_PINCONF select PM_OPP select PM_GENERIC_DOMAINS + select REGMAP config SOC_TEGRA_POWERGATE_BPMP def_bool y -- cgit v1.2.3 From d0d8cb7b94b8d23e9721cbbec5c7b00c04ae6514 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Mon, 2 May 2022 14:54:05 -0700 Subject: dt-bindings: arm: msm: Add sc8180x and sc8280xp LLCC compatibles Add compatibles for the SC8180X and SC8280XP platforms to the existing LLCC binding. Acked-by: Krzysztof Kozlowski Signed-off-by: Bjorn Andersson Acked-by: Rob Herring Link: https://lore.kernel.org/r/20220502215406.612967-2-bjorn.andersson@linaro.org --- Documentation/devicetree/bindings/arm/msm/qcom,llcc.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,llcc.yaml b/Documentation/devicetree/bindings/arm/msm/qcom,llcc.yaml index 30fcbe2ad8a3..5ea506412b4e 100644 --- a/Documentation/devicetree/bindings/arm/msm/qcom,llcc.yaml +++ b/Documentation/devicetree/bindings/arm/msm/qcom,llcc.yaml @@ -23,6 +23,8 @@ properties: enum: - qcom,sc7180-llcc - qcom,sc7280-llcc + - qcom,sc8180x-llcc + - qcom,sc8280xp-llcc - qcom,sdm845-llcc - qcom,sm6350-llcc - qcom,sm8150-llcc -- cgit v1.2.3 From ec69dfbdc426f22a9557e5c5408d7902fe0e0144 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Mon, 2 May 2022 14:54:06 -0700 Subject: soc: qcom: llcc: Add sc8180x and sc8280xp configurations Add LLCC configuration data for the SC8180X and SC8280XP platforms, based on the downstream tables. Signed-off-by: Bjorn Andersson Reviewed-by: Sai Prakash Ranjan Link: https://lore.kernel.org/r/20220502215406.612967-3-bjorn.andersson@linaro.org --- drivers/soc/qcom/llcc-qcom.c | 60 ++++++++++++++++++++++++++++++++++++++ include/linux/soc/qcom/llcc-qcom.h | 2 ++ 2 files changed, 62 insertions(+) diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index 85ba8209b182..4b143cf7b4ce 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -130,6 +130,50 @@ static const struct llcc_slice_config sc7280_data[] = { { LLCC_MODPE, 29, 64, 1, 1, 0x3f, 0x0, 0, 0, 0, 1, 0, 0}, }; +static const struct llcc_slice_config sc8180x_data[] = { + { LLCC_CPUSS, 1, 6144, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 1 }, + { LLCC_VIDSC0, 2, 512, 2, 1, 0xfff, 0x0, 0, 0, 0, 1, 0 }, + { LLCC_VIDSC1, 3, 512, 2, 1, 0xfff, 0x0, 0, 0, 0, 1, 0 }, + { LLCC_AUDIO, 6, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0 }, + { LLCC_MDMHPGRW, 7, 3072, 1, 1, 0x3ff, 0xc00, 0, 0, 0, 1, 0 }, + { LLCC_MDM, 8, 3072, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0 }, + { LLCC_MODHW, 9, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0 }, + { LLCC_CMPT, 10, 6144, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0 }, + { LLCC_GPUHTW, 11, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0 }, + { LLCC_GPU, 12, 5120, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0 }, + { LLCC_MMUHWT, 13, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 1 }, + { LLCC_CMPTDMA, 15, 6144, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0 }, + { LLCC_DISP, 16, 6144, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0 }, + { LLCC_VIDFW, 17, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0 }, + { LLCC_MDMHPFX, 20, 1024, 2, 1, 0xfff, 0x0, 0, 0, 0, 1, 0 }, + { LLCC_MDMPNG, 21, 1024, 0, 1, 0xc, 0x0, 0, 0, 0, 1, 0 }, + { LLCC_AUDHW, 22, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0 }, + { LLCC_NPU, 23, 6144, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0 }, + { LLCC_WLHW, 24, 6144, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0 }, + { LLCC_MODPE, 29, 512, 1, 1, 0xc, 0x0, 0, 0, 0, 1, 0 }, + { LLCC_APTCM, 30, 512, 3, 1, 0x0, 0x1, 1, 0, 0, 1, 0 }, + { LLCC_WRCACHE, 31, 128, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 0 }, +}; + +static const struct llcc_slice_config sc8280xp_data[] = { + { LLCC_CPUSS, 1, 6144, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 1, 0 }, + { LLCC_VIDSC0, 2, 512, 3, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 }, + { LLCC_AUDIO, 6, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 0, 0 }, + { LLCC_CMPT, 10, 6144, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 0, 0 }, + { LLCC_GPUHTW, 11, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 }, + { LLCC_GPU, 12, 4096, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 1 }, + { LLCC_MMUHWT, 13, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 }, + { LLCC_DISP, 16, 6144, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 }, + { LLCC_AUDHW, 22, 2048, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 }, + { LLCC_DRE, 26, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 }, + { LLCC_CVP, 28, 512, 3, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 }, + { LLCC_APTCM, 30, 1024, 3, 1, 0x0, 0x1, 1, 0, 0, 1, 0, 0 }, + { LLCC_WRCACHE, 31, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 }, + { LLCC_CVPFW, 32, 512, 1, 0, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 }, + { LLCC_CPUSS1, 33, 2048, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 }, + { LLCC_CPUHWT, 36, 512, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 }, +}; + static const struct llcc_slice_config sdm845_data[] = { { LLCC_CPUSS, 1, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 1 }, { LLCC_VIDSC0, 2, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0 }, @@ -276,6 +320,20 @@ static const struct qcom_llcc_config sc7280_cfg = { .reg_offset = llcc_v1_2_reg_offset, }; +static const struct qcom_llcc_config sc8180x_cfg = { + .sct_data = sc8180x_data, + .size = ARRAY_SIZE(sc8180x_data), + .need_llcc_cfg = true, + .reg_offset = llcc_v1_2_reg_offset, +}; + +static const struct qcom_llcc_config sc8280xp_cfg = { + .sct_data = sc8280xp_data, + .size = ARRAY_SIZE(sc8280xp_data), + .need_llcc_cfg = true, + .reg_offset = llcc_v1_2_reg_offset, +}; + static const struct qcom_llcc_config sdm845_cfg = { .sct_data = sdm845_data, .size = ARRAY_SIZE(sdm845_data), @@ -741,6 +799,8 @@ err: static const struct of_device_id qcom_llcc_of_match[] = { { .compatible = "qcom,sc7180-llcc", .data = &sc7180_cfg }, { .compatible = "qcom,sc7280-llcc", .data = &sc7280_cfg }, + { .compatible = "qcom,sc8180x-llcc", .data = &sc8180x_cfg }, + { .compatible = "qcom,sc8280xp-llcc", .data = &sc8280xp_cfg }, { .compatible = "qcom,sdm845-llcc", .data = &sdm845_cfg }, { .compatible = "qcom,sm6350-llcc", .data = &sm6350_cfg }, { .compatible = "qcom,sm8150-llcc", .data = &sm8150_cfg }, diff --git a/include/linux/soc/qcom/llcc-qcom.h b/include/linux/soc/qcom/llcc-qcom.h index 0bc21ee58fac..9ed5384c5ca1 100644 --- a/include/linux/soc/qcom/llcc-qcom.h +++ b/include/linux/soc/qcom/llcc-qcom.h @@ -29,6 +29,8 @@ #define LLCC_AUDHW 22 #define LLCC_NPU 23 #define LLCC_WLHW 24 +#define LLCC_PIMEM 25 +#define LLCC_DRE 26 #define LLCC_CVP 28 #define LLCC_MODPE 29 #define LLCC_APTCM 30 -- cgit v1.2.3 From 8239d67f59cf522dd4f7135392a2f9a3a25f9cff Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Wed, 6 Apr 2022 17:33:52 +0200 Subject: soc: imx: imx8m-blk-ctrl: set power device name Set the name for the virtual power device to the name of the attached blk-ctrl domain. Makes the debug output for the power domains a lot more pleasant to read. Signed-off-by: Lucas Stach Signed-off-by: Shawn Guo --- drivers/soc/imx/imx8m-blk-ctrl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/imx/imx8m-blk-ctrl.c b/drivers/soc/imx/imx8m-blk-ctrl.c index 122f9c884b38..3071a8eca8ef 100644 --- a/drivers/soc/imx/imx8m-blk-ctrl.c +++ b/drivers/soc/imx/imx8m-blk-ctrl.c @@ -241,6 +241,7 @@ static int imx8m_blk_ctrl_probe(struct platform_device *pdev) ret = PTR_ERR(domain->power_dev); goto cleanup_pds; } + dev_set_name(domain->power_dev, "%s", data->name); domain->genpd.name = data->name; domain->genpd.power_on = imx8m_blk_ctrl_power_on; -- cgit v1.2.3 From 556f5cf9568af772d494cff24ffaa7ea41e1ab40 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Wed, 6 Apr 2022 17:33:53 +0200 Subject: soc: imx: add i.MX8MP HSIO blk-ctrl The i.MX8MP added some blk-ctrl peripherals that don't follow the regular structure of the blk-ctrls in the previous SoCs. Add a new file for those with currently only the HSIO blk-ctrl being supported. Others will be added later on. Signed-off-by: Lucas Stach Signed-off-by: Shawn Guo --- drivers/soc/imx/Makefile | 1 + drivers/soc/imx/imx8mp-blk-ctrl.c | 503 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 504 insertions(+) create mode 100644 drivers/soc/imx/imx8mp-blk-ctrl.c diff --git a/drivers/soc/imx/Makefile b/drivers/soc/imx/Makefile index 8a707077914c..63cd29f6d4d2 100644 --- a/drivers/soc/imx/Makefile +++ b/drivers/soc/imx/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o obj-$(CONFIG_IMX_GPCV2_PM_DOMAINS) += gpcv2.o obj-$(CONFIG_SOC_IMX8M) += soc-imx8m.o obj-$(CONFIG_SOC_IMX8M) += imx8m-blk-ctrl.o +obj-$(CONFIG_SOC_IMX8M) += imx8mp-blk-ctrl.o diff --git a/drivers/soc/imx/imx8mp-blk-ctrl.c b/drivers/soc/imx/imx8mp-blk-ctrl.c new file mode 100644 index 000000000000..97cc8f08911e --- /dev/null +++ b/drivers/soc/imx/imx8mp-blk-ctrl.c @@ -0,0 +1,503 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * Copyright 2022 Pengutronix, Lucas Stach + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define GPR_REG0 0x0 +#define PCIE_CLOCK_MODULE_EN BIT(0) +#define USB_CLOCK_MODULE_EN BIT(1) + +struct imx8mp_blk_ctrl_domain; + +struct imx8mp_blk_ctrl { + struct device *dev; + struct notifier_block power_nb; + struct device *bus_power_dev; + struct regmap *regmap; + struct imx8mp_blk_ctrl_domain *domains; + struct genpd_onecell_data onecell_data; + void (*power_off) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain); + void (*power_on) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain); +}; + +struct imx8mp_blk_ctrl_domain_data { + const char *name; + const char * const *clk_names; + int num_clks; + const char *gpc_name; +}; + +#define DOMAIN_MAX_CLKS 2 + +struct imx8mp_blk_ctrl_domain { + struct generic_pm_domain genpd; + const struct imx8mp_blk_ctrl_domain_data *data; + struct clk_bulk_data clks[DOMAIN_MAX_CLKS]; + struct device *power_dev; + struct imx8mp_blk_ctrl *bc; + int id; +}; + +struct imx8mp_blk_ctrl_data { + int max_reg; + notifier_fn_t power_notifier_fn; + void (*power_off) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain); + void (*power_on) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain); + const struct imx8mp_blk_ctrl_domain_data *domains; + int num_domains; +}; + +static inline struct imx8mp_blk_ctrl_domain * +to_imx8mp_blk_ctrl_domain(struct generic_pm_domain *genpd) +{ + return container_of(genpd, struct imx8mp_blk_ctrl_domain, genpd); +} + +static void imx8mp_hsio_blk_ctrl_power_on(struct imx8mp_blk_ctrl *bc, + struct imx8mp_blk_ctrl_domain *domain) +{ + switch (domain->id) { + case IMX8MP_HSIOBLK_PD_USB: + regmap_set_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN); + break; + case IMX8MP_HSIOBLK_PD_PCIE: + regmap_set_bits(bc->regmap, GPR_REG0, PCIE_CLOCK_MODULE_EN); + break; + default: + break; + } +} + +static void imx8mp_hsio_blk_ctrl_power_off(struct imx8mp_blk_ctrl *bc, + struct imx8mp_blk_ctrl_domain *domain) +{ + switch (domain->id) { + case IMX8MP_HSIOBLK_PD_USB: + regmap_clear_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN); + break; + case IMX8MP_HSIOBLK_PD_PCIE: + regmap_clear_bits(bc->regmap, GPR_REG0, PCIE_CLOCK_MODULE_EN); + break; + default: + break; + } +} + +static int imx8mp_hsio_power_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct imx8mp_blk_ctrl *bc = container_of(nb, struct imx8mp_blk_ctrl, + power_nb); + struct clk_bulk_data *usb_clk = bc->domains[IMX8MP_HSIOBLK_PD_USB].clks; + int num_clks = bc->domains[IMX8MP_HSIOBLK_PD_USB].data->num_clks; + int ret; + + switch (action) { + case GENPD_NOTIFY_ON: + /* + * enable USB clock for a moment for the power-on ADB handshake + * to proceed + */ + ret = clk_bulk_prepare_enable(num_clks, usb_clk); + if (ret) + return NOTIFY_BAD; + regmap_set_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN); + + udelay(5); + + regmap_clear_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN); + clk_bulk_disable_unprepare(num_clks, usb_clk); + break; + case GENPD_NOTIFY_PRE_OFF: + /* enable USB clock for the power-down ADB handshake to work */ + ret = clk_bulk_prepare_enable(num_clks, usb_clk); + if (ret) + return NOTIFY_BAD; + + regmap_set_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN); + break; + case GENPD_NOTIFY_OFF: + clk_bulk_disable_unprepare(num_clks, usb_clk); + break; + default: + break; + } + + return NOTIFY_OK; +} + +static const struct imx8mp_blk_ctrl_domain_data imx8mp_hsio_domain_data[] = { + [IMX8MP_HSIOBLK_PD_USB] = { + .name = "hsioblk-usb", + .clk_names = (const char *[]){ "usb" }, + .num_clks = 1, + .gpc_name = "usb", + }, + [IMX8MP_HSIOBLK_PD_USB_PHY1] = { + .name = "hsioblk-usb-phy1", + .gpc_name = "usb-phy1", + }, + [IMX8MP_HSIOBLK_PD_USB_PHY2] = { + .name = "hsioblk-usb-phy2", + .gpc_name = "usb-phy2", + }, + [IMX8MP_HSIOBLK_PD_PCIE] = { + .name = "hsioblk-pcie", + .clk_names = (const char *[]){ "pcie" }, + .num_clks = 1, + .gpc_name = "pcie", + }, + [IMX8MP_HSIOBLK_PD_PCIE_PHY] = { + .name = "hsioblk-pcie-phy", + .gpc_name = "pcie-phy", + }, +}; + +static const struct imx8mp_blk_ctrl_data imx8mp_hsio_blk_ctl_dev_data = { + .max_reg = 0x24, + .power_on = imx8mp_hsio_blk_ctrl_power_on, + .power_off = imx8mp_hsio_blk_ctrl_power_off, + .power_notifier_fn = imx8mp_hsio_power_notifier, + .domains = imx8mp_hsio_domain_data, + .num_domains = ARRAY_SIZE(imx8mp_hsio_domain_data), +}; + +static int imx8mp_blk_ctrl_power_on(struct generic_pm_domain *genpd) +{ + struct imx8mp_blk_ctrl_domain *domain = to_imx8mp_blk_ctrl_domain(genpd); + const struct imx8mp_blk_ctrl_domain_data *data = domain->data; + struct imx8mp_blk_ctrl *bc = domain->bc; + int ret; + + /* make sure bus domain is awake */ + ret = pm_runtime_resume_and_get(bc->bus_power_dev); + if (ret < 0) { + dev_err(bc->dev, "failed to power up bus domain\n"); + return ret; + } + + /* enable upstream clocks */ + ret = clk_bulk_prepare_enable(data->num_clks, domain->clks);; + if (ret) { + dev_err(bc->dev, "failed to enable clocks\n"); + goto bus_put; + } + + /* domain specific blk-ctrl manipulation */ + bc->power_on(bc, domain); + + /* power up upstream GPC domain */ + ret = pm_runtime_resume_and_get(domain->power_dev); + if (ret < 0) { + dev_err(bc->dev, "failed to power up peripheral domain\n"); + goto clk_disable; + } + + clk_bulk_disable_unprepare(data->num_clks, domain->clks); + + return 0; + +clk_disable: + clk_bulk_disable_unprepare(data->num_clks, domain->clks); +bus_put: + pm_runtime_put(bc->bus_power_dev); + + return ret; +} + +static int imx8mp_blk_ctrl_power_off(struct generic_pm_domain *genpd) +{ + struct imx8mp_blk_ctrl_domain *domain = to_imx8mp_blk_ctrl_domain(genpd); + const struct imx8mp_blk_ctrl_domain_data *data = domain->data; + struct imx8mp_blk_ctrl *bc = domain->bc; + int ret; + + ret = clk_bulk_prepare_enable(data->num_clks, domain->clks);; + if (ret) { + dev_err(bc->dev, "failed to enable clocks\n"); + return ret; + } + + /* domain specific blk-ctrl manipulation */ + bc->power_off(bc, domain); + + clk_bulk_disable_unprepare(data->num_clks, domain->clks); + + /* power down upstream GPC domain */ + pm_runtime_put(domain->power_dev); + + /* allow bus domain to suspend */ + pm_runtime_put(bc->bus_power_dev); + + return 0; +} + +static struct generic_pm_domain * +imx8m_blk_ctrl_xlate(struct of_phandle_args *args, void *data) +{ + struct genpd_onecell_data *onecell_data = data; + unsigned int index = args->args[0]; + + if (args->args_count != 1 || + index >= onecell_data->num_domains) + return ERR_PTR(-EINVAL); + + return onecell_data->domains[index]; +} + +static struct lock_class_key blk_ctrl_genpd_lock_class; + +static int imx8mp_blk_ctrl_probe(struct platform_device *pdev) +{ + const struct imx8mp_blk_ctrl_data *bc_data; + struct device *dev = &pdev->dev; + struct imx8mp_blk_ctrl *bc; + void __iomem *base; + int num_domains, i, ret; + + struct regmap_config regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + }; + + bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL); + if (!bc) + return -ENOMEM; + + bc->dev = dev; + + bc_data = of_device_get_match_data(dev); + num_domains = bc_data->num_domains; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap_config.max_register = bc_data->max_reg; + bc->regmap = devm_regmap_init_mmio(dev, base, ®map_config); + if (IS_ERR(bc->regmap)) + return dev_err_probe(dev, PTR_ERR(bc->regmap), + "failed to init regmap\n"); + + bc->domains = devm_kcalloc(dev, num_domains, + sizeof(struct imx8mp_blk_ctrl_domain), + GFP_KERNEL); + if (!bc->domains) + return -ENOMEM; + + bc->onecell_data.num_domains = num_domains; + bc->onecell_data.xlate = imx8m_blk_ctrl_xlate; + bc->onecell_data.domains = + devm_kcalloc(dev, num_domains, + sizeof(struct generic_pm_domain *), GFP_KERNEL); + if (!bc->onecell_data.domains) + return -ENOMEM; + + bc->bus_power_dev = genpd_dev_pm_attach_by_name(dev, "bus"); + if (IS_ERR(bc->bus_power_dev)) + return dev_err_probe(dev, PTR_ERR(bc->bus_power_dev), + "failed to attach bus power domain\n"); + + bc->power_off = bc_data->power_off; + bc->power_on = bc_data->power_on; + + for (i = 0; i < num_domains; i++) { + const struct imx8mp_blk_ctrl_domain_data *data = &bc_data->domains[i]; + struct imx8mp_blk_ctrl_domain *domain = &bc->domains[i]; + int j; + + domain->data = data; + + for (j = 0; j < data->num_clks; j++) + domain->clks[j].id = data->clk_names[j]; + + ret = devm_clk_bulk_get(dev, data->num_clks, domain->clks); + if (ret) { + dev_err_probe(dev, ret, "failed to get clock\n"); + goto cleanup_pds; + } + + domain->power_dev = + dev_pm_domain_attach_by_name(dev, data->gpc_name); + if (IS_ERR(domain->power_dev)) { + dev_err_probe(dev, PTR_ERR(domain->power_dev), + "failed to attach power domain %s\n", + data->gpc_name); + ret = PTR_ERR(domain->power_dev); + goto cleanup_pds; + } + dev_set_name(domain->power_dev, "%s", data->name); + + domain->genpd.name = data->name; + domain->genpd.power_on = imx8mp_blk_ctrl_power_on; + domain->genpd.power_off = imx8mp_blk_ctrl_power_off; + domain->bc = bc; + domain->id = i; + + ret = pm_genpd_init(&domain->genpd, NULL, true); + if (ret) { + dev_err_probe(dev, ret, "failed to init power domain\n"); + dev_pm_domain_detach(domain->power_dev, true); + goto cleanup_pds; + } + + /* + * We use runtime PM to trigger power on/off of the upstream GPC + * domain, as a strict hierarchical parent/child power domain + * setup doesn't allow us to meet the sequencing requirements. + * This means we have nested locking of genpd locks, without the + * nesting being visible at the genpd level, so we need a + * separate lock class to make lockdep aware of the fact that + * this are separate domain locks that can be nested without a + * self-deadlock. + */ + lockdep_set_class(&domain->genpd.mlock, + &blk_ctrl_genpd_lock_class); + + bc->onecell_data.domains[i] = &domain->genpd; + } + + ret = of_genpd_add_provider_onecell(dev->of_node, &bc->onecell_data); + if (ret) { + dev_err_probe(dev, ret, "failed to add power domain provider\n"); + goto cleanup_pds; + } + + bc->power_nb.notifier_call = bc_data->power_notifier_fn; + ret = dev_pm_genpd_add_notifier(bc->bus_power_dev, &bc->power_nb); + if (ret) { + dev_err_probe(dev, ret, "failed to add power notifier\n"); + goto cleanup_provider; + } + + dev_set_drvdata(dev, bc); + + return 0; + +cleanup_provider: + of_genpd_del_provider(dev->of_node); +cleanup_pds: + for (i--; i >= 0; i--) { + pm_genpd_remove(&bc->domains[i].genpd); + dev_pm_domain_detach(bc->domains[i].power_dev, true); + } + + dev_pm_domain_detach(bc->bus_power_dev, true); + + return ret; +} + +static int imx8mp_blk_ctrl_remove(struct platform_device *pdev) +{ + struct imx8mp_blk_ctrl *bc = dev_get_drvdata(&pdev->dev); + int i; + + of_genpd_del_provider(pdev->dev.of_node); + + for (i = 0; bc->onecell_data.num_domains; i++) { + struct imx8mp_blk_ctrl_domain *domain = &bc->domains[i]; + + pm_genpd_remove(&domain->genpd); + dev_pm_domain_detach(domain->power_dev, true); + } + + dev_pm_genpd_remove_notifier(bc->bus_power_dev); + + dev_pm_domain_detach(bc->bus_power_dev, true); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int imx8mp_blk_ctrl_suspend(struct device *dev) +{ + struct imx8mp_blk_ctrl *bc = dev_get_drvdata(dev); + int ret, i; + + /* + * This may look strange, but is done so the generic PM_SLEEP code + * can power down our domains and more importantly power them up again + * after resume, without tripping over our usage of runtime PM to + * control the upstream GPC domains. Things happen in the right order + * in the system suspend/resume paths due to the device parent/child + * hierarchy. + */ + ret = pm_runtime_get_sync(bc->bus_power_dev); + if (ret < 0) { + pm_runtime_put_noidle(bc->bus_power_dev); + return ret; + } + + for (i = 0; i < bc->onecell_data.num_domains; i++) { + struct imx8mp_blk_ctrl_domain *domain = &bc->domains[i]; + + ret = pm_runtime_get_sync(domain->power_dev); + if (ret < 0) { + pm_runtime_put_noidle(domain->power_dev); + goto out_fail; + } + } + + return 0; + +out_fail: + for (i--; i >= 0; i--) + pm_runtime_put(bc->domains[i].power_dev); + + pm_runtime_put(bc->bus_power_dev); + + return ret; +} + +static int imx8mp_blk_ctrl_resume(struct device *dev) +{ + struct imx8mp_blk_ctrl *bc = dev_get_drvdata(dev); + int i; + + for (i = 0; i < bc->onecell_data.num_domains; i++) + pm_runtime_put(bc->domains[i].power_dev); + + pm_runtime_put(bc->bus_power_dev); + + return 0; +} +#endif + +static const struct dev_pm_ops imx8mp_blk_ctrl_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(imx8mp_blk_ctrl_suspend, + imx8mp_blk_ctrl_resume) +}; + +static const struct of_device_id imx8mp_blk_ctrl_of_match[] = { + { + .compatible = "fsl,imx8mp-hsio-blk-ctrl", + .data = &imx8mp_hsio_blk_ctl_dev_data, + }, { + /* Sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, imx8m_blk_ctrl_of_match); + +static struct platform_driver imx8mp_blk_ctrl_driver = { + .probe = imx8mp_blk_ctrl_probe, + .remove = imx8mp_blk_ctrl_remove, + .driver = { + .name = "imx8mp-blk-ctrl", + .pm = &imx8mp_blk_ctrl_pm_ops, + .of_match_table = imx8mp_blk_ctrl_of_match, + }, +}; +module_platform_driver(imx8mp_blk_ctrl_driver); -- cgit v1.2.3 From 07614fed00e9f4e272c4e30f4c317e63a44207f9 Mon Sep 17 00:00:00 2001 From: Paul Elder Date: Wed, 6 Apr 2022 17:33:55 +0200 Subject: soc: imx: imx8m-blk-ctrl: Add i.MX8MP media blk-ctrl Add the description for the i.MX8MP media blk-ctrl. Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Reviewed-by: Marek Vasut Tested-by: Marek Vasut # MX8MP LCDIF #1 and #2 Signed-off-by: Lucas Stach Signed-off-by: Shawn Guo --- drivers/soc/imx/imx8m-blk-ctrl.c | 123 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 121 insertions(+), 2 deletions(-) diff --git a/drivers/soc/imx/imx8m-blk-ctrl.c b/drivers/soc/imx/imx8m-blk-ctrl.c index 3071a8eca8ef..2b8c1e36a489 100644 --- a/drivers/soc/imx/imx8m-blk-ctrl.c +++ b/drivers/soc/imx/imx8m-blk-ctrl.c @@ -15,11 +15,12 @@ #include #include +#include #include #define BLK_SFT_RSTN 0x0 #define BLK_CLK_EN 0x4 -#define BLK_MIPI_RESET_DIV 0x8 /* Mini/Nano DISPLAY_BLK_CTRL only */ +#define BLK_MIPI_RESET_DIV 0x8 /* Mini/Nano/Plus DISPLAY_BLK_CTRL only */ struct imx8m_blk_ctrl_domain; @@ -41,7 +42,7 @@ struct imx8m_blk_ctrl_domain_data { u32 clk_mask; /* - * i.MX8M Mini and Nano have a third DISPLAY_BLK_CTRL register + * i.MX8M Mini, Nano and Plus have a third DISPLAY_BLK_CTRL register * which is used to control the reset for the MIPI Phy. * Since it's only present in certain circumstances, * an if-statement should be used before setting and clearing this @@ -591,6 +592,121 @@ static const struct imx8m_blk_ctrl_data imx8mn_disp_blk_ctl_dev_data = { .num_domains = ARRAY_SIZE(imx8mn_disp_blk_ctl_domain_data), }; +static int imx8mp_media_power_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl, + power_nb); + + if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF) + return NOTIFY_OK; + + /* Enable bus clock and deassert bus reset */ + regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(8)); + regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(8)); + + /* + * On power up we have no software backchannel to the GPC to + * wait for the ADB handshake to happen, so we just delay for a + * bit. On power down the GPC driver waits for the handshake. + */ + if (action == GENPD_NOTIFY_ON) + udelay(5); + + return NOTIFY_OK; +} + +/* + * From i.MX 8M Plus Applications Processor Reference Manual, Rev. 1, + * section 13.2.2, 13.2.3 + * isp-ahb and dwe are not in Figure 13-5. Media BLK_CTRL Clocks + */ +static const struct imx8m_blk_ctrl_domain_data imx8mp_media_blk_ctl_domain_data[] = { + [IMX8MP_MEDIABLK_PD_MIPI_DSI_1] = { + .name = "mediablk-mipi-dsi-1", + .clk_names = (const char *[]){ "apb", "phy", }, + .num_clks = 2, + .gpc_name = "mipi-dsi1", + .rst_mask = BIT(0) | BIT(1), + .clk_mask = BIT(0) | BIT(1), + .mipi_phy_rst_mask = BIT(17), + }, + [IMX8MP_MEDIABLK_PD_MIPI_CSI2_1] = { + .name = "mediablk-mipi-csi2-1", + .clk_names = (const char *[]){ "apb", "cam1" }, + .num_clks = 2, + .gpc_name = "mipi-csi1", + .rst_mask = BIT(2) | BIT(3), + .clk_mask = BIT(2) | BIT(3), + .mipi_phy_rst_mask = BIT(16), + }, + [IMX8MP_MEDIABLK_PD_LCDIF_1] = { + .name = "mediablk-lcdif-1", + .clk_names = (const char *[]){ "disp1", "apb", "axi", }, + .num_clks = 3, + .gpc_name = "lcdif1", + .rst_mask = BIT(4) | BIT(5) | BIT(23), + .clk_mask = BIT(4) | BIT(5) | BIT(23), + }, + [IMX8MP_MEDIABLK_PD_ISI] = { + .name = "mediablk-isi", + .clk_names = (const char *[]){ "axi", "apb" }, + .num_clks = 2, + .gpc_name = "isi", + .rst_mask = BIT(6) | BIT(7), + .clk_mask = BIT(6) | BIT(7), + }, + [IMX8MP_MEDIABLK_PD_MIPI_CSI2_2] = { + .name = "mediablk-mipi-csi2-2", + .clk_names = (const char *[]){ "apb", "cam2" }, + .num_clks = 2, + .gpc_name = "mipi-csi2", + .rst_mask = BIT(9) | BIT(10), + .clk_mask = BIT(9) | BIT(10), + .mipi_phy_rst_mask = BIT(30), + }, + [IMX8MP_MEDIABLK_PD_LCDIF_2] = { + .name = "mediablk-lcdif-2", + .clk_names = (const char *[]){ "disp1", "apb", "axi", }, + .num_clks = 3, + .gpc_name = "lcdif2", + .rst_mask = BIT(11) | BIT(12) | BIT(24), + .clk_mask = BIT(11) | BIT(12) | BIT(24), + }, + [IMX8MP_MEDIABLK_PD_ISP] = { + .name = "mediablk-isp", + .clk_names = (const char *[]){ "isp", "axi", "apb" }, + .num_clks = 3, + .gpc_name = "isp", + .rst_mask = BIT(16) | BIT(17) | BIT(18), + .clk_mask = BIT(16) | BIT(17) | BIT(18), + }, + [IMX8MP_MEDIABLK_PD_DWE] = { + .name = "mediablk-dwe", + .clk_names = (const char *[]){ "axi", "apb" }, + .num_clks = 2, + .gpc_name = "dwe", + .rst_mask = BIT(19) | BIT(20) | BIT(21), + .clk_mask = BIT(19) | BIT(20) | BIT(21), + }, + [IMX8MP_MEDIABLK_PD_MIPI_DSI_2] = { + .name = "mediablk-mipi-dsi-2", + .clk_names = (const char *[]){ "phy", }, + .num_clks = 1, + .gpc_name = "mipi-dsi2", + .rst_mask = BIT(22), + .clk_mask = BIT(22), + .mipi_phy_rst_mask = BIT(29), + }, +}; + +static const struct imx8m_blk_ctrl_data imx8mp_media_blk_ctl_dev_data = { + .max_reg = 0x138, + .power_notifier_fn = imx8mp_media_power_notifier, + .domains = imx8mp_media_blk_ctl_domain_data, + .num_domains = ARRAY_SIZE(imx8mp_media_blk_ctl_domain_data), +}; + static int imx8mq_vpu_power_notifier(struct notifier_block *nb, unsigned long action, void *data) { @@ -663,6 +779,9 @@ static const struct of_device_id imx8m_blk_ctrl_of_match[] = { }, { .compatible = "fsl,imx8mn-disp-blk-ctrl", .data = &imx8mn_disp_blk_ctl_dev_data + }, { + .compatible = "fsl,imx8mp-media-blk-ctrl", + .data = &imx8mp_media_blk_ctl_dev_data }, { .compatible = "fsl,imx8mq-vpu-blk-ctrl", .data = &imx8mq_vpu_blk_ctl_dev_data -- cgit v1.2.3 From e3442022f54301ed8c27a2087b031a0a7265c4ba Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Wed, 6 Apr 2022 17:33:58 +0200 Subject: soc: imx: add i.MX8MP HDMI blk-ctrl This adds driver support for the HDMI blk-ctrl found on the i.MX8MP SoC. Signed-off-by: Lucas Stach Signed-off-by: Shawn Guo --- drivers/soc/imx/imx8mp-blk-ctrl.c | 193 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) diff --git a/drivers/soc/imx/imx8mp-blk-ctrl.c b/drivers/soc/imx/imx8mp-blk-ctrl.c index 97cc8f08911e..f5692782bbdf 100644 --- a/drivers/soc/imx/imx8mp-blk-ctrl.c +++ b/drivers/soc/imx/imx8mp-blk-ctrl.c @@ -174,6 +174,196 @@ static const struct imx8mp_blk_ctrl_data imx8mp_hsio_blk_ctl_dev_data = { .num_domains = ARRAY_SIZE(imx8mp_hsio_domain_data), }; +#define HDMI_RTX_RESET_CTL0 0x20 +#define HDMI_RTX_CLK_CTL0 0x40 +#define HDMI_RTX_CLK_CTL1 0x50 +#define HDMI_RTX_CLK_CTL2 0x60 +#define HDMI_RTX_CLK_CTL3 0x70 +#define HDMI_RTX_CLK_CTL4 0x80 +#define HDMI_TX_CONTROL0 0x200 + +static void imx8mp_hdmi_blk_ctrl_power_on(struct imx8mp_blk_ctrl *bc, + struct imx8mp_blk_ctrl_domain *domain) +{ + switch (domain->id) { + case IMX8MP_HDMIBLK_PD_IRQSTEER: + regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(9)); + regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(16)); + break; + case IMX8MP_HDMIBLK_PD_LCDIF: + regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, + BIT(7) | BIT(16) | BIT(17) | BIT(18) | + BIT(19) | BIT(20)); + regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(11)); + regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, + BIT(4) | BIT(5) | BIT(6)); + break; + case IMX8MP_HDMIBLK_PD_PAI: + regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(17)); + regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(18)); + break; + case IMX8MP_HDMIBLK_PD_PVI: + regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(28)); + regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(22)); + break; + case IMX8MP_HDMIBLK_PD_TRNG: + regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(27) | BIT(30)); + regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(20)); + break; + case IMX8MP_HDMIBLK_PD_HDMI_TX: + regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, + BIT(2) | BIT(4) | BIT(5)); + regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, + BIT(12) | BIT(13) | BIT(14) | BIT(15) | BIT(16) | + BIT(18) | BIT(19) | BIT(20) | BIT(21)); + regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, + BIT(7) | BIT(10) | BIT(11)); + regmap_set_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(1)); + break; + case IMX8MP_HDMIBLK_PD_HDMI_TX_PHY: + regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(22) | BIT(24)); + regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(12)); + regmap_clear_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(3)); + break; + default: + break; + } +} + +static void imx8mp_hdmi_blk_ctrl_power_off(struct imx8mp_blk_ctrl *bc, + struct imx8mp_blk_ctrl_domain *domain) +{ + switch (domain->id) { + case IMX8MP_HDMIBLK_PD_IRQSTEER: + regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(9)); + regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(16)); + break; + case IMX8MP_HDMIBLK_PD_LCDIF: + regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, + BIT(4) | BIT(5) | BIT(6)); + regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(11)); + regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, + BIT(7) | BIT(16) | BIT(17) | BIT(18) | + BIT(19) | BIT(20)); + break; + case IMX8MP_HDMIBLK_PD_PAI: + regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(18)); + regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(17)); + break; + case IMX8MP_HDMIBLK_PD_PVI: + regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(22)); + regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(28)); + break; + case IMX8MP_HDMIBLK_PD_TRNG: + regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(20)); + regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(27) | BIT(30)); + break; + case IMX8MP_HDMIBLK_PD_HDMI_TX: + regmap_clear_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(1)); + regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, + BIT(7) | BIT(10) | BIT(11)); + regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, + BIT(12) | BIT(13) | BIT(14) | BIT(15) | BIT(16) | + BIT(18) | BIT(19) | BIT(20) | BIT(21)); + regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, + BIT(2) | BIT(4) | BIT(5)); + break; + case IMX8MP_HDMIBLK_PD_HDMI_TX_PHY: + regmap_set_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(3)); + regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(12)); + regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(22) | BIT(24)); + break; + default: + break; + } +} + +static int imx8mp_hdmi_power_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct imx8mp_blk_ctrl *bc = container_of(nb, struct imx8mp_blk_ctrl, + power_nb); + + if (action != GENPD_NOTIFY_ON) + return NOTIFY_OK; + + /* + * Contrary to other blk-ctrls the reset and clock don't clear when the + * power domain is powered down. To ensure the proper reset pulsing, + * first clear them all to asserted state, then enable the bus clocks + * and then release the ADB reset. + */ + regmap_write(bc->regmap, HDMI_RTX_RESET_CTL0, 0x0); + regmap_write(bc->regmap, HDMI_RTX_CLK_CTL0, 0x0); + regmap_write(bc->regmap, HDMI_RTX_CLK_CTL1, 0x0); + regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, + BIT(0) | BIT(1) | BIT(10)); + regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(0)); + + /* + * On power up we have no software backchannel to the GPC to + * wait for the ADB handshake to happen, so we just delay for a + * bit. On power down the GPC driver waits for the handshake. + */ + udelay(5); + + return NOTIFY_OK; +} + +static const struct imx8mp_blk_ctrl_domain_data imx8mp_hdmi_domain_data[] = { + [IMX8MP_HDMIBLK_PD_IRQSTEER] = { + .name = "hdmiblk-irqsteer", + .clk_names = (const char *[]){ "apb" }, + .num_clks = 1, + .gpc_name = "irqsteer", + }, + [IMX8MP_HDMIBLK_PD_LCDIF] = { + .name = "hdmiblk-lcdif", + .clk_names = (const char *[]){ "axi", "apb" }, + .num_clks = 2, + .gpc_name = "lcdif", + }, + [IMX8MP_HDMIBLK_PD_PAI] = { + .name = "hdmiblk-pai", + .clk_names = (const char *[]){ "apb" }, + .num_clks = 1, + .gpc_name = "pai", + }, + [IMX8MP_HDMIBLK_PD_PVI] = { + .name = "hdmiblk-pvi", + .clk_names = (const char *[]){ "apb" }, + .num_clks = 1, + .gpc_name = "pvi", + }, + [IMX8MP_HDMIBLK_PD_TRNG] = { + .name = "hdmiblk-trng", + .clk_names = (const char *[]){ "apb" }, + .num_clks = 1, + .gpc_name = "trng", + }, + [IMX8MP_HDMIBLK_PD_HDMI_TX] = { + .name = "hdmiblk-hdmi-tx", + .clk_names = (const char *[]){ "apb", "ref_266m" }, + .num_clks = 2, + .gpc_name = "hdmi-tx", + }, + [IMX8MP_HDMIBLK_PD_HDMI_TX_PHY] = { + .name = "hdmiblk-hdmi-tx-phy", + .clk_names = (const char *[]){ "apb", "ref_24m" }, + .num_clks = 2, + .gpc_name = "hdmi-tx-phy", + }, +}; + +static const struct imx8mp_blk_ctrl_data imx8mp_hdmi_blk_ctl_dev_data = { + .max_reg = 0x23c, + .power_on = imx8mp_hdmi_blk_ctrl_power_on, + .power_off = imx8mp_hdmi_blk_ctrl_power_off, + .power_notifier_fn = imx8mp_hdmi_power_notifier, + .domains = imx8mp_hdmi_domain_data, + .num_domains = ARRAY_SIZE(imx8mp_hdmi_domain_data), +}; + static int imx8mp_blk_ctrl_power_on(struct generic_pm_domain *genpd) { struct imx8mp_blk_ctrl_domain *domain = to_imx8mp_blk_ctrl_domain(genpd); @@ -485,6 +675,9 @@ static const struct of_device_id imx8mp_blk_ctrl_of_match[] = { { .compatible = "fsl,imx8mp-hsio-blk-ctrl", .data = &imx8mp_hsio_blk_ctl_dev_data, + }, { + .compatible = "fsl,imx8mp-hdmi-blk-ctrl", + .data = &imx8mp_hdmi_blk_ctl_dev_data, }, { /* Sentinel */ } -- cgit v1.2.3 From 27e7657a7cdc4b4a73bd5e4448e9ed733bae05c0 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 2 May 2022 15:35:24 +0200 Subject: soc: renesas: R-Car V3U is R-Car Gen4 Despite the name, R-Car V3U is the first member of the R-Car Gen4 family. Hence reflect this in the SoC Family field. Signed-off-by: Geert Uytterhoeven Reviewed-by: Wolfram Sang Link: https://lore.kernel.org/r/2bbecad7b6c24c0d5c1797b3f7f0733d5ba33842.1651497066.git.geert+renesas@glider.be --- drivers/soc/renesas/renesas-soc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c index 5b995952a747..d171f1b635c7 100644 --- a/drivers/soc/renesas/renesas-soc.c +++ b/drivers/soc/renesas/renesas-soc.c @@ -232,7 +232,7 @@ static const struct renesas_soc soc_rcar_d3 __initconst __maybe_unused = { }; static const struct renesas_soc soc_rcar_v3u __initconst __maybe_unused = { - .family = &fam_rcar_gen3, + .family = &fam_rcar_gen4, .id = 0x59, }; -- cgit v1.2.3 From 7a0c5cb67166ba546ec52e2dc2145b8f89caa9fc Mon Sep 17 00:00:00 2001 From: kernel test robot Date: Thu, 5 May 2022 19:29:01 +0800 Subject: soc: imx: fix semicolon.cocci warnings drivers/soc/imx/imx8mp-blk-ctrl.c:227:61-62: Unneeded semicolon drivers/soc/imx/imx8mp-blk-ctrl.c:192:61-62: Unneeded semicolon Remove unneeded semicolon. Generated by: scripts/coccinelle/misc/semicolon.cocci Fixes: 556f5cf9568a ("soc: imx: add i.MX8MP HSIO blk-ctrl") CC: Lucas Stach Reported-by: kernel test robot Signed-off-by: kernel test robot Signed-off-by: Shawn Guo --- drivers/soc/imx/imx8mp-blk-ctrl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soc/imx/imx8mp-blk-ctrl.c b/drivers/soc/imx/imx8mp-blk-ctrl.c index f5692782bbdf..4ca2ede6871b 100644 --- a/drivers/soc/imx/imx8mp-blk-ctrl.c +++ b/drivers/soc/imx/imx8mp-blk-ctrl.c @@ -379,7 +379,7 @@ static int imx8mp_blk_ctrl_power_on(struct generic_pm_domain *genpd) } /* enable upstream clocks */ - ret = clk_bulk_prepare_enable(data->num_clks, domain->clks);; + ret = clk_bulk_prepare_enable(data->num_clks, domain->clks); if (ret) { dev_err(bc->dev, "failed to enable clocks\n"); goto bus_put; @@ -414,7 +414,7 @@ static int imx8mp_blk_ctrl_power_off(struct generic_pm_domain *genpd) struct imx8mp_blk_ctrl *bc = domain->bc; int ret; - ret = clk_bulk_prepare_enable(data->num_clks, domain->clks);; + ret = clk_bulk_prepare_enable(data->num_clks, domain->clks); if (ret) { dev_err(bc->dev, "failed to enable clocks\n"); return ret; -- cgit v1.2.3 From 28e3dc8f8cd37eedc14e97e80a7a2e33359491df Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Fri, 22 Apr 2022 13:38:06 -0400 Subject: soc: qcom: pdr: use static for servreg_* variables Sparse reports this repesentative issue pdr_internal.h:31:22: warning: symbol 'servreg_location_entry_ei' was not declared. Should it be static? Similar for other servreg_* Global variables should not be defined in header files. This only works because pdr_internal.h is only included by pdr_interface.c. Single file use variables should be static so add static to their storage-class specifiers. Signed-off-by: Tom Rix Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220422173806.21982-1-trix@redhat.com --- drivers/soc/qcom/pdr_internal.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/soc/qcom/pdr_internal.h b/drivers/soc/qcom/pdr_internal.h index ab9ae8cdfa54..a30422214943 100644 --- a/drivers/soc/qcom/pdr_internal.h +++ b/drivers/soc/qcom/pdr_internal.h @@ -28,7 +28,7 @@ struct servreg_location_entry { u32 instance; }; -struct qmi_elem_info servreg_location_entry_ei[] = { +static struct qmi_elem_info servreg_location_entry_ei[] = { { .data_type = QMI_STRING, .elem_len = SERVREG_NAME_LENGTH + 1, @@ -74,7 +74,7 @@ struct servreg_get_domain_list_req { u32 domain_offset; }; -struct qmi_elem_info servreg_get_domain_list_req_ei[] = { +static struct qmi_elem_info servreg_get_domain_list_req_ei[] = { { .data_type = QMI_STRING, .elem_len = SERVREG_NAME_LENGTH + 1, @@ -116,7 +116,7 @@ struct servreg_get_domain_list_resp { struct servreg_location_entry domain_list[SERVREG_DOMAIN_LIST_LENGTH]; }; -struct qmi_elem_info servreg_get_domain_list_resp_ei[] = { +static struct qmi_elem_info servreg_get_domain_list_resp_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -199,7 +199,7 @@ struct servreg_register_listener_req { char service_path[SERVREG_NAME_LENGTH + 1]; }; -struct qmi_elem_info servreg_register_listener_req_ei[] = { +static struct qmi_elem_info servreg_register_listener_req_ei[] = { { .data_type = QMI_UNSIGNED_1_BYTE, .elem_len = 1, @@ -227,7 +227,7 @@ struct servreg_register_listener_resp { enum servreg_service_state curr_state; }; -struct qmi_elem_info servreg_register_listener_resp_ei[] = { +static struct qmi_elem_info servreg_register_listener_resp_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -263,7 +263,7 @@ struct servreg_restart_pd_req { char service_path[SERVREG_NAME_LENGTH + 1]; }; -struct qmi_elem_info servreg_restart_pd_req_ei[] = { +static struct qmi_elem_info servreg_restart_pd_req_ei[] = { { .data_type = QMI_STRING, .elem_len = SERVREG_NAME_LENGTH + 1, @@ -280,7 +280,7 @@ struct servreg_restart_pd_resp { struct qmi_response_type_v01 resp; }; -struct qmi_elem_info servreg_restart_pd_resp_ei[] = { +static struct qmi_elem_info servreg_restart_pd_resp_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -300,7 +300,7 @@ struct servreg_state_updated_ind { u16 transaction_id; }; -struct qmi_elem_info servreg_state_updated_ind_ei[] = { +static struct qmi_elem_info servreg_state_updated_ind_ei[] = { { .data_type = QMI_SIGNED_4_BYTE_ENUM, .elem_len = 1, @@ -336,7 +336,7 @@ struct servreg_set_ack_req { u16 transaction_id; }; -struct qmi_elem_info servreg_set_ack_req_ei[] = { +static struct qmi_elem_info servreg_set_ack_req_ei[] = { { .data_type = QMI_STRING, .elem_len = SERVREG_NAME_LENGTH + 1, @@ -362,7 +362,7 @@ struct servreg_set_ack_resp { struct qmi_response_type_v01 resp; }; -struct qmi_elem_info servreg_set_ack_resp_ei[] = { +static struct qmi_elem_info servreg_set_ack_resp_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, -- cgit v1.2.3 From d3a20dcbca4880ada8974b2d0167df99161bbfaf Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 6 May 2022 10:53:15 +0200 Subject: soc/tegra: pmc: Document core domain fields These fields are used to track the state of the core domain. Add basic descriptions so that kerneldoc can be properly generated for them. Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index c01db5309b5e..c77ecf61818b 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -394,6 +394,8 @@ struct tegra_pmc_soc { * @domain: IRQ domain provided by the PMC * @irq: chip implementation for the IRQ domain * @clk_nb: pclk clock changes handler + * @core_domain_state_synced: flag marking the core domain's state as synced + * @core_domain_registered: flag marking the core domain as registered */ struct tegra_pmc { struct device *dev; -- cgit v1.2.3 From 7ad7ab903681cc7ac2ee7be00d85ad63bfa78d08 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 6 May 2022 21:06:25 +0200 Subject: nvme-apple: fix sparse endianess warnings The new nvme-apple driver is missing a few conversions to and from little-endian data: drivers/nvme/host/apple.c:291:19: sparse: sparse: incorrect type in assignment (different base types) @@ expected unsigned long long [usertype] prp1 @@ got restricted __le64 [usertype] prp1 @@ drivers/nvme/host/apple.c:291:19: sparse: expected unsigned long long [usertype] prp1 drivers/nvme/host/apple.c:291:19: sparse: got restricted __le64 [usertype] prp1 drivers/nvme/host/apple.c:292:19: sparse: sparse: incorrect type in assignment (different base types) @@ expected unsigned long long [usertype] prp2 @@ got restricted __le64 [usertype] prp2 @@ drivers/nvme/host/apple.c:293:21: sparse: sparse: incorrect type in assignment (different base types) @@ expected unsigned int [usertype] length @@ got restricted __le16 [usertype] length @@ drivers/nvme/host/apple.c:351:52: sparse: sparse: incorrect type in initializer (different base types) @@ expected unsigned int [usertype] next_dma_addr @@ got restricted __le64 [usertype] @@ drivers/nvme/host/apple.c:456:45: sparse: sparse: incorrect type in assignment (different base types) @@ expected restricted __le64 [usertype] @@ got unsigned int [addressable] [usertype] prp_dma @@ drivers/nvme/host/apple.c:459:31: sparse: sparse: incorrect type in assignment (different base types) @@ expected restricted __le64 [usertype] @@ got unsigned long long [assigned] [usertype] dma_addr @@ drivers/nvme/host/apple.c:474:25: sparse: sparse: incorrect type in assignment (different base types) @@ expected restricted __le64 [usertype] prp1 @@ got unsigned int [usertype] dma_address @@ drivers/nvme/host/apple.c:475:25: sparse: sparse: incorrect type in assignment (different base types) @@ expected restricted __le64 [usertype] prp2 @@ got unsigned int [usertype] first_dma @@ Reported-by: kernel test robot Signed-off-by: Arnd Bergmann --- drivers/nvme/host/apple.c | 48 +++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/drivers/nvme/host/apple.c b/drivers/nvme/host/apple.c index b321f5c0563f..d702d7d60235 100644 --- a/drivers/nvme/host/apple.c +++ b/drivers/nvme/host/apple.c @@ -112,10 +112,10 @@ struct apple_nvmmu_tcb { u8 command_id; u8 _unk0; - u32 length; - u8 _unk1[16]; - u64 prp1; - u64 prp2; + __le16 length; + u8 _unk1[18]; + __le64 prp1; + __le64 prp2; u8 _unk2[16]; u8 aes_iv[8]; u8 _aes_unk[64]; @@ -348,7 +348,7 @@ static void apple_nvme_free_prps(struct apple_nvme *anv, struct request *req) for (i = 0; i < iod->npages; i++) { __le64 *prp_list = apple_nvme_iod_list(req)[i]; - dma_addr_t next_dma_addr = prp_list[last_prp]; + dma_addr_t next_dma_addr = le64_to_cpu(prp_list[last_prp]); dma_pool_free(anv->prp_page_pool, prp_list, dma_addr); dma_addr = next_dma_addr; @@ -453,10 +453,10 @@ static blk_status_t apple_nvme_setup_prps(struct apple_nvme *anv, goto free_prps; list[iod->npages++] = prp_list; prp_list[0] = old_prp_list[i - 1]; - old_prp_list[i - 1] = prp_dma; + old_prp_list[i - 1] = cpu_to_le64(prp_dma); i = 1; } - prp_list[i++] = dma_addr; + prp_list[i++] = cpu_to_le64(dma_addr); dma_len -= NVME_CTRL_PAGE_SIZE; dma_addr += NVME_CTRL_PAGE_SIZE; length -= NVME_CTRL_PAGE_SIZE; @@ -471,8 +471,8 @@ static blk_status_t apple_nvme_setup_prps(struct apple_nvme *anv, dma_len = sg_dma_len(sg); } done: - cmnd->dptr.prp1 = sg_dma_address(iod->sg); - cmnd->dptr.prp2 = iod->first_dma; + cmnd->dptr.prp1 = cpu_to_le64(sg_dma_address(iod->sg)); + cmnd->dptr.prp2 = cpu_to_le64(iod->first_dma); return BLK_STS_OK; free_prps: apple_nvme_free_prps(anv, req); @@ -498,9 +498,9 @@ static blk_status_t apple_nvme_setup_prp_simple(struct apple_nvme *anv, return BLK_STS_RESOURCE; iod->dma_len = bv->bv_len; - cmnd->dptr.prp1 = iod->first_dma; + cmnd->dptr.prp1 = cpu_to_le64(iod->first_dma); if (bv->bv_len > first_prp_len) - cmnd->dptr.prp2 = iod->first_dma + first_prp_len; + cmnd->dptr.prp2 = cpu_to_le64(iod->first_dma + first_prp_len); return BLK_STS_OK; } @@ -570,7 +570,7 @@ static inline bool apple_nvme_cqe_pending(struct apple_nvme_queue *q) { struct nvme_completion *hcqe = &q->cqes[q->cq_head]; - return (READ_ONCE(hcqe->status) & 1) == q->cq_phase; + return (le16_to_cpu(READ_ONCE(hcqe->status)) & 1) == q->cq_phase; } static inline struct blk_mq_tags * @@ -682,11 +682,11 @@ static int apple_nvme_create_cq(struct apple_nvme *anv) * is attached to the request. */ c.create_cq.opcode = nvme_admin_create_cq; - c.create_cq.prp1 = anv->ioq.cq_dma_addr; - c.create_cq.cqid = 1; - c.create_cq.qsize = APPLE_ANS_MAX_QUEUE_DEPTH - 1; - c.create_cq.cq_flags = NVME_QUEUE_PHYS_CONTIG | NVME_CQ_IRQ_ENABLED; - c.create_cq.irq_vector = 0; + c.create_cq.prp1 = cpu_to_le64(anv->ioq.cq_dma_addr); + c.create_cq.cqid = cpu_to_le16(1); + c.create_cq.qsize = cpu_to_le16(APPLE_ANS_MAX_QUEUE_DEPTH - 1); + c.create_cq.cq_flags = cpu_to_le16(NVME_QUEUE_PHYS_CONTIG | NVME_CQ_IRQ_ENABLED); + c.create_cq.irq_vector = cpu_to_le16(0); return nvme_submit_sync_cmd(anv->ctrl.admin_q, &c, NULL, 0); } @@ -696,7 +696,7 @@ static int apple_nvme_remove_cq(struct apple_nvme *anv) struct nvme_command c = {}; c.delete_queue.opcode = nvme_admin_delete_cq; - c.delete_queue.qid = 1; + c.delete_queue.qid = cpu_to_le16(1); return nvme_submit_sync_cmd(anv->ctrl.admin_q, &c, NULL, 0); } @@ -710,11 +710,11 @@ static int apple_nvme_create_sq(struct apple_nvme *anv) * is attached to the request. */ c.create_sq.opcode = nvme_admin_create_sq; - c.create_sq.prp1 = anv->ioq.sq_dma_addr; - c.create_sq.sqid = 1; - c.create_sq.qsize = APPLE_ANS_MAX_QUEUE_DEPTH - 1; - c.create_sq.sq_flags = NVME_QUEUE_PHYS_CONTIG; - c.create_sq.cqid = 1; + c.create_sq.prp1 = cpu_to_le64(anv->ioq.sq_dma_addr); + c.create_sq.sqid = cpu_to_le16(1); + c.create_sq.qsize = cpu_to_le16(APPLE_ANS_MAX_QUEUE_DEPTH - 1); + c.create_sq.sq_flags = cpu_to_le16(NVME_QUEUE_PHYS_CONTIG); + c.create_sq.cqid = cpu_to_le16(1); return nvme_submit_sync_cmd(anv->ctrl.admin_q, &c, NULL, 0); } @@ -724,7 +724,7 @@ static int apple_nvme_remove_sq(struct apple_nvme *anv) struct nvme_command c = {}; c.delete_queue.opcode = nvme_admin_delete_sq; - c.delete_queue.qid = 1; + c.delete_queue.qid = cpu_to_le16(1); return nvme_submit_sync_cmd(anv->ctrl.admin_q, &c, NULL, 0); } -- cgit v1.2.3 From 72c81bb67026a07d7cd40418520269e12f0657cb Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 6 May 2022 15:23:09 +0200 Subject: memory: tegra: Add Tegra234 support The memory controller and external memory controller found on Tegra234 is similar to the version found on earlier SoCs but supports a number of new memory clients. Add initial memory client definitions for the Tegra234 so that the SMMU stream ID override registers can be properly programmed at boot time. Signed-off-by: Thierry Reding Link: https://lore.kernel.org/r/20220506132312.3910637-2-thierry.reding@gmail.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/tegra/Makefile | 2 + drivers/memory/tegra/mc.c | 3 ++ drivers/memory/tegra/mc.h | 7 +++- drivers/memory/tegra/tegra186-emc.c | 3 ++ drivers/memory/tegra/tegra234.c | 81 +++++++++++++++++++++++++++++++++++++ 5 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 drivers/memory/tegra/tegra234.c diff --git a/drivers/memory/tegra/Makefile b/drivers/memory/tegra/Makefile index c992e87782d2..0750847dac3c 100644 --- a/drivers/memory/tegra/Makefile +++ b/drivers/memory/tegra/Makefile @@ -9,6 +9,7 @@ tegra-mc-$(CONFIG_ARCH_TEGRA_132_SOC) += tegra124.o tegra-mc-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210.o tegra-mc-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o tegra-mc-$(CONFIG_ARCH_TEGRA_194_SOC) += tegra186.o tegra194.o +tegra-mc-$(CONFIG_ARCH_TEGRA_234_SOC) += tegra186.o tegra234.o obj-$(CONFIG_TEGRA_MC) += tegra-mc.o @@ -19,5 +20,6 @@ obj-$(CONFIG_TEGRA210_EMC_TABLE) += tegra210-emc-table.o obj-$(CONFIG_TEGRA210_EMC) += tegra210-emc.o obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186-emc.o obj-$(CONFIG_ARCH_TEGRA_194_SOC) += tegra186-emc.o +obj-$(CONFIG_ARCH_TEGRA_234_SOC) += tegra186-emc.o tegra210-emc-y := tegra210-emc-core.o tegra210-emc-cc-r21021.o diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index 44b4a4080920..bf3abb6d8354 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -44,6 +44,9 @@ static const struct of_device_id tegra_mc_of_match[] = { #endif #ifdef CONFIG_ARCH_TEGRA_194_SOC { .compatible = "nvidia,tegra194-mc", .data = &tegra194_mc_soc }, +#endif +#ifdef CONFIG_ARCH_TEGRA_234_SOC + { .compatible = "nvidia,tegra234-mc", .data = &tegra234_mc_soc }, #endif { /* sentinel */ } }; diff --git a/drivers/memory/tegra/mc.h b/drivers/memory/tegra/mc.h index 1e492989c363..062886e94c04 100644 --- a/drivers/memory/tegra/mc.h +++ b/drivers/memory/tegra/mc.h @@ -137,6 +137,10 @@ extern const struct tegra_mc_soc tegra186_mc_soc; extern const struct tegra_mc_soc tegra194_mc_soc; #endif +#ifdef CONFIG_ARCH_TEGRA_234_SOC +extern const struct tegra_mc_soc tegra234_mc_soc; +#endif + #if defined(CONFIG_ARCH_TEGRA_3x_SOC) || \ defined(CONFIG_ARCH_TEGRA_114_SOC) || \ defined(CONFIG_ARCH_TEGRA_124_SOC) || \ @@ -147,7 +151,8 @@ extern const struct tegra_mc_ops tegra30_mc_ops; #endif #if defined(CONFIG_ARCH_TEGRA_186_SOC) || \ - defined(CONFIG_ARCH_TEGRA_194_SOC) + defined(CONFIG_ARCH_TEGRA_194_SOC) || \ + defined(CONFIG_ARCH_TEGRA_234_SOC) extern const struct tegra_mc_ops tegra186_mc_ops; #endif diff --git a/drivers/memory/tegra/tegra186-emc.c b/drivers/memory/tegra/tegra186-emc.c index 746c4ef2c0af..54b47ca33483 100644 --- a/drivers/memory/tegra/tegra186-emc.c +++ b/drivers/memory/tegra/tegra186-emc.c @@ -272,6 +272,9 @@ static const struct of_device_id tegra186_emc_of_match[] = { #endif #if defined(CONFIG_ARCH_TEGRA_194_SOC) { .compatible = "nvidia,tegra194-emc" }, +#endif +#if defined(CONFIG_ARCH_TEGRA_234_SOC) + { .compatible = "nvidia,tegra234-emc" }, #endif { /* sentinel */ } }; diff --git a/drivers/memory/tegra/tegra234.c b/drivers/memory/tegra/tegra234.c new file mode 100644 index 000000000000..45efc5139960 --- /dev/null +++ b/drivers/memory/tegra/tegra234.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 NVIDIA CORPORATION. All rights reserved. + */ + +#include + +#include + +#include "mc.h" + +static const struct tegra_mc_client tegra234_mc_clients[] = { + { + .id = TEGRA234_MEMORY_CLIENT_SDMMCRAB, + .name = "sdmmcrab", + .sid = TEGRA234_SID_SDMMC4, + .regs = { + .sid = { + .override = 0x318, + .security = 0x31c, + }, + }, + }, { + .id = TEGRA234_MEMORY_CLIENT_SDMMCWAB, + .name = "sdmmcwab", + .sid = TEGRA234_SID_SDMMC4, + .regs = { + .sid = { + .override = 0x338, + .security = 0x33c, + }, + }, + }, { + .id = TEGRA234_MEMORY_CLIENT_BPMPR, + .name = "bpmpr", + .sid = TEGRA234_SID_BPMP, + .regs = { + .sid = { + .override = 0x498, + .security = 0x49c, + }, + }, + }, { + .id = TEGRA234_MEMORY_CLIENT_BPMPW, + .name = "bpmpw", + .sid = TEGRA234_SID_BPMP, + .regs = { + .sid = { + .override = 0x4a0, + .security = 0x4a4, + }, + }, + }, { + .id = TEGRA234_MEMORY_CLIENT_BPMPDMAR, + .name = "bpmpdmar", + .sid = TEGRA234_SID_BPMP, + .regs = { + .sid = { + .override = 0x4a8, + .security = 0x4ac, + }, + }, + }, { + .id = TEGRA234_MEMORY_CLIENT_BPMPDMAW, + .name = "bpmpdmaw", + .sid = TEGRA234_SID_BPMP, + .regs = { + .sid = { + .override = 0x4b0, + .security = 0x4b4, + }, + }, + }, +}; + +const struct tegra_mc_soc tegra234_mc_soc = { + .num_clients = ARRAY_SIZE(tegra234_mc_clients), + .clients = tegra234_mc_clients, + .num_address_bits = 40, + .ops = &tegra186_mc_ops, +}; -- cgit v1.2.3 From cc3d696c01d83dfb2009a2d7ffbb330d2b506ac9 Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Fri, 6 May 2022 15:23:10 +0200 Subject: memory: tegra: Add APE memory clients for Tegra234 Add the memory clients on Tegra234 which are needed for APE DMA to properly use the SMMU. Signed-off-by: Sameer Pujar Signed-off-by: Thierry Reding Link: https://lore.kernel.org/r/20220506132312.3910637-3-thierry.reding@gmail.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/tegra/tegra234.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/memory/tegra/tegra234.c b/drivers/memory/tegra/tegra234.c index 45efc5139960..e22824a79f45 100644 --- a/drivers/memory/tegra/tegra234.c +++ b/drivers/memory/tegra/tegra234.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2021 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2021-2022, NVIDIA CORPORATION. All rights reserved. */ #include @@ -70,6 +70,26 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { .security = 0x4b4, }, }, + }, { + .id = TEGRA234_MEMORY_CLIENT_APEDMAR, + .name = "apedmar", + .sid = TEGRA234_SID_APE, + .regs = { + .sid = { + .override = 0x4f8, + .security = 0x4fc, + }, + }, + }, { + .id = TEGRA234_MEMORY_CLIENT_APEDMAW, + .name = "apedmaw", + .sid = TEGRA234_SID_APE, + .regs = { + .sid = { + .override = 0x500, + .security = 0x504, + }, + }, }, }; -- cgit v1.2.3 From a7cffa11fa9232eabf0c4f441dc53002978ab526 Mon Sep 17 00:00:00 2001 From: Ashish Mhetre Date: Fri, 6 May 2022 15:23:11 +0200 Subject: memory: tegra: Add memory controller channels support From Tegra186 onwards, the memory controller supports multiple channels. Add support for mapping the address spaces of these channels and specify the number of channels supported by Tegra186, Tegra194 and Tegra234. In case of old bindings, channels won't be present. If channels are not present then print a warning and continue so that backward compatibility will be preserved in driver. During error interrupts from memory controller, appropriate registers from these channels need to be accessed for logging error info. Signed-off-by: Ashish Mhetre Reviewed-by: Dmitry Osipenko Signed-off-by: Thierry Reding Link: https://lore.kernel.org/r/20220506132312.3910637-4-thierry.reding@gmail.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/tegra/tegra186.c | 30 ++++++++++++++++++++++++++++++ drivers/memory/tegra/tegra194.c | 1 + drivers/memory/tegra/tegra234.c | 1 + include/soc/tegra/mc.h | 3 +++ 4 files changed, 35 insertions(+) diff --git a/drivers/memory/tegra/tegra186.c b/drivers/memory/tegra/tegra186.c index 3d153881abc1..4a84752403d8 100644 --- a/drivers/memory/tegra/tegra186.c +++ b/drivers/memory/tegra/tegra186.c @@ -48,8 +48,37 @@ static void tegra186_mc_program_sid(struct tegra_mc *mc) static int tegra186_mc_probe(struct tegra_mc *mc) { + struct platform_device *pdev = to_platform_device(mc->dev); + unsigned int i; + char name[8]; int err; + mc->bcast_ch_regs = devm_platform_ioremap_resource_byname(pdev, "broadcast"); + if (IS_ERR(mc->bcast_ch_regs)) { + if (PTR_ERR(mc->bcast_ch_regs) == -EINVAL) { + dev_warn(&pdev->dev, + "Broadcast channel is missing, please update your device-tree\n"); + mc->bcast_ch_regs = NULL; + goto populate; + } + + return PTR_ERR(mc->bcast_ch_regs); + } + + mc->ch_regs = devm_kcalloc(mc->dev, mc->soc->num_channels, sizeof(*mc->ch_regs), + GFP_KERNEL); + if (!mc->ch_regs) + return -ENOMEM; + + for (i = 0; i < mc->soc->num_channels; i++) { + snprintf(name, sizeof(name), "ch%u", i); + + mc->ch_regs[i] = devm_platform_ioremap_resource_byname(pdev, name); + if (IS_ERR(mc->ch_regs[i])) + return PTR_ERR(mc->ch_regs[i]); + } + +populate: err = of_platform_populate(mc->dev->of_node, NULL, NULL, mc->dev); if (err < 0) return err; @@ -875,6 +904,7 @@ const struct tegra_mc_soc tegra186_mc_soc = { .num_clients = ARRAY_SIZE(tegra186_mc_clients), .clients = tegra186_mc_clients, .num_address_bits = 40, + .num_channels = 4, .ops = &tegra186_mc_ops, }; #endif diff --git a/drivers/memory/tegra/tegra194.c b/drivers/memory/tegra/tegra194.c index cab998b8bd5c..94001174deaf 100644 --- a/drivers/memory/tegra/tegra194.c +++ b/drivers/memory/tegra/tegra194.c @@ -1347,5 +1347,6 @@ const struct tegra_mc_soc tegra194_mc_soc = { .num_clients = ARRAY_SIZE(tegra194_mc_clients), .clients = tegra194_mc_clients, .num_address_bits = 40, + .num_channels = 16, .ops = &tegra186_mc_ops, }; diff --git a/drivers/memory/tegra/tegra234.c b/drivers/memory/tegra/tegra234.c index e22824a79f45..6335a132be2d 100644 --- a/drivers/memory/tegra/tegra234.c +++ b/drivers/memory/tegra/tegra234.c @@ -97,5 +97,6 @@ const struct tegra_mc_soc tegra234_mc_soc = { .num_clients = ARRAY_SIZE(tegra234_mc_clients), .clients = tegra234_mc_clients, .num_address_bits = 40, + .num_channels = 16, .ops = &tegra186_mc_ops, }; diff --git a/include/soc/tegra/mc.h b/include/soc/tegra/mc.h index 1066b1194a5a..40f1d02a1358 100644 --- a/include/soc/tegra/mc.h +++ b/include/soc/tegra/mc.h @@ -194,6 +194,7 @@ struct tegra_mc_soc { unsigned int atom_size; u8 client_id_mask; + u8 num_channels; const struct tegra_smmu_soc *smmu; @@ -212,6 +213,8 @@ struct tegra_mc { struct tegra_smmu *smmu; struct gart_device *gart; void __iomem *regs; + void __iomem *bcast_ch_regs; + void __iomem **ch_regs; struct clk *clk; int irq; -- cgit v1.2.3 From 54a85e09f44c5fa322a2d186f50862d09f517225 Mon Sep 17 00:00:00 2001 From: Ashish Mhetre Date: Fri, 6 May 2022 15:23:12 +0200 Subject: memory: tegra: Add MC error logging on Tegra186 onward Add support for logging memory controller errors on Tegra186, Tegra194 and Tegra234. On these SoCs, interrupts can occur on multiple channels. Add support required to read the status of interrupts across multiple channels, log and clear them. Also add new interrupts supported on these SoCs. Reviewed-by: Dmitry Osipenko Signed-off-by: Ashish Mhetre Signed-off-by: Thierry Reding Link: https://lore.kernel.org/r/20220506132312.3910637-5-thierry.reding@gmail.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/tegra/mc.c | 134 +++++++++++++++++++++++++++++++++++----- drivers/memory/tegra/mc.h | 43 ++++++++++++- drivers/memory/tegra/tegra186.c | 9 +++ drivers/memory/tegra/tegra194.c | 8 +++ drivers/memory/tegra/tegra234.c | 8 +++ include/soc/tegra/mc.h | 5 +- 6 files changed, 189 insertions(+), 18 deletions(-) diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index bf3abb6d8354..8395ab6046cf 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -508,14 +508,54 @@ int tegra30_mc_probe(struct tegra_mc *mc) return 0; } -static irqreturn_t tegra30_mc_handle_irq(int irq, void *data) +const struct tegra_mc_ops tegra30_mc_ops = { + .probe = tegra30_mc_probe, + .handle_irq = tegra30_mc_handle_irq, +}; +#endif + +static int mc_global_intstatus_to_channel(const struct tegra_mc *mc, u32 status, + unsigned int *mc_channel) +{ + if ((status & mc->soc->ch_intmask) == 0) + return -EINVAL; + + *mc_channel = __ffs((status & mc->soc->ch_intmask) >> + mc->soc->global_intstatus_channel_shift); + + return 0; +} + +static u32 mc_channel_to_global_intstatus(const struct tegra_mc *mc, + unsigned int channel) +{ + return BIT(channel) << mc->soc->global_intstatus_channel_shift; +} + +irqreturn_t tegra30_mc_handle_irq(int irq, void *data) { struct tegra_mc *mc = data; + unsigned int bit, channel; unsigned long status; - unsigned int bit; - /* mask all interrupts to avoid flooding */ - status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmask; + if (mc->soc->num_channels) { + u32 global_status; + int err; + + global_status = mc_ch_readl(mc, MC_BROADCAST_CHANNEL, MC_GLOBAL_INTSTATUS); + err = mc_global_intstatus_to_channel(mc, global_status, &channel); + if (err < 0) { + dev_err_ratelimited(mc->dev, "unknown interrupt channel 0x%08x\n", + global_status); + return IRQ_NONE; + } + + /* mask all interrupts to avoid flooding */ + status = mc_ch_readl(mc, channel, MC_INTSTATUS) & mc->soc->intmask; + } else { + status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmask; + } + if (!status) return IRQ_NONE; @@ -523,18 +563,70 @@ static irqreturn_t tegra30_mc_handle_irq(int irq, void *data) const char *error = tegra_mc_status_names[bit] ?: "unknown"; const char *client = "unknown", *desc; const char *direction, *secure; + u32 status_reg, addr_reg; + u32 intmask = BIT(bit); phys_addr_t addr = 0; +#ifdef CONFIG_PHYS_ADDR_T_64BIT + u32 addr_hi_reg = 0; +#endif unsigned int i; char perm[7]; u8 id, type; u32 value; - value = mc_readl(mc, MC_ERR_STATUS); + switch (intmask) { + case MC_INT_DECERR_VPR: + status_reg = MC_ERR_VPR_STATUS; + addr_reg = MC_ERR_VPR_ADR; + break; + + case MC_INT_SECERR_SEC: + status_reg = MC_ERR_SEC_STATUS; + addr_reg = MC_ERR_SEC_ADR; + break; + + case MC_INT_DECERR_MTS: + status_reg = MC_ERR_MTS_STATUS; + addr_reg = MC_ERR_MTS_ADR; + break; + + case MC_INT_DECERR_GENERALIZED_CARVEOUT: + status_reg = MC_ERR_GENERALIZED_CARVEOUT_STATUS; + addr_reg = MC_ERR_GENERALIZED_CARVEOUT_ADR; + break; + + case MC_INT_DECERR_ROUTE_SANITY: + status_reg = MC_ERR_ROUTE_SANITY_STATUS; + addr_reg = MC_ERR_ROUTE_SANITY_ADR; + break; + + default: + status_reg = MC_ERR_STATUS; + addr_reg = MC_ERR_ADR; + +#ifdef CONFIG_PHYS_ADDR_T_64BIT + if (mc->soc->has_addr_hi_reg) + addr_hi_reg = MC_ERR_ADR_HI; +#endif + break; + } + + if (mc->soc->num_channels) + value = mc_ch_readl(mc, channel, status_reg); + else + value = mc_readl(mc, status_reg); #ifdef CONFIG_PHYS_ADDR_T_64BIT if (mc->soc->num_address_bits > 32) { - addr = ((value >> MC_ERR_STATUS_ADR_HI_SHIFT) & - MC_ERR_STATUS_ADR_HI_MASK); + if (addr_hi_reg) { + if (mc->soc->num_channels) + addr = mc_ch_readl(mc, channel, addr_hi_reg); + else + addr = mc_readl(mc, addr_hi_reg); + } else { + addr = ((value >> MC_ERR_STATUS_ADR_HI_SHIFT) & + MC_ERR_STATUS_ADR_HI_MASK); + } addr <<= 32; } #endif @@ -591,7 +683,10 @@ static irqreturn_t tegra30_mc_handle_irq(int irq, void *data) break; } - value = mc_readl(mc, MC_ERR_ADR); + if (mc->soc->num_channels) + value = mc_ch_readl(mc, channel, addr_reg); + else + value = mc_readl(mc, addr_reg); addr |= value; dev_err_ratelimited(mc->dev, "%s: %s%s @%pa: %s (%s%s)\n", @@ -600,17 +695,18 @@ static irqreturn_t tegra30_mc_handle_irq(int irq, void *data) } /* clear interrupts */ - mc_writel(mc, status, MC_INTSTATUS); + if (mc->soc->num_channels) { + mc_ch_writel(mc, channel, status, MC_INTSTATUS); + mc_ch_writel(mc, MC_BROADCAST_CHANNEL, + mc_channel_to_global_intstatus(mc, channel), + MC_GLOBAL_INTSTATUS); + } else { + mc_writel(mc, status, MC_INTSTATUS); + } return IRQ_HANDLED; } -const struct tegra_mc_ops tegra30_mc_ops = { - .probe = tegra30_mc_probe, - .handle_irq = tegra30_mc_handle_irq, -}; -#endif - const char *const tegra_mc_status_names[32] = { [ 1] = "External interrupt", [ 6] = "EMEM address decode error", @@ -622,6 +718,8 @@ const char *const tegra_mc_status_names[32] = { [12] = "VPR violation", [13] = "Secure carveout violation", [16] = "MTS carveout violation", + [17] = "Generalized carveout violation", + [20] = "Route Sanity error", }; const char *const tegra_mc_error_names[8] = { @@ -764,7 +862,11 @@ static int tegra_mc_probe(struct platform_device *pdev) WARN(!mc->soc->client_id_mask, "missing client ID mask for this SoC\n"); - mc_writel(mc, mc->soc->intmask, MC_INTMASK); + if (mc->soc->num_channels) + mc_ch_writel(mc, MC_BROADCAST_CHANNEL, mc->soc->intmask, + MC_INTMASK); + else + mc_writel(mc, mc->soc->intmask, MC_INTMASK); err = devm_request_irq(&pdev->dev, mc->irq, mc->soc->ops->handle_irq, 0, dev_name(&pdev->dev), mc); diff --git a/drivers/memory/tegra/mc.h b/drivers/memory/tegra/mc.h index 062886e94c04..bc01586b6560 100644 --- a/drivers/memory/tegra/mc.h +++ b/drivers/memory/tegra/mc.h @@ -43,7 +43,21 @@ #define MC_EMEM_ARB_OVERRIDE 0xe8 #define MC_TIMING_CONTROL_DBG 0xf8 #define MC_TIMING_CONTROL 0xfc - +#define MC_ERR_VPR_STATUS 0x654 +#define MC_ERR_VPR_ADR 0x658 +#define MC_ERR_SEC_STATUS 0x67c +#define MC_ERR_SEC_ADR 0x680 +#define MC_ERR_MTS_STATUS 0x9b0 +#define MC_ERR_MTS_ADR 0x9b4 +#define MC_ERR_ROUTE_SANITY_STATUS 0x9c0 +#define MC_ERR_ROUTE_SANITY_ADR 0x9c4 +#define MC_ERR_GENERALIZED_CARVEOUT_STATUS 0xc00 +#define MC_ERR_GENERALIZED_CARVEOUT_ADR 0xc04 +#define MC_GLOBAL_INTSTATUS 0xf24 +#define MC_ERR_ADR_HI 0x11fc + +#define MC_INT_DECERR_ROUTE_SANITY BIT(20) +#define MC_INT_DECERR_GENERALIZED_CARVEOUT BIT(17) #define MC_INT_DECERR_MTS BIT(16) #define MC_INT_SECERR_SEC BIT(13) #define MC_INT_DECERR_VPR BIT(12) @@ -78,6 +92,8 @@ #define MC_TIMING_UPDATE BIT(0) +#define MC_BROADCAST_CHANNEL ~0 + static inline u32 tegra_mc_scale_percents(u64 val, unsigned int percents) { val = val * percents; @@ -92,6 +108,30 @@ icc_provider_to_tegra_mc(struct icc_provider *provider) return container_of(provider, struct tegra_mc, provider); } +static inline u32 mc_ch_readl(const struct tegra_mc *mc, int ch, + unsigned long offset) +{ + if (!mc->bcast_ch_regs) + return 0; + + if (ch == MC_BROADCAST_CHANNEL) + return readl_relaxed(mc->bcast_ch_regs + offset); + + return readl_relaxed(mc->ch_regs[ch] + offset); +} + +static inline void mc_ch_writel(const struct tegra_mc *mc, int ch, + u32 value, unsigned long offset) +{ + if (!mc->bcast_ch_regs) + return; + + if (ch == MC_BROADCAST_CHANNEL) + writel_relaxed(value, mc->bcast_ch_regs + offset); + else + writel_relaxed(value, mc->ch_regs[ch] + offset); +} + static inline u32 mc_readl(const struct tegra_mc *mc, unsigned long offset) { return readl_relaxed(mc->regs + offset); @@ -156,6 +196,7 @@ extern const struct tegra_mc_ops tegra30_mc_ops; extern const struct tegra_mc_ops tegra186_mc_ops; #endif +irqreturn_t tegra30_mc_handle_irq(int irq, void *data); extern const char * const tegra_mc_status_names[32]; extern const char * const tegra_mc_error_names[8]; diff --git a/drivers/memory/tegra/tegra186.c b/drivers/memory/tegra/tegra186.c index 4a84752403d8..62477e592bf5 100644 --- a/drivers/memory/tegra/tegra186.c +++ b/drivers/memory/tegra/tegra186.c @@ -16,6 +16,8 @@ #include #endif +#include "mc.h" + #define MC_SID_STREAMID_OVERRIDE_MASK GENMASK(7, 0) #define MC_SID_STREAMID_SECURITY_WRITE_ACCESS_DISABLED BIT(16) #define MC_SID_STREAMID_SECURITY_OVERRIDE BIT(8) @@ -173,6 +175,7 @@ const struct tegra_mc_ops tegra186_mc_ops = { .remove = tegra186_mc_remove, .resume = tegra186_mc_resume, .probe_device = tegra186_mc_probe_device, + .handle_irq = tegra30_mc_handle_irq, }; #if defined(CONFIG_ARCH_TEGRA_186_SOC) @@ -905,6 +908,12 @@ const struct tegra_mc_soc tegra186_mc_soc = { .clients = tegra186_mc_clients, .num_address_bits = 40, .num_channels = 4, + .client_id_mask = 0xff, + .intmask = MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS | + MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | + MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM, .ops = &tegra186_mc_ops, + .ch_intmask = 0x0000000f, + .global_intstatus_channel_shift = 0, }; #endif diff --git a/drivers/memory/tegra/tegra194.c b/drivers/memory/tegra/tegra194.c index 94001174deaf..b2416ee3ac26 100644 --- a/drivers/memory/tegra/tegra194.c +++ b/drivers/memory/tegra/tegra194.c @@ -1348,5 +1348,13 @@ const struct tegra_mc_soc tegra194_mc_soc = { .clients = tegra194_mc_clients, .num_address_bits = 40, .num_channels = 16, + .client_id_mask = 0xff, + .intmask = MC_INT_DECERR_ROUTE_SANITY | + MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS | + MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | + MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM, + .has_addr_hi_reg = true, .ops = &tegra186_mc_ops, + .ch_intmask = 0x00000f00, + .global_intstatus_channel_shift = 8, }; diff --git a/drivers/memory/tegra/tegra234.c b/drivers/memory/tegra/tegra234.c index 6335a132be2d..e23ebd421f17 100644 --- a/drivers/memory/tegra/tegra234.c +++ b/drivers/memory/tegra/tegra234.c @@ -98,5 +98,13 @@ const struct tegra_mc_soc tegra234_mc_soc = { .clients = tegra234_mc_clients, .num_address_bits = 40, .num_channels = 16, + .client_id_mask = 0x1ff, + .intmask = MC_INT_DECERR_ROUTE_SANITY | + MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS | + MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | + MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM, + .has_addr_hi_reg = true, .ops = &tegra186_mc_ops, + .ch_intmask = 0x0000ff00, + .global_intstatus_channel_shift = 8, }; diff --git a/include/soc/tegra/mc.h b/include/soc/tegra/mc.h index 40f1d02a1358..47ce6d434427 100644 --- a/include/soc/tegra/mc.h +++ b/include/soc/tegra/mc.h @@ -193,12 +193,15 @@ struct tegra_mc_soc { unsigned int num_address_bits; unsigned int atom_size; - u8 client_id_mask; + u16 client_id_mask; u8 num_channels; const struct tegra_smmu_soc *smmu; u32 intmask; + u32 ch_intmask; + u32 global_intstatus_channel_shift; + bool has_addr_hi_reg; const struct tegra_mc_reset_ops *reset_ops; const struct tegra_mc_reset *resets; -- cgit v1.2.3