summaryrefslogtreecommitdiff
path: root/arch/arm/mach-at91rm9200
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-at91rm9200')
-rw-r--r--arch/arm/mach-at91rm9200/Kconfig54
-rw-r--r--arch/arm/mach-at91rm9200/Makefile27
-rw-r--r--arch/arm/mach-at91rm9200/Makefile.boot9
-rw-r--r--arch/arm/mach-at91rm9200/clock.c620
-rw-r--r--arch/arm/mach-at91rm9200/common.c115
-rw-r--r--arch/arm/mach-at91rm9200/devices.c291
-rw-r--r--arch/arm/mach-at91rm9200/generic.h18
-rw-r--r--arch/arm/mach-at91rm9200/gpio.c302
-rw-r--r--arch/arm/mach-at91rm9200/irq.c170
-rw-r--r--arch/arm/mach-at91rm9200/time.c127
10 files changed, 1733 insertions, 0 deletions
diff --git a/arch/arm/mach-at91rm9200/Kconfig b/arch/arm/mach-at91rm9200/Kconfig
new file mode 100644
index 000000000000..4b7218fc3eb1
--- /dev/null
+++ b/arch/arm/mach-at91rm9200/Kconfig
@@ -0,0 +1,54 @@
+if ARCH_AT91RM9200
+
+menu "AT91RM9200 Implementations"
+
+comment "AT91RM9200 Board Type"
+
+config ARCH_AT91RM9200DK
+ bool "Atmel AT91RM9200-DK Development board"
+ depends on ARCH_AT91RM9200
+ help
+ Select this if you are using Atmel's AT91RM9200-DK Development board
+
+config MACH_AT91RM9200EK
+ bool "Atmel AT91RM9200-EK Evaluation Kit"
+ depends on ARCH_AT91RM9200
+ help
+ Select this if you are using Atmel's AT91RM9200-EK Evaluation Kit
+
+config MACH_CSB337
+ bool "Cogent CSB337 board"
+ depends on ARCH_AT91RM9200
+ help
+ Select this if you are using Cogent's CSB337 board
+
+config MACH_CSB637
+ bool "Cogent CSB637 board"
+ depends on ARCH_AT91RM9200
+ help
+ Select this if you are using Cogent's CSB637 board
+
+config MACH_CARMEVA
+ bool "Conitec's ARM&EVA"
+ depends on ARCH_AT91RM9200
+ help
+ Select this if you are using Conitec's AT91RM9200-MCU-Module
+
+config MACH_KB9200
+ bool "KwikByte's KB920x"
+ depends on ARCH_AT91RM9200
+ help
+ Select this if you are using KwikByte's KB920x board
+
+
+comment "AT91RM9200 Feature Selections"
+
+config AT91_PROGRAMMABLE_CLOCKS
+ bool "Programmable Clocks"
+ help
+ Select this if you need to program one or more of the PCK0..PCK3
+ programmable clock outputs.
+
+endmenu
+
+endif
diff --git a/arch/arm/mach-at91rm9200/Makefile b/arch/arm/mach-at91rm9200/Makefile
new file mode 100644
index 000000000000..1f2805ca6e21
--- /dev/null
+++ b/arch/arm/mach-at91rm9200/Makefile
@@ -0,0 +1,27 @@
+#
+# Makefile for the linux kernel.
+#
+
+obj-y := clock.o irq.o time.o gpio.o common.o devices.o
+obj-m :=
+obj-n :=
+obj- :=
+
+# Board-specific support
+#obj-$(CONFIG_ARCH_AT91RM9200DK) += board-dk.o
+#obj-$(CONFIG_MACH_AT91RM9200EK) += board-ek.o
+#obj-$(CONFIG_MACH_CSB337) += board-csb337.o
+#obj-$(CONFIG_MACH_CSB637) += board-csb637.o
+#obj-$(CONFIG_MACH_CARMEVA) += board-carmeva.o
+#obj-$(CONFIG_MACH_KB9200) += board-kb9202.o
+
+# LEDs support
+#led-$(CONFIG_ARCH_AT91RM9200DK) += leds.o
+#led-$(CONFIG_MACH_AT91RM9200EK) += leds.o
+#led-$(CONFIG_MACH_CSB337) += leds.o
+#led-$(CONFIG_MACH_CSB637) += leds.o
+#led-$(CONFIG_MACH_KB9200) += leds.o
+obj-$(CONFIG_LEDS) += $(led-y)
+
+# VGA support
+#obj-$(CONFIG_FB_S1D13XXX) += ics1523.o
diff --git a/arch/arm/mach-at91rm9200/Makefile.boot b/arch/arm/mach-at91rm9200/Makefile.boot
new file mode 100644
index 000000000000..e667dcc7cd34
--- /dev/null
+++ b/arch/arm/mach-at91rm9200/Makefile.boot
@@ -0,0 +1,9 @@
+# Note: the following conditions must always be true:
+# ZRELADDR == virt_to_phys(TEXTADDR)
+# PARAMS_PHYS must be within 4MB of ZRELADDR
+# INITRD_PHYS must be in RAM
+
+ zreladdr-y := 0x20008000
+params_phys-y := 0x20000100
+initrd_phys-y := 0x20410000
+
diff --git a/arch/arm/mach-at91rm9200/clock.c b/arch/arm/mach-at91rm9200/clock.c
new file mode 100644
index 000000000000..ec8195a2a3cc
--- /dev/null
+++ b/arch/arm/mach-at91rm9200/clock.c
@@ -0,0 +1,620 @@
+/*
+ * linux/arch/arm/mach-at91rm9200/clock.c
+ *
+ * Copyright (C) 2005 David Brownell
+ * Copyright (C) 2005 Ivan Kokshaysky
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+
+#include <asm/semaphore.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+
+#include <asm/arch/hardware.h>
+#include <asm/arch/board.h> /* for master clock global */
+
+#include "generic.h"
+
+#undef DEBUG
+
+/*
+ * There's a lot more which can be done with clocks, including cpufreq
+ * integration, slow clock mode support (for system suspend), letting
+ * PLLB be used at other rates (on boards that don't need USB), etc.
+ */
+
+struct clk {
+ const char *name;
+ unsigned long rate_hz;
+ struct clk *parent;
+ u32 pmc_mask;
+ void (*mode)(struct clk *, int);
+ unsigned id:2; /* PCK0..3, or 32k/main/a/b */
+ unsigned primary:1;
+ unsigned pll:1;
+ unsigned programmable:1;
+ u16 users;
+};
+
+static spinlock_t clk_lock;
+static u32 at91_pllb_usb_init;
+
+/*
+ * Four primary clock sources: two crystal oscillators (32K, main), and
+ * two PLLs. PLLA usually runs the master clock; and PLLB must run at
+ * 48 MHz (unless no USB function clocks are needed). The main clock and
+ * both PLLs are turned off to run in "slow clock mode" (system suspend).
+ */
+static struct clk clk32k = {
+ .name = "clk32k",
+ .rate_hz = AT91_SLOW_CLOCK,
+ .users = 1, /* always on */
+ .id = 0,
+ .primary = 1,
+};
+static struct clk main_clk = {
+ .name = "main",
+ .pmc_mask = 1 << 0, /* in PMC_SR */
+ .users = 1,
+ .id = 1,
+ .primary = 1,
+};
+static struct clk plla = {
+ .name = "plla",
+ .parent = &main_clk,
+ .pmc_mask = 1 << 1, /* in PMC_SR */
+ .id = 2,
+ .primary = 1,
+ .pll = 1,
+};
+
+static void pllb_mode(struct clk *clk, int is_on)
+{
+ u32 value;
+
+ if (is_on) {
+ is_on = AT91_PMC_LOCKB;
+ value = at91_pllb_usb_init;
+ } else
+ value = 0;
+
+ at91_sys_write(AT91_CKGR_PLLBR, value);
+
+ do {
+ cpu_relax();
+ } while ((at91_sys_read(AT91_PMC_SR) & AT91_PMC_LOCKB) != is_on);
+}
+
+static struct clk pllb = {
+ .name = "pllb",
+ .parent = &main_clk,
+ .pmc_mask = 1 << 2, /* in PMC_SR */
+ .mode = pllb_mode,
+ .id = 3,
+ .primary = 1,
+ .pll = 1,
+};
+
+static void pmc_sys_mode(struct clk *clk, int is_on)
+{
+ if (is_on)
+ at91_sys_write(AT91_PMC_SCER, clk->pmc_mask);
+ else
+ at91_sys_write(AT91_PMC_SCDR, clk->pmc_mask);
+}
+
+/* USB function clocks (PLLB must be 48 MHz) */
+static struct clk udpck = {
+ .name = "udpck",
+ .parent = &pllb,
+ .pmc_mask = AT91_PMC_UDP,
+ .mode = pmc_sys_mode,
+};
+static struct clk uhpck = {
+ .name = "uhpck",
+ .parent = &pllb,
+ .pmc_mask = AT91_PMC_UHP,
+ .mode = pmc_sys_mode,
+};
+
+#ifdef CONFIG_AT91_PROGRAMMABLE_CLOCKS
+/*
+ * The four programmable clocks can be parented by any primary clock.
+ * You must configure pin multiplexing to bring these signals out.
+ */
+static struct clk pck0 = {
+ .name = "pck0",
+ .pmc_mask = AT91_PMC_PCK0,
+ .mode = pmc_sys_mode,
+ .programmable = 1,
+ .id = 0,
+};
+static struct clk pck1 = {
+ .name = "pck1",
+ .pmc_mask = AT91_PMC_PCK1,
+ .mode = pmc_sys_mode,
+ .programmable = 1,
+ .id = 1,
+};
+static struct clk pck2 = {
+ .name = "pck2",
+ .pmc_mask = AT91_PMC_PCK2,
+ .mode = pmc_sys_mode,
+ .programmable = 1,
+ .id = 2,
+};
+static struct clk pck3 = {
+ .name = "pck3",
+ .pmc_mask = AT91_PMC_PCK3,
+ .mode = pmc_sys_mode,
+ .programmable = 1,
+ .id = 3,
+};
+#endif /* CONFIG_AT91_PROGRAMMABLE_CLOCKS */
+
+
+/*
+ * The master clock is divided from the CPU clock (by 1-4). It's used for
+ * memory, interfaces to on-chip peripherals, the AIC, and sometimes more
+ * (e.g baud rate generation). It's sourced from one of the primary clocks.
+ */
+static struct clk mck = {
+ .name = "mck",
+ .pmc_mask = 1 << 3, /* in PMC_SR */
+ .users = 1, /* (must be) always on */
+};
+
+static void pmc_periph_mode(struct clk *clk, int is_on)
+{
+ if (is_on)
+ at91_sys_write(AT91_PMC_PCER, clk->pmc_mask);
+ else
+ at91_sys_write(AT91_PMC_PCDR, clk->pmc_mask);
+}
+
+static struct clk udc_clk = {
+ .name = "udc_clk",
+ .parent = &mck,
+ .pmc_mask = 1 << AT91_ID_UDP,
+ .mode = pmc_periph_mode,
+};
+static struct clk ohci_clk = {
+ .name = "ohci_clk",
+ .parent = &mck,
+ .pmc_mask = 1 << AT91_ID_UHP,
+ .mode = pmc_periph_mode,
+};
+
+static struct clk *const clock_list[] = {
+ /* four primary clocks -- MUST BE FIRST! */
+ &clk32k,
+ &main_clk,
+ &plla,
+ &pllb,
+
+ /* PLLB children (USB) */
+ &udpck,
+ &uhpck,
+
+#ifdef CONFIG_AT91_PROGRAMMABLE_CLOCKS
+ /* programmable clocks */
+ &pck0,
+ &pck1,
+ &pck2,
+ &pck3,
+#endif /* CONFIG_AT91_PROGRAMMABLE_CLOCKS */
+
+ /* MCK and peripherals */
+ &mck,
+ // usart0..usart3
+ // mmc
+ &udc_clk,
+ // i2c
+ // spi
+ // ssc0..ssc2
+ // tc0..tc5
+ &ohci_clk,
+ // ether
+};
+
+
+/* clocks are all static for now; no refcounting necessary */
+struct clk *clk_get(struct device *dev, const char *id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(clock_list); i++) {
+ if (strcmp(id, clock_list[i]->name) == 0)
+ return clock_list[i];
+ }
+
+ return ERR_PTR(-ENOENT);
+}
+EXPORT_SYMBOL(clk_get);
+
+void clk_put(struct clk *clk)
+{
+}
+EXPORT_SYMBOL(clk_put);
+
+static void __clk_enable(struct clk *clk)
+{
+ if (clk->parent)
+ __clk_enable(clk->parent);
+ if (clk->users++ == 0 && clk->mode)
+ clk->mode(clk, 1);
+}
+
+int clk_enable(struct clk *clk)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&clk_lock, flags);
+ __clk_enable(clk);
+ spin_unlock_irqrestore(&clk_lock, flags);
+ return 0;
+}
+EXPORT_SYMBOL(clk_enable);
+
+static void __clk_disable(struct clk *clk)
+{
+ BUG_ON(clk->users == 0);
+ if (--clk->users == 0 && clk->mode)
+ clk->mode(clk, 0);
+ if (clk->parent)
+ __clk_disable(clk->parent);
+}
+
+void clk_disable(struct clk *clk)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&clk_lock, flags);
+ __clk_disable(clk);
+ spin_unlock_irqrestore(&clk_lock, flags);
+}
+EXPORT_SYMBOL(clk_disable);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+ unsigned long flags;
+ unsigned long rate;
+
+ spin_lock_irqsave(&clk_lock, flags);
+ for (;;) {
+ rate = clk->rate_hz;
+ if (rate || !clk->parent)
+ break;
+ clk = clk->parent;
+ }
+ spin_unlock_irqrestore(&clk_lock, flags);
+ return rate;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+/*------------------------------------------------------------------------*/
+
+#ifdef CONFIG_AT91_PROGRAMMABLE_CLOCKS
+
+/*
+ * For now, only the programmable clocks support reparenting (MCK could
+ * do this too, with care) or rate changing (the PLLs could do this too,
+ * ditto MCK but that's more for cpufreq). Drivers may reparent to get
+ * a better rate match; we don't.
+ */
+
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long flags;
+ unsigned prescale;
+ unsigned long actual;
+
+ if (!clk->programmable)
+ return -EINVAL;
+ spin_lock_irqsave(&clk_lock, flags);
+
+ actual = clk->parent->rate_hz;
+ for (prescale = 0; prescale < 7; prescale++) {
+ if (actual && actual <= rate)
+ break;
+ actual >>= 1;
+ }
+
+ spin_unlock_irqrestore(&clk_lock, flags);
+ return (prescale < 7) ? actual : -ENOENT;
+}
+EXPORT_SYMBOL(clk_round_rate);
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long flags;
+ unsigned prescale;
+ unsigned long actual;
+
+ if (!clk->programmable)
+ return -EINVAL;
+ if (clk->users)
+ return -EBUSY;
+ spin_lock_irqsave(&clk_lock, flags);
+
+ actual = clk->parent->rate_hz;
+ for (prescale = 0; prescale < 7; prescale++) {
+ if (actual && actual <= rate) {
+ u32 pckr;
+
+ pckr = at91_sys_read(AT91_PMC_PCKR(clk->id));
+ pckr &= 0x03;
+ pckr |= prescale << 2;
+ at91_sys_write(AT91_PMC_PCKR(clk->id), pckr);
+ clk->rate_hz = actual;
+ break;
+ }
+ actual >>= 1;
+ }
+
+ spin_unlock_irqrestore(&clk_lock, flags);
+ return (prescale < 7) ? actual : -ENOENT;
+}
+EXPORT_SYMBOL(clk_set_rate);
+
+struct clk *clk_get_parent(struct clk *clk)
+{
+ return clk->parent;
+}
+EXPORT_SYMBOL(clk_get_parent);
+
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ unsigned long flags;
+
+ if (clk->users)
+ return -EBUSY;
+ if (!parent->primary || !clk->programmable)
+ return -EINVAL;
+ spin_lock_irqsave(&clk_lock, flags);
+
+ clk->rate_hz = parent->rate_hz;
+ clk->parent = parent;
+ at91_sys_write(AT91_PMC_PCKR(clk->id), parent->id);
+
+ spin_unlock_irqrestore(&clk_lock, flags);
+ return 0;
+}
+EXPORT_SYMBOL(clk_set_parent);
+
+#endif /* CONFIG_AT91_PROGRAMMABLE_CLOCKS */
+
+/*------------------------------------------------------------------------*/
+
+#ifdef CONFIG_DEBUG_FS
+
+static int at91_clk_show(struct seq_file *s, void *unused)
+{
+ u32 scsr, pcsr, sr;
+ unsigned i;
+
+ seq_printf(s, "SCSR = %8x\n", scsr = at91_sys_read(AT91_PMC_SCSR));
+ seq_printf(s, "PCSR = %8x\n", pcsr = at91_sys_read(AT91_PMC_PCSR));
+
+ seq_printf(s, "MOR = %8x\n", at91_sys_read(AT91_CKGR_MOR));
+ seq_printf(s, "MCFR = %8x\n", at91_sys_read(AT91_CKGR_MCFR));
+ seq_printf(s, "PLLA = %8x\n", at91_sys_read(AT91_CKGR_PLLAR));
+ seq_printf(s, "PLLB = %8x\n", at91_sys_read(AT91_CKGR_PLLBR));
+
+ seq_printf(s, "MCKR = %8x\n", at91_sys_read(AT91_PMC_MCKR));
+ for (i = 0; i < 4; i++)
+ seq_printf(s, "PCK%d = %8x\n", i, at91_sys_read(AT91_PMC_PCKR(i)));
+ seq_printf(s, "SR = %8x\n", sr = at91_sys_read(AT91_PMC_SR));
+
+ seq_printf(s, "\n");
+
+ for (i = 0; i < ARRAY_SIZE(clock_list); i++) {
+ char *state;
+ struct clk *clk = clock_list[i];
+
+ if (clk->mode == pmc_sys_mode)
+ state = (scsr & clk->pmc_mask) ? "on" : "off";
+ else if (clk->mode == pmc_periph_mode)
+ state = (pcsr & clk->pmc_mask) ? "on" : "off";
+ else if (clk->pmc_mask)
+ state = (sr & clk->pmc_mask) ? "on" : "off";
+ else if (clk == &clk32k || clk == &main_clk)
+ state = "on";
+ else
+ state = "";
+
+ seq_printf(s, "%-10s users=%d %-3s %9ld Hz %s\n",
+ clk->name, clk->users, state, clk_get_rate(clk),
+ clk->parent ? clk->parent->name : "");
+ }
+ return 0;
+}
+
+static int at91_clk_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, at91_clk_show, NULL);
+}
+
+static struct file_operations at91_clk_operations = {
+ .open = at91_clk_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init at91_clk_debugfs_init(void)
+{
+ /* /sys/kernel/debug/at91_clk */
+ (void) debugfs_create_file("at91_clk", S_IFREG | S_IRUGO, NULL, NULL, &at91_clk_operations);
+
+ return 0;
+}
+postcore_initcall(at91_clk_debugfs_init);
+
+#endif
+
+/*------------------------------------------------------------------------*/
+
+static u32 __init at91_pll_rate(struct clk *pll, u32 freq, u32 reg)
+{
+ unsigned mul, div;
+
+ div = reg & 0xff;
+ mul = (reg >> 16) & 0x7ff;
+ if (div && mul) {
+ freq /= div;
+ freq *= mul + 1;
+ } else
+ freq = 0;
+ if (pll == &pllb && (reg & (1 << 28)))
+ freq /= 2;
+ return freq;
+}
+
+static unsigned __init at91_pll_calc(unsigned main_freq, unsigned out_freq)
+{
+ unsigned i, div = 0, mul = 0, diff = 1 << 30;
+ unsigned ret = (out_freq > 155000000) ? 0xbe00 : 0x3e00;
+
+ /* PLL output max 240 MHz (or 180 MHz per errata) */
+ if (out_freq > 240000000)
+ goto fail;
+
+ for (i = 1; i < 256; i++) {
+ int diff1;
+ unsigned input, mul1;
+
+ /*
+ * PLL input between 1MHz and 32MHz per spec, but lower
+ * frequences seem necessary in some cases so allow 100K.
+ */
+ input = main_freq / i;
+ if (input < 100000)
+ continue;
+ if (input > 32000000)
+ continue;
+
+ mul1 = out_freq / input;
+ if (mul1 > 2048)
+ continue;
+ if (mul1 < 2)
+ goto fail;
+
+ diff1 = out_freq - input * mul1;
+ if (diff1 < 0)
+ diff1 = -diff1;
+ if (diff > diff1) {
+ diff = diff1;
+ div = i;
+ mul = mul1;
+ if (diff == 0)
+ break;
+ }
+ }
+ if (i == 256 && diff > (out_freq >> 5))
+ goto fail;
+ return ret | ((mul - 1) << 16) | div;
+fail:
+ return 0;
+}
+
+int __init at91_clock_init(unsigned long main_clock)
+{
+ unsigned tmp, freq, mckr;
+
+ spin_lock_init(&clk_lock);
+
+ /*
+ * When the bootloader initialized the main oscillator correctly,
+ * there's no problem using the cycle counter. But if it didn't,
+ * or when using oscillator bypass mode, we must be told the speed
+ * of the main clock.
+ */
+ if (!main_clock) {
+ do {
+ tmp = at91_sys_read(AT91_CKGR_MCFR);
+ } while (!(tmp & 0x10000));
+ main_clock = (tmp & 0xffff) * (AT91_SLOW_CLOCK / 16);
+ }
+ main_clk.rate_hz = main_clock;
+
+ /* report if PLLA is more than mildly overclocked */
+ plla.rate_hz = at91_pll_rate(&plla, main_clock, at91_sys_read(AT91_CKGR_PLLAR));
+ if (plla.rate_hz > 209000000)
+ pr_info("Clocks: PLLA overclocked, %ld MHz\n", plla.rate_hz / 1000000);
+
+ /*
+ * USB clock init: choose 48 MHz PLLB value, turn all clocks off,
+ * disable 48MHz clock during usb peripheral suspend.
+ *
+ * REVISIT: assumes MCK doesn't derive from PLLB!
+ */
+ at91_pllb_usb_init = at91_pll_calc(main_clock, 48000000 * 2) | 0x10000000;
+ pllb.rate_hz = at91_pll_rate(&pllb, main_clock, at91_pllb_usb_init);
+ at91_sys_write(AT91_PMC_PCDR, (1 << AT91_ID_UHP) | (1 << AT91_ID_UDP));
+ at91_sys_write(AT91_PMC_SCDR, AT91_PMC_UHP | AT91_PMC_UDP);
+ at91_sys_write(AT91_CKGR_PLLBR, 0);
+ at91_sys_write(AT91_PMC_SCER, AT91_PMC_MCKUDP);
+
+ /*
+ * MCK and CPU derive from one of those primary clocks.
+ * For now, assume this parentage won't change.
+ */
+ mckr = at91_sys_read(AT91_PMC_MCKR);
+ mck.parent = clock_list[mckr & AT91_PMC_CSS];
+ mck.parent->users++;
+ freq = mck.parent->rate_hz;
+ freq /= (1 << ((mckr >> 2) & 3)); /* prescale */
+ mck.rate_hz = freq / (1 + ((mckr >> 8) & 3)); /* mdiv */
+
+ printk("Clocks: CPU %u MHz, master %u MHz, main %u.%03u MHz\n",
+ freq / 1000000, (unsigned) mck.rate_hz / 1000000,
+ (unsigned) main_clock / 1000000,
+ ((unsigned) main_clock % 1000000) / 1000);
+
+ /* FIXME get rid of master_clock global */
+ at91_master_clock = mck.rate_hz;
+
+#ifdef CONFIG_AT91_PROGRAMMABLE_CLOCKS
+ /* establish PCK0..PCK3 parentage */
+ for (tmp = 0; tmp < ARRAY_SIZE(clock_list); tmp++) {
+ struct clk *clk = clock_list[tmp], *parent;
+ u32 pckr;
+
+ if (!clk->programmable)
+ continue;
+
+ pckr = at91_sys_read(AT91_PMC_PCKR(clk->id));
+ parent = clock_list[pckr & 3];
+ clk->parent = parent;
+ clk->rate_hz = parent->rate_hz / (1 << ((pckr >> 2) & 3));
+ }
+#else
+ /* disable unused clocks */
+ at91_sys_write(AT91_PMC_SCDR, AT91_PMC_PCK0 | AT91_PMC_PCK1 | AT91_PMC_PCK2 | AT91_PMC_PCK3);
+#endif /* CONFIG_AT91_PROGRAMMABLE_CLOCKS */
+
+ /* FIXME several unused clocks may still be active... provide
+ * a CONFIG option to turn off all unused clocks at some point
+ * before driver init starts.
+ */
+
+ return 0;
+}
diff --git a/arch/arm/mach-at91rm9200/common.c b/arch/arm/mach-at91rm9200/common.c
new file mode 100644
index 000000000000..3848fd2d5596
--- /dev/null
+++ b/arch/arm/mach-at91rm9200/common.c
@@ -0,0 +1,115 @@
+/*
+ * arch/arm/mach-at91rm9200/common.c
+ *
+ * Copyright (C) 2005 SAN People
+ *
+ * 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.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <asm/arch/hardware.h>
+
+static struct map_desc at91rm9200_io_desc[] __initdata = {
+ {
+ .virtual = AT91_VA_BASE_SYS,
+ .pfn = __phys_to_pfn(AT91_BASE_SYS),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = AT91_VA_BASE_SPI,
+ .pfn = __phys_to_pfn(AT91_BASE_SPI),
+ .length = SZ_16K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = AT91_VA_BASE_SSC2,
+ .pfn = __phys_to_pfn(AT91_BASE_SSC2),
+ .length = SZ_16K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = AT91_VA_BASE_SSC1,
+ .pfn = __phys_to_pfn(AT91_BASE_SSC1),
+ .length = SZ_16K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = AT91_VA_BASE_SSC0,
+ .pfn = __phys_to_pfn(AT91_BASE_SSC0),
+ .length = SZ_16K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = AT91_VA_BASE_US3,
+ .pfn = __phys_to_pfn(AT91_BASE_US3),
+ .length = SZ_16K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = AT91_VA_BASE_US2,
+ .pfn = __phys_to_pfn(AT91_BASE_US2),
+ .length = SZ_16K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = AT91_VA_BASE_US1,
+ .pfn = __phys_to_pfn(AT91_BASE_US1),
+ .length = SZ_16K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = AT91_VA_BASE_US0,
+ .pfn = __phys_to_pfn(AT91_BASE_US0),
+ .length = SZ_16K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = AT91_VA_BASE_EMAC,
+ .pfn = __phys_to_pfn(AT91_BASE_EMAC),
+ .length = SZ_16K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = AT91_VA_BASE_TWI,
+ .pfn = __phys_to_pfn(AT91_BASE_TWI),
+ .length = SZ_16K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = AT91_VA_BASE_MCI,
+ .pfn = __phys_to_pfn(AT91_BASE_MCI),
+ .length = SZ_16K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = AT91_VA_BASE_UDP,
+ .pfn = __phys_to_pfn(AT91_BASE_UDP),
+ .length = SZ_16K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = AT91_VA_BASE_TCB1,
+ .pfn = __phys_to_pfn(AT91_BASE_TCB1),
+ .length = SZ_16K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = AT91_VA_BASE_TCB0,
+ .pfn = __phys_to_pfn(AT91_BASE_TCB0),
+ .length = SZ_16K,
+ .type = MT_DEVICE,
+ },
+};
+
+void __init at91rm9200_map_io(void)
+{
+ iotable_init(at91rm9200_io_desc, ARRAY_SIZE(at91rm9200_io_desc));
+}
+
+
+unsigned long at91_master_clock;
+
+EXPORT_SYMBOL(at91_master_clock);
+
+
+int at91_serial_map[AT91_NR_UART];
+int at91_console_port;
+
+EXPORT_SYMBOL(at91_serial_map);
+EXPORT_SYMBOL(at91_console_port);
diff --git a/arch/arm/mach-at91rm9200/devices.c b/arch/arm/mach-at91rm9200/devices.c
new file mode 100644
index 000000000000..8df3e5245651
--- /dev/null
+++ b/arch/arm/mach-at91rm9200/devices.c
@@ -0,0 +1,291 @@
+/*
+ * arch/arm/mach-at91rm9200/devices.c
+ *
+ * Copyright (C) 2005 Thibaut VARENE <varenet@parisc-linux.org>
+ * Copyright (C) 2005 David Brownell
+ *
+ * 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.
+ *
+ */
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <linux/config.h>
+#include <linux/platform_device.h>
+
+#include <asm/arch/board.h>
+#include <asm/arch/pio.h>
+
+
+/* --------------------------------------------------------------------
+ * USB Host
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE)
+static u64 ohci_dmamask = 0xffffffffUL;
+static struct at91_usbh_data usbh_data;
+
+static struct resource at91rm9200_usbh_resource[] = {
+ [0] = {
+ .start = AT91_UHP_BASE,
+ .end = AT91_UHP_BASE + SZ_1M -1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = AT91_ID_UHP,
+ .end = AT91_ID_UHP,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device at91rm9200_usbh_device = {
+ .name = "at91rm9200-ohci",
+ .id = -1,
+ .dev = {
+ .dma_mask = &ohci_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ .platform_data = &usbh_data,
+ },
+ .resource = at91rm9200_usbh_resource,
+ .num_resources = ARRAY_SIZE(at91rm9200_usbh_resource),
+};
+
+void __init at91_add_device_usbh(struct at91_usbh_data *data)
+{
+ if (!data)
+ return;
+
+ usbh_data = *data;
+ platform_device_register(&at91rm9200_usbh_device);
+}
+#else
+void __init at91_add_device_usbh(struct at91_usbh_data *data) {}
+#endif
+
+
+/* --------------------------------------------------------------------
+ * USB Device (Gadget)
+ * -------------------------------------------------------------------- */
+
+#ifdef CONFIG_USB_GADGET_AT91
+static struct at91_udc_data udc_data;
+
+static struct resource at91_udc_resources[] = {
+ {
+ .start = AT91_BASE_UDP,
+ .end = AT91_BASE_UDP + SZ_16K - 1,
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+static struct platform_device at91rm9200_udc_device = {
+ .name = "at91_udc",
+ .id = -1,
+ .dev = {
+ .platform_data = &udc_data,
+ },
+ .resource = at91_udc_resources,
+ .num_resources = ARRAY_SIZE(at91_udc_resources),
+};
+
+void __init at91_add_device_udc(struct at91_udc_data *data)
+{
+ if (!data)
+ return;
+
+ if (data->vbus_pin) {
+ at91_set_gpio_input(data->vbus_pin, 0);
+ at91_set_deglitch(data->vbus_pin, 1);
+ }
+ if (data->pullup_pin)
+ at91_set_gpio_output(data->pullup_pin, 0);
+
+ udc_data = *data;
+ platform_device_register(&at91rm9200_udc_device);
+}
+#else
+void __init at91_add_device_udc(struct at91_udc_data *data) {}
+#endif
+
+
+/* --------------------------------------------------------------------
+ * Ethernet
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_ARM_AT91_ETHER) || defined(CONFIG_ARM_AT91_ETHER_MODULE)
+static u64 eth_dmamask = 0xffffffffUL;
+static struct at91_eth_data eth_data;
+
+static struct platform_device at91rm9200_eth_device = {
+ .name = "at91_ether",
+ .id = -1,
+ .dev = {
+ .dma_mask = &eth_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ .platform_data = &eth_data,
+ },
+ .num_resources = 0,
+};
+
+void __init at91_add_device_eth(struct at91_eth_data *data)
+{
+ if (!data)
+ return;
+
+ if (data->phy_irq_pin) {
+ at91_set_gpio_input(data->phy_irq_pin, 0);
+ at91_set_deglitch(data->phy_irq_pin, 1);
+ }
+
+ /* Pins used for MII and RMII */
+ at91_set_A_periph(AT91_PIN_PA16, 0); /* EMDIO */
+ at91_set_A_periph(AT91_PIN_PA15, 0); /* EMDC */
+ at91_set_A_periph(AT91_PIN_PA14, 0); /* ERXER */
+ at91_set_A_periph(AT91_PIN_PA13, 0); /* ERX1 */
+ at91_set_A_periph(AT91_PIN_PA12, 0); /* ERX0 */
+ at91_set_A_periph(AT91_PIN_PA11, 0); /* ECRS_ECRSDV */
+ at91_set_A_periph(AT91_PIN_PA10, 0); /* ETX1 */
+ at91_set_A_periph(AT91_PIN_PA9, 0); /* ETX0 */
+ at91_set_A_periph(AT91_PIN_PA8, 0); /* ETXEN */
+ at91_set_A_periph(AT91_PIN_PA7, 0); /* ETXCK_EREFCK */
+
+ if (!data->is_rmii) {
+ at91_set_B_periph(AT91_PIN_PB19, 0); /* ERXCK */
+ at91_set_B_periph(AT91_PIN_PB18, 0); /* ECOL */
+ at91_set_B_periph(AT91_PIN_PB17, 0); /* ERXDV */
+ at91_set_B_periph(AT91_PIN_PB16, 0); /* ERX3 */
+ at91_set_B_periph(AT91_PIN_PB15, 0); /* ERX2 */
+ at91_set_B_periph(AT91_PIN_PB14, 0); /* ETXER */
+ at91_set_B_periph(AT91_PIN_PB13, 0); /* ETX3 */
+ at91_set_B_periph(AT91_PIN_PB12, 0); /* ETX2 */
+ }
+
+ eth_data = *data;
+ platform_device_register(&at91rm9200_eth_device);
+}
+#else
+void __init at91_add_device_eth(struct at91_eth_data *data) {}
+#endif
+
+
+/* --------------------------------------------------------------------
+ * Compact Flash / PCMCIA
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_AT91_CF) || defined(CONFIG_AT91_CF_MODULE)
+static struct at91_cf_data cf_data;
+
+static struct platform_device at91rm9200_cf_device = {
+ .name = "at91_cf",
+ .id = -1,
+ .dev = {
+ .platform_data = &cf_data,
+ },
+ .num_resources = 0,
+};
+
+void __init at91_add_device_cf(struct at91_cf_data *data)
+{
+ if (!data)
+ return;
+
+ /* input/irq */
+ if (data->irq_pin) {
+ at91_set_gpio_input(data->irq_pin, 1);
+ at91_set_deglitch(data->irq_pin, 1);
+ }
+ at91_set_gpio_input(data->det_pin, 1);
+ at91_set_deglitch(data->det_pin, 1);
+
+ /* outputs, initially off */
+ if (data->vcc_pin)
+ at91_set_gpio_output(data->vcc_pin, 0);
+ at91_set_gpio_output(data->rst_pin, 0);
+
+ cf_data = *data;
+ platform_device_register(&at91rm9200_cf_device);
+}
+#else
+void __init at91_add_device_cf(struct at91_cf_data *data) {}
+#endif
+
+
+/* --------------------------------------------------------------------
+ * MMC / SD
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_MMC_AT91RM9200) || defined(CONFIG_MMC_AT91RM9200_MODULE)
+static u64 mmc_dmamask = 0xffffffffUL;
+static struct at91_mmc_data mmc_data;
+
+static struct resource at91_mmc_resources[] = {
+ {
+ .start = AT91_BASE_MCI,
+ .end = AT91_BASE_MCI + SZ_16K - 1,
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+static struct platform_device at91rm9200_mmc_device = {
+ .name = "at91rm9200_mci",
+ .id = -1,
+ .dev = {
+ .dma_mask = &mmc_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ .platform_data = &mmc_data,
+ },
+ .resource = at91_mmc_resources,
+ .num_resources = ARRAY_SIZE(at91_mmc_resources),
+};
+
+void __init at91_add_device_mmc(struct at91_mmc_data *data)
+{
+ if (!data)
+ return;
+
+ /* input/irq */
+ if (data->det_pin) {
+ at91_set_gpio_input(data->det_pin, 1);
+ at91_set_deglitch(data->det_pin, 1);
+ }
+ if (data->wp_pin)
+ at91_set_gpio_input(data->wp_pin, 1);
+
+ /* CLK */
+ at91_set_A_periph(AT91_PIN_PA27, 0);
+
+ if (data->is_b) {
+ /* CMD */
+ at91_set_B_periph(AT91_PIN_PA8, 0);
+
+ /* DAT0, maybe DAT1..DAT3 */
+ at91_set_B_periph(AT91_PIN_PA9, 0);
+ if (data->wire4) {
+ at91_set_B_periph(AT91_PIN_PA10, 0);
+ at91_set_B_periph(AT91_PIN_PA11, 0);
+ at91_set_B_periph(AT91_PIN_PA12, 0);
+ }
+ } else {
+ /* CMD */
+ at91_set_A_periph(AT91_PIN_PA28, 0);
+
+ /* DAT0, maybe DAT1..DAT3 */
+ at91_set_A_periph(AT91_PIN_PA29, 0);
+ if (data->wire4) {
+ at91_set_B_periph(AT91_PIN_PB3, 0);
+ at91_set_B_periph(AT91_PIN_PB4, 0);
+ at91_set_B_periph(AT91_PIN_PB5, 0);
+ }
+ }
+
+ mmc_data = *data;
+ platform_device_register(&at91rm9200_mmc_device);
+}
+#else
+void __init at91_add_device_mmc(struct at91_mmc_data *data) {}
+#endif
+
+/* -------------------------------------------------------------------- */
diff --git a/arch/arm/mach-at91rm9200/generic.h b/arch/arm/mach-at91rm9200/generic.h
new file mode 100644
index 000000000000..9bd541eba0a0
--- /dev/null
+++ b/arch/arm/mach-at91rm9200/generic.h
@@ -0,0 +1,18 @@
+/*
+ * linux/arch/arm/mach-at91rm9200/generic.h
+ *
+ * Copyright (C) 2005 David Brownell
+ *
+ * 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.
+ */
+
+void at91_gpio_irq_setup(unsigned banks);
+
+struct sys_timer;
+extern struct sys_timer at91rm9200_timer;
+
+extern void __init at91rm9200_map_io(void);
+
+extern int __init at91_clock_init(unsigned long main_clock);
diff --git a/arch/arm/mach-at91rm9200/gpio.c b/arch/arm/mach-at91rm9200/gpio.c
new file mode 100644
index 000000000000..2fd2ef583e4d
--- /dev/null
+++ b/arch/arm/mach-at91rm9200/gpio.c
@@ -0,0 +1,302 @@
+/*
+ * linux/arch/arm/mach-at91rm9200/gpio.c
+ *
+ * Copyright (C) 2005 HP Labs
+ *
+ * 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.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+
+#include <asm/io.h>
+#include <asm/mach/irq.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/gpio.h>
+
+static const u32 pio_controller_offset[4] = {
+ AT91_PIOA,
+ AT91_PIOB,
+ AT91_PIOC,
+ AT91_PIOD,
+};
+
+static inline void __iomem *pin_to_controller(unsigned pin)
+{
+ void __iomem *sys_base = (void __iomem *) AT91_VA_BASE_SYS;
+
+ pin -= PIN_BASE;
+ pin /= 32;
+ if (likely(pin < BGA_GPIO_BANKS))
+ return sys_base + pio_controller_offset[pin];
+
+ return NULL;
+}
+
+static inline unsigned pin_to_mask(unsigned pin)
+{
+ pin -= PIN_BASE;
+ return 1 << (pin % 32);
+}
+
+
+/*--------------------------------------------------------------------------*/
+
+/* Not all hardware capabilities are exposed through these calls; they
+ * only encapsulate the most common features and modes. (So if you
+ * want to change signals in groups, do it directly.)
+ *
+ * Bootloaders will usually handle some of the pin multiplexing setup.
+ * The intent is certainly that by the time Linux is fully booted, all
+ * pins should have been fully initialized. These setup calls should
+ * only be used by board setup routines, or possibly in driver probe().
+ *
+ * For bootloaders doing all that setup, these calls could be inlined
+ * as NOPs so Linux won't duplicate any setup code
+ */
+
+
+/*
+ * mux the pin to the "A" internal peripheral role.
+ */
+int __init_or_module at91_set_A_periph(unsigned pin, int use_pullup)
+{
+ void __iomem *pio = pin_to_controller(pin);
+ unsigned mask = pin_to_mask(pin);
+
+ if (!pio)
+ return -EINVAL;
+
+ __raw_writel(mask, pio + PIO_IDR);
+ __raw_writel(mask, pio + (use_pullup ? PIO_PUER : PIO_PUDR));
+ __raw_writel(mask, pio + PIO_ASR);
+ __raw_writel(mask, pio + PIO_PDR);
+ return 0;
+}
+EXPORT_SYMBOL(at91_set_A_periph);
+
+
+/*
+ * mux the pin to the "B" internal peripheral role.
+ */
+int __init_or_module at91_set_B_periph(unsigned pin, int use_pullup)
+{
+ void __iomem *pio = pin_to_controller(pin);
+ unsigned mask = pin_to_mask(pin);
+
+ if (!pio)
+ return -EINVAL;
+
+ __raw_writel(mask, pio + PIO_IDR);
+ __raw_writel(mask, pio + (use_pullup ? PIO_PUER : PIO_PUDR));
+ __raw_writel(mask, pio + PIO_BSR);
+ __raw_writel(mask, pio + PIO_PDR);
+ return 0;
+}
+EXPORT_SYMBOL(at91_set_B_periph);
+
+
+/*
+ * mux the pin to the gpio controller (instead of "A" or "B" peripheral), and
+ * configure it for an input.
+ */
+int __init_or_module at91_set_gpio_input(unsigned pin, int use_pullup)
+{
+ void __iomem *pio = pin_to_controller(pin);
+ unsigned mask = pin_to_mask(pin);
+
+ if (!pio)
+ return -EINVAL;
+
+ __raw_writel(mask, pio + PIO_IDR);
+ __raw_writel(mask, pio + (use_pullup ? PIO_PUER : PIO_PUDR));
+ __raw_writel(mask, pio + PIO_ODR);
+ __raw_writel(mask, pio + PIO_PER);
+ return 0;
+}
+EXPORT_SYMBOL(at91_set_gpio_input);
+
+
+/*
+ * mux the pin to the gpio controller (instead of "A" or "B" peripheral),
+ * and configure it for an output.
+ */
+int __init_or_module at91_set_gpio_output(unsigned pin, int value)
+{
+ void __iomem *pio = pin_to_controller(pin);
+ unsigned mask = pin_to_mask(pin);
+
+ if (!pio)
+ return -EINVAL;
+
+ __raw_writel(mask, pio + PIO_IDR);
+ __raw_writel(mask, pio + PIO_PUDR);
+ __raw_writel(mask, pio + (value ? PIO_SODR : PIO_CODR));
+ __raw_writel(mask, pio + PIO_OER);
+ __raw_writel(mask, pio + PIO_PER);
+ return 0;
+}
+EXPORT_SYMBOL(at91_set_gpio_output);
+
+
+/*
+ * enable/disable the glitch filter; mostly used with IRQ handling.
+ */
+int __init_or_module at91_set_deglitch(unsigned pin, int is_on)
+{
+ void __iomem *pio = pin_to_controller(pin);
+ unsigned mask = pin_to_mask(pin);
+
+ if (!pio)
+ return -EINVAL;
+ __raw_writel(mask, pio + (is_on ? PIO_IFER : PIO_IFDR));
+ return 0;
+}
+EXPORT_SYMBOL(at91_set_deglitch);
+
+/*--------------------------------------------------------------------------*/
+
+
+/*
+ * assuming the pin is muxed as a gpio output, set its value.
+ */
+int at91_set_gpio_value(unsigned pin, int value)
+{
+ void __iomem *pio = pin_to_controller(pin);
+ unsigned mask = pin_to_mask(pin);
+
+ if (!pio)
+ return -EINVAL;
+ __raw_writel(mask, pio + (value ? PIO_SODR : PIO_CODR));
+ return 0;
+}
+EXPORT_SYMBOL(at91_set_gpio_value);
+
+
+/*
+ * read the pin's value (works even if it's not muxed as a gpio).
+ */
+int at91_get_gpio_value(unsigned pin)
+{
+ void __iomem *pio = pin_to_controller(pin);
+ unsigned mask = pin_to_mask(pin);
+ u32 pdsr;
+
+ if (!pio)
+ return -EINVAL;
+ pdsr = __raw_readl(pio + PIO_PDSR);
+ return (pdsr & mask) != 0;
+}
+EXPORT_SYMBOL(at91_get_gpio_value);
+
+/*--------------------------------------------------------------------------*/
+
+
+/* Several AIC controller irqs are dispatched through this GPIO handler.
+ * To use any AT91_PIN_* as an externally triggered IRQ, first call
+ * at91_set_gpio_input() then maybe enable its glitch filter.
+ * Then just request_irq() with the pin ID; it works like any ARM IRQ
+ * handler, though it always triggers on rising and falling edges.
+ *
+ * Alternatively, certain pins may be used directly as IRQ0..IRQ6 after
+ * configuring them with at91_set_a_periph() or at91_set_b_periph().
+ * IRQ0..IRQ6 should be configurable, e.g. level vs edge triggering.
+ */
+
+static void gpio_irq_mask(unsigned pin)
+{
+ void __iomem *pio = pin_to_controller(pin);
+ unsigned mask = pin_to_mask(pin);
+
+ if (pio)
+ __raw_writel(mask, pio + PIO_IDR);
+}
+
+static void gpio_irq_unmask(unsigned pin)
+{
+ void __iomem *pio = pin_to_controller(pin);
+ unsigned mask = pin_to_mask(pin);
+
+ if (pio)
+ __raw_writel(mask, pio + PIO_IER);
+}
+
+static int gpio_irq_type(unsigned pin, unsigned type)
+{
+ return (type == IRQT_BOTHEDGE) ? 0 : -EINVAL;
+}
+
+static struct irqchip gpio_irqchip = {
+ .mask = gpio_irq_mask,
+ .unmask = gpio_irq_unmask,
+ .set_type = gpio_irq_type,
+};
+
+static void gpio_irq_handler(unsigned irq, struct irqdesc *desc, struct pt_regs *regs)
+{
+ unsigned pin;
+ struct irqdesc *gpio;
+ void __iomem *pio;
+ u32 isr;
+
+ pio = (void __force __iomem *) desc->chipdata;
+
+ /* temporarily mask (level sensitive) parent IRQ */
+ desc->chip->ack(irq);
+ for (;;) {
+ isr = __raw_readl(pio + PIO_ISR) & __raw_readl(pio + PIO_IMR);
+ if (!isr)
+ break;
+
+ pin = (unsigned) desc->data;
+ gpio = &irq_desc[pin];
+
+ while (isr) {
+ if (isr & 1)
+ gpio->handle(pin, gpio, regs);
+ pin++;
+ gpio++;
+ isr >>= 1;
+ }
+ }
+ desc->chip->unmask(irq);
+ /* now it may re-trigger */
+}
+
+/* call this from board-specific init_irq */
+void __init at91_gpio_irq_setup(unsigned banks)
+{
+ unsigned pioc, pin, id;
+
+ if (banks > 4)
+ banks = 4;
+ for (pioc = 0, pin = PIN_BASE, id = AT91_ID_PIOA;
+ pioc < banks;
+ pioc++, id++) {
+ void __iomem *controller;
+ unsigned i;
+
+ controller = (void __iomem *) AT91_VA_BASE_SYS + pio_controller_offset[pioc];
+ __raw_writel(~0, controller + PIO_IDR);
+
+ set_irq_data(id, (void *) pin);
+ set_irq_chipdata(id, (void __force *) controller);
+
+ for (i = 0; i < 32; i++, pin++) {
+ set_irq_chip(pin, &gpio_irqchip);
+ set_irq_handler(pin, do_simple_IRQ);
+ set_irq_flags(pin, IRQF_VALID);
+ }
+
+ set_irq_chained_handler(id, gpio_irq_handler);
+
+ /* enable the PIO peripheral clock */
+ at91_sys_write(AT91_PMC_PCER, 1 << id);
+ }
+ pr_info("AT91: %d gpio irqs in %d banks\n", pin - PIN_BASE, banks);
+}
diff --git a/arch/arm/mach-at91rm9200/irq.c b/arch/arm/mach-at91rm9200/irq.c
new file mode 100644
index 000000000000..cb62bc83a1dd
--- /dev/null
+++ b/arch/arm/mach-at91rm9200/irq.c
@@ -0,0 +1,170 @@
+/*
+ * linux/arch/arm/mach-at91rm9200/irq.c
+ *
+ * Copyright (C) 2004 SAN People
+ * Copyright (C) 2004 ATMEL
+ * Copyright (C) Rick Bronson
+ *
+ * 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/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/types.h>
+
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+#include <asm/setup.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/map.h>
+
+#include "generic.h"
+
+/*
+ * The default interrupt priority levels (0 = lowest, 7 = highest).
+ */
+static unsigned int at91rm9200_default_irq_priority[NR_AIC_IRQS] __initdata = {
+ 7, /* Advanced Interrupt Controller */
+ 7, /* System Peripheral */
+ 0, /* Parallel IO Controller A */
+ 0, /* Parallel IO Controller B */
+ 0, /* Parallel IO Controller C */
+ 0, /* Parallel IO Controller D */
+ 6, /* USART 0 */
+ 6, /* USART 1 */
+ 6, /* USART 2 */
+ 6, /* USART 3 */
+ 0, /* Multimedia Card Interface */
+ 4, /* USB Device Port */
+ 0, /* Two-Wire Interface */
+ 6, /* Serial Peripheral Interface */
+ 5, /* Serial Synchronous Controller */
+ 5, /* Serial Synchronous Controller */
+ 5, /* Serial Synchronous Controller */
+ 0, /* Timer Counter 0 */
+ 0, /* Timer Counter 1 */
+ 0, /* Timer Counter 2 */
+ 0, /* Timer Counter 3 */
+ 0, /* Timer Counter 4 */
+ 0, /* Timer Counter 5 */
+ 3, /* USB Host port */
+ 3, /* Ethernet MAC */
+ 0, /* Advanced Interrupt Controller */
+ 0, /* Advanced Interrupt Controller */
+ 0, /* Advanced Interrupt Controller */
+ 0, /* Advanced Interrupt Controller */
+ 0, /* Advanced Interrupt Controller */
+ 0, /* Advanced Interrupt Controller */
+ 0 /* Advanced Interrupt Controller */
+};
+
+
+static void at91rm9200_mask_irq(unsigned int irq)
+{
+ /* Disable interrupt on AIC */
+ at91_sys_write(AT91_AIC_IDCR, 1 << irq);
+}
+
+static void at91rm9200_unmask_irq(unsigned int irq)
+{
+ /* Enable interrupt on AIC */
+ at91_sys_write(AT91_AIC_IECR, 1 << irq);
+}
+
+static int at91rm9200_irq_type(unsigned irq, unsigned type)
+{
+ unsigned int smr, srctype;
+
+ /* change triggering only for FIQ and external IRQ0..IRQ6 */
+ if ((irq < AT91_ID_IRQ0) && (irq != AT91_ID_FIQ))
+ return -EINVAL;
+
+ switch (type) {
+ case IRQT_HIGH:
+ srctype = AT91_AIC_SRCTYPE_HIGH;
+ break;
+ case IRQT_RISING:
+ srctype = AT91_AIC_SRCTYPE_RISING;
+ break;
+ case IRQT_LOW:
+ srctype = AT91_AIC_SRCTYPE_LOW;
+ break;
+ case IRQT_FALLING:
+ srctype = AT91_AIC_SRCTYPE_FALLING;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ smr = at91_sys_read(AT91_AIC_SMR(irq)) & ~AT91_AIC_SRCTYPE;
+ at91_sys_write(AT91_AIC_SMR(irq), smr | srctype);
+ return 0;
+}
+
+static struct irqchip at91rm9200_irq_chip = {
+ .ack = at91rm9200_mask_irq,
+ .mask = at91rm9200_mask_irq,
+ .unmask = at91rm9200_unmask_irq,
+ .set_type = at91rm9200_irq_type,
+};
+
+/*
+ * Initialize the AIC interrupt controller.
+ */
+void __init at91rm9200_init_irq(unsigned int priority[NR_AIC_IRQS])
+{
+ unsigned int i;
+
+ /* No priority list specified for this board -> use defaults */
+ if (priority == NULL)
+ priority = at91rm9200_default_irq_priority;
+
+ /*
+ * The IVR is used by macro get_irqnr_and_base to read and verify.
+ * The irq number is NR_AIC_IRQS when a spurious interrupt has occurred.
+ */
+ for (i = 0; i < NR_AIC_IRQS; i++) {
+ /* Put irq number in Source Vector Register: */
+ at91_sys_write(AT91_AIC_SVR(i), i);
+ /* Store the Source Mode Register as defined in table above */
+ at91_sys_write(AT91_AIC_SMR(i), AT91_AIC_SRCTYPE_LOW | priority[i]);
+
+ set_irq_chip(i, &at91rm9200_irq_chip);
+ set_irq_handler(i, do_level_IRQ);
+ set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
+
+ /* Perform 8 End Of Interrupt Command to make sure AIC will not Lock out nIRQ */
+ if (i < 8)
+ at91_sys_write(AT91_AIC_EOICR, 0);
+ }
+
+ /*
+ * Spurious Interrupt ID in Spurious Vector Register is NR_AIC_IRQS
+ * When there is no current interrupt, the IRQ Vector Register reads the value stored in AIC_SPU
+ */
+ at91_sys_write(AT91_AIC_SPU, NR_AIC_IRQS);
+
+ /* No debugging in AIC: Debug (Protect) Control Register */
+ at91_sys_write(AT91_AIC_DCR, 0);
+
+ /* Disable and clear all interrupts initially */
+ at91_sys_write(AT91_AIC_IDCR, 0xFFFFFFFF);
+ at91_sys_write(AT91_AIC_ICCR, 0xFFFFFFFF);
+}
diff --git a/arch/arm/mach-at91rm9200/time.c b/arch/arm/mach-at91rm9200/time.c
new file mode 100644
index 000000000000..1b6dd2deeb22
--- /dev/null
+++ b/arch/arm/mach-at91rm9200/time.c
@@ -0,0 +1,127 @@
+/*
+ * linux/arch/arm/mach-at91rm9200/time.c
+ *
+ * Copyright (C) 2003 SAN People
+ * Copyright (C) 2003 ATMEL
+ *
+ * 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/config.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mach/time.h>
+
+/*
+ * The ST_CRTR is updated asynchronously to the master clock. It is therefore
+ * necessary to read it twice (with the same value) to ensure accuracy.
+ */
+static inline unsigned long read_CRTR(void) {
+ unsigned long x1, x2;
+
+ do {
+ x1 = at91_sys_read(AT91_ST_CRTR);
+ x2 = at91_sys_read(AT91_ST_CRTR);
+ } while (x1 != x2);
+
+ return x1;
+}
+
+/*
+ * Returns number of microseconds since last timer interrupt. Note that interrupts
+ * will have been disabled by do_gettimeofday()
+ * 'LATCH' is hwclock ticks (see CLOCK_TICK_RATE in timex.h) per jiffy.
+ * 'tick' is usecs per jiffy (linux/timex.h).
+ */
+static unsigned long at91rm9200_gettimeoffset(void)
+{
+ unsigned long elapsed;
+
+ elapsed = (read_CRTR() - at91_sys_read(AT91_ST_RTAR)) & AT91_ST_ALMV;
+
+ return (unsigned long)(elapsed * (tick_nsec / 1000)) / LATCH;
+}
+
+/*
+ * IRQ handler for the timer.
+ */
+static irqreturn_t at91rm9200_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long rtar;
+
+ if (at91_sys_read(AT91_ST_SR) & AT91_ST_PITS) { /* This is a shared interrupt */
+ write_seqlock(&xtime_lock);
+
+ do {
+ timer_tick(regs);
+ rtar = (at91_sys_read(AT91_ST_RTAR) + LATCH) & AT91_ST_ALMV;
+ at91_sys_write(AT91_ST_RTAR, rtar);
+ } while (((read_CRTR() - at91_sys_read(AT91_ST_RTAR)) & AT91_ST_ALMV) >= LATCH);
+
+ write_sequnlock(&xtime_lock);
+
+ return IRQ_HANDLED;
+ }
+ else
+ return IRQ_NONE; /* not handled */
+}
+
+static struct irqaction at91rm9200_timer_irq = {
+ .name = "at91_tick",
+ .flags = SA_SHIRQ | SA_INTERRUPT,
+ .handler = at91rm9200_timer_interrupt
+};
+
+/*
+ * Set up timer interrupt.
+ */
+void __init at91rm9200_timer_init(void)
+{
+ /* Disable all timer interrupts */
+ at91_sys_write(AT91_ST_IDR, AT91_ST_PITS | AT91_ST_WDOVF | AT91_ST_RTTINC | AT91_ST_ALMS);
+ (void) at91_sys_read(AT91_ST_SR); /* Clear any pending interrupts */
+
+ /*
+ * Make IRQs happen for the system timer.
+ */
+ setup_irq(AT91_ID_SYS, &at91rm9200_timer_irq);
+
+ /* Set initial alarm to 0 */
+ at91_sys_write(AT91_ST_RTAR, 0);
+
+ /* Real time counter incremented every 30.51758 microseconds */
+ at91_sys_write(AT91_ST_RTMR, 1);
+
+ /* Set Period Interval timer */
+ at91_sys_write(AT91_ST_PIMR, LATCH);
+
+ /* Change the kernel's 'tick' value to 10009 usec. (the default is 10000) */
+ tick_usec = (LATCH * 1000000) / CLOCK_TICK_RATE;
+
+ /* Enable Period Interval Timer interrupt */
+ at91_sys_write(AT91_ST_IER, AT91_ST_PITS);
+}
+
+struct sys_timer at91rm9200_timer = {
+ .init = at91rm9200_timer_init,
+ .offset = at91rm9200_gettimeoffset,
+};