12496af39SMoore, Eric Dean /* 22496af39SMoore, Eric Dean * linux/drivers/message/fusion/mptfc.c 32496af39SMoore, Eric Dean * For use with LSI Logic PCI chip/adapter(s) 42496af39SMoore, Eric Dean * running LSI Logic Fusion MPT (Message Passing Technology) firmware. 52496af39SMoore, Eric Dean * 62496af39SMoore, Eric Dean * Copyright (c) 1999-2005 LSI Logic Corporation 72496af39SMoore, Eric Dean * (mailto:mpt_linux_developer@lsil.com) 82496af39SMoore, Eric Dean * 92496af39SMoore, Eric Dean */ 102496af39SMoore, Eric Dean /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ 112496af39SMoore, Eric Dean /* 122496af39SMoore, Eric Dean This program is free software; you can redistribute it and/or modify 132496af39SMoore, Eric Dean it under the terms of the GNU General Public License as published by 142496af39SMoore, Eric Dean the Free Software Foundation; version 2 of the License. 152496af39SMoore, Eric Dean 162496af39SMoore, Eric Dean This program is distributed in the hope that it will be useful, 172496af39SMoore, Eric Dean but WITHOUT ANY WARRANTY; without even the implied warranty of 182496af39SMoore, Eric Dean MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 192496af39SMoore, Eric Dean GNU General Public License for more details. 202496af39SMoore, Eric Dean 212496af39SMoore, Eric Dean NO WARRANTY 222496af39SMoore, Eric Dean THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR 232496af39SMoore, Eric Dean CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT 242496af39SMoore, Eric Dean LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, 252496af39SMoore, Eric Dean MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is 262496af39SMoore, Eric Dean solely responsible for determining the appropriateness of using and 272496af39SMoore, Eric Dean distributing the Program and assumes all risks associated with its 282496af39SMoore, Eric Dean exercise of rights under this Agreement, including but not limited to 292496af39SMoore, Eric Dean the risks and costs of program errors, damage to or loss of data, 302496af39SMoore, Eric Dean programs or equipment, and unavailability or interruption of operations. 312496af39SMoore, Eric Dean 322496af39SMoore, Eric Dean DISCLAIMER OF LIABILITY 332496af39SMoore, Eric Dean NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY 342496af39SMoore, Eric Dean DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 352496af39SMoore, Eric Dean DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND 362496af39SMoore, Eric Dean ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 372496af39SMoore, Eric Dean TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 382496af39SMoore, Eric Dean USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED 392496af39SMoore, Eric Dean HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES 402496af39SMoore, Eric Dean 412496af39SMoore, Eric Dean You should have received a copy of the GNU General Public License 422496af39SMoore, Eric Dean along with this program; if not, write to the Free Software 432496af39SMoore, Eric Dean Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 442496af39SMoore, Eric Dean */ 452496af39SMoore, Eric Dean /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ 462496af39SMoore, Eric Dean #include "linux_compat.h" /* linux-2.6 tweaks */ 472496af39SMoore, Eric Dean #include <linux/module.h> 482496af39SMoore, Eric Dean #include <linux/kernel.h> 492496af39SMoore, Eric Dean #include <linux/init.h> 502496af39SMoore, Eric Dean #include <linux/errno.h> 512496af39SMoore, Eric Dean #include <linux/kdev_t.h> 522496af39SMoore, Eric Dean #include <linux/blkdev.h> 532496af39SMoore, Eric Dean #include <linux/delay.h> /* for mdelay */ 542496af39SMoore, Eric Dean #include <linux/interrupt.h> /* needed for in_interrupt() proto */ 552496af39SMoore, Eric Dean #include <linux/reboot.h> /* notifier code */ 562496af39SMoore, Eric Dean #include <linux/sched.h> 572496af39SMoore, Eric Dean #include <linux/workqueue.h> 58*05e8ec17SMichael Reed #include <linux/sort.h> 592496af39SMoore, Eric Dean 602496af39SMoore, Eric Dean #include <scsi/scsi.h> 612496af39SMoore, Eric Dean #include <scsi/scsi_cmnd.h> 622496af39SMoore, Eric Dean #include <scsi/scsi_device.h> 632496af39SMoore, Eric Dean #include <scsi/scsi_host.h> 642496af39SMoore, Eric Dean #include <scsi/scsi_tcq.h> 65*05e8ec17SMichael Reed #include <scsi/scsi_transport_fc.h> 662496af39SMoore, Eric Dean 672496af39SMoore, Eric Dean #include "mptbase.h" 682496af39SMoore, Eric Dean #include "mptscsih.h" 692496af39SMoore, Eric Dean 702496af39SMoore, Eric Dean /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ 712496af39SMoore, Eric Dean #define my_NAME "Fusion MPT FC Host driver" 722496af39SMoore, Eric Dean #define my_VERSION MPT_LINUX_VERSION_COMMON 732496af39SMoore, Eric Dean #define MYNAM "mptfc" 742496af39SMoore, Eric Dean 752496af39SMoore, Eric Dean MODULE_AUTHOR(MODULEAUTHOR); 762496af39SMoore, Eric Dean MODULE_DESCRIPTION(my_NAME); 772496af39SMoore, Eric Dean MODULE_LICENSE("GPL"); 782496af39SMoore, Eric Dean 792496af39SMoore, Eric Dean /* Command line args */ 802496af39SMoore, Eric Dean static int mpt_pq_filter = 0; 812496af39SMoore, Eric Dean module_param(mpt_pq_filter, int, 0); 822496af39SMoore, Eric Dean MODULE_PARM_DESC(mpt_pq_filter, " Enable peripheral qualifier filter: enable=1 (default=0)"); 832496af39SMoore, Eric Dean 84*05e8ec17SMichael Reed #define MPTFC_DEV_LOSS_TMO (60) 85*05e8ec17SMichael Reed static int mptfc_dev_loss_tmo = MPTFC_DEV_LOSS_TMO; /* reasonable default */ 86*05e8ec17SMichael Reed module_param(mptfc_dev_loss_tmo, int, 0); 87*05e8ec17SMichael Reed MODULE_PARM_DESC(mptfc_dev_loss_tmo, " Initial time the driver programs the " 88*05e8ec17SMichael Reed " transport to wait for an rport to " 89*05e8ec17SMichael Reed " return following a device loss event." 90*05e8ec17SMichael Reed " Default=60."); 91*05e8ec17SMichael Reed 922496af39SMoore, Eric Dean static int mptfcDoneCtx = -1; 932496af39SMoore, Eric Dean static int mptfcTaskCtx = -1; 942496af39SMoore, Eric Dean static int mptfcInternalCtx = -1; /* Used only for internal commands */ 952496af39SMoore, Eric Dean 96*05e8ec17SMichael Reed int mptfc_slave_alloc(struct scsi_device *device); 97*05e8ec17SMichael Reed static int mptfc_qcmd(struct scsi_cmnd *SCpnt, 98*05e8ec17SMichael Reed void (*done)(struct scsi_cmnd *)); 99*05e8ec17SMichael Reed 100*05e8ec17SMichael Reed static void mptfc_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout); 101*05e8ec17SMichael Reed static void __devexit mptfc_remove(struct pci_dev *pdev); 102*05e8ec17SMichael Reed 1032496af39SMoore, Eric Dean static struct scsi_host_template mptfc_driver_template = { 104f78496daSMoore, Eric Dean .module = THIS_MODULE, 1052496af39SMoore, Eric Dean .proc_name = "mptfc", 1062496af39SMoore, Eric Dean .proc_info = mptscsih_proc_info, 1072496af39SMoore, Eric Dean .name = "MPT FC Host", 1082496af39SMoore, Eric Dean .info = mptscsih_info, 109*05e8ec17SMichael Reed .queuecommand = mptfc_qcmd, 110c7c82987SMoore, Eric Dean .target_alloc = mptscsih_target_alloc, 111*05e8ec17SMichael Reed .slave_alloc = mptfc_slave_alloc, 1122496af39SMoore, Eric Dean .slave_configure = mptscsih_slave_configure, 113c7c82987SMoore, Eric Dean .target_destroy = mptscsih_target_destroy, 1142496af39SMoore, Eric Dean .slave_destroy = mptscsih_slave_destroy, 1156e3815baSMoore, Eric Dean .change_queue_depth = mptscsih_change_queue_depth, 1162496af39SMoore, Eric Dean .eh_abort_handler = mptscsih_abort, 1172496af39SMoore, Eric Dean .eh_device_reset_handler = mptscsih_dev_reset, 1182496af39SMoore, Eric Dean .eh_bus_reset_handler = mptscsih_bus_reset, 1192496af39SMoore, Eric Dean .eh_host_reset_handler = mptscsih_host_reset, 1202496af39SMoore, Eric Dean .bios_param = mptscsih_bios_param, 1212496af39SMoore, Eric Dean .can_queue = MPT_FC_CAN_QUEUE, 1222496af39SMoore, Eric Dean .this_id = -1, 1232496af39SMoore, Eric Dean .sg_tablesize = MPT_SCSI_SG_DEPTH, 1242496af39SMoore, Eric Dean .max_sectors = 8192, 1252496af39SMoore, Eric Dean .cmd_per_lun = 7, 1262496af39SMoore, Eric Dean .use_clustering = ENABLE_CLUSTERING, 1272496af39SMoore, Eric Dean }; 1282496af39SMoore, Eric Dean 1292496af39SMoore, Eric Dean /**************************************************************************** 1302496af39SMoore, Eric Dean * Supported hardware 1312496af39SMoore, Eric Dean */ 1322496af39SMoore, Eric Dean 1332496af39SMoore, Eric Dean static struct pci_device_id mptfc_pci_table[] = { 1342496af39SMoore, Eric Dean { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC909, 1352496af39SMoore, Eric Dean PCI_ANY_ID, PCI_ANY_ID }, 1362496af39SMoore, Eric Dean { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC919, 1372496af39SMoore, Eric Dean PCI_ANY_ID, PCI_ANY_ID }, 1382496af39SMoore, Eric Dean { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC929, 1392496af39SMoore, Eric Dean PCI_ANY_ID, PCI_ANY_ID }, 1402496af39SMoore, Eric Dean { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC919X, 1412496af39SMoore, Eric Dean PCI_ANY_ID, PCI_ANY_ID }, 1422496af39SMoore, Eric Dean { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC929X, 1432496af39SMoore, Eric Dean PCI_ANY_ID, PCI_ANY_ID }, 1443fadc59dSMoore, Eric Dean { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC939X, 1453fadc59dSMoore, Eric Dean PCI_ANY_ID, PCI_ANY_ID }, 1463fadc59dSMoore, Eric Dean { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC949X, 1473fadc59dSMoore, Eric Dean PCI_ANY_ID, PCI_ANY_ID }, 1482496af39SMoore, Eric Dean {0} /* Terminating entry */ 1492496af39SMoore, Eric Dean }; 1502496af39SMoore, Eric Dean MODULE_DEVICE_TABLE(pci, mptfc_pci_table); 1512496af39SMoore, Eric Dean 152*05e8ec17SMichael Reed static struct scsi_transport_template *mptfc_transport_template = NULL; 153*05e8ec17SMichael Reed 154*05e8ec17SMichael Reed struct fc_function_template mptfc_transport_functions = { 155*05e8ec17SMichael Reed .dd_fcrport_size = 8, 156*05e8ec17SMichael Reed .show_host_node_name = 1, 157*05e8ec17SMichael Reed .show_host_port_name = 1, 158*05e8ec17SMichael Reed .show_host_supported_classes = 1, 159*05e8ec17SMichael Reed .show_host_port_id = 1, 160*05e8ec17SMichael Reed .show_rport_supported_classes = 1, 161*05e8ec17SMichael Reed .show_starget_node_name = 1, 162*05e8ec17SMichael Reed .show_starget_port_name = 1, 163*05e8ec17SMichael Reed .show_starget_port_id = 1, 164*05e8ec17SMichael Reed .set_rport_dev_loss_tmo = mptfc_set_rport_loss_tmo, 165*05e8ec17SMichael Reed .show_rport_dev_loss_tmo = 1, 166*05e8ec17SMichael Reed 167*05e8ec17SMichael Reed }; 168*05e8ec17SMichael Reed 169*05e8ec17SMichael Reed /* FIXME! values controlling firmware RESCAN event 170*05e8ec17SMichael Reed * need to be set low to allow dev_loss_tmo to 171*05e8ec17SMichael Reed * work as expected. Currently, firmware doesn't 172*05e8ec17SMichael Reed * notify driver of RESCAN event until some number 173*05e8ec17SMichael Reed * of seconds elapse. This value can be set via 174*05e8ec17SMichael Reed * lsiutil. 1752496af39SMoore, Eric Dean */ 176*05e8ec17SMichael Reed static void 177*05e8ec17SMichael Reed mptfc_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout) 178*05e8ec17SMichael Reed { 179*05e8ec17SMichael Reed if (timeout > 0) 180*05e8ec17SMichael Reed rport->dev_loss_tmo = timeout; 181*05e8ec17SMichael Reed else 182*05e8ec17SMichael Reed rport->dev_loss_tmo = mptfc_dev_loss_tmo; 183*05e8ec17SMichael Reed } 184*05e8ec17SMichael Reed 185*05e8ec17SMichael Reed static int 186*05e8ec17SMichael Reed mptfc_FcDevPage0_cmp_func(const void *a, const void *b) 187*05e8ec17SMichael Reed { 188*05e8ec17SMichael Reed FCDevicePage0_t **aa = (FCDevicePage0_t **)a; 189*05e8ec17SMichael Reed FCDevicePage0_t **bb = (FCDevicePage0_t **)b; 190*05e8ec17SMichael Reed 191*05e8ec17SMichael Reed if ((*aa)->CurrentBus == (*bb)->CurrentBus) { 192*05e8ec17SMichael Reed if ((*aa)->CurrentTargetID == (*bb)->CurrentTargetID) 193*05e8ec17SMichael Reed return 0; 194*05e8ec17SMichael Reed if ((*aa)->CurrentTargetID < (*bb)->CurrentTargetID) 195*05e8ec17SMichael Reed return -1; 196*05e8ec17SMichael Reed return 1; 197*05e8ec17SMichael Reed } 198*05e8ec17SMichael Reed if ((*aa)->CurrentBus < (*bb)->CurrentBus) 199*05e8ec17SMichael Reed return -1; 200*05e8ec17SMichael Reed return 1; 201*05e8ec17SMichael Reed } 202*05e8ec17SMichael Reed 203*05e8ec17SMichael Reed static int 204*05e8ec17SMichael Reed mptfc_GetFcDevPage0(MPT_ADAPTER *ioc, int ioc_port, 205*05e8ec17SMichael Reed void(*func)(MPT_ADAPTER *ioc,int channel, FCDevicePage0_t *arg)) 206*05e8ec17SMichael Reed { 207*05e8ec17SMichael Reed ConfigPageHeader_t hdr; 208*05e8ec17SMichael Reed CONFIGPARMS cfg; 209*05e8ec17SMichael Reed FCDevicePage0_t *ppage0_alloc, *fc; 210*05e8ec17SMichael Reed dma_addr_t page0_dma; 211*05e8ec17SMichael Reed int data_sz; 212*05e8ec17SMichael Reed int ii; 213*05e8ec17SMichael Reed 214*05e8ec17SMichael Reed FCDevicePage0_t *p0_array=NULL, *p_p0; 215*05e8ec17SMichael Reed FCDevicePage0_t **pp0_array=NULL, **p_pp0; 216*05e8ec17SMichael Reed 217*05e8ec17SMichael Reed int rc = -ENOMEM; 218*05e8ec17SMichael Reed U32 port_id = 0xffffff; 219*05e8ec17SMichael Reed int num_targ = 0; 220*05e8ec17SMichael Reed int max_bus = ioc->facts.MaxBuses; 221*05e8ec17SMichael Reed int max_targ = ioc->facts.MaxDevices; 222*05e8ec17SMichael Reed 223*05e8ec17SMichael Reed if (max_bus == 0 || max_targ == 0) 224*05e8ec17SMichael Reed goto out; 225*05e8ec17SMichael Reed 226*05e8ec17SMichael Reed data_sz = sizeof(FCDevicePage0_t) * max_bus * max_targ; 227*05e8ec17SMichael Reed p_p0 = p0_array = kzalloc(data_sz, GFP_KERNEL); 228*05e8ec17SMichael Reed if (!p0_array) 229*05e8ec17SMichael Reed goto out; 230*05e8ec17SMichael Reed 231*05e8ec17SMichael Reed data_sz = sizeof(FCDevicePage0_t *) * max_bus * max_targ; 232*05e8ec17SMichael Reed p_pp0 = pp0_array = kzalloc(data_sz, GFP_KERNEL); 233*05e8ec17SMichael Reed if (!pp0_array) 234*05e8ec17SMichael Reed goto out; 235*05e8ec17SMichael Reed 236*05e8ec17SMichael Reed do { 237*05e8ec17SMichael Reed /* Get FC Device Page 0 header */ 238*05e8ec17SMichael Reed hdr.PageVersion = 0; 239*05e8ec17SMichael Reed hdr.PageLength = 0; 240*05e8ec17SMichael Reed hdr.PageNumber = 0; 241*05e8ec17SMichael Reed hdr.PageType = MPI_CONFIG_PAGETYPE_FC_DEVICE; 242*05e8ec17SMichael Reed cfg.cfghdr.hdr = &hdr; 243*05e8ec17SMichael Reed cfg.physAddr = -1; 244*05e8ec17SMichael Reed cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; 245*05e8ec17SMichael Reed cfg.dir = 0; 246*05e8ec17SMichael Reed cfg.pageAddr = port_id; 247*05e8ec17SMichael Reed cfg.timeout = 0; 248*05e8ec17SMichael Reed 249*05e8ec17SMichael Reed if ((rc = mpt_config(ioc, &cfg)) != 0) 250*05e8ec17SMichael Reed break; 251*05e8ec17SMichael Reed 252*05e8ec17SMichael Reed if (hdr.PageLength <= 0) 253*05e8ec17SMichael Reed break; 254*05e8ec17SMichael Reed 255*05e8ec17SMichael Reed data_sz = hdr.PageLength * 4; 256*05e8ec17SMichael Reed ppage0_alloc = pci_alloc_consistent(ioc->pcidev, data_sz, 257*05e8ec17SMichael Reed &page0_dma); 258*05e8ec17SMichael Reed rc = -ENOMEM; 259*05e8ec17SMichael Reed if (!ppage0_alloc) 260*05e8ec17SMichael Reed break; 261*05e8ec17SMichael Reed 262*05e8ec17SMichael Reed cfg.physAddr = page0_dma; 263*05e8ec17SMichael Reed cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; 264*05e8ec17SMichael Reed 265*05e8ec17SMichael Reed if ((rc = mpt_config(ioc, &cfg)) == 0) { 266*05e8ec17SMichael Reed ppage0_alloc->PortIdentifier = 267*05e8ec17SMichael Reed le32_to_cpu(ppage0_alloc->PortIdentifier); 268*05e8ec17SMichael Reed 269*05e8ec17SMichael Reed ppage0_alloc->WWNN.Low = 270*05e8ec17SMichael Reed le32_to_cpu(ppage0_alloc->WWNN.Low); 271*05e8ec17SMichael Reed 272*05e8ec17SMichael Reed ppage0_alloc->WWNN.High = 273*05e8ec17SMichael Reed le32_to_cpu(ppage0_alloc->WWNN.High); 274*05e8ec17SMichael Reed 275*05e8ec17SMichael Reed ppage0_alloc->WWPN.Low = 276*05e8ec17SMichael Reed le32_to_cpu(ppage0_alloc->WWPN.Low); 277*05e8ec17SMichael Reed 278*05e8ec17SMichael Reed ppage0_alloc->WWPN.High = 279*05e8ec17SMichael Reed le32_to_cpu(ppage0_alloc->WWPN.High); 280*05e8ec17SMichael Reed 281*05e8ec17SMichael Reed ppage0_alloc->BBCredit = 282*05e8ec17SMichael Reed le16_to_cpu(ppage0_alloc->BBCredit); 283*05e8ec17SMichael Reed 284*05e8ec17SMichael Reed ppage0_alloc->MaxRxFrameSize = 285*05e8ec17SMichael Reed le16_to_cpu(ppage0_alloc->MaxRxFrameSize); 286*05e8ec17SMichael Reed 287*05e8ec17SMichael Reed port_id = ppage0_alloc->PortIdentifier; 288*05e8ec17SMichael Reed num_targ++; 289*05e8ec17SMichael Reed *p_p0 = *ppage0_alloc; /* save data */ 290*05e8ec17SMichael Reed *p_pp0++ = p_p0++; /* save addr */ 291*05e8ec17SMichael Reed } 292*05e8ec17SMichael Reed pci_free_consistent(ioc->pcidev, data_sz, 293*05e8ec17SMichael Reed (u8 *) ppage0_alloc, page0_dma); 294*05e8ec17SMichael Reed if (rc != 0) 295*05e8ec17SMichael Reed break; 296*05e8ec17SMichael Reed 297*05e8ec17SMichael Reed } while (port_id <= 0xff0000); 298*05e8ec17SMichael Reed 299*05e8ec17SMichael Reed if (num_targ) { 300*05e8ec17SMichael Reed /* sort array */ 301*05e8ec17SMichael Reed if (num_targ > 1) 302*05e8ec17SMichael Reed sort (pp0_array, num_targ, sizeof(FCDevicePage0_t *), 303*05e8ec17SMichael Reed mptfc_FcDevPage0_cmp_func, NULL); 304*05e8ec17SMichael Reed /* call caller's func for each targ */ 305*05e8ec17SMichael Reed for (ii = 0; ii < num_targ; ii++) { 306*05e8ec17SMichael Reed fc = *(pp0_array+ii); 307*05e8ec17SMichael Reed func(ioc, ioc_port, fc); 308*05e8ec17SMichael Reed } 309*05e8ec17SMichael Reed } 310*05e8ec17SMichael Reed 311*05e8ec17SMichael Reed out: 312*05e8ec17SMichael Reed if (pp0_array) 313*05e8ec17SMichael Reed kfree(pp0_array); 314*05e8ec17SMichael Reed if (p0_array) 315*05e8ec17SMichael Reed kfree(p0_array); 316*05e8ec17SMichael Reed return rc; 317*05e8ec17SMichael Reed } 318*05e8ec17SMichael Reed 319*05e8ec17SMichael Reed static int 320*05e8ec17SMichael Reed mptfc_generate_rport_ids(FCDevicePage0_t *pg0, struct fc_rport_identifiers *rid) 321*05e8ec17SMichael Reed { 322*05e8ec17SMichael Reed /* not currently usable */ 323*05e8ec17SMichael Reed if (pg0->Flags & (MPI_FC_DEVICE_PAGE0_FLAGS_PLOGI_INVALID | 324*05e8ec17SMichael Reed MPI_FC_DEVICE_PAGE0_FLAGS_PRLI_INVALID)) 325*05e8ec17SMichael Reed return -1; 326*05e8ec17SMichael Reed 327*05e8ec17SMichael Reed if (!(pg0->Flags & MPI_FC_DEVICE_PAGE0_FLAGS_TARGETID_BUS_VALID)) 328*05e8ec17SMichael Reed return -1; 329*05e8ec17SMichael Reed 330*05e8ec17SMichael Reed if (!(pg0->Protocol & MPI_FC_DEVICE_PAGE0_PROT_FCP_TARGET)) 331*05e8ec17SMichael Reed return -1; 332*05e8ec17SMichael Reed 333*05e8ec17SMichael Reed /* 334*05e8ec17SMichael Reed * board data structure already normalized to platform endianness 335*05e8ec17SMichael Reed * shifted to avoid unaligned access on 64 bit architecture 336*05e8ec17SMichael Reed */ 337*05e8ec17SMichael Reed rid->node_name = ((u64)pg0->WWNN.High) << 32 | (u64)pg0->WWNN.Low; 338*05e8ec17SMichael Reed rid->port_name = ((u64)pg0->WWPN.High) << 32 | (u64)pg0->WWPN.Low; 339*05e8ec17SMichael Reed rid->port_id = pg0->PortIdentifier; 340*05e8ec17SMichael Reed rid->roles = FC_RPORT_ROLE_UNKNOWN; 341*05e8ec17SMichael Reed rid->roles |= FC_RPORT_ROLE_FCP_TARGET; 342*05e8ec17SMichael Reed if (pg0->Protocol & MPI_FC_DEVICE_PAGE0_PROT_FCP_INITIATOR) 343*05e8ec17SMichael Reed rid->roles |= FC_RPORT_ROLE_FCP_INITIATOR; 344*05e8ec17SMichael Reed 345*05e8ec17SMichael Reed return 0; 346*05e8ec17SMichael Reed } 347*05e8ec17SMichael Reed 348*05e8ec17SMichael Reed static void 349*05e8ec17SMichael Reed mptfc_register_dev(MPT_ADAPTER *ioc, int channel, FCDevicePage0_t *pg0) 350*05e8ec17SMichael Reed { 351*05e8ec17SMichael Reed struct fc_rport_identifiers rport_ids; 352*05e8ec17SMichael Reed struct fc_rport *rport; 353*05e8ec17SMichael Reed struct mptfc_rport_info *ri; 354*05e8ec17SMichael Reed int match = 0; 355*05e8ec17SMichael Reed u64 port_name; 356*05e8ec17SMichael Reed unsigned long flags; 357*05e8ec17SMichael Reed 358*05e8ec17SMichael Reed if (mptfc_generate_rport_ids(pg0, &rport_ids) < 0) 359*05e8ec17SMichael Reed return; 360*05e8ec17SMichael Reed 361*05e8ec17SMichael Reed /* scan list looking for a match */ 362*05e8ec17SMichael Reed spin_lock_irqsave(&ioc->fc_rport_lock, flags); 363*05e8ec17SMichael Reed list_for_each_entry(ri, &ioc->fc_rports, list) { 364*05e8ec17SMichael Reed port_name = (u64)ri->pg0.WWPN.High << 32 | (u64)ri->pg0.WWPN.Low; 365*05e8ec17SMichael Reed if (port_name == rport_ids.port_name) { /* match */ 366*05e8ec17SMichael Reed list_move_tail(&ri->list, &ioc->fc_rports); 367*05e8ec17SMichael Reed match = 1; 368*05e8ec17SMichael Reed break; 369*05e8ec17SMichael Reed } 370*05e8ec17SMichael Reed } 371*05e8ec17SMichael Reed if (!match) { /* allocate one */ 372*05e8ec17SMichael Reed spin_unlock_irqrestore(&ioc->fc_rport_lock, flags); 373*05e8ec17SMichael Reed ri = kzalloc(sizeof(struct mptfc_rport_info), GFP_KERNEL); 374*05e8ec17SMichael Reed if (!ri) 375*05e8ec17SMichael Reed return; 376*05e8ec17SMichael Reed spin_lock_irqsave(&ioc->fc_rport_lock, flags); 377*05e8ec17SMichael Reed list_add_tail(&ri->list, &ioc->fc_rports); 378*05e8ec17SMichael Reed } 379*05e8ec17SMichael Reed 380*05e8ec17SMichael Reed ri->pg0 = *pg0; /* add/update pg0 data */ 381*05e8ec17SMichael Reed ri->flags &= ~MPT_RPORT_INFO_FLAGS_MISSING; 382*05e8ec17SMichael Reed 383*05e8ec17SMichael Reed if (!(ri->flags & MPT_RPORT_INFO_FLAGS_REGISTERED)) { 384*05e8ec17SMichael Reed ri->flags |= MPT_RPORT_INFO_FLAGS_REGISTERED; 385*05e8ec17SMichael Reed spin_unlock_irqrestore(&ioc->fc_rport_lock, flags); 386*05e8ec17SMichael Reed rport = fc_remote_port_add(ioc->sh,channel, &rport_ids); 387*05e8ec17SMichael Reed spin_lock_irqsave(&ioc->fc_rport_lock, flags); 388*05e8ec17SMichael Reed if (rport) { 389*05e8ec17SMichael Reed if (*((struct mptfc_rport_info **)rport->dd_data) != ri) { 390*05e8ec17SMichael Reed ri->flags &= ~MPT_RPORT_INFO_FLAGS_MAPPED_VDEV; 391*05e8ec17SMichael Reed ri->vdev = NULL; 392*05e8ec17SMichael Reed ri->rport = rport; 393*05e8ec17SMichael Reed *((struct mptfc_rport_info **)rport->dd_data) = ri; 394*05e8ec17SMichael Reed } 395*05e8ec17SMichael Reed rport->dev_loss_tmo = mptfc_dev_loss_tmo; 396*05e8ec17SMichael Reed /* 397*05e8ec17SMichael Reed * if already mapped, remap here. If not mapped, 398*05e8ec17SMichael Reed * slave_alloc will allocate vdev and map 399*05e8ec17SMichael Reed */ 400*05e8ec17SMichael Reed if (ri->flags & MPT_RPORT_INFO_FLAGS_MAPPED_VDEV) { 401*05e8ec17SMichael Reed ri->vdev->target_id = ri->pg0.CurrentTargetID; 402*05e8ec17SMichael Reed ri->vdev->bus_id = ri->pg0.CurrentBus; 403*05e8ec17SMichael Reed ri->vdev->vtarget->target_id = ri->vdev->target_id; 404*05e8ec17SMichael Reed ri->vdev->vtarget->bus_id = ri->vdev->bus_id; 405*05e8ec17SMichael Reed } 406*05e8ec17SMichael Reed #ifdef MPT_DEBUG 407*05e8ec17SMichael Reed printk ("mptfc_reg_dev.%d: %x, %llx / %llx, tid %d, " 408*05e8ec17SMichael Reed "rport tid %d, tmo %d\n", 409*05e8ec17SMichael Reed ioc->sh->host_no, 410*05e8ec17SMichael Reed pg0->PortIdentifier, 411*05e8ec17SMichael Reed pg0->WWNN, 412*05e8ec17SMichael Reed pg0->WWPN, 413*05e8ec17SMichael Reed pg0->CurrentTargetID, 414*05e8ec17SMichael Reed ri->rport->scsi_target_id, 415*05e8ec17SMichael Reed ri->rport->dev_loss_tmo); 416*05e8ec17SMichael Reed #endif 417*05e8ec17SMichael Reed } else { 418*05e8ec17SMichael Reed list_del(&ri->list); 419*05e8ec17SMichael Reed kfree(ri); 420*05e8ec17SMichael Reed ri = NULL; 421*05e8ec17SMichael Reed } 422*05e8ec17SMichael Reed } 423*05e8ec17SMichael Reed spin_unlock_irqrestore(&ioc->fc_rport_lock,flags); 424*05e8ec17SMichael Reed 425*05e8ec17SMichael Reed } 426*05e8ec17SMichael Reed 427*05e8ec17SMichael Reed /* 428*05e8ec17SMichael Reed * OS entry point to allow host driver to alloc memory 429*05e8ec17SMichael Reed * for each scsi device. Called once per device the bus scan. 430*05e8ec17SMichael Reed * Return non-zero if allocation fails. 431*05e8ec17SMichael Reed * Init memory once per LUN. 432*05e8ec17SMichael Reed */ 433*05e8ec17SMichael Reed int 434*05e8ec17SMichael Reed mptfc_slave_alloc(struct scsi_device *sdev) 435*05e8ec17SMichael Reed { 436*05e8ec17SMichael Reed MPT_SCSI_HOST *hd; 437*05e8ec17SMichael Reed VirtTarget *vtarget; 438*05e8ec17SMichael Reed VirtDevice *vdev; 439*05e8ec17SMichael Reed struct scsi_target *starget; 440*05e8ec17SMichael Reed struct fc_rport *rport; 441*05e8ec17SMichael Reed struct mptfc_rport_info *ri; 442*05e8ec17SMichael Reed unsigned long flags; 443*05e8ec17SMichael Reed 444*05e8ec17SMichael Reed 445*05e8ec17SMichael Reed rport = starget_to_rport(scsi_target(sdev)); 446*05e8ec17SMichael Reed 447*05e8ec17SMichael Reed if (!rport || fc_remote_port_chkready(rport)) 448*05e8ec17SMichael Reed return -ENXIO; 449*05e8ec17SMichael Reed 450*05e8ec17SMichael Reed hd = (MPT_SCSI_HOST *)sdev->host->hostdata; 451*05e8ec17SMichael Reed 452*05e8ec17SMichael Reed vdev = kmalloc(sizeof(VirtDevice), GFP_KERNEL); 453*05e8ec17SMichael Reed if (!vdev) { 454*05e8ec17SMichael Reed printk(MYIOC_s_ERR_FMT "slave_alloc kmalloc(%zd) FAILED!\n", 455*05e8ec17SMichael Reed hd->ioc->name, sizeof(VirtDevice)); 456*05e8ec17SMichael Reed return -ENOMEM; 457*05e8ec17SMichael Reed } 458*05e8ec17SMichael Reed memset(vdev, 0, sizeof(VirtDevice)); 459*05e8ec17SMichael Reed 460*05e8ec17SMichael Reed spin_lock_irqsave(&hd->ioc->fc_rport_lock,flags); 461*05e8ec17SMichael Reed 462*05e8ec17SMichael Reed if (!(ri = *((struct mptfc_rport_info **)rport->dd_data))) { 463*05e8ec17SMichael Reed spin_unlock_irqrestore(&hd->ioc->fc_rport_lock,flags); 464*05e8ec17SMichael Reed kfree(vdev); 465*05e8ec17SMichael Reed return -ENODEV; 466*05e8ec17SMichael Reed } 467*05e8ec17SMichael Reed 468*05e8ec17SMichael Reed sdev->hostdata = vdev; 469*05e8ec17SMichael Reed starget = scsi_target(sdev); 470*05e8ec17SMichael Reed vtarget = starget->hostdata; 471*05e8ec17SMichael Reed if (vtarget->num_luns == 0) { 472*05e8ec17SMichael Reed vtarget->tflags = MPT_TARGET_FLAGS_Q_YES | 473*05e8ec17SMichael Reed MPT_TARGET_FLAGS_VALID_INQUIRY; 474*05e8ec17SMichael Reed hd->Targets[sdev->id] = vtarget; 475*05e8ec17SMichael Reed } 476*05e8ec17SMichael Reed 477*05e8ec17SMichael Reed vtarget->target_id = vdev->target_id; 478*05e8ec17SMichael Reed vtarget->bus_id = vdev->bus_id; 479*05e8ec17SMichael Reed 480*05e8ec17SMichael Reed vdev->vtarget = vtarget; 481*05e8ec17SMichael Reed vdev->ioc_id = hd->ioc->id; 482*05e8ec17SMichael Reed vdev->lun = sdev->lun; 483*05e8ec17SMichael Reed vdev->target_id = ri->pg0.CurrentTargetID; 484*05e8ec17SMichael Reed vdev->bus_id = ri->pg0.CurrentBus; 485*05e8ec17SMichael Reed 486*05e8ec17SMichael Reed ri->flags |= MPT_RPORT_INFO_FLAGS_MAPPED_VDEV; 487*05e8ec17SMichael Reed ri->vdev = vdev; 488*05e8ec17SMichael Reed 489*05e8ec17SMichael Reed spin_unlock_irqrestore(&hd->ioc->fc_rport_lock,flags); 490*05e8ec17SMichael Reed 491*05e8ec17SMichael Reed vtarget->num_luns++; 492*05e8ec17SMichael Reed 493*05e8ec17SMichael Reed #ifdef MPT_DEBUG 494*05e8ec17SMichael Reed printk ("mptfc_slv_alloc.%d: num_luns %d, sdev.id %d, " 495*05e8ec17SMichael Reed "CurrentTargetID %d, %x %llx %llx\n", 496*05e8ec17SMichael Reed sdev->host->host_no, 497*05e8ec17SMichael Reed vtarget->num_luns, 498*05e8ec17SMichael Reed sdev->id, ri->pg0.CurrentTargetID, 499*05e8ec17SMichael Reed ri->pg0.PortIdentifier, ri->pg0.WWPN, ri->pg0.WWNN); 500*05e8ec17SMichael Reed #endif 501*05e8ec17SMichael Reed 502*05e8ec17SMichael Reed return 0; 503*05e8ec17SMichael Reed } 504*05e8ec17SMichael Reed 505*05e8ec17SMichael Reed static int 506*05e8ec17SMichael Reed mptfc_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) 507*05e8ec17SMichael Reed { 508*05e8ec17SMichael Reed struct fc_rport *rport = starget_to_rport(scsi_target(SCpnt->device)); 509*05e8ec17SMichael Reed int err; 510*05e8ec17SMichael Reed 511*05e8ec17SMichael Reed err = fc_remote_port_chkready(rport); 512*05e8ec17SMichael Reed if (unlikely(err)) { 513*05e8ec17SMichael Reed SCpnt->result = err; 514*05e8ec17SMichael Reed done(SCpnt); 515*05e8ec17SMichael Reed return 0; 516*05e8ec17SMichael Reed } 517*05e8ec17SMichael Reed return mptscsih_qcmd(SCpnt,done); 518*05e8ec17SMichael Reed } 519*05e8ec17SMichael Reed 520*05e8ec17SMichael Reed static void 521*05e8ec17SMichael Reed mptfc_init_host_attr(MPT_ADAPTER *ioc,int portnum) 522*05e8ec17SMichael Reed { 523*05e8ec17SMichael Reed unsigned class = 0, cos = 0; 524*05e8ec17SMichael Reed 525*05e8ec17SMichael Reed /* don't know what to do as only one scsi (fc) host was allocated */ 526*05e8ec17SMichael Reed if (portnum != 0) 527*05e8ec17SMichael Reed return; 528*05e8ec17SMichael Reed 529*05e8ec17SMichael Reed class = ioc->fc_port_page0[portnum].SupportedServiceClass; 530*05e8ec17SMichael Reed if (class & MPI_FCPORTPAGE0_SUPPORT_CLASS_1) 531*05e8ec17SMichael Reed cos |= FC_COS_CLASS1; 532*05e8ec17SMichael Reed if (class & MPI_FCPORTPAGE0_SUPPORT_CLASS_2) 533*05e8ec17SMichael Reed cos |= FC_COS_CLASS2; 534*05e8ec17SMichael Reed if (class & MPI_FCPORTPAGE0_SUPPORT_CLASS_3) 535*05e8ec17SMichael Reed cos |= FC_COS_CLASS3; 536*05e8ec17SMichael Reed 537*05e8ec17SMichael Reed fc_host_node_name(ioc->sh) = 538*05e8ec17SMichael Reed (u64)ioc->fc_port_page0[portnum].WWNN.High << 32 539*05e8ec17SMichael Reed | (u64)ioc->fc_port_page0[portnum].WWNN.Low; 540*05e8ec17SMichael Reed 541*05e8ec17SMichael Reed fc_host_port_name(ioc->sh) = 542*05e8ec17SMichael Reed (u64)ioc->fc_port_page0[portnum].WWPN.High << 32 543*05e8ec17SMichael Reed | (u64)ioc->fc_port_page0[portnum].WWPN.Low; 544*05e8ec17SMichael Reed 545*05e8ec17SMichael Reed fc_host_port_id(ioc->sh) = ioc->fc_port_page0[portnum].PortIdentifier; 546*05e8ec17SMichael Reed 547*05e8ec17SMichael Reed fc_host_supported_classes(ioc->sh) = cos; 548*05e8ec17SMichael Reed 549*05e8ec17SMichael Reed fc_host_tgtid_bind_type(ioc->sh) = FC_TGTID_BIND_BY_WWPN; 550*05e8ec17SMichael Reed } 551*05e8ec17SMichael Reed 552*05e8ec17SMichael Reed static void 553*05e8ec17SMichael Reed mptfc_rescan_devices(void *arg) 554*05e8ec17SMichael Reed { 555*05e8ec17SMichael Reed MPT_ADAPTER *ioc = (MPT_ADAPTER *)arg; 556*05e8ec17SMichael Reed int ii; 557*05e8ec17SMichael Reed int work_to_do; 558*05e8ec17SMichael Reed unsigned long flags; 559*05e8ec17SMichael Reed struct mptfc_rport_info *ri; 560*05e8ec17SMichael Reed 561*05e8ec17SMichael Reed do { 562*05e8ec17SMichael Reed /* start by tagging all ports as missing */ 563*05e8ec17SMichael Reed spin_lock_irqsave(&ioc->fc_rport_lock,flags); 564*05e8ec17SMichael Reed list_for_each_entry(ri, &ioc->fc_rports, list) { 565*05e8ec17SMichael Reed if (ri->flags & MPT_RPORT_INFO_FLAGS_REGISTERED) { 566*05e8ec17SMichael Reed ri->flags |= MPT_RPORT_INFO_FLAGS_MISSING; 567*05e8ec17SMichael Reed } 568*05e8ec17SMichael Reed } 569*05e8ec17SMichael Reed spin_unlock_irqrestore(&ioc->fc_rport_lock,flags); 570*05e8ec17SMichael Reed 571*05e8ec17SMichael Reed /* 572*05e8ec17SMichael Reed * now rescan devices known to adapter, 573*05e8ec17SMichael Reed * will reregister existing rports 574*05e8ec17SMichael Reed */ 575*05e8ec17SMichael Reed for (ii=0; ii < ioc->facts.NumberOfPorts; ii++) { 576*05e8ec17SMichael Reed (void) mptbase_GetFcPortPage0(ioc, ii); 577*05e8ec17SMichael Reed mptfc_init_host_attr(ioc,ii); /* refresh */ 578*05e8ec17SMichael Reed mptfc_GetFcDevPage0(ioc,ii,mptfc_register_dev); 579*05e8ec17SMichael Reed } 580*05e8ec17SMichael Reed 581*05e8ec17SMichael Reed /* delete devices still missing */ 582*05e8ec17SMichael Reed spin_lock_irqsave(&ioc->fc_rport_lock, flags); 583*05e8ec17SMichael Reed list_for_each_entry(ri, &ioc->fc_rports, list) { 584*05e8ec17SMichael Reed /* if newly missing, delete it */ 585*05e8ec17SMichael Reed if ((ri->flags & (MPT_RPORT_INFO_FLAGS_REGISTERED | 586*05e8ec17SMichael Reed MPT_RPORT_INFO_FLAGS_MISSING)) 587*05e8ec17SMichael Reed == (MPT_RPORT_INFO_FLAGS_REGISTERED | 588*05e8ec17SMichael Reed MPT_RPORT_INFO_FLAGS_MISSING)) { 589*05e8ec17SMichael Reed 590*05e8ec17SMichael Reed ri->flags &= ~(MPT_RPORT_INFO_FLAGS_REGISTERED| 591*05e8ec17SMichael Reed MPT_RPORT_INFO_FLAGS_MISSING); 592*05e8ec17SMichael Reed fc_remote_port_delete(ri->rport); 593*05e8ec17SMichael Reed /* 594*05e8ec17SMichael Reed * remote port not really deleted 'cause 595*05e8ec17SMichael Reed * binding is by WWPN and driver only 596*05e8ec17SMichael Reed * registers FCP_TARGETs 597*05e8ec17SMichael Reed */ 598*05e8ec17SMichael Reed #ifdef MPT_DEBUG 599*05e8ec17SMichael Reed printk ("mptfc_rescan.%d: %llx deleted\n", 600*05e8ec17SMichael Reed ioc->sh->host_no, ri->pg0.WWPN); 601*05e8ec17SMichael Reed #endif 602*05e8ec17SMichael Reed } 603*05e8ec17SMichael Reed } 604*05e8ec17SMichael Reed spin_unlock_irqrestore(&ioc->fc_rport_lock,flags); 605*05e8ec17SMichael Reed 606*05e8ec17SMichael Reed /* 607*05e8ec17SMichael Reed * allow multiple passes as target state 608*05e8ec17SMichael Reed * might have changed during scan 609*05e8ec17SMichael Reed */ 610*05e8ec17SMichael Reed spin_lock_irqsave(&ioc->fc_rescan_work_lock, flags); 611*05e8ec17SMichael Reed if (ioc->fc_rescan_work_count > 2) /* only need one more */ 612*05e8ec17SMichael Reed ioc->fc_rescan_work_count = 2; 613*05e8ec17SMichael Reed work_to_do = --ioc->fc_rescan_work_count; 614*05e8ec17SMichael Reed spin_unlock_irqrestore(&ioc->fc_rescan_work_lock, flags); 615*05e8ec17SMichael Reed } while (work_to_do); 616*05e8ec17SMichael Reed } 617*05e8ec17SMichael Reed 6182496af39SMoore, Eric Dean static int 6192496af39SMoore, Eric Dean mptfc_probe(struct pci_dev *pdev, const struct pci_device_id *id) 6202496af39SMoore, Eric Dean { 6212496af39SMoore, Eric Dean struct Scsi_Host *sh; 6222496af39SMoore, Eric Dean MPT_SCSI_HOST *hd; 6232496af39SMoore, Eric Dean MPT_ADAPTER *ioc; 6242496af39SMoore, Eric Dean unsigned long flags; 6251ca00bb7SChristoph Hellwig int ii; 6262496af39SMoore, Eric Dean int numSGE = 0; 6272496af39SMoore, Eric Dean int scale; 6282496af39SMoore, Eric Dean int ioc_cap; 6292496af39SMoore, Eric Dean int error=0; 6302496af39SMoore, Eric Dean int r; 6312496af39SMoore, Eric Dean 6322496af39SMoore, Eric Dean if ((r = mpt_attach(pdev,id)) != 0) 6332496af39SMoore, Eric Dean return r; 6342496af39SMoore, Eric Dean 6352496af39SMoore, Eric Dean ioc = pci_get_drvdata(pdev); 636d335cc38SMoore, Eric Dean ioc->DoneCtx = mptfcDoneCtx; 637d335cc38SMoore, Eric Dean ioc->TaskCtx = mptfcTaskCtx; 638d335cc38SMoore, Eric Dean ioc->InternalCtx = mptfcInternalCtx; 6392496af39SMoore, Eric Dean 6402496af39SMoore, Eric Dean /* Added sanity check on readiness of the MPT adapter. 6412496af39SMoore, Eric Dean */ 6422496af39SMoore, Eric Dean if (ioc->last_state != MPI_IOC_STATE_OPERATIONAL) { 6432496af39SMoore, Eric Dean printk(MYIOC_s_WARN_FMT 6442496af39SMoore, Eric Dean "Skipping because it's not operational!\n", 6452496af39SMoore, Eric Dean ioc->name); 6467acec1e7SMoore, Eric Dean error = -ENODEV; 6477acec1e7SMoore, Eric Dean goto out_mptfc_probe; 6482496af39SMoore, Eric Dean } 6492496af39SMoore, Eric Dean 6502496af39SMoore, Eric Dean if (!ioc->active) { 6512496af39SMoore, Eric Dean printk(MYIOC_s_WARN_FMT "Skipping because it's disabled!\n", 6522496af39SMoore, Eric Dean ioc->name); 6537acec1e7SMoore, Eric Dean error = -ENODEV; 6547acec1e7SMoore, Eric Dean goto out_mptfc_probe; 6552496af39SMoore, Eric Dean } 6562496af39SMoore, Eric Dean 6572496af39SMoore, Eric Dean /* Sanity check - ensure at least 1 port is INITIATOR capable 6582496af39SMoore, Eric Dean */ 6592496af39SMoore, Eric Dean ioc_cap = 0; 6602496af39SMoore, Eric Dean for (ii=0; ii < ioc->facts.NumberOfPorts; ii++) { 6612496af39SMoore, Eric Dean if (ioc->pfacts[ii].ProtocolFlags & 6622496af39SMoore, Eric Dean MPI_PORTFACTS_PROTOCOL_INITIATOR) 6632496af39SMoore, Eric Dean ioc_cap ++; 6642496af39SMoore, Eric Dean } 6652496af39SMoore, Eric Dean 6662496af39SMoore, Eric Dean if (!ioc_cap) { 6672496af39SMoore, Eric Dean printk(MYIOC_s_WARN_FMT 6682496af39SMoore, Eric Dean "Skipping ioc=%p because SCSI Initiator mode is NOT enabled!\n", 6692496af39SMoore, Eric Dean ioc->name, ioc); 670*05e8ec17SMichael Reed return -ENODEV; 6712496af39SMoore, Eric Dean } 6722496af39SMoore, Eric Dean 6732496af39SMoore, Eric Dean sh = scsi_host_alloc(&mptfc_driver_template, sizeof(MPT_SCSI_HOST)); 6742496af39SMoore, Eric Dean 6752496af39SMoore, Eric Dean if (!sh) { 6762496af39SMoore, Eric Dean printk(MYIOC_s_WARN_FMT 6772496af39SMoore, Eric Dean "Unable to register controller with SCSI subsystem\n", 6782496af39SMoore, Eric Dean ioc->name); 6797acec1e7SMoore, Eric Dean error = -1; 6807acec1e7SMoore, Eric Dean goto out_mptfc_probe; 6812496af39SMoore, Eric Dean } 6822496af39SMoore, Eric Dean 683*05e8ec17SMichael Reed INIT_WORK(&ioc->fc_rescan_work, mptfc_rescan_devices,(void *)ioc); 684*05e8ec17SMichael Reed 6852496af39SMoore, Eric Dean spin_lock_irqsave(&ioc->FreeQlock, flags); 6862496af39SMoore, Eric Dean 6872496af39SMoore, Eric Dean /* Attach the SCSI Host to the IOC structure 6882496af39SMoore, Eric Dean */ 6892496af39SMoore, Eric Dean ioc->sh = sh; 6902496af39SMoore, Eric Dean 6912496af39SMoore, Eric Dean sh->io_port = 0; 6922496af39SMoore, Eric Dean sh->n_io_port = 0; 6932496af39SMoore, Eric Dean sh->irq = 0; 6942496af39SMoore, Eric Dean 6952496af39SMoore, Eric Dean /* set 16 byte cdb's */ 6962496af39SMoore, Eric Dean sh->max_cmd_len = 16; 6972496af39SMoore, Eric Dean 6982496af39SMoore, Eric Dean sh->max_id = MPT_MAX_FC_DEVICES<256 ? MPT_MAX_FC_DEVICES : 255; 6992496af39SMoore, Eric Dean 7002496af39SMoore, Eric Dean sh->max_lun = MPT_LAST_LUN + 1; 7012496af39SMoore, Eric Dean sh->max_channel = 0; 7022496af39SMoore, Eric Dean sh->this_id = ioc->pfacts[0].PortSCSIID; 7032496af39SMoore, Eric Dean 7042496af39SMoore, Eric Dean /* Required entry. 7052496af39SMoore, Eric Dean */ 7062496af39SMoore, Eric Dean sh->unique_id = ioc->id; 7072496af39SMoore, Eric Dean 7082496af39SMoore, Eric Dean /* Verify that we won't exceed the maximum 7092496af39SMoore, Eric Dean * number of chain buffers 7102496af39SMoore, Eric Dean * We can optimize: ZZ = req_sz/sizeof(SGE) 7112496af39SMoore, Eric Dean * For 32bit SGE's: 7122496af39SMoore, Eric Dean * numSGE = 1 + (ZZ-1)*(maxChain -1) + ZZ 7132496af39SMoore, Eric Dean * + (req_sz - 64)/sizeof(SGE) 7142496af39SMoore, Eric Dean * A slightly different algorithm is required for 7152496af39SMoore, Eric Dean * 64bit SGEs. 7162496af39SMoore, Eric Dean */ 7172496af39SMoore, Eric Dean scale = ioc->req_sz/(sizeof(dma_addr_t) + sizeof(u32)); 7182496af39SMoore, Eric Dean if (sizeof(dma_addr_t) == sizeof(u64)) { 7192496af39SMoore, Eric Dean numSGE = (scale - 1) * 7202496af39SMoore, Eric Dean (ioc->facts.MaxChainDepth-1) + scale + 7212496af39SMoore, Eric Dean (ioc->req_sz - 60) / (sizeof(dma_addr_t) + 7222496af39SMoore, Eric Dean sizeof(u32)); 7232496af39SMoore, Eric Dean } else { 7242496af39SMoore, Eric Dean numSGE = 1 + (scale - 1) * 7252496af39SMoore, Eric Dean (ioc->facts.MaxChainDepth-1) + scale + 7262496af39SMoore, Eric Dean (ioc->req_sz - 64) / (sizeof(dma_addr_t) + 7272496af39SMoore, Eric Dean sizeof(u32)); 7282496af39SMoore, Eric Dean } 7292496af39SMoore, Eric Dean 7302496af39SMoore, Eric Dean if (numSGE < sh->sg_tablesize) { 7312496af39SMoore, Eric Dean /* Reset this value */ 7322496af39SMoore, Eric Dean dprintk((MYIOC_s_INFO_FMT 7332496af39SMoore, Eric Dean "Resetting sg_tablesize to %d from %d\n", 7342496af39SMoore, Eric Dean ioc->name, numSGE, sh->sg_tablesize)); 7352496af39SMoore, Eric Dean sh->sg_tablesize = numSGE; 7362496af39SMoore, Eric Dean } 7372496af39SMoore, Eric Dean 7382496af39SMoore, Eric Dean spin_unlock_irqrestore(&ioc->FreeQlock, flags); 7392496af39SMoore, Eric Dean 7402496af39SMoore, Eric Dean hd = (MPT_SCSI_HOST *) sh->hostdata; 7412496af39SMoore, Eric Dean hd->ioc = ioc; 7422496af39SMoore, Eric Dean 7432496af39SMoore, Eric Dean /* SCSI needs scsi_cmnd lookup table! 7442496af39SMoore, Eric Dean * (with size equal to req_depth*PtrSz!) 7452496af39SMoore, Eric Dean */ 7461ca00bb7SChristoph Hellwig hd->ScsiLookup = kcalloc(ioc->req_depth, sizeof(void *), GFP_ATOMIC); 7471ca00bb7SChristoph Hellwig if (!hd->ScsiLookup) { 7482496af39SMoore, Eric Dean error = -ENOMEM; 7497acec1e7SMoore, Eric Dean goto out_mptfc_probe; 7502496af39SMoore, Eric Dean } 7512496af39SMoore, Eric Dean 7521ca00bb7SChristoph Hellwig dprintk((MYIOC_s_INFO_FMT "ScsiLookup @ %p\n", 7531ca00bb7SChristoph Hellwig ioc->name, hd->ScsiLookup)); 7542496af39SMoore, Eric Dean 7552496af39SMoore, Eric Dean /* Allocate memory for the device structures. 7562496af39SMoore, Eric Dean * A non-Null pointer at an offset 7572496af39SMoore, Eric Dean * indicates a device exists. 7582496af39SMoore, Eric Dean * max_id = 1 + maximum id (hosts.h) 7592496af39SMoore, Eric Dean */ 7601ca00bb7SChristoph Hellwig hd->Targets = kcalloc(sh->max_id, sizeof(void *), GFP_ATOMIC); 7611ca00bb7SChristoph Hellwig if (!hd->Targets) { 7622496af39SMoore, Eric Dean error = -ENOMEM; 7637acec1e7SMoore, Eric Dean goto out_mptfc_probe; 7642496af39SMoore, Eric Dean } 7652496af39SMoore, Eric Dean 7661ca00bb7SChristoph Hellwig dprintk((KERN_INFO " vdev @ %p\n", hd->Targets)); 7672496af39SMoore, Eric Dean 7682496af39SMoore, Eric Dean /* Clear the TM flags 7692496af39SMoore, Eric Dean */ 7702496af39SMoore, Eric Dean hd->tmPending = 0; 7712496af39SMoore, Eric Dean hd->tmState = TM_STATE_NONE; 7722496af39SMoore, Eric Dean hd->resetPending = 0; 7732496af39SMoore, Eric Dean hd->abortSCpnt = NULL; 7742496af39SMoore, Eric Dean 7752496af39SMoore, Eric Dean /* Clear the pointer used to store 7762496af39SMoore, Eric Dean * single-threaded commands, i.e., those 7772496af39SMoore, Eric Dean * issued during a bus scan, dv and 7782496af39SMoore, Eric Dean * configuration pages. 7792496af39SMoore, Eric Dean */ 7802496af39SMoore, Eric Dean hd->cmdPtr = NULL; 7812496af39SMoore, Eric Dean 7822496af39SMoore, Eric Dean /* Initialize this SCSI Hosts' timers 7832496af39SMoore, Eric Dean * To use, set the timer expires field 7842496af39SMoore, Eric Dean * and add_timer 7852496af39SMoore, Eric Dean */ 7862496af39SMoore, Eric Dean init_timer(&hd->timer); 7872496af39SMoore, Eric Dean hd->timer.data = (unsigned long) hd; 7882496af39SMoore, Eric Dean hd->timer.function = mptscsih_timer_expired; 7892496af39SMoore, Eric Dean 7902496af39SMoore, Eric Dean hd->mpt_pq_filter = mpt_pq_filter; 7912496af39SMoore, Eric Dean 7922496af39SMoore, Eric Dean ddvprintk((MYIOC_s_INFO_FMT 7932496af39SMoore, Eric Dean "mpt_pq_filter %x\n", 7942496af39SMoore, Eric Dean ioc->name, 7952496af39SMoore, Eric Dean mpt_pq_filter)); 7962496af39SMoore, Eric Dean 7972496af39SMoore, Eric Dean init_waitqueue_head(&hd->scandv_waitq); 7982496af39SMoore, Eric Dean hd->scandv_wait_done = 0; 7992496af39SMoore, Eric Dean hd->last_queue_full = 0; 8002496af39SMoore, Eric Dean 801*05e8ec17SMichael Reed sh->transportt = mptfc_transport_template; 8022496af39SMoore, Eric Dean error = scsi_add_host (sh, &ioc->pcidev->dev); 8032496af39SMoore, Eric Dean if(error) { 8042496af39SMoore, Eric Dean dprintk((KERN_ERR MYNAM 8052496af39SMoore, Eric Dean "scsi_add_host failed\n")); 8067acec1e7SMoore, Eric Dean goto out_mptfc_probe; 8072496af39SMoore, Eric Dean } 8082496af39SMoore, Eric Dean 809*05e8ec17SMichael Reed for (ii=0; ii < ioc->facts.NumberOfPorts; ii++) { 810*05e8ec17SMichael Reed mptfc_init_host_attr(ioc,ii); 811*05e8ec17SMichael Reed mptfc_GetFcDevPage0(ioc,ii,mptfc_register_dev); 812*05e8ec17SMichael Reed } 813*05e8ec17SMichael Reed 8142496af39SMoore, Eric Dean return 0; 8152496af39SMoore, Eric Dean 8167acec1e7SMoore, Eric Dean out_mptfc_probe: 8172496af39SMoore, Eric Dean 8182496af39SMoore, Eric Dean mptscsih_remove(pdev); 8192496af39SMoore, Eric Dean return error; 8202496af39SMoore, Eric Dean } 8212496af39SMoore, Eric Dean 8222496af39SMoore, Eric Dean static struct pci_driver mptfc_driver = { 8232496af39SMoore, Eric Dean .name = "mptfc", 8242496af39SMoore, Eric Dean .id_table = mptfc_pci_table, 8252496af39SMoore, Eric Dean .probe = mptfc_probe, 826*05e8ec17SMichael Reed .remove = __devexit_p(mptfc_remove), 8272496af39SMoore, Eric Dean .shutdown = mptscsih_shutdown, 8282496af39SMoore, Eric Dean #ifdef CONFIG_PM 8292496af39SMoore, Eric Dean .suspend = mptscsih_suspend, 8302496af39SMoore, Eric Dean .resume = mptscsih_resume, 8312496af39SMoore, Eric Dean #endif 8322496af39SMoore, Eric Dean }; 8332496af39SMoore, Eric Dean 8342496af39SMoore, Eric Dean /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ 8352496af39SMoore, Eric Dean /** 8362496af39SMoore, Eric Dean * mptfc_init - Register MPT adapter(s) as SCSI host(s) with 8372496af39SMoore, Eric Dean * linux scsi mid-layer. 8382496af39SMoore, Eric Dean * 8392496af39SMoore, Eric Dean * Returns 0 for success, non-zero for failure. 8402496af39SMoore, Eric Dean */ 8412496af39SMoore, Eric Dean static int __init 8422496af39SMoore, Eric Dean mptfc_init(void) 8432496af39SMoore, Eric Dean { 844*05e8ec17SMichael Reed int error; 8452496af39SMoore, Eric Dean 8462496af39SMoore, Eric Dean show_mptmod_ver(my_NAME, my_VERSION); 8472496af39SMoore, Eric Dean 848*05e8ec17SMichael Reed /* sanity check module parameter */ 849*05e8ec17SMichael Reed if (mptfc_dev_loss_tmo == 0) 850*05e8ec17SMichael Reed mptfc_dev_loss_tmo = MPTFC_DEV_LOSS_TMO; 851*05e8ec17SMichael Reed 852*05e8ec17SMichael Reed mptfc_transport_template = 853*05e8ec17SMichael Reed fc_attach_transport(&mptfc_transport_functions); 854*05e8ec17SMichael Reed 855*05e8ec17SMichael Reed if (!mptfc_transport_template) 856*05e8ec17SMichael Reed return -ENODEV; 857*05e8ec17SMichael Reed 8582496af39SMoore, Eric Dean mptfcDoneCtx = mpt_register(mptscsih_io_done, MPTFC_DRIVER); 8592496af39SMoore, Eric Dean mptfcTaskCtx = mpt_register(mptscsih_taskmgmt_complete, MPTFC_DRIVER); 8602496af39SMoore, Eric Dean mptfcInternalCtx = mpt_register(mptscsih_scandv_complete, MPTFC_DRIVER); 8612496af39SMoore, Eric Dean 8622496af39SMoore, Eric Dean if (mpt_event_register(mptfcDoneCtx, mptscsih_event_process) == 0) { 8632496af39SMoore, Eric Dean devtprintk((KERN_INFO MYNAM 8642496af39SMoore, Eric Dean ": Registered for IOC event notifications\n")); 8652496af39SMoore, Eric Dean } 8662496af39SMoore, Eric Dean 8672496af39SMoore, Eric Dean if (mpt_reset_register(mptfcDoneCtx, mptscsih_ioc_reset) == 0) { 8682496af39SMoore, Eric Dean dprintk((KERN_INFO MYNAM 8692496af39SMoore, Eric Dean ": Registered for IOC reset notifications\n")); 8702496af39SMoore, Eric Dean } 8712496af39SMoore, Eric Dean 872*05e8ec17SMichael Reed error = pci_register_driver(&mptfc_driver); 873*05e8ec17SMichael Reed if (error) { 874*05e8ec17SMichael Reed fc_release_transport(mptfc_transport_template); 875*05e8ec17SMichael Reed } 876*05e8ec17SMichael Reed 877*05e8ec17SMichael Reed return error; 878*05e8ec17SMichael Reed } 879*05e8ec17SMichael Reed 880*05e8ec17SMichael Reed /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ 881*05e8ec17SMichael Reed /** 882*05e8ec17SMichael Reed * mptfc_remove - Removed fc infrastructure for devices 883*05e8ec17SMichael Reed * @pdev: Pointer to pci_dev structure 884*05e8ec17SMichael Reed * 885*05e8ec17SMichael Reed */ 886*05e8ec17SMichael Reed static void __devexit mptfc_remove(struct pci_dev *pdev) 887*05e8ec17SMichael Reed { 888*05e8ec17SMichael Reed MPT_ADAPTER *ioc = pci_get_drvdata(pdev); 889*05e8ec17SMichael Reed struct mptfc_rport_info *p, *n; 890*05e8ec17SMichael Reed 891*05e8ec17SMichael Reed fc_remove_host(ioc->sh); 892*05e8ec17SMichael Reed 893*05e8ec17SMichael Reed list_for_each_entry_safe(p, n, &ioc->fc_rports, list) { 894*05e8ec17SMichael Reed list_del(&p->list); 895*05e8ec17SMichael Reed kfree(p); 896*05e8ec17SMichael Reed } 897*05e8ec17SMichael Reed 898*05e8ec17SMichael Reed mptscsih_remove(pdev); 8992496af39SMoore, Eric Dean } 9002496af39SMoore, Eric Dean 9012496af39SMoore, Eric Dean /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ 9022496af39SMoore, Eric Dean /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ 9032496af39SMoore, Eric Dean /** 9042496af39SMoore, Eric Dean * mptfc_exit - Unregisters MPT adapter(s) 9052496af39SMoore, Eric Dean * 9062496af39SMoore, Eric Dean */ 9072496af39SMoore, Eric Dean static void __exit 9082496af39SMoore, Eric Dean mptfc_exit(void) 9092496af39SMoore, Eric Dean { 9102496af39SMoore, Eric Dean pci_unregister_driver(&mptfc_driver); 911*05e8ec17SMichael Reed fc_release_transport(mptfc_transport_template); 9122496af39SMoore, Eric Dean 9132496af39SMoore, Eric Dean mpt_reset_deregister(mptfcDoneCtx); 9142496af39SMoore, Eric Dean dprintk((KERN_INFO MYNAM 9152496af39SMoore, Eric Dean ": Deregistered for IOC reset notifications\n")); 9162496af39SMoore, Eric Dean 9172496af39SMoore, Eric Dean mpt_event_deregister(mptfcDoneCtx); 9182496af39SMoore, Eric Dean dprintk((KERN_INFO MYNAM 9192496af39SMoore, Eric Dean ": Deregistered for IOC event notifications\n")); 9202496af39SMoore, Eric Dean 9212496af39SMoore, Eric Dean mpt_deregister(mptfcInternalCtx); 9222496af39SMoore, Eric Dean mpt_deregister(mptfcTaskCtx); 9232496af39SMoore, Eric Dean mpt_deregister(mptfcDoneCtx); 9242496af39SMoore, Eric Dean } 9252496af39SMoore, Eric Dean 9262496af39SMoore, Eric Dean module_init(mptfc_init); 9272496af39SMoore, Eric Dean module_exit(mptfc_exit); 928