Commit fd58e55f authored by Mark Maule's avatar Mark Maule Committed by Greg Kroah-Hartman

[PATCH] PCI: msi abstractions and support for altix

Abstract portions of the MSI core for platforms that do not use standard
APIC interrupt controllers.  This is implemented through a new arch-specific
msi setup routine, and a set of msi ops which can be set on a per platform
basis.
Signed-off-by: default avatarMark Maule <maule@sgi.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent c34b4c73
......@@ -26,7 +26,11 @@ obj-$(CONFIG_PPC32) += setup-irq.o
obj-$(CONFIG_PPC64) += setup-bus.o
obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o
obj-$(CONFIG_X86_VISWS) += setup-irq.o
obj-$(CONFIG_PCI_MSI) += msi.o
msiobj-y := msi.o msi-apic.o
msiobj-$(CONFIG_IA64_GENERIC) += msi-altix.o
msiobj-$(CONFIG_IA64_SGI_SN2) += msi-altix.o
obj-$(CONFIG_PCI_MSI) += $(msiobj-y)
#
# ACPI Related PCI FW Functions
......
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2006 Silicon Graphics, Inc. All Rights Reserved.
*/
#include <asm/errno.h>
int
sn_msi_init(void)
{
/*
* return error until MSI is supported on altix platforms
*/
return -EINVAL;
}
/*
* MSI hooks for standard x86 apic
*/
#include <linux/pci.h>
#include <linux/irq.h>
#include "msi.h"
/*
* Shifts for APIC-based data
*/
#define MSI_DATA_VECTOR_SHIFT 0
#define MSI_DATA_VECTOR(v) (((u8)v) << MSI_DATA_VECTOR_SHIFT)
#define MSI_DATA_DELIVERY_SHIFT 8
#define MSI_DATA_DELIVERY_FIXED (0 << MSI_DATA_DELIVERY_SHIFT)
#define MSI_DATA_DELIVERY_LOWPRI (1 << MSI_DATA_DELIVERY_SHIFT)
#define MSI_DATA_LEVEL_SHIFT 14
#define MSI_DATA_LEVEL_DEASSERT (0 << MSI_DATA_LEVEL_SHIFT)
#define MSI_DATA_LEVEL_ASSERT (1 << MSI_DATA_LEVEL_SHIFT)
#define MSI_DATA_TRIGGER_SHIFT 15
#define MSI_DATA_TRIGGER_EDGE (0 << MSI_DATA_TRIGGER_SHIFT)
#define MSI_DATA_TRIGGER_LEVEL (1 << MSI_DATA_TRIGGER_SHIFT)
/*
* Shift/mask fields for APIC-based bus address
*/
#define MSI_ADDR_HEADER 0xfee00000
#define MSI_ADDR_DESTID_MASK 0xfff0000f
#define MSI_ADDR_DESTID_CPU(cpu) ((cpu) << MSI_TARGET_CPU_SHIFT)
#define MSI_ADDR_DESTMODE_SHIFT 2
#define MSI_ADDR_DESTMODE_PHYS (0 << MSI_ADDR_DESTMODE_SHIFT)
#define MSI_ADDR_DESTMODE_LOGIC (1 << MSI_ADDR_DESTMODE_SHIFT)
#define MSI_ADDR_REDIRECTION_SHIFT 3
#define MSI_ADDR_REDIRECTION_CPU (0 << MSI_ADDR_REDIRECTION_SHIFT)
#define MSI_ADDR_REDIRECTION_LOWPRI (1 << MSI_ADDR_REDIRECTION_SHIFT)
static void
msi_target_apic(unsigned int vector,
unsigned int dest_cpu,
u32 *address_hi, /* in/out */
u32 *address_lo) /* in/out */
{
u32 addr = *address_lo;
addr &= MSI_ADDR_DESTID_MASK;
addr |= MSI_ADDR_DESTID_CPU(cpu_physical_id(dest_cpu));
*address_lo = addr;
}
static int
msi_setup_apic(struct pci_dev *pdev, /* unused in generic */
unsigned int vector,
u32 *address_hi,
u32 *address_lo,
u32 *data)
{
unsigned long dest_phys_id;
dest_phys_id = cpu_physical_id(first_cpu(cpu_online_map));
*address_hi = 0;
*address_lo = MSI_ADDR_HEADER |
MSI_ADDR_DESTMODE_PHYS |
MSI_ADDR_REDIRECTION_CPU |
MSI_ADDR_DESTID_CPU(dest_phys_id);
*data = MSI_DATA_TRIGGER_EDGE |
MSI_DATA_LEVEL_ASSERT |
MSI_DATA_DELIVERY_FIXED |
MSI_DATA_VECTOR(vector);
return 0;
}
static void
msi_teardown_apic(unsigned int vector)
{
return; /* no-op */
}
/*
* Generic ops used on most IA archs/platforms. Set with msi_register()
*/
struct msi_ops msi_apic_ops = {
.setup = msi_setup_apic,
.teardown = msi_teardown_apic,
.target = msi_target_apic,
};
This diff is collapsed.
......@@ -6,6 +6,68 @@
#ifndef MSI_H
#define MSI_H
/*
* MSI operation vector. Used by the msi core code (drivers/pci/msi.c)
* to abstract platform-specific tasks relating to MSI address generation
* and resource management.
*/
struct msi_ops {
/**
* setup - generate an MSI bus address and data for a given vector
* @pdev: PCI device context (in)
* @vector: vector allocated by the msi core (in)
* @addr_hi: upper 32 bits of PCI bus MSI address (out)
* @addr_lo: lower 32 bits of PCI bus MSI address (out)
* @data: MSI data payload (out)
*
* Description: The setup op is used to generate a PCI bus addres and
* data which the msi core will program into the card MSI capability
* registers. The setup routine is responsible for picking an initial
* cpu to target the MSI at. The setup routine is responsible for
* examining pdev to determine the MSI capabilities of the card and
* generating a suitable address/data. The setup routine is
* responsible for allocating and tracking any system resources it
* needs to route the MSI to the cpu it picks, and for associating
* those resources with the passed in vector.
*
* Returns 0 if the MSI address/data was successfully setup.
**/
int (*setup) (struct pci_dev *pdev, unsigned int vector,
u32 *addr_hi, u32 *addr_lo, u32 *data);
/**
* teardown - release resources allocated by setup
* @vector: vector context for resources (in)
*
* Description: The teardown op is used to release any resources
* that were allocated in the setup routine associated with the passed
* in vector.
**/
void (*teardown) (unsigned int vector);
/**
* target - retarget an MSI at a different cpu
* @vector: vector context for resources (in)
* @cpu: new cpu to direct vector at (in)
* @addr_hi: new value of PCI bus upper 32 bits (in/out)
* @addr_lo: new value of PCI bus lower 32 bits (in/out)
*
* Description: The target op is used to redirect an MSI vector
* at a different cpu. addr_hi/addr_lo coming in are the existing
* values that the MSI core has programmed into the card. The
* target code is responsible for freeing any resources (if any)
* associated with the old address, and generating a new PCI bus
* addr_hi/addr_lo that will redirect the vector at the indicated cpu.
**/
void (*target) (unsigned int vector, unsigned int cpu,
u32 *addr_hi, u32 *addr_lo);
};
extern int msi_register(struct msi_ops *ops);
#include <asm/msi.h>
/*
......@@ -63,67 +125,6 @@ extern int pci_vector_resources(int last, int nr_released);
#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_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
struct msg_data {
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u32 vector : 8;
__u32 delivery_mode : 3; /* 000b: FIXED | 001b: lowest prior */
__u32 reserved_1 : 3;
__u32 level : 1; /* 0: deassert | 1: assert */
__u32 trigger : 1; /* 0: edge | 1: level */
__u32 reserved_2 : 16;
#elif defined(__BIG_ENDIAN_BITFIELD)
__u32 reserved_2 : 16;
__u32 trigger : 1; /* 0: edge | 1: level */
__u32 level : 1; /* 0: deassert | 1: assert */
__u32 reserved_1 : 3;
__u32 delivery_mode : 3; /* 000b: FIXED | 001b: lowest prior */
__u32 vector : 8;
#else
#error "Bitfield endianness not defined! Check your byteorder.h"
#endif
} __attribute__ ((packed));
struct msg_address {
union {
struct {
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u32 reserved_1 : 2;
__u32 dest_mode : 1; /*0:physic | 1:logic */
__u32 redirection_hint: 1; /*0: dedicated CPU
1: lowest priority */
__u32 reserved_2 : 4;
__u32 dest_id : 24; /* Destination ID */
#elif defined(__BIG_ENDIAN_BITFIELD)
__u32 dest_id : 24; /* Destination ID */
__u32 reserved_2 : 4;
__u32 redirection_hint: 1; /*0: dedicated CPU
1: lowest priority */
__u32 dest_mode : 1; /*0:physic | 1:logic */
__u32 reserved_1 : 2;
#else
#error "Bitfield endianness not defined! Check your byteorder.h"
#endif
}u;
__u32 value;
}lo_address;
__u32 hi_address;
} __attribute__ ((packed));
struct msi_desc {
struct {
__u8 type : 5; /* {0: unused, 5h:MSI, 11h:MSI-X} */
......@@ -132,7 +133,7 @@ struct msi_desc {
__u8 reserved: 1; /* reserved */
__u8 entry_nr; /* specific enabled entry */
__u8 default_vector; /* default pre-assigned vector */
__u8 current_cpu; /* current destination cpu */
__u8 unused; /* formerly unused destination cpu*/
}msi_attrib;
struct {
......@@ -142,6 +143,14 @@ struct msi_desc {
void __iomem *mask_base;
struct pci_dev *dev;
#ifdef CONFIG_PM
/* PM save area for MSIX address/data */
u32 address_hi_save;
u32 address_lo_save;
u32 data_save;
#endif
};
#endif /* MSI_H */
......@@ -12,4 +12,12 @@
#define LAST_DEVICE_VECTOR 232
#define MSI_TARGET_CPU_SHIFT 12
extern struct msi_ops msi_apic_ops;
static inline int msi_arch_init(void)
{
msi_register(&msi_apic_ops);
return 0;
}
#endif /* ASM_MSI_H */
......@@ -75,6 +75,7 @@ typedef unsigned char ia64_mv_readb_relaxed_t (const volatile void __iomem *);
typedef unsigned short ia64_mv_readw_relaxed_t (const volatile void __iomem *);
typedef unsigned int ia64_mv_readl_relaxed_t (const volatile void __iomem *);
typedef unsigned long ia64_mv_readq_relaxed_t (const volatile void __iomem *);
typedef int ia64_mv_msi_init_t (void);
static inline void
machvec_noop (void)
......@@ -153,6 +154,7 @@ extern void machvec_tlb_migrate_finish (struct mm_struct *);
# define platform_readl_relaxed ia64_mv.readl_relaxed
# define platform_readq_relaxed ia64_mv.readq_relaxed
# define platform_migrate ia64_mv.migrate
# define platform_msi_init ia64_mv.msi_init
# endif
/* __attribute__((__aligned__(16))) is required to make size of the
......@@ -202,6 +204,7 @@ struct ia64_machine_vector {
ia64_mv_readl_relaxed_t *readl_relaxed;
ia64_mv_readq_relaxed_t *readq_relaxed;
ia64_mv_migrate_t *migrate;
ia64_mv_msi_init_t *msi_init;
} __attribute__((__aligned__(16))); /* align attrib? see above comment */
#define MACHVEC_INIT(name) \
......@@ -247,6 +250,7 @@ struct ia64_machine_vector {
platform_readl_relaxed, \
platform_readq_relaxed, \
platform_migrate, \
platform_msi_init, \
}
extern struct ia64_machine_vector ia64_mv;
......@@ -400,5 +404,8 @@ extern int ia64_pci_legacy_write(struct pci_bus *bus, u16 port, u32 val, u8 size
#ifndef platform_migrate
# define platform_migrate machvec_noop_task
#endif
#ifndef platform_msi_init
# define platform_msi_init ((ia64_mv_msi_init_t*)NULL)
#endif
#endif /* _ASM_IA64_MACHVEC_H */
......@@ -67,6 +67,8 @@ extern ia64_mv_dma_sync_sg_for_device sn_dma_sync_sg_for_device;
extern ia64_mv_dma_mapping_error sn_dma_mapping_error;
extern ia64_mv_dma_supported sn_dma_supported;
extern ia64_mv_migrate_t sn_migrate;
extern ia64_mv_msi_init_t sn_msi_init;
/*
* This stuff has dual use!
......@@ -117,6 +119,11 @@ extern ia64_mv_migrate_t sn_migrate;
#define platform_dma_mapping_error sn_dma_mapping_error
#define platform_dma_supported sn_dma_supported
#define platform_migrate sn_migrate
#ifdef CONFIG_PCI_MSI
#define platform_msi_init sn_msi_init
#else
#define platform_msi_init ((ia64_mv_msi_init_t*)NULL)
#endif
#include <asm/sn/io.h>
......
......@@ -14,4 +14,16 @@ static inline void set_intr_gate (int nr, void *func) {}
#define ack_APIC_irq ia64_eoi
#define MSI_TARGET_CPU_SHIFT 4
extern struct msi_ops msi_apic_ops;
static inline int msi_arch_init(void)
{
if (platform_msi_init)
return platform_msi_init();
/* default ops for most ia64 platforms */
msi_register(&msi_apic_ops);
return 0;
}
#endif /* ASM_MSI_H */
......@@ -13,4 +13,12 @@
#define LAST_DEVICE_VECTOR 232
#define MSI_TARGET_CPU_SHIFT 12
extern struct msi_ops msi_apic_ops;
static inline int msi_arch_init(void)
{
msi_register(&msi_apic_ops);
return 0;
}
#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