root/drivers/bus/stm32_rifsc.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");