Skip to content

Commit

Permalink
[SPARC64]: Consolidate MSI support code.
Browse files Browse the repository at this point in the history
This also makes us use the MSI queues correctly.

Each MSI queue is serviced by a normal sun4u/sun4v INO interrupt
handler.  This handler runs the MSI queue and dispatches the
virtual interrupts indicated by arriving MSIs in that MSI queue.

All of the common logic is placed in pci_msi.c, with callbacks to
handle the PCI controller specific aspects of the operations.

This common infrastructure will make it much easier to add MSG
support.

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Oct 14, 2007
1 parent a2cd155 commit 759f89e
Show file tree
Hide file tree
Showing 7 changed files with 704 additions and 777 deletions.
1 change: 1 addition & 0 deletions arch/sparc64/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o \
pci_psycho.o pci_sabre.o pci_schizo.o \
pci_sun4v.o pci_sun4v_asm.o pci_fire.o
obj-$(CONFIG_PCI_MSI) += pci_msi.o
obj-$(CONFIG_SMP) += smp.o trampoline.o hvtramp.o
obj-$(CONFIG_SPARC32_COMPAT) += sys32.o sys_sparc32.o signal32.o
obj-$(CONFIG_BINFMT_ELF32) += binfmt_elf32.o
Expand Down
230 changes: 23 additions & 207 deletions arch/sparc64/kernel/irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
#include <linux/seq_file.h>
#include <linux/bootmem.h>
#include <linux/irq.h>
#include <linux/msi.h>

#include <asm/ptrace.h>
#include <asm/processor.h>
Expand Down Expand Up @@ -92,39 +91,46 @@ static struct {
unsigned int dev_handle;
unsigned int dev_ino;
} virt_to_real_irq_table[NR_IRQS];
static DEFINE_SPINLOCK(virt_irq_alloc_lock);

static unsigned char virt_irq_alloc(unsigned int real_irq)
unsigned char virt_irq_alloc(unsigned int real_irq)
{
unsigned long flags;
unsigned char ent;

BUILD_BUG_ON(NR_IRQS >= 256);

spin_lock_irqsave(&virt_irq_alloc_lock, flags);

for (ent = 1; ent < NR_IRQS; ent++) {
if (!virt_to_real_irq_table[ent].irq)
break;
}
if (ent >= NR_IRQS) {
printk(KERN_ERR "IRQ: Out of virtual IRQs.\n");
return 0;
ent = 0;
} else {
virt_to_real_irq_table[ent].irq = real_irq;
}

virt_to_real_irq_table[ent].irq = real_irq;
spin_unlock_irqrestore(&virt_irq_alloc_lock, flags);

return ent;
}

#ifdef CONFIG_PCI_MSI
static void virt_irq_free(unsigned int virt_irq)
void virt_irq_free(unsigned int virt_irq)
{
unsigned int real_irq;
unsigned long flags;

if (virt_irq >= NR_IRQS)
return;

real_irq = virt_to_real_irq_table[virt_irq].irq;
spin_lock_irqsave(&virt_irq_alloc_lock, flags);

virt_to_real_irq_table[virt_irq].irq = 0;

__bucket(real_irq)->virt_irq = 0;
spin_unlock_irqrestore(&virt_irq_alloc_lock, flags);
}
#endif

Expand Down Expand Up @@ -217,27 +223,8 @@ struct irq_handler_data {
void (*pre_handler)(unsigned int, void *, void *);
void *pre_handler_arg1;
void *pre_handler_arg2;

u32 msi;
};

void sparc64_set_msi(unsigned int virt_irq, u32 msi)
{
struct irq_handler_data *data = get_irq_chip_data(virt_irq);

if (data)
data->msi = msi;
}

u32 sparc64_get_msi(unsigned int virt_irq)
{
struct irq_handler_data *data = get_irq_chip_data(virt_irq);

if (data)
return data->msi;
return 0xffffffff;
}

static inline struct ino_bucket *virt_irq_to_bucket(unsigned int virt_irq)
{
unsigned int real_irq = virt_to_real_irq(virt_irq);
Expand Down Expand Up @@ -405,32 +392,6 @@ static void sun4v_irq_disable(unsigned int virt_irq)
}
}

#ifdef CONFIG_PCI_MSI
static void sun4u_msi_enable(unsigned int virt_irq)
{
sun4u_irq_enable(virt_irq);
unmask_msi_irq(virt_irq);
}

static void sun4u_msi_disable(unsigned int virt_irq)
{
mask_msi_irq(virt_irq);
sun4u_irq_disable(virt_irq);
}

static void sun4v_msi_enable(unsigned int virt_irq)
{
sun4v_irq_enable(virt_irq);
unmask_msi_irq(virt_irq);
}

static void sun4v_msi_disable(unsigned int virt_irq)
{
mask_msi_irq(virt_irq);
sun4v_irq_disable(virt_irq);
}
#endif

static void sun4v_irq_end(unsigned int virt_irq)
{
struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq);
Expand Down Expand Up @@ -585,39 +546,6 @@ static struct irq_chip sun4v_irq = {
.set_affinity = sun4v_set_affinity,
};

static struct irq_chip sun4v_irq_ack = {
.typename = "sun4v+ack",
.enable = sun4v_irq_enable,
.disable = sun4v_irq_disable,
.ack = run_pre_handler,
.end = sun4v_irq_end,
.set_affinity = sun4v_set_affinity,
};

