summaryrefslogtreecommitdiff
path: root/arch/arm/mach-integrator
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-integrator')
-rw-r--r--arch/arm/mach-integrator/Kconfig33
-rw-r--r--arch/arm/mach-integrator/Makefile14
-rw-r--r--arch/arm/mach-integrator/Makefile.boot4
-rw-r--r--arch/arm/mach-integrator/clock.c141
-rw-r--r--arch/arm/mach-integrator/clock.h25
-rw-r--r--arch/arm/mach-integrator/common.h2
-rw-r--r--arch/arm/mach-integrator/core.c271
-rw-r--r--arch/arm/mach-integrator/cpu.c221
-rw-r--r--arch/arm/mach-integrator/dma.c35
-rw-r--r--arch/arm/mach-integrator/impd1.c475
-rw-r--r--arch/arm/mach-integrator/integrator_ap.c302
-rw-r--r--arch/arm/mach-integrator/integrator_cp.c528
-rw-r--r--arch/arm/mach-integrator/leds.c88
-rw-r--r--arch/arm/mach-integrator/lm.c96
-rw-r--r--arch/arm/mach-integrator/pci.c132
-rw-r--r--arch/arm/mach-integrator/pci_v3.c604
-rw-r--r--arch/arm/mach-integrator/time.c213
17 files changed, 3184 insertions, 0 deletions
diff --git a/arch/arm/mach-integrator/Kconfig b/arch/arm/mach-integrator/Kconfig
new file mode 100644
index 00000000000..df97d16390e
--- /dev/null
+++ b/arch/arm/mach-integrator/Kconfig
@@ -0,0 +1,33 @@
+if ARCH_INTEGRATOR
+
+menu "Integrator Options"
+
+config ARCH_INTEGRATOR_AP
+ bool "Support Integrator/AP and Integrator/PP2 platforms"
+ help
+ Include support for the ARM(R) Integrator/AP and
+ Integrator/PP2 platforms.
+
+config ARCH_INTEGRATOR_CP
+ bool "Support Integrator/CP platform"
+ select ARCH_CINTEGRATOR
+ help
+ Include support for the ARM(R) Integrator CP platform.
+
+config ARCH_CINTEGRATOR
+ bool
+
+config INTEGRATOR_IMPD1
+ tristate "Include support for Integrator/IM-PD1"
+ depends on ARCH_INTEGRATOR_AP
+ help
+ The IM-PD1 is an add-on logic module for the Integrator which
+ allows ARM(R) Ltd PrimeCells to be developed and evaluated.
+ The IM-PD1 can be found on the Integrator/PP2 platform.
+
+ To compile this driver as a module, choose M here: the
+ module will be called impd1.
+
+endmenu
+
+endif
diff --git a/arch/arm/mach-integrator/Makefile b/arch/arm/mach-integrator/Makefile
new file mode 100644
index 00000000000..158daaf9e3b
--- /dev/null
+++ b/arch/arm/mach-integrator/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for the linux kernel.
+#
+
+# Object file lists.
+
+obj-y := clock.o core.o lm.o time.o
+obj-$(CONFIG_ARCH_INTEGRATOR_AP) += integrator_ap.o
+obj-$(CONFIG_ARCH_INTEGRATOR_CP) += integrator_cp.o
+
+obj-$(CONFIG_LEDS) += leds.o
+obj-$(CONFIG_PCI) += pci_v3.o pci.o
+obj-$(CONFIG_CPU_FREQ_INTEGRATOR) += cpu.o
+obj-$(CONFIG_INTEGRATOR_IMPD1) += impd1.o
diff --git a/arch/arm/mach-integrator/Makefile.boot b/arch/arm/mach-integrator/Makefile.boot
new file mode 100644
index 00000000000..c7e75acfe6c
--- /dev/null
+++ b/arch/arm/mach-integrator/Makefile.boot
@@ -0,0 +1,4 @@
+ zreladdr-y := 0x00008000
+params_phys-y := 0x00000100
+initrd_phys-y := 0x00800000
+
diff --git a/arch/arm/mach-integrator/clock.c b/arch/arm/mach-integrator/clock.c
new file mode 100644
index 00000000000..56200594db3
--- /dev/null
+++ b/arch/arm/mach-integrator/clock.c
@@ -0,0 +1,141 @@
+/*
+ * linux/arch/arm/mach-integrator/clock.c
+ *
+ * Copyright (C) 2004 ARM Limited.
+ * Written by Deep Blue Solutions Limited.
+ *
+ * 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/list.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+
+#include <asm/semaphore.h>
+#include <asm/hardware/clock.h>
+#include <asm/hardware/icst525.h>
+
+#include "clock.h"
+
+static LIST_HEAD(clocks);
+static DECLARE_MUTEX(clocks_sem);
+
+struct clk *clk_get(struct device *dev, const char *id)
+{
+ struct clk *p, *clk = ERR_PTR(-ENOENT);
+
+ down(&clocks_sem);
+ list_for_each_entry(p, &clocks, node) {
+ if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
+ clk = p;
+ break;
+ }
+ }
+ up(&clocks_sem);
+
+ return clk;
+}
+EXPORT_SYMBOL(clk_get);
+
+void clk_put(struct clk *clk)
+{
+ module_put(clk->owner);
+}
+EXPORT_SYMBOL(clk_put);
+
+int clk_enable(struct clk *clk)
+{
+ return 0;
+}
+EXPORT_SYMBOL(clk_enable);
+
+void clk_disable(struct clk *clk)
+{
+}
+EXPORT_SYMBOL(clk_disable);
+
+int clk_use(struct clk *clk)
+{
+ return 0;
+}
+EXPORT_SYMBOL(clk_use);
+
+void clk_unuse(struct clk *clk)
+{
+}
+EXPORT_SYMBOL(clk_unuse);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+ return clk->rate;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ struct icst525_vco vco;
+
+ vco = icst525_khz_to_vco(clk->params, rate / 1000);
+ return icst525_khz(clk->params, vco) * 1000;
+}
+EXPORT_SYMBOL(clk_round_rate);
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ int ret = -EIO;
+ if (clk->setvco) {
+ struct icst525_vco vco;
+
+ vco = icst525_khz_to_vco(clk->params, rate / 1000);
+ clk->rate = icst525_khz(clk->params, vco) * 1000;
+
+ printk("Clock %s: setting VCO reg params: S=%d R=%d V=%d\n",
+ clk->name, vco.s, vco.r, vco.v);
+
+ clk->setvco(clk, vco);
+ ret = 0;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(clk_set_rate);
+
+/*
+ * These are fixed clocks.
+ */
+static struct clk kmi_clk = {
+ .name = "KMIREFCLK",
+ .rate = 24000000,
+};
+
+static struct clk uart_clk = {
+ .name = "UARTCLK",
+ .rate = 14745600,
+};
+
+int clk_register(struct clk *clk)
+{
+ down(&clocks_sem);
+ list_add(&clk->node, &clocks);
+ up(&clocks_sem);
+ return 0;
+}
+EXPORT_SYMBOL(clk_register);
+
+void clk_unregister(struct clk *clk)
+{
+ down(&clocks_sem);
+ list_del(&clk->node);
+ up(&clocks_sem);
+}
+EXPORT_SYMBOL(clk_unregister);
+
+static int __init clk_init(void)
+{
+ clk_register(&kmi_clk);
+ clk_register(&uart_clk);
+ return 0;
+}
+arch_initcall(clk_init);
diff --git a/arch/arm/mach-integrator/clock.h b/arch/arm/mach-integrator/clock.h
new file mode 100644
index 00000000000..09e6328ceba
--- /dev/null
+++ b/arch/arm/mach-integrator/clock.h
@@ -0,0 +1,25 @@
+/*
+ * linux/arch/arm/mach-integrator/clock.h
+ *
+ * Copyright (C) 2004 ARM Limited.
+ * Written by Deep Blue Solutions Limited.
+ *
+ * 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.
+ */
+struct module;
+struct icst525_params;
+
+struct clk {
+ struct list_head node;
+ unsigned long rate;
+ struct module *owner;
+ const char *name;
+ const struct icst525_params *params;
+ void *data;
+ void (*setvco)(struct clk *, struct icst525_vco vco);
+};
+
+int clk_register(struct clk *clk);
+void clk_unregister(struct clk *clk);
diff --git a/arch/arm/mach-integrator/common.h b/arch/arm/mach-integrator/common.h
new file mode 100644
index 00000000000..609c49de3d4
--- /dev/null
+++ b/arch/arm/mach-integrator/common.h
@@ -0,0 +1,2 @@
+extern void integrator_time_init(unsigned long, unsigned int);
+extern unsigned long integrator_gettimeoffset(void);
diff --git a/arch/arm/mach-integrator/core.c b/arch/arm/mach-integrator/core.c
new file mode 100644
index 00000000000..86c50c3889b
--- /dev/null
+++ b/arch/arm/mach-integrator/core.c
@@ -0,0 +1,271 @@
+/*
+ * linux/arch/arm/mach-integrator/core.c
+ *
+ * Copyright (C) 2000-2003 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 version 2, as
+ * published by the Free Software Foundation.
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/hardware/amba.h>
+#include <asm/arch/cm.h>
+#include <asm/system.h>
+#include <asm/leds.h>
+#include <asm/mach/time.h>
+
+#include "common.h"
+
+static struct amba_device rtc_device = {
+ .dev = {
+ .bus_id = "mb:15",
+ },
+ .res = {
+ .start = INTEGRATOR_RTC_BASE,
+ .end = INTEGRATOR_RTC_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ .irq = { IRQ_RTCINT, NO_IRQ },
+ .periphid = 0x00041030,
+};
+
+static struct amba_device uart0_device = {
+ .dev = {
+ .bus_id = "mb:16",
+ },
+ .res = {
+ .start = INTEGRATOR_UART0_BASE,
+ .end = INTEGRATOR_UART0_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ .irq = { IRQ_UARTINT0, NO_IRQ },
+ .periphid = 0x0041010,
+};
+
+static struct amba_device uart1_device = {
+ .dev = {
+ .bus_id = "mb:17",
+ },
+ .res = {
+ .start = INTEGRATOR_UART1_BASE,
+ .end = INTEGRATOR_UART1_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ .irq = { IRQ_UARTINT1, NO_IRQ },
+ .periphid = 0x0041010,
+};
+
+static struct amba_device kmi0_device = {
+ .dev = {
+ .bus_id = "mb:18",
+ },
+ .res = {
+ .start = KMI0_BASE,
+ .end = KMI0_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ .irq = { IRQ_KMIINT0, NO_IRQ },
+ .periphid = 0x00041050,
+};
+
+static struct amba_device kmi1_device = {
+ .dev = {
+ .bus_id = "mb:19",
+ },
+ .res = {
+ .start = KMI1_BASE,
+ .end = KMI1_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ .irq = { IRQ_KMIINT1, NO_IRQ },
+ .periphid = 0x00041050,
+};
+
+static struct amba_device *amba_devs[] __initdata = {
+ &rtc_device,
+ &uart0_device,
+ &uart1_device,
+ &kmi0_device,
+ &kmi1_device,
+};
+
+static int __init integrator_init(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(amba_devs); i++) {
+ struct amba_device *d = amba_devs[i];
+ amba_device_register(d, &iomem_resource);
+ }
+
+ return 0;
+}
+
+arch_initcall(integrator_init);
+
+#define CM_CTRL IO_ADDRESS(INTEGRATOR_HDR_BASE) + INTEGRATOR_HDR_CTRL_OFFSET
+
+static DEFINE_SPINLOCK(cm_lock);
+
+/**
+ * cm_control - update the CM_CTRL register.
+ * @mask: bits to change
+ * @set: bits to set
+ */
+void cm_control(u32 mask, u32 set)
+{
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&cm_lock, flags);
+ val = readl(CM_CTRL) & ~mask;
+ writel(val | set, CM_CTRL);
+ spin_unlock_irqrestore(&cm_lock, flags);
+}
+
+EXPORT_SYMBOL(cm_control);
+
+/*
+ * Where is the timer (VA)?
+ */
+#define TIMER0_VA_BASE (IO_ADDRESS(INTEGRATOR_CT_BASE)+0x00000000)
+#define TIMER1_VA_BASE (IO_ADDRESS(INTEGRATOR_CT_BASE)+0x00000100)
+#define TIMER2_VA_BASE (IO_ADDRESS(INTEGRATOR_CT_BASE)+0x00000200)
+#define VA_IC_BASE IO_ADDRESS(INTEGRATOR_IC_BASE)
+
+/*
+ * How long is the timer interval?
+ */
+#define TIMER_INTERVAL (TICKS_PER_uSEC * mSEC_10)
+#if TIMER_INTERVAL >= 0x100000
+#define TICKS2USECS(x) (256 * (x) / TICKS_PER_uSEC)
+#elif TIMER_INTERVAL >= 0x10000
+#define TICKS2USECS(x) (16 * (x) / TICKS_PER_uSEC)
+#else
+#define TICKS2USECS(x) ((x) / TICKS_PER_uSEC)
+#endif
+
+/*
+ * What does it look like?
+ */
+typedef struct TimerStruct {
+ unsigned long TimerLoad;
+ unsigned long TimerValue;
+ unsigned long TimerControl;
+ unsigned long TimerClear;
+} TimerStruct_t;
+
+static unsigned long timer_reload;
+
+/*
+ * Returns number of ms since last clock interrupt. Note that interrupts
+ * will have been disabled by do_gettimeoffset()
+ */
+unsigned long integrator_gettimeoffset(void)
+{
+ volatile TimerStruct_t *timer1 = (TimerStruct_t *)TIMER1_VA_BASE;
+ unsigned long ticks1, ticks2, status;
+
+ /*
+ * Get the current number of ticks. Note that there is a race
+ * condition between us reading the timer and checking for
+ * an interrupt. We get around this by ensuring that the
+ * counter has not reloaded between our two reads.
+ */
+ ticks2 = timer1->TimerValue & 0xffff;
+ do {
+ ticks1 = ticks2;
+ status = __raw_readl(VA_IC_BASE + IRQ_RAW_STATUS);
+ ticks2 = timer1->TimerValue & 0xffff;
+ } while (ticks2 > ticks1);
+
+ /*
+ * Number of ticks since last interrupt.
+ */
+ ticks1 = timer_reload - ticks2;
+
+ /*
+ * Interrupt pending? If so, we've reloaded once already.
+ */
+ if (status & (1 << IRQ_TIMERINT1))
+ ticks1 += timer_reload;
+
+ /*
+ * Convert the ticks to usecs
+ */
+ return TICKS2USECS(ticks1);
+}
+
+/*
+ * IRQ handler for the timer
+ */
+static irqreturn_t
+integrator_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ volatile TimerStruct_t *timer1 = (volatile TimerStruct_t *)TIMER1_VA_BASE;
+
+ write_seqlock(&xtime_lock);
+
+ // ...clear the interrupt
+ timer1->TimerClear = 1;
+
+ timer_tick(regs);
+
+ write_sequnlock(&xtime_lock);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction integrator_timer_irq = {
+ .name = "Integrator Timer Tick",
+ .flags = SA_INTERRUPT,
+ .handler = integrator_timer_interrupt
+};
+
+/*
+ * Set up timer interrupt, and return the current time in seconds.
+ */
+void __init integrator_time_init(unsigned long reload, unsigned int ctrl)
+{
+ volatile TimerStruct_t *timer0 = (volatile TimerStruct_t *)TIMER0_VA_BASE;
+ volatile TimerStruct_t *timer1 = (volatile TimerStruct_t *)TIMER1_VA_BASE;
+ volatile TimerStruct_t *timer2 = (volatile TimerStruct_t *)TIMER2_VA_BASE;
+ unsigned int timer_ctrl = 0x80 | 0x40; /* periodic */
+
+ timer_reload = reload;
+ timer_ctrl |= ctrl;
+
+ if (timer_reload > 0x100000) {
+ timer_reload >>= 8;
+ timer_ctrl |= 0x08; /* /256 */
+ } else if (timer_reload > 0x010000) {
+ timer_reload >>= 4;
+ timer_ctrl |= 0x04; /* /16 */
+ }
+
+ /*
+ * Initialise to a known state (all timers off)
+ */
+ timer0->TimerControl = 0;
+ timer1->TimerControl = 0;
+ timer2->TimerControl = 0;
+
+ timer1->TimerLoad = timer_reload;
+ timer1->TimerValue = timer_reload;
+ timer1->TimerControl = timer_ctrl;
+
+ /*
+ * Make irqs happen for the system timer
+ */
+ setup_irq(IRQ_TIMERINT1, &integrator_timer_irq);
+}
diff --git a/arch/arm/mach-integrator/cpu.c b/arch/arm/mach-integrator/cpu.c
new file mode 100644
index 00000000000..71c58bff304
--- /dev/null
+++ b/arch/arm/mach-integrator/cpu.c
@@ -0,0 +1,221 @@
+/*
+ * linux/arch/arm/mach-integrator/cpu.c
+ *
+ * Copyright (C) 2001-2002 Deep Blue Solutions Ltd.
+ *
+ * $Id: cpu.c,v 1.6 2002/07/18 13:58:51 rmk Exp $
+ *
+ * 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.
+ *
+ * CPU support functions
+ */
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/cpufreq.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <asm/hardware/icst525.h>
+
+static struct cpufreq_driver integrator_driver;
+
+#define CM_ID (IO_ADDRESS(INTEGRATOR_HDR_BASE)+INTEGRATOR_HDR_ID_OFFSET)
+#define CM_OSC (IO_ADDRESS(INTEGRATOR_HDR_BASE)+INTEGRATOR_HDR_OSC_OFFSET)
+#define CM_STAT (IO_ADDRESS(INTEGRATOR_HDR_BASE)+INTEGRATOR_HDR_STAT_OFFSET)
+#define CM_LOCK (IO_ADDRESS(INTEGRATOR_HDR_BASE)+INTEGRATOR_HDR_LOCK_OFFSET)
+
+static const struct icst525_params lclk_params = {
+ .ref = 24000,
+ .vco_max = 320000,
+ .vd_min = 8,
+ .vd_max = 132,
+ .rd_min = 24,
+ .rd_max = 24,
+};
+
+static const struct icst525_params cclk_params = {
+ .ref = 24000,
+ .vco_max = 320000,
+ .vd_min = 12,
+ .vd_max = 160,
+ .rd_min = 24,
+ .rd_max = 24,
+};
+
+/*
+ * Validate the speed policy.
+ */
+static int integrator_verify_policy(struct cpufreq_policy *policy)
+{
+ struct icst525_vco vco;
+
+ cpufreq_verify_within_limits(policy,
+ policy->cpuinfo.min_freq,
+ policy->cpuinfo.max_freq);
+
+ vco = icst525_khz_to_vco(&cclk_params, policy->max);
+ policy->max = icst525_khz(&cclk_params, vco);
+
+ vco = icst525_khz_to_vco(&cclk_params, policy->min);
+ policy->min = icst525_khz(&cclk_params, vco);
+
+ cpufreq_verify_within_limits(policy,
+ policy->cpuinfo.min_freq,
+ policy->cpuinfo.max_freq);
+
+ return 0;
+}
+
+
+static int integrator_set_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ cpumask_t cpus_allowed;
+ int cpu = policy->cpu;
+ struct icst525_vco vco;
+ struct cpufreq_freqs freqs;
+ u_int cm_osc;
+
+ /*
+ * Save this threads cpus_allowed mask.
+ */
+ cpus_allowed = current->cpus_allowed;
+
+ /*
+ * Bind to the specified CPU. When this call returns,
+ * we should be running on the right CPU.
+ */
+ set_cpus_allowed(current, cpumask_of_cpu(cpu));
+ BUG_ON(cpu != smp_processor_id());
+
+ /* get current setting */
+ cm_osc = __raw_readl(CM_OSC);
+
+ if (machine_is_integrator()) {
+ vco.s = (cm_osc >> 8) & 7;
+ } else if (machine_is_cintegrator()) {
+ vco.s = 1;
+ }
+ vco.v = cm_osc & 255;
+ vco.r = 22;
+ freqs.old = icst525_khz(&cclk_params, vco);
+
+ /* icst525_khz_to_vco rounds down -- so we need the next
+ * larger freq in case of CPUFREQ_RELATION_L.
+ */
+ if (relation == CPUFREQ_RELATION_L)
+ target_freq += 999;
+ if (target_freq > policy->max)
+ target_freq = policy->max;
+ vco = icst525_khz_to_vco(&cclk_params, target_freq);
+ freqs.new = icst525_khz(&cclk_params, vco);
+
+ freqs.cpu = policy->cpu;
+
+ if (freqs.old == freqs.new) {
+ set_cpus_allowed(current, cpus_allowed);
+ return 0;
+ }
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+ cm_osc = __raw_readl(CM_OSC);
+
+ if (machine_is_integrator()) {
+ cm_osc &= 0xfffff800;
+ cm_osc |= vco.s << 8;
+ } else if (machine_is_cintegrator()) {
+ cm_osc &= 0xffffff00;
+ }
+ cm_osc |= vco.v;
+
+ __raw_writel(0xa05f, CM_LOCK);
+ __raw_writel(cm_osc, CM_OSC);
+ __raw_writel(0, CM_LOCK);
+
+ /*
+ * Restore the CPUs allowed mask.
+ */
+ set_cpus_allowed(current, cpus_allowed);
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+ return 0;
+}
+
+static unsigned int integrator_get(unsigned int cpu)
+{
+ cpumask_t cpus_allowed;
+ unsigned int current_freq;
+ u_int cm_osc;
+ struct icst525_vco vco;
+
+ cpus_allowed = current->cpus_allowed;
+
+ set_cpus_allowed(current, cpumask_of_cpu(cpu));
+ BUG_ON(cpu != smp_processor_id());
+
+ /* detect memory etc. */
+ cm_osc = __raw_readl(CM_OSC);
+
+ if (machine_is_integrator()) {
+ vco.s = (cm_osc >> 8) & 7;
+ } else if (machine_is_cintegrator()) {
+ vco.s = 1;
+ }
+ vco.v = cm_osc & 255;
+ vco.r = 22;
+
+ current_freq = icst525_khz(&cclk_params, vco); /* current freq */
+
+ set_cpus_allowed(current, cpus_allowed);
+
+ return current_freq;
+}
+
+static int integrator_cpufreq_init(struct cpufreq_policy *policy)
+{
+
+ /* set default policy and cpuinfo */
+ policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+ policy->cpuinfo.max_freq = 160000;
+ policy->cpuinfo.min_freq = 12000;
+ policy->cpuinfo.transition_latency = 1000000; /* 1 ms, assumed */
+ policy->cur = policy->min = policy->max = integrator_get(policy->cpu);
+
+ return 0;
+}
+
+static struct cpufreq_driver integrator_driver = {
+ .verify = integrator_verify_policy,
+ .target = integrator_set_target,
+ .get = integrator_get,
+ .init = integrator_cpufreq_init,
+ .name = "integrator",
+};
+
+static int __init integrator_cpu_init(void)
+{
+ return cpufreq_register_driver(&integrator_driver);
+}
+
+static void __exit integrator_cpu_exit(void)
+{
+ cpufreq_unregister_driver(&integrator_driver);
+}
+
+MODULE_AUTHOR ("Russell M. King");
+MODULE_DESCRIPTION ("cpufreq driver for ARM Integrator CPUs");
+MODULE_LICENSE ("GPL");
+
+module_init(integrator_cpu_init);
+module_exit(integrator_cpu_exit);
diff --git a/arch/arm/mach-integrator/dma.c b/arch/arm/mach-integrator/dma.c
new file mode 100644
index 00000000000..aae6f23cd72
--- /dev/null
+++ b/arch/arm/mach-integrator/dma.c
@@ -0,0 +1,35 @@
+/*
+ * linux/arch/arm/mach-integrator/dma.c
+ *
+ * Copyright (C) 1999 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
+ */
+#include <linux/slab.h>
+#include <linux/mman.h>
+#include <linux/init.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/hardware.h>
+
+#include <asm/mach/dma.h>
+
+void __init arch_dma_init(dma_t *dma)
+{
+}
diff --git a/arch/arm/mach-integrator/impd1.c b/arch/arm/mach-integrator/impd1.c
new file mode 100644
index 00000000000..c3c2f17d030
--- /dev/null
+++ b/arch/arm/mach-integrator/impd1.c
@@ -0,0 +1,475 @@
+/*
+ * linux/arch/arm/mach-integrator/impd1.c
+ *
+ * Copyright (C) 2003 Deep Blue Solutions 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.
+ *
+ * This file provides the core support for the IM-PD1 module.
+ *
+ * Module / boot parameters.
+ * lmid=n impd1.lmid=n - set the logic module position in stack to 'n'
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+
+#include <asm/io.h>
+#include <asm/hardware/icst525.h>
+#include <asm/hardware/amba.h>
+#include <asm/hardware/amba_clcd.h>
+#include <asm/arch/lm.h>
+#include <asm/arch/impd1.h>
+#include <asm/sizes.h>
+
+#include "clock.h"
+
+static int module_id;
+
+module_param_named(lmid, module_id, int, 0444);
+MODULE_PARM_DESC(lmid, "logic module stack position");
+
+struct impd1_module {
+ void __iomem *base;
+ struct clk vcos[2];
+};
+
+static const struct icst525_params impd1_vco_params = {
+ .ref = 24000, /* 24 MHz */
+ .vco_max = 200000, /* 200 MHz */
+ .vd_min = 12,
+ .vd_max = 519,
+ .rd_min = 3,
+ .rd_max = 120,
+};
+
+static void impd1_setvco(struct clk *clk, struct icst525_vco vco)
+{
+ struct impd1_module *impd1 = clk->data;
+ int vconr = clk - impd1->vcos;
+ u32 val;
+
+ val = vco.v | (vco.r << 9) | (vco.s << 16);
+
+ writel(0xa05f, impd1->base + IMPD1_LOCK);
+ switch (vconr) {
+ case 0:
+ writel(val, impd1->base + IMPD1_OSC1);
+ break;
+ case 1:
+ writel(val, impd1->base + IMPD1_OSC2);
+ break;
+ }
+ writel(0, impd1->base + IMPD1_LOCK);
+
+#if DEBUG
+ vco.v = val & 0x1ff;
+ vco.r = (val >> 9) & 0x7f;
+ vco.s = (val >> 16) & 7;
+
+ pr_debug("IM-PD1: VCO%d clock is %ld kHz\n",
+ vconr, icst525_khz(&impd1_vco_params, vco));
+#endif
+}
+
+void impd1_tweak_control(struct device *dev, u32 mask, u32 val)
+{
+ struct impd1_module *impd1 = dev_get_drvdata(dev);
+ u32 cur;
+
+ val &= mask;
+ cur = readl(impd1->base + IMPD1_CTRL) & ~mask;
+ writel(cur | val, impd1->base + IMPD1_CTRL);
+}
+
+EXPORT_SYMBOL(impd1_tweak_control);
+
+/*
+ * CLCD support
+ */
+#define PANEL PROSPECTOR
+
+#define LTM10C209 1
+#define PROSPECTOR 2
+#define SVGA 3
+#define VGA 4
+
+#if PANEL == VGA
+#define PANELTYPE vga
+static struct clcd_panel vga = {
+ .mode = {
+ .name = "VGA",
+ .refresh = 60,
+ .xres = 640,
+ .yres = 480,
+ .pixclock = 39721,
+ .left_margin = 40,
+ .right_margin = 24,
+ .upper_margin = 32,
+ .lower_margin = 11,
+ .hsync_len = 96,
+ .vsync_len = 2,
+ .sync = 0,
+ .vmode = FB_VMODE_NONINTERLACED,
+ },
+ .width = -1,
+ .height = -1,
+ .tim2 = TIM2_BCD | TIM2_IPC,
+ .cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
+ .connector = IMPD1_CTRL_DISP_VGA,
+ .bpp = 16,
+ .grayscale = 0,
+};
+
+#elif PANEL == SVGA
+#define PANELTYPE svga
+static struct clcd_panel svga = {
+ .mode = {
+ .name = "SVGA",
+ .refresh = 0,
+ .xres = 800,
+ .yres = 600,
+ .pixclock = 27778,
+ .left_margin = 20,
+ .right_margin = 20,
+ .upper_margin = 5,
+ .lower_margin = 5,
+ .hsync_len = 164,
+ .vsync_len = 62,
+ .sync = 0,
+ .vmode = FB_VMODE_NONINTERLACED,
+ },
+ .width = -1,
+ .height = -1,
+ .tim2 = TIM2_BCD,
+ .cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
+ .connector = IMPD1_CTRL_DISP_VGA,
+ .bpp = 16,
+ .grayscale = 0,
+};
+
+#elif PANEL == PROSPECTOR
+#define PANELTYPE prospector
+static struct clcd_panel prospector = {
+ .mode = {
+ .name = "PROSPECTOR",
+ .refresh = 0,
+ .xres = 640,
+ .yres = 480,
+ .pixclock = 40000,
+ .left_margin = 33,
+ .right_margin = 64,
+ .upper_margin = 36,
+ .lower_margin = 7,
+ .hsync_len = 64,
+ .vsync_len = 25,
+ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED,
+ },
+ .width = -1,
+ .height = -1,
+ .tim2 = TIM2_BCD,
+ .cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
+ .fixedtimings = 1,
+ .connector = IMPD1_CTRL_DISP_LCD,
+ .bpp = 16,
+ .grayscale = 0,
+};
+
+#elif PANEL == LTM10C209
+#define PANELTYPE ltm10c209
+/*
+ * Untested.
+ */
+static struct clcd_panel ltm10c209 = {
+ .mode = {
+ .name = "LTM10C209",
+ .refresh = 0,
+ .xres = 640,
+ .yres = 480,
+ .pixclock = 40000,
+ .left_margin = 20,
+ .right_margin = 20,
+ .upper_margin = 19,
+ .lower_margin = 19,
+ .hsync_len = 20,
+ .vsync_len = 10,
+ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED,
+ },
+ .width = -1,
+ .height = -1,
+ .tim2 = TIM2_BCD,
+ .cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
+ .fixedtimings = 1,
+ .connector = IMPD1_CTRL_DISP_LCD,
+ .bpp = 16,
+ .grayscale = 0,
+};
+#endif
+
+/*
+ * Disable all display connectors on the interface module.
+ */
+static void impd1fb_clcd_disable(struct clcd_fb *fb)
+{
+ impd1_tweak_control(fb->dev->dev.parent, IMPD1_CTRL_DISP_MASK, 0);
+}
+
+/*
+ * Enable the relevant connector on the interface module.
+ */
+static void impd1fb_clcd_enable(struct clcd_fb *fb)
+{
+ impd1_tweak_control(fb->dev->dev.parent, IMPD1_CTRL_DISP_MASK,
+ fb->panel->connector | IMPD1_CTRL_DISP_ENABLE);
+}
+
+static int impd1fb_clcd_setup(struct clcd_fb *fb)
+{
+ unsigned long framebase = fb->dev->res.start + 0x01000000;
+ unsigned long framesize = SZ_1M;
+ int ret = 0;
+
+ fb->panel = &PANELTYPE;
+
+ if (!request_mem_region(framebase, framesize, "clcd framebuffer")) {
+ printk(KERN_ERR "IM-PD1: unable to reserve framebuffer\n");
+ return -EBUSY;
+ }
+
+ fb->fb.screen_base = ioremap(framebase, framesize);
+ if (!fb->fb.screen_base) {
+ printk(KERN_ERR "IM-PD1: unable to map framebuffer\n");
+ ret = -ENOMEM;
+ goto free_buffer;
+ }
+
+ fb->fb.fix.smem_start = framebase;
+ fb->fb.fix.smem_len = framesize;
+
+ return 0;
+
+ free_buffer:
+ release_mem_region(framebase, framesize);
+ return ret;
+}
+
+static int impd1fb_clcd_mmap(struct clcd_fb *fb, struct vm_area_struct *vma)
+{
+ unsigned long start, size;
+
+ start = vma->vm_pgoff + (fb->fb.fix.smem_start >> PAGE_SHIFT);
+ size = vma->vm_end - vma->vm_start;
+
+ return remap_pfn_range(vma, vma->vm_start, start, size,
+ vma->vm_page_prot);
+}
+
+static void impd1fb_clcd_remove(struct clcd_fb *fb)
+{
+ iounmap(fb->fb.screen_base);
+ release_mem_region(fb->fb.fix.smem_start, fb->fb.fix.smem_len);
+}
+
+static struct clcd_board impd1_clcd_data = {
+ .name = "IM-PD/1",
+ .check = clcdfb_check,
+ .decode = clcdfb_decode,
+ .disable = impd1fb_clcd_disable,
+ .enable = impd1fb_clcd_enable,
+ .setup = impd1fb_clcd_setup,
+ .mmap = impd1fb_clcd_mmap,
+ .remove = impd1fb_clcd_remove,
+};
+
+struct impd1_device {
+ unsigned long offset;
+ unsigned int irq[2];
+ unsigned int id;
+ void *platform_data;
+};
+
+static struct impd1_device impd1_devs[] = {
+ {
+ .offset = 0x03000000,
+ .id = 0x00041190,
+ }, {
+ .offset = 0x00100000,
+ .irq = { 1 },
+ .id = 0x00141011,
+ }, {
+ .offset = 0x00200000,
+ .irq = { 2 },
+ .id = 0x00141011,
+ }, {
+ .offset = 0x00300000,
+ .irq = { 3 },
+ .id = 0x00041022,
+ }, {
+ .offset = 0x00400000,
+ .irq = { 4 },
+ .id = 0x00041061,
+ }, {
+ .offset = 0x00500000,
+ .irq = { 5 },
+ .id = 0x00041061,
+ }, {
+ .offset = 0x00600000,
+ .irq = { 6 },
+ .id = 0x00041130,
+ }, {
+ .offset = 0x00700000,
+ .irq = { 7, 8 },
+ .id = 0x00041181,
+ }, {
+ .offset = 0x00800000,
+ .irq = { 9 },
+ .id = 0x00041041,
+ }, {
+ .offset = 0x01000000,
+ .irq = { 11 },
+ .id = 0x00041110,
+ .platform_data = &impd1_clcd_data,
+ }
+};
+
+static const char *impd1_vconames[2] = {
+ "CLCDCLK",
+ "AUXVCO2",
+};
+
+static int impd1_probe(struct lm_device *dev)
+{
+ struct impd1_module *impd1;
+ int i, ret;
+
+ if (dev->id != module_id)
+ return -EINVAL;
+
+ if (!request_mem_region(dev->resource.start, SZ_4K, "LM registers"))
+ return -EBUSY;
+
+ impd1 = kmalloc(sizeof(struct impd1_module), GFP_KERNEL);
+ if (!impd1) {
+ ret = -ENOMEM;
+ goto release_lm;
+ }
+ memset(impd1, 0, sizeof(struct impd1_module));
+
+ impd1->base = ioremap(dev->resource.start, SZ_4K);
+ if (!impd1->base) {
+ ret = -ENOMEM;
+ goto free_impd1;
+ }
+
+ lm_set_drvdata(dev, impd1);
+
+ printk("IM-PD1 found at 0x%08lx\n", dev->resource.start);
+
+ for (i = 0; i < ARRAY_SIZE(impd1->vcos); i++) {
+ impd1->vcos[i].owner = THIS_MODULE,
+ impd1->vcos[i].name = impd1_vconames[i],
+ impd1->vcos[i].params = &impd1_vco_params,
+ impd1->vcos[i].data = impd1,
+ impd1->vcos[i].setvco = impd1_setvco;
+
+ clk_register(&impd1->vcos[i]);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(impd1_devs); i++) {
+ struct impd1_device *idev = impd1_devs + i;
+ struct amba_device *d;
+ unsigned long pc_base;
+
+ pc_base = dev->resource.start + idev->offset;
+
+ d = kmalloc(sizeof(struct amba_device), GFP_KERNEL);
+ if (!d)
+ continue;
+
+ memset(d, 0, sizeof(struct amba_device));
+
+ snprintf(d->dev.bus_id, sizeof(d->dev.bus_id),
+ "lm%x:%5.5lx", dev->id, idev->offset >> 12);
+
+ d->dev.parent = &dev->dev;
+ d->res.start = dev->resource.start + idev->offset;
+ d->res.end = d->res.start + SZ_4K - 1;
+ d->res.flags = IORESOURCE_MEM;
+ d->irq[0] = dev->irq;
+ d->irq[1] = dev->irq;
+ d->periphid = idev->id;
+ d->dev.platform_data = idev->platform_data;
+
+ ret = amba_device_register(d, &dev->resource);
+ if (ret) {
+ printk("unable to register device %s: %d\n",
+ d->dev.bus_id, ret);
+ kfree(d);
+ }
+ }
+
+ return 0;
+
+ free_impd1:
+ if (impd1 && impd1->base)
+ iounmap(impd1->base);
+ if (impd1)
+ kfree(impd1);
+ release_lm:
+ release_mem_region(dev->resource.start, SZ_4K);
+ return ret;
+}
+
+static void impd1_remove(struct lm_device *dev)
+{
+ struct impd1_module *impd1 = lm_get_drvdata(dev);
+ struct list_head *l, *n;
+ int i;
+
+ list_for_each_safe(l, n, &dev->dev.children) {
+ struct device *d = list_to_dev(l);
+
+ device_unregister(d);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(impd1->vcos); i++)
+ clk_unregister(&impd1->vcos[i]);
+
+ lm_set_drvdata(dev, NULL);
+
+ iounmap(impd1->base);
+ kfree(impd1);
+ release_mem_region(dev->resource.start, SZ_4K);
+}
+
+static struct lm_driver impd1_driver = {
+ .drv = {
+ .name = "impd1",
+ },
+ .probe = impd1_probe,
+ .remove = impd1_remove,
+};
+
+static int __init impd1_init(void)
+{
+ return lm_driver_register(&impd1_driver);
+}
+
+static void __exit impd1_exit(void)
+{
+ lm_driver_unregister(&impd1_driver);
+}
+
+module_init(impd1_init);
+module_exit(impd1_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Integrator/IM-PD1 logic module core driver");
+MODULE_AUTHOR("Deep Blue Solutions Ltd");
diff --git a/arch/arm/mach-integrator/integrator_ap.c b/arch/arm/mach-integrator/integrator_ap.c
new file mode 100644
index 00000000000..91ba9fd79c8
--- /dev/null
+++ b/arch/arm/mach-integrator/integrator_ap.c
@@ -0,0 +1,302 @@
+/*
+ * linux/arch/arm/mach-integrator/integrator_ap.c
+ *
+ * Copyright (C) 2000-2003 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
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/sysdev.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/hardware/amba.h>
+#include <asm/hardware/amba_kmi.h>
+
+#include <asm/arch/lm.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/flash.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/map.h>
+#include <asm/mach/time.h>
+
+#include "common.h"
+
+/*
+ * All IO addresses are mapped onto VA 0xFFFx.xxxx, where x.xxxx
+ * is the (PA >> 12).
+ *
+ * Setup a VA for the Integrator interrupt controller (for header #0,
+ * just for now).
+ */
+#define VA_IC_BASE IO_ADDRESS(INTEGRATOR_IC_BASE)
+#define VA_SC_BASE IO_ADDRESS(INTEGRATOR_SC_BASE)
+#define VA_EBI_BASE IO_ADDRESS(INTEGRATOR_EBI_BASE)
+#define VA_CMIC_BASE IO_ADDRESS(INTEGRATOR_HDR_BASE) + INTEGRATOR_HDR_IC_OFFSET
+
+/*
+ * Logical Physical
+ * e8000000 40000000 PCI memory PHYS_PCI_MEM_BASE (max 512M)
+ * ec000000 61000000 PCI config space PHYS_PCI_CONFIG_BASE (max 16M)
+ * ed000000 62000000 PCI V3 regs PHYS_PCI_V3_BASE (max 64k)
+ * ee000000 60000000 PCI IO PHYS_PCI_IO_BASE (max 16M)
+ * ef000000 Cache flush
+ * f1000000 10000000 Core module registers
+ * f1100000 11000000 System controller registers
+ * f1200000 12000000 EBI registers
+ * f1300000 13000000 Counter/Timer
+ * f1400000 14000000 Interrupt controller
+ * f1600000 16000000 UART 0
+ * f1700000 17000000 UART 1
+ * f1a00000 1a000000 Debug LEDs
+ * f1b00000 1b000000 GPIO
+ */
+
+static struct map_desc ap_io_desc[] __initdata = {
+ { IO_ADDRESS(INTEGRATOR_HDR_BASE), INTEGRATOR_HDR_BASE, SZ_4K, MT_DEVICE },
+ { IO_ADDRESS(INTEGRATOR_SC_BASE), INTEGRATOR_SC_BASE, SZ_4K, MT_DEVICE },
+ { IO_ADDRESS(INTEGRATOR_EBI_BASE), INTEGRATOR_EBI_BASE, SZ_4K, MT_DEVICE },
+ { IO_ADDRESS(INTEGRATOR_CT_BASE), INTEGRATOR_CT_BASE, SZ_4K, MT_DEVICE },
+ { IO_ADDRESS(INTEGRATOR_IC_BASE), INTEGRATOR_IC_BASE, SZ_4K, MT_DEVICE },
+ { IO_ADDRESS(INTEGRATOR_UART0_BASE), INTEGRATOR_UART0_BASE, SZ_4K, MT_DEVICE },
+ { IO_ADDRESS(INTEGRATOR_UART1_BASE), INTEGRATOR_UART1_BASE, SZ_4K, MT_DEVICE },
+ { IO_ADDRESS(INTEGRATOR_DBG_BASE), INTEGRATOR_DBG_BASE, SZ_4K, MT_DEVICE },
+ { IO_ADDRESS(INTEGRATOR_GPIO_BASE), INTEGRATOR_GPIO_BASE, SZ_4K, MT_DEVICE },
+ { PCI_MEMORY_VADDR, PHYS_PCI_MEM_BASE, SZ_16M, MT_DEVICE },
+ { PCI_CONFIG_VADDR, PHYS_PCI_CONFIG_BASE, SZ_16M, MT_DEVICE },
+ { PCI_V3_VADDR, PHYS_PCI_V3_BASE, SZ_64K, MT_DEVICE },
+ { PCI_IO_VADDR, PHYS_PCI_IO_BASE, SZ_64K, MT_DEVICE }
+};
+
+static void __init ap_map_io(void)
+{
+ iotable_init(ap_io_desc, ARRAY_SIZE(ap_io_desc));
+}
+
+#define INTEGRATOR_SC_VALID_INT 0x003fffff
+
+static void sc_mask_irq(unsigned int irq)
+{
+ writel(1 << irq, VA_IC_BASE + IRQ_ENABLE_CLEAR);
+}
+
+static void sc_unmask_irq(unsigned int irq)
+{
+ writel(1 << irq, VA_IC_BASE + IRQ_ENABLE_SET);
+}
+
+static struct irqchip sc_chip = {
+ .ack = sc_mask_irq,
+ .mask = sc_mask_irq,
+ .unmask = sc_unmask_irq,
+};
+
+static void __init ap_init_irq(void)
+{
+ unsigned int i;
+
+ /* Disable all interrupts initially. */
+ /* Do the core module ones */
+ writel(-1, VA_CMIC_BASE + IRQ_ENABLE_CLEAR);
+
+ /* do the header card stuff next */
+ writel(-1, VA_IC_BASE + IRQ_ENABLE_CLEAR);
+ writel(-1, VA_IC_BASE + FIQ_ENABLE_CLEAR);
+
+ for (i = 0; i < NR_IRQS; i++) {
+ if (((1 << i) & INTEGRATOR_SC_VALID_INT) != 0) {
+ set_irq_chip(i, &sc_chip);
+ set_irq_handler(i, do_level_IRQ);
+ set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
+ }
+ }
+}
+
+#ifdef CONFIG_PM
+static unsigned long ic_irq_enable;
+
+static int irq_suspend(struct sys_device *dev, pm_message_t state)
+{
+ ic_irq_enable = readl(VA_IC_BASE + IRQ_ENABLE);
+ return 0;
+}
+
+static int irq_resume(struct sys_device *dev)
+{
+ /* disable all irq sources */
+ writel(-1, VA_CMIC_BASE + IRQ_ENABLE_CLEAR);
+ writel(-1, VA_IC_BASE + IRQ_ENABLE_CLEAR);
+ writel(-1, VA_IC_BASE + FIQ_ENABLE_CLEAR);
+
+ writel(ic_irq_enable, VA_IC_BASE + IRQ_ENABLE_SET);
+ return 0;
+}
+#else
+#define irq_suspend NULL
+#define irq_resume NULL
+#endif
+
+static struct sysdev_class irq_class = {
+ set_kset_name("irq"),
+ .suspend = irq_suspend,
+ .resume = irq_resume,
+};
+
+static struct sys_device irq_device = {
+ .id = 0,
+ .cls = &irq_class,
+};
+
+static int __init irq_init_sysfs(void)
+{
+ int ret = sysdev_class_register(&irq_class);
+ if (ret == 0)
+ ret = sysdev_register(&irq_device);
+ return ret;
+}
+
+device_initcall(irq_init_sysfs);
+
+/*
+ * Flash handling.
+ */
+#define SC_CTRLC (VA_SC_BASE + INTEGRATOR_SC_CTRLC_OFFSET)
+#define SC_CTRLS (VA_SC_BASE + INTEGRATOR_SC_CTRLS_OFFSET)
+#define EBI_CSR1 (VA_EBI_BASE + INTEGRATOR_EBI_CSR1_OFFSET)
+#define EBI_LOCK (VA_EBI_BASE + INTEGRATOR_EBI_LOCK_OFFSET)
+
+static int ap_flash_init(void)
+{
+ u32 tmp;
+
+ writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC);
+
+ tmp = readl(EBI_CSR1) | INTEGRATOR_EBI_WRITE_ENABLE;
+ writel(tmp, EBI_CSR1);
+
+ if (!(readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE)) {
+ writel(0xa05f, EBI_LOCK);
+ writel(tmp, EBI_CSR1);
+ writel(0, EBI_LOCK);
+ }
+ return 0;
+}
+
+static void ap_flash_exit(void)
+{
+ u32 tmp;
+
+ writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC);
+
+ tmp = readl(EBI_CSR1) & ~INTEGRATOR_EBI_WRITE_ENABLE;
+ writel(tmp, EBI_CSR1);
+
+ if (readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE) {
+ writel(0xa05f, EBI_LOCK);
+ writel(tmp, EBI_CSR1);
+ writel(0, EBI_LOCK);
+ }
+}
+
+static void ap_flash_set_vpp(int on)
+{
+ unsigned long reg = on ? SC_CTRLS : SC_CTRLC;
+
+ writel(INTEGRATOR_SC_CTRL_nFLVPPEN, reg);
+}
+
+static struct flash_platform_data ap_flash_data = {
+ .map_name = "cfi_probe",
+ .width = 4,
+ .init = ap_flash_init,
+ .exit = ap_flash_exit,
+ .set_vpp = ap_flash_set_vpp,
+};
+
+static struct resource cfi_flash_resource = {
+ .start = INTEGRATOR_FLASH_BASE,
+ .end = INTEGRATOR_FLASH_BASE + INTEGRATOR_FLASH_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct platform_device cfi_flash_device = {
+ .name = "armflash",
+ .id = 0,
+ .dev = {
+ .platform_data = &ap_flash_data,
+ },
+ .num_resources = 1,
+ .resource = &cfi_flash_resource,
+};
+
+static void __init ap_init(void)
+{
+ unsigned long sc_dec;
+ int i;
+
+ platform_device_register(&cfi_flash_device);
+
+ sc_dec = readl(VA_SC_BASE + INTEGRATOR_SC_DEC_OFFSET);
+ for (i = 0; i < 4; i++) {
+ struct lm_device *lmdev;
+
+ if ((sc_dec & (16 << i)) == 0)
+ continue;
+
+ lmdev = kmalloc(sizeof(struct lm_device), GFP_KERNEL);
+ if (!lmdev)
+ continue;
+
+ memset(lmdev, 0, sizeof(struct lm_device));
+
+ lmdev->resource.start = 0xc0000000 + 0x10000000 * i;
+ lmdev->resource.end = lmdev->resource.start + 0x0fffffff;
+ lmdev->resource.flags = IORESOURCE_MEM;
+ lmdev->irq = IRQ_AP_EXPINT0 + i;
+ lmdev->id = i;
+
+ lm_device_register(lmdev);
+ }
+}
+
+static void __init ap_init_timer(void)
+{
+ integrator_time_init(1000000 * TICKS_PER_uSEC / HZ, 0);
+}
+
+static struct sys_timer ap_timer = {
+ .init = ap_init_timer,
+ .offset = integrator_gettimeoffset,
+};
+
+MACHINE_START(INTEGRATOR, "ARM-Integrator")
+ MAINTAINER("ARM Ltd/Deep Blue Solutions Ltd")
+ BOOT_MEM(0x00000000, 0x16000000, 0xf1600000)
+ BOOT_PARAMS(0x00000100)
+ MAPIO(ap_map_io)
+ INITIRQ(ap_init_irq)
+ .timer = &ap_timer,
+ INIT_MACHINE(ap_init)
+MACHINE_END
diff --git a/arch/arm/mach-integrator/integrator_cp.c b/arch/arm/mach-integrator/integrator_cp.c
new file mode 100644
index 00000000000..68e15c36e33
--- /dev/null
+++ b/arch/arm/mach-integrator/integrator_cp.c
@@ -0,0 +1,528 @@
+/*
+ * linux/arch/arm/mach-integrator/integrator_cp.c
+ *
+ * Copyright (C) 2003 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.
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/sysdev.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/hardware/amba.h>
+#include <asm/hardware/amba_kmi.h>
+#include <asm/hardware/amba_clcd.h>
+#include <asm/hardware/icst525.h>
+
+#include <asm/arch/cm.h>
+#include <asm/arch/lm.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/flash.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/mmc.h>
+#include <asm/mach/map.h>
+#include <asm/mach/time.h>
+
+#include "common.h"
+#include "clock.h"
+
+#define INTCP_PA_MMC_BASE 0x1c000000
+#define INTCP_PA_AACI_BASE 0x1d000000
+
+#define INTCP_PA_FLASH_BASE 0x24000000
+#define INTCP_FLASH_SIZE SZ_32M
+
+#define INTCP_PA_CLCD_BASE 0xc0000000
+
+#define INTCP_VA_CIC_BASE 0xf1000040
+#define INTCP_VA_PIC_BASE 0xf1400000
+#define INTCP_VA_SIC_BASE 0xfca00000
+
+#define INTCP_PA_ETH_BASE 0xc8000000
+#define INTCP_ETH_SIZE 0x10
+
+#define INTCP_VA_CTRL_BASE 0xfcb00000
+#define INTCP_FLASHPROG 0x04
+#define CINTEGRATOR_FLASHPROG_FLVPPEN (1 << 0)
+#define CINTEGRATOR_FLASHPROG_FLWREN (1 << 1)
+
+/*
+ * Logical Physical
+ * f1000000 10000000 Core module registers
+ * f1100000 11000000 System controller registers
+ * f1200000 12000000 EBI registers
+ * f1300000 13000000 Counter/Timer
+ * f1400000 14000000 Interrupt controller
+ * f1600000 16000000 UART 0
+ * f1700000 17000000 UART 1
+ * f1a00000 1a000000 Debug LEDs
+ * f1b00000 1b000000 GPIO
+ */
+
+static struct map_desc intcp_io_desc[] __initdata = {
+ { IO_ADDRESS(INTEGRATOR_HDR_BASE), INTEGRATOR_HDR_BASE, SZ_4K, MT_DEVICE },
+ { IO_ADDRESS(INTEGRATOR_SC_BASE), INTEGRATOR_SC_BASE, SZ_4K, MT_DEVICE },
+ { IO_ADDRESS(INTEGRATOR_EBI_BASE), INTEGRATOR_EBI_BASE, SZ_4K, MT_DEVICE },
+ { IO_ADDRESS(INTEGRATOR_CT_BASE), INTEGRATOR_CT_BASE, SZ_4K, MT_DEVICE },
+ { IO_ADDRESS(INTEGRATOR_IC_BASE), INTEGRATOR_IC_BASE, SZ_4K, MT_DEVICE },
+ { IO_ADDRESS(INTEGRATOR_UART0_BASE), INTEGRATOR_UART0_BASE, SZ_4K, MT_DEVICE },
+ { IO_ADDRESS(INTEGRATOR_UART1_BASE), INTEGRATOR_UART1_BASE, SZ_4K, MT_DEVICE },
+ { IO_ADDRESS(INTEGRATOR_DBG_BASE), INTEGRATOR_DBG_BASE, SZ_4K, MT_DEVICE },
+ { IO_ADDRESS(INTEGRATOR_GPIO_BASE), INTEGRATOR_GPIO_BASE, SZ_4K, MT_DEVICE },
+ { 0xfc900000, 0xc9000000, SZ_4K, MT_DEVICE },
+ { 0xfca00000, 0xca000000, SZ_4K, MT_DEVICE },
+ { 0xfcb00000, 0xcb000000, SZ_4K, MT_DEVICE },
+};
+
+static void __init intcp_map_io(void)
+{
+ iotable_init(intcp_io_desc, ARRAY_SIZE(intcp_io_desc));
+}
+
+#define cic_writel __raw_writel
+#define cic_readl __raw_readl
+#define pic_writel __raw_writel
+#define pic_readl __raw_readl
+#define sic_writel __raw_writel
+#define sic_readl __raw_readl
+
+static void cic_mask_irq(unsigned int irq)
+{
+ irq -= IRQ_CIC_START;
+ cic_writel(1 << irq, INTCP_VA_CIC_BASE + IRQ_ENABLE_CLEAR);
+}
+
+static void cic_unmask_irq(unsigned int irq)
+{
+ irq -= IRQ_CIC_START;
+ cic_writel(1 << irq, INTCP_VA_CIC_BASE + IRQ_ENABLE_SET);
+}
+
+static struct irqchip cic_chip = {
+ .ack = cic_mask_irq,
+ .mask = cic_mask_irq,
+ .unmask = cic_unmask_irq,
+};
+
+static void pic_mask_irq(unsigned int irq)
+{
+ irq -= IRQ_PIC_START;
+ pic_writel(1 << irq, INTCP_VA_PIC_BASE + IRQ_ENABLE_CLEAR);
+}
+
+static void pic_unmask_irq(unsigned int irq)
+{
+ irq -= IRQ_PIC_START;
+ pic_writel(1 << irq, INTCP_VA_PIC_BASE + IRQ_ENABLE_SET);
+}
+
+static struct irqchip pic_chip = {
+ .ack = pic_mask_irq,
+ .mask = pic_mask_irq,
+ .unmask = pic_unmask_irq,
+};
+
+static void sic_mask_irq(unsigned int irq)
+{
+ irq -= IRQ_SIC_START;
+ sic_writel(1 << irq, INTCP_VA_SIC_BASE + IRQ_ENABLE_CLEAR);
+}
+
+static void sic_unmask_irq(unsigned int irq)
+{
+ irq -= IRQ_SIC_START;
+ sic_writel(1 << irq, INTCP_VA_SIC_BASE + IRQ_ENABLE_SET);
+}
+
+static struct irqchip sic_chip = {
+ .ack = sic_mask_irq,
+ .mask = sic_mask_irq,
+ .unmask = sic_unmask_irq,
+};
+
+static void
+sic_handle_irq(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs)
+{
+ unsigned long status = sic_readl(INTCP_VA_SIC_BASE + IRQ_STATUS);
+
+ if (status == 0) {
+ do_bad_IRQ(irq, desc, regs);
+ return;
+ }
+
+ do {
+ irq = ffs(status) - 1;
+ status &= ~(1 << irq);
+
+ irq += IRQ_SIC_START;
+
+ desc = irq_desc + irq;
+ desc->handle(irq, desc, regs);
+ } while (status);
+}
+
+static void __init intcp_init_irq(void)
+{
+ unsigned int i;
+
+ /*
+ * Disable all interrupt sources
+ */
+ pic_writel(0xffffffff, INTCP_VA_PIC_BASE + IRQ_ENABLE_CLEAR);
+ pic_writel(0xffffffff, INTCP_VA_PIC_BASE + FIQ_ENABLE_CLEAR);
+
+ for (i = IRQ_PIC_START; i <= IRQ_PIC_END; i++) {
+ if (i == 11)
+ i = 22;
+ if (i == IRQ_CP_CPPLDINT)
+ i++;
+ if (i == 29)
+ break;
+ set_irq_chip(i, &pic_chip);
+ set_irq_handler(i, do_level_IRQ);
+ set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
+ }
+
+ cic_writel(0xffffffff, INTCP_VA_CIC_BASE + IRQ_ENABLE_CLEAR);
+ cic_writel(0xffffffff, INTCP_VA_CIC_BASE + FIQ_ENABLE_CLEAR);
+
+ for (i = IRQ_CIC_START; i <= IRQ_CIC_END; i++) {
+ set_irq_chip(i, &cic_chip);
+ set_irq_handler(i, do_level_IRQ);
+ set_irq_flags(i, IRQF_VALID);
+ }
+
+ sic_writel(0x00000fff, INTCP_VA_SIC_BASE + IRQ_ENABLE_CLEAR);
+ sic_writel(0x00000fff, INTCP_VA_SIC_BASE + FIQ_ENABLE_CLEAR);
+
+ for (i = IRQ_SIC_START; i <= IRQ_SIC_END; i++) {
+ set_irq_chip(i, &sic_chip);
+ set_irq_handler(i, do_level_IRQ);
+ set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
+ }
+
+ set_irq_handler(IRQ_CP_CPPLDINT, sic_handle_irq);
+ pic_unmask_irq(IRQ_CP_CPPLDINT);
+}
+
+/*
+ * Clock handling
+ */
+#define CM_LOCK (IO_ADDRESS(INTEGRATOR_HDR_BASE)+INTEGRATOR_HDR_LOCK_OFFSET)
+#define CM_AUXOSC (IO_ADDRESS(INTEGRATOR_HDR_BASE)+0x1c)
+
+static const struct icst525_params cp_auxvco_params = {
+ .ref = 24000,
+ .vco_max = 320000,
+ .vd_min = 8,
+ .vd_max = 263,
+ .rd_min = 3,
+ .rd_max = 65,
+};
+
+static void cp_auxvco_set(struct clk *clk, struct icst525_vco vco)
+{
+ u32 val;
+
+ val = readl(CM_AUXOSC) & ~0x7ffff;
+ val |= vco.v | (vco.r << 9) | (vco.s << 16);
+
+ writel(0xa05f, CM_LOCK);
+ writel(val, CM_AUXOSC);
+ writel(0, CM_LOCK);
+}
+
+static struct clk cp_clcd_clk = {
+ .name = "CLCDCLK",
+ .params = &cp_auxvco_params,
+ .setvco = cp_auxvco_set,
+};
+
+static struct clk cp_mmci_clk = {
+ .name = "MCLK",
+ .rate = 14745600,
+};
+
+/*
+ * Flash handling.
+ */
+static int intcp_flash_init(void)
+{
+ u32 val;
+
+ val = readl(INTCP_VA_CTRL_BASE + INTCP_FLASHPROG);
+ val |= CINTEGRATOR_FLASHPROG_FLWREN;
+ writel(val, INTCP_VA_CTRL_BASE + INTCP_FLASHPROG);
+
+ return 0;
+}
+
+static void intcp_flash_exit(void)
+{
+ u32 val;
+
+ val = readl(INTCP_VA_CTRL_BASE + INTCP_FLASHPROG);
+ val &= ~(CINTEGRATOR_FLASHPROG_FLVPPEN|CINTEGRATOR_FLASHPROG_FLWREN);
+ writel(val, INTCP_VA_CTRL_BASE + INTCP_FLASHPROG);
+}
+
+static void intcp_flash_set_vpp(int on)
+{
+ u32 val;
+
+ val = readl(INTCP_VA_CTRL_BASE + INTCP_FLASHPROG);
+ if (on)
+ val |= CINTEGRATOR_FLASHPROG_FLVPPEN;
+ else
+ val &= ~CINTEGRATOR_FLASHPROG_FLVPPEN;
+ writel(val, INTCP_VA_CTRL_BASE + INTCP_FLASHPROG);
+}
+
+static struct flash_platform_data intcp_flash_data = {
+ .map_name = "cfi_probe",
+ .width = 4,
+ .init = intcp_flash_init,
+ .exit = intcp_flash_exit,
+ .set_vpp = intcp_flash_set_vpp,
+};
+
+static struct resource intcp_flash_resource = {
+ .start = INTCP_PA_FLASH_BASE,
+ .end = INTCP_PA_FLASH_BASE + INTCP_FLASH_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct platform_device intcp_flash_device = {
+ .name = "armflash",
+ .id = 0,
+ .dev = {
+ .platform_data = &intcp_flash_data,
+ },
+ .num_resources = 1,
+ .resource = &intcp_flash_resource,
+};
+
+static struct resource smc91x_resources[] = {
+ [0] = {
+ .start = INTCP_PA_ETH_BASE,
+ .end = INTCP_PA_ETH_BASE + INTCP_ETH_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_CP_ETHINT,
+ .end = IRQ_CP_ETHINT,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device smc91x_device = {
+ .name = "smc91x",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(smc91x_resources),
+ .resource = smc91x_resources,
+};
+
+static struct platform_device *intcp_devs[] __initdata = {
+ &intcp_flash_device,
+ &smc91x_device,
+};
+
+/*
+ * It seems that the card insertion interrupt remains active after
+ * we've acknowledged it. We therefore ignore the interrupt, and
+ * rely on reading it from the SIC. This also means that we must
+ * clear the latched interrupt.
+ */
+static unsigned int mmc_status(struct device *dev)
+{
+ unsigned int status = readl(0xfca00004);
+ writel(8, 0xfcb00008);
+
+ return status & 8;
+}
+
+static struct mmc_platform_data mmc_data = {
+ .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,
+ .status = mmc_status,
+};
+
+static struct amba_device mmc_device = {
+ .dev = {
+ .bus_id = "mb:1c",
+ .platform_data = &mmc_data,
+ },
+ .res = {
+ .start = INTCP_PA_MMC_BASE,
+ .end = INTCP_PA_MMC_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ .irq = { IRQ_CP_MMCIINT0, IRQ_CP_MMCIINT1 },
+ .periphid = 0,
+};
+
+static struct amba_device aaci_device = {
+ .dev = {
+ .bus_id = "mb:1d",
+ },
+ .res = {
+ .start = INTCP_PA_AACI_BASE,
+ .end = INTCP_PA_AACI_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ .irq = { IRQ_CP_AACIINT, NO_IRQ },
+ .periphid = 0,
+};
+
+
+/*
+ * CLCD support
+ */
+static struct clcd_panel vga = {
+ .mode = {
+ .name = "VGA",
+ .refresh = 60,
+ .xres = 640,
+ .yres = 480,
+ .pixclock = 39721,
+ .left_margin = 40,
+ .right_margin = 24,
+ .upper_margin = 32,
+ .lower_margin = 11,
+ .hsync_len = 96,
+ .vsync_len = 2,
+ .sync = 0,
+ .vmode = FB_VMODE_NONINTERLACED,
+ },
+ .width = -1,
+ .height = -1,
+ .tim2 = TIM2_BCD | TIM2_IPC,
+ .cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
+ .bpp = 16,
+ .grayscale = 0,
+};
+
+/*
+ * Ensure VGA is selected.
+ */
+static void cp_clcd_enable(struct clcd_fb *fb)
+{
+ cm_control(CM_CTRL_LCDMUXSEL_MASK, CM_CTRL_LCDMUXSEL_VGA);
+}
+
+static unsigned long framesize = SZ_1M;
+
+static int cp_clcd_setup(struct clcd_fb *fb)
+{
+ dma_addr_t dma;
+
+ fb->panel = &vga;
+
+ fb->fb.screen_base = dma_alloc_writecombine(&fb->dev->dev, framesize,
+ &dma, GFP_KERNEL);
+ if (!fb->fb.screen_base) {
+ printk(KERN_ERR "CLCD: unable to map framebuffer\n");
+ return -ENOMEM;
+ }
+
+ fb->fb.fix.smem_start = dma;
+ fb->fb.fix.smem_len = framesize;
+
+ return 0;
+}
+
+static int cp_clcd_mmap(struct clcd_fb *fb, struct vm_area_struct *vma)
+{
+ return dma_mmap_writecombine(&fb->dev->dev, vma,
+ fb->fb.screen_base,
+ fb->fb.fix.smem_start,
+ fb->fb.fix.smem_len);
+}
+
+static void cp_clcd_remove(struct clcd_fb *fb)
+{
+ dma_free_writecombine(&fb->dev->dev, fb->fb.fix.smem_len,
+ fb->fb.screen_base, fb->fb.fix.smem_start);
+}
+
+static struct clcd_board clcd_data = {
+ .name = "Integrator/CP",
+ .check = clcdfb_check,
+ .decode = clcdfb_decode,
+ .enable = cp_clcd_enable,
+ .setup = cp_clcd_setup,
+ .mmap = cp_clcd_mmap,
+ .remove = cp_clcd_remove,
+};
+
+static struct amba_device clcd_device = {
+ .dev = {
+ .bus_id = "mb:c0",
+ .coherent_dma_mask = ~0,
+ .platform_data = &clcd_data,
+ },
+ .res = {
+ .start = INTCP_PA_CLCD_BASE,
+ .end = INTCP_PA_CLCD_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ .dma_mask = ~0,
+ .irq = { IRQ_CP_CLCDCINT, NO_IRQ },
+ .periphid = 0,
+};
+
+static struct amba_device *amba_devs[] __initdata = {
+ &mmc_device,
+ &aaci_device,
+ &clcd_device,
+};
+
+static void __init intcp_init(void)
+{
+ int i;
+
+ clk_register(&cp_clcd_clk);
+ clk_register(&cp_mmci_clk);
+
+ platform_add_devices(intcp_devs, ARRAY_SIZE(intcp_devs));
+
+ for (i = 0; i < ARRAY_SIZE(amba_devs); i++) {
+ struct amba_device *d = amba_devs[i];
+ amba_device_register(d, &iomem_resource);
+ }
+}
+
+#define TIMER_CTRL_IE (1 << 5) /* Interrupt Enable */
+
+static void __init intcp_timer_init(void)
+{
+ integrator_time_init(1000000 / HZ, TIMER_CTRL_IE);
+}
+
+static struct sys_timer cp_timer = {
+ .init = intcp_timer_init,
+ .offset = integrator_gettimeoffset,
+};
+
+MACHINE_START(CINTEGRATOR, "ARM-IntegratorCP")
+ MAINTAINER("ARM Ltd/Deep Blue Solutions Ltd")
+ BOOT_MEM(0x00000000, 0x16000000, 0xf1600000)
+ BOOT_PARAMS(0x00000100)
+ MAPIO(intcp_map_io)
+ INITIRQ(intcp_init_irq)
+ .timer = &cp_timer,
+ INIT_MACHINE(intcp_init)
+MACHINE_END
diff --git a/arch/arm/mach-integrator/leds.c b/arch/arm/mach-integrator/leds.c
new file mode 100644
index 00000000000..9d182b77b31
--- /dev/null
+++ b/arch/arm/mach-integrator/leds.c
@@ -0,0 +1,88 @@
+/*
+ * linux/arch/arm/mach-integrator/leds.c
+ *
+ * Integrator/AP and Integrator/CP LED control routines
+ *
+ * Copyright (C) 1999 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
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/leds.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <asm/arch/cm.h>
+
+static int saved_leds;
+
+static void integrator_leds_event(led_event_t ledevt)
+{
+ unsigned long flags;
+ const unsigned int dbg_base = IO_ADDRESS(INTEGRATOR_DBG_BASE);
+ unsigned int update_alpha_leds;
+
+ // yup, change the LEDs
+ local_irq_save(flags);
+ update_alpha_leds = 0;
+
+ switch(ledevt) {
+ case led_idle_start:
+ cm_control(CM_CTRL_LED, 0);
+ break;
+
+ case led_idle_end:
+ cm_control(CM_CTRL_LED, CM_CTRL_LED);
+ break;
+
+ case led_timer:
+ saved_leds ^= GREEN_LED;
+ update_alpha_leds = 1;
+ break;
+
+ case led_red_on:
+ saved_leds |= RED_LED;
+ update_alpha_leds = 1;
+ break;
+
+ case led_red_off:
+ saved_leds &= ~RED_LED;
+ update_alpha_leds = 1;
+ break;
+
+ default:
+ break;
+ }
+
+ if (update_alpha_leds) {
+ while (__raw_readl(dbg_base + INTEGRATOR_DBG_ALPHA_OFFSET) & 1);
+ __raw_writel(saved_leds, dbg_base + INTEGRATOR_DBG_LEDS_OFFSET);
+ }
+ local_irq_restore(flags);
+}
+
+static int __init leds_init(void)
+{
+ if (machine_is_integrator() || machine_is_cintegrator())
+ leds_event = integrator_leds_event;
+
+ return 0;
+}
+
+__initcall(leds_init);
diff --git a/arch/arm/mach-integrator/lm.c b/arch/arm/mach-integrator/lm.c
new file mode 100644
index 00000000000..c5f19d16059
--- /dev/null
+++ b/arch/arm/mach-integrator/lm.c
@@ -0,0 +1,96 @@
+/*
+ * linux/arch/arm/mach-integrator/lm.c
+ *
+ * Copyright (C) 2003 Deep Blue Solutions 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/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+
+#include <asm/arch/lm.h>
+
+#define to_lm_device(d) container_of(d, struct lm_device, dev)
+#define to_lm_driver(d) container_of(d, struct lm_driver, drv)
+
+static int lm_match(struct device *dev, struct device_driver *drv)
+{
+ return 1;
+}
+
+static struct bus_type lm_bustype = {
+ .name = "logicmodule",
+ .match = lm_match,
+// .suspend = lm_suspend,
+// .resume = lm_resume,
+};
+
+static int __init lm_init(void)
+{
+ return bus_register(&lm_bustype);
+}
+
+postcore_initcall(lm_init);
+
+static int lm_bus_probe(struct device *dev)
+{
+ struct lm_device *lmdev = to_lm_device(dev);
+ struct lm_driver *lmdrv = to_lm_driver(dev->driver);
+
+ return lmdrv->probe(lmdev);
+}
+
+static int lm_bus_remove(struct device *dev)
+{
+ struct lm_device *lmdev = to_lm_device(dev);
+ struct lm_driver *lmdrv = to_lm_driver(dev->driver);
+
+ lmdrv->remove(lmdev);
+ return 0;
+}
+
+int lm_driver_register(struct lm_driver *drv)
+{
+ drv->drv.bus = &lm_bustype;
+ drv->drv.probe = lm_bus_probe;
+ drv->drv.remove = lm_bus_remove;
+
+ return driver_register(&drv->drv);
+}
+
+void lm_driver_unregister(struct lm_driver *drv)
+{
+ driver_unregister(&drv->drv);
+}
+
+static void lm_device_release(struct device *dev)
+{
+ struct lm_device *d = to_lm_device(dev);
+
+ kfree(d);
+}
+
+int lm_device_register(struct lm_device *dev)
+{
+ int ret;
+
+ dev->dev.release = lm_device_release;
+ dev->dev.bus = &lm_bustype;
+
+ snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id), "lm%d", dev->id);
+ dev->resource.name = dev->dev.bus_id;
+
+ ret = request_resource(&iomem_resource, &dev->resource);
+ if (ret == 0) {
+ ret = device_register(&dev->dev);
+ if (ret)
+ release_resource(&dev->resource);
+ }
+ return ret;
+}
+
+EXPORT_SYMBOL(lm_driver_register);
+EXPORT_SYMBOL(lm_driver_unregister);
diff --git a/arch/arm/mach-integrator/pci.c b/arch/arm/mach-integrator/pci.c
new file mode 100644
index 00000000000..394ec9261c4
--- /dev/null
+++ b/arch/arm/mach-integrator/pci.c
@@ -0,0 +1,132 @@
+/*
+ * linux/arch/arm/mach-integrator/pci-integrator.c
+ *
+ * Copyright (C) 1999 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
+ *
+ *
+ * PCI functions for Integrator
+ */
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/ptrace.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/mach/pci.h>
+#include <asm/mach-types.h>
+
+/*
+ * A small note about bridges and interrupts. The DECchip 21050 (and
+ * later) adheres to the PCI-PCI bridge specification. This says that
+ * the interrupts on the other side of a bridge are swizzled in the
+ * following manner:
+ *
+ * Dev Interrupt Interrupt
+ * Pin on Pin on
+ * Device Connector
+ *
+ * 4 A A
+ * B B
+ * C C
+ * D D
+ *
+ * 5 A B
+ * B C
+ * C D
+ * D A
+ *
+ * 6 A C
+ * B D
+ * C A
+ * D B
+ *
+ * 7 A D
+ * B A
+ * C B
+ * D C
+ *
+ * Where A = pin 1, B = pin 2 and so on and pin=0 = default = A.
+ * Thus, each swizzle is ((pin-1) + (device#-4)) % 4
+ *
+ * The following code swizzles for exactly one bridge.
+ */
+static inline int bridge_swizzle(int pin, unsigned int slot)
+{
+ return (pin + slot) & 3;
+}
+
+/*
+ * This routine handles multiple bridges.
+ */
+static u8 __init integrator_swizzle(struct pci_dev *dev, u8 *pinp)
+{
+ int pin = *pinp;
+
+ if (pin == 0)
+ pin = 1;
+
+ pin -= 1;
+ while (dev->bus->self) {
+ pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn));
+ /*
+ * move up the chain of bridges, swizzling as we go.
+ */
+ dev = dev->bus->self;
+ }
+ *pinp = pin + 1;
+
+ return PCI_SLOT(dev->devfn);
+}
+
+static int irq_tab[4] __initdata = {
+ IRQ_AP_PCIINT0, IRQ_AP_PCIINT1, IRQ_AP_PCIINT2, IRQ_AP_PCIINT3
+};
+
+/*
+ * map the specified device/slot/pin to an IRQ. This works out such
+ * that slot 9 pin 1 is INT0, pin 2 is INT1, and slot 10 pin 1 is INT1.
+ */
+static int __init integrator_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+ int intnr = ((slot - 9) + (pin - 1)) & 3;
+
+ return irq_tab[intnr];
+}
+
+extern void pci_v3_init(void *);
+
+static struct hw_pci integrator_pci __initdata = {
+ .swizzle = integrator_swizzle,
+ .map_irq = integrator_map_irq,
+ .setup = pci_v3_setup,
+ .nr_controllers = 1,
+ .scan = pci_v3_scan_bus,
+ .preinit = pci_v3_preinit,
+ .postinit = pci_v3_postinit,
+};
+
+static int __init integrator_pci_init(void)
+{
+ if (machine_is_integrator())
+ pci_common_init(&integrator_pci);
+ return 0;
+}
+
+subsys_initcall(integrator_pci_init);
diff --git a/arch/arm/mach-integrator/pci_v3.c b/arch/arm/mach-integrator/pci_v3.c
new file mode 100644
index 00000000000..229a63a525c
--- /dev/null
+++ b/arch/arm/mach-integrator/pci_v3.c
@@ -0,0 +1,604 @@
+/*
+ * linux/arch/arm/mach-integrator/pci_v3.c
+ *
+ * PCI functions for V3 host PCI bridge
+ *
+ * Copyright (C) 1999 ARM Limited
+ * Copyright (C) 2000-2001 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
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/mach/pci.h>
+
+#include <asm/hardware/pci_v3.h>
+
+/*
+ * The V3 PCI interface chip in Integrator provides several windows from
+ * local bus memory into the PCI memory areas. Unfortunately, there
+ * are not really enough windows for our usage, therefore we reuse
+ * one of the windows for access to PCI configuration space. The
+ * memory map is as follows:
+ *
+ * Local Bus Memory Usage
+ *
+ * 40000000 - 4FFFFFFF PCI memory. 256M non-prefetchable
+ * 50000000 - 5FFFFFFF PCI memory. 256M prefetchable
+ * 60000000 - 60FFFFFF PCI IO. 16M
+ * 61000000 - 61FFFFFF PCI Configuration. 16M
+ *
+ * There are three V3 windows, each described by a pair of V3 registers.
+ * These are LB_BASE0/LB_MAP0, LB_BASE1/LB_MAP1 and LB_BASE2/LB_MAP2.
+ * Base0 and Base1 can be used for any type of PCI memory access. Base2
+ * can be used either for PCI I/O or for I20 accesses. By default, uHAL
+ * uses this only for PCI IO space.
+ *
+ * Normally these spaces are mapped using the following base registers:
+ *
+ * Usage Local Bus Memory Base/Map registers used
+ *
+ * Mem 40000000 - 4FFFFFFF LB_BASE0/LB_MAP0
+ * Mem 50000000 - 5FFFFFFF LB_BASE1/LB_MAP1
+ * IO 60000000 - 60FFFFFF LB_BASE2/LB_MAP2
+ * Cfg 61000000 - 61FFFFFF
+ *
+ * This means that I20 and PCI configuration space accesses will fail.
+ * When PCI configuration accesses are needed (via the uHAL PCI
+ * configuration space primitives) we must remap the spaces as follows:
+ *
+ * Usage Local Bus Memory Base/Map registers used
+ *
+ * Mem 40000000 - 4FFFFFFF LB_BASE0/LB_MAP0
+ * Mem 50000000 - 5FFFFFFF LB_BASE0/LB_MAP0
+ * IO 60000000 - 60FFFFFF LB_BASE2/LB_MAP2
+ * Cfg 61000000 - 61FFFFFF LB_BASE1/LB_MAP1
+ *
+ * To make this work, the code depends on overlapping windows working.
+ * The V3 chip translates an address by checking its range within
+ * each of the BASE/MAP pairs in turn (in ascending register number
+ * order). It will use the first matching pair. So, for example,
+ * if the same address is mapped by both LB_BASE0/LB_MAP0 and
+ * LB_BASE1/LB_MAP1, the V3 will use the translation from
+ * LB_BASE0/LB_MAP0.
+ *
+ * To allow PCI Configuration space access, the code enlarges the
+ * window mapped by LB_BASE0/LB_MAP0 from 256M to 512M. This occludes
+ * the windows currently mapped by LB_BASE1/LB_MAP1 so that it can
+ * be remapped for use by configuration cycles.
+ *
+ * At the end of the PCI Configuration space accesses,
+ * LB_BASE1/LB_MAP1 is reset to map PCI Memory. Finally the window
+ * mapped by LB_BASE0/LB_MAP0 is reduced in size from 512M to 256M to
+ * reveal the now restored LB_BASE1/LB_MAP1 window.
+ *
+ * NOTE: We do not set up I2O mapping. I suspect that this is only
+ * for an intelligent (target) device. Using I2O disables most of
+ * the mappings into PCI memory.
+ */
+
+// V3 access routines
+#define v3_writeb(o,v) __raw_writeb(v, PCI_V3_VADDR + (unsigned int)(o))
+#define v3_readb(o) (__raw_readb(PCI_V3_VADDR + (unsigned int)(o)))
+
+#define v3_writew(o,v) __raw_writew(v, PCI_V3_VADDR + (unsigned int)(o))
+#define v3_readw(o) (__raw_readw(PCI_V3_VADDR + (unsigned int)(o)))
+
+#define v3_writel(o,v) __raw_writel(v, PCI_V3_VADDR + (unsigned int)(o))
+#define v3_readl(o) (__raw_readl(PCI_V3_VADDR + (unsigned int)(o)))
+
+/*============================================================================
+ *
+ * routine: uHALir_PCIMakeConfigAddress()
+ *
+ * parameters: bus = which bus
+ * device = which device
+ * function = which function
+ * offset = configuration space register we are interested in
+ *
+ * description: this routine will generate a platform dependent config
+ * address.
+ *
+ * calls: none
+ *
+ * returns: configuration address to play on the PCI bus
+ *
+ * To generate the appropriate PCI configuration cycles in the PCI
+ * configuration address space, you present the V3 with the following pattern
+ * (which is very nearly a type 1 (except that the lower two bits are 00 and
+ * not 01). In order for this mapping to work you need to set up one of
+ * the local to PCI aperatures to 16Mbytes in length translating to
+ * PCI configuration space starting at 0x0000.0000.
+ *
+ * PCI configuration cycles look like this:
+ *
+ * Type 0:
+ *
+ * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1
+ * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | | |D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|0|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * 31:11 Device select bit.
+ * 10:8 Function number
+ * 7:2 Register number
+ *
+ * Type 1:
+ *
+ * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1
+ * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * 31:24 reserved
+ * 23:16 bus number (8 bits = 128 possible buses)
+ * 15:11 Device number (5 bits)
+ * 10:8 function number
+ * 7:2 register number
+ *
+ */
+static DEFINE_SPINLOCK(v3_lock);
+
+#define PCI_BUS_NONMEM_START 0x00000000
+#define PCI_BUS_NONMEM_SIZE SZ_256M
+
+#define PCI_BUS_PREMEM_START PCI_BUS_NONMEM_START + PCI_BUS_NONMEM_SIZE
+#define PCI_BUS_PREMEM_SIZE SZ_256M
+
+#if PCI_BUS_NONMEM_START & 0x000fffff
+#error PCI_BUS_NONMEM_START must be megabyte aligned
+#endif
+#if PCI_BUS_PREMEM_START & 0x000fffff
+#error PCI_BUS_PREMEM_START must be megabyte aligned
+#endif
+
+#undef V3_LB_BASE_PREFETCH
+#define V3_LB_BASE_PREFETCH 0
+
+static unsigned long v3_open_config_window(struct pci_bus *bus,
+ unsigned int devfn, int offset)
+{
+ unsigned int address, mapaddress, busnr;
+
+ busnr = bus->number;
+
+ /*
+ * Trap out illegal values
+ */
+ if (offset > 255)
+ BUG();
+ if (busnr > 255)
+ BUG();
+ if (devfn > 255)
+ BUG();
+
+ if (busnr == 0) {
+ int slot = PCI_SLOT(devfn);
+
+ /*
+ * local bus segment so need a type 0 config cycle
+ *
+ * build the PCI configuration "address" with one-hot in
+ * A31-A11
+ *
+ * mapaddress:
+ * 3:1 = config cycle (101)
+ * 0 = PCI A1 & A0 are 0 (0)
+ */
+ address = PCI_FUNC(devfn) << 8;
+ mapaddress = V3_LB_MAP_TYPE_CONFIG;
+
+ if (slot > 12)
+ /*
+ * high order bits are handled by the MAP register
+ */
+ mapaddress |= 1 << (slot - 5);
+ else
+ /*
+ * low order bits handled directly in the address
+ */
+ address |= 1 << (slot + 11);
+ } else {
+ /*
+ * not the local bus segment so need a type 1 config cycle
+ *
+ * address:
+ * 23:16 = bus number
+ * 15:11 = slot number (7:3 of devfn)
+ * 10:8 = func number (2:0 of devfn)
+ *
+ * mapaddress:
+ * 3:1 = config cycle (101)
+ * 0 = PCI A1 & A0 from host bus (1)
+ */
+ mapaddress = V3_LB_MAP_TYPE_CONFIG | V3_LB_MAP_AD_LOW_EN;
+ address = (busnr << 16) | (devfn << 8);
+ }
+
+ /*
+ * Set up base0 to see all 512Mbytes of memory space (not
+ * prefetchable), this frees up base1 for re-use by
+ * configuration memory
+ */
+ v3_writel(V3_LB_BASE0, v3_addr_to_lb_base(PHYS_PCI_MEM_BASE) |
+ V3_LB_BASE_ADR_SIZE_512MB | V3_LB_BASE_ENABLE);
+
+ /*
+ * Set up base1/map1 to point into configuration space.
+ */
+ v3_writel(V3_LB_BASE1, v3_addr_to_lb_base(PHYS_PCI_CONFIG_BASE) |
+ V3_LB_BASE_ADR_SIZE_16MB | V3_LB_BASE_ENABLE);
+ v3_writew(V3_LB_MAP1, mapaddress);
+
+ return PCI_CONFIG_VADDR + address + offset;
+}
+
+static void v3_close_config_window(void)
+{
+ /*
+ * Reassign base1 for use by prefetchable PCI memory
+ */
+ v3_writel(V3_LB_BASE1, v3_addr_to_lb_base(PHYS_PCI_MEM_BASE + SZ_256M) |
+ V3_LB_BASE_ADR_SIZE_256MB | V3_LB_BASE_PREFETCH |
+ V3_LB_BASE_ENABLE);
+ v3_writew(V3_LB_MAP1, v3_addr_to_lb_map(PCI_BUS_PREMEM_START) |
+ V3_LB_MAP_TYPE_MEM_MULTIPLE);
+
+ /*
+ * And shrink base0 back to a 256M window (NOTE: MAP0 already correct)
+ */
+ v3_writel(V3_LB_BASE0, v3_addr_to_lb_base(PHYS_PCI_MEM_BASE) |
+ V3_LB_BASE_ADR_SIZE_256MB | V3_LB_BASE_ENABLE);
+}
+
+static int v3_read_config(struct pci_bus *bus, unsigned int devfn, int where,
+ int size, u32 *val)
+{
+ unsigned long addr;
+ unsigned long flags;
+ u32 v;
+
+ spin_lock_irqsave(&v3_lock, flags);
+ addr = v3_open_config_window(bus, devfn, where);
+
+ switch (size) {
+ case 1:
+ v = __raw_readb(addr);
+ break;
+
+ case 2:
+ v = __raw_readw(addr);
+ break;
+
+ default:
+ v = __raw_readl(addr);
+ break;
+ }
+
+ v3_close_config_window();
+ spin_unlock_irqrestore(&v3_lock, flags);
+
+ *val = v;
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int v3_write_config(struct pci_bus *bus, unsigned int devfn, int where,
+ int size, u32 val)
+{
+ unsigned long addr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&v3_lock, flags);
+ addr = v3_open_config_window(bus, devfn, where);
+
+ switch (size) {
+ case 1:
+ __raw_writeb((u8)val, addr);
+ __raw_readb(addr);
+ break;
+
+ case 2:
+ __raw_writew((u16)val, addr);
+ __raw_readw(addr);
+ break;
+
+ case 4:
+ __raw_writel(val, addr);
+ __raw_readl(addr);
+ break;
+ }
+
+ v3_close_config_window();
+ spin_unlock_irqrestore(&v3_lock, flags);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops pci_v3_ops = {
+ .read = v3_read_config,
+ .write = v3_write_config,
+};
+
+static struct resource non_mem = {
+ .name = "PCI non-prefetchable",
+ .start = PHYS_PCI_MEM_BASE + PCI_BUS_NONMEM_START,
+ .end = PHYS_PCI_MEM_BASE + PCI_BUS_NONMEM_START + PCI_BUS_NONMEM_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct resource pre_mem = {
+ .name = "PCI prefetchable",
+ .start = PHYS_PCI_MEM_BASE + PCI_BUS_PREMEM_START,
+ .end = PHYS_PCI_MEM_BASE + PCI_BUS_PREMEM_START + PCI_BUS_PREMEM_SIZE - 1,
+ .flags = IORESOURCE_MEM | IORESOURCE_PREFETCH,
+};
+
+static int __init pci_v3_setup_resources(struct resource **resource)
+{
+ if (request_resource(&iomem_resource, &non_mem)) {
+ printk(KERN_ERR "PCI: unable to allocate non-prefetchable "
+ "memory region\n");
+ return -EBUSY;
+ }
+ if (request_resource(&iomem_resource, &pre_mem)) {
+ release_resource(&non_mem);
+ printk(KERN_ERR "PCI: unable to allocate prefetchable "
+ "memory region\n");
+ return -EBUSY;
+ }
+
+ /*
+ * bus->resource[0] is the IO resource for this bus
+ * bus->resource[1] is the mem resource for this bus
+ * bus->resource[2] is the prefetch mem resource for this bus
+ */
+ resource[0] = &ioport_resource;
+ resource[1] = &non_mem;
+ resource[2] = &pre_mem;
+
+ return 1;
+}
+
+/*
+ * These don't seem to be implemented on the Integrator I have, which
+ * means I can't get additional information on the reason for the pm2fb
+ * problems. I suppose I'll just have to mind-meld with the machine. ;)
+ */
+#define SC_PCI (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_PCIENABLE_OFFSET)
+#define SC_LBFADDR (IO_ADDRESS(INTEGRATOR_SC_BASE) + 0x20)
+#define SC_LBFCODE (IO_ADDRESS(INTEGRATOR_SC_BASE) + 0x24)
+
+static int
+v3_pci_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
+{
+ unsigned long pc = instruction_pointer(regs);
+ unsigned long instr = *(unsigned long *)pc;
+#if 0
+ char buf[128];
+
+ sprintf(buf, "V3 fault: addr 0x%08lx, FSR 0x%03x, PC 0x%08lx [%08lx] LBFADDR=%08x LBFCODE=%02x ISTAT=%02x\n",
+ addr, fsr, pc, instr, __raw_readl(SC_LBFADDR), __raw_readl(SC_LBFCODE) & 255,
+ v3_readb(V3_LB_ISTAT));
+ printk(KERN_DEBUG "%s", buf);
+ printascii(buf);
+#endif
+
+ v3_writeb(V3_LB_ISTAT, 0);
+ __raw_writel(3, SC_PCI);
+
+ /*
+ * If the instruction being executed was a read,
+ * make it look like it read all-ones.
+ */
+ if ((instr & 0x0c100000) == 0x04100000) {
+ int reg = (instr >> 12) & 15;
+ unsigned long val;
+
+ if (instr & 0x00400000)
+ val = 255;
+ else
+ val = -1;
+
+ regs->uregs[reg] = val;
+ regs->ARM_pc += 4;
+ return 0;
+ }
+
+ if ((instr & 0x0e100090) == 0x00100090) {
+ int reg = (instr >> 12) & 15;
+
+ regs->uregs[reg] = -1;
+ regs->ARM_pc += 4;
+ return 0;
+ }
+
+ return 1;
+}
+
+static irqreturn_t v3_irq(int irq, void *devid, struct pt_regs *regs)
+{
+#ifdef CONFIG_DEBUG_LL
+ unsigned long pc = instruction_pointer(regs);
+ unsigned long instr = *(unsigned long *)pc;
+ char buf[128];
+
+ sprintf(buf, "V3 int %d: pc=0x%08lx [%08lx] LBFADDR=%08x LBFCODE=%02x ISTAT=%02x\n", irq,
+ pc, instr, __raw_readl(SC_LBFADDR), __raw_readl(SC_LBFCODE) & 255,
+ v3_readb(V3_LB_ISTAT));
+ printascii(buf);
+#endif
+
+ v3_writew(V3_PCI_STAT, 0xf000);
+ v3_writeb(V3_LB_ISTAT, 0);
+ __raw_writel(3, SC_PCI);
+
+#ifdef CONFIG_DEBUG_LL
+ /*
+ * If the instruction being executed was a read,
+ * make it look like it read all-ones.
+ */
+ if ((instr & 0x0c100000) == 0x04100000) {
+ int reg = (instr >> 16) & 15;
+ sprintf(buf, " reg%d = %08lx\n", reg, regs->uregs[reg]);
+ printascii(buf);
+ }
+#endif
+ return IRQ_HANDLED;
+}
+
+int __init pci_v3_setup(int nr, struct pci_sys_data *sys)
+{
+ int ret = 0;
+
+ if (nr == 0) {
+ sys->mem_offset = PHYS_PCI_MEM_BASE;
+ ret = pci_v3_setup_resources(sys->resource);
+ }
+
+ return ret;
+}
+
+struct pci_bus *pci_v3_scan_bus(int nr, struct pci_sys_data *sys)
+{
+ return pci_scan_bus(sys->busnr, &pci_v3_ops, sys);
+}
+
+/*
+ * V3_LB_BASE? - local bus address
+ * V3_LB_MAP? - pci bus address
+ */
+void __init pci_v3_preinit(void)
+{
+ unsigned long flags;
+ unsigned int temp;
+ int ret;
+
+ /*
+ * Hook in our fault handler for PCI errors
+ */
+ hook_fault_code(4, v3_pci_fault, SIGBUS, "external abort on linefetch");
+ hook_fault_code(6, v3_pci_fault, SIGBUS, "external abort on linefetch");
+ hook_fault_code(8, v3_pci_fault, SIGBUS, "external abort on non-linefetch");
+ hook_fault_code(10, v3_pci_fault, SIGBUS, "external abort on non-linefetch");
+
+ spin_lock_irqsave(&v3_lock, flags);
+
+ /*
+ * Unlock V3 registers, but only if they were previously locked.
+ */
+ if (v3_readw(V3_SYSTEM) & V3_SYSTEM_M_LOCK)
+ v3_writew(V3_SYSTEM, 0xa05f);
+
+ /*
+ * Setup window 0 - PCI non-prefetchable memory
+ * Local: 0x40000000 Bus: 0x00000000 Size: 256MB
+ */
+ v3_writel(V3_LB_BASE0, v3_addr_to_lb_base(PHYS_PCI_MEM_BASE) |
+ V3_LB_BASE_ADR_SIZE_256MB | V3_LB_BASE_ENABLE);
+ v3_writew(V3_LB_MAP0, v3_addr_to_lb_map(PCI_BUS_NONMEM_START) |
+ V3_LB_MAP_TYPE_MEM);
+
+ /*
+ * Setup window 1 - PCI prefetchable memory
+ * Local: 0x50000000 Bus: 0x10000000 Size: 256MB
+ */
+ v3_writel(V3_LB_BASE1, v3_addr_to_lb_base(PHYS_PCI_MEM_BASE + SZ_256M) |
+ V3_LB_BASE_ADR_SIZE_256MB | V3_LB_BASE_PREFETCH |
+ V3_LB_BASE_ENABLE);
+ v3_writew(V3_LB_MAP1, v3_addr_to_lb_map(PCI_BUS_PREMEM_START) |
+ V3_LB_MAP_TYPE_MEM_MULTIPLE);
+
+ /*
+ * Setup window 2 - PCI IO
+ */
+ v3_writel(V3_LB_BASE2, v3_addr_to_lb_base2(PHYS_PCI_IO_BASE) |
+ V3_LB_BASE_ENABLE);
+ v3_writew(V3_LB_MAP2, v3_addr_to_lb_map2(0));
+
+ /*
+ * Disable PCI to host IO cycles
+ */
+ temp = v3_readw(V3_PCI_CFG) & ~V3_PCI_CFG_M_I2O_EN;
+ temp |= V3_PCI_CFG_M_IO_REG_DIS | V3_PCI_CFG_M_IO_DIS;
+ v3_writew(V3_PCI_CFG, temp);
+
+ printk(KERN_DEBUG "FIFO_CFG: %04x FIFO_PRIO: %04x\n",
+ v3_readw(V3_FIFO_CFG), v3_readw(V3_FIFO_PRIORITY));
+
+ /*
+ * Set the V3 FIFO such that writes have higher priority than
+ * reads, and local bus write causes local bus read fifo flush.
+ * Same for PCI.
+ */
+ v3_writew(V3_FIFO_PRIORITY, 0x0a0a);
+
+ /*
+ * Re-lock the system register.
+ */
+ temp = v3_readw(V3_SYSTEM) | V3_SYSTEM_M_LOCK;
+ v3_writew(V3_SYSTEM, temp);
+
+ /*
+ * Clear any error conditions, and enable write errors.
+ */
+ v3_writeb(V3_LB_ISTAT, 0);
+ v3_writew(V3_LB_CFG, v3_readw(V3_LB_CFG) | (1 << 10));
+ v3_writeb(V3_LB_IMASK, 0x28);
+ __raw_writel(3, SC_PCI);
+
+ /*
+ * Grab the PCI error interrupt.
+ */
+ ret = request_irq(IRQ_AP_V3INT, v3_irq, 0, "V3", NULL);
+ if (ret)
+ printk(KERN_ERR "PCI: unable to grab PCI error "
+ "interrupt: %d\n", ret);
+
+ spin_unlock_irqrestore(&v3_lock, flags);
+}
+
+void __init pci_v3_postinit(void)
+{
+ unsigned int pci_cmd;
+
+ pci_cmd = PCI_COMMAND_MEMORY |
+ PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE;
+
+ v3_writew(V3_PCI_CMD, pci_cmd);
+
+ v3_writeb(V3_LB_ISTAT, ~0x40);
+ v3_writeb(V3_LB_IMASK, 0x68);
+
+#if 0
+ ret = request_irq(IRQ_AP_LBUSTIMEOUT, lb_timeout, 0, "bus timeout", NULL);
+ if (ret)
+ printk(KERN_ERR "PCI: unable to grab local bus timeout "
+ "interrupt: %d\n", ret);
+#endif
+}
diff --git a/arch/arm/mach-integrator/time.c b/arch/arm/mach-integrator/time.c
new file mode 100644
index 00000000000..20729de2af2
--- /dev/null
+++ b/arch/arm/mach-integrator/time.c
@@ -0,0 +1,213 @@
+/*
+ * linux/arch/arm/mach-integrator/time.c
+ *
+ * Copyright (C) 2000-2001 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/mc146818rtc.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/device.h>
+
+#include <asm/hardware/amba.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/rtc.h>
+
+#include <asm/mach/time.h>
+
+#define RTC_DR (0)
+#define RTC_MR (4)
+#define RTC_STAT (8)
+#define RTC_EOI (8)
+#define RTC_LR (12)
+#define RTC_CR (16)
+#define RTC_CR_MIE (1 << 0)
+
+extern int (*set_rtc)(void);
+static void __iomem *rtc_base;
+
+static int integrator_set_rtc(void)
+{
+ __raw_writel(xtime.tv_sec, rtc_base + RTC_LR);
+ return 1;
+}
+
+static void rtc_read_alarm(struct rtc_wkalrm *alrm)
+{
+ rtc_time_to_tm(readl(rtc_base + RTC_MR), &alrm->time);
+}
+
+static int rtc_set_alarm(struct rtc_wkalrm *alrm)
+{
+ unsigned long time;
+ int ret;
+
+ ret = rtc_tm_to_time(&alrm->time, &time);
+ if (ret == 0)
+ writel(time, rtc_base + RTC_MR);
+ return ret;
+}
+
+static void rtc_read_time(struct rtc_time *tm)
+{
+ rtc_time_to_tm(readl(rtc_base + RTC_DR), tm);
+}
+
+/*
+ * Set the RTC time. Unfortunately, we can't accurately set
+ * the point at which the counter updates.
+ *
+ * Also, since RTC_LR is transferred to RTC_CR on next rising
+ * edge of the 1Hz clock, we must write the time one second
+ * in advance.
+ */
+static int rtc_set_time(struct rtc_time *tm)
+{
+ unsigned long time;
+ int ret;
+
+ ret = rtc_tm_to_time(tm, &time);
+ if (ret == 0)
+ writel(time + 1, rtc_base + RTC_LR);
+
+ return ret;
+}
+
+static struct rtc_ops rtc_ops = {
+ .owner = THIS_MODULE,
+ .read_time = rtc_read_time,
+ .set_time = rtc_set_time,
+ .read_alarm = rtc_read_alarm,
+ .set_alarm = rtc_set_alarm,
+};
+
+static irqreturn_t rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ writel(0, rtc_base + RTC_EOI);
+ return IRQ_HANDLED;
+}
+
+static int rtc_probe(struct amba_device *dev, void *id)
+{
+ int ret;
+
+ if (rtc_base)
+ return -EBUSY;
+
+ ret = amba_request_regions(dev, NULL);
+ if (ret)
+ goto out;
+
+ rtc_base = ioremap(dev->res.start, SZ_4K);
+ if (!rtc_base) {
+ ret = -ENOMEM;
+ goto res_out;
+ }
+
+ __raw_writel(0, rtc_base + RTC_CR);
+ __raw_writel(0, rtc_base + RTC_EOI);
+
+ xtime.tv_sec = __raw_readl(rtc_base + RTC_DR);
+
+ ret = request_irq(dev->irq[0], rtc_interrupt, SA_INTERRUPT,
+ "rtc-pl030", dev);
+ if (ret)
+ goto map_out;
+
+ ret = register_rtc(&rtc_ops);
+ if (ret)
+ goto irq_out;
+
+ set_rtc = integrator_set_rtc;
+ return 0;
+
+ irq_out:
+ free_irq(dev->irq[0], dev);
+ map_out:
+ iounmap(rtc_base);
+ rtc_base = NULL;
+ res_out:
+ amba_release_regions(dev);
+ out:
+ return ret;
+}
+
+static int rtc_remove(struct amba_device *dev)
+{
+ set_rtc = NULL;
+
+ writel(0, rtc_base + RTC_CR);
+
+ free_irq(dev->irq[0], dev);
+ unregister_rtc(&rtc_ops);
+
+ iounmap(rtc_base);
+ rtc_base = NULL;
+ amba_release_regions(dev);
+
+ return 0;
+}
+
+static struct timespec rtc_delta;
+
+static int rtc_suspend(struct amba_device *dev, pm_message_t state)
+{
+ struct timespec rtc;
+
+ rtc.tv_sec = readl(rtc_base + RTC_DR);
+ rtc.tv_nsec = 0;
+ save_time_delta(&rtc_delta, &rtc);
+
+ return 0;
+}
+
+static int rtc_resume(struct amba_device *dev)
+{
+ struct timespec rtc;
+
+ rtc.tv_sec = readl(rtc_base + RTC_DR);
+ rtc.tv_nsec = 0;
+ restore_time_delta(&rtc_delta, &rtc);
+
+ return 0;
+}
+
+static struct amba_id rtc_ids[] = {
+ {
+ .id = 0x00041030,
+ .mask = 0x000fffff,
+ },
+ { 0, 0 },
+};
+
+static struct amba_driver rtc_driver = {
+ .drv = {
+ .name = "rtc-pl030",
+ },
+ .probe = rtc_probe,
+ .remove = rtc_remove,
+ .suspend = rtc_suspend,
+ .resume = rtc_resume,
+ .id_table = rtc_ids,
+};
+
+static int __init integrator_rtc_init(void)
+{
+ return amba_driver_register(&rtc_driver);
+}
+
+static void __exit integrator_rtc_exit(void)
+{
+ amba_driver_unregister(&rtc_driver);
+}
+
+module_init(integrator_rtc_init);
+module_exit(integrator_rtc_exit);