diff options
Diffstat (limited to 'arch/arm/common')
-rw-r--r-- | arch/arm/common/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/common/gic.c | 107 | ||||
-rw-r--r-- | arch/arm/common/it8152.c | 1 | ||||
-rw-r--r-- | arch/arm/common/timer-sp.c | 154 |
4 files changed, 227 insertions, 36 deletions
diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index e6e8664a941..e7521bca2c3 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile @@ -17,3 +17,4 @@ obj-$(CONFIG_ARCH_IXP2000) += uengine.o obj-$(CONFIG_ARCH_IXP23XX) += uengine.o obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o obj-$(CONFIG_COMMON_CLKDEV) += clkdev.o +obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index 772f95f1aec..0b89ef00133 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c @@ -35,6 +35,9 @@ static DEFINE_SPINLOCK(irq_controller_lock); +/* Address of GIC 0 CPU interface */ +void __iomem *gic_cpu_base_addr __read_mostly; + struct gic_chip_data { unsigned int irq_offset; void __iomem *dist_base; @@ -45,7 +48,7 @@ struct gic_chip_data { #define MAX_GIC_NR 1 #endif -static struct gic_chip_data gic_data[MAX_GIC_NR]; +static struct gic_chip_data gic_data[MAX_GIC_NR] __read_mostly; static inline void __iomem *gic_dist_base(unsigned int irq) { @@ -146,9 +149,15 @@ static int gic_set_cpu(unsigned int irq, const struct cpumask *mask_val) unsigned int shift = (irq % 4) * 8; unsigned int cpu = cpumask_first(mask_val); u32 val; + struct irq_desc *desc; spin_lock(&irq_controller_lock); - irq_desc[irq].node = cpu; + desc = irq_to_desc(irq); + if (desc == NULL) { + spin_unlock(&irq_controller_lock); + return -EINVAL; + } + desc->node = cpu; val = readl(reg) & ~(0xff << shift); val |= 1 << (cpu + shift); writel(val, reg); @@ -207,68 +216,65 @@ void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq) set_irq_chained_handler(irq, gic_handle_cascade_irq); } -void __init gic_dist_init(unsigned int gic_nr, void __iomem *base, - unsigned int irq_start) +static void __init gic_dist_init(struct gic_chip_data *gic, + unsigned int irq_start) { - unsigned int max_irq, i; + unsigned int gic_irqs, irq_limit, i; + void __iomem *base = gic->dist_base; u32 cpumask = 1 << smp_processor_id(); - if (gic_nr >= MAX_GIC_NR) - BUG(); - cpumask |= cpumask << 8; cpumask |= cpumask << 16; - gic_data[gic_nr].dist_base = base; - gic_data[gic_nr].irq_offset = (irq_start - 1) & ~31; - writel(0, base + GIC_DIST_CTRL); /* * Find out how many interrupts are supported. - */ - max_irq = readl(base + GIC_DIST_CTR) & 0x1f; - max_irq = (max_irq + 1) * 32; - - /* * The GIC only supports up to 1020 interrupt sources. - * Limit this to either the architected maximum, or the - * platform maximum. */ - if (max_irq > max(1020, NR_IRQS)) - max_irq = max(1020, NR_IRQS); + gic_irqs = readl(base + GIC_DIST_CTR) & 0x1f; + gic_irqs = (gic_irqs + 1) * 32; + if (gic_irqs > 1020) + gic_irqs = 1020; /* * Set all global interrupts to be level triggered, active low. */ - for (i = 32; i < max_irq; i += 16) + for (i = 32; i < gic_irqs; i += 16) writel(0, base + GIC_DIST_CONFIG + i * 4 / 16); /* * Set all global interrupts to this CPU only. */ - for (i = 32; i < max_irq; i += 4) + for (i = 32; i < gic_irqs; i += 4) writel(cpumask, base + GIC_DIST_TARGET + i * 4 / 4); /* * Set priority on all global interrupts. */ - for (i = 32; i < max_irq; i += 4) + for (i = 32; i < gic_irqs; i += 4) writel(0xa0a0a0a0, base + GIC_DIST_PRI + i * 4 / 4); /* * Disable all interrupts. Leave the PPI and SGIs alone * as these enables are banked registers. */ - for (i = 32; i < max_irq; i += 32) + for (i = 32; i < gic_irqs; i += 32) writel(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32); /* + * Limit number of interrupts registered to the platform maximum + */ + irq_limit = gic->irq_offset + gic_irqs; + if (WARN_ON(irq_limit > NR_IRQS)) + irq_limit = NR_IRQS; + + /* * Setup the Linux IRQ subsystem. */ - for (i = irq_start; i < gic_data[gic_nr].irq_offset + max_irq; i++) { + for (i = irq_start; i < irq_limit; i++) { set_irq_chip(i, &gic_chip); - set_irq_chip_data(i, &gic_data[gic_nr]); + set_irq_chip_data(i, gic); set_irq_handler(i, handle_level_irq); set_irq_flags(i, IRQF_VALID | IRQF_PROBE); } @@ -276,19 +282,12 @@ void __init gic_dist_init(unsigned int gic_nr, void __iomem *base, writel(1, base + GIC_DIST_CTRL); } -void __cpuinit gic_cpu_init(unsigned int gic_nr, void __iomem *base) +static void __cpuinit gic_cpu_init(struct gic_chip_data *gic) { - void __iomem *dist_base; + void __iomem *dist_base = gic->dist_base; + void __iomem *base = gic->cpu_base; int i; - if (gic_nr >= MAX_GIC_NR) - BUG(); - - dist_base = gic_data[gic_nr].dist_base; - BUG_ON(!dist_base); - - gic_data[gic_nr].cpu_base = base; - /* * Deal with the banked PPI and SGI interrupts - disable all * PPI interrupts, ensure all SGI interrupts are enabled. @@ -306,6 +305,42 @@ void __cpuinit gic_cpu_init(unsigned int gic_nr, void __iomem *base) writel(1, base + GIC_CPU_CTRL); } +void __init gic_init(unsigned int gic_nr, unsigned int irq_start, + void __iomem *dist_base, void __iomem *cpu_base) +{ + struct gic_chip_data *gic; + + BUG_ON(gic_nr >= MAX_GIC_NR); + + gic = &gic_data[gic_nr]; + gic->dist_base = dist_base; + gic->cpu_base = cpu_base; + gic->irq_offset = (irq_start - 1) & ~31; + + if (gic_nr == 0) + gic_cpu_base_addr = cpu_base; + + gic_dist_init(gic, irq_start); + gic_cpu_init(gic); +} + +void __cpuinit gic_secondary_init(unsigned int gic_nr) +{ + BUG_ON(gic_nr >= MAX_GIC_NR); + + gic_cpu_init(&gic_data[gic_nr]); +} + +void __cpuinit gic_enable_ppi(unsigned int irq) +{ + unsigned long flags; + + local_irq_save(flags); + irq_to_desc(irq)->status |= IRQ_NOPROBE; + gic_unmask_irq(irq); + local_irq_restore(flags); +} + #ifdef CONFIG_SMP void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) { diff --git a/arch/arm/common/it8152.c b/arch/arm/common/it8152.c index 1bec96e8519..42ff90b46df 100644 --- a/arch/arm/common/it8152.c +++ b/arch/arm/common/it8152.c @@ -352,3 +352,4 @@ struct pci_bus * __init it8152_pci_scan_bus(int nr, struct pci_sys_data *sys) return pci_scan_bus(nr, &it8152_ops, sys); } +EXPORT_SYMBOL(dma_set_coherent_mask); diff --git a/arch/arm/common/timer-sp.c b/arch/arm/common/timer-sp.c new file mode 100644 index 00000000000..4740313daa5 --- /dev/null +++ b/arch/arm/common/timer-sp.c @@ -0,0 +1,154 @@ +/* + * linux/arch/arm/common/timer-sp.c + * + * Copyright (C) 1999 - 2003 ARM Limited + * Copyright (C) 2000 Deep Blue Solutions Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/clocksource.h> +#include <linux/clockchips.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/io.h> + +#include <asm/hardware/arm_timer.h> + +/* + * These timers are currently always setup to be clocked at 1MHz. + */ +#define TIMER_FREQ_KHZ (1000) +#define TIMER_RELOAD (TIMER_FREQ_KHZ * 1000 / HZ) + +static void __iomem *clksrc_base; + +static cycle_t sp804_read(struct clocksource *cs) +{ + return ~readl(clksrc_base + TIMER_VALUE); +} + +static struct clocksource clocksource_sp804 = { + .name = "timer3", + .rating = 200, + .read = sp804_read, + .mask = CLOCKSOURCE_MASK(32), + .shift = 20, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +void __init sp804_clocksource_init(void __iomem *base) +{ + struct clocksource *cs = &clocksource_sp804; + + clksrc_base = base; + + /* setup timer 0 as free-running clocksource */ + writel(0, clksrc_base + TIMER_CTRL); + writel(0xffffffff, clksrc_base + TIMER_LOAD); + writel(0xffffffff, clksrc_base + TIMER_VALUE); + writel(TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC, + clksrc_base + TIMER_CTRL); + + cs->mult = clocksource_khz2mult(TIMER_FREQ_KHZ, cs->shift); + clocksource_register(cs); +} + + +static void __iomem *clkevt_base; + +/* + * IRQ handler for the timer + */ +static irqreturn_t sp804_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + + /* clear the interrupt */ + writel(1, clkevt_base + TIMER_INTCLR); + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static void sp804_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + unsigned long ctrl = TIMER_CTRL_32BIT | TIMER_CTRL_IE; + + writel(ctrl, clkevt_base + TIMER_CTRL); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + writel(TIMER_RELOAD, clkevt_base + TIMER_LOAD); + ctrl |= TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE; + break; + + case CLOCK_EVT_MODE_ONESHOT: + /* period set, and timer enabled in 'next_event' hook */ + ctrl |= TIMER_CTRL_ONESHOT; + break; + + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + default: + break; + } + + writel(ctrl, clkevt_base + TIMER_CTRL); +} + +static int sp804_set_next_event(unsigned long next, + struct clock_event_device *evt) +{ + unsigned long ctrl = readl(clkevt_base + TIMER_CTRL); + + writel(next, clkevt_base + TIMER_LOAD); + writel(ctrl | TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL); + + return 0; +} + +static struct clock_event_device sp804_clockevent = { + .name = "timer0", + .shift = 32, + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = sp804_set_mode, + .set_next_event = sp804_set_next_event, + .rating = 300, + .cpumask = cpu_all_mask, +}; + +static struct irqaction sp804_timer_irq = { + .name = "timer", + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, + .handler = sp804_timer_interrupt, + .dev_id = &sp804_clockevent, +}; + +void __init sp804_clockevents_init(void __iomem *base, unsigned int timer_irq) +{ + struct clock_event_device *evt = &sp804_clockevent; + + clkevt_base = base; + + evt->irq = timer_irq; + evt->mult = div_sc(TIMER_FREQ_KHZ, NSEC_PER_MSEC, evt->shift); + evt->max_delta_ns = clockevent_delta2ns(0xffffffff, evt); + evt->min_delta_ns = clockevent_delta2ns(0xf, evt); + + setup_irq(timer_irq, &sp804_timer_irq); + clockevents_register_device(evt); +} |