From: Patrick Delaunay <patrick.delau...@foss.st.com>

This driver is checking the access rights of the different
peripherals connected to the RIFSC bus. If access is denied,
the associated device is not binded.

Signed-off-by: Gatien Chevallier <gatien.chevall...@foss.st.com>
Signed-off-by: Patrick Delaunay <patrick.delau...@foss.st.com>
Signed-off-by: Patrice Chotard <patrice.chot...@foss.st.com>
Reviewed-by: Patrick Delaunay <patrick.delau...@foss.st.com>

---

Changes in v2:
  - Replace LOG_CATEGORY UCLASS_SIMPLE_BUS by UCLASS_NOP in both
    /arch/arm/mach-stm32mp/stm32mp2/rifsc.c and
    /arch/arm/mach-stm32mp/stm32mp1/etzpc.c.
  - Update board/st/stm32mp1/MAINTAINERS.
  - Fix DSI clock ssetting.

 arch/arm/mach-stm32mp/include/mach/rif.h |  26 ++
 arch/arm/mach-stm32mp/stm32mp2/Makefile  |   1 +
 arch/arm/mach-stm32mp/stm32mp2/rifsc.c   | 364 +++++++++++++++++++++++
 3 files changed, 391 insertions(+)
 create mode 100644 arch/arm/mach-stm32mp/include/mach/rif.h
 create mode 100644 arch/arm/mach-stm32mp/stm32mp2/rifsc.c

diff --git a/arch/arm/mach-stm32mp/include/mach/rif.h 
b/arch/arm/mach-stm32mp/include/mach/rif.h
new file mode 100644
index 00000000000..10b22108120
--- /dev/null
+++ b/arch/arm/mach-stm32mp/include/mach/rif.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*
+ * Copyright (C) 2023, STMicroelectronics - All Rights Reserved
+ */
+
+#ifndef MACH_RIF_H
+#define MACH_RIF_H
+
+#include <linux/types.h>
+
+/**
+ * stm32_rifsc_check_access - Check RIF accesses for given device node
+ *
+ * @device_node                Node of the device for which the accesses are 
checked
+ */
+int stm32_rifsc_check_access(ofnode device_node);
+
+/**
+ * stm32_rifsc_check_access - Check RIF accesses for given id
+ *
+ * @device_node                Node of the device to get a reference on RIFSC
+ * @id                 ID of the resource to check
+ */
+int stm32_rifsc_check_access_by_id(ofnode device_node, u32 id);
+
+#endif /* MACH_RIF_H*/
diff --git a/arch/arm/mach-stm32mp/stm32mp2/Makefile 
b/arch/arm/mach-stm32mp/stm32mp2/Makefile
index b579ce5a800..5dbf75daa76 100644
--- a/arch/arm/mach-stm32mp/stm32mp2/Makefile
+++ b/arch/arm/mach-stm32mp/stm32mp2/Makefile
@@ -5,5 +5,6 @@
 
 obj-y += cpu.o
 obj-y += arm64-mmu.o
+obj-y += rifsc.o
 obj-$(CONFIG_OF_SYSTEM_SETUP) += fdt.o
 obj-$(CONFIG_STM32MP25X) += stm32mp25x.o
