[PATCH 2/8] arm: mach-armada: add source files
Thomas Petazzoni
thomas.petazzoni at free-electrons.com
Tue May 15 04:54:38 EDT 2012
This patch adds basic source files for Marvell Armada SoCs.
Signed-off-by: Gregory CLEMENT <gregory.clement at free-electrons.com>
Signed-off-by: Thomas Petazzoni <thomas.petazzoni at free-electrons.com>
Signed-off-by: Lior Amsalem <alior at marvell.com>
---
arch/arm/boot/dts/a370.dtsi | 23 ++++
arch/arm/boot/dts/armada.dtsi | 67 ++++++++++
arch/arm/boot/dts/axp.dtsi | 43 +++++++
arch/arm/mach-armada/Kconfig | 5 +
arch/arm/mach-armada/Makefile | 2 +
arch/arm/mach-armada/Makefile.boot | 1 +
arch/arm/mach-armada/common.c | 56 +++++++++
arch/arm/mach-armada/common.h | 27 ++++
arch/arm/mach-armada/irq.c | 116 +++++++++++++++++
arch/arm/mach-armada/time.c | 243 ++++++++++++++++++++++++++++++++++++
10 files changed, 583 insertions(+)
create mode 100644 arch/arm/boot/dts/a370.dtsi
create mode 100644 arch/arm/boot/dts/armada.dtsi
create mode 100644 arch/arm/boot/dts/axp.dtsi
create mode 100644 arch/arm/mach-armada/Kconfig
create mode 100644 arch/arm/mach-armada/Makefile
create mode 100644 arch/arm/mach-armada/Makefile.boot
create mode 100644 arch/arm/mach-armada/common.c
create mode 100644 arch/arm/mach-armada/common.h
create mode 100644 arch/arm/mach-armada/irq.c
create mode 100644 arch/arm/mach-armada/time.c
diff --git a/arch/arm/boot/dts/a370.dtsi b/arch/arm/boot/dts/a370.dtsi
new file mode 100644
index 0000000..f11e56a
--- /dev/null
+++ b/arch/arm/boot/dts/a370.dtsi
@@ -0,0 +1,23 @@
+/*
+ * Device Tree Include file for Marvell Armada 370 family SoC
+ *
+ * Copyright (C) 2012 Marvell
+ *
+ * Lior Amsalem <alior at marvell.com>
+ * Gregory CLEMENT <gregory.clement at free-electrons.com>
+ * Thomas Petazzoni <thomas.petazzoni at free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * Contains definitions specific to the Armada 370 SoC that are not
+ * common to all Armada SoCs.
+ */
+
+/include/ "armada.dtsi"
+
+/ {
+ model = "Marvell Armada 370 family SoC";
+ compatible = "marvell,armada370", "marvell,armada";
+ };
diff --git a/arch/arm/boot/dts/armada.dtsi b/arch/arm/boot/dts/armada.dtsi
new file mode 100644
index 0000000..3c99c30
--- /dev/null
+++ b/arch/arm/boot/dts/armada.dtsi
@@ -0,0 +1,67 @@
+/*
+ * Device Tree Include file for Marvell Armada family SoC
+ *
+ * Copyright (C) 2012 Marvell
+ *
+ * Lior Amsalem <alior at marvell.com>
+ * Gregory CLEMENT <gregory.clement at free-electrons.com>
+ * Thomas Petazzoni <thomas.petazzoni at free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * This file contains the definitions that are common to the Armada
+ * 370 and Armada XP SoC.
+ */
+
+/include/ "skeleton.dtsi"
+
+/ {
+ model = "Marvell Armada family SoC";
+ compatible = "marvell,armada";
+
+ cpus {
+ cpu at 0 {
+ compatible = "marvell,sheeva-v7";
+ };
+ };
+
+ mpic: interrupt-controller at d0020000 {
+ compatible = "marvell,mpic";
+ #interrupt-cells = <1>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ interrupt-controller;
+ reg = <0xd0020000 0x1000>,
+ <0xd0021000 0x1000>;
+ };
+
+ soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "simple-bus";
+ interrupt-parent = <&mpic>;
+ ranges;
+
+ serial at d0012000 {
+ compatible = "ns16550";
+ reg = <0xd0012000 0x100>;
+ reg-shift = <2>;
+ interrupts = <41>;
+ };
+ serial at d0012100 {
+ compatible = "ns16550";
+ reg = <0xd0012100 0x100>;
+ reg-shift = <2>;
+ interrupts = <42>;
+ };
+
+ timer at d0020300 {
+ compatible = "marvell,timer";
+ reg = <0xd0020300 0x30>;
+ interrupts = <37>, <38>, <39>, <40>;
+ };
+ };
+};
+
diff --git a/arch/arm/boot/dts/axp.dtsi b/arch/arm/boot/dts/axp.dtsi
new file mode 100644
index 0000000..6427268
--- /dev/null
+++ b/arch/arm/boot/dts/axp.dtsi
@@ -0,0 +1,43 @@
+/*
+ * Device Tree Include file for Marvell Armada XP family SoC
+ *
+ * Copyright (C) 2012 Marvell
+ *
+ * Lior Amsalem <alior at marvell.com>
+ * Gregory CLEMENT <gregory.clement at free-electrons.com>
+ * Thomas Petazzoni <thomas.petazzoni at free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * Contains definitions specific to the Armada 370 SoC that are not
+ * common to all Armada SoCs.
+ */
+
+/include/ "armada.dtsi"
+
+/ {
+ model = "Marvell Armada XP family SoC";
+ compatible = "marvell,armadaxp", "marvell,armada";
+
+ soc {
+ serial at d0012200 {
+ compatible = "ns16550";
+ reg = <0xd0012200 0x100>;
+ reg-shift = <2>;
+ interrupts = <43>;
+ };
+ serial at d0012300 {
+ compatible = "ns16550";
+ reg = <0xd0012300 0x100>;
+ reg-shift = <2>;
+ interrupts = <44>;
+ };
+
+ timer at d0020300 {
+ marvell,timer-25Mhz;
+ };
+ };
+ };
+
diff --git a/arch/arm/mach-armada/Kconfig b/arch/arm/mach-armada/Kconfig
new file mode 100644
index 0000000..010a6a3
--- /dev/null
+++ b/arch/arm/mach-armada/Kconfig
@@ -0,0 +1,5 @@
+if ARCH_ARMADA
+
+menu "Marvell Armada Implementations"
+
+endif
diff --git a/arch/arm/mach-armada/Makefile b/arch/arm/mach-armada/Makefile
new file mode 100644
index 0000000..6765961
--- /dev/null
+++ b/arch/arm/mach-armada/Makefile
@@ -0,0 +1,2 @@
+obj-y += common.o irq.o time.o
+
diff --git a/arch/arm/mach-armada/Makefile.boot b/arch/arm/mach-armada/Makefile.boot
new file mode 100644
index 0000000..b327175
--- /dev/null
+++ b/arch/arm/mach-armada/Makefile.boot
@@ -0,0 +1 @@
+zreladdr-y := 0x00008000
diff --git a/arch/arm/mach-armada/common.c b/arch/arm/mach-armada/common.c
new file mode 100644
index 0000000..92d9ec2
--- /dev/null
+++ b/arch/arm/mach-armada/common.c
@@ -0,0 +1,56 @@
+/*
+ * Core functions for Marvell Armada System On Chip
+ *
+ * Copyright (C) 2012 Marvell
+ *
+ * Lior Amsalem <alior at marvell.com>
+ * Gregory CLEMENT <gregory.clement at free-electrons.com>
+ * Thomas Petazzoni <thomas.petazzoni at free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/timex.h>
+#include <asm/page.h>
+#include <asm/setup.h>
+#include <asm/mach/map.h>
+#include <plat/addr-map.h>
+#include <mach/armada.h>
+#include <mach/bridge-regs.h>
+#include "common.h"
+
+static struct map_desc armada_io_desc[] __initdata = {
+ {
+ .virtual = ARMADA_REGS_VIRT_BASE,
+ .pfn = __phys_to_pfn(ARMADA_REGS_PHYS_BASE),
+ .length = ARMADA_REGS_SIZE,
+ .type = MT_DEVICE,
+ },
+};
+
+void __init armada_map_io(void)
+{
+ iotable_init(armada_io_desc, ARRAY_SIZE(armada_io_desc));
+}
+
+void armada_restart(char mode, const char *cmd)
+{
+ /*
+ * Enable soft reset to assert RSTOUTn.
+ */
+ writel(SOFT_RESET_OUT_EN, RSTOUTn_MASK);
+
+ /*
+ * Assert soft reset.
+ */
+ writel(SOFT_RESET, SYSTEM_SOFT_RESET);
+ while (1)
+ ;
+}
diff --git a/arch/arm/mach-armada/common.h b/arch/arm/mach-armada/common.h
new file mode 100644
index 0000000..e453161
--- /dev/null
+++ b/arch/arm/mach-armada/common.h
@@ -0,0 +1,27 @@
+/*
+ * Core functions for Marvell Armada System On Chip
+ *
+ * Copyright (C) 2012 Marvell
+ *
+ * Lior Amsalem <alior at marvell.com>
+ * Gregory CLEMENT <gregory.clement at free-electrons.com>
+ * Thomas Petazzoni <thomas.petazzoni at free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __ARCH_ARMADA_COMMON_H
+#define __ARCH_ARMADA_COMMON_H
+
+#include <asm/exception.h>
+
+extern struct sys_timer armada_timer;
+
+void armada_map_io(void);
+void armada_init_irq(void);
+void armada_restart(char, const char *);
+asmlinkage void __exception_irq_entry armada_handle_irq(struct pt_regs *regs);
+
+#endif
diff --git a/arch/arm/mach-armada/irq.c b/arch/arm/mach-armada/irq.c
new file mode 100644
index 0000000..7006429
--- /dev/null
+++ b/arch/arm/mach-armada/irq.c
@@ -0,0 +1,116 @@
+/*
+ * Marvall Armada SoC IRQ handling
+ *
+ * Copyright (C) 2012 Marvell
+ *
+ * Lior Amsalem <alior at marvell.com>
+ * Gregory CLEMENT <gregory.clement at free-electrons.com>
+ * Thomas Petazzoni <thomas.petazzoni at free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irqdomain.h>
+#include <asm/mach/arch.h>
+#include <asm/exception.h>
+
+/* Interrupt Controller Registers Map */
+#define ARMADA_INT_SET_MASK_OFFS (0xBC)
+#define ARMADA_INT_CLEAR_MASK_OFFS (0xB8)
+
+#define ARMADA_INT_SET_ENABLE_OFFS (0xA30)
+#define ARMADA_INT_CLEAR_ENABLE_OFFS (0xA34)
+
+#define ARMADA_CPU_INTACK_OFFS (0xB4)
+
+static void __iomem *per_cpu_int_base;
+static void __iomem *main_int_base;
+static struct irq_domain *armada_mpic_domain;
+
+static void armada_irq_mask(struct irq_data *d)
+{
+ writel(d->irq, per_cpu_int_base + ARMADA_INT_CLEAR_MASK_OFFS);
+}
+
+static void armada_irq_unmask(struct irq_data *d)
+{
+ writel(d->irq, per_cpu_int_base + ARMADA_INT_SET_MASK_OFFS);
+}
+
+static struct irq_chip armada_irq_chip = {
+ .name = "armada_irq",
+ .irq_mask = armada_irq_mask,
+ .irq_mask_ack = armada_irq_mask,
+ .irq_unmask = armada_irq_unmask,
+};
+
+static int armada_mpic_irq_map(struct irq_domain *h,
+ unsigned int virq,
+ irq_hw_number_t hw)
+{
+ armada_irq_mask(irq_get_irq_data(virq));
+ writel(virq, main_int_base + ARMADA_INT_SET_ENABLE_OFFS);
+
+ irq_set_chip_and_handler(virq, &armada_irq_chip,
+ handle_level_irq);
+ irq_set_status_flags(virq, IRQ_LEVEL);
+ set_irq_flags(virq, IRQF_VALID | IRQF_PROBE);
+ return 0;
+}
+
+static struct irq_domain_ops armada_mpic_irq_ops = {
+ .map = armada_mpic_irq_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static void __init armada_mpic_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ main_int_base = of_iomap(node, 0);
+ per_cpu_int_base = of_iomap(node, 1);
+
+ armada_mpic_domain = irq_domain_add_linear(node, NR_IRQS,
+ &armada_mpic_irq_ops, NULL);
+ if (!armada_mpic_domain)
+ panic("Unable to add Armada MPIC irq domain (DT)\n");
+
+ irq_set_default_host(armada_mpic_domain);
+}
+
+asmlinkage void __exception_irq_entry armada_handle_irq(struct pt_regs *regs)
+{
+ u32 irqstat, irqnr;
+
+ do {
+ irqstat = readl_relaxed(per_cpu_int_base +
+ ARMADA_CPU_INTACK_OFFS);
+ irqnr = irqstat & 0x3FF;
+
+ if (irqnr < 1023) {
+ irqnr = irq_find_mapping(armada_mpic_domain, irqnr);
+ handle_IRQ(irqnr, regs);
+ continue;
+ }
+
+ break;
+ } while (1);
+}
+
+static const struct of_device_id mpic_of_match[] __initconst = {
+ { .compatible = "marvell,mpic", .data = armada_mpic_of_init },
+ {},
+};
+
+void __init armada_init_irq(void)
+{
+ of_irq_init(mpic_of_match);
+}
diff --git a/arch/arm/mach-armada/time.c b/arch/arm/mach-armada/time.c
new file mode 100644
index 0000000..73817ea
--- /dev/null
+++ b/arch/arm/mach-armada/time.c
@@ -0,0 +1,243 @@
+/*
+ * Marvell Armada SoC timer handling.
+ *
+ * Copyright (C) 2012 Marvell
+ *
+ * Lior Amsalem <alior at marvell.com>
+ * Gregory CLEMENT <gregory.clement at free-electrons.com>
+ * Thomas Petazzoni <thomas.petazzoni at free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * Timer 0 is used as free-running clocksource, while timer 1 is
+ * used as clock_event_device.
+ */
+
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/clockchips.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/irq.h>
+#include <asm/sched_clock.h>
+#include <asm/mach/time.h>
+
+/*
+ * Timer block registers.
+ */
+#define TIMER_CTRL_OFF 0x0000
+#define TIMER0_EN 0x0001
+#define TIMER0_RELOAD_EN 0x0002
+#define TIMER0_25MHZ 0x0800
+#define TIMER0_DIV(div) ((div) << 19)
+#define TIMER1_EN 0x0004
+#define TIMER1_RELOAD_EN 0x0008
+#define TIMER1_25MHZ 0x1000
+#define TIMER1_DIV(div) ((div) << 22)
+#define TIMER_EVENTS_STATUS 0x0004
+#define TIMER0_CLR_MASK (~0x1)
+#define TIMER1_CLR_MASK (~0x100)
+#define TIMER0_RELOAD_OFF 0x0010
+#define TIMER0_VAL_OFF 0x0014
+#define TIMER1_RELOAD_OFF 0x0018
+#define TIMER1_VAL_OFF 0x001c
+
+/* Global timers are connected to the coherency fabric clock, and the
+ below divider reduces their incrementing frequency. */
+#define TIMER_DIVIDER_SHIFT 5
+#define TIMER_DIVIDER (1 << TIMER_DIVIDER_SHIFT)
+
+/*
+ * SoC-specific data.
+ */
+static void __iomem *timer_base;
+static int timer_irq;
+
+/*
+ * Number of timer ticks per jiffy.
+ */
+static u32 ticks_per_jiffy;
+
+static u32 notrace armada_read_sched_clock(void)
+{
+ return ~readl(timer_base + TIMER0_VAL_OFF);
+}
+
+/*
+ * Clockevent handling.
+ */
+static int
+armada_clkevt_next_event(unsigned long delta, struct clock_event_device *dev)
+{
+ unsigned long flags;
+ u32 u;
+
+ if (delta == 0)
+ return -ETIME;
+
+ local_irq_save(flags);
+
+ /*
+ * Clear clockevent timer interrupt.
+ */
+ writel(TIMER1_CLR_MASK, timer_base + TIMER_EVENTS_STATUS);
+
+ /*
+ * Setup new clockevent timer value.
+ */
+ writel(delta, timer_base + TIMER1_VAL_OFF);
+
+ /*
+ * Enable the timer.
+ */
+ u = readl(timer_base + TIMER_CTRL_OFF);
+ u = ((u & ~TIMER1_RELOAD_EN) | TIMER1_EN |
+ TIMER1_DIV(TIMER_DIVIDER_SHIFT));
+ writel(u, timer_base + TIMER_CTRL_OFF);
+
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+static void
+armada_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev)
+{
+ unsigned long flags;
+ u32 u;
+
+ local_irq_save(flags);
+ if (mode == CLOCK_EVT_MODE_PERIODIC) {
+ /*
+ * Setup timer to fire at 1/HZ intervals.
+ */
+ writel(ticks_per_jiffy - 1, timer_base + TIMER1_RELOAD_OFF);
+ writel(ticks_per_jiffy - 1, timer_base + TIMER1_VAL_OFF);
+
+ /*
+ * Enable timer.
+ */
+ u = readl(timer_base + TIMER_CTRL_OFF);
+
+ writel((u | TIMER1_EN | TIMER1_RELOAD_EN |
+ TIMER1_DIV(TIMER_DIVIDER_SHIFT)),
+ timer_base + TIMER_CTRL_OFF);
+ } else {
+ /*
+ * Disable timer.
+ */
+ u = readl(timer_base + TIMER_CTRL_OFF);
+ writel(u & ~TIMER1_EN, timer_base + TIMER_CTRL_OFF);
+
+ /*
+ * ACK pending timer interrupt.
+ */
+ writel(TIMER1_CLR_MASK,
+ timer_base + TIMER_EVENTS_STATUS);
+
+ }
+ local_irq_restore(flags);
+}
+
+static struct clock_event_device armada_clkevt = {
+ .name = "armada_tick",
+ .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
+ .shift = 32,
+ .rating = 300,
+ .set_next_event = armada_clkevt_next_event,
+ .set_mode = armada_clkevt_mode,
+};
+
+static irqreturn_t armada_timer_interrupt(int irq, void *dev_id)
+{
+ /*
+ * ACK timer interrupt and call event handler.
+ */
+
+ writel(TIMER1_CLR_MASK, timer_base + TIMER_EVENTS_STATUS);
+ armada_clkevt.event_handler(&armada_clkevt);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction armada_timer_irq = {
+ .name = "armada_tick",
+ .flags = IRQF_DISABLED | IRQF_TIMER,
+ .handler = armada_timer_interrupt
+};
+
+static void __init armada_time_init(void)
+{
+ u32 u;
+ struct device_node *np;
+ unsigned int timer_clk;
+
+ np = of_find_compatible_node(NULL, NULL, "marvell,timer");
+ timer_base = of_iomap(np, 0);
+ WARN_ON(!timer_base);
+
+ if (of_find_property(np, "marvell,timer-25Mhz", NULL)) {
+ /* The fixed 25MHz timer is available so let's use it*/
+ u = readl(timer_base + TIMER_CTRL_OFF);
+ writel(u | TIMER0_25MHZ | TIMER1_25MHZ,
+ timer_base + TIMER_CTRL_OFF);
+ timer_clk = 25000000;
+ } else {
+ u32 clk;
+ of_property_read_u32(np, "clock-frequency", &clk);
+ WARN_ON(!clk);
+ u = readl(timer_base + TIMER_CTRL_OFF);
+ writel(u & ~(TIMER0_25MHZ | TIMER1_25MHZ),
+ timer_base + TIMER_CTRL_OFF);
+ timer_clk = clk / TIMER_DIVIDER;
+ }
+
+ /* We use timer 0 as clocksource, and timer 1 for
+ clockevents */
+ timer_irq = irq_of_parse_and_map(np, 1);
+
+ ticks_per_jiffy = (timer_clk + HZ/2) / HZ;
+
+ /*
+ * Set scale and timer for sched_clock.
+ */
+ setup_sched_clock(armada_read_sched_clock, 32, timer_clk);
+
+ /*
+ * Setup free-running clocksource timer (interrupts
+ * disabled).
+ */
+ writel(0xffffffff, timer_base + TIMER0_VAL_OFF);
+ writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF);
+
+ u = readl(timer_base + TIMER_CTRL_OFF);
+
+ writel((u | TIMER0_EN | TIMER0_RELOAD_EN |
+ TIMER0_DIV(TIMER_DIVIDER_SHIFT)),
+ timer_base + TIMER_CTRL_OFF);
+
+ clocksource_mmio_init(timer_base + TIMER0_VAL_OFF,
+ "armada_clocksource",
+ timer_clk, 300, 32,
+ clocksource_mmio_readl_down);
+
+ /*
+ * Setup clockevent timer (interrupt-driven).
+ */
+ setup_irq(timer_irq, &armada_timer_irq);
+ armada_clkevt.mult = div_sc(timer_clk, NSEC_PER_SEC,
+ armada_clkevt.shift);
+ armada_clkevt.max_delta_ns = clockevent_delta2ns(0xfffffffe,
+ &armada_clkevt);
+ armada_clkevt.min_delta_ns = clockevent_delta2ns(1, &armada_clkevt);
+ armada_clkevt.cpumask = cpumask_of(0);
+ clockevents_register_device(&armada_clkevt);
+}
+
+struct sys_timer armada_timer = {
+ .init = armada_time_init,
+};
--
1.7.9.5
More information about the linux-arm-kernel
mailing list