[linux-yocto] [PATCH 03/42] arch/arm/mach-axxia: kernel files to support the mach-axxia

Cristian Bercaru cristian.bercaru at windriver.com
Thu Jun 11 01:31:48 PDT 2015


From: Charlie Paul <cpaul.windriver at gmail.com>

These files are added and modified to support the LSI axxia
5500 board.

Signed-off-by: Charlie Paul <cpaul.windriver at gmail.com>
---
 arch/arm/include/asm/lsi/acp_ncr.h             |   45 +
 arch/arm/mach-axxia/Kconfig                    |   31 +
 arch/arm/mach-axxia/Makefile                   |   16 +
 arch/arm/mach-axxia/Makefile.boot              |    5 +
 arch/arm/mach-axxia/axxia-gic.c                | 1600 ++++++++++++++++++++++++
 arch/arm/mach-axxia/axxia.c                    |  252 ++++
 arch/arm/mach-axxia/axxia.h                    |   10 +
 arch/arm/mach-axxia/axxia_circular_queue.c     |   63 +
 arch/arm/mach-axxia/axxia_circular_queue.h     |   30 +
 arch/arm/mach-axxia/clock.c                    |  109 ++
 arch/arm/mach-axxia/ddr_retention.c            |  358 ++++++
 arch/arm/mach-axxia/ddr_shutdown.c             |  332 +++++
 arch/arm/mach-axxia/headsmp.S                  |   71 ++
 arch/arm/mach-axxia/include/mach/axxia-gic.h   |   17 +
 arch/arm/mach-axxia/include/mach/debug-macro.S |   21 +
 arch/arm/mach-axxia/include/mach/entry-macro.S |    5 +
 arch/arm/mach-axxia/include/mach/gpio.h        |    1 +
 arch/arm/mach-axxia/include/mach/hardware.h    |   24 +
 arch/arm/mach-axxia/include/mach/io.h          |   39 +
 arch/arm/mach-axxia/include/mach/irqs.h        |    5 +
 arch/arm/mach-axxia/include/mach/ncr.h         |   44 +
 arch/arm/mach-axxia/include/mach/pci.h         |    6 +
 arch/arm/mach-axxia/include/mach/rio.h         |   44 +
 arch/arm/mach-axxia/include/mach/system.h      |   33 +
 arch/arm/mach-axxia/include/mach/timers.h      |   39 +
 arch/arm/mach-axxia/include/mach/timex.h       |   23 +
 arch/arm/mach-axxia/include/mach/uncompress.h  |   65 +
 arch/arm/mach-axxia/io.c                       |   40 +
 arch/arm/mach-axxia/pci.c                      | 1127 +++++++++++++++++
 arch/arm/mach-axxia/pci.h                      |    1 +
 arch/arm/mach-axxia/platsmp.c                  |  315 +++++
 arch/arm/mach-axxia/smon.c                     |  223 ++++
 arch/arm/mach-axxia/smon.h                     |   72 ++
 arch/arm/mach-axxia/ssp-gpio.c                 |  132 ++
 arch/arm/mach-axxia/timers.c                   |  224 ++++
 35 files changed, 5422 insertions(+)
 create mode 100644 arch/arm/include/asm/lsi/acp_ncr.h
 create mode 100644 arch/arm/mach-axxia/Kconfig
 create mode 100644 arch/arm/mach-axxia/Makefile
 create mode 100644 arch/arm/mach-axxia/Makefile.boot
 create mode 100644 arch/arm/mach-axxia/axxia-gic.c
 create mode 100644 arch/arm/mach-axxia/axxia.c
 create mode 100644 arch/arm/mach-axxia/axxia.h
 create mode 100644 arch/arm/mach-axxia/axxia_circular_queue.c
 create mode 100644 arch/arm/mach-axxia/axxia_circular_queue.h
 create mode 100644 arch/arm/mach-axxia/clock.c
 create mode 100644 arch/arm/mach-axxia/ddr_retention.c
 create mode 100644 arch/arm/mach-axxia/ddr_shutdown.c
 create mode 100644 arch/arm/mach-axxia/headsmp.S
 create mode 100644 arch/arm/mach-axxia/include/mach/axxia-gic.h
 create mode 100644 arch/arm/mach-axxia/include/mach/debug-macro.S
 create mode 100644 arch/arm/mach-axxia/include/mach/entry-macro.S
 create mode 100644 arch/arm/mach-axxia/include/mach/gpio.h
 create mode 100644 arch/arm/mach-axxia/include/mach/hardware.h
 create mode 100644 arch/arm/mach-axxia/include/mach/io.h
 create mode 100644 arch/arm/mach-axxia/include/mach/irqs.h
 create mode 100644 arch/arm/mach-axxia/include/mach/ncr.h
 create mode 100644 arch/arm/mach-axxia/include/mach/pci.h
 create mode 100644 arch/arm/mach-axxia/include/mach/rio.h
 create mode 100644 arch/arm/mach-axxia/include/mach/system.h
 create mode 100644 arch/arm/mach-axxia/include/mach/timers.h
 create mode 100644 arch/arm/mach-axxia/include/mach/timex.h
 create mode 100644 arch/arm/mach-axxia/include/mach/uncompress.h
 create mode 100644 arch/arm/mach-axxia/io.c
 create mode 100644 arch/arm/mach-axxia/pci.c
 create mode 100644 arch/arm/mach-axxia/pci.h
 create mode 100644 arch/arm/mach-axxia/platsmp.c
 create mode 100644 arch/arm/mach-axxia/smon.c
 create mode 100644 arch/arm/mach-axxia/smon.h
 create mode 100644 arch/arm/mach-axxia/ssp-gpio.c
 create mode 100644 arch/arm/mach-axxia/timers.c

