diff mbox

[2/2,RESEND] ARM: prima2: add NetWork on Chip driver for atlas7

Message ID 1429012556-14041-2-git-send-email-21cnbao@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Barry Song April 14, 2015, 11:55 a.m. UTC
From: Guo Zeng <Guo.Zeng@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@csr.com>
Signed-off-by: Barry Song <Baohua.Song@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

Comments

Arnd Bergmann April 15, 2015, 3:53 p.m. UTC | #1
On Tuesday 14 April 2015 11:55:56 Barry Song wrote:
> 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

I think we should have a more generic binding here, which describes the
differences between the instances of this bus in separate properties
rather than keying them off the compatible string.

That way you get a simpler driver that is automatically reusable
across chip generations, potentially using small extensions, but
not per-chip instance lists.

> 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@csr.com> */
>  	.smp            = smp_ops(sirfsoc_smp_ops),
> +	.init_machine   = sirfsoc_init_mach,
>  	.dt_compat      = atlas7_dt_match,
>  MACHINE_END
>  #endif

I think we can avoid adding this part.
> +#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;
> +};
>

Can you move the dram controller parts into a child driver at
drivers/memory?

> +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"},

This table seems artificially soc specific. 

> +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 *);

In general, please try to avoid forward declarations within on C file,
and just reorder the code appropriately.

> +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,
> +	},
> +};

The index values here could easily be DT properties, and you already
have a name for each from the device node. The fault enable and
error log can also be simple boolean properties.

> +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);

Any new sysfs files need a full description in Documentation/ABI.

Without that documentation, it's also hard to tell whether this ABI
is good.

> +__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);
> +	}

This should become simpler if you have a platform driver for the
base device and use of_platform_populate to create its child devices.

	Arnd
Barry Song April 16, 2015, 2:48 a.m. UTC | #2
2015-04-15 23:53 GMT+08:00 Arnd Bergmann <arnd@arndb.de>:
> On Tuesday 14 April 2015 11:55:56 Barry Song wrote:
>> 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
>
> I think we should have a more generic binding here, which describes the
> differences between the instances of this bus in separate properties
> rather than keying them off the compatible string.
>

if there is a general binding, it might be "sirf, nocfw" string with
different properties. here each firewall has name, index, different
register offset and initialization.
it seems we need some careful thinking to figure out what is suitable
to be a property.

> That way you get a simpler driver that is automatically reusable
> across chip generations, potentially using small extensions, but
> not per-chip instance lists.
>
>> 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@csr.com> */
>>       .smp            = smp_ops(sirfsoc_smp_ops),
>> +     .init_machine   = sirfsoc_init_mach,
>>       .dt_compat      = atlas7_dt_match,
>>  MACHINE_END
>>  #endif
>
> I think we can avoid adding this part.

this has not be avoided because platform_driver.probe() is not able to
call hook_fault_code(). this results in a section mismatch.
we used to have a platform_driver, finally, i feel it is more
difficult to convince people to move hook_fault_code() to support
hotplug.
do you think it is reasonable to refine hook_fault_code() to hotplug support?

>> +#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;
>> +};
>>
>
> Can you move the dram controller parts into a child driver at
> drivers/memory?

i have no idea why we do that. this firewall driver controls all
firewall features and ddr is only one of the devices using firewall.
all firewall are sharing some same low-level codes.
>
>> +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"},
>
> This table seems artificially soc specific.

real. so you mean they can be in dts?

>
>> +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 *);
>
> In general, please try to avoid forward declarations within on C file,
> and just reorder the code appropriately.

ok. let's try.

>
>> +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,
>> +     },
>> +};
>
> The index values here could easily be DT properties, and you already
> have a name for each from the device node. The fault enable and
> error log can also be simple boolean properties.

it seems here you are giving some good suggestions about how to
provide properties in your 1st comment.

>
>> +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);
>
> Any new sysfs files need a full description in Documentation/ABI.
>
> Without that documentation, it's also hard to tell whether this ABI
> is good.

this sysfs is only for debug purpose. it is not a normal API. firewall
has been set in bootloader and linux running in non-secure mode
actaully has no permission to do it.
so it is only for the automatic test when linux is running in secure mode.
do you think we can have a
#ifdef CONFIG_DEBUG...
#endif

>
>> +__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);
>> +     }
>
> This should become simpler if you have a platform driver for the
> base device and use of_platform_populate to create its child devices.

this has been explained by hook_fault_code() reason.


>
>         Arnd

-barry
Barry Song April 21, 2015, 3:06 a.m. UTC | #3
2015-04-16 10:48 GMT+08:00 Barry Song <21cnbao@gmail.com>:
> 2015-04-15 23:53 GMT+08:00 Arnd Bergmann <arnd@arndb.de>:
>> On Tuesday 14 April 2015 11:55:56 Barry Song wrote:
>>> 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
>>
>> I think we should have a more generic binding here, which describes the
>> differences between the instances of this bus in separate properties
>> rather than keying them off the compatible string.
>>
>
> if there is a general binding, it might be "sirf, nocfw" string with
> different properties. here each firewall has name, index, different
> register offset and initialization.
> it seems we need some careful thinking to figure out what is suitable
> to be a property.
>
>> That way you get a simpler driver that is automatically reusable
>> across chip generations, potentially using small extensions, but
>> not per-chip instance lists.
>>
>>> 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@csr.com> */
>>>       .smp            = smp_ops(sirfsoc_smp_ops),
>>> +     .init_machine   = sirfsoc_init_mach,
>>>       .dt_compat      = atlas7_dt_match,
>>>  MACHINE_END
>>>  #endif
>>
>> I think we can avoid adding this part.
>
> this has not be avoided because platform_driver.probe() is not able to
> call hook_fault_code(). this results in a section mismatch.
> we used to have a platform_driver, finally, i feel it is more
> difficult to convince people to move hook_fault_code() to support
> hotplug.
> do you think it is reasonable to refine hook_fault_code() to hotplug support?
>

here the background is that firewall can stop illegal memory/devices
access for both CPU and DMA, and generate error information for these
illegal behaviors. for example, if non-secure linux access secure RAM,
interrupts and aborts will generate.

1. for CPU, firewall can generate external async abort and interrupts,
both aborts and interrupts enable can be set/clear in firewall.
2. for non-CPU, firewall can generate interrupts.

originally, we want to use aborts for CPU firewall as we suppose the
abort should be sync, then we can capture the instructions causing the
aborts directly, and easy the debug. but finally HW guys told us the
abort is async. so here it seems there is no difference for us to move
to interrupts for CPU. then we can unify the codes for all MARCOs and
avoid the hook_fault_code(). and codes should also be more clean.

i need your comments for this, and now i strongly feel we can move to
interrupts for all. the external async aborts have no more helps for
debugging the illegal access for CPU.

-barry
diff mbox

Patch

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 <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@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;
+}