linux/arch/sparc/kernel/sun4m_irq.c
<<
>>
Prefs
   1/*  sun4m_irq.c
   2 *  arch/sparc/kernel/sun4m_irq.c:
   3 *
   4 *  djhr: Hacked out of irq.c into a CPU dependent version.
   5 *
   6 *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
   7 *  Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
   8 *  Copyright (C) 1995 Pete A. Zaitcev (zaitcev@yahoo.com)
   9 *  Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
  10 */
  11
  12#include <linux/errno.h>
  13#include <linux/linkage.h>
  14#include <linux/kernel_stat.h>
  15#include <linux/signal.h>
  16#include <linux/sched.h>
  17#include <linux/ptrace.h>
  18#include <linux/smp.h>
  19#include <linux/interrupt.h>
  20#include <linux/slab.h>
  21#include <linux/init.h>
  22#include <linux/ioport.h>
  23
  24#include <asm/ptrace.h>
  25#include <asm/processor.h>
  26#include <asm/system.h>
  27#include <asm/psr.h>
  28#include <asm/vaddrs.h>
  29#include <asm/timer.h>
  30#include <asm/openprom.h>
  31#include <asm/oplib.h>
  32#include <asm/traps.h>
  33#include <asm/pgalloc.h>
  34#include <asm/pgtable.h>
  35#include <asm/smp.h>
  36#include <asm/irq.h>
  37#include <asm/io.h>
  38#include <asm/sbus.h>
  39#include <asm/cacheflush.h>
  40
  41#include "irq.h"
  42
  43/* On the sun4m, just like the timers, we have both per-cpu and master
  44 * interrupt registers.
  45 */
  46
  47/* These registers are used for sending/receiving irqs from/to
  48 * different cpu's.
  49 */
  50struct sun4m_intreg_percpu {
  51        unsigned int tbt;        /* Interrupts still pending for this cpu. */
  52
  53        /* These next two registers are WRITE-ONLY and are only
  54         * "on bit" sensitive, "off bits" written have NO affect.
  55         */
  56        unsigned int clear;  /* Clear this cpus irqs here. */
  57        unsigned int set;    /* Set this cpus irqs here. */
  58        unsigned char space[PAGE_SIZE - 12];
  59};
  60
  61/*
  62 * djhr
  63 * Actually the clear and set fields in this struct are misleading..
  64 * according to the SLAVIO manual (and the same applies for the SEC)
  65 * the clear field clears bits in the mask which will ENABLE that IRQ
  66 * the set field sets bits in the mask to DISABLE the IRQ.
  67 *
  68 * Also the undirected_xx address in the SLAVIO is defined as
  69 * RESERVED and write only..
  70 *
  71 * DAVEM_NOTE: The SLAVIO only specifies behavior on uniprocessor
  72 *             sun4m machines, for MP the layout makes more sense.
  73 */
  74struct sun4m_intregs {
  75        struct sun4m_intreg_percpu cpu_intregs[SUN4M_NCPUS];
  76        unsigned int tbt;                /* IRQ's that are still pending. */
  77        unsigned int irqs;               /* Master IRQ bits. */
  78
  79        /* Again, like the above, two these registers are WRITE-ONLY. */
  80        unsigned int clear;              /* Clear master IRQ's by setting bits here. */
  81        unsigned int set;                /* Set master IRQ's by setting bits here. */
  82
  83        /* This register is both READ and WRITE. */
  84        unsigned int undirected_target;  /* Which cpu gets undirected irqs. */
  85};
  86
  87static unsigned long dummy;
  88
  89struct sun4m_intregs *sun4m_interrupts;
  90unsigned long *irq_rcvreg = &dummy;
  91
  92/* Dave Redman (djhr@tadpole.co.uk)
  93 * The sun4m interrupt registers.
  94 */
  95#define SUN4M_INT_ENABLE        0x80000000
  96#define SUN4M_INT_E14           0x00000080
  97#define SUN4M_INT_E10           0x00080000
  98
  99#define SUN4M_HARD_INT(x)       (0x000000001 << (x))
 100#define SUN4M_SOFT_INT(x)       (0x000010000 << (x))
 101
 102#define SUN4M_INT_MASKALL       0x80000000        /* mask all interrupts */
 103#define SUN4M_INT_MODULE_ERR    0x40000000        /* module error */
 104#define SUN4M_INT_M2S_WRITE     0x20000000        /* write buffer error */
 105#define SUN4M_INT_ECC           0x10000000        /* ecc memory error */
 106#define SUN4M_INT_FLOPPY        0x00400000        /* floppy disk */
 107#define SUN4M_INT_MODULE        0x00200000        /* module interrupt */
 108#define SUN4M_INT_VIDEO         0x00100000        /* onboard video */
 109#define SUN4M_INT_REALTIME      0x00080000        /* system timer */
 110#define SUN4M_INT_SCSI          0x00040000        /* onboard scsi */
 111#define SUN4M_INT_AUDIO         0x00020000        /* audio/isdn */
 112#define SUN4M_INT_ETHERNET      0x00010000        /* onboard ethernet */
 113#define SUN4M_INT_SERIAL        0x00008000        /* serial ports */
 114#define SUN4M_INT_KBDMS         0x00004000        /* keyboard/mouse */
 115#define SUN4M_INT_SBUSBITS      0x00003F80        /* sbus int bits */
 116
 117#define SUN4M_INT_SBUS(x)       (1 << (x+7))
 118#define SUN4M_INT_VME(x)        (1 << (x))
 119
 120/* These tables only apply for interrupts greater than 15..
 121 * 
 122 * any intr value below 0x10 is considered to be a soft-int
 123 * this may be useful or it may not.. but that's how I've done it.
 124 * and it won't clash with what OBP is telling us about devices.
 125 *
 126 * take an encoded intr value and lookup if it's valid
 127 * then get the mask bits that match from irq_mask
 128 *
 129 * P3: Translation from irq 0x0d to mask 0x2000 is for MrCoffee.
 130 */
 131static unsigned char irq_xlate[32] = {
 132    /*  0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  a,  b,  c,  d,  e,  f */
 133        0,  0,  0,  0,  1,  0,  2,  0,  3,  0,  4,  5,  6, 14,  0,  7,
 134        0,  0,  8,  9,  0, 10,  0, 11,  0, 12,  0, 13,  0, 14,  0,  0
 135};
 136
 137static unsigned long irq_mask[] = {
 138        0,                                                /* illegal index */
 139        SUN4M_INT_SCSI,                                   /*  1 irq 4 */
 140        SUN4M_INT_ETHERNET,                               /*  2 irq 6 */
 141        SUN4M_INT_VIDEO,                                  /*  3 irq 8 */
 142        SUN4M_INT_REALTIME,                               /*  4 irq 10 */
 143        SUN4M_INT_FLOPPY,                                 /*  5 irq 11 */
 144        (SUN4M_INT_SERIAL | SUN4M_INT_KBDMS),             /*  6 irq 12 */
 145        SUN4M_INT_MODULE_ERR,                             /*  7 irq 15 */
 146        SUN4M_INT_SBUS(0),                                /*  8 irq 2 */
 147        SUN4M_INT_SBUS(1),                                /*  9 irq 3 */
 148        SUN4M_INT_SBUS(2),                                /* 10 irq 5 */
 149        SUN4M_INT_SBUS(3),                                /* 11 irq 7 */
 150        SUN4M_INT_SBUS(4),                                /* 12 irq 9 */
 151        SUN4M_INT_SBUS(5),                                /* 13 irq 11 */
 152        SUN4M_INT_SBUS(6)                                 /* 14 irq 13 */
 153};
 154
 155static int sun4m_pil_map[] = { 0, 2, 3, 5, 7, 9, 11, 13 };
 156
 157static unsigned int sun4m_sbint_to_irq(struct sbus_dev *sdev,
 158                                       unsigned int sbint)
 159{
 160        if (sbint >= sizeof(sun4m_pil_map)) {
 161                printk(KERN_ERR "%s: bogus SBINT %d\n", sdev->prom_name, sbint);
 162                BUG();
 163        }
 164        return sun4m_pil_map[sbint] | 0x30;
 165}
 166
 167static unsigned long sun4m_get_irqmask(unsigned int irq)
 168{
 169        unsigned long mask;
 170    
 171        if (irq > 0x20) {
 172                /* OBIO/SBUS interrupts */
 173                irq &= 0x1f;
 174                mask = irq_mask[irq_xlate[irq]];
 175                if (!mask)
 176                        printk("sun4m_get_irqmask: IRQ%d has no valid mask!\n",irq);
 177        } else {
 178                /* Soft Interrupts will come here.
 179                 * Currently there is no way to trigger them but I'm sure
 180                 * something could be cooked up.
 181                 */
 182                irq &= 0xf;
 183                mask = SUN4M_SOFT_INT(irq);
 184        }
 185        return mask;
 186}
 187
 188static void sun4m_disable_irq(unsigned int irq_nr)
 189{
 190        unsigned long mask, flags;
 191        int cpu = smp_processor_id();
 192
 193        mask = sun4m_get_irqmask(irq_nr);
 194        local_irq_save(flags);
 195        if (irq_nr > 15)
 196                sun4m_interrupts->set = mask;
 197        else
 198                sun4m_interrupts->cpu_intregs[cpu].set = mask;
 199        local_irq_restore(flags);    
 200}
 201
 202static void sun4m_enable_irq(unsigned int irq_nr)
 203{
 204        unsigned long mask, flags;
 205        int cpu = smp_processor_id();
 206
 207        /* Dreadful floppy hack. When we use 0x2b instead of
 208         * 0x0b the system blows (it starts to whistle!).
 209         * So we continue to use 0x0b. Fixme ASAP. --P3
 210         */
 211        if (irq_nr != 0x0b) {
 212                mask = sun4m_get_irqmask(irq_nr);
 213                local_irq_save(flags);
 214                if (irq_nr > 15)
 215                        sun4m_interrupts->clear = mask;
 216                else
 217                        sun4m_interrupts->cpu_intregs[cpu].clear = mask;
 218                local_irq_restore(flags);    
 219        } else {
 220                local_irq_save(flags);
 221                sun4m_interrupts->clear = SUN4M_INT_FLOPPY;
 222                local_irq_restore(flags);
 223        }
 224}
 225
 226static unsigned long cpu_pil_to_imask[16] = {
 227/*0*/   0x00000000,
 228/*1*/   0x00000000,
 229/*2*/   SUN4M_INT_SBUS(0) | SUN4M_INT_VME(0),
 230/*3*/   SUN4M_INT_SBUS(1) | SUN4M_INT_VME(1),
 231/*4*/   SUN4M_INT_SCSI,
 232/*5*/   SUN4M_INT_SBUS(2) | SUN4M_INT_VME(2),
 233/*6*/   SUN4M_INT_ETHERNET,
 234/*7*/   SUN4M_INT_SBUS(3) | SUN4M_INT_VME(3),
 235/*8*/   SUN4M_INT_VIDEO,
 236/*9*/   SUN4M_INT_SBUS(4) | SUN4M_INT_VME(4) | SUN4M_INT_MODULE_ERR,
 237/*10*/  SUN4M_INT_REALTIME,
 238/*11*/  SUN4M_INT_SBUS(5) | SUN4M_INT_VME(5) | SUN4M_INT_FLOPPY,
 239/*12*/  SUN4M_INT_SERIAL | SUN4M_INT_KBDMS,
 240/*13*/  SUN4M_INT_AUDIO,
 241/*14*/  SUN4M_INT_E14,
 242/*15*/  0x00000000
 243};
 244
 245/* We assume the caller has disabled local interrupts when these are called,
 246 * or else very bizarre behavior will result.
 247 */
 248static void sun4m_disable_pil_irq(unsigned int pil)
 249{
 250        sun4m_interrupts->set = cpu_pil_to_imask[pil];
 251}
 252
 253static void sun4m_enable_pil_irq(unsigned int pil)
 254{
 255        sun4m_interrupts->clear = cpu_pil_to_imask[pil];
 256}
 257
 258#ifdef CONFIG_SMP
 259static void sun4m_send_ipi(int cpu, int level)
 260{
 261        unsigned long mask;
 262
 263        mask = sun4m_get_irqmask(level);
 264        sun4m_interrupts->cpu_intregs[cpu].set = mask;
 265}
 266
 267static void sun4m_clear_ipi(int cpu, int level)
 268{
 269        unsigned long mask;
 270
 271        mask = sun4m_get_irqmask(level);
 272        sun4m_interrupts->cpu_intregs[cpu].clear = mask;
 273}
 274
 275static void sun4m_set_udt(int cpu)
 276{
 277        sun4m_interrupts->undirected_target = cpu;
 278}
 279#endif
 280
 281#define OBIO_INTR       0x20
 282#define TIMER_IRQ       (OBIO_INTR | 10)
 283#define PROFILE_IRQ     (OBIO_INTR | 14)
 284
 285static struct sun4m_timer_regs *sun4m_timers;
 286unsigned int lvl14_resolution = (((1000000/HZ) + 1) << 10);
 287
 288static void sun4m_clear_clock_irq(void)
 289{
 290        volatile unsigned int clear_intr;
 291        clear_intr = sun4m_timers->l10_timer_limit;
 292}
 293
 294static void sun4m_clear_profile_irq(int cpu)
 295{
 296        volatile unsigned int clear;
 297    
 298        clear = sun4m_timers->cpu_timers[cpu].l14_timer_limit;
 299}
 300
 301static void sun4m_load_profile_irq(int cpu, unsigned int limit)
 302{
 303        sun4m_timers->cpu_timers[cpu].l14_timer_limit = limit;
 304}
 305
 306static void __init sun4m_init_timers(irq_handler_t counter_fn)
 307{
 308        int reg_count, irq, cpu;
 309        struct linux_prom_registers cnt_regs[PROMREG_MAX];
 310        int obio_node, cnt_node;
 311        struct resource r;
 312
 313        cnt_node = 0;
 314        if((obio_node =
 315            prom_searchsiblings (prom_getchild(prom_root_node), "obio")) == 0 ||
 316           (obio_node = prom_getchild (obio_node)) == 0 ||
 317           (cnt_node = prom_searchsiblings (obio_node, "counter")) == 0) {
 318                prom_printf("Cannot find /obio/counter node\n");
 319                prom_halt();
 320        }
 321        reg_count = prom_getproperty(cnt_node, "reg",
 322                                     (void *) cnt_regs, sizeof(cnt_regs));
 323        reg_count = (reg_count/sizeof(struct linux_prom_registers));
 324    
 325        /* Apply the obio ranges to the timer registers. */
 326        prom_apply_obio_ranges(cnt_regs, reg_count);
 327    
 328        cnt_regs[4].phys_addr = cnt_regs[reg_count-1].phys_addr;
 329        cnt_regs[4].reg_size = cnt_regs[reg_count-1].reg_size;
 330        cnt_regs[4].which_io = cnt_regs[reg_count-1].which_io;
 331        for(obio_node = 1; obio_node < 4; obio_node++) {
 332                cnt_regs[obio_node].phys_addr =
 333                        cnt_regs[obio_node-1].phys_addr + PAGE_SIZE;
 334                cnt_regs[obio_node].reg_size = cnt_regs[obio_node-1].reg_size;
 335                cnt_regs[obio_node].which_io = cnt_regs[obio_node-1].which_io;
 336        }
 337
 338        memset((char*)&r, 0, sizeof(struct resource));
 339        /* Map the per-cpu Counter registers. */
 340        r.flags = cnt_regs[0].which_io;
 341        r.start = cnt_regs[0].phys_addr;
 342        sun4m_timers = (struct sun4m_timer_regs *) sbus_ioremap(&r, 0,
 343            PAGE_SIZE*SUN4M_NCPUS, "sun4m_cpu_cnt");
 344        /* Map the system Counter register. */
 345        /* XXX Here we expect consequent calls to yeld adjusent maps. */
 346        r.flags = cnt_regs[4].which_io;
 347        r.start = cnt_regs[4].phys_addr;
 348        sbus_ioremap(&r, 0, cnt_regs[4].reg_size, "sun4m_sys_cnt");
 349
 350        sun4m_timers->l10_timer_limit =  (((1000000/HZ) + 1) << 10);
 351        master_l10_counter = &sun4m_timers->l10_cur_count;
 352        master_l10_limit = &sun4m_timers->l10_timer_limit;
 353
 354        irq = request_irq(TIMER_IRQ,
 355                          counter_fn,
 356                          (IRQF_DISABLED | SA_STATIC_ALLOC),
 357                          "timer", NULL);
 358        if (irq) {
 359                prom_printf("time_init: unable to attach IRQ%d\n",TIMER_IRQ);
 360                prom_halt();
 361        }
 362   
 363        if (!cpu_find_by_instance(1, NULL, NULL)) {
 364                for(cpu = 0; cpu < 4; cpu++)
 365                        sun4m_timers->cpu_timers[cpu].l14_timer_limit = 0;
 366                sun4m_interrupts->set = SUN4M_INT_E14;
 367        } else {
 368                sun4m_timers->cpu_timers[0].l14_timer_limit = 0;
 369        }
 370#ifdef CONFIG_SMP
 371        {
 372                unsigned long flags;
 373                extern unsigned long lvl14_save[4];
 374                struct tt_entry *trap_table = &sparc_ttable[SP_TRAP_IRQ1 + (14 - 1)];
 375
 376                /* For SMP we use the level 14 ticker, however the bootup code
 377                 * has copied the firmware's level 14 vector into the boot cpu's
 378                 * trap table, we must fix this now or we get squashed.
 379                 */
 380                local_irq_save(flags);
 381                trap_table->inst_one = lvl14_save[0];
 382                trap_table->inst_two = lvl14_save[1];
 383                trap_table->inst_three = lvl14_save[2];
 384                trap_table->inst_four = lvl14_save[3];
 385                local_flush_cache_all();
 386                local_irq_restore(flags);
 387        }
 388#endif
 389}
 390
 391void __init sun4m_init_IRQ(void)
 392{
 393        int ie_node,i;
 394        struct linux_prom_registers int_regs[PROMREG_MAX];
 395        int num_regs;
 396        struct resource r;
 397        int mid;
 398    
 399        local_irq_disable();
 400        if((ie_node = prom_searchsiblings(prom_getchild(prom_root_node), "obio")) == 0 ||
 401           (ie_node = prom_getchild (ie_node)) == 0 ||
 402           (ie_node = prom_searchsiblings (ie_node, "interrupt")) == 0) {
 403                prom_printf("Cannot find /obio/interrupt node\n");
 404                prom_halt();
 405        }
 406        num_regs = prom_getproperty(ie_node, "reg", (char *) int_regs,
 407                                    sizeof(int_regs));
 408        num_regs = (num_regs/sizeof(struct linux_prom_registers));
 409    
 410        /* Apply the obio ranges to these registers. */
 411        prom_apply_obio_ranges(int_regs, num_regs);
 412    
 413        int_regs[4].phys_addr = int_regs[num_regs-1].phys_addr;
 414        int_regs[4].reg_size = int_regs[num_regs-1].reg_size;
 415        int_regs[4].which_io = int_regs[num_regs-1].which_io;
 416        for(ie_node = 1; ie_node < 4; ie_node++) {
 417                int_regs[ie_node].phys_addr = int_regs[ie_node-1].phys_addr + PAGE_SIZE;
 418                int_regs[ie_node].reg_size = int_regs[ie_node-1].reg_size;
 419                int_regs[ie_node].which_io = int_regs[ie_node-1].which_io;
 420        }
 421
 422        memset((char *)&r, 0, sizeof(struct resource));
 423        /* Map the interrupt registers for all possible cpus. */
 424        r.flags = int_regs[0].which_io;
 425        r.start = int_regs[0].phys_addr;
 426        sun4m_interrupts = (struct sun4m_intregs *) sbus_ioremap(&r, 0,
 427            PAGE_SIZE*SUN4M_NCPUS, "interrupts_percpu");
 428
 429        /* Map the system interrupt control registers. */
 430        r.flags = int_regs[4].which_io;
 431        r.start = int_regs[4].phys_addr;
 432        sbus_ioremap(&r, 0, int_regs[4].reg_size, "interrupts_system");
 433
 434        sun4m_interrupts->set = ~SUN4M_INT_MASKALL;
 435        for (i = 0; !cpu_find_by_instance(i, NULL, &mid); i++)
 436                sun4m_interrupts->cpu_intregs[mid].clear = ~0x17fff;
 437
 438        if (!cpu_find_by_instance(1, NULL, NULL)) {
 439                /* system wide interrupts go to cpu 0, this should always
 440                 * be safe because it is guaranteed to be fitted or OBP doesn't
 441                 * come up
 442                 *
 443                 * Not sure, but writing here on SLAVIO systems may puke
 444                 * so I don't do it unless there is more than 1 cpu.
 445                 */
 446                irq_rcvreg = (unsigned long *)
 447                                &sun4m_interrupts->undirected_target;
 448                sun4m_interrupts->undirected_target = 0;
 449        }
 450        BTFIXUPSET_CALL(sbint_to_irq, sun4m_sbint_to_irq, BTFIXUPCALL_NORM);
 451        BTFIXUPSET_CALL(enable_irq, sun4m_enable_irq, BTFIXUPCALL_NORM);
 452        BTFIXUPSET_CALL(disable_irq, sun4m_disable_irq, BTFIXUPCALL_NORM);
 453        BTFIXUPSET_CALL(enable_pil_irq, sun4m_enable_pil_irq, BTFIXUPCALL_NORM);
 454        BTFIXUPSET_CALL(disable_pil_irq, sun4m_disable_pil_irq, BTFIXUPCALL_NORM);
 455        BTFIXUPSET_CALL(clear_clock_irq, sun4m_clear_clock_irq, BTFIXUPCALL_NORM);
 456        BTFIXUPSET_CALL(clear_profile_irq, sun4m_clear_profile_irq, BTFIXUPCALL_NORM);
 457        BTFIXUPSET_CALL(load_profile_irq, sun4m_load_profile_irq, BTFIXUPCALL_NORM);
 458        sparc_init_timers = sun4m_init_timers;
 459#ifdef CONFIG_SMP
 460        BTFIXUPSET_CALL(set_cpu_int, sun4m_send_ipi, BTFIXUPCALL_NORM);
 461        BTFIXUPSET_CALL(clear_cpu_int, sun4m_clear_ipi, BTFIXUPCALL_NORM);
 462        BTFIXUPSET_CALL(set_irq_udt, sun4m_set_udt, BTFIXUPCALL_NORM);
 463#endif
 464        /* Cannot enable interrupts until OBP ticker is disabled. */
 465}
 466