From patchwork Wed Mar 25 22:53:12 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Barry Song <21cnbao@gmail.com> X-Patchwork-Id: 6095601 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 36354BF90F for ; Wed, 25 Mar 2015 22:58:50 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 2892A2035E for ; Wed, 25 Mar 2015 22:58:48 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 0F49720384 for ; Wed, 25 Mar 2015 22:58:46 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1YauDR-0000Hn-9H; Wed, 25 Mar 2015 22:56:01 +0000 Received: from mail-wi0-x231.google.com ([2a00:1450:400c:c05::231]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1YauDG-00006j-11 for linux-arm-kernel@lists.infradead.org; Wed, 25 Mar 2015 22:55:53 +0000 Received: by wibgn9 with SMTP id gn9so60740389wib.1 for ; Wed, 25 Mar 2015 15:55:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id; bh=5OHe/VorJI3a8L+yXbjsoLMRBq2b3VR3avBvMNwntaw=; b=NZNMLTQYtXWcjMVFwnTFxwZbyL9zVJtrEvvvv7nJMGEt+oJdVZKbrvUnIhtUPI49f7 iLTTUd9DF5I0Ou7muhJ+Iqq5Vdd7sUsjgY9g1n5bIub6yJ2VA8mWGcK6w/wSgaYcBBw9 JI/oZGN2B0ETTiL8oZywolWOykNyNFxxhLX9ltWIqdozJYdLM5qfChBeoPFEo5QHd/m7 MIetYrwrG3yzkDXGA0t/gjP18kCjyTT0cXF07yciPwUS0Bs1RjBFhoJIL3MtIjku9YZg CqfvZfEQJKD5SrpNo1FRuTp8GR2vZ4hmQ3QdgN11hp1kCBkay6SFVHLoouXK5CpBSGBm oUYQ== X-Received: by 10.194.83.66 with SMTP id o2mr22706637wjy.55.1427324127757; Wed, 25 Mar 2015 15:55:27 -0700 (PDT) Received: from localhost.localdomain (ec2-54-65-106-64.ap-northeast-1.compute.amazonaws.com. [54.65.106.64]) by mx.google.com with ESMTPSA id fm10sm6274561wib.7.2015.03.25.15.55.22 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 25 Mar 2015 15:55:26 -0700 (PDT) From: Barry Song <21cnbao@gmail.com> To: arm@kernel.org Subject: [PATCH] ARM: prima2: add NetWork on Chip driver for atlas7 Date: Thu, 26 Mar 2015 06:53:12 +0800 Message-Id: <1427323992-3710-1-git-send-email-21cnbao@gmail.com> X-Mailer: git-send-email 1.9.1 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20150325_155550_446243_E37A25F1 X-CRM114-Status: GOOD ( 16.86 ) X-Spam-Score: -0.8 (/) Cc: Guo Zeng , linux-arm-kernel@lists.infradead.org, Barry Song X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.1 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_MED, T_DKIM_INVALID, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Guo Zeng 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 Signed-off-by: Barry Song --- .../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 9ab8932..2500f8b 100644 --- a/arch/arm/mach-prima2/Kconfig +++ b/arch/arm/mach-prima2/Kconfig @@ -42,4 +42,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 #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 */ .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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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<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<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; +}