[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, &params.startaddr,
+				&params.endaddr, &params.initiator,
+				&params.access, &params.initiator_access,
+				&params.rpnum, &params.flags) != 8)
+		return -EINVAL;
+
+	nocm = &noc_macro_list[DRAMFW_IDX];
+	params.mbase = nocm->mbase + schedport_off;
+
+	noc_dramfw_set(&params);
+	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",
+				&params.startaddr, &params.endaddr,
+				&params.initiator, &params.access,
+				&params.initiator_access,
+				&params.rpnum, &params.flags) != 7)
+
+		return -EINVAL;
+	nocm = &noc_macro_list[SPRFW_IDX];
+	params.mbase = nocm->mbase;
+	noc_dramfw_set(&params);
+	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