#ifdef CONFIG_PCI_MSI
static struct irq_chip sun4u_msi = {
.typename = "sun4u+msi",
.mask = mask_msi_irq,
.unmask = unmask_msi_irq,
.enable = sun4u_msi_enable,
.disable = sun4u_msi_disable,
.ack = run_pre_handler,
.end = sun4u_irq_end,
.set_affinity = sun4u_set_affinity,
};

static struct irq_chip sun4v_msi = {
.typename = "sun4v+msi",
.mask = mask_msi_irq,
.unmask = unmask_msi_irq,
.enable = sun4v_msi_enable,
.disable = sun4v_msi_disable,
.ack = run_pre_handler,
.end = sun4v_irq_end,
.set_affinity = sun4v_set_affinity,
};
#endif

static struct irq_chip sun4v_virq = {
.typename = "vsun4v",
.enable = sun4v_virq_enable,
Expand All @@ -626,42 +554,27 @@ static struct irq_chip sun4v_virq = {
.set_affinity = sun4v_virt_set_affinity,
};

static struct irq_chip sun4v_virq_ack = {
.typename = "vsun4v+ack",
.enable = sun4v_virq_enable,
.disable = sun4v_virq_disable,
.ack = run_pre_handler,
.end = sun4v_virq_end,
.set_affinity = sun4v_virt_set_affinity,
};

void irq_install_pre_handler(int virt_irq,
void (*func)(unsigned int, void *, void *),
void *arg1, void *arg2)
{
struct irq_handler_data *data = get_irq_chip_data(virt_irq);
struct irq_chip *chip;
struct irq_chip *chip = get_irq_chip(virt_irq);

if (WARN_ON(chip == &sun4v_irq || chip == &sun4v_virq)) {
printk(KERN_ERR "IRQ: Trying to install pre-handler on "
"sun4v irq %u\n", virt_irq);
return;
}

data->pre_handler = func;
data->pre_handler_arg1 = arg1;
data->pre_handler_arg2 = arg2;

chip = get_irq_chip(virt_irq);
if (chip == &sun4u_irq_ack ||
chip == &sun4v_irq_ack ||
chip == &sun4v_virq_ack
#ifdef CONFIG_PCI_MSI
|| chip == &sun4u_msi
|| chip == &sun4v_msi
#endif
)
if (chip == &sun4u_irq_ack)
return;

chip = (chip == &sun4u_irq ?
&sun4u_irq_ack :
(chip == &sun4v_irq ?
&sun4v_irq_ack : &sun4v_virq_ack));
set_irq_chip(virt_irq, chip);
set_irq_chip(virt_irq, &sun4u_irq_ack);
}

unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap)
Expand Down Expand Up @@ -765,103 +678,6 @@ unsigned int sun4v_build_virq(u32 devhandle, unsigned int devino)
return virq;
}

#ifdef CONFIG_PCI_MSI
unsigned int sun4v_build_msi(u32 devhandle, unsigned int *virt_irq_p,
unsigned int msi_start, unsigned int msi_end)
{
struct ino_bucket *bucket;
struct irq_handler_data *data;
unsigned long sysino;
unsigned int devino;

BUG_ON(tlb_type != hypervisor);

/* Find a free devino in the given range. */
for (devino = msi_start; devino < msi_end; devino++) {
sysino = sun4v_devino_to_sysino(devhandle, devino);
bucket = &ivector_table[sysino];
if (!bucket->virt_irq)
break;
}
if (devino >= msi_end)
return -ENOSPC;

sysino = sun4v_devino_to_sysino(devhandle, devino);
bucket = &ivector_table[sysino];
bucket->virt_irq = virt_irq_alloc(__irq(bucket));
*virt_irq_p = bucket->virt_irq;
set_irq_chip(bucket->virt_irq, &sun4v_msi);

data = get_irq_chip_data(bucket->virt_irq);
if (unlikely(data))
return devino;

data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC);
if (unlikely(!data)) {
virt_irq_free(*virt_irq_p);
return -ENOMEM;
}
set_irq_chip_data(bucket->virt_irq, data);

data->imap = ~0UL;
data->iclr = ~0UL;

return devino;
}

void sun4v_destroy_msi(unsigned int virt_irq)
{
virt_irq_free(virt_irq);
}

unsigned int sun4u_build_msi(u32 portid, unsigned int *virt_irq_p,
unsigned int msi_start, unsigned int msi_end,
unsigned long imap_base, unsigned long iclr_base)
{
struct ino_bucket *bucket;
struct irq_handler_data *data;
unsigned long sysino;
unsigned int devino;

/* Find a free devino in the given range. */
for (devino = msi_start; devino < msi_end; devino++) {
sysino = (portid << 6) | devino;
bucket = &ivector_table[sysino];
if (!bucket->virt_irq)
break;
}
if (devino >= msi_end)
return -ENOSPC;

sysino = (portid << 6) | devino;
bucket = &ivector_table[sysino];
bucket->virt_irq = virt_irq_alloc(__irq(bucket));
*virt_irq_p = bucket->virt_irq;
set_irq_chip(bucket->virt_irq, &sun4u_msi);

data = get_irq_chip_data(bucket->virt_irq);
if (unlikely(data))
return devino;

data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC);
if (unlikely(!data)) {
virt_irq_free(*virt_irq_p);
return -ENOMEM;
}
set_irq_chip_data(bucket->virt_irq, data);

data->imap = (imap_base + (devino * 0x8UL));
data->iclr = (iclr_base + (devino * 0x8UL));

return devino;
}

void sun4u_destroy_msi(unsigned int virt_irq)
{
virt_irq_free(virt_irq);
}
#endif

void ack_bad_irq(unsigned int virt_irq)
{
struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq);
Expand Down
Loading

0 comments on commit 759f89e

Please sign in to comment.