[PATCH] ARM: prima2: add NetWork on Chip driver for atlas7
Barry Song
21cnbao at gmail.com
Sun Mar 8 23:05:07 PDT 2015
From: Guo Zeng <Guo.Zeng at csr.com>
CSR atlas7 uses a NoC bus, SoC is splitted into mutiple MACROs. Every MACRO
holds some hardware modules.
All the devices connected to NoC MRCROs are described using sub-node to this MARCO.
For example, AUDMSCM MARCO includes multimediam nodes such as KAS, AC97, IACC, I2S,
USP0~3, LVDS etc.
For each MARCO, there is at least a firewall sub-node. This firewall can detect
illegal hardware access for security protection.
For CPU access, an external abort will generate for it; for other DMA access,
interrupts will trigger to CPU. In the abort and interrupt handlers, we can dump
the status.
Signed-off-by: Guo Zeng <Guo.Zeng at csr.com>
Signed-off-by: Barry Song <Baohua.Song at csr.com>
---
.../devicetree/bindings/bus/atlas7-noc.txt | 34 +
arch/arm/mach-prima2/Kconfig | 8 +
arch/arm/mach-prima2/Makefile | 2 +
arch/arm/mach-prima2/common.c | 8 +
arch/arm/mach-prima2/common.h | 6 +
arch/arm/mach-prima2/noc.c | 931 +++++++++++++++++++++
6 files changed, 989 insertions(+)
create mode 100644 Documentation/devicetree/bindings/bus/atlas7-noc.txt
create mode 100644 arch/arm/mach-prima2/noc.c
diff --git a/Documentation/devicetree/bindings/bus/atlas7-noc.txt b/Documentation/devicetree/bindings/bus/atlas7-noc.txt
new file mode 100644
index 0000000..449ddb5
--- /dev/null
+++ b/Documentation/devicetree/bindings/bus/atlas7-noc.txt
@@ -0,0 +1,34 @@
+Device tree bindings for CSRatlas7 NoC(Network on Chip)
+
+CSR atlas7 uses a NoC bus, SoC is splitted into mutiple MACROs. Every MACRO
+holds some hardware modules. For each MACRO
+properties:
+- compatible : Should be "arteris, flexnoc"
+- #address-cells: should be 1
+- #size-cells: should be 1
+- ranges : the child address space are mapped 1:1 onto the parent address space
+
+Sub-nodes:
+All the devices connected to noc are described using sub-node to noc. For
+example, AUDMSCM MARCO includes multimediam nodes such as KAS, AC97, IACC, I2S,
+USP0~3, LVDS.
+For each MARCO, there is at least a firewall sub-node. This firewall can detect
+illegal hardware access for security protection.
+
+Firewall sub-nodes:
+properties:
+- compatible : Should be one of
+ "sirf,nocfw-cpum",
+ "sirf,nocfw-cgum",
+ "sirf,nocfw-btm",
+ "sirf,nocfw-gnssm",
+ "sirf,nocfw-gpum",
+ "sirf,nocfw-mediam",
+ "sirf,nocfw-vdifm",
+ "sirf,nocfw-audiom",
+ "sirf,nocfw-ddrm",
+ "sirf,nocfw-rtcm",
+ "sirf,nocfw-dramfw",
+ "sirf,nocfw-spramfw"
+- reg: A resource specifier for the register space
+- interrupts : Should be the interrupt number - optional
diff --git a/arch/arm/mach-prima2/Kconfig b/arch/arm/mach-prima2/Kconfig
index e03d8b5..ed868d1 100644
--- a/arch/arm/mach-prima2/Kconfig
+++ b/arch/arm/mach-prima2/Kconfig
@@ -41,4 +41,12 @@ config ARCH_PRIMA2
config SIRF_IRQ
bool
+config ATLAS7DA_NOC
+ bool "CSR A7DA NOC"
+ default y
+ help
+ Support CSR SiRFSoC A7DA Platform NOC bus, with security dram/reg
+ firewall and related configure for validation, with errlog shown
+ on data abort or interrupt handler when bus transaction failed.
+
endif
diff --git a/arch/arm/mach-prima2/Makefile b/arch/arm/mach-prima2/Makefile
index d7d02b0..1248418 100644
--- a/arch/arm/mach-prima2/Makefile
+++ b/arch/arm/mach-prima2/Makefile
@@ -2,6 +2,8 @@ obj-y += rstc.o
obj-y += common.o
obj-y += rtciobrg.o
obj-$(CONFIG_SUSPEND) += pm.o sleep.o
+
+obj-$(CONFIG_ATLAS7DA_NOC) += noc.o
obj-$(CONFIG_SMP) += platsmp.o headsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
diff --git a/arch/arm/mach-prima2/common.c b/arch/arm/mach-prima2/common.c
index 8cadb30..4a9dcad 100644
--- a/arch/arm/mach-prima2/common.c
+++ b/arch/arm/mach-prima2/common.c
@@ -15,6 +15,13 @@
#include <linux/of_platform.h>
#include "common.h"
+static void __init sirfsoc_init_mach(void)
+{
+ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+
+ sirfsoc_noc_init();
+}
+
static void __init sirfsoc_init_late(void)
{
sirfsoc_pm_init();
@@ -60,6 +67,7 @@ static const char *const atlas7_dt_match[] __initconst = {
DT_MACHINE_START(ATLAS7_DT, "Generic ATLAS7 (Flattened Device Tree)")
/* Maintainer: Barry Song <baohua.song at csr.com> */
.smp = smp_ops(sirfsoc_smp_ops),
+ .init_machine = sirfsoc_init_mach,
.dt_compat = atlas7_dt_match,
MACHINE_END
#endif
diff --git a/arch/arm/mach-prima2/common.h b/arch/arm/mach-prima2/common.h
index 3916a66..122d8f9 100644
--- a/arch/arm/mach-prima2/common.h
+++ b/arch/arm/mach-prima2/common.h
@@ -28,4 +28,10 @@ extern int sirfsoc_pm_init(void);
static inline int sirfsoc_pm_init(void) { return 0; }
#endif
+#ifdef CONFIG_ATLAS7DA_NOC
+extern int sirfsoc_noc_init(void);
+#else
+static inline void sirfsoc_noc_init(void) { return 0; }
+#endif
+
#endif
diff --git a/arch/arm/mach-prima2/noc.c b/arch/arm/mach-prima2/noc.c
new file mode 100644
index 0000000..2c74121
--- /dev/null
+++ b/arch/arm/mach-prima2/noc.c
@@ -0,0 +1,931 @@
+/*
+* Atlas7 NoC support
+*/
+
+#define pr_fmt(fmt) "NoC: " fmt
+
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/io.h>
+#include <linux/sysfs.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+
+#include <asm/signal.h>
+
+#define NOC_CPUM_ERRLOG 0x800
+#define NOC_CPUM_FAULTEN 0x900
+
+#define NOC_AUDMSCM_ERRLOG 0xC00
+#define NOC_AUDMSCM_FAULTEN 0x400
+
+
+#define NOC_DDRM_ERRLOG 0x180
+#define NOC_DDRM_FAULTEN 0x800
+
+
+#define NOC_RTCM_ERRLOG 0xA00
+#define NOC_RTCM_FAULTEN 0x900
+
+
+
+#define ERRORLOGGER_0_ID_COREID 0x0
+#define ERRORLOGGER_0_ID_REVISIONID 0x4
+#define ERRORLOGGER_0_FAULTEN 0x8
+#define ERRORLOGGER_0_ERRVLD 0xc
+#define ERRORLOGGER_0_ERRCLR 0x10
+#define ERRORLOGGER_0_ERRLOG0 0x14
+#define ERRORLOGGER_0_ERRLOG1 0x18
+#define ERRORLOGGER_0_ERRLOG3 0x20
+#define ERRORLOGGER_0_ERRLOG5 0x28
+
+#define NOC_SB_FAULTEN 0x08
+#define NOC_SB_FLAGINEN0 0x10
+
+#define FLAGS_CPU BIT(0)
+#define FLAGS_STRICT BIT(1)
+#define FLAGS_NS BIT(2)
+#define FLAGS_S BIT(3)
+#define FLAGS_INITIATOR_NS BIT(4)
+#define FLAGS_INITIATOR_S BIT(5)
+
+
+#define CPUMASK_KAS 3
+#define CPUMASK_CM3 2
+#define CPUMASK_CSSI 1
+#define CPUMASK_CA7 0
+
+#define ACCESS_READ BIT(0)
+#define ACCESS_WRITE BIT(1)
+#define ACCESS_INITIATOR_READ BIT(2)
+#define ACCESS_INITIATOR_WRITE BIT(3)
+
+
+#define FW_RP_ENABLE_SET 0x3F04
+
+struct sirfsoc_nocfw_dram_t {
+ u32 rpbase;
+ u32 startaddr;
+ u32 endaddr;
+ u32 initiator;
+ u32 access;
+ u32 initiator_access;
+ u32 rpnum;
+ u32 flags;
+};
+
+struct dramfw_regs_access_t {
+ u32 initiator_r_set;
+ u32 initiator_r_clr;
+ u32 initiator_w_set;
+ u32 initiator_w_clr;
+};
+
+
+/* dram fw */
+struct dramfw_regs_t {
+ u32 start;
+ u32 end;
+ u32 reserved_0[2];
+ struct dramfw_regs_access_t access[4];
+ u32 reserved_1[4];
+ u32 fw_cpu_set;
+ u32 fw_cpu_clr;
+ u32 reserved_2[2];
+ u32 prot_set;
+ u32 prot_clr;
+ u32 prot_val_set;
+ u32 prot_val_clr;
+ u32 target_set;
+ u32 target_clr;
+ u32 target_val_set;
+ u32 target_val_clr;
+};
+
+/* reg fw */
+struct regfw_regs_t {
+ u32 ns_set;
+ u32 ns_clr;
+ u32 ns_sts;
+ u32 a7_set;
+ u32 a7_clr;
+ u32 a7_sts;
+ u32 cssi_set;
+ u32 cssi_clr;
+ u32 cssi_sts;
+ u32 m3_set;
+ u32 m3_clr;
+ u32 m3_sts;
+ u32 kas_set;
+ u32 kas_clr;
+ u32 kas_sts;
+};
+
+struct sirfsoc_nocfw_reg_t {
+ u32 base;
+ u32 off;
+ u32 ns;
+ u32 a7;
+
+ u32 cssi;
+ u32 m3;
+ u32 kas;
+};
+
+#define ddrm_SecureState_ReadSet0 0x1050
+#define ddrm_SecureState_ReadClr0 0x1054
+#define ddrm_SecureState_ReadSts0 0x1058
+
+#define ddrm_SecureState_ReadSet1 0x105C
+#define ddrm_SecureState_ReadClr1 0x1060
+#define ddrm_SecureState_ReadSts1 0x1064
+
+#define ddrm_SecureState_ReadSet2 0x1068
+#define ddrm_SecureState_ReadClr2 0x106C
+#define ddrm_SecureState_ReadSts2 0x1070
+
+#define ddrm_SecureState_ReadSet3 0x1074
+#define ddrm_SecureState_ReadClr3 0x1078
+#define ddrm_SecureState_ReadSts3 0x107C
+
+
+#define ddrm_SecureState_WriteSet0 0x1080
+#define ddrm_SecureState_WriteClr0 0x1084
+#define ddrm_SecureState_WriteSts0 0x1088
+
+#define ddrm_SecureState_WriteSet1 0x108C
+#define ddrm_SecureState_WriteClr1 0x1090
+#define ddrm_SecureState_WriteSts1 0x1094
+
+#define ddrm_SecureState_WriteSet2 0x1098
+#define ddrm_SecureState_WriteClr2 0x109C
+#define ddrm_SecureState_WriteSts2 0x10A0
+
+#define ddrm_SecureState_WriteSet3 0x10A4
+#define ddrm_SecureState_WriteClr3 0x10A8
+#define ddrm_SecureState_WriteSts3 0x10AC
+
+struct dramfw_reg_secure_t {
+ u32 readset;
+ u32 readclr;
+ u32 writeset;
+ u32 writeclr;
+};
+
+static struct dramfw_reg_secure_t dramfw_reg_secure_list[] = {
+ {ddrm_SecureState_ReadSet0, ddrm_SecureState_ReadClr0,
+ ddrm_SecureState_WriteSet0, ddrm_SecureState_WriteClr0},
+ {ddrm_SecureState_ReadSet1, ddrm_SecureState_ReadClr1,
+ ddrm_SecureState_WriteSet1, ddrm_SecureState_WriteClr1},
+ {ddrm_SecureState_ReadSet2, ddrm_SecureState_ReadClr2,
+ ddrm_SecureState_WriteSet2, ddrm_SecureState_WriteClr2},
+ {ddrm_SecureState_ReadSet3, ddrm_SecureState_ReadClr3,
+ ddrm_SecureState_WriteSet3, ddrm_SecureState_WriteClr3}
+};
+
+struct noc_info_t {
+ const char *desc;
+};
+
+static struct noc_info_t noc_initator_id_list[] = {
+ {"dmac2_ac97_aux_fifo"},
+ {"kas_dram"},
+ {"afe_cvd_vip0"},
+ {"usp0_axi_i"},
+ {"sgx"},
+ {"sdr"},
+ {"dmac2_usp1rx"},
+ {"dmac2_usp1tx"},
+ {"usb0"},
+ {"usb1"},
+ {"dmac2_usp0rx"},
+ {"dmac2_usp0tx"},
+ {"dmac2_usp2rx"},
+ {"dmac2_usp2tx"},
+ {"reserved"},
+ {"reserved"},
+ {"dmac3_iaccrx"},
+ {"dmac3_i2s1rx"},
+ {"dmac3_i2s1tx"},
+ {"dmac3_iacctx2"},
+ {"reserved"},
+ {"reserved"},
+ {"dmac3_ac97rx_fifo"},
+ {"dmac3_iacctx0"},
+ {"dmac3_iacctx1"},
+ {"dmac3_iacctx3"},
+ {"dmac3_ac97tx_fifo5"},
+ {"dmac3_ac97tx_fifo6"},
+ {"dmac3_ac97tx_fifo1"},
+ {"dmac3_ac97tx_fifo2"},
+ {"dmac3_ac97tx_fifo3"},
+ {"dmac3_ac97tx_fifo4"},
+ {"dmac4_usp3rx"},
+ {"dmac4_usp3tx"},
+ {"vpp0"},
+ {"vpp1"},
+ {"vip1"},
+ {"dcu"},
+ {"g2d"},
+ {"nand"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"dmac4_uart6rx"},
+ {"dmac4_uart6tx"},
+ {"reserved"},
+ {"reserved"},
+ {"dmac0_uart4rx"},
+ {"dmac0_uart4tx"},
+ {"dmac0_uart0tx"},
+ {"dmac0_uart0rx"},
+ {"dmac0_uart3rx"},
+ {"dmac0_uart3tx"},
+ {"dmac0_uart2rx"},
+ {"dmac0_uart2tx"},
+ {"dmac0_uart5rx"},
+ {"dmac0_uart5tx"},
+ {"sec_secure"},
+ {"sec_public"},
+ {"dmac0_spi1rx"},
+ {"dmac0_spi1tx"},
+ {"reserved"},
+ {"reserved"},
+ {"sys2pci_vdifm"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"sys2pci_mediam"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"armm3_data"},
+ {"qspi"},
+ {"hash"},
+ {"cssi_etr_axi"},
+ {"eth_avb"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"lcd0_ly0_rd"},
+ {"lcd0_ly1_rd"},
+ {"lcd0_ly2_rd"},
+ {"lcd0_ly3_rd"},
+ {"lcd0_wb_rd"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"lcd1_ly1_rd"},
+ {"lcd1_ly1_rd"},
+ {"lcd1_ly2_rd"},
+ {"lcd1_ly3_rd"},
+ {"lcd1_wb_rd"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"vxd_mmu"},
+ {"vxd_dmac"},
+ {"vxd_vec"},
+ {"vxd_dmc"},
+ {"vxd_deb"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"jpeg_tar"},
+ {"jpeg_code"},
+ {"jpeg_thumb"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+};
+
+enum NOC_MACRO_IDX {
+ CPUM_IDX = 0,
+ CGUM_IDX,
+ BTM_IDX,
+ GNSSM_IDX,
+ GPUM_IDX,
+ MEDIAM_IDX,
+ VDIFM_IDX,
+ AUDIOM_IDX,
+ DDRM_IDX,
+ RTCM_IDX,
+ DRAMFW_IDX,
+ SPRFW_IDX,
+};
+
+/*register firewall offset based on macro index*/
+static u32 noc_regfw_offset_list[10] = {0x1050, 0x50, 0x1050, 0x1050,
+ 0x1050, 0x1050, 0x2050, 0x2050, 0x2050, 0x2050};
+
+/*dram firewall sched port:six*/
+#define FW_A7 0x0
+#define FW_DDR_BE 0x4000
+#define FW_DDR_RTLL 0x8000
+#define FW_DDR_RT 0xC000
+#define FW_DDR_SGX 0x10000
+#define FW_DDR_VXD 0x14000
+
+#define NOC_MACRO_NUM 12
+#define NOC_MACRO_NAME_LEN 12
+
+struct noc_macro {
+ void __iomem *mbase;
+ spinlock_t lock;
+ u32 idx;
+ u32 irq;
+ u32 errlogoff;
+ u32 faultenoff;
+ char name[NOC_MACRO_NAME_LEN];
+ int (*init_macro)(struct platform_device *);
+};
+
+static int noc_macro_init(struct platform_device *);
+static int noc_spram_firewall_init(struct platform_device *);
+static int noc_dram_firewall_init(struct platform_device *);
+static int noc_a7_init(struct platform_device *);
+
+static struct noc_macro noc_macro_list[] = {
+ {
+ .name = "cpum",
+ .idx = CPUM_IDX,
+ .errlogoff = NOC_CPUM_ERRLOG,
+ .faultenoff = NOC_CPUM_FAULTEN,
+ .init_macro = noc_a7_init,
+ }, {
+ .name = "cgum",
+ .idx = CGUM_IDX,
+ }, {
+ .name = "btm",
+ .idx = BTM_IDX,
+ }, {
+ .name = "gnssm",
+ .idx = GNSSM_IDX,
+ }, {
+ .name = "gpum",
+ .idx = GPUM_IDX,
+ }, {
+ .name = "mediam",
+ .idx = MEDIAM_IDX,
+ }, {
+ .name = "vdifm",
+ .idx = VDIFM_IDX,
+ }, {
+ .name = "audiom",
+ .idx = AUDIOM_IDX,
+ .errlogoff = NOC_AUDMSCM_ERRLOG,
+ .faultenoff = NOC_AUDMSCM_FAULTEN,
+ .init_macro = noc_macro_init,
+ }, {
+ .name = "ddrm",
+ .idx = DDRM_IDX,
+ .errlogoff = NOC_DDRM_ERRLOG,
+ .faultenoff = NOC_DDRM_FAULTEN,
+ .init_macro = noc_macro_init,
+ }, {
+ .name = "rtcm",
+ .idx = RTCM_IDX,
+ .errlogoff = NOC_RTCM_ERRLOG,
+ .faultenoff = NOC_RTCM_FAULTEN,
+ .init_macro = noc_macro_init,
+ }, {
+ .name = "dramfw",
+ .idx = DRAMFW_IDX,
+ .init_macro = noc_dram_firewall_init,
+ }, {
+ .name = "spramfw",
+ .idx = SPRFW_IDX,
+ .init_macro = noc_spram_firewall_init,
+ },
+};
+
+
+
+struct noc_dram_params_t {
+ void __iomem *mbase;
+ u32 startaddr;
+ u32 endaddr;
+ u32 initiator;
+ u32 access;
+ u32 initiator_access;
+ u32 rpnum;
+ u32 flags;
+};
+
+#define to_noc_fw_inf(d) container_of(d, struct noc_fw_inf, dev)
+
+static struct noc_info_t noc_err_list[] = {
+ {"target error detected by slave"},
+ {"address decode error"},
+ {"unsupported request"},
+ {"power disconnect"},
+ {"security violation"},
+ {"hidden security violation"},
+ {"timout"},
+ {"reserved"},
+};
+
+static struct noc_info_t noc_cpu_list[] = {
+ {"A7"},
+ {"coresight"},
+ {"M3"},
+ {"KAS"},
+};
+
+static struct noc_info_t noc_opc_list[] = {
+ {"read"},
+ {"wrap read"},
+ {"link read"},
+ {"exclusive read"},
+ {"write"},
+ {"wrap write"},
+ {"condition write"},
+ {"reserved"},
+ {"preable packet"},
+ {"urgency packet"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+ {"reserved"},
+};
+/*data abort handler can not get base list*/
+
+static int noc_has_err(void __iomem *noc_errlog_mbase)
+{
+ u32 vld;
+
+ vld = readl_relaxed(noc_errlog_mbase + ERRORLOGGER_0_ERRVLD);
+ vld &= 0x1;
+ /* 1 indicates an error has been logged (default: 0x0) */
+ return vld;
+}
+
+static int noc_dump_errlog(struct noc_macro *nocm)
+{
+ u32 errCode0, errCode1, errCode3, errCode5, vld;
+ void __iomem *noc_errlog_mbase;
+
+ if (!nocm)
+ goto err;
+
+ noc_errlog_mbase = (void __iomem *)(nocm->mbase + nocm->errlogoff);
+
+ /*return 1 for normal abort handler */
+ vld = noc_has_err(noc_errlog_mbase);
+ if (0 == vld)
+ goto err;
+
+ errCode0 = readl_relaxed(noc_errlog_mbase + ERRORLOGGER_0_ERRLOG0);
+ errCode1 = readl_relaxed(noc_errlog_mbase + ERRORLOGGER_0_ERRLOG1);
+ errCode3 = readl_relaxed(noc_errlog_mbase + ERRORLOGGER_0_ERRLOG3);
+ errCode5 = readl_relaxed(noc_errlog_mbase + ERRORLOGGER_0_ERRLOG5);
+
+ /*error type*/
+ pr_info("err:\t%s\n", noc_err_list[(errCode0>>8) & 0x7].desc);
+
+ /*initiator id*/
+ if (nocm->idx == CPUM_IDX)
+ pr_info("ID:\t%s\n", noc_cpu_list[(errCode5>>3) & 0x3].desc);
+ else if (0 == (errCode5 & 0x1))
+ pr_info("ID:\t%s\n", noc_cpu_list[(errCode5>>2) & 0x3].desc);
+ else
+ pr_info("ID:\%s\n", noc_initator_id_list[(errCode5>>7 & 0x1F)
+ | ((errCode5>>2 & 0x3)<<5)].desc);
+
+ pr_info("Opc:\t%s\n", noc_opc_list[(errCode0>>1) & 0xF].desc);
+ pr_info("Addr\t%08x\n", errCode3);
+ pr_info("Len\t%08x\n", errCode0>>16 & 0x3F);
+
+ /* clear the NoC errlog */
+ writel_relaxed(0x1, noc_errlog_mbase + ERRORLOGGER_0_ERRCLR);
+
+ return 0;
+err:
+ return 1;
+}
+static int noc_abort_handler(unsigned long addr, unsigned int fsr,
+ struct pt_regs *regs)
+{
+ int ret;
+
+ ret = noc_dump_errlog(&noc_macro_list[CPUM_IDX]);
+ if (0 != ret)
+ return 1;
+ /*
+ * If it was not an imprecise abort (Bit10==0),
+ * then we need to correct the
+ * return address to be _after_ the instruction.
+ */
+ if (!(fsr & (1 << 10)))
+ regs->ARM_pc += 4;
+
+ return 0;
+}
+
+/* handler noc audio macro interrupt */
+static irqreturn_t noc_irq_handle(int irq, void *data)
+{
+ struct noc_macro *nocm = (struct noc_macro *)data;
+
+ noc_dump_errlog(nocm);
+ return IRQ_HANDLED;
+}
+
+static void noc_fault_enable(struct noc_macro *nocm)
+{
+ writel_relaxed(0x1, nocm->mbase +
+ nocm->faultenoff + NOC_SB_FAULTEN);
+ /*
+ *rtcm_sb_main_SidebandManager_FlagInEn0
+ *0 StatAlarm rtcm_probe Statistics alarm
+ *1 Fault rtcm_observer Error logging event
+ */
+ writel_relaxed(0x3, nocm->mbase +
+ nocm->faultenoff + NOC_SB_FLAGINEN0);
+ writel_relaxed(0x1, nocm->mbase +
+ nocm->errlogoff + ERRORLOGGER_0_FAULTEN);
+}
+
+static void noc_dramfw_cpu_set(void __iomem *fw_cpu_clr,
+ void __iomem *fw_cpu_set, u32 initiator, u32 access)
+{
+ /*
+ * clear all except r_CA7 and w_CA7,set r_CA7 and w_CA7
+ * r_KAS r_CM3 r_CSSI r_CA7 w_KAS w_CM3 w_CSSI w_CA7
+ */
+ writel_relaxed(0xFFFFFFFF, fw_cpu_clr);
+ if (access & ACCESS_READ)
+ writel_relaxed(1<<(initiator+4), fw_cpu_set);
+
+ if (access & ACCESS_WRITE)
+ writel_relaxed(1<<initiator, fw_cpu_set);
+}
+
+static void noc_dramfw_noncpu_acess_set(struct dramfw_regs_t *base,
+ void __iomem *ddrm_addr, u32 initiator, u32 access,
+ u32 initiator_access, u32 flags)
+{
+ u32 i = 0;
+ u32 val;
+
+ /* non-cpu initiator */
+ for (i = 0; i < 4; i++) {
+ writel_relaxed(0xFFFFFFFF,
+ &base->access[i].initiator_r_clr);
+ writel_relaxed(0xFFFFFFFF,
+ &base->access[i].initiator_w_clr);
+ }
+
+ i = initiator / 32;
+ val = 1<<(initiator - 32 * i);
+ /* dram access read/write */
+ if (access & ACCESS_READ)
+ writel_relaxed(val, &base->access[i].initiator_r_set);
+ if (access & ACCESS_WRITE)
+ writel_relaxed(val, &base->access[i].initiator_w_set);
+
+ /* initiator access read/write */
+ if (initiator_access & ACCESS_INITIATOR_READ) {
+ if (FLAGS_INITIATOR_S & flags)
+ writel_relaxed(val,
+ ddrm_addr +
+ dramfw_reg_secure_list[i].readclr);
+ else if (FLAGS_INITIATOR_NS & flags)
+ writel_relaxed(val,
+ ddrm_addr +
+ dramfw_reg_secure_list[i].readset);
+ }
+ if (initiator_access & ACCESS_INITIATOR_WRITE) {
+ if (FLAGS_INITIATOR_S & flags)
+ writel_relaxed(val,
+ ddrm_addr +
+ dramfw_reg_secure_list[i].writeclr);
+ else if (FLAGS_INITIATOR_NS & flags)
+ writel_relaxed(val,
+ ddrm_addr +
+ dramfw_reg_secure_list[i].writeset);
+ }
+}
+static void noc_dramfw_noncpu_secure_set(struct dramfw_regs_t *base,
+ u32 access, u32 flags)
+{
+ /*
+ *clear AxPROT[0] and AxPROT[2] enable,set AxPROT[1]
+ * arprot_2 arprot_1 arprot_0 r.awprot_2 awprot_1 awprot_0
+ */
+ writel_relaxed(0x00000077, &base->prot_clr);
+ writel_relaxed(0x000000FF, &base->prot_val_clr);
+
+ /*
+ * r_strict arprot_2 arprot_1 arprot_0 w_strict
+ *awprot_2 awprot_1 awprot_0
+ */
+
+ if (access & ACCESS_READ) {
+ writel_relaxed(0x00000020, &base->prot_set);
+
+ if (FLAGS_STRICT & flags)
+ writel_relaxed(0x00000080, &base->prot_val_set);
+
+ if (FLAGS_NS & flags)
+ writel_relaxed(0x00000020, &base->prot_val_set);
+ }
+
+ if (access & ACCESS_WRITE) {
+ writel_relaxed(0x00000002, &base->prot_set);
+
+ if (FLAGS_STRICT & flags)
+ writel_relaxed(0x00000008, &base->prot_val_set);
+
+ if (FLAGS_NS & flags)
+ writel_relaxed(0x00000002, &base->prot_val_set);
+
+ }
+}
+
+static void noc_dramfw_set(struct noc_dram_params_t *params)
+{
+ struct dramfw_regs_t *base;
+ struct noc_macro *nocm;
+ void __iomem *mbase;
+ u32 startaddr;
+ u32 endaddr;
+ u32 initiator;
+ u32 access;
+ u32 initiator_access;
+ u32 rpnum;
+ u32 flags;
+
+ if (!params)
+ return;
+
+ mbase = params->mbase;
+ startaddr = params->startaddr;
+ endaddr = params->endaddr;
+ initiator = params->initiator;
+ access = params->access;
+ initiator_access = params->initiator_access;
+ rpnum = params->rpnum;
+ flags = params->flags;
+
+ base = (struct dramfw_regs_t *)(mbase +
+ 0x100 * rpnum);
+
+ initiator &= 0xFF;
+
+ writel_relaxed(startaddr, &base->start);
+ writel_relaxed(endaddr, &base->end);
+
+ if (flags & FLAGS_CPU)
+ noc_dramfw_cpu_set(&base->fw_cpu_clr,
+ &base->fw_cpu_set, initiator, access);
+ else {
+ nocm = &noc_macro_list[DDRM_IDX];
+ noc_dramfw_noncpu_acess_set(base,
+ nocm->mbase,
+ initiator, access, initiator_access, flags);
+ }
+ noc_dramfw_noncpu_secure_set(base, access, flags);
+
+ /* last step enable rp */
+ writel_relaxed(1<<rpnum, mbase + FW_RP_ENABLE_SET);
+}
+
+static void noc_regfw_setval(void __iomem *addr_clr,
+ void __iomem *addr_set, u32 val)
+{
+ writel_relaxed(0xFFFFFFFF, addr_clr);
+ writel_relaxed(val, addr_set);
+}
+
+static void noc_regfw_set(void __iomem *mbase, u32 off, u32 ns,
+ u32 a7, u32 cssi, u32 m3, u32 kas)
+{
+ struct regfw_regs_t *base;
+
+ if (!mbase)
+ return;
+
+ base = (struct regfw_regs_t *)(mbase + off);
+ noc_regfw_setval(&base->ns_clr, &base->ns_set, ns);
+ noc_regfw_setval(&base->a7_clr, &base->a7_set, a7);
+ noc_regfw_setval(&base->m3_clr, &base->m3_set, m3);
+ noc_regfw_setval(&base->cssi_clr, &base->cssi_set, cssi);
+ noc_regfw_setval(&base->kas_clr, &base->kas_set, kas);
+}
+
+static ssize_t dramfw_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct noc_dram_params_t params;
+ struct noc_macro *nocm;
+ u32 schedport_off;
+
+ if (sscanf(buf, "%x %x %x %x %x %x %x %x\n",
+ &schedport_off, ¶ms.startaddr,
+ ¶ms.endaddr, ¶ms.initiator,
+ ¶ms.access, ¶ms.initiator_access,
+ ¶ms.rpnum, ¶ms.flags) != 8)
+ return -EINVAL;
+
+ nocm = &noc_macro_list[DRAMFW_IDX];
+ params.mbase = nocm->mbase + schedport_off;
+
+ noc_dramfw_set(¶ms);
+ return len;
+}
+static DEVICE_ATTR_WO(dramfw);
+
+static ssize_t spramfw_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct noc_macro *nocm;
+ struct noc_dram_params_t params;
+
+ if (sscanf(buf, "%x %x %x %x %x %x %x\n",
+ ¶ms.startaddr, ¶ms.endaddr,
+ ¶ms.initiator, ¶ms.access,
+ ¶ms.initiator_access,
+ ¶ms.rpnum, ¶ms.flags) != 7)
+
+ return -EINVAL;
+ nocm = &noc_macro_list[SPRFW_IDX];
+ params.mbase = nocm->mbase;
+ noc_dramfw_set(¶ms);
+ return len;
+}
+static DEVICE_ATTR_WO(spramfw);
+
+static ssize_t regfw_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct noc_macro *nocm;
+
+ u32 idx, ns, a7, cssi, m3, kas;
+
+ if (sscanf(buf, "%x %x %x %x %x %x\n", &idx, &ns, &a7,
+ &cssi, &m3, &kas) != 6 || idx > 9)
+ return -EINVAL;
+
+ nocm = &noc_macro_list[idx];
+ noc_regfw_set(nocm->mbase,
+ noc_regfw_offset_list[idx], ns, a7, cssi, m3, kas);
+ return len;
+}
+
+static DEVICE_ATTR_WO(regfw);
+
+
+static const struct of_device_id sirfsoc_nocfw_ids[] = {
+ { .compatible = "sirf,nocfw-cpum", .data = &noc_macro_list[0] },
+ { .compatible = "sirf,nocfw-cgum", .data = &noc_macro_list[1] },
+ { .compatible = "sirf,nocfw-btm", .data = &noc_macro_list[2] },
+ { .compatible = "sirf,nocfw-gnssm", .data = &noc_macro_list[3] },
+ { .compatible = "sirf,nocfw-gpum", .data = &noc_macro_list[4] },
+ { .compatible = "sirf,nocfw-mediam", .data = &noc_macro_list[5] },
+ { .compatible = "sirf,nocfw-vdifm", .data = &noc_macro_list[6] },
+ { .compatible = "sirf,nocfw-audiom", .data = &noc_macro_list[7] },
+ { .compatible = "sirf,nocfw-ddrm", .data = &noc_macro_list[8] },
+ { .compatible = "sirf,nocfw-rtcm", .data = &noc_macro_list[9] },
+ { .compatible = "sirf,nocfw-dramfw", .data = &noc_macro_list[10] },
+ { .compatible = "sirf,nocfw-spramfw", .data = &noc_macro_list[11] },
+
+};
+
+static int noc_dram_firewall_init(struct platform_device *pdev)
+{
+ int ret;
+ /*
+ * fireware has been set earlier in secure mode, here
+ * it is only for debug purpose
+ */
+ ret = device_create_file(&pdev->dev, &dev_attr_dramfw);
+ if (ret)
+ dev_err(&pdev->dev,
+ "failed to create dram firewall attribute, %d\n",
+ ret);
+
+ ret = device_create_file(&pdev->dev, &dev_attr_regfw);
+ if (ret)
+ dev_err(&pdev->dev,
+ "failed to create spram firewall attribute, %d\n",
+ ret);
+ return 0;
+}
+
+static int noc_spram_firewall_init(struct platform_device *pdev)
+{
+ int ret;
+ /*
+ * fireware has been set earlier in secure mode, here
+ * it is only for debug purpose
+ */
+ ret = device_create_file(&pdev->dev, &dev_attr_spramfw);
+ if (ret)
+ dev_err(&pdev->dev,
+ "failed to create spram firewall attribute, %d\n",
+ ret);
+
+ return 0;
+}
+
+static int noc_a7_init(struct platform_device *pdev)
+{
+ struct noc_macro *nocm;
+
+ nocm = platform_get_drvdata(pdev);
+ /* enable errlog trigger, A7 use abort */
+ noc_fault_enable(nocm);
+
+ return 0;
+}
+
+static int noc_macro_init(struct platform_device *pdev)
+{
+ int ret;
+ struct noc_macro *nocm;
+
+ nocm = platform_get_drvdata(pdev);
+
+ ret = of_irq_get(pdev->dev.of_node, 0);
+ if (ret <= 0) {
+ dev_info(&pdev->dev,
+ "Unable to find IRQ number. ret=%d\n", ret);
+ goto err;
+ }
+ nocm->irq = ret;
+ /* enable errlog trigger, thus irq/abort could come */
+ noc_fault_enable(nocm);
+ ret = devm_request_irq(&pdev->dev,
+ nocm->irq,
+ noc_irq_handle,
+ 0,
+ nocm->name, nocm);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ return ret;
+}
+
+__init int sirfsoc_noc_init(void)
+{
+ struct device_node *np;
+ const struct of_device_id *match;
+ struct noc_macro *nocm;
+ struct platform_device *pdev;
+
+ for_each_matching_node_and_match(np, sirfsoc_nocfw_ids, &match) {
+ if (!of_device_is_available(np))
+ continue;
+
+ nocm = (struct noc_macro *)match->data;
+ nocm->mbase = of_iomap(np, 0);
+ if (!nocm->mbase)
+ return -ENOMEM;
+
+ spin_lock_init(&nocm->lock);
+ pdev = of_find_device_by_node(np);
+ platform_set_drvdata(pdev, nocm);
+
+ hook_fault_code(8, noc_abort_handler, SIGBUS, 0,
+ "external abort on non-linefetch");
+
+ hook_fault_code(22, noc_abort_handler, SIGBUS, 0,
+ "imprecise external abort");
+
+ if (nocm->init_macro)
+ nocm->init_macro(pdev);
+ }
+
+ return 0;
+}
--
2.3.0
More information about the linux-arm-kernel
mailing list