From 698753e4a51a5a6670d527a3db084c3616253abc Mon Sep 17 00:00:00 2001 From: Clément Le Goffic Date: Tue, 10 Feb 2026 20:05:45 +0100 Subject: bus: firewall: move stm32_firewall header file in include folder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Other driver than RIFSC and ETZPC can implement firewall ops, such as RCC. In order for them to have access to the ops and type of this framework, we need to get the `stm32_firewall.h` file in the include/ folder. Signed-off-by: Clément Le Goffic Signed-off-by: Clément Le Goffic Acked-by: Gatien Chevallier Link: https://lore.kernel.org/r/20260210-b4-firewall-upstream-v8-1-097c1e47af82@gmail.com Signed-off-by: Alexandre Torgue --- drivers/bus/stm32_etzpc.c | 3 +- drivers/bus/stm32_firewall.c | 3 +- drivers/bus/stm32_firewall.h | 83 -------------------------------------- drivers/bus/stm32_rifsc.c | 3 +- include/linux/bus/stm32_firewall.h | 83 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 86 insertions(+), 89 deletions(-) delete mode 100644 drivers/bus/stm32_firewall.h create mode 100644 include/linux/bus/stm32_firewall.h diff --git a/drivers/bus/stm32_etzpc.c b/drivers/bus/stm32_etzpc.c index 7fc0f16960be..4918a14e507e 100644 --- a/drivers/bus/stm32_etzpc.c +++ b/drivers/bus/stm32_etzpc.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -16,8 +17,6 @@ #include #include -#include "stm32_firewall.h" - /* * ETZPC registers */ diff --git a/drivers/bus/stm32_firewall.c b/drivers/bus/stm32_firewall.c index 7e7afe8007db..bbcf42ad668a 100644 --- a/drivers/bus/stm32_firewall.c +++ b/drivers/bus/stm32_firewall.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -18,8 +19,6 @@ #include #include -#include "stm32_firewall.h" - /* Corresponds to STM32_FIREWALL_MAX_EXTRA_ARGS + firewall ID */ #define STM32_FIREWALL_MAX_ARGS (STM32_FIREWALL_MAX_EXTRA_ARGS + 1) diff --git a/drivers/bus/stm32_firewall.h b/drivers/bus/stm32_firewall.h deleted file mode 100644 index e5fac85fe346..000000000000 --- a/drivers/bus/stm32_firewall.h +++ /dev/null @@ -1,83 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) 2023, STMicroelectronics - All Rights Reserved - */ - -#ifndef _STM32_FIREWALL_H -#define _STM32_FIREWALL_H - -#include -#include -#include -#include -#include - -/** - * STM32_PERIPHERAL_FIREWALL: This type of firewall protects peripherals - * STM32_MEMORY_FIREWALL: This type of firewall protects memories/subsets of memory - * zones - * STM32_NOTYPE_FIREWALL: Undefined firewall type - */ - -#define STM32_PERIPHERAL_FIREWALL BIT(1) -#define STM32_MEMORY_FIREWALL BIT(2) -#define STM32_NOTYPE_FIREWALL BIT(3) - -/** - * struct stm32_firewall_controller - Information on firewall controller supplying services - * - * @name: Name of the firewall controller - * @dev: Device reference of the firewall controller - * @mmio: Base address of the firewall controller - * @entry: List entry of the firewall controller list - * @type: Type of firewall - * @max_entries: Number of entries covered by the firewall - * @grant_access: Callback used to grant access for a device access against a - * firewall controller - * @release_access: Callback used to release resources taken by a device when access was - * granted - * @grant_memory_range_access: Callback used to grant access for a device to a given memory region - */ -struct stm32_firewall_controller { - const char *name; - struct device *dev; - void __iomem *mmio; - struct list_head entry; - unsigned int type; - unsigned int max_entries; - - int (*grant_access)(struct stm32_firewall_controller *ctrl, u32 id); - void (*release_access)(struct stm32_firewall_controller *ctrl, u32 id); - int (*grant_memory_range_access)(struct stm32_firewall_controller *ctrl, phys_addr_t paddr, - size_t size); -}; - -/** - * stm32_firewall_controller_register - Register a firewall controller to the STM32 firewall - * framework - * @firewall_controller: Firewall controller to register - * - * Returns 0 in case of success or -ENODEV if no controller was given. - */ -int stm32_firewall_controller_register(struct stm32_firewall_controller *firewall_controller); - -/** - * stm32_firewall_controller_unregister - Unregister a firewall controller from the STM32 - * firewall framework - * @firewall_controller: Firewall controller to unregister - */ -void stm32_firewall_controller_unregister(struct stm32_firewall_controller *firewall_controller); - -/** - * stm32_firewall_populate_bus - Populate device tree nodes that have a correct firewall - * configuration. This is used at boot-time only, as a sanity check - * between device tree and firewalls hardware configurations to - * prevent a kernel crash when a device driver is not granted access - * - * @firewall_controller: Firewall controller which nodes will be populated or not - * - * Returns 0 in case of success or appropriate errno code if error occurred. - */ -int stm32_firewall_populate_bus(struct stm32_firewall_controller *firewall_controller); - -#endif /* _STM32_FIREWALL_H */ diff --git a/drivers/bus/stm32_rifsc.c b/drivers/bus/stm32_rifsc.c index debeaf8ea1bd..65990ae8dd08 100644 --- a/drivers/bus/stm32_rifsc.c +++ b/drivers/bus/stm32_rifsc.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -17,8 +18,6 @@ #include #include -#include "stm32_firewall.h" - /* * RIFSC offset register */ diff --git a/include/linux/bus/stm32_firewall.h b/include/linux/bus/stm32_firewall.h new file mode 100644 index 000000000000..e5fac85fe346 --- /dev/null +++ b/include/linux/bus/stm32_firewall.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2023, STMicroelectronics - All Rights Reserved + */ + +#ifndef _STM32_FIREWALL_H +#define _STM32_FIREWALL_H + +#include +#include +#include +#include +#include + +/** + * STM32_PERIPHERAL_FIREWALL: This type of firewall protects peripherals + * STM32_MEMORY_FIREWALL: This type of firewall protects memories/subsets of memory + * zones + * STM32_NOTYPE_FIREWALL: Undefined firewall type + */ + +#define STM32_PERIPHERAL_FIREWALL BIT(1) +#define STM32_MEMORY_FIREWALL BIT(2) +#define STM32_NOTYPE_FIREWALL BIT(3) + +/** + * struct stm32_firewall_controller - Information on firewall controller supplying services + * + * @name: Name of the firewall controller + * @dev: Device reference of the firewall controller + * @mmio: Base address of the firewall controller + * @entry: List entry of the firewall controller list + * @type: Type of firewall + * @max_entries: Number of entries covered by the firewall + * @grant_access: Callback used to grant access for a device access against a + * firewall controller + * @release_access: Callback used to release resources taken by a device when access was + * granted + * @grant_memory_range_access: Callback used to grant access for a device to a given memory region + */ +struct stm32_firewall_controller { + const char *name; + struct device *dev; + void __iomem *mmio; + struct list_head entry; + unsigned int type; + unsigned int max_entries; + + int (*grant_access)(struct stm32_firewall_controller *ctrl, u32 id); + void (*release_access)(struct stm32_firewall_controller *ctrl, u32 id); + int (*grant_memory_range_access)(struct stm32_firewall_controller *ctrl, phys_addr_t paddr, + size_t size); +}; + +/** + * stm32_firewall_controller_register - Register a firewall controller to the STM32 firewall + * framework + * @firewall_controller: Firewall controller to register + * + * Returns 0 in case of success or -ENODEV if no controller was given. + */ +int stm32_firewall_controller_register(struct stm32_firewall_controller *firewall_controller); + +/** + * stm32_firewall_controller_unregister - Unregister a firewall controller from the STM32 + * firewall framework + * @firewall_controller: Firewall controller to unregister + */ +void stm32_firewall_controller_unregister(struct stm32_firewall_controller *firewall_controller); + +/** + * stm32_firewall_populate_bus - Populate device tree nodes that have a correct firewall + * configuration. This is used at boot-time only, as a sanity check + * between device tree and firewalls hardware configurations to + * prevent a kernel crash when a device driver is not granted access + * + * @firewall_controller: Firewall controller which nodes will be populated or not + * + * Returns 0 in case of success or appropriate errno code if error occurred. + */ +int stm32_firewall_populate_bus(struct stm32_firewall_controller *firewall_controller); + +#endif /* _STM32_FIREWALL_H */ -- cgit v1.2.3 From 256b4efd98b1dcbe80e817a67b17c07becb80969 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 5 Jan 2026 15:36:59 +0100 Subject: bus: stm32_firewall: Simplify with scoped for each OF child loop Use scoped for-each loop when iterating over device nodes to make code a bit simpler. Signed-off-by: Krzysztof Kozlowski Tested-by: Gatien Chevallier Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260105143657.383621-5-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Alexandre Torgue --- drivers/bus/stm32_firewall.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/bus/stm32_firewall.c b/drivers/bus/stm32_firewall.c index bbcf42ad668a..b023d1f4fcf7 100644 --- a/drivers/bus/stm32_firewall.c +++ b/drivers/bus/stm32_firewall.c @@ -240,7 +240,6 @@ EXPORT_SYMBOL_GPL(stm32_firewall_controller_unregister); int stm32_firewall_populate_bus(struct stm32_firewall_controller *firewall_controller) { struct stm32_firewall *firewalls; - struct device_node *child; struct device *parent; unsigned int i; int len; @@ -250,25 +249,20 @@ int stm32_firewall_populate_bus(struct stm32_firewall_controller *firewall_contr dev_dbg(parent, "Populating %s system bus\n", dev_name(firewall_controller->dev)); - for_each_available_child_of_node(dev_of_node(parent), child) { + for_each_available_child_of_node_scoped(dev_of_node(parent), child) { /* The access-controllers property is mandatory for firewall bus devices */ len = of_count_phandle_with_args(child, "access-controllers", "#access-controller-cells"); - if (len <= 0) { - of_node_put(child); + if (len <= 0) return -EINVAL; - } firewalls = kzalloc_objs(*firewalls, len); - if (!firewalls) { - of_node_put(child); + if (!firewalls) return -ENOMEM; - } err = stm32_firewall_get_firewall(child, firewalls, (unsigned int)len); if (err) { kfree(firewalls); - of_node_put(child); return err; } -- cgit v1.2.3 From a6e1842be2e4fd590136da5c100fbad6c3129d96 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 23 Feb 2026 22:12:11 +0100 Subject: bus: rifsc: Replace snprintf("%s") with strscpy Replace snprintf("%s", ...) with the faster and more direct strscpy(). Signed-off-by: Thorsten Blum Acked-by: Gatien Chevallier Link: https://lore.kernel.org/r/20260223211212.344855-1-thorsten.blum@linux.dev Signed-off-by: Alexandre Torgue --- drivers/bus/stm32_rifsc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/bus/stm32_rifsc.c b/drivers/bus/stm32_rifsc.c index 65990ae8dd08..ab8d4e3128c3 100644 --- a/drivers/bus/stm32_rifsc.c +++ b/drivers/bus/stm32_rifsc.c @@ -16,6 +16,7 @@ #include #include #include +#include #include /* @@ -449,7 +450,7 @@ static void stm32_rifsc_fill_rimu_dbg_entry(struct rifsc_dbg_private *rifsc, const struct stm32_rifsc_resources_names *dbg_names = rifsc->res_names; u32 rimc_attr = readl_relaxed(rifsc->mmio + RIFSC_RIMC_ATTR0 + 0x4 * i); - snprintf(dbg_entry->m_name, sizeof(dbg_entry->m_name), "%s", dbg_names->initiator_names[i]); + strscpy(dbg_entry->m_name, dbg_names->initiator_names[i]); dbg_entry->m_cid = FIELD_GET(RIFSC_RIMC_MCID_MASK, rimc_attr); dbg_entry->cidsel = rimc_attr & RIFSC_RIMC_CIDSEL; dbg_entry->m_sec = rimc_attr & RIFSC_RIMC_MSEC; @@ -468,8 +469,7 @@ static void stm32_rifsc_fill_dev_dbg_entry(struct rifsc_dbg_private *rifsc, sec_cfgr = readl_relaxed(rifsc->mmio + RIFSC_RISC_SECCFGR0 + 0x4 * reg_id); priv_cfgr = readl_relaxed(rifsc->mmio + RIFSC_RISC_PRIVCFGR0 + 0x4 * reg_id); - snprintf(dbg_entry->dev_name, sizeof(dbg_entry->dev_name), "%s", - dbg_names->device_names[i]); + strscpy(dbg_entry->dev_name, dbg_names->device_names[i]); dbg_entry->dev_id = i; dbg_entry->dev_cid_filt_en = cid_cfgr & CIDCFGR_CFEN; dbg_entry->dev_sem_en = cid_cfgr & CIDCFGR_SEMEN; -- cgit v1.2.3 From d5ce3b4e951bc41a6ce877c8500bb4fe42146669 Mon Sep 17 00:00:00 2001 From: Gatien Chevallier Date: Thu, 29 Jan 2026 13:56:17 +0100 Subject: bus: rifsc: fix RIF configuration check for peripherals Peripheral holding CID0 cannot be accessed, remove this completely incorrect check. While there, fix and simplify the semaphore checking that should be performed when the CID filtering is enabled. Fixes: a18208457253 ("bus: rifsc: introduce RIFSC firewall controller driver") Signed-off-by: Gatien Chevallier Link: https://lore.kernel.org/r/20260129-fix_cid_check_rifsc-v1-1-ef280ccf764d@foss.st.com Signed-off-by: Alexandre Torgue --- drivers/bus/stm32_rifsc.c | 52 ++++++++++++++++------------------------------- 1 file changed, 18 insertions(+), 34 deletions(-) diff --git a/drivers/bus/stm32_rifsc.c b/drivers/bus/stm32_rifsc.c index ab8d4e3128c3..19d10379dcef 100644 --- a/drivers/bus/stm32_rifsc.c +++ b/drivers/bus/stm32_rifsc.c @@ -687,34 +687,6 @@ static int stm32_rifsc_grant_access(struct stm32_firewall_controller *ctrl, u32 sec_reg_value = readl(rifsc_controller->mmio + RIFSC_RISC_SECCFGR0 + 0x4 * reg_id); cid_reg_value = readl(rifsc_controller->mmio + RIFSC_RISC_PER0_CIDCFGR + 0x8 * firewall_id); - /* First check conditions for semaphore mode, which doesn't take into account static CID. */ - if ((cid_reg_value & CIDCFGR_SEMEN) && (cid_reg_value & CIDCFGR_CFEN)) { - if (cid_reg_value & BIT(RIF_CID1 + SEMWL_SHIFT)) { - /* Static CID is irrelevant if semaphore mode */ - goto skip_cid_check; - } else { - dev_dbg(rifsc_controller->dev, - "Invalid bus semaphore configuration: index %d\n", firewall_id); - return -EACCES; - } - } - - /* - * Skip CID check if CID filtering isn't enabled or filtering is enabled on CID0, which - * corresponds to whatever CID. - */ - if (!(cid_reg_value & CIDCFGR_CFEN) || - FIELD_GET(RIFSC_RISC_SCID_MASK, cid_reg_value) == RIF_CID0) - goto skip_cid_check; - - /* Coherency check with the CID configuration */ - if (FIELD_GET(RIFSC_RISC_SCID_MASK, cid_reg_value) != RIF_CID1) { - dev_dbg(rifsc_controller->dev, "Invalid CID configuration for peripheral: %d\n", - firewall_id); - return -EACCES; - } - -skip_cid_check: /* Check security configuration */ if (sec_reg_value & BIT(reg_offset)) { dev_dbg(rifsc_controller->dev, @@ -722,19 +694,31 @@ skip_cid_check: return -EACCES; } - /* - * If the peripheral is in semaphore mode, take the semaphore so that - * the CID1 has the ownership. - */ - if ((cid_reg_value & CIDCFGR_SEMEN) && (cid_reg_value & CIDCFGR_CFEN)) { + /* Skip CID check if CID filtering isn't enabled */ + if (!(cid_reg_value & CIDCFGR_CFEN)) + goto skip_cid_check; + + /* First check conditions for semaphore mode, which doesn't take into account static CID. */ + if (cid_reg_value & CIDCFGR_SEMEN) { + if (!(cid_reg_value & BIT(RIF_CID1 + SEMWL_SHIFT))) { + dev_dbg(rifsc_controller->dev, + "Invalid bus semaphore configuration: index %d\n", firewall_id); + return -EACCES; + } + rc = stm32_rif_acquire_semaphore(rifsc_controller, firewall_id); if (rc) { - dev_err(rifsc_controller->dev, + dev_dbg(rifsc_controller->dev, "Couldn't acquire semaphore for peripheral: %d\n", firewall_id); return rc; } + } else if (FIELD_GET(RIFSC_RISC_SCID_MASK, cid_reg_value) != RIF_CID1) { + dev_dbg(rifsc_controller->dev, "Invalid CID configuration for peripheral: %d\n", + firewall_id); + return -EACCES; } +skip_cid_check: return 0; } -- cgit v1.2.3 From c75617dd949b343ade70f35ecd2d4546738b57bf Mon Sep 17 00:00:00 2001 From: Gatien Chevallier Date: Thu, 26 Feb 2026 11:30:16 +0100 Subject: dt-bindings: document access-controllers property for coresight peripherals Document the access-controllers for coresight peripherals in case some access checks need to be performed to use them. Signed-off-by: Gatien Chevallier Reviewed-by: Rob Herring (Arm) Acked-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260226-debug_bus-v6-1-5d794697798d@foss.st.com Signed-off-by: Alexandre Torgue --- Documentation/devicetree/bindings/arm/arm,coresight-cti.yaml | 3 +++ .../devicetree/bindings/arm/arm,coresight-dynamic-funnel.yaml | 3 +++ Documentation/devicetree/bindings/arm/arm,coresight-etm.yaml | 3 +++ Documentation/devicetree/bindings/arm/arm,coresight-stm.yaml | 3 +++ Documentation/devicetree/bindings/arm/arm,coresight-tmc.yaml | 3 +++ Documentation/devicetree/bindings/arm/arm,coresight-tpiu.yaml | 3 +++ 6 files changed, 18 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/arm,coresight-cti.yaml b/Documentation/devicetree/bindings/arm/arm,coresight-cti.yaml index 2a91670ccb8c..949444aba1f8 100644 --- a/Documentation/devicetree/bindings/arm/arm,coresight-cti.yaml +++ b/Documentation/devicetree/bindings/arm/arm,coresight-cti.yaml @@ -128,6 +128,9 @@ properties: "#address-cells": const: 1 + access-controllers: + maxItems: 1 + patternProperties: '^trig-conns@([0-9]+)$': type: object diff --git a/Documentation/devicetree/bindings/arm/arm,coresight-dynamic-funnel.yaml b/Documentation/devicetree/bindings/arm/arm,coresight-dynamic-funnel.yaml index b74db15e5f8a..b0693cd46d27 100644 --- a/Documentation/devicetree/bindings/arm/arm,coresight-dynamic-funnel.yaml +++ b/Documentation/devicetree/bindings/arm/arm,coresight-dynamic-funnel.yaml @@ -78,6 +78,9 @@ properties: description: Output connection to CoreSight Trace bus $ref: /schemas/graph.yaml#/properties/port + access-controllers: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/arm/arm,coresight-etm.yaml b/Documentation/devicetree/bindings/arm/arm,coresight-etm.yaml index 71f2e1ed27e5..10ebbbeadf93 100644 --- a/Documentation/devicetree/bindings/arm/arm,coresight-etm.yaml +++ b/Documentation/devicetree/bindings/arm/arm,coresight-etm.yaml @@ -118,6 +118,9 @@ properties: description: Output connection from the ETM to CoreSight Trace bus. $ref: /schemas/graph.yaml#/properties/port + access-controllers: + maxItems: 1 + required: - compatible - clocks diff --git a/Documentation/devicetree/bindings/arm/arm,coresight-stm.yaml b/Documentation/devicetree/bindings/arm/arm,coresight-stm.yaml index 378380c3f5aa..f243e76f597f 100644 --- a/Documentation/devicetree/bindings/arm/arm,coresight-stm.yaml +++ b/Documentation/devicetree/bindings/arm/arm,coresight-stm.yaml @@ -73,6 +73,9 @@ properties: description: Output connection to the CoreSight Trace bus. $ref: /schemas/graph.yaml#/properties/port + access-controllers: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/arm/arm,coresight-tmc.yaml b/Documentation/devicetree/bindings/arm/arm,coresight-tmc.yaml index 96dd5b5f771a..9dc096698c65 100644 --- a/Documentation/devicetree/bindings/arm/arm,coresight-tmc.yaml +++ b/Documentation/devicetree/bindings/arm/arm,coresight-tmc.yaml @@ -128,6 +128,9 @@ properties: - const: tracedata - const: metadata + access-controllers: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/arm/arm,coresight-tpiu.yaml b/Documentation/devicetree/bindings/arm/arm,coresight-tpiu.yaml index a207f6899e67..29bbc3961fdf 100644 --- a/Documentation/devicetree/bindings/arm/arm,coresight-tpiu.yaml +++ b/Documentation/devicetree/bindings/arm/arm,coresight-tpiu.yaml @@ -70,6 +70,9 @@ properties: description: Input connection from the CoreSight Trace bus. $ref: /schemas/graph.yaml#/properties/port + access-controllers: + maxItems: 1 + required: - compatible - reg -- cgit v1.2.3 From f7cfd2b29127893982b4f73908df1036cfc0b9ff Mon Sep 17 00:00:00 2001 From: Gatien Chevallier Date: Thu, 26 Feb 2026 11:30:17 +0100 Subject: dt-bindings: pinctrl: document access-controllers property for stm32 HDP HDP being functional depends on the debug configuration on the platform that can be checked using the access-controllers property, document it. Signed-off-by: Gatien Chevallier Acked-by: Rob Herring (Arm) Acked-by: Linus Walleij Link: https://lore.kernel.org/r/20260226-debug_bus-v6-2-5d794697798d@foss.st.com Signed-off-by: Alexandre Torgue --- Documentation/devicetree/bindings/pinctrl/st,stm32-hdp.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/pinctrl/st,stm32-hdp.yaml b/Documentation/devicetree/bindings/pinctrl/st,stm32-hdp.yaml index 845b6b7b7552..8f8b4b68aaa3 100644 --- a/Documentation/devicetree/bindings/pinctrl/st,stm32-hdp.yaml +++ b/Documentation/devicetree/bindings/pinctrl/st,stm32-hdp.yaml @@ -27,6 +27,12 @@ properties: clocks: maxItems: 1 + access-controllers: + minItems: 1 + items: + - description: debug configuration access controller + - description: access controller that manages the HDP as a peripheral + patternProperties: "^hdp[0-7]-pins$": type: object -- cgit v1.2.3 From 2b690a435643bde649a088bfc59be5282acf018f Mon Sep 17 00:00:00 2001 From: Gatien Chevallier Date: Thu, 26 Feb 2026 11:30:18 +0100 Subject: dt-bindings: bus: document the stm32 debug bus Document the stm32 debug bus. The debug bus is responsible for checking the debug sub-system accessibility before probing any related drivers. Signed-off-by: Gatien Chevallier Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20260226-debug_bus-v6-3-5d794697798d@foss.st.com Signed-off-by: Alexandre Torgue --- .../bindings/bus/st,stm32mp131-dbg-bus.yaml | 76 ++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 Documentation/devicetree/bindings/bus/st,stm32mp131-dbg-bus.yaml diff --git a/Documentation/devicetree/bindings/bus/st,stm32mp131-dbg-bus.yaml b/Documentation/devicetree/bindings/bus/st,stm32mp131-dbg-bus.yaml new file mode 100644 index 000000000000..6c74433efbe3 --- /dev/null +++ b/Documentation/devicetree/bindings/bus/st,stm32mp131-dbg-bus.yaml @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/bus/st,stm32mp131-dbg-bus.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: STM32 Coresight bus + +maintainers: + - Gatien Chevallier + +description: + The STM32 debug bus is in charge of checking the debug configuration + of the platform before probing the peripheral drivers that rely on the debug + domain. + +properties: + compatible: + items: + - enum: + - st,stm32mp131-dbg-bus + - st,stm32mp151-dbg-bus + + "#address-cells": + const: 1 + + "#size-cells": + const: 1 + + ranges: + minItems: 1 + maxItems: 2 + + "#access-controller-cells": + const: 1 + description: + Contains the debug profile necessary to access the peripheral. + +patternProperties: + "@[0-9a-f]+$": + description: Debug related peripherals + type: object + + additionalProperties: true + + required: + - access-controllers + +required: + - "#access-controller-cells" + - "#address-cells" + - "#size-cells" + - compatible + - ranges + +additionalProperties: false + +examples: + - | + #include + + dbg_bus: bus@50080000 { + compatible = "st,stm32mp131-dbg-bus"; + #address-cells = <1>; + #size-cells = <1>; + #access-controller-cells = <1>; + ranges = <0x50080000 0x50080000 0x3f80000>; + + cti@50094000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x50094000 0x1000>; + clocks = <&rcc CK_DBG>; + clock-names = "apb_pclk"; + access-controllers = <&dbg_bus 0>; + }; + }; -- cgit v1.2.3 From 892320d20362b7d567b759aec2bbe9c26b404e00 Mon Sep 17 00:00:00 2001 From: Gatien Chevallier Date: Thu, 26 Feb 2026 11:30:19 +0100 Subject: bus: stm32_firewall: allow check on different firewall controllers Current implementation restricts the check on the firewall controller being the bus parent. Change this by using the controller referenced in each firewall queries. Signed-off-by: Gatien Chevallier Link: https://lore.kernel.org/r/20260226-debug_bus-v6-4-5d794697798d@foss.st.com Signed-off-by: Alexandre Torgue --- drivers/bus/stm32_firewall.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bus/stm32_firewall.c b/drivers/bus/stm32_firewall.c index b023d1f4fcf7..e69bc8c1a4e7 100644 --- a/drivers/bus/stm32_firewall.c +++ b/drivers/bus/stm32_firewall.c @@ -267,7 +267,7 @@ int stm32_firewall_populate_bus(struct stm32_firewall_controller *firewall_contr } for (i = 0; i < len; i++) { - if (firewall_controller->grant_access(firewall_controller, + if (firewall_controller->grant_access(firewalls[i].firewall_ctrl, firewalls[i].firewall_id)) { /* * Peripheral access not allowed or not defined. -- cgit v1.2.3 From 4ea96cfc728bbc7c1c3051cce9209bf9a6eeb23e Mon Sep 17 00:00:00 2001 From: Gatien Chevallier Date: Thu, 26 Feb 2026 11:30:20 +0100 Subject: bus: stm32_firewall: add stm32_firewall_get_grant_all_access() API Add the stm32_firewall_get_grant_all_access() API to be able to fetch all firewall references in an access-controllers property and try to grant access to all of them. Signed-off-by: Gatien Chevallier Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20260226-debug_bus-v6-5-5d794697798d@foss.st.com Signed-off-by: Alexandre Torgue --- drivers/bus/stm32_firewall.c | 42 +++++++++++++++++++++++++++++++ include/linux/bus/stm32_firewall_device.h | 26 +++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/drivers/bus/stm32_firewall.c b/drivers/bus/stm32_firewall.c index e69bc8c1a4e7..e3619dba8b06 100644 --- a/drivers/bus/stm32_firewall.c +++ b/drivers/bus/stm32_firewall.c @@ -184,6 +184,48 @@ void stm32_firewall_release_access_by_id(struct stm32_firewall *firewall, u32 su } EXPORT_SYMBOL_GPL(stm32_firewall_release_access_by_id); +int stm32_firewall_get_grant_all_access(struct device *dev, struct stm32_firewall **firewall, + int *nb_firewall) +{ + struct stm32_firewall *loc_firewall; + int err; + int i; + + *nb_firewall = of_count_phandle_with_args(dev->of_node, "access-controllers", + "#access-controller-cells"); + if (*nb_firewall < 0) + return *nb_firewall; + + if (!*nb_firewall) { + *firewall = NULL; + return 0; + } + + loc_firewall = devm_kcalloc(dev, *nb_firewall, sizeof(*loc_firewall), GFP_KERNEL); + if (!loc_firewall) + return -ENOMEM; + + /* Get stm32 firewall information */ + err = stm32_firewall_get_firewall(dev->of_node, loc_firewall, *nb_firewall); + if (err) + return err; + + for (i = 0; i < *nb_firewall; i++) { + err = stm32_firewall_grant_access(&loc_firewall[i]); + if (err) { + while (i--) + stm32_firewall_release_access(&loc_firewall[i]); + + return err; + } + } + + *firewall = loc_firewall; + + return 0; +} +EXPORT_SYMBOL_GPL(stm32_firewall_get_grant_all_access); + /* Firewall controller API */ int stm32_firewall_controller_register(struct stm32_firewall_controller *firewall_controller) diff --git a/include/linux/bus/stm32_firewall_device.h b/include/linux/bus/stm32_firewall_device.h index eaa7a3f54450..6c878f3ca86f 100644 --- a/include/linux/bus/stm32_firewall_device.h +++ b/include/linux/bus/stm32_firewall_device.h @@ -112,6 +112,25 @@ int stm32_firewall_grant_access_by_id(struct stm32_firewall *firewall, u32 subsy */ void stm32_firewall_release_access_by_id(struct stm32_firewall *firewall, u32 subsystem_id); +/** + * stm32_firewall_get_grant_all_access - Allocate and get all the firewall(s) associated to given + * device. Then, try to grant access rights for each element. + * This function is basically a helper function that wraps + * both stm32_firewall_get_firewall() and + * stm32_firewall_grant_access() on all firewall references of + * a device along with the allocation of the array. + * Realease access using stm32_firewall_release_access* APIs + * when done. + * + * @dev: Device performing the checks + * @firewall: Pointer to the array of firewall references to be allocated + * @nb_firewall: Number of allocated elements in @firewall + * + * Returns 0 on success, or appropriate errno code if error occurred. + */ +int stm32_firewall_get_grant_all_access(struct device *dev, struct stm32_firewall **firewall, + int *nb_firewall); + #else /* CONFIG_STM32_FIREWALL */ static inline int stm32_firewall_get_firewall(struct device_node *np, @@ -141,5 +160,12 @@ static inline void stm32_firewall_release_access_by_id(struct stm32_firewall *fi { } +static inline int stm32_firewall_get_grant_all_access(struct device *dev, + struct stm32_firewall **firewall, + int *nb_firewall) +{ + return -ENODEV; +} + #endif /* CONFIG_STM32_FIREWALL */ #endif /* STM32_FIREWALL_DEVICE_H */ -- cgit v1.2.3 From f1939c6a0bed6a4af8339fd0aeb28a17b7a0d4c7 Mon Sep 17 00:00:00 2001 From: Gatien Chevallier Date: Thu, 26 Feb 2026 11:30:21 +0100 Subject: drivers: bus: add the stm32 debug bus driver Add the stm32 debug bus driver that is responsible of checking the debug subsystem accessibility before probing the related peripheral drivers. This driver is OP-TEE dependent and relies on the STM32 debug access PTA. Signed-off-by: Gatien Chevallier Link: https://lore.kernel.org/r/20260226-debug_bus-v6-6-5d794697798d@foss.st.com Signed-off-by: Alexandre Torgue --- MAINTAINERS | 1 + drivers/bus/Kconfig | 10 ++ drivers/bus/Makefile | 1 + drivers/bus/stm32_dbg_bus.c | 250 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 262 insertions(+) create mode 100644 drivers/bus/stm32_dbg_bus.c diff --git a/MAINTAINERS b/MAINTAINERS index 55af015174a5..63f59748d661 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25009,6 +25009,7 @@ F: drivers/hwmon/pmbus/stef48h28.c ST STM32 FIREWALL M: Gatien Chevallier S: Maintained +F: drivers/bus/stm32_dbg_bus.c F: drivers/bus/stm32_etzpc.c F: drivers/bus/stm32_firewall.c F: drivers/bus/stm32_rifsc.c diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 2a1b46f07080..7a1b04007efb 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -169,6 +169,16 @@ config QCOM_SSC_BLOCK_BUS i2c/spi/uart controllers, a hexagon core, and a clock controller which provides clocks for the above. +config STM32_DBG_BUS + tristate "OP-TEE based debug access bus" + depends on OPTEE && STM32_FIREWALL + depends on ARCH_STM32 || COMPILE_TEST + help + Select this to get the support for the OP-TEE based STM32 debug bus + driver that is used to handle debug-related peripherals on STM32 + platforms when the debug configuration is not accessible by the + normal world. + config STM32_FIREWALL bool "STM32 Firewall framework" depends on (ARCH_STM32 || COMPILE_TEST) && OF diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index 8e693fe8a03a..799724cfc2df 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -27,6 +27,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_STM32_DBG_BUS) += stm32_dbg_bus.o obj-$(CONFIG_STM32_FIREWALL) += stm32_firewall.o stm32_rifsc.o stm32_etzpc.o obj-$(CONFIG_SUN50I_DE2_BUS) += sun50i-de2.o obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o diff --git a/drivers/bus/stm32_dbg_bus.c b/drivers/bus/stm32_dbg_bus.c new file mode 100644 index 000000000000..e30ef3465609 --- /dev/null +++ b/drivers/bus/stm32_dbg_bus.c @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026, STMicroelectronics - All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum stm32_dbg_profile { + PERIPHERAL_DBG_PROFILE = 0, + HDP_DBG_PROFILE = 1, +}; + +enum stm32_dbg_pta_command { + /* + * PTA_CMD_GRANT_DBG_ACCESS - Verify the debug configuration against the given debug profile + * and grant access or not + * + * [in] value[0].a Debug profile to grant access to. + */ + PTA_CMD_GRANT_DBG_ACCESS, +}; + +/** + * struct stm32_dbg_bus - OP-TEE based STM32 debug bus private data + * @dev: STM32 debug bus device. + * @ctx: OP-TEE context handler. + */ +struct stm32_dbg_bus { + struct device *dev; + struct tee_context *ctx; +}; + +/* Expect at most 1 instance of this driver */ +static struct stm32_dbg_bus *stm32_dbg_bus_priv; + +static int stm32_dbg_pta_open_session(u32 *id) +{ + struct tee_client_device *dbg_bus_dev = to_tee_client_device(stm32_dbg_bus_priv->dev); + struct tee_ioctl_open_session_arg sess_arg; + int ret; + + memset(&sess_arg, 0, sizeof(sess_arg)); + export_uuid(sess_arg.uuid, &dbg_bus_dev->id.uuid); + sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL; + + ret = tee_client_open_session(stm32_dbg_bus_priv->ctx, &sess_arg, NULL); + if (ret < 0 || sess_arg.ret) { + dev_err(stm32_dbg_bus_priv->dev, "Failed opening tee session, err: %#x\n", + sess_arg.ret); + return -EOPNOTSUPP; + } + + *id = sess_arg.session; + + return 0; +} + +static void stm32_dbg_pta_close_session(u32 id) +{ + tee_client_close_session(stm32_dbg_bus_priv->ctx, id); +} + +static int stm32_dbg_bus_grant_access(struct stm32_firewall_controller *ctrl, u32 dbg_profile) +{ + struct tee_ioctl_invoke_arg inv_arg = {0}; + struct tee_param param[1] = {0}; + u32 session_id; + int ret; + + if (dbg_profile != PERIPHERAL_DBG_PROFILE && dbg_profile != HDP_DBG_PROFILE) + return -EOPNOTSUPP; + + ret = stm32_dbg_pta_open_session(&session_id); + if (ret) + return ret; + + inv_arg.func = PTA_CMD_GRANT_DBG_ACCESS; + inv_arg.session = session_id; + inv_arg.num_params = 1; + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; + param[0].u.value.a = dbg_profile; + + ret = tee_client_invoke_func(stm32_dbg_bus_priv->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_dbg(stm32_dbg_bus_priv->dev, + "When invoking function, err %x, TEE returns: %x\n", ret, inv_arg.ret); + if (!ret) + ret = -EACCES; + } + + stm32_dbg_pta_close_session(session_id); + + return ret; +} + +/* Implement mandatory release_access ops even if it does nothing*/ +static void stm32_dbg_bus_release_access(struct stm32_firewall_controller *ctrl, u32 dbg_profile) +{ +} + +static int stm32_dbg_bus_plat_probe(struct platform_device *pdev) +{ + struct stm32_firewall_controller *dbg_controller; + int ret; + + /* Defer if OP-TEE service is not yet available */ + if (!stm32_dbg_bus_priv) + return -EPROBE_DEFER; + + dbg_controller = devm_kzalloc(&pdev->dev, sizeof(*dbg_controller), GFP_KERNEL); + if (!dbg_controller) + return dev_err_probe(&pdev->dev, -ENOMEM, "Couldn't allocate debug controller\n"); + + dbg_controller->dev = &pdev->dev; + dbg_controller->mmio = NULL; + dbg_controller->name = dev_driver_string(dbg_controller->dev); + dbg_controller->type = STM32_PERIPHERAL_FIREWALL; + dbg_controller->grant_access = stm32_dbg_bus_grant_access; + dbg_controller->release_access = stm32_dbg_bus_release_access; + + ret = stm32_firewall_controller_register(dbg_controller); + if (ret) { + dev_err(dbg_controller->dev, "Couldn't register as a firewall controller: %d", ret); + return ret; + } + + ret = stm32_firewall_populate_bus(dbg_controller); + if (ret) { + dev_err(dbg_controller->dev, "Couldn't populate debug bus: %d", ret); + stm32_firewall_controller_unregister(dbg_controller); + return ret; + } + + pm_runtime_enable(&pdev->dev); + + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (ret) { + dev_err(dbg_controller->dev, "Couldn't populate the node: %d", ret); + stm32_firewall_controller_unregister(dbg_controller); + return ret; + } + + return 0; +} + +static const struct of_device_id stm32_dbg_bus_of_match[] = { + { .compatible = "st,stm32mp131-dbg-bus", }, + { .compatible = "st,stm32mp151-dbg-bus", }, + { }, +}; +MODULE_DEVICE_TABLE(of, stm32_dbg_bus_of_match); + +static struct platform_driver stm32_dbg_bus_driver = { + .probe = stm32_dbg_bus_plat_probe, + .driver = { + .name = "stm32-dbg-bus", + .of_match_table = stm32_dbg_bus_of_match, + }, +}; + +static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) +{ + return (ver->impl_id == TEE_IMPL_ID_OPTEE); +} + +static void stm32_dbg_bus_remove(struct tee_client_device *tee_dev) +{ + tee_client_close_context(stm32_dbg_bus_priv->ctx); + stm32_dbg_bus_priv = NULL; + + of_platform_depopulate(&tee_dev->dev); +} + +static int stm32_dbg_bus_probe(struct tee_client_device *tee_dev) +{ + struct device *dev = &tee_dev->dev; + struct stm32_dbg_bus *priv; + int ret = 0; + + if (stm32_dbg_bus_priv) + return dev_err_probe(dev, -EBUSY, + "A STM32 debug bus device is already initialized\n"); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* Open context with TEE driver */ + priv->ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL); + if (IS_ERR_OR_NULL(priv->ctx)) + return dev_err_probe(dev, PTR_ERR_OR_ZERO(priv->ctx), "Cannot open TEE context\n"); + + stm32_dbg_bus_priv = priv; + stm32_dbg_bus_priv->dev = dev; + + return ret; +} + +static const struct tee_client_device_id optee_dbg_bus_id_table[] = { + {UUID_INIT(0xdd05bc8b, 0x9f3b, 0x49f0, + 0xb6, 0x49, 0x01, 0xaa, 0x10, 0xc1, 0xc2, 0x10)}, + {} +}; + +static struct tee_client_driver stm32_optee_dbg_bus_driver = { + .id_table = optee_dbg_bus_id_table, + .probe = stm32_dbg_bus_probe, + .remove = stm32_dbg_bus_remove, + .driver = { + .name = "optee_dbg_bus", + }, +}; + +static void __exit stm32_optee_dbg_bus_driver_exit(void) +{ + platform_driver_unregister(&stm32_dbg_bus_driver); + tee_client_driver_unregister(&stm32_optee_dbg_bus_driver); +} +module_exit(stm32_optee_dbg_bus_driver_exit); + +static int __init stm32_optee_dbg_bus_driver_init(void) +{ + int err; + + err = tee_client_driver_register(&stm32_optee_dbg_bus_driver); + if (err) + return err; + + err = platform_driver_register(&stm32_dbg_bus_driver); + if (err) + tee_client_driver_unregister(&stm32_optee_dbg_bus_driver); + + return err; +} +module_init(stm32_optee_dbg_bus_driver_init); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Gatien Chevallier "); +MODULE_DESCRIPTION("OP-TEE based STM32 debug access bus driver"); -- cgit v1.2.3 From 348741a97c28d7679fb1e204aeec0d6305fc6817 Mon Sep 17 00:00:00 2001 From: Gatien Chevallier Date: Thu, 26 Feb 2026 11:30:27 +0100 Subject: pinctrl: stm32: add firewall checks before probing the HDP driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because the HDP peripheral both depends on debug and firewall configuration, when CONFIG_STM32_FIREWALL is present, use the stm32 firewall framework to be able to check these configuration against the relevant controllers. Signed-off-by: Gatien Chevallier Reviewed-by: Clément Le Goffic Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20260226-debug_bus-v6-12-5d794697798d@foss.st.com Signed-off-by: Alexandre Torgue --- drivers/pinctrl/stm32/pinctrl-stm32-hdp.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/pinctrl/stm32/pinctrl-stm32-hdp.c b/drivers/pinctrl/stm32/pinctrl-stm32-hdp.c index 0b1dff01e04c..cce477e86ef9 100644 --- a/drivers/pinctrl/stm32/pinctrl-stm32-hdp.c +++ b/drivers/pinctrl/stm32/pinctrl-stm32-hdp.c @@ -4,6 +4,7 @@ * Author: Clément Le Goffic for STMicroelectronics. */ #include +#include #include #include #include @@ -46,9 +47,11 @@ struct stm32_hdp { void __iomem *base; struct clk *clk; struct pinctrl_dev *pctl_dev; + struct stm32_firewall *firewall; struct gpio_generic_chip gpio_chip; u32 mux_conf; u32 gposet_conf; + int nb_firewall_entries; const char * const *func_name; }; @@ -615,6 +618,13 @@ static int stm32_hdp_probe(struct platform_device *pdev) return -ENOMEM; hdp->dev = dev; + if (IS_ENABLED(CONFIG_STM32_FIREWALL)) { + err = stm32_firewall_get_grant_all_access(dev, &hdp->firewall, + &hdp->nb_firewall_entries); + if (err) + return err; + } + platform_set_drvdata(pdev, hdp); hdp->base = devm_platform_ioremap_resource(pdev, 0); @@ -670,8 +680,12 @@ static int stm32_hdp_probe(struct platform_device *pdev) static void stm32_hdp_remove(struct platform_device *pdev) { struct stm32_hdp *hdp = platform_get_drvdata(pdev); + int i; writel_relaxed(HDP_CTRL_DISABLE, hdp->base + HDP_CTRL); + + for (i = 0; i < hdp->nb_firewall_entries; i++) + stm32_firewall_release_access(&hdp->firewall[i]); } static int stm32_hdp_suspend(struct device *dev) -- cgit v1.2.3