mirror of https://github.com/torvalds/linux.git
842 lines
20 KiB
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");
|