diff --git a/arch/arm/mach-stm32mp/stm32mp2/rifsc.c 
b/arch/arm/mach-stm32mp/stm32mp2/rifsc.c
new file mode 100644
index 00000000000..50dececf77b
--- /dev/null
+++ b/arch/arm/mach-stm32mp/stm32mp2/rifsc.c
@@ -0,0 +1,364 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
+/*
+ * Copyright (C) 2023, STMicroelectronics - All Rights Reserved
+ */
+
+#define LOG_CATEGORY UCLASS_NOP
+
+#include <dm.h>
+#include <asm/io.h>
+#include <dm/device.h>
+#include <dm/device_compat.h>
+#include <dm/lists.h>
+#include <linux/bitfield.h>
+#include <mach/rif.h>
+
+/* RIFSC offset register */
+#define RIFSC_RISC_SECCFGR0(id)                (0x10 + 0x4 * (id))
+#define RIFSC_RISC_PER0_CIDCFGR(id)    (0x100 + 0x8 * (id))
+#define RIFSC_RISC_PER0_SEMCR(id)      (0x104 + 0x8 * (id))
+
+/*
+ * SEMCR register
+ */
+#define SEMCR_MUTEX                    BIT(0)
+
+/* RIFSC miscellaneous */
+#define RIFSC_RISC_SCID_MASK           GENMASK(6, 4)
+#define RIFSC_RISC_SEMWL_MASK          GENMASK(23, 16)
+
+#define IDS_PER_RISC_SEC_PRIV_REGS     32
+
+/*
+ * CIDCFGR register fields
+ */
+#define CIDCFGR_CFEN                   BIT(0)
+#define CIDCFGR_SEMEN                  BIT(1)
+
+#define SEMWL_SHIFT                    16
+
+#define STM32MP25_RIFSC_ENTRIES                178
+
+/* Compartiment IDs */
+#define RIF_CID0                       0x0
+#define RIF_CID1                       0x1
+
+/*
+ * struct stm32_rifsc_plat: Information about RIFSC device
+ *
+ * @base: Base address of RIFSC
+ */
+struct stm32_rifsc_plat {
+       void *base;
+};
+
+/*
+ * struct stm32_rifsc_child_plat: Information about each child
+ *
+ * @domain_id: Domain id
+ */
+struct stm32_rifsc_child_plat {
+       u32 domain_id;
+};
+
+static bool stm32_rif_is_semaphore_available(void *base, u32 id)
+{
+       void *addr = base + RIFSC_RISC_PER0_SEMCR(id);
+
+       return !(readl(addr) & SEMCR_MUTEX);
+}
+
+static int stm32_rif_acquire_semaphore(void *base, u32 id)
+{
+       void *addr = base + RIFSC_RISC_PER0_SEMCR(id);
+
+       /* Check that the semaphore is available */
+       if (!stm32_rif_is_semaphore_available(base, id))
+               return -EACCES;
+
+       setbits_le32(addr, SEMCR_MUTEX);
+
+       /* Check that CID1 has the semaphore */
+       if (stm32_rif_is_semaphore_available(base, id) ||
+           FIELD_GET(RIFSC_RISC_SCID_MASK, (readl(addr)) != RIF_CID1))
+               return -EACCES;
+
+       return 0;
+}
+
+static int stm32_rif_release_semaphore(void *base, u32 id)
+{
+       void *addr = base + RIFSC_RISC_PER0_SEMCR(id);
+
+       if (stm32_rif_is_semaphore_available(base, id))
+               return 0;
+
+       clrbits_le32(addr, SEMCR_MUTEX);
+
+       /* Ok if another compartment takes the semaphore before the check */
+       if (!stm32_rif_is_semaphore_available(base, id) &&
+           FIELD_GET(RIFSC_RISC_SCID_MASK, (readl(addr)) == RIF_CID1))
+               return -EACCES;
+
+       return 0;
+}
+
+static int rifsc_parse_access_controller(ofnode node, struct 
ofnode_phandle_args *args)
+{
+       int ret;
+
+       ret = ofnode_parse_phandle_with_args(node, "access-controllers",
+                                            "#access-controller-cells", 0,
+                                            0, args);
+       if (ret) {
+               log_debug("failed to parse access-controller (%d)\n", ret);
+               return ret;
+       }
+
+       if (args->args_count != 1) {
+               log_debug("invalid domain args_count: %d\n", args->args_count);
+               return -EINVAL;
+       }
+
+       if (args->args[0] >= STM32MP25_RIFSC_ENTRIES) {
+               log_err("Invalid sys bus ID for %s\n", ofnode_get_name(node));
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int rifsc_check_access(void *base, u32 id)
+{
+       u32 reg_offset, reg_id, sec_reg_value, cid_reg_value, sem_reg_value;
+
+       /*
+        * RIFSC_RISC_PRIVCFGRx and RIFSC_RISC_SECCFGRx both handle 
configuration access for
+        * 32 peripherals. On the other hand, there is one 
_RIFSC_RISC_PERx_CIDCFGR register
+        * per peripheral
+        */
+       reg_id = id / IDS_PER_RISC_SEC_PRIV_REGS;
+       reg_offset = id % IDS_PER_RISC_SEC_PRIV_REGS;
+       sec_reg_value = readl(base + RIFSC_RISC_SECCFGR0(reg_id));
+       cid_reg_value = readl(base + RIFSC_RISC_PER0_CIDCFGR(id));
+       sem_reg_value = readl(base + RIFSC_RISC_PER0_SEMCR(id));
+
+       /*
+        * First check conditions for semaphore mode, which doesn't take into
+        * account static CID.
+        */
+       if (cid_reg_value & CIDCFGR_SEMEN)
+               goto skip_cid_check;
+
+       /*
+        * 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) {
+               log_debug("Invalid CID configuration for peripheral %d\n", id);
+               return -EACCES;
+       }
+
+       /* Check semaphore accesses */
+       if (cid_reg_value & CIDCFGR_SEMEN) {
+               if (!(FIELD_GET(RIFSC_RISC_SEMWL_MASK, cid_reg_value) & 
BIT(RIF_CID1))) {
+                       log_debug("Not in semaphore whitelist for peripheral 
%d\n", id);
+                       return -EACCES;
+               }
+               if (!stm32_rif_is_semaphore_available(base, id) &&
+                   !(FIELD_GET(RIFSC_RISC_SCID_MASK, sem_reg_value) & 
BIT(RIF_CID1))) {
+                       log_debug("Semaphore unavailable for peripheral %d\n", 
id);
+                       return -EACCES;
+               }
+       }
+
+skip_cid_check:
+       /* Check security configuration */
+       if (sec_reg_value & BIT(reg_offset)) {
+               log_debug("Invalid security configuration for peripheral %d\n", 
id);
+               return -EACCES;
+       }
+
+       return 0;
+}
+
+int stm32_rifsc_check_access_by_id(ofnode device_node, u32 id)
+{
+       struct ofnode_phandle_args args;
+       int err;
+
+       if (id >= STM32MP25_RIFSC_ENTRIES)
+               return -EINVAL;
+
+       err = rifsc_parse_access_controller(device_node, &args);
+       if (err)
+               return err;
+
+       return rifsc_check_access((void *)ofnode_get_addr(args.node), id);
+}
+
+int stm32_rifsc_check_access(ofnode device_node)
+{
+       struct ofnode_phandle_args args;
+       int err;
+
+       err = rifsc_parse_access_controller(device_node, &args);
+       if (err)
+               return err;
+
+       return rifsc_check_access((void *)ofnode_get_addr(args.node), 
args.args[0]);
+}
+
+static int stm32_rifsc_child_pre_probe(struct udevice *dev)
+{
+       struct stm32_rifsc_plat *plat = dev_get_plat(dev->parent);
+       struct stm32_rifsc_child_plat *child_plat = dev_get_parent_plat(dev);
+       u32 cid_reg_value;
+       int err;
+       u32 id = child_plat->domain_id;
+
+       cid_reg_value = readl(plat->base + RIFSC_RISC_PER0_CIDCFGR(id));
+
+       /*
+        * If the peripheral is in semaphore mode, take the semaphore so that
+        * the CID1 has the ownership.
+        */
+       if (cid_reg_value & CIDCFGR_SEMEN &&
+           (FIELD_GET(RIFSC_RISC_SEMWL_MASK, cid_reg_value) & BIT(RIF_CID1))) {
+               err = stm32_rif_acquire_semaphore(plat->base, id);
+               if (err) {
+                       dev_err(dev, "Couldn't acquire RIF semaphore for 
peripheral %d (%d)\n",
+                               id, err);
+                       return err;
+               }
+               dev_dbg(dev, "Acquiring semaphore for peripheral %d\n", id);
+       }
+
+       return 0;
+}
+
+static int stm32_rifsc_child_post_remove(struct udevice *dev)
+{
+       struct stm32_rifsc_plat *plat = dev_get_plat(dev->parent);
+       struct stm32_rifsc_child_plat *child_plat = dev_get_parent_plat(dev);
+       u32 cid_reg_value;
+       int err;
+       u32 id = child_plat->domain_id;
+
+       cid_reg_value = readl(plat->base + RIFSC_RISC_PER0_CIDCFGR(id));
+
+       /*
+        * If the peripheral is in semaphore mode, release the semaphore so that
+        * there's no ownership.
+        */
+       if (cid_reg_value & CIDCFGR_SEMEN &&
+           (FIELD_GET(RIFSC_RISC_SEMWL_MASK, cid_reg_value) & BIT(RIF_CID1))) {
+               err = stm32_rif_release_semaphore(plat->base, id);
+               if (err)
+                       dev_err(dev, "Couldn't release rif semaphore for 
peripheral %d (%d)\n",
+                               id, err);
+       }
+
+       return 0;
+}
+
+static int stm32_rifsc_child_post_bind(struct udevice *dev)
+{
+       struct stm32_rifsc_child_plat *child_plat = dev_get_parent_plat(dev);
+       struct ofnode_phandle_args args;
+       int ret;
+
+       if (!dev_has_ofnode(dev))
+               return -EPERM;
+
+       ret = rifsc_parse_access_controller(dev_ofnode(dev), &args);
+       if (ret)
+               return ret;
+
+       child_plat->domain_id = args.args[0];
+
+       return 0;
+}
+
+static int stm32_rifsc_bind(struct udevice *dev)
+{
+       struct stm32_rifsc_plat *plat = dev_get_plat(dev);
+       struct ofnode_phandle_args args;
+       int ret = 0, err = 0;
+       ofnode node;
+
+       plat->base = dev_read_addr_ptr(dev);
+       if (!plat->base) {
+               dev_err(dev, "can't get registers base address\n");
+               return -ENOENT;
+       }
+
+       for (node = ofnode_first_subnode(dev_ofnode(dev));
+            ofnode_valid(node);
+            node = ofnode_next_subnode(node)) {
+               const char *node_name = ofnode_get_name(node);
+
+               if (!ofnode_is_enabled(node))
+                       continue;
+
+               err = rifsc_parse_access_controller(node, &args);
+               if (err) {
+                       dev_dbg(dev, "%s failed to parse child on bus (%d)\n", 
node_name, err);
+                       continue;
+               }
+
+               err = rifsc_check_access(plat->base, args.args[0]);
+               if (err) {
+                       dev_info(dev, "%s not allowed on bus (%d)\n", 
node_name, err);
+                       continue;
+               }
+
+               err = lists_bind_fdt(dev, node, NULL, NULL,
+                                    gd->flags & GD_FLG_RELOC ? false : true);
+               if (err && !ret) {
+                       ret = err;
+                       dev_err(dev, "%s failed to bind on bus (%d)\n", 
node_name, ret);
+               }
+       }
+
+       if (ret)
+               dev_err(dev, "Some child failed to bind (%d)\n", ret);
+
+       return ret;
+}
+
+static int stm32_rifsc_remove(struct udevice *bus)
+{
+       struct udevice *dev;
+
+       /* Deactivate all child devices not yet removed */
+       for (device_find_first_child(bus, &dev); dev; 
device_find_next_child(&dev))
+               if (device_active(dev))
+                       stm32_rifsc_child_post_remove(dev);
+
+       return 0;
+}
+
+static const struct udevice_id stm32_rifsc_ids[] = {
+       { .compatible = "st,stm32mp25-rifsc" },
+       {},
+};
+
+U_BOOT_DRIVER(stm32_rifsc) = {
+       .name = "stm32_rifsc",
+       .id = UCLASS_NOP,
+       .of_match = stm32_rifsc_ids,
+       .bind = stm32_rifsc_bind,
+       .remove = stm32_rifsc_remove,
+       .child_post_bind = stm32_rifsc_child_post_bind,
+       .child_pre_probe = stm32_rifsc_child_pre_probe,
+       .child_post_remove = stm32_rifsc_child_post_remove,
+       .plat_auto = sizeof(struct stm32_rifsc_plat),
+       .per_child_plat_auto = sizeof(struct stm32_rifsc_child_plat),
+       .flags = DM_FLAG_OS_PREPARE,
+};
-- 
2.25.1

Reply via email to