New version of the mptsas driver that works with the new transport class code and supports extenders. Thanks a lot to Eric Moore for supply me with code to support reading extender information from the Fusion firmware. This needs all the fusion changes I sent out earlier applied, aswell as the SAS transport class patch I just posted. Index: scsi-misc-2.6/drivers/message/fusion/Kconfig =================================================================== --- scsi-misc-2.6.orig/drivers/message/fusion/Kconfig 2005-09-05 01:52:46.000000000 +0200 +++ scsi-misc-2.6/drivers/message/fusion/Kconfig 2005-09-05 01:55:20.000000000 +0200 @@ -35,6 +35,23 @@ LSIFC929X LSIFC929XL +config FUSION_SAS + tristate "Fusion MPT ScsiHost drivers for SAS" + depends on PCI && SCSI + select FUSION + select SCSI_SAS_ATTRS + ---help--- + SCSI HOST support for a SAS host adapters. + + List of supported controllers: + + LSISAS1064 + LSISAS1066 + LSISAS1068 + LSISAS1064E + LSISAS1066E + LSISAS1068E + config FUSION_MAX_SGE int "Maximum number of scatter gather entries (16 - 128)" depends on FUSION Index: scsi-misc-2.6/drivers/message/fusion/Makefile =================================================================== --- scsi-misc-2.6.orig/drivers/message/fusion/Makefile 2005-09-05 01:52:46.000000000 +0200 +++ scsi-misc-2.6/drivers/message/fusion/Makefile 2005-09-05 01:55:20.000000000 +0200 @@ -34,5 +34,6 @@ obj-$(CONFIG_FUSION_SPI) += mptbase.o mptscsih.o mptspi.o obj-$(CONFIG_FUSION_FC) += mptbase.o mptscsih.o mptfc.o +obj-$(CONFIG_FUSION_SAS) += mptbase.o mptscsih.o mptsas.o obj-$(CONFIG_FUSION_CTL) += mptctl.o obj-$(CONFIG_FUSION_LAN) += mptlan.o Index: scsi-misc-2.6/drivers/message/fusion/mptsas.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ scsi-misc-2.6/drivers/message/fusion/mptsas.c 2005-09-05 01:58:31.000000000 +0200 @@ -0,0 +1,1191 @@ +/* + * linux/drivers/message/fusion/mptsas.c + * For use with LSI Logic PCI chip/adapter(s) + * running LSI Logic Fusion MPT (Message Passing Technology) firmware. + * + * Copyright (c) 1999-2005 LSI Logic Corporation + * (mailto:mpt_linux_developer@xxxxxxxx) + * Copyright (c) 2005 Dell + */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + 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; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + NO WARRANTY + THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + solely responsible for determining the appropriateness of using and + distributing the Program and assumes all risks associated with its + exercise of rights under this Agreement, including but not limited to + the risks and costs of program errors, damage to or loss of data, + programs or equipment, and unavailability or interruption of operations. + + DISCLAIMER OF LIABILITY + NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/workqueue.h> + +#include <scsi/scsi_host.h> +#include <scsi/scsi_transport_sas.h> + +#include "mptbase.h" +#include "mptscsih.h" + + +#define my_NAME "Fusion MPT SAS Host driver" +#define my_VERSION MPT_LINUX_VERSION_COMMON +#define MYNAM "mptsas" + +MODULE_AUTHOR(MODULEAUTHOR); +MODULE_DESCRIPTION(my_NAME); +MODULE_LICENSE("GPL"); + +static int mpt_pq_filter; +module_param(mpt_pq_filter, int, 0); +MODULE_PARM_DESC(mpt_pq_filter, + "Enable peripheral qualifier filter: enable=1 " + "(default=0)"); + +static int mpt_pt_clear; +module_param(mpt_pt_clear, int, 0); +MODULE_PARM_DESC(mpt_pt_clear, + "Clear persistency table: enable=1 " + "(default=MPTSCSIH_PT_CLEAR=0)"); + +static int mptsasDoneCtx = -1; +static int mptsasTaskCtx = -1; +static int mptsasInternalCtx = -1; /* Used only for internal commands */ + + +/* + * SAS topology structures + * + * The MPT Fusion firmware interface spreads information about the + * SAS topology over many manufacture pages, thus we need some data + * structure to collect it and process it for the SAS transport class. + */ + +struct mptsas_devinfo { + u16 handle; /* unique id to address this device */ + u8 phy_id; /* phy number of parent device */ + u8 port_id; /* sas physical port this device + is assoc'd with */ + u8 target; /* logical target id of this device */ + u8 bus; /* logical bus number of this device */ + u64 sas_address; /* WWN of this device, + SATA is assigned by HBA,expander */ + u32 device_info; /* bitfield detailed info about this device */ +}; + +struct mptsas_phyinfo { + u8 phy_id; /* phy index */ + u8 port_id; /* port number this phy is part of */ + u8 negotiated_link_rate; /* nego'd link rate for this phy */ + u8 hw_link_rate; /* hardware max/min phys link rate */ + u8 programmed_link_rate; /* programmed max/min phy link rate */ + struct mptsas_devinfo identify; /* point to phy device info */ + struct mptsas_devinfo attached; /* point to attached device info */ +}; + +struct mptsas_portinfo { + u16 handle; /* unique id to address this */ + u8 num_phys; /* number of phys */ + struct mptsas_phyinfo *phy_info; +}; + + +//#define SASDEBUG 1 +#ifdef SASDEBUG +static void mptsas_print_phy_data(MPI_SAS_IO_UNIT0_PHY_DATA *phy_data) +{ + printk("---- IO UNIT PAGE 0 ------------\n"); + printk("Handle=0x%X\n", + le16_to_cpu(phy_data->AttachedDeviceHandle)); + printk("Controller Handle=0x%X\n", + le16_to_cpu(phy_data->ControllerDevHandle)); + printk("Port=0x%X\n", phy_data->Port); + printk("Port Flags=0x%X\n", phy_data->PortFlags); + printk("PHY Flags=0x%X\n", phy_data->PhyFlags); + printk("Negotiated Link Rate=0x%X\n", phy_data->NegotiatedLinkRate); + printk("Controller PHY Device Info=0x%X\n", + le32_to_cpu(phy_data->ControllerPhyDeviceInfo)); + printk("DiscoveryStatus=0x%X\n", + le32_to_cpu(phy_data->DiscoveryStatus)); + printk("\n"); +} + +static void mptsas_print_phy_pg0(SasPhyPage0_t *pg0) +{ + __le64 sas_address; + + memcpy(&sas_address, &pg0->SASAddress, sizeof(__le64)); + + printk("---- SAS PHY PAGE 0 ------------\n"); + printk("Attached Device Handle=0x%X\n", le16_to_cpu(pg0->AttachedDevHandle)); + printk("SAS Address=0x%llX\n", + (unsigned long long)le64_to_cpu(sas_address)); + printk("Attached PHY Identifier=0x%X\n", pg0->AttachedPhyIdentifier); + printk("Attached Device Info=0x%X\n", + le32_to_cpu(pg0->AttachedDeviceInfo)); + printk("Programmed Link Rate=0x%X\n", pg0->ProgrammedLinkRate); + printk("Change Count=0x%X\n", pg0->ChangeCount); + printk("PHY Info=0x%X\n", le32_to_cpu(pg0->PhyInfo)); + printk("\n"); +} + +static void mptsas_print_device_pg0(SasDevicePage0_t *pg0) +{ + __le64 sas_address; + + memcpy(&sas_address, &pg0->SASAddress, sizeof(__le64)); + + printk("---- SAS DEVICE PAGE 0 ---------\n"); + printk("Handle=0x%X\n" ,le16_to_cpu(pg0->DevHandle)); + printk("Enclosure Handle=0x%X\n", le16_to_cpu(pg0->EnclosureHandle)); + printk("Slot=0x%X\n", le16_to_cpu(pg0->Slot)); + printk("SAS Address=0x%llX\n", le64_to_cpu(sas_address)); + printk("Target ID=0x%X\n", pg0->TargetID); + printk("Bus=0x%X\n", pg0->Bus); + printk("PhyNum=0x%X\n", pg0->PhyNum); + printk("AccessStatus=0x%X\n", le16_to_cpu(pg0->AccessStatus)); + printk("Device Info=0x%X\n", le32_to_cpu(pg0->DeviceInfo)); + printk("Flags=0x%X\n", le16_to_cpu(pg0->Flags)); + printk("Physical Port=0x%X\n", pg0->PhysicalPort); + printk("\n"); +} + +static void mptsas_print_expander_pg1(SasExpanderPage1_t *pg1) +{ + printk("---- SAS EXPANDER PAGE 1 ------------\n"); + + printk("Physical Port=0x%X\n", pg1->PhysicalPort); + printk("PHY Identifier=0x%X\n", pg1->Phy); + printk("Negotiated Link Rate=0x%X\n", pg1->NegotiatedLinkRate); + printk("Programmed Link Rate=0x%X\n", pg1->ProgrammedLinkRate); + printk("Hardware Link Rate=0x%X\n", pg1->HwLinkRate); + printk("Owner Device Handle=0x%X\n", le16_to_cpu(pg1->OwnerDevHandle)); + printk("Attached Device Handle=0x%X\n", le16_to_cpu(pg1->AttachedDevHandle)); +} + +#else +#define mptsas_print_phy_data(phy_data) do { } while (0) +#define mptsas_print_phy_pg0(pg0) do { } while (0) +#define mptsas_print_device_pg0(pg0) do { } while (0) +#define mptsas_print_expander_pg1(pg1) do { } while (0) +#endif + +static u32 +mptsas_sas_io_unit_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info) +{ + ConfigExtendedPageHeader_t hdr; + CONFIGPARMS cfg; + SasIOUnitPage0_t *sasIoUnitPg0=NULL; + dma_addr_t sasIoUnitPg0_dma; + int sasIoUnitPg0_data_sz=0; + int ii,rc=0; + + if(!port_info || port_info->num_phys || port_info->phy_info ){ + rc=-1; + goto sas_io_unit_pg0_exit; + } + + hdr.PageVersion = MPI_SASIOUNITPAGE0_PAGEVERSION; + hdr.ExtPageLength = 0; + hdr.PageNumber = 0; + hdr.Reserved1 = 0; + hdr.Reserved2 = 0; + hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED; + hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT; + + cfg.cfghdr.ehdr = &hdr; + cfg.physAddr = -1; + cfg.pageAddr = 0; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + cfg.dir = 0; /* read */ + cfg.timeout = 10; + + if ((rc = mpt_config(ioc, &cfg)) != 0) { + rc=-1; + goto sas_io_unit_pg0_exit; + } + + if (hdr.ExtPageLength == 0) { + rc=-1; + goto sas_io_unit_pg0_exit; + } + + sasIoUnitPg0_data_sz = hdr.ExtPageLength * 4; + sasIoUnitPg0 = (SasIOUnitPage0_t *) pci_alloc_consistent(ioc->pcidev, + sasIoUnitPg0_data_sz, &sasIoUnitPg0_dma); + if (!sasIoUnitPg0) { + rc=-1; + goto sas_io_unit_pg0_exit; + } + + memset((u8 *)sasIoUnitPg0, 0, sasIoUnitPg0_data_sz); + cfg.physAddr = sasIoUnitPg0_dma; + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + + if ((rc = mpt_config(ioc, &cfg)) != 0) { + rc=-1; + goto sas_io_unit_pg0_exit; + } + + /* save config data */ + port_info->num_phys = sasIoUnitPg0->NumPhys; + port_info->phy_info = kcalloc(port_info->num_phys, + sizeof(struct mptsas_phyinfo),GFP_KERNEL); + if (!port_info->phy_info) { + rc=-1; + goto sas_io_unit_pg0_exit; + } + for (ii = 0; ii < port_info->num_phys; ii++) { + mptsas_print_phy_data(&sasIoUnitPg0->PhyData[ii]); + port_info->phy_info[ii].phy_id = ii; + port_info->phy_info[ii].port_id = + sasIoUnitPg0->PhyData[ii].Port; + port_info->phy_info[ii].negotiated_link_rate = + sasIoUnitPg0->PhyData[ii].NegotiatedLinkRate; + } + +sas_io_unit_pg0_exit: + + if (sasIoUnitPg0) + pci_free_consistent(ioc->pcidev, sasIoUnitPg0_data_sz, + (u8 *) sasIoUnitPg0, sasIoUnitPg0_dma); + + return rc; +} + + +static u32 +mptsas_sas_phy_pg0(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info, + u32 form, u32 form_specific) +{ + ConfigExtendedPageHeader_t hdr; + CONFIGPARMS cfg; + SasPhyPage0_t *sasPhyPg0=NULL; + dma_addr_t sasPhyPg0_dma; + int sasPhyPg0_data_sz=0; + int rc=0; + + if(!phy_info){ + rc=-1; + goto sas_device_pg0_exit; + } + + /* Issue a config request to get phy information. */ + hdr.PageVersion = MPI_SASPHY0_PAGEVERSION; + hdr.ExtPageLength = 0; + hdr.PageNumber = 0; + hdr.Reserved1 = 0; + hdr.Reserved2 = 0; + hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED; + hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_PHY; + + cfg.cfghdr.ehdr = &hdr; + cfg.dir = 0; /* read */ + cfg.timeout = 10; + + /* Get Phy Pg 0 for each Phy. */ + cfg.physAddr = -1; + cfg.pageAddr = form + form_specific; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + + if ((rc = mpt_config(ioc, &cfg)) != 0) { + rc=-1; + goto sas_device_pg0_exit; + } + + if (hdr.ExtPageLength == 0) { + rc=-1; + goto sas_device_pg0_exit; + } + + sasPhyPg0_data_sz = hdr.ExtPageLength * 4; + sasPhyPg0 = (SasPhyPage0_t *) pci_alloc_consistent( + ioc->pcidev, sasPhyPg0_data_sz, &sasPhyPg0_dma); + if (!sasPhyPg0) { + rc=-1; + goto sas_device_pg0_exit; + } + + memset((u8 *)sasPhyPg0, 0, sasPhyPg0_data_sz); + cfg.physAddr = sasPhyPg0_dma; + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + + if ((rc = mpt_config(ioc, &cfg)) != 0) { + rc=-1; + goto sas_device_pg0_exit; + } + + mptsas_print_phy_pg0(sasPhyPg0); + /* save config data */ + phy_info->hw_link_rate = sasPhyPg0->HwLinkRate; + phy_info->programmed_link_rate = sasPhyPg0->ProgrammedLinkRate; + phy_info->identify.handle = le16_to_cpu(sasPhyPg0->OwnerDevHandle); + phy_info->attached.handle = le16_to_cpu(sasPhyPg0->AttachedDevHandle); + +sas_device_pg0_exit: + + if (sasPhyPg0) + pci_free_consistent(ioc->pcidev, sasPhyPg0_data_sz, + (u8 *) sasPhyPg0, sasPhyPg0_dma); + + return rc; +} + +static u32 +mptsas_sas_device_pg0(MPT_ADAPTER *ioc, struct mptsas_devinfo *device_info, + u32 form, u32 form_specific) +{ + ConfigExtendedPageHeader_t hdr; + CONFIGPARMS cfg; + SasDevicePage0_t *sasDevicePg0=NULL; + dma_addr_t sasDevicePg0_dma; + int sasDevicePg0_data_sz=0; + int rc=0; + u64 SASAddress64; + + if(!device_info){ + rc=-1; + printk("no device info\n"); + goto sas_device_pg0_exit; + } + + hdr.PageVersion = MPI_SASDEVICE0_PAGEVERSION; + hdr.ExtPageLength = 0; + hdr.PageNumber = 0; + hdr.Reserved1 = 0; + hdr.Reserved2 = 0; + hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED; + hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE; + + cfg.cfghdr.ehdr = &hdr; + cfg.pageAddr = form + form_specific; + cfg.physAddr = -1; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + cfg.dir = 0; /* read */ + cfg.timeout = 10; + + if ((rc = mpt_config(ioc, &cfg)) != 0) { + printk("mpt_config failed"); + goto sas_device_pg0_exit; + } + + if (hdr.ExtPageLength == 0) { + printk("no exthdr"); + rc = -EFAULT; + goto sas_device_pg0_exit; + } + + sasDevicePg0_data_sz = hdr.ExtPageLength * 4; + sasDevicePg0 = (SasDevicePage0_t *) pci_alloc_consistent( + ioc->pcidev, sasDevicePg0_data_sz, &sasDevicePg0_dma); + if (!sasDevicePg0) { + printk("alloc failed"); + rc = -ENOMEM; + goto sas_device_pg0_exit; + } + + memset((u8 *)sasDevicePg0, 0, sasDevicePg0_data_sz); + cfg.physAddr = sasDevicePg0_dma; + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + + if ((rc = mpt_config(ioc, &cfg)) != 0) { + printk("second mpt_config failed\n"); + rc=-1; + goto sas_device_pg0_exit; + } + + mptsas_print_device_pg0(sasDevicePg0); + + /* save config data */ + device_info->handle = le16_to_cpu(sasDevicePg0->DevHandle); + device_info->phy_id = sasDevicePg0->PhyNum; + device_info->port_id = sasDevicePg0->PhysicalPort; + device_info->target = sasDevicePg0->TargetID; + device_info->bus = sasDevicePg0->Bus; + memcpy(&SASAddress64,&sasDevicePg0->SASAddress, + sizeof(sasDevicePg0->SASAddress)); + le64_to_cpus(&SASAddress64); + device_info->sas_address = SASAddress64; + device_info->device_info = + le32_to_cpu(sasDevicePg0->DeviceInfo); + +sas_device_pg0_exit: + + if (sasDevicePg0) + pci_free_consistent(ioc->pcidev, sasDevicePg0_data_sz, + (u8 *) sasDevicePg0, sasDevicePg0_dma); + + return rc; +} + + +static u32 +mptsas_sas_expander_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info, + u32 form, u32 form_specific) +{ + ConfigExtendedPageHeader_t hdr; + CONFIGPARMS cfg; + SasExpanderPage0_t *sasExpanderPg0=NULL; + dma_addr_t sasExpanderPg0_dma; + int sasExpanderPg0_data_sz=0; + int rc=0; + + hdr.PageVersion = MPI_SASEXPANDER0_PAGEVERSION; + hdr.ExtPageLength = 0; + hdr.PageNumber = 0; + hdr.Reserved1 = 0; + hdr.Reserved2 = 0; + hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED; + hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_EXPANDER; + + cfg.cfghdr.ehdr = &hdr; + cfg.physAddr = -1; + cfg.pageAddr = form + form_specific; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + cfg.dir = 0; /* read */ + cfg.timeout = 10; + + if ((rc = mpt_config(ioc, &cfg)) != 0) { + rc=-1; + goto sas_expander_pg0_exit; + } + + if (hdr.ExtPageLength == 0) { + rc=-1; + goto sas_expander_pg0_exit; + } + + sasExpanderPg0_data_sz = hdr.ExtPageLength * 4; + sasExpanderPg0 = (SasExpanderPage0_t *) pci_alloc_consistent(ioc->pcidev, + sasExpanderPg0_data_sz, &sasExpanderPg0_dma); + if (!sasExpanderPg0) { + rc=-1; + goto sas_expander_pg0_exit; + } + + memset((u8 *)sasExpanderPg0, 0, sasExpanderPg0_data_sz); + cfg.physAddr = sasExpanderPg0_dma; + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + + if ((rc = mpt_config(ioc, &cfg)) != 0) { + rc=-1; + goto sas_expander_pg0_exit; + } + + /* save config data */ + port_info->num_phys = sasExpanderPg0->NumPhys; + port_info->handle = le16_to_cpu(sasExpanderPg0->DevHandle); + port_info->phy_info = kcalloc(port_info->num_phys, + sizeof(struct mptsas_phyinfo),GFP_KERNEL); + if (!port_info->phy_info) { + rc=-1; + goto sas_expander_pg0_exit; + } + +sas_expander_pg0_exit: + + if (sasExpanderPg0) + pci_free_consistent(ioc->pcidev, sasExpanderPg0_data_sz, + (u8 *) sasExpanderPg0, sasExpanderPg0_dma); + + return rc; +} + +static u32 +mptsas_sas_expander_pg1(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info, + u32 form, u32 form_specific) +{ + ConfigExtendedPageHeader_t hdr; + CONFIGPARMS cfg; + SasExpanderPage1_t *sasExpanderPg1=NULL; + dma_addr_t sasExpanderPg1_dma; + int sasExpanderPg1_data_sz=0; + int rc=0; + + if(!phy_info) { + rc=-1; + goto sas_expander_pg1_exit; + } + + hdr.PageVersion = MPI_SASEXPANDER0_PAGEVERSION; + hdr.ExtPageLength = 0; + hdr.PageNumber = 1; + hdr.Reserved1 = 0; + hdr.Reserved2 = 0; + hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED; + hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_EXPANDER; + + cfg.cfghdr.ehdr = &hdr; + cfg.physAddr = -1; + cfg.pageAddr = form + form_specific; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + cfg.dir = 0; /* read */ + cfg.timeout = 10; + + if ((rc = mpt_config(ioc, &cfg)) != 0) { + rc=-1; + goto sas_expander_pg1_exit; + } + + if (hdr.ExtPageLength == 0) { + rc=-1; + goto sas_expander_pg1_exit; + } + + sasExpanderPg1_data_sz = hdr.ExtPageLength * 4; + sasExpanderPg1 = (SasExpanderPage1_t *) pci_alloc_consistent(ioc->pcidev, + sasExpanderPg1_data_sz, &sasExpanderPg1_dma); + if (!sasExpanderPg1) { + rc=-1; + goto sas_expander_pg1_exit; + } + + memset((u8 *)sasExpanderPg1, 0, sasExpanderPg1_data_sz); + cfg.physAddr = sasExpanderPg1_dma; + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + + if ((rc = mpt_config(ioc, &cfg)) != 0) { + rc=-1; + goto sas_expander_pg1_exit; + } + + mptsas_print_expander_pg1(sasExpanderPg1); + + /* save config data */ + phy_info->phy_id = sasExpanderPg1->Phy; + phy_info->port_id = sasExpanderPg1->PhysicalPort; + phy_info->negotiated_link_rate = sasExpanderPg1->NegotiatedLinkRate; + phy_info->programmed_link_rate = sasExpanderPg1->ProgrammedLinkRate; + phy_info->hw_link_rate = sasExpanderPg1->HwLinkRate; + phy_info->identify.handle = le16_to_cpu(sasExpanderPg1->OwnerDevHandle); + phy_info->attached.handle = le16_to_cpu(sasExpanderPg1->AttachedDevHandle); + + +sas_expander_pg1_exit: + + if (sasExpanderPg1) + pci_free_consistent(ioc->pcidev, sasExpanderPg1_data_sz, + (u8 *) sasExpanderPg1, sasExpanderPg1_dma); + + return rc; +} + +static void +mptsas_parse_device_info(struct sas_identify *identify, + struct mptsas_devinfo *device_info) +{ + u16 protocols; + + identify->sas_address = device_info->sas_address; + identify->phy_identifier = device_info->phy_id; + + /* + * Fill in Phy Initiator Port Protocol. + * Bits 6:3, more than one bit can be set, fall through cases. + */ + protocols = device_info->device_info & 0x78; + identify->initiator_port_protocols = 0; + if (protocols & MPI_SAS_DEVICE_INFO_SSP_INITIATOR) + identify->initiator_port_protocols |= SAS_PROTOCOL_SSP; + if (protocols & MPI_SAS_DEVICE_INFO_STP_INITIATOR) + identify->initiator_port_protocols |= SAS_PROTOCOL_STP; + if (protocols & MPI_SAS_DEVICE_INFO_SMP_INITIATOR) + identify->initiator_port_protocols |= SAS_PROTOCOL_SMP; + if (protocols & MPI_SAS_DEVICE_INFO_SATA_HOST) + identify->initiator_port_protocols |= SAS_PROTOCOL_SATA; + + /* + * Fill in Phy Target Port Protocol. + * Bits 10:7, more than one bit can be set, fall through cases. + */ + protocols = device_info->device_info & 0x780; + identify->target_port_protocols = 0; + if (protocols & MPI_SAS_DEVICE_INFO_SSP_TARGET) + identify->target_port_protocols |= SAS_PROTOCOL_SSP; + if (protocols & MPI_SAS_DEVICE_INFO_STP_TARGET) + identify->target_port_protocols |= SAS_PROTOCOL_STP; + if (protocols & MPI_SAS_DEVICE_INFO_SMP_TARGET) + identify->target_port_protocols |= SAS_PROTOCOL_SMP; + if (protocols & MPI_SAS_DEVICE_INFO_SATA_DEVICE) + identify->target_port_protocols |= SAS_PROTOCOL_SATA; + + /* + * Fill in Attached device type. + */ + switch (device_info->device_info & + MPI_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) { + case MPI_SAS_DEVICE_INFO_NO_DEVICE: + identify->device_type = SAS_PHY_UNUSED; + break; + case MPI_SAS_DEVICE_INFO_END_DEVICE: + identify->device_type = SAS_END_DEVICE; + break; + case MPI_SAS_DEVICE_INFO_EDGE_EXPANDER: + identify->device_type = SAS_EDGE_EXPANDER_DEVICE; + break; + case MPI_SAS_DEVICE_INFO_FANOUT_EXPANDER: + identify->device_type = SAS_FANOUT_EXPANDER_DEVICE; + break; + } +} + +static int mptsas_probe_one_phy(struct device *dev, + struct mptsas_phyinfo *phy_info, int index) +{ + struct sas_port *port; + + port = sas_port_alloc(dev, index); + if (!port) + return -ENOMEM; + + port->port_identifier = phy_info->port_id; + mptsas_parse_device_info(&port->identify, &phy_info->identify); + + /* + * Set Negotiated link rate. + */ + switch (phy_info->negotiated_link_rate) { + case MPI_SAS_IOUNIT0_RATE_PHY_DISABLED: + port->negotiated_linkrate = SAS_PHY_DISABLED; + break; + case MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION: + port->negotiated_linkrate = SAS_LINK_RATE_FAILED; + break; + case MPI_SAS_IOUNIT0_RATE_1_5: + port->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS; + break; + case MPI_SAS_IOUNIT0_RATE_3_0: + port->negotiated_linkrate = SAS_LINK_RATE_3_0_GBPS; + break; + case MPI_SAS_IOUNIT0_RATE_SATA_OOB_COMPLETE: + case MPI_SAS_IOUNIT0_RATE_UNKNOWN: + default: + port->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN; + break; + } + + /* + * Set Max hardware link rate. + */ + switch (phy_info->hw_link_rate & MPI_SAS_PHY0_PRATE_MAX_RATE_MASK) { + case MPI_SAS_PHY0_HWRATE_MAX_RATE_1_5: + port->maximum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS; + break; + case MPI_SAS_PHY0_PRATE_MAX_RATE_3_0: + port->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS; + break; + default: + break; + } + + /* + * Set Max programmed link rate. + */ + switch (phy_info->programmed_link_rate & MPI_SAS_PHY0_PRATE_MAX_RATE_MASK) { + case MPI_SAS_PHY0_PRATE_MAX_RATE_1_5: + port->maximum_linkrate = SAS_LINK_RATE_1_5_GBPS; + break; + case MPI_SAS_PHY0_PRATE_MAX_RATE_3_0: + port->maximum_linkrate = SAS_LINK_RATE_3_0_GBPS; + break; + default: + break; + } + + /* + * Set Min hardware link rate. + */ + switch (phy_info->hw_link_rate & MPI_SAS_PHY0_HWRATE_MIN_RATE_MASK) { + case MPI_SAS_PHY0_HWRATE_MIN_RATE_1_5: + port->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS; + break; + case MPI_SAS_PHY0_PRATE_MIN_RATE_3_0: + port->minimum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS; + break; + default: + break; + } + + /* + * Set Min programmed link rate. + */ + switch (phy_info->programmed_link_rate & MPI_SAS_PHY0_PRATE_MIN_RATE_MASK) { + case MPI_SAS_PHY0_PRATE_MIN_RATE_1_5: + port->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS; + break; + case MPI_SAS_PHY0_PRATE_MIN_RATE_3_0: + port->minimum_linkrate = SAS_LINK_RATE_3_0_GBPS; + break; + default: + break; + } + + sas_port_add(port); + + if (phy_info->attached.handle) { + struct sas_rport *rport; + + rport = sas_rport_alloc(dev, index, phy_info->attached.bus, + phy_info->attached.target); + if (!rport) + return 0; /* non-fatal: an rport can be added later */ + + mptsas_parse_device_info(&rport->identify, &phy_info->attached); + sas_rport_add(rport); + } + + return 0; +} + +static void +mptsas_scan_sas_topology(MPT_ADAPTER *ioc) +{ + struct mptsas_portinfo port_info; + u32 ii; + u32 handle; + int index = 0; + + memset(&port_info, 0, sizeof(port_info)); + + /* direct attached hba - port info */ + if((mptsas_sas_io_unit_pg0(ioc, &port_info) != 0) || + !port_info.num_phys) { + kfree(port_info.phy_info); + return; + } + + /* direct attached hba - phys info */ + for (ii = 0; ii < port_info.num_phys; ii++) + mptsas_sas_phy_pg0(ioc, + &port_info.phy_info[ii], + (MPI_SAS_PHY_PGAD_FORM_PHY_NUMBER << + MPI_SAS_PHY_PGAD_FORM_SHIFT), + ii); + + /* direct attached hba - fill identify device info */ + handle = 0xFFFF; + for (ii = 0; ii < port_info.num_phys; ii++) { + mptsas_sas_device_pg0(ioc, + &port_info.phy_info[ii].identify, + (MPI_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE << + MPI_SAS_DEVICE_PGAD_FORM_SHIFT), + handle); + handle = port_info.phy_info[ii].identify.handle; + } + + /* direct attached hba - fill attached device info */ + for (ii = 0; ii < port_info.num_phys; ii++) { + if (port_info.phy_info[ii].attached.handle) { + mptsas_sas_device_pg0(ioc, + &port_info.phy_info[ii].attached, + (MPI_SAS_DEVICE_PGAD_FORM_HANDLE << + MPI_SAS_DEVICE_PGAD_FORM_SHIFT), + port_info.phy_info[ii].attached.handle); + handle = port_info.phy_info[ii].identify.handle; + } + } + + for (ii = 0; ii < port_info.num_phys; ii++) { + mptsas_probe_one_phy(&ioc->sh->shost_gendev, + &port_info.phy_info[ii], index++); + } + + /* add expanders */ + handle = 0xFFFF; + while (1) { + memset(&port_info, 0, sizeof(port_info)); + if( mptsas_sas_expander_pg0(ioc, + &port_info, + (MPI_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE << + MPI_SAS_EXPAND_PGAD_FORM_SHIFT), + handle) != 0) { + kfree(port_info.phy_info); + break; + } + handle = port_info.handle; + + for (ii = 0; ii < port_info.num_phys; ii++) + mptsas_sas_expander_pg1(ioc, + &port_info.phy_info[ii], + (MPI_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM << + MPI_SAS_EXPAND_PGAD_FORM_SHIFT), + (ii << 16) + handle); + + for (ii = 0; ii < port_info.num_phys; ii++) + if (port_info.phy_info[ii].identify.handle) + mptsas_sas_device_pg0(ioc, + &port_info.phy_info[ii].identify, + (MPI_SAS_DEVICE_PGAD_FORM_HANDLE << + MPI_SAS_DEVICE_PGAD_FORM_SHIFT), + port_info.phy_info[ii].identify.handle); + + for (ii = 0; ii < port_info.num_phys; ii++) + if (port_info.phy_info[ii].attached.handle) + mptsas_sas_device_pg0(ioc, + &port_info.phy_info[ii].attached, + (MPI_SAS_DEVICE_PGAD_FORM_HANDLE << + MPI_SAS_DEVICE_PGAD_FORM_SHIFT), + port_info.phy_info[ii].attached.handle); + + for (ii = 0; ii < port_info.num_phys; ii++) { + mptsas_probe_one_phy(&ioc->sh->shost_gendev, + &port_info.phy_info[ii], + index++); + } + } +} + +static struct sas_function_template mptsas_transport_functions = { +}; + +static struct scsi_transport_template *mptsas_transport_template; + +static struct scsi_host_template mptsas_driver_template = { + .proc_name = "mptsas", + .proc_info = mptscsih_proc_info, + .name = "MPT SPI Host", + .info = mptscsih_info, + .queuecommand = mptscsih_qcmd, + .slave_alloc = mptscsih_slave_alloc, + .slave_configure = mptscsih_slave_configure, + .slave_destroy = mptscsih_slave_destroy, + .change_queue_depth = mptscsih_change_queue_depth, + .eh_abort_handler = mptscsih_abort, + .eh_device_reset_handler = mptscsih_dev_reset, + .eh_bus_reset_handler = mptscsih_bus_reset, + .eh_host_reset_handler = mptscsih_host_reset, + .bios_param = mptscsih_bios_param, + .can_queue = MPT_FC_CAN_QUEUE, + .this_id = -1, + .sg_tablesize = MPT_SCSI_SG_DEPTH, + .max_sectors = 8192, + .cmd_per_lun = 7, + .use_clustering = ENABLE_CLUSTERING, +}; + +static int +mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct Scsi_Host *sh; + MPT_SCSI_HOST *hd; + MPT_ADAPTER *ioc; + unsigned long flags; + int sz, ii; + int numSGE = 0; + int scale; + int ioc_cap; + u8 *mem; + int error=0; + int r; + + r = mpt_attach(pdev,id); + if (r) + return r; + + ioc = pci_get_drvdata(pdev); + ioc->DoneCtx = mptsasDoneCtx; + ioc->TaskCtx = mptsasTaskCtx; + ioc->InternalCtx = mptsasInternalCtx; + + /* Added sanity check on readiness of the MPT adapter. + */ + if (ioc->last_state != MPI_IOC_STATE_OPERATIONAL) { + printk(MYIOC_s_WARN_FMT + "Skipping because it's not operational!\n", + ioc->name); + return -ENODEV; + } + + if (!ioc->active) { + printk(MYIOC_s_WARN_FMT "Skipping because it's disabled!\n", + ioc->name); + return -ENODEV; + } + + /* Sanity check - ensure at least 1 port is INITIATOR capable + */ + ioc_cap = 0; + for (ii = 0; ii < ioc->facts.NumberOfPorts; ii++) { + if (ioc->pfacts[ii].ProtocolFlags & + MPI_PORTFACTS_PROTOCOL_INITIATOR) + ioc_cap++; + } + + if (!ioc_cap) { + printk(MYIOC_s_WARN_FMT + "Skipping ioc=%p because SCSI Initiator mode " + "is NOT enabled!\n", ioc->name, ioc); + return -ENODEV; + } + + sh = scsi_host_alloc(&mptsas_driver_template, sizeof(MPT_SCSI_HOST)); + if (!sh) { + printk(MYIOC_s_WARN_FMT + "Unable to register controller with SCSI subsystem\n", + ioc->name); + return -1; + } + + spin_lock_irqsave(&ioc->FreeQlock, flags); + + /* Attach the SCSI Host to the IOC structure + */ + ioc->sh = sh; + + sh->io_port = 0; + sh->n_io_port = 0; + sh->irq = 0; + + /* set 16 byte cdb's */ + sh->max_cmd_len = 16; + + sh->max_id = ioc->pfacts->MaxDevices + 1; + + sh->transportt = mptsas_transport_template; + + sh->max_lun = MPT_LAST_LUN + 1; + sh->max_channel = 0; + sh->this_id = ioc->pfacts[0].PortSCSIID; + + /* Required entry. + */ + sh->unique_id = ioc->id; + + /* Verify that we won't exceed the maximum + * number of chain buffers + * We can optimize: ZZ = req_sz/sizeof(SGE) + * For 32bit SGE's: + * numSGE = 1 + (ZZ-1)*(maxChain -1) + ZZ + * + (req_sz - 64)/sizeof(SGE) + * A slightly different algorithm is required for + * 64bit SGEs. + */ + scale = ioc->req_sz/(sizeof(dma_addr_t) + sizeof(u32)); + if (sizeof(dma_addr_t) == sizeof(u64)) { + numSGE = (scale - 1) * + (ioc->facts.MaxChainDepth-1) + scale + + (ioc->req_sz - 60) / (sizeof(dma_addr_t) + + sizeof(u32)); + } else { + numSGE = 1 + (scale - 1) * + (ioc->facts.MaxChainDepth-1) + scale + + (ioc->req_sz - 64) / (sizeof(dma_addr_t) + + sizeof(u32)); + } + + if (numSGE < sh->sg_tablesize) { + /* Reset this value */ + dprintk((MYIOC_s_INFO_FMT + "Resetting sg_tablesize to %d from %d\n", + ioc->name, numSGE, sh->sg_tablesize)); + sh->sg_tablesize = numSGE; + } + + spin_unlock_irqrestore(&ioc->FreeQlock, flags); + + hd = (MPT_SCSI_HOST *) sh->hostdata; + hd->ioc = ioc; + + /* SCSI needs scsi_cmnd lookup table! + * (with size equal to req_depth*PtrSz!) + */ + sz = ioc->req_depth * sizeof(void *); + mem = kmalloc(sz, GFP_ATOMIC); + if (mem == NULL) { + error = -ENOMEM; + goto mptsas_probe_failed; + } + + memset(mem, 0, sz); + hd->ScsiLookup = (struct scsi_cmnd **) mem; + + dprintk((MYIOC_s_INFO_FMT "ScsiLookup @ %p, sz=%d\n", + ioc->name, hd->ScsiLookup, sz)); + + /* Allocate memory for the device structures. + * A non-Null pointer at an offset + * indicates a device exists. + * max_id = 1 + maximum id (hosts.h) + */ + sz = sh->max_id * sizeof(void *); + mem = kmalloc(sz, GFP_ATOMIC); + if (mem == NULL) { + error = -ENOMEM; + goto mptsas_probe_failed; + } + + memset(mem, 0, sz); + hd->Targets = (VirtDevice **) mem; + + dprintk((KERN_INFO + " Targets @ %p, sz=%d\n", hd->Targets, sz)); + + /* Clear the TM flags + */ + hd->tmPending = 0; + hd->tmState = TM_STATE_NONE; + hd->resetPending = 0; + hd->abortSCpnt = NULL; + + /* Clear the pointer used to store + * single-threaded commands, i.e., those + * issued during a bus scan, dv and + * configuration pages. + */ + hd->cmdPtr = NULL; + + /* Initialize this SCSI Hosts' timers + * To use, set the timer expires field + * and add_timer + */ + init_timer(&hd->timer); + hd->timer.data = (unsigned long) hd; + hd->timer.function = mptscsih_timer_expired; + + hd->mpt_pq_filter = mpt_pq_filter; + ioc->sas_data.ptClear = mpt_pt_clear; + + if (ioc->sas_data.ptClear==1) { + mptbase_sas_persist_operation( + ioc, MPI_SAS_OP_CLEAR_ALL_PERSISTENT); + } + + ddvprintk((MYIOC_s_INFO_FMT + "mpt_pq_filter %x mpt_pq_filter %x\n", + ioc->name, + mpt_pq_filter, + mpt_pq_filter)); + + init_waitqueue_head(&hd->scandv_waitq); + hd->scandv_wait_done = 0; + hd->last_queue_full = 0; + + error = scsi_add_host(sh, &ioc->pcidev->dev); + if (error) { + dprintk((KERN_ERR MYNAM + "scsi_add_host failed\n")); + goto mptsas_probe_failed; + } + + mptsas_scan_sas_topology(ioc); + + return 0; + +mptsas_probe_failed: + + mptscsih_remove(pdev); + return error; +} + +static void __devexit mptsas_remove(struct pci_dev *pdev) +{ + MPT_ADAPTER *ioc = pci_get_drvdata(pdev); + + sas_remove_host(ioc->sh); + mptscsih_remove(pdev); +} + +static struct pci_device_id mptsas_pci_table[] = { + { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1064, + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1066, + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1068, + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1064E, + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1066E, + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1068E, + PCI_ANY_ID, PCI_ANY_ID }, + {0} /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(pci, mptsas_pci_table); + + +static struct pci_driver mptsas_driver = { + .name = "mptsas", + .id_table = mptsas_pci_table, + .probe = mptsas_probe, + .remove = __devexit_p(mptsas_remove), + .shutdown = mptscsih_shutdown, +#ifdef CONFIG_PM + .suspend = mptscsih_suspend, + .resume = mptscsih_resume, +#endif +}; + +static int __init +mptsas_init(void) +{ + show_mptmod_ver(my_NAME, my_VERSION); + + mptsas_transport_template = + sas_attach_transport(&mptsas_transport_functions); + if (!mptsas_transport_template) + return -ENODEV; + + mptsasDoneCtx = mpt_register(mptscsih_io_done, MPTSAS_DRIVER); + mptsasTaskCtx = mpt_register(mptscsih_taskmgmt_complete, MPTSAS_DRIVER); + mptsasInternalCtx = + mpt_register(mptscsih_scandv_complete, MPTSAS_DRIVER); + + if (mpt_event_register(mptsasDoneCtx, mptscsih_event_process) == 0) { + devtprintk((KERN_INFO MYNAM + ": Registered for IOC event notifications\n")); + } + + if (mpt_reset_register(mptsasDoneCtx, mptscsih_ioc_reset) == 0) { + dprintk((KERN_INFO MYNAM + ": Registered for IOC reset notifications\n")); + } + + return pci_register_driver(&mptsas_driver); +} + +static void __exit +mptsas_exit(void) +{ + pci_unregister_driver(&mptsas_driver); + sas_release_transport(mptsas_transport_template); + + mpt_reset_deregister(mptsasDoneCtx); + mpt_event_deregister(mptsasDoneCtx); + + mpt_deregister(mptsasInternalCtx); + mpt_deregister(mptsasTaskCtx); + mpt_deregister(mptsasDoneCtx); +} + +module_init(mptsas_init); +module_exit(mptsas_exit); - : send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html