Commit 9938e2c2 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] ia64 MSI support

From: "Nguyen, Tom L" <tom.l.nguyen@intel.com>

Adds MSI support for ia64.

- Modified existing code in drivers/pci/msi.c and drivers/pci/msi.h to
  include MSI support on IA64 platform.

- Based on the comments received from Zwane Mwaikambo and David Mosberger,
  this patch consolidates the vector allocators as
  assign_irq_vector(AUTO_ASSIGN) has the same semantics as
  ia64_alloc_vector() by converting the existing uses of ia64_alloc_vector()
  to assign_irq_vector(AUTO_ASSIGN).

- Based on the comments received from Zwane Mwaikambo, this patch
  consolidates the semantics of vector allocator assign_irq_vector() in
  drivers/pci/msi.c into the relevant architecture's vector allocator
  assign_irq_vector() in arch/i386/kernel/io_apic.c.

- Regarding vector allocation, this patch modifies the existing function
  assign_irq_vector() to maximize the number of allocated vectors to 188
  before going -ENOSPC.

- Based on your comments, this patch creates <asm-i386/msi.h>,
  <asm-ia64/msi.h> and <asm-x86_64/msi.h>, includes <asm/msi.h> from within
  drivers/pci/msi.h and then places all the code which is currently under
  ifdef in msi.h into the relevant architecture's <asm/msi.h> file.

- Based on your comments, this patch places pci_vector_resources() in
  existing drivers/pci/msi.c in the relevant architecture implementations
  such as into arch/.../pci/irq.c.
