diff options
Diffstat (limited to 'arch/arm/mach-integrator')
-rw-r--r-- | arch/arm/mach-integrator/Kconfig | 33 | ||||
-rw-r--r-- | arch/arm/mach-integrator/Makefile | 14 | ||||
-rw-r--r-- | arch/arm/mach-integrator/Makefile.boot | 4 | ||||
-rw-r--r-- | arch/arm/mach-integrator/clock.c | 141 | ||||
-rw-r--r-- | arch/arm/mach-integrator/clock.h | 25 | ||||
-rw-r--r-- | arch/arm/mach-integrator/common.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-integrator/core.c | 271 | ||||
-rw-r--r-- | arch/arm/mach-integrator/cpu.c | 221 | ||||
-rw-r--r-- | arch/arm/mach-integrator/dma.c | 35 | ||||
-rw-r--r-- | arch/arm/mach-integrator/impd1.c | 475 | ||||
-rw-r--r-- | arch/arm/mach-integrator/integrator_ap.c | 302 | ||||
-rw-r--r-- | arch/arm/mach-integrator/integrator_cp.c | 528 | ||||
-rw-r--r-- | arch/arm/mach-integrator/leds.c | 88 | ||||
-rw-r--r-- | arch/arm/mach-integrator/lm.c | 96 | ||||
-rw-r--r-- | arch/arm/mach-integrator/pci.c | 132 | ||||
-rw-r--r-- | arch/arm/mach-integrator/pci_v3.c | 604 | ||||
-rw-r--r-- | arch/arm/mach-integrator/time.c | 213 |
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); |