[linux-yocto] [PATCH 13/87] arch/arm/mach-axxia: adding mach-axxia support
Paul Butler
butler.paul at gmail.com
Mon May 27 09:55:44 PDT 2013
Signed-off-by: Paul Butler <paul.butler at windriver.com>
---
arch/arm/mach-axxia/Kconfig | 20 +
arch/arm/mach-axxia/Makefile | 10 +
arch/arm/mach-axxia/Makefile.boot | 5 +
arch/arm/mach-axxia/axxia.c | 232 +++++++
arch/arm/mach-axxia/axxia.h | 5 +
arch/arm/mach-axxia/clock.c | 79 +++
arch/arm/mach-axxia/headsmp.S | 43 ++
arch/arm/mach-axxia/hotplug.c | 124 ++++
arch/arm/mach-axxia/include/mach/debug-macro.S | 35 +
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 | 1 +
arch/arm/mach-axxia/include/mach/io.h | 39 ++
arch/arm/mach-axxia/include/mach/irqs.h | 4 +
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 | 908 +++++++++++++++++++++++++
arch/arm/mach-axxia/pci.h | 1 +
arch/arm/mach-axxia/platsmp.c | 174 +++++
arch/arm/mach-axxia/timers.c | 225 ++++++
23 files changed, 2111 insertions(+)
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.c
create mode 100644 arch/arm/mach-axxia/axxia.h
create mode 100644 arch/arm/mach-axxia/clock.c
create mode 100644 arch/arm/mach-axxia/headsmp.S
create mode 100644 arch/arm/mach-axxia/hotplug.c
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/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/timers.c
diff --git a/arch/arm/mach-axxia/Kconfig b/arch/arm/mach-axxia/Kconfig
new file mode 100644
index 0000000..b309d99
--- /dev/null
+++ b/arch/arm/mach-axxia/Kconfig
@@ -0,0 +1,20 @@
+menu "Axxia platform type"
+ depends on ARCH_AXXIA
+
+config ARCH_AXXIA_DT
+ bool "Device Tree support for LSI Axxia platforms"
+ select ARM_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..6e157dd
--- /dev/null
+++ b/arch/arm/mach-axxia/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the linux kernel.
+#
+obj-y := axxia.o
+obj-y += clock.o
+obj-y += io.o
+obj-y += timers.o
+obj-y += pci.o
+obj-$(CONFIG_SMP) += platsmp.o headsmp.o
+obj-$(CONFIG_HOTPLUG_CPU) += hotplug.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.c b/arch/arm/mach-axxia/axxia.c
new file mode 100644
index 0000000..623957e
--- /dev/null
+++ b/arch/arm/mach-axxia/axxia.c
@@ -0,0 +1,232 @@
+/*
+ * 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/smsc911x.h>
+#include <linux/spi/spi.h>
+#include <linux/clkdev.h>
+#ifdef CONFIG_ARM_ARCH_TIMER
+#include <asm/arch_timer.h>
+#endif
+#include <asm/mach-types.h>
+#include <asm/sizes.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/time.h>
+#include <asm/hardware/cache-l2x0.h>
+#include <asm/hardware/gic.h>
+#include <mach/timers.h>
+#include "axxia.h"
+#include "pci.h"
+
+static const char *axxia_dt_match[] __initconst = {
+ "lsi,axm5516", /* AXM5516 */
+ NULL
+};
+
+
+static void __iomem *ssp_base;
+
+void __init axxia_dt_map_io(void)
+{
+}
+
+void __init axxia_dt_init_early(void)
+{
+}
+
+static struct of_device_id axxia_irq_match[] __initdata = {
+ {
+ .compatible = "arm,cortex-a15-gic",
+ .data = gic_of_init,
+ },
+ { }
+};
+
+static void __init axxia_dt_init_irq(void)
+{
+ of_irq_init(axxia_irq_match);
+}
+
+void __init axxia_dt_timer_init(void)
+{
+ const char *path;
+ struct device_node *node;
+ void __iomem *base;
+
+ axxia_init_clocks();
+
+#ifdef CONFIG_ARM_ARCH_TIMER
+ {
+ int err = arch_timer_of_register();
+ if (err == 0)
+ err = arch_timer_sched_clock_init();
+ WARN_ON(err);
+ }
+#endif
+
+ if (of_property_read_string(of_aliases, "timer", &path)) {
+ WARN_ON(1);
+ return;
+ }
+
+ node = of_find_node_by_path(path);
+ if (WARN_ON(node == NULL))
+ return;
+
+ base = of_iomap(node, 0);
+ if (WARN_ON(base == NULL))
+ return;
+
+ __sp804_clocksource_and_sched_clock_init(base, "axxia-timer0", 0);
+ sp804_clockevents_init(base + 0x20, irq_of_parse_and_map(node, 1),
+ "axxia-timer1");
+}
+
+
+static struct sys_timer axxia_dt_timer = {
+ .init = axxia_dt_timer_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
+};
+
+struct pl061_platform_data gpio0_plat_data = {
+ .gpio_base = 0,
+ .irq_base = 0,
+ .directions = 0, /* startup directions, 1: out, 0: in */
+ .values = 0 /* startup values */
+};
+
+struct pl061_platform_data gpio1_plat_data = {
+ .gpio_base = 8,
+ .irq_base = 0,
+ .directions = 0, /* startup directions, 1: out, 0: in */
+ .values = 0 /* startup values */
+};
+
+static struct pl022_ssp_controller ssp_plat_data = {
+ .bus_id = 0,
+ .num_chipselect = 5,
+ .enable_dma = 0
+};
+
+static struct of_dev_auxdata axxia_auxdata_lookup[] __initdata = {
+ OF_DEV_AUXDATA("arm,primecell", 0x20101E0000ULL,
+ "mmci", &mmc_plat_data),
+ OF_DEV_AUXDATA("arm,primecell", 0x2010088000ULL,
+ "ssp", &ssp_plat_data),
+ OF_DEV_AUXDATA("arm,primecell", 0x2010092000ULL,
+ "gpio0", &gpio0_plat_data),
+ OF_DEV_AUXDATA("arm,primecell", 0x2010093000ULL,
+ "gpio1", &gpio1_plat_data),
+ {}
+};
+
+struct pl022_config_chip spi_eeprom = {
+ .iface = SSP_INTERFACE_MOTOROLA_SPI,
+ .clk_freq = {
+ .cpsdvsr = 0, /* value from 2 to 254 (even only!) */
+ .scr = 0, /* value from 0 to 255 */
+ },
+ .com_mode = POLLING_TRANSFER,
+};
+
+static struct spi_board_info spi_devs[] __initdata = {
+ {
+ .modalias = "spidev",
+ .controller_data = &spi_eeprom,
+ .bus_num = 0,
+ .chip_select = 0,
+ .max_speed_hz = 12000000,
+ .mode = SPI_MODE_0,
+ },
+ {
+ .modalias = "spidev",
+ .controller_data = &spi_eeprom,
+ .bus_num = 0,
+ .chip_select = 1,
+ .max_speed_hz = 12000000,
+ .mode = SPI_MODE_0,
+ },
+ {
+ .modalias = "spidev",
+ .controller_data = &spi_eeprom,
+ .bus_num = 0,
+ .chip_select = 2,
+ .max_speed_hz = 12000000,
+ .mode = SPI_MODE_0,
+ },
+};
+
+void __init axxia_dt_init(void)
+{
+ l2x0_of_init(0x00400000, 0xfe0fffff);
+ of_platform_populate(NULL, of_default_bus_match_table,
+ axxia_auxdata_lookup, NULL);
+ pm_power_off = NULL; /* TBD */
+
+ spi_register_board_info(spi_devs, ARRAY_SIZE(spi_devs));
+
+ /*
+ * Setup PL022 to handle chip-select signal automatically
+ */
+ ssp_base =
+ of_iomap(of_find_compatible_node(NULL, NULL, "arm,pl022"), 0);
+ if (!WARN_ON(ssp_base == NULL)) {
+ printk(KERN_INFO "SSPCSR = %08x\n", readl(ssp_base+0x30));
+ writel((0x1F << 8) | (0<<5) | (0x1F<<0), ssp_base+0x30);
+ }
+
+ axxia_pcie_init();
+}
+
+static void axxia_restart(char str, const char *cmd)
+{
+ /* TBD */
+}
+
+DT_MACHINE_START(AXXIA_DT, "LSI Axxia")
+ .dt_compat = axxia_dt_match,
+ .map_io = axxia_dt_map_io,
+ .init_early = axxia_dt_init_early,
+ .init_irq = axxia_dt_init_irq,
+ .timer = &axxia_dt_timer,
+ .init_machine = axxia_dt_init,
+ .handle_irq = gic_handle_irq,
+ .restart = axxia_restart,
+MACHINE_END
diff --git a/arch/arm/mach-axxia/axxia.h b/arch/arm/mach-axxia/axxia.h
new file mode 100644
index 0000000..1c8e800
--- /dev/null
+++ b/arch/arm/mach-axxia/axxia.h
@@ -0,0 +1,5 @@
+#ifndef _AXXIA_H
+
+void axxia_init_clocks(void);
+
+#endif
diff --git a/arch/arm/mach-axxia/clock.c b/arch/arm/mach-axxia/clock.c
new file mode 100644
index 0000000..619ba5d
--- /dev/null
+++ b/arch/arm/mach-axxia/clock.c
@@ -0,0 +1,79 @@
+/*
+ * 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>
+
+#define AXXIA_CPU_CLOCK 1400000000
+#define AXXIA_SYS_CLOCK 450000000
+#define AXXIA_DDR_CLOCK 1866000000
+
+#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)
+
+
+void __init
+axxia_init_clocks(void)
+{
+ struct clk *clk;
+ int i;
+
+ /* APB clock dummy */
+ clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL,
+ CLK_IS_ROOT, AXXIA_SYS_CLOCK/2);
+ clk_register_clkdev(clk, "apb_pclk", NULL);
+
+ /* CPU core clock (1400MHz) from CPU_PLL */
+ clk = clk_register_fixed_rate(NULL, "clk_cpu", NULL,
+ CLK_IS_ROOT, AXXIA_CPU_CLOCK);
+
+ /* APB and System AXI clock from CPU_PLL */
+ clk = clk_register_fixed_rate(NULL, "clk_pclk", NULL,
+ CLK_IS_ROOT, AXXIA_CPU_CLOCK/9);
+
+ /* DDR3 (interface 1) clock from SMEM1_PLL */
+ clk = clk_register_fixed_rate(NULL, "clk_smem1_2x", NULL,
+ CLK_IS_ROOT, AXXIA_DDR_CLOCK);
+
+ /* AXIS slow peripheral clock from SMEM1_PLL. */
+ clk = clk_register_fixed_rate(NULL, "clk_per", NULL,
+ CLK_IS_ROOT, 24000000);
+ /* PL011 UART0 */
+ clk_register_clkdev(clk, NULL, "2010080000.uart");
+ /* PL011 UART1 */
+ clk_register_clkdev(clk, NULL, "2010081000.uart");
+ /* PL011 UART2 */
+ clk_register_clkdev(clk, NULL, "2010082000.uart");
+ /* PL011 UART3 */
+ clk_register_clkdev(clk, NULL, "2010083000.uart");
+ /* PL022 SSP */
+ clk_register_clkdev(clk, NULL, "ssp");
+
+ /* Timers 1MHz clock */
+ clk = clk_register_fixed_rate(NULL, "clk_1mhz", NULL,
+ CLK_IS_ROOT, 1000000);
+ /* SP804 timers */
+ clk_register_clkdev(clk, NULL, "sp804");
+ for (i = 0; i < 8; i++)
+ clk_register_clkdev(clk, NULL, "axxia-timer%d", i);
+
+ /* Dummy MMC clk */
+ clk = clk_register_fixed_rate(NULL, "clk_mmci", NULL,
+ CLK_IS_ROOT, 25000000);
+ /* PL180 MMCI */
+ clk_register_clkdev(clk, NULL, "mmci");
+}
diff --git a/arch/arm/mach-axxia/headsmp.S b/arch/arm/mach-axxia/headsmp.S
new file mode 100644
index 0000000..bc7414b
--- /dev/null
+++ b/arch/arm/mach-axxia/headsmp.S
@@ -0,0 +1,43 @@
+/*
+ * 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
+ and r0, r0, #15
+ adr r4, 1f
+ ldmia r4, {r5, r6}
+ sub r4, r4, r5
+ add r6, r6, r4
+pen: ldr r7, [r6]
+ 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/hotplug.c b/arch/arm/mach-axxia/hotplug.c
new file mode 100644
index 0000000..c504a72
--- /dev/null
+++ b/arch/arm/mach-axxia/hotplug.c
@@ -0,0 +1,124 @@
+/*
+ * linux/arch/arm/mach-realview/hotplug.c
+ *
+ * Copyright (C) 2002 ARM Ltd.
+ * 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/kernel.h>
+#include <linux/errno.h>
+#include <linux/smp.h>
+
+#include <asm/cacheflush.h>
+#include <asm/smp_plat.h>
+#include <asm/cp15.h>
+
+extern volatile int pen_release;
+
+static inline void cpu_enter_lowpower(void)
+{
+ unsigned int v;
+
+ flush_cache_all();
+ asm volatile(
+ "mcr p15, 0, %1, c7, c5, 0\n"
+ " mcr p15, 0, %1, c7, c10, 4\n"
+ /*
+ * Turn off coherency
+ */
+ " mrc p15, 0, %0, c1, c0, 1\n"
+ " bic %0, %0, %3\n"
+ " mcr p15, 0, %0, c1, c0, 1\n"
+ " mrc p15, 0, %0, c1, c0, 0\n"
+ " bic %0, %0, %2\n"
+ " mcr p15, 0, %0, c1, c0, 0\n"
+ : "=&r" (v)
+ : "r" (0), "Ir" (CR_C), "Ir" (0x40)
+ : "cc");
+}
+
+static inline void cpu_leave_lowpower(void)
+{
+ unsigned int v;
+
+ asm volatile(
+ "mrc p15, 0, %0, c1, c0, 0\n"
+ " orr %0, %0, %1\n"
+ " mcr p15, 0, %0, c1, c0, 0\n"
+ " mrc p15, 0, %0, c1, c0, 1\n"
+ " orr %0, %0, %2\n"
+ " mcr p15, 0, %0, c1, c0, 1\n"
+ : "=&r" (v)
+ : "Ir" (CR_C), "Ir" (0x40)
+ : "cc");
+}
+
+static inline void platform_do_lowpower(unsigned int cpu, int *spurious)
+{
+ /*
+ * there is no power-control hardware on this platform, so all
+ * we can do is put the core into WFI; this is safe as the calling
+ * code will have already disabled interrupts
+ */
+ for (;;) {
+ wfi();
+
+ if (pen_release == cpu_logical_map(cpu)) {
+ /*
+ * OK, proper wakeup, we're done
+ */
+ break;
+ }
+
+ /*
+ * Getting here, means that we have come out of WFI without
+ * having been woken up - this shouldn't happen
+ *
+ * Just note it happening - when we're woken, we can report
+ * its occurrence.
+ */
+ (*spurious)++;
+ }
+}
+
+int platform_cpu_kill(unsigned int cpu)
+{
+ return 1;
+}
+
+/*
+ * platform-specific code to shutdown a CPU
+ *
+ * Called with IRQs disabled
+ */
+void platform_cpu_die(unsigned int cpu)
+{
+ int spurious = 0;
+
+ /*
+ * we're ready for shutdown now, so do it
+ */
+ cpu_enter_lowpower();
+ platform_do_lowpower(cpu, &spurious);
+
+ /*
+ * bring this CPU back into the world of cache
+ * coherency, and then restore interrupts
+ */
+ cpu_leave_lowpower();
+
+ if (spurious)
+ pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious);
+}
+
+int platform_cpu_disable(unsigned int cpu)
+{
+ /*
+ * we don't allow CPU 0 to be shutdown (it is still too special
+ * e.g. clock tick interrupts)
+ */
+ return cpu == 0 ? -EPERM : 0;
+}
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..f25a024
--- /dev/null
+++ b/arch/arm/mach-axxia/include/mach/debug-macro.S
@@ -0,0 +1,35 @@
+/* arch/arm/mach-realview/include/mach/debug-macro.S
+ *
+ * Debugging macro include header
+ *
+ * Copyright (C) 1994-1999 Russell King
+ * Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
+ *
+ * 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.
+ */
+
+#ifdef CONFIG_DEBUG_VEXPRESS_CA9X4_UART
+#define DEBUG_LL_PHYS_BASE 0x10000000
+#define DEBUG_LL_UART_OFFSET 0x00009000
+#endif
+
+#ifdef CONFIG_DEBUG_VEXPRESS_RS1_UART
+#define DEBUG_LL_PHYS_BASE 0x1c000000
+#define DEBUG_LL_UART_OFFSET 0x00090000
+#endif
+
+#define DEBUG_LL_VIRT_BASE 0xf8000000
+
+#ifndef DEBUG_LL_UART_OFFSET
+#error "Unknown vexpress UART offset"
+#endif
+
+ .macro addruart,rp,rv,tmp
+ mov \rp, #DEBUG_LL_UART_OFFSET
+ orr \rv, \rp, #DEBUG_LL_VIRT_BASE
+ orr \rp, \rp, #DEBUG_LL_PHYS_BASE
+ .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..40a8c17
--- /dev/null
+++ b/arch/arm/mach-axxia/include/mach/hardware.h
@@ -0,0 +1 @@
+/* empty */
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..40924cd
--- /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 __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..4b10ee7
--- /dev/null
+++ b/arch/arm/mach-axxia/include/mach/irqs.h
@@ -0,0 +1,4 @@
+#define IRQ_LOCALTIMER 29
+#define IRQ_LOCALWDOG 30
+
+#define NR_IRQS 256
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..2fff1b7
--- /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, 0, 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..5ba4b80
--- /dev/null
+++ b/arch/arm/mach-axxia/pci.c
@@ -0,0 +1,908 @@
+/*
+ * PCI / PCI-X / PCI-Express support for ARM A15 Cortex parts
+ */
+
+#undef DEBUG
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/bootmem.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 <asm/io.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 <linux/msi.h>
+
+#define AXXIA55xx_NUM_MSI_IRQS 256
+static DECLARE_BITMAP(msi_irq_in_use, AXXIA55xx_NUM_MSI_IRQS);
+
+
+#undef PRINT_CONFIG_ACCESSES
+/*#define PRINT_CONFIG_ACCESSES*/
+
+static u32 last_mpage;
+static u32 last_port;
+
+#define U64_TO_U32_LOW(val) ((u32)((val) & 0x00000000ffffffffULL))
+#define U64_TO_U32_HIGH(val) ((u32)((val) >> 32))
+
+#define ACPX1_PCIE_MPAGE_UPPER(n) (0x1010 + (n * 8))
+#define ACPX1_PCIE_MPAGE_LOWER(n) (0x1014 + (n * 8))
+
+struct axxia_pciex_port {
+ unsigned int index;
+ u8 root_bus_nr;
+ bool link_up;
+ void __iomem *cfg_addr;
+ void __iomem *cfg_data;
+ u64 pci_addr;
+ int endpoint;
+ struct device_node *node;
+ struct resource utl_regs;
+ struct resource cfg_space;
+ struct resource res;
+};
+
+static struct axxia_pciex_port *axxia_pciex_ports;
+static unsigned int axxia_pciex_port_count = 3;
+
+static void axxia_probe_pciex_bridge(struct device_node *np);
+
+static void __init
+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
+ */
+ printk(KERN_INFO
+ "PCI: Setting 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;
+
+ return ;
+}
+
+DECLARE_PCI_FIXUP_HEADER(0x1000, 0x5101, fixup_axxia_pci_bridge);
+DECLARE_PCI_FIXUP_HEADER(0x1000, 0x5108, 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;
+}
+
+/* Get 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)
+{
+ unsigned mpage;
+ u32 addr;
+ int dev, fn;
+ int cfg_type;
+ int relbus;
+
+ if (bus->number == port->root_bus_nr) {
+ return port->cfg_addr;
+ } else {
+ relbus = bus->number - (port->root_bus_nr + 1);
+ dev = ((PCI_SLOT(devfn) & 0xf8) >> 3);
+ fn = PCI_FUNC(devfn) & 0x7;
+
+ if (dev > 31)
+ return NULL;
+
+ /* Primary bus */
+ if (relbus && (bus->number != port->root_bus_nr))
+ cfg_type = 1;
+ else
+ cfg_type = 0;
+
+ /* build the mpage register */
+ mpage = (bus->number << 11) | (dev << 6) | (cfg_type << 5);
+ mpage |= 0x10; /* enable MPAGE for configuration access */
+ mpage |= (fn << 19);
+
+ if ((mpage != last_mpage) || (port->index != last_port)) {
+ addr = ((u32)port->cfg_addr) +
+ ACPX1_PCIE_MPAGE_UPPER(7);
+ writel(0x0, (u32 *) addr);
+ addr = ((u32)port->cfg_addr) +
+ ACPX1_PCIE_MPAGE_LOWER(7);
+ writel(mpage, (u32 *) addr);
+ last_mpage = mpage;
+ last_port = port->index;
+ }
+ return port->cfg_data;
+ }
+}
+
+/* Read the 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 *)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
+ printk(KERN_INFO
+ "acp_read_config for PEI%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 the 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 *)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
+ printk(KERN_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 *)(bus_addr));
+ break;
+ case 2:
+ writew(val, (u16 *)(bus_addr));
+ break;
+ default:
+ writel(val, (u32 *)(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,
+};
+
+/* ACP PCIe ISR to handle Legacy Interrupts */
+static irqreturn_t
+acp_pcie_isr(int irq, void *arg)
+{
+ u32 intr_status;
+ u32 msg_fifo_stat;
+ u32 msg_fifo_info;
+ u8 externalPciIntr = 0;
+ struct axxia_pciex_port *port = (struct axxia_pciex_port *)arg;
+ void __iomem *mbase = (void __iomem *)port->cfg_addr;
+
+ /* read the PEI interrupt status register */
+ intr_status = readl(mbase+0x10c0);
+
+ /* check if this is a PCIe message from an external device */
+ if (intr_status & 0x00000010) {
+ externalPciIntr = 1;
+ msg_fifo_stat = readl(mbase+0x10b4);
+
+ /* loop until the message fifo is empty */
+ while ((msg_fifo_stat & 0x01) == 0) {
+ u8 bus, dev, fn;
+ u8 msg_type;
+ msg_fifo_info = readl(mbase+0x10b0);
+ bus = (msg_fifo_info >> 16) & 0xff;
+ dev = (msg_fifo_info >> 11) & 0x1f;
+ fn = (msg_fifo_info >> 8) & 0x07;
+ msg_type = msg_fifo_info & 0xff;
+
+ /* print out the BDF and message type.
+ * We ignore the common message types.
+ */
+ switch (msg_type) {
+ case 0x20: /* assert_INTa */
+ printk(KERN_INFO
+ "PEI%d --> INTa asserted\n",
+ port->index);
+ break;
+ case 0x21: /* assert_INTb */
+ printk(KERN_INFO
+ "PEI%d --> INTb asserted\n",
+ port->index);
+ break;
+ case 0x22: /* assert_INTc */
+ printk(KERN_INFO
+ "PEI%d --> INTc asserted\n",
+ port->index);
+ break;
+ case 0x23: /* assert_INTd */
+ printk(KERN_INFO
+ "PEI%d --> INTd asserted\n",
+ port->index);
+ break;
+ case 0x24: /* de-assert_INTa */
+ printk(KERN_INFO
+ "PEI%d --> INTa de-asserted\n",
+ port->index);
+ break;
+ case 0x25: /* de-assert_INTb */
+ printk(KERN_INFO
+ "PEI%d --> INTb de-asserted\n",
+ port->index);
+ break;
+ case 0x26: /* de-assert_INTc */
+ printk(KERN_INFO
+ "PEI%d --> INTc de-asserted\n",
+ port->index);
+ break;
+ case 0x27: /* de-assert_INTd */
+ printk(KERN_INFO
+ "PEI%d --> INTd de-asserted\n",
+ port->index);
+ break;
+ default:
+ printk(KERN_INFO
+ "BDF %02x:%02x.%x sent msgtype 0x%02x\n",
+ bus, dev, fn, msg_type);
+ break;
+ }
+
+ /* re-read fifo status */
+ msg_fifo_stat = readl(mbase+0x10b4);
+ }
+ } else {
+ /*
+ Ignore the common interrupts, still need to figure
+ out what they all mean.
+ */
+ if (intr_status & 0xf3ffffab) {
+ u32 t2a_err_stat;
+ u32 t2a_other_err_stat;
+ u32 int_enb;
+ u32 linkStatus;
+ u32 offset;
+
+ printk(KERN_ERR
+ "ACP_PCIE_ISR: got PEI%d error interrupt 0x%08x\n",
+ intr_status, port->index);
+
+ linkStatus = readl(mbase+0x117c);
+ printk(KERN_ERR
+ "link_status (0x117c) = 0x%08x\n",
+ linkStatus);
+
+ if (intr_status & 0x00020000) {
+ t2a_err_stat = readl(mbase+0x1170);
+ printk(KERN_ERR
+ "t2a_fn_indp_err_stat = 0x%08x\n",
+ t2a_err_stat);
+ int_enb = readl(mbase+0x10c4);
+ int_enb &= 0xfffdffff;
+ writel(int_enb, mbase + 0x10c4);
+ }
+
+ if (intr_status & 0x00040000) {
+ t2a_other_err_stat = readl(mbase+0x1174);
+ printk(KERN_ERR "t2a_fn_indp_other_err_stat = 0x%08x\n",
+ t2a_other_err_stat);
+ int_enb = readl(mbase+0x10c4);
+ int_enb &= 0xfffbffff;
+ writel(int_enb, mbase + 0x10c4);
+ }
+
+ if (intr_status & 0x00000800) {
+ printk(KERN_INFO
+ "pci_config = 0x%08x\n",
+ readl(mbase + 0x1000));
+ printk(KERN_INFO
+ "pci_status = 0x%08x\n",
+ readl(mbase + 0x1004));
+
+ int_enb = readl(mbase + 0x10c4);
+ int_enb &= 0xfffff7ff;
+ writel(int_enb, mbase + 0x10c4);
+ }
+
+ /*
+ * dump all the potentially interesting PEI registers
+ */
+ for (offset = 0x114c; offset <= 0x1180; offset += 4) {
+ printk(KERN_INFO
+ " 0x%04x : 0x%08x\n",
+ offset, readl(mbase + offset));
+ }
+ }
+ }
+
+ /*
+ * 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 MPIC won't be
+ * re-enabled until all external handlers have been called.
+ */
+ writel(intr_status, mbase + 0x10c0);
+
+ return externalPciIntr ? IRQ_NONE : IRQ_HANDLED;
+}
+
+/* ACP PCIe ISR to handle MSI Interrupts */
+static irqreturn_t
+acp_pcie_MSI_isr(int irq, void *arg)
+{
+ u32 intr_status;
+ u32 msg_fifo_stat;
+ u32 msg_fifo_info;
+ u8 msiIntr = 0, bit = 0;
+ struct axxia_pciex_port *port = (struct axxia_pciex_port *)arg;
+ void __iomem *mbase = (void __iomem *)port->cfg_addr;
+ u32 statusReg, statusVal;
+
+ /* read the PEI MSI Level2 interrupt status register */
+ intr_status = readl(mbase+0x1230);
+
+ /* check if this is a PCIe MSI interrupt */
+ if (intr_status & 0x0000ffff) {
+ msiIntr = 1;
+ for (bit = 0; bit < 16; bit++) {
+ if (intr_status & (0x1 << bit)) {
+ printk(KERN_INFO
+ "PEI%d --> MSI%d-%d interrupt asserted\n",
+ port->index, bit*16, ((bit+1)*16)-1);
+ /* MSI Level 1 interrupt status */
+ statusReg = (0x123c + (0xc * bit));
+ statusVal = readl(mbase+statusReg);
+ printk(KERN_INFO
+ "MSI status Reg 0x%x val = 0x%x\n",
+ statusReg, statusVal);
+ /* clear statusReg */
+ writel(statusVal, mbase+statusReg);
+ }
+ }
+ }
+ /*
+ * We clear all the interrupts in the PEI status, even though
+ * interrupts from MSI devices have not yet been handled.
+ */
+ writel(intr_status, mbase + 0x1230);
+
+ return msiIntr ? IRQ_NONE : IRQ_HANDLED;
+}
+
+
+/* PCIe setup function */
+int axxia_pcie_setup(int portno, struct pci_sys_data *sys)
+{
+ struct axxia_pciex_port *port;
+ u32 pci_config, pci_status, link_state;
+ int i, num_pages;
+ u32 mpage_lower, pciah, pcial;
+ u32 msiLower, msiHigher, msiFinal;
+ u64 size, msiAddr;
+ void __iomem *cfg_addr, *cfg_data;
+ int mappedIrq;
+ int err;
+
+ port = &axxia_pciex_ports[sys->domain];
+ printk(KERN_INFO
+ "cfg_space start = 0x%012llx, end = 0x%012llx\n",
+ port->cfg_space.start, port->cfg_space.end);
+ printk(KERN_INFO
+ "utl_regs start = 0x%012llx, end = 0x%012llx\n",
+ port->utl_regs.start, port->utl_regs.end);
+ port->root_bus_nr = sys->busnr;
+
+ /* 1M external config */
+ cfg_data = ioremap(port->cfg_space.start, 0x100000);
+ if (cfg_data == NULL) {
+ printk(KERN_ERR "%s: Can't map external config space !",
+ port->node->full_name);
+ goto fail;
+ }
+ port->cfg_data = cfg_data;
+
+ /* IORESOURCE_MEM */
+ port->res.name = "PCIe MEM";
+ port->res.start = port->cfg_space.start - 0x38000000;
+ /* allocate 256 M -- 2 MPAGEs worth */
+ port->res.end = port->res.start + 0x10000000 - 1;
+ port->res.flags = IORESOURCE_MEM;
+
+ if (request_resource(&iomem_resource, &port->res))
+ panic("Request PCIe Memory resource failed for port %d\n",
+ portno);
+
+ pci_add_resource_offset(&sys->resources, &port->res, sys->mem_offset);
+ printk(KERN_INFO "port res start = 0x%012llx, end = 0x%012llx\n",
+ port->res.start, port->res.end);
+ printk(KERN_INFO "port system mem_offset start = 0x%012llx\n",
+ sys->mem_offset);
+
+ /* 4K internal config */
+ cfg_addr = ioremap(port->utl_regs.start, 0x10000);
+ if (cfg_addr == NULL) {
+ printk(KERN_ERR "%s: Can't map external config space !",
+ port->node->full_name);
+ goto fail;
+ }
+ port->cfg_addr = cfg_addr;
+ printk(KERN_INFO "cfg_addr for port %d = 0x%8x\n",
+ port->index, port->cfg_addr);
+ pci_config = readl(cfg_addr);
+#ifdef PRINT_CONFIG_ACCESSES
+ printk(KERN_INFO "pci_vendor = 0x%08x\n", pci_config);
+#endif
+
+ /* hookup an interrupt handler */
+ printk(KERN_INFO "PCIE%d mapping interrupt\n", port->index);
+ mappedIrq = irq_of_parse_and_map(port->node, 0);
+
+ if (sys->domain == 0) {
+ /* IRQ# 68 for PEI0 */
+ mappedIrq = 100;
+ } else if (sys->domain == 2) {
+ /* IRQ# 70 for PEI2 */
+ mappedIrq = 102;
+ }
+ printk(KERN_INFO
+ "Requesting irq#%d for PEI%d Legacy INTs\n",
+ mappedIrq, port->index);
+ err = request_irq(mappedIrq, acp_pcie_isr, IRQF_SHARED,
+ "acp_pcie", port);
+ if (err) {
+ printk(KERN_ERR
+ "request_irq failed!!!! for IRQ# %d err = %d\n",
+ mappedIrq, err);
+ goto fail;
+ }
+
+ /* Setup as root complex */
+ pci_config = readl(cfg_addr + 0x1000);
+#ifdef PRINT_CONFIG_ACCESSES
+ printk(KERN_INFO "pci_config = 0x%08x\n", pci_config);
+#endif
+
+ pci_status = readl(cfg_addr + 0x1004);
+ link_state = (pci_status & 0x3f00) >> 8;
+ printk(KERN_INFO
+ "PCIE%d status = 0x%08x : PCI link state = 0x%x\n",
+ port->index, pci_status, link_state);
+
+ /* make sure the ACP device is configured as PCI Root Complex */
+ if ((pci_status & 0x18) != 0x18) {
+ printk(KERN_ERR
+ "ACP device is not PCI Root Complex! status = 0x%08x\n",
+ pci_status);
+ goto fail;
+ }
+
+ /* make sure the link is up */
+ if (link_state != 0xb) {
+ /* reset */
+ printk(KERN_ERR
+ "PCI link in bad state - resetting\n");
+ pci_config |= 1;
+ writel(pci_config, cfg_addr + 0x1000);
+ msleep(1000);
+ pci_status = readl(cfg_addr + 0x1004);
+ link_state = (pci_status & 0x3f00) >> 8;
+ printk(KERN_ERR
+ "PCI link state now = 0x%x\n", link_state);
+ if (link_state != 0xb) {
+ printk(KERN_ERR
+ "PCI link still in bad state - giving up!\n");
+ goto fail;
+ }
+ }
+
+ /* setup ACP for 4GB 1=Prefetchable, 10=Locate anywhere in
+ * 64 bit address space */
+ writel(0x1000000c, cfg_addr + 0x10);
+ writel(0x0, cfg_addr + 0x14);
+ writel(((0xff << 16) | (0x1 << 8) | 0x0), cfg_addr + PCI_PRIMARY_BUS);
+
+ /* ACP X1 setup MPAGE registers */
+ /*
+ * MPAGE7 is dedicated to config access, so we only
+ * have 7 128MB pages available for memory i/o.
+ * Calculate how many pages we need
+ */
+ size = 7 * 1024*128*1024;
+ num_pages = ((size - 1) >> 27) + 1;
+ for (i = 0; i < num_pages; i++) {
+ pciah = U64_TO_U32_HIGH(port->pci_addr);
+ pcial = U64_TO_U32_LOW(port->pci_addr);
+ mpage_lower = (pcial & 0xf8000000);
+ mpage_lower |= 0x0;
+ writel(pciah, cfg_addr + ACPX1_PCIE_MPAGE_UPPER(i));
+ writel(mpage_lower, cfg_addr + ACPX1_PCIE_MPAGE_LOWER(i));
+ pcial += 0x08000000;
+ }
+
+ if (sys->domain == 0) {
+ /* Setup MSI address for PEI0 only since it support MSI */
+ msiAddr = port->cfg_space.end & 0xfffffffffffffC00;
+ msiLower = U64_TO_U32_LOW(msiAddr);
+ msiLower = msiLower >> 10;
+ msiHigher = U64_TO_U32_HIGH(msiAddr);
+ msiHigher = msiHigher & 0x3F;
+ msiFinal = ((msiHigher << 22) | (msiLower));
+ printk(KERN_ERR
+ "PEI%d msiHigher = 0x%x, msiLower = 0x%x\n",
+ sys->domain, msiHigher, msiLower);
+ printk(KERN_ERR
+ "PEI%d msiAddr = 0x%012llx, msiFinal = 0x%x\n",
+ sys->domain, msiAddr, msiFinal);
+ writel(msiFinal, cfg_addr + 0x1190);
+ }
+ return 1;
+fail:
+ if (cfg_data)
+ iounmap(cfg_data);
+ if (cfg_addr)
+ iounmap(cfg_addr);
+ return 0;
+}
+
+/* Just a dummy arch_setup_msi_irq() function */
+int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
+{
+ return 0;
+}
+
+
+void arch_teardown_msi_irq(unsigned int irq)
+{
+}
+
+
+
+/* Scan PCIe bus */
+static struct pci_bus __init *
+axxia_pcie_scan_bus(int nr, struct pci_sys_data *sys)
+{
+ struct pci_bus *bus;
+ struct axxia_pciex_port *port;
+
+ /* get the pointer to port struct from domain# */
+ port = &axxia_pciex_ports[sys->domain];
+
+ if (nr < axxia_pciex_port_count) {
+ bus = pci_scan_root_bus(NULL, sys->busnr,
+ &axxia_pciex_pci_ops,
+ sys, &sys->resources);
+ } else {
+ bus = NULL;
+ BUG();
+ }
+
+ return bus;
+}
+
+/* Tegra PCIE requires relaxed ordering */
+static void __devinit axxia_pcie_msi_enable(struct pci_dev *dev)
+{
+ u32 msi_lower, msi_higher = 0;
+ int pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
+ struct axxia_pciex_port *port;
+ int mappedIrq;
+ int err;
+
+ port = bus_to_port(dev->bus);
+
+ if (pos <= 0) {
+ dev_err(&dev->dev, "skipping MSI enable\n");
+ printk(KERN_ERR "skipping MSI enable\n");
+ return;
+ }
+
+ if (port->index == 0) {
+ /* MSI support only in PEI0 */
+ msi_lower = port->pci_addr | (port->cfg_space.end & 0xfffc00);
+ printk(KERN_ERR
+ "PEI%d axxia_pcie_msi_enable Found MSI, msi_lower = 0x%x\n",
+ port->index, msi_lower);
+ pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_LO,
+ msi_lower);
+ pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_HI,
+ msi_higher);
+ /* IRQ# 73-88 for PEI0 MSI INTs */
+ for (mappedIrq = 73; mappedIrq <= 88; mappedIrq++) {
+ printk(KERN_ERR
+ "Requesting irq#%d for PEI0 MSI INTs\n",
+ mappedIrq+32);
+ err = request_irq(mappedIrq+32, acp_pcie_MSI_isr,
+ IRQF_SHARED, "acp_pcie_MSI", port);
+ if (err) {
+ printk(KERN_ERR
+ "request_irq failed!!!! for IRQ# %d err = %d\n",
+ mappedIrq+32, err);
+ return;
+ }
+ }
+ }
+ return;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, axxia_pcie_msi_enable);
+
+/* Port definition struct
+ * Please note: PEI core#1 is not used in AXM5500 */
+static struct hw_pci axxia_pcie_hw[] = {
+ [0] = {
+ .nr_controllers = 1,
+ .domain = 0,
+ .swizzle = pci_std_swizzle,
+ .setup = axxia_pcie_setup,
+ .scan = axxia_pcie_scan_bus
+ },
+ [1] = {
+ .nr_controllers = 0,
+ .domain = 1
+ },
+ [2] = {
+ .nr_controllers = 1,
+ .domain = 2,
+ .swizzle = pci_std_swizzle,
+ .setup = axxia_pcie_setup,
+ .scan = axxia_pcie_scan_bus
+ }
+};
+
+/* Initialize PCIe */
+void __init axxia_pcie_init(void)
+{
+ struct device_node *np;
+ /* allocate memory */
+ axxia_pciex_ports = kzalloc(axxia_pciex_port_count *
+ sizeof(struct axxia_pciex_port),
+ GFP_KERNEL);
+
+ if (!axxia_pciex_ports) {
+ printk(KERN_WARNING "PCIE: failed to allocate ports array\n");
+ return;
+ }
+ for_each_compatible_node(np, NULL, "lsi,plb-pciex")
+ axxia_probe_pciex_bridge(np);
+
+ pci_common_init(&axxia_pcie_hw[0]);
+ pci_common_init(&axxia_pcie_hw[1]);
+ pci_common_init(&axxia_pcie_hw[2]);
+
+ return;
+}
+
+static void axxia_probe_pciex_bridge(struct device_node *np)
+{
+ struct axxia_pciex_port *port;
+ const u32 pval;
+ int portno;
+ const char *val;
+ const u32 *field;
+ int rlen;
+ int pna = of_n_addr_cells(np);
+ int num = pna + 5;
+
+ /* Get the port number from the device-tree */
+ if (!of_property_read_u32(np, "port", &pval)) {
+ portno = pval;
+ if (portno == 1) {
+ /* only PCIe0 and PCIe2 are supported in AXM5500 */
+ return;
+ }
+ printk(KERN_INFO "PCIE Port %d found\n", portno);
+ } else {
+ printk(KERN_ERR "PCIE: Can't find port number for %s\n",
+ np->full_name);
+ return;
+ }
+
+ if (portno > axxia_pciex_port_count) {
+ printk(KERN_ERR "PCIE: port number out of range for %s\n",
+ np->full_name);
+ return;
+ }
+
+ port = &axxia_pciex_ports[portno];
+ port->index = 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);
+ if (!strcmp(val, "pci-endpoint")) {
+ port->endpoint = 1;
+ } else if (!strcmp(val, "pci")) {
+ port->endpoint = 0;
+ } else {
+ printk(KERN_ERR
+ "PCIE%d: missing or incorrect device_type for %s\n",
+ portno, np->full_name);
+ return;
+ }
+ printk(KERN_ERR "PCIE%d: endpoint = %d\n", portno, port->endpoint);
+
+ /* Fetch config space registers address */
+ if (of_address_to_resource(np, 0, &port->cfg_space)) {
+ printk(KERN_ERR "%s: Can't get PCI-E config space !",
+ np->full_name);
+ return;
+ }
+ printk(KERN_ERR "cfg_space start = 0x%012llx, end = 0x%012llx\n",
+ port->cfg_space.start, port->cfg_space.end);
+
+ /* Fetch host bridge internal registers address */
+ if (of_address_to_resource(np, 1, &port->utl_regs)) {
+ printk(KERN_ERR "%s: Can't get UTL register base !",
+ np->full_name);
+ return;
+ }
+ printk(KERN_ERR "utl_regs start = 0x%012llx, end = 0x%012llx\n",
+ port->utl_regs.start, port->utl_regs.end);
+
+ field = of_get_property(np, "dma-ranges", &rlen);
+ if (field == NULL)
+ printk(KERN_ERR "not able to get dma-ranges\n");
+
+ /* Walk it */
+ while ((rlen -= num * 4) >= 0) {
+ u64 pci_addr = of_read_number(field + 1, 2);
+ printk(KERN_ERR "pci_addr = 0x%012llx\n", pci_addr);
+ port->pci_addr = pci_addr;
+ break;
+ }
+ printk(KERN_ERR "%s PCIE%d config base = 0x%012llx\n", np->full_name,
+ port->index, port->utl_regs.start);
+}
+
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..7a63b4a
--- /dev/null
+++ b/arch/arm/mach-axxia/platsmp.c
@@ -0,0 +1,174 @@
+/*
+ * 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_fdt.h>
+#include <asm/smp_plat.h>
+#include <asm/cacheflush.h>
+#include <asm/hardware/gic.h>
+#include <asm/mach/map.h>
+
+/*
+ * control for which core is the next to come out of the secondary
+ * boot "holding pen"
+ */
+volatile int __cpuinitdata pen_release = -1;
+
+extern void axxia_secondary_startup(void);
+
+/*
+ * 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 __cpuinit 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 __cpuinit platform_secondary_init(unsigned int cpu)
+{
+ /*
+ * if any interrupts are already enabled for the primary
+ * core (e.g. timer irq), then they will not have been enabled
+ * for us: do so
+ */
+ gic_secondary_init(0);
+
+ /*
+ * 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 __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+ unsigned long timeout;
+
+ /*
+ * Set synchronisation state between this boot processor
+ * and the secondary one
+ */
+ _raw_spin_lock(&boot_lock);
+
+ /*
+ * This is really belt and braces; we hold unintended secondary
+ * CPUs in the holding pen until we're ready for them. However,
+ * since we haven't sent them a soft interrupt, they shouldn't
+ * be there.
+ */
+ write_pen_release(cpu_logical_map(cpu));
+
+ /*
+ * Send the secondary CPU a soft interrupt, thereby causing
+ * the boot monitor to read the system wide flags register,
+ * and branch to the address found there.
+ */
+ gic_raise_softirq(cpumask_of(cpu), 1);
+
+ timeout = jiffies + (1 * HZ);
+ while (time_before(jiffies, timeout)) {
+ smp_rmb();
+ if (pen_release == -1)
+ break;
+
+ udelay(10);
+ }
+
+ /*
+ * 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 int __init axxia_dt_cpus_num(unsigned long node, const char *uname,
+ int depth, void *data)
+{
+ static int prev_depth = -1;
+ static int nr_cpus = -1;
+
+ if (prev_depth > depth && nr_cpus > 0)
+ return nr_cpus;
+
+ if (nr_cpus < 0 && strcmp(uname, "cpus") == 0)
+ nr_cpus = 0;
+
+ if (nr_cpus >= 0) {
+ const char *device_type = of_get_flat_dt_prop(node,
+ "device_type", NULL);
+
+ if (device_type && strcmp(device_type, "cpu") == 0)
+ nr_cpus++;
+ }
+
+ prev_depth = depth;
+
+ return 0;
+}
+
+/*
+ * Initialise the CPU possible map early - this describes the CPUs
+ * which may be present or become present in the system.
+ */
+void __init smp_init_cpus(void)
+{
+ int ncores = 0, i;
+
+ ncores = of_scan_flat_dt(axxia_dt_cpus_num, NULL);
+
+ if (ncores < 2)
+ return;
+
+ if (ncores > nr_cpu_ids) {
+ pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
+ ncores, nr_cpu_ids);
+ ncores = nr_cpu_ids;
+ }
+
+ for (i = 0; i < ncores; ++i)
+ set_cpu_possible(i, true);
+
+ set_smp_cross_call(gic_raise_softirq);
+}
+
+void __init platform_smp_prepare_cpus(unsigned int max_cpus)
+{
+ int i;
+
+ /*
+ * Initialise the present map, which describes the set of CPUs
+ * actually populated at the present time.
+ */
+ for (i = 0; i < max_cpus; i++)
+ set_cpu_present(i, true);
+
+ *(u32 *)phys_to_virt(0x10000020) =
+ virt_to_phys(axxia_secondary_startup);
+}
diff --git a/arch/arm/mach-axxia/timers.c b/arch/arm/mach-axxia/timers.c
new file mode 100644
index 0000000..17b0c73
--- /dev/null
+++ b/arch/arm/mach-axxia/timers.c
@@ -0,0 +1,225 @@
+/*
+ * 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 <asm/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;
+
+ printk(KERN_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.8.3
More information about the linux-yocto
mailing list