diff --git a/arch/arm/include/asm/lsi/acp_ncr.h b/arch/arm/include/asm/lsi/acp_ncr.h
new file mode 100644
index 0000000..114a36b
--- /dev/null
+++ b/arch/arm/include/asm/lsi/acp_ncr.h
@@ -0,0 +1,45 @@
+/*
+ * asm/lsi/acp_ncr.h
+ *
+ * Copyright (C) 2010 LSI
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307	 USA
+ */
+
+#ifndef __DRIVERS_LSI_ACP_NCR_H
+#define __DRIVERS_LSI_ACP_NCR_H
+
+#ifndef NCP_REGION_ID
+#define NCP_REGION_ID(node, target) \
+(unsigned long)((((node) & 0xffff) << 16) | ((target) & 0xffff))
+#endif
+
+#ifndef NCP_NODE_ID
+#define NCP_NODE_ID(region) (((region) >> 16) & 0xffff)
+#endif
+
+#ifndef NCP_TARGET_ID
+#define NCP_TARGET_ID(region) ((region) & 0xffff)
+#endif
+
+int ncr_read(unsigned long, unsigned long, int, void *);
+int ncr_write(unsigned long, unsigned long, int, void *);
+
+int is_asic(void);
+
+extern int acp_mdio_read(unsigned long, unsigned long, unsigned short *, int);
+extern int acp_mdio_write(unsigned long, unsigned long, unsigned short, int);
+
+#endif /*  __DRIVERS_LSI_ACP_NCR_H */
diff --git a/arch/arm/mach-axxia/Kconfig b/arch/arm/mach-axxia/Kconfig
new file mode 100644
index 0000000..4a4922f
--- /dev/null
+++ b/arch/arm/mach-axxia/Kconfig
@@ -0,0 +1,31 @@
+menu "Axxia platform type"
+	depends on ARCH_AXXIA
+
+config ARCH_AXXIA_GIC
+        bool "Multi-cluster ARM GIC support for the LSI Axxia platforms"
+        select IRQ_DOMAIN
+        select MULTI_IRQ_HANDLER
+	help
+	  The LSI Axxia platforms can have up to four clusters, each having
+	  four cores (so a total of 16 cores). This requires the use of a
+	  distributed interrupt system in lieu of of a single ARM GIC.
+
+	  This option enables support for this multi-cluster setup.
+
+config ARCH_AXXIA_DT
+	bool "Device Tree support for LSI Axxia platforms"
+	select ARCH_AXXIA_GIC
+	select ARM_PATCH_PHYS_VIRT
+	select AUTO_ZRELADDR
+	select CPU_V7
+	select HAVE_SMP
+	select MIGHT_HAVE_CACHE_L2X0
+	select USE_OF
+	help
+	  The LSI Axxia platforms require a Flattened Device Tree to be passed
+	  to the kernel.
+
+	  If your bootloader supports Flattened Device Tree based booting,
+	  say Y here.
+
+endmenu
diff --git a/arch/arm/mach-axxia/Makefile b/arch/arm/mach-axxia/Makefile
new file mode 100644
index 0000000..0cceb04
--- /dev/null
+++ b/arch/arm/mach-axxia/Makefile
@@ -0,0 +1,16 @@
+#
+# Makefile for the linux kernel.
+#
+obj-y					+= axxia.o
+obj-y					+= clock.o
+obj-y					+= io.o
+obj-y					+= ssp-gpio.o
+# obj-y					+= ncr.o
+obj-y					+= timers.o
+obj-y					+= pci.o
+obj-y					+= ddr_retention.o ddr_shutdown.o
+obj-$(CONFIG_SMP)			+= platsmp.o headsmp.o
+obj-$(CONFIG_ARCH_AXXIA_GIC)		+= axxia-gic.o
+obj-$(CONFIG_HOTPLUG_CPU)		+= hotplug.o lsi_power_management.o axxia_circular_queue.o
+obj-$(CONFIG_AXXIA_RIO)                 += rapidio.o
+obj-$(CONFIG_HW_PERF_EVENTS)            += perf_event_platform.o smon.o
diff --git a/arch/arm/mach-axxia/Makefile.boot b/arch/arm/mach-axxia/Makefile.boot
new file mode 100644
index 0000000..c6dd891
--- /dev/null
+++ b/arch/arm/mach-axxia/Makefile.boot
@@ -0,0 +1,5 @@
+# Those numbers are used only by the non-DT V2P-CA9 platform
+# The DT-enabled ones require CONFIG_AUTO_ZRELADDR=y
+   zreladdr-y	+= 0x60008000
+params_phys-y	:= 0x60000100
+initrd_phys-y	:= 0x60800000
diff --git a/arch/arm/mach-axxia/axxia-gic.c b/arch/arm/mach-axxia/axxia-gic.c
new file mode 100644
index 0000000..6b3a759
--- /dev/null
+++ b/arch/arm/mach-axxia/axxia-gic.c
@@ -0,0 +1,1600 @@
+/*
+ *  linux/arch/arm/mach-axxia/axxia-gic.c
+ *
+ *  Cloned from linux/arch/arm/common/gic.c
+ *
+ *  Copyright (C) 2013 LSI Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Interrupt architecture for the Axxia:
+ *
+ * o The Axxia chip can have up to four clusters, and each cluster has
+ *   an ARM GIC interrupt controller.
+ *
+ * o In each GIC, there is one Interrupt Distributor, which receives
+ *   interrupts from system devices and sends them to the Interrupt
+ *   Controllers.
+ *
+ * o There is one CPU Interface per CPU, which sends interrupts sent
+ *   by the Distributor, and interrupts generated locally, to the
+ *   associated CPU. The base address of the CPU interface is usually
+ *   aliased so that the same address points to different chips depending
+ *   on the CPU it is accessed from.
+ *
+ * o The Axxia chip uses a distributed interrupt interface that's used
+ *   for IPI messaging between clusters. Therefore, this design does not
+ *   use the GIC software generated interrupts (0 - 16).
+ *
+ * Note that IRQs 0-31 are special - they are local to each CPU.
+ * As such, the enable set/clear, pending set/clear and active bit
+ * registers are banked per-cpu for these sources.
+ */
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/cpu_pm.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip/arm-gic.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+
+#include <asm/exception.h>
+#include <asm/smp_plat.h>
+#include <asm/mach/irq.h>
+
+#include <mach/axxia-gic.h>
+#include "lsi_power_management.h"
+#include "axxia_circular_queue.h"
+
+#define MAX_GIC_INTERRUPTS  1020
+
+static u32 irq_cpuid[MAX_GIC_INTERRUPTS];
+static void __iomem *ipi_mask_reg_base;
+static void __iomem *ipi_send_reg_base;
+
+/* AXM IPI numbers */
+enum axxia_ext_ipi_num {
+	IPI0_CPU0 = 227,	/* Axm IPI 195 */
+	IPI0_CPU1,
+	IPI0_CPU2,
+	IPI0_CPU3,
+	IPI1_CPU0,		/* Axm IPI 199 */
+	IPI1_CPU1,
+	IPI1_CPU2,
+	IPI1_CPU3,
+	IPI2_CPU0,		/* Axm IPI 203 */
+	IPI2_CPU1,
+	IPI2_CPU2,
+	IPI2_CPU3,
+	IPI3_CPU0,		/* Axm IPI 207 */
+	IPI3_CPU1,
+	IPI3_CPU2,
+	IPI3_CPU3,
+	MAX_AXM_IPI_NUM
+};
+
+/* MUX Message types. */
+enum axxia_mux_msg_type {
+	MUX_MSG_CALL_FUNC = 0,
+	MUX_MSG_CALL_FUNC_SINGLE,
+	MUX_MSG_CPU_STOP,
+	MUX_MSG_CPU_WAKEUP,
+	MUX_MSG_IRQ_WORK,
+	MUX_MSG_COMPLETION
+};
+
+struct axxia_mux_msg {
+	u32 msg;
+};
+
+static DEFINE_PER_CPU_SHARED_ALIGNED(struct axxia_mux_msg, ipi_mux_msg);
+
+static void muxed_ipi_message_pass(const struct cpumask *mask,
+				   enum axxia_mux_msg_type ipi_num)
+{
+	struct axxia_mux_msg *info;
+	int cpu;
+
+	for_each_cpu(cpu, mask) {
+		info = &per_cpu(ipi_mux_msg, cpu_logical_map(cpu));
+		info->msg |= 1 << ipi_num;
+	}
+}
+
+static void axxia_ipi_demux(struct pt_regs *regs)
+{
+	struct axxia_mux_msg *info = &__get_cpu_var(ipi_mux_msg);
+	u32 all;
+
+	do {
+		all = xchg(&info->msg, 0);
+		if (all & (1 << MUX_MSG_CALL_FUNC))
+			handle_IPI(3, regs); /* 3 = ARM IPI_CALL_FUNC */
+		if (all & (1 << MUX_MSG_CALL_FUNC_SINGLE))
+			handle_IPI(4, regs); /* 4 = ARM IPI_CALL_FUNC_SINGLE */
+		if (all & (1 << MUX_MSG_CPU_STOP))
+			handle_IPI(5, regs); /* 5 = ARM IPI_CPU_STOP */
+		if (all & (1 << MUX_MSG_IRQ_WORK))
+			handle_IPI(6, regs); /* 6 = ARM IPI_IRQ_WORK */
+		if (all & (1 << MUX_MSG_COMPLETION))
+			handle_IPI(7, regs); /* 7 = ARM IPI_COMPLETION */
+		if (all & (1 << MUX_MSG_CPU_WAKEUP))
+			handle_IPI(0, regs); /* 0 = ARM IPI_WAKEUP */
+	} while (info->msg);
+}
+
+union gic_base {
+	void __iomem *common_base;
+	void __percpu __iomem **percpu_base;
+};
+
+struct gic_chip_data {
+	union gic_base dist_base;
+	union gic_base cpu_base;
+#ifdef CONFIG_CPU_PM
+	u32 saved_spi_enable[DIV_ROUND_UP(MAX_GIC_INTERRUPTS, 32)]
+			    [MAX_NUM_CLUSTERS];
+	u32 saved_spi_conf[DIV_ROUND_UP(MAX_GIC_INTERRUPTS, 16)]
+			  [MAX_NUM_CLUSTERS];
+	u32 saved_spi_target[DIV_ROUND_UP(MAX_GIC_INTERRUPTS, 4)]
+			    [MAX_NUM_CLUSTERS];
+	u32 __percpu *saved_ppi_enable[MAX_NUM_CLUSTERS];
+	u32 __percpu *saved_ppi_conf[MAX_NUM_CLUSTERS];
+#endif
+	struct irq_domain *domain;
+	unsigned int gic_irqs;
+	unsigned long dist_init_done;
+};
+
+enum gic_rpc_func_mask {
+	IRQ_MASK = 0x01,
+	IRQ_UNMASK = 0x02,
+	SET_TYPE = 0x04,
+	SET_AFFINITY = 0x08,
+	CLR_AFFINITY = 0x10,
+	GIC_NOTIFIER = 0x20,
+	MAX_GIC_FUNC_MASK
+};
+
+
+#ifdef CONFIG_CPU_PM
+struct gic_notifier_data {
+	struct notifier_block *self;
+	unsigned long cmd;
+	void *v;
+
+};
+#endif
+
+struct gic_rpc_data {
+	struct irq_data *d;
+	u32 func_mask;
+	u32 cpu, oldcpu;
+	u32 type;
+	bool update_enable;
+	const struct cpumask *mask_val;
+#ifdef CONFIG_CPU_PM
+	struct gic_notifier_data gn_data;
+#endif
+};
+
+
+static DEFINE_RAW_SPINLOCK(irq_controller_lock);
+
+static DEFINE_MUTEX(irq_bus_lock);
+
+static struct gic_chip_data gic_data __read_mostly;
+static struct gic_rpc_data gic_rpc_data = {NULL, 0, 0, 0, 0, NULL};
+
+#define gic_data_dist_base(d)	((d)->dist_base.common_base)
+#define gic_data_cpu_base(d)	((d)->cpu_base.common_base)
+#define gic_set_base_accessor(d, f)
+
+static inline void __iomem *gic_dist_base(struct irq_data *d)
+{
+	struct gic_chip_data *gic_data = irq_data_get_irq_chip_data(d);
+	return gic_data_dist_base(gic_data);
+}
+
+static inline void __iomem *gic_cpu_base(struct irq_data *d)
+{
+	struct gic_chip_data *gic_data = irq_data_get_irq_chip_data(d);
+	return gic_data_cpu_base(gic_data);
+}
+
+static inline unsigned int gic_irq(struct irq_data *d)
+{
+	return d->hwirq;
+}
+
+
+/*************************** CIRCULAR QUEUE **************************************/
+struct circular_queue_t axxia_circ_q;
+static void axxia_gic_flush_affinity_queue(struct work_struct *dummy);
+static void gic_set_affinity_remote(void *info);
+static void gic_clr_affinity_remote(void *info);
+
+static DECLARE_WORK(axxia_gic_affinity_work, axxia_gic_flush_affinity_queue);
+static DEFINE_MUTEX(affinity_lock);
+
+enum axxia_affinity_mode {
+	AFFINITY_CLEAR_LOCAL = 1,
+	AFFINITY_CLEAR_OTHER_CLUSTER,
+	AFFINITY_SET_LOCAL,
+	AFFINITY_SET_OTHER_CLUSTER
+};
+
+static void axxia_gic_flush_affinity_queue(struct work_struct *dummy)
+{
+
+	void *qdata;
+	struct gic_rpc_data *rpc_data;
+
+	while (axxia_get_item(&axxia_circ_q, &qdata) != -1) {
+
+		rpc_data = (struct gic_rpc_data *) qdata;
+		if (rpc_data->func_mask == SET_AFFINITY) {
+			if (cpu_online(rpc_data->cpu)) {
+				smp_call_function_single(rpc_data->cpu, gic_set_affinity_remote,
+						qdata, 1);
+			}
+		} else if (rpc_data->func_mask == CLR_AFFINITY) {
+			if (cpu_online(rpc_data->cpu)) {
+				smp_call_function_single(rpc_data->cpu, gic_clr_affinity_remote,
+						qdata, 1);
+			}
+		}
+		kfree(qdata);
+	}
+
+}
+
+/*
+ * This GIC driver implements IRQ management routines (e.g., gic_mask_irq,
+ * etc.) that work across multiple clusters. Since a core cannot directly
+ * manipulate GIC registers on another cluster, the Linux RPC mechanism
+ * (smp_call_function_single) is used to remotely execute these IRQ management
+ * routines. However, the IRQ management routines are invoked in thread
+ * context (interrupts disabled on the local core), and for this reason,
+ * smp_call_function_single() cannot be used directly.
+ *
+ * The Linux interrupt code has a mechanism, which is called bus lock/unlock,
+ * which was created for irq chips hanging off slow busses like i2c/spi. The
+ * bus lock is mutex that is used to serialize bus accesses. We take advantage
+ *
+ * of this feature here, because we can think of IRQ management routines having
+ * to remotely execute on other clusters as a "slow bus" action. Doing this
+ * here serializes all IRQ management interfaces and guarantees that different
+ * callers cannot interfere.
+ *
+ * So the way this works is as follows:
+ *
+ * ==> Start IRQ management action
+ * chip->bus_lock()			<== Mutex is taken
+ * raw_spin_lock_irq(&irqdesc->lock)	<== Interrupts disabled on local core
+ * chip->(GIC IRQ management routine)	<== IRQ mgmt routine is executed. If
+ *					    the intended target core is on the
+ *					    the same core, then the work is
+ *					    done here. If the target core is on
+ *					    another cluster, then a global
+ *					    structure (gic_rpc_data) is filled
+ *					    in to pass along to a remote routine
+ *					    to execute, and no work is done yet.
+ * raw_spin_unlock_irq(&irqdesc->lock)	<== Interrupts are re-enabled
+ * chip->bus_unlock()			<== If the gic_rpc_data global was
+ *					    filled in, then the specified
+ *					    remote routine is executed via
+ *					    smp_call_function_single(). The
+ *					    mutex is then given. Note that
+ *					    here, IRQs are already re-enabled,
+ *					    so its safe to use the RPC here.
+ * <== End IRQ management action
+ *
+ * The gic_rpc_data global is filled in by the chip callback routines (e.g.,
+ * gic_mask_irq, gic_set_type, etc.). The bus lock/unlock routines are
+ * implemented as gic_irq_lock() and gic_irq_sync_unlock() respectively.
+ *
+ */
+
+/*
+ * Routines to acknowledge, disable and enable interrupts.
+ */
+
+static void _gic_mask_irq(struct irq_data *d, bool do_mask)
+{
+	u32 mask = 1 << (gic_irq(d) % 32);
+
+	raw_spin_lock(&irq_controller_lock);
+	if (do_mask)
+		writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_CLEAR
+					+ (gic_irq(d) / 32) * 4);
+	else
+		writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_SET
+					+ (gic_irq(d) / 32) * 4);
+	raw_spin_unlock(&irq_controller_lock);
+}
+
+/*
+ * Functions called by smp_call_function_single() must take the form:
+ *
+ * static void foo(void *)
+ *
+ */
+static void gic_mask_remote(void *info)
+{
+	struct irq_data *d = (struct irq_data *)info;
+	_gic_mask_irq(d, 1);
+}
+static void gic_unmask_remote(void *info)
+{
+	struct irq_data *d = (struct irq_data *)info;
+	_gic_mask_irq(d, 0);
+}
+
+static void gic_mask_unmask(struct irq_data *d, bool do_mask)
+{
+	u32 pcpu = cpu_logical_map(smp_processor_id());
+	u32 irqid = gic_irq(d);
+
+	BUG_ON(!irqs_disabled());
+
+	if (irqid >= MAX_GIC_INTERRUPTS)
+		return;
+
+	/* Don't mess with the AXM IPIs. */
+	if ((irqid >= IPI0_CPU0) && (irqid < MAX_AXM_IPI_NUM))
+		return;
+
+	/* Don't mess with the PMU IRQ either. */
+	if (irqid == IRQ_PMU)
+		return;
+
+	/* Deal with PPI interrupts directly. */
+	if ((irqid > 16) && (irqid < 32)) {
+		_gic_mask_irq(d, do_mask);
+		return;
+	}
+
+	/*
+	 * If the cpu that this interrupt is assigned to falls within
+	 * the same cluster as the cpu we're currently running on, do
+	 * the IRQ [un]masking directly. Otherwise, use the RPC mechanism
+	 * to remotely do the masking.
+	 */
+	if ((irq_cpuid[irqid] / CORES_PER_CLUSTER) ==
+		(pcpu / CORES_PER_CLUSTER)) {
+		_gic_mask_irq(d, do_mask);
+	} else {
+		if (do_mask)
+			gic_rpc_data.func_mask |= IRQ_MASK;
+		else
+			gic_rpc_data.func_mask |= IRQ_UNMASK;
+		gic_rpc_data.cpu = irq_cpuid[irqid];
+		gic_rpc_data.d = d;
+	}
+}
+
+static void gic_mask_irq(struct irq_data *d)
+{
+	gic_mask_unmask(d, true);
+}
+
+static void gic_unmask_irq(struct irq_data *d)
+{
+	gic_mask_unmask(d, false);
+}
+
+static void gic_eoi_irq(struct irq_data *d)
+{
+	/*
+	 * This always runs on the same cpu that is handling
+	 * an IRQ, so no need to worry about running this on
+	 * remote clusters.
+	 */
+	writel_relaxed(gic_irq(d), gic_cpu_base(d) + GIC_CPU_EOI);
+}
+
+static int _gic_set_type(struct irq_data *d, unsigned int type)
+{
+	void __iomem *base = gic_dist_base(d);
+	unsigned int gicirq = gic_irq(d);
+	u32 enablemask = 1 << (gicirq % 32);
+	u32 enableoff = (gicirq / 32) * 4;
+	u32 confmask = 0x2 << ((gicirq % 16) * 2);
+	u32 confoff = (gicirq / 16) * 4;
+	bool enabled = false;
+	u32 val;
+
+	raw_spin_lock(&irq_controller_lock);
+
+	val = readl_relaxed(base + GIC_DIST_CONFIG + confoff);
+	if (type == IRQ_TYPE_LEVEL_HIGH)
+		val &= ~confmask;
+	else if (type == IRQ_TYPE_EDGE_RISING)
+		val |= confmask;
+
+	/*
+	 * As recommended by the ARM GIC architecture spec, disable the
+	 * interrupt before changing the configuration. We cannot rely
+	 * on IRQCHIP_SET_TYPE_MASKED behavior for this.
+	 */
+	if (readl_relaxed(base + GIC_DIST_ENABLE_SET + enableoff)
+			  & enablemask) {
+		writel_relaxed(enablemask,
+			       base + GIC_DIST_ENABLE_CLEAR + enableoff);
+		enabled = true;
+	}
+
+	writel_relaxed(val, base + GIC_DIST_CONFIG + confoff);
+
+	if (enabled)
+		writel_relaxed(enablemask,
+			       base + GIC_DIST_ENABLE_SET + enableoff);
+
+	raw_spin_unlock(&irq_controller_lock);
+
+	return IRQ_SET_MASK_OK;
+}
+
+/*
+ * Functions called by smp_call_function_single() must take the form:
+ *
+ * static void foo(void *)
+ *
+ */
+static void gic_set_type_remote(void *info)
+{
+	struct gic_rpc_data *rpc = (struct gic_rpc_data *)info;
+	_gic_set_type(rpc->d, rpc->type);
+}
+
+static int gic_set_type(struct irq_data *d, unsigned int type)
+{
+	unsigned int gicirq = gic_irq(d);
+
+	BUG_ON(!irqs_disabled());
+
+	/* Interrupt configuration for SGIs can't be changed. */
+	if (gicirq < 16)
+		return -EINVAL;
+
+	/* Interrupt configuration for the AXM IPIs can't be changed. */
+	if ((gicirq >= IPI0_CPU0) && (gicirq < MAX_AXM_IPI_NUM))
+		return -EINVAL;
+
+	/* We only support two interrupt trigger types. */
+	if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING)
+		return -EINVAL;
+
+	/*
+	 * Duplicate IRQ type settings across all clusters. Run
+	 * directly for this cluster, use RPC for all others.
+	 */
+	_gic_set_type(d, type);
+
+	gic_rpc_data.d = d;
+	gic_rpc_data.func_mask |= SET_TYPE;
+	gic_rpc_data.cpu = cpu_logical_map(smp_processor_id());
+	gic_rpc_data.type = type;
+
+	return IRQ_SET_MASK_OK;
+}
+
+static int gic_retrigger(struct irq_data *d)
+{
+	return -ENXIO;
+}
+
+static void gic_set_irq_target(void __iomem *dist_base,
+		u32 irqid, u32 cpu, bool set)
+{
+	void __iomem *reg;
+	unsigned int shift;
+	u32 val;
+	u32 mask = 0;
+	u32 bit;
+
+	reg =  dist_base + GIC_DIST_TARGET + (irqid & ~3);
+	shift = (irqid % 4) * 8;
+	mask = 0xff << shift;
+
+	val = readl_relaxed(reg) & ~mask;
+
+	if (!set)
+		/* Clear affinity, mask IRQ. */
+		writel_relaxed(val, reg);
+	else {
+		bit = 1 << ((cpu_logical_map(cpu) % CORES_PER_CLUSTER) + shift);
+		writel_relaxed(val | bit, reg);
+	}
+
+}
+
+static int _gic_clear_affinity(struct irq_data *d, u32 cpu, bool update_enable)
+{
+
+	u32 enable_mask, enable_offset;
+
+	raw_spin_lock(&irq_controller_lock);
+
+	gic_set_irq_target(gic_dist_base(d), gic_irq(d), cpu, false);
+
+	if (update_enable) {
+		enable_mask = 1 << (gic_irq(d) % 32);
+		enable_offset = 4 * (gic_irq(d) / 32);
+		writel_relaxed(enable_mask,
+				gic_data_dist_base(&gic_data) + GIC_DIST_ENABLE_CLEAR + enable_offset);
+	}
+
+	raw_spin_unlock(&irq_controller_lock);
+
+	return IRQ_SET_MASK_OK;
+
+}
+
+static int _gic_set_affinity(struct irq_data *d,
+			     u32 cpu,
+			     bool update_enable)
+{
+	u32 enable_mask, enable_offset;
+
+	raw_spin_lock(&irq_controller_lock);
+
+	gic_set_irq_target(gic_dist_base(d), gic_irq(d), cpu, true);
+
+	if (update_enable) {
+		enable_mask = 1 << (gic_irq(d) % 32);
+		enable_offset = 4 * (gic_irq(d) / 32);
+		writel_relaxed(enable_mask,
+				gic_data_dist_base(&gic_data) + GIC_DIST_ENABLE_SET + enable_offset);
+	}
+
+	raw_spin_unlock(&irq_controller_lock);
+
+	return IRQ_SET_MASK_OK;
+}
+
+/*
+ * Functions called by smp_call_function_single() must take the form:
+ *
+ * static void foo(void *)
+ *
+ */
+static void gic_set_affinity_remote(void *info)
+{
+	struct gic_rpc_data *rpc = (struct gic_rpc_data *)info;
+	_gic_set_affinity(rpc->d, rpc->cpu, rpc->update_enable);
+
+}
+static void gic_clr_affinity_remote(void *info)
+{
+	struct gic_rpc_data *rpc = (struct gic_rpc_data *)info;
+	_gic_clear_affinity(rpc->d, rpc->oldcpu, rpc->update_enable);
+
+}
+
+static int gic_set_affinity(struct irq_data *d,
+			    const struct cpumask *mask_val,
+			    bool force)
+{
+	u32 pcpu;
+	unsigned int irqid;
+	struct cpumask *affinity_mask;
+	u32 mask;
+	u32 oldcpu;
+	struct gic_rpc_data *gic_rpc_ptr;
+	int rval;
+	bool new_same_core = false;
+	bool old_same_core = false;
+	bool update_enable = false;
+	u32 clear_needed = 0;
+	u32  set_needed = 0;
+	u32 add_cpu;
+	u32 del_cpu;
+
+	BUG_ON(!irqs_disabled());
+
+
+	pcpu = cpu_logical_map(smp_processor_id());
+	irqid = gic_irq(d);
+	affinity_mask = (struct cpumask *)mask_val;
+	oldcpu = irq_cpuid[irqid];
+
+	if (irqid >= MAX_GIC_INTERRUPTS)
+		return -EINVAL;
+
+	/* Interrupt affinity for the AXM IPIs can't be changed. */
+	if ((irqid >= IPI0_CPU0) && (irqid < MAX_AXM_IPI_NUM))
+		return IRQ_SET_MASK_OK;
+
+	if (force)
+		add_cpu = cpumask_any(cpu_online_mask);
+	else
+		add_cpu = cpumask_any_and(affinity_mask, cpu_online_mask);
+
+	if (add_cpu >= nr_cpu_ids) {
+		pr_err("ERROR: no cpus left\n");
+		return -EINVAL;
+	}
+
+	del_cpu = oldcpu;
+
+	if (add_cpu == del_cpu)
+		return IRQ_SET_MASK_OK;
+
+	new_same_core =
+			((add_cpu / CORES_PER_CLUSTER) == (pcpu / CORES_PER_CLUSTER)) ?
+					true : false;
+	old_same_core =
+			((del_cpu / CORES_PER_CLUSTER) == (pcpu / CORES_PER_CLUSTER)) ?
+					true : false;
+
+	update_enable = ((add_cpu / CORES_PER_CLUSTER) == (del_cpu / CORES_PER_CLUSTER)) ? false : true;
+
+	if (new_same_core) {
+
+		if (old_same_core) {
+			clear_needed = AFFINITY_CLEAR_LOCAL;
+			set_needed = AFFINITY_SET_LOCAL;
+		} else {
+			set_needed = AFFINITY_SET_LOCAL;
+			clear_needed = AFFINITY_CLEAR_OTHER_CLUSTER;
+		}
+
+	} else {
+
+		if (old_same_core) {
+			set_needed = AFFINITY_SET_OTHER_CLUSTER;
+			clear_needed = AFFINITY_CLEAR_LOCAL;
+		} else {
+			set_needed = AFFINITY_SET_OTHER_CLUSTER;
+			clear_needed = AFFINITY_CLEAR_OTHER_CLUSTER;
+		}
+	}
+
+
+	/*
+	 * We clear first to make sure the affinity mask always has a bit set,
+	 * especially when the two cpus are in the same cluster.
+	 */
+	if (irqid != IRQ_PMU) {
+		if (clear_needed == AFFINITY_CLEAR_LOCAL) {
+
+			_gic_clear_affinity(d, del_cpu, update_enable);
+
+		} else if (clear_needed == AFFINITY_CLEAR_OTHER_CLUSTER) {
+
+			mask = 0xf << ((oldcpu / CORES_PER_CLUSTER) * 4);
+			del_cpu = cpumask_any_and((struct cpumask *)&mask,
+					cpu_online_mask);
+
+			if (del_cpu < nr_cpu_ids) {
+
+				gic_rpc_ptr = kmalloc(sizeof(struct gic_rpc_data), GFP_KERNEL);
+				if (!gic_rpc_ptr) {
+					pr_err(
+							"ERROR: failed to get memory for workqueue to set affinity false\n");
+					mutex_unlock(&affinity_lock);
+					return -EFAULT;
+				}
+
+				gic_rpc_ptr->func_mask = CLR_AFFINITY;
+				gic_rpc_ptr->cpu = del_cpu;
+				gic_rpc_ptr->oldcpu = oldcpu;
+				gic_rpc_ptr->d = d;
+				gic_rpc_ptr->update_enable = update_enable;
+				get_cpu();
+				rval = axxia_put_item(&axxia_circ_q, (void *) gic_rpc_ptr);
+				put_cpu();
+				if (rval) {
+					pr_err(
+							"ERROR: failed to add CLR_AFFINITY request for cpu: %d\n",
+							del_cpu);
+					kfree((void *) gic_rpc_ptr);
+					mutex_unlock(&affinity_lock);
+					return rval;
+				}
+				schedule_work_on(0, &axxia_gic_affinity_work);
+			} else
+				pr_err("ERROR: no CPUs left\n");
+		}
+	}
+
+	if (set_needed == AFFINITY_SET_LOCAL) {
+
+		_gic_set_affinity(d, add_cpu, update_enable);
+
+	} else if (set_needed == AFFINITY_SET_OTHER_CLUSTER) {
+
+		gic_rpc_ptr = kmalloc(sizeof(struct gic_rpc_data), GFP_KERNEL);
+		if (!gic_rpc_ptr) {
+			pr_err(
+					"ERROR: failed to get memory for workqueue to set affinity false\n");
+			mutex_unlock(&affinity_lock);
+			return -EFAULT;
+		}
+
+		gic_rpc_ptr->func_mask = SET_AFFINITY;
+		gic_rpc_ptr->cpu = add_cpu;
+		gic_rpc_ptr->update_enable = update_enable;
+		gic_rpc_ptr->d = d;
+		get_cpu();
+		rval = axxia_put_item(&axxia_circ_q, (void *) gic_rpc_ptr);
+		put_cpu();
+		if (rval) {
+			pr_err("ERROR: failed to add SET_AFFINITY request for cpu: %d\n",
+					add_cpu);
+			kfree((void *) gic_rpc_ptr);
+			mutex_unlock(&affinity_lock);
+			return rval;
+		}
+		schedule_work_on(0, &axxia_gic_affinity_work);
+
+	}
+
+	/* Update Axxia IRQ affinity table with the new physical CPU number. */
+	irq_cpuid[irqid] = cpu_logical_map(add_cpu);
+
+	return IRQ_SET_MASK_OK;
+}
+
+#ifdef CONFIG_PM
+static int gic_set_wake(struct irq_data *d, unsigned int on)
+{
+	int ret = -ENXIO;
+
+	return ret;
+}
+
+#else
+#define gic_set_wake	NULL
+#endif
+
+static u32 get_cluster_id(void)
+{
+	u32 mpidr, cluster;
+
+	mpidr = read_cpuid_mpidr();
+	cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
+
+	/*
+	 * Cluster ID should always be between 0 and 3.
+	 * Anything else, return 0.
+	 */
+	if (cluster >= MAX_NUM_CLUSTERS)
+		cluster = 0;
+
+	return cluster;
+}
+
+#ifdef CONFIG_CPU_PM
+
+/*
+ * Saves the GIC distributor registers during suspend or idle.  Must be called
+ * with interrupts disabled but before powering down the GIC.  After calling
+ * this function, no interrupts will be delivered by the GIC, and another
+ * platform-specific wakeup source must be enabled.
+ */
+static void gic_dist_save(void)
+{
+	unsigned int gic_irqs;
+	void __iomem *dist_base;
+	int i;
+	u32 this_cluster;
+
+	this_cluster = get_cluster_id();
+
+	gic_irqs = gic_data.gic_irqs;
+	dist_base = gic_data_dist_base(&gic_data);
+
+	if (!dist_base)
+		return;
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++)
+		gic_data.saved_spi_conf[i][this_cluster] =
+			readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+		gic_data.saved_spi_target[i][this_cluster] =
+			readl_relaxed(dist_base + GIC_DIST_TARGET + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
+		gic_data.saved_spi_enable[i][this_cluster] =
+			readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
+}
+
+/*
+ * Restores the GIC distributor registers during resume or when coming out of
+ * idle.  Must be called before enabling interrupts.  If a level interrupt
+ * that occured while the GIC was suspended is still present, it will be
+ * handled normally, but any edge interrupts that occured will not be seen by
+ * the GIC and need to be handled by the platform-specific wakeup source.
+ */
+static void gic_dist_restore(void)
+{
+	unsigned int gic_irqs;
+	unsigned int i;
+	void __iomem *dist_base;
+	u32 this_cluster;
+
+	this_cluster = get_cluster_id();
+
+	gic_irqs = gic_data.gic_irqs;
+	dist_base = gic_data_dist_base(&gic_data);
+
+	if (!dist_base)
+		return;
+
+	writel_relaxed(0, dist_base + GIC_DIST_CTRL);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++)
+		writel_relaxed(gic_data.saved_spi_conf[i][this_cluster],
+			dist_base + GIC_DIST_CONFIG + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+		writel_relaxed(0xa0a0a0a0,
+			dist_base + GIC_DIST_PRI + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+		writel_relaxed(gic_data.saved_spi_target[i][this_cluster],
+			dist_base + GIC_DIST_TARGET + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
+		writel_relaxed(gic_data.saved_spi_enable[i][this_cluster],
+			dist_base + GIC_DIST_ENABLE_SET + i * 4);
+
+	writel_relaxed(1, dist_base + GIC_DIST_CTRL);
+}
+
+static void gic_cpu_save(void)
+{
+	int i;
+	u32 *ptr;
+	void __iomem *dist_base;
+	void __iomem *cpu_base;
+	u32 this_cluster;
+
+	this_cluster = get_cluster_id();
+
+	dist_base = gic_data_dist_base(&gic_data);
+	cpu_base = gic_data_cpu_base(&gic_data);
+
+	if (!dist_base || !cpu_base)
+		return;
+
+	ptr = __this_cpu_ptr(gic_data.saved_ppi_enable[this_cluster]);
+	for (i = 0; i < DIV_ROUND_UP(32, 32); i++)
+		ptr[i] = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
+
+	ptr = __this_cpu_ptr(gic_data.saved_ppi_conf[this_cluster]);
+	for (i = 0; i < DIV_ROUND_UP(32, 16); i++)
+		ptr[i] = readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4);
+
+}
+
+static void gic_cpu_restore(void)
+{
+	int i;
+	u32 *ptr;
+	void __iomem *dist_base;
+	void __iomem *cpu_base;
+	u32 this_cluster;
+
+	this_cluster = get_cluster_id();
+
+	dist_base = gic_data_dist_base(&gic_data);
+	cpu_base = gic_data_cpu_base(&gic_data);
+
+	if (!dist_base || !cpu_base)
+		return;
+
+	ptr = __this_cpu_ptr(gic_data.saved_ppi_enable[this_cluster]);
+	for (i = 0; i < DIV_ROUND_UP(32, 32); i++)
+		writel_relaxed(ptr[i], dist_base + GIC_DIST_ENABLE_SET + i * 4);
+
+	ptr = __this_cpu_ptr(gic_data.saved_ppi_conf[this_cluster]);
+	for (i = 0; i < DIV_ROUND_UP(32, 16); i++)
+		writel_relaxed(ptr[i], dist_base + GIC_DIST_CONFIG + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(32, 4); i++)
+		writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4);
+
+	writel_relaxed(0xf0, cpu_base + GIC_CPU_PRIMASK);
+	writel_relaxed(1, cpu_base + GIC_CPU_CTRL);
+}
+
+static int _gic_notifier(struct notifier_block *self,
+			 unsigned long cmd, void *v)
+{
+	switch (cmd) {
+	case CPU_PM_ENTER:
+		gic_cpu_save();
+		break;
+	case CPU_PM_ENTER_FAILED:
+	case CPU_PM_EXIT:
+		gic_cpu_restore();
+		break;
+	case CPU_CLUSTER_PM_ENTER:
+		gic_dist_save();
+		break;
+	case CPU_CLUSTER_PM_ENTER_FAILED:
+	case CPU_CLUSTER_PM_EXIT:
+		gic_dist_restore();
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+/* Mechanism for forwarding PM events to other clusters. */
+struct gic_notifier_wrapper_struct {
+	struct notifier_block *self;
+	unsigned long cmd;
+	void *v;
+};
+
+/*
+ * Functions called by smp_call_function_single() must take the form:
+ *
+ * static void foo(void *)
+ *
+ */
+static void gic_notifier_remote(void *info)
+{
+	struct gic_rpc_data *rpc = (struct gic_rpc_data *)info;
+
+	_gic_notifier(rpc->gn_data.self, rpc->gn_data.cmd, rpc->gn_data.v);
+}
+
+static int gic_notifier(struct notifier_block *self, unsigned long cmd,	void *v)
+{
+	/* Execute on this cluster. */
+	_gic_notifier(self, cmd, v);
+
+	/* Use RPC mechanism to execute this at other clusters. */
+	gic_rpc_data.func_mask |= GIC_NOTIFIER;
+	gic_rpc_data.cpu = cpu_logical_map(smp_processor_id());
+	gic_rpc_data.gn_data.self = self;
+	gic_rpc_data.gn_data.cmd = cmd;
+	gic_rpc_data.gn_data.v = v;
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block gic_notifier_block = {
+	.notifier_call = gic_notifier,
+};
+
+static void __init gic_pm_init(struct gic_chip_data *gic)
+{
+	int i;
+
+	for (i = 0; i < MAX_NUM_CLUSTERS; i++) {
+		gic->saved_ppi_enable[i] =
+			__alloc_percpu(DIV_ROUND_UP(32, 32) * 4, sizeof(u32));
+		BUG_ON(!gic->saved_ppi_enable[i]);
+
+		gic->saved_ppi_conf[i] =
+			__alloc_percpu(DIV_ROUND_UP(32, 16) * 4, sizeof(u32));
+		BUG_ON(!gic->saved_ppi_conf[i]);
+	}
+
+	if (gic == &gic_data)
+		cpu_pm_register_notifier(&gic_notifier_block);
+}
+#else
+static void __init gic_pm_init(struct gic_chip_data *gic)
+{
+}
+#endif /* CONFIG_CPU_PM */
+
+/*
+ * GIC bus lock/unlock routines.
+ */
+
+static void gic_irq_lock(struct irq_data *d)
+{
+	/* Take the bus lock. */
+	mutex_lock(&irq_bus_lock);
+}
+
+static void gic_irq_sync_unlock(struct irq_data *d)
+{
+	int i, j, cpu;
+	int nr_cluster_ids = ((nr_cpu_ids - 1) / CORES_PER_CLUSTER) + 1;
+
+
+	if (gic_rpc_data.func_mask & IRQ_MASK) {
+		smp_call_function_single(gic_rpc_data.cpu,
+					 gic_mask_remote,
+					 d, 1);
+	}
+
+	if (gic_rpc_data.func_mask & IRQ_UNMASK) {
+		smp_call_function_single(gic_rpc_data.cpu,
+					 gic_unmask_remote,
+					 d, 1);
+	}
+
+	if (gic_rpc_data.func_mask & SET_TYPE) {
+		for (i = 0; i < nr_cluster_ids; i++) {
+
+			/* No need to run on local cluster. */
+			if (i == (gic_rpc_data.cpu / CORES_PER_CLUSTER))
+				continue;
+
+			/*
+			 * Have some core in each cluster execute this,
+			 * Start with the first core on that cluster.
+			 */
+			cpu = i * CORES_PER_CLUSTER;
+			for (j = cpu; j < cpu + CORES_PER_CLUSTER; j++) {
+				if (cpu_online(j)) {
+					smp_call_function_single(j,
+							gic_set_type_remote,
+							&gic_rpc_data, 1);
+					break;
+				}
+			}
+		}
+	}
+
+#ifdef CONFIG_CPU_PM
+	if (gic_rpc_data.func_mask & GIC_NOTIFIER) {
+		for (i = 0; i < nr_cluster_ids; i++) {
+			/* No need to run on local cluster. */
+			if (i == (gic_rpc_data.cpu / CORES_PER_CLUSTER))
+				continue;
+
+			/*
+			 * Have some core in each cluster execute this,
+			 * Start with the first core on that cluster.
+			 */
+			cpu = i * CORES_PER_CLUSTER;
+			for (j = cpu; j < cpu + CORES_PER_CLUSTER; j++) {
+				if (cpu_online(j)) {
+					smp_call_function_single(j,
+							gic_notifier_remote,
+							&gic_rpc_data, 1);
+					break;
+				}
+			}
+		}
+	}
+#endif
+
+	/* Reset RPC data. */
+	gic_rpc_data.func_mask = 0;
+
+	/* Give the bus lock. */
+	mutex_unlock(&irq_bus_lock);
+
+}
+
+static
+asmlinkage void __exception_irq_entry axxia_gic_handle_irq(struct pt_regs *regs)
+{
+	u32 irqstat, irqnr;
+	struct gic_chip_data *gic = &gic_data;
+	void __iomem *cpu_base = gic_data_cpu_base(gic);
+	void __iomem *dist_base = gic_data_dist_base(gic);
+	u32 pcpu = cpu_logical_map(smp_processor_id());
+	u32 cluster = pcpu / CORES_PER_CLUSTER;
+	u32 next, mask;
+
+	do {
+		irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
+		irqnr = irqstat & ~0x1c00;
+
+		if (likely(irqnr > 15 && irqnr <= MAX_GIC_INTERRUPTS)) {
+			irqnr = irq_find_mapping(gic->domain, irqnr);
+
+			/*
+			 * Check if this is an external Axxia IPI interrupt.
+			 * Translate to a standard ARM internal IPI number.
+			 * The Axxia only has 4 IPI interrupts, so we
+			 * multiplex various ARM IPIs into a single line
+			 * as outlined below:
+			 *
+			 * IPI0_CPUx = IPI_TIMER (1)
+			 * IPI1_CPUx = IPI_RESCHEDULE (2)
+			 * IPI2_CPUx = IPI_CALL_FUNC (3) |
+			 *             IPI_CALL_FUNC_SINGLE (4) |
+			 *             IPI_CPU_STOP (5) |
+			 *             IPI_WAKEUP (0)
+			 * IPI3_CPUx = Not Used
+			 *
+			 * Note that if the ipi_msg_type enum changes in
+			 * arch/arm/kernel/smp.c then this will have to be
+			 * updated as well.
+			 */
+			switch (irqnr) {
+			case IPI0_CPU0:
+			case IPI0_CPU1:
+			case IPI0_CPU2:
+			case IPI0_CPU3:
+				writel_relaxed(irqnr, cpu_base + GIC_CPU_EOI);
+				handle_IPI(1, regs);
+				break;
+
+			case IPI1_CPU0:
+			case IPI1_CPU1:
+			case IPI1_CPU2:
+			case IPI1_CPU3:
+				writel_relaxed(irqnr, cpu_base + GIC_CPU_EOI);
+				handle_IPI(2, regs);
+				break;
+
+			case IPI2_CPU0:
+			case IPI2_CPU1:
+			case IPI2_CPU2:
+			case IPI2_CPU3:
+				writel_relaxed(irqnr, cpu_base + GIC_CPU_EOI);
+				axxia_ipi_demux(regs);
+				break;
+
+			case IPI3_CPU0:
+			case IPI3_CPU1:
+			case IPI3_CPU2:
+			case IPI3_CPU3:
+				/* Not currently used */
+				writel_relaxed(irqnr, cpu_base + GIC_CPU_EOI);
+				break;
+
+			case IRQ_PMU:
+				/*
+				 * The PMU IRQ line is OR'ed among all cores
+				 * within a cluster, so no way to tell which
+				 * core actually generated the interrupt.
+				 * Therefore, rotate PMU IRQ affinity to allow
+				 * perf to work accurately as possible. Skip
+				 * over offline cpus.
+				 */
+				do {
+					next = (++pcpu % CORES_PER_CLUSTER) +
+						(cluster * CORES_PER_CLUSTER);
+				} while (!cpu_online(next));
+
+				mask = 0x01 << (next % CORES_PER_CLUSTER);
+				raw_spin_lock(&irq_controller_lock);
+				writeb_relaxed(mask, dist_base +
+						GIC_DIST_TARGET + IRQ_PMU);
+				raw_spin_unlock(&irq_controller_lock);
+				/* Fall through ... */
+
+			default:
+				/* External interrupt */
+				handle_IRQ(irqnr, regs);
+				break;
+			}
+			continue;
+		}
+		if (irqnr < 16) {
+			writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
+			handle_IPI(irqnr, regs);
+			continue;
+		}
+		break;
+	} while (1);
+}
+
+static struct irq_chip gic_chip = {
+	.name			= "GIC",
+	.irq_bus_lock		= gic_irq_lock,
+	.irq_bus_sync_unlock	= gic_irq_sync_unlock,
+	.irq_mask		= gic_mask_irq,
+	.irq_unmask		= gic_unmask_irq,
+	.irq_eoi		= gic_eoi_irq,
+	.irq_set_type		= gic_set_type,
+	.irq_retrigger		= gic_retrigger,
+	.irq_set_affinity	= gic_set_affinity,
+	.irq_set_wake		= gic_set_wake,
+};
+
+static void __init gic_axxia_init(struct gic_chip_data *gic)
+{
+	int i;
+	u32 cpumask;
+
+	/*
+	 * Initialize the Axxia IRQ affinity table. All non-IPI
+	 * interrupts are initially assigned to physical cpu 0.
+	 */
+	for (i = 0; i < MAX_GIC_INTERRUPTS; i++)
+		irq_cpuid[i] = 0;
+
+	/* Unmask all Axxia IPI interrupts */
+	cpumask = 0;
+	for (i = 0; i < nr_cpu_ids; i++)
+		cpumask |= 1 << i;
+	for (i = 0; i < nr_cpu_ids; i++)
+		writel_relaxed(cpumask, ipi_mask_reg_base + 0x40 + i * 4);
+}
+
+static void  gic_dist_init(struct gic_chip_data *gic)
+{
+	unsigned int i;
+	unsigned int gic_irqs = gic->gic_irqs;
+	void __iomem *base = gic_data_dist_base(gic);
+	u32 cpu = cpu_logical_map(smp_processor_id());
+	u8 cpumask_8;
+	u32 confmask;
+	u32 confoff;
+	u32 enablemask;
+	u32 enableoff;
+	u32 val;
+	u32 this_cluster = get_cluster_id();
+	u32 powered_on;
+	u32 ccpu;
+
+	/* Initialize the distributor interface once per CPU cluster */
+	if ((test_and_set_bit(get_cluster_id(), &gic->dist_init_done)) && (!cluster_power_up[this_cluster]))
+		return;
+
+	writel_relaxed(0, base + GIC_DIST_CTRL);
+
+	/*################################# CONFIG IRQS ####################################*/
+
+	/*
+	 * Set all global interrupts to be level triggered, active low.
+	 */
+	for (i = 32; i < gic_irqs; i += 16)
+		writel_relaxed(0, base + GIC_DIST_CONFIG + i * 4 / 16);
+
+	/*
+	 * Set Axxia IPI interrupts to be edge triggered.
+	 */
+	for (i = IPI0_CPU0; i < MAX_AXM_IPI_NUM; i++) {
+		confmask = 0x2 << ((i % 16) * 2);
+		confoff = (i / 16) * 4;
+		val = readl_relaxed(base + GIC_DIST_CONFIG + confoff);
+		val |= confmask;
+		writel_relaxed(val, base + GIC_DIST_CONFIG + confoff);
+	}
+
+	/*################################# PRIORITY  ####################################*/
+	/*
+	 * Set priority on PPI and SGI interrupts
+	 */
+	for (i = 0; i < 32; i += 4)
+		writel_relaxed(0xa0a0a0a0,
+				base + GIC_DIST_PRI + i * 4 / 4);
+
+	/*
+	 * Set priority on all global interrupts.
+	 */
+	for (i = 32; i < gic_irqs; i += 4)
+		writel_relaxed(0xa0a0a0a0, base + GIC_DIST_PRI + i * 4 / 4);
+
+
+	/*################################# TARGET ####################################*/
+	/*
+	 * Set all global interrupts to this CPU only.
+	 * (Only do this for the first core on cluster 0).
+	 */
+	if (cpu == 0)
+		for (i = 32; i < gic_irqs; i += 4)
+			writel_relaxed(0x01010101, base + GIC_DIST_TARGET + i * 4 / 4);
+
+	/*
+	 * Set Axxia IPI interrupts for all CPUs in this cluster.
+	 */
+	powered_on = (~pm_cpu_powered_down) & 0xFFFF;
+	for (i = IPI0_CPU0; i < MAX_AXM_IPI_NUM; i++) {
+		cpumask_8 = 1 << ((i - IPI0_CPU0) % 4);
+		ccpu = (this_cluster * 4) + ((i - IPI0_CPU0) % CORES_PER_CLUSTER);
+		if ((1 << ccpu) & powered_on)
+			writeb_relaxed(cpumask_8, base + GIC_DIST_TARGET + i);
+		else
+			writeb_relaxed(0x00, base + GIC_DIST_TARGET + i);
+	}
+
+	/*################################# ENABLE IRQS ####################################*/
+	/*
+	 * Do the initial enable of the Axxia IPI interrupts here.
+	 * NOTE: Writing a 0 to this register has no effect, so
+	 * no need to read and OR in bits, just writing is OK.
+	 */
+
+	powered_on = (~pm_cpu_powered_down) & 0xFFFF;
+	for (i = IPI0_CPU0; i < MAX_AXM_IPI_NUM; i++) {
+		enablemask = 1 << (i % 32);
+		enableoff = (i / 32) * 4;
+		ccpu = (this_cluster * 4) + ((i - IPI0_CPU0) % CORES_PER_CLUSTER);
+		if ((1 << ccpu) & powered_on)
+			writel_relaxed(enablemask, base + GIC_DIST_ENABLE_SET + enableoff);
+	}
+
+	/*
+	 * Do the initial enable of the PMU IRQ here.
+	 */
+	enablemask = 1 << (IRQ_PMU % 32);
+	enableoff = (IRQ_PMU / 32) * 4;
+	writel_relaxed(enablemask, base + GIC_DIST_ENABLE_SET + enableoff);
+
+
+	writel_relaxed(1, base + GIC_DIST_CTRL);
+
+}
+
+static void  gic_cpu_init(struct gic_chip_data *gic)
+{
+	void __iomem *dist_base = gic_data_dist_base(gic);
+	void __iomem *base = gic_data_cpu_base(gic);
+	int i;
+	u32 enablemask;
+	u32 enableoff;
+	u32 ccpu;
+	u32 cpu = smp_processor_id();
+	u32 cluster = cpu / CORES_PER_CLUSTER;
+	u32 cpumask_8;
+
+	/*
+	 * Deal with the banked PPI and SGI interrupts - disable all
+	 * PPI interrupts, and also all SGI interrupts (we don't use
+	 * SGIs in the Axxia).
+	 */
+	writel_relaxed(0xffffffff, dist_base + GIC_DIST_ENABLE_CLEAR);
+
+	if (!cluster_power_up[cluster]) {
+		writel_relaxed(0, dist_base + GIC_DIST_CTRL);
+		for (i = IPI0_CPU0; i < MAX_AXM_IPI_NUM; i++) {
+			cpumask_8 = 1 << ((i - IPI0_CPU0) % 4);
+			enablemask = 1 << (i % 32);
+			enableoff = (i / 32) * 4;
+			ccpu = (cluster * 4) + ((i - IPI0_CPU0) % CORES_PER_CLUSTER);
+			if (ccpu == cpu) {
+				writeb_relaxed(cpumask_8, dist_base + GIC_DIST_TARGET + i);
+				writel_relaxed(enablemask, dist_base + GIC_DIST_ENABLE_SET + enableoff);
+			}
+		}
+		writel_relaxed(1, dist_base + GIC_DIST_CTRL);
+	}
+
+	writel_relaxed(0xf0, base + GIC_CPU_PRIMASK);
+
+	writel_relaxed(1, base + GIC_CPU_CTRL);
+}
+
+void axxia_gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
+{
+	int cpu;
+	unsigned long map = 0;
+	unsigned int regoffset;
+	u32 phys_cpu = cpu_logical_map(smp_processor_id());
+
+	/* Sanity check the physical cpu number */
+	if (phys_cpu >= nr_cpu_ids) {
+		pr_err("Invalid cpu num (%d) >= max (%d)\n",
+			phys_cpu, nr_cpu_ids);
+		return;
+	}
+
+	/* Convert our logical CPU mask into a physical one. */
+	for_each_cpu(cpu, mask)
+		map |= 1 << cpu_logical_map(cpu);
+
+	/*
+	 * Convert the standard ARM IPI number (as defined in
+	 * arch/arm/kernel/smp.c) to an Axxia IPI interrupt.
+	 * The Axxia sends IPI interrupts to other cores via
+	 * the use of "IPI send" registers. Each register is
+	 * specific to a sending CPU and IPI number. For example:
+	 * regoffset 0x0 = CPU0 uses to send IPI0 to other CPUs
+	 * regoffset 0x4 = CPU0 uses to send IPI1 to other CPUs
+	 * ...
+	 * regoffset 0x1000 = CPU1 uses to send IPI0 to other CPUs
+	 * regoffset 0x1004 = CPU1 uses to send IPI1 to other CPUs
+	 * ...
+	 */
+
+	if (phys_cpu < 8)
+		regoffset = phys_cpu * 0x1000;
+	else
+		regoffset = (phys_cpu - 8) * 0x1000 + 0x10000;
+
+	switch (irq) {
+	case 0: /* IPI_WAKEUP */
+		regoffset += 0x8; /* Axxia IPI2 */
+		muxed_ipi_message_pass(mask, MUX_MSG_CPU_WAKEUP);
+		break;
+
+	case 1: /* IPI_TIMER */
+		regoffset += 0x0; /* Axxia IPI0 */
+		break;
+
+	case 2: /* IPI_RESCHEDULE */
+		regoffset += 0x4; /* Axxia IPI1 */
+		break;
+
+	case 3: /* IPI_CALL_FUNC */
+		regoffset += 0x8; /* Axxia IPI2 */
+		muxed_ipi_message_pass(mask, MUX_MSG_CALL_FUNC);
+		break;
+
+	case 4: /* IPI_CALL_FUNC_SINGLE */
+		regoffset += 0x8; /* Axxia IPI2 */
+		muxed_ipi_message_pass(mask, MUX_MSG_CALL_FUNC_SINGLE);
+		break;
+
+	case 5: /* IPI_CPU_STOP */
+		regoffset += 0x8; /* Axxia IPI2 */
+		muxed_ipi_message_pass(mask, MUX_MSG_CPU_STOP);
+		break;
+
+	case 6: /* IPI_IRQ_WORK */
+		regoffset += 0x8; /* Axxia IPI2 */
+		muxed_ipi_message_pass(mask, MUX_MSG_IRQ_WORK);
+		break;
+
+	case 7: /* IPI_COMPLETE */
+		regoffset += 0x8; /* Axxia IPI2 */
+		muxed_ipi_message_pass(mask, MUX_MSG_COMPLETION);
+		break;
+
+	default:
+		/* Unknown ARM IPI */
+		pr_err("Unknown ARM IPI num (%d)!\n", irq);
+		return;
+	}
+
+	/*
+	 * Ensure that stores to Normal memory are visible to the
+	 * other CPUs before issuing the IPI.
+	 */
+	dsb();
+
+	/* Axxia chip uses external SPI interrupts for IPI functionality. */
+	writel_relaxed(map, ipi_send_reg_base + regoffset);
+}
+
+static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
+				irq_hw_number_t hw)
+{
+	if (hw < 32) {
+		irq_set_percpu_devid(irq);
+		irq_set_chip_and_handler(irq, &gic_chip,
+					 handle_percpu_devid_irq);
+		set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
+	} else {
+		irq_set_chip_and_handler(irq, &gic_chip,
+					 handle_fasteoi_irq);
+		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+	}
+	irq_set_chip_data(irq, d->host_data);
+	return 0;
+}
+
+static int gic_irq_domain_xlate(struct irq_domain *d,
+				struct device_node *controller,
+				const u32 *intspec,
+				unsigned int intsize,
+				unsigned long *out_hwirq,
+				unsigned int *out_type)
+{
+	if (d->of_node != controller)
+		return -EINVAL;
+	if (intsize < 3)
+		return -EINVAL;
+
+	/* Get the interrupt number and add 16 to skip over SGIs */
+	*out_hwirq = intspec[1] + 16;
+
+	/* For SPIs, we need to add 16 more to get the GIC irq ID number */
+	if (!intspec[0])
+		*out_hwirq += 16;
+
+	*out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
+	return 0;
+}
+
+const struct irq_domain_ops gic_irq_domain_ops = {
+	.map = gic_irq_domain_map,
+	.xlate = gic_irq_domain_xlate,
+};
+
+void __init axxia_gic_init_bases(int irq_start,
+				 void __iomem *dist_base,
+				 void __iomem *cpu_base,
+				 struct device_node *node)
+{
+	irq_hw_number_t hwirq_base;
+	struct gic_chip_data *gic;
+	int gic_irqs, irq_base;
+
+	gic = &gic_data;
+
+	/* Normal, sane GIC... */
+	gic->dist_base.common_base = dist_base;
+	gic->cpu_base.common_base = cpu_base;
+	gic_set_base_accessor(gic, gic_get_common_base);
+
+	/*
+	 * For primary GICs, skip over SGIs.
+	 * For secondary GICs, skip over PPIs, too.
+	 */
+	if ((irq_start & 31) > 0) {
+		hwirq_base = 16;
+		if (irq_start != -1)
+			irq_start = (irq_start & ~31) + 16;
+	} else {
+		hwirq_base = 32;
+	}
+
+	/*
+	 * Find out how many interrupts are supported.
+	 * The GIC only supports up to 1020 interrupt sources.
+	 */
+	gic_irqs = readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_CTR) & 0x1f;
+	gic_irqs = (gic_irqs + 1) * 32;
+	if (gic_irqs > MAX_GIC_INTERRUPTS)
+		gic_irqs = MAX_GIC_INTERRUPTS;
+	gic->gic_irqs = gic_irqs;
+
+	gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */
+	irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, numa_node_id());
+	if (IS_ERR_VALUE(irq_base)) {
+		WARN(1,
+		 "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
+		     irq_start);
+		irq_base = irq_start;
+	}
+	gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base,
+				    hwirq_base, &gic_irq_domain_ops, gic);
+	if (WARN_ON(!gic->domain))
+		return;
+
+	set_smp_cross_call(axxia_gic_raise_softirq);
+	set_handle_irq(axxia_gic_handle_irq);
+
+	gic_axxia_init(gic);
+	gic_dist_init(gic);
+	gic_cpu_init(gic);
+	gic_pm_init(gic);
+
+	axxia_initialize_queue(&axxia_circ_q);
+
+}
+
+void  axxia_gic_secondary_init(void)
+{
+	struct gic_chip_data *gic = &gic_data;
+
+	gic_dist_init(gic);
+	gic_cpu_init(&gic_data);
+}
+
+#ifdef CONFIG_OF
+
+int __init axxia_gic_of_init(struct device_node *node,
+			     struct device_node *parent)
+{
+	void __iomem *cpu_base;
+	void __iomem *dist_base;
+
+	if (WARN_ON(!node))
+		return -ENODEV;
+
+	dist_base = of_iomap(node, 0);
+	WARN(!dist_base, "unable to map gic dist registers\n");
+
+	cpu_base = of_iomap(node, 1);
+	WARN(!cpu_base, "unable to map gic cpu registers\n");
+
+	ipi_mask_reg_base = of_iomap(node, 4);
+	WARN(!ipi_mask_reg_base, "unable to map Axxia IPI mask registers\n");
+
+	ipi_send_reg_base = of_iomap(node, 5);
+	WARN(!ipi_send_reg_base, "unable to map Axxia IPI send registers\n");
+
+	axxia_gic_init_bases(-1, dist_base, cpu_base, node);
+
+
+
+	return 0;
+}
+#endif
diff --git a/arch/arm/mach-axxia/axxia.c b/arch/arm/mach-axxia/axxia.c
new file mode 100644
index 0000000..59dd9f8
--- /dev/null
+++ b/arch/arm/mach-axxia/axxia.c
@@ -0,0 +1,252 @@
+/*
+ * arch/arm/mach-axxia/axxia.c
+ *
+ * Support for the LSI Axxia boards based on ARM cores.
+ *
+ * Copyright (C) 2012 LSI
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/amba/bus.h>
+#include <linux/amba/mmci.h>
+#include <linux/amba/pl022.h>
+#include <linux/amba/pl061.h>
+#include <linux/device.h>
+#include <linux/of_address.h>
+#include <linux/of_fdt.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/smsc911x.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#ifdef CONFIG_ARM_ARCH_TIMER
+#include <asm/arch_timer.h>
+#endif
+#include <asm/sizes.h>
+#include <asm/pmu.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/kexec.h>
+#include <asm/mach/time.h>
+#include <asm/hardware/cache-l2x0.h>
+#include <mach/hardware.h>
+#include <mach/timers.h>
+#include <mach/axxia-gic.h>
+#include <linux/irqchip/arm-gic.h>
+#include <mach/ncr.h>
+#include "axxia.h"
+#include "pci.h"
+#ifdef CONFIG_AXXIA_RIO
+#include <mach/rio.h>
+#endif
+
+static const char *axxia_dt_match[] __initconst = {
+	"lsi,axm5500",
+	NULL
+};
+
+static void __iomem *base;
+
+#ifdef CONFIG_KEXEC
+
+static void __iomem *dickens;
+
+static void set_l3_pstate(u32 newstate)
+{
+	static const u8 hnf[] = {
+		0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27
+	};
+	int i;
+	u32 status;
+
+	for (i = 0; i < ARRAY_SIZE(hnf); ++i)
+		writel(newstate, dickens + (hnf[i] << 16) + 0x10);
+
+	for (i = 0; i < ARRAY_SIZE(hnf); ++i) {
+		int retry;
+		for (retry = 10000; retry > 0; --retry) {
+			status = readl(dickens + (hnf[i] << 16) + 0x18);
+			if (((status >> 2) & 3) == newstate)
+				break;
+			udelay(1);
+		}
+		BUG_ON(retry == 0);
+	}
+}
+
+static void
+flush_l3(void)
+{
+	/* Shutdown to flush */
+	set_l3_pstate(0);
+	/* ...and then back up again */
+	set_l3_pstate(3);
+}
+
+#endif
+
+static struct map_desc axxia_static_mappings[] __initdata = {
+#ifdef CONFIG_DEBUG_LL
+	{
+		.virtual	=  AXXIA_DEBUG_UART_VIRT,
+		.pfn		= __phys_to_pfn(AXXIA_DEBUG_UART_PHYS),
+		.length		= SZ_4K,
+		.type		= MT_DEVICE
+	},
+#endif
+};
+
+void __init axxia_dt_map_io(void)
+{
+	iotable_init(axxia_static_mappings, ARRAY_SIZE(axxia_static_mappings));
+}
+
+void __init axxia_dt_init_early(void)
+{
+	 init_dma_coherent_pool_size(SZ_1M);
+}
+
+static struct of_device_id axxia_irq_match[] __initdata = {
+	{
+		.compatible = "arm,cortex-a15-gic",
+		.data = axxia_gic_of_init,
+	},
+	{ }
+};
+
+static void __init axxia_dt_init_irq(void)
+{
+	of_irq_init(axxia_irq_match);
+}
+
+void __init axxia_dt_timer_init(void)
+{
+	int is_sim;
+
+	is_sim = of_find_compatible_node(NULL, NULL, "lsi,axm5500-sim") != NULL;
+
+	axxia_init_clocks(is_sim);
+
+	of_clk_init(NULL);
+	clocksource_of_init();
+}
+
+static struct mmci_platform_data mmc_plat_data = {
+	.ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34,
+	.status	  = NULL,
+	.gpio_wp  = -ENOSYS,
+	.gpio_cd  = -ENOSYS
+};
+
+static struct of_dev_auxdata axxia_auxdata_lookup[] __initdata = {
+	OF_DEV_AUXDATA("arm,primecell", 0x20101E0000ULL,
+		       "mmci",  &mmc_plat_data),
+	{}
+};
+
+static struct resource axxia_pmu_resources[] = {
+	[0] = {
+		.start  = IRQ_PMU,
+		.end    = IRQ_PMU,
+		.flags  = IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device pmu_device = {
+	.name			= "arm-pmu",
+	.id                     = -1,
+	.num_resources		= ARRAY_SIZE(axxia_pmu_resources),
+	.resource		= axxia_pmu_resources,
+};
+
+static int
+axxia_bus_notifier(struct notifier_block *nb, unsigned long event, void *obj)
+{
+	struct device *dev = obj;
+
+	if (event != BUS_NOTIFY_ADD_DEVICE)
+		return NOTIFY_DONE;
+
+	if (!of_property_read_bool(dev->of_node, "dma-coherent"))
+		return NOTIFY_DONE;
+
+	set_dma_ops(dev, &arm_coherent_dma_ops);
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block axxia_platform_nb = {
+	.notifier_call = axxia_bus_notifier,
+};
+
+static struct notifier_block axxia_amba_nb = {
+	.notifier_call = axxia_bus_notifier,
+};
+
+void __init axxia_dt_init(void)
+{
+	base = ioremap(0x2010000000, 0x40000);
+#ifdef CONFIG_KEXEC
+	if (!of_find_compatible_node(NULL, NULL, "lsi,axm5500-sim")) {
+		dickens = ioremap(0x2000000000, SZ_4M);
+		kexec_reinit = flush_l3;
+	}
+#endif
+
+	bus_register_notifier(&platform_bus_type, &axxia_platform_nb);
+	bus_register_notifier(&amba_bustype, &axxia_amba_nb);
+
+	of_platform_populate(NULL, of_default_bus_match_table,
+			     axxia_auxdata_lookup, NULL);
+	pm_power_off = NULL; /* TBD */
+
+	ncr_init();
+	axxia_ddr_retention_init();
+
+	axxia_pcie_init();
+
+#ifdef CONFIG_AXXIA_RIO
+	axxia_rapidio_init();
+#endif
+
+	platform_device_register(&pmu_device);
+}
+
+static void axxia_restart(enum reboot_mode reboot, const char *cmd)
+{
+	writel(0x000000ab, base + 0x31000); /* Access Key */
+	writel(0x00000040, base + 0x31004); /* Intrnl Boot, 0xffff0000 Target */
+	writel(0x80000000, base + 0x3180c); /* Set ResetReadDone */
+	writel(0x00080802, base + 0x31008); /* Chip Reset */
+}
+
+DT_MACHINE_START(AXXIA_DT, "LSI Axxia")
+	.dt_compat	= axxia_dt_match,
+	.smp		= smp_ops(axxia_smp_ops),
+	.map_io		= axxia_dt_map_io,
+	.init_early	= axxia_dt_init_early,
+	.init_irq	= axxia_dt_init_irq,
+	.init_time	= axxia_dt_timer_init,
+	.init_machine	= axxia_dt_init,
+	.restart	= axxia_restart,
+#if defined(CONFIG_ZONE_DMA) && defined(CONFIG_ARM_LPAE)
+	.dma_zone_size	= (4ULL * SZ_1G) - 1,
+#endif
+MACHINE_END
diff --git a/arch/arm/mach-axxia/axxia.h b/arch/arm/mach-axxia/axxia.h
new file mode 100644
index 0000000..000adc8
--- /dev/null
+++ b/arch/arm/mach-axxia/axxia.h
@@ -0,0 +1,10 @@
+#ifndef _AXXIA_H
+
+void axxia_init_clocks(int is_sim);
+void axxia_ddr_retention_init(void);
+void axxia_platform_cpu_die(unsigned int cpu);
+int axxia_platform_cpu_kill(unsigned int cpu);
+
+extern struct smp_operations axxia_smp_ops;
+
+#endif
diff --git a/arch/arm/mach-axxia/axxia_circular_queue.c b/arch/arm/mach-axxia/axxia_circular_queue.c
new file mode 100644
index 0000000..971aead
--- /dev/null
+++ b/arch/arm/mach-axxia/axxia_circular_queue.c
@@ -0,0 +1,63 @@
+/*
+ * axxia_circular_queue.c
+ *
+ *  Created on: Sep 30, 2014
+ *      Author: z8cpaul
+ */
+
+
+#include <asm/exception.h>
+#include "axxia_circular_queue.h"
+
+
+void axxia_initialize_queue(struct circular_queue_t *queue)
+{
+	int i;
+
+	queue->valid_items = 0;
+	queue->first = 0;
+	queue->last = 0;
+
+	for (i = 0; i < MAX_ITEMS; i++)
+		queue->data[i] = NULL;
+
+	return;
+}
+
+bool axxia_is_empty(struct circular_queue_t *queue)
+{
+
+	if (queue->valid_items == 0)
+		return true;
+	else
+		return false;
+}
+
+int axxia_put_item(struct circular_queue_t *queue, void *item_value)
+
+{
+	if (queue->valid_items >= MAX_ITEMS) {
+		pr_err("ERROR: queue is full\n");
+		return -EINVAL;
+	} else {
+		queue->valid_items++;
+		queue->data[queue->last] = item_value;
+		queue->last = (queue->last + 1) % MAX_ITEMS;
+	}
+	return 0;
+}
+
+
+int axxia_get_item(struct circular_queue_t *queue, void **item_value)
+{
+
+	if (axxia_is_empty(queue)) {
+		return -1;
+	} else {
+		*item_value = queue->data[queue->first];
+		queue->first = (queue->first + 1) % MAX_ITEMS;
+		queue->valid_items--;
+	}
+	return 0;
+
+}
diff --git a/arch/arm/mach-axxia/axxia_circular_queue.h b/arch/arm/mach-axxia/axxia_circular_queue.h
new file mode 100644
index 0000000..0fe88a0
--- /dev/null
+++ b/arch/arm/mach-axxia/axxia_circular_queue.h
@@ -0,0 +1,30 @@
+/*
+ * axxia_circular_queue.h
+ *
+ *  Created on: Sep 30, 2014
+ *      Author: z8cpaul
+ */
+
+#ifndef AXXIA_CIRCULAR_QUEUE_H_
+#define AXXIA_CIRCULAR_QUEUE_H_
+
+#define MAX_ITEMS    1020
+
+struct circular_queue_t
+
+{
+	int first;
+	int last;
+	int valid_items;
+	void *data[MAX_ITEMS];
+};
+
+void axxia_initialize_queue(struct circular_queue_t *queue);
+
+bool axxia_is_empty(struct circular_queue_t *queue);
+
+int axxia_put_item(struct circular_queue_t *queue, void *item_value);
+
+int axxia_get_item(struct circular_queue_t *queue, void **item_value);
+
+#endif
diff --git a/arch/arm/mach-axxia/clock.c b/arch/arm/mach-axxia/clock.c
new file mode 100644
index 0000000..6295795
--- /dev/null
+++ b/arch/arm/mach-axxia/clock.c
@@ -0,0 +1,109 @@
+/*
+ *  linux/arch/arm/mach-axxia/clock.c
+ *
+ *  Copyright (C) 2012 LSI
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/of_fdt.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+
+
+#define clk_register_clkdev(_clk, _conid, _devfmt, ...) \
+	do { \
+		struct clk_lookup *cl; \
+		cl = clkdev_alloc(_clk, _conid, _devfmt, ## __VA_ARGS__); \
+		clkdev_add(cl); \
+	} while (0)
+
+enum clk_ids {
+	clk_cpu,
+	clk_per,
+	clk_mmc,
+	clk_apb,
+	clk_1mhz,
+	NR_CLK_IDS
+};
+
+static struct dt_clk_lookup {
+	const char  *path;
+	const char  *name;
+	enum clk_ids id;
+	u32          default_freq;
+} dt_clks[] = {
+	{"/clocks/cpu",        "clk_cpu", clk_cpu, 1400000000 },
+	{"/clocks/peripheral", "clk_per", clk_per,  200000000 },
+	{"/clocks/emmc",       "clk_mmc", clk_mmc,  200000000 },
+};
+
+static struct clk *clk[NR_CLK_IDS];
+
+static void axxia_register_clks(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(dt_clks); ++i) {
+		struct dt_clk_lookup *c = &dt_clks[i];
+		struct device_node *np = of_find_node_by_path(c->path);
+		u32 freq;
+
+		if (!np || of_property_read_u32(np, "frequency", &freq)) {
+			pr_warn("axxia: No 'frequency' in %s\n", c->path);
+			freq = c->default_freq;
+		}
+		clk[c->id] = clk_register_fixed_rate(NULL, c->name, NULL,
+						     CLK_IS_ROOT, freq);
+	}
+
+	/* APB clock dummy */
+	clk[clk_apb] = clk_register_fixed_rate(NULL, "apb_pclk", NULL,
+					       CLK_IS_ROOT, 1000000);
+
+	clk[clk_1mhz] = clk_register_fixed_rate(NULL, "clk_1mhz", NULL,
+						CLK_IS_ROOT, 1000000);
+}
+
+void __init
+axxia_init_clocks(int is_sim)
+{
+	int i;
+
+	pr_info("axxia: init_clocks: is_sim=%d\n", is_sim);
+
+	axxia_register_clks();
+
+	/* PL011 UARTs */
+	clk_register_clkdev(clk[clk_per], NULL, "2010080000.uart");
+	clk_register_clkdev(clk[clk_per], NULL, "2010081000.uart");
+	clk_register_clkdev(clk[clk_per], NULL, "2010082000.uart");
+	clk_register_clkdev(clk[clk_per], NULL, "2010083000.uart");
+
+	/* PL022 SSP */
+	clk_register_clkdev(clk[clk_per], NULL, "2010088000.ssp");
+
+	/* I2C */
+	clk_register_clkdev(clk[clk_per], NULL, "2010084000.i2c");
+	clk_register_clkdev(clk[clk_per], NULL, "2010085000.i2c");
+	clk_register_clkdev(clk[clk_per], NULL, "2010086000.i2c");
+	clk_register_clkdev(clk[clk_per], NULL, "2010087000.i2c");
+
+	/* SP804 timers */
+	clk_register_clkdev(clk[is_sim ? clk_1mhz : clk_per], NULL, "sp804");
+	for (i = 0; i < 8; i++)
+		clk_register_clkdev(clk[is_sim ? clk_1mhz : clk_per],
+				    NULL, "axxia-timer%d", i);
+
+	/* PL180 MMCI */
+	clk_register_clkdev(clk[clk_mmc], NULL, "mmci");
+
+	clk_register_clkdev(clk[clk_apb], "apb_pclk", NULL);
+}
diff --git a/arch/arm/mach-axxia/ddr_retention.c b/arch/arm/mach-axxia/ddr_retention.c
new file mode 100644
index 0000000..58a4845
--- /dev/null
+++ b/arch/arm/mach-axxia/ddr_retention.c
@@ -0,0 +1,358 @@
+/*
+ *  Copyright (C) 2013 LSI Corporation
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/cpu.h>
+#include <linux/reboot.h>
+#include <linux/syscore_ops.h>
+#include <linux/proc_fs.h>
+#include <linux/prefetch.h>
+#include <linux/delay.h>
+
+#include <linux/of.h>
+#include <asm/io.h>
+#include <asm/cacheflush.h>
+#include <mach/ncr.h>
+
+static void __iomem *nca;
+static void __iomem *apb;
+static void __iomem *dickens;
+static int ddr_retention_enabled;
+
+enum {
+	AXXIA_ENGINE_CAAL,
+	AXXIA_ENGINE_CNAL
+};
+
+unsigned long
+ncp_caal_regions_acp55xx[] = {
+	NCP_REGION_ID(0x0b, 0x05),      /* SPPV2   */
+	NCP_REGION_ID(0x0c, 0x05),      /* SED     */
+	NCP_REGION_ID(0x0e, 0x05),      /* DPI_HFA */
+	NCP_REGION_ID(0x14, 0x05),      /* MTM     */
+	NCP_REGION_ID(0x14, 0x0a),      /* MTM2    */
+	NCP_REGION_ID(0x15, 0x00),      /* MME     */
+	NCP_REGION_ID(0x16, 0x05),      /* NCAV2   */
+	NCP_REGION_ID(0x16, 0x10),      /* NCAV22  */
+	NCP_REGION_ID(0x17, 0x05),      /* EIOAM1  */
+	NCP_REGION_ID(0x19, 0x05),      /* TMGR    */
+	NCP_REGION_ID(0x1a, 0x05),      /* MPPY    */
+	NCP_REGION_ID(0x1a, 0x23),      /* MPPY2   */
+	NCP_REGION_ID(0x1a, 0x21),      /* MPPY3   */
+	NCP_REGION_ID(0x1b, 0x05),      /* PIC     */
+	NCP_REGION_ID(0x1c, 0x05),      /* PAB     */
+	NCP_REGION_ID(0x1f, 0x05),      /* EIOAM0  */
+	NCP_REGION_ID(0x31, 0x05),      /* ISB     */
+	NCP_REGION_ID(0xff, 0xff)
+};
+
+unsigned long
+ncp_cnal_regions_acp55xx[] = {
+	NCP_REGION_ID(0x28, 0x05),      /* EIOASM0 */
+	NCP_REGION_ID(0x29, 0x05),      /* EIOASM1 */
+	NCP_REGION_ID(0x2a, 0x05),      /* EIOAS2  */
+	NCP_REGION_ID(0x2b, 0x05),      /* EIOAS3  */
+	NCP_REGION_ID(0x2c, 0x05),      /* EIOAS4  */
+	NCP_REGION_ID(0x2d, 0x05),      /* EIOAS5  */
+	NCP_REGION_ID(0x32, 0x05),      /* ISBS    */
+	NCP_REGION_ID(0xff, 0xff)
+};
+
+
+/*
+  ------------------------------------------------------------------------------
+  flush_l3
+
+  This is NOT a general function to flush the L3 cache.  There are a number of
+  assumptions that are not usually true...
+
+  1) All other cores are " quiesced".
+  2) There is no need to worry about preemption or interrupts.
+*/
+
+static void
+flush_l3(void)
+{
+
+	unsigned long hnf_offsets[] = {
+		0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27
+	};
+
+	int i;
+	unsigned long status;
+	int retries;
+
+	for (i = 0; i < (sizeof(hnf_offsets) / sizeof(unsigned long)); ++i)
+		writel(0x0, dickens + (0x10000 * hnf_offsets[i]) + 0x10);
+
+	for (i = 0; i < (sizeof(hnf_offsets) / sizeof(unsigned long)); ++i) {
+		retries = 10000;
+
+		do {
+			status = readl(dickens +
+				       (0x10000 * hnf_offsets[i]) + 0x18);
+			udelay(1);
+		} while ((0 < --retries) && (0x0 != (status & 0xf)));
+
+		if (0 == retries)
+			BUG();
+	}
+
+	for (i = 0; i < (sizeof(hnf_offsets) / sizeof(unsigned long)); ++i)
+		writel(0x3, dickens + (0x10000 * hnf_offsets[i]) + 0x10);
+
+	for (i = 0; i < (sizeof(hnf_offsets) / sizeof(unsigned long)); ++i) {
+		retries = 10000;
+
+		do {
+			status = readl(dickens +
+				       (0x10000 * hnf_offsets[i]) + 0x18);
+			udelay(1);
+		} while ((0 < --retries) && (0xc != (status & 0xf)));
+
+		if (0 == retries)
+			BUG();
+	}
+
+	asm volatile ("dsb" : : : "memory");
+
+	return;
+}
+
+static void
+quiesce_vp_engine(int engineType)
+{
+	unsigned long *pEngineRegions;
+	unsigned long ortOff, owtOff;
+	unsigned long *pRegion;
+	unsigned ort, owt;
+	unsigned long buf = 0;
+	unsigned short node, target;
+	int loop;
+
+	pr_info("quiescing VP engines...\n");
+
+	switch (engineType) {
+	case AXXIA_ENGINE_CNAL:
+		pEngineRegions = ncp_cnal_regions_acp55xx;
+		ortOff = 0x1c0;
+		owtOff = 0x1c4;
+		break;
+
+	case AXXIA_ENGINE_CAAL:
+		pEngineRegions = ncp_caal_regions_acp55xx;
+		ortOff = 0xf8;
+		owtOff = 0xfc;
+		break;
+
+	default:
+		return;
+	}
+
+	pRegion = pEngineRegions;
+
+	while (*pRegion != NCP_REGION_ID(0xff, 0xff)) {
+		/* set read/write transaction limits to zero */
+		ncr_write(*pRegion, 0x8, 4, &buf);
+		ncr_write(*pRegion, 0xc, 4, &buf);
+		pRegion++;
+	}
+
+	pRegion = pEngineRegions;
+	loop = 0;
+
+	while (*pRegion != NCP_REGION_ID(0xff, 0xff)) {
+		node = (*pRegion & 0xffff0000) >> 16;
+		target = *pRegion & 0x0000ffff;
+		/* read the number of outstanding read/write transactions */
+		ncr_read(*pRegion, ortOff, 4, &ort);
+		ncr_read(*pRegion, owtOff, 4, &owt);
+
+		if ((ort == 0) && (owt == 0)) {
+			/* this engine has been quiesced, move on to the next */
+			pr_info("quiesced region 0x%02x.0x%02x\n",
+					node, target);
+			pRegion++;
+		} else {
+			if (loop++ > 10000) {
+				pr_info(
+						"Unable to quiesce region 0x%02x.0x%02x ort=0x%x, owt=0x%x\n",
+						node, target, ort, owt);
+				pRegion++;
+				loop = 0;
+				continue;
+			}
+		}
+	}
+
+	return;
+}
+
+static inline void cpu_disable_l2_prefetch(void)
+{
+	unsigned int v;
+
+    /*
+     * MRC p15, 1, <Rt>, c15, c0, 3; Read L2 Prefetch Control Register
+     * MCR p15, 1, <Rt>, c15, c0, 3; Write L2 Prefetch Control Register
+     *
+     */
+	asm volatile(
+	"       mrc     p15, 1, %0, c15, c0, 3\n"
+	"       and     %0, %0, #0x0000\n"
+	"       mcr     p15, 1, %0, c15, c0, 3\n"
+	: "=&r" (v)
+	:
+	: "cc");
+
+	isb();
+}
+
+static inline void
+reset_elm_trace(void)
+{
+	/* reset and disable ELM trace */
+	ncr_register_write(htonl(0x000fff04), (unsigned *) (apb + 0x68000));
+	ncr_register_write(htonl(0x000fff04), (unsigned *) (apb + 0x78000));
+
+	/* reset ELM statistics */
+	ncr_register_write(htonl(0x00001), (unsigned *) (apb + 0x60230));
+	ncr_register_write(htonl(0x00001), (unsigned *) (apb + 0x70230));
+
+	/* enable ELM trace */
+	ncr_register_write(htonl(0x000fff01), (unsigned *) (apb + 0x68000));
+	ncr_register_write(htonl(0x000fff01), (unsigned *) (apb + 0x78000));
+}
+
+
+extern void ncp_ddr_shutdown(void *, void *,  unsigned long);
+
+
+void
+initiate_retention_reset(void)
+{
+	unsigned long ctl_244 = 0;
+	unsigned long value;
+	unsigned cpu_id;
+	volatile long tmp;
+	volatile long *ptmp;
+
+	if (0 == ddr_retention_enabled) {
+		pr_info("DDR Retention Reset is Not Enabled\n");
+		return;
+	}
+
+	if (NULL == nca || NULL == apb || NULL == dickens)
+		BUG();
+
+	preempt_disable();
+	cpu_id = smp_processor_id();
+
+	/* send stop message to other CPUs */
+	local_irq_disable();
+	local_fiq_disable();
+	asm volatile ("dsb" : : : "memory");
+	asm volatile ("dmb" : : : "memory");
+	system_state = SYSTEM_RESTART;
+	smp_send_stop();
+	udelay(1000);
+
+	flush_cache_all();
+	flush_l3();
+
+	/* TODO - quiesce VP engines */
+	quiesce_vp_engine(AXXIA_ENGINE_CAAL);
+	quiesce_vp_engine(AXXIA_ENGINE_CNAL);
+
+
+	/* unlock reset register for later */
+	writel(0x000000ab, apb + 0x31000); /* Access Key */
+
+	/* prepare to put DDR in self refresh power-down mode */
+	/* first read the CTL_244 register and OR in the LP_CMD value */
+	ncr_read(NCP_REGION_ID(34, 0), 0x3d0, 4, &ctl_244);
+	ctl_244 |= 0x000a0000;
+
+	/* belts & braces: put secondary CPUs into reset */
+	value = ~(1 << cpu_id);
+	value &= 0xffff;
+	ncr_register_write(htonl(value), (unsigned *) (apb + 0x31030));
+
+	/* load entire ddr_shutdown function into L2 cache */
+	ptmp = (long *) ncp_ddr_shutdown;
+	do {
+		tmp += *ptmp++;
+	} while (ptmp < (long *) (ncp_ddr_shutdown + 0x1000));
+
+	asm volatile ("isb" : : : "memory");
+
+	/* disable L2 prefetching */
+	cpu_disable_l2_prefetch();
+
+	/* reset ELM DDR access trace buffer */
+	reset_elm_trace();
+
+	/* call cache resident ddr shutdown function */
+	ncp_ddr_shutdown(nca, apb, ctl_244);
+
+	return;
+}
+EXPORT_SYMBOL(initiate_retention_reset);
+
+static ssize_t
+axxia_ddr_retention_trigger(struct file *file, const char __user *buf,
+			    size_t count, loff_t *ppos)
+{
+	initiate_retention_reset();
+	return 0;
+}
+
+static const struct file_operations axxia_ddr_retention_proc_ops = {
+	.write      = axxia_ddr_retention_trigger,
+	.llseek     = noop_llseek,
+};
+
+void
+axxia_ddr_retention_init(void)
+{
+	/*
+	* Only available on ASIC systems.
+	*/
+
+	if (of_find_compatible_node(NULL, NULL, "lsi,axm5500-amarillo")) {
+		/* Create /proc entry. */
+		if (!proc_create("driver/axxia_ddr_retention_reset",
+				S_IWUSR, NULL, &axxia_ddr_retention_proc_ops)) {
+			pr_info("Failed to register DDR retention proc entry\n");
+		} else {
+			apb = ioremap(0x2010000000, 0x80000);
+			nca = ioremap(0x002020100000ULL, 0x20000);
+			dickens = ioremap(0x2000000000, 0x1000000);
+			ddr_retention_enabled = 1;
+			pr_info("DDR Retention Reset Initialized\n");
+		}
+	} else {
+		pr_info("DDR Retention Reset is Not Available\n");
+	}
+
+	return;
+}
diff --git a/arch/arm/mach-axxia/ddr_shutdown.c b/arch/arm/mach-axxia/ddr_shutdown.c
new file mode 100644
index 0000000..33b889a
--- /dev/null
+++ b/arch/arm/mach-axxia/ddr_shutdown.c
@@ -0,0 +1,332 @@
+
+#include <asm/io.h>
+
+/*
+ * private copies of the ioread/write macros
+ * These are defined with a different barrier
+ * to avoid the outer_sync() call that's part
+ * of the normal barrier.
+ */
+#define pvt_ioread32be(p)   ({ unsigned int __v = be32_to_cpu((__force __be32)__raw_readl(p)); dsb(); __v; })
+#define pvt_iowrite32be(v, p) ({ dsb(); __raw_writel((__force __u32)cpu_to_be32(v), p); })
+
+#define pvt_ioread32(p)   ({ unsigned int __v = (__raw_readl(p)); dsb(); __v; })
+#define pvt_iowrite32(v, p) ({ dsb(); __raw_writel((__force __u32)(v), p); })
+
+/* #define DDR_SHUTDOWN_DEBUG  */
+#ifdef DDR_SHUTDOWN_DEBUG
+#define dbg_write(v, p) pvt_iowrite32be(v, p)
+#else
+#define dbg_write(v, p)
+#endif
+
+/*
+ * Wait For Completion timeout
+ * how many loops to wait for the config ring access to complete
+ */
+#define WFC_TIMEOUT (400000)
+
+/*
+ * DDR status timeout
+ * how many times we read the DDR status waiting for self refresh complete
+ */
+#define DDR_TIMEOUT (1000)
+
+void ncp_ddr_shutdown(void *nca, void *apb, unsigned long ctl_244)
+{
+	unsigned long value;
+	int two_elms = 0;
+	int wfc_loop = 0;
+	int ddr_loop = 0;
+
+	/* determine if we are in one or two ELM/SMEM mode */
+	value = pvt_ioread32(apb + 0x60004);
+	two_elms = (value & 0x00000200);
+
+	/*
+	 * Issue command to put SMEM0 into self-refresh mode
+	 *
+	 * ncpWrite 0x22.0.0x3d0
+	 */
+	dbg_write(0xaaaa0001, (unsigned *)(nca + 0x1200));
+
+	/* write register value into CDAR[0] */
+	pvt_iowrite32be(ctl_244, (unsigned *)(nca + 0x1000));
+	/* CDR2 - Node.target */
+	pvt_iowrite32be(0x00002200, (unsigned *)(nca + 0xf8));
+	/* CDR1 - word offset 0xf4 (byte offset 0x3d0) */
+	pvt_iowrite32be(0x000000f4, (unsigned *)(nca + 0xf4));
+	/* CDR0 - write command */
+	pvt_iowrite32be(0x80050003, (unsigned *)(nca + 0xf0));
+	wfc_loop = 0;
+	do {
+		if (wfc_loop++ > WFC_TIMEOUT) {
+			dbg_write(value, (unsigned *)(nca + 0x11fc));
+			dbg_write(0xffff0001, (unsigned *)(nca + 0x1200));
+			goto do_reset;
+		}
+		dbg_write(wfc_loop, (unsigned *)(nca + 0x11f8));
+		value = pvt_ioread32be((unsigned *)
+				       (nca + 0xf0));
+	} while ((0x80000000UL & value));
+	dbg_write(0xaaaa0002, (unsigned *)(nca + 0x1200));
+
+	if (two_elms) {
+		/*
+		 * Issue command to put SMEM1 into self-refresh mode
+		 *
+		 * ncpWrite 0x0f.0.0x3d0
+		 */
+		/* CDR2 - Node.target */
+		pvt_iowrite32be(0x00000f00, (unsigned *)(nca + 0xf8));
+		/* CDR0 - write command */
+		pvt_iowrite32be(0x80050003, (unsigned *)(nca + 0xf0));
+		wfc_loop = 0;
+		do {
+			if (wfc_loop++ > WFC_TIMEOUT) {
+				dbg_write(value, (unsigned *)(nca + 0x11fc));
+				dbg_write(0xffff0002,
+					  (unsigned *)(nca + 0x1200));
+				goto do_reset;
+			}
+			value = pvt_ioread32be((unsigned *)
+					       (nca + 0xf0));
+		} while ((0x80000000UL & value));
+	}
+
+	dbg_write(0xaaaa0003, (unsigned *)(nca + 0x1200));
+
+	/*
+	 * Poll for SMEM0 refresh-mode command completion
+	 */
+	/* CDR1 - word offset 0x104 (byte offset 0x410) */
+	pvt_iowrite32be(0x00000104, (unsigned *)(nca + 0xf4));
+	/* CDR2 - Node.target */
+	pvt_iowrite32be(0x00002200, (unsigned *)(nca + 0xf8));
+	ddr_loop = 0;
+	do {
+		if (ddr_loop++ > DDR_TIMEOUT) {
+			dbg_write(value, (unsigned *)(nca + 0x11fc));
+			dbg_write(0xffff0003, (unsigned *)(nca + 0x1200));
+			goto do_reset;
+		}
+		pvt_iowrite32be(wfc_loop, (unsigned *)
+				(nca + 0x11f0));
+
+		/* issue config ring read */
+		pvt_iowrite32be(0x80040003, (unsigned *)
+				(nca + 0xf0));
+		wfc_loop = 0;
+		do {
+			if (wfc_loop++ > WFC_TIMEOUT) {
+				dbg_write(value, (unsigned *)(nca + 0x11fc));
+				dbg_write(0xffff0004,
+					  (unsigned *)(nca + 0x1200));
+				goto do_reset;
+			}
+			value = pvt_ioread32be((unsigned *)
+					       (nca + 0xf0));
+		} while ((0x80000000UL & value));
+
+		value = pvt_ioread32be((unsigned *)
+				       (nca + 0x1000));
+
+	} while ((value & 0x0200) == 0);
+	dbg_write(0xaaaa0004, (unsigned *)(nca + 0x1200));
+
+	if (two_elms) {
+		/*
+		 * Poll for SMEM1 refresh-mode command completion
+		 */
+		/* CDR2 - Node.target */
+		pvt_iowrite32be(0x00000f00, (unsigned *)(nca + 0xf8));
+		ddr_loop = 0;
+		do {
+			if (ddr_loop++ > DDR_TIMEOUT) {
+				dbg_write(value, (unsigned *)(nca + 0x11fc));
+				dbg_write(0xffff0005,
+					  (unsigned *)(nca + 0x1200));
+				goto do_reset;
+			}
+
+			/* issue config ring read */
+			pvt_iowrite32be(0x80040003, (unsigned *)(nca + 0xf0));
+			wfc_loop = 0;
+			do {
+				if (wfc_loop++ > WFC_TIMEOUT) {
+					dbg_write(value,
+						  (unsigned *)(nca + 0x11fc));
+					dbg_write(0xffff0006,
+						  (unsigned *)(nca + 0x1200));
+					goto do_reset;
+				}
+				value =
+				    pvt_ioread32be((unsigned *)(nca + 0xf0));
+			} while ((0x80000000UL & value));
+
+			value = pvt_ioread32be((unsigned *)
+					       (nca + 0x1000));
+			wfc_loop++;
+		} while ((value & 0x0200) == 0);
+	}
+
+	dbg_write(0xaaaa0005, (unsigned *)(nca + 0x1200));
+
+	/*
+	 * Tell U-Boot to do a DDR retention-reset
+	 * (i.e. set bit 0 of persist_scratch register)
+	 */
+	pvt_iowrite32(0x00000001, apb + 0x300dc);
+
+	dbg_write(0xaaaa0006, (unsigned *)(nca + 0x1200));
+do_reset:
+	/*
+	 * Issue Chip reset
+	 */
+	/* Intrnl Boot, 0xffff0000 Target */
+	pvt_iowrite32(0x00000040, apb + 0x31004);
+	/* Set ResetReadDone */
+	pvt_iowrite32(0x80000000, apb + 0x3180c);
+	/* Chip Reset */
+	pvt_iowrite32(0x00080802, apb + 0x31008);
+
+	while (1)
+		;
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+
+	return;
+}
+
+void ncp_ddr_shutdown_dummy(void)
+{
+	wfi();
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+	__asm__ __volatile__("nop\n\t");
+}
diff --git a/arch/arm/mach-axxia/headsmp.S b/arch/arm/mach-axxia/headsmp.S
new file mode 100644
index 0000000..b4fe409
--- /dev/null
+++ b/arch/arm/mach-axxia/headsmp.S
@@ -0,0 +1,71 @@
+/*
+ *  linux/arch/arm/mach-axxia/headsmp.S
+ *
+ *  Cloned from linux/arch/arm/mach-realview/headsmp.S
+ *
+ *  Copyright (c) 2003 ARM Limited
+ *  All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/linkage.h>
+#include <linux/init.h>
+
+	__CPUINIT
+
+/*
+ * Axxia specific entry point for secondary CPUs.  This provides
+ * a "holding pen" into which all secondary cores are held until we're
+ * ready for them to initialise.
+ */
+ENTRY(axxia_secondary_startup)
+	mrc	p15, 0, r0, c0, c0, 5
+	bic	r0, #0xff000000
+	adr	r4, 1f
+	ldmia	r4, {r5, r6}
+	sub	r4, r4, r5
+	add	r6, r6, r4
+pen:	ldr	r7, [r6]
+	/*
+	 * It seems that just looping over read/compare kills starves
+	 * ethernet, this will happen if we start the kernel with
+	 * maxcpus=X where X < 16. Which we really want in order to
+	 * isolate cores.
+	 *
+	 * FIXME: We should really use wfi or wfe here
+	 */
+	mov	r4, #0x7000
+__delay:
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	subs	r4,r4,#1
+	bne	__delay
+	cmp	r7, r0
+	bne	pen
+
+	/*
+	 * We've been released from the holding pen: secondary_stack
+	 * should now contain the SVC stack for this core
+	 */
+	b	secondary_startup
+ENDPROC(axxia_secondary_startup)
+
+	.align 2
+1:	.long	.
+	.long	pen_release
diff --git a/arch/arm/mach-axxia/include/mach/axxia-gic.h b/arch/arm/mach-axxia/include/mach/axxia-gic.h
new file mode 100644
index 0000000..b9e5574
--- /dev/null
+++ b/arch/arm/mach-axxia/include/mach/axxia-gic.h
@@ -0,0 +1,17 @@
+/*
+ *  arch/arm/mach-axxia/axxia-gic.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __AXXIA_GIC_H
+#define __AXXIA_GIC_H
+
+void axxia_gic_raise_softirq(const struct cpumask *mask, unsigned int irq);
+void axxia_gic_secondary_init(void);
+int __init axxia_gic_of_init(struct device_node *node,
+			     struct device_node *parent);
+void axxia_gic_dump_mask(char *tmp, const struct cpumask *mask);
+void axxia_gic_kill_cpu(u32 rcpu);
+#endif
diff --git a/arch/arm/mach-axxia/include/mach/debug-macro.S b/arch/arm/mach-axxia/include/mach/debug-macro.S
new file mode 100644
index 0000000..1a28f4a
--- /dev/null
+++ b/arch/arm/mach-axxia/include/mach/debug-macro.S
@@ -0,0 +1,21 @@
+/* arch/arm/mach-axxia/include/mach/debug-macro.S
+ *
+ * Debugging macro include header
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <mach/hardware.h>
+
+#if !defined(CONFIG_ARM_LPAE)
+#error "Axxia Peripherals Are Only Accessible Using the LPAE!"
+#endif
+
+	.macro	addruart, rp, rv, tmp
+	ldr	\rp, =(AXXIA_DEBUG_UART_PHYS & 0xffffffff)
+	ldr	\rv, =AXXIA_DEBUG_UART_VIRT
+	.endm
+
+#include <asm/hardware/debug-pl01x.S>
diff --git a/arch/arm/mach-axxia/include/mach/entry-macro.S b/arch/arm/mach-axxia/include/mach/entry-macro.S
new file mode 100644
index 0000000..a14f9e6
--- /dev/null
+++ b/arch/arm/mach-axxia/include/mach/entry-macro.S
@@ -0,0 +1,5 @@
+	.macro	disable_fiq
+	.endm
+
+	.macro	arch_ret_to_user, tmp1, tmp2
+	.endm
diff --git a/arch/arm/mach-axxia/include/mach/gpio.h b/arch/arm/mach-axxia/include/mach/gpio.h
new file mode 100644
index 0000000..40a8c17
--- /dev/null
+++ b/arch/arm/mach-axxia/include/mach/gpio.h
@@ -0,0 +1 @@
+/* empty */
diff --git a/arch/arm/mach-axxia/include/mach/hardware.h b/arch/arm/mach-axxia/include/mach/hardware.h
new file mode 100644
index 0000000..2f3686e
--- /dev/null
+++ b/arch/arm/mach-axxia/include/mach/hardware.h
@@ -0,0 +1,24 @@
+/*
+ * arch/arm/mach-axxia/include/mach/hardware.h
+ *
+ * Copyright (c) 2013 LSI Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+#ifndef __MACH_HARDWARE_H
+#define __MACH_HARDWARE_H
+
+#define AXXIA_UART0_PHYS       0x2010080000
+#define AXXIA_UART1_PHYS       0x2010081000
+#define AXXIA_UART2_PHYS       0x2010082000
+#define AXXIA_UART3_PHYS       0x2010083000
+
+#ifdef CONFIG_DEBUG_LL_AXXIA_UART0
+#define AXXIA_DEBUG_UART_VIRT  0xf0080000
+#define AXXIA_DEBUG_UART_PHYS  AXXIA_UART0_PHYS
+#endif
+
+#endif
diff --git a/arch/arm/mach-axxia/include/mach/io.h b/arch/arm/mach-axxia/include/mach/io.h
new file mode 100644
index 0000000..a4eddd9
--- /dev/null
+++ b/arch/arm/mach-axxia/include/mach/io.h
@@ -0,0 +1,39 @@
+/*
+ *  arch/arm/mach-vexpress/include/mach/io.h
+ *
+ *  Copyright (C) 2003 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_IO_H
+#define __ASM_ARM_ARCH_IO_H
+
+#include <linux/types.h>
+
+#define __io(a)		__typesafe_io(a)
+#define __mem_pci(a)	(a)
+
+/*
+  Make the first argument to ioremap() be phys_addr_t (64 bits in this
+  case) instead of unsigned long.  When __arch_ioremap is defiend,
+  __arch_iounmap must be defined also.  Just use the default for
+  iounmap().
+*/
+
+void __iomem *__axxia_arch_ioremap(phys_addr_t, size_t, unsigned int);
+#define __arch_ioremap __axxia_arch_ioremap
+#define __arch_iounmap __arm_iounmap
+
+#endif
diff --git a/arch/arm/mach-axxia/include/mach/irqs.h b/arch/arm/mach-axxia/include/mach/irqs.h
new file mode 100644
index 0000000..ebff99a
--- /dev/null
+++ b/arch/arm/mach-axxia/include/mach/irqs.h
@@ -0,0 +1,5 @@
+#define IRQ_LOCALTIMER		29
+#define IRQ_LOCALWDOG		30
+#define IRQ_PMU			222
+#define AXXIA_MSI_FIRST		224
+#define NR_IRQS			256
diff --git a/arch/arm/mach-axxia/include/mach/ncr.h b/arch/arm/mach-axxia/include/mach/ncr.h
new file mode 100644
index 0000000..926d366
--- /dev/null
+++ b/arch/arm/mach-axxia/include/mach/ncr.h
@@ -0,0 +1,44 @@
+/*
+ * arch/arm/mach-axxia/include/mach/ncr.h
+ *
+ * Copyright (C) 2010 LSI
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307	 USA
+ */
+
+#ifndef __ARCH_ARM_MACH_AXXIA_NCR_H
+#define __ARCH_ARM_MACH_AXXIA_NCR_H
+
+#ifndef NCP_REGION_ID
+#define NCP_REGION_ID(node, target) \
+((unsigned long) ((((node) & 0xffff) << 16) | ((target) & 0xffff)))
+#endif
+
+#ifndef NCP_NODE_ID
+#define NCP_NODE_ID(region) (((region) >> 16) & 0xffff)
+#endif
+
+#ifndef NCP_TARGET_ID
+#define NCP_TARGET_ID(region) ((region) & 0xffff)
+#endif
+
+unsigned long ncr_register_read(unsigned *);
+void ncr_register_write(const unsigned, unsigned *);
+int ncr_read(unsigned long, unsigned long, int, void *);
+int ncr_write(unsigned long, unsigned long, int, void *);
+int ncr_init(void);
+void ncr_exit(void);
+
+#endif /* __ARCH_ARM_MACH_AXXIA_NCR_H */
diff --git a/arch/arm/mach-axxia/include/mach/pci.h b/arch/arm/mach-axxia/include/mach/pci.h
new file mode 100644
index 0000000..3260654
--- /dev/null
+++ b/arch/arm/mach-axxia/include/mach/pci.h
@@ -0,0 +1,6 @@
+#ifndef _AXXIA_PCI_H
+#define _AXXIA_PCI_H
+
+void __init axxia_pcie_init(void);
+
+#endif
diff --git a/arch/arm/mach-axxia/include/mach/rio.h b/arch/arm/mach-axxia/include/mach/rio.h
new file mode 100644
index 0000000..a004480
--- /dev/null
+++ b/arch/arm/mach-axxia/include/mach/rio.h
@@ -0,0 +1,44 @@
+/*
+ * Helper module for board specific RAPIDIO bus registration
+ *
+ * Copyright (C) 2014 LSI Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#ifndef __ASM__ARCH_AXXIA_RAPIDIO_H
+#define __ASM__ARCH_AXXIA_RAPIDIO_H
+#include <linux/io.h>
+
+#define IN_SRIO8(a, v, ec)      do {            \
+	v = ioread8(a); ec = 0;  \
+	} while (0)
+#define IN_SRIO16(a, v, ec)     do {            \
+	v = ioread16be(a); ec = 0;  \
+	} while (0)
+#define IN_SRIO32(a, v, ec)     do {            \
+	v = ioread32be(a); ec = 0;  \
+	} while (0)
+
+#define OUT_SRIO8(a, v)         iowrite8(v, a)
+#define OUT_SRIO16(a, v)        iowrite16be(v, a)
+#define OUT_SRIO32(a, v)        iowrite32be(v, a)
+
+int axxia_rapidio_board_init(struct platform_device *dev, int devnum,
+							int *portndx);
+
+int axxia_rapidio_init(void);
+
+#endif /* __ASM__ARCH_AXXIA_RAPIDIO_H */
diff --git a/arch/arm/mach-axxia/include/mach/system.h b/arch/arm/mach-axxia/include/mach/system.h
new file mode 100644
index 0000000..f653a8e
--- /dev/null
+++ b/arch/arm/mach-axxia/include/mach/system.h
@@ -0,0 +1,33 @@
+/*
+ *  arch/arm/mach-vexpress/include/mach/system.h
+ *
+ *  Copyright (C) 2003 ARM Limited
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARCH_SYSTEM_H
+#define __ASM_ARCH_SYSTEM_H
+
+static inline void arch_idle(void)
+{
+	/*
+	 * This should do all the clock switching
+	 * and wait for interrupt tricks
+	 */
+	cpu_do_idle();
+}
+
+#endif
diff --git a/arch/arm/mach-axxia/include/mach/timers.h b/arch/arm/mach-axxia/include/mach/timers.h
new file mode 100644
index 0000000..8aa49c9
--- /dev/null
+++ b/arch/arm/mach-axxia/include/mach/timers.h
@@ -0,0 +1,39 @@
+/*
+ *  arch/arm/mach-axxia/include/mach/timers.h
+ *
+ *  Copyright (C) 2012 LSI
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*
+  This is based on arch/arm/include/asm/hardware/timer-sp.h
+
+  See arch/arm/mach-axxia/timers.c for details.
+ */
+
+#include <asm/hardware/timer-sp.h>
+
+#define AXXIA_TIMER_1_BASE 0x00
+#define AXXIA_TIMER_2_BASE 0x20
+#define AXXIA_TIMER_3_BASE 0x40
+#define AXXIA_TIMER_4_BASE 0x60
+#define AXXIA_TIMER_5_BASE 0x80
+#define AXXIA_TIMER_6_BASE 0xa0
+#define AXXIA_TIMER_7_BASE 0xc0
+#define AXXIA_TIMER_8_BASE 0xe0
+
+void axxia_timer_clocksource_init(void __iomem *, const char *);
+void axxia_timer_clockevents_init(void __iomem *, unsigned int, const char *);
diff --git a/arch/arm/mach-axxia/include/mach/timex.h b/arch/arm/mach-axxia/include/mach/timex.h
new file mode 100644
index 0000000..00029ba
--- /dev/null
+++ b/arch/arm/mach-axxia/include/mach/timex.h
@@ -0,0 +1,23 @@
+/*
+ *  arch/arm/mach-vexpress/include/mach/timex.h
+ *
+ *  RealView architecture timex specifications
+ *
+ *  Copyright (C) 2003 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#define CLOCK_TICK_RATE		(50000000 / 16)
diff --git a/arch/arm/mach-axxia/include/mach/uncompress.h b/arch/arm/mach-axxia/include/mach/uncompress.h
new file mode 100644
index 0000000..a386048
--- /dev/null
+++ b/arch/arm/mach-axxia/include/mach/uncompress.h
@@ -0,0 +1,65 @@
+/*
+ *  arch/arm/mach-vexpress/include/mach/uncompress.h
+ *
+ *  Copyright (C) 2003 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#define AMBA_UART_DR(base)	(*(volatile unsigned char *)((base) + 0x00))
+#define AMBA_UART_LCRH(base)	(*(volatile unsigned char *)((base) + 0x2c))
+#define AMBA_UART_CR(base)	(*(volatile unsigned char *)((base) + 0x30))
+#define AMBA_UART_FR(base)	(*(volatile unsigned char *)((base) + 0x18))
+
+
+#if defined(CONFIG_DEBUG_VEXPRESS_CA9X4_UART)
+#define get_uart_base()	(0x10000000 + 0x00009000)
+#elif defined(CONFIG_DEBUG_VEXPRESS_RS1_UART)
+#define get_uart_base()	(0x1c000000 + 0x00090000)
+#else
+#define get_uart_base() (0UL)
+#endif
+
+/*
+ * This does not append a newline
+ */
+static inline void putc(int c)
+{
+	unsigned long base = get_uart_base();
+
+	if (!base)
+		return;
+
+	while (AMBA_UART_FR(base) & (1 << 5))
+		barrier();
+
+	AMBA_UART_DR(base) = c;
+}
+
+static inline void flush(void)
+{
+	unsigned long base = get_uart_base();
+
+	if (!base)
+		return;
+
+	while (AMBA_UART_FR(base) & (1 << 3))
+		barrier();
+}
+
+/*
+ * nothing to do
+ */
+#define arch_decomp_setup()
+#define arch_decomp_wdog()
diff --git a/arch/arm/mach-axxia/io.c b/arch/arm/mach-axxia/io.c
new file mode 100644
index 0000000..bf473f9
--- /dev/null
+++ b/arch/arm/mach-axxia/io.c
@@ -0,0 +1,40 @@
+/*
+ * arch/arm/mach-axxia/io.c
+ *
+ * Support for the LSI Axxia boards based on ARM cores.
+ *
+ * Copyright (C) 2012 LSI
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <asm/page.h>
+#include <asm/io.h>
+
+void __iomem *
+__axxia_arch_ioremap(phys_addr_t physical_address, size_t size,
+		     unsigned int flags)
+{
+	unsigned long pfn;
+	unsigned long offset;
+
+	pfn = (unsigned long)((physical_address >> PAGE_SHIFT) & 0xffffffffULL);
+	offset = (unsigned long)(physical_address & (PAGE_SIZE - 1));
+
+	return __arm_ioremap_pfn(pfn, offset, size, flags);
+}
+EXPORT_SYMBOL(__axxia_arch_ioremap);
diff --git a/arch/arm/mach-axxia/pci.c b/arch/arm/mach-axxia/pci.c
new file mode 100644
index 0000000..9a402e6
--- /dev/null
+++ b/arch/arm/mach-axxia/pci.c
@@ -0,0 +1,1127 @@
+/*
+ * arch/arm/mach-axxia/pci.c
+ *
+ * PCIe support for AXM55xx.
+ *
+ * Copyright (C) 2013 LSI
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/of_irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/msi.h>
+#include <linux/kernel_stat.h>
+#include <asm/sizes.h>
+#include <asm/mach/pci.h>
+#include <asm/irq.h>
+#include <asm/mach/irq.h>
+#include <asm-generic/errno-base.h>
+#include <mach/pci.h>
+
+
+#define PCIE_CONFIG              (0x1000)
+#define PCIE_STATUS              (0x1004)
+#define PCIE_CORE_DEBUG          (0x1008)
+#define PCIE_LOOPBACK_FAIL       (0x100C)
+#define PCIE_MPAGE_U(n)          (0x1010 + (n * 8)) /* n = 0..7 */
+#define PCIE_MPAGE_L(n)          (0x1014 + (n * 8)) /* n = 0..7 */
+#define PCIE_TPAGE_BAR0(n)       (0x1050 + (n * 4)) /* n = 0..7 */
+#define     PCIE_TPAGE_32        (0<<31) /* AXI 32-bit access */
+#define     PCIE_TPAGE_128       (1<<31) /* AXI 128-bit access */
+#define PCIE_TPAGE_BAR1(n)       (0x1070 + (n * 4)) /* n = 0..7 */
+#define PCIE_TPAGE_BAR2(n)       (0x1090 + (n * 4)) /* n = 0..7 */
+#define PCIE_MSG_IN_FIFO         (0x10B0)
+#define PCIE_MSG_IN_FIFO_STATUS  (0x10B4)
+#define PCIE_MSG_OUT             (0x10B8)
+#define PCIE_TRN_ORDER_STATUS    (0x10BC)
+#define PCIE_INT0_STATUS         (0x10C0)
+#define PCIE_INT0_ENABLE         (0x10C4)
+#define PCIE_INT0_FORCE          (0x10C8)
+#define    INT0_MSI              0x80000000U
+#define    INT0_INT_ASSERTED     0x08000000U
+#define    INT0_INT_DEASSERTED   0x04000000U
+#define    INT0_ERROR            0x73FFFFABU
+#define PCIE_PHY_STATUS0         (0x10CC)
+#define PCIE_PHY_STATUS1         (0x10D0)
+#define PCIE_PHY_CONTROL0        (0x10D4)
+#define PCIE_PHY_CONTROL1        (0x10D8)
+#define PCIE_PHY_CONTROL2        (0x10DC)
+#define PCIE_RESERVED_E0         (0x10E0)
+#define PCIE_RESERVED_E4         (0x10E4)
+#define PCIE_RESERVED_E8         (0x10E8)
+#define PCIE_AXI_MASTER_WR       (0x10EC)
+#define PCIE_LINK_STATUS         (0x117C)
+#define PCIE_EP_BAR2_CFG         (0x1184)
+#define PCIE_AXI_MSI_ADDR        (0x1190)
+#define PCIE_INT1_STATUS         (0x11C4)
+#define PCIE_INT1_ENABLE         (0x11C8)
+#define PCIE_INT1_FORCE          (0x11CC)
+#define INT1_DOORBELL            0x00000001U
+#define PCIE_RC_BAR0_SIZE        (0x11F4)
+#define PCIE_MSI0_STATUS         (0x1230)
+#define PCIE_MSI0_ENABLE         (0x1234)
+#define PCIE_MSI0_FORCE          (0x1238)
+#define PCIE_MSI1_STATUS(_grp)   (0x123C+(_grp)*12)
+#define PCIE_MSI1_ENABLE(_grp)   (0x1240+(_grp)*12)
+#define PCIE_MSI1_FORCE(_grp)    (0x1244+(_grp)*12)
+
+/* Every MPAGE register maps 128MB in the AXI memory range */
+#define MPAGE_SIZE               (128U<<20)
+
+/* We have 7 MPAGE registers available for outbound window (one reserved for
+ * mapping PCI configuration space).
+ */
+#define MAX_OUTBOUND_SIZE	 (7 * MPAGE_SIZE)
+
+/* Number of IRQs allocated to MSI */
+#define NUM_MSI_IRQ (NR_IRQS - AXXIA_MSI_FIRST)
+
+/* Bitmap for allocated MSIs */
+static DECLARE_BITMAP(msi_irq_in_use, NUM_MSI_IRQ);
+
+static const struct resource pcie_outbound_default[] = {
+	[0] = {
+		.start = 0,
+		.end   = 0,
+		.flags = IORESOURCE_MEM
+	},
+	[1] = {
+		.start = 0,
+		.end   = 0,
+		.flags = IORESOURCE_MEM
+	}
+};
+
+struct axxia_pciex_port {
+	char                name[16];
+	unsigned int	    index;
+	u8                  root_bus_nr;
+	bool                link_up;
+	int                 irq[17]; /* 1 legacy, 16 MSI */
+	void __iomem	    *regs;
+	void __iomem	    *cfg_data;
+	u32                 last_mpage;
+	int                 endpoint;
+	struct device_node  *node;
+	struct resource	    utl_regs;
+	struct resource	    cfg_space;
+	/* Outbound PCI base address */
+	u64                 pci_addr;
+	/* Outbound range in (physical) CPU addresses */
+	struct resource	    outbound;
+	/* Inbound PCI base address */
+	u64                 pci_bar;
+	/* Inbound range in (physical) CPU addresses */
+	struct resource	    inbound;
+	/* Virtual and physical (CPU space) address for MSI table */
+	void                *msi_virt;
+	dma_addr_t          msi_phys;
+	/* PCI memory space address for MSI table */
+	u32                 msi_pci_addr;
+};
+
+#define PCIE_MAX_PORTS 2
+static struct axxia_pciex_port *axxia_pciex_ports;
+
+static void pcie_msi_dispatch(u32 group, struct axxia_pciex_port *port);
+
+static void
+fixup_axxia_pci_bridge(struct pci_dev *dev)
+{
+	/* if we aren't a PCIe don't bother */
+	if (!pci_find_capability(dev, PCI_CAP_ID_EXP))
+		return;
+
+	/* Set the class appropriately for a bridge device */
+	dev_info(&dev->dev,
+		 "Fixup PCI Class to PCI_CLASS_BRIDGE_HOST for %04x:%04x\n",
+		 dev->vendor, dev->device);
+	dev->class = PCI_CLASS_BRIDGE_HOST << 8;
+	/* Make the bridge transparent */
+	dev->transparent = 1;
+}
+
+DECLARE_PCI_FIXUP_HEADER(0x1000, 0x5101, fixup_axxia_pci_bridge);
+DECLARE_PCI_FIXUP_HEADER(0x1000, 0x5108, fixup_axxia_pci_bridge);
+DECLARE_PCI_FIXUP_HEADER(0x1000, 0x5120, fixup_axxia_pci_bridge);
+
+/* Convert to Bus# to PCIe port# */
+static struct axxia_pciex_port *bus_to_port(struct pci_bus *bus)
+{
+	return axxia_pciex_ports + pci_domain_nr(bus);
+}
+
+/*
+ * Validate the Bus#/Device#/Function#
+ */
+static int
+axxia_pciex_validate_bdf(struct pci_bus *bus, unsigned int devfn)
+{
+	struct axxia_pciex_port *port;
+
+	port = bus_to_port(bus);
+
+	/* Endpoint can not generate upstream(remote) config cycles */
+	if (port->endpoint)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	if (((!((PCI_FUNC(devfn) == 0) && (PCI_SLOT(devfn) == 0)))
+		&& (bus->number == port->root_bus_nr))
+		|| (!(PCI_SLOT(devfn) == 0)
+		&& (bus->number == port->root_bus_nr+1))) {
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	}
+	return 0;
+}
+
+/*
+ * Return the configuration access base address
+ */
+static void __iomem *
+axxia_pciex_get_config_base(struct axxia_pciex_port *port,
+			    struct pci_bus *bus,
+			    unsigned int devfn)
+{
+	int relbus, dev, fn;
+	unsigned mpage;
+
+	if (bus->number == port->root_bus_nr)
+		return port->regs;
+
+	relbus = bus->number - (port->root_bus_nr + 1);
+	dev    = PCI_SLOT(devfn);
+	fn     = PCI_FUNC(devfn);
+
+	if (dev > 31)
+		return NULL;
+
+	/* Build the mpage register (MPAGE[4]=1 for cfg access) */
+	mpage = (fn << 19) | (bus->number << 11) | (dev << 6) | (1<<4);
+
+	/* Primary bus */
+	if (relbus && (bus->number != port->root_bus_nr))
+		mpage |= 1<<5;
+
+	if (mpage != port->last_mpage) {
+		writel(0,     port->regs + PCIE_MPAGE_U(7));
+		writel(mpage, port->regs + PCIE_MPAGE_L(7));
+		port->last_mpage = mpage;
+	}
+
+	return port->cfg_data;
+}
+
+/*
+ * Read PCI config space
+ */
+static int
+arm_pciex_axxia_read_config(struct pci_bus *bus,
+			    unsigned int devfn,
+			    int offset,
+			    int len,
+			    u32 *val)
+{
+	struct axxia_pciex_port *port = bus_to_port(bus);
+	void __iomem *addr;
+	u32 bus_addr;
+	u32 val32;
+	int bo = offset & 0x3;
+	int rc = PCIBIOS_SUCCESSFUL;
+	u32 bus_addr1;
+
+	if (axxia_pciex_validate_bdf(bus, devfn) != 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	addr = axxia_pciex_get_config_base(port, bus, devfn);
+
+	if (!addr) {
+		*val = 0;
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	}
+
+	/*
+	 * addressing is different for local config access vs.
+	 * access through the mapped config space.
+	 */
+	if (bus->number == 0) {
+		int wo = offset & 0xfffffffc;
+		bus_addr = (u32)addr + wo;
+		bus_addr1 = bus_addr;
+	} else {
+		/*
+		 * mapped config space only supports 32-bit access
+		 *
+		 *  AXI address [3:0] is not used at all.
+		 *  AXI address[9:4] becomes register number.
+		 *  AXI address[13:10] becomes Ext. register number
+		 *  AXI address[17:14] becomes 1st DWBE for configuration
+		 *  read only.
+		 *  AXI address[29:27] is used to select one of 8 Mpage
+		 *  registers.
+		 */
+		bus_addr = (u32) addr + (offset << 2);
+		bus_addr1 = bus_addr;
+
+		switch (len) {
+		case 1:
+			bus_addr |=  ((1 << bo)) << 14;
+			break;
+		case 2:
+			bus_addr |=  ((3 << bo)) << 14;
+			break;
+		default:
+			bus_addr |=  (0xf) << 14;
+			break;
+		}
+	}
+	/*
+	 * do the read
+	 */
+	val32 = readl((u32 __iomem *)bus_addr);
+
+	switch (len) {
+	case 1:
+		*val = (val32 >> (bo * 8)) & 0xff;
+		break;
+	case 2:
+		*val = (val32 >> (bo * 8)) & 0xffff;
+		break;
+	default:
+		*val = val32;
+		break;
+	}
+
+#ifdef PRINT_CONFIG_ACCESSES
+	pr_info("acp_read_config for PCIE%d: %3d  fn=0x%04x o=0x%04x l=%d a=0x%08x v=0x%08x, dev=%d\n",
+			port->index, bus->number, devfn, offset, len,
+			bus_addr, *val, PCI_SLOT(devfn));
+#endif
+	return rc;
+}
+
+/*
+ * Write PCI config space.
+ */
+static int
+arm_pciex_axxia_write_config(struct pci_bus *bus,
+			     unsigned int devfn,
+			     int offset,
+			     int len,
+			     u32 val)
+{
+	struct axxia_pciex_port *port = bus_to_port(bus);
+	void __iomem *addr;
+	u32 bus_addr;
+	u32 val32;
+
+	if (axxia_pciex_validate_bdf(bus, devfn) != 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	addr = axxia_pciex_get_config_base(port, bus, devfn);
+
+	if (!addr)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	/*
+	 * addressing is different for local config access vs.
+	 * access through the mapped config space. We need to
+	 * translate the offset for mapped config access
+	 */
+	if (bus->number == 0) {
+		/* the local ACP RC only supports 32-bit dword config access,
+		 * so if this is a byte or 16-bit word access we need to
+		 * perform a read-modify write
+		 */
+		if (len == 4) {
+			bus_addr = (u32) addr + offset;
+		} else {
+			int bs = ((offset & 0x3) * 8);
+
+			bus_addr = (u32) addr + (offset & 0xfffffffc);
+			val32 = readl((u32 __iomem *)bus_addr);
+
+			if (len == 2) {
+				val32 = (val32 & ~(0xffff << bs))
+					| ((val & 0xffff) << bs);
+			} else {
+				val32 = (val32 & ~(0xff << bs))
+					| ((val & 0xff) << bs);
+			}
+
+			val = val32;
+			len = 4;
+		}
+	} else {
+		bus_addr = (u32) addr + (offset << 2) + (offset & 0x3);
+	}
+
+#ifdef PRINT_CONFIG_ACCESSES
+	pr_info("acp_write_config: bus=%3d devfn=0x%04x offset=0x%04x len=%d addr=0x%08x val=0x%08x\n",
+		bus->number, devfn, offset, len, bus_addr, val);
+#endif
+
+	switch (len) {
+	case 1:
+		writeb(val, (u8 __iomem *)(bus_addr));
+		break;
+	case 2:
+		writew(val, (u16 __iomem *)(bus_addr));
+		break;
+	default:
+		writel(val, (u32 __iomem *)(bus_addr));
+		break;
+	}
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops axxia_pciex_pci_ops = {
+	.read  = arm_pciex_axxia_read_config,
+	.write = arm_pciex_axxia_write_config,
+};
+
+/*
+ * pcie_doorbell_isr
+ *
+ * This ISR is for doorbell interrupts for
+ * Endpoint mode which has a dedicated IRQ line
+ * This support expects kernel module to handle the doorbell
+ * interrupt
+ */
+static irqreturn_t
+pcie_doorbell_isr(int irq, void *arg)
+{
+	struct axxia_pciex_port *port  = arg;
+	void __iomem            *mbase = port->regs;
+	u32                     intr1_status;
+
+	intr1_status = readl(mbase + PCIE_INT1_STATUS);
+
+	if (intr1_status & INT1_DOORBELL) {
+		/* EP doorbell interrupt. This support expects kernel module
+		 * to handle this doorbell interrupt
+		 */
+		/* Clear it */
+		writel(INT1_DOORBELL, mbase + PCIE_INT1_STATUS);
+	}
+	return IRQ_NONE;
+}
+
+
+/*
+ * pcie_legacy_isr
+ *
+ * The interrupt line for this handler is shared between the PCIE controller
+ * itself (for status and error interrupts) and devices using legacy PCI
+ * interupt signalling. Statis and error interrupts are serviced here and this
+ * handler will return IRQ_HANDLED. If the reasont is the assertion of a device
+ * legacy interrupt, this handler returns IRQ_NONE the next action on this line
+ * will be called (the PCI EP interrupt service routine).
+ */
+static irqreturn_t
+pcie_legacy_isr(int irq, void *arg)
+{
+	struct axxia_pciex_port *port  = arg;
+	void __iomem            *mbase = port->regs;
+	u32                      intr_status, intr1_status;
+	irqreturn_t              retval = IRQ_HANDLED;
+
+	/* read the PEI interrupt status register */
+	intr_status = readl(mbase + PCIE_INT0_STATUS);
+	intr1_status = readl(mbase + PCIE_INT1_STATUS);
+
+	/* check if this is a PCIe message not from an external device */
+	if (intr_status & INT0_ERROR) {
+			u32 int_enb;
+			u32 offset;
+
+			pr_info("PCIE%d: Error interrupt %#x\n",
+				port->index, intr_status);
+
+			pr_info("PCIE%d: link status = %#x\n",
+				port->index, readl(mbase + PCIE_LINK_STATUS));
+
+			if (intr_status & 0x00020000) {
+				pr_info("PCIE%d: t2a_fn_indp_err_stat = %#x\n",
+					port->index, readl(mbase+0x1170));
+				int_enb = readl(mbase + PCIE_INT0_ENABLE);
+				int_enb &= 0xfffdffff;
+				writel(int_enb, mbase + PCIE_INT0_ENABLE);
+			}
+
+			if (intr_status & 0x00040000) {
+				pr_info("PCIE%d: t2a_fn_indp_other_err_stat = %#x\n",
+					port->index, readl(mbase+0x1174));
+				int_enb = readl(mbase + PCIE_INT0_ENABLE);
+				int_enb &= 0xfffbffff;
+				writel(int_enb, mbase + PCIE_INT0_ENABLE);
+			}
+
+			if (intr_status & 0x00000800) {
+				pr_info("PCIE%d: config=%#x status=%#x\n",
+					port->index,
+					readl(mbase + PCIE_CONFIG),
+					readl(mbase + PCIE_STATUS));
+				int_enb = readl(mbase + PCIE_INT0_ENABLE);
+				int_enb &= 0xfffff7ff;
+				writel(int_enb, mbase + PCIE_INT0_ENABLE);
+			}
+
+			/*
+			 * Dump all the potentially interesting PEI registers
+			 */
+			for (offset = 0x114c; offset <= 0x1180; offset += 4) {
+				pr_err("  [0x%04x] = 0x%08x\n",
+				       offset, readl(mbase + offset));
+			}
+	} else if (intr_status & INT0_INT_ASSERTED) {
+		/* Empty the message FIFO */
+		while ((readl(port->regs + PCIE_MSG_IN_FIFO_STATUS) & 1) == 0)
+			(void) readl(port->regs + PCIE_MSG_IN_FIFO);
+		/* Next handler in chain will service this interrupt */
+		retval = IRQ_NONE;
+	} else if (intr_status & INT0_MSI) {
+		u32 msi_status = readl(port->regs + PCIE_MSI0_STATUS);
+		if (msi_status == 0) {
+			retval = IRQ_NONE;
+		} else {
+			u32 group = ffs(msi_status) - 1;
+			pcie_msi_dispatch(group, port);
+		}
+	}
+
+	if (intr1_status & INT1_DOORBELL) {
+		/* RC doorbell interrupt. This support expects kernel module
+		 * to handle this doorbell interrupt
+		 */
+		/* Clear it */
+		writel(INT1_DOORBELL, mbase + PCIE_INT1_STATUS);
+		return IRQ_NONE;
+	}
+
+	/*
+	 *  We clear all the interrupts in the PEI status, even though
+	 *  interrupts from external devices have not yet been handled.
+	 *  That should be okay, since the PCI IRQ in the GIC won't be
+	 *  re-enabled until all external handlers have been called.
+	 */
+	writel(intr_status, mbase + PCIE_INT0_STATUS);
+	return retval;
+}
+
+/*
+ * MSI handler
+ *
+ * This is the handler for PCIE MSI service. It will decode the signalled MSI
+ * using the following hierarchy of status bits. This handler is installed as a
+ * chained handler for each of the 16 interrupt lines on the top-level
+ * interrupt controller. When a pending MSI is found, this handler forwards the
+ * interrupt service to the corresponding MSI IRQs (numbered from
+ * AXXIA_MSI_FIRST..NR_IRQS).
+ *
+ *                                             PCIE_MSI1_STATUS(group)
+ *
+ *                 PCIE_MSI0_STATUS                  +----------+
+ *                                                   | MSI      |
+ *   +----------+    +----------+                    | 0..15    |
+ *   | ARM GIC  |    | GROUP    |        /-----------+          |
+ *   |          +----+ 0..15    +-------/            |          |
+ *   |          |    |          |                    |          |
+ *   |          +----+          +-------\            +----------+
+ *   |          |    |          |        \
+ *   |          +----+          |         \          +----------+
+ *   |          |    |          |          \         | MSI      |
+ *   |          +----+          |           \        | 16..31   |
+ *   |          |    |          |            \-------+          |
+ *   |          +----+          |                    |          |
+ *   |          |    |          |                    |          |
+ *   |          |    |          |                    +----------+
+ *   |          | .  |          |
+ *   |          | .  |          |                    ...
+ *   |          | .  |          |
+ *   |          |    |          |                    +----------+
+ *   |          |    |          |                    | MSI      |
+ *   |          +----+          +--------------------+ 240..255 |
+ *   |          |    |          |                    |          |
+ *   +----------+    +----------+                    |          |
+ *                                                   |          |
+ *                                                   +----------+
+ */
+static void
+pcie_msi_dispatch(u32 group, struct axxia_pciex_port *port)
+{
+	u32 status;
+
+	/* Check next level interrupt status */
+	status = readl(port->regs + PCIE_MSI1_STATUS(group)) & 0xffff;
+	while (status) {
+		u32 line = ffs(status) - 1;
+		status &= ~(1 << line);
+		/* Clear interrupt on sub-level */
+		writel((1 << line), port->regs + PCIE_MSI1_STATUS(group));
+		generic_handle_irq(AXXIA_MSI_FIRST + (group * 16) + line);
+	}
+
+	/* Clear interrupt on top-level*/
+	writel(1 << group, port->regs + PCIE_MSI0_STATUS);
+}
+
+static void
+pcie_msi_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+	struct axxia_pciex_port *port = &axxia_pciex_ports[0];
+	u32 group = irq - port->irq[1];
+	u32 status;
+
+	/* Check if interrupt is pending */
+	status = readl(port->regs + PCIE_MSI0_STATUS);
+	if (status & (1 << group)) {
+		kstat_incr_irqs_this_cpu(irq, desc);
+		/* Handle the PCIe interrupt */
+		pcie_msi_dispatch(group, port);
+	} else {
+		handle_bad_irq(irq, desc);
+	}
+	/* Signal end-of-interrupt */
+	irq_desc_get_chip(desc)->irq_eoi(&desc->irq_data);
+}
+
+/* PCIe setup function */
+static int axxia_pcie_setup(int portno, struct pci_sys_data *sys)
+{
+	struct axxia_pciex_port *port = &axxia_pciex_ports[sys->domain];
+	u32 pci_config, pci_status, link_state;
+	int i, num_pages, err, ret;
+	u32 outbound_size;
+	u32 inbound_size;
+	u64 dest;
+
+	port->root_bus_nr = sys->busnr;
+
+	/* Map PCIe bridge control registers */
+	port->regs = ioremap(port->utl_regs.start,
+			     resource_size(&port->utl_regs));
+	if (!port->regs) {
+		pr_err("PCIE%d: Failed to map control registers\n",
+			sys->domain);
+		goto fail;
+	}
+
+	/* Map range for access to PCI configuration space */
+	port->cfg_data = ioremap(port->cfg_space.start,
+				 resource_size(&port->cfg_space));
+	if (!port->cfg_data) {
+		pr_err("PCIE%d: Failed to map config space\n", sys->domain);
+		goto fail;
+	}
+
+	pci_add_resource_offset(&sys->resources, &port->outbound,
+				port->outbound.start - port->pci_addr);
+
+	/* add IO_RESOURCE for legacy support expected by bios32 driver
+	 * not used by AXM55xx */
+	sys->io_res.start = (sys->domain * 0x100);
+	sys->io_res.end =  sys->io_res.start + 0xff;
+	sys->io_res.flags = IORESOURCE_IO;
+	sys->io_res.name = sys->io_res_name;
+	sprintf(sys->io_res_name, "PCI%d I/O", sys->domain);
+	pr_info("PCIE%d: ioport start =  %#llx (PCI) -> %#llx\n",
+		sys->domain, sys->io_res.start, sys->io_res.end);
+
+	ret = request_resource(&ioport_resource, &sys->io_res);
+	if (ret) {
+		pr_err("PCI: unable to allocate I/O port region (%d)\n", ret);
+		goto fail;
+	}
+	pci_add_resource_offset(&sys->resources, &sys->io_res,
+		sys->io_offset);
+
+	/* Status/error interrupt */
+	port->irq[0] = irq_of_parse_and_map(port->node, 0);
+	err = request_irq(port->irq[0], pcie_legacy_isr, IRQF_SHARED,
+			  "pcie", port);
+	if (err) {
+		pr_err("PCIE%d: Failed to request IRQ#%d (%d)\n",
+		       sys->domain, port->irq[0], err);
+		release_resource(&sys->io_res);
+		goto fail;
+	}
+
+	/* MSI interrupts */
+	for (i = 1; i <= 16; i++) {
+		port->irq[i] = irq_of_parse_and_map(port->node, i);
+		if (!port->irq[i])
+			break;
+		irq_set_chained_handler(port->irq[i], pcie_msi_irq_handler);
+	}
+
+	/* Setup as root complex */
+	pci_config = readl(port->regs + PCIE_CONFIG);
+	pci_status = readl(port->regs + PCIE_STATUS);
+	link_state = (pci_status >> 8) & 0x3f;
+	pr_info("PCIE%d: status=0x%08x, link state=%#x\n",
+		port->index, pci_status, link_state);
+
+	/* make sure the ACP device is configured as PCI Root Complex */
+	if ((pci_status & 0x18) != 0x18) {
+		/* Endpoint */
+		pr_err("PCIE%d: Device is not Root Complex\n", port->index);
+		if (sys->domain == 0) {
+			/* PEI0 */
+			err = request_irq(port->irq[0]+3, pcie_doorbell_isr,
+			IRQF_SHARED, "pcie_db", port);
+			if (err) {
+				pr_err("PCIE%d: Failed to request IRQ#%d (%d)\n",
+					sys->domain, port->irq[0], err);
+				release_resource(&sys->io_res);
+				goto fail;
+			}
+		} else if (sys->domain == 1) {
+			/* PEI1 */
+			err = request_irq(port->irq[0]+2, pcie_doorbell_isr,
+			IRQF_SHARED, "pcie_db", port);
+			if (err) {
+				pr_err("PCIE%d: Failed to request IRQ#%d (%d)\n",
+					sys->domain, port->irq[0], err);
+				release_resource(&sys->io_res);
+				goto fail;
+			}
+		}
+		/* Enable doorbell interrupts */
+		writel(INT1_DOORBELL,
+			port->regs + PCIE_INT1_ENABLE);
+		release_resource(&sys->io_res);
+		return 0;
+	}
+
+	/* Make sure the link is up */
+	if (link_state != 0xb) {
+		/* Reset */
+		pr_warn("PCIE%d: Link in bad state - resetting\n", port->index);
+		pci_config |= 1;
+		writel(pci_config, port->regs + PCIE_CONFIG);
+		msleep(1000);
+		pci_status = readl(port->regs + PCIE_STATUS);
+		link_state = (pci_status & 0x3f00) >> 8;
+		pr_warn("PCIE%d: (after reset) link state=%#x\n",
+			port->index, link_state);
+		if (link_state != 0xb) {
+			pr_warn("PCIE%d: Link in bad state - giving up!\n",
+				port->index);
+			release_resource(&sys->io_res);
+			goto fail;
+		}
+	}
+
+	/*
+	 * Setup outbound PCI Memory Window
+	 */
+
+	outbound_size = resource_size(&port->outbound);
+	num_pages = (outbound_size + MPAGE_SIZE - 1) / MPAGE_SIZE;
+	dest = port->pci_addr;
+	for (i = 0; i < num_pages; i++) {
+		u32 mpage_u = dest >> 32;
+		u32 mpage_l = (u32)dest & ~(MPAGE_SIZE-1);
+		writel(mpage_u, port->regs + PCIE_MPAGE_U(i));
+		writel(mpage_l, port->regs + PCIE_MPAGE_L(i));
+		pr_debug("PCIE%d: MPAGE(%d) = %08x %08x\n",
+			 port->index, i, mpage_u, mpage_l);
+		dest += MPAGE_SIZE;
+	}
+
+	/*
+	 * Setup inbound PCI window
+	 */
+
+	/* Configure the inbound window size */
+	inbound_size = (u32) resource_size(&port->inbound);
+	writel(~(inbound_size-1), port->regs + PCIE_RC_BAR0_SIZE);
+
+	/* Verify BAR0 size */
+	{
+		u32 bar0_size;
+		writel(~0, port->regs + PCI_BASE_ADDRESS_0);
+		bar0_size = readl(port->regs + PCI_BASE_ADDRESS_0);
+		if ((bar0_size & ~0xf) != ~(inbound_size-1))
+			pr_err("PCIE%d: Config BAR0 failed\n", port->index);
+	}
+
+	/* Set the BASE0 address to start of PCIe base */
+	writel(port->pci_bar, port->regs + PCI_BASE_ADDRESS_0);
+
+	/* Set the BASE1 address to 0x0 */
+	writel(0x0, port->regs + PCI_BASE_ADDRESS_1);
+
+
+	/* Setup TPAGE registers for inbound mapping
+	 *
+	 * We set the MSB of each TPAGE to select 128-bit AXI access. For the
+	 * address field we simply program an incrementing value to map
+	 * consecutive pages
+	 */
+	for (i = 0; i < 8; i++)
+		writel(PCIE_TPAGE_128 | i, port->regs + PCIE_TPAGE_BAR0(i));
+
+
+	/* Enable all legacy/status/error interrupts */
+	writel(INT0_MSI | INT0_INT_ASSERTED | INT0_ERROR,
+	       port->regs + PCIE_INT0_ENABLE);
+
+	/* Enable doorbell interrupts */
+	writel(INT1_DOORBELL,
+	       port->regs + PCIE_INT1_ENABLE);
+
+	/* Enable all MSI interrupt groups */
+	writel(0xFFFF, port->regs + PCIE_MSI0_ENABLE);
+	/* Enable all lines in all subgroups */
+	for (i = 0; i < 16; i++)
+		writel(0xFFFF, port->regs + PCIE_MSI1_ENABLE(i));
+
+	return 1;
+fail:
+	if (port->cfg_data)
+		iounmap(port->cfg_data);
+	if (port->regs)
+		iounmap(port->regs);
+	return 0;
+}
+
+/*
+ * Allocate MSI page. A MSI is generated when EP writes to this PCI address.
+ * The region must be 1Kb to manage 256 MSIs.
+ */
+static void *
+pcie_alloc_msi_table(struct pci_dev *pdev, struct axxia_pciex_port *port)
+{
+	u32 msi_lower;
+	void *msi_virt;
+
+	if (dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64)) != 0 &&
+	    dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)) != 0) {
+		dev_err(&pdev->dev, "No memory for MSI table\n");
+		return NULL;
+	}
+
+	msi_virt = dma_alloc_coherent(&pdev->dev, 1024,
+				      &port->msi_phys,
+				      GFP_KERNEL);
+	if (msi_virt) {
+		msi_lower = (u32)port->msi_phys;
+		/* Please note we have 1:1 mapping for inbound */
+		port->msi_pci_addr = port->inbound.start + msi_lower;
+		writel(msi_lower>>10, port->regs + PCIE_AXI_MSI_ADDR);
+	}
+
+	return msi_virt;
+}
+
+
+/*
+ * Scan PCIe bus
+ */
+static struct pci_bus *
+axxia_pcie_scan_bus(int nr, struct pci_sys_data *sys)
+{
+	if (WARN_ON(nr >= PCIE_MAX_PORTS))
+		return NULL;
+
+	return pci_scan_root_bus(NULL, sys->busnr, &axxia_pciex_pci_ops,
+				 sys, &sys->resources);
+}
+
+
+
+static int
+axxia_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+	struct pci_sys_data *sys = dev->sysdata;
+	struct axxia_pciex_port *port = &axxia_pciex_ports[sys->domain];
+	return port->irq[0];
+}
+
+/* IRQ chip ops for MSI IRQs */
+static struct irq_chip axxia_msi_chip = {
+	.name = "PCI-MSI",
+	.irq_enable  = unmask_msi_irq,
+	.irq_disable = mask_msi_irq,
+	.irq_mask    = mask_msi_irq,
+	.irq_unmask  = unmask_msi_irq,
+};
+
+/* Port definition struct */
+static struct hw_pci axxia_pcie_hw[] = {
+	[0] = {
+		.nr_controllers = 1,
+		.domain = 0,
+		.setup = axxia_pcie_setup,
+		.scan = axxia_pcie_scan_bus,
+		.map_irq = axxia_pcie_map_irq
+	},
+	[1] = {
+		.nr_controllers = 1,
+		.domain = 1,
+		.setup = axxia_pcie_setup,
+		.scan = axxia_pcie_scan_bus,
+		.map_irq = axxia_pcie_map_irq
+	}
+};
+
+static void
+axxia_probe_pciex_bridge(struct device_node *np)
+{
+	struct axxia_pciex_port *port;
+	u32 portno;
+	const char *val;
+	const u32 *field;
+	int rlen;
+	int pna = of_n_addr_cells(np); /* address-size of parent */
+	u32 pci_space;
+	u64 pci_addr;
+	u64 cpu_addr;
+	u64 size;
+
+	/* Check if device is enabled */
+	if (!of_device_is_available(np))
+		return;
+
+	/* Get the port number from the device-tree */
+	if (of_property_read_u32(np, "port", &portno) != 0) {
+		pr_err("%s: No 'port' property\n", np->full_name);
+		return;
+	}
+
+	if (portno >= PCIE_MAX_PORTS) {
+		pr_err("%s: Port %d out of range\n", np->full_name, portno);
+		return;
+	}
+
+	port = &axxia_pciex_ports[portno];
+	port->index = portno;
+	snprintf(port->name, sizeof(port->name) - 1, "PCIE%d", portno);
+	port->node = of_node_get(np);
+
+	/* Check if device_type property is set to "pci" or "pci-endpoint".
+	 * Resulting from this setup this PCIe port will be configured
+	 * as root-complex or as endpoint.
+	 */
+	val = of_get_property(port->node, "device_type", NULL);
+	port->endpoint = val && strcmp(val, "pci-endpoint") == 0;
+
+	/* Fetch address range for PCIE config space */
+	if (of_address_to_resource(np, 0, &port->cfg_space)) {
+		pr_err("PCIE%d: No resource for PCIe config space\n", portno);
+		return;
+	}
+
+	/* Fetch address range for host bridge internal registers */
+	if (of_address_to_resource(np, 1, &port->utl_regs)) {
+		pr_err("PCIE%d: No resource for PCIe registers\n", portno);
+		return;
+	}
+
+	if (request_resource(&iomem_resource, &port->utl_regs))
+		return;
+
+	/*
+	 * Outbound PCI memory window
+	 */
+
+	/* Defaults */
+	port->outbound = pcie_outbound_default[portno];
+	port->outbound.name = port->name;
+
+	field = of_get_property(np, "ranges", &rlen);
+	if (field) {
+		pci_space = of_read_number(field + 0, 1);
+		switch ((pci_space >> 24) & 3) {
+		case 0:
+			pr_err("PCIE%d: Invalid 'ranges'\n", portno);
+			break;
+		case 1: /* PCI IO Space */
+			pr_err("PCIE%d: PCI IO not supported\n", portno);
+			break;
+		case 2: /* PCI MEM 32-bit */
+		case 3: /* PCI MEM 64-bit */
+			cpu_addr  = of_read_number(field + 3, 2);
+			size      = of_read_number(field + 5, 2);
+			port->outbound.start = cpu_addr;
+			port->outbound.end   = cpu_addr + size - 1;
+			port->pci_addr = of_read_number(field + 1, 2);
+			break;
+		}
+	}
+
+	if (resource_size(&port->outbound) > MAX_OUTBOUND_SIZE) {
+		pr_err("PCIE%d: Outbound window too large (using max %#x)\n",
+		       portno, MAX_OUTBOUND_SIZE);
+		port->outbound.end = (port->outbound.start +
+				      MAX_OUTBOUND_SIZE - 1);
+	}
+
+	if (request_resource(&iomem_resource, &port->outbound)) {
+		pr_err("PCIE%d: Memory resource request failed\n", portno);
+		return;
+	}
+
+	if (request_resource(&iomem_resource, &port->cfg_space)) {
+		pr_err("PCIE%d: Config space request failed\n", portno);
+		return;
+	}
+
+	pr_info("PCIE%d: Outbound %#llx..%#llx (CPU) -> %#llx (PCI)\n",
+		portno,
+		port->outbound.start, port->outbound.end,
+		port->pci_addr);
+
+	/*
+	 * Inbound PCI memory window
+	 */
+
+	/* Default 4GB */
+	port->inbound.name  = "PCIE DMA";
+	port->inbound.start = 0x00000000;
+	port->inbound.end   = 0xffffffff;
+	port->inbound.flags = IORESOURCE_MEM | IORESOURCE_PREFETCH;
+
+	/* Get dma-ranges property */
+	field = of_get_property(np, "dma-ranges", &rlen);
+	if (!field) {
+		pr_info("PCIE%d: No 'dma-ranges' property, using defaults\n",
+			portno);
+	} else {
+		BUILD_BUG_ON(sizeof(resource_size_t) != sizeof(u64));
+
+		/* Limited to one inbound window for now... */
+		pci_space = of_read_number(field + 0, 1);
+		pci_addr  = of_read_number(field + 1, 2);
+		cpu_addr  = of_read_number(field + 3, pna);
+		size      = of_read_number(field + pna + 3, 2);
+
+		port->inbound.start = cpu_addr;
+		port->inbound.end   = cpu_addr + size - 1;
+		port->pci_bar       = pci_addr;
+	}
+
+	pr_info("PCIE%d: Inbound %#llx (PCI) -> %#llx..%#llx (CPU)\n",
+		portno,
+		port->pci_bar,
+		port->inbound.start, port->inbound.end);
+}
+
+/*
+ * Allocate a MSI interrupt number and IRQ (the IRQ is a constant offset from
+ * the MSI index). The kernel IRQs for MSI are in a range above hardware IRQs.
+ *
+ * First call also allocates the 1Kb MSI table and configures the controller.
+ */
+int
+arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
+{
+	struct axxia_pciex_port *port = bus_to_port(pdev->bus);
+	struct msi_msg msg;
+	int pos, irq;
+
+	/* Allocate MSI table on demand when first device needs it */
+	if (!port->msi_virt)
+		port->msi_virt = pcie_alloc_msi_table(pdev, port);
+
+	if (!port->msi_virt)
+		return -ENOMEM;
+
+	/* Find available MSI */
+again:
+	pos = find_first_zero_bit(msi_irq_in_use, NUM_MSI_IRQ);
+	/* Offset to get the IRQ */
+	irq = AXXIA_MSI_FIRST + pos;
+	if (irq >= NR_IRQS)
+		return -ENOSYS;
+	if (test_and_set_bit(pos, msi_irq_in_use))
+		goto again;
+
+	/* Initialize IRQ descriptor */
+	dynamic_irq_init(irq);
+	if (irq_set_msi_desc(irq, desc) != 0) {
+		dev_err(&pdev->dev, "Bad IRQ descriptor for IRQ%d\n", irq);
+		clear_bit(pos, msi_irq_in_use);
+		return -EINVAL;
+	}
+	/* Use a simple handle for our "SW" MSI IRQs */
+	irq_set_chip_and_handler(irq, &axxia_msi_chip, handle_simple_irq);
+	set_irq_flags(irq, IRQF_VALID);
+
+	/* Configure PCI device with its MSI address */
+	msg.address_hi = 0x0;
+	msg.address_lo = port->msi_pci_addr + 4*pos;
+	msg.data       = irq;
+	write_msi_msg(irq, &msg);
+
+	return 0;
+}
+
+/*
+ * Called by the generic MSI layer to free MSI IRQ.
+ */
+void
+arch_teardown_msi_irq(unsigned int irq)
+{
+	int pos = irq - AXXIA_MSI_FIRST;
+
+	if (0 <= pos && pos < NR_IRQS) {
+		clear_bit(pos, msi_irq_in_use);
+		dynamic_irq_cleanup(irq);
+	}
+}
+
+/**
+ * Initialize PCIe controller(s) found in the device tree.
+ */
+void __init
+axxia_pcie_init(void)
+{
+	struct device_node *np;
+	int num_ports = 0;
+
+	/* allocate memory */
+	axxia_pciex_ports = kzalloc(PCIE_MAX_PORTS *
+				    sizeof(struct axxia_pciex_port),
+				    GFP_KERNEL);
+	if (!axxia_pciex_ports) {
+		pr_err("PCIE: No memory\n");
+		return;
+	}
+
+
+	for_each_compatible_node(np, NULL, "lsi,plb-pciex") {
+		if (!of_device_is_available(np)) {
+			num_ports++;
+			continue;
+		}
+
+		axxia_probe_pciex_bridge(np);
+		pci_common_init(&axxia_pcie_hw[num_ports]);
+		if (++num_ports == PCIE_MAX_PORTS)
+			break;
+	}
+}
diff --git a/arch/arm/mach-axxia/pci.h b/arch/arm/mach-axxia/pci.h
new file mode 100644
index 0000000..d4211a4
--- /dev/null
+++ b/arch/arm/mach-axxia/pci.h
@@ -0,0 +1 @@
+void axxia_pcie_init(void);
diff --git a/arch/arm/mach-axxia/platsmp.c b/arch/arm/mach-axxia/platsmp.c
new file mode 100644
index 0000000..2804fce
--- /dev/null
+++ b/arch/arm/mach-axxia/platsmp.c
@@ -0,0 +1,315 @@
+/*
+ *  linux/arch/arm/mach-axxia/platsmp.c
+ *
+ *  Copyright (C) 2012 LSI
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/smp.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <asm/smp_plat.h>
+#include <asm/cacheflush.h>
+#include <linux/irqchip/arm-gic.h>
+#include <asm/mach/map.h>
+#include <asm/virt.h>
+
+#include "axxia.h"
+#include "lsi_power_management.h"
+#include "axxia_circular_queue.h"
+#include <mach/axxia-gic.h>
+
+extern void axxia_secondary_startup(void);
+extern void axxia_cpu_power_management_gic_control(u32 cpu, bool enable);
+extern void axxia_dist_power_management_gic_control(bool enable);
+extern struct circular_queue_t axxia_circ_q;
+
+#define SYSCON_PHYS_ADDR 0x002010030000ULL
+
+static int __cpuinitdata wfe_fixup;
+static int wfe_available;
+
+inline void
+__axxia_arch_wfe(void)
+{
+	if (0 != wfe_available)
+		wfe();
+
+	return;
+}
+EXPORT_SYMBOL(__axxia_arch_wfe);
+
+/*
+ * Check if we need to enable cross-cluster SEV workaround for a bug in
+ * revision 1.0 silicon (which could cause event signals (from SEV instruction)
+ * to get lost between clusters). As a workaround, we set the bit 7 in CP15
+ * ACTLR (enable WFE as a no-operation) for each core.
+ */
+static void __init check_fixup_sev(void __iomem *syscon)
+{
+	u32 pfuse = readl(syscon + 0x34);
+	u32 chip_type = pfuse & 0x1f;
+	u32 chip_ver  = (pfuse >> 5) & 0x3f;
+
+	/* Set flag for secondary cores */
+	wfe_fixup = (chip_type == 0 || chip_type == 9) && (chip_ver == 0);
+	pr_info("axxia: Cross-cluster SEV fixup: %s\n", wfe_fixup ? "yes" : "no");
+}
+
+static void  do_fixup_sev(void)
+{
+	u32 tmp;
+
+	if (wfe_fixup) {
+		asm volatile("mrc\tp15, 0, %0, c1, c0, 1" : "=r"(tmp));
+		tmp |= (1<<7);
+		asm volatile("mcr\tp15, 0, %0, c1, c0, 1" :: "r"(tmp));
+		isb();
+	}
+}
+
+/*
+ * Write pen_release in a way that is guaranteed to be visible to all
+ * observers, irrespective of whether they're taking part in coherency
+ * or not.  This is necessary for the hotplug code to work reliably.
+ */
+static void  write_pen_release(int val)
+{
+	pen_release = val;
+	smp_wmb();
+	__cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
+	outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
+}
+
+static DEFINE_RAW_SPINLOCK(boot_lock);
+
+void  axxia_secondary_init(unsigned int cpu)
+{
+	int phys_cpu;
+	int phys_cluster;
+
+	phys_cpu = cpu_logical_map(cpu);
+	phys_cluster = phys_cpu / 4;
+
+	/*
+	 * Only execute this when powering up a cpu for hotplug.
+	 */
+	if (!pm_in_progress[phys_cpu]) {
+		/* Fixup for cross-cluster SEV */
+		do_fixup_sev();
+
+		axxia_gic_secondary_init();
+	} else {
+
+#ifdef CONFIG_HOTPLUG_CPU_COMPLETE_POWER_DOWN
+		if (cluster_power_up[phys_cluster])
+			pm_cluster_logical_powerup();
+		pm_cpu_logical_powerup();
+#endif
+		get_cpu();
+		axxia_gic_secondary_init();
+		put_cpu();
+
+#ifdef CONFIG_HOTPLUG_CPU_COMPLETE_POWER_DOWN
+		cluster_power_up[phys_cluster] = false;
+		pm_in_progress[phys_cpu] = false;
+#endif
+	}
+
+	/*
+	 * Let the primary processor know we're out of the
+	 * pen, then head off into the C entry point.
+	 */
+	write_pen_release(-1);
+
+	/*
+	 * Synchronise with the boot thread.
+	 */
+	_raw_spin_lock(&boot_lock);
+	_raw_spin_unlock(&boot_lock);
+}
+
+int  axxia_boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+
+	int phys_cpu, cluster;
+	unsigned long timeout;
+	unsigned long powered_down_cpu;
+	u32 i;
+	u32 dummy;
+
+
+	/*
+	 * Set synchronisation state between this boot processor
+	 * and the secondary one.
+	 */
+	_raw_spin_lock(&boot_lock);
+
+	phys_cpu = cpu_logical_map(cpu);
+
+	powered_down_cpu = pm_get_powered_down_cpu();
+
+	if (powered_down_cpu & (1 << phys_cpu)) {
+		pm_in_progress[phys_cpu] = true;
+		pm_cpu_powerup(phys_cpu);
+	}
+
+	/*
+	 * In the Axxia, the bootloader does not put the secondary cores
+	 * into a wait-for-event (wfe) or wait-for-interrupt (wfi) state
+	 * because of the multi-cluster design (i.e., there's no way for
+	 * the primary core in cluster 0 to send an event or interrupt
+	 * to secondary cores in the other clusters).
+	 *
+	 * Instead, the secondary cores are immediately put into a loop
+	 * that polls the "pen_release" global and MPIDR register. The two
+	 * are compared and if they match, a secondary core then executes
+	 * the Axxia secondary startup code.
+	 *
+	 * Here we convert the "cpu" variable to be compatible with the
+	 * ARM MPIDR register format (CLUSTERID and CPUID):
+	 *
+	 * Bits:   |11 10 9 8|7 6 5 4 3 2|1 0
+	 *         | CLUSTER | Reserved  |CPU
+	 */
+	cluster = (phys_cpu / 4) << 8;
+	phys_cpu = cluster + (phys_cpu % 4);
+
+	/* Release the specified core */
+	write_pen_release(phys_cpu);
+
+	/* Send a wakeup IPI to get the idled cpu out of WFI state */
+	arch_send_wakeup_ipi_mask(cpumask_of(cpu));
+
+
+	/* Wait for so long, then give up if nothing happens ... */
+	timeout = jiffies + (1 * HZ);
+	while (time_before(jiffies, timeout)) {
+		smp_rmb();
+
+		if (pen_release == -1)
+			break;
+
+		/* Wait 10 cycles */
+		for (i = 0; i < 10; i++)
+			dummy = i;
+	}
+
+	/*
+	 * Now the secondary core is starting up let it run its
+	 * calibrations, then wait for it to finish.
+	 */
+	_raw_spin_unlock(&boot_lock);
+
+	return pen_release != -1 ? -ENOSYS : 0;
+}
+
+static __init struct device_node *get_cpu_node(int cpu)
+{
+	struct device_node *np;
+
+	for_each_node_by_type(np, "cpu") {
+		u32 reg;
+		if (of_property_read_u32(np, "reg", &reg))
+			continue;
+		if (reg == cpu_logical_map(cpu))
+			return np;
+	}
+
+	return NULL;
+}
+
+static void __init axxia_smp_prepare_cpus(unsigned int max_cpus)
+{
+	void __iomem *syscon;
+	int cpu_count = 0;
+	int cpu;
+
+	syscon = ioremap(SYSCON_PHYS_ADDR, SZ_64K);
+	if (WARN_ON(!syscon))
+		return;
+
+	check_fixup_sev(syscon);
+	do_fixup_sev();
+
+	if (of_find_compatible_node(NULL, NULL,
+				    "lsi,axm5500-sim") != NULL ||
+	    of_find_compatible_node(NULL, NULL,
+				    "lsi,axm5500-emu") != NULL)
+		wfe_available = 0;
+	else
+		wfe_available = 1;
+
+	/*
+	 * Initialise the present map, which describes the set of CPUs actually
+	 * populated at the present time.
+	 */
+	for_each_possible_cpu(cpu) {
+		struct device_node *np;
+		u32 release_phys;
+		u32 *release_virt;
+
+		np = get_cpu_node(cpu);
+		if (!np)
+			continue;
+		if (of_property_read_u32(np, "cpu-release-addr", &release_phys))
+			continue;
+
+		/*
+		 * Release all physical cpus since we might want to
+		 * bring them online later.
+		 */
+		if (cpu != 0) {
+			u32 phys_cpu = cpu_logical_map(cpu);
+			u32 tmp = readl(syscon + 0x1010);
+			writel(0xab, syscon + 0x1000);
+			tmp &= ~(1 << phys_cpu);
+			writel(tmp, syscon + 0x1010);
+		}
+
+		if (cpu_count < max_cpus) {
+			set_cpu_present(cpu, true);
+			cpu_count++;
+		}
+
+		/*
+		 * This is the entry point of the routine that the secondary
+		 * cores will execute once they are released from their
+		 * "holding pen".
+		 */
+		if (release_phys != 0) {
+			int is_kmapped = pfn_valid(__phys_to_pfn(release_phys));
+			if (is_kmapped)
+				release_virt = phys_to_virt(release_phys);
+			else
+				release_virt = ioremap(release_phys, PAGE_SIZE);
+			*release_virt = virt_to_phys(axxia_secondary_startup);
+			smp_wmb();
+			__cpuc_flush_dcache_area(release_virt, sizeof(u32));
+			if (!is_kmapped)
+				iounmap(release_virt);
+		}
+	}
+
+	iounmap(syscon);
+
+}
+
+struct smp_operations axxia_smp_ops __initdata = {
+	.smp_prepare_cpus	= axxia_smp_prepare_cpus,
+	.smp_secondary_init	= axxia_secondary_init,
+	.smp_boot_secondary	= axxia_boot_secondary,
+#ifdef CONFIG_HOTPLUG_CPU
+	.cpu_die		= axxia_platform_cpu_die,
+	.cpu_kill		= axxia_platform_cpu_kill,
+#endif
+
+};
diff --git a/arch/arm/mach-axxia/smon.c b/arch/arm/mach-axxia/smon.c
new file mode 100644
index 0000000..313a3ce
--- /dev/null
+++ b/arch/arm/mach-axxia/smon.c
@@ -0,0 +1,223 @@
+/*
+ * linux/arch/arm/mach-axxia/smon.c
+ *
+ * Platform perf helper module for generic VP statistical monitor
+ *
+ * Copyright (C) 2013 LSI Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#include <linux/io.h>
+
+#include <../../../drivers/misc/lsi-ncr.h>
+
+#include "smon.h"
+
+static void memcpy32_fromio(uint32_t *dest, uint32_t *src, uint32_t len)
+{
+	uint32_t i;
+
+	for (i = 0; i < len; i++)
+		dest[i] = ioread32(src + i);
+}
+
+static void memcpy32_toio(uint32_t *dest, uint32_t *src, uint32_t len)
+{
+	uint32_t i;
+
+	for (i = 0; i < len; i++)
+		iowrite32(src[i], dest + i);
+}
+
+void smon_init_ncp(struct smon_s *smon, uint32_t node, uint32_t target,
+	uint32_t offset)
+{
+	smon->assigned[0] = UNASSIGNED;
+	smon->assigned[1] = UNASSIGNED;
+	smon->type = NCP_SMON;
+	smon->node = node;
+	smon->target = target;
+	smon->offset = offset;
+}
+
+void smon_init_mem(struct smon_s *smon, uint64_t addr, uint32_t offset)
+{
+	smon->assigned[0] = UNASSIGNED;
+	smon->assigned[1] = UNASSIGNED;
+	smon->type = MEM_SMON;
+	smon->addr = ioremap(addr, SZ_4K);
+	smon->offset = offset;
+
+	if (smon->addr == NULL)
+		pr_err("axxia perf, smon can't remap memory %lld\n", addr);
+}
+
+void smon_stop_if_unassigned(struct smon_s *smon)
+{
+	uint32_t control = 0;
+
+	if (smon->assigned[0] == UNASSIGNED &&
+	    smon->assigned[1] == UNASSIGNED) {
+		ncr_write(NCP_REGION_ID(smon->node, smon->target), smon->offset,
+			  1 * REG_SZ, &control);
+	}
+}
+
+uint32_t smon_allocate(struct smon_s *smon, uint8_t event)
+{
+	if (smon->assigned[0] == UNASSIGNED) {
+		smon->events[0] = event;
+		smon->assigned[0] = ASSIGNED;
+	} else if (smon->assigned[1] == UNASSIGNED) {
+		smon->events[1] = event;
+		smon->assigned[1] = ASSIGNED;
+	} else {
+		pr_warn("smon_allocate, no counter availible\n");
+		return -ENOCOUNTER;
+	}
+
+	return 0;
+}
+
+uint32_t smon_deallocate(struct smon_s *smon, uint8_t event)
+{
+	if ((smon->assigned[0] == ASSIGNED) && (smon->events[0] == event))
+		smon->assigned[0] = UNASSIGNED;
+	else if ((smon->assigned[1] == ASSIGNED) && (smon->events[1] == event))
+		smon->assigned[1] = UNASSIGNED;
+	else
+		return -ENOCOUNTER;
+
+	return 0;
+}
+
+uint32_t smon_event_active(struct smon_s *smon, uint8_t event)
+{
+	if ((smon->assigned[0] == ASSIGNED) && (smon->events[0] == event))
+		return 0;
+	else if ((smon->assigned[1] == ASSIGNED) && (smon->events[1] == event))
+		return 0;
+
+	return -ENOCOUNTER;
+}
+
+uint32_t smon_read(struct smon_s *smon, uint8_t event)
+{
+	uint32_t deltacount;
+
+	if (smon->type == NCP_SMON)
+		ncr_read(NCP_REGION_ID(smon->node, smon->target), smon->offset,
+			 8 * REG_SZ, &smon->regs);
+	else if (smon->type == MEM_SMON)
+		memcpy32_fromio((uint32_t *)&smon->regs,
+			(uint32_t *)smon->addr + smon->offset, 8);
+
+	if ((smon->assigned[0] == ASSIGNED) &&
+			(smon->events[0] == event)) {
+		if (smon->regs.count0 >= smon->lastread[0])
+			deltacount = smon->regs.count0 - smon->lastread[0];
+		else
+			deltacount = 0xffffffff - smon->lastread[0]
+					+ smon->regs.count0;
+
+		smon->lastread[0] = smon->regs.count0;
+
+		return deltacount;
+	} else if ((smon->assigned[1] == ASSIGNED) &&
+			(smon->events[1] == event)) {
+		if (smon->regs.count1 >= smon->lastread[1])
+			deltacount = smon->regs.count1 - smon->lastread[1];
+		else
+			deltacount = 0xffffffff - smon->lastread[1]
+					+ smon->regs.count1;
+
+		smon->lastread[1] = smon->regs.count1;
+
+		return deltacount;
+	}
+
+	return -ENOEVENT;
+}
+
+uint32_t smon_start(struct smon_s *smon, uint8_t event)
+{
+	/* get currect configuration */
+	if (smon->type == NCP_SMON)
+		ncr_read(NCP_REGION_ID(smon->node, smon->target), smon->offset,
+			 8 * REG_SZ, &smon->regs);
+	else if (smon->type == MEM_SMON)
+		memcpy32_fromio((uint32_t *)&smon->regs,
+			(uint32_t *)smon->addr + smon->offset, 8);
+
+	smon->regs.control = 1;	/* run counters */
+
+	if ((smon->assigned[0] == ASSIGNED) && (smon->events[0] == event)) {
+		smon->regs.event0 = event;
+		smon->regs.count0 = 0;
+		smon->lastread[0] = 0;
+
+		if (smon->type == NCP_SMON) {
+			/* write configuration, but do not change count reg */
+			ncr_write(NCP_REGION_ID(smon->node, smon->target),
+				smon->offset, 2 * REG_SZ, &smon->regs);
+
+			/* clear this events counter register */
+			ncr_write(NCP_REGION_ID(smon->node, smon->target),
+				smon->offset + 4 * REG_SZ, 1 * REG_SZ,
+				&smon->regs.count0);
+		} else if (smon->type == MEM_SMON) {
+			/* write configuration, but do not change count reg */
+			memcpy32_toio((uint32_t *)smon->addr + smon->offset,
+				(uint32_t *)&smon->regs, 2);
+
+			/* clear this events counter register */
+			memcpy32_toio((uint32_t *)smon->addr + smon->offset + 4,
+				(uint32_t *)&smon->regs.count0, 1);
+
+		}
+
+	} else if ((smon->assigned[1] == ASSIGNED)
+		&& (smon->events[1] == event)) {
+		smon->regs.event1 = event;
+		smon->regs.count1 = 0;
+		smon->lastread[1] = 0;
+
+		if (smon->type == NCP_SMON) {
+			/* write configuration, but do not change count reg */
+			ncr_write(NCP_REGION_ID(smon->node, smon->target),
+				smon->offset, 2 * REG_SZ, &smon->regs);
+
+			/* clear this events counter register */
+			ncr_write(NCP_REGION_ID(smon->node, smon->target),
+				  smon->offset + 5 * REG_SZ, 1 * REG_SZ,
+				  &smon->regs.count1);
+		} else if (smon->type == MEM_SMON) {
+			/* write configuration, but do not change count reg */
+			memcpy32_toio((uint32_t *)smon->addr + smon->offset,
+				(uint32_t *)&smon->regs, 2);
+
+			/* clear this events counter register */
+			memcpy32_toio((uint32_t *)smon->addr + smon->offset + 5,
+				(uint32_t *)&smon->regs.count1, 1);
+		}
+
+	} else {
+		pr_warn("smon_start, no counter availible\n");
+		return -ENOCOUNTER;
+	}
+
+	return 0;
+}
diff --git a/arch/arm/mach-axxia/smon.h b/arch/arm/mach-axxia/smon.h
new file mode 100644
index 0000000..d3573cd
--- /dev/null
+++ b/arch/arm/mach-axxia/smon.h
@@ -0,0 +1,72 @@
+/*
+ * Helper module for board specific I2C bus registration
+ *
+ * Copyright (C) 2014 LSI Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#ifndef __ASM__ARCH_AXXIA_SMON_H
+#define __ASM__ARCH_AXXIA_SMON_H
+
+#include <linux/kernel.h>
+
+struct smon_reg_s {
+	uint32_t control;
+	uint8_t event0;
+	uint8_t event1;
+	uint16_t reserved;
+	uint32_t compare0;
+	uint32_t compare1;
+	uint32_t count0;
+	uint32_t count1;
+	uint32_t time;
+	uint32_t maxtime;
+};
+
+struct smon_s {
+	struct smon_reg_s regs;
+	uint32_t type; /* NCP_SMON or MEM_SMON */
+	uint32_t *addr; /* MEM_SMON */
+	uint32_t node; /* NCP_SMON */
+	uint32_t target; /* " */
+	uint32_t offset;
+	uint32_t lastread[2];
+	uint8_t assigned[2];
+	uint8_t events[2];
+};
+
+#define REG_SZ 4
+
+#define MEM_SMON 0
+#define NCP_SMON 1
+
+#define UNASSIGNED 0
+#define ASSIGNED 1
+
+#define ENOCOUNTER 1
+#define ENOEVENT 2
+
+void smon_init_ncp(struct smon_s *smon, uint32_t node, uint32_t target,
+	uint32_t offset);
+void smon_init_mem(struct smon_s *smon, uint64_t addr, uint32_t offset);
+void smon_stop_if_unassigned(struct smon_s *smon);
+uint32_t smon_allocate(struct smon_s *smon, uint8_t event);
+uint32_t smon_deallocate(struct smon_s *smon, uint8_t event);
+uint32_t smon_event_active(struct smon_s *smon, uint8_t event);
+uint32_t smon_read(struct smon_s *smon, uint8_t event);
+uint32_t smon_start(struct smon_s *smon, uint8_t event);
+
+#endif /* __ASM__ARCH_AXXIA_SMON_H */
diff --git a/arch/arm/mach-axxia/ssp-gpio.c b/arch/arm/mach-axxia/ssp-gpio.c
new file mode 100644
index 0000000..c718edd
--- /dev/null
+++ b/arch/arm/mach-axxia/ssp-gpio.c
@@ -0,0 +1,132 @@
+/*
+ * GPIO interface for SSP chip select pins.
+ *
+ * Copyright (C) 2013 LSI Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+struct gpio_dev {
+	void __iomem    *regs;
+	struct gpio_chip gpio_chip;
+};
+
+static int
+ssp_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	struct gpio_dev *priv = dev_get_drvdata(chip->dev);
+	u32 tmp = readl(priv->regs + 0x30);
+	return !!(tmp & (1<<offset));
+}
+
+static int
+ssp_gpio_direction_out(struct gpio_chip *chip, unsigned offset, int value)
+{
+	/* Pins are only outputs */
+	return 0;
+}
+
+static void
+ssp_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct gpio_dev *priv = dev_get_drvdata(chip->dev);
+	u32 tmp = readl(priv->regs + 0x30);
+	if (value)
+		tmp |= (1<<offset);
+	else
+		tmp &= ~(1<<offset);
+	writel(tmp, priv->regs + 0x30);
+}
+
+static int
+ssp_gpio_probe(struct platform_device *pdev)
+{
+	struct gpio_dev *priv;
+	struct resource *io;
+	struct gpio_chip *chip;
+	int ret;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, priv);
+
+	io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!io)
+		return -EINVAL;
+
+	priv->regs = devm_ioremap_nocache(&pdev->dev,
+					   io->start, resource_size(io));
+	if (!priv->regs)
+		return -ENXIO;
+
+	chip = &priv->gpio_chip;
+	chip->dev = &pdev->dev;
+#ifdef CONFIG_OF_GPIO
+	chip->of_node = pdev->dev.of_node;
+#endif
+	chip->get = ssp_gpio_get;
+	chip->direction_output = ssp_gpio_direction_out;
+	chip->set = ssp_gpio_set;
+	chip->label = "ssp-gpio";
+	chip->owner = THIS_MODULE;
+	chip->base = -1;
+	chip->ngpio = 5;
+
+	/* Deassert all */
+	writel(0x1f, priv->regs + 0x30);
+
+	ret = gpiochip_add(chip);
+	if (ret < 0)
+		dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret);
+
+	return ret;
+}
+
+static int
+ssp_gpio_remove(struct platform_device *pdev)
+{
+	struct gpio_dev *priv = dev_get_drvdata(&pdev->dev);
+	return gpiochip_remove(&priv->gpio_chip);
+}
+
+static const struct of_device_id ssp_gpio_id_table[] = {
+	{ .compatible = "lsi,ssp-gpio" },
+	{}
+};
+MODULE_DEVICE_TABLE(platform, ssp_gpio_id_table);
+
+static struct platform_driver ssp_gpio_driver = {
+	.driver = {
+		.name	= "ssp-gpio",
+		.owner	= THIS_MODULE,
+		.of_match_table = ssp_gpio_id_table
+	},
+	.probe		= ssp_gpio_probe,
+	.remove		= ssp_gpio_remove,
+};
+
+module_platform_driver(ssp_gpio_driver);
+
+MODULE_AUTHOR("LSI Corporation");
+MODULE_DESCRIPTION("GPIO interface for SSP chip selects");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-axxia/timers.c b/arch/arm/mach-axxia/timers.c
new file mode 100644
index 0000000..02319e0
--- /dev/null
+++ b/arch/arm/mach-axxia/timers.c
@@ -0,0 +1,224 @@
+/*
+ *  arch/arm/mach-axxia/timers.c
+ *
+ *  Copyright (C) 2012 LSI
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*
+  This is based on arch/arm/common/timer-sp.c.
+
+  The timers used are SP804s, but, there are 8 timers instead of 2,
+  AND the ID registers are missing.
+ */
+
+#include <linux/clk.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/sched_clock.h>
+#include <asm/hardware/arm_timer.h>
+
+struct axxia_timer {
+	struct clock_event_device  dev;
+	struct irqaction           irqaction;
+	void __iomem               *base;
+	unsigned long              reload;
+};
+
+#define timer_to_clock_event(_x) container_of(_x, struct axxia_timer, dev)
+
+static void __iomem *sched_clock_base;
+
+static u32 sp804_read(void)
+{
+	return ~readl_relaxed(sched_clock_base + TIMER_VALUE);
+}
+
+/**
+ * axxia_timer_set_mode
+ */
+static void
+axxia_timer_set_mode(enum clock_event_mode mode, struct clock_event_device *evt)
+{
+	struct axxia_timer *timer = timer_to_clock_event(evt);
+	unsigned long ctrl = TIMER_CTRL_32BIT | TIMER_CTRL_IE;
+
+	pr_info("axxia_timer_set_mode: CPU#%d set mode %d on timer %s\n",
+		smp_processor_id(), mode, timer->dev.name);
+
+	writel(ctrl, timer->base + TIMER_CTRL);
+
+	switch (mode) {
+	case CLOCK_EVT_MODE_PERIODIC:
+		writel(timer->reload, timer->base + TIMER_LOAD);
+		ctrl |= TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE;
+		break;
+
+	case CLOCK_EVT_MODE_ONESHOT:
+		/* period set, and timer enabled in 'next_event' hook */
+		ctrl |= TIMER_CTRL_ONESHOT;
+		break;
+
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+	default:
+		break;
+	}
+
+	writel(ctrl, timer->base + TIMER_CTRL);
+}
+
+/**
+ * axxia_timer_set_next_event
+ *
+ */
+static int
+axxia_timer_set_next_event(unsigned long next, struct clock_event_device *evt)
+{
+	struct axxia_timer *timer = timer_to_clock_event(evt);
+	unsigned long ctrl;
+
+	ctrl = readl(timer->base + TIMER_CTRL);
+	writel(next, timer->base + TIMER_LOAD);
+	writel(ctrl | TIMER_CTRL_ENABLE, timer->base + TIMER_CTRL);
+
+	return 0;
+}
+
+
+/**
+ * axxia_timer_handler - IRQ handler for the timer.
+ *
+ */
+static irqreturn_t
+axxia_timer_handler(int irq, void *dev_id)
+{
+	struct axxia_timer *timer = (struct axxia_timer *)dev_id;
+
+	/* clear the interrupt */
+	writel(1, timer->base + TIMER_INTCLR);
+
+	timer->dev.event_handler(&timer->dev);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * axxia_timer_get_clock_rate
+ *
+ */
+static long __init
+axxia_timer_get_clock_rate(const char *name)
+{
+	struct clk *clk;
+	long rate;
+	int err;
+
+	clk = clk_get_sys("sp804", name);
+	if (IS_ERR(clk)) {
+		pr_err("sp804: %s clock not found: %d\n", name,
+			(int)PTR_ERR(clk));
+		return PTR_ERR(clk);
+	}
+
+	err = clk_prepare(clk);
+	if (err) {
+		pr_err("sp804: %s clock failed to prepare: %d\n", name, err);
+		clk_put(clk);
+		return err;
+	}
+
+	err = clk_enable(clk);
+	if (err) {
+		pr_err("sp804: %s clock failed to enable: %d\n", name, err);
+		clk_unprepare(clk);
+		clk_put(clk);
+		return err;
+	}
+
+	rate = clk_get_rate(clk);
+	if (rate < 0) {
+		pr_err("sp804: %s clock failed to get rate: %ld\n", name, rate);
+		clk_disable(clk);
+		clk_unprepare(clk);
+		clk_put(clk);
+	}
+
+	return rate;
+}
+
+void __init
+axxia_timer_clocksource_init(void __iomem *base, const char *name)
+{
+	long rate;
+
+	rate = axxia_timer_get_clock_rate(name);
+	if (WARN_ON(rate < 0))
+		return;
+
+	/* Setup timer 0 as free-running clocksource */
+	writel(0, base + TIMER_CTRL);
+	writel(0xffffffff, base + TIMER_LOAD);
+	writel(0xffffffff, base + TIMER_VALUE);
+	writel(TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC,
+		base + TIMER_CTRL);
+
+	clocksource_mmio_init(base + TIMER_VALUE, name,
+		rate, 200, 32, clocksource_mmio_readl_down);
+
+	sched_clock_base = base;
+	setup_sched_clock(sp804_read, 32, rate);
+}
+
+void __init
+axxia_timer_clockevents_init(void __iomem *base,
+			     unsigned int irq, const char *name)
+{
+	struct axxia_timer *evt;
+	long               rate;
+
+	rate = axxia_timer_get_clock_rate(name);
+	if (WARN_ON(rate < 0))
+		return;
+
+	evt = kzalloc(sizeof(*evt), GFP_KERNEL);
+	if (evt == NULL)
+		return;
+
+	evt->dev.features       = CLOCK_EVT_FEAT_PERIODIC |
+				  CLOCK_EVT_FEAT_ONESHOT,
+	evt->dev.set_mode	= axxia_timer_set_mode,
+	evt->dev.set_next_event	= axxia_timer_set_next_event,
+	evt->dev.rating		= 400,
+	evt->dev.name           = name;
+	evt->dev.irq            = irq;
+	evt->dev.cpumask	= cpu_all_mask,
+	evt->base               = base;
+	evt->reload             = DIV_ROUND_CLOSEST(rate, HZ);
+
+	evt->irqaction.name     = name;
+	evt->irqaction.flags    = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL;
+	evt->irqaction.handler	= axxia_timer_handler;
+	evt->irqaction.dev_id	= evt;
+
+	setup_irq(irq, &evt->irqaction);
+	clockevents_config_and_register(&evt->dev, rate, 0xf, 0xffffffff);
+}
-- 
1.7.9.5



More information about the linux-yocto mailing list