mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2025-12-21 01:12:11 +01:00
Replace RIFSC check access APIs by grant/release access ones that handle the RIF semaphores. Signed-off-by: Gatien Chevallier <gatien.chevallier@foss.st.com> Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> Reviewed-by: Patrick Delaunay <patrick.delaunay@foss.st.com>
387 lines
9.6 KiB
C
387 lines
9.6 KiB
C
// 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 *addr)
|
|
{
|
|
return !(readl(addr) & SEMCR_MUTEX);
|
|
}
|
|
|
|
static int stm32_rifsc_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(addr) &&
|
|
FIELD_GET(RIFSC_RISC_SCID_MASK, (readl(addr)) != RIF_CID1))
|
|
return -EACCES;
|
|
|
|
setbits_le32(addr, SEMCR_MUTEX);
|
|
|
|
/* Check that CID1 has the semaphore */
|
|
if (stm32_rif_is_semaphore_available(addr) ||
|
|
FIELD_GET(RIFSC_RISC_SCID_MASK, (readl(addr)) != RIF_CID1))
|
|
return -EACCES;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int stm32_rifsc_release_semaphore(void *base, u32 id)
|
|
{
|
|
void *addr = base + RIFSC_RISC_PER0_SEMCR(id);
|
|
|
|
if (stm32_rif_is_semaphore_available(addr))
|
|
return 0;
|
|
|
|
clrbits_le32(addr, SEMCR_MUTEX);
|
|
|
|
/* Ok if another compartment takes the semaphore before the check */
|
|
if (!stm32_rif_is_semaphore_available(addr) &&
|
|
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 = 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 + RIFSC_RISC_PER0_SEMCR(id)) &&
|
|
!(FIELD_GET(RIFSC_RISC_SCID_MASK, sem_reg_value) & 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_grant_access_by_id(ofnode device_node, u32 id)
|
|
{
|
|
struct ofnode_phandle_args args;
|
|
u32 cid_reg_value;
|
|
void *rifsc_base;
|
|
int err;
|
|
|
|
err = rifsc_parse_access_controller(device_node, &args);
|
|
if (err)
|
|
panic("Failed to parse access-controllers property\n");
|
|
|
|
rifsc_base = (void *)ofnode_get_addr(args.node);
|
|
|
|
err = rifsc_check_access(rifsc_base, id);
|
|
if (err)
|
|
return err;
|
|
|
|
cid_reg_value = readl(rifsc_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_rifsc_acquire_semaphore(rifsc_base, id);
|
|
if (err) {
|
|
pr_err("Couldn't acquire RIF semaphore for peripheral %d (%d)\n",
|
|
id, err);
|
|
return err;
|
|
}
|
|
pr_debug("Acquiring RIF semaphore for peripheral %d\n", id);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int stm32_rifsc_grant_access(ofnode device_node)
|
|
{
|
|
struct ofnode_phandle_args args;
|
|
int err;
|
|
|
|
err = rifsc_parse_access_controller(device_node, &args);
|
|
if (err)
|
|
return err;
|
|
|
|
return stm32_rifsc_grant_access_by_id(device_node, args.args[0]);
|
|
|
|
}
|
|
|
|
void stm32_rifsc_release_access_by_id(ofnode device_node, u32 id)
|
|
{
|
|
struct ofnode_phandle_args args;
|
|
u32 cid_reg_value;
|
|
void *rifsc_base;
|
|
int err;
|
|
|
|
err = rifsc_parse_access_controller(device_node, &args);
|
|
if (err)
|
|
panic("Failed to parse access-controllers property\n");
|
|
|
|
rifsc_base = (void *)ofnode_get_addr(args.node);
|
|
|
|
cid_reg_value = readl(rifsc_base + RIFSC_RISC_PER0_CIDCFGR(id));
|
|
|
|
/* If the peripheral is in semaphore mode, release it if we have the ownership */
|
|
if (cid_reg_value & CIDCFGR_SEMEN &&
|
|
(FIELD_GET(RIFSC_RISC_SEMWL_MASK, cid_reg_value) & BIT(RIF_CID1))) {
|
|
err = stm32_rifsc_release_semaphore(rifsc_base, id);
|
|
if (err) {
|
|
panic("Couldn't release RIF semaphore for peripheral %d (%d)\n", id, err);
|
|
}
|
|
pr_debug("Releasing RIF semaphore for peripheral %d\n", id);
|
|
}
|
|
}
|
|
|
|
void stm32_rifsc_release_access(ofnode device_node)
|
|
{
|
|
struct ofnode_phandle_args args;
|
|
int err;
|
|
|
|
err = rifsc_parse_access_controller(device_node, &args);
|
|
if (err)
|
|
panic("Failed to parse access-controllers property\n");
|
|
|
|
stm32_rifsc_release_access_by_id(device_node, args.args[0]);
|
|
}
|
|
|
|
static int stm32_rifsc_child_pre_probe(struct udevice *dev)
|
|
{
|
|
struct stm32_rifsc_child_plat *child_plat = dev_get_parent_plat(dev);
|
|
|
|
return stm32_rifsc_grant_access_by_id(dev_ofnode(dev), child_plat->domain_id);
|
|
}
|
|
|
|
static int stm32_rifsc_child_post_remove(struct udevice *dev)
|
|
{
|
|
struct stm32_rifsc_child_plat *child_plat = dev_get_parent_plat(dev);
|
|
|
|
stm32_rifsc_release_access_by_id(dev_ofnode(dev), child_plat->domain_id);
|
|
|
|
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,
|
|
};
|