Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fb86a35b authored by Mike Miller's avatar Mike Miller Committed by Linus Torvalds
Browse files

[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: default avatarMike Miller <mike.miller@hp.com>
Cc: Jens Axboe <axboe@suse.de>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent d09cf7d7
Loading
Loading
Loading
Loading
+76 −11
Original line number Original line Diff line number Diff line
/*
/*
 *    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 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
			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 */
+7 −1
Original line number Original line Diff line number Diff line
@@ -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];
+1 −1
Original line number Original line Diff line number Diff line
@@ -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)