diff options
author | Mike Miller <mike.miller@hp.com> | 2006-01-08 04:03:50 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-01-08 23:14:00 -0500 |
commit | fb86a35b9ded8a7e53a432cbf28df603cdd4849c (patch) | |
tree | 6cfc9de386c26f5b1c9a126aee2bdc8f80bc8e2b /drivers/block | |
parent | d09cf7d77f62f6fb2f6d63fe5980583805f2d559 (diff) |
[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>
Diffstat (limited to 'drivers/block')
-rw-r--r-- | drivers/block/cciss.c | 87 | ||||
-rw-r--r-- | drivers/block/cciss.h | 8 | ||||
-rw-r--r-- | drivers/block/cciss_scsi.c | 2 |
3 files changed, 84 insertions, 13 deletions
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index bdb9c2717d40..46e8356a962a 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * Disk Array driver for HP SA 5xxx and 6xxx Controllers | 2 | * Disk Array driver for HP SA 5xxx and 6xxx Controllers |
3 | * Copyright 2000, 2005 Hewlett-Packard Development Company, L.P. | 3 | * Copyright 2000, 2006 Hewlett-Packard Development Company, L.P. |
4 | * | 4 | * |
5 | * This program is free software; you can redistribute it and/or modify | 5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by | 6 | * it under the terms of the GNU General Public License as published by |
@@ -47,12 +47,12 @@ | |||
47 | #include <linux/completion.h> | 47 | #include <linux/completion.h> |
48 | 48 | ||
49 | #define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin)) | 49 | #define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin)) |
50 | #define DRIVER_NAME "HP CISS Driver (v 2.6.8)" | 50 | #define DRIVER_NAME "HP CISS Driver (v 2.6.10)" |
51 | #define DRIVER_VERSION CCISS_DRIVER_VERSION(2,6,8) | 51 | #define DRIVER_VERSION CCISS_DRIVER_VERSION(2,6,10) |
52 | 52 | ||
53 | /* Embedded module documentation macros - see modules.h */ | 53 | /* Embedded module documentation macros - see modules.h */ |
54 | MODULE_AUTHOR("Hewlett-Packard Company"); | 54 | MODULE_AUTHOR("Hewlett-Packard Company"); |
55 | MODULE_DESCRIPTION("Driver for HP Controller SA5xxx SA6xxx version 2.6.8"); | 55 | MODULE_DESCRIPTION("Driver for HP Controller SA5xxx SA6xxx version 2.6.10"); |
56 | MODULE_SUPPORTED_DEVICE("HP SA5i SA5i+ SA532 SA5300 SA5312 SA641 SA642 SA6400" | 56 | MODULE_SUPPORTED_DEVICE("HP SA5i SA5i+ SA532 SA5300 SA5312 SA641 SA642 SA6400" |
57 | " SA6i P600 P800 P400 P400i E200 E200i"); | 57 | " SA6i P600 P800 P400 P400i E200 E200i"); |
58 | MODULE_LICENSE("GPL"); | 58 | MODULE_LICENSE("GPL"); |
@@ -167,7 +167,7 @@ static void cciss_geometry_inquiry(int ctlr, int logvol, | |||
167 | unsigned int block_size, InquiryData_struct *inq_buff, | 167 | unsigned int block_size, InquiryData_struct *inq_buff, |
168 | drive_info_struct *drv); | 168 | drive_info_struct *drv); |
169 | static void cciss_getgeometry(int cntl_num); | 169 | static void cciss_getgeometry(int cntl_num); |
170 | 170 | static void __devinit cciss_interrupt_mode(ctlr_info_t *, struct pci_dev *, __u32); | |
171 | static void start_io( ctlr_info_t *h); | 171 | static void start_io( ctlr_info_t *h); |
172 | static int sendcmd( __u8 cmd, int ctlr, void *buff, size_t size, | 172 | static int sendcmd( __u8 cmd, int ctlr, void *buff, size_t size, |
173 | unsigned int use_unit_num, unsigned int log_unit, __u8 page_code, | 173 | 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, | |||
284 | h->product_name, | 284 | h->product_name, |
285 | (unsigned long)h->board_id, | 285 | (unsigned long)h->board_id, |
286 | h->firm_ver[0], h->firm_ver[1], h->firm_ver[2], h->firm_ver[3], | 286 | h->firm_ver[0], h->firm_ver[1], h->firm_ver[2], h->firm_ver[3], |
287 | (unsigned int)h->intr, | 287 | (unsigned int)h->intr[SIMPLE_MODE_INT], |
288 | h->num_luns, | 288 | h->num_luns, |
289 | h->Qdepth, h->commands_outstanding, | 289 | h->Qdepth, h->commands_outstanding, |
290 | h->maxQsinceinit, h->max_outstanding, h->maxSG); | 290 | h->maxQsinceinit, h->max_outstanding, h->maxSG); |
@@ -2662,6 +2662,60 @@ static int find_PCI_BAR_index(struct pci_dev *pdev, | |||
2662 | return -1; | 2662 | return -1; |
2663 | } | 2663 | } |
2664 | 2664 | ||
2665 | /* If MSI/MSI-X is supported by the kernel we will try to enable it on | ||
2666 | * controllers that are capable. If not, we use IO-APIC mode. | ||
2667 | */ | ||
2668 | |||
2669 | static void __devinit cciss_interrupt_mode(ctlr_info_t *c, struct pci_dev *pdev, __u32 board_id) | ||
2670 | { | ||
2671 | #ifdef CONFIG_PCI_MSI | ||
2672 | int err; | ||
2673 | struct msix_entry cciss_msix_entries[4] = {{0,0}, {0,1}, | ||
2674 | {0,2}, {0,3}}; | ||
2675 | |||
2676 | /* Some boards advertise MSI but don't really support it */ | ||
2677 | if ((board_id == 0x40700E11) || | ||
2678 | (board_id == 0x40800E11) || | ||
2679 | (board_id == 0x40820E11) || | ||
2680 | (board_id == 0x40830E11)) | ||
2681 | goto default_int_mode; | ||
2682 | |||
2683 | if (pci_find_capability(pdev, PCI_CAP_ID_MSIX)) { | ||
2684 | err = pci_enable_msix(pdev, cciss_msix_entries, 4); | ||
2685 | if (!err) { | ||
2686 | c->intr[0] = cciss_msix_entries[0].vector; | ||
2687 | c->intr[1] = cciss_msix_entries[1].vector; | ||
2688 | c->intr[2] = cciss_msix_entries[2].vector; | ||
2689 | c->intr[3] = cciss_msix_entries[3].vector; | ||
2690 | c->msix_vector = 1; | ||
2691 | return; | ||
2692 | } | ||
2693 | if (err > 0) { | ||
2694 | printk(KERN_WARNING "cciss: only %d MSI-X vectors " | ||
2695 | "available\n", err); | ||
2696 | } else { | ||
2697 | printk(KERN_WARNING "cciss: MSI-X init failed %d\n", | ||
2698 | err); | ||
2699 | } | ||
2700 | } | ||
2701 | if (pci_find_capability(pdev, PCI_CAP_ID_MSI)) { | ||
2702 | if (!pci_enable_msi(pdev)) { | ||
2703 | c->intr[SIMPLE_MODE_INT] = pdev->irq; | ||
2704 | c->msi_vector = 1; | ||
2705 | return; | ||
2706 | } else { | ||
2707 | printk(KERN_WARNING "cciss: MSI init failed\n"); | ||
2708 | c->intr[SIMPLE_MODE_INT] = pdev->irq; | ||
2709 | return; | ||
2710 | } | ||
2711 | } | ||
2712 | #endif /* CONFIG_PCI_MSI */ | ||
2713 | /* if we get here we're going to use the default interrupt mode */ | ||
2714 | default_int_mode: | ||
2715 | c->intr[SIMPLE_MODE_INT] = pdev->irq; | ||
2716 | return; | ||
2717 | } | ||
2718 | |||
2665 | static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) | 2719 | static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) |
2666 | { | 2720 | { |
2667 | ushort subsystem_vendor_id, subsystem_device_id, command; | 2721 | 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) | |||
2722 | printk("board_id = %x\n", board_id); | 2776 | printk("board_id = %x\n", board_id); |
2723 | #endif /* CCISS_DEBUG */ | 2777 | #endif /* CCISS_DEBUG */ |
2724 | 2778 | ||
2725 | c->intr = pdev->irq; | 2779 | /* If the kernel supports MSI/MSI-X we will try to enable that functionality, |
2780 | * else we use the IO-APIC interrupt assigned to us by system ROM. | ||
2781 | */ | ||
2782 | cciss_interrupt_mode(c, pdev, board_id); | ||
2726 | 2783 | ||
2727 | /* | 2784 | /* |
2728 | * Memory base addr is first addr , the second points to the config | 2785 | * 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, | |||
3076 | 3133 | ||
3077 | /* make sure the board interrupts are off */ | 3134 | /* make sure the board interrupts are off */ |
3078 | hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_OFF); | 3135 | hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_OFF); |
3079 | if( request_irq(hba[i]->intr, do_cciss_intr, | 3136 | if( request_irq(hba[i]->intr[SIMPLE_MODE_INT], do_cciss_intr, |
3080 | SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, | 3137 | SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, |
3081 | hba[i]->devname, hba[i])) { | 3138 | hba[i]->devname, hba[i])) { |
3082 | printk(KERN_ERR "cciss: Unable to get irq %d for %s\n", | 3139 | printk(KERN_ERR "cciss: Unable to get irq %d for %s\n", |
3083 | hba[i]->intr, hba[i]->devname); | 3140 | hba[i]->intr[SIMPLE_MODE_INT], hba[i]->devname); |
3084 | goto clean2; | 3141 | goto clean2; |
3085 | } | 3142 | } |
3086 | hba[i]->cmd_pool_bits = kmalloc(((NR_CMDS+BITS_PER_LONG-1)/BITS_PER_LONG)*sizeof(unsigned long), GFP_KERNEL); | 3143 | 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: | |||
3186 | NR_CMDS * sizeof( ErrorInfo_struct), | 3243 | NR_CMDS * sizeof( ErrorInfo_struct), |
3187 | hba[i]->errinfo_pool, | 3244 | hba[i]->errinfo_pool, |
3188 | hba[i]->errinfo_pool_dhandle); | 3245 | hba[i]->errinfo_pool_dhandle); |
3189 | free_irq(hba[i]->intr, hba[i]); | 3246 | free_irq(hba[i]->intr[SIMPLE_MODE_INT], hba[i]); |
3190 | clean2: | 3247 | clean2: |
3191 | unregister_blkdev(hba[i]->major, hba[i]->devname); | 3248 | unregister_blkdev(hba[i]->major, hba[i]->devname); |
3192 | clean1: | 3249 | clean1: |
@@ -3227,7 +3284,15 @@ static void __devexit cciss_remove_one (struct pci_dev *pdev) | |||
3227 | printk(KERN_WARNING "Error Flushing cache on controller %d\n", | 3284 | printk(KERN_WARNING "Error Flushing cache on controller %d\n", |
3228 | i); | 3285 | i); |
3229 | } | 3286 | } |
3230 | free_irq(hba[i]->intr, hba[i]); | 3287 | free_irq(hba[i]->intr[2], hba[i]); |
3288 | |||
3289 | #ifdef CONFIG_PCI_MSI | ||
3290 | if (hba[i]->msix_vector) | ||
3291 | pci_disable_msix(hba[i]->pdev); | ||
3292 | else if (hba[i]->msi_vector) | ||
3293 | pci_disable_msi(hba[i]->pdev); | ||
3294 | #endif /* CONFIG_PCI_MSI */ | ||
3295 | |||
3231 | pci_set_drvdata(pdev, NULL); | 3296 | pci_set_drvdata(pdev, NULL); |
3232 | iounmap(hba[i]->vaddr); | 3297 | iounmap(hba[i]->vaddr); |
3233 | cciss_unregister_scsi(i); /* unhook from SCSI subsystem */ | 3298 | cciss_unregister_scsi(i); /* unhook from SCSI subsystem */ |
diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h index 3b0858c83897..ad45e581a91d 100644 --- a/drivers/block/cciss.h +++ b/drivers/block/cciss.h | |||
@@ -65,7 +65,6 @@ struct ctlr_info | |||
65 | unsigned long io_mem_addr; | 65 | unsigned long io_mem_addr; |
66 | unsigned long io_mem_length; | 66 | unsigned long io_mem_length; |
67 | CfgTable_struct __iomem *cfgtable; | 67 | CfgTable_struct __iomem *cfgtable; |
68 | unsigned int intr; | ||
69 | int interrupts_enabled; | 68 | int interrupts_enabled; |
70 | int major; | 69 | int major; |
71 | int max_commands; | 70 | int max_commands; |
@@ -74,6 +73,13 @@ struct ctlr_info | |||
74 | int num_luns; | 73 | int num_luns; |
75 | int highest_lun; | 74 | int highest_lun; |
76 | int usage_count; /* number of opens all all minor devices */ | 75 | int usage_count; /* number of opens all all minor devices */ |
76 | # define DOORBELL_INT 0 | ||
77 | # define PERF_MODE_INT 1 | ||
78 | # define SIMPLE_MODE_INT 2 | ||
79 | # define MEMQ_MODE_INT 3 | ||
80 | unsigned int intr[4]; | ||
81 | unsigned int msix_vector; | ||
82 | unsigned int msi_vector; | ||
77 | 83 | ||
78 | // information about each logical volume | 84 | // information about each logical volume |
79 | drive_info_struct drv[CISS_MAX_LUN]; | 85 | drive_info_struct drv[CISS_MAX_LUN]; |
diff --git a/drivers/block/cciss_scsi.c b/drivers/block/cciss_scsi.c index 2942d32280a5..9e35de05d5c5 100644 --- a/drivers/block/cciss_scsi.c +++ b/drivers/block/cciss_scsi.c | |||
@@ -714,7 +714,7 @@ cciss_scsi_detect(int ctlr) | |||
714 | ((struct cciss_scsi_adapter_data_t *) | 714 | ((struct cciss_scsi_adapter_data_t *) |
715 | hba[ctlr]->scsi_ctlr)->scsi_host = (void *) sh; | 715 | hba[ctlr]->scsi_ctlr)->scsi_host = (void *) sh; |
716 | sh->hostdata[0] = (unsigned long) hba[ctlr]; | 716 | sh->hostdata[0] = (unsigned long) hba[ctlr]; |
717 | sh->irq = hba[ctlr]->intr; | 717 | sh->irq = hba[ctlr]->intr[SIMPLE_MODE_INT]; |
718 | sh->unique_id = sh->irq; | 718 | sh->unique_id = sh->irq; |
719 | error = scsi_add_host(sh, &hba[ctlr]->pdev->dev); | 719 | error = scsi_add_host(sh, &hba[ctlr]->pdev->dev); |
720 | if (error) | 720 | if (error) |