diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/megaraid/megaraid_mbox.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/scsi/megaraid/megaraid_mbox.c')
-rw-r--r-- | drivers/scsi/megaraid/megaraid_mbox.c | 4276 |
1 files changed, 4276 insertions, 0 deletions
diff --git a/drivers/scsi/megaraid/megaraid_mbox.c b/drivers/scsi/megaraid/megaraid_mbox.c new file mode 100644 index 00000000000..138fa481583 --- /dev/null +++ b/drivers/scsi/megaraid/megaraid_mbox.c @@ -0,0 +1,4276 @@ +/* + * + * Linux MegaRAID device driver + * + * Copyright (c) 2003-2004 LSI Logic Corporation. + * + * 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 the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * FILE : megaraid_mbox.c + * Version : v2.20.4.5 (Feb 03 2005) + * + * Authors: + * Atul Mukker <Atul.Mukker@lsil.com> + * Sreenivas Bagalkote <Sreenivas.Bagalkote@lsil.com> + * Manoj Jose <Manoj.Jose@lsil.com> + * + * List of supported controllers + * + * OEM Product Name VID DID SSVID SSID + * --- ------------ --- --- ---- ---- + * Dell PERC3/QC 101E 1960 1028 0471 + * Dell PERC3/DC 101E 1960 1028 0493 + * Dell PERC3/SC 101E 1960 1028 0475 + * Dell PERC3/Di 1028 1960 1028 0123 + * Dell PERC4/SC 1000 1960 1028 0520 + * Dell PERC4/DC 1000 1960 1028 0518 + * Dell PERC4/QC 1000 0407 1028 0531 + * Dell PERC4/Di 1028 000F 1028 014A + * Dell PERC 4e/Si 1028 0013 1028 016c + * Dell PERC 4e/Di 1028 0013 1028 016d + * Dell PERC 4e/Di 1028 0013 1028 016e + * Dell PERC 4e/Di 1028 0013 1028 016f + * Dell PERC 4e/Di 1028 0013 1028 0170 + * Dell PERC 4e/DC 1000 0408 1028 0002 + * Dell PERC 4e/SC 1000 0408 1028 0001 + * + * + * LSI MegaRAID SCSI 320-0 1000 1960 1000 A520 + * LSI MegaRAID SCSI 320-1 1000 1960 1000 0520 + * LSI MegaRAID SCSI 320-2 1000 1960 1000 0518 + * LSI MegaRAID SCSI 320-0X 1000 0407 1000 0530 + * LSI MegaRAID SCSI 320-2X 1000 0407 1000 0532 + * LSI MegaRAID SCSI 320-4X 1000 0407 1000 0531 + * LSI MegaRAID SCSI 320-1E 1000 0408 1000 0001 + * LSI MegaRAID SCSI 320-2E 1000 0408 1000 0002 + * LSI MegaRAID SATA 150-4 1000 1960 1000 4523 + * LSI MegaRAID SATA 150-6 1000 1960 1000 0523 + * LSI MegaRAID SATA 300-4X 1000 0409 1000 3004 + * LSI MegaRAID SATA 300-8X 1000 0409 1000 3008 + * + * INTEL RAID Controller SRCU42X 1000 0407 8086 0532 + * INTEL RAID Controller SRCS16 1000 1960 8086 0523 + * INTEL RAID Controller SRCU42E 1000 0408 8086 0002 + * INTEL RAID Controller SRCZCRX 1000 0407 8086 0530 + * INTEL RAID Controller SRCS28X 1000 0409 8086 3008 + * INTEL RAID Controller SROMBU42E 1000 0408 8086 3431 + * INTEL RAID Controller SROMBU42E 1000 0408 8086 3499 + * INTEL RAID Controller SRCU51L 1000 1960 8086 0520 + * + * FSC MegaRAID PCI Express ROMB 1000 0408 1734 1065 + * + * ACER MegaRAID ROMB-2E 1000 0408 1025 004D + * + * NEC MegaRAID PCI Express ROMB 1000 0408 1033 8287 + * + * For history of changes, see Documentation/ChangeLog.megaraid + */ + +#include "megaraid_mbox.h" + +static int megaraid_init(void); +static void megaraid_exit(void); + +static int megaraid_probe_one(struct pci_dev*, const struct pci_device_id *); +static void megaraid_detach_one(struct pci_dev *); +static void megaraid_mbox_shutdown(struct device *); + +static int megaraid_io_attach(adapter_t *); +static void megaraid_io_detach(adapter_t *); + +static int megaraid_init_mbox(adapter_t *); +static void megaraid_fini_mbox(adapter_t *); + +static int megaraid_alloc_cmd_packets(adapter_t *); +static void megaraid_free_cmd_packets(adapter_t *); + +static int megaraid_mbox_setup_dma_pools(adapter_t *); +static void megaraid_mbox_teardown_dma_pools(adapter_t *); + +static int megaraid_sysfs_alloc_resources(adapter_t *); +static void megaraid_sysfs_free_resources(adapter_t *); + +static int megaraid_abort_handler(struct scsi_cmnd *); +static int megaraid_reset_handler(struct scsi_cmnd *); + +static int mbox_post_sync_cmd(adapter_t *, uint8_t []); +static int mbox_post_sync_cmd_fast(adapter_t *, uint8_t []); +static int megaraid_busywait_mbox(mraid_device_t *); +static int megaraid_mbox_product_info(adapter_t *); +static int megaraid_mbox_extended_cdb(adapter_t *); +static int megaraid_mbox_support_ha(adapter_t *, uint16_t *); +static int megaraid_mbox_support_random_del(adapter_t *); +static int megaraid_mbox_get_max_sg(adapter_t *); +static void megaraid_mbox_enum_raid_scsi(adapter_t *); +static void megaraid_mbox_flush_cache(adapter_t *); + +static void megaraid_mbox_display_scb(adapter_t *, scb_t *); +static void megaraid_mbox_setup_device_map(adapter_t *); + +static int megaraid_queue_command(struct scsi_cmnd *, + void (*)(struct scsi_cmnd *)); +static scb_t *megaraid_mbox_build_cmd(adapter_t *, struct scsi_cmnd *, int *); +static void megaraid_mbox_runpendq(adapter_t *, scb_t *); +static void megaraid_mbox_prepare_pthru(adapter_t *, scb_t *, + struct scsi_cmnd *); +static void megaraid_mbox_prepare_epthru(adapter_t *, scb_t *, + struct scsi_cmnd *); + +static irqreturn_t megaraid_isr(int, void *, struct pt_regs *); + +static void megaraid_mbox_dpc(unsigned long); + +static ssize_t megaraid_sysfs_show_app_hndl(struct class_device *, char *); +static ssize_t megaraid_sysfs_show_ldnum(struct device *, char *); + +static int megaraid_cmm_register(adapter_t *); +static int megaraid_cmm_unregister(adapter_t *); +static int megaraid_mbox_mm_handler(unsigned long, uioc_t *, uint32_t); +static int megaraid_mbox_mm_command(adapter_t *, uioc_t *); +static void megaraid_mbox_mm_done(adapter_t *, scb_t *); +static int gather_hbainfo(adapter_t *, mraid_hba_info_t *); +static int wait_till_fw_empty(adapter_t *); + + + +MODULE_AUTHOR("LSI Logic Corporation"); +MODULE_DESCRIPTION("LSI Logic MegaRAID Mailbox Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(MEGARAID_VERSION); + +/* + * ### modules parameters for driver ### + */ + +/** + * Set to enable driver to expose unconfigured disk to kernel + */ +static int megaraid_expose_unconf_disks = 0; +module_param_named(unconf_disks, megaraid_expose_unconf_disks, int, 0); +MODULE_PARM_DESC(unconf_disks, + "Set to expose unconfigured disks to kernel (default=0)"); + +/** + * driver wait time if the adapter's mailbox is busy + */ +static unsigned int max_mbox_busy_wait = MBOX_BUSY_WAIT; +module_param_named(busy_wait, max_mbox_busy_wait, int, 0); +MODULE_PARM_DESC(busy_wait, + "Max wait for mailbox in microseconds if busy (default=10)"); + +/** + * number of sectors per IO command + */ +static unsigned int megaraid_max_sectors = MBOX_MAX_SECTORS; +module_param_named(max_sectors, megaraid_max_sectors, int, 0); +MODULE_PARM_DESC(max_sectors, + "Maximum number of sectors per IO command (default=128)"); + +/** + * number of commands per logical unit + */ +static unsigned int megaraid_cmd_per_lun = MBOX_DEF_CMD_PER_LUN; +module_param_named(cmd_per_lun, megaraid_cmd_per_lun, int, 0); +MODULE_PARM_DESC(cmd_per_lun, + "Maximum number of commands per logical unit (default=64)"); + + +/** + * Fast driver load option, skip scanning for physical devices during load. + * This would result in non-disk devices being skipped during driver load + * time. These can be later added though, using /proc/scsi/scsi + */ +static unsigned int megaraid_fast_load = 0; +module_param_named(fast_load, megaraid_fast_load, int, 0); +MODULE_PARM_DESC(fast_load, + "Faster loading of the driver, skips physical devices! (default=0)"); + + +/** + * mraid_debug level - threshold for amount of information to be displayed by + * the driver. This level can be changed through modules parameters, ioctl or + * sysfs/proc interface. By default, print the announcement messages only. + */ +int mraid_debug_level = CL_ANN; +module_param_named(debug_level, mraid_debug_level, int, 0); +MODULE_PARM_DESC(debug_level, "Debug level for driver (default=0)"); + +/* + * ### global data ### + */ +static uint8_t megaraid_mbox_version[8] = + { 0x02, 0x20, 0x04, 0x05, 2, 3, 20, 5 }; + + +/* + * PCI table for all supported controllers. + */ +static struct pci_device_id pci_id_table_g[] = { + { + PCI_VENDOR_ID_DELL, + PCI_DEVICE_ID_PERC4_DI_DISCOVERY, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4_DI_DISCOVERY, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_PERC4_SC, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4_SC, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_PERC4_DC, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4_DC, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_PERC4_QC, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4_QC, + }, + { + PCI_VENDOR_ID_DELL, + PCI_DEVICE_ID_PERC4_DI_EVERGLADES, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4_DI_EVERGLADES, + }, + { + PCI_VENDOR_ID_DELL, + PCI_DEVICE_ID_PERC4E_SI_BIGBEND, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4E_SI_BIGBEND, + }, + { + PCI_VENDOR_ID_DELL, + PCI_DEVICE_ID_PERC4E_DI_KOBUK, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4E_DI_KOBUK, + }, + { + PCI_VENDOR_ID_DELL, + PCI_DEVICE_ID_PERC4E_DI_CORVETTE, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4E_DI_CORVETTE, + }, + { + PCI_VENDOR_ID_DELL, + PCI_DEVICE_ID_PERC4E_DI_EXPEDITION, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4E_DI_EXPEDITION, + }, + { + PCI_VENDOR_ID_DELL, + PCI_DEVICE_ID_PERC4E_DI_GUADALUPE, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4E_DI_GUADALUPE, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_PERC4E_DC_320_2E, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4E_DC_320_2E, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_PERC4E_SC_320_1E, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4E_SC_320_1E, + }, + { + PCI_VENDOR_ID_AMI, + PCI_DEVICE_ID_AMI_MEGARAID3, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC3_QC, + }, + { + PCI_VENDOR_ID_AMI, + PCI_DEVICE_ID_AMI_MEGARAID3, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC3_DC, + }, + { + PCI_VENDOR_ID_AMI, + PCI_DEVICE_ID_AMI_MEGARAID3, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC3_SC, + }, + { + PCI_VENDOR_ID_AMI, + PCI_DEVICE_ID_AMI_MEGARAID3, + PCI_VENDOR_ID_AMI, + PCI_SUBSYS_ID_PERC3_SC, + }, + { + PCI_VENDOR_ID_AMI, + PCI_DEVICE_ID_AMI_MEGARAID3, + PCI_VENDOR_ID_AMI, + PCI_SUBSYS_ID_PERC3_DC, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SCSI_320_0, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SCSI_320_0, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SCSI_320_1, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SCSI_320_1, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SCSI_320_2, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SCSI_320_2, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SCSI_320_0x, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SCSI_320_0x, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SCSI_320_2x, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SCSI_320_2x, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SCSI_320_4x, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SCSI_320_4x, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SCSI_320_1E, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SCSI_320_1E, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SCSI_320_2E, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SCSI_320_2E, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_I4_133_RAID, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_I4_133_RAID, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SATA_150_4, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SATA_150_4, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SATA_150_6, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SATA_150_6, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SATA_300_4x, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SATA_300_4x, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SATA_300_8x, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SATA_300_8x, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_INTEL_RAID_SRCU42X, + PCI_VENDOR_ID_INTEL, + PCI_SUBSYS_ID_INTEL_RAID_SRCU42X, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_INTEL_RAID_SRCS16, + PCI_VENDOR_ID_INTEL, + PCI_SUBSYS_ID_INTEL_RAID_SRCS16, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_INTEL_RAID_SRCU42E, + PCI_VENDOR_ID_INTEL, + PCI_SUBSYS_ID_INTEL_RAID_SRCU42E, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_INTEL_RAID_SRCZCRX, + PCI_VENDOR_ID_INTEL, + PCI_SUBSYS_ID_INTEL_RAID_SRCZCRX, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_INTEL_RAID_SRCS28X, + PCI_VENDOR_ID_INTEL, + PCI_SUBSYS_ID_INTEL_RAID_SRCS28X, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_INTEL_RAID_SROMBU42E_ALIEF, + PCI_VENDOR_ID_INTEL, + PCI_SUBSYS_ID_INTEL_RAID_SROMBU42E_ALIEF, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_INTEL_RAID_SROMBU42E_HARWICH, + PCI_VENDOR_ID_INTEL, + PCI_SUBSYS_ID_INTEL_RAID_SROMBU42E_HARWICH, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_INTEL_RAID_SRCU41L_LAKE_SHETEK, + PCI_VENDOR_ID_INTEL, + PCI_SUBSYS_ID_INTEL_RAID_SRCU41L_LAKE_SHETEK, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_FSC_MEGARAID_PCI_EXPRESS_ROMB, + PCI_SUBSYS_ID_FSC, + PCI_SUBSYS_ID_FSC_MEGARAID_PCI_EXPRESS_ROMB, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_ACER_ROMB_2E, + PCI_VENDOR_ID_AI, + PCI_SUBSYS_ID_MEGARAID_ACER_ROMB_2E, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_NEC_ROMB_2E, + PCI_VENDOR_ID_NEC, + PCI_SUBSYS_ID_MEGARAID_NEC_ROMB_2E, + }, + {0} /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(pci, pci_id_table_g); + + +static struct pci_driver megaraid_pci_driver_g = { + .name = "megaraid", + .id_table = pci_id_table_g, + .probe = megaraid_probe_one, + .remove = __devexit_p(megaraid_detach_one), + .driver = { + .shutdown = megaraid_mbox_shutdown, + } +}; + + + +// definitions for the device attributes for exporting logical drive number +// for a scsi address (Host, Channel, Id, Lun) + +CLASS_DEVICE_ATTR(megaraid_mbox_app_hndl, S_IRUSR, megaraid_sysfs_show_app_hndl, + NULL); + +// Host template initializer for megaraid mbox sysfs device attributes +static struct class_device_attribute *megaraid_shost_attrs[] = { + &class_device_attr_megaraid_mbox_app_hndl, + NULL, +}; + + +DEVICE_ATTR(megaraid_mbox_ld, S_IRUSR, megaraid_sysfs_show_ldnum, NULL); + +// Host template initializer for megaraid mbox sysfs device attributes +static struct device_attribute *megaraid_sdev_attrs[] = { + &dev_attr_megaraid_mbox_ld, + NULL, +}; + + +/* + * Scsi host template for megaraid unified driver + */ +static struct scsi_host_template megaraid_template_g = { + .module = THIS_MODULE, + .name = "LSI Logic MegaRAID driver", + .proc_name = "megaraid", + .queuecommand = megaraid_queue_command, + .eh_abort_handler = megaraid_abort_handler, + .eh_device_reset_handler = megaraid_reset_handler, + .eh_bus_reset_handler = megaraid_reset_handler, + .eh_host_reset_handler = megaraid_reset_handler, + .use_clustering = ENABLE_CLUSTERING, + .sdev_attrs = megaraid_sdev_attrs, + .shost_attrs = megaraid_shost_attrs, +}; + + +/** + * megaraid_init - module load hook + * + * We register ourselves as hotplug enabled module and let PCI subsystem + * discover our adaters + **/ +static int __init +megaraid_init(void) +{ + int rval; + + // Announce the driver version + con_log(CL_ANN, (KERN_INFO "megaraid: %s %s\n", MEGARAID_VERSION, + MEGARAID_EXT_VERSION)); + + // check validity of module parameters + if (megaraid_cmd_per_lun > MBOX_MAX_SCSI_CMDS) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid mailbox: max commands per lun reset to %d\n", + MBOX_MAX_SCSI_CMDS)); + + megaraid_cmd_per_lun = MBOX_MAX_SCSI_CMDS; + } + + + // register as a PCI hot-plug driver module + if ((rval = pci_module_init(&megaraid_pci_driver_g))) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: could not register hotplug support.\n")); + } + + return rval; +} + + +/** + * megaraid_exit - driver unload entry point + * + * We simply unwrap the megaraid_init routine here + */ +static void __exit +megaraid_exit(void) +{ + con_log(CL_DLEVEL1, (KERN_NOTICE "megaraid: unloading framework\n")); + + // unregister as PCI hotplug driver + pci_unregister_driver(&megaraid_pci_driver_g); + + return; +} + + +/** + * megaraid_probe_one - PCI hotplug entry point + * @param pdev : handle to this controller's PCI configuration space + * @param id : pci device id of the class of controllers + * + * This routine should be called whenever a new adapter is detected by the + * PCI hotplug susbsytem. + **/ +static int __devinit +megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) +{ + adapter_t *adapter; + + + // detected a new controller + con_log(CL_ANN, (KERN_INFO + "megaraid: probe new device %#4.04x:%#4.04x:%#4.04x:%#4.04x: ", + pdev->vendor, pdev->device, pdev->subsystem_vendor, + pdev->subsystem_device)); + + con_log(CL_ANN, ("bus %d:slot %d:func %d\n", pdev->bus->number, + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn))); + + if (pci_enable_device(pdev)) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: pci_enable_device failed\n")); + + return -ENODEV; + } + + // Enable bus-mastering on this controller + pci_set_master(pdev); + + // Allocate the per driver initialization structure + adapter = kmalloc(sizeof(adapter_t), GFP_KERNEL); + + if (adapter == NULL) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: out of memory, %s %d.\n", __FUNCTION__, __LINE__)); + + goto out_probe_one; + } + memset(adapter, 0, sizeof(adapter_t)); + + + // set up PCI related soft state and other pre-known parameters + adapter->unique_id = pdev->bus->number << 8 | pdev->devfn; + adapter->irq = pdev->irq; + adapter->pdev = pdev; + + atomic_set(&adapter->being_detached, 0); + + // Setup the default DMA mask. This would be changed later on + // depending on hardware capabilities + if (pci_set_dma_mask(adapter->pdev, 0xFFFFFFFF) != 0) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid: pci_set_dma_mask failed:%d\n", __LINE__)); + + goto out_free_adapter; + } + + + // Initialize the synchronization lock for kernel and LLD + spin_lock_init(&adapter->lock); + adapter->host_lock = &adapter->lock; + + + // Initialize the command queues: the list of free SCBs and the list + // of pending SCBs. + INIT_LIST_HEAD(&adapter->kscb_pool); + spin_lock_init(SCSI_FREE_LIST_LOCK(adapter)); + + INIT_LIST_HEAD(&adapter->pend_list); + spin_lock_init(PENDING_LIST_LOCK(adapter)); + + INIT_LIST_HEAD(&adapter->completed_list); + spin_lock_init(COMPLETED_LIST_LOCK(adapter)); + + + // Start the mailbox based controller + if (megaraid_init_mbox(adapter) != 0) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: maibox adapter did not initialize\n")); + + goto out_free_adapter; + } + + // Register with LSI Common Management Module + if (megaraid_cmm_register(adapter) != 0) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid: could not register with management module\n")); + + goto out_fini_mbox; + } + + // setup adapter handle in PCI soft state + pci_set_drvdata(pdev, adapter); + + // attach with scsi mid-layer + if (megaraid_io_attach(adapter) != 0) { + + con_log(CL_ANN, (KERN_WARNING "megaraid: io attach failed\n")); + + goto out_cmm_unreg; + } + + return 0; + +out_cmm_unreg: + pci_set_drvdata(pdev, NULL); + megaraid_cmm_unregister(adapter); +out_fini_mbox: + megaraid_fini_mbox(adapter); +out_free_adapter: + kfree(adapter); +out_probe_one: + pci_disable_device(pdev); + + return -ENODEV; +} + + +/** + * megaraid_detach_one - release the framework resources and call LLD release + * routine + * @param pdev : handle for our PCI cofiguration space + * + * This routine is called during driver unload. We free all the allocated + * resources and call the corresponding LLD so that it can also release all + * its resources. + * + * This routine is also called from the PCI hotplug system + **/ +static void +megaraid_detach_one(struct pci_dev *pdev) +{ + adapter_t *adapter; + struct Scsi_Host *host; + + + // Start a rollback on this adapter + adapter = pci_get_drvdata(pdev); + + if (!adapter) { + con_log(CL_ANN, (KERN_CRIT + "megaraid: Invalid detach on %#4.04x:%#4.04x:%#4.04x:%#4.04x\n", + pdev->vendor, pdev->device, pdev->subsystem_vendor, + pdev->subsystem_device)); + + return; + } + else { + con_log(CL_ANN, (KERN_NOTICE + "megaraid: detaching device %#4.04x:%#4.04x:%#4.04x:%#4.04x\n", + pdev->vendor, pdev->device, pdev->subsystem_vendor, + pdev->subsystem_device)); + } + + + host = adapter->host; + + // do not allow any more requests from the management module for this + // adapter. + // FIXME: How do we account for the request which might still be + // pending with us? + atomic_set(&adapter->being_detached, 1); + + // detach from the IO sub-system + megaraid_io_detach(adapter); + + // reset the device state in the PCI structure. We check this + // condition when we enter here. If the device state is NULL, + // that would mean the device has already been removed + pci_set_drvdata(pdev, NULL); + + // Unregister from common management module + // + // FIXME: this must return success or failure for conditions if there + // is a command pending with LLD or not. + megaraid_cmm_unregister(adapter); + + // finalize the mailbox based controller and release all resources + megaraid_fini_mbox(adapter); + + kfree(adapter); + + scsi_host_put(host); + + pci_disable_device(pdev); + + return; +} + + +/** + * megaraid_mbox_shutdown - PCI shutdown for megaraid HBA + * @param device : generice driver model device + * + * Shutdown notification, perform flush cache + */ +static void +megaraid_mbox_shutdown(struct device *device) +{ + adapter_t *adapter = pci_get_drvdata(to_pci_dev(device)); + static int counter; + + if (!adapter) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: null device in shutdown\n")); + return; + } + + // flush caches now + con_log(CL_ANN, (KERN_INFO "megaraid: flushing adapter %d...", + counter++)); + + megaraid_mbox_flush_cache(adapter); + + con_log(CL_ANN, ("done\n")); +} + + +/** + * megaraid_io_attach - attach a device with the IO subsystem + * @param adapter : controller's soft state + * + * Attach this device with the IO subsystem + **/ +static int +megaraid_io_attach(adapter_t *adapter) +{ + struct Scsi_Host *host; + + // Initialize SCSI Host structure + host = scsi_host_alloc(&megaraid_template_g, 8); + if (!host) { + con_log(CL_ANN, (KERN_WARNING + "megaraid mbox: scsi_register failed\n")); + + return -1; + } + + SCSIHOST2ADAP(host) = (caddr_t)adapter; + adapter->host = host; + + // export the parameters required by the mid-layer + scsi_assign_lock(host, adapter->host_lock); + scsi_set_device(host, &adapter->pdev->dev); + + host->irq = adapter->irq; + host->unique_id = adapter->unique_id; + host->can_queue = adapter->max_cmds; + host->this_id = adapter->init_id; + host->sg_tablesize = adapter->sglen; + host->max_sectors = adapter->max_sectors; + host->cmd_per_lun = adapter->cmd_per_lun; + host->max_channel = adapter->max_channel; + host->max_id = adapter->max_target; + host->max_lun = adapter->max_lun; + + + // notify mid-layer about the new controller + if (scsi_add_host(host, &adapter->pdev->dev)) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid mbox: scsi_add_host failed\n")); + + scsi_host_put(host); + + return -1; + } + + scsi_scan_host(host); + + return 0; +} + + +/** + * megaraid_io_detach - detach a device from the IO subsystem + * @param adapter : controller's soft state + * + * Detach this device from the IO subsystem + **/ +static void +megaraid_io_detach(adapter_t *adapter) +{ + struct Scsi_Host *host; + + con_log(CL_DLEVEL1, (KERN_INFO "megaraid: io detach\n")); + + host = adapter->host; + + scsi_remove_host(host); + + return; +} + + +/* + * START: Mailbox Low Level Driver + * + * This is section specific to the single mailbox based controllers + */ + +/** + * megaraid_init_mbox - initialize controller + * @param adapter - our soft state + * + * . Allocate 16-byte aligned mailbox memory for firmware handshake + * . Allocate controller's memory resources + * . Find out all initialization data + * . Allocate memory required for all the commands + * . Use internal library of FW routines, build up complete soft state + */ +static int __init +megaraid_init_mbox(adapter_t *adapter) +{ + struct pci_dev *pdev; + mraid_device_t *raid_dev; + int i; + + + adapter->ito = MBOX_TIMEOUT; + pdev = adapter->pdev; + + /* + * Allocate and initialize the init data structure for mailbox + * controllers + */ + raid_dev = kmalloc(sizeof(mraid_device_t), GFP_KERNEL); + if (raid_dev == NULL) return -1; + + memset(raid_dev, 0, sizeof(mraid_device_t)); + + /* + * Attach the adapter soft state to raid device soft state + */ + adapter->raid_device = (caddr_t)raid_dev; + raid_dev->fast_load = megaraid_fast_load; + + + // our baseport + raid_dev->baseport = pci_resource_start(pdev, 0); + + if (pci_request_regions(pdev, "MegaRAID: LSI Logic Corporation") != 0) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid: mem region busy\n")); + + goto out_free_raid_dev; + } + + raid_dev->baseaddr = ioremap_nocache(raid_dev->baseport, 128); + + if (!raid_dev->baseaddr) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid: could not map hba memory\n") ); + + goto out_release_regions; + } + + // + // Setup the rest of the soft state using the library of FW routines + // + + // request IRQ and register the interrupt service routine + if (request_irq(adapter->irq, megaraid_isr, SA_SHIRQ, "megaraid", + adapter)) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid: Couldn't register IRQ %d!\n", adapter->irq)); + + goto out_iounmap; + } + + + // initialize the mutual exclusion lock for the mailbox + spin_lock_init(&raid_dev->mailbox_lock); + + // allocate memory required for commands + if (megaraid_alloc_cmd_packets(adapter) != 0) { + goto out_free_irq; + } + + // Product info + if (megaraid_mbox_product_info(adapter) != 0) { + goto out_alloc_cmds; + } + + // Do we support extended CDBs + adapter->max_cdb_sz = 10; + if (megaraid_mbox_extended_cdb(adapter) == 0) { + adapter->max_cdb_sz = 16; + } + + /* + * Do we support cluster environment, if we do, what is the initiator + * id. + * NOTE: In a non-cluster aware firmware environment, the LLD should + * return 7 as initiator id. + */ + adapter->ha = 0; + adapter->init_id = -1; + if (megaraid_mbox_support_ha(adapter, &adapter->init_id) == 0) { + adapter->ha = 1; + } + + /* + * Prepare the device ids array to have the mapping between the kernel + * device address and megaraid device address. + * We export the physical devices on their actual addresses. The + * logical drives are exported on a virtual SCSI channel + */ + megaraid_mbox_setup_device_map(adapter); + + // If the firmware supports random deletion, update the device id map + if (megaraid_mbox_support_random_del(adapter)) { + + // Change the logical drives numbers in device_ids array one + // slot in device_ids is reserved for target id, that's why + // "<=" below + for (i = 0; i <= MAX_LOGICAL_DRIVES_40LD; i++) { + adapter->device_ids[adapter->max_channel][i] += 0x80; + } + adapter->device_ids[adapter->max_channel][adapter->init_id] = + 0xFF; + + raid_dev->random_del_supported = 1; + } + + /* + * find out the maximum number of scatter-gather elements supported by + * this firmware + */ + adapter->sglen = megaraid_mbox_get_max_sg(adapter); + + // enumerate RAID and SCSI channels so that all devices on SCSI + // channels can later be exported, including disk devices + megaraid_mbox_enum_raid_scsi(adapter); + + /* + * Other parameters required by upper layer + * + * maximum number of sectors per IO command + */ + adapter->max_sectors = megaraid_max_sectors; + + /* + * number of queued commands per LUN. + */ + adapter->cmd_per_lun = megaraid_cmd_per_lun; + + /* + * Allocate resources required to issue FW calls, when sysfs is + * accessed + */ + if (megaraid_sysfs_alloc_resources(adapter) != 0) { + goto out_alloc_cmds; + } + + // Set the DMA mask to 64-bit. All supported controllers as capable of + // DMA in this range + if (pci_set_dma_mask(adapter->pdev, 0xFFFFFFFFFFFFFFFFULL) != 0) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid: could not set DMA mask for 64-bit.\n")); + + goto out_free_sysfs_res; + } + + // setup tasklet for DPC + tasklet_init(&adapter->dpc_h, megaraid_mbox_dpc, + (unsigned long)adapter); + + con_log(CL_DLEVEL1, (KERN_INFO + "megaraid mbox hba successfully initialized\n")); + + return 0; + +out_free_sysfs_res: + megaraid_sysfs_free_resources(adapter); +out_alloc_cmds: + megaraid_free_cmd_packets(adapter); +out_free_irq: + free_irq(adapter->irq, adapter); +out_iounmap: + iounmap(raid_dev->baseaddr); +out_release_regions: + pci_release_regions(pdev); +out_free_raid_dev: + kfree(raid_dev); + + return -1; +} + + +/** + * megaraid_fini_mbox - undo controller initialization + * @param adapter : our soft state + */ +static void +megaraid_fini_mbox(adapter_t *adapter) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + + // flush all caches + megaraid_mbox_flush_cache(adapter); + + tasklet_kill(&adapter->dpc_h); + + megaraid_sysfs_free_resources(adapter); + + megaraid_free_cmd_packets(adapter); + + free_irq(adapter->irq, adapter); + + iounmap(raid_dev->baseaddr); + + pci_release_regions(adapter->pdev); + + kfree(raid_dev); + + return; +} + + +/** + * megaraid_alloc_cmd_packets - allocate shared mailbox + * @param adapter : soft state of the raid controller + * + * Allocate and align the shared mailbox. This maibox is used to issue + * all the commands. For IO based controllers, the mailbox is also regsitered + * with the FW. Allocate memory for all commands as well. + * This is our big allocator + */ +static int +megaraid_alloc_cmd_packets(adapter_t *adapter) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + struct pci_dev *pdev; + unsigned long align; + scb_t *scb; + mbox_ccb_t *ccb; + struct mraid_pci_blk *epthru_pci_blk; + struct mraid_pci_blk *sg_pci_blk; + struct mraid_pci_blk *mbox_pci_blk; + int i; + + pdev = adapter->pdev; + + /* + * Setup the mailbox + * Allocate the common 16-byte aligned memory for the handshake + * mailbox. + */ + raid_dev->una_mbox64 = pci_alloc_consistent(adapter->pdev, + sizeof(mbox64_t), &raid_dev->una_mbox64_dma); + + if (!raid_dev->una_mbox64) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: out of memory, %s %d\n", __FUNCTION__, + __LINE__)); + return -1; + } + memset(raid_dev->una_mbox64, 0, sizeof(mbox64_t)); + + /* + * Align the mailbox at 16-byte boundary + */ + raid_dev->mbox = &raid_dev->una_mbox64->mbox32; + + raid_dev->mbox = (mbox_t *)((((unsigned long)raid_dev->mbox) + 15) & + (~0UL ^ 0xFUL)); + + raid_dev->mbox64 = (mbox64_t *)(((unsigned long)raid_dev->mbox) - 8); + + align = ((void *)raid_dev->mbox - + ((void *)&raid_dev->una_mbox64->mbox32)); + + raid_dev->mbox_dma = (unsigned long)raid_dev->una_mbox64_dma + 8 + + align; + + // Allocate memory for commands issued internally + adapter->ibuf = pci_alloc_consistent(pdev, MBOX_IBUF_SIZE, + &adapter->ibuf_dma_h); + if (!adapter->ibuf) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid: out of memory, %s %d\n", __FUNCTION__, + __LINE__)); + + goto out_free_common_mbox; + } + memset(adapter->ibuf, 0, MBOX_IBUF_SIZE); + + // Allocate memory for our SCSI Command Blocks and their associated + // memory + + /* + * Allocate memory for the base list of scb. Later allocate memory for + * CCBs and embedded components of each CCB and point the pointers in + * scb to the allocated components + * NOTE: The code to allocate SCB will be duplicated in all the LLD + * since the calling routine does not yet know the number of available + * commands. + */ + adapter->kscb_list = kmalloc(sizeof(scb_t) * MBOX_MAX_SCSI_CMDS, + GFP_KERNEL); + + if (adapter->kscb_list == NULL) { |