linux/arch/ia64/kernel/irq_ia64.c
<<
>>
Prefs
   1/*
   2 * linux/arch/ia64/kernel/irq.c
   3 *
   4 * Copyright (C) 1998-2001 Hewlett-Packard Co
   5 *      Stephane Eranian <eranian@hpl.hp.com>
   6 *      David Mosberger-Tang <davidm@hpl.hp.com>
   7 *
   8 *  6/10/99: Updated to bring in sync with x86 version to facilitate
   9 *           support for SMP and different interrupt controllers.
  10 *
  11 * 09/15/00 Goutham Rao <goutham.rao@intel.com> Implemented pci_irq_to_vector
  12 *                      PCI to vector allocation routine.
  13 * 04/14/2004 Ashok Raj <ashok.raj@intel.com>
  14 *                                              Added CPU Hotplug handling for IPF.
  15 */
  16
  17#include <linux/module.h>
  18
  19#include <linux/jiffies.h>
  20#include <linux/errno.h>
  21#include <linux/init.h>
  22#include <linux/interrupt.h>
  23#include <linux/ioport.h>
  24#include <linux/kernel_stat.h>
  25#include <linux/slab.h>
  26#include <linux/ptrace.h>
  27#include <linux/random.h>       /* for rand_initialize_irq() */
  28#include <linux/signal.h>
  29#include <linux/smp.h>
  30#include <linux/smp_lock.h>
  31#include <linux/threads.h>
  32#include <linux/bitops.h>
  33
  34#include <asm/delay.h>
  35#include <asm/intrinsics.h>
  36#include <asm/io.h>
  37#include <asm/hw_irq.h>
  38#include <asm/machvec.h>
  39#include <asm/pgtable.h>
  40#include <asm/system.h>
  41
  42#ifdef CONFIG_PERFMON
  43# include <asm/perfmon.h>
  44#endif
  45
  46#define IRQ_DEBUG       0
  47
  48/* These can be overridden in platform_irq_init */
  49int ia64_first_device_vector = IA64_DEF_FIRST_DEVICE_VECTOR;
  50int ia64_last_device_vector = IA64_DEF_LAST_DEVICE_VECTOR;
  51
  52/* default base addr of IPI table */
  53void __iomem *ipi_base_addr = ((void __iomem *)
  54                               (__IA64_UNCACHED_OFFSET | IA64_IPI_DEFAULT_BASE_ADDR));
  55
  56/*
  57 * Legacy IRQ to IA-64 vector translation table.
  58 */
  59__u8 isa_irq_to_vector_map[16] = {
  60        /* 8259 IRQ translation, first 16 entries */
  61        0x2f, 0x20, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29,
  62        0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21
  63};
  64EXPORT_SYMBOL(isa_irq_to_vector_map);
  65
  66static unsigned long ia64_vector_mask[BITS_TO_LONGS(IA64_MAX_DEVICE_VECTORS)];
  67
  68int
  69assign_irq_vector (int irq)
  70{
  71        int pos, vector;
  72 again:
  73        pos = find_first_zero_bit(ia64_vector_mask, IA64_NUM_DEVICE_VECTORS);
  74        vector = IA64_FIRST_DEVICE_VECTOR + pos;
  75        if (vector > IA64_LAST_DEVICE_VECTOR)
  76                return -ENOSPC;
  77        if (test_and_set_bit(pos, ia64_vector_mask))
  78                goto again;
  79        return vector;
  80}
  81
  82void
  83free_irq_vector (int vector)
  84{
  85        int pos;
  86
  87        if (vector < IA64_FIRST_DEVICE_VECTOR || vector > IA64_LAST_DEVICE_VECTOR)
  88                return;
  89
  90        pos = vector - IA64_FIRST_DEVICE_VECTOR;
  91        if (!test_and_clear_bit(pos, ia64_vector_mask))
  92                printk(KERN_WARNING "%s: double free!\n", __FUNCTION__);
  93}
  94
  95int
  96reserve_irq_vector (int vector)
  97{
  98        int pos;
  99
 100        if (vector < IA64_FIRST_DEVICE_VECTOR ||
 101            vector > IA64_LAST_DEVICE_VECTOR)
 102                return -EINVAL;
 103
 104        pos = vector - IA64_FIRST_DEVICE_VECTOR;
 105        return test_and_set_bit(pos, ia64_vector_mask);
 106}
 107
 108#ifdef CONFIG_SMP
 109#       define IS_RESCHEDULE(vec)       (vec == IA64_IPI_RESCHEDULE)
 110#else
 111#       define IS_RESCHEDULE(vec)       (0)
 112#endif
 113/*
 114 * That's where the IVT branches when we get an external
 115 * interrupt. This branches to the correct hardware IRQ handler via
 116 * function ptr.
 117 */
 118void
 119ia64_handle_irq (ia64_vector vector, struct pt_regs *regs)
 120{
 121        unsigned long saved_tpr;
 122
 123#if IRQ_DEBUG
 124        {
 125                unsigned long bsp, sp;
 126
 127                /*
 128                 * Note: if the interrupt happened while executing in
 129                 * the context switch routine (ia64_switch_to), we may
 130                 * get a spurious stack overflow here.  This is
 131                 * because the register and the memory stack are not
 132                 * switched atomically.
 133                 */
 134                bsp = ia64_getreg(_IA64_REG_AR_BSP);
 135                sp = ia64_getreg(_IA64_REG_SP);
 136
 137                if ((sp - bsp) < 1024) {
 138                        static unsigned char count;
 139                        static long last_time;
 140
 141                        if (jiffies - last_time > 5*HZ)
 142                                count = 0;
 143                        if (++count < 5) {
 144                                last_time = jiffies;
 145                                printk("ia64_handle_irq: DANGER: less than "
 146                                       "1KB of free stack space!!\n"
 147                                       "(bsp=0x%lx, sp=%lx)\n", bsp, sp);
 148                        }
 149                }
 150        }
 151#endif /* IRQ_DEBUG */
 152
 153        /*
 154         * Always set TPR to limit maximum interrupt nesting depth to
 155         * 16 (without this, it would be ~240, which could easily lead
 156         * to kernel stack overflows).
 157         */
 158        irq_enter();
 159        saved_tpr = ia64_getreg(_IA64_REG_CR_TPR);
 160        ia64_srlz_d();
 161        while (vector != IA64_SPURIOUS_INT_VECTOR) {
 162                if (!IS_RESCHEDULE(vector)) {
 163                        ia64_setreg(_IA64_REG_CR_TPR, vector);
 164                        ia64_srlz_d();
 165
 166                        __do_IRQ(local_vector_to_irq(vector), regs);
 167
 168                        /*
 169                         * Disable interrupts and send EOI:
 170                         */
 171                        local_irq_disable();
 172                        ia64_setreg(_IA64_REG_CR_TPR, saved_tpr);
 173                }
 174                ia64_eoi();
 175                vector = ia64_get_ivr();
 176        }
 177        /*
 178         * This must be done *after* the ia64_eoi().  For example, the keyboard softirq
 179         * handler needs to be able to wait for further keyboard interrupts, which can't
 180         * come through until ia64_eoi() has been done.
 181         */
 182        irq_exit();
 183}
 184
 185#ifdef CONFIG_HOTPLUG_CPU
 186/*
 187 * This function emulates a interrupt processing when a cpu is about to be
 188 * brought down.
 189 */
 190void ia64_process_pending_intr(void)
 191{
 192        ia64_vector vector;
 193        unsigned long saved_tpr;
 194        extern unsigned int vectors_in_migration[NR_IRQS];
 195
 196        vector = ia64_get_ivr();
 197
 198         irq_enter();
 199         saved_tpr = ia64_getreg(_IA64_REG_CR_TPR);
 200         ia64_srlz_d();
 201
 202         /*
 203          * Perform normal interrupt style processing
 204          */
 205        while (vector != IA64_SPURIOUS_INT_VECTOR) {
 206                if (!IS_RESCHEDULE(vector)) {
 207                        ia64_setreg(_IA64_REG_CR_TPR, vector);
 208                        ia64_srlz_d();
 209
 210                        /*
 211                         * Now try calling normal ia64_handle_irq as it would have got called
 212                         * from a real intr handler. Try passing null for pt_regs, hopefully
 213                         * it will work. I hope it works!.
 214                         * Probably could shared code.
 215                         */
 216                        vectors_in_migration[local_vector_to_irq(vector)]=0;
 217                        __do_IRQ(local_vector_to_irq(vector), NULL);
 218
 219                        /*
 220                         * Disable interrupts and send EOI
 221                         */
 222                        local_irq_disable();
 223                        ia64_setreg(_IA64_REG_CR_TPR, saved_tpr);
 224                }
 225                ia64_eoi();
 226                vector = ia64_get_ivr();
 227        }
 228        irq_exit();
 229}
 230#endif
 231
 232
 233#ifdef CONFIG_SMP
 234extern irqreturn_t handle_IPI (int irq, void *dev_id, struct pt_regs *regs);
 235
 236static struct irqaction ipi_irqaction = {
 237        .handler =      handle_IPI,
 238        .flags =        IRQF_DISABLED,
 239        .name =         "IPI"
 240};
 241#endif
 242
 243void
 244register_percpu_irq (ia64_vector vec, struct irqaction *action)
 245{
 246        irq_desc_t *desc;
 247        unsigned int irq;
 248
 249        for (irq = 0; irq < NR_IRQS; ++irq)
 250                if (irq_to_vector(irq) == vec) {
 251                        desc = irq_desc + irq;
 252                        desc->status |= IRQ_PER_CPU;
 253                        desc->chip = &irq_type_ia64_lsapic;
 254                        if (action)
 255                                setup_irq(irq, action);
 256                }
 257}
 258
 259void __init
 260init_IRQ (void)
 261{
 262        register_percpu_irq(IA64_SPURIOUS_INT_VECTOR, NULL);
 263#ifdef CONFIG_SMP
 264        register_percpu_irq(IA64_IPI_VECTOR, &ipi_irqaction);
 265#endif
 266#ifdef CONFIG_PERFMON
 267        pfm_init_percpu();
 268#endif
 269        platform_irq_init();
 270}
 271
 272void
 273ia64_send_ipi (int cpu, int vector, int delivery_mode, int redirect)
 274{
 275        void __iomem *ipi_addr;
 276        unsigned long ipi_data;
 277        unsigned long phys_cpu_id;
 278
 279#ifdef CONFIG_SMP
 280        phys_cpu_id = cpu_physical_id(cpu);
 281#else
 282        phys_cpu_id = (ia64_getreg(_IA64_REG_CR_LID) >> 16) & 0xffff;
 283#endif
 284
 285        /*
 286         * cpu number is in 8bit ID and 8bit EID
 287         */
 288
 289        ipi_data = (delivery_mode << 8) | (vector & 0xff);
 290        ipi_addr = ipi_base_addr + ((phys_cpu_id << 4) | ((redirect & 1) << 3));
 291
 292        writeq(ipi_data, ipi_addr);
 293}
 294
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.