linux/drivers/bus/stm32_rifsc.c

842 lines
20 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/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/types.h>
#include "stm32_firewall.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);
snprintf(dbg_entry->m_name, sizeof(dbg_entry->m_name), "%s", 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);
snprintf(dbg_entry->dev_name, sizeof(dbg_entry->dev_name), "%s",
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);
/* 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,
"Invalid security configuration for peripheral: %d\n", firewall_id);
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)) {
rc = stm32_rif_acquire_semaphore(rifsc_controller, firewall_id);
if (rc) {
dev_err(rifsc_controller->dev,
"Couldn't acquire semaphore for peripheral: %d\n", firewall_id);
return rc;
}
}
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");