Commit 303932fd authored by Don Brace's avatar Don Brace Committed by James Bottomley

[SCSI] hpsa: Allow multiple command completions per interrupt.

This is done by adding support for the so-called "performant mode"
(that's really what they called it).  Smart Array controllers
have a mode which enables multiple command completions to be
delivered with a single interrupt, "performant" mode.  We want to use
that mode, as some newer controllers will be requiring this mode.
Signed-off-by: default avatarDon Brace <brace@beardog.cce.hp.com>
Signed-off-by: default avatarStephen M. Cameron <scameron@beardog.cce.hp.com>
Signed-off-by: default avatarMike Miller <mikem@beardog.cce.hp.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@suse.de>
parent 900c5440
This diff is collapsed.
...@@ -60,14 +60,15 @@ struct ctlr_info { ...@@ -60,14 +60,15 @@ struct ctlr_info {
unsigned long paddr; unsigned long paddr;
int nr_cmds; /* Number of commands allowed on this controller */ int nr_cmds; /* Number of commands allowed on this controller */
struct CfgTable __iomem *cfgtable; struct CfgTable __iomem *cfgtable;
int max_sg_entries;
int interrupts_enabled; int interrupts_enabled;
int major; int major;
int max_commands; int max_commands;
int commands_outstanding; int commands_outstanding;
int max_outstanding; /* Debug */ int max_outstanding; /* Debug */
int usage_count; /* number of opens all all minor devices */ int usage_count; /* number of opens all all minor devices */
# define DOORBELL_INT 0 # define PERF_MODE_INT 0
# define PERF_MODE_INT 1 # define DOORBELL_INT 1
# define SIMPLE_MODE_INT 2 # define SIMPLE_MODE_INT 2
# define MEMQ_MODE_INT 3 # define MEMQ_MODE_INT 3
unsigned int intr[4]; unsigned int intr[4];
...@@ -102,6 +103,23 @@ struct ctlr_info { ...@@ -102,6 +103,23 @@ struct ctlr_info {
int ndevices; /* number of used elements in .dev[] array. */ int ndevices; /* number of used elements in .dev[] array. */
#define HPSA_MAX_SCSI_DEVS_PER_HBA 256 #define HPSA_MAX_SCSI_DEVS_PER_HBA 256
struct hpsa_scsi_dev_t *dev[HPSA_MAX_SCSI_DEVS_PER_HBA]; struct hpsa_scsi_dev_t *dev[HPSA_MAX_SCSI_DEVS_PER_HBA];
/*
* Performant mode tables.
*/
u32 trans_support;
u32 trans_offset;
struct TransTable_struct *transtable;
unsigned long transMethod;
/*
* Performant mode completion buffer
*/
u64 *reply_pool;
dma_addr_t reply_pool_dhandle;
u64 *reply_pool_head;
size_t reply_pool_size;
unsigned char reply_pool_wraparound;
u32 *blockFetchTable;
}; };
#define HPSA_ABORT_MSG 0 #define HPSA_ABORT_MSG 0
#define HPSA_DEVICE_RESET_MSG 1 #define HPSA_DEVICE_RESET_MSG 1
...@@ -165,6 +183,16 @@ struct ctlr_info { ...@@ -165,6 +183,16 @@ struct ctlr_info {
#define HPSA_ERROR_BIT 0x02 #define HPSA_ERROR_BIT 0x02
/* Performant mode flags */
#define SA5_PERF_INTR_PENDING 0x04
#define SA5_PERF_INTR_OFF 0x05
#define SA5_OUTDB_STATUS_PERF_BIT 0x01
#define SA5_OUTDB_CLEAR_PERF_BIT 0x01
#define SA5_OUTDB_CLEAR 0xA0
#define SA5_OUTDB_CLEAR_PERF_BIT 0x01
#define SA5_OUTDB_STATUS 0x9C
#define HPSA_INTR_ON 1 #define HPSA_INTR_ON 1
#define HPSA_INTR_OFF 0 #define HPSA_INTR_OFF 0
/* /*
...@@ -173,7 +201,8 @@ struct ctlr_info { ...@@ -173,7 +201,8 @@ struct ctlr_info {
static void SA5_submit_command(struct ctlr_info *h, static void SA5_submit_command(struct ctlr_info *h,
struct CommandList *c) struct CommandList *c)
{ {
dev_dbg(&h->pdev->dev, "Sending %x\n", c->busaddr); dev_dbg(&h->pdev->dev, "Sending %x, tag = %x\n", c->busaddr,
c->Header.Tag.lower);
writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET); writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET);
h->commands_outstanding++; h->commands_outstanding++;
if (h->commands_outstanding > h->max_outstanding) if (h->commands_outstanding > h->max_outstanding)
...@@ -196,6 +225,52 @@ static void SA5_intr_mask(struct ctlr_info *h, unsigned long val) ...@@ -196,6 +225,52 @@ static void SA5_intr_mask(struct ctlr_info *h, unsigned long val)
h->vaddr + SA5_REPLY_INTR_MASK_OFFSET); h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
} }
} }
static void SA5_performant_intr_mask(struct ctlr_info *h, unsigned long val)
{
if (val) { /* turn on interrupts */
h->interrupts_enabled = 1;
writel(0, h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
} else {
h->interrupts_enabled = 0;
writel(SA5_PERF_INTR_OFF,
h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
}
}
static unsigned long SA5_performant_completed(struct ctlr_info *h)
{
unsigned long register_value = FIFO_EMPTY;
/* flush the controller write of the reply queue by reading
* outbound doorbell status register.
*/
register_value = readl(h->vaddr + SA5_OUTDB_STATUS);
/* msi auto clears the interrupt pending bit. */
if (!(h->msi_vector || h->msix_vector)) {
writel(SA5_OUTDB_CLEAR_PERF_BIT, h->vaddr + SA5_OUTDB_CLEAR);
/* Do a read in order to flush the write to the controller
* (as per spec.)
*/
register_value = readl(h->vaddr + SA5_OUTDB_STATUS);
}
if ((*(h->reply_pool_head) & 1) == (h->reply_pool_wraparound)) {
register_value = *(h->reply_pool_head);
(h->reply_pool_head)++;
h->commands_outstanding--;
} else {
register_value = FIFO_EMPTY;
}
/* Check for wraparound */
if (h->reply_pool_head == (h->reply_pool + h->max_commands)) {
h->reply_pool_head = h->reply_pool;
h->reply_pool_wraparound ^= 1;
}
return register_value;
}
/* /*
* Returns true if fifo is full. * Returns true if fifo is full.
* *
...@@ -241,6 +316,20 @@ static bool SA5_intr_pending(struct ctlr_info *h) ...@@ -241,6 +316,20 @@ static bool SA5_intr_pending(struct ctlr_info *h)
return register_value & SA5_INTR_PENDING; return register_value & SA5_INTR_PENDING;
} }
static bool SA5_performant_intr_pending(struct ctlr_info *h)
{
unsigned long register_value = readl(h->vaddr + SA5_INTR_STATUS);
if (!register_value)
return false;
if (h->msi_vector || h->msix_vector)
return true;
/* Read outbound doorbell to flush */
register_value = readl(h->vaddr + SA5_OUTDB_STATUS);
return register_value & SA5_OUTDB_STATUS_PERF_BIT;
}
static struct access_method SA5_access = { static struct access_method SA5_access = {
SA5_submit_command, SA5_submit_command,
...@@ -250,14 +339,19 @@ static struct access_method SA5_access = { ...@@ -250,14 +339,19 @@ static struct access_method SA5_access = {
SA5_completed, SA5_completed,
}; };
static struct access_method SA5_performant_access = {
SA5_submit_command,
SA5_performant_intr_mask,
SA5_fifo_full,
SA5_performant_intr_pending,
SA5_performant_completed,
};
struct board_type { struct board_type {
u32 board_id; u32 board_id;
char *product_name; char *product_name;
struct access_method *access; struct access_method *access;
}; };
/* end of old hpsa_scsi.h file */
#endif /* HPSA_H */ #endif /* HPSA_H */
...@@ -101,6 +101,7 @@ ...@@ -101,6 +101,7 @@
#define CFGTBL_AccCmds 0x00000001l #define CFGTBL_AccCmds 0x00000001l
#define CFGTBL_Trans_Simple 0x00000002l #define CFGTBL_Trans_Simple 0x00000002l
#define CFGTBL_Trans_Performant 0x00000004l
#define CFGTBL_BusType_Ultra2 0x00000001l #define CFGTBL_BusType_Ultra2 0x00000001l
#define CFGTBL_BusType_Ultra3 0x00000002l #define CFGTBL_BusType_Ultra3 0x00000002l
...@@ -267,12 +268,31 @@ struct ErrorInfo { ...@@ -267,12 +268,31 @@ struct ErrorInfo {
#define CMD_IOCTL_PEND 0x01 #define CMD_IOCTL_PEND 0x01
#define CMD_SCSI 0x03 #define CMD_SCSI 0x03
/* This structure needs to be divisible by 32 for new
* indexing method and performant mode.
*/
#define PAD32 32
#define PAD64DIFF 0
#define USEEXTRA ((sizeof(void *) - 4)/4)
#define PADSIZE (PAD32 + PAD64DIFF * USEEXTRA)
#define DIRECT_LOOKUP_SHIFT 5
#define DIRECT_LOOKUP_BIT 0x10
#define HPSA_ERROR_BIT 0x02
struct ctlr_info; /* defined in hpsa.h */ struct ctlr_info; /* defined in hpsa.h */
/* The size of this structure needs to be divisible by 8 /* The size of this structure needs to be divisible by 32
* on all architectures, because the controller uses 2 * on all architectures because low 5 bits of the addresses
* lower bits of the address, and the driver uses 1 lower * are used as follows:
* bit (3 bits total.) *
* bit 0: to device, used to indicate "performant mode" command
* from device, indidcates error status.
* bit 1-3: to device, indicates block fetch table entry for
* reducing DMA in fetching commands from host memory.
* bit 4: used to indicate whether tag is "direct lookup" (index),
* or a bus address.
*/ */
struct CommandList { struct CommandList {
struct CommandListHeader Header; struct CommandListHeader Header;
struct RequestBlock Request; struct RequestBlock Request;
...@@ -291,6 +311,14 @@ struct CommandList { ...@@ -291,6 +311,14 @@ struct CommandList {
struct completion *waiting; struct completion *waiting;
int retry_count; int retry_count;
void *scsi_cmd; void *scsi_cmd;
/* on 64 bit architectures, to get this to be 32-byte-aligned
* it so happens we need no padding, on 32 bit systems,
* we need 8 bytes of padding. This does that.
*/
#define COMMANDLIST_PAD ((8 - sizeof(long))/4 * 8)
u8 pad[COMMANDLIST_PAD];
}; };
/* Configuration Table Structure */ /* Configuration Table Structure */
...@@ -301,6 +329,10 @@ struct HostWrite { ...@@ -301,6 +329,10 @@ struct HostWrite {
u32 CoalIntCount; u32 CoalIntCount;
}; };
#define SIMPLE_MODE 0x02
#define PERFORMANT_MODE 0x04
#define MEMQ_MODE 0x08
struct CfgTable { struct CfgTable {
u8 Signature[4]; u8 Signature[4];
u32 SpecValence; u32 SpecValence;
...@@ -309,10 +341,26 @@ struct CfgTable { ...@@ -309,10 +341,26 @@ struct CfgTable {
struct HostWrite HostWrite; struct HostWrite HostWrite;
u32 CmdsOutMax; u32 CmdsOutMax;
u32 BusTypes; u32 BusTypes;
u32 Reserved; u32 TransMethodOffset;
u8 ServerName[16]; u8 ServerName[16];
u32 HeartBeat; u32 HeartBeat;
u32 SCSI_Prefetch; u32 SCSI_Prefetch;
u32 MaxScatterGatherElements;
u32 MaxLogicalUnits;
u32 MaxPhysicalDevices;
u32 MaxPhysicalDrivesPerLogicalUnit;
u32 MaxPerformantModeCommands;
};
#define NUM_BLOCKFETCH_ENTRIES 8
struct TransTable_struct {
u32 BlockFetch[NUM_BLOCKFETCH_ENTRIES];
u32 RepQSize;
u32 RepQCount;
u32 RepQCtrAddrLow32;
u32 RepQCtrAddrHigh32;
u32 RepQAddr0Low32;
u32 RepQAddr0High32;
}; };
struct hpsa_pci_info { struct hpsa_pci_info {
......
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