Files
linux/drivers/bus/stm32_rifsc.c
Gatien Chevallier d5ce3b4e95 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: a182084572 ("bus: rifsc: introduce RIFSC firewall controller driver")
Signed-off-by: Gatien Chevallier <gatien.chevallier@foss.st.com>
Link: https://lore.kernel.org/r/20260129-fix_cid_check_rifsc-v1-1-ef280ccf764d@foss.st.com
Signed-off-by: Alexandre Torgue <alexandre.torgue@foss.st.com>
2026-03-25 17:40:21 +01:00

825 lines
19 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2023, STMicroelectronics - All Rights Reserved
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/bus/stm32_firewall.h>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/string.h>
#include <linux/types.h>
/*
* RIFSC offset register
*/
#define RIFSC_RISC_SECCFGR0 0x10
#define RIFSC_RISC_PRIVCFGR0 0x30
#define RIFSC_RISC_PER0_CIDCFGR 0x100
#define RIFSC_RISC_PER0_SEMCR 0x104
#define RIFSC_RISC_REG0_ACFGR 0x900
#define RIFSC_RISC_REG3_AADDR 0x924
#define RIFSC_RISC_HWCFGR2 0xFEC
/*
* SEMCR register
*/
#define SEMCR_MUTEX BIT(0)
/*
* HWCFGR2 register
*/
#define HWCFGR2_CONF1_MASK GENMASK(15, 0)
#define HWCFGR2_CONF2_MASK GENMASK(23, 16)
#define HWCFGR2_CONF3_MASK GENMASK(31, 24)
/*
* RIFSC miscellaneous
*/
#define RIFSC_RISC_CFEN_MASK BIT(0)
#define RIFSC_RISC_SEM_EN_MASK BIT(1)
#define RIFSC_RISC_SCID_MASK GENMASK(6, 4)
#define RIFSC_RISC_SEML_SHIFT 16
#define RIFSC_RISC_SEMWL_MASK GENMASK(23, 16)
#define RIFSC_RISC_PER_ID_MASK GENMASK(31, 24)
#define RIFSC_RISC_PERx_CID_MASK (RIFSC_RISC_CFEN_MASK | \
RIFSC_RISC_SEM_EN_MASK | \
RIFSC_RISC_SCID_MASK | \
RIFSC_RISC_SEMWL_MASK)
#define IDS_PER_RISC_SEC_PRIV_REGS 32
/* RIF miscellaneous */
/*
* CIDCFGR register fields
*/
#define CIDCFGR_CFEN BIT(0)
#define CIDCFGR_SEMEN BIT(1)
#define CIDCFGR_SEMWL(x) BIT(RIFSC_RISC_SEML_SHIFT + (x))
#define SEMWL_SHIFT 16
/* Compartiment IDs */
#define RIF_CID0 0x0
#define RIF_CID1 0x1
#if defined(CONFIG_DEBUG_FS)
#define RIFSC_RISUP_ENTRIES 128
#define RIFSC_RIMU_ENTRIES 16
#define RIFSC_RISAL_SUBREGIONS 2
#define RIFSC_RISAL_GRANULARITY 8
#define RIFSC_RIMC_ATTR0 0xC10
#define RIFSC_RIMC_CIDSEL BIT(2)
#define RIFSC_RIMC_MCID_MASK GENMASK(6, 4)
#define RIFSC_RIMC_MSEC BIT(8)
#define RIFSC_RIMC_MPRIV BIT(9)
#define RIFSC_RISC_SRCID_MASK GENMASK(6, 4)
#define RIFSC_RISC_SRPRIV BIT(9)
#define RIFSC_RISC_SRSEC BIT(8)
#define RIFSC_RISC_SRRLOCK BIT(1)
#define RIFSC_RISC_SREN BIT(0)
#define RIFSC_RISC_SRLENGTH_MASK GENMASK(27, 16)
#define RIFSC_RISC_SRSTART_MASK GENMASK(10, 0)
static const char *stm32mp21_rifsc_rimu_names[RIFSC_RIMU_ENTRIES] = {
"ETR",
"SDMMC1",
"SDMMC2",
"SDMMC3",
"OTG_HS",
"USBH",
"ETH1",
"ETH2",
"RESERVED",
"RESERVED",
"DCMIPP",
"LTDC_L1/L2",
"LTDC_L3",
"RESERVED",
"RESERVED",
"RESERVED",
};
static const char *stm32mp25_rifsc_rimu_names[RIFSC_RIMU_ENTRIES] = {
"ETR",
"SDMMC1",
"SDMMC2",
"SDMMC3",
"USB3DR",
"USBH",
"ETH1",
"ETH2",
"PCIE",
"GPU",
"DMCIPP",
"LTDC_L0/L1",
"LTDC_L2",
"LTDC_ROT",
"VDEC",
"VENC"
};
static const char *stm32mp21_rifsc_risup_names[RIFSC_RISUP_ENTRIES] = {
"TIM1",
"TIM2",
"TIM3",
"TIM4",
"TIM5",
"TIM6",
"TIM7",
"TIM8",
"TIM10",
"TIM11",
"TIM12",
"TIM13",
"TIM14",
"TIM15",
"TIM16",
"TIM17",
"RESERVED",
"LPTIM1",
"LPTIM2",
"LPTIM3",
"LPTIM4",
"LPTIM5",
"SPI1",
"SPI2",
"SPI3",
"SPI4",
"SPI5",
"SPI6",
"RESERVED",
"RESERVED",
"SPDIFRX",
"USART1",
"USART2",
"USART3",
"UART4",
"UART5",
"USART6",
"UART7",
"RESERVED",
"RESERVED",
"LPUART1",
"I2C1",
"I2C2",
"I2C3",
"RESERVED",
"RESERVED",
"RESERVED",
"RESERVED",
"RESERVED",
"SAI1",
"SAI2",
"SAI3",
"SAI4",
"RESERVED",
"MDF1",
"RESERVED",
"FDCAN",
"HDP",
"ADC1",
"ADC2",
"ETH1",
"ETH2",
"RESERVED",
"USBH",
"RESERVED",
"RESERVED",
"OTG_HS",
"DDRPERFM",
"RESERVED",
"RESERVED",
"RESERVED",
"RESERVED",
"RESERVED",
"STGEN",
"OCTOSPI1",
"RESERVED",
"SDMMC1",
"SDMMC2",
"SDMMC3",
"RESERVED",
"LTDC_CMN",
"RESERVED",
"RESERVED",
"RESERVED",
"RESERVED",
"RESERVED",
"CSI",
"DCMIPP",
"DCMI_PSSI",
"RESERVED",
"RESERVED",
"RESERVED",
"RNG1",
"RNG2",
"PKA",
"SAES",
"HASH1",
"HASH2",
"CRYP1",
"CRYP2",
"IWDG1",
"IWDG2",
"IWDG3",
"IWDG4",
"WWDG1",
"RESERVED",
"VREFBUF",
"DTS",
"RAMCFG",
"CRC",
"SERC",
"RESERVED",
"RESERVED",
"RESERVED",
"I3C1",
"I3C2",
"I3C3",
"RESERVED",
"ICACHE_DCACHE",
"LTDC_L1L2",
"LTDC_L3",
"RESERVED",
"RESERVED",
"RESERVED",
"RESERVED",
"OTFDEC1",
"RESERVED",
"IAC",
};
static const char *stm32mp25_rifsc_risup_names[RIFSC_RISUP_ENTRIES] = {
"TIM1",
"TIM2",
"TIM3",
"TIM4",
"TIM5",
"TIM6",
"TIM7",
"TIM8",
"TIM10",
"TIM11",
"TIM12",
"TIM13",
"TIM14",
"TIM15",
"TIM16",
"TIM17",
"TIM20",
"LPTIM1",
"LPTIM2",
"LPTIM3",
"LPTIM4",
"LPTIM5",
"SPI1",
"SPI2",
"SPI3",
"SPI4",
"SPI5",
"SPI6",
"SPI7",
"SPI8",
"SPDIFRX",
"USART1",
"USART2",
"USART3",
"UART4",
"UART5",
"USART6",
"UART7",
"UART8",
"UART9",
"LPUART1",
"I2C1",
"I2C2",
"I2C3",
"I2C4",
"I2C5",
"I2C6",
"I2C7",
"I2C8",
"SAI1",
"SAI2",
"SAI3",
"SAI4",
"RESERVED",
"MDF1",
"ADF1",
"FDCAN",
"HDP",
"ADC12",
"ADC3",
"ETH1",
"ETH2",
"RESERVED",
"USBH",
"RESERVED",
"RESERVED",
"USB3DR",
"COMBOPHY",
"PCIE",
"UCPD1",
"ETHSW_DEIP",
"ETHSW_ACM_CF",
"ETHSW_ACM_MSGBU",
"STGEN",
"OCTOSPI1",
"OCTOSPI2",
"SDMMC1",
"SDMMC2",
"SDMMC3",
"GPU",
"LTDC_CMN",
"DSI_CMN",
"RESERVED",
"RESERVED",
"LVDS",
"RESERVED",
"CSI",
"DCMIPP",
"DCMI_PSSI",
"VDEC",
"VENC",
"RESERVED",
"RNG",
"PKA",
"SAES",
"HASH",
"CRYP1",
"CRYP2",
"IWDG1",
"IWDG2",
"IWDG3",
"IWDG4",
"IWDG5",
"WWDG1",
"WWDG2",
"RESERVED",
"VREFBUF",
"DTS",
"RAMCFG",
"CRC",
"SERC",
"OCTOSPIM",
"GICV2M",
"RESERVED",
"I3C1",
"I3C2",
"I3C3",
"I3C4",
"ICACHE_DCACHE",
"LTDC_L0L1",
"LTDC_L2",
"LTDC_ROT",
"DSI_TRIG",
"DSI_RDFIFO",
"RESERVED",
"OTFDEC1",
"OTFDEC2",
"IAC",
};
struct rifsc_risup_debug_data {
char dev_name[15];
u8 dev_cid;
u8 dev_sem_cids;
u8 dev_id;
bool dev_cid_filt_en;
bool dev_sem_en;
bool dev_priv;
bool dev_sec;
};
struct rifsc_rimu_debug_data {
char m_name[11];
u8 m_cid;
bool cidsel;
bool m_sec;
bool m_priv;
};
struct rifsc_subreg_debug_data {
bool sr_sec;
bool sr_priv;
u8 sr_cid;
bool sr_rlock;
bool sr_enable;
u16 sr_start;
u16 sr_length;
};
struct stm32_rifsc_resources_names {
const char **device_names;
const char **initiator_names;
};
struct rifsc_dbg_private {
const struct stm32_rifsc_resources_names *res_names;
void __iomem *mmio;
unsigned int nb_risup;
unsigned int nb_rimu;
unsigned int nb_risal;
};
static const struct stm32_rifsc_resources_names rifsc_mp21_res_names = {
.device_names = stm32mp21_rifsc_risup_names,
.initiator_names = stm32mp21_rifsc_rimu_names,
};
static const struct stm32_rifsc_resources_names rifsc_mp25_res_names = {
.device_names = stm32mp25_rifsc_risup_names,
.initiator_names = stm32mp25_rifsc_rimu_names,
};
static void stm32_rifsc_fill_rimu_dbg_entry(struct rifsc_dbg_private *rifsc,
struct rifsc_rimu_debug_data *dbg_entry, int i)
{
const struct stm32_rifsc_resources_names *dbg_names = rifsc->res_names;
u32 rimc_attr = readl_relaxed(rifsc->mmio + RIFSC_RIMC_ATTR0 + 0x4 * 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;
dbg_entry->m_priv = rimc_attr & RIFSC_RIMC_MPRIV;
}
static void stm32_rifsc_fill_dev_dbg_entry(struct rifsc_dbg_private *rifsc,
struct rifsc_risup_debug_data *dbg_entry, int i)
{
const struct stm32_rifsc_resources_names *dbg_names = rifsc->res_names;
u32 cid_cfgr, sec_cfgr, priv_cfgr;
u8 reg_id = i / IDS_PER_RISC_SEC_PRIV_REGS;
u8 reg_offset = i % IDS_PER_RISC_SEC_PRIV_REGS;
cid_cfgr = readl_relaxed(rifsc->mmio + RIFSC_RISC_PER0_CIDCFGR + 0x8 * i);
sec_cfgr = readl_relaxed(rifsc->mmio + RIFSC_RISC_SECCFGR0 + 0x4 * reg_id);
priv_cfgr = readl_relaxed(rifsc->mmio + RIFSC_RISC_PRIVCFGR0 + 0x4 * reg_id);
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;
dbg_entry->dev_cid = FIELD_GET(RIFSC_RISC_SCID_MASK, cid_cfgr);
dbg_entry->dev_sem_cids = FIELD_GET(RIFSC_RISC_SEMWL_MASK, cid_cfgr);
dbg_entry->dev_sec = sec_cfgr & BIT(reg_offset) ? true : false;
dbg_entry->dev_priv = priv_cfgr & BIT(reg_offset) ? true : false;
}
static void stm32_rifsc_fill_subreg_dbg_entry(struct rifsc_dbg_private *rifsc,
struct rifsc_subreg_debug_data *dbg_entry, int i,
int j)
{
u32 risc_xcfgr = readl_relaxed(rifsc->mmio + RIFSC_RISC_REG0_ACFGR + 0x10 * i + 0x8 * j);
u32 risc_xaddr;
dbg_entry->sr_sec = risc_xcfgr & RIFSC_RISC_SRSEC;
dbg_entry->sr_priv = risc_xcfgr & RIFSC_RISC_SRPRIV;
dbg_entry->sr_cid = FIELD_GET(RIFSC_RISC_SRCID_MASK, risc_xcfgr);
dbg_entry->sr_rlock = risc_xcfgr & RIFSC_RISC_SRRLOCK;
dbg_entry->sr_enable = risc_xcfgr & RIFSC_RISC_SREN;
if (i == 2) {
risc_xaddr = readl_relaxed(rifsc->mmio + RIFSC_RISC_REG3_AADDR + 0x8 * j);
dbg_entry->sr_length = FIELD_GET(RIFSC_RISC_SRLENGTH_MASK, risc_xaddr);
dbg_entry->sr_start = FIELD_GET(RIFSC_RISC_SRSTART_MASK, risc_xaddr);
} else {
dbg_entry->sr_start = 0;
dbg_entry->sr_length = U16_MAX;
}
}
static int stm32_rifsc_conf_dump_show(struct seq_file *s, void *data)
{
struct rifsc_dbg_private *rifsc = (struct rifsc_dbg_private *)s->private;
int i, j;
seq_puts(s, "\n=============================================\n");
seq_puts(s, " RIFSC dump\n");
seq_puts(s, "=============================================\n\n");
seq_puts(s, "\n=============================================\n");
seq_puts(s, " RISUP dump\n");
seq_puts(s, "=============================================\n");
seq_printf(s, "\n| %-15s |", "Peripheral name");
seq_puts(s, "| Firewall ID |");
seq_puts(s, "| N/SECURE |");
seq_puts(s, "| N/PRIVILEGED |");
seq_puts(s, "| CID filtering |");
seq_puts(s, "| Semaphore mode |");
seq_puts(s, "| SCID |");
seq_printf(s, "| %7s |\n", "SEMWL");
for (i = 0; i < RIFSC_RISUP_ENTRIES && i < rifsc->nb_risup; i++) {
struct rifsc_risup_debug_data d_dbg_entry;
stm32_rifsc_fill_dev_dbg_entry(rifsc, &d_dbg_entry, i);
seq_printf(s, "| %-15s |", d_dbg_entry.dev_name);
seq_printf(s, "| %-11d |", d_dbg_entry.dev_id);
seq_printf(s, "| %-8s |", d_dbg_entry.dev_sec ? "SEC" : "NSEC");
seq_printf(s, "| %-12s |", d_dbg_entry.dev_priv ? "PRIV" : "NPRIV");
seq_printf(s, "| %-13s |", str_enabled_disabled(d_dbg_entry.dev_cid_filt_en));
seq_printf(s, "| %-14s |", str_enabled_disabled(d_dbg_entry.dev_sem_en));
seq_printf(s, "| %-4d |", d_dbg_entry.dev_cid);
seq_printf(s, "| %#-7x |\n", d_dbg_entry.dev_sem_cids);
}
seq_puts(s, "\n=============================================\n");
seq_puts(s, " RIMU dump\n");
seq_puts(s, "=============================================\n");
seq_puts(s, "| RIMU's name |");
seq_puts(s, "| CIDSEL |");
seq_puts(s, "| MCID |");
seq_puts(s, "| N/SECURE |");
seq_puts(s, "| N/PRIVILEGED |\n");
for (i = 0; i < RIFSC_RIMU_ENTRIES && rifsc->nb_rimu; i++) {
struct rifsc_rimu_debug_data m_dbg_entry;
stm32_rifsc_fill_rimu_dbg_entry(rifsc, &m_dbg_entry, i);
seq_printf(s, "| %-11s |", m_dbg_entry.m_name);
seq_printf(s, "| %-6s |", m_dbg_entry.cidsel ? "CIDSEL" : "");
seq_printf(s, "| %-4d |", m_dbg_entry.m_cid);
seq_printf(s, "| %-8s |", m_dbg_entry.m_sec ? "SEC" : "NSEC");
seq_printf(s, "| %-12s |\n", m_dbg_entry.m_priv ? "PRIV" : "NPRIV");
}
if (rifsc->nb_risal > 0) {
seq_puts(s, "\n=============================================\n");
seq_puts(s, " RISAL dump\n");
seq_puts(s, "=============================================\n");
seq_puts(s, "| Memory |");
seq_puts(s, "| Subreg. |");
seq_puts(s, "| N/SECURE |");
seq_puts(s, "| N/PRIVILEGED |");
seq_puts(s, "| Subreg. CID |");
seq_puts(s, "| Resource lock |");
seq_puts(s, "| Subreg. enable |");
seq_puts(s, "| Subreg. start |");
seq_puts(s, "| Subreg. end |\n");
for (i = 0; i < rifsc->nb_risal; i++) {
for (j = 0; j < RIFSC_RISAL_SUBREGIONS; j++) {
struct rifsc_subreg_debug_data sr_dbg_entry;
stm32_rifsc_fill_subreg_dbg_entry(rifsc, &sr_dbg_entry, i, j);
seq_printf(s, "| LPSRAM%1d |", i + 1);
seq_printf(s, "| %1s |", (j == 0) ? "A" : "B");
seq_printf(s, "| %-8s |", sr_dbg_entry.sr_sec ? "SEC" : "NSEC");
seq_printf(s, "| %-12s |", sr_dbg_entry.sr_priv ? "PRIV" : "NPRIV");
seq_printf(s, "| 0x%-9x |", sr_dbg_entry.sr_cid);
seq_printf(s, "| %-13s |",
sr_dbg_entry.sr_rlock ? "locked (1)" : "unlocked (0)");
seq_printf(s, "| %-14s |",
str_enabled_disabled(sr_dbg_entry.sr_enable));
seq_printf(s, "| 0x%-11x |", sr_dbg_entry.sr_start);
seq_printf(s, "| 0x%-11x |\n", sr_dbg_entry.sr_start +
sr_dbg_entry.sr_length - 1);
}
}
}
return 0;
}
DEFINE_SHOW_ATTRIBUTE(stm32_rifsc_conf_dump);
static int stm32_rifsc_register_debugfs(struct stm32_firewall_controller *rifsc_controller,
u32 nb_risup, u32 nb_rimu, u32 nb_risal)
{
struct rifsc_dbg_private *rifsc_priv;
struct dentry *root = NULL;
rifsc_priv = devm_kzalloc(rifsc_controller->dev, sizeof(*rifsc_priv), GFP_KERNEL);
if (!rifsc_priv)
return -ENOMEM;
rifsc_priv->mmio = rifsc_controller->mmio;
rifsc_priv->nb_risup = nb_risup;
rifsc_priv->nb_rimu = nb_rimu;
rifsc_priv->nb_risal = nb_risal;
rifsc_priv->res_names = of_device_get_match_data(rifsc_controller->dev);
root = debugfs_lookup("stm32_firewall", NULL);
if (!root)
root = debugfs_create_dir("stm32_firewall", NULL);
if (IS_ERR(root))
return PTR_ERR(root);
debugfs_create_file("rifsc", 0444, root, rifsc_priv, &stm32_rifsc_conf_dump_fops);
return 0;
}
#endif /* defined(CONFIG_DEBUG_FS) */
static bool stm32_rifsc_is_semaphore_available(void __iomem *addr)
{
return !(readl(addr) & SEMCR_MUTEX);
}
static int stm32_rif_acquire_semaphore(struct stm32_firewall_controller *stm32_firewall_controller,
int id)
{
void __iomem *addr = stm32_firewall_controller->mmio + RIFSC_RISC_PER0_SEMCR + 0x8 * id;
writel(SEMCR_MUTEX, addr);
/* Check that CID1 has the semaphore */
if (stm32_rifsc_is_semaphore_available(addr) ||
FIELD_GET(RIFSC_RISC_SCID_MASK, readl(addr)) != RIF_CID1)
return -EACCES;
return 0;
}
static void stm32_rif_release_semaphore(struct stm32_firewall_controller *stm32_firewall_controller,
int id)
{
void __iomem *addr = stm32_firewall_controller->mmio + RIFSC_RISC_PER0_SEMCR + 0x8 * id;
if (stm32_rifsc_is_semaphore_available(addr))
return;
writel(SEMCR_MUTEX, addr);
/* Ok if another compartment takes the semaphore before the check */
WARN_ON(!stm32_rifsc_is_semaphore_available(addr) &&
FIELD_GET(RIFSC_RISC_SCID_MASK, readl(addr)) == RIF_CID1);
}
static int stm32_rifsc_grant_access(struct stm32_firewall_controller *ctrl, u32 firewall_id)
{
struct stm32_firewall_controller *rifsc_controller = ctrl;
u32 reg_offset, reg_id, sec_reg_value, cid_reg_value;
int rc;
if (firewall_id >= rifsc_controller->max_entries) {
dev_err(rifsc_controller->dev, "Invalid sys bus ID %u", firewall_id);
return -EINVAL;
}
/*
* 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 = firewall_id / IDS_PER_RISC_SEC_PRIV_REGS;
reg_offset = firewall_id % IDS_PER_RISC_SEC_PRIV_REGS;
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);
/* Check security configuration */
if (sec_reg_value & BIT(reg_offset)) {
dev_dbg(rifsc_controller->dev,
"Invalid security configuration for peripheral: %d\n", firewall_id);
return -EACCES;
}
/* 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_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;
}
static void stm32_rifsc_release_access(struct stm32_firewall_controller *ctrl, u32 firewall_id)
{
stm32_rif_release_semaphore(ctrl, firewall_id);
}
static int stm32_rifsc_probe(struct platform_device *pdev)
{
struct stm32_firewall_controller *rifsc_controller;
struct device_node *np = pdev->dev.of_node;
u32 nb_risup, nb_rimu, nb_risal;
struct resource *res;
void __iomem *mmio;
int rc;
rifsc_controller = devm_kzalloc(&pdev->dev, sizeof(*rifsc_controller), GFP_KERNEL);
if (!rifsc_controller)
return -ENOMEM;
mmio = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(mmio))
return PTR_ERR(mmio);
rifsc_controller->dev = &pdev->dev;
rifsc_controller->mmio = mmio;
rifsc_controller->name = dev_driver_string(rifsc_controller->dev);
rifsc_controller->type = STM32_PERIPHERAL_FIREWALL | STM32_MEMORY_FIREWALL;
rifsc_controller->grant_access = stm32_rifsc_grant_access;
rifsc_controller->release_access = stm32_rifsc_release_access;
/* Get number of RIFSC entries*/
nb_risup = FIELD_GET(HWCFGR2_CONF1_MASK,
readl(rifsc_controller->mmio + RIFSC_RISC_HWCFGR2));
nb_rimu = FIELD_GET(HWCFGR2_CONF2_MASK,
readl(rifsc_controller->mmio + RIFSC_RISC_HWCFGR2));
nb_risal = FIELD_GET(HWCFGR2_CONF3_MASK,
readl(rifsc_controller->mmio + RIFSC_RISC_HWCFGR2));
/*
* On STM32MP21, RIFSC_RISC_HWCFGR2 shows an incorrect number of RISAL (NUM_RISAL is 3
* instead of 0). A software workaround is implemented using the st,mem-map property in the
* device tree. This property is absent or left empty if there is no RISAL.
*/
if (of_device_is_compatible(np, "st,stm32mp21-rifsc"))
nb_risal = 0;
rifsc_controller->max_entries = nb_risup + nb_rimu + nb_risal;
platform_set_drvdata(pdev, rifsc_controller);
rc = stm32_firewall_controller_register(rifsc_controller);
if (rc) {
dev_err(rifsc_controller->dev, "Couldn't register as a firewall controller: %d",
rc);
return rc;
}
rc = stm32_firewall_populate_bus(rifsc_controller);
if (rc) {
dev_err(rifsc_controller->dev, "Couldn't populate RIFSC bus: %d",
rc);
return rc;
}
#if defined(CONFIG_DEBUG_FS)
rc = stm32_rifsc_register_debugfs(rifsc_controller, nb_risup, nb_rimu, nb_risal);
if (rc)
return dev_err_probe(rifsc_controller->dev, rc, "Failed creating debugfs entry\n");
#endif
/* Populate all allowed nodes */
return of_platform_populate(np, NULL, NULL, &pdev->dev);
}
static const struct of_device_id stm32_rifsc_of_match[] = {
{
.compatible = "st,stm32mp25-rifsc",
#if defined(CONFIG_DEBUG_FS)
.data = &rifsc_mp25_res_names,
#endif
},
{
.compatible = "st,stm32mp21-rifsc",
#if defined(CONFIG_DEBUG_FS)
.data = &rifsc_mp21_res_names,
#endif
},
{}
};
MODULE_DEVICE_TABLE(of, stm32_rifsc_of_match);
static struct platform_driver stm32_rifsc_driver = {
.probe = stm32_rifsc_probe,
.driver = {
.name = "stm32-rifsc",
.of_match_table = stm32_rifsc_of_match,
},
};
module_platform_driver(stm32_rifsc_driver);
MODULE_AUTHOR("Gatien Chevallier <gatien.chevallier@foss.st.com>");
MODULE_DESCRIPTION("STMicroelectronics RIFSC driver");
MODULE_LICENSE("GPL");