parent 27b5c750
......@@ -76,8 +76,8 @@ static struct irq_pin_list {
int apic, pin, next;
} irq_2_pin[PIN_MAP_SIZE];
int vector_irq[NR_VECTORS] = { [0 ... NR_VECTORS - 1] = -1};
#ifdef CONFIG_PCI_USE_VECTOR
int vector_irq[NR_IRQS] = { [0 ... NR_IRQS -1] = -1};
#define vector_to_irq(vector) \
(platform_legacy_irq(vector) ? vector : vector_irq[vector])
#else
......@@ -1149,12 +1149,16 @@ static inline int IO_APIC_irq_trigger(int irq)
/* irq_vectors is indexed by the sum of all RTEs in all I/O APICs. */
u8 irq_vector[NR_IRQ_VECTORS] = { FIRST_DEVICE_VECTOR , 0 };
#ifndef CONFIG_PCI_USE_VECTOR
#ifdef CONFIG_PCI_USE_VECTOR
int assign_irq_vector(int irq)
#else
int __init assign_irq_vector(int irq)
#endif
{
static int current_vector = FIRST_DEVICE_VECTOR, offset = 0;
BUG_ON(irq >= NR_IRQ_VECTORS);
if (IO_APIC_VECTOR(irq) > 0)
if (irq != AUTO_ASSIGN && IO_APIC_VECTOR(irq) > 0)
return IO_APIC_VECTOR(irq);
next:
current_vector += 8;
......@@ -1162,15 +1166,18 @@ int __init assign_irq_vector(int irq)
goto next;
if (current_vector >= FIRST_SYSTEM_VECTOR) {
offset = (offset + 1) & 7;
offset++;
if (!(offset%8))
return -ENOSPC;
current_vector = FIRST_DEVICE_VECTOR + offset;
}
IO_APIC_VECTOR(irq) = current_vector;
vector_irq[current_vector] = irq;
if (irq != AUTO_ASSIGN)
IO_APIC_VECTOR(irq) = current_vector;
return current_vector;
}
#endif
static struct hw_interrupt_type ioapic_level_type;
static struct hw_interrupt_type ioapic_edge_type;
......
......@@ -15,6 +15,7 @@
#include <asm/io.h>
#include <asm/smp.h>
#include <asm/io_apic.h>
#include <asm/hw_irq.h>
#include "pci.h"
......@@ -1005,3 +1006,33 @@ int pirq_enable_irq(struct pci_dev *dev)
pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
return 0;
}
int pci_vector_resources(int last, int nr_released)
{
int count = nr_released;
int next = last;
int offset = (last % 8);
while (next < FIRST_SYSTEM_VECTOR) {
next += 8;
#ifdef CONFIG_X86_64
if (next == IA32_SYSCALL_VECTOR)
continue;
#else
if (next == SYSCALL_VECTOR)
continue;
#endif
count++;
if (next >= FIRST_SYSTEM_VECTOR) {
if (offset%8) {
next = FIRST_DEVICE_VECTOR + offset;
offset++;
continue;
}
count--;
}
}
return count;
}
......@@ -361,6 +361,16 @@ config PCI
information about which PCI hardware does work under Linux and which
doesn't.
config PCI_USE_VECTOR
bool
default y if IA64
help
This enables MSI, Message Signaled Interrupt, on specific
MSI capable device functions detected upon requests from the
device drivers. Message Signal Interrupt enables an MSI-capable
hardware device to send an inbound Memory Write on its PCI bus
instead of asserting IRQ signal on device IRQ pin.
config PCI_DOMAINS
bool
default PCI
......
......@@ -228,7 +228,7 @@ simeth_probe1(void)
return err;
}
dev->irq = ia64_alloc_vector();
dev->irq = assign_irq_vector(AUTO_ASSIGN);
/*
* attach the interrupt in the simulator, this does enable interrupts
......
......@@ -1051,7 +1051,7 @@ simrs_init (void)
if (state->type == PORT_UNKNOWN) continue;
if (!state->irq) {
state->irq = ia64_alloc_vector();
state->irq = assign_irq_vector(AUTO_ASSIGN);
ia64_ssc_connect_irq(KEYBOARD_INTR, state->irq);
}
......
......@@ -435,7 +435,7 @@ iosapic_reassign_vector (int vector)
|| iosapic_intr_info[vector].gsi_base || iosapic_intr_info[vector].dmode
|| iosapic_intr_info[vector].polarity || iosapic_intr_info[vector].trigger)
{
new_vector = ia64_alloc_vector();
new_vector = assign_irq_vector(AUTO_ASSIGN);
printk(KERN_INFO "Reassigning vector %d to %d\n", vector, new_vector);
memcpy(&iosapic_intr_info[new_vector], &iosapic_intr_info[vector],
sizeof(struct iosapic_intr_info));
......@@ -500,7 +500,7 @@ iosapic_register_intr (unsigned int gsi,
vector = gsi_to_vector(gsi);
if (vector < 0)
vector = ia64_alloc_vector();
vector = assign_irq_vector(AUTO_ASSIGN);
register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY,
polarity, trigger);
......@@ -538,7 +538,7 @@ iosapic_register_platform_intr (u32 int_type, unsigned int gsi,
delivery = IOSAPIC_PMI;
break;
case ACPI_INTERRUPT_INIT:
vector = ia64_alloc_vector();
vector = assign_irq_vector(AUTO_ASSIGN);
delivery = IOSAPIC_INIT;
break;
case ACPI_INTERRUPT_CPEI:
......@@ -708,7 +708,7 @@ iosapic_parse_prt (void)
vector = isa_irq_to_vector(gsi);
else
/* new GSI; allocate a vector for it */
vector = ia64_alloc_vector();
vector = assign_irq_vector(AUTO_ASSIGN);
register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY, IOSAPIC_POL_LOW,
IOSAPIC_LEVEL);
......
......@@ -73,13 +73,13 @@ irq_exit (void)
}
int
ia64_alloc_vector (void)
assign_irq_vector (int irq)
{
static int next_vector = IA64_FIRST_DEVICE_VECTOR;
if (next_vector > IA64_LAST_DEVICE_VECTOR)
/* XXX could look for sharable vectors instead of panic'ing... */
panic("ia64_alloc_vector: out of interrupt vectors!");
panic("assign_irq_vector: out of interrupt vectors!");
return next_vector++;
}
......
......@@ -34,6 +34,7 @@
# include <asm/smp.h>
#endif
#include <asm/irq.h>
#include <asm/hw_irq.h>
#undef DEBUG
......@@ -567,3 +568,12 @@ pcibios_prep_mwi (struct pci_dev *dev)
}
return rc;
}
int pci_vector_resources(int last, int nr_released)
{
int count = nr_released;
count += (IA64_LAST_DEVICE_VECTOR - last);
return count;
}
......@@ -19,26 +19,22 @@
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/smp.h>
#include <asm/desc.h>
#include <asm/io_apic.h>
#include <mach_apic.h>
#include "msi.h"
static spinlock_t msi_lock = SPIN_LOCK_UNLOCKED;
static struct msi_desc* msi_desc[NR_IRQS] = { [0 ... NR_IRQS-1] = NULL };
static kmem_cache_t* msi_cachep;
static int pci_msi_enable = 1;
static int nr_alloc_vectors = 0;
static int last_alloc_vector = 0;
static int nr_released_vectors = 0;
static int nr_reserved_vectors = NR_HP_RESERVED_VECTORS;
static int nr_msix_devices = 0;
#ifndef CONFIG_X86_IO_APIC
int vector_irq[NR_IRQS] = { [0 ... NR_IRQS -1] = -1};
u8 irq_vector[NR_IRQS] = { FIRST_DEVICE_VECTOR , 0 };
int vector_irq[NR_VECTORS] = { [0 ... NR_VECTORS - 1] = -1};
u8 irq_vector[NR_IRQ_VECTORS] = { FIRST_DEVICE_VECTOR , 0 };
#endif
static void msi_cache_ctor(void *p, kmem_cache_t *cache, unsigned long flags)
......@@ -96,7 +92,6 @@ static void set_msi_affinity(unsigned int vector, cpumask_t cpu_mask)
{
struct msi_desc *entry;
struct msg_address address;
unsigned int dest_id;
entry = (struct msi_desc *)msi_desc[vector];
if (!entry || !entry->dev)
......@@ -113,10 +108,9 @@ static void set_msi_affinity(unsigned int vector, cpumask_t cpu_mask)
entry->dev->bus->ops->read(entry->dev->bus, entry->dev->devfn,
msi_lower_address_reg(pos), 4,
&address.lo_address.value);
dest_id = (address.lo_address.u.dest_id &
MSI_ADDRESS_HEADER_MASK) |
(cpu_mask_to_apicid(cpu_mask) << MSI_TARGET_CPU_SHIFT);
address.lo_address.u.dest_id = dest_id;
address.lo_address.value &= MSI_ADDRESS_DEST_ID_MASK;
address.lo_address.value |= (cpu_mask_to_apicid(cpu_mask) <<
MSI_TARGET_CPU_SHIFT);
entry->msi_attrib.current_cpu = cpu_mask_to_apicid(cpu_mask);
entry->dev->bus->ops->write(entry->dev->bus, entry->dev->devfn,
msi_lower_address_reg(pos), 4,
......@@ -129,10 +123,9 @@ static void set_msi_affinity(unsigned int vector, cpumask_t cpu_mask)
PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET;
address.lo_address.value = readl(entry->mask_base + offset);
dest_id = (address.lo_address.u.dest_id &
MSI_ADDRESS_HEADER_MASK) |
(cpu_mask_to_apicid(cpu_mask) << MSI_TARGET_CPU_SHIFT);
address.lo_address.u.dest_id = dest_id;
address.lo_address.value &= MSI_ADDRESS_DEST_ID_MASK;
address.lo_address.value |= (cpu_mask_to_apicid(cpu_mask) <<
MSI_TARGET_CPU_SHIFT);
entry->msi_attrib.current_cpu = cpu_mask_to_apicid(cpu_mask);
writel(address.lo_address.value, entry->mask_base + offset);
break;
......@@ -265,61 +258,11 @@ static void msi_address_init(struct msg_address *msi_address)
memset(msi_address, 0, sizeof(struct msg_address));
msi_address->hi_address = (u32)0;
dest_id = (MSI_ADDRESS_HEADER << MSI_ADDRESS_HEADER_SHIFT) |
(MSI_TARGET_CPU << MSI_TARGET_CPU_SHIFT);
msi_address->lo_address.u.dest_mode = MSI_LOGICAL_MODE;
dest_id = (MSI_ADDRESS_HEADER << MSI_ADDRESS_HEADER_SHIFT);
msi_address->lo_address.u.dest_mode = MSI_DEST_MODE;
msi_address->lo_address.u.redirection_hint = MSI_REDIRECTION_HINT_MODE;
msi_address->lo_address.u.dest_id = dest_id;
}
static int pci_vector_resources(void)
{
static int res = -EINVAL;
int nr_free_vectors;
if (res == -EINVAL) {
int i, repeat;
for (i = NR_REPEATS; i > 0; i--) {
if ((FIRST_DEVICE_VECTOR + i * 8) > FIRST_SYSTEM_VECTOR)
continue;
break;
}
i++;
repeat = (FIRST_SYSTEM_VECTOR - FIRST_DEVICE_VECTOR)/i;
res = i * repeat - NR_RESERVED_VECTORS + 1;
}
nr_free_vectors = res + nr_released_vectors - nr_alloc_vectors;
return nr_free_vectors;
}
int assign_irq_vector(int irq)
{
static int current_vector = FIRST_DEVICE_VECTOR, offset = 0;
if (irq != MSI_AUTO && IO_APIC_VECTOR(irq) > 0)
return IO_APIC_VECTOR(irq);
next:
current_vector += 8;
if (current_vector == SYSCALL_VECTOR)
goto next;
if (current_vector > FIRST_SYSTEM_VECTOR) {
offset++;
current_vector = FIRST_DEVICE_VECTOR + offset;
}
if (current_vector == FIRST_SYSTEM_VECTOR)
return -ENOSPC;
vector_irq[current_vector] = irq;
if (irq != MSI_AUTO)
IO_APIC_VECTOR(irq) = current_vector;
nr_alloc_vectors++;
return current_vector;
msi_address->lo_address.value |= (MSI_TARGET_CPU << MSI_TARGET_CPU_SHIFT);
}
static int assign_msi_vector(void)
......@@ -333,10 +276,6 @@ static int assign_msi_vector(void)
* vector is assigned unique among drivers.
*/
spin_lock_irqsave(&msi_lock, flags);
if (!(pci_vector_resources() > 0)) {
spin_unlock_irqrestore(&msi_lock, flags);
return -EBUSY;
}
if (!new_vector_avail) {
/*
......@@ -363,9 +302,9 @@ static int assign_msi_vector(void)
spin_unlock_irqrestore(&msi_lock, flags);
return -EBUSY;
}
vector = assign_irq_vector(MSI_AUTO);
if (vector == (FIRST_SYSTEM_VECTOR - 8))
vector = assign_irq_vector(AUTO_ASSIGN);
last_alloc_vector = vector;
if (vector == LAST_DEVICE_VECTOR)
new_vector_avail = 0;
spin_unlock_irqrestore(&msi_lock, flags);
......@@ -924,7 +863,8 @@ int msi_alloc_vectors(struct pci_dev* dev, int *vector, int nvec)
* msi_lock is provided to ensure that enough vectors resources are
* available before granting.
*/
free_vectors = pci_vector_resources();
free_vectors = pci_vector_resources(last_alloc_vector,
nr_released_vectors);
/* Ensure that each MSI/MSI-X device has one vector reserved by
default to avoid any MSI-X driver to take all available
resources */
......
/*
* File: msi.h
*
* Copyright (C) 2003-2004 Intel
* Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
*/
......@@ -8,9 +6,7 @@
#ifndef MSI_H
#define MSI_H
#define MSI_AUTO -1
#define NR_REPEATS 23
#define NR_RESERVED_VECTORS 3 /*FIRST_DEVICE_VECTOR,FIRST_SYSTEM_VECTOR,0x80 */
#include <asm/msi.h>
/*
* Assume the maximum number of hot plug slots supported by the system is about
......@@ -22,9 +18,10 @@
*/
#define NR_HP_RESERVED_VECTORS 20
extern int vector_irq[NR_IRQS];
extern int vector_irq[NR_VECTORS];
extern cpumask_t pending_irq_balance_cpumask[NR_IRQS];
extern void (*interrupt[NR_IRQS])(void);
extern int pci_vector_resources(int last, int nr_released);
#ifdef CONFIG_SMP
#define set_msi_irq_affinity set_msi_affinity
......@@ -36,13 +33,6 @@ extern void (*interrupt[NR_IRQS])(void);
static inline void move_msi(int vector) {}
#endif
#ifndef CONFIG_X86_IO_APIC
static inline int get_ioapic_vector(struct pci_dev *dev) { return -1;}
static inline void restore_ioapic_irq_handler(int irq) {}
#else
extern void restore_ioapic_irq_handler(int irq);
#endif
/*
* MSI-X Address Register
*/
......@@ -85,25 +75,20 @@ extern void restore_ioapic_irq_handler(int irq);
#define msix_mask(address) (address | PCI_MSIX_FLAGS_BITMASK)
#define msix_is_pending(address) (address & PCI_MSIX_FLAGS_PENDMASK)
/*
* MSI Defined Data Structures
*/
#define MSI_ADDRESS_HEADER 0xfee
#define MSI_ADDRESS_HEADER_SHIFT 12
#define MSI_ADDRESS_HEADER_MASK 0xfff000
#define MSI_TARGET_CPU_SHIFT 4
#define MSI_ADDRESS_DEST_ID_MASK 0xfff0000f
#define MSI_TARGET_CPU_MASK 0xff
#define MSI_DELIVERY_MODE 0
#define MSI_LEVEL_MODE 1 /* Edge always assert */
#define MSI_TRIGGER_MODE 0 /* MSI is edge sensitive */
#define MSI_PHYSICAL_MODE 0
#define MSI_LOGICAL_MODE 1
#define MSI_REDIRECTION_HINT_MODE 0
#ifdef CONFIG_SMP
#define MSI_TARGET_CPU logical_smp_processor_id()
#else
#define MSI_TARGET_CPU TARGET_CPUS
#endif
struct msg_data {
#if defined(__LITTLE_ENDIAN_BITFIELD)
......
......@@ -27,6 +27,7 @@
extern u8 irq_vector[NR_IRQ_VECTORS];
#define IO_APIC_VECTOR(irq) (irq_vector[irq])
#define AUTO_ASSIGN -1
extern void (*interrupt[NR_IRQS])(void);
......
/*
* Copyright (C) 2003-2004 Intel
* Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
*/
#ifndef ASM_MSI_H
#define ASM_MSI_H
#include <asm/desc.h>
#include <mach_apic.h>
#define LAST_DEVICE_VECTOR 232
#define MSI_DEST_MODE MSI_LOGICAL_MODE
#define MSI_TARGET_CPU_SHIFT 12
#ifdef CONFIG_SMP
#define MSI_TARGET_CPU logical_smp_processor_id()
#else
#define MSI_TARGET_CPU TARGET_CPUS
#endif
#endif /* ASM_MSI_H */
......@@ -34,6 +34,8 @@ typedef u8 ia64_vector;
#define IA64_MAX_VECTORED_IRQ 255
#define IA64_NUM_VECTORS 256
#define AUTO_ASSIGN -1
#define IA64_SPURIOUS_INT_VECTOR 0x0f
/*
......@@ -80,7 +82,7 @@ extern unsigned long ipi_base_addr;
extern struct hw_interrupt_type irq_type_ia64_lsapic; /* CPU-internal interrupt controller */
extern int ia64_alloc_vector (void); /* allocate a free vector */
extern int assign_irq_vector (int irq); /* allocate a free vector */
extern void ia64_send_ipi (int cpu, int vector, int delivery_mode, int redirect);
extern void register_percpu_irq (ia64_vector vec, struct irqaction *action);
......
......@@ -12,6 +12,7 @@
*/
#define NR_IRQS 256
#define NR_IRQ_VECTORS NR_IRQS
static __inline__ int
irq_canonicalize (int irq)
......
/*
* Copyright (C) 2003-2004 Intel
* Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
*/
#ifndef ASM_MSI_H
#define ASM_MSI_H
#define FIRST_DEVICE_VECTOR IA64_FIRST_DEVICE_VECTOR
#define LAST_DEVICE_VECTOR IA64_LAST_DEVICE_VECTOR
static inline void set_intr_gate (int nr, void *func) {}
#define IO_APIC_VECTOR(irq) (irq)
#define ack_APIC_irq ia64_eoi
#define irq_desc _irq_desc
#define cpu_mask_to_apicid(mask) cpu_physical_id(first_cpu(mask))
#define MSI_DEST_MODE MSI_PHYSICAL_MODE
#define MSI_TARGET_CPU ((ia64_getreg(_IA64_REG_CR_LID) >> 16) & 0xffff)
#define MSI_TARGET_CPU_SHIFT 4
#endif /* ASM_MSI_H */
/*
* Copyright (C) 2003-2004 Intel
* Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
*/
#ifndef ASM_MSI_H
#define ASM_MSI_H
#include <asm/desc.h>
#define LAST_DEVICE_VECTOR 232
#define MSI_DEST_MODE MSI_LOGICAL_MODE
#define MSI_TARGET_CPU_SHIFT 12
#ifdef CONFIG_SMP
#define MSI_TARGET_CPU logical_smp_processor_id()
#else
#define MSI_TARGET_CPU TARGET_CPUS
#endif
#endif /* ASM_MSI_H */
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