Commit 495c0217 authored by Mahesh Rajashekhara's avatar Mahesh Rajashekhara Committed by James Bottomley

aacraid: MSI-x support

Signed-off-by: default avatarMahesh Rajashekhara <Mahesh.Rajashekhara@pmcs.com>
Reviewed-by: default avatarHannes Reinecke <hare@suse.de>
Reviewed-by: default avatarMurthy Bhat <Murthy.Bhat@pmcs.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Odin.com>
parent b836439f
...@@ -6,11 +6,61 @@ ...@@ -6,11 +6,61 @@
#define nblank(x) _nblank(x)[0] #define nblank(x) _nblank(x)[0]
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/pci.h>
/*------------------------------------------------------------------------------ /*------------------------------------------------------------------------------
* D E F I N E S * D E F I N E S
*----------------------------------------------------------------------------*/ *----------------------------------------------------------------------------*/
#define AAC_MAX_MSIX 32 /* vectors */
#define AAC_PCI_MSI_ENABLE 0x8000
enum {
AAC_ENABLE_INTERRUPT = 0x0,
AAC_DISABLE_INTERRUPT,
AAC_ENABLE_MSIX,
AAC_DISABLE_MSIX,
AAC_CLEAR_AIF_BIT,
AAC_CLEAR_SYNC_BIT,
AAC_ENABLE_INTX
};
#define AAC_INT_MODE_INTX (1<<0)
#define AAC_INT_MODE_MSI (1<<1)
#define AAC_INT_MODE_AIF (1<<2)
#define AAC_INT_MODE_SYNC (1<<3)
#define AAC_INT_ENABLE_TYPE1_INTX 0xfffffffb
#define AAC_INT_ENABLE_TYPE1_MSIX 0xfffffffa
#define AAC_INT_DISABLE_ALL 0xffffffff
/* Bit definitions in IOA->Host Interrupt Register */
#define PMC_TRANSITION_TO_OPERATIONAL (1<<31)
#define PMC_IOARCB_TRANSFER_FAILED (1<<28)
#define PMC_IOA_UNIT_CHECK (1<<27)
#define PMC_NO_HOST_RRQ_FOR_CMD_RESPONSE (1<<26)
#define PMC_CRITICAL_IOA_OP_IN_PROGRESS (1<<25)
#define PMC_IOARRIN_LOST (1<<4)
#define PMC_SYSTEM_BUS_MMIO_ERROR (1<<3)
#define PMC_IOA_PROCESSOR_IN_ERROR_STATE (1<<2)
#define PMC_HOST_RRQ_VALID (1<<1)
#define PMC_OPERATIONAL_STATUS (1<<31)
#define PMC_ALLOW_MSIX_VECTOR0 (1<<0)
#define PMC_IOA_ERROR_INTERRUPTS (PMC_IOARCB_TRANSFER_FAILED | \
PMC_IOA_UNIT_CHECK | \
PMC_NO_HOST_RRQ_FOR_CMD_RESPONSE | \
PMC_IOARRIN_LOST | \
PMC_SYSTEM_BUS_MMIO_ERROR | \
PMC_IOA_PROCESSOR_IN_ERROR_STATE)
#define PMC_ALL_INTERRUPT_BITS (PMC_IOA_ERROR_INTERRUPTS | \
PMC_HOST_RRQ_VALID | \
PMC_TRANSITION_TO_OPERATIONAL | \
PMC_ALLOW_MSIX_VECTOR0)
#define PMC_GLOBAL_INT_BIT2 0x00000004
#define PMC_GLOBAL_INT_BIT0 0x00000001
#ifndef AAC_DRIVER_BUILD #ifndef AAC_DRIVER_BUILD
# define AAC_DRIVER_BUILD 30300 # define AAC_DRIVER_BUILD 30300
# define AAC_DRIVER_BRANCH "-ms" # define AAC_DRIVER_BRANCH "-ms"
...@@ -36,6 +86,7 @@ ...@@ -36,6 +86,7 @@
#define CONTAINER_TO_ID(cont) (cont) #define CONTAINER_TO_ID(cont) (cont)
#define CONTAINER_TO_LUN(cont) (0) #define CONTAINER_TO_LUN(cont) (0)
#define PMC_DEVICE_S6 0x28b
#define PMC_DEVICE_S7 0x28c #define PMC_DEVICE_S7 0x28c
#define PMC_DEVICE_S8 0x28d #define PMC_DEVICE_S8 0x28d
#define PMC_DEVICE_S9 0x28f #define PMC_DEVICE_S9 0x28f
...@@ -434,7 +485,7 @@ enum fib_xfer_state { ...@@ -434,7 +485,7 @@ enum fib_xfer_state {
struct aac_init struct aac_init
{ {
__le32 InitStructRevision; __le32 InitStructRevision;
__le32 MiniPortRevision; __le32 Sa_MSIXVectors;
__le32 fsrev; __le32 fsrev;
__le32 CommHeaderAddress; __le32 CommHeaderAddress;
__le32 FastIoCommAreaAddress; __le32 FastIoCommAreaAddress;
...@@ -755,7 +806,8 @@ struct rkt_registers { ...@@ -755,7 +806,8 @@ struct rkt_registers {
struct src_mu_registers { struct src_mu_registers {
/* PCI*| Name */ /* PCI*| Name */
__le32 reserved0[8]; /* 00h | Reserved */ __le32 reserved0[6]; /* 00h | Reserved */
__le32 IOAR[2]; /* 18h | IOA->host interrupt register */
__le32 IDR; /* 20h | Inbound Doorbell Register */ __le32 IDR; /* 20h | Inbound Doorbell Register */
__le32 IISR; /* 24h | Inbound Int. Status Register */ __le32 IISR; /* 24h | Inbound Int. Status Register */
__le32 reserved1[3]; /* 28h | Reserved */ __le32 reserved1[3]; /* 28h | Reserved */
...@@ -767,17 +819,18 @@ struct src_mu_registers { ...@@ -767,17 +819,18 @@ struct src_mu_registers {
__le32 OMR; /* bch | Outbound Message Register */ __le32 OMR; /* bch | Outbound Message Register */
__le32 IQ_L; /* c0h | Inbound Queue (Low address) */ __le32 IQ_L; /* c0h | Inbound Queue (Low address) */
__le32 IQ_H; /* c4h | Inbound Queue (High address) */ __le32 IQ_H; /* c4h | Inbound Queue (High address) */
__le32 ODR_MSI; /* c8h | MSI register for sync./AIF */
}; };
struct src_registers { struct src_registers {
struct src_mu_registers MUnit; /* 00h - c7h */ struct src_mu_registers MUnit; /* 00h - cbh */
union { union {
struct { struct {
__le32 reserved1[130790]; /* c8h - 7fc5fh */ __le32 reserved1[130789]; /* cch - 7fc5fh */
struct src_inbound IndexRegs; /* 7fc60h */ struct src_inbound IndexRegs; /* 7fc60h */
} tupelo; } tupelo;
struct { struct {
__le32 reserved1[974]; /* c8h - fffh */ __le32 reserved1[973]; /* cch - fffh */
struct src_inbound IndexRegs; /* 1000h */ struct src_inbound IndexRegs; /* 1000h */
} denali; } denali;
} u; } u;
...@@ -1029,6 +1082,11 @@ struct aac_bus_info_response { ...@@ -1029,6 +1082,11 @@ struct aac_bus_info_response {
#define AAC_OPT_NEW_COMM_TYPE3 cpu_to_le32(1<<30) #define AAC_OPT_NEW_COMM_TYPE3 cpu_to_le32(1<<30)
#define AAC_OPT_NEW_COMM_TYPE4 cpu_to_le32(1<<31) #define AAC_OPT_NEW_COMM_TYPE4 cpu_to_le32(1<<31)
/* MSIX context */
struct aac_msix_ctx {
int vector_no;
struct aac_dev *dev;
};
struct aac_dev struct aac_dev
{ {
...@@ -1084,8 +1142,10 @@ struct aac_dev ...@@ -1084,8 +1142,10 @@ struct aac_dev
* if AAC_COMM_MESSAGE_TYPE1 */ * if AAC_COMM_MESSAGE_TYPE1 */
dma_addr_t host_rrq_pa; /* phys. address */ dma_addr_t host_rrq_pa; /* phys. address */
u32 host_rrq_idx; /* index into rrq buffer */ /* index into rrq buffer */
u32 host_rrq_idx[AAC_MAX_MSIX];
atomic_t rrq_outstanding[AAC_MAX_MSIX];
u32 fibs_pushed_no;
struct pci_dev *pdev; /* Our PCI interface */ struct pci_dev *pdev; /* Our PCI interface */
void * printfbuf; /* pointer to buffer used for printf's from the adapter */ void * printfbuf; /* pointer to buffer used for printf's from the adapter */
void * comm_addr; /* Base address of Comm area */ void * comm_addr; /* Base address of Comm area */
...@@ -1154,6 +1214,11 @@ struct aac_dev ...@@ -1154,6 +1214,11 @@ struct aac_dev
int sync_mode; int sync_mode;
struct fib *sync_fib; struct fib *sync_fib;
struct list_head sync_fib_list; struct list_head sync_fib_list;
u32 max_msix; /* max. MSI-X vectors */
u32 vector_cap; /* MSI-X vector capab.*/
int msi_enabled; /* MSI/MSI-X enabled */
struct msix_entry msixentry[AAC_MAX_MSIX];
struct aac_msix_ctx aac_msix[AAC_MAX_MSIX]; /* context */
}; };
#define aac_adapter_interrupt(dev) \ #define aac_adapter_interrupt(dev) \
...@@ -2036,6 +2101,7 @@ void aac_consumer_free(struct aac_dev * dev, struct aac_queue * q, u32 qnum); ...@@ -2036,6 +2101,7 @@ void aac_consumer_free(struct aac_dev * dev, struct aac_queue * q, u32 qnum);
int aac_fib_complete(struct fib * context); int aac_fib_complete(struct fib * context);
#define fib_data(fibctx) ((void *)(fibctx)->hw_fib_va->data) #define fib_data(fibctx) ((void *)(fibctx)->hw_fib_va->data)
struct aac_dev *aac_init_adapter(struct aac_dev *dev); struct aac_dev *aac_init_adapter(struct aac_dev *dev);
void aac_src_access_devreg(struct aac_dev *dev, int mode);
int aac_get_config_status(struct aac_dev *dev, int commit_flag); int aac_get_config_status(struct aac_dev *dev, int commit_flag);
int aac_get_containers(struct aac_dev *dev); int aac_get_containers(struct aac_dev *dev);
int aac_scsi_cmd(struct scsi_cmnd *cmd); int aac_scsi_cmd(struct scsi_cmnd *cmd);
......
...@@ -43,6 +43,8 @@ ...@@ -43,6 +43,8 @@
#include "aacraid.h" #include "aacraid.h"
static void aac_define_int_mode(struct aac_dev *dev);
struct aac_common aac_config = { struct aac_common aac_config = {
.irq_mod = 1 .irq_mod = 1
}; };
...@@ -91,7 +93,7 @@ static int aac_alloc_comm(struct aac_dev *dev, void **commaddr, unsigned long co ...@@ -91,7 +93,7 @@ static int aac_alloc_comm(struct aac_dev *dev, void **commaddr, unsigned long co
init->InitStructRevision = cpu_to_le32(ADAPTER_INIT_STRUCT_REVISION); init->InitStructRevision = cpu_to_le32(ADAPTER_INIT_STRUCT_REVISION);
if (dev->max_fib_size != sizeof(struct hw_fib)) if (dev->max_fib_size != sizeof(struct hw_fib))
init->InitStructRevision = cpu_to_le32(ADAPTER_INIT_STRUCT_REVISION_4); init->InitStructRevision = cpu_to_le32(ADAPTER_INIT_STRUCT_REVISION_4);
init->MiniPortRevision = cpu_to_le32(Sa_MINIPORT_REVISION); init->Sa_MSIXVectors = cpu_to_le32(Sa_MINIPORT_REVISION);
init->fsrev = cpu_to_le32(dev->fsrev); init->fsrev = cpu_to_le32(dev->fsrev);
/* /*
...@@ -140,7 +142,8 @@ static int aac_alloc_comm(struct aac_dev *dev, void **commaddr, unsigned long co ...@@ -140,7 +142,8 @@ static int aac_alloc_comm(struct aac_dev *dev, void **commaddr, unsigned long co
INITFLAGS_NEW_COMM_TYPE2_SUPPORTED | INITFLAGS_FAST_JBOD_SUPPORTED); INITFLAGS_NEW_COMM_TYPE2_SUPPORTED | INITFLAGS_FAST_JBOD_SUPPORTED);
init->HostRRQ_AddrHigh = cpu_to_le32((u32)((u64)dev->host_rrq_pa >> 32)); init->HostRRQ_AddrHigh = cpu_to_le32((u32)((u64)dev->host_rrq_pa >> 32));
init->HostRRQ_AddrLow = cpu_to_le32((u32)(dev->host_rrq_pa & 0xffffffff)); init->HostRRQ_AddrLow = cpu_to_le32((u32)(dev->host_rrq_pa & 0xffffffff));
init->MiniPortRevision = cpu_to_le32(0L); /* number of MSI-X */ /* number of MSI-X */
init->Sa_MSIXVectors = cpu_to_le32(dev->max_msix);
dprintk((KERN_WARNING"aacraid: New Comm Interface type2 enabled\n")); dprintk((KERN_WARNING"aacraid: New Comm Interface type2 enabled\n"));
} }
...@@ -228,6 +231,11 @@ int aac_send_shutdown(struct aac_dev * dev) ...@@ -228,6 +231,11 @@ int aac_send_shutdown(struct aac_dev * dev)
/* FIB should be freed only after getting the response from the F/W */ /* FIB should be freed only after getting the response from the F/W */
if (status != -ERESTARTSYS) if (status != -ERESTARTSYS)
aac_fib_free(fibctx); aac_fib_free(fibctx);
if ((dev->pdev->device == PMC_DEVICE_S7 ||
dev->pdev->device == PMC_DEVICE_S8 ||
dev->pdev->device == PMC_DEVICE_S9) &&
dev->msi_enabled)
aac_src_access_devreg(dev, AAC_ENABLE_INTX);
return status; return status;
} }
...@@ -388,6 +396,8 @@ struct aac_dev *aac_init_adapter(struct aac_dev *dev) ...@@ -388,6 +396,8 @@ struct aac_dev *aac_init_adapter(struct aac_dev *dev)
} }
} }
} }
dev->max_msix = 0;
dev->msi_enabled = 0;
if ((!aac_adapter_sync_cmd(dev, GET_COMM_PREFERRED_SETTINGS, if ((!aac_adapter_sync_cmd(dev, GET_COMM_PREFERRED_SETTINGS,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
status+0, status+1, status+2, status+3, status+4)) status+0, status+1, status+2, status+3, status+4))
...@@ -461,6 +471,11 @@ struct aac_dev *aac_init_adapter(struct aac_dev *dev) ...@@ -461,6 +471,11 @@ struct aac_dev *aac_init_adapter(struct aac_dev *dev)
if (host->can_queue > AAC_NUM_IO_FIB) if (host->can_queue > AAC_NUM_IO_FIB)
host->can_queue = AAC_NUM_IO_FIB; host->can_queue = AAC_NUM_IO_FIB;
if (dev->pdev->device == PMC_DEVICE_S6 ||
dev->pdev->device == PMC_DEVICE_S7 ||
dev->pdev->device == PMC_DEVICE_S8 ||
dev->pdev->device == PMC_DEVICE_S9)
aac_define_int_mode(dev);
/* /*
* Ok now init the communication subsystem * Ok now init the communication subsystem
*/ */
...@@ -489,4 +504,78 @@ struct aac_dev *aac_init_adapter(struct aac_dev *dev) ...@@ -489,4 +504,78 @@ struct aac_dev *aac_init_adapter(struct aac_dev *dev)
return dev; return dev;
} }
static void aac_define_int_mode(struct aac_dev *dev)
{
int i, msi_count;
/* max. vectors from GET_COMM_PREFERRED_SETTINGS */
if (dev->max_msix == 0 ||
dev->pdev->device == PMC_DEVICE_S6 ||
dev->sync_mode) {
dev->max_msix = 1;
dev->vector_cap =
dev->scsi_host_ptr->can_queue +
AAC_NUM_MGT_FIB;
return;
}
msi_count = min(dev->max_msix,
(unsigned int)num_online_cpus());
dev->max_msix = msi_count;
if (msi_count > AAC_MAX_MSIX)
msi_count = AAC_MAX_MSIX;
for (i = 0; i < msi_count; i++)
dev->msixentry[i].entry = i;
if (msi_count > 1 &&
pci_find_capability(dev->pdev, PCI_CAP_ID_MSIX)) {
i = pci_enable_msix(dev->pdev,
dev->msixentry,
msi_count);
/* Check how many MSIX vectors are allocated */
if (i >= 0) {
dev->msi_enabled = 1;
if (i) {
msi_count = i;
if (pci_enable_msix(dev->pdev,
dev->msixentry,
msi_count)) {
dev->msi_enabled = 0;
printk(KERN_ERR "%s%d: MSIX not supported!! Will try MSI 0x%x.\n",
dev->name, dev->id, i);
}
}
} else {
dev->msi_enabled = 0;
printk(KERN_ERR "%s%d: MSIX not supported!! Will try MSI 0x%x.\n",
dev->name, dev->id, i);
}
}
if (!dev->msi_enabled) {
msi_count = 1;
i = pci_enable_msi(dev->pdev);
if (!i) {
dev->msi_enabled = 1;
dev->msi = 1;
} else {
printk(KERN_ERR "%s%d: MSI not supported!! Will try INTx 0x%x.\n",
dev->name, dev->id, i);
}
}
if (!dev->msi_enabled)
dev->max_msix = msi_count = 1;
else {
if (dev->max_msix > msi_count)
dev->max_msix = msi_count;
}
dev->vector_cap =
(dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB) /
msi_count;
}
...@@ -868,7 +868,7 @@ void aac_printf(struct aac_dev *dev, u32 val) ...@@ -868,7 +868,7 @@ void aac_printf(struct aac_dev *dev, u32 val)
* dispatches it to the appropriate routine for handling. * dispatches it to the appropriate routine for handling.
*/ */
#define AIF_SNIFF_TIMEOUT (30*HZ) #define AIF_SNIFF_TIMEOUT (500*HZ)
static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr) static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr)
{ {
struct hw_fib * hw_fib = fibptr->hw_fib_va; struct hw_fib * hw_fib = fibptr->hw_fib_va;
...@@ -1251,7 +1251,7 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr) ...@@ -1251,7 +1251,7 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr)
static int _aac_reset_adapter(struct aac_dev *aac, int forced) static int _aac_reset_adapter(struct aac_dev *aac, int forced)
{ {
int index, quirks; int index, quirks;
int retval; int retval, i;
struct Scsi_Host *host; struct Scsi_Host *host;
struct scsi_device *dev; struct scsi_device *dev;
struct scsi_cmnd *command; struct scsi_cmnd *command;
...@@ -1319,7 +1319,21 @@ static int _aac_reset_adapter(struct aac_dev *aac, int forced) ...@@ -1319,7 +1319,21 @@ static int _aac_reset_adapter(struct aac_dev *aac, int forced)
aac->comm_phys = 0; aac->comm_phys = 0;
kfree(aac->queues); kfree(aac->queues);
aac->queues = NULL; aac->queues = NULL;
if (aac->pdev->device == PMC_DEVICE_S6 ||
aac->pdev->device == PMC_DEVICE_S7 ||
aac->pdev->device == PMC_DEVICE_S8 ||
aac->pdev->device == PMC_DEVICE_S9) {
if (aac->max_msix > 1) {
for (i = 0; i < aac->max_msix; i++)
free_irq(aac->msixentry[i].vector,
&(aac->aac_msix[i]));
pci_disable_msix(aac->pdev);
} else {
free_irq(aac->pdev->irq, &(aac->aac_msix[0]));
}
} else {
free_irq(aac->pdev->irq, aac); free_irq(aac->pdev->irq, aac);
}
if (aac->msi) if (aac->msi)
pci_disable_msi(aac->pdev); pci_disable_msi(aac->pdev);
kfree(aac->fsa_dev); kfree(aac->fsa_dev);
......
...@@ -389,8 +389,13 @@ unsigned int aac_intr_normal(struct aac_dev *dev, u32 index, ...@@ -389,8 +389,13 @@ unsigned int aac_intr_normal(struct aac_dev *dev, u32 index,
* NOTE: we cannot touch the fib after this * NOTE: we cannot touch the fib after this
* call, because it may have been deallocated. * call, because it may have been deallocated.
*/ */
if (likely(fib->callback && fib->callback_data)) {
fib->flags &= FIB_CONTEXT_FLAG_FASTRESP; fib->flags &= FIB_CONTEXT_FLAG_FASTRESP;
fib->callback(fib->callback_data, fib); fib->callback(fib->callback_data, fib);
} else {
aac_fib_complete(fib);
aac_fib_free(fib);
}
} else { } else {
unsigned long flagv; unsigned long flagv;
dprintk((KERN_INFO "event_wait up\n")); dprintk((KERN_INFO "event_wait up\n"));
......
...@@ -1082,6 +1082,8 @@ static struct scsi_host_template aac_driver_template = { ...@@ -1082,6 +1082,8 @@ static struct scsi_host_template aac_driver_template = {
static void __aac_shutdown(struct aac_dev * aac) static void __aac_shutdown(struct aac_dev * aac)
{ {
int i;
if (aac->aif_thread) { if (aac->aif_thread) {
int i; int i;
/* Clear out events first */ /* Clear out events first */
...@@ -1095,9 +1097,25 @@ static void __aac_shutdown(struct aac_dev * aac) ...@@ -1095,9 +1097,25 @@ static void __aac_shutdown(struct aac_dev * aac)
} }
aac_send_shutdown(aac); aac_send_shutdown(aac);
aac_adapter_disable_int(aac); aac_adapter_disable_int(aac);
if (aac->pdev->device == PMC_DEVICE_S6 ||
aac->pdev->device == PMC_DEVICE_S7 ||
aac->pdev->device == PMC_DEVICE_S8 ||
aac->pdev->device == PMC_DEVICE_S9) {
if (aac->max_msix > 1) {
for (i = 0; i < aac->max_msix; i++)
free_irq(aac->msixentry[i].vector,
&(aac->aac_msix[i]));
} else {
free_irq(aac->pdev->irq,
&(aac->aac_msix[0]));
}
} else {
free_irq(aac->pdev->irq, aac); free_irq(aac->pdev->irq, aac);
}
if (aac->msi) if (aac->msi)
pci_disable_msi(aac->pdev); pci_disable_msi(aac->pdev);
else if (aac->max_msix > 1)
pci_disable_msix(aac->pdev);
} }
static int aac_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) static int aac_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
......
...@@ -44,66 +44,61 @@ ...@@ -44,66 +44,61 @@
#include "aacraid.h" #include "aacraid.h"
static irqreturn_t aac_src_intr_message(int irq, void *dev_id) static int aac_src_get_sync_status(struct aac_dev *dev);
irqreturn_t aac_src_intr_message(int irq, void *dev_id)
{ {
struct aac_dev *dev = dev_id; struct aac_msix_ctx *ctx;
struct aac_dev *dev;
unsigned long bellbits, bellbits_shifted; unsigned long bellbits, bellbits_shifted;
int our_interrupt = 0; int vector_no;
int isFastResponse; int isFastResponse, mode;
u32 index, handle; u32 index, handle;
ctx = (struct aac_msix_ctx *)dev_id;
dev = ctx->dev;
vector_no = ctx->vector_no;
if (dev->msi_enabled) {
mode = AAC_INT_MODE_MSI;
if (vector_no == 0) {
bellbits = src_readl(dev, MUnit.ODR_MSI);
if (bellbits & 0x40000)
mode |= AAC_INT_MODE_AIF;
if (bellbits & 0x1000)
mode |= AAC_INT_MODE_SYNC;
}
} else {
mode = AAC_INT_MODE_INTX;
bellbits = src_readl(dev, MUnit.ODR_R); bellbits = src_readl(dev, MUnit.ODR_R);
if (bellbits & PmDoorBellResponseSent) { if (bellbits & PmDoorBellResponseSent) {
bellbits = PmDoorBellResponseSent; bellbits = PmDoorBellResponseSent;
/* handle async. status */
src_writel(dev, MUnit.ODR_C, bellbits); src_writel(dev, MUnit.ODR_C, bellbits);
src_readl(dev, MUnit.ODR_C); src_readl(dev, MUnit.ODR_C);
our_interrupt = 1;
index = dev->host_rrq_idx;
for (;;) {
isFastResponse = 0;
/* remove toggle bit (31) */
handle = le32_to_cpu(dev->host_rrq[index]) & 0x7fffffff;
/* check fast response bit (30) */
if (handle & 0x40000000)
isFastResponse = 1;
handle &= 0x0000ffff;
if (handle == 0)
break;
aac_intr_normal(dev, handle-1, 0, isFastResponse, NULL);
dev->host_rrq[index++] = 0;
if (index == dev->scsi_host_ptr->can_queue +
AAC_NUM_MGT_FIB)
index = 0;
dev->host_rrq_idx = index;
}
} else { } else {
bellbits_shifted = (bellbits >> SRC_ODR_SHIFT); bellbits_shifted = (bellbits >> SRC_ODR_SHIFT);
if (bellbits_shifted & DoorBellAifPending) {
src_writel(dev, MUnit.ODR_C, bellbits); src_writel(dev, MUnit.ODR_C, bellbits);
src_readl(dev, MUnit.ODR_C); src_readl(dev, MUnit.ODR_C);
our_interrupt = 1;
/* handle AIF */ if (bellbits_shifted & DoorBellAifPending)
aac_intr_normal(dev, 0, 2, 0, NULL); mode |= AAC_INT_MODE_AIF;
} else if (bellbits_shifted & OUTBOUNDDOORBELL_0) { else if (bellbits_shifted & OUTBOUNDDOORBELL_0)
mode |= AAC_INT_MODE_SYNC;
}
}
if (mode & AAC_INT_MODE_SYNC) {
unsigned long sflags; unsigned long sflags;
struct list_head *entry; struct list_head *entry;
int send_it = 0; int send_it = 0;
extern int aac_sync_mode; extern int aac_sync_mode;
if (!aac_sync_mode && !dev->msi_enabled) {
src_writel(dev, MUnit.ODR_C, bellbits); src_writel(dev, MUnit.ODR_C, bellbits);
src_readl(dev, MUnit.ODR_C); src_readl(dev, MUnit.ODR_C);
if (!aac_sync_mode) {
src_writel(dev, MUnit.ODR_C, bellbits);
src_readl(dev, MUnit.ODR_C);
our_interrupt = 1;
} }
if (dev->sync_fib) { if (dev->sync_fib) {
our_interrupt = 1;
if (dev->sync_fib->callback) if (dev->sync_fib->callback)
dev->sync_fib->callback(dev->sync_fib->callback_data, dev->sync_fib->callback(dev->sync_fib->callback_data,
dev->sync_fib); dev->sync_fib);
...@@ -112,11 +107,14 @@ static irqreturn_t aac_src_intr_message(int irq, void *dev_id) ...@@ -112,11 +107,14 @@ static irqreturn_t aac_src_intr_message(int irq, void *dev_id)
dev->management_fib_count--; dev->management_fib_count--;
up(&dev->sync_fib->event_wait); up(&dev->sync_fib->event_wait);
} }
spin_unlock_irqrestore(&dev->sync_fib->event_lock, sflags); spin_unlock_irqrestore(&dev->sync_fib->event_lock,
sflags);
spin_lock_irqsave(&dev->sync_lock, sflags); spin_lock_irqsave(&dev->sync_lock, sflags);
if (!list_empty(&dev->sync_fib_list)) { if (!list_empty(&dev->sync_fib_list)) {
entry = dev->sync_fib_list.next; entry = dev->sync_fib_list.next;
dev->sync_fib = list_entry(entry, struct fib, fiblink); dev->sync_fib = list_entry(entry,
struct fib,
fiblink);
list_del(entry); list_del(entry);
send_it = 1; send_it = 1;
} else { } else {
...@@ -125,17 +123,49 @@ static irqreturn_t aac_src_intr_message(int irq, void *dev_id) ...@@ -125,17 +123,49 @@ static irqreturn_t aac_src_intr_message(int irq, void *dev_id)
spin_unlock_irqrestore(&dev->sync_lock, sflags); spin_unlock_irqrestore(&dev->sync_lock, sflags);
if (send_it) { if (send_it) {
aac_adapter_sync_cmd(dev, SEND_SYNCHRONOUS_FIB, aac_adapter_sync_cmd(dev, SEND_SYNCHRONOUS_FIB,
(u32)dev->sync_fib->hw_fib_pa, 0, 0, 0, 0, 0, (u32)dev->sync_fib->hw_fib_pa,
0, 0, 0, 0, 0,
NULL, NULL, NULL, NULL, NULL); NULL, NULL, NULL, NULL, NULL);
} }
} }
if (!dev->msi_enabled)
mode = 0;
} }
if (mode & AAC_INT_MODE_AIF) {
/* handle AIF */
aac_intr_normal(dev, 0, 2, 0, NULL);
if (dev->msi_enabled)
aac_src_access_devreg(dev, AAC_CLEAR_AIF_BIT);
mode = 0;
} }
if (our_interrupt) { if (mode) {
return IRQ_HANDLED; index = dev->host_rrq_idx[vector_no];
for (;;) {
isFastResponse = 0;
/* remove toggle bit (31) */
handle = (dev->host_rrq[index] & 0x7fffffff);
/* check fast response bit (30) */
if (handle & 0x40000000)
isFastResponse = 1;
handle &= 0x0000ffff;
if (handle == 0)
break;
if (dev->msi_enabled && dev->max_msix > 1)
atomic_dec(&dev->rrq_outstanding[vector_no]);
aac_intr_normal(dev, handle-1, 0, isFastResponse, NULL);
dev->host_rrq[index++] = 0;
if (index == (vector_no + 1) * dev->vector_cap)
index = vector_no * dev->vector_cap;
dev->host_rrq_idx[vector_no] = index;
}
mode = 0;
} }
return IRQ_NONE;
return IRQ_HANDLED;
} }
/** /**
...@@ -155,7 +185,7 @@ static void aac_src_disable_interrupt(struct aac_dev *dev) ...@@ -155,7 +185,7 @@ static void aac_src_disable_interrupt(struct aac_dev *dev)
static void aac_src_enable_interrupt_message(struct aac_dev *dev) static void aac_src_enable_interrupt_message(struct aac_dev *dev)
{ {
src_writel(dev, MUnit.OIMR, dev->OIMR = 0xfffffff8); aac_src_access_devreg(dev, AAC_ENABLE_INTERRUPT);
} }
/** /**
...@@ -191,7 +221,10 @@ static int src_sync_cmd(struct aac_dev *dev, u32 command, ...@@ -191,7 +221,10 @@ static int src_sync_cmd(struct aac_dev *dev, u32 command,
/* /*
* Clear the synch command doorbell to start on a clean slate. * Clear the synch command doorbell to start on a clean slate.
*/ */
src_writel(dev, MUnit.ODR_C, OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT); if (!dev->msi_enabled)
src_writel(dev,
MUnit.ODR_C,
OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT);
/* /*
* Disable doorbell interrupts * Disable doorbell interrupts
...@@ -221,11 +254,17 @@ static int src_sync_cmd(struct aac_dev *dev, u32 command, ...@@ -221,11 +254,17 @@ static int src_sync_cmd(struct aac_dev *dev, u32 command,
/* /*
* Mon960 will set doorbell0 bit when it has completed the command. * Mon960 will set doorbell0 bit when it has completed the command.
*/ */
if ((src_readl(dev, MUnit.ODR_R) >> SRC_ODR_SHIFT) & OUTBOUNDDOORBELL_0) { if (aac_src_get_sync_status(dev) & OUTBOUNDDOORBELL_0) {
/* /*
* Clear the doorbell. * Clear the doorbell.
*/ */
src_writel(dev, MUnit.ODR_C, OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT); if (dev->msi_enabled)
aac_src_access_devreg(dev,
AAC_CLEAR_SYNC_BIT);
else
src_writel(dev,
MUnit.ODR_C,
OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT);
ok = 1; ok = 1;
break; break;
} }
...@@ -254,11 +293,16 @@ static int src_sync_cmd(struct aac_dev *dev, u32 command, ...@@ -254,11 +293,16 @@ static int src_sync_cmd(struct aac_dev *dev, u32 command,
*r3 = readl(&dev->IndexRegs->Mailbox[3]); *r3 = readl(&dev->IndexRegs->Mailbox[3]);
if (r4) if (r4)
*r4 = readl(&dev->IndexRegs->Mailbox[4]); *r4 = readl(&dev->IndexRegs->Mailbox[4]);
if (command == GET_COMM_PREFERRED_SETTINGS)
dev->max_msix =
readl(&dev->IndexRegs->Mailbox[5]) & 0xFFFF;
/* /*
* Clear the synch command doorbell. * Clear the synch command doorbell.
*/ */
src_writel(dev, MUnit.ODR_C, OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT); if (!dev->msi_enabled)
src_writel(dev,
MUnit.ODR_C,
OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT);
} }
/* /*
...@@ -335,9 +379,14 @@ static void aac_src_notify_adapter(struct aac_dev *dev, u32 event) ...@@ -335,9 +379,14 @@ static void aac_src_notify_adapter(struct aac_dev *dev, u32 event)
static void aac_src_start_adapter(struct aac_dev *dev) static void aac_src_start_adapter(struct aac_dev *dev)
{ {
struct aac_init *init; struct aac_init *init;
int i;
/* reset host_rrq_idx first */ /* reset host_rrq_idx first */
dev->host_rrq_idx = 0; for (i = 0; i < dev->max_msix; i++) {
dev->host_rrq_idx[i] = i * dev->vector_cap;
atomic_set(&dev->rrq_outstanding[i], 0);
}
dev->fibs_pushed_no = 0;
init = dev->init; init = dev->init;
init->HostElapsedSeconds = cpu_to_le32(get_seconds()); init->HostElapsedSeconds = cpu_to_le32(get_seconds());
...@@ -400,6 +449,33 @@ static int aac_src_deliver_message(struct fib *fib) ...@@ -400,6 +449,33 @@ static int aac_src_deliver_message(struct fib *fib)
q->numpending++; q->numpending++;
spin_unlock_irqrestore(q->lock, qflags); spin_unlock_irqrestore(q->lock, qflags);
if (dev->msi_enabled && fib->hw_fib_va->header.Command != AifRequest &&
dev->max_msix > 1) {
u_int16_t vector_no, first_choice = 0xffff;
vector_no = dev->fibs_pushed_no % dev->max_msix;
do {
vector_no += 1;
if (vector_no == dev->max_msix)
vector_no = 1;
if (atomic_read(&dev->rrq_outstanding[vector_no]) <
dev->vector_cap)
break;
if (0xffff == first_choice)
first_choice = vector_no;
else if (vector_no == first_choice)
break;
} while (1);
if (vector_no == first_choice)
vector_no = 0;
atomic_inc(&dev->rrq_outstanding[vector_no]);
if (dev->fibs_pushed_no == 0xffffffff)
dev->fibs_pushed_no = 0;
else
dev->fibs_pushed_no++;
fib->hw_fib_va->header.Handle += (vector_no << 16);
}
if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE2) { if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE2) {
/* Calculate the amount to the fibsize bits */ /* Calculate the amount to the fibsize bits */
fibsize = (hdr_size + 127) / 128 - 1; fibsize = (hdr_size + 127) / 128 - 1;
...@@ -502,10 +578,19 @@ static int aac_src_restart_adapter(struct aac_dev *dev, int bled) ...@@ -502,10 +578,19 @@ static int aac_src_restart_adapter(struct aac_dev *dev, int bled)
0, 0, 0, 0, 0, 0, &var, &reset_mask, NULL, NULL, NULL); 0, 0, 0, 0, 0, 0, &var, &reset_mask, NULL, NULL, NULL);
if (bled || (var != 0x00000001)) if (bled || (var != 0x00000001))
return -EINVAL; return -EINVAL;
if ((dev->pdev->device == PMC_DEVICE_S7 ||
dev->pdev->device == PMC_DEVICE_S8 ||
dev->pdev->device == PMC_DEVICE_S9) && dev->msi_enabled) {
aac_src_access_devreg(dev, AAC_ENABLE_INTX);
dev->msi_enabled = 0;
msleep(5000); /* Delay 5 seconds */
}
if (dev->supplement_adapter_info.SupportedOptions2 & if (dev->supplement_adapter_info.SupportedOptions2 &
AAC_OPTION_DOORBELL_RESET) { AAC_OPTION_DOORBELL_RESET) {
src_writel(dev, MUnit.IDR, reset_mask); src_writel(dev, MUnit.IDR, reset_mask);
msleep(5000); /* Delay 5 seconds */ ssleep(45);
} }
} }
...@@ -646,8 +731,11 @@ int aac_src_init(struct aac_dev *dev) ...@@ -646,8 +731,11 @@ int aac_src_init(struct aac_dev *dev)
dev->msi = aac_msi && !pci_enable_msi(dev->pdev); dev->msi = aac_msi && !pci_enable_msi(dev->pdev);
dev->aac_msix[0].vector_no = 0;
dev->aac_msix[0].dev = dev;
if (request_irq(dev->pdev->irq, dev->a_ops.adapter_intr, if (request_irq(dev->pdev->irq, dev->a_ops.adapter_intr,
IRQF_SHARED, "aacraid", dev) < 0) { IRQF_SHARED, "aacraid", &(dev->aac_msix[0])) < 0) {
if (dev->msi) if (dev->msi)
pci_disable_msi(dev->pdev); pci_disable_msi(dev->pdev);
...@@ -688,6 +776,7 @@ int aac_srcv_init(struct aac_dev *dev) ...@@ -688,6 +776,7 @@ int aac_srcv_init(struct aac_dev *dev)
unsigned long status; unsigned long status;
int restart = 0; int restart = 0;
int instance = dev->id; int instance = dev->id;
int i, j;
const char *name = dev->name; const char *name = dev->name;
dev->a_ops.adapter_ioremap = aac_srcv_ioremap; dev->a_ops.adapter_ioremap = aac_srcv_ioremap;
...@@ -802,15 +891,42 @@ int aac_srcv_init(struct aac_dev *dev) ...@@ -802,15 +891,42 @@ int aac_srcv_init(struct aac_dev *dev)
goto error_iounmap; goto error_iounmap;
if (dev->comm_interface != AAC_COMM_MESSAGE_TYPE2) if (dev->comm_interface != AAC_COMM_MESSAGE_TYPE2)
goto error_iounmap; goto error_iounmap;
dev->msi = aac_msi && !pci_enable_msi(dev->pdev); if (dev->msi_enabled)
aac_src_access_devreg(dev, AAC_ENABLE_MSIX);
if (!dev->sync_mode && dev->msi_enabled && dev->max_msix > 1) {
for (i = 0; i < dev->max_msix; i++) {
dev->aac_msix[i].vector_no = i;
dev->aac_msix[i].dev = dev;
if (request_irq(dev->msixentry[i].vector,
dev->a_ops.adapter_intr,
0,
"aacraid",
&(dev->aac_msix[i]))) {
printk(KERN_ERR "%s%d: Failed to register IRQ for vector %d.\n",
name, instance, i);
for (j = 0 ; j < i ; j++)
free_irq(dev->msixentry[j].vector,
&(dev->aac_msix[j]));
pci_disable_msix(dev->pdev);
goto error_iounmap;
}
}
} else {
dev->aac_msix[0].vector_no = 0;
dev->aac_msix[0].dev = dev;
if (request_irq(dev->pdev->irq, dev->a_ops.adapter_intr, if (request_irq(dev->pdev->irq, dev->a_ops.adapter_intr,
IRQF_SHARED, "aacraid", dev) < 0) { IRQF_SHARED,
"aacraid",
&(dev->aac_msix[0])) < 0) {
if (dev->msi) if (dev->msi)
pci_disable_msi(dev->pdev); pci_disable_msi(dev->pdev);
printk(KERN_ERR "%s%d: Interrupt unavailable.\n", printk(KERN_ERR "%s%d: Interrupt unavailable.\n",
name, instance); name, instance);
goto error_iounmap; goto error_iounmap;
} }
}
dev->dbg_base = dev->base_start; dev->dbg_base = dev->base_start;
dev->dbg_base_mapped = dev->base; dev->dbg_base_mapped = dev->base;
dev->dbg_size = dev->base_size; dev->dbg_size = dev->base_size;
...@@ -831,3 +947,93 @@ int aac_srcv_init(struct aac_dev *dev) ...@@ -831,3 +947,93 @@ int aac_srcv_init(struct aac_dev *dev)
return -1; return -1;
} }
void aac_src_access_devreg(struct aac_dev *dev, int mode)
{
u_int32_t val;
switch (mode) {
case AAC_ENABLE_INTERRUPT:
src_writel(dev,
MUnit.OIMR,
dev->OIMR = (dev->msi_enabled ?
AAC_INT_ENABLE_TYPE1_MSIX :
AAC_INT_ENABLE_TYPE1_INTX));
break;
case AAC_DISABLE_INTERRUPT:
src_writel(dev,
MUnit.OIMR,
dev->OIMR = AAC_INT_DISABLE_ALL);
break;
case AAC_ENABLE_MSIX:
/* set bit 6 */
val = src_readl(dev, MUnit.IDR);
val |= 0x40;
src_writel(dev, MUnit.IDR, val);
src_readl(dev, MUnit.IDR);
/* unmask int. */
val = PMC_ALL_INTERRUPT_BITS;
src_writel(dev, MUnit.IOAR, val);
val = src_readl(dev, MUnit.OIMR);
src_writel(dev,
MUnit.OIMR,
val & (~(PMC_GLOBAL_INT_BIT2 | PMC_GLOBAL_INT_BIT0)));
break;
case AAC_DISABLE_MSIX:
/* reset bit 6 */
val = src_readl(dev, MUnit.IDR);
val &= ~0x40;
src_writel(dev, MUnit.IDR, val);
src_readl(dev, MUnit.IDR);
break;
case AAC_CLEAR_AIF_BIT:
/* set bit 5 */
val = src_readl(dev, MUnit.IDR);
val |= 0x20;
src_writel(dev, MUnit.IDR, val);
src_readl(dev, MUnit.IDR);
break;
case AAC_CLEAR_SYNC_BIT:
/* set bit 4 */
val = src_readl(dev, MUnit.IDR);
val |= 0x10;
src_writel(dev, MUnit.IDR, val);
src_readl(dev, MUnit.IDR);
break;
case AAC_ENABLE_INTX:
/* set bit 7 */
val = src_readl(dev, MUnit.IDR);
val |= 0x80;
src_writel(dev, MUnit.IDR, val);
src_readl(dev, MUnit.IDR);
/* unmask int. */
val = PMC_ALL_INTERRUPT_BITS;
src_writel(dev, MUnit.IOAR, val);
src_readl(dev, MUnit.IOAR);
val = src_readl(dev, MUnit.OIMR);
src_writel(dev, MUnit.OIMR,
val & (~(PMC_GLOBAL_INT_BIT2)));
break;
default:
break;
}
}
static int aac_src_get_sync_status(struct aac_dev *dev)
{
int val;
if (dev->msi_enabled)
val = src_readl(dev, MUnit.ODR_MSI) & 0x1000 ? 1 : 0;
else
val = src_readl(dev, MUnit.ODR_R) >> SRC_ODR_SHIFT;
return val;
}
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