patch-2.1.79 linux/arch/sparc/kernel/sun4d_irq.c
Next file: linux/arch/sparc/kernel/sun4m_irq.c
Previous file: linux/arch/sparc/kernel/sparc_ksyms.c
Back to the patch index
Back to the overall index
- Lines: 484
- Date:
Mon Jan 12 15:15:43 1998
- Orig file:
v2.1.78/linux/arch/sparc/kernel/sun4d_irq.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.1.78/linux/arch/sparc/kernel/sun4d_irq.c linux/arch/sparc/kernel/sun4d_irq.c
@@ -0,0 +1,483 @@
+/* $Id: sun4d_irq.c,v 1.3 1997/12/22 16:09:15 jj Exp $
+ * arch/sparc/kernel/sun4d_irq.c:
+ * SS1000/SC2000 interrupt handling.
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Heavily based on arch/sparc/kernel/irq.c.
+ */
+
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/linkage.h>
+#include <linux/kernel_stat.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/malloc.h>
+#include <linux/random.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/psr.h>
+#include <asm/smp.h>
+#include <asm/vaddrs.h>
+#include <asm/timer.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/traps.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/spinlock.h>
+#include <asm/sbus.h>
+#include <asm/sbi.h>
+
+struct sun4d_timer_regs *sun4d_timers;
+#define TIMER_IRQ 10
+
+#define MAX_STATIC_ALLOC 4
+extern struct irqaction static_irqaction[MAX_STATIC_ALLOC];
+extern int static_irq_count;
+
+extern struct irqaction *irq_action[];
+
+struct sbus_action {
+ struct irqaction *action;
+ unsigned char lock;
+ unsigned char active;
+ unsigned char disabled;
+} *sbus_actions;
+
+static int pil_to_sbus[] = {
+ 0, 0, 1, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 0,
+};
+
+static int nsbi;
+
+int sun4d_get_irq_list(char *buf)
+{
+ int i, j = 0, k = 0, len = 0, sbusl;
+ struct irqaction * action;
+
+ for (i = 0 ; i < NR_IRQS ; i++) {
+ sbusl = pil_to_sbus[i];
+ if (!sbusl) {
+ action = *(i + irq_action);
+ if (!action)
+ continue;
+ } else {
+ for (j = 0; j < nsbi; j++) {
+ for (k = 0; k < 4; k++)
+ if ((action = sbus_actions [(j << 5) + (sbusl << 2) + k].action))
+ goto found_it;
+ }
+ continue;
+ }
+found_it: len += sprintf(buf+len, "%2d: %8d %c %s",
+ i, kstat.interrupts[i],
+ (action->flags & SA_INTERRUPT) ? '+' : ' ',
+ action->name);
+ action = action->next;
+ for (;;) {
+ for (; action; action = action->next) {
+ len += sprintf(buf+len, ",%s %s",
+ (action->flags & SA_INTERRUPT) ? " +" : "",
+ action->name);
+ }
+ if (!sbusl) break;
+ k++;
+ if (k < 4)
+ action = sbus_actions [(j << 5) + (sbusl << 2) + k].action;
+ else {
+ j++;
+ if (j == nsbi) break;
+ k = 0;
+ action = sbus_actions [(j << 5) + (sbusl << 2)].action;
+ }
+ }
+ len += sprintf(buf+len, "\n");
+ }
+ return len;
+}
+
+void sun4d_free_irq(unsigned int irq, void *dev_id)
+{
+ struct irqaction *action, **actionp;
+ struct irqaction *tmp = NULL;
+ unsigned long flags;
+
+ if (irq < 15)
+ actionp = irq + irq_action;
+ else
+ actionp = &(sbus_actions[irq - (1 << 5)].action);
+ action = *actionp;
+ if (!action) {
+ printk("Trying to free free IRQ%d\n",irq);
+ return;
+ }
+ if (dev_id) {
+ for (; action; action = action->next) {
+ if (action->dev_id == dev_id)
+ break;
+ tmp = action;
+ }
+ if (!action) {
+ printk("Trying to free free shared IRQ%d\n",irq);
+ return;
+ }
+ } else if (action->flags & SA_SHIRQ) {
+ printk("Trying to free shared IRQ%d with NULL device ID\n", irq);
+ return;
+ }
+ if (action->flags & SA_STATIC_ALLOC)
+ {
+ /* This interrupt is marked as specially allocated
+ * so it is a bad idea to free it.
+ */
+ printk("Attempt to free statically allocated IRQ%d (%s)\n",
+ irq, action->name);
+ return;
+ }
+
+ save_and_cli(flags);
+ if (action && tmp)
+ tmp->next = action->next;
+ else
+ *actionp = action->next;
+
+ kfree_s(action, sizeof(struct irqaction));
+
+ if (!(*actionp))
+ disable_irq(irq);
+
+ restore_flags(flags);
+}
+
+extern void unexpected_irq(int, void *, struct pt_regs *);
+
+void sun4d_handler_irq(int irq, struct pt_regs * regs)
+{
+ struct irqaction * action;
+ int cpu = smp_processor_id();
+ /* SBUS IRQ level (1 - 7) */
+ int sbusl = pil_to_sbus[irq];
+
+ /* FIXME: Is this necessary?? */
+ cc_get_ipen();
+
+ cc_set_iclr(1 << irq);
+
+ irq_enter(cpu, irq, regs);
+ kstat.interrupts[irq]++;
+ if (!sbusl) {
+ action = *(irq + irq_action);
+ if (!action)
+ unexpected_irq(irq, 0, regs);
+ do {
+ action->handler(irq, action->dev_id, regs);
+ action = action->next;
+ } while (action);
+ } else {
+ int bus_mask = bw_get_intr_mask(sbusl) & 0x3ffff;
+ int lock;
+ int sbino;
+ struct sbus_action *actionp;
+ unsigned mask, slot;
+ int sbil = (sbusl << 2);
+
+ bw_clear_intr_mask(sbusl, bus_mask);
+
+ /* Loop for each pending SBI */
+ for (sbino = 0; bus_mask; sbino++, bus_mask >>= 1)
+ if (bus_mask & 1) {
+ mask = acquire_sbi(SBI2DEVID(sbino), 0xf << sbil);
+ mask &= (0xf << sbil);
+ actionp = sbus_actions + (sbino << 5) + (sbil);
+ /* Loop for each pending SBI slot */
+ for (slot = (1 << sbil); mask; slot <<= 1, actionp++)
+ if (mask & slot) {
+ mask &= ~slot;
+ action = actionp->action;
+ __asm__ __volatile__ ("ldstub [%1 + 4], %0"
+ : "=r" (lock) : "r" (actionp));
+
+ if (!lock) {
+ if (!action)
+ unexpected_irq(irq, 0, regs);
+ do {
+ action->handler(irq, action->dev_id, regs);
+ action = action->next;
+ } while (action);
+ actionp->lock = 0;
+ } else
+ actionp->active = 1;
+ release_sbi(SBI2DEVID(sbino), slot);
+ }
+ }
+ }
+ irq_exit(cpu, irq);
+}
+
+int sun4d_request_irq(unsigned int irq,
+ void (*handler)(int, void *, struct pt_regs *),
+ unsigned long irqflags, const char * devname, void *dev_id)
+{
+ struct irqaction *action, *tmp = NULL, **actionp;
+ unsigned long flags;
+ int sbusl;
+ unsigned int *ret = NULL;
+ struct linux_sbus_device *sdev = NULL;
+
+ if(irq > 14)
+ return -EINVAL;
+
+ if (!handler)
+ return -EINVAL;
+
+ if (irqflags & SA_DCOOKIE) {
+ struct devid_cookie *d = (struct devid_cookie *)dev_id;
+
+ dev_id = d->real_dev_id;
+ sdev = (struct linux_sbus_device *)d->bus_cookie;
+ ret = &d->ret_ino;
+ }
+
+ sbusl = pil_to_sbus[irq];
+ if (sbusl && !sdev) {
+ printk ("Attempt to register SBUS IRQ %d without DCOOKIE\n", irq);
+ return -EINVAL;
+ }
+ if (sbusl) {
+ actionp = &(sbus_actions[(sdev->my_bus->board << 5) +
+ (sbusl << 2) + sdev->slot].action);
+ *ret = ((sdev->my_bus->board + 1) << 5) +
+ (sbusl << 2) + sdev->slot;
+ } else
+ actionp = irq + irq_action;
+ action = *actionp;
+
+ if (action) {
+ if ((action->flags & SA_SHIRQ) && (irqflags & SA_SHIRQ)) {
+ for (tmp = action; tmp->next; tmp = tmp->next);
+ } else {
+ return -EBUSY;
+ }
+ if ((action->flags & SA_INTERRUPT) ^ (irqflags & SA_INTERRUPT)) {
+ printk("Attempt to mix fast and slow interrupts on IRQ%d denied\n", irq);
+ return -EBUSY;
+ }
+ action = NULL; /* Or else! */
+ }
+
+ save_and_cli(flags);
+
+ /* If this is flagged as statically allocated then we use our
+ * private struct which is never freed.
+ */
+ if (irqflags & SA_STATIC_ALLOC)
+ if (static_irq_count < MAX_STATIC_ALLOC)
+ action = &static_irqaction[static_irq_count++];
+ else
+ printk("Request for IRQ%d (%s) SA_STATIC_ALLOC failed using kmalloc\n",irq, devname);
+
+ if (action == NULL)
+ action = (struct irqaction *)kmalloc(sizeof(struct irqaction),
+ GFP_KERNEL);
+
+ if (!action) {
+ restore_flags(flags);
+ return -ENOMEM;
+ }
+
+ action->handler = handler;
+ action->flags = irqflags;
+ action->mask = 0;
+ action->name = devname;
+ action->next = NULL;
+ action->dev_id = dev_id;
+
+ if (tmp)
+ tmp->next = action;
+ else
+ *actionp = action;
+
+ if (ret) irq = *ret;
+
+ if (irq > NR_IRQS) {
+ struct sbus_action *s = sbus_actions + irq - (1 << 5);
+
+ if (s->disabled) {
+ s->disabled = 0;
+ s->active = 0;
+ s->lock = 0;
+ }
+ }
+
+ restore_flags(flags);
+ return 0;
+}
+
+static void sun4d_disable_irq(unsigned int irq)
+{
+ struct sbus_action *s;
+
+ if (irq < NR_IRQS) {
+ /* FIXME */
+ printk ("Unable to disable IRQ %d\n", irq);
+ return;
+ }
+ s = sbus_actions + irq - (1 << 5);
+
+ if (s->disabled) return;
+ s->disabled = 1;
+ __asm__ __volatile__ ("
+1: ldstub [%0 + 4], %%g1
+ orcc %%g1, 0, %%g0
+ bne 1b"
+ : : "r" (s) : "g1", "cc");
+}
+
+static void sun4d_enable_irq(unsigned int irq)
+{
+ struct sbus_action *s;
+ struct irqaction *action;
+
+ if (irq < NR_IRQS)
+ /* FIXME */
+ return;
+ s = sbus_actions + irq - (1 << 5);
+
+ if (!s->disabled) return;
+ action = s->action;
+ s->disabled = 0;
+ while (s->active) {
+ s->active = 0;
+ while (action) {
+ /* FIXME: Hope no sbus intr handler uses regs */
+ action->handler(irq, action->dev_id, NULL);
+ action = action->next;
+ }
+ }
+ s->lock = 0;
+}
+
+#ifdef __SMP__
+
+/* +-------+-------------+-----------+------------------------------------+
+ * | bcast | devid | sid | levels mask |
+ * +-------+-------------+-----------+------------------------------------+
+ * 31 30 23 22 15 14 0
+ */
+#define IGEN_MESSAGE(bcast, devid, sid, levels) \
+ (((bcast) << 31) | ((devid) << 23) | ((sid) << 15) | (levels))
+
+static void sun4d_send_ipi(int cpu, int level)
+{
+ cc_set_igen(IGEN_MESSAGE(0, cpu << 3, 6 + ((level >> 1) & 7), 1 << (level - 1)));
+}
+
+static void sun4d_clear_ipi(int cpu, int level)
+{
+}
+
+static void sun4d_set_udt(int cpu)
+{
+}
+#endif
+
+static void sun4d_clear_clock_irq(void)
+{
+ volatile unsigned int clear_intr;
+ clear_intr = sun4d_timers->l10_timer_limit;
+}
+
+static void sun4d_clear_profile_irq(int cpu)
+{
+ bw_get_prof_limit(cpu);
+}
+
+static void sun4d_load_profile_irq(int cpu, unsigned int limit)
+{
+ bw_set_prof_limit(cpu, limit);
+}
+
+__initfunc(static void sun4d_init_timers(void (*counter_fn)(int, void *, struct pt_regs *)))
+{
+ int irq;
+ extern struct prom_cpuinfo linux_cpus[NCPUS];
+ int cpu;
+
+ /* Map the User Timer registers. */
+ sun4d_timers = sparc_alloc_io(BW_LOCAL_BASE+BW_TIMER_LIMIT, 0,
+ PAGE_SIZE, "user timer", 0xf, 0x0);
+
+ sun4d_timers->l10_timer_limit = (((1000000/HZ) + 1) << 10);
+ master_l10_counter = &sun4d_timers->l10_cur_count;
+ master_l10_limit = &sun4d_timers->l10_timer_limit;
+
+ irq = request_irq(TIMER_IRQ,
+ counter_fn,
+ (SA_INTERRUPT | SA_STATIC_ALLOC),
+ "timer", NULL);
+ if (irq) {
+ prom_printf("time_init: unable to attach IRQ%d\n",TIMER_IRQ);
+ prom_halt();
+ }
+
+ /* Enable user timer free run for CPU 0 in BW */
+ /* bw_set_ctrl(0, bw_get_ctrl(0) | BW_CTRL_USER_TIMER); */
+
+ for(cpu = 0; cpu < NCPUS; cpu++)
+ sun4d_load_profile_irq(linux_cpus[cpu].mid, 0);
+}
+
+__initfunc(unsigned long sun4d_init_sbi_irq(unsigned long memory_start))
+{
+ struct linux_sbus *sbus;
+ struct sbus_action *s;
+ int i;
+ unsigned mask;
+
+ nsbi = 0;
+ for_each_sbus(sbus)
+ nsbi++;
+ memory_start = ((memory_start + 7) & ~7);
+ sbus_actions = (struct sbus_action *)memory_start;
+ memory_start += (nsbi * 8 * 4 * sizeof(struct sbus_action));
+ memset (sbus_actions, 0, (nsbi * 8 * 4 * sizeof(struct sbus_action)));
+ for (i = 0, s = sbus_actions; i < nsbi * 8 * 4; i++, s++) {
+ s->lock = 0xff;
+ s->disabled = 1;
+ }
+ for_each_sbus(sbus) {
+ /* Get rid of pending irqs from PROM */
+ mask = acquire_sbi(sbus->devid, 0xffffffff);
+ if (mask) {
+ printk ("Clearing pending IRQs %08x on SBI %d\n", mask, sbus->board);
+ release_sbi(sbus->devid, mask);
+ }
+ }
+ return memory_start;
+}
+
+__initfunc(void sun4d_init_IRQ(void))
+{
+ __cli();
+
+ enable_irq = sun4d_enable_irq;
+ disable_irq = sun4d_disable_irq;
+ clear_clock_irq = sun4d_clear_clock_irq;
+ clear_profile_irq = sun4d_clear_profile_irq;
+ load_profile_irq = sun4d_load_profile_irq;
+ init_timers = sun4d_init_timers;
+#ifdef __SMP__
+ set_cpu_int = (void (*) (int, int))sun4d_send_ipi;
+ clear_cpu_int = (void (*) (int, int))sun4d_clear_ipi;
+ set_irq_udt = (void (*) (int))sun4d_set_udt;
+#endif
+ /* Cannot enable interrupts until OBP ticker is disabled. */
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov