This driver is checking the access rights of the different
peripherals connected to the system bus. If access is denied,
the associated device tree node is skipped so the platform bus
does not probe it.
Signed-off-by: Gatien Chevallier <[email protected]>
Signed-off-by: Loic PALLARDY <[email protected]>
---
Changes in V2:
- Re-ordered Signed-off-by tags
MAINTAINERS | 6 ++
drivers/bus/Kconfig | 9 ++
drivers/bus/Makefile | 1 +
drivers/bus/stm32_sys_bus.c | 180 ++++++++++++++++++++++++++++++++++++
4 files changed, 196 insertions(+)
create mode 100644 drivers/bus/stm32_sys_bus.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 886d3f69ee64..768a8272233e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19522,6 +19522,12 @@ L: [email protected]
S: Maintained
F: drivers/spi/spi-stm32.c
+ST STM32 SYS BUS DRIVER
+M: Gatien Chevallier <[email protected]>
+S: Maintained
+F: Documentation/devicetree/bindings/bus/st,sys-bus.yaml
+F: drivers/bus/stm32_sys_bus.c
+
ST STPDDC60 DRIVER
M: Daniel Nilsson <[email protected]>
L: [email protected]
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index 7bfe998f3514..638bf5839cb0 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -163,6 +163,15 @@ config QCOM_SSC_BLOCK_BUS
i2c/spi/uart controllers, a hexagon core, and a clock controller
which provides clocks for the above.
+config STM32_SYS_BUS
+ bool "STM32 System bus controller"
+ depends on ARCH_STM32
+ default MACH_STM32MP157 || MACH_STM32MP13
+ help
+ Say y to enable device access right verification before device probing.
+ If access not granted, device won't be probed and an error message will
+ provide the reason.
+
config SUN50I_DE2_BUS
bool "Allwinner A64 DE2 Bus Driver"
default ARM64
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index d90eed189a65..b15fdc42d0be 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -26,6 +26,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_SYS_BUS) += stm32_sys_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/stm32_sys_bus.c b/drivers/bus/stm32_sys_bus.c
new file mode 100644
index 000000000000..e4c21e24b65e
--- /dev/null
+++ b/drivers/bus/stm32_sys_bus.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022, STMicroelectronics - All Rights Reserved
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+/* ETZPC peripheral as firewall bus */
+/* ETZPC registers */
+#define ETZPC_DECPROT 0x10
+
+/* ETZPC miscellaneous */
+#define ETZPC_PROT_MASK GENMASK(1, 0)
+#define ETZPC_PROT_A7NS 0x3
+#define ETZPC_DECPROT_SHIFT 1
+
+#define IDS_PER_DECPROT_REGS 16
+#define STM32MP15_ETZPC_ENTRIES 96
+#define STM32MP13_ETZPC_ENTRIES 64
+
+struct sys_bus_data;
+
+struct stm32_sys_bus_match_data {
+ const u32 *map_table;
+ unsigned int max_entries;
+ int (*sys_bus_get_access)(struct sys_bus_data *pdata, struct device_node *np);
+};
+
+struct sys_bus_data {
+ struct stm32_sys_bus_match_data *pconf;
+ void __iomem *sys_bus_base;
+ struct device *dev;
+};
+
+static int stm32_sys_bus_get_periph_id(struct sys_bus_data *pdata, struct device_node *np, u32 *id)
+{
+ int err;
+ u32 feature_domain_cell[2];
+ u32 id_bus;
+
+ /* Get reg from device node */
+ err = of_property_read_u32_array(np, "feature-domains", feature_domain_cell, 2);
+ if (err) {
+ dev_err(pdata->dev, "Unable to find feature-domains property\n");
+ return -ENODEV;
+ }
+
+ id_bus = feature_domain_cell[1];
+
+ if (id_bus >= pdata->pconf->max_entries) {
+ dev_err(pdata->dev, "Invalid sys bus ID for %s\n", np->full_name);
+ return -EINVAL;
+ }
+
+ *id = id_bus;
+
+ return 0;
+}
+
+static int stm32_etzpc_get_access(struct sys_bus_data *pdata, struct device_node *np)
+{
+ int err;
+ u32 offset, reg_offset, sec_val, id;
+
+ err = stm32_sys_bus_get_periph_id(pdata, np, &id);
+ if (err)
+ return err;
+
+ /* Check access configuration, 16 peripherals per register */
+ reg_offset = ETZPC_DECPROT + 0x4 * (id / IDS_PER_DECPROT_REGS);
+ offset = (id % IDS_PER_DECPROT_REGS) << ETZPC_DECPROT_SHIFT;
+
+ /* Verify peripheral is non-secure and attributed to cortex A7 */
+ sec_val = (readl(pdata->sys_bus_base + reg_offset) >> offset) & ETZPC_PROT_MASK;
+ if (sec_val != ETZPC_PROT_A7NS) {
+ dev_dbg(pdata->dev, "Invalid bus configuration: reg_offset %#x, value %d\n",
+ reg_offset, sec_val);
+ return -EACCES;
+ }
+
+ return 0;
+}
+
+static void stm32_sys_bus_populate(struct sys_bus_data *pdata)
+{
+ struct device *parent;
+ struct device_node *child;
+
+ parent = pdata->dev;
+
+ dev_dbg(parent, "Populating %s system bus\n", pdata->dev->driver->name);
+
+ for_each_available_child_of_node(dev_of_node(parent), child) {
+ if (pdata->pconf->sys_bus_get_access(pdata, child)) {
+ /*
+ * Peripheral access not allowed.
+ * Mark the node as populated so platform bus won't probe it
+ */
+ of_node_set_flag(child, OF_POPULATED);
+ dev_dbg(parent, "%s: Peripheral will not be probed\n",
+ child->full_name);
+ }
+ }
+}
+
+static int stm32_sys_bus_probe(struct platform_device *pdev)
+{
+ struct sys_bus_data *pdata;
+ struct resource *res;
+ void __iomem *mmio;
+ struct stm32_sys_bus_match_data *mdata;
+ struct device_node *np = pdev->dev.of_node;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ mmio = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(mmio))
+ return PTR_ERR(mmio);
+
+ pdata->sys_bus_base = mmio;
+
+ mdata = (struct stm32_sys_bus_match_data *)of_device_get_match_data(&pdev->dev);
+ if (!mdata)
+ return -EINVAL;
+
+ pdata->pconf = mdata;
+ pdata->dev = &pdev->dev;
+
+ platform_set_drvdata(pdev, pdata);
+
+ stm32_sys_bus_populate(pdata);
+
+ /* Populate all available nodes */
+ return of_platform_populate(np, NULL, NULL, &pdev->dev);
+}
+
+static const struct stm32_sys_bus_match_data stm32mp15_sys_bus_data = {
+ .max_entries = STM32MP15_ETZPC_ENTRIES,
+ .sys_bus_get_access = stm32_etzpc_get_access,
+};
+
+static const struct stm32_sys_bus_match_data stm32mp13_sys_bus_data = {
+ .max_entries = STM32MP13_ETZPC_ENTRIES,
+ .sys_bus_get_access = stm32_etzpc_get_access,
+};
+
+static const struct of_device_id stm32_sys_bus_of_match[] = {
+ { .compatible = "st,stm32mp15-sys-bus", .data = &stm32mp15_sys_bus_data },
+ { .compatible = "st,stm32mp13-sys-bus", .data = &stm32mp13_sys_bus_data },
+ {}
+};
+MODULE_DEVICE_TABLE(of, stm32_sys_bus_of_match);
+
+static struct platform_driver stm32_sys_bus_driver = {
+ .probe = stm32_sys_bus_probe,
+ .driver = {
+ .name = "stm32-sys-bus",
+ .of_match_table = stm32_sys_bus_of_match,
+ },
+};
+
+static int __init stm32_sys_bus_init(void)
+{
+ return platform_driver_register(&stm32_sys_bus_driver);
+}
+arch_initcall(stm32_sys_bus_init);
+
--
2.25.1
The STM32 System Bus is an internal bus on which devices are connected.
ETZPC is a peripheral overseeing the firewall bus that configures
and control access to the peripherals connected on it.
For more information on which peripheral is securable, please read
the STM32MP13 reference manual.
Signed-off-by: Gatien Chevallier <[email protected]>
---
arch/arm/boot/dts/stm32mp131.dtsi | 242 ++++++++++++++++--------------
1 file changed, 130 insertions(+), 112 deletions(-)
diff --git a/arch/arm/boot/dts/stm32mp131.dtsi b/arch/arm/boot/dts/stm32mp131.dtsi
index dd35a607073d..0b3934fe6c34 100644
--- a/arch/arm/boot/dts/stm32mp131.dtsi
+++ b/arch/arm/boot/dts/stm32mp131.dtsi
@@ -3,6 +3,7 @@
* Copyright (C) STMicroelectronics 2021 - All Rights Reserved
* Author: Alexandre Torgue <[email protected]> for STMicroelectronics.
*/
+#include <dt-bindings/bus/stm32mp13_sys_bus.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/stm32mp13-clks.h>
#include <dt-bindings/reset/stm32mp13-resets.h>
@@ -231,88 +232,6 @@ dmamux1: dma-router@48002000 {
dma-channels = <16>;
};
- spi4: spi@4c002000 {
- compatible = "st,stm32h7-spi";
- reg = <0x4c002000 0x400>;
- interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&rcc SPI4_K>;
- resets = <&rcc SPI4_R>;
- #address-cells = <1>;
- #size-cells = <0>;
- dmas = <&dmamux1 83 0x400 0x01>,
- <&dmamux1 84 0x400 0x01>;
- dma-names = "rx", "tx";
- status = "disabled";
- };
-
- spi5: spi@4c003000 {
- compatible = "st,stm32h7-spi";
- reg = <0x4c003000 0x400>;
- interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&rcc SPI5_K>;
- resets = <&rcc SPI5_R>;
- #address-cells = <1>;
- #size-cells = <0>;
- dmas = <&dmamux1 85 0x400 0x01>,
- <&dmamux1 86 0x400 0x01>;
- dma-names = "rx", "tx";
- status = "disabled";
- };
-
- i2c3: i2c@4c004000 {
- compatible = "st,stm32mp13-i2c";
- reg = <0x4c004000 0x400>;
- interrupt-names = "event", "error";
- interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&rcc I2C3_K>;
- resets = <&rcc I2C3_R>;
- #address-cells = <1>;
- #size-cells = <0>;
- dmas = <&dmamux1 73 0x400 0x1>,
- <&dmamux1 74 0x400 0x1>;
- dma-names = "rx", "tx";
- st,syscfg-fmp = <&syscfg 0x4 0x4>;
- i2c-analog-filter;
- status = "disabled";
- };
-
- i2c4: i2c@4c005000 {
- compatible = "st,stm32mp13-i2c";
- reg = <0x4c005000 0x400>;
- interrupt-names = "event", "error";
- interrupts = <GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&rcc I2C4_K>;
- resets = <&rcc I2C4_R>;
- #address-cells = <1>;
- #size-cells = <0>;
- dmas = <&dmamux1 75 0x400 0x1>,
- <&dmamux1 76 0x400 0x1>;
- dma-names = "rx", "tx";
- st,syscfg-fmp = <&syscfg 0x4 0x8>;
- i2c-analog-filter;
- status = "disabled";
- };
-
- i2c5: i2c@4c006000 {
- compatible = "st,stm32mp13-i2c";
- reg = <0x4c006000 0x400>;
- interrupt-names = "event", "error";
- interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&rcc I2C5_K>;
- resets = <&rcc I2C5_R>;
- #address-cells = <1>;
- #size-cells = <0>;
- dmas = <&dmamux1 115 0x400 0x1>,
- <&dmamux1 116 0x400 0x1>;
- dma-names = "rx", "tx";
- st,syscfg-fmp = <&syscfg 0x4 0x10>;
- i2c-analog-filter;
- status = "disabled";
- };
-
rcc: rcc@50000000 {
compatible = "st,stm32mp13-rcc", "syscon";
reg = <0x50000000 0x1000>;
@@ -349,36 +268,6 @@ mdma: dma-controller@58000000 {
dma-requests = <48>;
};
- sdmmc1: mmc@58005000 {
- compatible = "st,stm32-sdmmc2", "arm,pl18x", "arm,primecell";
- arm,primecell-periphid = <0x20253180>;
- reg = <0x58005000 0x1000>, <0x58006000 0x1000>;
- interrupts = <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>;
- interrupt-names = "cmd_irq";
- clocks = <&rcc SDMMC1_K>;
- clock-names = "apb_pclk";
- resets = <&rcc SDMMC1_R>;
- cap-sd-highspeed;
- cap-mmc-highspeed;
- max-frequency = <130000000>;
- status = "disabled";
- };
-
- sdmmc2: mmc@58007000 {
- compatible = "st,stm32-sdmmc2", "arm,pl18x", "arm,primecell";
- arm,primecell-periphid = <0x20253180>;
- reg = <0x58007000 0x1000>, <0x58008000 0x1000>;
- interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
- interrupt-names = "cmd_irq";
- clocks = <&rcc SDMMC2_K>;
- clock-names = "apb_pclk";
- resets = <&rcc SDMMC2_R>;
- cap-sd-highspeed;
- cap-mmc-highspeed;
- max-frequency = <130000000>;
- status = "disabled";
- };
-
iwdg2: watchdog@5a002000 {
compatible = "st,stm32mp1-iwdg";
reg = <0x5a002000 0x400>;
@@ -414,6 +303,135 @@ ts_cal2: calib@5e {
};
};
+ etzpc: etzpc@5c007000 {
+ compatible = "st,stm32mp13-sys-bus";
+ reg = <0x5c007000 0x400>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ feature-domain-controller;
+ #feature-domain-cells = <2>;
+ ranges;
+
+ spi4: spi@4c002000 {
+ compatible = "st,stm32h7-spi";
+ reg = <0x4c002000 0x400>;
+ interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&rcc SPI4_K>;
+ resets = <&rcc SPI4_R>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ dmas = <&dmamux1 83 0x400 0x01>,
+ <&dmamux1 84 0x400 0x01>;
+ dma-names = "rx", "tx";
+ feature-domains = <&etzpc STM32MP1_ETZPC_SPI4_ID>;
+ status = "disabled";
+ };
+
+ spi5: spi@4c003000 {
+ compatible = "st,stm32h7-spi";
+ reg = <0x4c003000 0x400>;
+ interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&rcc SPI5_K>;
+ resets = <&rcc SPI5_R>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ dmas = <&dmamux1 85 0x400 0x01>,
+ <&dmamux1 86 0x400 0x01>;
+ dma-names = "rx", "tx";
+ feature-domains = <&etzpc STM32MP1_ETZPC_SPI5_ID>;
+ status = "disabled";
+ };
+
+ i2c3: i2c@4c004000 {
+ compatible = "st,stm32mp13-i2c";
+ reg = <0x4c004000 0x400>;
+ interrupt-names = "event", "error";
+ interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&rcc I2C3_K>;
+ resets = <&rcc I2C3_R>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ dmas = <&dmamux1 73 0x400 0x1>,
+ <&dmamux1 74 0x400 0x1>;
+ dma-names = "rx", "tx";
+ st,syscfg-fmp = <&syscfg 0x4 0x4>;
+ i2c-analog-filter;
+ feature-domains = <&etzpc STM32MP1_ETZPC_I2C3_ID>;
+ status = "disabled";
+ };
+
+ i2c4: i2c@4c005000 {
+ compatible = "st,stm32mp13-i2c";
+ reg = <0x4c005000 0x400>;
+ interrupt-names = "event", "error";
+ interrupts = <GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&rcc I2C4_K>;
+ resets = <&rcc I2C4_R>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ dmas = <&dmamux1 75 0x400 0x1>,
+ <&dmamux1 76 0x400 0x1>;
+ dma-names = "rx", "tx";
+ st,syscfg-fmp = <&syscfg 0x4 0x8>;
+ i2c-analog-filter;
+ feature-domains = <&etzpc STM32MP1_ETZPC_I2C4_ID>;
+ status = "disabled";
+ };
+
+ i2c5: i2c@4c006000 {
+ compatible = "st,stm32mp13-i2c";
+ reg = <0x4c006000 0x400>;
+ interrupt-names = "event", "error";
+ interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&rcc I2C5_K>;
+ resets = <&rcc I2C5_R>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ dmas = <&dmamux1 115 0x400 0x1>,
+ <&dmamux1 116 0x400 0x1>;
+ dma-names = "rx", "tx";
+ st,syscfg-fmp = <&syscfg 0x4 0x10>;
+ i2c-analog-filter;
+ feature-domains = <&etzpc STM32MP1_ETZPC_I2C5_ID>;
+ status = "disabled";
+ };
+
+ sdmmc1: mmc@58005000 {
+ compatible = "st,stm32-sdmmc2", "arm,pl18x", "arm,primecell";
+ arm,primecell-periphid = <0x20253180>;
+ reg = <0x58005000 0x1000>, <0x58006000 0x1000>;
+ interrupts = <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "cmd_irq";
+ clocks = <&rcc SDMMC1_K>;
+ clock-names = "apb_pclk";
+ resets = <&rcc SDMMC1_R>;
+ cap-sd-highspeed;
+ cap-mmc-highspeed;
+ max-frequency = <130000000>;
+ feature-domains = <&etzpc STM32MP1_ETZPC_SDMMC1_ID>;
+ status = "disabled";
+ };
+
+ sdmmc2: mmc@58007000 {
+ compatible = "st,stm32-sdmmc2", "arm,pl18x", "arm,primecell";
+ arm,primecell-periphid = <0x20253180>;
+ reg = <0x58007000 0x1000>, <0x58008000 0x1000>;
+ interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "cmd_irq";
+ clocks = <&rcc SDMMC2_K>;
+ clock-names = "apb_pclk";
+ resets = <&rcc SDMMC2_R>;
+ cap-sd-highspeed;
+ cap-mmc-highspeed;
+ max-frequency = <130000000>;
+ feature-domains = <&etzpc STM32MP1_ETZPC_SDMMC2_ID>;
+ status = "disabled";
+ };
+
+ };
/*
* Break node order to solve dependency probe issue between
* pinctrl and exti.
--
2.25.1