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
......
This diff is collapsed.
...@@ -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