Commit 088dd1f8 authored by David S. Miller's avatar David S. Miller

[SPARC64]: Add support for IRQ pre-handlers.

This allows a PCI controller to shim into IRQ delivery
so that DMA queues can be drained, if necessary.

If some bus specific code needs to run before an IRQ
handler is invoked, the bus driver simply needs to setup
the function pointer in bucket->irq_info->pre_handler and
the two args bucket->irq_info->pre_handler_arg[12].

The Schizo PCI driver is converted over to use a pre-handler
for the DMA write-sync processing it needs when a device
is behind a PCI->PCI bus deeper than the top-level APB
bridges.

While we're here, clean up all of the action allocation
and handling.  Now, we allocate the irqaction as part of
the bucket->irq_info area.  There is an array of 4 irqaction
(for PCI irq sharing) and a bitmask saying which entries
are active.

The bucket->irq_info is allocated at build_irq() time, not
at request_irq() time.  This simplifies request_irq() and
free_irq() tremendously.

The SMP dynamic IRQ retargetting code got removed in this
change too.  It was disabled for a few months now, and we
can resurrect it in the future if we want.
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 06326e40
...@@ -553,13 +553,11 @@ do_ivec: ...@@ -553,13 +553,11 @@ do_ivec:
sllx %g3, 5, %g3 sllx %g3, 5, %g3
or %g2, %lo(ivector_table), %g2 or %g2, %lo(ivector_table), %g2
add %g2, %g3, %g3 add %g2, %g3, %g3
ldx [%g3 + 0x08], %g2 /* irq_info */
ldub [%g3 + 0x04], %g4 /* pil */ ldub [%g3 + 0x04], %g4 /* pil */
brz,pn %g2, do_ivec_spurious mov 1, %g2
mov 1, %g2
sllx %g2, %g4, %g2 sllx %g2, %g4, %g2
sllx %g4, 2, %g4 sllx %g4, 2, %g4
lduw [%g6 + %g4], %g5 /* g5 = irq_work(cpu, pil) */ lduw [%g6 + %g4], %g5 /* g5 = irq_work(cpu, pil) */
stw %g5, [%g3 + 0x00] /* bucket->irq_chain = g5 */ stw %g5, [%g3 + 0x00] /* bucket->irq_chain = g5 */
stw %g3, [%g6 + %g4] /* irq_work(cpu, pil) = bucket */ stw %g3, [%g6 + %g4] /* irq_work(cpu, pil) = bucket */
...@@ -567,9 +565,9 @@ do_ivec: ...@@ -567,9 +565,9 @@ do_ivec:
retry retry
do_ivec_xcall: do_ivec_xcall:
mov 0x50, %g1 mov 0x50, %g1
ldxa [%g1 + %g0] ASI_INTR_R, %g1 ldxa [%g1 + %g0] ASI_INTR_R, %g1
srl %g3, 0, %g3 srl %g3, 0, %g3
mov 0x60, %g7 mov 0x60, %g7
ldxa [%g7 + %g0] ASI_INTR_R, %g7 ldxa [%g7 + %g0] ASI_INTR_R, %g7
stxa %g0, [%g0] ASI_INTR_RECEIVE stxa %g0, [%g0] ASI_INTR_RECEIVE
...@@ -581,19 +579,6 @@ do_ivec_xcall: ...@@ -581,19 +579,6 @@ do_ivec_xcall:
1: jmpl %g3, %g0 1: jmpl %g3, %g0
nop nop
do_ivec_spurious:
stw %g3, [%g6 + 0x00] /* irq_work(cpu, 0) = bucket */
rdpr %pstate, %g5
wrpr %g5, PSTATE_IG | PSTATE_AG, %pstate
sethi %hi(109f), %g7
ba,pt %xcc, etrap
109: or %g7, %lo(109b), %g7
call catch_disabled_ivec
add %sp, PTREGS_OFF, %o0
ba,pt %xcc, rtrap
clr %l6
.globl save_alternate_globals .globl save_alternate_globals
save_alternate_globals: /* %o0 = save_area */ save_alternate_globals: /* %o0 = save_area */
rdpr %pstate, %o5 rdpr %pstate, %o5
......
...@@ -71,31 +71,7 @@ struct irq_work_struct { ...@@ -71,31 +71,7 @@ struct irq_work_struct {
struct irq_work_struct __irq_work[NR_CPUS]; struct irq_work_struct __irq_work[NR_CPUS];
#define irq_work(__cpu, __pil) &(__irq_work[(__cpu)].irq_worklists[(__pil)]) #define irq_work(__cpu, __pil) &(__irq_work[(__cpu)].irq_worklists[(__pil)])
#ifdef CONFIG_PCI static struct irqaction *irq_action[NR_IRQS+1];
/* This is a table of physical addresses used to deal with IBF_DMA_SYNC.
* It is used for PCI only to synchronize DMA transfers with IRQ delivery
* for devices behind busses other than APB on Sabre systems.
*
* Currently these physical addresses are just config space accesses
* to the command register for that device.
*/
unsigned long pci_dma_wsync;
unsigned long dma_sync_reg_table[256];
unsigned char dma_sync_reg_table_entry = 0;
#endif
/* This is based upon code in the 32-bit Sparc kernel written mostly by
* David Redman (djhr@tadpole.co.uk).
*/
#define MAX_STATIC_ALLOC 4
static struct irqaction static_irqaction[MAX_STATIC_ALLOC];
static int static_irq_count;
/* This is exported so that fast IRQ handlers can get at it... -DaveM */
struct irqaction *irq_action[NR_IRQS+1] = {
NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL
};
/* This only synchronizes entities which modify IRQ handler /* This only synchronizes entities which modify IRQ handler
* state and some selected user-level spots that want to * state and some selected user-level spots that want to
...@@ -241,17 +217,22 @@ void disable_irq(unsigned int irq) ...@@ -241,17 +217,22 @@ void disable_irq(unsigned int irq)
* the CPU %tick register and not by some normal vectored interrupt * the CPU %tick register and not by some normal vectored interrupt
* source. To handle this special case, we use this dummy INO bucket. * source. To handle this special case, we use this dummy INO bucket.
*/ */
static struct irq_desc pil0_dummy_desc;
static struct ino_bucket pil0_dummy_bucket = { static struct ino_bucket pil0_dummy_bucket = {
0, /* irq_chain */ .irq_info = &pil0_dummy_desc,
0, /* pil */
0, /* pending */
0, /* flags */
0, /* __unused */
NULL, /* irq_info */
0UL, /* iclr */
0UL, /* imap */
}; };
static void build_irq_error(const char *msg, unsigned int ino, int pil, int inofixup,
unsigned long iclr, unsigned long imap,
struct ino_bucket *bucket)
{
prom_printf("IRQ: INO %04x (%d:%016lx:%016lx) --> "
"(%d:%d:%016lx:%016lx), halting...\n",
ino, bucket->pil, bucket->iclr, bucket->imap,
pil, inofixup, iclr, imap);
prom_halt();
}
unsigned int build_irq(int pil, int inofixup, unsigned long iclr, unsigned long imap) unsigned int build_irq(int pil, int inofixup, unsigned long iclr, unsigned long imap)
{ {
struct ino_bucket *bucket; struct ino_bucket *bucket;
...@@ -280,28 +261,35 @@ unsigned int build_irq(int pil, int inofixup, unsigned long iclr, unsigned long ...@@ -280,28 +261,35 @@ unsigned int build_irq(int pil, int inofixup, unsigned long iclr, unsigned long
prom_halt(); prom_halt();
} }
/* Ok, looks good, set it up. Don't touch the irq_chain or
* the pending flag.
*/
bucket = &ivector_table[ino]; bucket = &ivector_table[ino];
if ((bucket->flags & IBF_ACTIVE) || if (bucket->flags & IBF_ACTIVE)
(bucket->irq_info != NULL)) { build_irq_error("IRQ: Trying to build active INO bucket.\n",
/* This is a gross fatal error if it happens here. */ ino, pil, inofixup, iclr, imap, bucket);
prom_printf("IRQ: Trying to reinit INO bucket, fatal error.\n");
prom_printf("IRQ: Request INO %04x (%d:%d:%016lx:%016lx)\n", if (bucket->irq_info) {
ino, pil, inofixup, iclr, imap); if (bucket->imap != imap || bucket->iclr != iclr)
prom_printf("IRQ: Existing (%d:%016lx:%016lx)\n", build_irq_error("IRQ: Trying to reinit INO bucket.\n",
bucket->pil, bucket->iclr, bucket->imap); ino, pil, inofixup, iclr, imap, bucket);
prom_printf("IRQ: Cannot continue, halting...\n");
goto out;
}
bucket->irq_info = kmalloc(sizeof(struct irq_desc), GFP_ATOMIC);
if (!bucket->irq_info) {
prom_printf("IRQ: Error, kmalloc(irq_desc) failed.\n");
prom_halt(); prom_halt();
} }
memset(bucket->irq_info, 0, sizeof(struct irq_desc));
/* Ok, looks good, set it up. Don't touch the irq_chain or
* the pending flag.
*/
bucket->imap = imap; bucket->imap = imap;
bucket->iclr = iclr; bucket->iclr = iclr;
bucket->pil = pil; bucket->pil = pil;
bucket->flags = 0; bucket->flags = 0;
bucket->irq_info = NULL; out:
return __irq(bucket); return __irq(bucket);
} }
...@@ -319,26 +307,65 @@ static void atomic_bucket_insert(struct ino_bucket *bucket) ...@@ -319,26 +307,65 @@ static void atomic_bucket_insert(struct ino_bucket *bucket)
__asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate)); __asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate));
} }
static int check_irq_sharing(int pil, unsigned long irqflags)
{
struct irqaction *action, *tmp;
action = *(irq_action + pil);
if (action) {
if ((action->flags & SA_SHIRQ) && (irqflags & SA_SHIRQ)) {
for (tmp = action; tmp->next; tmp = tmp->next)
;
} else {
return -EBUSY;
}
}
return 0;
}
static void append_irq_action(int pil, struct irqaction *action)
{
struct irqaction **pp = irq_action + pil;
while (*pp)
pp = &((*pp)->next);
*pp = action;
}
static struct irqaction *get_action_slot(struct ino_bucket *bucket)
{
struct irq_desc *desc = bucket->irq_info;
int max_irq, i;
max_irq = 1;
if (bucket->flags & IBF_PCI)
max_irq = MAX_IRQ_DESC_ACTION;
for (i = 0; i < max_irq; i++) {
struct irqaction *p = &desc->action[i];
u32 mask = (1 << i);
if (desc->action_active_mask & mask)
continue;
desc->action_active_mask |= mask;
return p;
}
return NULL;
}
int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *), int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *),
unsigned long irqflags, const char *name, void *dev_id) unsigned long irqflags, const char *name, void *dev_id)
{ {
struct irqaction *action, *tmp = NULL; struct irqaction *action;
struct ino_bucket *bucket = __bucket(irq); struct ino_bucket *bucket = __bucket(irq);
unsigned long flags; unsigned long flags;
int pending = 0; int pending = 0;
if ((bucket != &pil0_dummy_bucket) && if (unlikely(!handler))
(bucket < &ivector_table[0] ||
bucket >= &ivector_table[NUM_IVECS])) {
unsigned int *caller;
__asm__ __volatile__("mov %%i7, %0" : "=r" (caller));
printk(KERN_CRIT "request_irq: Old style IRQ registry attempt "
"from %p, irq %08x.\n", caller, irq);
return -EINVAL; return -EINVAL;
}
if (!handler) if (unlikely(!bucket->irq_info))
return -EINVAL; return -ENODEV;
if ((bucket != &pil0_dummy_bucket) && (irqflags & SA_SAMPLE_RANDOM)) { if ((bucket != &pil0_dummy_bucket) && (irqflags & SA_SAMPLE_RANDOM)) {
/* /*
...@@ -356,93 +383,20 @@ int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_ ...@@ -356,93 +383,20 @@ int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_
spin_lock_irqsave(&irq_action_lock, flags); spin_lock_irqsave(&irq_action_lock, flags);
action = *(bucket->pil + irq_action); if (check_irq_sharing(bucket->pil, irqflags)) {
if (action) { spin_unlock_irqrestore(&irq_action_lock, flags);
if ((action->flags & SA_SHIRQ) && (irqflags & SA_SHIRQ)) return -EBUSY;
for (tmp = action; tmp->next; tmp = tmp->next)
;
else {
spin_unlock_irqrestore(&irq_action_lock, flags);
return -EBUSY;
}
action = NULL; /* Or else! */
} }
/* If this is flagged as statically allocated then we use our action = get_action_slot(bucket);
* 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, name);
}
if (action == NULL)
action = (struct irqaction *)kmalloc(sizeof(struct irqaction),
GFP_ATOMIC);
if (!action) { if (!action) {
spin_unlock_irqrestore(&irq_action_lock, flags); spin_unlock_irqrestore(&irq_action_lock, flags);
return -ENOMEM; return -ENOMEM;
} }
if (bucket == &pil0_dummy_bucket) { bucket->flags |= IBF_ACTIVE;
bucket->irq_info = action; pending = 0;
bucket->flags |= IBF_ACTIVE; if (bucket != &pil0_dummy_bucket) {
} else {
if ((bucket->flags & IBF_ACTIVE) != 0) {
void *orig = bucket->irq_info;
void **vector = NULL;
if ((bucket->flags & IBF_PCI) == 0) {
printk("IRQ: Trying to share non-PCI bucket.\n");
goto free_and_ebusy;
}
if ((bucket->flags & IBF_MULTI) == 0) {
vector = kmalloc(sizeof(void *) * 4, GFP_ATOMIC);
if (vector == NULL)
goto free_and_enomem;
/* We might have slept. */
if ((bucket->flags & IBF_MULTI) != 0) {
int ent;
kfree(vector);
vector = (void **)bucket->irq_info;
for(ent = 0; ent < 4; ent++) {
if (vector[ent] == NULL) {
vector[ent] = action;
break;
}
}
if (ent == 4)
goto free_and_ebusy;
} else {
vector[0] = orig;
vector[1] = action;
vector[2] = NULL;
vector[3] = NULL;
bucket->irq_info = vector;
bucket->flags |= IBF_MULTI;
}
} else {
int ent;
vector = (void **)orig;
for (ent = 0; ent < 4; ent++) {
if (vector[ent] == NULL) {
vector[ent] = action;
break;
}
}
if (ent == 4)
goto free_and_ebusy;
}
} else {
bucket->irq_info = action;
bucket->flags |= IBF_ACTIVE;
}
pending = bucket->pending; pending = bucket->pending;
if (pending) if (pending)
bucket->pending = 0; bucket->pending = 0;
...@@ -456,10 +410,7 @@ int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_ ...@@ -456,10 +410,7 @@ int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_
put_ino_in_irqaction(action, irq); put_ino_in_irqaction(action, irq);
put_smpaff_in_irqaction(action, CPU_MASK_NONE); put_smpaff_in_irqaction(action, CPU_MASK_NONE);
if (tmp) append_irq_action(bucket->pil, action);
tmp->next = action;
else
*(bucket->pil + irq_action) = action;
enable_irq(irq); enable_irq(irq);
...@@ -468,147 +419,103 @@ int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_ ...@@ -468,147 +419,103 @@ int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_
atomic_bucket_insert(bucket); atomic_bucket_insert(bucket);
set_softint(1 << bucket->pil); set_softint(1 << bucket->pil);
} }
spin_unlock_irqrestore(&irq_action_lock, flags); spin_unlock_irqrestore(&irq_action_lock, flags);
if ((bucket != &pil0_dummy_bucket) && (!(irqflags & SA_STATIC_ALLOC)))
if (bucket != &pil0_dummy_bucket)
register_irq_proc(__irq_ino(irq)); register_irq_proc(__irq_ino(irq));
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
distribute_irqs(); distribute_irqs();
#endif #endif
return 0; return 0;
free_and_ebusy:
kfree(action);
spin_unlock_irqrestore(&irq_action_lock, flags);
return -EBUSY;
free_and_enomem:
kfree(action);
spin_unlock_irqrestore(&irq_action_lock, flags);
return -ENOMEM;
} }
EXPORT_SYMBOL(request_irq); EXPORT_SYMBOL(request_irq);
void free_irq(unsigned int irq, void *dev_id) static struct irqaction *unlink_irq_action(unsigned int irq, void *dev_id)
{ {
struct irqaction *action; struct ino_bucket *bucket = __bucket(irq);
struct irqaction *tmp = NULL; struct irqaction *action, **pp;
unsigned long flags;
struct ino_bucket *bucket = __bucket(irq), *bp;
if ((bucket != &pil0_dummy_bucket) &&
(bucket < &ivector_table[0] ||
bucket >= &ivector_table[NUM_IVECS])) {
unsigned int *caller;
__asm__ __volatile__("mov %%i7, %0" : "=r" (caller)); pp = irq_action + bucket->pil;
printk(KERN_CRIT "free_irq: Old style IRQ removal attempt " action = *pp;
"from %p, irq %08x.\n", caller, irq); if (unlikely(!action))
return; return NULL;
}
spin_lock_irqsave(&irq_action_lock, flags);
action = *(bucket->pil + irq_action); if (unlikely(!action->handler)) {
if (!action->handler) {
printk("Freeing free IRQ %d\n", bucket->pil); printk("Freeing free IRQ %d\n", bucket->pil);
return; return NULL;
}
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", bucket->pil);
spin_unlock_irqrestore(&irq_action_lock, flags);
return;
}
} else if (action->flags & SA_SHIRQ) {
printk("Trying to free shared IRQ %d with NULL device ID\n", bucket->pil);
spin_unlock_irqrestore(&irq_action_lock, flags);
return;
} }
if (action->flags & SA_STATIC_ALLOC) { while (action && action->dev_id != dev_id) {
printk("Attempt to free statically allocated IRQ %d (%s)\n", pp = &action->next;
bucket->pil, action->name); action = *pp;
spin_unlock_irqrestore(&irq_action_lock, flags);
return;
} }
if (action && tmp) if (likely(action))
tmp->next = action->next; *pp = action->next;
else
*(bucket->pil + irq_action) = action->next; return action;
}
void free_irq(unsigned int irq, void *dev_id)
{
struct irqaction *action;
struct ino_bucket *bucket;
unsigned long flags;
spin_lock_irqsave(&irq_action_lock, flags);
action = unlink_irq_action(irq, dev_id);
spin_unlock_irqrestore(&irq_action_lock, flags); spin_unlock_irqrestore(&irq_action_lock, flags);
if (unlikely(!action))
return;
synchronize_irq(irq); synchronize_irq(irq);
spin_lock_irqsave(&irq_action_lock, flags); spin_lock_irqsave(&irq_action_lock, flags);
bucket = __bucket(irq);
if (bucket != &pil0_dummy_bucket) { if (bucket != &pil0_dummy_bucket) {
struct irq_desc *desc = bucket->irq_info;
unsigned long imap = bucket->imap; unsigned long imap = bucket->imap;
void **vector, *orig; int ent, i;
int ent;
orig = bucket->irq_info;
vector = (void **)orig;
if ((bucket->flags & IBF_MULTI) != 0) {
int other = 0;
void *orphan = NULL;
for (ent = 0; ent < 4; ent++) {
if (vector[ent] == action)
vector[ent] = NULL;
else if (vector[ent] != NULL) {
orphan = vector[ent];
other++;
}
}
/* Only free when no other shared irq for (i = 0; i < MAX_IRQ_DESC_ACTION; i++) {
* uses this bucket. struct irqaction *p = &desc->action[i];
*/
if (other) { if (p == action) {
if (other == 1) { desc->action_active_mask &= ~(1 << i);
/* Convert back to non-shared bucket. */ break;
bucket->irq_info = orphan;
bucket->flags &= ~(IBF_MULTI);
kfree(vector);
}
goto out;
} }
} else {
bucket->irq_info = NULL;
} }
/* This unique interrupt source is now inactive. */ if (!desc->action_active_mask) {
bucket->flags &= ~IBF_ACTIVE; /* This unique interrupt source is now inactive. */
bucket->flags &= ~IBF_ACTIVE;
/* See if any other buckets share this bucket's IMAP /* See if any other buckets share this bucket's IMAP
* and are still active. * and are still active.
*/ */
for (ent = 0; ent < NUM_IVECS; ent++) { for (ent = 0; ent < NUM_IVECS; ent++) {
bp = &ivector_table[ent]; struct ino_bucket *bp = &ivector_table[ent];
if (bp != bucket && if (bp != bucket &&
bp->imap == imap && bp->imap == imap &&
(bp->flags & IBF_ACTIVE) != 0) (bp->flags & IBF_ACTIVE) != 0)
break; break;
} }
/* Only disable when no other sub-irq levels of /* Only disable when no other sub-irq levels of
* the same IMAP are active. * the same IMAP are active.
*/ */
if (ent == NUM_IVECS) if (ent == NUM_IVECS)
disable_irq(irq); disable_irq(irq);
}
} }
out:
kfree(action);
spin_unlock_irqrestore(&irq_action_lock, flags); spin_unlock_irqrestore(&irq_action_lock, flags);
} }
...@@ -647,99 +554,55 @@ void synchronize_irq(unsigned int irq) ...@@ -647,99 +554,55 @@ void synchronize_irq(unsigned int irq)
} }
#endif /* CONFIG_SMP */ #endif /* CONFIG_SMP */
void catch_disabled_ivec(struct pt_regs *regs) static void process_bucket(int irq, struct ino_bucket *bp, struct pt_regs *regs)
{
int cpu = smp_processor_id();
struct ino_bucket *bucket = __bucket(*irq_work(cpu, 0));
/* We can actually see this on Ultra/PCI PCI cards, which are bridges
* to other devices. Here a single IMAP enabled potentially multiple
* unique interrupt sources (which each do have a unique ICLR register.
*
* So what we do is just register that the IVEC arrived, when registered
* for real the request_irq() code will check the bit and signal
* a local CPU interrupt for it.
*/
#if 0
printk("IVEC: Spurious interrupt vector (%x) received at (%016lx)\n",
bucket - &ivector_table[0], regs->tpc);
#endif
*irq_work(cpu, 0) = 0;
bucket->pending = 1;
}
/* Tune this... */
#define FORWARD_VOLUME 12
#ifdef CONFIG_SMP
static inline void redirect_intr(int cpu, struct ino_bucket *bp)
{ {
/* Ok, here is what is going on: struct irq_desc *desc = bp->irq_info;
* 1) Retargeting IRQs on Starfire is very unsigned char flags = bp->flags;
* expensive so just forget about it on them. u32 action_mask, i;
* 2) Moving around very high priority interrupts int random;
* is a losing game.
* 3) If the current cpu is idle, interrupts are
* useful work, so keep them here. But do not
* pass to our neighbour if he is not very idle.
* 4) If sysadmin explicitly asks for directed intrs,
* Just Do It.
*/
struct irqaction *ap = bp->irq_info;
cpumask_t cpu_mask;
unsigned int buddy, ticks;
cpu_mask = get_smpaff_in_irqaction(ap); bp->flags |= IBF_INPROGRESS;
cpus_and(cpu_mask, cpu_mask, cpu_online_map);
if (cpus_empty(cpu_mask))
cpu_mask = cpu_online_map;
if (this_is_starfire != 0 || if (unlikely(!(flags & IBF_ACTIVE))) {
bp->pil >= 10 || current->pid == 0) bp->pending = 1;
goto out; goto out;
/* 'cpu' is the MID (ie. UPAID), calculate the MID
* of our buddy.
*/
buddy = cpu + 1;
if (buddy >= NR_CPUS)
buddy = 0;
ticks = 0;
while (!cpu_isset(buddy, cpu_mask)) {
if (++buddy >= NR_CPUS)
buddy = 0;
if (++ticks > NR_CPUS) {
put_smpaff_in_irqaction(ap, CPU_MASK_NONE);
goto out;
}
} }
if (buddy == cpu) if (desc->pre_handler)
goto out; desc->pre_handler(bp,
desc->pre_handler_arg1,
desc->pre_handler_arg2);
/* Voo-doo programming. */ action_mask = desc->action_active_mask;
if (cpu_data(buddy).idle_volume < FORWARD_VOLUME) random = 0;
goto out; for (i = 0; i < MAX_IRQ_DESC_ACTION; i++) {
struct irqaction *p = &desc->action[i];
u32 mask = (1 << i);
/* This just so happens to be correct on Cheetah if (!(action_mask & mask))
* at the moment. continue;
*/
buddy <<= 26; action_mask &= ~mask;
/* Push it to our buddy. */ if (p->handler(__irq(bp), p->dev_id, regs) == IRQ_HANDLED)
upa_writel(buddy | IMAP_VALID, bp->imap); random |= p->flags;
if (!action_mask)
break;
}
if (bp->pil != 0) {
upa_writel(ICLR_IDLE, bp->iclr);
/* Test and add entropy */
if (random & SA_SAMPLE_RANDOM)
add_interrupt_randomness(irq);
}
out: out:
return; bp->flags &= ~IBF_INPROGRESS;
} }
#endif
void handler_irq(int irq, struct pt_regs *regs) void handler_irq(int irq, struct pt_regs *regs)
{ {
struct ino_bucket *bp, *nbp; struct ino_bucket *bp;
int cpu = smp_processor_id(); int cpu = smp_processor_id();
#ifndef CONFIG_SMP #ifndef CONFIG_SMP
...@@ -757,8 +620,6 @@ void handler_irq(int irq, struct pt_regs *regs) ...@@ -757,8 +620,6 @@ void handler_irq(int irq, struct pt_regs *regs)
clear_softint(clr_mask); clear_softint(clr_mask);
} }
#else #else
int should_forward = 0;
clear_softint(1 << irq); clear_softint(1 << irq);
#endif #endif
...@@ -773,63 +634,12 @@ void handler_irq(int irq, struct pt_regs *regs) ...@@ -773,63 +634,12 @@ void handler_irq(int irq, struct pt_regs *regs)
#else #else
bp = __bucket(xchg32(irq_work(cpu, irq), 0)); bp = __bucket(xchg32(irq_work(cpu, irq), 0));
#endif #endif
for ( ; bp != NULL; bp = nbp) { while (bp) {
unsigned char flags = bp->flags; struct ino_bucket *nbp = __bucket(bp->irq_chain);
unsigned char random = 0;
nbp = __bucket(bp->irq_chain);
bp->irq_chain = 0; bp->irq_chain = 0;
process_bucket(irq, bp, regs);
bp->flags |= IBF_INPROGRESS; bp = nbp;
if ((flags & IBF_ACTIVE) != 0) {
#ifdef CONFIG_PCI
if ((flags & IBF_DMA_SYNC) != 0) {
upa_readl(dma_sync_reg_table[bp->synctab_ent]);
upa_readq(pci_dma_wsync);
}
#endif
if ((flags & IBF_MULTI) == 0) {
struct irqaction *ap = bp->irq_info;
int ret;
ret = ap->handler(__irq(bp), ap->dev_id, regs);
if (ret == IRQ_HANDLED)
random |= ap->flags;
} else {
void **vector = (void **)bp->irq_info;
int ent;
for (ent = 0; ent < 4; ent++) {
struct irqaction *ap = vector[ent];
if (ap != NULL) {
int ret;
ret = ap->handler(__irq(bp),
ap->dev_id,
regs);
if (ret == IRQ_HANDLED)
random |= ap->flags;
}
}
}
/* Only the dummy bucket lacks IMAP/ICLR. */
if (bp->pil != 0) {
#ifdef CONFIG_SMP
if (should_forward) {
redirect_intr(cpu, bp);
should_forward = 0;
}
#endif
upa_writel(ICLR_IDLE, bp->iclr);
/* Test and add entropy */
if (random & SA_SAMPLE_RANDOM)
add_interrupt_randomness(irq);
}
} else
bp->pending = 1;
bp->flags &= ~IBF_INPROGRESS;
} }
irq_exit(); irq_exit();
} }
...@@ -959,7 +769,10 @@ static void distribute_irqs(void) ...@@ -959,7 +769,10 @@ static void distribute_irqs(void)
*/ */
for (level = 1; level < NR_IRQS; level++) { for (level = 1; level < NR_IRQS; level++) {
struct irqaction *p = irq_action[level]; struct irqaction *p = irq_action[level];
if (level == 12) continue;
if (level == 12)
continue;
while(p) { while(p) {
cpu = retarget_one_irq(p, cpu); cpu = retarget_one_irq(p, cpu);
p = p->next; p = p->next;
......
...@@ -595,6 +595,23 @@ static int __init sabre_ino_to_pil(struct pci_dev *pdev, unsigned int ino) ...@@ -595,6 +595,23 @@ static int __init sabre_ino_to_pil(struct pci_dev *pdev, unsigned int ino)
return ret; return ret;
} }
/* When a device lives behind a bridge deeper in the PCI bus topology
* than APB, a special sequence must run to make sure all pending DMA
* transfers at the time of IRQ delivery are visible in the coherency
* domain by the cpu. This sequence is to perform a read on the far
* side of the non-APB bridge, then perform a read of Sabre's DMA
* write-sync register.
*/
static void sabre_wsync_handler(struct ino_bucket *bucket, void *_arg1, void *_arg2)
{
struct pci_dev *pdev = _arg1;
unsigned long sync_reg = (unsigned long) _arg2;
u16 _unused;
pci_read_config_word(pdev, PCI_VENDOR_ID, &_unused);
sabre_read(sync_reg);
}
static unsigned int __init sabre_irq_build(struct pci_pbm_info *pbm, static unsigned int __init sabre_irq_build(struct pci_pbm_info *pbm,
struct pci_dev *pdev, struct pci_dev *pdev,
unsigned int ino) unsigned int ino)
...@@ -639,24 +656,14 @@ static unsigned int __init sabre_irq_build(struct pci_pbm_info *pbm, ...@@ -639,24 +656,14 @@ static unsigned int __init sabre_irq_build(struct pci_pbm_info *pbm,
if (pdev) { if (pdev) {
struct pcidev_cookie *pcp = pdev->sysdata; struct pcidev_cookie *pcp = pdev->sysdata;
/* When a device lives behind a bridge deeper in the
* PCI bus topology than APB, a special sequence must
* run to make sure all pending DMA transfers at the
* time of IRQ delivery are visible in the coherency
* domain by the cpu. This sequence is to perform
* a read on the far side of the non-APB bridge, then
* perform a read of Sabre's DMA write-sync register.
*
* Currently, the PCI_CONFIG register for the device
* is used for this read from the far side of the bridge.
*/
if (pdev->bus->number != pcp->pbm->pci_first_busno) { if (pdev->bus->number != pcp->pbm->pci_first_busno) {
bucket->flags |= IBF_DMA_SYNC; struct pci_controller_info *p = pcp->pbm->parent;
bucket->synctab_ent = dma_sync_reg_table_entry++; struct irq_desc *d = bucket->irq_info;
dma_sync_reg_table[bucket->synctab_ent] =
(unsigned long) sabre_pci_config_mkaddr( d->pre_handler = sabre_wsync_handler;
pcp->pbm, d->pre_handler_arg1 = pdev;
pdev->bus->number, pdev->devfn, PCI_COMMAND); d->pre_handler_arg2 = (void *)
p->pbm_A.controller_regs + SABRE_WRSYNC;
} }
} }
return __irq(bucket); return __irq(bucket);
...@@ -1626,10 +1633,9 @@ void __init sabre_init(int pnode, char *model_name) ...@@ -1626,10 +1633,9 @@ void __init sabre_init(int pnode, char *model_name)
*/ */
p->pbm_A.controller_regs = pr_regs[0].phys_addr; p->pbm_A.controller_regs = pr_regs[0].phys_addr;
p->pbm_B.controller_regs = pr_regs[0].phys_addr; p->pbm_B.controller_regs = pr_regs[0].phys_addr;
pci_dma_wsync = p->pbm_A.controller_regs + SABRE_WRSYNC;
printk("PCI: Found SABRE, main regs at %016lx, wsync at %016lx\n", printk("PCI: Found SABRE, main regs at %016lx\n",
p->pbm_A.controller_regs, pci_dma_wsync); p->pbm_A.controller_regs);
/* Clear interrupts */ /* Clear interrupts */
......
...@@ -973,7 +973,7 @@ static void sparc64_start_timers(irqreturn_t (*cfunc)(int, void *, struct pt_reg ...@@ -973,7 +973,7 @@ static void sparc64_start_timers(irqreturn_t (*cfunc)(int, void *, struct pt_reg
int err; int err;
/* Register IRQ handler. */ /* Register IRQ handler. */
err = request_irq(build_irq(0, 0, 0UL, 0UL), cfunc, SA_STATIC_ALLOC, err = request_irq(build_irq(0, 0, 0UL, 0UL), cfunc, 0,
"timer", NULL); "timer", NULL);
if (err) { if (err) {
......
...@@ -16,6 +16,18 @@ ...@@ -16,6 +16,18 @@
#include <asm/pil.h> #include <asm/pil.h>
#include <asm/ptrace.h> #include <asm/ptrace.h>
struct ino_bucket;
#define MAX_IRQ_DESC_ACTION 4
struct irq_desc {
void (*pre_handler)(struct ino_bucket *, void *, void *);
void *pre_handler_arg1;
void *pre_handler_arg2;
u32 action_active_mask;
struct irqaction action[MAX_IRQ_DESC_ACTION];
};
/* You should not mess with this directly. That's the job of irq.c. /* You should not mess with this directly. That's the job of irq.c.
* *
* If you make changes here, please update hand coded assembler of * If you make changes here, please update hand coded assembler of
...@@ -42,24 +54,11 @@ struct ino_bucket { ...@@ -42,24 +54,11 @@ struct ino_bucket {
/* Miscellaneous flags. */ /* Miscellaneous flags. */
/*0x06*/unsigned char flags; /*0x06*/unsigned char flags;
/* This is used to deal with IBF_DMA_SYNC on /* Currently unused. */
* Sabre systems. /*0x07*/unsigned char __pad;
*/
/*0x07*/unsigned char synctab_ent; /* Reference to IRQ descriptor for this bucket. */
/*0x08*/struct irq_desc *irq_info;
/* Reference to handler for this IRQ. If this is
* non-NULL this means it is active and should be
* serviced. Else the pending member is set to one
* and later registry of the interrupt checks for
* this condition.
*
* Normally this is just an irq_action structure.
* But, on PCI, if multiple interrupt sources behind
* a bridge have multiple interrupt sources that share
* the same INO bucket, this points to an array of
* pointers to four IRQ action structures.
*/
/*0x08*/void *irq_info;
/* Sun5 Interrupt Clear Register. */ /* Sun5 Interrupt Clear Register. */
/*0x10*/unsigned long iclr; /*0x10*/unsigned long iclr;
...@@ -69,12 +68,6 @@ struct ino_bucket { ...@@ -69,12 +68,6 @@ struct ino_bucket {
}; };
#ifdef CONFIG_PCI
extern unsigned long pci_dma_wsync;
extern unsigned long dma_sync_reg_table[256];
extern unsigned char dma_sync_reg_table_entry;
#endif
/* IMAP/ICLR register defines */ /* IMAP/ICLR register defines */
#define IMAP_VALID 0x80000000 /* IRQ Enabled */ #define IMAP_VALID 0x80000000 /* IRQ Enabled */
#define IMAP_TID_UPA 0x7c000000 /* UPA TargetID */ #define IMAP_TID_UPA 0x7c000000 /* UPA TargetID */
...@@ -90,11 +83,9 @@ extern unsigned char dma_sync_reg_table_entry; ...@@ -90,11 +83,9 @@ extern unsigned char dma_sync_reg_table_entry;
#define ICLR_PENDING 0x00000003 /* Pending state */ #define ICLR_PENDING 0x00000003 /* Pending state */
/* Only 8-bits are available, be careful. -DaveM */ /* Only 8-bits are available, be careful. -DaveM */
#define IBF_DMA_SYNC 0x01 /* DMA synchronization behind PCI bridge needed. */ #define IBF_PCI 0x02 /* PSYCHO/SABRE/SCHIZO PCI interrupt. */
#define IBF_PCI 0x02 /* Indicates PSYCHO/SABRE/SCHIZO PCI interrupt. */ #define IBF_ACTIVE 0x04 /* Interrupt is active and has a handler.*/
#define IBF_ACTIVE 0x04 /* This interrupt is active and has a handler. */ #define IBF_INPROGRESS 0x10 /* IRQ is being serviced. */
#define IBF_MULTI 0x08 /* On PCI, indicates shared bucket. */
#define IBF_INPROGRESS 0x10 /* IRQ is being serviced. */
#define NUM_IVECS (IMAP_INR + 1) #define NUM_IVECS (IMAP_INR + 1)
extern struct ino_bucket ivector_table[NUM_IVECS]; extern struct ino_bucket ivector_table[NUM_IVECS];
......
...@@ -162,21 +162,6 @@ struct sigstack { ...@@ -162,21 +162,6 @@ struct sigstack {
#define MINSIGSTKSZ 4096 #define MINSIGSTKSZ 4096
#define SIGSTKSZ 16384 #define SIGSTKSZ 16384
#ifdef __KERNEL__
/*
* DJHR
* SA_STATIC_ALLOC is used for the SPARC system to indicate that this
* interrupt handler's irq structure should be statically allocated
* by the request_irq routine.
* The alternative is that arch/sparc/kernel/irq.c has carnal knowledge
* of interrupt usage and that sucks. Also without a flag like this
* it may be possible for the free_irq routine to attempt to free
* statically allocated data.. which is NOT GOOD.
*
*/
#define SA_STATIC_ALLOC 0x80
#endif
#include <asm-generic/signal.h> #include <asm-generic/signal.h>
struct __new_sigaction { struct __new_sigaction {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment