[PATCH] cciss: adds MSI and MSI-X support
This creates a new function, cciss_interrupt_mode called from cciss_pci_init. This function determines what type of interrupt vector to use, i.e., MSI, MSI-X, or IO-APIC. One noticeable difference is changing the interrupt field of the controller struct to an array of 4 unsigned ints. The Smart Array HW is capable of generating 4 distinct interrupts depending on the transport method in use during operation. These are: #define DOORBELL_INT 0 Used to notify the contoller of configuration updates. We only use this feature when in polling mode. #define PERF_MODE_INT 0 Used when the controller is in Performant Mode. #define SIMPLE_MODE_INT 2 Used when the controller is in Simple Mode (current Linux implementation). #define MEMQ_INT_MODE 3 Not used. When using IO-APIC interrupts these 4 lines are OR'ed together so when any one fires an interrupt an is generated. In MSI or MSI-X mode this hardware OR'ing is ignored. We must register for our interrupt depending on what mode the controller is running. For Linux we use SIMPLE_MODE_INT exclusively at this time. Please consider this for inclusion. Signed-off-by: Mike Miller <mike.miller@hp.com> Cc: Jens Axboe <axboe@suse.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
d09cf7d77f
commit
fb86a35b9d
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Disk Array driver for HP SA 5xxx and 6xxx Controllers
|
* Disk Array driver for HP SA 5xxx and 6xxx Controllers
|
||||||
* Copyright 2000, 2005 Hewlett-Packard Development Company, L.P.
|
* Copyright 2000, 2006 Hewlett-Packard Development Company, L.P.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -47,12 +47,12 @@
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
|
|
||||||
#define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin))
|
#define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin))
|
||||||
#define DRIVER_NAME "HP CISS Driver (v 2.6.8)"
|
#define DRIVER_NAME "HP CISS Driver (v 2.6.10)"
|
||||||
#define DRIVER_VERSION CCISS_DRIVER_VERSION(2,6,8)
|
#define DRIVER_VERSION CCISS_DRIVER_VERSION(2,6,10)
|
||||||
|
|
||||||
/* Embedded module documentation macros - see modules.h */
|
/* Embedded module documentation macros - see modules.h */
|
||||||
MODULE_AUTHOR("Hewlett-Packard Company");
|
MODULE_AUTHOR("Hewlett-Packard Company");
|
||||||
MODULE_DESCRIPTION("Driver for HP Controller SA5xxx SA6xxx version 2.6.8");
|
MODULE_DESCRIPTION("Driver for HP Controller SA5xxx SA6xxx version 2.6.10");
|
||||||
MODULE_SUPPORTED_DEVICE("HP SA5i SA5i+ SA532 SA5300 SA5312 SA641 SA642 SA6400"
|
MODULE_SUPPORTED_DEVICE("HP SA5i SA5i+ SA532 SA5300 SA5312 SA641 SA642 SA6400"
|
||||||
" SA6i P600 P800 P400 P400i E200 E200i");
|
" SA6i P600 P800 P400 P400i E200 E200i");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
@ -167,7 +167,7 @@ static void cciss_geometry_inquiry(int ctlr, int logvol,
|
||||||
unsigned int block_size, InquiryData_struct *inq_buff,
|
unsigned int block_size, InquiryData_struct *inq_buff,
|
||||||
drive_info_struct *drv);
|
drive_info_struct *drv);
|
||||||
static void cciss_getgeometry(int cntl_num);
|
static void cciss_getgeometry(int cntl_num);
|
||||||
|
static void __devinit cciss_interrupt_mode(ctlr_info_t *, struct pci_dev *, __u32);
|
||||||
static void start_io( ctlr_info_t *h);
|
static void start_io( ctlr_info_t *h);
|
||||||
static int sendcmd( __u8 cmd, int ctlr, void *buff, size_t size,
|
static int sendcmd( __u8 cmd, int ctlr, void *buff, size_t size,
|
||||||
unsigned int use_unit_num, unsigned int log_unit, __u8 page_code,
|
unsigned int use_unit_num, unsigned int log_unit, __u8 page_code,
|
||||||
|
@ -284,7 +284,7 @@ static int cciss_proc_get_info(char *buffer, char **start, off_t offset,
|
||||||
h->product_name,
|
h->product_name,
|
||||||
(unsigned long)h->board_id,
|
(unsigned long)h->board_id,
|
||||||
h->firm_ver[0], h->firm_ver[1], h->firm_ver[2], h->firm_ver[3],
|
h->firm_ver[0], h->firm_ver[1], h->firm_ver[2], h->firm_ver[3],
|
||||||
(unsigned int)h->intr,
|
(unsigned int)h->intr[SIMPLE_MODE_INT],
|
||||||
h->num_luns,
|
h->num_luns,
|
||||||
h->Qdepth, h->commands_outstanding,
|
h->Qdepth, h->commands_outstanding,
|
||||||
h->maxQsinceinit, h->max_outstanding, h->maxSG);
|
h->maxQsinceinit, h->max_outstanding, h->maxSG);
|
||||||
|
@ -2662,6 +2662,60 @@ static int find_PCI_BAR_index(struct pci_dev *pdev,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If MSI/MSI-X is supported by the kernel we will try to enable it on
|
||||||
|
* controllers that are capable. If not, we use IO-APIC mode.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void __devinit cciss_interrupt_mode(ctlr_info_t *c, struct pci_dev *pdev, __u32 board_id)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_PCI_MSI
|
||||||
|
int err;
|
||||||
|
struct msix_entry cciss_msix_entries[4] = {{0,0}, {0,1},
|
||||||
|
{0,2}, {0,3}};
|
||||||
|
|
||||||
|
/* Some boards advertise MSI but don't really support it */
|
||||||
|
if ((board_id == 0x40700E11) ||
|
||||||
|
(board_id == 0x40800E11) ||
|
||||||
|
(board_id == 0x40820E11) ||
|
||||||
|
(board_id == 0x40830E11))
|
||||||
|
goto default_int_mode;
|
||||||
|
|
||||||
|
if (pci_find_capability(pdev, PCI_CAP_ID_MSIX)) {
|
||||||
|
err = pci_enable_msix(pdev, cciss_msix_entries, 4);
|
||||||
|
if (!err) {
|
||||||
|
c->intr[0] = cciss_msix_entries[0].vector;
|
||||||
|
c->intr[1] = cciss_msix_entries[1].vector;
|
||||||
|
c->intr[2] = cciss_msix_entries[2].vector;
|
||||||
|
c->intr[3] = cciss_msix_entries[3].vector;
|
||||||
|
c->msix_vector = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (err > 0) {
|
||||||
|
printk(KERN_WARNING "cciss: only %d MSI-X vectors "
|
||||||
|
"available\n", err);
|
||||||
|
} else {
|
||||||
|
printk(KERN_WARNING "cciss: MSI-X init failed %d\n",
|
||||||
|
err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pci_find_capability(pdev, PCI_CAP_ID_MSI)) {
|
||||||
|
if (!pci_enable_msi(pdev)) {
|
||||||
|
c->intr[SIMPLE_MODE_INT] = pdev->irq;
|
||||||
|
c->msi_vector = 1;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
printk(KERN_WARNING "cciss: MSI init failed\n");
|
||||||
|
c->intr[SIMPLE_MODE_INT] = pdev->irq;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_PCI_MSI */
|
||||||
|
/* if we get here we're going to use the default interrupt mode */
|
||||||
|
default_int_mode:
|
||||||
|
c->intr[SIMPLE_MODE_INT] = pdev->irq;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
|
static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
|
||||||
{
|
{
|
||||||
ushort subsystem_vendor_id, subsystem_device_id, command;
|
ushort subsystem_vendor_id, subsystem_device_id, command;
|
||||||
|
@ -2722,7 +2776,10 @@ static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
|
||||||
printk("board_id = %x\n", board_id);
|
printk("board_id = %x\n", board_id);
|
||||||
#endif /* CCISS_DEBUG */
|
#endif /* CCISS_DEBUG */
|
||||||
|
|
||||||
c->intr = pdev->irq;
|
/* If the kernel supports MSI/MSI-X we will try to enable that functionality,
|
||||||
|
* else we use the IO-APIC interrupt assigned to us by system ROM.
|
||||||
|
*/
|
||||||
|
cciss_interrupt_mode(c, pdev, board_id);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Memory base addr is first addr , the second points to the config
|
* Memory base addr is first addr , the second points to the config
|
||||||
|
@ -3076,11 +3133,11 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
|
||||||
|
|
||||||
/* make sure the board interrupts are off */
|
/* make sure the board interrupts are off */
|
||||||
hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_OFF);
|
hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_OFF);
|
||||||
if( request_irq(hba[i]->intr, do_cciss_intr,
|
if( request_irq(hba[i]->intr[SIMPLE_MODE_INT], do_cciss_intr,
|
||||||
SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM,
|
SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM,
|
||||||
hba[i]->devname, hba[i])) {
|
hba[i]->devname, hba[i])) {
|
||||||
printk(KERN_ERR "cciss: Unable to get irq %d for %s\n",
|
printk(KERN_ERR "cciss: Unable to get irq %d for %s\n",
|
||||||
hba[i]->intr, hba[i]->devname);
|
hba[i]->intr[SIMPLE_MODE_INT], hba[i]->devname);
|
||||||
goto clean2;
|
goto clean2;
|
||||||
}
|
}
|
||||||
hba[i]->cmd_pool_bits = kmalloc(((NR_CMDS+BITS_PER_LONG-1)/BITS_PER_LONG)*sizeof(unsigned long), GFP_KERNEL);
|
hba[i]->cmd_pool_bits = kmalloc(((NR_CMDS+BITS_PER_LONG-1)/BITS_PER_LONG)*sizeof(unsigned long), GFP_KERNEL);
|
||||||
|
@ -3186,7 +3243,7 @@ clean4:
|
||||||
NR_CMDS * sizeof( ErrorInfo_struct),
|
NR_CMDS * sizeof( ErrorInfo_struct),
|
||||||
hba[i]->errinfo_pool,
|
hba[i]->errinfo_pool,
|
||||||
hba[i]->errinfo_pool_dhandle);
|
hba[i]->errinfo_pool_dhandle);
|
||||||
free_irq(hba[i]->intr, hba[i]);
|
free_irq(hba[i]->intr[SIMPLE_MODE_INT], hba[i]);
|
||||||
clean2:
|
clean2:
|
||||||
unregister_blkdev(hba[i]->major, hba[i]->devname);
|
unregister_blkdev(hba[i]->major, hba[i]->devname);
|
||||||
clean1:
|
clean1:
|
||||||
|
@ -3227,7 +3284,15 @@ static void __devexit cciss_remove_one (struct pci_dev *pdev)
|
||||||
printk(KERN_WARNING "Error Flushing cache on controller %d\n",
|
printk(KERN_WARNING "Error Flushing cache on controller %d\n",
|
||||||
i);
|
i);
|
||||||
}
|
}
|
||||||
free_irq(hba[i]->intr, hba[i]);
|
free_irq(hba[i]->intr[2], hba[i]);
|
||||||
|
|
||||||
|
#ifdef CONFIG_PCI_MSI
|
||||||
|
if (hba[i]->msix_vector)
|
||||||
|
pci_disable_msix(hba[i]->pdev);
|
||||||
|
else if (hba[i]->msi_vector)
|
||||||
|
pci_disable_msi(hba[i]->pdev);
|
||||||
|
#endif /* CONFIG_PCI_MSI */
|
||||||
|
|
||||||
pci_set_drvdata(pdev, NULL);
|
pci_set_drvdata(pdev, NULL);
|
||||||
iounmap(hba[i]->vaddr);
|
iounmap(hba[i]->vaddr);
|
||||||
cciss_unregister_scsi(i); /* unhook from SCSI subsystem */
|
cciss_unregister_scsi(i); /* unhook from SCSI subsystem */
|
||||||
|
|
|
@ -65,7 +65,6 @@ struct ctlr_info
|
||||||
unsigned long io_mem_addr;
|
unsigned long io_mem_addr;
|
||||||
unsigned long io_mem_length;
|
unsigned long io_mem_length;
|
||||||
CfgTable_struct __iomem *cfgtable;
|
CfgTable_struct __iomem *cfgtable;
|
||||||
unsigned int intr;
|
|
||||||
int interrupts_enabled;
|
int interrupts_enabled;
|
||||||
int major;
|
int major;
|
||||||
int max_commands;
|
int max_commands;
|
||||||
|
@ -74,6 +73,13 @@ struct ctlr_info
|
||||||
int num_luns;
|
int num_luns;
|
||||||
int highest_lun;
|
int highest_lun;
|
||||||
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 1
|
||||||
|
# define SIMPLE_MODE_INT 2
|
||||||
|
# define MEMQ_MODE_INT 3
|
||||||
|
unsigned int intr[4];
|
||||||
|
unsigned int msix_vector;
|
||||||
|
unsigned int msi_vector;
|
||||||
|
|
||||||
// information about each logical volume
|
// information about each logical volume
|
||||||
drive_info_struct drv[CISS_MAX_LUN];
|
drive_info_struct drv[CISS_MAX_LUN];
|
||||||
|
|
|
@ -714,7 +714,7 @@ cciss_scsi_detect(int ctlr)
|
||||||
((struct cciss_scsi_adapter_data_t *)
|
((struct cciss_scsi_adapter_data_t *)
|
||||||
hba[ctlr]->scsi_ctlr)->scsi_host = (void *) sh;
|
hba[ctlr]->scsi_ctlr)->scsi_host = (void *) sh;
|
||||||
sh->hostdata[0] = (unsigned long) hba[ctlr];
|
sh->hostdata[0] = (unsigned long) hba[ctlr];
|
||||||
sh->irq = hba[ctlr]->intr;
|
sh->irq = hba[ctlr]->intr[SIMPLE_MODE_INT];
|
||||||
sh->unique_id = sh->irq;
|
sh->unique_id = sh->irq;
|
||||||
error = scsi_add_host(sh, &hba[ctlr]->pdev->dev);
|
error = scsi_add_host(sh, &hba[ctlr]->pdev->dev);
|
||||||
if (error)
|
if (error)
|
||||||
|
|
Loading…
Reference in a new issue