diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-07 09:28:53 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-07 09:28:53 -0700 |
commit | 4dfddf503670d8def0fddb497e628130fc4522a8 (patch) | |
tree | f4bbf85589c13a1994fef75622ec80c48684f475 /drivers/scsi | |
parent | d4e65476bc68dbc9231b3c772b71f1576579b6fb (diff) | |
parent | 14bf41dcef651c13911a1715e83220732a3a4071 (diff) |
Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
Pull SCSI updates from James Bottomley:
"This update includes the usual round of major driver updates (hpsa,
be2iscsi, hisi_sas, zfcp, cxlflash). There's a new incarnation of hpsa
called smartpqi for which a driver is added, there's some cleanup work
of the ibm vscsi target and updates to libfc, plus a whole host of
minor fixes and updates and finally the removal of several ISA drivers
which seem not to have been used for years"
* tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi: (173 commits)
scsi: mvsas: Mark symbols static where possible
scsi: pm8001: Mark symbols static where possible
scsi: arcmsr: Simplify user_len checking
scsi: fcoe: fix off by one in eth2fc_speed()
scsi: dtc: remove from tree
scsi: t128: remove from tree
scsi: pas16: remove from tree
scsi: u14-34f: remove from tree
scsi: ultrastor: remove from tree
scsi: in2000: remove from tree
scsi: wd7000: remove from tree
scsi: scsi_dh_alua: Fix memory leak in alua_rtpg()
scsi: lpfc: Mark symbols static where possible
scsi: hpsa: correct call to hpsa_do_reset
scsi: ufs: Get a TM service response from the correct offset
scsi: ibmvfc: Fix I/O hang when port is not mapped
scsi: megaraid_sas: clean function declarations in megaraid_sas_base.c up
scsi: ipr: Remove redundant messages at adapter init time
scsi: ipr: Don't log unnecessary 9084 error details
scsi: smartpqi: raid bypass lba calculation fix
...
Diffstat (limited to 'drivers/scsi')
99 files changed, 12547 insertions, 13287 deletions
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 7d1b4317eccc..3e2bdb90813c 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -396,18 +396,6 @@ config SCSI_3W_SAS Please read the comments at the top of <file:drivers/scsi/3w-sas.c>. -config SCSI_7000FASST - tristate "7000FASST SCSI support" - depends on ISA && SCSI && ISA_DMA_API - select CHECK_SIGNATURE - help - This driver supports the Western Digital 7000 SCSI host adapter - family. Some information is in the source: - <file:drivers/scsi/wd7000.c>. - - To compile this driver as a module, choose M here: the - module will be called wd7000. - config SCSI_ACARD tristate "ACARD SCSI support" depends on PCI && SCSI @@ -512,18 +500,6 @@ config SCSI_ADVANSYS To compile this driver as a module, choose M here: the module will be called advansys. -config SCSI_IN2000 - tristate "Always IN2000 SCSI support" - depends on ISA && SCSI - help - This is support for an ISA bus SCSI host adapter. You'll find more - information in <file:Documentation/scsi/in2000.txt>. If it doesn't work - out of the box, you may have to change the jumpers for IRQ or - address selection. - - To compile this driver as a module, choose M here: the - module will be called in2000. - config SCSI_ARCMSR tristate "ARECA (ARC11xx/12xx/13xx/16xx) SATA/SAS RAID Host Adapter" depends on PCI && SCSI @@ -540,6 +516,7 @@ config SCSI_ARCMSR source "drivers/scsi/esas2r/Kconfig" source "drivers/scsi/megaraid/Kconfig.megaraid" source "drivers/scsi/mpt3sas/Kconfig" +source "drivers/scsi/smartpqi/Kconfig" source "drivers/scsi/ufs/Kconfig" config SCSI_HPTIOP @@ -660,20 +637,6 @@ config SCSI_DMX3191D To compile this driver as a module, choose M here: the module will be called dmx3191d. -config SCSI_DTC3280 - tristate "DTC3180/3280 SCSI support" - depends on ISA && SCSI - select SCSI_SPI_ATTRS - select CHECK_SIGNATURE - help - This is support for DTC 3180/3280 SCSI Host Adapters. Please read - the SCSI-HOWTO, available from - <http://www.tldp.org/docs.html#howto>, and the file - <file:Documentation/scsi/dtc3x80.txt>. - - To compile this driver as a module, choose M here: the - module will be called dtc. - config SCSI_EATA tristate "EATA ISA/EISA/PCI (DPT and generic EATA/DMA-compliant boards) support" depends on (ISA || EISA || PCI) && SCSI && ISA_DMA_API @@ -1248,20 +1211,6 @@ config SCSI_NCR53C8XX_NO_DISCONNECT not allow targets to disconnect is not reasonable if there is more than 1 device on a SCSI bus. The normal answer therefore is N. -config SCSI_PAS16 - tristate "PAS16 SCSI support" - depends on ISA && SCSI - select SCSI_SPI_ATTRS - ---help--- - This is support for a SCSI host adapter. It is explained in section - 3.10 of the SCSI-HOWTO, available from - <http://www.tldp.org/docs.html#howto>. If it doesn't work out - of the box, you may have to change some settings in - <file:drivers/scsi/pas16.h>. - - To compile this driver as a module, choose M here: the - module will be called pas16. - config SCSI_QLOGIC_FAS tristate "Qlogic FAS SCSI support" depends on ISA && SCSI @@ -1382,89 +1331,6 @@ config SCSI_AM53C974 To compile this driver as a module, choose M here: the module will be called am53c974. -config SCSI_T128 - tristate "Trantor T128/T128F/T228 SCSI support" - depends on ISA && SCSI - select SCSI_SPI_ATTRS - select CHECK_SIGNATURE - ---help--- - This is support for a SCSI host adapter. It is explained in section - 3.11 of the SCSI-HOWTO, available from - <http://www.tldp.org/docs.html#howto>. If it doesn't work out - of the box, you may have to change some settings in - <file:drivers/scsi/t128.h>. Note that Trantor was purchased by - Adaptec, and some former Trantor products are being sold under the - Adaptec name. - - To compile this driver as a module, choose M here: the - module will be called t128. - -config SCSI_U14_34F - tristate "UltraStor 14F/34F support" - depends on ISA && SCSI && ISA_DMA_API - ---help--- - This is support for the UltraStor 14F and 34F SCSI-2 host adapters. - The source at <file:drivers/scsi/u14-34f.c> contains some - information about this hardware. If the driver doesn't work out of - the box, you may have to change some settings in - <file: drivers/scsi/u14-34f.c>. Read the SCSI-HOWTO, available from - <http://www.tldp.org/docs.html#howto>. Note that there is also - another driver for the same hardware: "UltraStor SCSI support", - below. You should say Y to both only if you want 24F support as - well. - - To compile this driver as a module, choose M here: the - module will be called u14-34f. - -config SCSI_U14_34F_TAGGED_QUEUE - bool "enable tagged command queueing" - depends on SCSI_U14_34F - help - This is a feature of SCSI-2 which improves performance: the host - adapter can send several SCSI commands to a device's queue even if - previous commands haven't finished yet. - This is equivalent to the "u14-34f=tc:y" boot option. - -config SCSI_U14_34F_LINKED_COMMANDS - bool "enable elevator sorting" - depends on SCSI_U14_34F - help - This option enables elevator sorting for all probed SCSI disks and - CD-ROMs. It definitely reduces the average seek distance when doing - random seeks, but this does not necessarily result in a noticeable - performance improvement: your mileage may vary... - This is equivalent to the "u14-34f=lc:y" boot option. - -config SCSI_U14_34F_MAX_TAGS - int "maximum number of queued commands" - depends on SCSI_U14_34F - default "8" - help - This specifies how many SCSI commands can be maximally queued for - each probed SCSI device. You should reduce the default value of 8 - only if you have disks with buggy or limited tagged command support. - Minimum is 2 and maximum is 14. This value is also the window size - used by the elevator sorting option above. The effective value used - by the driver for each probed SCSI device is reported at boot time. - This is equivalent to the "u14-34f=mq:8" boot option. - -config SCSI_ULTRASTOR - tristate "UltraStor SCSI support" - depends on X86 && ISA && SCSI && ISA_DMA_API - ---help--- - This is support for the UltraStor 14F, 24F and 34F SCSI-2 host - adapter family. This driver is explained in section 3.12 of the - SCSI-HOWTO, available from - <http://www.tldp.org/docs.html#howto>. If it doesn't work out - of the box, you may have to change some settings in - <file:drivers/scsi/ultrastor.h>. - - Note that there is also another driver for the same hardware: - "UltraStor 14F/34F support", above. - - To compile this driver as a module, choose M here: the - module will be called ultrastor. - config SCSI_NSP32 tristate "Workbit NinjaSCSI-32Bi/UDE support" depends on PCI && SCSI && !64BIT diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index d5397987e731..38d938d7fe67 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -61,9 +61,7 @@ obj-$(CONFIG_SCSI_SIM710) += 53c700.o sim710.o obj-$(CONFIG_SCSI_ADVANSYS) += advansys.o obj-$(CONFIG_SCSI_BUSLOGIC) += BusLogic.o obj-$(CONFIG_SCSI_DPT_I2O) += dpt_i2o.o -obj-$(CONFIG_SCSI_U14_34F) += u14-34f.o obj-$(CONFIG_SCSI_ARCMSR) += arcmsr/ -obj-$(CONFIG_SCSI_ULTRASTOR) += ultrastor.o obj-$(CONFIG_SCSI_AHA152X) += aha152x.o obj-$(CONFIG_SCSI_AHA1542) += aha1542.o obj-$(CONFIG_SCSI_AHA1740) += aha1740.o @@ -75,7 +73,6 @@ obj-$(CONFIG_SCSI_PM8001) += pm8001/ obj-$(CONFIG_SCSI_ISCI) += isci/ obj-$(CONFIG_SCSI_IPS) += ips.o obj-$(CONFIG_SCSI_FUTURE_DOMAIN)+= fdomain.o -obj-$(CONFIG_SCSI_IN2000) += in2000.o obj-$(CONFIG_SCSI_GENERIC_NCR5380) += g_NCR5380.o obj-$(CONFIG_SCSI_GENERIC_NCR5380_MMIO) += g_NCR5380_mmio.o obj-$(CONFIG_SCSI_NCR53C406A) += NCR53c406a.o @@ -90,15 +87,12 @@ obj-$(CONFIG_SCSI_QLA_ISCSI) += libiscsi.o qla4xxx/ obj-$(CONFIG_SCSI_LPFC) += lpfc/ obj-$(CONFIG_SCSI_BFA_FC) += bfa/ obj-$(CONFIG_SCSI_CHELSIO_FCOE) += csiostor/ -obj-$(CONFIG_SCSI_PAS16) += pas16.o -obj-$(CONFIG_SCSI_T128) += t128.o obj-$(CONFIG_SCSI_DMX3191D) += dmx3191d.o obj-$(CONFIG_SCSI_HPSA) += hpsa.o -obj-$(CONFIG_SCSI_DTC3280) += dtc.o +obj-$(CONFIG_SCSI_SMARTPQI) += smartpqi/ obj-$(CONFIG_SCSI_SYM53C8XX_2) += sym53c8xx_2/ obj-$(CONFIG_SCSI_ZALON) += zalon7xx.o obj-$(CONFIG_SCSI_EATA_PIO) += eata_pio.o -obj-$(CONFIG_SCSI_7000FASST) += wd7000.o obj-$(CONFIG_SCSI_EATA) += eata.o obj-$(CONFIG_SCSI_DC395x) += dc395x.o obj-$(CONFIG_SCSI_AM53C974) += esp_scsi.o am53c974.o diff --git a/drivers/scsi/NCR5380.c b/drivers/scsi/NCR5380.c index 43908bbb3b23..db2739079cbb 100644 --- a/drivers/scsi/NCR5380.c +++ b/drivers/scsi/NCR5380.c @@ -230,13 +230,6 @@ static int NCR5380_poll_politely2(struct Scsi_Host *instance, return -ETIMEDOUT; } -static inline int NCR5380_poll_politely(struct Scsi_Host *instance, - int reg, int bit, int val, int wait) -{ - return NCR5380_poll_politely2(instance, reg, bit, val, - reg, bit, val, wait); -} - #if NDEBUG static struct { unsigned char mask; @@ -1854,11 +1847,11 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) /* XXX - need to source or sink data here, as appropriate */ } } else { - /* Break up transfer into 3 ms chunks, - * presuming 6 accesses per handshake. + /* Transfer a small chunk so that the + * irq mode lock is not held too long. */ - transfersize = min((unsigned long)cmd->SCp.this_residual, - hostdata->accesses_per_ms / 2); + transfersize = min(cmd->SCp.this_residual, + NCR5380_PIO_CHUNK_SIZE); len = transfersize; NCR5380_transfer_pio(instance, &phase, &len, (unsigned char **)&cmd->SCp.ptr); diff --git a/drivers/scsi/NCR5380.h b/drivers/scsi/NCR5380.h index c60728785d89..965d92339455 100644 --- a/drivers/scsi/NCR5380.h +++ b/drivers/scsi/NCR5380.h @@ -250,6 +250,8 @@ struct NCR5380_cmd { #define NCR5380_CMD_SIZE (sizeof(struct NCR5380_cmd)) +#define NCR5380_PIO_CHUNK_SIZE 256 + static inline struct scsi_cmnd *NCR5380_to_scmd(struct NCR5380_cmd *ncmd_ptr) { return ((struct scsi_cmnd *)ncmd_ptr) - 1; @@ -292,8 +294,14 @@ static void NCR5380_reselect(struct Scsi_Host *instance); static struct scsi_cmnd *NCR5380_select(struct Scsi_Host *, struct scsi_cmnd *); static int NCR5380_transfer_dma(struct Scsi_Host *instance, unsigned char *phase, int *count, unsigned char **data); static int NCR5380_transfer_pio(struct Scsi_Host *instance, unsigned char *phase, int *count, unsigned char **data); -static int NCR5380_poll_politely(struct Scsi_Host *, int, int, int, int); static int NCR5380_poll_politely2(struct Scsi_Host *, int, int, int, int, int, int, int); +static inline int NCR5380_poll_politely(struct Scsi_Host *instance, + int reg, int bit, int val, int wait) +{ + return NCR5380_poll_politely2(instance, reg, bit, val, + reg, bit, val, wait); +} + #endif /* __KERNEL__ */ #endif /* NCR5380_H */ diff --git a/drivers/scsi/aacraid/src.c b/drivers/scsi/aacraid/src.c index 28f8b8a1b8a4..0c453880f214 100644 --- a/drivers/scsi/aacraid/src.c +++ b/drivers/scsi/aacraid/src.c @@ -613,7 +613,7 @@ static int aac_src_restart_adapter(struct aac_dev *dev, int bled) * @dev: Adapter * @comm: communications method */ -int aac_src_select_comm(struct aac_dev *dev, int comm) +static int aac_src_select_comm(struct aac_dev *dev, int comm) { switch (comm) { case AAC_COMM_MESSAGE: diff --git a/drivers/scsi/aic94xx/aic94xx_hwi.c b/drivers/scsi/aic94xx/aic94xx_hwi.c index 0fdc98bc2338..7c713f797535 100644 --- a/drivers/scsi/aic94xx/aic94xx_hwi.c +++ b/drivers/scsi/aic94xx/aic94xx_hwi.c @@ -632,7 +632,7 @@ int asd_init_hw(struct asd_ha_struct *asd_ha) pci_name(asd_ha->pcidev)); return err; } - pci_write_config_dword(asd_ha->pcidev, PCIC_HSTPCIX_CNTRL, + err = pci_write_config_dword(asd_ha->pcidev, PCIC_HSTPCIX_CNTRL, v | SC_TMR_DIS); if (err) { asd_printk("couldn't disable split completion timer of %s\n", diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index 7640498964a5..3d53d636b17b 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -2388,15 +2388,23 @@ static int arcmsr_iop_message_xfer(struct AdapterControlBlock *acb, } case ARCMSR_MESSAGE_WRITE_WQBUFFER: { unsigned char *ver_addr; - int32_t user_len, cnt2end; + uint32_t user_len; + int32_t cnt2end; uint8_t *pQbuffer, *ptmpuserbuffer; + + user_len = pcmdmessagefld->cmdmessage.Length; + if (user_len > ARCMSR_API_DATA_BUFLEN) { + retvalue = ARCMSR_MESSAGE_FAIL; + goto message_out; + } + ver_addr = kmalloc(ARCMSR_API_DATA_BUFLEN, GFP_ATOMIC); if (!ver_addr) { retvalue = ARCMSR_MESSAGE_FAIL; goto message_out; } ptmpuserbuffer = ver_addr; - user_len = pcmdmessagefld->cmdmessage.Length; + memcpy(ptmpuserbuffer, pcmdmessagefld->messagedatabuffer, user_len); spin_lock_irqsave(&acb->wqbuffer_lock, flags); diff --git a/drivers/scsi/be2iscsi/be.h b/drivers/scsi/be2iscsi/be.h index ee5ace873535..b1d0fdc5d5e1 100644 --- a/drivers/scsi/be2iscsi/be.h +++ b/drivers/scsi/be2iscsi/be.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Emulex + * Copyright (C) 2005 - 2016 Broadcom * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -8,7 +8,7 @@ * Public License is included in this distribution in the file called COPYING. * * Contact Information: - * linux-drivers@avagotech.com + * linux-drivers@broadcom.com * * Emulex * 3333 Susan Street @@ -89,7 +89,7 @@ struct be_aic_obj { /* Adaptive interrupt coalescing (AIC) info */ u32 max_eqd; /* in usecs */ u32 prev_eqd; /* in usecs */ u32 et_eqd; /* configured val when aic is off */ - ulong jiffs; + ulong jiffies; u64 eq_prev; /* Used to calculate eqe */ }; @@ -100,7 +100,7 @@ struct be_eq_obj { struct be_queue_info q; struct beiscsi_hba *phba; struct be_queue_info *cq; - struct work_struct work_cqs; /* Work Item */ + struct work_struct mcc_work; /* Work Item */ struct irq_poll iopoll; }; @@ -111,8 +111,11 @@ struct be_mcc_obj { struct beiscsi_mcc_tag_state { unsigned long tag_state; -#define MCC_TAG_STATE_RUNNING 1 -#define MCC_TAG_STATE_TIMEOUT 2 +#define MCC_TAG_STATE_RUNNING 0 +#define MCC_TAG_STATE_TIMEOUT 1 +#define MCC_TAG_STATE_ASYNC 2 +#define MCC_TAG_STATE_IGNORE 3 + void (*cbfn)(struct beiscsi_hba *, unsigned int); struct be_dma_mem tag_mem_state; }; diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c index a55eaeea37e7..be65da2988fb 100644 --- a/drivers/scsi/be2iscsi/be_cmds.c +++ b/drivers/scsi/be2iscsi/be_cmds.c @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Emulex + * Copyright (C) 2005 - 2016 Broadcom * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -8,7 +8,7 @@ * Public License is included in this distribution in the file called COPYING. * * Contact Information: - * linux-drivers@avagotech.com + * linux-drivers@broadcom.com * * Emulex * 3333 Susan Street @@ -21,110 +21,77 @@ #include "be.h" #include "be_mgmt.h" -int beiscsi_pci_soft_reset(struct beiscsi_hba *phba) -{ - u32 sreset; - u8 *pci_reset_offset = 0; - u8 *pci_online0_offset = 0; - u8 *pci_online1_offset = 0; - u32 pconline0 = 0; - u32 pconline1 = 0; - u32 i; - - pci_reset_offset = (u8 *)phba->pci_va + BE2_SOFT_RESET; - pci_online0_offset = (u8 *)phba->pci_va + BE2_PCI_ONLINE0; - pci_online1_offset = (u8 *)phba->pci_va + BE2_PCI_ONLINE1; - sreset = readl((void *)pci_reset_offset); - sreset |= BE2_SET_RESET; - writel(sreset, (void *)pci_reset_offset); - - i = 0; - while (sreset & BE2_SET_RESET) { - if (i > 64) - break; - msleep(100); - sreset = readl((void *)pci_reset_offset); - i++; - } - - if (sreset & BE2_SET_RESET) { - printk(KERN_ERR DRV_NAME - " Soft Reset did not deassert\n"); - return -EIO; - } - pconline1 = BE2_MPU_IRAM_ONLINE; - writel(pconline0, (void *)pci_online0_offset); - writel(pconline1, (void *)pci_online1_offset); - - sreset |= BE2_SET_RESET; - writel(sreset, (void *)pci_reset_offset); - - i = 0; - while (sreset & BE2_SET_RESET) { - if (i > 64) - break; - msleep(1); - sreset = readl((void *)pci_reset_offset); - i++; - } - if (sreset & BE2_SET_RESET) { - printk(KERN_ERR DRV_NAME - " MPU Online Soft Reset did not deassert\n"); - return -EIO; - } - return 0; -} - -int be_chk_reset_complete(struct beiscsi_hba *phba) -{ - unsigned int num_loop; - u8 *mpu_sem = 0; - u32 status; - - num_loop = 1000; - mpu_sem = (u8 *)phba->csr_va + MPU_EP_SEMAPHORE; - msleep(5000); - - while (num_loop) { - status = readl((void *)mpu_sem); - - if ((status & 0x80000000) || (status & 0x0000FFFF) == 0xC000) - break; - msleep(60); - num_loop--; - } - - if ((status & 0x80000000) || (!num_loop)) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BC_%d : Failed in be_chk_reset_complete" - "status = 0x%x\n", status); - return -EIO; - } - - return 0; -} - -unsigned int alloc_mcc_tag(struct beiscsi_hba *phba) -{ - unsigned int tag = 0; +/* UE Status Low CSR */ +static const char * const desc_ue_status_low[] = { + "CEV", + "CTX", + "DBUF", + "ERX", + "Host", + "MPU", + "NDMA", + "PTC ", + "RDMA ", + "RXF ", + "RXIPS ", + "RXULP0 ", + "RXULP1 ", + "RXULP2 ", + "TIM ", + "TPOST ", + "TPRE ", + "TXIPS ", + "TXULP0 ", + "TXULP1 ", + "UC ", + "WDMA ", + "TXULP2 ", + "HOST1 ", + "P0_OB_LINK ", + "P1_OB_LINK ", + "HOST_GPIO ", + "MBOX ", + "AXGMAC0", + "AXGMAC1", + "JTAG", + "MPU_INTPEND" +}; - spin_lock(&phba->ctrl.mcc_lock); - if (phba->ctrl.mcc_tag_available) { - tag = phba->ctrl.mcc_tag[phba->ctrl.mcc_alloc_index]; - phba->ctrl.mcc_tag[phba->ctrl.mcc_alloc_index] = 0; - phba->ctrl.mcc_tag_status[tag] = 0; - phba->ctrl.ptag_state[tag].tag_state = 0; - } - if (tag) { - phba->ctrl.mcc_tag_available--; - if (phba->ctrl.mcc_alloc_index == (MAX_MCC_CMD - 1)) - phba->ctrl.mcc_alloc_index = 0; - else - phba->ctrl.mcc_alloc_index++; - } - spin_unlock(&phba->ctrl.mcc_lock); - return tag; -} +/* UE Status High CSR */ +static const char * const desc_ue_status_hi[] = { + "LPCMEMHOST", + "MGMT_MAC", + "PCS0ONLINE", + "MPU_IRAM", + "PCS1ONLINE", + "PCTL0", + "PCTL1", + "PMEM", + "RR", + "TXPB", + "RXPP", + "XAUI", + "TXP", + "ARM", + "IPC", + "HOST2", + "HOST3", + "HOST4", + "HOST5", + "HOST6", + "HOST7", + "HOST8", + "HOST9", + "NETC", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown" +}; struct be_mcc_wrb *alloc_mcc_wrb(struct beiscsi_hba *phba, unsigned int *ref_tag) @@ -133,7 +100,7 @@ struct be_mcc_wrb *alloc_mcc_wrb(struct beiscsi_hba *phba, struct be_mcc_wrb *wrb = NULL; unsigned int tag; - spin_lock_bh(&phba->ctrl.mcc_lock); + spin_lock(&phba->ctrl.mcc_lock); if (mccq->used == mccq->len) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, @@ -160,6 +127,7 @@ struct be_mcc_wrb *alloc_mcc_wrb(struct beiscsi_hba *phba, phba->ctrl.mcc_tag[phba->ctrl.mcc_alloc_index] = 0; phba->ctrl.mcc_tag_status[tag] = 0; phba->ctrl.ptag_state[tag].tag_state = 0; + phba->ctrl.ptag_state[tag].cbfn = NULL; phba->ctrl.mcc_tag_available--; if (phba->ctrl.mcc_alloc_index == (MAX_MCC_CMD - 1)) phba->ctrl.mcc_alloc_index = 0; @@ -174,7 +142,7 @@ struct be_mcc_wrb *alloc_mcc_wrb(struct beiscsi_hba *phba, mccq->used++; alloc_failed: - spin_unlock_bh(&phba->ctrl.mcc_lock); + spin_unlock(&phba->ctrl.mcc_lock); return wrb; } @@ -182,7 +150,7 @@ void free_mcc_wrb(struct be_ctrl_info *ctrl, unsigned int tag) { struct be_queue_info *mccq = &ctrl->mcc_obj.q; - spin_lock_bh(&ctrl->mcc_lock); + spin_lock(&ctrl->mcc_lock); tag = tag & MCC_Q_CMD_TAG_MASK; ctrl->mcc_tag[ctrl->mcc_free_index] = tag; if (ctrl->mcc_free_index == (MAX_MCC_CMD - 1)) @@ -191,16 +159,71 @@ void free_mcc_wrb(struct be_ctrl_info *ctrl, unsigned int tag) ctrl->mcc_free_index++; ctrl->mcc_tag_available++; mccq->used--; - spin_unlock_bh(&ctrl->mcc_lock); + spin_unlock(&ctrl->mcc_lock); } -/** - * beiscsi_fail_session(): Closing session with appropriate error - * @cls_session: ptr to session - **/ -void beiscsi_fail_session(struct iscsi_cls_session *cls_session) +/* + * beiscsi_mcc_compl_status - Return the status of MCC completion + * @phba: Driver private structure + * @tag: Tag for the MBX Command + * @wrb: the WRB used for the MBX Command + * @mbx_cmd_mem: ptr to memory allocated for MBX Cmd + * + * return + * Success: 0 + * Failure: Non-Zero + */ +int __beiscsi_mcc_compl_status(struct beiscsi_hba *phba, + unsigned int tag, + struct be_mcc_wrb **wrb, + struct be_dma_mem *mbx_cmd_mem) { - iscsi_session_failure(cls_session->dd_data, ISCSI_ERR_CONN_FAILED); + struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q; + uint16_t status = 0, addl_status = 0, wrb_num = 0; + struct be_cmd_resp_hdr *mbx_resp_hdr; + struct be_cmd_req_hdr *mbx_hdr; + struct be_mcc_wrb *temp_wrb; + uint32_t mcc_tag_status; + int rc = 0; + + mcc_tag_status = phba->ctrl.mcc_tag_status[tag]; + status = (mcc_tag_status & CQE_STATUS_MASK); + addl_status = ((mcc_tag_status & CQE_STATUS_ADDL_MASK) >> + CQE_STATUS_ADDL_SHIFT); + + if (mbx_cmd_mem) { + mbx_hdr = (struct be_cmd_req_hdr *)mbx_cmd_mem->va; + } else { + wrb_num = (mcc_tag_status & CQE_STATUS_WRB_MASK) >> + CQE_STATUS_WRB_SHIFT; + temp_wrb = (struct be_mcc_wrb *)queue_get_wrb(mccq, wrb_num); + mbx_hdr = embedded_payload(temp_wrb); + + if (wrb) + *wrb = temp_wrb; + } + + if (status || addl_status) { + beiscsi_log(phba, KERN_WARNING, + BEISCSI_LOG_INIT | BEISCSI_LOG_EH | + BEISCSI_LOG_CONFIG, + "BC_%d : MBX Cmd Failed for Subsys : %d Opcode : %d with Status : %d and Extd_Status : %d\n", + mbx_hdr->subsystem, mbx_hdr->opcode, + status, addl_status); + rc = -EIO; + if (status == MCC_STATUS_INSUFFICIENT_BUFFER) { + mbx_resp_hdr = (struct be_cmd_resp_hdr *)mbx_hdr; + beiscsi_log(phba, KERN_WARNING, + BEISCSI_LOG_INIT | BEISCSI_LOG_EH | + BEISCSI_LOG_CONFIG, + "BC_%d : Insufficient Buffer Error Resp_Len : %d Actual_Resp_Len : %d\n", + mbx_resp_hdr->response_length, + mbx_resp_hdr->actual_resp_len); + rc = -EAGAIN; + } + } + + return rc; } /* @@ -217,26 +240,34 @@ void beiscsi_fail_session(struct iscsi_cls_session *cls_session) * Failure: Non-Zero **/ int beiscsi_mccq_compl_wait(struct beiscsi_hba *phba, - uint32_t tag, struct be_mcc_wrb **wrb, + unsigned int tag, + struct be_mcc_wrb **wrb, struct be_dma_mem *mbx_cmd_mem) { int rc = 0; - uint32_t mcc_tag_status; - uint16_t status = 0, addl_status = 0, wrb_num = 0; - struct be_mcc_wrb *temp_wrb; - struct be_cmd_req_hdr *mbx_hdr; - struct be_cmd_resp_hdr *mbx_resp_hdr; - struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q; - if (beiscsi_error(phba)) - return -EPERM; + if (beiscsi_hba_in_error(phba)) { + clear_bit(MCC_TAG_STATE_RUNNING, + &phba->ctrl.ptag_state[tag].tag_state); + return -EIO; + } /* wait for the mccq completion */ - rc = wait_event_interruptible_timeout( - phba->ctrl.mcc_wait[tag], - phba->ctrl.mcc_tag_status[tag], - msecs_to_jiffies( - BEISCSI_HOST_MBX_TIMEOUT)); + rc = wait_event_interruptible_timeout(phba->ctrl.mcc_wait[tag], + phba->ctrl.mcc_tag_status[tag], + msecs_to_jiffies( + BEISCSI_HOST_MBX_TIMEOUT)); + /** + * Return EIO if port is being disabled. Associated DMA memory, if any, + * is freed by the caller. When port goes offline, MCCQ is cleaned up + * so does WRB. + */ + if (!test_bit(BEISCSI_HBA_ONLINE, &phba->state)) { + clear_bit(MCC_TAG_STATE_RUNNING, + &phba->ctrl.ptag_state[tag].tag_state); + return -EIO; + } + /** * If MBOX cmd timeout expired, tag and resource allocated * for cmd is not freed until FW returns completion. @@ -270,47 +301,7 @@ int beiscsi_mccq_compl_wait(struct beiscsi_hba *phba, return -EBUSY; } - rc = 0; - mcc_tag_status = phba->ctrl.mcc_tag_status[tag]; - status = (mcc_tag_status & CQE_STATUS_MASK); - addl_status = ((mcc_tag_status & CQE_STATUS_ADDL_MASK) >> - CQE_STATUS_ADDL_SHIFT); - - if (mbx_cmd_mem) { - mbx_hdr = (struct be_cmd_req_hdr *)mbx_cmd_mem->va; - } else { - wrb_num = (mcc_tag_status & CQE_STATUS_WRB_MASK) >> - CQE_STATUS_WRB_SHIFT; - temp_wrb = (struct be_mcc_wrb *)queue_get_wrb(mccq, wrb_num); - mbx_hdr = embedded_payload(temp_wrb); - - if (wrb) - *wrb = temp_wrb; - } - - if (status || addl_status) { - beiscsi_log(phba, KERN_WARNING, - BEISCSI_LOG_INIT | BEISCSI_LOG_EH | - BEISCSI_LOG_CONFIG, - "BC_%d : MBX Cmd Failed for " - "Subsys : %d Opcode : %d with " - "Status : %d and Extd_Status : %d\n", - mbx_hdr->subsystem, - mbx_hdr->opcode, - status, addl_status); - rc = -EIO; - if (status == MCC_STATUS_INSUFFICIENT_BUFFER) { - mbx_resp_hdr = (struct be_cmd_resp_hdr *) mbx_hdr; - beiscsi_log(phba, KERN_WARNING, - BEISCSI_LOG_INIT | BEISCSI_LOG_EH | - BEISCSI_LOG_CONFIG, - "BC_%d : Insufficient Buffer Error " - "Resp_Len : %d Actual_Resp_Len : %d\n", - mbx_resp_hdr->response_length, - mbx_resp_hdr->actual_resp_len); - rc = -EAGAIN; - } - } + rc = __beiscsi_mcc_compl_status(phba, tag, wrb, mbx_cmd_mem); free_mcc_wrb(&phba->ctrl, tag); return rc; @@ -330,11 +321,10 @@ int beiscsi_mccq_compl_wait(struct beiscsi_hba *phba, static int beiscsi_process_mbox_compl(struct be_ctrl_info *ctrl, struct be_mcc_compl *compl) { - u16 compl_status, extd_status; struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); struct beiscsi_hba *phba = pci_get_drvdata(ctrl->pdev); struct be_cmd_req_hdr *hdr = embedded_payload(wrb); - struct be_cmd_resp_hdr *resp_hdr; + u16 compl_status, extd_status; /** * To check if valid bit is set, check the entire word as we don't know @@ -368,14 +358,7 @@ static int beiscsi_process_mbox_compl(struct be_ctrl_info *ctrl, beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, "BC_%d : error in cmd completion: Subsystem : %d Opcode : %d status(compl/extd)=%d/%d\n", hdr->subsystem, hdr->opcode, compl_status, extd_status); - - if (compl_status == MCC_STATUS_INSUFFICIENT_BUFFER) { - /* if status is insufficient buffer, check the length */ - resp_hdr = (struct be_cmd_resp_hdr *) hdr; - if (resp_hdr->response_length) - return 0; - } - return -EINVAL; + return compl_status; } static void beiscsi_process_async_link(struct beiscsi_hba *phba, @@ -391,18 +374,19 @@ static void beiscsi_process_async_link(struct beiscsi_hba *phba, * This has been newly introduced in SKH-R Firmware 10.0.338.45. **/ if (evt->port_link_status & BE_ASYNC_LINK_UP_MASK) { - phba->state = BE_ADAPTER_LINK_UP | BE_ADAPTER_CHECK_BOOT; - phba->get_boot = BE_GET_BOOT_RETRIES; + set_bit(BEISCSI_HBA_LINK_UP, &phba->state); + if (test_bit(BEISCSI_HBA_BOOT_FOUND, &phba->state)) + beiscsi_start_boot_work(phba, BE_BOOT_INVALID_SHANDLE); __beiscsi_log(phba, KERN_ERR, "BC_%d : Link Up on Port %d tag 0x%x\n", evt->physical_port, evt->event_tag); } else { - phba->state = BE_ADAPTER_LINK_DOWN; + clear_bit(BEISCSI_HBA_LINK_UP, &phba->state); __beiscsi_log(phba, KERN_ERR, "BC_%d : Link Down on Port %d tag 0x%x\n", evt->physical_port, evt->event_tag); iscsi_host_for_each_session(phba->shost, - beiscsi_fail_session); + beiscsi_session_fail); } } @@ -482,8 +466,8 @@ void beiscsi_process_async_event(struct beiscsi_hba *phba, beiscsi_process_async_link(phba, compl); break; case ASYNC_EVENT_CODE_ISCSI: - phba->state |= BE_ADAPTER_CHECK_BOOT; - phba->get_boot = BE_GET_BOOT_RETRIES; + if (test_bit(BEISCSI_HBA_BOOT_FOUND, &phba->state)) + beiscsi_start_boot_work(phba, BE_BOOT_INVALID_SHANDLE); sev = KERN_ERR; break; case ASYNC_EVENT_CODE_SLI: @@ -519,6 +503,9 @@ int beiscsi_process_mcc_compl(struct be_ctrl_info *ctrl, return 0; } + /* end MCC with this tag */ + clear_bit(MCC_TAG_STATE_RUNNING, &ctrl->ptag_state[tag].tag_state); + if (test_bit(MCC_TAG_STATE_TIMEOUT, &ctrl->ptag_state[tag].tag_state)) { beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_MBOX | BEISCSI_LOG_INIT | @@ -529,9 +516,11 @@ int beiscsi_process_mcc_compl(struct be_ctrl_info *ctrl, * Only for non-embedded cmd, PCI resource is allocated. **/ tag_mem = &ctrl->ptag_state[tag].tag_mem_state; - if (tag_mem->size) + if (tag_mem->size) { pci_free_consistent(ctrl->pdev, tag_mem->size, tag_mem->va, tag_mem->dma); + tag_mem->size = 0; + } free_mcc_wrb(ctrl, tag); return 0; } @@ -550,57 +539,25 @@ int beiscsi_process_mcc_compl(struct be_ctrl_info *ctrl, CQE_STATUS_ADDL_MASK; ctrl->mcc_tag_status[tag] |= (compl_status & CQE_STATUS_MASK); - /* write ordering forced in wake_up_interruptible */ - clear_bit(MCC_TAG_STATE_RUNNING, &ctrl->ptag_state[tag].tag_state); - wake_up_interruptible(&ctrl->mcc_wait[tag]); - return 0; -} - -/* - * be_mcc_compl_poll()- Wait for MBX completion - * @phba: driver private structure - * - * Wait till no more pending mcc requests are present - * - * return - * Success: 0 - * Failure: Non-Zero - * - **/ -int be_mcc_compl_poll(struct beiscsi_hba *phba, unsigned int tag) -{ - struct be_ctrl_info *ctrl = &phba->ctrl; - int i; - - if (!test_bit(MCC_TAG_STATE_RUNNING, - &ctrl->ptag_state[tag].tag_state)) { - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, - "BC_%d: tag %u state not running\n", tag); + if (test_bit(MCC_TAG_STATE_ASYNC, &ctrl->ptag_state[tag].tag_state)) { + if (ctrl->ptag_state[tag].cbfn) + ctrl->ptag_state[tag].cbfn(phba, tag); + else + __beiscsi_log(phba, KERN_ERR, + "BC_%d : MBX ASYNC command with no callback\n"); + free_mcc_wrb(ctrl, tag); return 0; } - for (i = 0; i < mcc_timeout; i++) { - if (beiscsi_error(phba)) - return -EIO; - beiscsi_process_mcc_cq(phba); - /* after polling, wrb and tag need to be released */ - if (!test_bit(MCC_TAG_STATE_RUNNING, - &ctrl->ptag_state[tag].tag_state)) { - free_mcc_wrb(ctrl, tag); - break; - } - udelay(100); - } - - if (i < mcc_timeout) + if (test_bit(MCC_TAG_STATE_IGNORE, &ctrl->ptag_state[tag].tag_state)) { + /* just check completion status and free wrb */ + __beiscsi_mcc_compl_status(phba, tag, NULL, NULL); + free_mcc_wrb(ctrl, tag); return 0; + } - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, - "BC_%d : FW Timed Out\n"); - phba->fw_timeout = true; - beiscsi_ue_detect(phba); - return -EBUSY; + wake_up_interruptible(&ctrl->mcc_wait[tag]); + return 0; } void be_mcc_notify(struct beiscsi_hba *phba, unsigned int tag) @@ -642,7 +599,7 @@ static int be_mbox_db_ready_poll(struct be_ctrl_info *ctrl) */ timeout = jiffies + msecs_to_jiffies(BEISCSI_MBX_RDY_BIT_TIMEOUT); do { - if (beiscsi_error(phba)) + if (beiscsi_hba_in_error(phba)) return -EIO; ready = ioread32(db); @@ -655,16 +612,14 @@ static int be_mbox_db_ready_poll(struct be_ctrl_info *ctrl) if (time_after(jiffies, timeout)) break; - msleep(20); + /* 1ms sleep is enough in most cases */ + schedule_timeout_uninterruptible(msecs_to_jiffies(1)); } while (!ready); beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, "BC_%d : FW Timed Out\n"); - - phba->fw_timeout = true; - beiscsi_ue_detect(phba); - + set_bit(BEISCSI_HBA_FW_TIMEOUT, &phba->state); return -EBUSY; } @@ -679,7 +634,7 @@ static int be_mbox_db_ready_poll(struct be_ctrl_info *ctrl) * Success: 0 * Failure: Non-Zero **/ -int be_mbox_notify(struct be_ctrl_info *ctrl) +static int be_mbox_notify(struct be_ctrl_info *ctrl) { int status; u32 val = 0; @@ -819,87 +774,6 @@ int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl, return status; } -/** - * be_cmd_fw_initialize()- Initialize FW - * @ctrl: Pointer to function control structure - * - * Send FW initialize pattern for the function. - * - * return - * Success: 0 - * Failure: Non-Zero value - **/ -int be_cmd_fw_initialize(struct be_ctrl_info *ctrl) -{ - struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); - struct beiscsi_hba *phba = pci_get_drvdata(ctrl->pdev); - int status; - u8 *endian_check; - - mutex_lock(&ctrl->mbox_lock); - memset(wrb, 0, sizeof(*wrb)); - - endian_check = (u8 *) wrb; - *endian_check++ = 0xFF; - *endian_check++ = 0x12; - *endian_check++ = 0x34; - *endian_check++ = 0xFF; - *endian_check++ = 0xFF; - *endian_check++ = 0x56; - *endian_check++ = 0x78; - *endian_check++ = 0xFF; - be_dws_cpu_to_le(wrb, sizeof(*wrb)); - - status = be_mbox_notify(ctrl); - if (status) - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BC_%d : be_cmd_fw_initialize Failed\n"); - - mutex_unlock(&ctrl->mbox_lock); - return status; -} - -/** - * be_cmd_fw_uninit()- Uinitialize FW - * @ctrl: Pointer to function control structure - * - * Send FW uninitialize pattern for the function - * - * return - * Success: 0 - * Failure: Non-Zero value - **/ -int be_cmd_fw_uninit(struct be_ctrl_info *ctrl) -{ - struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); - struct beiscsi_hba *phba = pci_get_drvdata(ctrl->pdev); - int status; - u8 *endian_check; - - mutex_lock(&ctrl->mbox_lock); - memset(wrb, 0, sizeof(*wrb)); - - endian_check = (u8 *) wrb; - *endian_check++ = 0xFF; - *endian_check++ = 0xAA; - *endian_check++ = 0xBB; - *endian_check++ = 0xFF; - *endian_check++ = 0xFF; - *endian_check++ = 0xCC; - *endian_check++ = 0xDD; - *endian_check = 0xFF; - - be_dws_cpu_to_le(wrb, sizeof(*wrb)); - - status = be_mbox_notify(ctrl); - if (status) - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BC_%d : be_cmd_fw_uninit Failed\n"); - - mutex_unlock(&ctrl->mbox_lock); - return status; -} - int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl, struct be_queue_info *cq, struct be_queue_info *eq, bool sol_evts, bool no_delay, int coalesce_wm) @@ -1343,25 +1217,6 @@ error: return status; } -int beiscsi_cmd_reset_function(struct beiscsi_hba *phba) -{ - struct be_ctrl_info *ctrl = &phba->ctrl; - struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); - struct be_post_sgl_pages_req *req = embedded_payload(wrb); - int status; - - mutex_lock(&ctrl->mbox_lock); - - req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); - be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, - OPCODE_COMMON_FUNCTION_RESET, sizeof(*req)); - status = be_mbox_notify(ctrl); - - mutex_unlock(&ctrl->mbox_lock); - return status; -} - /** * be_cmd_set_vlan()- Configure VLAN paramters on the adapter * @phba: device priv structure instance @@ -1402,3 +1257,564 @@ int be_cmd_set_vlan(struct beiscsi_hba *phba, return tag; } + +int beiscsi_check_supported_fw(struct be_ctrl_info *ctrl, + struct beiscsi_hba *phba) +{ + struct be_dma_mem nonemb_cmd; + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_mgmt_controller_attributes *req; + struct be_sge *sge = nonembedded_sgl(wrb); + int status = 0; + + nonemb_cmd.va = pci_alloc_consistent(ctrl->pdev, + sizeof(struct be_mgmt_controller_attributes), + &nonemb_cmd.dma); + if (nonemb_cmd.va == NULL) { + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, + "BG_%d : pci_alloc_consistent failed in %s\n", + __func__); + return -ENOMEM; + } + nonemb_cmd.size = sizeof(struct be_mgmt_controller_attributes); + req = nonemb_cmd.va; + memset(req, 0, sizeof(*req)); + mutex_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, + OPCODE_COMMON_GET_CNTL_ATTRIBUTES, sizeof(*req)); + sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd.dma)); + sge->pa_lo = cpu_to_le32(nonemb_cmd.dma & 0xFFFFFFFF); + sge->len = cpu_to_le32(nonemb_cmd.size); + status = be_mbox_notify(ctrl); + if (!status) { + struct be_mgmt_controller_attributes_resp *resp = nonemb_cmd.va; + + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, + "BG_%d : Firmware Version of CMD : %s\n" + "Firmware Version is : %s\n" + "Developer Build, not performing version check...\n", + resp->params.hba_attribs + .flashrom_version_string, + resp->params.hba_attribs. + firmware_version_string); + + phba->fw_config.iscsi_features = + resp->params.hba_attribs.iscsi_features; + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, + "BM_%d : phba->fw_config.iscsi_features = %d\n", + phba->fw_config.iscsi_features); + memcpy(phba->fw_ver_str, resp->params.hba_attribs. + firmware_version_string, BEISCSI_VER_STRLEN); + } else + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, + "BG_%d : Failed in beiscsi_check_supported_fw\n"); + mutex_unlock(&ctrl->mbox_lock); + if (nonemb_cmd.va) + pci_free_consistent(ctrl->pdev, nonemb_cmd.size, + nonemb_cmd.va, nonemb_cmd.dma); + + return status; +} + +/** + * beiscsi_get_fw_config()- Get the FW config for the function + * @ctrl: ptr to Ctrl Info + * @phba: ptr to the dev priv structure + * + * Get the FW config and resources available for the function. + * The resources are created based on the count received here. + * + * return + * Success: 0 + * Failure: Non-Zero Value + **/ +int beiscsi_get_fw_config(struct be_ctrl_info *ctrl, + struct beiscsi_hba *phba) +{ + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_fw_cfg *pfw_cfg = embedded_payload(wrb); + uint32_t cid_count, icd_count; + int status = -EINVAL; + uint8_t ulp_num = 0; + + mutex_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + be_wrb_hdr_prepare(wrb, sizeof(*pfw_cfg), true, 0); + + be_cmd_hdr_prepare(&pfw_cfg->hdr, CMD_SUBSYSTEM_COMMON, + OPCODE_COMMON_QUERY_FIRMWARE_CONFIG, + EMBED_MBX_MAX_PAYLOAD_SIZE); + + if (be_mbox_notify(ctrl)) { + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, + "BG_%d : Failed in beiscsi_get_fw_config\n"); + goto fail_init; + } + + /* FW response formats depend on port id */ + phba->fw_config.phys_port = pfw_cfg->phys_port; + if (phba->fw_config.phys_port >= BEISCSI_PHYS_PORT_MAX) { + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, + "BG_%d : invalid physical port id %d\n", + phba->fw_config.phys_port); + goto fail_init; + } + + /* populate and check FW config against min and max values */ + if (!is_chip_be2_be3r(phba)) { + phba->fw_config.eqid_count = pfw_cfg->eqid_count; + phba->fw_config.cqid_count = pfw_cfg->cqid_count; + if (phba->fw_config.eqid_count == 0 || + phba->fw_config.eqid_count > 2048) { + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, + "BG_%d : invalid EQ count %d\n", + phba->fw_config.eqid_count); + goto fail_init; + } + if (phba->fw_config.cqid_count == 0 || + phba->fw_config.cqid_count > 4096) { + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, + "BG_%d : invalid CQ count %d\n", + phba->fw_config.cqid_count); + goto fail_init; + } + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, + "BG_%d : EQ_Count : %d CQ_Count : %d\n", + phba->fw_config.eqid_count, + phba->fw_config.cqid_count); + } + + /** + * Check on which all ULP iSCSI Protocol is loaded. + * Set the Bit for those ULP. This set flag is used + * at all places in the code to check on which ULP + * iSCSi Protocol is loaded + **/ + for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) { + if (pfw_cfg->ulp[ulp_num].ulp_mode & + BEISCSI_ULP_ISCSI_INI_MODE) { + set_bit(ulp_num, &phba->fw_config.ulp_supported); + + /* Get the CID, ICD and Chain count for each ULP */ + phba->fw_config.iscsi_cid_start[ulp_num] = + pfw_cfg->ulp[ulp_num].sq_base; + phba->fw_config.iscsi_cid_count[ulp_num] = + pfw_cfg->ulp[ulp_num].sq_count; + + phba->fw_config.iscsi_icd_start[ulp_num] = + pfw_cfg->ulp[ulp_num].icd_base; + phba->fw_config.iscsi_icd_count[ulp_num] = + pfw_cfg->ulp[ulp_num].icd_count; + + phba->fw_config.iscsi_chain_start[ulp_num] = + pfw_cfg->chain_icd[ulp_num].chain_base; + phba->fw_config.iscsi_chain_count[ulp_num] = + pfw_cfg->chain_icd[ulp_num].chain_count; + + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, + "BG_%d : Function loaded on ULP : %d\n" + "\tiscsi_cid_count : %d\n" + "\tiscsi_cid_start : %d\n" + "\t iscsi_icd_count : %d\n" + "\t iscsi_icd_start : %d\n", + ulp_num, + phba->fw_config. + iscsi_cid_count[ulp_num], + phba->fw_config. + iscsi_cid_start[ulp_num], + phba->fw_config. + iscsi_icd_count[ulp_num], + phba->fw_config. + iscsi_icd_start[ulp_num]); + } + } + + if (phba->fw_config.ulp_supported == 0) { + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, + "BG_%d : iSCSI initiator mode not set: ULP0 %x ULP1 %x\n", + pfw_cfg->ulp[BEISCSI_ULP0].ulp_mode, + pfw_cfg->ulp[BEISCSI_ULP1].ulp_mode); + goto fail_init; + } + + /** + * ICD is shared among ULPs. Use icd_count of any one loaded ULP + **/ + for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) + if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) + break; + icd_count = phba->fw_config.iscsi_icd_count[ulp_num]; + if (icd_count == 0 || icd_count > 65536) { + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, + "BG_%d: invalid ICD count %d\n", icd_count); + goto fail_init; + } + + cid_count = BEISCSI_GET_CID_COUNT(phba, BEISCSI_ULP0) + + BEISCSI_GET_CID_COUNT(phba, BEISCSI_ULP1); + if (cid_count == 0 || cid_count > 4096) { + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, + "BG_%d: invalid CID count %d\n", cid_count); + goto fail_init; + } + + /** + * Check FW is dual ULP aware i.e. can handle either + * of the protocols. + */ + phba->fw_config.dual_ulp_aware = (pfw_cfg->function_mode & + BEISCSI_FUNC_DUA_MODE); + + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, + "BG_%d : DUA Mode : 0x%x\n", + phba->fw_config.dual_ulp_aware); + + /* all set, continue using this FW config */ + status = 0; +fail_init: + mutex_unlock(&ctrl->mbox_lock); + return status; +} + +/** + * beiscsi_get_port_name()- Get port name for the function + * @ctrl: ptr to Ctrl Info + * @phba: ptr to the dev priv structure + * + * Get the alphanumeric character for port + * + **/ +int beiscsi_get_port_name(struct be_ctrl_info *ctrl, struct beiscsi_hba *phba) +{ + int ret = 0; + struct be_mcc_wrb *wrb; + struct be_cmd_get_port_name *ioctl; + + mutex_lock(&ctrl->mbox_lock); + wrb = wrb_from_mbox(&ctrl->mbox_mem); + memset(wrb, 0, sizeof(*wrb)); + ioctl = embedded_payload(wrb); + + be_wrb_hdr_prepare(wrb, sizeof(*ioctl), true, 0); + be_cmd_hdr_prepare(&ioctl->h.req_hdr, CMD_SUBSYSTEM_COMMON, + OPCODE_COMMON_GET_PORT_NAME, + EMBED_MBX_MAX_PAYLOAD_SIZE); + ret = be_mbox_notify(ctrl); + phba->port_name = 0; + if (!ret) { + phba->port_name = ioctl->p.resp.port_names >> + (phba->fw_config.phys_port * 8) & 0xff; + } else { + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, + "BG_%d : GET_PORT_NAME ret 0x%x status 0x%x\n", + ret, ioctl->h.resp_hdr.status); + } + + if (phba->port_name == 0) + phba->port_name = '?'; + + mutex_unlock(&ctrl->mbox_lock); + return ret; +} + +int beiscsi_set_uer_feature(struct beiscsi_hba *phba) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_cmd_set_features *ioctl; + struct be_mcc_wrb *wrb; + int ret = 0; + + mutex_lock(&ctrl->mbox_lock); + wrb = wrb_from_mbox(&ctrl->mbox_mem); + memset(wrb, 0, sizeof(*wrb)); + ioctl = embedded_payload(wrb); + + be_wrb_hdr_prepare(wrb, sizeof(*ioctl), true, 0); + be_cmd_hdr_prepare(&ioctl->h.req_hdr, CMD_SUBSYSTEM_COMMON, + OPCODE_COMMON_SET_FEATURES, + EMBED_MBX_MAX_PAYLOAD_SIZE); + ioctl->feature = BE_CMD_SET_FEATURE_UER; + ioctl->param_len = sizeof(ioctl->param.req); + ioctl->param.req.uer = BE_CMD_UER_SUPP_BIT; + ret = be_mbox_notify(ctrl); + if (!ret) { + phba->ue2rp = ioctl->param.resp.ue2rp; + set_bit(BEISCSI_HBA_UER_SUPP, &phba->state); + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, + "BG_%d : HBA error recovery supported\n"); + } else { + /** + * Check "MCC_STATUS_INVALID_LENGTH" for SKH. + * Older FW versions return this error. + */ + if (ret == MCC_STATUS_ILLEGAL_REQUEST || + ret == MCC_STATUS_INVALID_LENGTH) + __beiscsi_log(phba, KERN_INFO, + "BG_%d : HBA error recovery not supported\n"); + } + + mutex_unlock(&ctrl->mbox_lock); + return ret; +} + +static u32 beiscsi_get_post_stage(struct beiscsi_hba *phba) +{ + u32 sem; + + if (is_chip_be2_be3r(phba)) + sem = ioread32(phba->csr_va + SLIPORT_SEMAPHORE_OFFSET_BEx); + else + pci_read_config_dword(phba->pcidev, + SLIPORT_SEMAPHORE_OFFSET_SH, &sem); + return sem; +} + +int beiscsi_check_fw_rdy(struct beiscsi_hba *phba) +{ + u32 loop, post, rdy = 0; + + loop = 1000; + while (loop--) { + post = beiscsi_get_post_stage(phba); + if (post & POST_ERROR_BIT) + break; + if ((post & POST_STAGE_MASK) == POST_STAGE_ARMFW_RDY) { + rdy = 1; + break; + } + msleep(60); + } + + if (!rdy) { + __beiscsi_log(phba, KERN_ERR, + "BC_%d : FW not ready 0x%x\n", post); + } + + return rdy; +} + +int beiscsi_cmd_function_reset(struct beiscsi_hba *phba) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_post_sgl_pages_req *req = embedded_payload(wrb); + int status; + + mutex_lock(&ctrl->mbox_lock); + + req = embedded_payload(wrb); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, + OPCODE_COMMON_FUNCTION_RESET, sizeof(*req)); + status = be_mbox_notify(ctrl); + + mutex_unlock(&ctrl->mbox_lock); + return status; +} + +int beiscsi_cmd_special_wrb(struct be_ctrl_info *ctrl, u32 load) +{ + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct beiscsi_hba *phba = pci_get_drvdata(ctrl->pdev); + u8 *endian_check; + int status; + + mutex_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + + endian_check = (u8 *) wrb; + if (load) { + /* to start communicating */ + *endian_check++ = 0xFF; + *endian_check++ = 0x12; + *endian_check++ = 0x34; + *endian_check++ = 0xFF; + *endian_check++ = 0xFF; + *endian_check++ = 0x56; + *endian_check++ = 0x78; + *endian_check++ = 0xFF; + } else { + /* to stop communicating */ + *endian_check++ = 0xFF; + *endian_check++ = 0xAA; + *endian_check++ = 0xBB; + *endian_check++ = 0xFF; + *endian_check++ = 0xFF; + *endian_check++ = 0xCC; + *endian_check++ = 0xDD; + *endian_check = 0xFF; + } + be_dws_cpu_to_le(wrb, sizeof(*wrb)); + + status = be_mbox_notify(ctrl); + if (status) + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, + "BC_%d : special WRB message failed\n"); + mutex_unlock(&ctrl->mbox_lock); + return status; +} + +int beiscsi_init_sliport(struct beiscsi_hba *phba) +{ + int status; + + /* check POST stage before talking to FW */ + status = beiscsi_check_fw_rdy(phba); + if (!status) + return -EIO; + + /* clear all error states after checking FW rdy */ + phba->state &= ~BEISCSI_HBA_IN_ERR; + + /* check again UER support */ + phba->state &= ~BEISCSI_HBA_UER_SUPP; + + /* + * SLI COMMON_FUNCTION_RESET completion is indicated by BMBX RDY bit. + * It should clean up any stale info in FW for this fn. + */ + status = beiscsi_cmd_function_reset(phba); + if (status) { + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, + "BC_%d : SLI Function Reset failed\n"); + return status; + } + + /* indicate driver is loading */ + return beiscsi_cmd_special_wrb(&phba->ctrl, 1); +} + +/** + * beiscsi_cmd_iscsi_cleanup()- Inform FW to cleanup EP data structures. + * @phba: pointer to dev priv structure + * @ulp: ULP number. + * + * return + * Success: 0 + * Failure: Non-Zero Value + **/ +int beiscsi_cmd_iscsi_cleanup(struct beiscsi_hba *phba, unsigned short ulp) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct iscsi_cleanup_req_v1 *req_v1; + struct iscsi_cleanup_req *req; + struct be_mcc_wrb *wrb; + int status; + + mutex_lock(&ctrl->mbox_lock); + wrb = wrb_from_mbox(&ctrl->mbox_mem); + req = embedded_payload(wrb); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI, + OPCODE_COMMON_ISCSI_CLEANUP, sizeof(*req)); + + /** + * TODO: Check with FW folks the chute value to be set. + * For now, use the ULP_MASK as the chute value. + */ + if (is_chip_be2_be3r(phba)) { + req->chute = (1 << ulp); + req->hdr_ring_id = HWI_GET_DEF_HDRQ_ID(phba, ulp); + req->data_ring_id = HWI_GET_DEF_BUFQ_ID(phba, ulp); + } else { + req_v1 = (struct iscsi_cleanup_req_v1 *)req; + req_v1->hdr.version = 1; + req_v1->hdr_ring_id = cpu_to_le16(HWI_GET_DEF_HDRQ_ID(phba, + ulp)); + req_v1->data_ring_id = cpu_to_le16(HWI_GET_DEF_BUFQ_ID(phba, + ulp)); + } + + status = be_mbox_notify(ctrl); + if (status) + beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_INIT, + "BG_%d : %s failed %d\n", __func__, ulp); + mutex_unlock(&ctrl->mbox_lock); + return status; +} + +/* + * beiscsi_detect_ue()- Detect Unrecoverable Error on adapter + * @phba: Driver priv structure + * + * Read registers linked to UE and check for the UE status + **/ +int beiscsi_detect_ue(struct beiscsi_hba *phba) +{ + uint32_t ue_mask_hi = 0, ue_mask_lo = 0; + uint32_t ue_hi = 0, ue_lo = 0; + uint8_t i = 0; + int ret = 0; + + pci_read_config_dword(phba->pcidev, + PCICFG_UE_STATUS_LOW, &ue_lo); + pci_read_config_dword(phba->pcidev, + PCICFG_UE_STATUS_MASK_LOW, + &ue_mask_lo); + pci_read_config_dword(phba->pcidev, + PCICFG_UE_STATUS_HIGH, + &ue_hi); + pci_read_config_dword(phba->pcidev, + PCICFG_UE_STATUS_MASK_HI, + &ue_mask_hi); + + ue_lo = (ue_lo & ~ue_mask_lo); + ue_hi = (ue_hi & ~ue_mask_hi); + + + if (ue_lo || ue_hi) { + set_bit(BEISCSI_HBA_IN_UE, &phba->state); + __beiscsi_log(phba, KERN_ERR, + "BC_%d : HBA error detected\n"); + ret = 1; + } + + if (ue_lo) { + for (i = 0; ue_lo; ue_lo >>= 1, i++) { + if (ue_lo & 1) + __beiscsi_log(phba, KERN_ERR, + "BC_%d : UE_LOW %s bit set\n", + desc_ue_status_low[i]); + } + } + + if (ue_hi) { + for (i = 0; ue_hi; ue_hi >>= 1, i++) { + if (ue_hi & 1) + __beiscsi_log(phba, KERN_ERR, + "BC_%d : UE_HIGH %s bit set\n", + desc_ue_status_hi[i]); + } + } + return ret; +} + +/* + * beiscsi_detect_tpe()- Detect Transient Parity Error on adapter + * @phba: Driver priv structure + * + * Read SLIPORT SEMAPHORE register to check for UER + * + **/ +int beiscsi_detect_tpe(struct beiscsi_hba *phba) +{ + u32 post, status; + int ret = 0; + + post = beiscsi_get_post_stage(phba); + status = post & POST_STAGE_MASK; + if ((status & POST_ERR_RECOVERY_CODE_MASK) == + POST_STAGE_RECOVERABLE_ERR) { + set_bit(BEISCSI_HBA_IN_TPE, &phba->state); + __beiscsi_log(phba, KERN_INFO, + "BC_%d : HBA error recoverable: 0x%x\n", post); + ret = 1; + } else { + __beiscsi_log(phba, KERN_INFO, + "BC_%d : HBA in UE: 0x%x\n", post); + } + + return ret; +} diff --git a/drivers/scsi/be2iscsi/be_cmds.h b/drivers/scsi/be2iscsi/be_cmds.h index deeb951e6874..328fb5b973cd 100644 --- a/drivers/scsi/be2iscsi/be_cmds.h +++ b/drivers/scsi/be2iscsi/be_cmds.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Emulex + * Copyright (C) 2005 - 2016 Broadcom * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -8,7 +8,7 @@ * Public License is included in this distribution in the file called COPYING. * * Contact Information: - * linux-drivers@avagotech.com + * linux-drivers@broadcom.com * * Emulex * 3333 Susan Street @@ -57,6 +57,7 @@ struct be_mcc_wrb { #define MCC_STATUS_ILLEGAL_REQUEST 0x2 #define MCC_STATUS_ILLEGAL_FIELD 0x3 #define MCC_STATUS_INSUFFICIENT_BUFFER 0x4 +#define MCC_STATUS_INVALID_LENGTH 0x74 #define CQE_STATUS_COMPL_MASK 0xFFFF #define CQE_STATUS_COMPL_SHIFT 0 /* bits 0 - 15 */ @@ -97,11 +98,23 @@ struct be_mcc_compl { #define MPU_MAILBOX_DB_RDY_MASK 0x1 /* bit 0 */ #define MPU_MAILBOX_DB_HI_MASK 0x2 /* bit 1 */ -/********** MPU semphore ******************/ -#define MPU_EP_SEMAPHORE_OFFSET 0xac -#define EP_SEMAPHORE_POST_STAGE_MASK 0x0000FFFF -#define EP_SEMAPHORE_POST_ERR_MASK 0x1 -#define EP_SEMAPHORE_POST_ERR_SHIFT 31 +/********** MPU semphore: used for SH & BE ******************/ +#define SLIPORT_SOFTRESET_OFFSET 0x5c /* CSR BAR offset */ +#define SLIPORT_SEMAPHORE_OFFSET_BEx 0xac /* CSR BAR offset */ +#define SLIPORT_SEMAPHORE_OFFSET_SH 0x94 /* PCI-CFG offset */ +#define POST_STAGE_MASK 0x0000FFFF +#define POST_ERROR_BIT 0x80000000 +#define POST_ERR_RECOVERY_CODE_MASK 0xF000 + +/* Soft Reset register masks */ +#define SLIPORT_SOFTRESET_SR_MASK 0x00000080 /* SR bit */ + +/* MPU semphore POST stage values */ +#define POST_STAGE_AWAITING_HOST_RDY 0x1 /* FW awaiting goahead from host */ +#define POST_STAGE_HOST_RDY 0x2 /* Host has given go-ahed to FW */ +#define POST_STAGE_BE_RESET 0x3 /* Host wants to reset chip */ +#define POST_STAGE_ARMFW_RDY 0xC000 /* FW is done with POST */ +#define POST_STAGE_RECOVERABLE_ERR 0xE000 /* Recoverable err detected */ /********** MCC door bell ************/ #define DB_MCCQ_OFFSET 0x140 @@ -109,9 +122,6 @@ struct be_mcc_compl { /* Number of entries posted */ #define DB_MCCQ_NUM_POSTED_SHIFT 16 /* bits 16 - 29 */ -/* MPU semphore POST stage values */ -#define POST_STAGE_ARMFW_RDY 0xc000 /* FW is done with POST */ - /** * When the async bit of mcc_compl is set, the last 4 bytes of * mcc_compl is interpreted as follows: @@ -217,6 +227,7 @@ struct be_mcc_mailbox { #define OPCODE_COMMON_QUERY_FIRMWARE_CONFIG 58 #define OPCODE_COMMON_FUNCTION_RESET 61 #define OPCODE_COMMON_GET_PORT_NAME 77 +#define OPCODE_COMMON_SET_FEATURES 191 /** * LIST of opcodes that are common between Initiator and Target @@ -345,8 +356,8 @@ struct be_cmd_req_logout_fw_sess { struct be_cmd_resp_logout_fw_sess { struct be_cmd_resp_hdr hdr; /* dw[4] */ -#define BEISCSI_MGMT_SESSION_CLOSE 0x20 uint32_t session_status; +#define BE_SESS_STATUS_CLOSE 0x20 } __packed; struct mgmt_conn_login_options { @@ -365,6 +376,14 @@ struct ip_addr_format { u16 size_of_structure; u8 reserved; u8 ip_type; +#define BEISCSI_IP_TYPE_V4 0x1 +#define BEISCSI_IP_TYPE_STATIC_V4 0x3 +#define BEISCSI_IP_TYPE_DHCP_V4 0x5 +/* type v4 values < type v6 values */ +#define BEISCSI_IP_TYPE_V6 0x10 +#define BEISCSI_IP_TYPE_ROUTABLE_V6 0x30 +#define BEISCSI_IP_TYPE_LINK_LOCAL_V6 0x50 +#define BEISCSI_IP_TYPE_AUTO_V6 0x90 u8 addr[16]; u32 rsvd0; } __packed; @@ -430,8 +449,13 @@ struct be_cmd_get_boot_target_req { struct be_cmd_get_boot_target_resp { struct be_cmd_resp_hdr hdr; - u32 boot_session_count; - int boot_session_handle; + u32 boot_session_count; + u32 boot_session_handle; +/** + * FW returns 0xffffffff if it couldn't establish connection with + * configured boot target. + */ +#define BE_BOOT_INVALID_SHANDLE 0xffffffff }; struct be_cmd_reopen_session_req { @@ -699,16 +723,59 @@ struct be_cmd_get_nic_conf_resp { u8 mac_address[ETH_ALEN]; } __packed; -#define BEISCSI_ALIAS_LEN 32 +/******************** Get HBA NAME *******************/ struct be_cmd_hba_name { struct be_cmd_req_hdr hdr; u16 flags; u16 rsvd0; u8 initiator_name[ISCSI_NAME_LEN]; - u8 initiator_alias[BEISCSI_ALIAS_LEN]; +#define BE_INI_ALIAS_LEN 32 + u8 initiator_alias[BE_INI_ALIAS_LEN]; } __packed; +/******************** COMMON SET Features *******************/ +#define BE_CMD_SET_FEATURE_UER 0x10 +#define BE_CMD_UER_SUPP_BIT 0x1 +struct be_uer_req { + u32 uer; + u32 rsvd; +}; + +struct be_uer_resp { + u32 uer; + u16 ue2rp; + u16 ue2sr; +}; + +struct be_cmd_set_features { + union { + struct be_cmd_req_hdr req_hdr; + struct be_cmd_resp_hdr resp_hdr; + } h; + u32 feature; + u32 param_len; + union { + struct be_uer_req req; + struct be_uer_resp resp; + u32 rsvd[2]; + } param; +} __packed; + +int beiscsi_cmd_function_reset(struct beiscsi_hba *phba); + +int beiscsi_cmd_special_wrb(struct be_ctrl_info *ctrl, u32 load); + +int beiscsi_check_fw_rdy(struct beiscsi_hba *phba); + +int beiscsi_init_sliport(struct beiscsi_hba *phba); + +int beiscsi_cmd_iscsi_cleanup(struct beiscsi_hba *phba, unsigned short ulp_num); + +int beiscsi_detect_ue(struct beiscsi_hba *phba); + +int beiscsi_detect_tpe(struct beiscsi_hba *phba); + int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl, struct be_queue_info *eq, int eq_delay); @@ -723,24 +790,21 @@ int beiscsi_cmd_mccq_create(struct beiscsi_hba *phba, struct be_queue_info *mccq, struct be_queue_info *cq); -int be_poll_mcc(struct be_ctrl_info *ctrl); -int mgmt_check_supported_fw(struct be_ctrl_info *ctrl, - struct beiscsi_hba *phba); unsigned int be_cmd_get_initname(struct beiscsi_hba *phba); void free_mcc_wrb(struct be_ctrl_info *ctrl, unsigned int tag); -int be_cmd_modify_eq_delay(struct beiscsi_hba *phba, struct be_set_eqd *, +int beiscsi_modify_eq_delay(struct beiscsi_hba *phba, struct be_set_eqd *, int num); int beiscsi_mccq_compl_wait(struct beiscsi_hba *phba, - uint32_t tag, struct be_mcc_wrb **wrb, + unsigned int tag, + struct be_mcc_wrb **wrb, struct be_dma_mem *mbx_cmd_mem); -/*ISCSI Functuions */ -int be_cmd_fw_initialize(struct be_ctrl_info *ctrl); -int be_cmd_fw_uninit(struct be_ctrl_info *ctrl); - +int __beiscsi_mcc_compl_status(struct beiscsi_hba *phba, + unsigned int tag, + struct be_mcc_wrb **wrb, + struct be_dma_mem *mbx_cmd_mem); struct be_mcc_wrb *wrb_from_mbox(struct be_dma_mem *mbox_mem); -int be_mcc_compl_poll(struct beiscsi_hba *phba, unsigned int tag); void be_mcc_notify(struct beiscsi_hba *phba, unsigned int tag); struct be_mcc_wrb *alloc_mcc_wrb(struct beiscsi_hba *phba, unsigned int *ref_tag); @@ -749,9 +813,6 @@ void beiscsi_process_async_event(struct beiscsi_hba *phba, int beiscsi_process_mcc_compl(struct be_ctrl_info *ctrl, struct be_mcc_compl *compl); - -int be_mbox_notify(struct be_ctrl_info *ctrl); - int be_cmd_create_default_pdu_queue(struct be_ctrl_info *ctrl, struct be_queue_info *cq, struct be_queue_info *dq, int length, @@ -767,8 +828,6 @@ int be_cmd_iscsi_post_sgl_pages(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem, u32 page_offset, u32 num_pages); -int beiscsi_cmd_reset_function(struct beiscsi_hba *phba); - int be_cmd_wrbq_create(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem, struct be_queue_info *wrbq, struct hwi_wrb_context *pwrb_context, @@ -777,6 +836,15 @@ int be_cmd_wrbq_create(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem, /* Configuration Functions */ int be_cmd_set_vlan(struct beiscsi_hba *phba, uint16_t vlan_tag); +int beiscsi_check_supported_fw(struct be_ctrl_info *ctrl, + struct beiscsi_hba *phba); + +int beiscsi_get_fw_config(struct be_ctrl_info *ctrl, struct beiscsi_hba *phba); + +int beiscsi_get_port_name(struct be_ctrl_info *ctrl, struct beiscsi_hba *phba); + +int beiscsi_set_uer_feature(struct beiscsi_hba *phba); + struct be_default_pdu_context { u32 dw[4]; } __packed; @@ -999,7 +1067,16 @@ struct iscsi_cleanup_req { u16 chute; u8 hdr_ring_id; u8 data_ring_id; +} __packed; +struct iscsi_cleanup_req_v1 { + struct be_cmd_req_hdr hdr; + u16 chute; + u16 rsvd1; + u16 hdr_ring_id; + u16 rsvd2; + u16 data_ring_id; + u16 rsvd3; } __packed; struct eq_delay { @@ -1368,14 +1445,9 @@ struct be_cmd_get_port_name { * the cxn */ -int beiscsi_pci_soft_reset(struct beiscsi_hba *phba); -int be_chk_reset_complete(struct beiscsi_hba *phba); - void be_wrb_hdr_prepare(struct be_mcc_wrb *wrb, int payload_len, bool embedded, u8 sge_cnt); void be_cmd_hdr_prepare(struct be_cmd_req_hdr *req_hdr, u8 subsystem, u8 opcode, int cmd_len); - -void beiscsi_fail_session(struct iscsi_cls_session *cls_session); #endif /* !BEISCSI_CMDS_H */ diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c index 09f89a3eaa87..ba258217614e 100644 --- a/drivers/scsi/be2iscsi/be_iscsi.c +++ b/drivers/scsi/be2iscsi/be_iscsi.c @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Emulex + * Copyright (C) 2005 - 2016 Broadcom * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -7,10 +7,10 @@ * as published by the Free Software Foundation. The full GNU General * Public License is included in this distribution in the file called COPYING. * - * Written by: Jayamohan Kallickal (jayamohan.kallickal@avagotech.com) + * Written by: Jayamohan Kallickal (jayamohan.kallickal@broadcom.com) * * Contact Information: - * linux-drivers@avagotech.com + * linux-drivers@broadcom.com * * Emulex * 3333 Susan Street @@ -52,22 +52,20 @@ struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep, if (!ep) { - printk(KERN_ERR - "beiscsi_session_create: invalid ep\n"); + pr_err("beiscsi_session_create: invalid ep\n"); return NULL; } beiscsi_ep = ep->dd_data; phba = beiscsi_ep->phba; - if (phba->state & BE_ADAPTER_PCI_ERR) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : PCI_ERROR Recovery\n"); - return NULL; - } else { + if (!beiscsi_hba_is_online(phba)) { beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, - "BS_%d : In beiscsi_session_create\n"); + "BS_%d : HBA in error 0x%lx\n", phba->state); + return NULL; } + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, + "BS_%d : In beiscsi_session_create\n"); if (cmds_max > beiscsi_ep->phba->params.wrbs_per_cxn) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, "BS_%d : Cannot handle %d cmds." @@ -120,6 +118,16 @@ void beiscsi_session_destroy(struct iscsi_cls_session *cls_session) } /** + * beiscsi_session_fail(): Closing session with appropriate error + * @cls_session: ptr to session + **/ +void beiscsi_session_fail(struct iscsi_cls_session *cls_session) +{ + iscsi_session_failure(cls_session->dd_data, ISCSI_ERR_CONN_FAILED); +} + + +/** * beiscsi_conn_create - create an instance of iscsi connection * @cls_session: ptr to iscsi_cls_session * @cid: iscsi cid @@ -237,7 +245,7 @@ int beiscsi_conn_bind(struct iscsi_cls_session *cls_session, return beiscsi_bindconn_cid(phba, beiscsi_conn, beiscsi_ep->ep_cid); } -static int beiscsi_create_ipv4_iface(struct beiscsi_hba *phba) +static int beiscsi_iface_create_ipv4(struct beiscsi_hba *phba) { if (phba->ipv4_iface) return 0; @@ -256,7 +264,7 @@ static int beiscsi_create_ipv4_iface(struct beiscsi_hba *phba) return 0; } -static int beiscsi_create_ipv6_iface(struct beiscsi_hba *phba) +static int beiscsi_iface_create_ipv6(struct beiscsi_hba *phba) { if (phba->ipv6_iface) return 0; @@ -275,79 +283,31 @@ static int beiscsi_create_ipv6_iface(struct beiscsi_hba *phba) return 0; } -void beiscsi_create_def_ifaces(struct beiscsi_hba *phba) +void beiscsi_iface_create_default(struct beiscsi_hba *phba) { struct be_cmd_get_if_info_resp *if_info; - if (!mgmt_get_if_info(phba, BE2_IPV4, &if_info)) { - beiscsi_create_ipv4_iface(phba); + if (!beiscsi_if_get_info(phba, BEISCSI_IP_TYPE_V4, &if_info)) { + beiscsi_iface_create_ipv4(phba); kfree(if_info); } - if (!mgmt_get_if_info(phba, BE2_IPV6, &if_info)) { - beiscsi_create_ipv6_iface(phba); + if (!beiscsi_if_get_info(phba, BEISCSI_IP_TYPE_V6, &if_info)) { + beiscsi_iface_create_ipv6(phba); kfree(if_info); } } -void beiscsi_destroy_def_ifaces(struct beiscsi_hba *phba) +void beiscsi_iface_destroy_default(struct beiscsi_hba *phba) { - if (phba->ipv6_iface) + if (phba->ipv6_iface) { iscsi_destroy_iface(phba->ipv6_iface); - if (phba->ipv4_iface) - iscsi_destroy_iface(phba->ipv4_iface); -} - -static int -beiscsi_set_static_ip(struct Scsi_Host *shost, - struct iscsi_iface_param_info *iface_param, - void *data, uint32_t dt_len) -{ - struct beiscsi_hba *phba = iscsi_host_priv(shost); - struct iscsi_iface_param_info *iface_ip = NULL; - struct iscsi_iface_param_info *iface_subnet = NULL; - struct nlattr *nla; - int ret; - - - switch (iface_param->param) { - case ISCSI_NET_PARAM_IPV4_BOOTPROTO: - nla = nla_find(data, dt_len, ISCSI_NET_PARAM_IPV4_ADDR); - if (nla) - iface_ip = nla_data(nla); - - nla = nla_find(data, dt_len, ISCSI_NET_PARAM_IPV4_SUBNET); - if (nla) - iface_subnet = nla_data(nla); - break; - case ISCSI_NET_PARAM_IPV4_ADDR: - iface_ip = iface_param; - nla = nla_find(data, dt_len, ISCSI_NET_PARAM_IPV4_SUBNET); - if (nla) - iface_subnet = nla_data(nla); - break; - case ISCSI_NET_PARAM_IPV4_SUBNET: - iface_subnet = iface_param; - nla = nla_find(data, dt_len, ISCSI_NET_PARAM_IPV4_ADDR); - if (nla) - iface_ip = nla_data(nla); - break; - default: - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : Unsupported param %d\n", - iface_param->param); + phba->ipv6_iface = NULL; } - - if (!iface_ip || !iface_subnet) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : IP and Subnet Mask required\n"); - return -EINVAL; + if (phba->ipv4_iface) { + iscsi_destroy_iface(phba->ipv4_iface); + phba->ipv4_iface = NULL; } - - ret = mgmt_set_ip(phba, iface_ip, iface_subnet, - ISCSI_BOOTPROTO_STATIC); - - return ret; } /** @@ -363,137 +323,141 @@ beiscsi_set_static_ip(struct Scsi_Host *shost, * Failure: Non-Zero Value **/ static int -beiscsi_set_vlan_tag(struct Scsi_Host *shost, - struct iscsi_iface_param_info *iface_param) +beiscsi_iface_config_vlan(struct Scsi_Host *shost, + struct iscsi_iface_param_info *iface_param) { struct beiscsi_hba *phba = iscsi_host_priv(shost); - int ret; - - /* Get the Interface Handle */ - ret = mgmt_get_all_if_id(phba); - if (ret) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : Getting Interface Handle Failed\n"); - return ret; - } + int ret = -EPERM; switch (iface_param->param) { case ISCSI_NET_PARAM_VLAN_ENABLED: + ret = 0; if (iface_param->value[0] != ISCSI_VLAN_ENABLE) - ret = mgmt_set_vlan(phba, BEISCSI_VLAN_DISABLE); + ret = beiscsi_if_set_vlan(phba, BEISCSI_VLAN_DISABLE); break; case ISCSI_NET_PARAM_VLAN_TAG: - ret = mgmt_set_vlan(phba, - *((uint16_t *)iface_param->value)); + ret = beiscsi_if_set_vlan(phba, + *((uint16_t *)iface_param->value)); break; - default: - beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, - "BS_%d : Unknown Param Type : %d\n", - iface_param->param); - return -ENOSYS; } return ret; } static int -beiscsi_set_ipv4(struct Scsi_Host *shost, - struct iscsi_iface_param_info *iface_param, - void *data, uint32_t dt_len) +beiscsi_iface_config_ipv4(struct Scsi_Host *shost, + struct iscsi_iface_param_info *info, + void *data, uint32_t dt_len) { struct beiscsi_hba *phba = iscsi_host_priv(shost); - int ret = 0; + u8 *ip = NULL, *subnet = NULL, *gw; + struct nlattr *nla; + int ret = -EPERM; /* Check the param */ - switch (iface_param->param) { + switch (info->param) { + case ISCSI_NET_PARAM_IFACE_ENABLE: + if (info->value[0] == ISCSI_IFACE_ENABLE) + ret = beiscsi_iface_create_ipv4(phba); + else { + iscsi_destroy_iface(phba->ipv4_iface); + phba->ipv4_iface = NULL; + } + break; case ISCSI_NET_PARAM_IPV4_GW: - ret = mgmt_set_gateway(phba, iface_param); + gw = info->value; + ret = beiscsi_if_set_gw(phba, BEISCSI_IP_TYPE_V4, gw); break; case ISCSI_NET_PARAM_IPV4_BOOTPROTO: - if (iface_param->value[0] == ISCSI_BOOTPROTO_DHCP) - ret = mgmt_set_ip(phba, iface_param, - NULL, ISCSI_BOOTPROTO_DHCP); - else if (iface_param->value[0] == ISCSI_BOOTPROTO_STATIC) - ret = beiscsi_set_static_ip(shost, iface_param, - data, dt_len); + if (info->value[0] == ISCSI_BOOTPROTO_DHCP) + ret = beiscsi_if_en_dhcp(phba, BEISCSI_IP_TYPE_V4); + else if (info->value[0] == ISCSI_BOOTPROTO_STATIC) + /* release DHCP IP address */ + ret = beiscsi_if_en_static(phba, BEISCSI_IP_TYPE_V4, + NULL, NULL); else beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, "BS_%d : Invalid BOOTPROTO: %d\n", - iface_param->value[0]); + info->value[0]); break; - case ISCSI_NET_PARAM_IFACE_ENABLE: - if (iface_param->value[0] == ISCSI_IFACE_ENABLE) - ret = beiscsi_create_ipv4_iface(phba); - else - iscsi_destroy_iface(phba->ipv4_iface); - break; - case ISCSI_NET_PARAM_IPV4_SUBNET: case ISCSI_NET_PARAM_IPV4_ADDR: - ret = beiscsi_set_static_ip(shost, iface_param, - data, dt_len); + ip = info->value; + nla = nla_find(data, dt_len, ISCSI_NET_PARAM_IPV4_SUBNET); + if (nla) { + info = nla_data(nla); + subnet = info->value; + } + ret = beiscsi_if_en_static(phba, BEISCSI_IP_TYPE_V4, + ip, subnet); break; - case ISCSI_NET_PARAM_VLAN_ENABLED: - case ISCSI_NET_PARAM_VLAN_TAG: - ret = beiscsi_set_vlan_tag(shost, iface_param); + case ISCSI_NET_PARAM_IPV4_SUBNET: + /* + * OPCODE_COMMON_ISCSI_NTWK_MODIFY_IP_ADDR ioctl needs IP + * and subnet both. Find IP to be applied for this subnet. + */ + subnet = info->value; + nla = nla_find(data, dt_len, ISCSI_NET_PARAM_IPV4_ADDR); + if (nla) { + info = nla_data(nla); + ip = info->value; + } + ret = beiscsi_if_en_static(phba, BEISCSI_IP_TYPE_V4, + ip, subnet); break; - default: - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : Param %d not supported\n", - iface_param->param); } return ret; } static int -beiscsi_set_ipv6(struct Scsi_Host *shost, - struct iscsi_iface_param_info *iface_param, - void *data, uint32_t dt_len) +beiscsi_iface_config_ipv6(struct Scsi_Host *shost, + struct iscsi_iface_param_info *iface_param, + void *data, uint32_t dt_len) { struct beiscsi_hba *phba = iscsi_host_priv(shost); - int ret = 0; + int ret = -EPERM; switch (iface_param->param) { case ISCSI_NET_PARAM_IFACE_ENABLE: if (iface_param->value[0] == ISCSI_IFACE_ENABLE) - ret = beiscsi_create_ipv6_iface(phba); + ret = beiscsi_iface_create_ipv6(phba); else { iscsi_destroy_iface(phba->ipv6_iface); - ret = 0; + phba->ipv6_iface = NULL; } break; case ISCSI_NET_PARAM_IPV6_ADDR: - ret = mgmt_set_ip(phba, iface_param, NULL, - ISCSI_BOOTPROTO_STATIC); + ret = beiscsi_if_en_static(phba, BEISCSI_IP_TYPE_V6, + iface_param->value, NULL); break; - case ISCSI_NET_PARAM_VLAN_ENABLED: - case ISCSI_NET_PARAM_VLAN_TAG: - ret = beiscsi_set_vlan_tag(shost, iface_param); - break; - default: - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : Param %d not supported\n", - iface_param->param); } return ret; } -int be2iscsi_iface_set_param(struct Scsi_Host *shost, - void *data, uint32_t dt_len) +int beiscsi_iface_set_param(struct Scsi_Host *shost, + void *data, uint32_t dt_len) { struct iscsi_iface_param_info *iface_param = NULL; struct beiscsi_hba *phba = iscsi_host_priv(shost); struct nlattr *attrib; uint32_t rm_len = dt_len; - int ret = 0 ; + int ret; - if (phba->state & BE_ADAPTER_PCI_ERR) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : In PCI_ERROR Recovery\n"); + if (!beiscsi_hba_is_online(phba)) { + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, + "BS_%d : HBA in error 0x%lx\n", phba->state); return -EBUSY; } + /* update interface_handle */ + ret = beiscsi_if_get_handle(phba); + if (ret) { + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, + "BS_%d : Getting Interface Handle Failed\n"); + return ret; + } + nla_for_each_attr(attrib, data, dt_len, rm_len) { iface_param = nla_data(attrib); @@ -512,40 +476,58 @@ int be2iscsi_iface_set_param(struct Scsi_Host *shost, return -EINVAL; } - switch (iface_param->iface_type) { - case ISCSI_IFACE_TYPE_IPV4: - ret = beiscsi_set_ipv4(shost, iface_param, - data, dt_len); - break; - case ISCSI_IFACE_TYPE_IPV6: - ret = beiscsi_set_ipv6(shost, iface_param, - data, dt_len); + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, + "BS_%d : %s.0 set param %d", + (iface_param->iface_type == ISCSI_IFACE_TYPE_IPV4) ? + "ipv4" : "ipv6", iface_param->param); + + ret = -EPERM; + switch (iface_param->param) { + case ISCSI_NET_PARAM_VLAN_ENABLED: + case ISCSI_NET_PARAM_VLAN_TAG: + ret = beiscsi_iface_config_vlan(shost, iface_param); break; default: - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : Invalid iface type :%d passed\n", - iface_param->iface_type); - break; + switch (iface_param->iface_type) { + case ISCSI_IFACE_TYPE_IPV4: + ret = beiscsi_iface_config_ipv4(shost, + iface_param, + data, dt_len); + break; + case ISCSI_IFACE_TYPE_IPV6: + ret = beiscsi_iface_config_ipv6(shost, + iface_param, + data, dt_len); + break; + } } + if (ret == -EPERM) { + __beiscsi_log(phba, KERN_ERR, + "BS_%d : %s.0 set param %d not permitted", + (iface_param->iface_type == + ISCSI_IFACE_TYPE_IPV4) ? "ipv4" : "ipv6", + iface_param->param); + ret = 0; + } if (ret) - return ret; + break; } return ret; } -static int be2iscsi_get_if_param(struct beiscsi_hba *phba, - struct iscsi_iface *iface, int param, - char *buf) +static int __beiscsi_iface_get_param(struct beiscsi_hba *phba, + struct iscsi_iface *iface, + int param, char *buf) { struct be_cmd_get_if_info_resp *if_info; - int len, ip_type = BE2_IPV4; + int len, ip_type = BEISCSI_IP_TYPE_V4; if (iface->iface_type == ISCSI_IFACE_TYPE_IPV6) - ip_type = BE2_IPV6; + ip_type = BEISCSI_IP_TYPE_V6; - len = mgmt_get_if_info(phba, ip_type, &if_info); + len = beiscsi_if_get_info(phba, ip_type, &if_info); if (len) return len; @@ -567,24 +549,24 @@ static int be2iscsi_get_if_param(struct beiscsi_hba *phba, break; case ISCSI_NET_PARAM_VLAN_ENABLED: len = sprintf(buf, "%s\n", - (if_info->vlan_priority == BEISCSI_VLAN_DISABLE) - ? "Disabled\n" : "Enabled\n"); + (if_info->vlan_priority == BEISCSI_VLAN_DISABLE) ? + "disable" : "enable"); break; case ISCSI_NET_PARAM_VLAN_ID: if (if_info->vlan_priority == BEISCSI_VLAN_DISABLE) len = -EINVAL; else len = sprintf(buf, "%d\n", - (if_info->vlan_priority & - ISCSI_MAX_VLAN_ID)); + (if_info->vlan_priority & + ISCSI_MAX_VLAN_ID)); break; case ISCSI_NET_PARAM_VLAN_PRIORITY: if (if_info->vlan_priority == BEISCSI_VLAN_DISABLE) len = -EINVAL; else len = sprintf(buf, "%d\n", - ((if_info->vlan_priority >> 13) & - ISCSI_MAX_VLAN_PRIORITY)); + ((if_info->vlan_priority >> 13) & + ISCSI_MAX_VLAN_PRIORITY)); break; default: WARN_ON(1); @@ -594,18 +576,20 @@ static int be2iscsi_get_if_param(struct beiscsi_hba *phba, return len; } -int be2iscsi_iface_get_param(struct iscsi_iface *iface, - enum iscsi_param_type param_type, - int param, char *buf) +int beiscsi_iface_get_param(struct iscsi_iface *iface, + enum iscsi_param_type param_type, + int param, char *buf) { struct Scsi_Host *shost = iscsi_iface_to_shost(iface); struct beiscsi_hba *phba = iscsi_host_priv(shost); struct be_cmd_get_def_gateway_resp gateway; - int len = -ENOSYS; + int len = -EPERM; - if (phba->state & BE_ADAPTER_PCI_ERR) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : In PCI_ERROR Recovery\n"); + if (param_type != ISCSI_NET_PARAM) + return 0; + if (!beiscsi_hba_is_online(phba)) { + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, + "BS_%d : HBA in error 0x%lx\n", phba->state); return -EBUSY; } @@ -617,19 +601,22 @@ int be2iscsi_iface_get_param(struct iscsi_iface *iface, case ISCSI_NET_PARAM_VLAN_ENABLED: case ISCSI_NET_PARAM_VLAN_ID: case ISCSI_NET_PARAM_VLAN_PRIORITY: - len = be2iscsi_get_if_param(phba, iface, param, buf); + len = __beiscsi_iface_get_param(phba, iface, param, buf); break; case ISCSI_NET_PARAM_IFACE_ENABLE: - len = sprintf(buf, "enabled\n"); + if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4) + len = sprintf(buf, "%s\n", + phba->ipv4_iface ? "enable" : "disable"); + else if (iface->iface_type == ISCSI_IFACE_TYPE_IPV6) + len = sprintf(buf, "%s\n", + phba->ipv6_iface ? "enable" : "disable"); break; case ISCSI_NET_PARAM_IPV4_GW: memset(&gateway, 0, sizeof(gateway)); - len = mgmt_get_gateway(phba, BE2_IPV4, &gateway); + len = beiscsi_if_get_gw(phba, BEISCSI_IP_TYPE_V4, &gateway); if (!len) len = sprintf(buf, "%pI4\n", &gateway.ip_addr.addr); break; - default: - len = -ENOSYS; } return len; @@ -647,7 +634,7 @@ int beiscsi_ep_get_param(struct iscsi_endpoint *ep, enum iscsi_param param, char *buf) { struct beiscsi_endpoint *beiscsi_ep = ep->dd_data; - int len = 0; + int len; beiscsi_log(beiscsi_ep->phba, KERN_INFO, BEISCSI_LOG_CONFIG, @@ -659,13 +646,13 @@ int beiscsi_ep_get_param(struct iscsi_endpoint *ep, len = sprintf(buf, "%hu\n", beiscsi_ep->dst_tcpport); break; case ISCSI_PARAM_CONN_ADDRESS: - if (beiscsi_ep->ip_type == BE2_IPV4) + if (beiscsi_ep->ip_type == BEISCSI_IP_TYPE_V4) len = sprintf(buf, "%pI4\n", &beiscsi_ep->dst_addr); else len = sprintf(buf, "%pI6\n", &beiscsi_ep->dst6_addr); break; default: - return -ENOSYS; + len = -EPERM; } return len; } @@ -758,7 +745,7 @@ static void beiscsi_get_port_state(struct Scsi_Host *shost) struct beiscsi_hba *phba = iscsi_host_priv(shost); struct iscsi_cls_host *ihost = shost->shost_data; - ihost->port_state = (phba->state & BE_ADAPTER_LINK_UP) ? + ihost->port_state = test_bit(BEISCSI_HBA_LINK_UP, &phba->state) ? ISCSI_PORT_STATE_UP : ISCSI_PORT_STATE_DOWN; } @@ -810,16 +797,13 @@ int beiscsi_get_host_param(struct Scsi_Host *shost, struct beiscsi_hba *phba = iscsi_host_priv(shost); int status = 0; - - if (phba->state & BE_ADAPTER_PCI_ERR) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : In PCI_ERROR Recovery\n"); - return -EBUSY; - } else { + if (!beiscsi_hba_is_online(phba)) { beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, - "BS_%d : In beiscsi_get_host_param," - " param = %d\n", param); + "BS_%d : HBA in error 0x%lx\n", phba->state); + return -EBUSY; } + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, + "BS_%d : In beiscsi_get_host_param, param = %d\n", param); switch (param) { case ISCSI_HOST_PARAM_HWADDRESS: @@ -961,15 +945,13 @@ int beiscsi_conn_start(struct iscsi_cls_conn *cls_conn) phba = ((struct beiscsi_conn *)conn->dd_data)->phba; - if (phba->state & BE_ADAPTER_PCI_ERR) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : In PCI_ERROR Recovery\n"); + if (!beiscsi_hba_is_online(phba)) { + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, + "BS_%d : HBA in error 0x%lx\n", phba->state); return -EBUSY; - } else { - beiscsi_log(beiscsi_conn->phba, KERN_INFO, - BEISCSI_LOG_CONFIG, - "BS_%d : In beiscsi_conn_start\n"); } + beiscsi_log(beiscsi_conn->phba, KERN_INFO, BEISCSI_LOG_CONFIG, + "BS_%d : In beiscsi_conn_start\n"); memset(¶ms, 0, sizeof(struct beiscsi_offload_params)); beiscsi_ep = beiscsi_conn->ep; @@ -1186,28 +1168,20 @@ beiscsi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr, struct iscsi_endpoint *ep; int ret; - if (shost) - phba = iscsi_host_priv(shost); - else { + if (!shost) { ret = -ENXIO; - printk(KERN_ERR - "beiscsi_ep_connect shost is NULL\n"); + pr_err("beiscsi_ep_connect shost is NULL\n"); return ERR_PTR(ret); } - if (beiscsi_error(phba)) { + phba = iscsi_host_priv(shost); + if (!beiscsi_hba_is_online(phba)) { ret = -EIO; - beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, - "BS_%d : The FW state Not Stable!!!\n"); + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, + "BS_%d : HBA in error 0x%lx\n", phba->state); return ERR_PTR(ret); } - - if (phba->state & BE_ADAPTER_PCI_ERR) { - ret = -EBUSY; - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : In PCI_ERROR Recovery\n"); - return ERR_PTR(ret); - } else if (phba->state & BE_ADAPTER_LINK_DOWN) { + if (!test_bit(BEISCSI_HBA_LINK_UP, &phba->state)) { ret = -EBUSY; beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, "BS_%d : The Adapter Port state is Down!!!\n"); @@ -1361,9 +1335,9 @@ void beiscsi_ep_disconnect(struct iscsi_endpoint *ep) tcp_upload_flag = CONNECTION_UPLOAD_ABORT; } - if (phba->state & BE_ADAPTER_PCI_ERR) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, - "BS_%d : PCI_ERROR Recovery\n"); + if (!beiscsi_hba_is_online(phba)) { + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, + "BS_%d : HBA in error 0x%lx\n", phba->state); goto free_ep; } @@ -1386,7 +1360,7 @@ free_ep: iscsi_destroy_endpoint(beiscsi_ep->openiscsi_ep); } -umode_t be2iscsi_attr_is_visible(int param_type, int param) +umode_t beiscsi_attr_is_visible(int param_type, int param) { switch (param_type) { case ISCSI_NET_PARAM: diff --git a/drivers/scsi/be2iscsi/be_iscsi.h b/drivers/scsi/be2iscsi/be_iscsi.h index 0c84e1c0763a..e4d67dfea4cb 100644 --- a/drivers/scsi/be2iscsi/be_iscsi.h +++ b/drivers/scsi/be2iscsi/be_iscsi.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Avago Technologies + * Copyright (C) 2005 - 2016 Broadcom * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -7,10 +7,10 @@ * as published by the Free Software Foundation. The full GNU General * Public License is included in this distribution in the file called COPYING. * - * Written by: Jayamohan Kallickal (jayamohan.kallickal@avagotech.com) + * Written by: Jayamohan Kallickal (jayamohan.kallickal@broadcom.com) * * Contact Information: - * linux-drivers@avagotech.com + * linux-drivers@broadcom.com * * Avago Technologies * 3333 Susan Street @@ -23,25 +23,18 @@ #include "be_main.h" #include "be_mgmt.h" -#define BE2_IPV4 0x1 -#define BE2_IPV6 0x10 -#define BE2_DHCP_V4 0x05 +void beiscsi_iface_create_default(struct beiscsi_hba *phba); -#define NON_BLOCKING 0x0 -#define BLOCKING 0x1 +void beiscsi_iface_destroy_default(struct beiscsi_hba *phba); -void beiscsi_create_def_ifaces(struct beiscsi_hba *phba); - -void beiscsi_destroy_def_ifaces(struct beiscsi_hba *phba); - -int be2iscsi_iface_get_param(struct iscsi_iface *iface, +int beiscsi_iface_get_param(struct iscsi_iface *iface, enum iscsi_param_type param_type, int param, char *buf); -int be2iscsi_iface_set_param(struct Scsi_Host *shost, +int beiscsi_iface_set_param(struct Scsi_Host *shost, void *data, uint32_t count); -umode_t be2iscsi_attr_is_visible(int param_type, int param); +umode_t beiscsi_attr_is_visible(int param_type, int param); void beiscsi_offload_connection(struct beiscsi_conn *beiscsi_conn, struct beiscsi_offload_params *params); @@ -57,6 +50,8 @@ struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep, void beiscsi_session_destroy(struct iscsi_cls_session *cls_session); +void beiscsi_session_fail(struct iscsi_cls_session *cls_session); + struct iscsi_cls_conn *beiscsi_conn_create(struct iscsi_cls_session *cls_session, uint32_t cid); diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index f05e7737107d..6a6906f847db 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Emulex + * Copyright (C) 2005 - 2016 Broadcom * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -7,10 +7,10 @@ * as published by the Free Software Foundation. The full GNU General * Public License is included in this distribution in the file called COPYING. * - * Written by: Jayamohan Kallickal (jayamohan.kallickal@avagotech.com) + * Written by: Jayamohan Kallickal (jayamohan.kallickal@broadcom.com) * * Contact Information: - * linux-drivers@avagotech.com + * linux-drivers@broadcom.com * * Emulex * 3333 Susan Street @@ -374,170 +374,6 @@ static int beiscsi_eh_device_reset(struct scsi_cmnd *sc) return iscsi_eh_device_reset(sc); } -static ssize_t beiscsi_show_boot_tgt_info(void *data, int type, char *buf) -{ - struct beiscsi_hba *phba = data; - struct mgmt_session_info *boot_sess = &phba->boot_sess; - struct mgmt_conn_info *boot_conn = &boot_sess->conn_list[0]; - char *str = buf; - int rc; - - switch (type) { - case ISCSI_BOOT_TGT_NAME: - rc = sprintf(buf, "%.*s\n", - (int)strlen(boot_sess->target_name), - (char *)&boot_sess->target_name); - break; - case ISCSI_BOOT_TGT_IP_ADDR: - if (boot_conn->dest_ipaddr.ip_type == 0x1) - rc = sprintf(buf, "%pI4\n", - (char *)&boot_conn->dest_ipaddr.addr); - else - rc = sprintf(str, "%pI6\n", - (char *)&boot_conn->dest_ipaddr.addr); - break; - case ISCSI_BOOT_TGT_PORT: - rc = sprintf(str, "%d\n", boot_conn->dest_port); - break; - - case ISCSI_BOOT_TGT_CHAP_NAME: - rc = sprintf(str, "%.*s\n", - boot_conn->negotiated_login_options.auth_data.chap. - target_chap_name_length, - (char *)&boot_conn->negotiated_login_options. - auth_data.chap.target_chap_name); - break; - case ISCSI_BOOT_TGT_CHAP_SECRET: - rc = sprintf(str, "%.*s\n", - boot_conn->negotiated_login_options.auth_data.chap. - target_secret_length, - (char *)&boot_conn->negotiated_login_options. - auth_data.chap.target_secret); - break; - case ISCSI_BOOT_TGT_REV_CHAP_NAME: - rc = sprintf(str, "%.*s\n", - boot_conn->negotiated_login_options.auth_data.chap. - intr_chap_name_length, - (char *)&boot_conn->negotiated_login_options. - auth_data.chap.intr_chap_name); - break; - case ISCSI_BOOT_TGT_REV_CHAP_SECRET: - rc = sprintf(str, "%.*s\n", - boot_conn->negotiated_login_options.auth_data.chap. - intr_secret_length, - (char *)&boot_conn->negotiated_login_options. - auth_data.chap.intr_secret); - break; - case ISCSI_BOOT_TGT_FLAGS: - rc = sprintf(str, "2\n"); - break; - case ISCSI_BOOT_TGT_NIC_ASSOC: - rc = sprintf(str, "0\n"); - break; - default: - rc = -ENOSYS; - break; - } - return rc; -} - -static ssize_t beiscsi_show_boot_ini_info(void *data, int type, char *buf) -{ - struct beiscsi_hba *phba = data; - char *str = buf; - int rc; - - switch (type) { - case ISCSI_BOOT_INI_INITIATOR_NAME: - rc = sprintf(str, "%s\n", phba->boot_sess.initiator_iscsiname); - break; - default: - rc = -ENOSYS; - break; - } - return rc; -} - -static ssize_t beiscsi_show_boot_eth_info(void *data, int type, char *buf) -{ - struct beiscsi_hba *phba = data; - char *str = buf; - int rc; - - switch (type) { - case ISCSI_BOOT_ETH_FLAGS: - rc = sprintf(str, "2\n"); - break; - case ISCSI_BOOT_ETH_INDEX: - rc = sprintf(str, "0\n"); - break; - case ISCSI_BOOT_ETH_MAC: - rc = beiscsi_get_macaddr(str, phba); - break; - default: - rc = -ENOSYS; - break; - } - return rc; -} - - -static umode_t beiscsi_tgt_get_attr_visibility(void *data, int type) -{ - umode_t rc; - - switch (type) { - case ISCSI_BOOT_TGT_NAME: - case ISCSI_BOOT_TGT_IP_ADDR: - case ISCSI_BOOT_TGT_PORT: - case ISCSI_BOOT_TGT_CHAP_NAME: - case ISCSI_BOOT_TGT_CHAP_SECRET: - case ISCSI_BOOT_TGT_REV_CHAP_NAME: - case ISCSI_BOOT_TGT_REV_CHAP_SECRET: - case ISCSI_BOOT_TGT_NIC_ASSOC: - case ISCSI_BOOT_TGT_FLAGS: - rc = S_IRUGO; - break; - default: - rc = 0; - break; - } - return rc; -} - -static umode_t beiscsi_ini_get_attr_visibility(void *data, int type) -{ - umode_t rc; - - switch (type) { - case ISCSI_BOOT_INI_INITIATOR_NAME: - rc = S_IRUGO; - break; - default: - rc = 0; - break; - } - return rc; -} - - -static umode_t beiscsi_eth_get_attr_visibility(void *data, int type) -{ - umode_t rc; - - switch (type) { - case ISCSI_BOOT_ETH_FLAGS: - case ISCSI_BOOT_ETH_MAC: - case ISCSI_BOOT_ETH_INDEX: - rc = S_IRUGO; - break; - default: - rc = 0; - break; - } - return rc; -} - /*------------------- PCI Driver operations and data ----------------- */ static const struct pci_device_id beiscsi_pci_id_table[] = { { PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID1) }, @@ -850,12 +686,11 @@ static void hwi_ring_eq_db(struct beiscsi_hba *phba, static irqreturn_t be_isr_mcc(int irq, void *dev_id) { struct beiscsi_hba *phba; - struct be_eq_entry *eqe = NULL; + struct be_eq_entry *eqe; struct be_queue_info *eq; struct be_queue_info *mcc; - unsigned int num_eq_processed; + unsigned int mcc_events; struct be_eq_obj *pbe_eq; - unsigned long flags; pbe_eq = dev_id; eq = &pbe_eq->q; @@ -863,27 +698,23 @@ static irqreturn_t be_isr_mcc(int irq, void *dev_id) mcc = &phba->ctrl.mcc_obj.cq; eqe = queue_tail_node(eq); - num_eq_processed = 0; - + mcc_events = 0; while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] & EQE_VALID_MASK) { if (((eqe->dw[offsetof(struct amap_eq_entry, resource_id) / 32] & EQE_RESID_MASK) >> 16) == mcc->id) { - spin_lock_irqsave(&phba->isr_lock, flags); - pbe_eq->todo_mcc_cq = true; - spin_unlock_irqrestore(&phba->isr_lock, flags); + mcc_events++; } AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); queue_tail_inc(eq); eqe = queue_tail_node(eq); - num_eq_processed++; } - if (pbe_eq->todo_mcc_cq) - queue_work(phba->wq, &pbe_eq->work_cqs); - if (num_eq_processed) - hwi_ring_eq_db(phba, eq->id, 1, num_eq_processed, 1, 1); + if (mcc_events) { + queue_work(phba->wq, &pbe_eq->mcc_work); + hwi_ring_eq_db(phba, eq->id, 1, mcc_events, 1, 1); + } return IRQ_HANDLED; } @@ -902,7 +733,6 @@ static irqreturn_t be_isr_msix(int irq, void *dev_id) eq = &pbe_eq->q; phba = pbe_eq->phba; - /* disable interrupt till iopoll completes */ hwi_ring_eq_db(phba, eq->id, 1, 0, 0, 1); irq_poll_sched(&pbe_eq->iopoll); @@ -920,14 +750,13 @@ static irqreturn_t be_isr(int irq, void *dev_id) struct beiscsi_hba *phba; struct hwi_controller *phwi_ctrlr; struct hwi_context_memory *phwi_context; - struct be_eq_entry *eqe = NULL; + struct be_eq_entry *eqe; struct be_queue_info *eq; struct be_queue_info *mcc; - unsigned long flags, index; - unsigned int num_mcceq_processed, num_ioeq_processed; + unsigned int mcc_events, io_events; struct be_ctrl_info *ctrl; struct be_eq_obj *pbe_eq; - int isr; + int isr, rearm; phba = dev_id; ctrl = &phba->ctrl; @@ -942,44 +771,35 @@ static irqreturn_t be_isr(int irq, void *dev_id) eq = &phwi_context->be_eq[0].q; mcc = &phba->ctrl.mcc_obj.cq; - index = 0; eqe = queue_tail_node(eq); - num_ioeq_processed = 0; - num_mcceq_processed = 0; + io_events = 0; + mcc_events = 0; while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] & EQE_VALID_MASK) { if (((eqe->dw[offsetof(struct amap_eq_entry, - resource_id) / 32] & - EQE_RESID_MASK) >> 16) == mcc->id) { - spin_lock_irqsave(&phba->isr_lock, flags); - pbe_eq->todo_mcc_cq = true; - spin_unlock_irqrestore(&phba->isr_lock, flags); - num_mcceq_processed++; - } else { - irq_poll_sched(&pbe_eq->iopoll); - num_ioeq_processed++; - } + resource_id) / 32] & EQE_RESID_MASK) >> 16) == mcc->id) + mcc_events++; + else + io_events++; AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); queue_tail_inc(eq); eqe = queue_tail_node(eq); } - if (num_ioeq_processed || num_mcceq_processed) { - if (pbe_eq->todo_mcc_cq) - queue_work(phba->wq, &pbe_eq->work_cqs); - - if ((num_mcceq_processed) && (!num_ioeq_processed)) - hwi_ring_eq_db(phba, eq->id, 0, - (num_ioeq_processed + - num_mcceq_processed) , 1, 1); - else - hwi_ring_eq_db(phba, eq->id, 0, - (num_ioeq_processed + - num_mcceq_processed), 0, 1); - - return IRQ_HANDLED; - } else + if (!io_events && !mcc_events) return IRQ_NONE; + + /* no need to rearm if interrupt is only for IOs */ + rearm = 0; + if (mcc_events) { + queue_work(phba->wq, &pbe_eq->mcc_work); + /* rearm for MCCQ */ + rearm = 1; + } + if (io_events) + irq_poll_sched(&pbe_eq->iopoll); + hwi_ring_eq_db(phba, eq->id, 0, (io_events + mcc_events), rearm, 1); + return IRQ_HANDLED; } @@ -1077,57 +897,6 @@ void hwi_ring_cq_db(struct beiscsi_hba *phba, iowrite32(val, phba->db_va + DB_CQ_OFFSET); } -static unsigned int -beiscsi_process_async_pdu(struct beiscsi_conn *beiscsi_conn, - struct beiscsi_hba *phba, - struct pdu_base *ppdu, - unsigned long pdu_len, - void *pbuffer, unsigned long buf_len) -{ - struct iscsi_conn *conn = beiscsi_conn->conn; - struct iscsi_session *session = conn->session; - struct iscsi_task *task; - struct beiscsi_io_task *io_task; - struct iscsi_hdr *login_hdr; - - switch (ppdu->dw[offsetof(struct amap_pdu_base, opcode) / 32] & - PDUBASE_OPCODE_MASK) { - case ISCSI_OP_NOOP_IN: - pbuffer = NULL; - buf_len = 0; - break; - case ISCSI_OP_ASYNC_EVENT: - break; - case ISCSI_OP_REJECT: - WARN_ON(!pbuffer); - WARN_ON(!(buf_len == 48)); - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO, - "BM_%d : In ISCSI_OP_REJECT\n"); - break; - case ISCSI_OP_LOGIN_RSP: - case ISCSI_OP_TEXT_RSP: - task = conn->login_task; - io_task = task->dd_data; - login_hdr = (struct iscsi_hdr *)ppdu; - login_hdr->itt = io_task->libiscsi_itt; - break; - default: - beiscsi_log(phba, KERN_WARNING, - BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG, - "BM_%d : Unrecognized opcode 0x%x in async msg\n", - (ppdu-> - dw[offsetof(struct amap_pdu_base, opcode) / 32] - & PDUBASE_OPCODE_MASK)); - return 1; - } - - spin_lock_bh(&session->back_lock); - __iscsi_complete_pdu(conn, (struct iscsi_hdr *)ppdu, pbuffer, buf_len); - spin_unlock_bh(&session->back_lock); - return 0; -} - static struct sgl_handle *alloc_io_sgl_handle(struct beiscsi_hba *phba) { struct sgl_handle *psgl_handle; @@ -1199,6 +968,9 @@ beiscsi_get_wrb_handle(struct hwi_wrb_context *pwrb_context, pwrb_context->alloc_index++; spin_unlock_bh(&pwrb_context->wrb_lock); + if (pwrb_handle) + memset(pwrb_handle->pwrb, 0, sizeof(*pwrb_handle->pwrb)); + return pwrb_handle; } @@ -1440,11 +1212,10 @@ hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn, struct beiscsi_hba *phba, struct sol_cqe *psol) { struct hwi_wrb_context *pwrb_context; - struct wrb_handle *pwrb_handle = NULL; + uint16_t wrb_index, cid, cri_index; struct hwi_controller *phwi_ctrlr; + struct wrb_handle *pwrb_handle; struct iscsi_task *task; - struct beiscsi_io_task *io_task; - uint16_t wrb_index, cid, cri_index; phwi_ctrlr = phba->phwi_ctrlr; if (is_chip_be2_be3r(phba)) { @@ -1463,9 +1234,6 @@ hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn, pwrb_context = &phwi_ctrlr->wrb_context[cri_index]; pwrb_handle = pwrb_context->pwrb_handle_basestd[wrb_index]; task = pwrb_handle->pio_handle; - - io_task = task->dd_data; - memset(io_task->pwrb_handle->pwrb, 0, sizeof(struct iscsi_wrb)); iscsi_put_task(task); } @@ -1614,431 +1382,428 @@ static void hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn, spin_unlock_bh(&session->back_lock); } -static struct list_head *hwi_get_async_busy_list(struct hwi_async_pdu_context - *pasync_ctx, unsigned int is_header, - unsigned int host_write_ptr) +/** + * ASYNC PDUs include + * a. Unsolicited NOP-In (target initiated NOP-In) + * b. ASYNC Messages + * c. Reject PDU + * d. Login response + * These headers arrive unprocessed by the EP firmware. + * iSCSI layer processes them. + */ +static unsigned int +beiscsi_complete_pdu(struct beiscsi_conn *beiscsi_conn, + struct pdu_base *phdr, void *pdata, unsigned int dlen) { - if (is_header) - return &pasync_ctx->async_entry[host_write_ptr]. - header_busy_list; - else - return &pasync_ctx->async_entry[host_write_ptr].data_busy_list; + struct beiscsi_hba *phba = beiscsi_conn->phba; + struct iscsi_conn *conn = beiscsi_conn->conn; + struct beiscsi_io_task *io_task; + struct iscsi_hdr *login_hdr; + struct iscsi_task *task; + u8 code; + + code = AMAP_GET_BITS(struct amap_pdu_base, opcode, phdr); + switch (code) { + case ISCSI_OP_NOOP_IN: + pdata = NULL; + dlen = 0; + break; + case ISCSI_OP_ASYNC_EVENT: + break; + case ISCSI_OP_REJECT: + WARN_ON(!pdata); + WARN_ON(!(dlen == 48)); + beiscsi_log(phba, KERN_ERR, + BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO, + "BM_%d : In ISCSI_OP_REJECT\n"); + break; + case ISCSI_OP_LOGIN_RSP: + case ISCSI_OP_TEXT_RSP: + task = conn->login_task; + io_task = task->dd_data; + login_hdr = (struct iscsi_hdr *)phdr; + login_hdr->itt = io_task->libiscsi_itt; + break; + default: + beiscsi_log(phba, KERN_WARNING, + BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG, + "BM_%d : unrecognized async PDU opcode 0x%x\n", + code); + return 1; + } + __iscsi_complete_pdu(conn, (struct iscsi_hdr *)phdr, pdata, dlen); + return 0; +} + +static inline void +beiscsi_hdl_put_handle(struct hd_async_context *pasync_ctx, + struct hd_async_handle *pasync_handle) +{ + if (pasync_handle->is_header) { + list_add_tail(&pasync_handle->link, + &pasync_ctx->async_header.free_list); + pasync_ctx->async_header.free_entries++; + } else { + list_add_tail(&pasync_handle->link, + &pasync_ctx->async_data.free_list); + pasync_ctx->async_data.free_entries++; + } } -static struct async_pdu_handle * -hwi_get_async_handle(struct beiscsi_hba *phba, - struct beiscsi_conn *beiscsi_conn, - struct hwi_async_pdu_context *pasync_ctx, - struct i_t_dpdu_cqe *pdpdu_cqe, unsigned int *pcq_index) +static struct hd_async_handle * +beiscsi_hdl_get_handle(struct beiscsi_conn *beiscsi_conn, + struct hd_async_context *pasync_ctx, + struct i_t_dpdu_cqe *pdpdu_cqe) { + struct beiscsi_hba *phba = beiscsi_conn->phba; + struct hd_async_handle *pasync_handle; struct be_bus_address phys_addr; - struct list_head *pbusy_list; - struct async_pdu_handle *pasync_handle = NULL; - unsigned char is_header = 0; - unsigned int index, dpl; + u8 final, error = 0; + u16 cid, code, ci; + u32 dpl; + cid = beiscsi_conn->beiscsi_conn_cid; + /** + * This function is invoked to get the right async_handle structure + * from a given DEF PDU CQ entry. + * + * - index in CQ entry gives the vertical index + * - address in CQ entry is the offset where the DMA last ended + * - final - no more notifications for this PDU + */ if (is_chip_be2_be3r(phba)) { dpl = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe, dpl, pdpdu_cqe); - index = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe, + ci = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe, index, pdpdu_cqe); + final = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe, + final, pdpdu_cqe); } else { dpl = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2, dpl, pdpdu_cqe); - index = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2, + ci = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2, index, pdpdu_cqe); + final = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2, + final, pdpdu_cqe); } - phys_addr.u.a32.address_lo = - (pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, - db_addr_lo) / 32] - dpl); - phys_addr.u.a32.address_hi = - pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, - db_addr_hi) / 32]; - - phys_addr.u.a64.address = - *((unsigned long long *)(&phys_addr.u.a64.address)); - - switch (pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, code) / 32] - & PDUCQE_CODE_MASK) { + /** + * DB addr Hi/Lo is same for BE and SKH. + * Subtract the dataplacementlength to get to the base. + */ + phys_addr.u.a32.address_lo = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe, + db_addr_lo, pdpdu_cqe); + phys_addr.u.a32.address_lo -= dpl; + phys_addr.u.a32.address_hi = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe, + db_addr_hi, pdpdu_cqe); + + code = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe, code, pdpdu_cqe); + switch (code) { case UNSOL_HDR_NOTIFY: - is_header = 1; - - pbusy_list = hwi_get_async_busy_list(pasync_ctx, - is_header, index); + pasync_handle = pasync_ctx->async_entry[ci].header; break; + case UNSOL_DATA_DIGEST_ERROR_NOTIFY: + error = 1; case UNSOL_DATA_NOTIFY: - pbusy_list = hwi_get_async_busy_list(pasync_ctx, - is_header, index); + pasync_handle = pasync_ctx->async_entry[ci].data; break; + /* called only for above codes */ default: - pbusy_list = NULL; - beiscsi_log(phba, KERN_WARNING, - BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG, - "BM_%d : Unexpected code=%d\n", - pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, - code) / 32] & PDUCQE_CODE_MASK); - return NULL; + pasync_handle = NULL; + break; } - WARN_ON(list_empty(pbusy_list)); - list_for_each_entry(pasync_handle, pbusy_list, link) { - if (pasync_handle->pa.u.a64.address == phys_addr.u.a64.address) - break; + if (!pasync_handle) { + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI, + "BM_%d : cid %d async PDU handle not found - code %d ci %d addr %llx\n", + cid, code, ci, phys_addr.u.a64.address); + return pasync_handle; } - WARN_ON(!pasync_handle); + if (pasync_handle->pa.u.a64.address != phys_addr.u.a64.address || + pasync_handle->index != ci) { + /* driver bug - if ci does not match async handle index */ + error = 1; + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI, + "BM_%d : cid %u async PDU handle mismatch - addr in %cQE %llx at %u:addr in CQE %llx ci %u\n", + cid, pasync_handle->is_header ? 'H' : 'D', + pasync_handle->pa.u.a64.address, + pasync_handle->index, + phys_addr.u.a64.address, ci); + /* FW has stale address - attempt continuing by dropping */ + } - pasync_handle->cri = BE_GET_ASYNC_CRI_FROM_CID( - beiscsi_conn->beiscsi_conn_cid); - pasync_handle->is_header = is_header; + /** + * Each CID is associated with unique CRI. + * ASYNC_CRI_FROM_CID mapping and CRI_FROM_CID are totaly different. + **/ + pasync_handle->cri = BE_GET_ASYNC_CRI_FROM_CID(cid); + pasync_handle->is_final = final; pasync_handle->buffer_len = dpl; - *pcq_index = index; + /* empty the slot */ + if (pasync_handle->is_header) + pasync_ctx->async_entry[ci].header = NULL; + else + pasync_ctx->async_entry[ci].data = NULL; + /** + * DEF PDU header and data buffers with errors should be simply + * dropped as there are no consumers for it. + */ + if (error) { + beiscsi_hdl_put_handle(pasync_ctx, pasync_handle); + pasync_handle = NULL; + } return pasync_handle; } -static unsigned int -hwi_update_async_writables(struct beiscsi_hba *phba, - struct hwi_async_pdu_context *pasync_ctx, - unsigned int is_header, unsigned int cq_index) +static void +beiscsi_hdl_purge_handles(struct beiscsi_hba *phba, + struct hd_async_context *pasync_ctx, + u16 cri) { - struct list_head *pbusy_list; - struct async_pdu_handle *pasync_handle; - unsigned int num_entries, writables = 0; - unsigned int *pep_read_ptr, *pwritables; - - num_entries = pasync_ctx->num_entries; - if (is_header) { - pep_read_ptr = &pasync_ctx->async_header.ep_read_ptr; - pwritables = &pasync_ctx->async_header.writables; - } else { - pep_read_ptr = &pasync_ctx->async_data.ep_read_ptr; - pwritables = &pasync_ctx->async_data.writables; - } - - while ((*pep_read_ptr) != cq_index) { - (*pep_read_ptr)++; - *pep_read_ptr = (*pep_read_ptr) % num_entries; - - pbusy_list = hwi_get_async_busy_list(pasync_ctx, is_header, - *pep_read_ptr); - if (writables == 0) - WARN_ON(list_empty(pbusy_list)); - - if (!list_empty(pbusy_list)) { - pasync_handle = list_entry(pbusy_list->next, - struct async_pdu_handle, - link); - WARN_ON(!pasync_handle); - pasync_handle->consumed = 1; - } - - writables++; - } + struct hd_async_handle *pasync_handle, *tmp_handle; + struct list_head *plist; - if (!writables) { - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO, - "BM_%d : Duplicate notification received - index 0x%x!!\n", - cq_index); - WARN_ON(1); + plist = &pasync_ctx->async_entry[cri].wq.list; + list_for_each_entry_safe(pasync_handle, tmp_handle, plist, link) { + list_del(&pasync_handle->link); + beiscsi_hdl_put_handle(pasync_ctx, pasync_handle); } - *pwritables = *pwritables + writables; - return 0; + INIT_LIST_HEAD(&pasync_ctx->async_entry[cri].wq.list); + pasync_ctx->async_entry[cri].wq.hdr_len = 0; + pasync_ctx->async_entry[cri].wq.bytes_received = 0; + pasync_ctx->async_entry[cri].wq.bytes_needed = 0; } -static void hwi_free_async_msg(struct beiscsi_hba *phba, - struct hwi_async_pdu_context *pasync_ctx, - unsigned int cri) +static unsigned int +beiscsi_hdl_fwd_pdu(struct beiscsi_conn *beiscsi_conn, + struct hd_async_context *pasync_ctx, + u16 cri) { - struct async_pdu_handle *pasync_handle, *tmp_handle; + struct iscsi_session *session = beiscsi_conn->conn->session; + struct hd_async_handle *pasync_handle, *plast_handle; + struct beiscsi_hba *phba = beiscsi_conn->phba; + void *phdr = NULL, *pdata = NULL; + u32 dlen = 0, status = 0; struct list_head *plist; - plist = &pasync_ctx->async_entry[cri].wait_queue.list; - list_for_each_entry_safe(pasync_handle, tmp_handle, plist, link) { - list_del(&pasync_handle->link); - - if (pasync_handle->is_header) { - list_add_tail(&pasync_handle->link, - &pasync_ctx->async_header.free_list); - pasync_ctx->async_header.free_entries++; - } else { - list_add_tail(&pasync_handle->link, - &pasync_ctx->async_data.free_list); - pasync_ctx->async_data.free_entries++; + plist = &pasync_ctx->async_entry[cri].wq.list; + plast_handle = NULL; + list_for_each_entry(pasync_handle, plist, link) { + plast_handle = pasync_handle; + /* get the header, the first entry */ + if (!phdr) { + phdr = pasync_handle->pbuffer; + continue; + } + /* use first buffer to collect all the data */ + if (!pdata) { + pdata = pasync_handle->pbuffer; + dlen = pasync_handle->buffer_len; + continue; } + memcpy(pdata + dlen, pasync_handle->pbuffer, + pasync_handle->buffer_len); + dlen += pasync_handle->buffer_len; } - INIT_LIST_HEAD(&pasync_ctx->async_entry[cri].wait_queue.list); - pasync_ctx->async_entry[cri].wait_queue.hdr_received = 0; - pasync_ctx->async_entry[cri].wait_queue.bytes_received = 0; + if (!plast_handle->is_final) { + /* last handle should have final PDU notification from FW */ + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI, + "BM_%d : cid %u %p fwd async PDU with last handle missing - HL%u:DN%u:DR%u\n", + beiscsi_conn->beiscsi_conn_cid, plast_handle, + pasync_ctx->async_entry[cri].wq.hdr_len, + pasync_ctx->async_entry[cri].wq.bytes_needed, + pasync_ctx->async_entry[cri].wq.bytes_received); + } + spin_lock_bh(&session->back_lock); + status = beiscsi_complete_pdu(beiscsi_conn, phdr, pdata, dlen); + spin_unlock_bh(&session->back_lock); + beiscsi_hdl_purge_handles(phba, pasync_ctx, cri); + return status; } -static struct phys_addr * -hwi_get_ring_address(struct hwi_async_pdu_context *pasync_ctx, - unsigned int is_header, unsigned int host_write_ptr) +static unsigned int +beiscsi_hdl_gather_pdu(struct beiscsi_conn *beiscsi_conn, + struct hd_async_context *pasync_ctx, + struct hd_async_handle *pasync_handle) { - struct phys_addr *pasync_sge = NULL; + unsigned int bytes_needed = 0, status = 0; + u16 cri = pasync_handle->cri; + struct cri_wait_queue *wq; + struct beiscsi_hba *phba; + struct pdu_base *ppdu; + char *err = ""; - if (is_header) - pasync_sge = pasync_ctx->async_header.ring_base; - else - pasync_sge = pasync_ctx->async_data.ring_base; + phba = beiscsi_conn->phba; + wq = &pasync_ctx->async_entry[cri].wq; + if (pasync_handle->is_header) { + /* check if PDU hdr is rcv'd when old hdr not completed */ + if (wq->hdr_len) { + err = "incomplete"; + goto drop_pdu; + } + ppdu = pasync_handle->pbuffer; + bytes_needed = AMAP_GET_BITS(struct amap_pdu_base, + data_len_hi, ppdu); + bytes_needed <<= 16; + bytes_needed |= be16_to_cpu(AMAP_GET_BITS(struct amap_pdu_base, + data_len_lo, ppdu)); + wq->hdr_len = pasync_handle->buffer_len; + wq->bytes_received = 0; + wq->bytes_needed = bytes_needed; + list_add_tail(&pasync_handle->link, &wq->list); + if (!bytes_needed) + status = beiscsi_hdl_fwd_pdu(beiscsi_conn, + pasync_ctx, cri); + } else { + /* check if data received has header and is needed */ + if (!wq->hdr_len || !wq->bytes_needed) { + err = "header less"; + goto drop_pdu; + } + wq->bytes_received += pasync_handle->buffer_len; + /* Something got overwritten? Better catch it here. */ + if (wq->bytes_received > wq->bytes_needed) { + err = "overflow"; + goto drop_pdu; + } + list_add_tail(&pasync_handle->link, &wq->list); + if (wq->bytes_received == wq->bytes_needed) + status = beiscsi_hdl_fwd_pdu(beiscsi_conn, + pasync_ctx, cri); + } + return status; - return pasync_sge + host_write_ptr; +drop_pdu: + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI, + "BM_%d : cid %u async PDU %s - def-%c:HL%u:DN%u:DR%u\n", + beiscsi_conn->beiscsi_conn_cid, err, + pasync_handle->is_header ? 'H' : 'D', + wq->hdr_len, wq->bytes_needed, + pasync_handle->buffer_len); + /* discard this handle */ + beiscsi_hdl_put_handle(pasync_ctx, pasync_handle); + /* free all the other handles in cri_wait_queue */ + beiscsi_hdl_purge_handles(phba, pasync_ctx, cri); + /* try continuing */ + return status; } -static void hwi_post_async_buffers(struct beiscsi_hba *phba, - unsigned int is_header, uint8_t ulp_num) +static void +beiscsi_hdq_post_handles(struct beiscsi_hba *phba, + u8 header, u8 ulp_num) { + struct hd_async_handle *pasync_handle, *tmp, **slot; + struct hd_async_context *pasync_ctx; struct hwi_controller *phwi_ctrlr; - struct hwi_async_pdu_context *pasync_ctx; - struct async_pdu_handle *pasync_handle; - struct list_head *pfree_link, *pbusy_list; + struct list_head *hfree_list; struct phys_addr *pasync_sge; - unsigned int ring_id, num_entries; - unsigned int host_write_num, doorbell_offset; - unsigned int writables; - unsigned int i = 0; - u32 doorbell = 0; + u32 ring_id, doorbell = 0; + u16 index, num_entries; + u32 doorbell_offset; + u16 prod = 0, cons; phwi_ctrlr = phba->phwi_ctrlr; pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr, ulp_num); num_entries = pasync_ctx->num_entries; - - if (is_header) { - writables = min(pasync_ctx->async_header.writables, - pasync_ctx->async_header.free_entries); - pfree_link = pasync_ctx->async_header.free_list.next; - host_write_num = pasync_ctx->async_header.host_write_ptr; + if (header) { + cons = pasync_ctx->async_header.free_entries; + hfree_list = &pasync_ctx->async_header.free_list; ring_id = phwi_ctrlr->default_pdu_hdr[ulp_num].id; doorbell_offset = phwi_ctrlr->default_pdu_hdr[ulp_num]. - doorbell_offset; + doorbell_offset; } else { - writables = min(pasync_ctx->async_data.writables, - pasync_ctx->async_data.free_entries); - pfree_link = pasync_ctx->async_data.free_list.next; - host_write_num = pasync_ctx->async_data.host_write_ptr; + cons = pasync_ctx->async_data.free_entries; + hfree_list = &pasync_ctx->async_data.free_list; ring_id = phwi_ctrlr->default_pdu_data[ulp_num].id; doorbell_offset = phwi_ctrlr->default_pdu_data[ulp_num]. - doorbell_offset; + doorbell_offset; } + /* number of entries posted must be in multiples of 8 */ + if (cons % 8) + return; - writables = (writables / 8) * 8; - if (writables) { - for (i = 0; i < writables; i++) { - pbusy_list = - hwi_get_async_busy_list(pasync_ctx, is_header, - host_write_num); - pasync_handle = - list_entry(pfree_link, struct async_pdu_handle, - link); - WARN_ON(!pasync_handle); - pasync_handle->consumed = 0; - - pfree_link = pfree_link->next; - - pasync_sge = hwi_get_ring_address(pasync_ctx, - is_header, host_write_num); - - pasync_sge->hi = pasync_handle->pa.u.a32.address_lo; - pasync_sge->lo = pasync_handle->pa.u.a32.address_hi; - - list_move(&pasync_handle->link, pbusy_list); - - host_write_num++; - host_write_num = host_write_num % num_entries; - } - - if (is_header) { - pasync_ctx->async_header.host_write_ptr = - host_write_num; - pasync_ctx->async_header.free_entries -= writables; - pasync_ctx->async_header.writables -= writables; - pasync_ctx->async_header.busy_entries += writables; - } else { - pasync_ctx->async_data.host_write_ptr = host_write_num; - pasync_ctx->async_data.free_entries -= writables; - pasync_ctx->async_data.writables -= writables; - pasync_ctx->async_data.busy_entries += writables; - } - - doorbell |= ring_id & DB_DEF_PDU_RING_ID_MASK; - doorbell |= 1 << DB_DEF_PDU_REARM_SHIFT; - doorbell |= 0 << DB_DEF_PDU_EVENT_SHIFT; - doorbell |= (writables & DB_DEF_PDU_CQPROC_MASK) - << DB_DEF_PDU_CQPROC_SHIFT; - - iowrite32(doorbell, phba->db_va + doorbell_offset); - } -} - -static void hwi_flush_default_pdu_buffer(struct beiscsi_hba *phba, - struct beiscsi_conn *beiscsi_conn, - struct i_t_dpdu_cqe *pdpdu_cqe) -{ - struct hwi_controller *phwi_ctrlr; - struct hwi_async_pdu_context *pasync_ctx; - struct async_pdu_handle *pasync_handle = NULL; - unsigned int cq_index = -1; - uint16_t cri_index = BE_GET_CRI_FROM_CID( - beiscsi_conn->beiscsi_conn_cid); - - phwi_ctrlr = phba->phwi_ctrlr; - pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr, - BEISCSI_GET_ULP_FROM_CRI(phwi_ctrlr, - cri_index)); - - pasync_handle = hwi_get_async_handle(phba, beiscsi_conn, pasync_ctx, - pdpdu_cqe, &cq_index); - BUG_ON(pasync_handle->is_header != 0); - if (pasync_handle->consumed == 0) - hwi_update_async_writables(phba, pasync_ctx, - pasync_handle->is_header, cq_index); - - hwi_free_async_msg(phba, pasync_ctx, pasync_handle->cri); - hwi_post_async_buffers(phba, pasync_handle->is_header, - BEISCSI_GET_ULP_FROM_CRI(phwi_ctrlr, - cri_index)); -} - -static unsigned int -hwi_fwd_async_msg(struct beiscsi_conn *beiscsi_conn, - struct beiscsi_hba *phba, - struct hwi_async_pdu_context *pasync_ctx, unsigned short cri) -{ - struct list_head *plist; - struct async_pdu_handle *pasync_handle; - void *phdr = NULL; - unsigned int hdr_len = 0, buf_len = 0; - unsigned int status, index = 0, offset = 0; - void *pfirst_buffer = NULL; - unsigned int num_buf = 0; - - plist = &pasync_ctx->async_entry[cri].wait_queue.list; + list_for_each_entry_safe(pasync_handle, tmp, hfree_list, link) { + list_del_init(&pasync_handle->link); + pasync_handle->is_final = 0; + pasync_handle->buffer_len = 0; - list_for_each_entry(pasync_handle, plist, link) { - if (index == 0) { - phdr = pasync_handle->pbuffer; - hdr_len = pasync_handle->buffer_len; - } else { - buf_len = pasync_handle->buffer_len; - if (!num_buf) { - pfirst_buffer = pasync_handle->pbuffer; - num_buf++; - } - memcpy(pfirst_buffer + offset, - pasync_handle->pbuffer, buf_len); - offset += buf_len; + /* handles can be consumed out of order, use index in handle */ + index = pasync_handle->index; + WARN_ON(pasync_handle->is_header != header); + if (header) + slot = &pasync_ctx->async_entry[index].header; + else + slot = &pasync_ctx->async_entry[index].data; + /** + * The slot just tracks handle's hold and release, so + * overwriting at the same index won't do any harm but + * needs to be caught. + */ + if (*slot != NULL) { + beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI, + "BM_%d : async PDU %s slot at %u not empty\n", + header ? "header" : "data", index); } - index++; + /** + * We use same freed index as in completion to post so this + * operation is not required for refills. Its required only + * for ring creation. + */ + if (header) + pasync_sge = pasync_ctx->async_header.ring_base; + else + pasync_sge = pasync_ctx->async_data.ring_base; + pasync_sge += index; + /* if its a refill then address is same; hi is lo */ + WARN_ON(pasync_sge->hi && + pasync_sge->hi != pasync_handle->pa.u.a32.address_lo); + WARN_ON(pasync_sge->lo && + pasync_sge->lo != pasync_handle->pa.u.a32.address_hi); + pasync_sge->hi = pasync_handle->pa.u.a32.address_lo; + pasync_sge->lo = pasync_handle->pa.u.a32.address_hi; + + *slot = pasync_handle; + if (++prod == cons) + break; } + if (header) + pasync_ctx->async_header.free_entries -= prod; + else + pasync_ctx->async_data.free_entries -= prod; - status = beiscsi_process_async_pdu(beiscsi_conn, phba, - phdr, hdr_len, pfirst_buffer, - offset); - - hwi_free_async_msg(phba, pasync_ctx, cri); - return 0; -} - -static unsigned int -hwi_gather_async_pdu(struct beiscsi_conn *beiscsi_conn, - struct beiscsi_hba *phba, - struct async_pdu_handle *pasync_handle) -{ - struct hwi_async_pdu_context *pasync_ctx; - struct hwi_controller *phwi_ctrlr; - unsigned int bytes_needed = 0, status = 0; - unsigned short cri = pasync_handle->cri; - struct pdu_base *ppdu; - - phwi_ctrlr = phba->phwi_ctrlr; - pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr, - BEISCSI_GET_ULP_FROM_CRI(phwi_ctrlr, - BE_GET_CRI_FROM_CID(beiscsi_conn-> - beiscsi_conn_cid))); - - list_del(&pasync_handle->link); - if (pasync_handle->is_header) { - pasync_ctx->async_header.busy_entries--; - if (pasync_ctx->async_entry[cri].wait_queue.hdr_received) { - hwi_free_async_msg(phba, pasync_ctx, cri); - BUG(); - } - - pasync_ctx->async_entry[cri].wait_queue.bytes_received = 0; - pasync_ctx->async_entry[cri].wait_queue.hdr_received = 1; - pasync_ctx->async_entry[cri].wait_queue.hdr_len = - (unsigned short)pasync_handle->buffer_len; - list_add_tail(&pasync_handle->link, - &pasync_ctx->async_entry[cri].wait_queue.list); - - ppdu = pasync_handle->pbuffer; - bytes_needed = ((((ppdu->dw[offsetof(struct amap_pdu_base, - data_len_hi) / 32] & PDUBASE_DATALENHI_MASK) << 8) & - 0xFFFF0000) | ((be16_to_cpu((ppdu-> - dw[offsetof(struct amap_pdu_base, data_len_lo) / 32] - & PDUBASE_DATALENLO_MASK) >> 16)) & 0x0000FFFF)); - - if (status == 0) { - pasync_ctx->async_entry[cri].wait_queue.bytes_needed = - bytes_needed; - - if (bytes_needed == 0) - status = hwi_fwd_async_msg(beiscsi_conn, phba, - pasync_ctx, cri); - } - } else { - pasync_ctx->async_data.busy_entries--; - if (pasync_ctx->async_entry[cri].wait_queue.hdr_received) { - list_add_tail(&pasync_handle->link, - &pasync_ctx->async_entry[cri].wait_queue. - list); - pasync_ctx->async_entry[cri].wait_queue. - bytes_received += - (unsigned short)pasync_handle->buffer_len; - - if (pasync_ctx->async_entry[cri].wait_queue. - bytes_received >= - pasync_ctx->async_entry[cri].wait_queue. - bytes_needed) - status = hwi_fwd_async_msg(beiscsi_conn, phba, - pasync_ctx, cri); - } - } - return status; + doorbell |= ring_id & DB_DEF_PDU_RING_ID_MASK; + doorbell |= 1 << DB_DEF_PDU_REARM_SHIFT; + doorbell |= 0 << DB_DEF_PDU_EVENT_SHIFT; + doorbell |= (prod & DB_DEF_PDU_CQPROC_MASK) << DB_DEF_PDU_CQPROC_SHIFT; + iowrite32(doorbell, phba->db_va + doorbell_offset); } -static void hwi_process_default_pdu_ring(struct beiscsi_conn *beiscsi_conn, - struct beiscsi_hba *phba, - struct i_t_dpdu_cqe *pdpdu_cqe) +static void +beiscsi_hdq_process_compl(struct beiscsi_conn *beiscsi_conn, + struct i_t_dpdu_cqe *pdpdu_cqe) { + struct beiscsi_hba *phba = beiscsi_conn->phba; + struct hd_async_handle *pasync_handle = NULL; + struct hd_async_context *pasync_ctx; struct hwi_controller *phwi_ctrlr; - struct hwi_async_pdu_context *pasync_ctx; - struct async_pdu_handle *pasync_handle = NULL; - unsigned int cq_index = -1; - uint16_t cri_index = BE_GET_CRI_FROM_CID( - beiscsi_conn->beiscsi_conn_cid); + u16 cid_cri; + u8 ulp_num; phwi_ctrlr = phba->phwi_ctrlr; - pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr, - BEISCSI_GET_ULP_FROM_CRI(phwi_ctrlr, - cri_index)); - - pasync_handle = hwi_get_async_handle(phba, beiscsi_conn, pasync_ctx, - pdpdu_cqe, &cq_index); - - if (pasync_handle->consumed == 0) - hwi_update_async_writables(phba, pasync_ctx, - pasync_handle->is_header, cq_index); + cid_cri = BE_GET_CRI_FROM_CID(beiscsi_conn->beiscsi_conn_cid); + ulp_num = BEISCSI_GET_ULP_FROM_CRI(phwi_ctrlr, cid_cri); + pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr, ulp_num); + pasync_handle = beiscsi_hdl_get_handle(beiscsi_conn, pasync_ctx, + pdpdu_cqe); + if (!pasync_handle) + return; - hwi_gather_async_pdu(beiscsi_conn, phba, pasync_handle); - hwi_post_async_buffers(phba, pasync_handle->is_header, - BEISCSI_GET_ULP_FROM_CRI( - phwi_ctrlr, cri_index)); + beiscsi_hdl_gather_pdu(beiscsi_conn, pasync_ctx, pasync_handle); + beiscsi_hdq_post_handles(phba, pasync_handle->is_header, ulp_num); } void beiscsi_process_mcc_cq(struct beiscsi_hba *phba) @@ -2051,6 +1816,9 @@ void beiscsi_process_mcc_cq(struct beiscsi_hba *phba) mcc_compl = queue_tail_node(mcc_cq); mcc_compl->flags = le32_to_cpu(mcc_compl->flags); while (mcc_compl->flags & CQE_FLAGS_VALID_MASK) { + if (beiscsi_hba_in_error(phba)) + return; + if (num_processed >= 32) { hwi_ring_cq_db(phba, mcc_cq->id, num_processed, 0); @@ -2073,6 +1841,19 @@ void beiscsi_process_mcc_cq(struct beiscsi_hba *phba) hwi_ring_cq_db(phba, mcc_cq->id, num_processed, 1); } +static void beiscsi_mcc_work(struct work_struct *work) +{ + struct be_eq_obj *pbe_eq; + struct beiscsi_hba *phba; + + pbe_eq = container_of(work, struct be_eq_obj, mcc_work); + phba = pbe_eq->phba; + beiscsi_process_mcc_cq(phba); + /* rearm EQ for further interrupts */ + if (!beiscsi_hba_in_error(phba)) + hwi_ring_eq_db(phba, pbe_eq->q.id, 0, 0, 1, 1); +} + /** * beiscsi_process_cq()- Process the Completion Queue * @pbe_eq: Event Q on which the Completion has come @@ -2101,6 +1882,9 @@ unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq, int budget) while (sol->dw[offsetof(struct amap_sol_cqe, valid) / 32] & CQE_VALID_MASK) { + if (beiscsi_hba_in_error(phba)) + return 0; + be_dws_le_to_cpu(sol, sizeof(struct sol_cqe)); code = (sol->dw[offsetof(struct amap_sol_cqe, code) / @@ -2165,8 +1949,8 @@ unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq, int budget) cqe_desc[code], code, cid); spin_lock_bh(&phba->async_pdu_lock); - hwi_process_default_pdu_ring(beiscsi_conn, phba, - (struct i_t_dpdu_cqe *)sol); + beiscsi_hdq_process_compl(beiscsi_conn, + (struct i_t_dpdu_cqe *)sol); spin_unlock_bh(&phba->async_pdu_lock); break; case UNSOL_DATA_NOTIFY: @@ -2176,8 +1960,8 @@ unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq, int budget) cqe_desc[code], code, cid); spin_lock_bh(&phba->async_pdu_lock); - hwi_process_default_pdu_ring(beiscsi_conn, phba, - (struct i_t_dpdu_cqe *)sol); + beiscsi_hdq_process_compl(beiscsi_conn, + (struct i_t_dpdu_cqe *)sol); spin_unlock_bh(&phba->async_pdu_lock); break; case CXN_INVALIDATE_INDEX_NOTIFY: @@ -2213,8 +1997,9 @@ unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq, int budget) "BM_%d : Dropping %s[%d] on DPDU ring on CID : %d\n", cqe_desc[code], code, cid); spin_lock_bh(&phba->async_pdu_lock); - hwi_flush_default_pdu_buffer(phba, beiscsi_conn, - (struct i_t_dpdu_cqe *) sol); + /* driver consumes the entry and drops the contents */ + beiscsi_hdq_process_compl(beiscsi_conn, + (struct i_t_dpdu_cqe *)sol); spin_unlock_bh(&phba->async_pdu_lock); break; case CXN_KILLED_PDU_SIZE_EXCEEDS_DSL: @@ -2262,60 +2047,32 @@ proc_next_cqe: return total; } -void beiscsi_process_all_cqs(struct work_struct *work) -{ - unsigned long flags; - struct hwi_controller *phwi_ctrlr; - struct hwi_context_memory *phwi_context; - struct beiscsi_hba *phba; - struct be_eq_obj *pbe_eq = - container_of(work, struct be_eq_obj, work_cqs); - - phba = pbe_eq->phba; - phwi_ctrlr = phba->phwi_ctrlr; - phwi_context = phwi_ctrlr->phwi_ctxt; - - if (pbe_eq->todo_mcc_cq) { - spin_lock_irqsave(&phba->isr_lock, flags); - pbe_eq->todo_mcc_cq = false; - spin_unlock_irqrestore(&phba->isr_lock, flags); - beiscsi_process_mcc_cq(phba); - } - - if (pbe_eq->todo_cq) { - spin_lock_irqsave(&phba->isr_lock, flags); - pbe_eq->todo_cq = false; - spin_unlock_irqrestore(&phba->isr_lock, flags); - beiscsi_process_cq(pbe_eq, BE2_MAX_NUM_CQ_PROC); - } - - /* rearm EQ for further interrupts */ - hwi_ring_eq_db(phba, pbe_eq->q.id, 0, 0, 1, 1); -} - static int be_iopoll(struct irq_poll *iop, int budget) { - unsigned int ret, num_eq_processed; + unsigned int ret, io_events; struct beiscsi_hba *phba; struct be_eq_obj *pbe_eq; struct be_eq_entry *eqe = NULL; struct be_queue_info *eq; - num_eq_processed = 0; pbe_eq = container_of(iop, struct be_eq_obj, iopoll); phba = pbe_eq->phba; + if (beiscsi_hba_in_error(phba)) { + irq_poll_complete(iop); + return 0; + } + + io_events = 0; eq = &pbe_eq->q; eqe = queue_tail_node(eq); - while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] & EQE_VALID_MASK) { AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); queue_tail_inc(eq); eqe = queue_tail_node(eq); - num_eq_processed++; + io_events++; } - - hwi_ring_eq_db(phba, eq->id, 1, num_eq_processed, 0, 1); + hwi_ring_eq_db(phba, eq->id, 1, io_events, 0, 1); ret = beiscsi_process_cq(pbe_eq, budget); pbe_eq->cq_count += ret; @@ -2325,7 +2082,8 @@ static int be_iopoll(struct irq_poll *iop, int budget) BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO, "BM_%d : rearm pbe_eq->q.id =%d ret %d\n", pbe_eq->q.id, ret); - hwi_ring_eq_db(phba, pbe_eq->q.id, 0, 0, 1, 1); + if (!beiscsi_hba_in_error(phba)) + hwi_ring_eq_db(phba, pbe_eq->q.id, 0, 0, 1, 1); } return ret; } @@ -2691,20 +2449,20 @@ static void beiscsi_find_mem_req(struct beiscsi_hba *phba) (ulp_num * MEM_DESCR_OFFSET)); phba->mem_req[mem_descr_index] = BEISCSI_GET_CID_COUNT(phba, ulp_num) * - sizeof(struct async_pdu_handle); + sizeof(struct hd_async_handle); mem_descr_index = (HWI_MEM_ASYNC_DATA_HANDLE_ULP0 + (ulp_num * MEM_DESCR_OFFSET)); phba->mem_req[mem_descr_index] = BEISCSI_GET_CID_COUNT(phba, ulp_num) * - sizeof(struct async_pdu_handle); + sizeof(struct hd_async_handle); mem_descr_index = (HWI_MEM_ASYNC_PDU_CONTEXT_ULP0 + (ulp_num * MEM_DESCR_OFFSET)); phba->mem_req[mem_descr_index] = - sizeof(struct hwi_async_pdu_context) + + sizeof(struct hd_async_context) + (BEISCSI_GET_CID_COUNT(phba, ulp_num) * - sizeof(struct hwi_async_entry)); + sizeof(struct hd_async_entry)); } } } @@ -2963,35 +2721,34 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) uint8_t ulp_num; struct hwi_controller *phwi_ctrlr; struct hba_parameters *p = &phba->params; - struct hwi_async_pdu_context *pasync_ctx; - struct async_pdu_handle *pasync_header_h, *pasync_data_h; + struct hd_async_context *pasync_ctx; + struct hd_async_handle *pasync_header_h, *pasync_data_h; unsigned int index, idx, num_per_mem, num_async_data; struct be_mem_descriptor *mem_descr; for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) { if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) { - + /* get async_ctx for each ULP */ mem_descr = (struct be_mem_descriptor *)phba->init_mem; mem_descr += (HWI_MEM_ASYNC_PDU_CONTEXT_ULP0 + (ulp_num * MEM_DESCR_OFFSET)); phwi_ctrlr = phba->phwi_ctrlr; phwi_ctrlr->phwi_ctxt->pasync_ctx[ulp_num] = - (struct hwi_async_pdu_context *) + (struct hd_async_context *) mem_descr->mem_array[0].virtual_address; pasync_ctx = phwi_ctrlr->phwi_ctxt->pasync_ctx[ulp_num]; memset(pasync_ctx, 0, sizeof(*pasync_ctx)); pasync_ctx->async_entry = - (struct hwi_async_entry *) + (struct hd_async_entry *) ((long unsigned int)pasync_ctx + - sizeof(struct hwi_async_pdu_context)); + sizeof(struct hd_async_context)); pasync_ctx->num_entries = BEISCSI_GET_CID_COUNT(phba, ulp_num); - pasync_ctx->buffer_size = p->defpdu_hdr_sz; - + /* setup header buffers */ mem_descr = (struct be_mem_descriptor *)phba->init_mem; mem_descr += HWI_MEM_ASYNC_HEADER_BUF_ULP0 + (ulp_num * MEM_DESCR_OFFSET); @@ -3008,6 +2765,7 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) "BM_%d : No Virtual address for ULP : %d\n", ulp_num); + pasync_ctx->async_header.buffer_size = p->defpdu_hdr_sz; pasync_ctx->async_header.va_base = mem_descr->mem_array[0].virtual_address; @@ -3015,6 +2773,7 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) mem_descr->mem_array[0]. bus_address.u.a64.address; + /* setup header buffer sgls */ mem_descr = (struct be_mem_descriptor *)phba->init_mem; mem_descr += HWI_MEM_ASYNC_HEADER_RING_ULP0 + (ulp_num * MEM_DESCR_OFFSET); @@ -3034,6 +2793,7 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) pasync_ctx->async_header.ring_base = mem_descr->mem_array[0].virtual_address; + /* setup header buffer handles */ mem_descr = (struct be_mem_descriptor *)phba->init_mem; mem_descr += HWI_MEM_ASYNC_HEADER_HANDLE_ULP0 + (ulp_num * MEM_DESCR_OFFSET); @@ -3052,9 +2812,9 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) pasync_ctx->async_header.handle_base = mem_descr->mem_array[0].virtual_address; - pasync_ctx->async_header.writables = 0; INIT_LIST_HEAD(&pasync_ctx->async_header.free_list); + /* setup data buffer sgls */ mem_descr = (struct be_mem_descriptor *)phba->init_mem; mem_descr += HWI_MEM_ASYNC_DATA_RING_ULP0 + (ulp_num * MEM_DESCR_OFFSET); @@ -3074,6 +2834,7 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) pasync_ctx->async_data.ring_base = mem_descr->mem_array[0].virtual_address; + /* setup data buffer handles */ mem_descr = (struct be_mem_descriptor *)phba->init_mem; mem_descr += HWI_MEM_ASYNC_DATA_HANDLE_ULP0 + (ulp_num * MEM_DESCR_OFFSET); @@ -3085,16 +2846,16 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) pasync_ctx->async_data.handle_base = mem_descr->mem_array[0].virtual_address; - pasync_ctx->async_data.writables = 0; INIT_LIST_HEAD(&pasync_ctx->async_data.free_list); pasync_header_h = - (struct async_pdu_handle *) + (struct hd_async_handle *) pasync_ctx->async_header.handle_base; pasync_data_h = - (struct async_pdu_handle *) + (struct hd_async_handle *) pasync_ctx->async_data.handle_base; + /* setup data buffers */ mem_descr = (struct be_mem_descriptor *)phba->init_mem; mem_descr += HWI_MEM_ASYNC_DATA_BUF_ULP0 + (ulp_num * MEM_DESCR_OFFSET); @@ -3112,6 +2873,7 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) ulp_num); idx = 0; + pasync_ctx->async_data.buffer_size = p->defpdu_data_sz; pasync_ctx->async_data.va_base = mem_descr->mem_array[idx].virtual_address; pasync_ctx->async_data.pa_base.u.a64.address = @@ -3125,7 +2887,8 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) for (index = 0; index < BEISCSI_GET_CID_COUNT (phba, ulp_num); index++) { pasync_header_h->cri = -1; - pasync_header_h->index = (char)index; + pasync_header_h->is_header = 1; + pasync_header_h->index = index; INIT_LIST_HEAD(&pasync_header_h->link); pasync_header_h->pbuffer = (void *)((unsigned long) @@ -3142,14 +2905,13 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) free_list); pasync_header_h++; pasync_ctx->async_header.free_entries++; - pasync_ctx->async_header.writables++; - INIT_LIST_HEAD(&pasync_ctx->async_entry[index]. - wait_queue.list); - INIT_LIST_HEAD(&pasync_ctx->async_entry[index]. - header_busy_list); + wq.list); + pasync_ctx->async_entry[index].header = NULL; + pasync_data_h->cri = -1; - pasync_data_h->index = (char)index; + pasync_data_h->is_header = 0; + pasync_data_h->index = index; INIT_LIST_HEAD(&pasync_data_h->link); if (!num_async_data) { @@ -3184,16 +2946,8 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) free_list); pasync_data_h++; pasync_ctx->async_data.free_entries++; - pasync_ctx->async_data.writables++; - - INIT_LIST_HEAD(&pasync_ctx->async_entry[index]. - data_busy_list); + pasync_ctx->async_entry[index].data = NULL; } - - pasync_ctx->async_header.host_write_ptr = 0; - pasync_ctx->async_header.ep_read_ptr = -1; - pasync_ctx->async_data.host_write_ptr = 0; - pasync_ctx->async_data.ep_read_ptr = -1; } } @@ -3265,8 +3019,8 @@ static int be_fill_queue(struct be_queue_info *q, static int beiscsi_create_eqs(struct beiscsi_hba *phba, struct hwi_context_memory *phwi_context) { + int ret = -ENOMEM, eq_for_mcc; unsigned int i, num_eq_pages; - int ret = 0, eq_for_mcc; struct be_queue_info *eq; struct be_dma_mem *mem; void *eq_vaddress; @@ -3284,8 +3038,8 @@ static int beiscsi_create_eqs(struct beiscsi_hba *phba, mem = &eq->dma_mem; phwi_context->be_eq[i].phba = phba; eq_vaddress = pci_alloc_consistent(phba->pcidev, - num_eq_pages * PAGE_SIZE, - &paddr); + num_eq_pages * PAGE_SIZE, + &paddr); if (!eq_vaddress) goto create_eq_error; @@ -3313,6 +3067,7 @@ static int beiscsi_create_eqs(struct beiscsi_hba *phba, phwi_context->be_eq[i].q.id); } return 0; + create_eq_error: for (i = 0; i < (phba->num_cpus + eq_for_mcc); i++) { eq = &phwi_context->be_eq[i].q; @@ -3329,11 +3084,11 @@ static int beiscsi_create_cqs(struct beiscsi_hba *phba, struct hwi_context_memory *phwi_context) { unsigned int i, num_cq_pages; - int ret = 0; struct be_queue_info *cq, *eq; struct be_dma_mem *mem; struct be_eq_obj *pbe_eq; void *cq_vaddress; + int ret = -ENOMEM; dma_addr_t paddr; num_cq_pages = PAGES_REQUIRED(phba->params.num_cq_entries * \ @@ -3347,10 +3102,11 @@ static int beiscsi_create_cqs(struct beiscsi_hba *phba, pbe_eq->phba = phba; mem = &cq->dma_mem; cq_vaddress = pci_alloc_consistent(phba->pcidev, - num_cq_pages * PAGE_SIZE, - &paddr); + num_cq_pages * PAGE_SIZE, + &paddr); if (!cq_vaddress) goto create_cq_error; + ret = be_fill_queue(cq, phba->params.num_cq_entries, sizeof(struct sol_cqe), cq_vaddress); if (ret) { @@ -3385,7 +3141,6 @@ create_cq_error: mem->va, mem->dma); } return ret; - } static int @@ -3437,7 +3192,6 @@ beiscsi_create_def_hdr(struct beiscsi_hba *phba, "BM_%d : iscsi hdr def pdu id for ULP : %d is %d\n", ulp_num, phwi_context->be_def_hdrq[ulp_num].id); - hwi_post_async_buffers(phba, BEISCSI_DEFQ_HDR, ulp_num); return 0; } @@ -3492,11 +3246,9 @@ beiscsi_create_def_data(struct beiscsi_hba *phba, ulp_num, phwi_context->be_def_dataq[ulp_num].id); - hwi_post_async_buffers(phba, BEISCSI_DEFQ_DATA, ulp_num); beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, "BM_%d : DEFAULT PDU DATA RING CREATED" "on ULP : %d\n", ulp_num); - return 0; } @@ -3716,10 +3468,53 @@ static void free_wrb_handles(struct beiscsi_hba *phba) static void be_mcc_queues_destroy(struct beiscsi_hba *phba) { - struct be_queue_info *q; struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_dma_mem *ptag_mem; + struct be_queue_info *q; + int i, tag; q = &phba->ctrl.mcc_obj.q; + for (i = 0; i < MAX_MCC_CMD; i++) { + tag = i + 1; + if (!test_bit(MCC_TAG_STATE_RUNNING, + &ctrl->ptag_state[tag].tag_state)) + continue; + + if (test_bit(MCC_TAG_STATE_TIMEOUT, + &ctrl->ptag_state[tag].tag_state)) { + ptag_mem = &ctrl->ptag_state[tag].tag_mem_state; + if (ptag_mem->size) { + pci_free_consistent(ctrl->pdev, + ptag_mem->size, + ptag_mem->va, + ptag_mem->dma); + ptag_mem->size = 0; + } + continue; + } + /** + * If MCC is still active and waiting then wake up the process. + * We are here only because port is going offline. The process + * sees that (BEISCSI_HBA_ONLINE is cleared) and EIO error is + * returned for the operation and allocated memory cleaned up. + */ + if (waitqueue_active(&ctrl->mcc_wait[tag])) { + ctrl->mcc_tag_status[tag] = MCC_STATUS_FAILED; + ctrl->mcc_tag_status[tag] |= CQE_VALID_MASK; + wake_up_interruptible(&ctrl->mcc_wait[tag]); + /* + * Control tag info gets reinitialized in enable + * so wait for the process to clear running state. + */ + while (test_bit(MCC_TAG_STATE_RUNNING, + &ctrl->ptag_state[tag].tag_state)) + schedule_timeout_uninterruptible(HZ); + } + /** + * For MCC with tag_states MCC_TAG_STATE_ASYNC and + * MCC_TAG_STATE_IGNORE nothing needs to done. + */ + } if (q->created) { beiscsi_cmd_q_destroy(ctrl, q, QTYPE_MCCQ); be_queue_free(phba, q); @@ -3732,68 +3527,6 @@ static void be_mcc_queues_destroy(struct beiscsi_hba *phba) } } -static void hwi_cleanup(struct beiscsi_hba *phba) -{ - struct be_queue_info *q; - struct be_ctrl_info *ctrl = &phba->ctrl; - struct hwi_controller *phwi_ctrlr; - struct hwi_context_memory *phwi_context; - struct hwi_async_pdu_context *pasync_ctx; - int i, eq_for_mcc, ulp_num; - - phwi_ctrlr = phba->phwi_ctrlr; - phwi_context = phwi_ctrlr->phwi_ctxt; - - be_cmd_iscsi_remove_template_hdr(ctrl); - - for (i = 0; i < phba->params.cxns_per_ctrl; i++) { - q = &phwi_context->be_wrbq[i]; - if (q->created) - beiscsi_cmd_q_destroy(ctrl, q, QTYPE_WRBQ); - } - kfree(phwi_context->be_wrbq); - free_wrb_handles(phba); - - for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) { - if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) { - - q = &phwi_context->be_def_hdrq[ulp_num]; - if (q->created) - beiscsi_cmd_q_destroy(ctrl, q, QTYPE_DPDUQ); - - q = &phwi_context->be_def_dataq[ulp_num]; - if (q->created) - beiscsi_cmd_q_destroy(ctrl, q, QTYPE_DPDUQ); - - pasync_ctx = phwi_ctrlr->phwi_ctxt->pasync_ctx[ulp_num]; - } - } - - beiscsi_cmd_q_destroy(ctrl, NULL, QTYPE_SGL); - - for (i = 0; i < (phba->num_cpus); i++) { - q = &phwi_context->be_cq[i]; - if (q->created) { - be_queue_free(phba, q); - beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ); - } - } - - be_mcc_queues_destroy(phba); - if (phba->msix_enabled) - eq_for_mcc = 1; - else - eq_for_mcc = 0; - for (i = 0; i < (phba->num_cpus + eq_for_mcc); i++) { - q = &phwi_context->be_eq[i].q; - if (q->created) { - be_queue_free(phba, q); - beiscsi_cmd_q_destroy(ctrl, q, QTYPE_EQ); - } - } - be_cmd_fw_uninit(ctrl); -} - static int be_mcc_queues_create(struct beiscsi_hba *phba, struct hwi_context_memory *phwi_context) { @@ -3875,6 +3608,118 @@ static void find_num_cpus(struct beiscsi_hba *phba) } } +static void hwi_purge_eq(struct beiscsi_hba *phba) +{ + struct hwi_controller *phwi_ctrlr; + struct hwi_context_memory *phwi_context; + struct be_queue_info *eq; + struct be_eq_entry *eqe = NULL; + int i, eq_msix; + unsigned int num_processed; + + if (beiscsi_hba_in_error(phba)) + return; + + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; + if (phba->msix_enabled) + eq_msix = 1; + else + eq_msix = 0; + + for (i = 0; i < (phba->num_cpus + eq_msix); i++) { + eq = &phwi_context->be_eq[i].q; + eqe = queue_tail_node(eq); + num_processed = 0; + while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] + & EQE_VALID_MASK) { + AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); + queue_tail_inc(eq); + eqe = queue_tail_node(eq); + num_processed++; + } + + if (num_processed) + hwi_ring_eq_db(phba, eq->id, 1, num_processed, 1, 1); + } +} + +static void hwi_cleanup_port(struct beiscsi_hba *phba) +{ + struct be_queue_info *q; + struct be_ctrl_info *ctrl = &phba->ctrl; + struct hwi_controller *phwi_ctrlr; + struct hwi_context_memory *phwi_context; + struct hd_async_context *pasync_ctx; + int i, eq_for_mcc, ulp_num; + + for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) + if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) + beiscsi_cmd_iscsi_cleanup(phba, ulp_num); + + /** + * Purge all EQ entries that may have been left out. This is to + * workaround a problem we've seen occasionally where driver gets an + * interrupt with EQ entry bit set after stopping the controller. + */ + hwi_purge_eq(phba); + + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; + + be_cmd_iscsi_remove_template_hdr(ctrl); + + for (i = 0; i < phba->params.cxns_per_ctrl; i++) { + q = &phwi_context->be_wrbq[i]; + if (q->created) + beiscsi_cmd_q_destroy(ctrl, q, QTYPE_WRBQ); + } + kfree(phwi_context->be_wrbq); + free_wrb_handles(phba); + + for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) { + if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) { + + q = &phwi_context->be_def_hdrq[ulp_num]; + if (q->created) + beiscsi_cmd_q_destroy(ctrl, q, QTYPE_DPDUQ); + + q = &phwi_context->be_def_dataq[ulp_num]; + if (q->created) + beiscsi_cmd_q_destroy(ctrl, q, QTYPE_DPDUQ); + + pasync_ctx = phwi_ctrlr->phwi_ctxt->pasync_ctx[ulp_num]; + } + } + + beiscsi_cmd_q_destroy(ctrl, NULL, QTYPE_SGL); + + for (i = 0; i < (phba->num_cpus); i++) { + q = &phwi_context->be_cq[i]; + if (q->created) { + be_queue_free(phba, q); + beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ); + } + } + + be_mcc_queues_destroy(phba); + if (phba->msix_enabled) + eq_for_mcc = 1; + else + eq_for_mcc = 0; + for (i = 0; i < (phba->num_cpus + eq_for_mcc); i++) { + q = &phwi_context->be_eq[i].q; + if (q->created) { + be_queue_free(phba, q); + beiscsi_cmd_q_destroy(ctrl, q, QTYPE_EQ); + } + } + /* this ensures complete FW cleanup */ + beiscsi_cmd_function_reset(phba); + /* last communication, indicate driver is unloading */ + beiscsi_cmd_special_wrb(&phba->ctrl, 0); +} + static int hwi_init_port(struct beiscsi_hba *phba) { struct hwi_controller *phwi_ctrlr; @@ -3887,9 +3732,8 @@ static int hwi_init_port(struct beiscsi_hba *phba) phwi_context = phwi_ctrlr->phwi_ctxt; phwi_context->max_eqd = 128; phwi_context->min_eqd = 0; - phwi_context->cur_eqd = 0; - be_cmd_fw_initialize(&phba->ctrl); - /* set optic state to unknown */ + phwi_context->cur_eqd = 32; + /* set port optic state to unknown */ phba->optic_state = 0xff; status = beiscsi_create_eqs(phba, phwi_context); @@ -3903,7 +3747,7 @@ static int hwi_init_port(struct beiscsi_hba *phba) if (status != 0) goto error; - status = mgmt_check_supported_fw(ctrl, phba); + status = beiscsi_check_supported_fw(ctrl, phba); if (status != 0) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, "BM_%d : Unsupported fw version\n"); @@ -3919,7 +3763,6 @@ static int hwi_init_port(struct beiscsi_hba *phba) for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) { if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) { - def_pdu_ring_sz = BEISCSI_GET_CID_COUNT(phba, ulp_num) * sizeof(struct phys_addr); @@ -3945,6 +3788,15 @@ static int hwi_init_port(struct beiscsi_hba *phba) ulp_num); goto error; } + /** + * Now that the default PDU rings have been created, + * let EP know about it. + * Call beiscsi_cmd_iscsi_cleanup before posting? + */ + beiscsi_hdq_post_handles(phba, BEISCSI_DEFQ_HDR, + ulp_num); + beiscsi_hdq_post_handles(phba, BEISCSI_DEFQ_DATA, + ulp_num); } } @@ -3973,7 +3825,7 @@ static int hwi_init_port(struct beiscsi_hba *phba) if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) { uint16_t cri = 0; - struct hwi_async_pdu_context *pasync_ctx; + struct hd_async_context *pasync_ctx; pasync_ctx = HWI_GET_ASYNC_PDU_CTX( phwi_ctrlr, ulp_num); @@ -3985,6 +3837,14 @@ static int hwi_init_port(struct beiscsi_hba *phba) phwi_ctrlr->wrb_context[cri].cid] = async_arr_idx++; } + /** + * Now that the default PDU rings have been created, + * let EP know about it. + */ + beiscsi_hdq_post_handles(phba, BEISCSI_DEFQ_HDR, + ulp_num); + beiscsi_hdq_post_handles(phba, BEISCSI_DEFQ_DATA, + ulp_num); } } @@ -3995,7 +3855,7 @@ static int hwi_init_port(struct beiscsi_hba *phba) error: beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, "BM_%d : hwi_init_port failed"); - hwi_cleanup(phba); + hwi_cleanup_port(phba); return status; } @@ -4354,149 +4214,6 @@ static void hwi_disable_intr(struct beiscsi_hba *phba) "BM_%d : In hwi_disable_intr, Already Disabled\n"); } -/** - * beiscsi_get_boot_info()- Get the boot session info - * @phba: The device priv structure instance - * - * Get the boot target info and store in driver priv structure - * - * return values - * Success: 0 - * Failure: Non-Zero Value - **/ -static int beiscsi_get_boot_info(struct beiscsi_hba *phba) -{ - struct be_cmd_get_session_resp *session_resp; - struct be_dma_mem nonemb_cmd; - unsigned int tag; - unsigned int s_handle; - int ret = -ENOMEM; - - /* Get the session handle of the boot target */ - ret = be_mgmt_get_boot_shandle(phba, &s_handle); - if (ret) { - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, - "BM_%d : No boot session\n"); - - if (ret == -ENXIO) - phba->get_boot = 0; - - - return ret; - } - phba->get_boot = 0; - nonemb_cmd.va = pci_zalloc_consistent(phba->ctrl.pdev, - sizeof(*session_resp), - &nonemb_cmd.dma); - if (nonemb_cmd.va == NULL) { - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, - "BM_%d : Failed to allocate memory for" - "beiscsi_get_session_info\n"); - - return -ENOMEM; - } - - tag = mgmt_get_session_info(phba, s_handle, - &nonemb_cmd); - if (!tag) { - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, - "BM_%d : beiscsi_get_session_info" - " Failed\n"); - - goto boot_freemem; - } - - ret = beiscsi_mccq_compl_wait(phba, tag, NULL, &nonemb_cmd); - if (ret) { - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, - "BM_%d : beiscsi_get_session_info Failed"); - - if (ret != -EBUSY) - goto boot_freemem; - else - return ret; - } - - session_resp = nonemb_cmd.va ; - - memcpy(&phba->boot_sess, &session_resp->session_info, - sizeof(struct mgmt_session_info)); - - beiscsi_logout_fw_sess(phba, - phba->boot_sess.session_handle); - ret = 0; - -boot_freemem: - pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size, - nonemb_cmd.va, nonemb_cmd.dma); - return ret; -} - -static void beiscsi_boot_release(void *data) -{ - struct beiscsi_hba *phba = data; - - scsi_host_put(phba->shost); -} - -static int beiscsi_setup_boot_info(struct beiscsi_hba *phba) -{ - struct iscsi_boot_kobj *boot_kobj; - - /* it has been created previously */ - if (phba->boot_kset) - return 0; - - /* get boot info using mgmt cmd */ - if (beiscsi_get_boot_info(phba)) - /* Try to see if we can carry on without this */ - return 0; - - phba->boot_kset = iscsi_boot_create_host_kset(phba->shost->host_no); - if (!phba->boot_kset) - return -ENOMEM; - - /* get a ref because the show function will ref the phba */ - if (!scsi_host_get(phba->shost)) - goto free_kset; - boot_kobj = iscsi_boot_create_target(phba->boot_kset, 0, phba, - beiscsi_show_boot_tgt_info, - beiscsi_tgt_get_attr_visibility, - beiscsi_boot_release); - if (!boot_kobj) - goto put_shost; - - if (!scsi_host_get(phba->shost)) - goto free_kset; - boot_kobj = iscsi_boot_create_initiator(phba->boot_kset, 0, phba, - beiscsi_show_boot_ini_info, - beiscsi_ini_get_attr_visibility, - beiscsi_boot_release); - if (!boot_kobj) - goto put_shost; - - if (!scsi_host_get(phba->shost)) - goto free_kset; - boot_kobj = iscsi_boot_create_ethernet(phba->boot_kset, 0, phba, - beiscsi_show_boot_eth_info, - beiscsi_eth_get_attr_visibility, - beiscsi_boot_release); - if (!boot_kobj) - goto put_shost; - return 0; - -put_shost: - scsi_host_put(phba->shost); -free_kset: - iscsi_boot_destroy_kset(phba->boot_kset); - phba->boot_kset = NULL; - return -ENOMEM; -} - static int beiscsi_init_port(struct beiscsi_hba *phba) { int ret; @@ -4516,7 +4233,8 @@ static int beiscsi_init_port(struct beiscsi_hba *phba) goto do_cleanup_ctrlr; } - if (hba_setup_cid_tbls(phba)) { + ret = hba_setup_cid_tbls(phba); + if (ret < 0) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, "BM_%d : Failed in hba_setup_cid_tbls\n"); kfree(phba->io_sgl_hndl_base); @@ -4527,61 +4245,15 @@ static int beiscsi_init_port(struct beiscsi_hba *phba) return ret; do_cleanup_ctrlr: - hwi_cleanup(phba); + hwi_cleanup_port(phba); return ret; } -static void hwi_purge_eq(struct beiscsi_hba *phba) -{ - struct hwi_controller *phwi_ctrlr; - struct hwi_context_memory *phwi_context; - struct be_queue_info *eq; - struct be_eq_entry *eqe = NULL; - int i, eq_msix; - unsigned int num_processed; - - phwi_ctrlr = phba->phwi_ctrlr; - phwi_context = phwi_ctrlr->phwi_ctxt; - if (phba->msix_enabled) - eq_msix = 1; - else - eq_msix = 0; - - for (i = 0; i < (phba->num_cpus + eq_msix); i++) { - eq = &phwi_context->be_eq[i].q; - eqe = queue_tail_node(eq); - num_processed = 0; - while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] - & EQE_VALID_MASK) { - AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); - queue_tail_inc(eq); - eqe = queue_tail_node(eq); - num_processed++; - } - - if (num_processed) - hwi_ring_eq_db(phba, eq->id, 1, num_processed, 1, 1); - } -} - -static void beiscsi_clean_port(struct beiscsi_hba *phba) +static void beiscsi_cleanup_port(struct beiscsi_hba *phba) { - int mgmt_status, ulp_num; struct ulp_cid_info *ptr_cid_info = NULL; + int ulp_num; - for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) { - if (test_bit(ulp_num, (void *)&phba->fw_config.ulp_supported)) { - mgmt_status = mgmt_epfw_cleanup(phba, ulp_num); - if (mgmt_status) - beiscsi_log(phba, KERN_WARNING, - BEISCSI_LOG_INIT, - "BM_%d : mgmt_epfw_cleanup FAILED" - " for ULP_%d\n", ulp_num); - } - } - - hwi_purge_eq(phba); - hwi_cleanup(phba); kfree(phba->io_sgl_hndl_base); kfree(phba->eh_sgl_hndl_base); kfree(phba->ep_array); @@ -4598,7 +4270,6 @@ static void beiscsi_clean_port(struct beiscsi_hba *phba) } } } - } /** @@ -4625,16 +4296,12 @@ beiscsi_free_mgmt_task_handles(struct beiscsi_conn *beiscsi_conn, io_task = task->dd_data; if (io_task->pwrb_handle) { - memset(io_task->pwrb_handle->pwrb, 0, - sizeof(struct iscsi_wrb)); - free_wrb_handle(phba, pwrb_context, - io_task->pwrb_handle); + free_wrb_handle(phba, pwrb_context, io_task->pwrb_handle); io_task->pwrb_handle = NULL; } if (io_task->psgl_handle) { - free_mgmt_sgl_handle(phba, - io_task->psgl_handle); + free_mgmt_sgl_handle(phba, io_task->psgl_handle); io_task->psgl_handle = NULL; } @@ -4671,6 +4338,7 @@ static void beiscsi_cleanup_task(struct iscsi_task *task) pci_pool_free(beiscsi_sess->bhs_pool, io_task->cmd_bhs, io_task->bhs_pa.u.a64.address); io_task->cmd_bhs = NULL; + task->hdr = NULL; } if (task->sc) { @@ -4686,7 +4354,8 @@ static void beiscsi_cleanup_task(struct iscsi_task *task) } if (io_task->scsi_cmnd) { - scsi_dma_unmap(io_task->scsi_cmnd); + if (io_task->num_sg) + scsi_dma_unmap(io_task->scsi_cmnd); io_task->scsi_cmnd = NULL; } } else { @@ -5051,7 +4720,6 @@ static int beiscsi_mtask(struct iscsi_task *task) cid = beiscsi_conn->beiscsi_conn_cid; pwrb = io_task->pwrb_handle->pwrb; - memset(pwrb, 0, sizeof(*pwrb)); if (is_chip_be2_be3r(phba)) { AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb, @@ -5165,6 +4833,15 @@ static int beiscsi_task_xmit(struct iscsi_task *task) int num_sg; unsigned int writedir = 0, xferlen = 0; + phba = io_task->conn->phba; + /** + * HBA in error includes BEISCSI_HBA_FW_TIMEOUT. IO path might be + * operational if FW still gets heartbeat from EP FW. Is management + * path really needed to continue further? + */ + if (!beiscsi_hba_is_online(phba)) + return -EIO; + if (!io_task->conn->login_in_progress) task->hdr->exp_statsn = 0; @@ -5172,8 +4849,8 @@ static int beiscsi_task_xmit(struct iscsi_task *task) return beiscsi_mtask(task); io_task->scsi_cmnd = sc; + io_task->num_sg = 0; num_sg = scsi_dma_map(sc); - phba = io_task->conn->phba; if (num_sg < 0) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_IO | BEISCSI_LOG_ISCSI, @@ -5184,6 +4861,11 @@ static int beiscsi_task_xmit(struct iscsi_task *task) return num_sg; } + /** + * For scsi cmd task, check num_sg before unmapping in cleanup_task. + * For management task, cleanup_task checks mtask_addr before unmapping. + */ + io_task->num_sg = num_sg; xferlen = scsi_bufflen(sc); sg = scsi_sglist(sc); if (sc->sc_data_direction == DMA_TO_DEVICE) @@ -5213,6 +4895,12 @@ static int beiscsi_bsg_request(struct bsg_job *job) shost = iscsi_job_to_shost(job); phba = iscsi_host_priv(shost); + if (!beiscsi_hba_is_online(phba)) { + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, + "BM_%d : HBA in error 0x%lx\n", phba->state); + return -ENXIO; + } + switch (bsg_req->msgcode) { case ISCSI_BSG_HST_VENDOR: nonemb_cmd.va = pci_alloc_consistent(phba->ctrl.pdev, @@ -5240,6 +4928,14 @@ static int beiscsi_bsg_request(struct bsg_job *job) phba->ctrl.mcc_tag_status[tag], msecs_to_jiffies( BEISCSI_HOST_MBX_TIMEOUT)); + + if (!test_bit(BEISCSI_HBA_ONLINE, &phba->state)) { + clear_bit(MCC_TAG_STATE_RUNNING, + &phba->ctrl.ptag_state[tag].tag_state); + pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size, + nonemb_cmd.va, nonemb_cmd.dma); + return -EIO; + } extd_status = (phba->ctrl.mcc_tag_status[tag] & CQE_STATUS_ADDL_MASK) >> CQE_STATUS_ADDL_SHIFT; status = phba->ctrl.mcc_tag_status[tag] & CQE_STATUS_MASK; @@ -5283,106 +4979,294 @@ void beiscsi_hba_attrs_init(struct beiscsi_hba *phba) beiscsi_log_enable_init(phba, beiscsi_log_enable); } -/* - * beiscsi_quiesce()- Cleanup Driver resources - * @phba: Instance Priv structure - * @unload_state:i Clean or EEH unload state - * - * Free the OS and HW resources held by the driver - **/ -static void beiscsi_quiesce(struct beiscsi_hba *phba, - uint32_t unload_state) +void beiscsi_start_boot_work(struct beiscsi_hba *phba, unsigned int s_handle) { - struct hwi_controller *phwi_ctrlr; - struct hwi_context_memory *phwi_context; - struct be_eq_obj *pbe_eq; - unsigned int i, msix_vec; + if (phba->boot_struct.boot_kset) + return; - phwi_ctrlr = phba->phwi_ctrlr; - phwi_context = phwi_ctrlr->phwi_ctxt; - hwi_disable_intr(phba); - if (phba->msix_enabled) { - for (i = 0; i <= phba->num_cpus; i++) { - msix_vec = phba->msix_entries[i].vector; - free_irq(msix_vec, &phwi_context->be_eq[i]); - kfree(phba->msi_name[i]); - } - } else - if (phba->pcidev->irq) - free_irq(phba->pcidev->irq, phba); - pci_disable_msix(phba->pcidev); - cancel_delayed_work_sync(&phba->beiscsi_hw_check_task); + /* skip if boot work is already in progress */ + if (test_and_set_bit(BEISCSI_HBA_BOOT_WORK, &phba->state)) + return; - for (i = 0; i < phba->num_cpus; i++) { - pbe_eq = &phwi_context->be_eq[i]; - irq_poll_disable(&pbe_eq->iopoll); + phba->boot_struct.retry = 3; + phba->boot_struct.tag = 0; + phba->boot_struct.s_handle = s_handle; + phba->boot_struct.action = BEISCSI_BOOT_GET_SHANDLE; + schedule_work(&phba->boot_work); +} + +static ssize_t beiscsi_show_boot_tgt_info(void *data, int type, char *buf) +{ + struct beiscsi_hba *phba = data; + struct mgmt_session_info *boot_sess = &phba->boot_struct.boot_sess; + struct mgmt_conn_info *boot_conn = &boot_sess->conn_list[0]; + char *str = buf; + int rc = -EPERM; + + switch (type) { + case ISCSI_BOOT_TGT_NAME: + rc = sprintf(buf, "%.*s\n", + (int)strlen(boot_sess->target_name), + (char *)&boot_sess->target_name); + break; + case ISCSI_BOOT_TGT_IP_ADDR: + if (boot_conn->dest_ipaddr.ip_type == BEISCSI_IP_TYPE_V4) + rc = sprintf(buf, "%pI4\n", + (char *)&boot_conn->dest_ipaddr.addr); + else + rc = sprintf(str, "%pI6\n", + (char *)&boot_conn->dest_ipaddr.addr); + break; + case ISCSI_BOOT_TGT_PORT: + rc = sprintf(str, "%d\n", boot_conn->dest_port); + break; + + case ISCSI_BOOT_TGT_CHAP_NAME: + rc = sprintf(str, "%.*s\n", + boot_conn->negotiated_login_options.auth_data.chap. + target_chap_name_length, + (char *)&boot_conn->negotiated_login_options. + auth_data.chap.target_chap_name); + break; + case ISCSI_BOOT_TGT_CHAP_SECRET: + rc = sprintf(str, "%.*s\n", + boot_conn->negotiated_login_options.auth_data.chap. + target_secret_length, + (char *)&boot_conn->negotiated_login_options. + auth_data.chap.target_secret); + break; + case ISCSI_BOOT_TGT_REV_CHAP_NAME: + rc = sprintf(str, "%.*s\n", + boot_conn->negotiated_login_options.auth_data.chap. + intr_chap_name_length, + (char *)&boot_conn->negotiated_login_options. + auth_data.chap.intr_chap_name); + break; + case ISCSI_BOOT_TGT_REV_CHAP_SECRET: + rc = sprintf(str, "%.*s\n", + boot_conn->negotiated_login_options.auth_data.chap. + intr_secret_length, + (char *)&boot_conn->negotiated_login_options. + auth_data.chap.intr_secret); + break; + case ISCSI_BOOT_TGT_FLAGS: + rc = sprintf(str, "2\n"); + break; + case ISCSI_BOOT_TGT_NIC_ASSOC: + rc = sprintf(str, "0\n"); + break; } + return rc; +} - if (unload_state == BEISCSI_CLEAN_UNLOAD) { - destroy_workqueue(phba->wq); - beiscsi_clean_port(phba); - beiscsi_free_mem(phba); +static ssize_t beiscsi_show_boot_ini_info(void *data, int type, char *buf) +{ + struct beiscsi_hba *phba = data; + char *str = buf; + int rc = -EPERM; - beiscsi_unmap_pci_function(phba); - pci_free_consistent(phba->pcidev, - phba->ctrl.mbox_mem_alloced.size, - phba->ctrl.mbox_mem_alloced.va, - phba->ctrl.mbox_mem_alloced.dma); - } else { - hwi_purge_eq(phba); - hwi_cleanup(phba); + switch (type) { + case ISCSI_BOOT_INI_INITIATOR_NAME: + rc = sprintf(str, "%s\n", + phba->boot_struct.boot_sess.initiator_iscsiname); + break; } + return rc; +} + +static ssize_t beiscsi_show_boot_eth_info(void *data, int type, char *buf) +{ + struct beiscsi_hba *phba = data; + char *str = buf; + int rc = -EPERM; + switch (type) { + case ISCSI_BOOT_ETH_FLAGS: + rc = sprintf(str, "2\n"); + break; + case ISCSI_BOOT_ETH_INDEX: + rc = sprintf(str, "0\n"); + break; + case ISCSI_BOOT_ETH_MAC: + rc = beiscsi_get_macaddr(str, phba); + break; + } + return rc; } -static void beiscsi_remove(struct pci_dev *pcidev) +static umode_t beiscsi_tgt_get_attr_visibility(void *data, int type) { - struct beiscsi_hba *phba = NULL; + umode_t rc = 0; - phba = pci_get_drvdata(pcidev); - if (!phba) { - dev_err(&pcidev->dev, "beiscsi_remove called with no phba\n"); - return; + switch (type) { + case ISCSI_BOOT_TGT_NAME: + case ISCSI_BOOT_TGT_IP_ADDR: + case ISCSI_BOOT_TGT_PORT: + case ISCSI_BOOT_TGT_CHAP_NAME: + case ISCSI_BOOT_TGT_CHAP_SECRET: + case ISCSI_BOOT_TGT_REV_CHAP_NAME: + case ISCSI_BOOT_TGT_REV_CHAP_SECRET: + case ISCSI_BOOT_TGT_NIC_ASSOC: + case ISCSI_BOOT_TGT_FLAGS: + rc = S_IRUGO; + break; } + return rc; +} - beiscsi_destroy_def_ifaces(phba); - iscsi_boot_destroy_kset(phba->boot_kset); - iscsi_host_remove(phba->shost); - beiscsi_quiesce(phba, BEISCSI_CLEAN_UNLOAD); - pci_dev_put(phba->pcidev); - iscsi_host_free(phba->shost); - pci_disable_pcie_error_reporting(pcidev); - pci_set_drvdata(pcidev, NULL); - pci_release_regions(pcidev); - pci_disable_device(pcidev); +static umode_t beiscsi_ini_get_attr_visibility(void *data, int type) +{ + umode_t rc = 0; + + switch (type) { + case ISCSI_BOOT_INI_INITIATOR_NAME: + rc = S_IRUGO; + break; + } + return rc; } -static void beiscsi_msix_enable(struct beiscsi_hba *phba) +static umode_t beiscsi_eth_get_attr_visibility(void *data, int type) { - int i, status; + umode_t rc = 0; - for (i = 0; i <= phba->num_cpus; i++) - phba->msix_entries[i].entry = i; + switch (type) { + case ISCSI_BOOT_ETH_FLAGS: + case ISCSI_BOOT_ETH_MAC: + case ISCSI_BOOT_ETH_INDEX: + rc = S_IRUGO; + break; + } + return rc; +} - status = pci_enable_msix_range(phba->pcidev, phba->msix_entries, - phba->num_cpus + 1, phba->num_cpus + 1); - if (status > 0) - phba->msix_enabled = true; +static void beiscsi_boot_kobj_release(void *data) +{ + struct beiscsi_hba *phba = data; - return; + scsi_host_put(phba->shost); } -static void be_eqd_update(struct beiscsi_hba *phba) +static int beiscsi_boot_create_kset(struct beiscsi_hba *phba) { + struct boot_struct *bs = &phba->boot_struct; + struct iscsi_boot_kobj *boot_kobj; + + if (bs->boot_kset) { + __beiscsi_log(phba, KERN_ERR, + "BM_%d: boot_kset already created\n"); + return 0; + } + + bs->boot_kset = iscsi_boot_create_host_kset(phba->shost->host_no); + if (!bs->boot_kset) { + __beiscsi_log(phba, KERN_ERR, + "BM_%d: boot_kset alloc failed\n"); + return -ENOMEM; + } + + /* get shost ref because the show function will refer phba */ + if (!scsi_host_get(phba->shost)) + goto free_kset; + + boot_kobj = iscsi_boot_create_target(bs->boot_kset, 0, phba, + beiscsi_show_boot_tgt_info, + beiscsi_tgt_get_attr_visibility, + beiscsi_boot_kobj_release); + if (!boot_kobj) + goto put_shost; + + if (!scsi_host_get(phba->shost)) + goto free_kset; + + boot_kobj = iscsi_boot_create_initiator(bs->boot_kset, 0, phba, + beiscsi_show_boot_ini_info, + beiscsi_ini_get_attr_visibility, + beiscsi_boot_kobj_release); + if (!boot_kobj) + goto put_shost; + + if (!scsi_host_get(phba->shost)) + goto free_kset; + + boot_kobj = iscsi_boot_create_ethernet(bs->boot_kset, 0, phba, + beiscsi_show_boot_eth_info, + beiscsi_eth_get_attr_visibility, + beiscsi_boot_kobj_release); + if (!boot_kobj) + goto put_shost; + + return 0; + +put_shost: + scsi_host_put(phba->shost); +free_kset: + iscsi_boot_destroy_kset(bs->boot_kset); + bs->boot_kset = NULL; + return -ENOMEM; +} + +static void beiscsi_boot_work(struct work_struct *work) +{ + struct beiscsi_hba *phba = + container_of(work, struct beiscsi_hba, boot_work); + struct boot_struct *bs = &phba->boot_struct; + unsigned int tag = 0; + + if (!beiscsi_hba_is_online(phba)) + return; + + beiscsi_log(phba, KERN_INFO, + BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, + "BM_%d : %s action %d\n", + __func__, phba->boot_struct.action); + + switch (phba->boot_struct.action) { + case BEISCSI_BOOT_REOPEN_SESS: + tag = beiscsi_boot_reopen_sess(phba); + break; + case BEISCSI_BOOT_GET_SHANDLE: + tag = __beiscsi_boot_get_shandle(phba, 1); + break; + case BEISCSI_BOOT_GET_SINFO: + tag = beiscsi_boot_get_sinfo(phba); + break; + case BEISCSI_BOOT_LOGOUT_SESS: + tag = beiscsi_boot_logout_sess(phba); + break; + case BEISCSI_BOOT_CREATE_KSET: + beiscsi_boot_create_kset(phba); + /** + * updated boot_kset is made visible to all before + * ending the boot work. + */ + mb(); + clear_bit(BEISCSI_HBA_BOOT_WORK, &phba->state); + return; + } + if (!tag) { + if (bs->retry--) + schedule_work(&phba->boot_work); + else + clear_bit(BEISCSI_HBA_BOOT_WORK, &phba->state); + } +} + +static void beiscsi_eqd_update_work(struct work_struct *work) +{ + struct hwi_context_memory *phwi_context; struct be_set_eqd set_eqd[MAX_CPUS]; - struct be_aic_obj *aic; - struct be_eq_obj *pbe_eq; struct hwi_controller *phwi_ctrlr; - struct hwi_context_memory *phwi_context; + struct be_eq_obj *pbe_eq; + struct beiscsi_hba *phba; + unsigned int pps, delta; + struct be_aic_obj *aic; int eqd, i, num = 0; - ulong now; - u32 pps, delta; - unsigned int tag; + unsigned long now; + + phba = container_of(work, struct beiscsi_hba, eqd_update.work); + if (!beiscsi_hba_is_online(phba)) + return; phwi_ctrlr = phba->phwi_ctrlr; phwi_context = phwi_ctrlr->phwi_ctxt; @@ -5391,13 +5275,13 @@ static void be_eqd_update(struct beiscsi_hba *phba) aic = &phba->aic_obj[i]; pbe_eq = &phwi_context->be_eq[i]; now = jiffies; - if (!aic->jiffs || time_before(now, aic->jiffs) || + if (!aic->jiffies || time_before(now, aic->jiffies) || pbe_eq->cq_count < aic->eq_prev) { - aic->jiffs = now; + aic->jiffies = now; aic->eq_prev = pbe_eq->cq_count; continue; } - delta = jiffies_to_msecs(now - aic->jiffs); + delta = jiffies_to_msecs(now - aic->jiffies); pps = (((u32)(pbe_eq->cq_count - aic->eq_prev) * 1000) / delta); eqd = (pps / 1500) << 2; @@ -5406,7 +5290,7 @@ static void be_eqd_update(struct beiscsi_hba *phba) eqd = min_t(u32, eqd, phwi_context->max_eqd); eqd = max_t(u32, eqd, phwi_context->min_eqd); - aic->jiffs = now; + aic->jiffies = now; aic->eq_prev = pbe_eq->cq_count; if (eqd != aic->prev_eqd) { @@ -5416,53 +5300,242 @@ static void be_eqd_update(struct beiscsi_hba *phba) num++; } } - if (num) { - tag = be_cmd_modify_eq_delay(phba, set_eqd, num); - if (tag) - beiscsi_mccq_compl_wait(phba, tag, NULL, NULL); + if (num) + /* completion of this is ignored */ + beiscsi_modify_eq_delay(phba, set_eqd, num); + + schedule_delayed_work(&phba->eqd_update, + msecs_to_jiffies(BEISCSI_EQD_UPDATE_INTERVAL)); +} + +static void beiscsi_msix_enable(struct beiscsi_hba *phba) +{ + int i, status; + + for (i = 0; i <= phba->num_cpus; i++) + phba->msix_entries[i].entry = i; + + status = pci_enable_msix_range(phba->pcidev, phba->msix_entries, + phba->num_cpus + 1, phba->num_cpus + 1); + if (status > 0) + phba->msix_enabled = true; +} + +static void beiscsi_hw_tpe_check(unsigned long ptr) +{ + struct beiscsi_hba *phba; + u32 wait; + + phba = (struct beiscsi_hba *)ptr; + /* if not TPE, do nothing */ + if (!beiscsi_detect_tpe(phba)) + return; + + /* wait default 4000ms before recovering */ + wait = 4000; + if (phba->ue2rp > BEISCSI_UE_DETECT_INTERVAL) + wait = phba->ue2rp - BEISCSI_UE_DETECT_INTERVAL; + queue_delayed_work(phba->wq, &phba->recover_port, + msecs_to_jiffies(wait)); +} + +static void beiscsi_hw_health_check(unsigned long ptr) +{ + struct beiscsi_hba *phba; + + phba = (struct beiscsi_hba *)ptr; + beiscsi_detect_ue(phba); + if (beiscsi_detect_ue(phba)) { + __beiscsi_log(phba, KERN_ERR, + "BM_%d : port in error: %lx\n", phba->state); + /* sessions are no longer valid, so first fail the sessions */ + queue_work(phba->wq, &phba->sess_work); + + /* detect UER supported */ + if (!test_bit(BEISCSI_HBA_UER_SUPP, &phba->state)) + return; + /* modify this timer to check TPE */ + phba->hw_check.function = beiscsi_hw_tpe_check; } + + mod_timer(&phba->hw_check, + jiffies + msecs_to_jiffies(BEISCSI_UE_DETECT_INTERVAL)); } -static void be_check_boot_session(struct beiscsi_hba *phba) +/* + * beiscsi_enable_port()- Enables the disabled port. + * Only port resources freed in disable function are reallocated. + * This is called in HBA error handling path. + * + * @phba: Instance of driver private structure + * + **/ +static int beiscsi_enable_port(struct beiscsi_hba *phba) { - if (beiscsi_setup_boot_info(phba)) - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BM_%d : Could not set up " - "iSCSI boot info on async event.\n"); + struct hwi_context_memory *phwi_context; + struct hwi_controller *phwi_ctrlr; + struct be_eq_obj *pbe_eq; + int ret, i; + + if (test_bit(BEISCSI_HBA_ONLINE, &phba->state)) { + __beiscsi_log(phba, KERN_ERR, + "BM_%d : %s : port is online %lx\n", + __func__, phba->state); + return 0; + } + + ret = beiscsi_init_sliport(phba); + if (ret) + return ret; + + if (enable_msix) + find_num_cpus(phba); + else + phba->num_cpus = 1; + if (enable_msix) { + beiscsi_msix_enable(phba); + if (!phba->msix_enabled) + phba->num_cpus = 1; + } + + beiscsi_get_params(phba); + /* Re-enable UER. If different TPE occurs then it is recoverable. */ + beiscsi_set_uer_feature(phba); + + phba->shost->max_id = phba->params.cxns_per_ctrl; + phba->shost->can_queue = phba->params.ios_per_ctrl; + ret = hwi_init_controller(phba); + if (ret) { + __beiscsi_log(phba, KERN_ERR, + "BM_%d : init controller failed %d\n", ret); + goto disable_msix; + } + + for (i = 0; i < MAX_MCC_CMD; i++) { + init_waitqueue_head(&phba->ctrl.mcc_wait[i + 1]); + phba->ctrl.mcc_tag[i] = i + 1; + phba->ctrl.mcc_tag_status[i + 1] = 0; + phba->ctrl.mcc_tag_available++; + } + + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; + for (i = 0; i < phba->num_cpus; i++) { + pbe_eq = &phwi_context->be_eq[i]; + irq_poll_init(&pbe_eq->iopoll, be_iopoll_budget, be_iopoll); + } + + i = (phba->msix_enabled) ? i : 0; + /* Work item for MCC handling */ + pbe_eq = &phwi_context->be_eq[i]; + INIT_WORK(&pbe_eq->mcc_work, beiscsi_mcc_work); + + ret = beiscsi_init_irqs(phba); + if (ret < 0) { + __beiscsi_log(phba, KERN_ERR, + "BM_%d : setup IRQs failed %d\n", ret); + goto cleanup_port; + } + hwi_enable_intr(phba); + /* port operational: clear all error bits */ + set_bit(BEISCSI_HBA_ONLINE, &phba->state); + __beiscsi_log(phba, KERN_INFO, + "BM_%d : port online: 0x%lx\n", phba->state); + + /* start hw_check timer and eqd_update work */ + schedule_delayed_work(&phba->eqd_update, + msecs_to_jiffies(BEISCSI_EQD_UPDATE_INTERVAL)); + + /** + * Timer function gets modified for TPE detection. + * Always reinit to do health check first. + */ + phba->hw_check.function = beiscsi_hw_health_check; + mod_timer(&phba->hw_check, + jiffies + msecs_to_jiffies(BEISCSI_UE_DETECT_INTERVAL)); + return 0; + +cleanup_port: + for (i = 0; i < phba->num_cpus; i++) { + pbe_eq = &phwi_context->be_eq[i]; + irq_poll_disable(&pbe_eq->iopoll); + } + hwi_cleanup_port(phba); + +disable_msix: + if (phba->msix_enabled) + pci_disable_msix(phba->pcidev); + + return ret; } /* - * beiscsi_hw_health_check()- Check adapter health - * @work: work item to check HW health + * beiscsi_disable_port()- Disable port and cleanup driver resources. + * This is called in HBA error handling and driver removal. + * @phba: Instance Priv structure + * @unload: indicate driver is unloading * - * Check if adapter in an unrecoverable state or not. + * Free the OS and HW resources held by the driver **/ -static void -beiscsi_hw_health_check(struct work_struct *work) +static void beiscsi_disable_port(struct beiscsi_hba *phba, int unload) { - struct beiscsi_hba *phba = - container_of(work, struct beiscsi_hba, - beiscsi_hw_check_task.work); + struct hwi_context_memory *phwi_context; + struct hwi_controller *phwi_ctrlr; + struct be_eq_obj *pbe_eq; + unsigned int i, msix_vec; - be_eqd_update(phba); + if (!test_and_clear_bit(BEISCSI_HBA_ONLINE, &phba->state)) + return; - if (phba->state & BE_ADAPTER_CHECK_BOOT) { - if ((phba->get_boot > 0) && (!phba->boot_kset)) { - phba->get_boot--; - if (!(phba->get_boot % BE_GET_BOOT_TO)) - be_check_boot_session(phba); - } else { - phba->state &= ~BE_ADAPTER_CHECK_BOOT; - phba->get_boot = 0; + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; + hwi_disable_intr(phba); + if (phba->msix_enabled) { + for (i = 0; i <= phba->num_cpus; i++) { + msix_vec = phba->msix_entries[i].vector; + free_irq(msix_vec, &phwi_context->be_eq[i]); + kfree(phba->msi_name[i]); } + } else + if (phba->pcidev->irq) + free_irq(phba->pcidev->irq, phba); + pci_disable_msix(phba->pcidev); + + for (i = 0; i < phba->num_cpus; i++) { + pbe_eq = &phwi_context->be_eq[i]; + irq_poll_disable(&pbe_eq->iopoll); } + cancel_delayed_work_sync(&phba->eqd_update); + cancel_work_sync(&phba->boot_work); + /* WQ might be running cancel queued mcc_work if we are not exiting */ + if (!unload && beiscsi_hba_in_error(phba)) { + pbe_eq = &phwi_context->be_eq[i]; + cancel_work_sync(&pbe_eq->mcc_work); + } + hwi_cleanup_port(phba); +} - beiscsi_ue_detect(phba); +static void beiscsi_sess_work(struct work_struct *work) +{ + struct beiscsi_hba *phba; - schedule_delayed_work(&phba->beiscsi_hw_check_task, - msecs_to_jiffies(1000)); + phba = container_of(work, struct beiscsi_hba, sess_work); + /* + * This work gets scheduled only in case of HBA error. + * Old sessions are gone so need to be re-established. + * iscsi_session_failure needs process context hence this work. + */ + iscsi_host_for_each_session(phba->shost, beiscsi_session_fail); } +static void beiscsi_recover_port(struct work_struct *work) +{ + struct beiscsi_hba *phba; + + phba = container_of(work, struct beiscsi_hba, recover_port.work); + beiscsi_disable_port(phba, 0); + beiscsi_enable_port(phba); +} static pci_ers_result_t beiscsi_eeh_err_detected(struct pci_dev *pdev, pci_channel_state_t state) @@ -5470,12 +5543,18 @@ static pci_ers_result_t beiscsi_eeh_err_detected(struct pci_dev *pdev, struct beiscsi_hba *phba = NULL; phba = (struct beiscsi_hba *)pci_get_drvdata(pdev); - phba->state |= BE_ADAPTER_PCI_ERR; + set_bit(BEISCSI_HBA_PCI_ERR, &phba->state); beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, "BM_%d : EEH error detected\n"); - beiscsi_quiesce(phba, BEISCSI_EEH_UNLOAD); + /* first stop UE detection when PCI error detected */ + del_timer_sync(&phba->hw_check); + cancel_delayed_work_sync(&phba->recover_port); + + /* sessions are no longer valid, so first fail the sessions */ + iscsi_host_for_each_session(phba->shost, beiscsi_session_fail); + beiscsi_disable_port(phba, 0); if (state == pci_channel_io_perm_failure) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, @@ -5515,9 +5594,8 @@ static pci_ers_result_t beiscsi_eeh_reset(struct pci_dev *pdev) pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); - /* Wait for the CHIP Reset to complete */ - status = be_chk_reset_complete(phba); - if (!status) { + status = beiscsi_check_fw_rdy(phba); + if (status) { beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_INIT, "BM_%d : EEH Reset Completed\n"); } else { @@ -5532,87 +5610,16 @@ static pci_ers_result_t beiscsi_eeh_reset(struct pci_dev *pdev) static void beiscsi_eeh_resume(struct pci_dev *pdev) { - int ret = 0, i; - struct be_eq_obj *pbe_eq; - struct beiscsi_hba *phba = NULL; - struct hwi_controller *phwi_ctrlr; - struct hwi_context_memory *phwi_context; + struct beiscsi_hba *phba; + int ret; phba = (struct beiscsi_hba *)pci_get_drvdata(pdev); pci_save_state(pdev); - if (enable_msix) - find_num_cpus(phba); - else - phba->num_cpus = 1; - - if (enable_msix) { - beiscsi_msix_enable(phba); - if (!phba->msix_enabled) - phba->num_cpus = 1; - } - - ret = beiscsi_cmd_reset_function(phba); - if (ret) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BM_%d : Reset Failed\n"); - goto ret_err; - } - - ret = be_chk_reset_complete(phba); - if (ret) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BM_%d : Failed to get out of reset.\n"); - goto ret_err; - } - - beiscsi_get_params(phba); - phba->shost->max_id = phba->params.cxns_per_ctrl; - phba->shost->can_queue = phba->params.ios_per_ctrl; - ret = hwi_init_controller(phba); - if (ret) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BM_%d : beiscsi_eeh_resume -" - "Failed to initialize beiscsi_hba.\n"); - goto ret_err; - } - - for (i = 0; i < MAX_MCC_CMD; i++) { - init_waitqueue_head(&phba->ctrl.mcc_wait[i + 1]); - phba->ctrl.mcc_tag[i] = i + 1; - phba->ctrl.mcc_tag_status[i + 1] = 0; - phba->ctrl.mcc_tag_available++; - } - - phwi_ctrlr = phba->phwi_ctrlr; - phwi_context = phwi_ctrlr->phwi_ctxt; - - for (i = 0; i < phba->num_cpus; i++) { - pbe_eq = &phwi_context->be_eq[i]; - irq_poll_init(&pbe_eq->iopoll, be_iopoll_budget, - be_iopoll); - } - - i = (phba->msix_enabled) ? i : 0; - /* Work item for MCC handling */ - pbe_eq = &phwi_context->be_eq[i]; - INIT_WORK(&pbe_eq->work_cqs, beiscsi_process_all_cqs); - - ret = beiscsi_init_irqs(phba); - if (ret < 0) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BM_%d : beiscsi_eeh_resume - " - "Failed to beiscsi_init_irqs\n"); - goto ret_err; - } - - hwi_enable_intr(phba); - phba->state &= ~BE_ADAPTER_PCI_ERR; - - return; -ret_err: - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BM_%d : AER EEH Resume Failed\n"); + ret = beiscsi_enable_port(phba); + if (ret) + __beiscsi_log(phba, KERN_ERR, + "BM_%d : AER EEH resume failed\n"); } static int beiscsi_dev_probe(struct pci_dev *pcidev, @@ -5622,7 +5629,8 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, struct hwi_controller *phwi_ctrlr; struct hwi_context_memory *phwi_context; struct be_eq_obj *pbe_eq; - int ret = 0, i; + unsigned int s_handle; + int ret, i; ret = beiscsi_enable_pci(pcidev); if (ret < 0) { @@ -5635,6 +5643,7 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, if (!phba) { dev_err(&pcidev->dev, "beiscsi_dev_probe - Failed in beiscsi_hba_alloc\n"); + ret = -ENOMEM; goto disable_pci; } @@ -5650,10 +5659,8 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, /* Initialize Driver configuration Paramters */ beiscsi_hba_attrs_init(phba); - phba->fw_timeout = false; phba->mac_addr_set = false; - switch (pcidev->device) { case BE_DEVICE_ID1: case OC_DEVICE_ID1: @@ -5677,39 +5684,26 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, ret = be_ctrl_init(phba, pcidev); if (ret) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BM_%d : beiscsi_dev_probe-" - "Failed in be_ctrl_init\n"); + "BM_%d : be_ctrl_init failed\n"); goto hba_free; } - /* - * FUNCTION_RESET should clean up any stale info in FW for this fn - */ - ret = beiscsi_cmd_reset_function(phba); - if (ret) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BM_%d : Reset Failed\n"); - goto hba_free; - } - ret = be_chk_reset_complete(phba); - if (ret) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BM_%d : Failed to get out of reset.\n"); + ret = beiscsi_init_sliport(phba); + if (ret) goto hba_free; - } spin_lock_init(&phba->io_sgl_lock); spin_lock_init(&phba->mgmt_sgl_lock); - spin_lock_init(&phba->isr_lock); spin_lock_init(&phba->async_pdu_lock); - ret = mgmt_get_fw_config(&phba->ctrl, phba); + ret = beiscsi_get_fw_config(&phba->ctrl, phba); if (ret != 0) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, "BM_%d : Error getting fw config\n"); goto free_port; } - mgmt_get_port_name(&phba->ctrl, phba); + beiscsi_get_port_name(&phba->ctrl, phba); beiscsi_get_params(phba); + beiscsi_set_uer_feature(phba); if (enable_msix) find_num_cpus(phba); @@ -5754,25 +5748,24 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, "BM_%d : beiscsi_dev_probe-" "Failed to allocate work queue\n"); + ret = -ENOMEM; goto free_twq; } - INIT_DELAYED_WORK(&phba->beiscsi_hw_check_task, - beiscsi_hw_health_check); + INIT_DELAYED_WORK(&phba->eqd_update, beiscsi_eqd_update_work); phwi_ctrlr = phba->phwi_ctrlr; phwi_context = phwi_ctrlr->phwi_ctxt; for (i = 0; i < phba->num_cpus; i++) { pbe_eq = &phwi_context->be_eq[i]; - irq_poll_init(&pbe_eq->iopoll, be_iopoll_budget, - be_iopoll); + irq_poll_init(&pbe_eq->iopoll, be_iopoll_budget, be_iopoll); } i = (phba->msix_enabled) ? i : 0; /* Work item for MCC handling */ pbe_eq = &phwi_context->be_eq[i]; - INIT_WORK(&pbe_eq->work_cqs, beiscsi_process_all_cqs); + INIT_WORK(&pbe_eq->mcc_work, beiscsi_mcc_work); ret = beiscsi_init_irqs(phba); if (ret < 0) { @@ -5783,22 +5776,42 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, } hwi_enable_intr(phba); - if (iscsi_host_add(phba->shost, &phba->pcidev->dev)) + ret = iscsi_host_add(phba->shost, &phba->pcidev->dev); + if (ret) goto free_blkenbld; - if (beiscsi_setup_boot_info(phba)) - /* - * log error but continue, because we may not be using - * iscsi boot. + /* set online bit after port is operational */ + set_bit(BEISCSI_HBA_ONLINE, &phba->state); + __beiscsi_log(phba, KERN_INFO, + "BM_%d : port online: 0x%lx\n", phba->state); + + INIT_WORK(&phba->boot_work, beiscsi_boot_work); + ret = beiscsi_boot_get_shandle(phba, &s_handle); + if (ret > 0) { + beiscsi_start_boot_work(phba, s_handle); + /** + * Set this bit after starting the work to let + * probe handle it first. + * ASYNC event can too schedule this work. */ - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BM_%d : Could not set up " - "iSCSI boot info.\n"); + set_bit(BEISCSI_HBA_BOOT_FOUND, &phba->state); + } - beiscsi_create_def_ifaces(phba); - schedule_delayed_work(&phba->beiscsi_hw_check_task, - msecs_to_jiffies(1000)); + beiscsi_iface_create_default(phba); + schedule_delayed_work(&phba->eqd_update, + msecs_to_jiffies(BEISCSI_EQD_UPDATE_INTERVAL)); + INIT_WORK(&phba->sess_work, beiscsi_sess_work); + INIT_DELAYED_WORK(&phba->recover_port, beiscsi_recover_port); + /** + * Start UE detection here. UE before this will cause stall in probe + * and eventually fail the probe. + */ + init_timer(&phba->hw_check); + phba->hw_check.function = beiscsi_hw_health_check; + phba->hw_check.data = (unsigned long)phba; + mod_timer(&phba->hw_check, + jiffies + msecs_to_jiffies(BEISCSI_UE_DETECT_INTERVAL)); beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, "\n\n\n BM_%d : SUCCESS - DRIVER LOADED\n\n\n"); return 0; @@ -5810,7 +5823,8 @@ free_blkenbld: irq_poll_disable(&pbe_eq->iopoll); } free_twq: - beiscsi_clean_port(phba); + hwi_cleanup_port(phba); + beiscsi_cleanup_port(phba); beiscsi_free_mem(phba); free_port: pci_free_consistent(phba->pcidev, @@ -5830,6 +5844,49 @@ disable_pci: return ret; } +static void beiscsi_remove(struct pci_dev *pcidev) +{ + struct beiscsi_hba *phba = NULL; + + phba = pci_get_drvdata(pcidev); + if (!phba) { + dev_err(&pcidev->dev, "beiscsi_remove called with no phba\n"); + return; + } + + /* first stop UE detection before unloading */ + del_timer_sync(&phba->hw_check); + cancel_delayed_work_sync(&phba->recover_port); + cancel_work_sync(&phba->sess_work); + + beiscsi_iface_destroy_default(phba); + iscsi_host_remove(phba->shost); + beiscsi_disable_port(phba, 1); + + /* after cancelling boot_work */ + iscsi_boot_destroy_kset(phba->boot_struct.boot_kset); + + /* free all resources */ + destroy_workqueue(phba->wq); + beiscsi_cleanup_port(phba); + beiscsi_free_mem(phba); + + /* ctrl uninit */ + beiscsi_unmap_pci_function(phba); + pci_free_consistent(phba->pcidev, + phba->ctrl.mbox_mem_alloced.size, + phba->ctrl.mbox_mem_alloced.va, + phba->ctrl.mbox_mem_alloced.dma); + + pci_dev_put(phba->pcidev); + iscsi_host_free(phba->shost); + pci_disable_pcie_error_reporting(pcidev); + pci_set_drvdata(pcidev, NULL); + pci_release_regions(pcidev); + pci_disable_device(pcidev); +} + + static struct pci_error_handlers beiscsi_eeh_handlers = { .error_detected = beiscsi_eeh_err_detected, .slot_reset = beiscsi_eeh_reset, @@ -5846,9 +5903,9 @@ struct iscsi_transport beiscsi_iscsi_transport = { .create_conn = beiscsi_conn_create, .bind_conn = beiscsi_conn_bind, .destroy_conn = iscsi_conn_teardown, - .attr_is_visible = be2iscsi_attr_is_visible, - .set_iface_param = be2iscsi_iface_set_param, - .get_iface_param = be2iscsi_iface_get_param, + .attr_is_visible = beiscsi_attr_is_visible, + .set_iface_param = beiscsi_iface_set_param, + .get_iface_param = beiscsi_iface_get_param, .set_param = beiscsi_set_param, .get_conn_param = iscsi_conn_get_param, .get_session_param = iscsi_session_get_param, @@ -5877,7 +5934,6 @@ static struct pci_driver beiscsi_pci_driver = { .err_handler = &beiscsi_eeh_handlers }; - static int __init beiscsi_module_init(void) { int ret; diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h index 30a4606d9a3b..6376657e45f7 100644 --- a/drivers/scsi/be2iscsi/be_main.h +++ b/drivers/scsi/be2iscsi/be_main.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Emulex + * Copyright (C) 2005 - 2016 Broadcom * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -7,10 +7,10 @@ * as published by the Free Software Foundation. The full GNU General * Public License is included in this distribution in the file called COPYING. * - * Written by: Jayamohan Kallickal (jayamohan.kallickal@avagotech.com) + * Written by: Jayamohan Kallickal (jayamohan.kallickal@broadcom.com) * * Contact Information: - * linux-drivers@avagotech.com + * linux-drivers@broadcom.com * * Emulex * 3333 Susan Street @@ -36,7 +36,7 @@ #include <scsi/scsi_transport_iscsi.h> #define DRV_NAME "be2iscsi" -#define BUILD_STR "11.0.0.0" +#define BUILD_STR "11.2.0.0" #define BE_NAME "Emulex OneConnect" \ "Open-iSCSI Driver version" BUILD_STR #define DRV_DESC BE_NAME " " "Driver" @@ -82,36 +82,12 @@ #define BEISCSI_MAX_FRAGS_INIT 192 #define BE_NUM_MSIX_ENTRIES 1 -#define MPU_EP_CONTROL 0 -#define MPU_EP_SEMAPHORE 0xac -#define BE2_SOFT_RESET 0x5c -#define BE2_PCI_ONLINE0 0xb0 -#define BE2_PCI_ONLINE1 0xb4 -#define BE2_SET_RESET 0x80 -#define BE2_MPU_IRAM_ONLINE 0x00000080 - #define BE_SENSE_INFO_SIZE 258 #define BE_ISCSI_PDU_HEADER_SIZE 64 #define BE_MIN_MEM_SIZE 16384 #define MAX_CMD_SZ 65536 #define IIOC_SCSI_DATA 0x05 /* Write Operation */ -#define INVALID_SESS_HANDLE 0xFFFFFFFF - -/** - * Adapter States - **/ -#define BE_ADAPTER_LINK_UP 0x001 -#define BE_ADAPTER_LINK_DOWN 0x002 -#define BE_ADAPTER_PCI_ERR 0x004 -#define BE_ADAPTER_CHECK_BOOT 0x008 - - -#define BEISCSI_CLEAN_UNLOAD 0x01 -#define BEISCSI_EEH_UNLOAD 0x02 - -#define BE_GET_BOOT_RETRIES 45 -#define BE_GET_BOOT_TO 20 /** * hardware needs the async PDU buffers to be posted in multiples of 8 * So have atleast 8 of them by default @@ -378,7 +354,6 @@ struct beiscsi_hba { struct sgl_handle **eh_sgl_hndl_base; spinlock_t io_sgl_lock; spinlock_t mgmt_sgl_lock; - spinlock_t isr_lock; spinlock_t async_pdu_lock; unsigned int age; struct list_head hba_queue; @@ -390,7 +365,6 @@ struct beiscsi_hba { struct ulp_cid_info *cid_array_info[BEISCSI_ULP_COUNT]; struct iscsi_endpoint **ep_array; struct beiscsi_conn **conn_table; - struct iscsi_boot_kset *boot_kset; struct Scsi_Host *shost; struct iscsi_iface *ipv4_iface; struct iscsi_iface *ipv6_iface; @@ -418,12 +392,33 @@ struct beiscsi_hba { unsigned long ulp_supported; } fw_config; - unsigned int state; + unsigned long state; +#define BEISCSI_HBA_ONLINE 0 +#define BEISCSI_HBA_LINK_UP 1 +#define BEISCSI_HBA_BOOT_FOUND 2 +#define BEISCSI_HBA_BOOT_WORK 3 +#define BEISCSI_HBA_UER_SUPP 4 +#define BEISCSI_HBA_PCI_ERR 5 +#define BEISCSI_HBA_FW_TIMEOUT 6 +#define BEISCSI_HBA_IN_UE 7 +#define BEISCSI_HBA_IN_TPE 8 + +/* error bits */ +#define BEISCSI_HBA_IN_ERR ((1 << BEISCSI_HBA_PCI_ERR) | \ + (1 << BEISCSI_HBA_FW_TIMEOUT) | \ + (1 << BEISCSI_HBA_IN_UE) | \ + (1 << BEISCSI_HBA_IN_TPE)) + u8 optic_state; - int get_boot; - bool fw_timeout; - bool ue_detected; - struct delayed_work beiscsi_hw_check_task; + struct delayed_work eqd_update; + /* update EQ delay timer every 1000ms */ +#define BEISCSI_EQD_UPDATE_INTERVAL 1000 + struct timer_list hw_check; + /* check for UE every 1000ms */ +#define BEISCSI_UE_DETECT_INTERVAL 1000 + u32 ue2rp; + struct delayed_work recover_port; + struct work_struct sess_work; bool mac_addr_set; u8 mac_address[ETH_ALEN]; @@ -435,7 +430,6 @@ struct beiscsi_hba { struct be_ctrl_info ctrl; unsigned int generation; unsigned int interface_handle; - struct mgmt_session_info boot_sess; struct invalidate_command_table inv_tbl[128]; struct be_aic_obj aic_obj[MAX_CPUS]; @@ -444,8 +438,29 @@ struct beiscsi_hba { struct scatterlist *sg, uint32_t num_sg, uint32_t xferlen, uint32_t writedir); + struct boot_struct { + int retry; + unsigned int tag; + unsigned int s_handle; + struct be_dma_mem nonemb_cmd; + enum { + BEISCSI_BOOT_REOPEN_SESS = 1, + BEISCSI_BOOT_GET_SHANDLE, + BEISCSI_BOOT_GET_SINFO, + BEISCSI_BOOT_LOGOUT_SESS, + BEISCSI_BOOT_CREATE_KSET, + } action; + struct mgmt_session_info boot_sess; + struct iscsi_boot_kset *boot_kset; + } boot_struct; + struct work_struct boot_work; }; +#define beiscsi_hba_in_error(phba) ((phba)->state & BEISCSI_HBA_IN_ERR) +#define beiscsi_hba_is_online(phba) \ + (!beiscsi_hba_in_error((phba)) && \ + test_bit(BEISCSI_HBA_ONLINE, &phba->state)) + struct beiscsi_session { struct pci_pool *bhs_pool; }; @@ -508,6 +523,7 @@ struct beiscsi_io_task { struct sgl_handle *psgl_handle; struct beiscsi_conn *conn; struct scsi_cmnd *scsi_cmnd; + int num_sg; struct hwi_wrb_context *pwrb_context; unsigned int cmd_sn; unsigned int flags; @@ -592,80 +608,81 @@ struct amap_beiscsi_offload_params { u8 max_recv_data_segment_length[32]; }; -/* void hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn, - struct beiscsi_hba *phba, struct sol_cqe *psol);*/ - -struct async_pdu_handle { +struct hd_async_handle { struct list_head link; struct be_bus_address pa; void *pbuffer; - unsigned int consumed; - unsigned char index; - unsigned char is_header; - unsigned short cri; - unsigned long buffer_len; + u32 buffer_len; + u16 index; + u16 cri; + u8 is_header; + u8 is_final; }; -struct hwi_async_entry { - struct { - unsigned char hdr_received; - unsigned char hdr_len; - unsigned short bytes_received; +/** + * This has list of async PDUs that are waiting to be processed. + * Buffers live in this list for a brief duration before they get + * processed and posted back to hardware. + * Note that we don't really need one cri_wait_queue per async_entry. + * We need one cri_wait_queue per CRI. Its easier to manage if this + * is tagged along with the async_entry. + */ +struct hd_async_entry { + struct cri_wait_queue { + unsigned short hdr_len; + unsigned int bytes_received; unsigned int bytes_needed; struct list_head list; - } wait_queue; - - struct list_head header_busy_list; - struct list_head data_busy_list; + } wq; + /* handles posted to FW resides here */ + struct hd_async_handle *header; + struct hd_async_handle *data; }; -struct hwi_async_pdu_context { - struct { - struct be_bus_address pa_base; - void *va_base; - void *ring_base; - struct async_pdu_handle *handle_base; - - unsigned int host_write_ptr; - unsigned int ep_read_ptr; - unsigned int writables; - - unsigned int free_entries; - unsigned int busy_entries; - - struct list_head free_list; - } async_header; +struct hd_async_buf_context { + struct be_bus_address pa_base; + void *va_base; + void *ring_base; + struct hd_async_handle *handle_base; + u16 free_entries; + u32 buffer_size; + /** + * Once iSCSI layer finishes processing an async PDU, the + * handles used for the PDU are added to this list. + * They are posted back to FW in groups of 8. + */ + struct list_head free_list; +}; - struct { - struct be_bus_address pa_base; - void *va_base; - void *ring_base; - struct async_pdu_handle *handle_base; - - unsigned int host_write_ptr; - unsigned int ep_read_ptr; - unsigned int writables; - - unsigned int free_entries; - unsigned int busy_entries; - struct list_head free_list; - } async_data; - - unsigned int buffer_size; - unsigned int num_entries; +/** + * hd_async_context is declared for each ULP supporting iSCSI function. + */ +struct hd_async_context { + struct hd_async_buf_context async_header; + struct hd_async_buf_context async_data; + u16 num_entries; + /** + * When unsol PDU is in, it needs to be chained till all the bytes are + * received and then processing is done. hd_async_entry is created + * based on the cid_count for each ULP. When unsol PDU comes in based + * on the conn_id it needs to be added to the correct async_entry wq. + * Below defined cid_to_async_cri_map is used to reterive the + * async_cri_map for a particular connection. + * + * This array is initialized after beiscsi_create_wrb_rings returns. + * + * - this method takes more memory space, fixed to 2K + * - any support for connections greater than this the array size needs + * to be incremented + */ #define BE_GET_ASYNC_CRI_FROM_CID(cid) (pasync_ctx->cid_to_async_cri_map[cid]) unsigned short cid_to_async_cri_map[BE_MAX_SESSION]; /** - * This is a varying size list! Do not add anything - * after this entry!! + * This is a variable size array. Don`t add anything after this field!! */ - struct hwi_async_entry *async_entry; + struct hd_async_entry *async_entry; }; -#define PDUCQE_CODE_MASK 0x0000003F -#define PDUCQE_DPL_MASK 0xFFFF0000 -#define PDUCQE_INDEX_MASK 0x0000FFFF - struct i_t_dpdu_cqe { u32 dw[4]; } __packed; @@ -845,7 +862,6 @@ struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid, void free_mgmt_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle); -void beiscsi_process_all_cqs(struct work_struct *work); void beiscsi_free_mgmt_task_handles(struct beiscsi_conn *beiscsi_conn, struct iscsi_task *task); @@ -856,11 +872,6 @@ void hwi_ring_cq_db(struct beiscsi_hba *phba, unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq, int budget); void beiscsi_process_mcc_cq(struct beiscsi_hba *phba); -static inline bool beiscsi_error(struct beiscsi_hba *phba) -{ - return phba->ue_detected || phba->fw_timeout; -} - struct pdu_nop_out { u32 dw[12]; }; @@ -1067,11 +1078,18 @@ struct hwi_context_memory { struct be_queue_info be_cq[MAX_CPUS - 1]; struct be_queue_info *be_wrbq; + /** + * Create array of ULP number for below entries as DEFQ + * will be created for both ULP if iSCSI Protocol is + * loaded on both ULP. + */ struct be_queue_info be_def_hdrq[BEISCSI_ULP_COUNT]; struct be_queue_info be_def_dataq[BEISCSI_ULP_COUNT]; - struct hwi_async_pdu_context *pasync_ctx[BEISCSI_ULP_COUNT]; + struct hd_async_context *pasync_ctx[BEISCSI_ULP_COUNT]; }; +void beiscsi_start_boot_work(struct beiscsi_hba *phba, unsigned int s_handle); + /* Logging related definitions */ #define BEISCSI_LOG_INIT 0x0001 /* Initialization events */ #define BEISCSI_LOG_MBOX 0x0002 /* Mailbox Events */ diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c index 83926e221f1e..aebc4ddb3060 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.c +++ b/drivers/scsi/be2iscsi/be_mgmt.c @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Emulex + * Copyright (C) 2005 - 2016 Broadcom * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -7,10 +7,10 @@ * as published by the Free Software Foundation. The full GNU General * Public License is included in this distribution in the file called COPYING. * - * Written by: Jayamohan Kallickal (jayamohan.kallickal@avagotech.com) + * Written by: Jayamohan Kallickal (jayamohan.kallickal@broadcom.com) * * Contact Information: - * linux-drivers@avagotech.com + * linux-drivers@broadcom.com * * Emulex * 3333 Susan Street @@ -24,139 +24,9 @@ #include "be_iscsi.h" #include "be_main.h" -/* UE Status Low CSR */ -static const char * const desc_ue_status_low[] = { - "CEV", - "CTX", - "DBUF", - "ERX", - "Host", - "MPU", - "NDMA", - "PTC ", - "RDMA ", - "RXF ", - "RXIPS ", - "RXULP0 ", - "RXULP1 ", - "RXULP2 ", - "TIM ", - "TPOST ", - "TPRE ", - "TXIPS ", - "TXULP0 ", - "TXULP1 ", - "UC ", - "WDMA ", - "TXULP2 ", - "HOST1 ", - "P0_OB_LINK ", - "P1_OB_LINK ", - "HOST_GPIO ", - "MBOX ", - "AXGMAC0", - "AXGMAC1", - "JTAG", - "MPU_INTPEND" -}; - -/* UE Status High CSR */ -static const char * const desc_ue_status_hi[] = { - "LPCMEMHOST", - "MGMT_MAC", - "PCS0ONLINE", - "MPU_IRAM", - "PCS1ONLINE", - "PCTL0", - "PCTL1", - "PMEM", - "RR", - "TXPB", - "RXPP", - "XAUI", - "TXP", - "ARM", - "IPC", - "HOST2", - "HOST3", - "HOST4", - "HOST5", - "HOST6", - "HOST7", - "HOST8", - "HOST9", - "NETC", - "Unknown", - "Unknown", - "Unknown", - "Unknown", - "Unknown", - "Unknown", - "Unknown", - "Unknown" -}; - -/* - * beiscsi_ue_detec()- Detect Unrecoverable Error on adapter - * @phba: Driver priv structure - * - * Read registers linked to UE and check for the UE status - **/ -void beiscsi_ue_detect(struct beiscsi_hba *phba) -{ - uint32_t ue_hi = 0, ue_lo = 0; - uint32_t ue_mask_hi = 0, ue_mask_lo = 0; - uint8_t i = 0; - - if (phba->ue_detected) - return; - - pci_read_config_dword(phba->pcidev, - PCICFG_UE_STATUS_LOW, &ue_lo); - pci_read_config_dword(phba->pcidev, - PCICFG_UE_STATUS_MASK_LOW, - &ue_mask_lo); - pci_read_config_dword(phba->pcidev, - PCICFG_UE_STATUS_HIGH, - &ue_hi); - pci_read_config_dword(phba->pcidev, - PCICFG_UE_STATUS_MASK_HI, - &ue_mask_hi); - - ue_lo = (ue_lo & ~ue_mask_lo); - ue_hi = (ue_hi & ~ue_mask_hi); - - - if (ue_lo || ue_hi) { - phba->ue_detected = true; - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, - "BG_%d : Error detected on the adapter\n"); - } - - if (ue_lo) { - for (i = 0; ue_lo; ue_lo >>= 1, i++) { - if (ue_lo & 1) - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_CONFIG, - "BG_%d : UE_LOW %s bit set\n", - desc_ue_status_low[i]); - } - } - - if (ue_hi) { - for (i = 0; ue_hi; ue_hi >>= 1, i++) { - if (ue_hi & 1) - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_CONFIG, - "BG_%d : UE_HIGH %s bit set\n", - desc_ue_status_hi[i]); - } - } -} - -int be_cmd_modify_eq_delay(struct beiscsi_hba *phba, - struct be_set_eqd *set_eqd, int num) +int beiscsi_modify_eq_delay(struct beiscsi_hba *phba, + struct be_set_eqd *set_eqd, + int num) { struct be_ctrl_info *ctrl = &phba->ctrl; struct be_mcc_wrb *wrb; @@ -174,7 +44,7 @@ int be_cmd_modify_eq_delay(struct beiscsi_hba *phba, req = embedded_payload(wrb); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, - OPCODE_COMMON_MODIFY_EQ_DELAY, sizeof(*req)); + OPCODE_COMMON_MODIFY_EQ_DELAY, sizeof(*req)); req->num_eq = cpu_to_le32(num); for (i = 0; i < num; i++) { @@ -184,386 +54,13 @@ int be_cmd_modify_eq_delay(struct beiscsi_hba *phba, cpu_to_le32(set_eqd[i].delay_multiplier); } + /* ignore the completion of this mbox command */ + set_bit(MCC_TAG_STATE_IGNORE, &ctrl->ptag_state[tag].tag_state); be_mcc_notify(phba, tag); mutex_unlock(&ctrl->mbox_lock); return tag; } -/** - * mgmt_reopen_session()- Reopen a session based on reopen_type - * @phba: Device priv structure instance - * @reopen_type: Type of reopen_session FW should do. - * @sess_handle: Session Handle of the session to be re-opened - * - * return - * the TAG used for MBOX Command - * - **/ -unsigned int mgmt_reopen_session(struct beiscsi_hba *phba, - unsigned int reopen_type, - unsigned int sess_handle) -{ - struct be_ctrl_info *ctrl = &phba->ctrl; - struct be_mcc_wrb *wrb; - struct be_cmd_reopen_session_req *req; - unsigned int tag; - - beiscsi_log(phba, KERN_INFO, - BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, - "BG_%d : In bescsi_get_boot_target\n"); - - mutex_lock(&ctrl->mbox_lock); - wrb = alloc_mcc_wrb(phba, &tag); - if (!wrb) { - mutex_unlock(&ctrl->mbox_lock); - return 0; - } - - req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); - be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI, - OPCODE_ISCSI_INI_DRIVER_REOPEN_ALL_SESSIONS, - sizeof(struct be_cmd_reopen_session_resp)); - - /* set the reopen_type,sess_handle */ - req->reopen_type = reopen_type; - req->session_handle = sess_handle; - - be_mcc_notify(phba, tag); - mutex_unlock(&ctrl->mbox_lock); - return tag; -} - -unsigned int mgmt_get_boot_target(struct beiscsi_hba *phba) -{ - struct be_ctrl_info *ctrl = &phba->ctrl; - struct be_mcc_wrb *wrb; - struct be_cmd_get_boot_target_req *req; - unsigned int tag; - - beiscsi_log(phba, KERN_INFO, - BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, - "BG_%d : In bescsi_get_boot_target\n"); - - mutex_lock(&ctrl->mbox_lock); - wrb = alloc_mcc_wrb(phba, &tag); - if (!wrb) { - mutex_unlock(&ctrl->mbox_lock); - return 0; - } - - req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); - be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI, - OPCODE_ISCSI_INI_BOOT_GET_BOOT_TARGET, - sizeof(struct be_cmd_get_boot_target_resp)); - - be_mcc_notify(phba, tag); - mutex_unlock(&ctrl->mbox_lock); - return tag; -} - -unsigned int mgmt_get_session_info(struct beiscsi_hba *phba, - u32 boot_session_handle, - struct be_dma_mem *nonemb_cmd) -{ - struct be_ctrl_info *ctrl = &phba->ctrl; - struct be_mcc_wrb *wrb; - unsigned int tag; - struct be_cmd_get_session_req *req; - struct be_cmd_get_session_resp *resp; - struct be_sge *sge; - - beiscsi_log(phba, KERN_INFO, - BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, - "BG_%d : In beiscsi_get_session_info\n"); - - mutex_lock(&ctrl->mbox_lock); - wrb = alloc_mcc_wrb(phba, &tag); - if (!wrb) { - mutex_unlock(&ctrl->mbox_lock); - return 0; - } - - nonemb_cmd->size = sizeof(*resp); - req = nonemb_cmd->va; - memset(req, 0, sizeof(*req)); - sge = nonembedded_sgl(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1); - be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI, - OPCODE_ISCSI_INI_SESSION_GET_A_SESSION, - sizeof(*resp)); - req->session_handle = boot_session_handle; - sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd->dma)); - sge->pa_lo = cpu_to_le32(nonemb_cmd->dma & 0xFFFFFFFF); - sge->len = cpu_to_le32(nonemb_cmd->size); - - be_mcc_notify(phba, tag); - mutex_unlock(&ctrl->mbox_lock); - return tag; -} - -/** - * mgmt_get_port_name()- Get port name for the function - * @ctrl: ptr to Ctrl Info - * @phba: ptr to the dev priv structure - * - * Get the alphanumeric character for port - * - **/ -int mgmt_get_port_name(struct be_ctrl_info *ctrl, - struct beiscsi_hba *phba) -{ - int ret = 0; - struct be_mcc_wrb *wrb; - struct be_cmd_get_port_name *ioctl; - - mutex_lock(&ctrl->mbox_lock); - wrb = wrb_from_mbox(&ctrl->mbox_mem); - memset(wrb, 0, sizeof(*wrb)); - ioctl = embedded_payload(wrb); - - be_wrb_hdr_prepare(wrb, sizeof(*ioctl), true, 0); - be_cmd_hdr_prepare(&ioctl->h.req_hdr, CMD_SUBSYSTEM_COMMON, - OPCODE_COMMON_GET_PORT_NAME, - EMBED_MBX_MAX_PAYLOAD_SIZE); - ret = be_mbox_notify(ctrl); - phba->port_name = 0; - if (!ret) { - phba->port_name = ioctl->p.resp.port_names >> - (phba->fw_config.phys_port * 8) & 0xff; - } else { - beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, - "BG_%d : GET_PORT_NAME ret 0x%x status 0x%x\n", - ret, ioctl->h.resp_hdr.status); - } - - if (phba->port_name == 0) - phba->port_name = '?'; - - mutex_unlock(&ctrl->mbox_lock); - return ret; -} - -/** - * mgmt_get_fw_config()- Get the FW config for the function - * @ctrl: ptr to Ctrl Info - * @phba: ptr to the dev priv structure - * - * Get the FW config and resources available for the function. - * The resources are created based on the count received here. - * - * return - * Success: 0 - * Failure: Non-Zero Value - **/ -int mgmt_get_fw_config(struct be_ctrl_info *ctrl, - struct beiscsi_hba *phba) -{ - struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); - struct be_fw_cfg *pfw_cfg = embedded_payload(wrb); - uint32_t cid_count, icd_count; - int status = -EINVAL; - uint8_t ulp_num = 0; - - mutex_lock(&ctrl->mbox_lock); - memset(wrb, 0, sizeof(*wrb)); - be_wrb_hdr_prepare(wrb, sizeof(*pfw_cfg), true, 0); - - be_cmd_hdr_prepare(&pfw_cfg->hdr, CMD_SUBSYSTEM_COMMON, - OPCODE_COMMON_QUERY_FIRMWARE_CONFIG, - EMBED_MBX_MAX_PAYLOAD_SIZE); - - if (be_mbox_notify(ctrl)) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BG_%d : Failed in mgmt_get_fw_config\n"); - goto fail_init; - } - - /* FW response formats depend on port id */ - phba->fw_config.phys_port = pfw_cfg->phys_port; - if (phba->fw_config.phys_port >= BEISCSI_PHYS_PORT_MAX) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BG_%d : invalid physical port id %d\n", - phba->fw_config.phys_port); - goto fail_init; - } - - /* populate and check FW config against min and max values */ - if (!is_chip_be2_be3r(phba)) { - phba->fw_config.eqid_count = pfw_cfg->eqid_count; - phba->fw_config.cqid_count = pfw_cfg->cqid_count; - if (phba->fw_config.eqid_count == 0 || - phba->fw_config.eqid_count > 2048) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BG_%d : invalid EQ count %d\n", - phba->fw_config.eqid_count); - goto fail_init; - } - if (phba->fw_config.cqid_count == 0 || - phba->fw_config.cqid_count > 4096) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BG_%d : invalid CQ count %d\n", - phba->fw_config.cqid_count); - goto fail_init; - } - beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, - "BG_%d : EQ_Count : %d CQ_Count : %d\n", - phba->fw_config.eqid_count, - phba->fw_config.cqid_count); - } - - /** - * Check on which all ULP iSCSI Protocol is loaded. - * Set the Bit for those ULP. This set flag is used - * at all places in the code to check on which ULP - * iSCSi Protocol is loaded - **/ - for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) { - if (pfw_cfg->ulp[ulp_num].ulp_mode & - BEISCSI_ULP_ISCSI_INI_MODE) { - set_bit(ulp_num, &phba->fw_config.ulp_supported); - - /* Get the CID, ICD and Chain count for each ULP */ - phba->fw_config.iscsi_cid_start[ulp_num] = - pfw_cfg->ulp[ulp_num].sq_base; - phba->fw_config.iscsi_cid_count[ulp_num] = - pfw_cfg->ulp[ulp_num].sq_count; - - phba->fw_config.iscsi_icd_start[ulp_num] = - pfw_cfg->ulp[ulp_num].icd_base; - phba->fw_config.iscsi_icd_count[ulp_num] = - pfw_cfg->ulp[ulp_num].icd_count; - - phba->fw_config.iscsi_chain_start[ulp_num] = - pfw_cfg->chain_icd[ulp_num].chain_base; - phba->fw_config.iscsi_chain_count[ulp_num] = - pfw_cfg->chain_icd[ulp_num].chain_count; - - beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, - "BG_%d : Function loaded on ULP : %d\n" - "\tiscsi_cid_count : %d\n" - "\tiscsi_cid_start : %d\n" - "\t iscsi_icd_count : %d\n" - "\t iscsi_icd_start : %d\n", - ulp_num, - phba->fw_config. - iscsi_cid_count[ulp_num], - phba->fw_config. - iscsi_cid_start[ulp_num], - phba->fw_config. - iscsi_icd_count[ulp_num], - phba->fw_config. - iscsi_icd_start[ulp_num]); - } - } - - if (phba->fw_config.ulp_supported == 0) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BG_%d : iSCSI initiator mode not set: ULP0 %x ULP1 %x\n", - pfw_cfg->ulp[BEISCSI_ULP0].ulp_mode, - pfw_cfg->ulp[BEISCSI_ULP1].ulp_mode); - goto fail_init; - } - - /** - * ICD is shared among ULPs. Use icd_count of any one loaded ULP - **/ - for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) - if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) - break; - icd_count = phba->fw_config.iscsi_icd_count[ulp_num]; - if (icd_count == 0 || icd_count > 65536) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BG_%d: invalid ICD count %d\n", icd_count); - goto fail_init; - } - - cid_count = BEISCSI_GET_CID_COUNT(phba, BEISCSI_ULP0) + - BEISCSI_GET_CID_COUNT(phba, BEISCSI_ULP1); - if (cid_count == 0 || cid_count > 4096) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BG_%d: invalid CID count %d\n", cid_count); - goto fail_init; - } - - /** - * Check FW is dual ULP aware i.e. can handle either - * of the protocols. - */ - phba->fw_config.dual_ulp_aware = (pfw_cfg->function_mode & - BEISCSI_FUNC_DUA_MODE); - - beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, - "BG_%d : DUA Mode : 0x%x\n", - phba->fw_config.dual_ulp_aware); - - /* all set, continue using this FW config */ - status = 0; -fail_init: - mutex_unlock(&ctrl->mbox_lock); - return status; -} - -int mgmt_check_supported_fw(struct be_ctrl_info *ctrl, - struct beiscsi_hba *phba) -{ - struct be_dma_mem nonemb_cmd; - struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); - struct be_mgmt_controller_attributes *req; - struct be_sge *sge = nonembedded_sgl(wrb); - int status = 0; - - nonemb_cmd.va = pci_alloc_consistent(ctrl->pdev, - sizeof(struct be_mgmt_controller_attributes), - &nonemb_cmd.dma); - if (nonemb_cmd.va == NULL) { - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BG_%d : Failed to allocate memory for " - "mgmt_check_supported_fw\n"); - return -ENOMEM; - } - nonemb_cmd.size = sizeof(struct be_mgmt_controller_attributes); - req = nonemb_cmd.va; - memset(req, 0, sizeof(*req)); - mutex_lock(&ctrl->mbox_lock); - memset(wrb, 0, sizeof(*wrb)); - be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1); - be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, - OPCODE_COMMON_GET_CNTL_ATTRIBUTES, sizeof(*req)); - sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd.dma)); - sge->pa_lo = cpu_to_le32(nonemb_cmd.dma & 0xFFFFFFFF); - sge->len = cpu_to_le32(nonemb_cmd.size); - status = be_mbox_notify(ctrl); - if (!status) { - struct be_mgmt_controller_attributes_resp *resp = nonemb_cmd.va; - beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, - "BG_%d : Firmware Version of CMD : %s\n" - "Firmware Version is : %s\n" - "Developer Build, not performing version check...\n", - resp->params.hba_attribs - .flashrom_version_string, - resp->params.hba_attribs. - firmware_version_string); - - phba->fw_config.iscsi_features = - resp->params.hba_attribs.iscsi_features; - beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT, - "BM_%d : phba->fw_config.iscsi_features = %d\n", - phba->fw_config.iscsi_features); - memcpy(phba->fw_ver_str, resp->params.hba_attribs. - firmware_version_string, BEISCSI_VER_STRLEN); - } else - beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, - "BG_%d : Failed in mgmt_check_supported_fw\n"); - mutex_unlock(&ctrl->mbox_lock); - if (nonemb_cmd.va) - pci_free_consistent(ctrl->pdev, nonemb_cmd.size, - nonemb_cmd.va, nonemb_cmd.dma); - - return status; -} - unsigned int mgmt_vendor_specific_fw_cmd(struct be_ctrl_info *ctrl, struct beiscsi_hba *phba, struct bsg_job *job, @@ -609,7 +106,7 @@ unsigned int mgmt_vendor_specific_fw_cmd(struct be_ctrl_info *ctrl, bsg_req->rqst_data.h_vendor.vendor_cmd[0]); mutex_unlock(&ctrl->mbox_lock); - return -ENOSYS; + return -EPERM; } wrb = alloc_mcc_wrb(phba, &tag); @@ -631,48 +128,6 @@ unsigned int mgmt_vendor_specific_fw_cmd(struct be_ctrl_info *ctrl, return tag; } -/** - * mgmt_epfw_cleanup()- Inform FW to cleanup data structures. - * @phba: pointer to dev priv structure - * @ulp_num: ULP number. - * - * return - * Success: 0 - * Failure: Non-Zero Value - **/ -int mgmt_epfw_cleanup(struct beiscsi_hba *phba, unsigned short ulp_num) -{ - struct be_ctrl_info *ctrl = &phba->ctrl; - struct be_mcc_wrb *wrb; - struct iscsi_cleanup_req *req; - unsigned int tag; - int status; - - mutex_lock(&ctrl->mbox_lock); - wrb = alloc_mcc_wrb(phba, &tag); - if (!wrb) { - mutex_unlock(&ctrl->mbox_lock); - return -EBUSY; - } - - req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); - be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI, - OPCODE_COMMON_ISCSI_CLEANUP, sizeof(*req)); - - req->chute = (1 << ulp_num); - req->hdr_ring_id = cpu_to_le16(HWI_GET_DEF_HDRQ_ID(phba, ulp_num)); - req->data_ring_id = cpu_to_le16(HWI_GET_DEF_BUFQ_ID(phba, ulp_num)); - - be_mcc_notify(phba, tag); - status = be_mcc_compl_poll(phba, tag); - if (status) - beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_INIT, - "BG_%d : mgmt_epfw_cleanup , FAILED\n"); - mutex_unlock(&ctrl->mbox_lock); - return status; -} - unsigned int mgmt_invalidate_icds(struct beiscsi_hba *phba, struct invalidate_command_table *inv_tbl, unsigned int num_invalidate, unsigned int cid, @@ -844,7 +299,7 @@ int mgmt_open_connection(struct beiscsi_hba *phba, nonemb_cmd->size); if (dst_addr->sa_family == PF_INET) { __be32 s_addr = daddr_in->sin_addr.s_addr; - req->ip_address.ip_type = BE2_IPV4; + req->ip_address.ip_type = BEISCSI_IP_TYPE_V4; req->ip_address.addr[0] = s_addr & 0x000000ff; req->ip_address.addr[1] = (s_addr & 0x0000ff00) >> 8; req->ip_address.addr[2] = (s_addr & 0x00ff0000) >> 16; @@ -852,17 +307,17 @@ int mgmt_open_connection(struct beiscsi_hba *phba, req->tcp_port = ntohs(daddr_in->sin_port); beiscsi_ep->dst_addr = daddr_in->sin_addr.s_addr; beiscsi_ep->dst_tcpport = ntohs(daddr_in->sin_port); - beiscsi_ep->ip_type = BE2_IPV4; + beiscsi_ep->ip_type = BEISCSI_IP_TYPE_V4; } else { /* else its PF_INET6 family */ - req->ip_address.ip_type = BE2_IPV6; + req->ip_address.ip_type = BEISCSI_IP_TYPE_V6; memcpy(&req->ip_address.addr, &daddr_in6->sin6_addr.in6_u.u6_addr8, 16); req->tcp_port = ntohs(daddr_in6->sin6_port); beiscsi_ep->dst_tcpport = ntohs(daddr_in6->sin6_port); memcpy(&beiscsi_ep->dst6_addr, &daddr_in6->sin6_addr.in6_u.u6_addr8, 16); - beiscsi_ep->ip_type = BE2_IPV6; + beiscsi_ep->ip_type = BEISCSI_IP_TYPE_V6; } req->cid = cid; i = phba->nxt_cqid++; @@ -883,7 +338,7 @@ int mgmt_open_connection(struct beiscsi_hba *phba, if (!is_chip_be2_be3r(phba)) { req->hdr.version = MBX_CMD_VER1; - req->tcp_window_size = 0; + req->tcp_window_size = 0x8000; req->tcp_window_scale_count = 2; } @@ -892,44 +347,6 @@ int mgmt_open_connection(struct beiscsi_hba *phba, return tag; } -unsigned int mgmt_get_all_if_id(struct beiscsi_hba *phba) -{ - struct be_ctrl_info *ctrl = &phba->ctrl; - struct be_mcc_wrb *wrb; - struct be_cmd_get_all_if_id_req *req; - struct be_cmd_get_all_if_id_req *pbe_allid; - unsigned int tag; - int status = 0; - - if (mutex_lock_interruptible(&ctrl->mbox_lock)) - return -EINTR; - wrb = alloc_mcc_wrb(phba, &tag); - if (!wrb) { - mutex_unlock(&ctrl->mbox_lock); - return -ENOMEM; - } - - req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); - be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI, - OPCODE_COMMON_ISCSI_NTWK_GET_ALL_IF_ID, - sizeof(*req)); - be_mcc_notify(phba, tag); - mutex_unlock(&ctrl->mbox_lock); - - status = beiscsi_mccq_compl_wait(phba, tag, &wrb, NULL); - if (status) { - beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, - "BG_%d : Failed in mgmt_get_all_if_id\n"); - return -EBUSY; - } - - pbe_allid = embedded_payload(wrb); - phba->interface_handle = pbe_allid->if_hndl_list[0]; - - return status; -} - /* * mgmt_exec_nonemb_cmd()- Execute Non Embedded MBX Cmd * @phba: Driver priv structure @@ -1001,72 +418,68 @@ static int mgmt_alloc_cmd_data(struct beiscsi_hba *phba, struct be_dma_mem *cmd, } cmd->size = size; be_cmd_hdr_prepare(cmd->va, CMD_SUBSYSTEM_ISCSI, iscsi_cmd, size); + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, + "BG_%d : subsystem iSCSI cmd %d size %d\n", + iscsi_cmd, size); return 0; } -static int -mgmt_static_ip_modify(struct beiscsi_hba *phba, - struct be_cmd_get_if_info_resp *if_info, - struct iscsi_iface_param_info *ip_param, - struct iscsi_iface_param_info *subnet_param, - uint32_t ip_action) +unsigned int beiscsi_if_get_handle(struct beiscsi_hba *phba) { - struct be_cmd_set_ip_addr_req *req; - struct be_dma_mem nonemb_cmd; - uint32_t ip_type; - int rc; + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_mcc_wrb *wrb; + struct be_cmd_get_all_if_id_req *req; + struct be_cmd_get_all_if_id_req *pbe_allid; + unsigned int tag; + int status = 0; - rc = mgmt_alloc_cmd_data(phba, &nonemb_cmd, - OPCODE_COMMON_ISCSI_NTWK_MODIFY_IP_ADDR, - sizeof(*req)); - if (rc) - return rc; + if (mutex_lock_interruptible(&ctrl->mbox_lock)) + return -EINTR; + wrb = alloc_mcc_wrb(phba, &tag); + if (!wrb) { + mutex_unlock(&ctrl->mbox_lock); + return -ENOMEM; + } - ip_type = (ip_param->param == ISCSI_NET_PARAM_IPV6_ADDR) ? - BE2_IPV6 : BE2_IPV4 ; + req = embedded_payload(wrb); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI, + OPCODE_COMMON_ISCSI_NTWK_GET_ALL_IF_ID, + sizeof(*req)); + be_mcc_notify(phba, tag); + mutex_unlock(&ctrl->mbox_lock); - req = nonemb_cmd.va; - req->ip_params.record_entry_count = 1; - req->ip_params.ip_record.action = ip_action; - req->ip_params.ip_record.interface_hndl = - phba->interface_handle; - req->ip_params.ip_record.ip_addr.size_of_structure = - sizeof(struct be_ip_addr_subnet_format); - req->ip_params.ip_record.ip_addr.ip_type = ip_type; + status = beiscsi_mccq_compl_wait(phba, tag, &wrb, NULL); + if (status) { + beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, + "BG_%d : %s failed: %d\n", __func__, status); + return -EBUSY; + } - if (ip_action == IP_ACTION_ADD) { - memcpy(req->ip_params.ip_record.ip_addr.addr, ip_param->value, - sizeof(req->ip_params.ip_record.ip_addr.addr)); + pbe_allid = embedded_payload(wrb); + /* we now support only one interface per function */ + phba->interface_handle = pbe_allid->if_hndl_list[0]; - if (subnet_param) - memcpy(req->ip_params.ip_record.ip_addr.subnet_mask, - subnet_param->value, - sizeof(req->ip_params.ip_record.ip_addr.subnet_mask)); - } else { - memcpy(req->ip_params.ip_record.ip_addr.addr, - if_info->ip_addr.addr, - sizeof(req->ip_params.ip_record.ip_addr.addr)); + return status; +} - memcpy(req->ip_params.ip_record.ip_addr.subnet_mask, - if_info->ip_addr.subnet_mask, - sizeof(req->ip_params.ip_record.ip_addr.subnet_mask)); - } +static inline bool beiscsi_if_zero_ip(u8 *ip, u32 ip_type) +{ + u32 len; - rc = mgmt_exec_nonemb_cmd(phba, &nonemb_cmd, NULL, 0); - if (rc < 0) - beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, - "BG_%d : Failed to Modify existing IP Address\n"); - return rc; + len = (ip_type < BEISCSI_IP_TYPE_V6) ? IP_V4_LEN : IP_V6_LEN; + while (len && !ip[len - 1]) + len--; + return (len == 0); } -static int mgmt_modify_gateway(struct beiscsi_hba *phba, uint8_t *gt_addr, - uint32_t gtway_action, uint32_t param_len) +static int beiscsi_if_mod_gw(struct beiscsi_hba *phba, + u32 action, u32 ip_type, u8 *gw) { struct be_cmd_set_def_gateway_req *req; struct be_dma_mem nonemb_cmd; int rt_val; - rt_val = mgmt_alloc_cmd_data(phba, &nonemb_cmd, OPCODE_COMMON_ISCSI_NTWK_MODIFY_DEFAULT_GATEWAY, sizeof(*req)); @@ -1074,200 +487,300 @@ static int mgmt_modify_gateway(struct beiscsi_hba *phba, uint8_t *gt_addr, return rt_val; req = nonemb_cmd.va; - req->action = gtway_action; - req->ip_addr.ip_type = BE2_IPV4; + req->action = action; + req->ip_addr.ip_type = ip_type; + memcpy(req->ip_addr.addr, gw, + (ip_type < BEISCSI_IP_TYPE_V6) ? IP_V4_LEN : IP_V6_LEN); + return mgmt_exec_nonemb_cmd(phba, &nonemb_cmd, NULL, 0); +} - memcpy(req->ip_addr.addr, gt_addr, sizeof(req->ip_addr.addr)); +int beiscsi_if_set_gw(struct beiscsi_hba *phba, u32 ip_type, u8 *gw) +{ + struct be_cmd_get_def_gateway_resp gw_resp; + int rt_val; - return mgmt_exec_nonemb_cmd(phba, &nonemb_cmd, NULL, 0); + memset(&gw_resp, 0, sizeof(gw_resp)); + rt_val = beiscsi_if_get_gw(phba, ip_type, &gw_resp); + if (rt_val) { + beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, + "BG_%d : Failed to Get Gateway Addr\n"); + return rt_val; + } + + if (!beiscsi_if_zero_ip(gw_resp.ip_addr.addr, ip_type)) { + rt_val = beiscsi_if_mod_gw(phba, IP_ACTION_DEL, ip_type, + gw_resp.ip_addr.addr); + if (rt_val) { + beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, + "BG_%d : Failed to clear Gateway Addr Set\n"); + return rt_val; + } + } + + rt_val = beiscsi_if_mod_gw(phba, IP_ACTION_ADD, ip_type, gw); + if (rt_val) + beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, + "BG_%d : Failed to Set Gateway Addr\n"); + + return rt_val; } -int mgmt_set_ip(struct beiscsi_hba *phba, - struct iscsi_iface_param_info *ip_param, - struct iscsi_iface_param_info *subnet_param, - uint32_t boot_proto) +int beiscsi_if_get_gw(struct beiscsi_hba *phba, u32 ip_type, + struct be_cmd_get_def_gateway_resp *resp) { - struct be_cmd_get_def_gateway_resp gtway_addr_set; - struct be_cmd_get_if_info_resp *if_info; - struct be_cmd_set_dhcp_req *dhcpreq; - struct be_cmd_rel_dhcp_req *reldhcp; + struct be_cmd_get_def_gateway_req *req; struct be_dma_mem nonemb_cmd; - uint8_t *gtway_addr; - uint32_t ip_type; int rc; - rc = mgmt_get_all_if_id(phba); + rc = mgmt_alloc_cmd_data(phba, &nonemb_cmd, + OPCODE_COMMON_ISCSI_NTWK_GET_DEFAULT_GATEWAY, + sizeof(*resp)); if (rc) return rc; - ip_type = (ip_param->param == ISCSI_NET_PARAM_IPV6_ADDR) ? - BE2_IPV6 : BE2_IPV4 ; + req = nonemb_cmd.va; + req->ip_type = ip_type; + + return mgmt_exec_nonemb_cmd(phba, &nonemb_cmd, resp, + sizeof(*resp)); +} + +static int +beiscsi_if_clr_ip(struct beiscsi_hba *phba, + struct be_cmd_get_if_info_resp *if_info) +{ + struct be_cmd_set_ip_addr_req *req; + struct be_dma_mem nonemb_cmd; + int rc; - rc = mgmt_get_if_info(phba, ip_type, &if_info); + rc = mgmt_alloc_cmd_data(phba, &nonemb_cmd, + OPCODE_COMMON_ISCSI_NTWK_MODIFY_IP_ADDR, + sizeof(*req)); if (rc) return rc; - if (boot_proto == ISCSI_BOOTPROTO_DHCP) { - if (if_info->dhcp_state) { - beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, - "BG_%d : DHCP Already Enabled\n"); - goto exit; - } - /* The ip_param->len is 1 in DHCP case. Setting - proper IP len as this it is used while - freeing the Static IP. - */ - ip_param->len = (ip_param->param == ISCSI_NET_PARAM_IPV6_ADDR) ? - IP_V6_LEN : IP_V4_LEN; - - } else { - if (if_info->dhcp_state) { + req = nonemb_cmd.va; + req->ip_params.record_entry_count = 1; + req->ip_params.ip_record.action = IP_ACTION_DEL; + req->ip_params.ip_record.interface_hndl = + phba->interface_handle; + req->ip_params.ip_record.ip_addr.size_of_structure = + sizeof(struct be_ip_addr_subnet_format); + req->ip_params.ip_record.ip_addr.ip_type = if_info->ip_addr.ip_type; + memcpy(req->ip_params.ip_record.ip_addr.addr, + if_info->ip_addr.addr, + sizeof(if_info->ip_addr.addr)); + memcpy(req->ip_params.ip_record.ip_addr.subnet_mask, + if_info->ip_addr.subnet_mask, + sizeof(if_info->ip_addr.subnet_mask)); + rc = mgmt_exec_nonemb_cmd(phba, &nonemb_cmd, NULL, 0); + if (rc < 0 || req->ip_params.ip_record.status) { + beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, + "BG_%d : failed to clear IP: rc %d status %d\n", + rc, req->ip_params.ip_record.status); + } + return rc; +} - memset(if_info, 0, sizeof(*if_info)); - rc = mgmt_alloc_cmd_data(phba, &nonemb_cmd, - OPCODE_COMMON_ISCSI_NTWK_REL_STATELESS_IP_ADDR, - sizeof(*reldhcp)); +static int +beiscsi_if_set_ip(struct beiscsi_hba *phba, u8 *ip, + u8 *subnet, u32 ip_type) +{ + struct be_cmd_set_ip_addr_req *req; + struct be_dma_mem nonemb_cmd; + uint32_t ip_len; + int rc; - if (rc) - goto exit; + rc = mgmt_alloc_cmd_data(phba, &nonemb_cmd, + OPCODE_COMMON_ISCSI_NTWK_MODIFY_IP_ADDR, + sizeof(*req)); + if (rc) + return rc; - reldhcp = nonemb_cmd.va; - reldhcp->interface_hndl = phba->interface_handle; - reldhcp->ip_type = ip_type; + req = nonemb_cmd.va; + req->ip_params.record_entry_count = 1; + req->ip_params.ip_record.action = IP_ACTION_ADD; + req->ip_params.ip_record.interface_hndl = + phba->interface_handle; + req->ip_params.ip_record.ip_addr.size_of_structure = + sizeof(struct be_ip_addr_subnet_format); + req->ip_params.ip_record.ip_addr.ip_type = ip_type; + ip_len = (ip_type < BEISCSI_IP_TYPE_V6) ? IP_V4_LEN : IP_V6_LEN; + memcpy(req->ip_params.ip_record.ip_addr.addr, ip, ip_len); + if (subnet) + memcpy(req->ip_params.ip_record.ip_addr.subnet_mask, + subnet, ip_len); - rc = mgmt_exec_nonemb_cmd(phba, &nonemb_cmd, NULL, 0); - if (rc < 0) { - beiscsi_log(phba, KERN_WARNING, - BEISCSI_LOG_CONFIG, - "BG_%d : Failed to Delete existing dhcp\n"); - goto exit; - } - } + rc = mgmt_exec_nonemb_cmd(phba, &nonemb_cmd, NULL, 0); + /** + * In some cases, host needs to look into individual record status + * even though FW reported success for that IOCTL. + */ + if (rc < 0 || req->ip_params.ip_record.status) { + __beiscsi_log(phba, KERN_ERR, + "BG_%d : failed to set IP: rc %d status %d\n", + rc, req->ip_params.ip_record.status); + if (req->ip_params.ip_record.status) + rc = -EINVAL; } + return rc; +} - /* Delete the Static IP Set */ - if (if_info->ip_addr.addr[0]) { - rc = mgmt_static_ip_modify(phba, if_info, ip_param, NULL, - IP_ACTION_DEL); +int beiscsi_if_en_static(struct beiscsi_hba *phba, u32 ip_type, + u8 *ip, u8 *subnet) +{ + struct be_cmd_get_if_info_resp *if_info; + struct be_cmd_rel_dhcp_req *reldhcp; + struct be_dma_mem nonemb_cmd; + int rc; + + rc = beiscsi_if_get_info(phba, ip_type, &if_info); + if (rc) + return rc; + + if (if_info->dhcp_state) { + rc = mgmt_alloc_cmd_data(phba, &nonemb_cmd, + OPCODE_COMMON_ISCSI_NTWK_REL_STATELESS_IP_ADDR, + sizeof(*reldhcp)); if (rc) goto exit; - } - /* Delete the Gateway settings if mode change is to DHCP */ - if (boot_proto == ISCSI_BOOTPROTO_DHCP) { - memset(>way_addr_set, 0, sizeof(gtway_addr_set)); - rc = mgmt_get_gateway(phba, BE2_IPV4, >way_addr_set); - if (rc) { + reldhcp = nonemb_cmd.va; + reldhcp->interface_hndl = phba->interface_handle; + reldhcp->ip_type = ip_type; + rc = mgmt_exec_nonemb_cmd(phba, &nonemb_cmd, NULL, 0); + if (rc < 0) { beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, - "BG_%d : Failed to Get Gateway Addr\n"); + "BG_%d : failed to release existing DHCP: %d\n", + rc); goto exit; } - - if (gtway_addr_set.ip_addr.addr[0]) { - gtway_addr = (uint8_t *)>way_addr_set.ip_addr.addr; - rc = mgmt_modify_gateway(phba, gtway_addr, - IP_ACTION_DEL, IP_V4_LEN); - - if (rc) { - beiscsi_log(phba, KERN_WARNING, - BEISCSI_LOG_CONFIG, - "BG_%d : Failed to clear Gateway Addr Set\n"); - goto exit; - } - } } - /* Set Adapter to DHCP/Static Mode */ - if (boot_proto == ISCSI_BOOTPROTO_DHCP) { - rc = mgmt_alloc_cmd_data(phba, &nonemb_cmd, - OPCODE_COMMON_ISCSI_NTWK_CONFIG_STATELESS_IP_ADDR, - sizeof(*dhcpreq)); + /* first delete any IP set */ + if (!beiscsi_if_zero_ip(if_info->ip_addr.addr, ip_type)) { + rc = beiscsi_if_clr_ip(phba, if_info); if (rc) goto exit; - - dhcpreq = nonemb_cmd.va; - dhcpreq->flags = BLOCKING; - dhcpreq->retry_count = 1; - dhcpreq->interface_hndl = phba->interface_handle; - dhcpreq->ip_type = BE2_DHCP_V4; - - rc = mgmt_exec_nonemb_cmd(phba, &nonemb_cmd, NULL, 0); - } else { - rc = mgmt_static_ip_modify(phba, if_info, ip_param, - subnet_param, IP_ACTION_ADD); } + /* if ip == NULL then this is called just to release DHCP IP */ + if (ip) + rc = beiscsi_if_set_ip(phba, ip, subnet, ip_type); exit: kfree(if_info); return rc; } -int mgmt_set_gateway(struct beiscsi_hba *phba, - struct iscsi_iface_param_info *gateway_param) +int beiscsi_if_en_dhcp(struct beiscsi_hba *phba, u32 ip_type) { - struct be_cmd_get_def_gateway_resp gtway_addr_set; - uint8_t *gtway_addr; - int rt_val; + struct be_cmd_get_def_gateway_resp gw_resp; + struct be_cmd_get_if_info_resp *if_info; + struct be_cmd_set_dhcp_req *dhcpreq; + struct be_dma_mem nonemb_cmd; + u8 *gw; + int rc; - memset(>way_addr_set, 0, sizeof(gtway_addr_set)); - rt_val = mgmt_get_gateway(phba, BE2_IPV4, >way_addr_set); - if (rt_val) { + rc = beiscsi_if_get_info(phba, ip_type, &if_info); + if (rc) + return rc; + + if (if_info->dhcp_state) { beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, - "BG_%d : Failed to Get Gateway Addr\n"); - return rt_val; + "BG_%d : DHCP Already Enabled\n"); + goto exit; } - if (gtway_addr_set.ip_addr.addr[0]) { - gtway_addr = (uint8_t *)>way_addr_set.ip_addr.addr; - rt_val = mgmt_modify_gateway(phba, gtway_addr, IP_ACTION_DEL, - gateway_param->len); - if (rt_val) { + /* first delete any IP set */ + if (!beiscsi_if_zero_ip(if_info->ip_addr.addr, ip_type)) { + rc = beiscsi_if_clr_ip(phba, if_info); + if (rc) + goto exit; + } + + /* delete gateway settings if mode change is to DHCP */ + memset(&gw_resp, 0, sizeof(gw_resp)); + /* use ip_type provided in if_info */ + rc = beiscsi_if_get_gw(phba, if_info->ip_addr.ip_type, &gw_resp); + if (rc) { + beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, + "BG_%d : Failed to Get Gateway Addr\n"); + goto exit; + } + gw = (u8 *)&gw_resp.ip_addr.addr; + if (!beiscsi_if_zero_ip(gw, if_info->ip_addr.ip_type)) { + rc = beiscsi_if_mod_gw(phba, IP_ACTION_DEL, + if_info->ip_addr.ip_type, gw); + if (rc) { beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, "BG_%d : Failed to clear Gateway Addr Set\n"); - return rt_val; + goto exit; } } - gtway_addr = (uint8_t *)&gateway_param->value; - rt_val = mgmt_modify_gateway(phba, gtway_addr, IP_ACTION_ADD, - gateway_param->len); + rc = mgmt_alloc_cmd_data(phba, &nonemb_cmd, + OPCODE_COMMON_ISCSI_NTWK_CONFIG_STATELESS_IP_ADDR, + sizeof(*dhcpreq)); + if (rc) + goto exit; - if (rt_val) - beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, - "BG_%d : Failed to Set Gateway Addr\n"); + dhcpreq = nonemb_cmd.va; + dhcpreq->flags = 1; /* 1 - blocking; 0 - non-blocking */ + dhcpreq->retry_count = 1; + dhcpreq->interface_hndl = phba->interface_handle; + dhcpreq->ip_type = ip_type; + rc = mgmt_exec_nonemb_cmd(phba, &nonemb_cmd, NULL, 0); - return rt_val; +exit: + kfree(if_info); + return rc; } -int mgmt_get_gateway(struct beiscsi_hba *phba, int ip_type, - struct be_cmd_get_def_gateway_resp *gateway) +/** + * beiscsi_if_set_vlan()- Issue and wait for CMD completion + * @phba: device private structure instance + * @vlan_tag: VLAN tag + * + * Issue the MBX Cmd and wait for the completion of the + * command. + * + * returns + * Success: 0 + * Failure: Non-Xero Value + **/ +int beiscsi_if_set_vlan(struct beiscsi_hba *phba, uint16_t vlan_tag) { - struct be_cmd_get_def_gateway_req *req; - struct be_dma_mem nonemb_cmd; int rc; + unsigned int tag; - rc = mgmt_alloc_cmd_data(phba, &nonemb_cmd, - OPCODE_COMMON_ISCSI_NTWK_GET_DEFAULT_GATEWAY, - sizeof(*gateway)); - if (rc) - return rc; - - req = nonemb_cmd.va; - req->ip_type = ip_type; + tag = be_cmd_set_vlan(phba, vlan_tag); + if (!tag) { + beiscsi_log(phba, KERN_ERR, + (BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX), + "BG_%d : VLAN Setting Failed\n"); + return -EBUSY; + } - return mgmt_exec_nonemb_cmd(phba, &nonemb_cmd, gateway, - sizeof(*gateway)); + rc = beiscsi_mccq_compl_wait(phba, tag, NULL, NULL); + if (rc) { + beiscsi_log(phba, KERN_ERR, + (BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX), + "BS_%d : VLAN MBX Cmd Failed\n"); + return rc; + } + return rc; } -int mgmt_get_if_info(struct beiscsi_hba *phba, int ip_type, - struct be_cmd_get_if_info_resp **if_info) + +int beiscsi_if_get_info(struct beiscsi_hba *phba, int ip_type, + struct be_cmd_get_if_info_resp **if_info) { struct be_cmd_get_if_info_req *req; struct be_dma_mem nonemb_cmd; uint32_t ioctl_size = sizeof(struct be_cmd_get_if_info_resp); int rc; - rc = mgmt_get_all_if_id(phba); + rc = beiscsi_if_get_handle(phba); if (rc) return rc; @@ -1364,123 +877,317 @@ unsigned int be_cmd_get_initname(struct beiscsi_hba *phba) return tag; } +static void beiscsi_boot_process_compl(struct beiscsi_hba *phba, + unsigned int tag) +{ + struct be_cmd_get_boot_target_resp *boot_resp; + struct be_cmd_resp_logout_fw_sess *logo_resp; + struct be_cmd_get_session_resp *sess_resp; + struct be_mcc_wrb *wrb; + struct boot_struct *bs; + int boot_work, status; + + if (!test_bit(BEISCSI_HBA_BOOT_WORK, &phba->state)) { + __beiscsi_log(phba, KERN_ERR, + "BG_%d : %s no boot work %lx\n", + __func__, phba->state); + return; + } + + if (phba->boot_struct.tag != tag) { + __beiscsi_log(phba, KERN_ERR, + "BG_%d : %s tag mismatch %d:%d\n", + __func__, tag, phba->boot_struct.tag); + return; + } + bs = &phba->boot_struct; + boot_work = 1; + status = 0; + switch (bs->action) { + case BEISCSI_BOOT_REOPEN_SESS: + status = __beiscsi_mcc_compl_status(phba, tag, NULL, NULL); + if (!status) + bs->action = BEISCSI_BOOT_GET_SHANDLE; + else + bs->retry--; + break; + case BEISCSI_BOOT_GET_SHANDLE: + status = __beiscsi_mcc_compl_status(phba, tag, &wrb, NULL); + if (!status) { + boot_resp = embedded_payload(wrb); + bs->s_handle = boot_resp->boot_session_handle; + } + if (bs->s_handle == BE_BOOT_INVALID_SHANDLE) { + bs->action = BEISCSI_BOOT_REOPEN_SESS; + bs->retry--; + } else { + bs->action = BEISCSI_BOOT_GET_SINFO; + } + break; + case BEISCSI_BOOT_GET_SINFO: + status = __beiscsi_mcc_compl_status(phba, tag, NULL, + &bs->nonemb_cmd); + if (!status) { + sess_resp = bs->nonemb_cmd.va; + memcpy(&bs->boot_sess, &sess_resp->session_info, + sizeof(struct mgmt_session_info)); + bs->action = BEISCSI_BOOT_LOGOUT_SESS; + } else { + __beiscsi_log(phba, KERN_ERR, + "BG_%d : get boot session info error : 0x%x\n", + status); + boot_work = 0; + } + pci_free_consistent(phba->ctrl.pdev, bs->nonemb_cmd.size, + bs->nonemb_cmd.va, bs->nonemb_cmd.dma); + bs->nonemb_cmd.va = NULL; + break; + case BEISCSI_BOOT_LOGOUT_SESS: + status = __beiscsi_mcc_compl_status(phba, tag, &wrb, NULL); + if (!status) { + logo_resp = embedded_payload(wrb); + if (logo_resp->session_status != BE_SESS_STATUS_CLOSE) { + __beiscsi_log(phba, KERN_ERR, + "BG_%d : FW boot session logout error : 0x%x\n", + logo_resp->session_status); + } + } + /* continue to create boot_kset even if logout failed? */ + bs->action = BEISCSI_BOOT_CREATE_KSET; + break; + default: + break; + } + + /* clear the tag so no other completion matches this tag */ + bs->tag = 0; + if (!bs->retry) { + boot_work = 0; + __beiscsi_log(phba, KERN_ERR, + "BG_%d : failed to setup boot target: status %d action %d\n", + status, bs->action); + } + if (!boot_work) { + /* wait for next event to start boot_work */ + clear_bit(BEISCSI_HBA_BOOT_WORK, &phba->state); + return; + } + schedule_work(&phba->boot_work); +} + /** - * be_mgmt_get_boot_shandle()- Get the session handle - * @phba: device priv structure instance - * @s_handle: session handle returned for boot session. + * beiscsi_boot_logout_sess()- Logout from boot FW session + * @phba: Device priv structure instance * - * Get the boot target session handle. In case of - * crashdump mode driver has to issue and MBX Cmd - * for FW to login to boot target + * return + * the TAG used for MBOX Command + * + */ +unsigned int beiscsi_boot_logout_sess(struct beiscsi_hba *phba) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_mcc_wrb *wrb; + struct be_cmd_req_logout_fw_sess *req; + unsigned int tag; + + mutex_lock(&ctrl->mbox_lock); + wrb = alloc_mcc_wrb(phba, &tag); + if (!wrb) { + mutex_unlock(&ctrl->mbox_lock); + return 0; + } + + req = embedded_payload(wrb); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI, + OPCODE_ISCSI_INI_SESSION_LOGOUT_TARGET, + sizeof(struct be_cmd_req_logout_fw_sess)); + /* Use the session handle copied into boot_sess */ + req->session_handle = phba->boot_struct.boot_sess.session_handle; + + phba->boot_struct.tag = tag; + set_bit(MCC_TAG_STATE_ASYNC, &ctrl->ptag_state[tag].tag_state); + ctrl->ptag_state[tag].cbfn = beiscsi_boot_process_compl; + + be_mcc_notify(phba, tag); + mutex_unlock(&ctrl->mbox_lock); + + return tag; +} +/** + * beiscsi_boot_reopen_sess()- Reopen boot session + * @phba: Device priv structure instance * * return - * Success: 0 - * Failure: Non-Zero value + * the TAG used for MBOX Command * **/ -int be_mgmt_get_boot_shandle(struct beiscsi_hba *phba, - unsigned int *s_handle) +unsigned int beiscsi_boot_reopen_sess(struct beiscsi_hba *phba) { - struct be_cmd_get_boot_target_resp *boot_resp; + struct be_ctrl_info *ctrl = &phba->ctrl; struct be_mcc_wrb *wrb; + struct be_cmd_reopen_session_req *req; unsigned int tag; - uint8_t boot_retry = 3; - int rc; - do { - /* Get the Boot Target Session Handle and Count*/ - tag = mgmt_get_boot_target(phba); - if (!tag) { - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_CONFIG | BEISCSI_LOG_INIT, - "BG_%d : Getting Boot Target Info Failed\n"); - return -EAGAIN; - } + mutex_lock(&ctrl->mbox_lock); + wrb = alloc_mcc_wrb(phba, &tag); + if (!wrb) { + mutex_unlock(&ctrl->mbox_lock); + return 0; + } - rc = beiscsi_mccq_compl_wait(phba, tag, &wrb, NULL); - if (rc) { - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, - "BG_%d : MBX CMD get_boot_target Failed\n"); - return -EBUSY; - } + req = embedded_payload(wrb); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI, + OPCODE_ISCSI_INI_DRIVER_REOPEN_ALL_SESSIONS, + sizeof(struct be_cmd_reopen_session_resp)); + req->reopen_type = BE_REOPEN_BOOT_SESSIONS; + req->session_handle = BE_BOOT_INVALID_SHANDLE; - boot_resp = embedded_payload(wrb); + phba->boot_struct.tag = tag; + set_bit(MCC_TAG_STATE_ASYNC, &ctrl->ptag_state[tag].tag_state); + ctrl->ptag_state[tag].cbfn = beiscsi_boot_process_compl; - /* Check if the there are any Boot targets configured */ - if (!boot_resp->boot_session_count) { - beiscsi_log(phba, KERN_INFO, - BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, - "BG_%d ;No boot targets configured\n"); - return -ENXIO; - } + be_mcc_notify(phba, tag); + mutex_unlock(&ctrl->mbox_lock); + return tag; +} - /* FW returns the session handle of the boot session */ - if (boot_resp->boot_session_handle != INVALID_SESS_HANDLE) { - *s_handle = boot_resp->boot_session_handle; - return 0; - } - /* Issue MBX Cmd to FW to login to the boot target */ - tag = mgmt_reopen_session(phba, BE_REOPEN_BOOT_SESSIONS, - INVALID_SESS_HANDLE); - if (!tag) { - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, - "BG_%d : mgmt_reopen_session Failed\n"); - return -EAGAIN; - } +/** + * beiscsi_boot_get_sinfo()- Get boot session info + * @phba: device priv structure instance + * + * Fetches the boot_struct.s_handle info from FW. + * return + * the TAG used for MBOX Command + * + **/ +unsigned int beiscsi_boot_get_sinfo(struct beiscsi_hba *phba) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_cmd_get_session_resp *resp; + struct be_cmd_get_session_req *req; + struct be_dma_mem *nonemb_cmd; + struct be_mcc_wrb *wrb; + struct be_sge *sge; + unsigned int tag; - rc = beiscsi_mccq_compl_wait(phba, tag, NULL, NULL); - if (rc) { - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, - "BG_%d : mgmt_reopen_session Failed"); - return rc; - } - } while (--boot_retry); + mutex_lock(&ctrl->mbox_lock); + wrb = alloc_mcc_wrb(phba, &tag); + if (!wrb) { + mutex_unlock(&ctrl->mbox_lock); + return 0; + } - /* Couldn't log into the boot target */ - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, - "BG_%d : Login to Boot Target Failed\n"); - return -ENXIO; + nonemb_cmd = &phba->boot_struct.nonemb_cmd; + nonemb_cmd->size = sizeof(*resp); + nonemb_cmd->va = pci_alloc_consistent(phba->ctrl.pdev, + sizeof(nonemb_cmd->size), + &nonemb_cmd->dma); + if (!nonemb_cmd->va) { + mutex_unlock(&ctrl->mbox_lock); + return 0; + } + + req = nonemb_cmd->va; + memset(req, 0, sizeof(*req)); + sge = nonembedded_sgl(wrb); + be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI, + OPCODE_ISCSI_INI_SESSION_GET_A_SESSION, + sizeof(*resp)); + req->session_handle = phba->boot_struct.s_handle; + sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd->dma)); + sge->pa_lo = cpu_to_le32(nonemb_cmd->dma & 0xFFFFFFFF); + sge->len = cpu_to_le32(nonemb_cmd->size); + + phba->boot_struct.tag = tag; + set_bit(MCC_TAG_STATE_ASYNC, &ctrl->ptag_state[tag].tag_state); + ctrl->ptag_state[tag].cbfn = beiscsi_boot_process_compl; + + be_mcc_notify(phba, tag); + mutex_unlock(&ctrl->mbox_lock); + return tag; +} + +unsigned int __beiscsi_boot_get_shandle(struct beiscsi_hba *phba, int async) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_mcc_wrb *wrb; + struct be_cmd_get_boot_target_req *req; + unsigned int tag; + + mutex_lock(&ctrl->mbox_lock); + wrb = alloc_mcc_wrb(phba, &tag); + if (!wrb) { + mutex_unlock(&ctrl->mbox_lock); + return 0; + } + + req = embedded_payload(wrb); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI, + OPCODE_ISCSI_INI_BOOT_GET_BOOT_TARGET, + sizeof(struct be_cmd_get_boot_target_resp)); + + if (async) { + phba->boot_struct.tag = tag; + set_bit(MCC_TAG_STATE_ASYNC, &ctrl->ptag_state[tag].tag_state); + ctrl->ptag_state[tag].cbfn = beiscsi_boot_process_compl; + } + + be_mcc_notify(phba, tag); + mutex_unlock(&ctrl->mbox_lock); + return tag; } /** - * mgmt_set_vlan()- Issue and wait for CMD completion - * @phba: device private structure instance - * @vlan_tag: VLAN tag + * beiscsi_boot_get_shandle()- Get boot session handle + * @phba: device priv structure instance + * @s_handle: session handle returned for boot session. * - * Issue the MBX Cmd and wait for the completion of the - * command. + * return + * Success: 1 + * Failure: negative * - * returns - * Success: 0 - * Failure: Non-Xero Value **/ -int mgmt_set_vlan(struct beiscsi_hba *phba, - uint16_t vlan_tag) +int beiscsi_boot_get_shandle(struct beiscsi_hba *phba, unsigned int *s_handle) { - int rc; + struct be_cmd_get_boot_target_resp *boot_resp; + struct be_mcc_wrb *wrb; unsigned int tag; + int rc; - tag = be_cmd_set_vlan(phba, vlan_tag); + *s_handle = BE_BOOT_INVALID_SHANDLE; + /* get configured boot session count and handle */ + tag = __beiscsi_boot_get_shandle(phba, 0); if (!tag) { beiscsi_log(phba, KERN_ERR, - (BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX), - "BG_%d : VLAN Setting Failed\n"); - return -EBUSY; + BEISCSI_LOG_CONFIG | BEISCSI_LOG_INIT, + "BG_%d : Getting Boot Target Info Failed\n"); + return -EAGAIN; } - rc = beiscsi_mccq_compl_wait(phba, tag, NULL, NULL); + rc = beiscsi_mccq_compl_wait(phba, tag, &wrb, NULL); if (rc) { beiscsi_log(phba, KERN_ERR, - (BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX), - "BS_%d : VLAN MBX Cmd Failed\n"); - return rc; + BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, + "BG_%d : MBX CMD get_boot_target Failed\n"); + return -EBUSY; } - return rc; + + boot_resp = embedded_payload(wrb); + /* check if there are any boot targets configured */ + if (!boot_resp->boot_session_count) { + __beiscsi_log(phba, KERN_INFO, + "BG_%d : No boot targets configured\n"); + return -ENXIO; + } + + /* only if FW has logged in to the boot target, s_handle is valid */ + *s_handle = boot_resp->boot_session_handle; + return 1; } /** @@ -1645,7 +1352,6 @@ void beiscsi_offload_cxn_v0(struct beiscsi_offload_params *params, { struct iscsi_wrb *pwrb = pwrb_handle->pwrb; - memset(pwrb, 0, sizeof(*pwrb)); AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, max_send_data_segment_length, pwrb, params->dw[offsetof(struct amap_beiscsi_offload_params, @@ -1717,8 +1423,6 @@ void beiscsi_offload_cxn_v2(struct beiscsi_offload_params *params, { struct iscsi_wrb *pwrb = pwrb_handle->pwrb; - memset(pwrb, 0, sizeof(*pwrb)); - AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2, max_burst_length, pwrb, params->dw[offsetof (struct amap_beiscsi_offload_params, @@ -1790,70 +1494,3 @@ void beiscsi_offload_cxn_v2(struct beiscsi_offload_params *params, (params->dw[offsetof(struct amap_beiscsi_offload_params, exp_statsn) / 32] + 1)); } - -/** - * beiscsi_logout_fw_sess()- Firmware Session Logout - * @phba: Device priv structure instance - * @fw_sess_handle: FW session handle - * - * Logout from the FW established sessions. - * returns - * Success: 0 - * Failure: Non-Zero Value - * - */ -int beiscsi_logout_fw_sess(struct beiscsi_hba *phba, - uint32_t fw_sess_handle) -{ - struct be_ctrl_info *ctrl = &phba->ctrl; - struct be_mcc_wrb *wrb; - struct be_cmd_req_logout_fw_sess *req; - struct be_cmd_resp_logout_fw_sess *resp; - unsigned int tag; - int rc; - - beiscsi_log(phba, KERN_INFO, - BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, - "BG_%d : In bescsi_logout_fwboot_sess\n"); - - mutex_lock(&ctrl->mbox_lock); - wrb = alloc_mcc_wrb(phba, &tag); - if (!wrb) { - mutex_unlock(&ctrl->mbox_lock); - beiscsi_log(phba, KERN_INFO, - BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, - "BG_%d : MBX Tag Failure\n"); - return -EINVAL; - } - - req = embedded_payload(wrb); - be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); - be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI, - OPCODE_ISCSI_INI_SESSION_LOGOUT_TARGET, - sizeof(struct be_cmd_req_logout_fw_sess)); - - /* Set the session handle */ - req->session_handle = fw_sess_handle; - be_mcc_notify(phba, tag); - mutex_unlock(&ctrl->mbox_lock); - - rc = beiscsi_mccq_compl_wait(phba, tag, &wrb, NULL); - if (rc) { - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, - "BG_%d : MBX CMD FW_SESSION_LOGOUT_TARGET Failed\n"); - return -EBUSY; - } - - resp = embedded_payload(wrb); - if (resp->session_status != - BEISCSI_MGMT_SESSION_CLOSE) { - beiscsi_log(phba, KERN_ERR, - BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, - "BG_%d : FW_SESSION_LOGOUT_TARGET resp : 0x%x\n", - resp->session_status); - rc = -EINVAL; - } - - return rc; -} diff --git a/drivers/scsi/be2iscsi/be_mgmt.h b/drivers/scsi/be2iscsi/be_mgmt.h index f3a48a04b2ca..b897cfd57c72 100644 --- a/drivers/scsi/be2iscsi/be_mgmt.h +++ b/drivers/scsi/be2iscsi/be_mgmt.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Emulex + * Copyright (C) 2005 - 2016 Broadcom * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -7,10 +7,10 @@ * as published by the Free Software Foundation. The full GNU General * Public License is included in this distribution in the file called COPYING. * - * Written by: Jayamohan Kallickal (jayamohan.kallickal@avagotech.com) + * Written by: Jayamohan Kallickal (jayamohan.kallickal@broadcom.com) * * Contact Information: - * linux-drivers@avagotech.com + * linux-drivers@broadcom.com * * Emulex * 3333 Susan Street @@ -96,7 +96,6 @@ struct mcc_wrb { struct mcc_wrb_payload payload; }; -int mgmt_epfw_cleanup(struct beiscsi_hba *phba, unsigned short chute); int mgmt_open_connection(struct beiscsi_hba *phba, struct sockaddr *dst_addr, struct beiscsi_endpoint *beiscsi_ep, @@ -266,50 +265,41 @@ struct beiscsi_endpoint { u16 cid_vld; }; -int mgmt_get_fw_config(struct be_ctrl_info *ctrl, - struct beiscsi_hba *phba); -int mgmt_get_port_name(struct be_ctrl_info *ctrl, - struct beiscsi_hba *phba); - unsigned int mgmt_invalidate_connection(struct beiscsi_hba *phba, struct beiscsi_endpoint *beiscsi_ep, unsigned short cid, unsigned short issue_reset, unsigned short savecfg_flag); -int mgmt_set_ip(struct beiscsi_hba *phba, - struct iscsi_iface_param_info *ip_param, - struct iscsi_iface_param_info *subnet_param, - uint32_t boot_proto); +int beiscsi_if_en_dhcp(struct beiscsi_hba *phba, u32 ip_type); -unsigned int mgmt_get_boot_target(struct beiscsi_hba *phba); +int beiscsi_if_en_static(struct beiscsi_hba *phba, u32 ip_type, + u8 *ip, u8 *subnet); -unsigned int mgmt_reopen_session(struct beiscsi_hba *phba, - unsigned int reopen_type, - unsigned sess_handle); +int beiscsi_if_set_gw(struct beiscsi_hba *phba, u32 ip_type, u8 *gw); -unsigned int mgmt_get_session_info(struct beiscsi_hba *phba, - u32 boot_session_handle, - struct be_dma_mem *nonemb_cmd); +int beiscsi_if_get_gw(struct beiscsi_hba *phba, u32 ip_type, + struct be_cmd_get_def_gateway_resp *resp); int mgmt_get_nic_conf(struct beiscsi_hba *phba, struct be_cmd_get_nic_conf_resp *mac); -int mgmt_get_if_info(struct beiscsi_hba *phba, int ip_type, - struct be_cmd_get_if_info_resp **if_info); +int beiscsi_if_get_info(struct beiscsi_hba *phba, int ip_type, + struct be_cmd_get_if_info_resp **if_info); + +unsigned int beiscsi_if_get_handle(struct beiscsi_hba *phba); + +int beiscsi_if_set_vlan(struct beiscsi_hba *phba, uint16_t vlan_tag); -int mgmt_get_gateway(struct beiscsi_hba *phba, int ip_type, - struct be_cmd_get_def_gateway_resp *gateway); +unsigned int beiscsi_boot_logout_sess(struct beiscsi_hba *phba); -int mgmt_set_gateway(struct beiscsi_hba *phba, - struct iscsi_iface_param_info *gateway_param); +unsigned int beiscsi_boot_reopen_sess(struct beiscsi_hba *phba); -int be_mgmt_get_boot_shandle(struct beiscsi_hba *phba, - unsigned int *s_handle); +unsigned int beiscsi_boot_get_sinfo(struct beiscsi_hba *phba); -unsigned int mgmt_get_all_if_id(struct beiscsi_hba *phba); +unsigned int __beiscsi_boot_get_shandle(struct beiscsi_hba *phba, int async); -int mgmt_set_vlan(struct beiscsi_hba *phba, uint16_t vlan_tag); +int beiscsi_boot_get_shandle(struct beiscsi_hba *phba, unsigned int *s_handle); ssize_t beiscsi_drvr_ver_disp(struct device *dev, struct device_attribute *attr, char *buf); @@ -339,7 +329,6 @@ void beiscsi_offload_cxn_v2(struct beiscsi_offload_params *params, struct wrb_handle *pwrb_handle, struct hwi_wrb_context *pwrb_context); -void beiscsi_ue_detect(struct beiscsi_hba *phba); int be_cmd_modify_eq_delay(struct beiscsi_hba *phba, struct be_set_eqd *, int num); diff --git a/drivers/scsi/bfa/bfa_fcs_lport.c b/drivers/scsi/bfa/bfa_fcs_lport.c index 7733ad5305d4..4ddda72f60e6 100644 --- a/drivers/scsi/bfa/bfa_fcs_lport.c +++ b/drivers/scsi/bfa/bfa_fcs_lport.c @@ -5827,13 +5827,13 @@ bfa_fcs_lport_get_rport_max_speed(bfa_fcs_lport_t *port) bfa_port_speed_t max_speed = 0; struct bfa_port_attr_s port_attr; bfa_port_speed_t port_speed, rport_speed; - bfa_boolean_t trl_enabled = bfa_fcport_is_ratelim(port->fcs->bfa); - + bfa_boolean_t trl_enabled; if (port == NULL) return 0; fcs = port->fcs; + trl_enabled = bfa_fcport_is_ratelim(port->fcs->bfa); /* Get Physical port's current speed */ bfa_fcport_get_attr(port->fcs->bfa, &port_attr); diff --git a/drivers/scsi/bnx2fc/bnx2fc_els.c b/drivers/scsi/bnx2fc/bnx2fc_els.c index 5beea776b9f5..68ca518d34b0 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_els.c +++ b/drivers/scsi/bnx2fc/bnx2fc_els.c @@ -254,7 +254,7 @@ int bnx2fc_send_rls(struct bnx2fc_rport *tgt, struct fc_frame *fp) return rc; } -void bnx2fc_srr_compl(struct bnx2fc_els_cb_arg *cb_arg) +static void bnx2fc_srr_compl(struct bnx2fc_els_cb_arg *cb_arg) { struct bnx2fc_mp_req *mp_req; struct fc_frame_header *fc_hdr, *fh; @@ -364,7 +364,7 @@ srr_compl_done: kref_put(&orig_io_req->refcount, bnx2fc_cmd_release); } -void bnx2fc_rec_compl(struct bnx2fc_els_cb_arg *cb_arg) +static void bnx2fc_rec_compl(struct bnx2fc_els_cb_arg *cb_arg) { struct bnx2fc_cmd *orig_io_req, *new_io_req; struct bnx2fc_cmd *rec_req; diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index a5052dd8d7e6..f9ddb6156f14 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -625,7 +625,7 @@ static void bnx2fc_recv_frame(struct sk_buff *skb) * * @arg: ptr to bnx2fc_percpu_info structure */ -int bnx2fc_percpu_io_thread(void *arg) +static int bnx2fc_percpu_io_thread(void *arg) { struct bnx2fc_percpu_s *p = arg; struct bnx2fc_work *work, *tmp; @@ -1410,9 +1410,10 @@ bind_err: return NULL; } -struct bnx2fc_interface *bnx2fc_interface_create(struct bnx2fc_hba *hba, - struct net_device *netdev, - enum fip_state fip_mode) +static struct bnx2fc_interface * +bnx2fc_interface_create(struct bnx2fc_hba *hba, + struct net_device *netdev, + enum fip_state fip_mode) { struct fcoe_ctlr_device *ctlr_dev; struct bnx2fc_interface *interface; @@ -2765,8 +2766,7 @@ static void __exit bnx2fc_mod_exit(void) * held. */ mutex_lock(&bnx2fc_dev_lock); - list_splice(&adapter_list, &to_be_deleted); - INIT_LIST_HEAD(&adapter_list); + list_splice_init(&adapter_list, &to_be_deleted); adapter_count = 0; mutex_unlock(&bnx2fc_dev_lock); diff --git a/drivers/scsi/bnx2fc/bnx2fc_hwi.c b/drivers/scsi/bnx2fc/bnx2fc_hwi.c index 28c671b609b2..5ff9f89c17c7 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_hwi.c +++ b/drivers/scsi/bnx2fc/bnx2fc_hwi.c @@ -994,7 +994,7 @@ void bnx2fc_arm_cq(struct bnx2fc_rport *tgt) } -struct bnx2fc_work *bnx2fc_alloc_work(struct bnx2fc_rport *tgt, u16 wqe) +static struct bnx2fc_work *bnx2fc_alloc_work(struct bnx2fc_rport *tgt, u16 wqe) { struct bnx2fc_work *work; work = kzalloc(sizeof(struct bnx2fc_work), GFP_ATOMIC); diff --git a/drivers/scsi/bnx2fc/bnx2fc_io.c b/drivers/scsi/bnx2fc/bnx2fc_io.c index 8f24d60f09d7..f501095f91ac 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_io.c +++ b/drivers/scsi/bnx2fc/bnx2fc_io.c @@ -1079,7 +1079,7 @@ int bnx2fc_eh_device_reset(struct scsi_cmnd *sc_cmd) return bnx2fc_initiate_tmf(sc_cmd, FCP_TMF_LUN_RESET); } -int bnx2fc_abts_cleanup(struct bnx2fc_cmd *io_req) +static int bnx2fc_abts_cleanup(struct bnx2fc_cmd *io_req) { struct bnx2fc_rport *tgt = io_req->tgt; int rc = SUCCESS; diff --git a/drivers/scsi/csiostor/csio_scsi.c b/drivers/scsi/csiostor/csio_scsi.c index c2a6f9f29427..89a52b941ea8 100644 --- a/drivers/scsi/csiostor/csio_scsi.c +++ b/drivers/scsi/csiostor/csio_scsi.c @@ -1721,7 +1721,7 @@ out: /* Wake up waiting threads */ csio_scsi_cmnd(req) = NULL; - complete_all(&req->cmplobj); + complete(&req->cmplobj); } /* @@ -1945,6 +1945,7 @@ csio_eh_abort_handler(struct scsi_cmnd *cmnd) ready = csio_is_lnode_ready(ln); tmo = CSIO_SCSI_ABRT_TMO_MS; + reinit_completion(&ioreq->cmplobj); spin_lock_irq(&hw->lock); rv = csio_do_abrt_cls(hw, ioreq, (ready ? SCSI_ABORT : SCSI_CLOSE)); spin_unlock_irq(&hw->lock); @@ -1964,8 +1965,6 @@ csio_eh_abort_handler(struct scsi_cmnd *cmnd) goto inval_scmnd; } - /* Wait for completion */ - init_completion(&ioreq->cmplobj); wait_for_completion_timeout(&ioreq->cmplobj, msecs_to_jiffies(tmo)); /* FW didnt respond to abort within our timeout */ diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index 661bb94e2548..b301655f91cd 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -823,17 +823,6 @@ static void notify_shutdown(struct cxlflash_cfg *cfg, bool wait) } /** - * cxlflash_shutdown() - shutdown handler - * @pdev: PCI device associated with the host. - */ -static void cxlflash_shutdown(struct pci_dev *pdev) -{ - struct cxlflash_cfg *cfg = pci_get_drvdata(pdev); - - notify_shutdown(cfg, false); -} - -/** * cxlflash_remove() - PCI entry point to tear down host * @pdev: PCI device associated with the host. * @@ -844,6 +833,11 @@ static void cxlflash_remove(struct pci_dev *pdev) struct cxlflash_cfg *cfg = pci_get_drvdata(pdev); ulong lock_flags; + if (!pci_is_enabled(pdev)) { + pr_debug("%s: Device is disabled\n", __func__); + return; + } + /* If a Task Management Function is active, wait for it to complete * before continuing with remove. */ @@ -1046,6 +1040,8 @@ static int wait_port_online(__be64 __iomem *fc_regs, u32 delay_us, u32 nretry) do { msleep(delay_us / 1000); status = readq_be(&fc_regs[FC_MTIP_STATUS / 8]); + if (status == U64_MAX) + nretry /= 2; } while ((status & FC_MTIP_STATUS_MASK) != FC_MTIP_STATUS_ONLINE && nretry--); @@ -1077,6 +1073,8 @@ static int wait_port_offline(__be64 __iomem *fc_regs, u32 delay_us, u32 nretry) do { msleep(delay_us / 1000); status = readq_be(&fc_regs[FC_MTIP_STATUS / 8]); + if (status == U64_MAX) + nretry /= 2; } while ((status & FC_MTIP_STATUS_MASK) != FC_MTIP_STATUS_OFFLINE && nretry--); @@ -1095,42 +1093,25 @@ static int wait_port_offline(__be64 __iomem *fc_regs, u32 delay_us, u32 nretry) * online. This toggling action can cause this routine to delay up to a few * seconds. When configured to use the internal LUN feature of the AFU, a * failure to come online is overridden. - * - * Return: - * 0 when the WWPN is successfully written and the port comes back online - * -1 when the port fails to go offline or come back up online */ -static int afu_set_wwpn(struct afu *afu, int port, __be64 __iomem *fc_regs, - u64 wwpn) +static void afu_set_wwpn(struct afu *afu, int port, __be64 __iomem *fc_regs, + u64 wwpn) { - int rc = 0; - set_port_offline(fc_regs); - if (!wait_port_offline(fc_regs, FC_PORT_STATUS_RETRY_INTERVAL_US, FC_PORT_STATUS_RETRY_CNT)) { pr_debug("%s: wait on port %d to go offline timed out\n", __func__, port); - rc = -1; /* but continue on to leave the port back online */ } - if (rc == 0) - writeq_be(wwpn, &fc_regs[FC_PNAME / 8]); - - /* Always return success after programming WWPN */ - rc = 0; + writeq_be(wwpn, &fc_regs[FC_PNAME / 8]); set_port_online(fc_regs); - if (!wait_port_online(fc_regs, FC_PORT_STATUS_RETRY_INTERVAL_US, FC_PORT_STATUS_RETRY_CNT)) { - pr_err("%s: wait on port %d to go online timed out\n", - __func__, port); + pr_debug("%s: wait on port %d to go online timed out\n", + __func__, port); } - - pr_debug("%s: returning rc=%d\n", __func__, rc); - - return rc; } /** @@ -1187,7 +1168,7 @@ static const struct asyc_intr_info ainfo[] = { {SISL_ASTATUS_FC0_LOGI_F, "login failed", 0, CLR_FC_ERROR}, {SISL_ASTATUS_FC0_LOGI_S, "login succeeded", 0, SCAN_HOST}, {SISL_ASTATUS_FC0_LINK_DN, "link down", 0, 0}, - {SISL_ASTATUS_FC0_LINK_UP, "link up", 0, SCAN_HOST}, + {SISL_ASTATUS_FC0_LINK_UP, "link up", 0, 0}, {SISL_ASTATUS_FC1_OTHER, "other error", 1, CLR_FC_ERROR | LINK_RESET}, {SISL_ASTATUS_FC1_LOGO, "target initiated LOGO", 1, 0}, {SISL_ASTATUS_FC1_CRC_T, "CRC threshold exceeded", 1, LINK_RESET}, @@ -1195,7 +1176,7 @@ static const struct asyc_intr_info ainfo[] = { {SISL_ASTATUS_FC1_LOGI_F, "login failed", 1, CLR_FC_ERROR}, {SISL_ASTATUS_FC1_LOGI_S, "login succeeded", 1, SCAN_HOST}, {SISL_ASTATUS_FC1_LINK_DN, "link down", 1, 0}, - {SISL_ASTATUS_FC1_LINK_UP, "link up", 1, SCAN_HOST}, + {SISL_ASTATUS_FC1_LINK_UP, "link up", 1, 0}, {0x0, "", 0, 0} /* terminator */ }; @@ -1631,15 +1612,10 @@ static int init_global(struct cxlflash_cfg *cfg) [FC_CRC_THRESH / 8]); /* Set WWPNs. If already programmed, wwpn[i] is 0 */ - if (wwpn[i] != 0 && - afu_set_wwpn(afu, i, - &afu->afu_map->global.fc_regs[i][0], - wwpn[i])) { - dev_err(dev, "%s: failed to set WWPN on port %d\n", - __func__, i); - rc = -EIO; - goto out; - } + if (wwpn[i] != 0) + afu_set_wwpn(afu, i, + &afu->afu_map->global.fc_regs[i][0], + wwpn[i]); /* Programming WWPN back to back causes additional * offline/online transitions and a PLOGI */ @@ -2048,6 +2024,11 @@ retry: * cxlflash_eh_host_reset_handler() - reset the host adapter * @scp: SCSI command from stack identifying host. * + * Following a reset, the state is evaluated again in case an EEH occurred + * during the reset. In such a scenario, the host reset will either yield + * until the EEH recovery is complete or return success or failure based + * upon the current device state. + * * Return: * SUCCESS as defined in scsi/scsi.h * FAILED as defined in scsi/scsi.h @@ -2080,7 +2061,8 @@ static int cxlflash_eh_host_reset_handler(struct scsi_cmnd *scp) } else cfg->state = STATE_NORMAL; wake_up_all(&cfg->reset_waitq); - break; + ssleep(1); + /* fall through */ case STATE_RESET: wait_event(cfg->reset_waitq, cfg->state != STATE_RESET); if (cfg->state == STATE_NORMAL) @@ -2596,6 +2578,9 @@ out_remove: * @pdev: PCI device struct. * @state: PCI channel state. * + * When an EEH occurs during an active reset, wait until the reset is + * complete and then take action based upon the device state. + * * Return: PCI_ERS_RESULT_NEED_RESET or PCI_ERS_RESULT_DISCONNECT */ static pci_ers_result_t cxlflash_pci_error_detected(struct pci_dev *pdev, @@ -2609,6 +2594,10 @@ static pci_ers_result_t cxlflash_pci_error_detected(struct pci_dev *pdev, switch (state) { case pci_channel_io_frozen: + wait_event(cfg->reset_waitq, cfg->state != STATE_RESET); + if (cfg->state == STATE_FAILTERM) + return PCI_ERS_RESULT_DISCONNECT; + cfg->state = STATE_RESET; scsi_block_requests(cfg->host); drain_ioctls(cfg); @@ -2685,7 +2674,7 @@ static struct pci_driver cxlflash_driver = { .id_table = cxlflash_pci_table, .probe = cxlflash_probe, .remove = cxlflash_remove, - .shutdown = cxlflash_shutdown, + .shutdown = cxlflash_remove, .err_handler = &cxlflash_err_handler, }; diff --git a/drivers/scsi/cxlflash/superpipe.c b/drivers/scsi/cxlflash/superpipe.c index ce1507023132..9636970d9611 100644 --- a/drivers/scsi/cxlflash/superpipe.c +++ b/drivers/scsi/cxlflash/superpipe.c @@ -709,14 +709,13 @@ int cxlflash_disk_release(struct scsi_device *sdev, * @cfg: Internal structure associated with the host. * @ctxi: Context to release. * - * This routine is safe to be called with a a non-initialized context - * and is tolerant of being called with the context's mutex held (it - * will be unlocked if necessary before freeing). Also note that the - * routine conditionally checks for the existence of the context control - * map before clearing the RHT registers and context capabilities because - * it is possible to destroy a context while the context is in the error - * state (previous mapping was removed [so there is no need to worry about - * clearing] and context is waiting for a new mapping). + * This routine is safe to be called with a a non-initialized context. + * Also note that the routine conditionally checks for the existence + * of the context control map before clearing the RHT registers and + * context capabilities because it is possible to destroy a context + * while the context is in the error state (previous mapping was + * removed [so there is no need to worry about clearing] and context + * is waiting for a new mapping). */ static void destroy_context(struct cxlflash_cfg *cfg, struct ctx_info *ctxi) @@ -732,9 +731,6 @@ static void destroy_context(struct cxlflash_cfg *cfg, writeq_be(0, &ctxi->ctrl_map->rht_cnt_id); writeq_be(0, &ctxi->ctrl_map->ctx_cap); } - - if (mutex_is_locked(&ctxi->mutex)) - mutex_unlock(&ctxi->mutex); } /* Free memory associated with context */ @@ -792,32 +788,58 @@ err: * @cfg: Internal structure associated with the host. * @ctx: Previously obtained CXL context reference. * @ctxid: Previously obtained process element associated with CXL context. - * @adap_fd: Previously obtained adapter fd associated with CXL context. * @file: Previously obtained file associated with CXL context. * @perms: User-specified permissions. - * - * Upon return, the context is marked as initialized and the context's mutex - * is locked. */ static void init_context(struct ctx_info *ctxi, struct cxlflash_cfg *cfg, - struct cxl_context *ctx, int ctxid, int adap_fd, - struct file *file, u32 perms) + struct cxl_context *ctx, int ctxid, struct file *file, + u32 perms) { struct afu *afu = cfg->afu; ctxi->rht_perms = perms; ctxi->ctrl_map = &afu->afu_map->ctrls[ctxid].ctrl; ctxi->ctxid = ENCODE_CTXID(ctxi, ctxid); - ctxi->lfd = adap_fd; ctxi->pid = current->tgid; /* tgid = pid */ ctxi->ctx = ctx; + ctxi->cfg = cfg; ctxi->file = file; ctxi->initialized = true; mutex_init(&ctxi->mutex); + kref_init(&ctxi->kref); INIT_LIST_HEAD(&ctxi->luns); INIT_LIST_HEAD(&ctxi->list); /* initialize for list_empty() */ +} +/** + * remove_context() - context kref release handler + * @kref: Kernel reference associated with context to be removed. + * + * When a context no longer has any references it can safely be removed + * from global access and destroyed. Note that it is assumed the thread + * relinquishing access to the context holds its mutex. + */ +static void remove_context(struct kref *kref) +{ + struct ctx_info *ctxi = container_of(kref, struct ctx_info, kref); + struct cxlflash_cfg *cfg = ctxi->cfg; + u64 ctxid = DECODE_CTXID(ctxi->ctxid); + + /* Remove context from table/error list */ + WARN_ON(!mutex_is_locked(&ctxi->mutex)); + ctxi->unavail = true; + mutex_unlock(&ctxi->mutex); + mutex_lock(&cfg->ctx_tbl_list_mutex); mutex_lock(&ctxi->mutex); + + if (!list_empty(&ctxi->list)) + list_del(&ctxi->list); + cfg->ctx_tbl[ctxid] = NULL; + mutex_unlock(&cfg->ctx_tbl_list_mutex); + mutex_unlock(&ctxi->mutex); + + /* Context now completely uncoupled/unreachable */ + destroy_context(cfg, ctxi); } /** @@ -845,7 +867,6 @@ static int _cxlflash_disk_detach(struct scsi_device *sdev, int i; int rc = 0; - int lfd; u64 ctxid = DECODE_CTXID(detach->context_id), rctxid = detach->context_id; @@ -887,40 +908,13 @@ static int _cxlflash_disk_detach(struct scsi_device *sdev, break; } - /* Tear down context following last LUN cleanup */ - if (list_empty(&ctxi->luns)) { - ctxi->unavail = true; - mutex_unlock(&ctxi->mutex); - mutex_lock(&cfg->ctx_tbl_list_mutex); - mutex_lock(&ctxi->mutex); - - /* Might not have been in error list so conditionally remove */ - if (!list_empty(&ctxi->list)) - list_del(&ctxi->list); - cfg->ctx_tbl[ctxid] = NULL; - mutex_unlock(&cfg->ctx_tbl_list_mutex); - mutex_unlock(&ctxi->mutex); - - lfd = ctxi->lfd; - destroy_context(cfg, ctxi); - ctxi = NULL; + /* + * Release the context reference and the sdev reference that + * bound this LUN to the context. + */ + if (kref_put(&ctxi->kref, remove_context)) put_ctx = false; - - /* - * As a last step, clean up external resources when not - * already on an external cleanup thread, i.e.: close(adap_fd). - * - * NOTE: this will free up the context from the CXL services, - * allowing it to dole out the same context_id on a future - * (or even currently in-flight) disk_attach operation. - */ - if (lfd != -1) - sys_close(lfd); - } - - /* Release the sdev reference that bound this LUN to the context */ scsi_device_put(sdev); - out: if (put_ctx) put_context(ctxi); @@ -941,34 +935,18 @@ static int cxlflash_disk_detach(struct scsi_device *sdev, * * This routine is the release handler for the fops registered with * the CXL services on an initial attach for a context. It is called - * when a close is performed on the adapter file descriptor returned - * to the user. Programmatically, the user is not required to perform - * the close, as it is handled internally via the detach ioctl when - * a context is being removed. Note that nothing prevents the user - * from performing a close, but the user should be aware that doing - * so is considered catastrophic and subsequent usage of the superpipe - * API with previously saved off tokens will fail. - * - * When initiated from an external close (either by the user or via - * a process tear down), the routine derives the context reference - * and calls detach for each LUN associated with the context. The - * final detach operation will cause the context itself to be freed. - * Note that the saved off lfd is reset prior to calling detach to - * signify that the final detach should not perform a close. - * - * When initiated from a detach operation as part of the tear down - * of a context, the context is first completely freed and then the - * close is performed. This routine will fail to derive the context - * reference (due to the context having already been freed) and then - * call into the CXL release entry point. + * when a close (explicity by the user or as part of a process tear + * down) is performed on the adapter file descriptor returned to the + * user. The user should be aware that explicitly performing a close + * considered catastrophic and subsequent usage of the superpipe API + * with previously saved off tokens will fail. * - * Thus, with exception to when the CXL process element (context id) - * lookup fails (a case that should theoretically never occur), every - * call into this routine results in a complete freeing of a context. - * - * As part of the detach, all per-context resources associated with the LUN - * are cleaned up. When detaching the last LUN for a context, the context - * itself is cleaned up and released. + * This routine derives the context reference and calls detach for + * each LUN associated with the context.The final detach operation + * causes the context itself to be freed. With exception to when the + * CXL process element (context id) lookup fails (a case that should + * theoretically never occur), every call into this routine results + * in a complete freeing of a context. * * Return: 0 on success */ @@ -1006,11 +984,8 @@ static int cxlflash_cxl_release(struct inode *inode, struct file *file) goto out; } - dev_dbg(dev, "%s: close(%d) for context %d\n", - __func__, ctxi->lfd, ctxid); + dev_dbg(dev, "%s: close for context %d\n", __func__, ctxid); - /* Reset the file descriptor to indicate we're on a close() thread */ - ctxi->lfd = -1; detach.context_id = ctxi->ctxid; list_for_each_entry_safe(lun_access, t, &ctxi->luns, list) _cxlflash_disk_detach(lun_access->sdev, ctxi, &detach); @@ -1110,8 +1085,7 @@ static int cxlflash_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) goto err; } - dev_dbg(dev, "%s: fault(%d) for context %d\n", - __func__, ctxi->lfd, ctxid); + dev_dbg(dev, "%s: fault for context %d\n", __func__, ctxid); if (likely(!ctxi->err_recovery_active)) { vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); @@ -1186,8 +1160,7 @@ static int cxlflash_cxl_mmap(struct file *file, struct vm_area_struct *vma) goto out; } - dev_dbg(dev, "%s: mmap(%d) for context %d\n", - __func__, ctxi->lfd, ctxid); + dev_dbg(dev, "%s: mmap for context %d\n", __func__, ctxid); rc = cxl_fd_mmap(file, vma); if (likely(!rc)) { @@ -1377,12 +1350,12 @@ static int cxlflash_disk_attach(struct scsi_device *sdev, lun_access->lli = lli; lun_access->sdev = sdev; - /* Non-NULL context indicates reuse */ + /* Non-NULL context indicates reuse (another context reference) */ if (ctxi) { dev_dbg(dev, "%s: Reusing context for LUN! (%016llX)\n", __func__, rctxid); + kref_get(&ctxi->kref); list_add(&lun_access->list, &ctxi->luns); - fd = ctxi->lfd; goto out_attach; } @@ -1430,7 +1403,7 @@ static int cxlflash_disk_attach(struct scsi_device *sdev, perms = SISL_RHT_PERM(attach->hdr.flags + 1); /* Context mutex is locked upon return */ - init_context(ctxi, cfg, ctx, ctxid, fd, file, perms); + init_context(ctxi, cfg, ctx, ctxid, file, perms); rc = afu_attach(cfg, ctxi); if (unlikely(rc)) { @@ -1445,7 +1418,6 @@ static int cxlflash_disk_attach(struct scsi_device *sdev, * knows about us yet; we can be the only one holding our mutex. */ list_add(&lun_access->list, &ctxi->luns); - mutex_unlock(&ctxi->mutex); mutex_lock(&cfg->ctx_tbl_list_mutex); mutex_lock(&ctxi->mutex); cfg->ctx_tbl[ctxid] = ctxi; @@ -1453,7 +1425,11 @@ static int cxlflash_disk_attach(struct scsi_device *sdev, fd_install(fd, file); out_attach: - attach->hdr.return_flags = 0; + if (fd != -1) + attach->hdr.return_flags = DK_CXLFLASH_APP_CLOSE_ADAP_FD; + else + attach->hdr.return_flags = 0; + attach->context_id = ctxi->ctxid; attach->block_size = gli->blk_len; attach->mmio_size = sizeof(afu->afu_map->hosts[0].harea); @@ -1494,7 +1470,7 @@ err: file = NULL; } - /* Cleanup our context; safe to call even with mutex locked */ + /* Cleanup our context */ if (ctxi) { destroy_context(cfg, ctxi); ctxi = NULL; @@ -1509,16 +1485,19 @@ err: * recover_context() - recovers a context in error * @cfg: Internal structure associated with the host. * @ctxi: Context to release. + * @adap_fd: Adapter file descriptor associated with new/recovered context. * * Restablishes the state for a context-in-error. * * Return: 0 on success, -errno on failure */ -static int recover_context(struct cxlflash_cfg *cfg, struct ctx_info *ctxi) +static int recover_context(struct cxlflash_cfg *cfg, + struct ctx_info *ctxi, + int *adap_fd) { struct device *dev = &cfg->dev->dev; int rc = 0; - int old_fd, fd = -1; + int fd = -1; int ctxid = -1; struct file *file; struct cxl_context *ctx; @@ -1566,9 +1545,7 @@ static int recover_context(struct cxlflash_cfg *cfg, struct ctx_info *ctxi) * No error paths after this point. Once the fd is installed it's * visible to user space and can't be undone safely on this thread. */ - old_fd = ctxi->lfd; ctxi->ctxid = ENCODE_CTXID(ctxi, ctxid); - ctxi->lfd = fd; ctxi->ctx = ctx; ctxi->file = file; @@ -1585,9 +1562,7 @@ static int recover_context(struct cxlflash_cfg *cfg, struct ctx_info *ctxi) cfg->ctx_tbl[ctxid] = ctxi; mutex_unlock(&cfg->ctx_tbl_list_mutex); fd_install(fd, file); - - /* Release the original adapter fd and associated CXL resources */ - sys_close(old_fd); + *adap_fd = fd; out: dev_dbg(dev, "%s: returning ctxid=%d fd=%d rc=%d\n", __func__, ctxid, fd, rc); @@ -1646,6 +1621,7 @@ static int cxlflash_afu_recover(struct scsi_device *sdev, rctxid = recover->context_id; long reg; int lretry = 20; /* up to 2 seconds */ + int new_adap_fd = -1; int rc = 0; atomic_inc(&cfg->recovery_threads); @@ -1675,7 +1651,7 @@ retry: if (ctxi->err_recovery_active) { retry_recover: - rc = recover_context(cfg, ctxi); + rc = recover_context(cfg, ctxi, &new_adap_fd); if (unlikely(rc)) { dev_err(dev, "%s: Recovery failed for context %llu (rc=%d)\n", __func__, ctxid, rc); @@ -1697,9 +1673,9 @@ retry_recover: ctxi->err_recovery_active = false; recover->context_id = ctxi->ctxid; - recover->adap_fd = ctxi->lfd; + recover->adap_fd = new_adap_fd; recover->mmio_size = sizeof(afu->afu_map->hosts[0].harea); - recover->hdr.return_flags |= + recover->hdr.return_flags = DK_CXLFLASH_APP_CLOSE_ADAP_FD | DK_CXLFLASH_RECOVER_AFU_CONTEXT_RESET; goto out; } diff --git a/drivers/scsi/cxlflash/superpipe.h b/drivers/scsi/cxlflash/superpipe.h index 5f9a091fda95..9e62ff304e4b 100644 --- a/drivers/scsi/cxlflash/superpipe.h +++ b/drivers/scsi/cxlflash/superpipe.h @@ -100,13 +100,14 @@ struct ctx_info { struct cxl_ioctl_start_work work; u64 ctxid; - int lfd; pid_t pid; bool initialized; bool unavail; bool err_recovery_active; struct mutex mutex; /* Context protection */ + struct kref kref; struct cxl_context *ctx; + struct cxlflash_cfg *cfg; struct list_head luns; /* LUNs attached to this context */ const struct vm_operations_struct *cxl_mmap_vmops; struct file *file; diff --git a/drivers/scsi/cxlflash/vlun.c b/drivers/scsi/cxlflash/vlun.c index 50f8e9300770..90c5d7f5278e 100644 --- a/drivers/scsi/cxlflash/vlun.c +++ b/drivers/scsi/cxlflash/vlun.c @@ -1135,14 +1135,13 @@ int cxlflash_disk_clone(struct scsi_device *sdev, ctxid_dst = DECODE_CTXID(clone->context_id_dst), rctxid_src = clone->context_id_src, rctxid_dst = clone->context_id_dst; - int adap_fd_src = clone->adap_fd_src; int i, j; int rc = 0; bool found; LIST_HEAD(sidecar); - pr_debug("%s: ctxid_src=%llu ctxid_dst=%llu adap_fd_src=%d\n", - __func__, ctxid_src, ctxid_dst, adap_fd_src); + pr_debug("%s: ctxid_src=%llu ctxid_dst=%llu\n", + __func__, ctxid_src, ctxid_dst); /* Do not clone yourself */ if (unlikely(rctxid_src == rctxid_dst)) { @@ -1166,13 +1165,6 @@ int cxlflash_disk_clone(struct scsi_device *sdev, goto out; } - if (unlikely(adap_fd_src != ctxi_src->lfd)) { - pr_debug("%s: Invalid source adapter fd! (%d)\n", - __func__, adap_fd_src); - rc = -EINVAL; - goto out; - } - /* Verify there is no open resource handle in the destination context */ for (i = 0; i < MAX_RHT_PER_CONTEXT; i++) if (ctxi_dst->rht_start[i].nmask != 0) { @@ -1257,7 +1249,6 @@ int cxlflash_disk_clone(struct scsi_device *sdev, out_success: list_splice(&sidecar, &ctxi_dst->luns); - sys_close(adap_fd_src); /* fall through */ out: diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 752b5c9d1ab2..241829e59668 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -583,6 +583,7 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) sdev_printk(KERN_ERR, sdev, "%s: rtpg retry\n", ALUA_DH_NAME); scsi_print_sense_hdr(sdev, ALUA_DH_NAME, &sense_hdr); + kfree(buff); return err; } sdev_printk(KERN_ERR, sdev, "%s: rtpg failed\n", diff --git a/drivers/scsi/dtc.c b/drivers/scsi/dtc.c deleted file mode 100644 index 459863f94e46..000000000000 --- a/drivers/scsi/dtc.c +++ /dev/null @@ -1,447 +0,0 @@ -/* - * DTC 3180/3280 driver, by - * Ray Van Tassle rayvt@comm.mot.com - * - * taken from ... - * Trantor T128/T128F/T228 driver by... - * - * Drew Eckhardt - * Visionary Computing - * (Unix and Linux consulting and custom programming) - * drew@colorado.edu - * +1 (303) 440-4894 - */ - -/* - * The card is detected and initialized in one of several ways : - * 1. Autoprobe (default) - since the board is memory mapped, - * a BIOS signature is scanned for to locate the registers. - * An interrupt is triggered to autoprobe for the interrupt - * line. - * - * 2. With command line overrides - dtc=address,irq may be - * used on the LILO command line to override the defaults. - * -*/ - -/*----------------------------------------------------------------*/ -/* the following will set the monitor border color (useful to find - where something crashed or gets stuck at */ -/* 1 = blue - 2 = green - 3 = cyan - 4 = red - 5 = magenta - 6 = yellow - 7 = white -*/ -#if 0 -#define rtrc(i) {inb(0x3da); outb(0x31, 0x3c0); outb((i), 0x3c0);} -#else -#define rtrc(i) {} -#endif - - -#include <linux/module.h> -#include <linux/blkdev.h> -#include <linux/string.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <scsi/scsi_host.h> - -#include "dtc.h" -#include "NCR5380.h" - -/* - * The DTC3180 & 3280 boards are memory mapped. - * - */ - -/* - */ -/* Offset from DTC_5380_OFFSET */ -#define DTC_CONTROL_REG 0x100 /* rw */ -#define D_CR_ACCESS 0x80 /* ro set=can access 3280 registers */ -#define CSR_DIR_READ 0x40 /* rw direction, 1 = read 0 = write */ - -#define CSR_RESET 0x80 /* wo Resets 53c400 */ -#define CSR_5380_REG 0x80 /* ro 5380 registers can be accessed */ -#define CSR_TRANS_DIR 0x40 /* rw Data transfer direction */ -#define CSR_SCSI_BUFF_INTR 0x20 /* rw Enable int on transfer ready */ -#define CSR_5380_INTR 0x10 /* rw Enable 5380 interrupts */ -#define CSR_SHARED_INTR 0x08 /* rw Interrupt sharing */ -#define CSR_HOST_BUF_NOT_RDY 0x04 /* ro Host buffer not ready */ -#define CSR_SCSI_BUF_RDY 0x02 /* ro SCSI buffer ready */ -#define CSR_GATED_5380_IRQ 0x01 /* ro Last block xferred */ -#define CSR_INT_BASE (CSR_SCSI_BUFF_INTR | CSR_5380_INTR) - - -#define DTC_BLK_CNT 0x101 /* rw - * # of 128-byte blocks to transfer */ - - -#define D_CR_ACCESS 0x80 /* ro set=can access 3280 registers */ - -#define DTC_SWITCH_REG 0x3982 /* ro - DIP switches */ -#define DTC_RESUME_XFER 0x3982 /* wo - resume data xfer - * after disconnect/reconnect*/ - -#define DTC_5380_OFFSET 0x3880 /* 8 registers here, see NCR5380.h */ - -/*!!!! for dtc, it's a 128 byte buffer at 3900 !!! */ -#define DTC_DATA_BUF 0x3900 /* rw 128 bytes long */ - -static struct override { - unsigned int address; - int irq; -} overrides -#ifdef OVERRIDE -[] __initdata = OVERRIDE; -#else -[4] __initdata = { - { 0, IRQ_AUTO }, { 0, IRQ_AUTO }, { 0, IRQ_AUTO }, { 0, IRQ_AUTO } -}; -#endif - -#define NO_OVERRIDES ARRAY_SIZE(overrides) - -static struct base { - unsigned long address; - int noauto; -} bases[] __initdata = { - { 0xcc000, 0 }, - { 0xc8000, 0 }, - { 0xdc000, 0 }, - { 0xd8000, 0 } -}; - -#define NO_BASES ARRAY_SIZE(bases) - -static const struct signature { - const char *string; - int offset; -} signatures[] = { - {"DATA TECHNOLOGY CORPORATION BIOS", 0x25}, -}; - -#define NO_SIGNATURES ARRAY_SIZE(signatures) - -#ifndef MODULE -/* - * Function : dtc_setup(char *str, int *ints) - * - * Purpose : LILO command line initialization of the overrides array, - * - * Inputs : str - unused, ints - array of integer parameters with ints[0] - * equal to the number of ints. - * - */ - -static int __init dtc_setup(char *str) -{ - static int commandline_current; - int i; - int ints[10]; - - get_options(str, ARRAY_SIZE(ints), ints); - if (ints[0] != 2) - printk("dtc_setup: usage dtc=address,irq\n"); - else if (commandline_current < NO_OVERRIDES) { - overrides[commandline_current].address = ints[1]; - overrides[commandline_current].irq = ints[2]; - for (i = 0; i < NO_BASES; ++i) - if (bases[i].address == ints[1]) { - bases[i].noauto = 1; - break; - } - ++commandline_current; - } - return 1; -} - -__setup("dtc=", dtc_setup); -#endif - -/* - * Function : int dtc_detect(struct scsi_host_template * tpnt) - * - * Purpose : detects and initializes DTC 3180/3280 controllers - * that were autoprobed, overridden on the LILO command line, - * or specified at compile time. - * - * Inputs : tpnt - template for this SCSI adapter. - * - * Returns : 1 if a host adapter was found, 0 if not. - * -*/ - -static int __init dtc_detect(struct scsi_host_template * tpnt) -{ - static int current_override, current_base; - struct Scsi_Host *instance; - unsigned int addr; - void __iomem *base; - int sig, count; - - for (count = 0; current_override < NO_OVERRIDES; ++current_override) { - addr = 0; - base = NULL; - - if (overrides[current_override].address) { - addr = overrides[current_override].address; - base = ioremap(addr, 0x2000); - if (!base) - addr = 0; - } else - for (; !addr && (current_base < NO_BASES); ++current_base) { - dprintk(NDEBUG_INIT, "dtc: probing address 0x%08x\n", - (unsigned int)bases[current_base].address); - if (bases[current_base].noauto) - continue; - base = ioremap(bases[current_base].address, 0x2000); - if (!base) - continue; - for (sig = 0; sig < NO_SIGNATURES; ++sig) { - if (check_signature(base + signatures[sig].offset, signatures[sig].string, strlen(signatures[sig].string))) { - addr = bases[current_base].address; - dprintk(NDEBUG_INIT, "dtc: detected board\n"); - goto found; - } - } - iounmap(base); - } - - dprintk(NDEBUG_INIT, "dtc: addr = 0x%08x\n", addr); - - if (!addr) - break; - -found: - instance = scsi_register(tpnt, sizeof(struct NCR5380_hostdata)); - if (instance == NULL) - goto out_unmap; - - instance->base = addr; - ((struct NCR5380_hostdata *)(instance)->hostdata)->base = base; - - if (NCR5380_init(instance, FLAG_LATE_DMA_SETUP)) - goto out_unregister; - - NCR5380_maybe_reset_bus(instance); - - NCR5380_write(DTC_CONTROL_REG, CSR_5380_INTR); /* Enable int's */ - if (overrides[current_override].irq != IRQ_AUTO) - instance->irq = overrides[current_override].irq; - else - instance->irq = NCR5380_probe_irq(instance, DTC_IRQS); - - /* Compatibility with documented NCR5380 kernel parameters */ - if (instance->irq == 255) - instance->irq = NO_IRQ; - - /* With interrupts enabled, it will sometimes hang when doing heavy - * reads. So better not enable them until I finger it out. */ - instance->irq = NO_IRQ; - - if (instance->irq != NO_IRQ) - if (request_irq(instance->irq, dtc_intr, 0, - "dtc", instance)) { - printk(KERN_ERR "scsi%d : IRQ%d not free, interrupts disabled\n", instance->host_no, instance->irq); - instance->irq = NO_IRQ; - } - - if (instance->irq == NO_IRQ) { - printk(KERN_WARNING "scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no); - printk(KERN_WARNING "scsi%d : please jumper the board for a free IRQ.\n", instance->host_no); - } - - dprintk(NDEBUG_INIT, "scsi%d : irq = %d\n", - instance->host_no, instance->irq); - - ++current_override; - ++count; - } - return count; - -out_unregister: - scsi_unregister(instance); -out_unmap: - iounmap(base); - return count; -} - -/* - * Function : int dtc_biosparam(Disk * disk, struct block_device *dev, int *ip) - * - * Purpose : Generates a BIOS / DOS compatible H-C-S mapping for - * the specified device / size. - * - * Inputs : size = size of device in sectors (512 bytes), dev = block device - * major / minor, ip[] = {heads, sectors, cylinders} - * - * Returns : always 0 (success), initializes ip - * -*/ - -/* - * XXX Most SCSI boards use this mapping, I could be incorrect. Some one - * using hard disks on a trantor should verify that this mapping corresponds - * to that used by the BIOS / ASPI driver by running the linux fdisk program - * and matching the H_C_S coordinates to what DOS uses. -*/ - -static int dtc_biosparam(struct scsi_device *sdev, struct block_device *dev, - sector_t capacity, int *ip) -{ - int size = capacity; - - ip[0] = 64; - ip[1] = 32; - ip[2] = size >> 11; - return 0; -} - - -/**************************************************************** - * Function : int NCR5380_pread (struct Scsi_Host *instance, - * unsigned char *dst, int len) - * - * Purpose : Fast 5380 pseudo-dma read function, reads len bytes to - * dst - * - * Inputs : dst = destination, len = length in bytes - * - * Returns : 0 on success, non zero on a failure such as a watchdog - * timeout. -*/ - -static inline int dtc_pread(struct Scsi_Host *instance, - unsigned char *dst, int len) -{ - unsigned char *d = dst; - int i; /* For counting time spent in the poll-loop */ - struct NCR5380_hostdata *hostdata = shost_priv(instance); - - i = 0; - if (instance->irq == NO_IRQ) - NCR5380_write(DTC_CONTROL_REG, CSR_DIR_READ); - else - NCR5380_write(DTC_CONTROL_REG, CSR_DIR_READ | CSR_INT_BASE); - NCR5380_write(DTC_BLK_CNT, len >> 7); /* Block count */ - rtrc(1); - while (len > 0) { - rtrc(2); - while (NCR5380_read(DTC_CONTROL_REG) & CSR_HOST_BUF_NOT_RDY) - ++i; - rtrc(3); - memcpy_fromio(d, hostdata->base + DTC_DATA_BUF, 128); - d += 128; - len -= 128; - rtrc(7); - /*** with int's on, it sometimes hangs after here. - * Looks like something makes HBNR go away. */ - } - rtrc(4); - while (!(NCR5380_read(DTC_CONTROL_REG) & D_CR_ACCESS)) - ++i; - rtrc(0); - return (0); -} - -/**************************************************************** - * Function : int NCR5380_pwrite (struct Scsi_Host *instance, - * unsigned char *src, int len) - * - * Purpose : Fast 5380 pseudo-dma write function, transfers len bytes from - * src - * - * Inputs : src = source, len = length in bytes - * - * Returns : 0 on success, non zero on a failure such as a watchdog - * timeout. -*/ - -static inline int dtc_pwrite(struct Scsi_Host *instance, - unsigned char *src, int len) -{ - int i; - struct NCR5380_hostdata *hostdata = shost_priv(instance); - - if (instance->irq == NO_IRQ) - NCR5380_write(DTC_CONTROL_REG, 0); - else - NCR5380_write(DTC_CONTROL_REG, CSR_5380_INTR); - NCR5380_write(DTC_BLK_CNT, len >> 7); /* Block count */ - for (i = 0; len > 0; ++i) { - rtrc(5); - /* Poll until the host buffer can accept data. */ - while (NCR5380_read(DTC_CONTROL_REG) & CSR_HOST_BUF_NOT_RDY) - ++i; - rtrc(3); - memcpy_toio(hostdata->base + DTC_DATA_BUF, src, 128); - src += 128; - len -= 128; - } - rtrc(4); - while (!(NCR5380_read(DTC_CONTROL_REG) & D_CR_ACCESS)) - ++i; - rtrc(6); - /* Wait until the last byte has been sent to the disk */ - while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT)) - ++i; - rtrc(7); - /* Check for parity error here. fixme. */ - rtrc(0); - return (0); -} - -static int dtc_dma_xfer_len(struct scsi_cmnd *cmd) -{ - int transfersize = cmd->transfersize; - - /* Limit transfers to 32K, for xx400 & xx406 - * pseudoDMA that transfers in 128 bytes blocks. - */ - if (transfersize > 32 * 1024 && cmd->SCp.this_residual && - !(cmd->SCp.this_residual % transfersize)) - transfersize = 32 * 1024; - - return transfersize; -} - -MODULE_LICENSE("GPL"); - -#include "NCR5380.c" - -static int dtc_release(struct Scsi_Host *shost) -{ - struct NCR5380_hostdata *hostdata = shost_priv(shost); - - if (shost->irq != NO_IRQ) - free_irq(shost->irq, shost); - NCR5380_exit(shost); - scsi_unregister(shost); - iounmap(hostdata->base); - return 0; -} - -static struct scsi_host_template driver_template = { - .name = "DTC 3180/3280", - .detect = dtc_detect, - .release = dtc_release, - .proc_name = "dtc3x80", - .info = dtc_info, - .queuecommand = dtc_queue_command, - .eh_abort_handler = dtc_abort, - .eh_bus_reset_handler = dtc_bus_reset, - .bios_param = dtc_biosparam, - .can_queue = 32, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 2, - .use_clustering = DISABLE_CLUSTERING, - .cmd_size = NCR5380_CMD_SIZE, - .max_sectors = 128, -}; -#include "scsi_module.c" diff --git a/drivers/scsi/dtc.h b/drivers/scsi/dtc.h deleted file mode 100644 index fcb0a8ea7bda..000000000000 --- a/drivers/scsi/dtc.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * DTC controller, taken from T128 driver by... - * Copyright 1993, Drew Eckhardt - * Visionary Computing - * (Unix and Linux consulting and custom programming) - * drew@colorado.edu - * +1 (303) 440-4894 - */ - -#ifndef DTC3280_H -#define DTC3280_H - -#define NCR5380_implementation_fields \ - void __iomem *base - -#define DTC_address(reg) \ - (((struct NCR5380_hostdata *)shost_priv(instance))->base + DTC_5380_OFFSET + reg) - -#define NCR5380_read(reg) (readb(DTC_address(reg))) -#define NCR5380_write(reg, value) (writeb(value, DTC_address(reg))) - -#define NCR5380_dma_xfer_len(instance, cmd, phase) \ - dtc_dma_xfer_len(cmd) -#define NCR5380_dma_recv_setup dtc_pread -#define NCR5380_dma_send_setup dtc_pwrite -#define NCR5380_dma_residual(instance) (0) - -#define NCR5380_intr dtc_intr -#define NCR5380_queue_command dtc_queue_command -#define NCR5380_abort dtc_abort -#define NCR5380_bus_reset dtc_bus_reset -#define NCR5380_info dtc_info - -#define NCR5380_io_delay(x) udelay(x) - -/* 15 12 11 10 - 1001 1100 0000 0000 */ - -#define DTC_IRQS 0x9c00 - - -#endif /* DTC3280_H */ diff --git a/drivers/scsi/esas2r/esas2r_init.c b/drivers/scsi/esas2r/esas2r_init.c index 78ce4d61a69b..d6e53aee2295 100644 --- a/drivers/scsi/esas2r/esas2r_init.c +++ b/drivers/scsi/esas2r/esas2r_init.c @@ -963,10 +963,6 @@ bool esas2r_init_adapter_struct(struct esas2r_adapter *a, /* initialize the allocated memory */ if (test_bit(AF_FIRST_INIT, &a->flags)) { - memset(a->req_table, 0, - (num_requests + num_ae_requests + - 1) * sizeof(struct esas2r_request *)); - esas2r_targ_db_initialize(a); /* prime parts of the inbound list */ diff --git a/drivers/scsi/esas2r/esas2r_main.c b/drivers/scsi/esas2r/esas2r_main.c index 2aca4d16f39e..5092c821d088 100644 --- a/drivers/scsi/esas2r/esas2r_main.c +++ b/drivers/scsi/esas2r/esas2r_main.c @@ -194,7 +194,7 @@ static ssize_t write_hw(struct file *file, struct kobject *kobj, int length = min(sizeof(struct atto_ioctl), count); if (!a->local_atto_ioctl) { - a->local_atto_ioctl = kzalloc(sizeof(struct atto_ioctl), + a->local_atto_ioctl = kmalloc(sizeof(struct atto_ioctl), GFP_KERNEL); if (a->local_atto_ioctl == NULL) { esas2r_log(ESAS2R_LOG_WARN, diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c index 7028dd37e5dd..375c536cbc68 100644 --- a/drivers/scsi/fcoe/fcoe_transport.c +++ b/drivers/scsi/fcoe/fcoe_transport.c @@ -83,6 +83,41 @@ static struct notifier_block libfcoe_notifier = { .notifier_call = libfcoe_device_notification, }; +static const struct { + u32 fc_port_speed; +#define SPEED_2000 2000 +#define SPEED_4000 4000 +#define SPEED_8000 8000 +#define SPEED_16000 16000 +#define SPEED_32000 32000 + u32 eth_port_speed; +} fcoe_port_speed_mapping[] = { + { FC_PORTSPEED_1GBIT, SPEED_1000 }, + { FC_PORTSPEED_2GBIT, SPEED_2000 }, + { FC_PORTSPEED_4GBIT, SPEED_4000 }, + { FC_PORTSPEED_8GBIT, SPEED_8000 }, + { FC_PORTSPEED_10GBIT, SPEED_10000 }, + { FC_PORTSPEED_16GBIT, SPEED_16000 }, + { FC_PORTSPEED_20GBIT, SPEED_20000 }, + { FC_PORTSPEED_25GBIT, SPEED_25000 }, + { FC_PORTSPEED_32GBIT, SPEED_32000 }, + { FC_PORTSPEED_40GBIT, SPEED_40000 }, + { FC_PORTSPEED_50GBIT, SPEED_50000 }, + { FC_PORTSPEED_100GBIT, SPEED_100000 }, +}; + +static inline u32 eth2fc_speed(u32 eth_port_speed) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fcoe_port_speed_mapping); i++) { + if (fcoe_port_speed_mapping[i].eth_port_speed == eth_port_speed) + return fcoe_port_speed_mapping[i].fc_port_speed; + } + + return FC_PORTSPEED_UNKNOWN; +} + /** * fcoe_link_speed_update() - Update the supported and actual link speeds * @lport: The local port to update speeds for @@ -126,23 +161,7 @@ int fcoe_link_speed_update(struct fc_lport *lport) SUPPORTED_40000baseLR4_Full)) lport->link_supported_speeds |= FC_PORTSPEED_40GBIT; - switch (ecmd.base.speed) { - case SPEED_1000: - lport->link_speed = FC_PORTSPEED_1GBIT; - break; - case SPEED_10000: - lport->link_speed = FC_PORTSPEED_10GBIT; - break; - case SPEED_20000: - lport->link_speed = FC_PORTSPEED_20GBIT; - break; - case SPEED_40000: - lport->link_speed = FC_PORTSPEED_40GBIT; - break; - default: - lport->link_speed = FC_PORTSPEED_UNKNOWN; - break; - } + lport->link_speed = eth2fc_speed(ecmd.base.speed); return 0; } return -1; diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 4731d3241323..72c98522bd26 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -23,7 +23,7 @@ #include <scsi/sas_ata.h> #include <scsi/libsas.h> -#define DRV_VERSION "v1.5" +#define DRV_VERSION "v1.6" #define HISI_SAS_MAX_PHYS 9 #define HISI_SAS_MAX_QUEUES 32 @@ -56,6 +56,11 @@ enum dev_status { HISI_SAS_DEV_EH, }; +enum { + HISI_SAS_INT_ABT_CMD = 0, + HISI_SAS_INT_ABT_DEV = 1, +}; + enum hisi_sas_dev_type { HISI_SAS_DEV_TYPE_STP = 0, HISI_SAS_DEV_TYPE_SSP, @@ -89,6 +94,13 @@ struct hisi_sas_port { struct hisi_sas_cq { struct hisi_hba *hisi_hba; + int rd_point; + int id; +}; + +struct hisi_sas_dq { + struct hisi_hba *hisi_hba; + int wr_point; int id; }; @@ -146,6 +158,9 @@ struct hisi_sas_hw { struct hisi_sas_slot *slot); int (*prep_stp)(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot); + int (*prep_abort)(struct hisi_hba *hisi_hba, + struct hisi_sas_slot *slot, + int device_id, int abort_flag, int tag_to_abort); int (*slot_complete)(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot, int abort); void (*phy_enable)(struct hisi_hba *hisi_hba, int phy_no); @@ -185,6 +200,7 @@ struct hisi_hba { struct Scsi_Host *shost; struct hisi_sas_cq cq[HISI_SAS_MAX_QUEUES]; + struct hisi_sas_dq dq[HISI_SAS_MAX_QUEUES]; struct hisi_sas_phy phy[HISI_SAS_MAX_PHYS]; struct hisi_sas_port port[HISI_SAS_MAX_PHYS]; diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 18dd5ea2c721..2f872f784e10 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -17,6 +17,10 @@ static int hisi_sas_debug_issue_ssp_tmf(struct domain_device *device, u8 *lun, struct hisi_sas_tmf_task *tmf); +static int +hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba, + struct domain_device *device, + int abort_flag, int tag); static struct hisi_hba *dev_to_hisi_hba(struct domain_device *device) { @@ -93,7 +97,7 @@ void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct sas_task *task, slot->task = NULL; slot->port = NULL; hisi_sas_slot_index_free(hisi_hba, slot->idx); - memset(slot, 0, sizeof(*slot)); + /* slot memory is fully zeroed when it is reused */ } EXPORT_SYMBOL_GPL(hisi_sas_slot_task_free); @@ -116,6 +120,14 @@ static int hisi_sas_task_prep_ata(struct hisi_hba *hisi_hba, return hisi_hba->hw->prep_stp(hisi_hba, slot); } +static int hisi_sas_task_prep_abort(struct hisi_hba *hisi_hba, + struct hisi_sas_slot *slot, + int device_id, int abort_flag, int tag_to_abort) +{ + return hisi_hba->hw->prep_abort(hisi_hba, slot, + device_id, abort_flag, tag_to_abort); +} + /* * This function will issue an abort TMF regardless of whether the * task is in the sdev or not. Then it will do the task complete @@ -192,27 +204,13 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_hba *hisi_hba, return rc; } port = device->port->lldd_port; - if (port && !port->port_attached && !tmf) { - if (sas_protocol_ata(task->task_proto)) { - struct task_status_struct *ts = &task->task_status; - - dev_info(dev, - "task prep: SATA/STP port%d not attach device\n", - device->port->id); - ts->resp = SAS_TASK_COMPLETE; - ts->stat = SAS_PHY_DOWN; - task->task_done(task); - } else { - struct task_status_struct *ts = &task->task_status; - - dev_info(dev, - "task prep: SAS port%d does not attach device\n", - device->port->id); - ts->resp = SAS_TASK_UNDELIVERED; - ts->stat = SAS_PHY_DOWN; - task->task_done(task); - } - return 0; + if (port && !port->port_attached) { + dev_info(dev, "task prep: %s port%d not attach device\n", + (sas_protocol_ata(task->task_proto)) ? + "SATA/STP" : "SAS", + device->port->id); + + return SAS_PHY_DOWN; } if (!sas_protocol_ata(task->task_proto)) { @@ -609,6 +607,9 @@ static void hisi_sas_dev_gone(struct domain_device *device) dev_info(dev, "found dev[%lld:%x] is gone\n", sas_dev->device_id, sas_dev->dev_type); + hisi_sas_internal_task_abort(hisi_hba, device, + HISI_SAS_INT_ABT_DEV, 0); + hisi_hba->hw->free_device(hisi_hba, sas_dev); device->lldd_dev = NULL; memset(sas_dev, 0, sizeof(*sas_dev)); @@ -729,6 +730,12 @@ static int hisi_sas_exec_internal_tmf_task(struct domain_device *device, } if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == TMF_RESP_FUNC_SUCC) { + res = TMF_RESP_FUNC_SUCC; + break; + } + + if (task->task_status.resp == SAS_TASK_COMPLETE && task->task_status.stat == SAS_DATA_UNDERRUN) { /* no error, but return the number of bytes of * underrun @@ -826,18 +833,22 @@ static int hisi_sas_abort_task(struct sas_task *task) } } + hisi_sas_internal_task_abort(hisi_hba, device, + HISI_SAS_INT_ABT_CMD, tag); } else if (task->task_proto & SAS_PROTOCOL_SATA || task->task_proto & SAS_PROTOCOL_STP) { if (task->dev->dev_type == SAS_SATA_DEV) { - struct hisi_slot_info *slot = task->lldd_task; - - dev_notice(dev, "abort task: hba=%p task=%p slot=%p\n", - hisi_hba, task, slot); - task->task_state_flags |= SAS_TASK_STATE_ABORTED; + hisi_sas_internal_task_abort(hisi_hba, device, + HISI_SAS_INT_ABT_DEV, 0); rc = TMF_RESP_FUNC_COMPLETE; - goto out; } + } else if (task->task_proto & SAS_PROTOCOL_SMP) { + /* SMP */ + struct hisi_sas_slot *slot = task->lldd_task; + u32 tag = slot->idx; + hisi_sas_internal_task_abort(hisi_hba, device, + HISI_SAS_INT_ABT_CMD, tag); } out: @@ -954,6 +965,157 @@ static int hisi_sas_query_task(struct sas_task *task) return rc; } +static int +hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, u64 device_id, + struct sas_task *task, int abort_flag, + int task_tag) +{ + struct domain_device *device = task->dev; + struct hisi_sas_device *sas_dev = device->lldd_dev; + struct device *dev = &hisi_hba->pdev->dev; + struct hisi_sas_port *port; + struct hisi_sas_slot *slot; + struct hisi_sas_cmd_hdr *cmd_hdr_base; + int dlvry_queue_slot, dlvry_queue, n_elem = 0, rc, slot_idx; + + if (!device->port) + return -1; + + port = device->port->lldd_port; + + /* simply get a slot and send abort command */ + rc = hisi_sas_slot_index_alloc(hisi_hba, &slot_idx); + if (rc) + goto err_out; + rc = hisi_hba->hw->get_free_slot(hisi_hba, &dlvry_queue, + &dlvry_queue_slot); + if (rc) + goto err_out_tag; + + slot = &hisi_hba->slot_info[slot_idx]; + memset(slot, 0, sizeof(struct hisi_sas_slot)); + + slot->idx = slot_idx; + slot->n_elem = n_elem; + slot->dlvry_queue = dlvry_queue; + slot->dlvry_queue_slot = dlvry_queue_slot; + cmd_hdr_base = hisi_hba->cmd_hdr[dlvry_queue]; + slot->cmd_hdr = &cmd_hdr_base[dlvry_queue_slot]; + slot->task = task; + slot->port = port; + task->lldd_task = slot; + + memset(slot->cmd_hdr, 0, sizeof(struct hisi_sas_cmd_hdr)); + + rc = hisi_sas_task_prep_abort(hisi_hba, slot, device_id, + abort_flag, task_tag); + if (rc) + goto err_out_tag; + + /* Port structure is static for the HBA, so + * even if the port is deformed it is ok + * to reference. + */ + list_add_tail(&slot->entry, &port->list); + spin_lock(&task->task_state_lock); + task->task_state_flags |= SAS_TASK_AT_INITIATOR; + spin_unlock(&task->task_state_lock); + + hisi_hba->slot_prep = slot; + + sas_dev->running_req++; + /* send abort command to our chip */ + hisi_hba->hw->start_delivery(hisi_hba); + + return 0; + +err_out_tag: + hisi_sas_slot_index_free(hisi_hba, slot_idx); +err_out: + dev_err(dev, "internal abort task prep: failed[%d]!\n", rc); + + return rc; +} + +/** + * hisi_sas_internal_task_abort -- execute an internal + * abort command for single IO command or a device + * @hisi_hba: host controller struct + * @device: domain device + * @abort_flag: mode of operation, device or single IO + * @tag: tag of IO to be aborted (only relevant to single + * IO mode) + */ +static int +hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba, + struct domain_device *device, + int abort_flag, int tag) +{ + struct sas_task *task; + struct hisi_sas_device *sas_dev = device->lldd_dev; + struct device *dev = &hisi_hba->pdev->dev; + int res; + unsigned long flags; + + if (!hisi_hba->hw->prep_abort) + return -EOPNOTSUPP; + + task = sas_alloc_slow_task(GFP_KERNEL); + if (!task) + return -ENOMEM; + + task->dev = device; + task->task_proto = device->tproto; + task->task_done = hisi_sas_task_done; + task->slow_task->timer.data = (unsigned long)task; + task->slow_task->timer.function = hisi_sas_tmf_timedout; + task->slow_task->timer.expires = jiffies + 20*HZ; + add_timer(&task->slow_task->timer); + + /* Lock as we are alloc'ing a slot, which cannot be interrupted */ + spin_lock_irqsave(&hisi_hba->lock, flags); + res = hisi_sas_internal_abort_task_exec(hisi_hba, sas_dev->device_id, + task, abort_flag, tag); + spin_unlock_irqrestore(&hisi_hba->lock, flags); + if (res) { + del_timer(&task->slow_task->timer); + dev_err(dev, "internal task abort: executing internal task failed: %d\n", + res); + goto exit; + } + wait_for_completion(&task->slow_task->completion); + res = TMF_RESP_FUNC_FAILED; + + if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == TMF_RESP_FUNC_COMPLETE) { + res = TMF_RESP_FUNC_COMPLETE; + goto exit; + } + + /* TMF timed out, return direct. */ + if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) { + if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { + dev_err(dev, "internal task abort: timeout.\n"); + if (task->lldd_task) { + struct hisi_sas_slot *slot = task->lldd_task; + + hisi_sas_slot_task_free(hisi_hba, task, slot); + } + } + } + +exit: + dev_info(dev, "internal task abort: task to dev %016llx task=%p " + "resp: 0x%x sts 0x%x\n", + SAS_ADDR(device->sas_addr), + task, + task->task_status.resp, /* 0 is complete, -1 is undelivered */ + task->task_status.stat); + sas_free_task(task); + + return res; +} + static void hisi_sas_port_formed(struct asd_sas_phy *sas_phy) { hisi_sas_port_notify_formed(sas_phy); @@ -1063,11 +1225,16 @@ static int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost) for (i = 0; i < hisi_hba->queue_count; i++) { struct hisi_sas_cq *cq = &hisi_hba->cq[i]; + struct hisi_sas_dq *dq = &hisi_hba->dq[i]; /* Completion queue structure */ cq->id = i; cq->hisi_hba = hisi_hba; + /* Delivery queue structure */ + dq->id = i; + dq->hisi_hba = hisi_hba; + /* Delivery queue */ s = sizeof(struct hisi_sas_cmd_hdr) * HISI_SAS_QUEUE_SLOTS; hisi_hba->cmd_hdr[i] = dma_alloc_coherent(dev, s, @@ -1128,7 +1295,7 @@ static int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost) memset(hisi_hba->breakpoint, 0, s); hisi_hba->slot_index_count = max_command_entries; - s = hisi_hba->slot_index_count / sizeof(unsigned long); + s = hisi_hba->slot_index_count / BITS_PER_BYTE; hisi_hba->slot_index_tags = devm_kzalloc(dev, s, GFP_KERNEL); if (!hisi_hba->slot_index_tags) goto err_out; @@ -1272,6 +1439,12 @@ static struct Scsi_Host *hisi_sas_shost_alloc(struct platform_device *pdev, &hisi_hba->queue_count)) goto err_out; + if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)) && + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32))) { + dev_err(dev, "No usable DMA addressing method\n"); + goto err_out; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); hisi_hba->regs = devm_ioremap_resource(dev, res); if (IS_ERR(hisi_hba->regs)) @@ -1319,13 +1492,6 @@ int hisi_sas_probe(struct platform_device *pdev, hisi_hba = shost_priv(shost); platform_set_drvdata(pdev, sha); - if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)) && - dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32))) { - dev_err(dev, "No usable DMA addressing method\n"); - rc = -EIO; - goto err_out_ha; - } - phy_nr = port_nr = hisi_hba->n_phy; arr_phy = devm_kcalloc(dev, phy_nr, sizeof(void *), GFP_KERNEL); diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c index 1abbc2e162df..c0ac49d8bc8d 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c @@ -490,25 +490,17 @@ static void config_id_frame_v1_hw(struct hisi_hba *hisi_hba, int phy_no) hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD0, __swab32(identify_buffer[0])); hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD1, - identify_buffer[2]); + __swab32(identify_buffer[1])); hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD2, - identify_buffer[1]); + __swab32(identify_buffer[2])); hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD3, - identify_buffer[4]); + __swab32(identify_buffer[3])); hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD4, - identify_buffer[3]); + __swab32(identify_buffer[4])); hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD5, __swab32(identify_buffer[5])); } -static void init_id_frame_v1_hw(struct hisi_hba *hisi_hba) -{ - int i; - - for (i = 0; i < hisi_hba->n_phy; i++) - config_id_frame_v1_hw(hisi_hba, i); -} - static void setup_itct_v1_hw(struct hisi_hba *hisi_hba, struct hisi_sas_device *sas_dev) { @@ -774,8 +766,6 @@ static int hw_init_v1_hw(struct hisi_hba *hisi_hba) msleep(100); init_reg_v1_hw(hisi_hba); - init_id_frame_v1_hw(hisi_hba); - return 0; } @@ -875,12 +865,13 @@ static int get_wideport_bitmap_v1_hw(struct hisi_hba *hisi_hba, int port_id) static int get_free_slot_v1_hw(struct hisi_hba *hisi_hba, int *q, int *s) { struct device *dev = &hisi_hba->pdev->dev; + struct hisi_sas_dq *dq; u32 r, w; int queue = hisi_hba->queue; while (1) { - w = hisi_sas_read32_relaxed(hisi_hba, - DLVRY_Q_0_WR_PTR + (queue * 0x14)); + dq = &hisi_hba->dq[queue]; + w = dq->wr_point; r = hisi_sas_read32_relaxed(hisi_hba, DLVRY_Q_0_RD_PTR + (queue * 0x14)); if (r == (w+1) % HISI_SAS_QUEUE_SLOTS) { @@ -903,10 +894,11 @@ static void start_delivery_v1_hw(struct hisi_hba *hisi_hba) { int dlvry_queue = hisi_hba->slot_prep->dlvry_queue; int dlvry_queue_slot = hisi_hba->slot_prep->dlvry_queue_slot; + struct hisi_sas_dq *dq = &hisi_hba->dq[dlvry_queue]; - hisi_sas_write32(hisi_hba, - DLVRY_Q_0_WR_PTR + (dlvry_queue * 0x14), - ++dlvry_queue_slot % HISI_SAS_QUEUE_SLOTS); + dq->wr_point = ++dlvry_queue_slot % HISI_SAS_QUEUE_SLOTS; + hisi_sas_write32(hisi_hba, DLVRY_Q_0_WR_PTR + (dlvry_queue * 0x14), + dq->wr_point); } static int prep_prd_sge_v1_hw(struct hisi_hba *hisi_hba, @@ -1565,14 +1557,11 @@ static irqreturn_t cq_interrupt_v1_hw(int irq, void *p) struct hisi_sas_complete_v1_hdr *complete_queue = (struct hisi_sas_complete_v1_hdr *) hisi_hba->complete_hdr[queue]; - u32 irq_value, rd_point, wr_point; + u32 irq_value, rd_point = cq->rd_point, wr_point; irq_value = hisi_sas_read32(hisi_hba, OQ_INT_SRC); hisi_sas_write32(hisi_hba, OQ_INT_SRC, 1 << queue); - - rd_point = hisi_sas_read32(hisi_hba, - COMPL_Q_0_RD_PTR + (0x14 * queue)); wr_point = hisi_sas_read32(hisi_hba, COMPL_Q_0_WR_PTR + (0x14 * queue)); @@ -1600,6 +1589,7 @@ static irqreturn_t cq_interrupt_v1_hw(int irq, void *p) } /* update rd_point */ + cq->rd_point = rd_point; hisi_sas_write32(hisi_hba, COMPL_Q_0_RD_PTR + (0x14 * queue), rd_point); return IRQ_HANDLED; diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index f96560431cf1..9825a3f49f53 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -117,6 +117,8 @@ #define SL_CONTROL (PORT_BASE + 0x94) #define SL_CONTROL_NOTIFY_EN_OFF 0 #define SL_CONTROL_NOTIFY_EN_MSK (0x1 << SL_CONTROL_NOTIFY_EN_OFF) +#define SL_CONTROL_CTA_OFF 17 +#define SL_CONTROL_CTA_MSK (0x1 << SL_CONTROL_CTA_OFF) #define TX_ID_DWORD0 (PORT_BASE + 0x9c) #define TX_ID_DWORD1 (PORT_BASE + 0xa0) #define TX_ID_DWORD2 (PORT_BASE + 0xa4) @@ -124,6 +126,9 @@ #define TX_ID_DWORD4 (PORT_BASE + 0xaC) #define TX_ID_DWORD5 (PORT_BASE + 0xb0) #define TX_ID_DWORD6 (PORT_BASE + 0xb4) +#define TXID_AUTO (PORT_BASE + 0xb8) +#define TXID_AUTO_CT3_OFF 1 +#define TXID_AUTO_CT3_MSK (0x1 << TXID_AUTO_CT3_OFF) #define RX_IDAF_DWORD0 (PORT_BASE + 0xc4) #define RX_IDAF_DWORD1 (PORT_BASE + 0xc8) #define RX_IDAF_DWORD2 (PORT_BASE + 0xcc) @@ -174,6 +179,10 @@ /* HW dma structures */ /* Delivery queue header */ /* dw0 */ +#define CMD_HDR_ABORT_FLAG_OFF 0 +#define CMD_HDR_ABORT_FLAG_MSK (0x3 << CMD_HDR_ABORT_FLAG_OFF) +#define CMD_HDR_ABORT_DEVICE_TYPE_OFF 2 +#define CMD_HDR_ABORT_DEVICE_TYPE_MSK (0x1 << CMD_HDR_ABORT_DEVICE_TYPE_OFF) #define CMD_HDR_RESP_REPORT_OFF 5 #define CMD_HDR_RESP_REPORT_MSK (0x1 << CMD_HDR_RESP_REPORT_OFF) #define CMD_HDR_TLR_CTRL_OFF 6 @@ -214,6 +223,8 @@ #define CMD_HDR_DIF_SGL_LEN_MSK (0xffff << CMD_HDR_DIF_SGL_LEN_OFF) #define CMD_HDR_DATA_SGL_LEN_OFF 16 #define CMD_HDR_DATA_SGL_LEN_MSK (0xffff << CMD_HDR_DATA_SGL_LEN_OFF) +#define CMD_HDR_ABORT_IPTT_OFF 16 +#define CMD_HDR_ABORT_IPTT_MSK (0xffff << CMD_HDR_ABORT_IPTT_OFF) /* Completion header */ /* dw0 */ @@ -221,6 +232,13 @@ #define CMPLT_HDR_RSPNS_XFRD_MSK (0x1 << CMPLT_HDR_RSPNS_XFRD_OFF) #define CMPLT_HDR_ERX_OFF 12 #define CMPLT_HDR_ERX_MSK (0x1 << CMPLT_HDR_ERX_OFF) +#define CMPLT_HDR_ABORT_STAT_OFF 13 +#define CMPLT_HDR_ABORT_STAT_MSK (0x7 << CMPLT_HDR_ABORT_STAT_OFF) +/* abort_stat */ +#define STAT_IO_NOT_VALID 0x1 +#define STAT_IO_NO_DEVICE 0x2 +#define STAT_IO_COMPLETE 0x3 +#define STAT_IO_ABORTED 0x4 /* dw1 */ #define CMPLT_HDR_IPTT_OFF 0 #define CMPLT_HDR_IPTT_MSK (0xffff << CMPLT_HDR_IPTT_OFF) @@ -549,25 +567,17 @@ static void config_id_frame_v2_hw(struct hisi_hba *hisi_hba, int phy_no) hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD0, __swab32(identify_buffer[0])); hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD1, - identify_buffer[2]); + __swab32(identify_buffer[1])); hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD2, - identify_buffer[1]); + __swab32(identify_buffer[2])); hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD3, - identify_buffer[4]); + __swab32(identify_buffer[3])); hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD4, - identify_buffer[3]); + __swab32(identify_buffer[4])); hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD5, __swab32(identify_buffer[5])); } -static void init_id_frame_v2_hw(struct hisi_hba *hisi_hba) -{ - int i; - - for (i = 0; i < hisi_hba->n_phy; i++) - config_id_frame_v2_hw(hisi_hba, i); -} - static void setup_itct_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_device *sas_dev) { @@ -589,6 +599,7 @@ static void setup_itct_v2_hw(struct hisi_hba *hisi_hba, qw0 = HISI_SAS_DEV_TYPE_SSP << ITCT_HDR_DEV_TYPE_OFF; break; case SAS_SATA_DEV: + case SAS_SATA_PENDING: if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type)) qw0 = HISI_SAS_DEV_TYPE_STP << ITCT_HDR_DEV_TYPE_OFF; else @@ -672,9 +683,7 @@ static int reset_hw_v2_hw(struct hisi_hba *hisi_hba) else reset_val = 0x7ffff; - /* Disable all of the DQ */ - for (i = 0; i < HISI_SAS_MAX_QUEUES; i++) - hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, 0); + hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, 0); /* Disable all of the PHYs */ for (i = 0; i < hisi_hba->n_phy; i++) { @@ -810,6 +819,8 @@ static void init_reg_v2_hw(struct hisi_hba *hisi_hba) hisi_sas_phy_write32(hisi_hba, i, PROG_PHY_LINK_RATE, 0x855); hisi_sas_phy_write32(hisi_hba, i, SAS_PHY_CTRL, 0x30b9908); hisi_sas_phy_write32(hisi_hba, i, SL_TOUT_CFG, 0x7d7d7d7d); + hisi_sas_phy_write32(hisi_hba, i, SL_CONTROL, 0x0); + hisi_sas_phy_write32(hisi_hba, i, TXID_AUTO, 0x2); hisi_sas_phy_write32(hisi_hba, i, DONE_RECEIVED_TIME, 0x10); hisi_sas_phy_write32(hisi_hba, i, CHL_INT0, 0xffffffff); hisi_sas_phy_write32(hisi_hba, i, CHL_INT1, 0xffffffff); @@ -901,8 +912,6 @@ static int hw_init_v2_hw(struct hisi_hba *hisi_hba) msleep(100); init_reg_v2_hw(hisi_hba); - init_id_frame_v2_hw(hisi_hba); - return 0; } @@ -952,14 +961,8 @@ static void start_phys_v2_hw(unsigned long data) static void phys_init_v2_hw(struct hisi_hba *hisi_hba) { - int i; struct timer_list *timer = &hisi_hba->timer; - for (i = 0; i < hisi_hba->n_phy; i++) { - hisi_sas_phy_write32(hisi_hba, i, CHL_INT2_MSK, 0x6a); - hisi_sas_phy_read32(hisi_hba, i, CHL_INT2_MSK); - } - setup_timer(timer, start_phys_v2_hw, (unsigned long)hisi_hba); mod_timer(timer, jiffies + HZ); } @@ -1010,12 +1013,13 @@ static int get_wideport_bitmap_v2_hw(struct hisi_hba *hisi_hba, int port_id) static int get_free_slot_v2_hw(struct hisi_hba *hisi_hba, int *q, int *s) { struct device *dev = &hisi_hba->pdev->dev; + struct hisi_sas_dq *dq; u32 r, w; int queue = hisi_hba->queue; while (1) { - w = hisi_sas_read32_relaxed(hisi_hba, - DLVRY_Q_0_WR_PTR + (queue * 0x14)); + dq = &hisi_hba->dq[queue]; + w = dq->wr_point; r = hisi_sas_read32_relaxed(hisi_hba, DLVRY_Q_0_RD_PTR + (queue * 0x14)); if (r == (w+1) % HISI_SAS_QUEUE_SLOTS) { @@ -1038,9 +1042,11 @@ static void start_delivery_v2_hw(struct hisi_hba *hisi_hba) { int dlvry_queue = hisi_hba->slot_prep->dlvry_queue; int dlvry_queue_slot = hisi_hba->slot_prep->dlvry_queue_slot; + struct hisi_sas_dq *dq = &hisi_hba->dq[dlvry_queue]; + dq->wr_point = ++dlvry_queue_slot % HISI_SAS_QUEUE_SLOTS; hisi_sas_write32(hisi_hba, DLVRY_Q_0_WR_PTR + (dlvry_queue * 0x14), - ++dlvry_queue_slot % HISI_SAS_QUEUE_SLOTS); + dq->wr_point); } static int prep_prd_sge_v2_hw(struct hisi_hba *hisi_hba, @@ -1563,6 +1569,30 @@ slot_complete_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot, goto out; } + /* Use SAS+TMF status codes */ + switch ((complete_hdr->dw0 & CMPLT_HDR_ABORT_STAT_MSK) + >> CMPLT_HDR_ABORT_STAT_OFF) { + case STAT_IO_ABORTED: + /* this io has been aborted by abort command */ + ts->stat = SAS_ABORTED_TASK; + goto out; + case STAT_IO_COMPLETE: + /* internal abort command complete */ + ts->stat = TMF_RESP_FUNC_COMPLETE; + goto out; + case STAT_IO_NO_DEVICE: + ts->stat = TMF_RESP_FUNC_COMPLETE; + goto out; + case STAT_IO_NOT_VALID: + /* abort single io, controller don't find + * the io need to abort + */ + ts->stat = TMF_RESP_FUNC_FAILED; + goto out; + default: + break; + } + if ((complete_hdr->dw0 & CMPLT_HDR_ERX_MSK) && (!(complete_hdr->dw0 & CMPLT_HDR_RSPNS_XFRD_MSK))) { @@ -1775,6 +1805,32 @@ static int prep_ata_v2_hw(struct hisi_hba *hisi_hba, return 0; } +static int prep_abort_v2_hw(struct hisi_hba *hisi_hba, + struct hisi_sas_slot *slot, + int device_id, int abort_flag, int tag_to_abort) +{ + struct sas_task *task = slot->task; + struct domain_device *dev = task->dev; + struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr; + struct hisi_sas_port *port = slot->port; + + /* dw0 */ + hdr->dw0 = cpu_to_le32((5 << CMD_HDR_CMD_OFF) | /*abort*/ + (port->id << CMD_HDR_PORT_OFF) | + ((dev_is_sata(dev) ? 1:0) << + CMD_HDR_ABORT_DEVICE_TYPE_OFF) | + (abort_flag << CMD_HDR_ABORT_FLAG_OFF)); + + /* dw1 */ + hdr->dw1 = cpu_to_le32(device_id << CMD_HDR_DEV_ID_OFF); + + /* dw7 */ + hdr->dw7 = cpu_to_le32(tag_to_abort << CMD_HDR_ABORT_IPTT_OFF); + hdr->transfer_tags = cpu_to_le32(slot->idx); + + return 0; +} + static int phy_up_v2_hw(int phy_no, struct hisi_hba *hisi_hba) { int i, res = 0; @@ -1818,9 +1874,6 @@ static int phy_up_v2_hw(int phy_no, struct hisi_hba *hisi_hba) frame_rcvd[i] = __swab32(idaf); } - /* Get the linkrates */ - link_rate = hisi_sas_read32(hisi_hba, PHY_CONN_RATE); - link_rate = (link_rate >> (phy_no * 4)) & 0xf; sas_phy->linkrate = link_rate; hard_phy_linkrate = hisi_sas_phy_read32(hisi_hba, phy_no, HARD_PHY_LINKRATE); @@ -1855,16 +1908,21 @@ end: static int phy_down_v2_hw(int phy_no, struct hisi_hba *hisi_hba) { int res = 0; - u32 phy_cfg, phy_state; + u32 phy_state, sl_ctrl, txid_auto; hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_NOT_RDY_MSK, 1); - phy_cfg = hisi_sas_phy_read32(hisi_hba, phy_no, PHY_CFG); - phy_state = hisi_sas_read32(hisi_hba, PHY_STATE); - hisi_sas_phy_down(hisi_hba, phy_no, (phy_state & 1 << phy_no) ? 1 : 0); + sl_ctrl = hisi_sas_phy_read32(hisi_hba, phy_no, SL_CONTROL); + hisi_sas_phy_write32(hisi_hba, phy_no, SL_CONTROL, + sl_ctrl & ~SL_CONTROL_CTA_MSK); + + txid_auto = hisi_sas_phy_read32(hisi_hba, phy_no, TXID_AUTO); + hisi_sas_phy_write32(hisi_hba, phy_no, TXID_AUTO, + txid_auto | TXID_AUTO_CT3_MSK); + hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0, CHL_INT0_NOT_RDY_MSK); hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_NOT_RDY_MSK, 0); @@ -1986,7 +2044,7 @@ static irqreturn_t cq_interrupt_v2_hw(int irq_no, void *p) struct hisi_sas_slot *slot; struct hisi_sas_itct *itct; struct hisi_sas_complete_v2_hdr *complete_queue; - u32 irq_value, rd_point, wr_point, dev_id; + u32 irq_value, rd_point = cq->rd_point, wr_point, dev_id; int queue = cq->id; complete_queue = hisi_hba->complete_hdr[queue]; @@ -1994,8 +2052,6 @@ static irqreturn_t cq_interrupt_v2_hw(int irq_no, void *p) hisi_sas_write32(hisi_hba, OQ_INT_SRC, 1 << queue); - rd_point = hisi_sas_read32(hisi_hba, COMPL_Q_0_RD_PTR + - (0x14 * queue)); wr_point = hisi_sas_read32(hisi_hba, COMPL_Q_0_WR_PTR + (0x14 * queue)); @@ -2043,6 +2099,7 @@ static irqreturn_t cq_interrupt_v2_hw(int irq_no, void *p) } /* update rd_point */ + cq->rd_point = rd_point; hisi_sas_write32(hisi_hba, COMPL_Q_0_RD_PTR + (0x14 * queue), rd_point); return IRQ_HANDLED; } @@ -2239,6 +2296,7 @@ static const struct hisi_sas_hw hisi_sas_v2_hw = { .prep_smp = prep_smp_v2_hw, .prep_ssp = prep_ssp_v2_hw, .prep_stp = prep_ata_v2_hw, + .prep_abort = prep_abort_v2_hw, .get_free_slot = get_free_slot_v2_hw, .start_delivery = start_delivery_v2_hw, .slot_complete = slot_complete_v2_hw, diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index ec6381e57eb7..258a3f9a2519 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -246,10 +246,6 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, shost->dma_dev = dma_dev; - error = device_add(&shost->shost_gendev); - if (error) - goto out_destroy_freelist; - /* * Increase usage count temporarily here so that calling * scsi_autopm_put_host() will trigger runtime idle if there is @@ -260,6 +256,10 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, pm_runtime_enable(&shost->shost_gendev); device_enable_async_suspend(&shost->shost_gendev); + error = device_add(&shost->shost_gendev); + if (error) + goto out_destroy_freelist; + scsi_host_set_state(shost, SHOST_RUNNING); get_device(shost->shost_gendev.parent); @@ -309,6 +309,10 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, out_del_gendev: device_del(&shost->shost_gendev); out_destroy_freelist: + device_disable_async_suspend(&shost->shost_gendev); + pm_runtime_disable(&shost->shost_gendev); + pm_runtime_set_suspended(&shost->shost_gendev); + pm_runtime_put_noidle(&shost->shost_gendev); scsi_destroy_command_freelist(shost); out_destroy_tags: if (shost_use_blk_mq(shost)) diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 030d0023e1d2..d007ec18179a 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -293,6 +293,8 @@ static int detect_controller_lockup(struct ctlr_info *h); static void hpsa_disable_rld_caching(struct ctlr_info *h); static inline int hpsa_scsi_do_report_phys_luns(struct ctlr_info *h, struct ReportExtendedLUNdata *buf, int bufsize); +static bool hpsa_vpd_page_supported(struct ctlr_info *h, + unsigned char scsi3addr[], u8 page); static int hpsa_luns_changed(struct ctlr_info *h); static bool hpsa_cmd_dev_match(struct ctlr_info *h, struct CommandList *c, struct hpsa_scsi_dev_t *dev, @@ -2388,7 +2390,8 @@ static void hpsa_cmd_free_and_done(struct ctlr_info *h, struct CommandList *c, struct scsi_cmnd *cmd) { hpsa_cmd_resolve_and_free(h, c); - cmd->scsi_done(cmd); + if (cmd && cmd->scsi_done) + cmd->scsi_done(cmd); } static void hpsa_retry_cmd(struct ctlr_info *h, struct CommandList *c) @@ -2489,7 +2492,17 @@ static void complete_scsi_command(struct CommandList *cp) ei = cp->err_info; cmd = cp->scsi_cmd; h = cp->h; + + if (!cmd->device) { + cmd->result = DID_NO_CONNECT << 16; + return hpsa_cmd_free_and_done(h, cp, cmd); + } + dev = cmd->device->hostdata; + if (!dev) { + cmd->result = DID_NO_CONNECT << 16; + return hpsa_cmd_free_and_done(h, cp, cmd); + } c2 = &h->ioaccel2_cmd_pool[cp->cmdindex]; scsi_dma_unmap(cmd); /* undo the DMA mappings */ @@ -2504,8 +2517,15 @@ static void complete_scsi_command(struct CommandList *cp) cmd->result = (DID_OK << 16); /* host byte */ cmd->result |= (COMMAND_COMPLETE << 8); /* msg byte */ - if (cp->cmd_type == CMD_IOACCEL2 || cp->cmd_type == CMD_IOACCEL1) - atomic_dec(&cp->phys_disk->ioaccel_cmds_out); + if (cp->cmd_type == CMD_IOACCEL2 || cp->cmd_type == CMD_IOACCEL1) { + if (dev->physical_device && dev->expose_device && + dev->removed) { + cmd->result = DID_NO_CONNECT << 16; + return hpsa_cmd_free_and_done(h, cp, cmd); + } + if (likely(cp->phys_disk != NULL)) + atomic_dec(&cp->phys_disk->ioaccel_cmds_out); + } /* * We check for lockup status here as it may be set for @@ -3074,11 +3094,19 @@ static void hpsa_get_raid_level(struct ctlr_info *h, buf = kzalloc(64, GFP_KERNEL); if (!buf) return; - rc = hpsa_scsi_do_inquiry(h, scsi3addr, VPD_PAGE | 0xC1, buf, 64); + + if (!hpsa_vpd_page_supported(h, scsi3addr, + HPSA_VPD_LV_DEVICE_GEOMETRY)) + goto exit; + + rc = hpsa_scsi_do_inquiry(h, scsi3addr, VPD_PAGE | + HPSA_VPD_LV_DEVICE_GEOMETRY, buf, 64); + if (rc == 0) *raid_level = buf[8]; if (*raid_level > RAID_UNKNOWN) *raid_level = RAID_UNKNOWN; +exit: kfree(buf); return; } @@ -3436,7 +3464,7 @@ static void hpsa_get_sas_address(struct ctlr_info *h, unsigned char *scsi3addr, } /* Get a device id from inquiry page 0x83 */ -static int hpsa_vpd_page_supported(struct ctlr_info *h, +static bool hpsa_vpd_page_supported(struct ctlr_info *h, unsigned char scsi3addr[], u8 page) { int rc; @@ -3446,7 +3474,7 @@ static int hpsa_vpd_page_supported(struct ctlr_info *h, buf = kzalloc(256, GFP_KERNEL); if (!buf) - return 0; + return false; /* Get the size of the page list first */ rc = hpsa_scsi_do_inquiry(h, scsi3addr, @@ -3473,10 +3501,10 @@ static int hpsa_vpd_page_supported(struct ctlr_info *h, goto exit_supported; exit_unsupported: kfree(buf); - return 0; + return false; exit_supported: kfree(buf); - return 1; + return true; } static void hpsa_get_ioaccel_status(struct ctlr_info *h, @@ -3525,18 +3553,25 @@ static int hpsa_get_device_id(struct ctlr_info *h, unsigned char *scsi3addr, int rc; unsigned char *buf; - if (buflen > 16) - buflen = 16; + /* Does controller have VPD for device id? */ + if (!hpsa_vpd_page_supported(h, scsi3addr, HPSA_VPD_LV_DEVICE_ID)) + return 1; /* not supported */ + buf = kzalloc(64, GFP_KERNEL); if (!buf) return -ENOMEM; - rc = hpsa_scsi_do_inquiry(h, scsi3addr, VPD_PAGE | 0x83, buf, 64); - if (rc == 0) - memcpy(device_id, &buf[index], buflen); + + rc = hpsa_scsi_do_inquiry(h, scsi3addr, VPD_PAGE | + HPSA_VPD_LV_DEVICE_ID, buf, 64); + if (rc == 0) { + if (buflen > 16) + buflen = 16; + memcpy(device_id, &buf[8], buflen); + } kfree(buf); - return rc != 0; + return rc; /*0 - got id, otherwise, didn't */ } static int hpsa_scsi_do_report_luns(struct ctlr_info *h, int logical, @@ -3807,8 +3842,15 @@ static int hpsa_update_device_info(struct ctlr_info *h, sizeof(this_device->model)); memset(this_device->device_id, 0, sizeof(this_device->device_id)); - hpsa_get_device_id(h, scsi3addr, this_device->device_id, 8, - sizeof(this_device->device_id)); + if (hpsa_get_device_id(h, scsi3addr, this_device->device_id, 8, + sizeof(this_device->device_id))) + dev_err(&h->pdev->dev, + "hpsa%d: %s: can't get device id for host %d:C0:T%d:L%d\t%s\t%.16s\n", + h->ctlr, __func__, + h->scsi_host->host_no, + this_device->target, this_device->lun, + scsi_device_type(this_device->devtype), + this_device->model); if ((this_device->devtype == TYPE_DISK || this_device->devtype == TYPE_ZBC) && @@ -4034,7 +4076,17 @@ static void hpsa_get_ioaccel_drive_info(struct ctlr_info *h, struct bmic_identify_physical_device *id_phys) { int rc; - struct ext_report_lun_entry *rle = &rlep->LUN[rle_index]; + struct ext_report_lun_entry *rle; + + /* + * external targets don't support BMIC + */ + if (dev->external) { + dev->queue_depth = 7; + return; + } + + rle = &rlep->LUN[rle_index]; dev->ioaccel_handle = rle->ioaccel_handle; if ((rle->device_flags & 0x08) && dev->ioaccel_handle) @@ -4270,6 +4322,11 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h) lunaddrbytes = figure_lunaddrbytes(h, raid_ctlr_position, i, nphysicals, nlogicals, physdev_list, logdev_list); + /* Determine if this is a lun from an external target array */ + tmpdevice->external = + figure_external_status(h, raid_ctlr_position, i, + nphysicals, nlocal_logicals); + /* * Skip over some devices such as a spare. */ @@ -4295,11 +4352,6 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h) continue; } - /* Determine if this is a lun from an external target array */ - tmpdevice->external = - figure_external_status(h, raid_ctlr_position, i, - nphysicals, nlocal_logicals); - figure_bus_target_lun(h, lunaddrbytes, tmpdevice); hpsa_update_device_supports_aborts(h, tmpdevice, lunaddrbytes); this_device = currentsd[ncurrent]; @@ -4513,7 +4565,9 @@ static int fixup_ioaccel_cdb(u8 *cdb, int *cdb_len) case READ_6: case READ_12: if (*cdb_len == 6) { - block = get_unaligned_be16(&cdb[2]); + block = (((cdb[1] & 0x1F) << 16) | + (cdb[2] << 8) | + cdb[3]); block_cnt = cdb[4]; if (block_cnt == 0) block_cnt = 256; @@ -4638,6 +4692,9 @@ static int hpsa_scsi_ioaccel_direct_map(struct ctlr_info *h, struct scsi_cmnd *cmd = c->scsi_cmd; struct hpsa_scsi_dev_t *dev = cmd->device->hostdata; + if (!dev) + return -1; + c->phys_disk = dev; return hpsa_scsi_ioaccel_queue_command(h, c, dev->ioaccel_handle, @@ -4670,9 +4727,11 @@ static void set_encrypt_ioaccel2(struct ctlr_info *h, */ switch (cmd->cmnd[0]) { /* Required? 6-byte cdbs eliminated by fixup_ioaccel_cdb */ - case WRITE_6: case READ_6: - first_block = get_unaligned_be16(&cmd->cmnd[2]); + case WRITE_6: + first_block = (((cmd->cmnd[1] & 0x1F) << 16) | + (cmd->cmnd[2] << 8) | + cmd->cmnd[3]); break; case WRITE_10: case READ_10: @@ -4714,6 +4773,12 @@ static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h, u32 len; u32 total_len = 0; + if (!cmd->device) + return -1; + + if (!cmd->device->hostdata) + return -1; + BUG_ON(scsi_sg_count(cmd) > h->maxsgentries); if (fixup_ioaccel_cdb(cdb, &cdb_len)) { @@ -4822,6 +4887,12 @@ static int hpsa_scsi_ioaccel_queue_command(struct ctlr_info *h, struct CommandList *c, u32 ioaccel_handle, u8 *cdb, int cdb_len, u8 *scsi3addr, struct hpsa_scsi_dev_t *phys_disk) { + if (!c->scsi_cmd->device) + return -1; + + if (!c->scsi_cmd->device->hostdata) + return -1; + /* Try to honor the device's queue depth */ if (atomic_inc_return(&phys_disk->ioaccel_cmds_out) > phys_disk->queue_depth) { @@ -4902,12 +4973,17 @@ static int hpsa_scsi_ioaccel_raid_map(struct ctlr_info *h, #endif int offload_to_mirror; + if (!dev) + return -1; + /* check for valid opcode, get LBA and block count */ switch (cmd->cmnd[0]) { case WRITE_6: is_write = 1; case READ_6: - first_block = get_unaligned_be16(&cmd->cmnd[2]); + first_block = (((cmd->cmnd[1] & 0x1F) << 16) | + (cmd->cmnd[2] << 8) | + cmd->cmnd[3]); block_cnt = cmd->cmnd[4]; if (block_cnt == 0) block_cnt = 256; @@ -5314,6 +5390,9 @@ static int hpsa_ioaccel_submit(struct ctlr_info *h, struct hpsa_scsi_dev_t *dev = cmd->device->hostdata; int rc = IO_ACCEL_INELIGIBLE; + if (!dev) + return SCSI_MLQUEUE_HOST_BUSY; + cmd->host_scribble = (unsigned char *) c; if (dev->offload_enabled) { @@ -5852,6 +5931,9 @@ static void setup_ioaccel2_abort_cmd(struct CommandList *c, struct ctlr_info *h, struct scsi_cmnd *scmd = command_to_abort->scsi_cmd; struct hpsa_scsi_dev_t *dev = scmd->device->hostdata; + if (!dev) + return; + /* * We're overlaying struct hpsa_tmf_struct on top of something which * was allocated as a struct io_accel2_cmd, so we better be sure it @@ -5935,7 +6017,7 @@ static int hpsa_send_reset_as_abort_ioaccel2(struct ctlr_info *h, "Reset as abort: Resetting physical device at scsi3addr 0x%02x%02x%02x%02x%02x%02x%02x%02x\n", psa[0], psa[1], psa[2], psa[3], psa[4], psa[5], psa[6], psa[7]); - rc = hpsa_do_reset(h, dev, psa, HPSA_RESET_TYPE_TARGET, reply_queue); + rc = hpsa_do_reset(h, dev, psa, HPSA_PHYS_TARGET_RESET, reply_queue); if (rc != 0) { dev_warn(&h->pdev->dev, "Reset as abort: Failed on physical device at scsi3addr 0x%02x%02x%02x%02x%02x%02x%02x%02x\n", @@ -5972,6 +6054,9 @@ static int hpsa_send_abort_ioaccel2(struct ctlr_info *h, struct io_accel2_cmd *c2; dev = abort->scsi_cmd->device->hostdata; + if (!dev) + return -1; + if (!dev->offload_enabled && !dev->hba_ioaccel_enabled) return -1; diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h index a1487e67f7a1..82cdfad874f3 100644 --- a/drivers/scsi/hpsa.h +++ b/drivers/scsi/hpsa.h @@ -312,7 +312,6 @@ struct offline_device_entry { #define HPSA_DEVICE_RESET_MSG 1 #define HPSA_RESET_TYPE_CONTROLLER 0x00 #define HPSA_RESET_TYPE_BUS 0x01 -#define HPSA_RESET_TYPE_TARGET 0x03 #define HPSA_RESET_TYPE_LUN 0x04 #define HPSA_PHYS_TARGET_RESET 0x99 /* not defined by cciss spec */ #define HPSA_MSG_SEND_RETRY_LIMIT 10 diff --git a/drivers/scsi/hpsa_cmd.h b/drivers/scsi/hpsa_cmd.h index a5be153d92d4..a584cdf07058 100644 --- a/drivers/scsi/hpsa_cmd.h +++ b/drivers/scsi/hpsa_cmd.h @@ -157,6 +157,7 @@ /* VPD Inquiry types */ #define HPSA_VPD_SUPPORTED_PAGES 0x00 +#define HPSA_VPD_LV_DEVICE_ID 0x83 #define HPSA_VPD_LV_DEVICE_GEOMETRY 0xC1 #define HPSA_VPD_LV_IOACCEL_STATUS 0xC2 #define HPSA_VPD_LV_STATUS 0xC3 diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index ab67ec4b6bd6..7e487c78279c 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c @@ -52,6 +52,7 @@ static unsigned int max_requests = IBMVFC_MAX_REQUESTS_DEFAULT; static unsigned int disc_threads = IBMVFC_MAX_DISC_THREADS; static unsigned int ibmvfc_debug = IBMVFC_DEBUG; static unsigned int log_level = IBMVFC_DEFAULT_LOG_LEVEL; +static unsigned int cls3_error = IBMVFC_CLS3_ERROR; static LIST_HEAD(ibmvfc_head); static DEFINE_SPINLOCK(ibmvfc_driver_lock); static struct scsi_transport_template *ibmvfc_transport_template; @@ -86,6 +87,9 @@ MODULE_PARM_DESC(debug, "Enable driver debug information. " module_param_named(log_level, log_level, uint, 0); MODULE_PARM_DESC(log_level, "Set to 0 - 4 for increasing verbosity of device driver. " "[Default=" __stringify(IBMVFC_DEFAULT_LOG_LEVEL) "]"); +module_param_named(cls3_error, cls3_error, uint, 0); +MODULE_PARM_DESC(cls3_error, "Enable FC Class 3 Error Recovery. " + "[Default=" __stringify(IBMVFC_CLS3_ERROR) "]"); static const struct { u16 status; @@ -717,7 +721,6 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost) spin_lock_irqsave(vhost->host->host_lock, flags); vhost->state = IBMVFC_NO_CRQ; vhost->logged_in = 0; - ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE); /* Clean out the queue */ memset(crq->msgs, 0, PAGE_SIZE); @@ -1335,6 +1338,9 @@ static int ibmvfc_map_sg_data(struct scsi_cmnd *scmd, struct srp_direct_buf *data = &vfc_cmd->ioba; struct ibmvfc_host *vhost = dev_get_drvdata(dev); + if (cls3_error) + vfc_cmd->flags |= cpu_to_be16(IBMVFC_CLASS_3_ERR); + sg_mapped = scsi_dma_map(scmd); if (!sg_mapped) { vfc_cmd->flags |= cpu_to_be16(IBMVFC_NO_MEM_DESC); @@ -3381,6 +3387,10 @@ static void ibmvfc_tgt_send_prli(struct ibmvfc_target *tgt) prli->parms.type = IBMVFC_SCSI_FCP_TYPE; prli->parms.flags = cpu_to_be16(IBMVFC_PRLI_EST_IMG_PAIR); prli->parms.service_parms = cpu_to_be32(IBMVFC_PRLI_INITIATOR_FUNC); + prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_READ_FCP_XFER_RDY_DISABLED); + + if (cls3_error) + prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_RETRY); ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT); if (ibmvfc_send_event(evt, vhost, default_timeout)) { diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h index 5c70a52ad346..9a0696f68f37 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.h +++ b/drivers/scsi/ibmvscsi/ibmvfc.h @@ -54,6 +54,7 @@ #define IBMVFC_DEV_LOSS_TMO (5 * 60) #define IBMVFC_DEFAULT_LOG_LEVEL 2 #define IBMVFC_MAX_CDB_LEN 16 +#define IBMVFC_CLS3_ERROR 0 /* * Ensure we have resources for ERP and initialization: diff --git a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c index b29fef9d0f27..642b739ad0da 100644 --- a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c +++ b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c @@ -1606,8 +1606,6 @@ static void ibmvscsis_send_messages(struct scsi_info *vscsi) if (!(vscsi->flags & RESPONSE_Q_DOWN)) { list_for_each_entry_safe(cmd, nxt, &vscsi->waiting_rsp, list) { - pr_debug("send_messages cmd %p\n", cmd); - iue = cmd->iue; crq->valid = VALID_CMD_RESP_EL; @@ -1934,6 +1932,8 @@ static int ibmvscsis_drop_nexus(struct ibmvscsis_tport *tport) /* * Release the SCSI I_T Nexus to the emulated ibmvscsis Target Port */ + target_wait_for_sess_cmds(se_sess); + transport_deregister_session_configfs(se_sess); transport_deregister_session(se_sess); tport->ibmv_nexus = NULL; kfree(nexus); @@ -1978,7 +1978,7 @@ static long ibmvscsis_srp_login(struct scsi_info *vscsi, reason = SRP_LOGIN_REJ_MULTI_CHANNEL_UNSUPPORTED; else if (fmt->buffers & (~SUPPORTED_FORMATS)) reason = SRP_LOGIN_REJ_UNSUPPORTED_DESCRIPTOR_FMT; - else if ((fmt->buffers | SUPPORTED_FORMATS) == 0) + else if ((fmt->buffers & SUPPORTED_FORMATS) == 0) reason = SRP_LOGIN_REJ_UNSUPPORTED_DESCRIPTOR_FMT; if (vscsi->state == SRP_PROCESSING) @@ -2554,10 +2554,6 @@ static void ibmvscsis_parse_cmd(struct scsi_info *vscsi, srp->lun.scsi_lun[0] &= 0x3f; - pr_debug("calling submit_cmd, se_cmd %p, lun 0x%llx, cdb 0x%x, attr:%d\n", - &cmd->se_cmd, scsilun_to_int(&srp->lun), (int)srp->cdb[0], - attr); - rc = target_submit_cmd(&cmd->se_cmd, nexus->se_sess, srp->cdb, cmd->sense_buf, scsilun_to_int(&srp->lun), data_len, attr, dir, 0); @@ -3142,8 +3138,6 @@ static int ibmvscsis_rdma(struct ibmvscsis_cmd *cmd, struct scatterlist *sg, long tx_len; long rc = 0; - pr_debug("rdma: dir %d, bytes 0x%x\n", dir, bytes); - if (bytes == 0) return 0; @@ -3192,12 +3186,6 @@ static int ibmvscsis_rdma(struct ibmvscsis_cmd *cmd, struct scatterlist *sg, vscsi->dds.window[LOCAL].liobn, server_ioba); } else { - /* write to client */ - struct srp_cmd *srp = (struct srp_cmd *)iue->sbuf->buf; - - if (!READ_CMD(srp->cdb)) - print_hex_dump_bytes(" data:", DUMP_PREFIX_NONE, - sg_virt(sgp), buf_len); /* The h_copy_rdma will cause phyp, running in another * partition, to read memory, so we need to make sure * the data has been written out, hence these syncs. @@ -3322,12 +3310,9 @@ cmd_work: rc = ibmvscsis_trans_event(vscsi, crq); } else if (vscsi->flags & TRANS_EVENT) { /* - * if a tranport event has occurred leave + * if a transport event has occurred leave * everything but transport events on the queue - */ - pr_debug("handle_crq, ignoring\n"); - - /* + * * need to decrement the queue index so we can * look at the elment again */ @@ -3461,6 +3446,7 @@ static int ibmvscsis_probe(struct vio_dev *vdev, vscsi->map_ioba = dma_map_single(&vdev->dev, vscsi->map_buf, PAGE_SIZE, DMA_BIDIRECTIONAL); if (dma_mapping_error(&vdev->dev, vscsi->map_ioba)) { + rc = -ENOMEM; dev_err(&vscsi->dev, "probe: error mapping command buffer\n"); goto free_buf; } @@ -3693,12 +3679,9 @@ static void ibmvscsis_release_cmd(struct se_cmd *se_cmd) se_cmd); struct scsi_info *vscsi = cmd->adapter; - pr_debug("release_cmd %p, flags %d\n", se_cmd, cmd->flags); - spin_lock_bh(&vscsi->intr_lock); /* Remove from active_q */ - list_del(&cmd->list); - list_add_tail(&cmd->list, &vscsi->waiting_rsp); + list_move_tail(&cmd->list, &vscsi->waiting_rsp); ibmvscsis_send_messages(vscsi); spin_unlock_bh(&vscsi->intr_lock); } @@ -3715,9 +3698,6 @@ static int ibmvscsis_write_pending(struct se_cmd *se_cmd) struct iu_entry *iue = cmd->iue; int rc; - pr_debug("write_pending, se_cmd %p, length 0x%x\n", - se_cmd, se_cmd->data_length); - rc = srp_transfer_data(cmd, &vio_iu(iue)->srp.cmd, ibmvscsis_rdma, 1, 1); if (rc) { @@ -3756,9 +3736,6 @@ static int ibmvscsis_queue_data_in(struct se_cmd *se_cmd) uint len = 0; int rc; - pr_debug("queue_data_in, se_cmd %p, length 0x%x\n", - se_cmd, se_cmd->data_length); - rc = srp_transfer_data(cmd, &vio_iu(iue)->srp.cmd, ibmvscsis_rdma, 1, 1); if (rc) { diff --git a/drivers/scsi/in2000.c b/drivers/scsi/in2000.c deleted file mode 100644 index 3882d9f519c8..000000000000 --- a/drivers/scsi/in2000.c +++ /dev/null @@ -1,2302 +0,0 @@ -/* - * in2000.c - Linux device driver for the - * Always IN2000 ISA SCSI card. - * - * Copyright (c) 1996 John Shifflett, GeoLog Consulting - * john@geolog.com - * jshiffle@netcom.com - * - * 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, or (at your option) - * any later version. - * - * 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. - * - * For the avoidance of doubt the "preferred form" of this code is one which - * is in an open non patent encumbered format. Where cryptographic key signing - * forms part of the process of creating an executable the information - * including keys needed to generate an equivalently functional executable - * are deemed to be part of the source code. - * - * Drew Eckhardt's excellent 'Generic NCR5380' sources provided - * much of the inspiration and some of the code for this driver. - * The Linux IN2000 driver distributed in the Linux kernels through - * version 1.2.13 was an extremely valuable reference on the arcane - * (and still mysterious) workings of the IN2000's fifo. It also - * is where I lifted in2000_biosparam(), the gist of the card - * detection scheme, and other bits of code. Many thanks to the - * talented and courageous people who wrote, contributed to, and - * maintained that driver (including Brad McLean, Shaun Savage, - * Bill Earnest, Larry Doolittle, Roger Sunshine, John Luckey, - * Matt Postiff, Peter Lu, zerucha@shell.portal.com, and Eric - * Youngdale). I should also mention the driver written by - * Hamish Macdonald for the (GASP!) Amiga A2091 card, included - * in the Linux-m68k distribution; it gave me a good initial - * understanding of the proper way to run a WD33c93 chip, and I - * ended up stealing lots of code from it. - * - * _This_ driver is (I feel) an improvement over the old one in - * several respects: - * - All problems relating to the data size of a SCSI request are - * gone (as far as I know). The old driver couldn't handle - * swapping to partitions because that involved 4k blocks, nor - * could it deal with the st.c tape driver unmodified, because - * that usually involved 4k - 32k blocks. The old driver never - * quite got away from a morbid dependence on 2k block sizes - - * which of course is the size of the card's fifo. - * - * - Target Disconnection/Reconnection is now supported. Any - * system with more than one device active on the SCSI bus - * will benefit from this. The driver defaults to what I'm - * calling 'adaptive disconnect' - meaning that each command - * is evaluated individually as to whether or not it should - * be run with the option to disconnect/reselect (if the - * device chooses), or as a "SCSI-bus-hog". - * - * - Synchronous data transfers are now supported. Because there - * are a few devices (and many improperly terminated systems) - * that choke when doing sync, the default is sync DISABLED - * for all devices. This faster protocol can (and should!) - * be enabled on selected devices via the command-line. - * - * - Runtime operating parameters can now be specified through - * either the LILO or the 'insmod' command line. For LILO do: - * "in2000=blah,blah,blah" - * and with insmod go like: - * "insmod /usr/src/linux/modules/in2000.o setup_strings=blah,blah" - * The defaults should be good for most people. See the comment - * for 'setup_strings' below for more details. - * - * - The old driver relied exclusively on what the Western Digital - * docs call "Combination Level 2 Commands", which are a great - * idea in that the CPU is relieved of a lot of interrupt - * overhead. However, by accepting a certain (user-settable) - * amount of additional interrupts, this driver achieves - * better control over the SCSI bus, and data transfers are - * almost as fast while being much easier to define, track, - * and debug. - * - * - You can force detection of a card whose BIOS has been disabled. - * - * - Multiple IN2000 cards might almost be supported. I've tried to - * keep it in mind, but have no way to test... - * - * - * TODO: - * tagged queuing. multiple cards. - * - * - * NOTE: - * When using this or any other SCSI driver as a module, you'll - * find that with the stock kernel, at most _two_ SCSI hard - * drives will be linked into the device list (ie, usable). - * If your IN2000 card has more than 2 disks on its bus, you - * might want to change the define of 'SD_EXTRA_DEVS' in the - * 'hosts.h' file from 2 to whatever is appropriate. It took - * me a while to track down this surprisingly obscure and - * undocumented little "feature". - * - * - * People with bug reports, wish-lists, complaints, comments, - * or improvements are asked to pah-leeez email me (John Shifflett) - * at john@geolog.com or jshiffle@netcom.com! I'm anxious to get - * this thing into as good a shape as possible, and I'm positive - * there are lots of lurking bugs and "Stupid Places". - * - * Updated for Linux 2.5 by Alan Cox <alan@lxorguk.ukuu.org.uk> - * - Using new_eh handler - * - Hopefully got all the locking right again - * See "FIXME" notes for items that could do with more work - */ - -#include <linux/module.h> -#include <linux/blkdev.h> -#include <linux/interrupt.h> -#include <linux/string.h> -#include <linux/delay.h> -#include <linux/proc_fs.h> -#include <linux/ioport.h> -#include <linux/stat.h> - -#include <asm/io.h> - -#include "scsi.h" -#include <scsi/scsi_host.h> - -#define IN2000_VERSION "1.33-2.5" -#define IN2000_DATE "2002/11/03" - -#include "in2000.h" - - -/* - * 'setup_strings' is a single string used to pass operating parameters and - * settings from the kernel/module command-line to the driver. 'setup_args[]' - * is an array of strings that define the compile-time default values for - * these settings. If Linux boots with a LILO or insmod command-line, those - * settings are combined with 'setup_args[]'. Note that LILO command-lines - * are prefixed with "in2000=" while insmod uses a "setup_strings=" prefix. - * The driver recognizes the following keywords (lower case required) and - * arguments: - * - * - ioport:addr -Where addr is IO address of a (usually ROM-less) card. - * - noreset -No optional args. Prevents SCSI bus reset at boot time. - * - nosync:x -x is a bitmask where the 1st 7 bits correspond with - * the 7 possible SCSI devices (bit 0 for device #0, etc). - * Set a bit to PREVENT sync negotiation on that device. - * The driver default is sync DISABLED on all devices. - * - period:ns -ns is the minimum # of nanoseconds in a SCSI data transfer - * period. Default is 500; acceptable values are 250 - 1000. - * - disconnect:x -x = 0 to never allow disconnects, 2 to always allow them. - * x = 1 does 'adaptive' disconnects, which is the default - * and generally the best choice. - * - debug:x -If 'DEBUGGING_ON' is defined, x is a bitmask that causes - * various types of debug output to printed - see the DB_xxx - * defines in in2000.h - * - proc:x -If 'PROC_INTERFACE' is defined, x is a bitmask that - * determines how the /proc interface works and what it - * does - see the PR_xxx defines in in2000.h - * - * Syntax Notes: - * - Numeric arguments can be decimal or the '0x' form of hex notation. There - * _must_ be a colon between a keyword and its numeric argument, with no - * spaces. - * - Keywords are separated by commas, no spaces, in the standard kernel - * command-line manner. - * - A keyword in the 'nth' comma-separated command-line member will overwrite - * the 'nth' element of setup_args[]. A blank command-line member (in - * other words, a comma with no preceding keyword) will _not_ overwrite - * the corresponding setup_args[] element. - * - * A few LILO examples (for insmod, use 'setup_strings' instead of 'in2000'): - * - in2000=ioport:0x220,noreset - * - in2000=period:250,disconnect:2,nosync:0x03 - * - in2000=debug:0x1e - * - in2000=proc:3 - */ - -/* Normally, no defaults are specified... */ -static char *setup_args[] = { "", "", "", "", "", "", "", "", "" }; - -/* filled in by 'insmod' */ -static char *setup_strings; - -module_param(setup_strings, charp, 0); - -static inline uchar read_3393(struct IN2000_hostdata *hostdata, uchar reg_num) -{ - write1_io(reg_num, IO_WD_ADDR); - return read1_io(IO_WD_DATA); -} - - -#define READ_AUX_STAT() read1_io(IO_WD_ASR) - - -static inline void write_3393(struct IN2000_hostdata *hostdata, uchar reg_num, uchar value) -{ - write1_io(reg_num, IO_WD_ADDR); - write1_io(value, IO_WD_DATA); -} - - -static inline void write_3393_cmd(struct IN2000_hostdata *hostdata, uchar cmd) -{ -/* while (READ_AUX_STAT() & ASR_CIP) - printk("|");*/ - write1_io(WD_COMMAND, IO_WD_ADDR); - write1_io(cmd, IO_WD_DATA); -} - - -static uchar read_1_byte(struct IN2000_hostdata *hostdata) -{ - uchar asr, x = 0; - - write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED); - write_3393_cmd(hostdata, WD_CMD_TRANS_INFO | 0x80); - do { - asr = READ_AUX_STAT(); - if (asr & ASR_DBR) - x = read_3393(hostdata, WD_DATA); - } while (!(asr & ASR_INT)); - return x; -} - - -static void write_3393_count(struct IN2000_hostdata *hostdata, unsigned long value) -{ - write1_io(WD_TRANSFER_COUNT_MSB, IO_WD_ADDR); - write1_io((value >> 16), IO_WD_DATA); - write1_io((value >> 8), IO_WD_DATA); - write1_io(value, IO_WD_DATA); -} - - -static unsigned long read_3393_count(struct IN2000_hostdata *hostdata) -{ - unsigned long value; - - write1_io(WD_TRANSFER_COUNT_MSB, IO_WD_ADDR); - value = read1_io(IO_WD_DATA) << 16; - value |= read1_io(IO_WD_DATA) << 8; - value |= read1_io(IO_WD_DATA); - return value; -} - - -/* The 33c93 needs to be told which direction a command transfers its - * data; we use this function to figure it out. Returns true if there - * will be a DATA_OUT phase with this command, false otherwise. - * (Thanks to Joerg Dorchain for the research and suggestion.) - */ -static int is_dir_out(Scsi_Cmnd * cmd) -{ - switch (cmd->cmnd[0]) { - case WRITE_6: - case WRITE_10: - case WRITE_12: - case WRITE_LONG: - case WRITE_SAME: - case WRITE_BUFFER: - case WRITE_VERIFY: - case WRITE_VERIFY_12: - case COMPARE: - case COPY: - case COPY_VERIFY: - case SEARCH_EQUAL: - case SEARCH_HIGH: - case SEARCH_LOW: - case SEARCH_EQUAL_12: - case SEARCH_HIGH_12: - case SEARCH_LOW_12: - case FORMAT_UNIT: - case REASSIGN_BLOCKS: - case RESERVE: - case MODE_SELECT: - case MODE_SELECT_10: - case LOG_SELECT: - case SEND_DIAGNOSTIC: - case CHANGE_DEFINITION: - case UPDATE_BLOCK: - case SET_WINDOW: - case MEDIUM_SCAN: - case SEND_VOLUME_TAG: - case 0xea: - return 1; - default: - return 0; - } -} - - - -static struct sx_period sx_table[] = { - {1, 0x20}, - {252, 0x20}, - {376, 0x30}, - {500, 0x40}, - {624, 0x50}, - {752, 0x60}, - {876, 0x70}, - {1000, 0x00}, - {0, 0} -}; - -static int round_period(unsigned int period) -{ - int x; - - for (x = 1; sx_table[x].period_ns; x++) { - if ((period <= sx_table[x - 0].period_ns) && (period > sx_table[x - 1].period_ns)) { - return x; - } - } - return 7; -} - -static uchar calc_sync_xfer(unsigned int period, unsigned int offset) -{ - uchar result; - - period *= 4; /* convert SDTR code to ns */ - result = sx_table[round_period(period)].reg_value; - result |= (offset < OPTIMUM_SX_OFF) ? offset : OPTIMUM_SX_OFF; - return result; -} - - - -static void in2000_execute(struct Scsi_Host *instance); - -static int in2000_queuecommand_lck(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) -{ - struct Scsi_Host *instance; - struct IN2000_hostdata *hostdata; - Scsi_Cmnd *tmp; - - instance = cmd->device->host; - hostdata = (struct IN2000_hostdata *) instance->hostdata; - - DB(DB_QUEUE_COMMAND, scmd_printk(KERN_DEBUG, cmd, "Q-%02x(", cmd->cmnd[0])) - -/* Set up a few fields in the Scsi_Cmnd structure for our own use: - * - host_scribble is the pointer to the next cmd in the input queue - * - scsi_done points to the routine we call when a cmd is finished - * - result is what you'd expect - */ - cmd->host_scribble = NULL; - cmd->scsi_done = done; - cmd->result = 0; - -/* We use the Scsi_Pointer structure that's included with each command - * as a scratchpad (as it's intended to be used!). The handy thing about - * the SCp.xxx fields is that they're always associated with a given - * cmd, and are preserved across disconnect-reselect. This means we - * can pretty much ignore SAVE_POINTERS and RESTORE_POINTERS messages - * if we keep all the critical pointers and counters in SCp: - * - SCp.ptr is the pointer into the RAM buffer - * - SCp.this_residual is the size of that buffer - * - SCp.buffer points to the current scatter-gather buffer - * - SCp.buffers_residual tells us how many S.G. buffers there are - * - SCp.have_data_in helps keep track of >2048 byte transfers - * - SCp.sent_command is not used - * - SCp.phase records this command's SRCID_ER bit setting - */ - - if (scsi_bufflen(cmd)) { - cmd->SCp.buffer = scsi_sglist(cmd); - cmd->SCp.buffers_residual = scsi_sg_count(cmd) - 1; - cmd->SCp.ptr = sg_virt(cmd->SCp.buffer); - cmd->SCp.this_residual = cmd->SCp.buffer->length; - } else { - cmd->SCp.buffer = NULL; - cmd->SCp.buffers_residual = 0; - cmd->SCp.ptr = NULL; - cmd->SCp.this_residual = 0; - } - cmd->SCp.have_data_in = 0; - -/* We don't set SCp.phase here - that's done in in2000_execute() */ - -/* WD docs state that at the conclusion of a "LEVEL2" command, the - * status byte can be retrieved from the LUN register. Apparently, - * this is the case only for *uninterrupted* LEVEL2 commands! If - * there are any unexpected phases entered, even if they are 100% - * legal (different devices may choose to do things differently), - * the LEVEL2 command sequence is exited. This often occurs prior - * to receiving the status byte, in which case the driver does a - * status phase interrupt and gets the status byte on its own. - * While such a command can then be "resumed" (ie restarted to - * finish up as a LEVEL2 command), the LUN register will NOT be - * a valid status byte at the command's conclusion, and we must - * use the byte obtained during the earlier interrupt. Here, we - * preset SCp.Status to an illegal value (0xff) so that when - * this command finally completes, we can tell where the actual - * status byte is stored. - */ - - cmd->SCp.Status = ILLEGAL_STATUS_BYTE; - -/* We need to disable interrupts before messing with the input - * queue and calling in2000_execute(). - */ - - /* - * Add the cmd to the end of 'input_Q'. Note that REQUEST_SENSE - * commands are added to the head of the queue so that the desired - * sense data is not lost before REQUEST_SENSE executes. - */ - - if (!(hostdata->input_Q) || (cmd->cmnd[0] == REQUEST_SENSE)) { - cmd->host_scribble = (uchar *) hostdata->input_Q; - hostdata->input_Q = cmd; - } else { /* find the end of the queue */ - for (tmp = (Scsi_Cmnd *) hostdata->input_Q; tmp->host_scribble; tmp = (Scsi_Cmnd *) tmp->host_scribble); - tmp->host_scribble = (uchar *) cmd; - } - -/* We know that there's at least one command in 'input_Q' now. - * Go see if any of them are runnable! - */ - - in2000_execute(cmd->device->host); - - DB(DB_QUEUE_COMMAND, printk(")Q ")) - return 0; -} - -static DEF_SCSI_QCMD(in2000_queuecommand) - - - -/* - * This routine attempts to start a scsi command. If the host_card is - * already connected, we give up immediately. Otherwise, look through - * the input_Q, using the first command we find that's intended - * for a currently non-busy target/lun. - * Note that this function is always called with interrupts already - * disabled (either from in2000_queuecommand() or in2000_intr()). - */ -static void in2000_execute(struct Scsi_Host *instance) -{ - struct IN2000_hostdata *hostdata; - Scsi_Cmnd *cmd, *prev; - int i; - unsigned short *sp; - unsigned short f; - unsigned short flushbuf[16]; - - - hostdata = (struct IN2000_hostdata *) instance->hostdata; - - DB(DB_EXECUTE, printk("EX(")) - - if (hostdata->selecting || hostdata->connected) { - - DB(DB_EXECUTE, printk(")EX-0 ")) - - return; - } - - /* - * Search through the input_Q for a command destined - * for an idle target/lun. - */ - - cmd = (Scsi_Cmnd *) hostdata->input_Q; - prev = NULL; - while (cmd) { - if (!(hostdata->busy[cmd->device->id] & (1 << cmd->device->lun))) - break; - prev = cmd; - cmd = (Scsi_Cmnd *) cmd->host_scribble; - } - - /* quit if queue empty or all possible targets are busy */ - - if (!cmd) { - - DB(DB_EXECUTE, printk(")EX-1 ")) - - return; - } - - /* remove command from queue */ - - if (prev) - prev->host_scribble = cmd->host_scribble; - else - hostdata->input_Q = (Scsi_Cmnd *) cmd->host_scribble; - -#ifdef PROC_STATISTICS - hostdata->cmd_cnt[cmd->device->id]++; -#endif - -/* - * Start the selection process - */ - - if (is_dir_out(cmd)) - write_3393(hostdata, WD_DESTINATION_ID, cmd->device->id); - else - write_3393(hostdata, WD_DESTINATION_ID, cmd->device->id | DSTID_DPD); - -/* Now we need to figure out whether or not this command is a good - * candidate for disconnect/reselect. We guess to the best of our - * ability, based on a set of hierarchical rules. When several - * devices are operating simultaneously, disconnects are usually - * an advantage. In a single device system, or if only 1 device - * is being accessed, transfers usually go faster if disconnects - * are not allowed: - * - * + Commands should NEVER disconnect if hostdata->disconnect = - * DIS_NEVER (this holds for tape drives also), and ALWAYS - * disconnect if hostdata->disconnect = DIS_ALWAYS. - * + Tape drive commands should always be allowed to disconnect. - * + Disconnect should be allowed if disconnected_Q isn't empty. - * + Commands should NOT disconnect if input_Q is empty. - * + Disconnect should be allowed if there are commands in input_Q - * for a different target/lun. In this case, the other commands - * should be made disconnect-able, if not already. - * - * I know, I know - this code would flunk me out of any - * "C Programming 101" class ever offered. But it's easy - * to change around and experiment with for now. - */ - - cmd->SCp.phase = 0; /* assume no disconnect */ - if (hostdata->disconnect == DIS_NEVER) - goto no; - if (hostdata->disconnect == DIS_ALWAYS) - goto yes; - if (cmd->device->type == 1) /* tape drive? */ - goto yes; - if (hostdata->disconnected_Q) /* other commands disconnected? */ - goto yes; - if (!(hostdata->input_Q)) /* input_Q empty? */ - goto no; - for (prev = (Scsi_Cmnd *) hostdata->input_Q; prev; prev = (Scsi_Cmnd *) prev->host_scribble) { - if ((prev->device->id != cmd->device->id) || (prev->device->lun != cmd->device->lun)) { - for (prev = (Scsi_Cmnd *) hostdata->input_Q; prev; prev = (Scsi_Cmnd *) prev->host_scribble) - prev->SCp.phase = 1; - goto yes; - } - } - goto no; - - yes: - cmd->SCp.phase = 1; - -#ifdef PROC_STATISTICS - hostdata->disc_allowed_cnt[cmd->device->id]++; -#endif - - no: - write_3393(hostdata, WD_SOURCE_ID, ((cmd->SCp.phase) ? SRCID_ER : 0)); - - write_3393(hostdata, WD_TARGET_LUN, cmd->device->lun); - write_3393(hostdata, WD_SYNCHRONOUS_TRANSFER, hostdata->sync_xfer[cmd->device->id]); - hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun); - - if ((hostdata->level2 <= L2_NONE) || (hostdata->sync_stat[cmd->device->id] == SS_UNSET)) { - - /* - * Do a 'Select-With-ATN' command. This will end with - * one of the following interrupts: - * CSR_RESEL_AM: failure - can try again later. - * CSR_TIMEOUT: failure - give up. - * CSR_SELECT: success - proceed. - */ - - hostdata->selecting = cmd; - -/* Every target has its own synchronous transfer setting, kept in - * the sync_xfer array, and a corresponding status byte in sync_stat[]. - * Each target's sync_stat[] entry is initialized to SS_UNSET, and its - * sync_xfer[] entry is initialized to the default/safe value. SS_UNSET - * means that the parameters are undetermined as yet, and that we - * need to send an SDTR message to this device after selection is - * complete. We set SS_FIRST to tell the interrupt routine to do so, - * unless we don't want to even _try_ synchronous transfers: In this - * case we set SS_SET to make the defaults final. - */ - if (hostdata->sync_stat[cmd->device->id] == SS_UNSET) { - if (hostdata->sync_off & (1 << cmd->device->id)) - hostdata->sync_stat[cmd->device->id] = SS_SET; - else - hostdata->sync_stat[cmd->device->id] = SS_FIRST; - } - hostdata->state = S_SELECTING; - write_3393_count(hostdata, 0); /* this guarantees a DATA_PHASE interrupt */ - write_3393_cmd(hostdata, WD_CMD_SEL_ATN); - } - - else { - - /* - * Do a 'Select-With-ATN-Xfer' command. This will end with - * one of the following interrupts: - * CSR_RESEL_AM: failure - can try again later. - * CSR_TIMEOUT: failure - give up. - * anything else: success - proceed. - */ - - hostdata->connected = cmd; - write_3393(hostdata, WD_COMMAND_PHASE, 0); - - /* copy command_descriptor_block into WD chip - * (take advantage of auto-incrementing) - */ - - write1_io(WD_CDB_1, IO_WD_ADDR); - for (i = 0; i < cmd->cmd_len; i++) - write1_io(cmd->cmnd[i], IO_WD_DATA); - - /* The wd33c93 only knows about Group 0, 1, and 5 commands when - * it's doing a 'select-and-transfer'. To be safe, we write the - * size of the CDB into the OWN_ID register for every case. This - * way there won't be problems with vendor-unique, audio, etc. - */ - - write_3393(hostdata, WD_OWN_ID, cmd->cmd_len); - - /* When doing a non-disconnect command, we can save ourselves a DATA - * phase interrupt later by setting everything up now. With writes we - * need to pre-fill the fifo; if there's room for the 32 flush bytes, - * put them in there too - that'll avoid a fifo interrupt. Reads are - * somewhat simpler. - * KLUDGE NOTE: It seems that you can't completely fill the fifo here: - * This results in the IO_FIFO_COUNT register rolling over to zero, - * and apparently the gate array logic sees this as empty, not full, - * so the 3393 chip is never signalled to start reading from the - * fifo. Or maybe it's seen as a permanent fifo interrupt condition. - * Regardless, we fix this by temporarily pretending that the fifo - * is 16 bytes smaller. (I see now that the old driver has a comment - * about "don't fill completely" in an analogous place - must be the - * same deal.) This results in CDROM, swap partitions, and tape drives - * needing an extra interrupt per write command - I think we can live - * with that! - */ - - if (!(cmd->SCp.phase)) { - write_3393_count(hostdata, cmd->SCp.this_residual); - write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_BUS); - write1_io(0, IO_FIFO_WRITE); /* clear fifo counter, write mode */ - - if (is_dir_out(cmd)) { - hostdata->fifo = FI_FIFO_WRITING; - if ((i = cmd->SCp.this_residual) > (IN2000_FIFO_SIZE - 16)) - i = IN2000_FIFO_SIZE - 16; - cmd->SCp.have_data_in = i; /* this much data in fifo */ - i >>= 1; /* Gulp. Assuming modulo 2. */ - sp = (unsigned short *) cmd->SCp.ptr; - f = hostdata->io_base + IO_FIFO; - -#ifdef FAST_WRITE_IO - - FAST_WRITE2_IO(); -#else - while (i--) - write2_io(*sp++, IO_FIFO); - -#endif - - /* Is there room for the flush bytes? */ - - if (cmd->SCp.have_data_in <= ((IN2000_FIFO_SIZE - 16) - 32)) { - sp = flushbuf; - i = 16; - -#ifdef FAST_WRITE_IO - - FAST_WRITE2_IO(); -#else - while (i--) - write2_io(0, IO_FIFO); - -#endif - - } - } - - else { - write1_io(0, IO_FIFO_READ); /* put fifo in read mode */ - hostdata->fifo = FI_FIFO_READING; - cmd->SCp.have_data_in = 0; /* nothing transferred yet */ - } - - } else { - write_3393_count(hostdata, 0); /* this guarantees a DATA_PHASE interrupt */ - } - hostdata->state = S_RUNNING_LEVEL2; - write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER); - } - - /* - * Since the SCSI bus can handle only 1 connection at a time, - * we get out of here now. If the selection fails, or when - * the command disconnects, we'll come back to this routine - * to search the input_Q again... - */ - - DB(DB_EXECUTE, printk("%s)EX-2 ", (cmd->SCp.phase) ? "d:" : "")) - -} - - - -static void transfer_pio(uchar * buf, int cnt, int data_in_dir, struct IN2000_hostdata *hostdata) -{ - uchar asr; - - DB(DB_TRANSFER, printk("(%p,%d,%s)", buf, cnt, data_in_dir ? "in" : "out")) - - write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED); - write_3393_count(hostdata, cnt); - write_3393_cmd(hostdata, WD_CMD_TRANS_INFO); - if (data_in_dir) { - do { - asr = READ_AUX_STAT(); - if (asr & ASR_DBR) - *buf++ = read_3393(hostdata, WD_DATA); - } while (!(asr & ASR_INT)); - } else { - do { - asr = READ_AUX_STAT(); - if (asr & ASR_DBR) - write_3393(hostdata, WD_DATA, *buf++); - } while (!(asr & ASR_INT)); - } - - /* Note: we are returning with the interrupt UN-cleared. - * Since (presumably) an entire I/O operation has - * completed, the bus phase is probably different, and - * the interrupt routine will discover this when it - * responds to the uncleared int. - */ - -} - - - -static void transfer_bytes(Scsi_Cmnd * cmd, int data_in_dir) -{ - struct IN2000_hostdata *hostdata; - unsigned short *sp; - unsigned short f; - int i; - - hostdata = (struct IN2000_hostdata *) cmd->device->host->hostdata; - -/* Normally, you'd expect 'this_residual' to be non-zero here. - * In a series of scatter-gather transfers, however, this - * routine will usually be called with 'this_residual' equal - * to 0 and 'buffers_residual' non-zero. This means that a - * previous transfer completed, clearing 'this_residual', and - * now we need to setup the next scatter-gather buffer as the - * source or destination for THIS transfer. - */ - if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) { - ++cmd->SCp.buffer; - --cmd->SCp.buffers_residual; - cmd->SCp.this_residual = cmd->SCp.buffer->length; - cmd->SCp.ptr = sg_virt(cmd->SCp.buffer); - } - -/* Set up hardware registers */ - - write_3393(hostdata, WD_SYNCHRONOUS_TRANSFER, hostdata->sync_xfer[cmd->device->id]); - write_3393_count(hostdata, cmd->SCp.this_residual); - write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_BUS); - write1_io(0, IO_FIFO_WRITE); /* zero counter, assume write */ - -/* Reading is easy. Just issue the command and return - we'll - * get an interrupt later when we have actual data to worry about. - */ - - if (data_in_dir) { - write1_io(0, IO_FIFO_READ); - if ((hostdata->level2 >= L2_DATA) || (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) { - write_3393(hostdata, WD_COMMAND_PHASE, 0x45); - write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER); - hostdata->state = S_RUNNING_LEVEL2; - } else - write_3393_cmd(hostdata, WD_CMD_TRANS_INFO); - hostdata->fifo = FI_FIFO_READING; - cmd->SCp.have_data_in = 0; - return; - } - -/* Writing is more involved - we'll start the WD chip and write as - * much data to the fifo as we can right now. Later interrupts will - * write any bytes that don't make it at this stage. - */ - - if ((hostdata->level2 >= L2_DATA) || (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) { - write_3393(hostdata, WD_COMMAND_PHASE, 0x45); - write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER); - hostdata->state = S_RUNNING_LEVEL2; - } else - write_3393_cmd(hostdata, WD_CMD_TRANS_INFO); - hostdata->fifo = FI_FIFO_WRITING; - sp = (unsigned short *) cmd->SCp.ptr; - - if ((i = cmd->SCp.this_residual) > IN2000_FIFO_SIZE) - i = IN2000_FIFO_SIZE; - cmd->SCp.have_data_in = i; - i >>= 1; /* Gulp. We assume this_residual is modulo 2 */ - f = hostdata->io_base + IO_FIFO; - -#ifdef FAST_WRITE_IO - - FAST_WRITE2_IO(); -#else - while (i--) - write2_io(*sp++, IO_FIFO); - -#endif - -} - - -/* We need to use spin_lock_irqsave() & spin_unlock_irqrestore() in this - * function in order to work in an SMP environment. (I'd be surprised - * if the driver is ever used by anyone on a real multi-CPU motherboard, - * but it _does_ need to be able to compile and run in an SMP kernel.) - */ - -static irqreturn_t in2000_intr(int irqnum, void *dev_id) -{ - struct Scsi_Host *instance = dev_id; - struct IN2000_hostdata *hostdata; - Scsi_Cmnd *patch, *cmd; - uchar asr, sr, phs, id, lun, *ucp, msg; - int i, j; - unsigned long length; - unsigned short *sp; - unsigned short f; - unsigned long flags; - - hostdata = (struct IN2000_hostdata *) instance->hostdata; - -/* Get the spin_lock and disable further ints, for SMP */ - - spin_lock_irqsave(instance->host_lock, flags); - -#ifdef PROC_STATISTICS - hostdata->int_cnt++; -#endif - -/* The IN2000 card has 2 interrupt sources OR'ed onto its IRQ line - the - * WD3393 chip and the 2k fifo (which is actually a dual-port RAM combined - * with a big logic array, so it's a little different than what you might - * expect). As far as I know, there's no reason that BOTH can't be active - * at the same time, but there's a problem: while we can read the 3393 - * to tell if _it_ wants an interrupt, I don't know of a way to ask the - * fifo the same question. The best we can do is check the 3393 and if - * it _isn't_ the source of the interrupt, then we can be pretty sure - * that the fifo is the culprit. - * UPDATE: I have it on good authority (Bill Earnest) that bit 0 of the - * IO_FIFO_COUNT register mirrors the fifo interrupt state. I - * assume that bit clear means interrupt active. As it turns - * out, the driver really doesn't need to check for this after - * all, so my remarks above about a 'problem' can safely be - * ignored. The way the logic is set up, there's no advantage - * (that I can see) to worrying about it. - * - * It seems that the fifo interrupt signal is negated when we extract - * bytes during read or write bytes during write. - * - fifo will interrupt when data is moving from it to the 3393, and - * there are 31 (or less?) bytes left to go. This is sort of short- - * sighted: what if you don't WANT to do more? In any case, our - * response is to push more into the fifo - either actual data or - * dummy bytes if need be. Note that we apparently have to write at - * least 32 additional bytes to the fifo after an interrupt in order - * to get it to release the ones it was holding on to - writing fewer - * than 32 will result in another fifo int. - * UPDATE: Again, info from Bill Earnest makes this more understandable: - * 32 bytes = two counts of the fifo counter register. He tells - * me that the fifo interrupt is a non-latching signal derived - * from a straightforward boolean interpretation of the 7 - * highest bits of the fifo counter and the fifo-read/fifo-write - * state. Who'd a thought? - */ - - write1_io(0, IO_LED_ON); - asr = READ_AUX_STAT(); - if (!(asr & ASR_INT)) { /* no WD33c93 interrupt? */ - -/* Ok. This is definitely a FIFO-only interrupt. - * - * If FI_FIFO_READING is set, there are up to 2048 bytes waiting to be read, - * maybe more to come from the SCSI bus. Read as many as we can out of the - * fifo and into memory at the location of SCp.ptr[SCp.have_data_in], and - * update have_data_in afterwards. - * - * If we have FI_FIFO_WRITING, the FIFO has almost run out of bytes to move - * into the WD3393 chip (I think the interrupt happens when there are 31 - * bytes left, but it may be fewer...). The 3393 is still waiting, so we - * shove some more into the fifo, which gets things moving again. If the - * original SCSI command specified more than 2048 bytes, there may still - * be some of that data left: fine - use it (from SCp.ptr[SCp.have_data_in]). - * Don't forget to update have_data_in. If we've already written out the - * entire buffer, feed 32 dummy bytes to the fifo - they're needed to - * push out the remaining real data. - * (Big thanks to Bill Earnest for getting me out of the mud in here.) - */ - - cmd = (Scsi_Cmnd *) hostdata->connected; /* assume we're connected */ - CHECK_NULL(cmd, "fifo_int") - - if (hostdata->fifo == FI_FIFO_READING) { - - DB(DB_FIFO, printk("{R:%02x} ", read1_io(IO_FIFO_COUNT))) - - sp = (unsigned short *) (cmd->SCp.ptr + cmd->SCp.have_data_in); - i = read1_io(IO_FIFO_COUNT) & 0xfe; - i <<= 2; /* # of words waiting in the fifo */ - f = hostdata->io_base + IO_FIFO; - -#ifdef FAST_READ_IO - - FAST_READ2_IO(); -#else - while (i--) - *sp++ = read2_io(IO_FIFO); - -#endif - - i = sp - (unsigned short *) (cmd->SCp.ptr + cmd->SCp.have_data_in); - i <<= 1; - cmd->SCp.have_data_in += i; - } - - else if (hostdata->fifo == FI_FIFO_WRITING) { - - DB(DB_FIFO, printk("{W:%02x} ", read1_io(IO_FIFO_COUNT))) - -/* If all bytes have been written to the fifo, flush out the stragglers. - * Note that while writing 16 dummy words seems arbitrary, we don't - * have another choice that I can see. What we really want is to read - * the 3393 transfer count register (that would tell us how many bytes - * needed flushing), but the TRANSFER_INFO command hasn't completed - * yet (not enough bytes!) and that register won't be accessible. So, - * we use 16 words - a number obtained through trial and error. - * UPDATE: Bill says this is exactly what Always does, so there. - * More thanks due him for help in this section. - */ - if (cmd->SCp.this_residual == cmd->SCp.have_data_in) { - i = 16; - while (i--) /* write 32 dummy bytes */ - write2_io(0, IO_FIFO); - } - -/* If there are still bytes left in the SCSI buffer, write as many as we - * can out to the fifo. - */ - - else { - sp = (unsigned short *) (cmd->SCp.ptr + cmd->SCp.have_data_in); - i = cmd->SCp.this_residual - cmd->SCp.have_data_in; /* bytes yet to go */ - j = read1_io(IO_FIFO_COUNT) & 0xfe; - j <<= 2; /* how many words the fifo has room for */ - if ((j << 1) > i) - j = (i >> 1); - while (j--) - write2_io(*sp++, IO_FIFO); - - i = sp - (unsigned short *) (cmd->SCp.ptr + cmd->SCp.have_data_in); - i <<= 1; - cmd->SCp.have_data_in += i; - } - } - - else { - printk("*** Spurious FIFO interrupt ***"); - } - - write1_io(0, IO_LED_OFF); - -/* release the SMP spin_lock and restore irq state */ - spin_unlock_irqrestore(instance->host_lock, flags); - return IRQ_HANDLED; - } - -/* This interrupt was triggered by the WD33c93 chip. The fifo interrupt - * may also be asserted, but we don't bother to check it: we get more - * detailed info from FIFO_READING and FIFO_WRITING (see below). - */ - - cmd = (Scsi_Cmnd *) hostdata->connected; /* assume we're connected */ - sr = read_3393(hostdata, WD_SCSI_STATUS); /* clear the interrupt */ - phs = read_3393(hostdata, WD_COMMAND_PHASE); - - if (!cmd && (sr != CSR_RESEL_AM && sr != CSR_TIMEOUT && sr != CSR_SELECT)) { - printk("\nNR:wd-intr-1\n"); - write1_io(0, IO_LED_OFF); - -/* release the SMP spin_lock and restore irq state */ - spin_unlock_irqrestore(instance->host_lock, flags); - return IRQ_HANDLED; - } - - DB(DB_INTR, printk("{%02x:%02x-", asr, sr)) - -/* After starting a FIFO-based transfer, the next _WD3393_ interrupt is - * guaranteed to be in response to the completion of the transfer. - * If we were reading, there's probably data in the fifo that needs - * to be copied into RAM - do that here. Also, we have to update - * 'this_residual' and 'ptr' based on the contents of the - * TRANSFER_COUNT register, in case the device decided to do an - * intermediate disconnect (a device may do this if it has to - * do a seek, or just to be nice and let other devices have - * some bus time during long transfers). - * After doing whatever is necessary with the fifo, we go on and - * service the WD3393 interrupt normally. - */ - if (hostdata->fifo == FI_FIFO_READING) { - -/* buffer index = start-of-buffer + #-of-bytes-already-read */ - - sp = (unsigned short *) (cmd->SCp.ptr + cmd->SCp.have_data_in); - -/* bytes remaining in fifo = (total-wanted - #-not-got) - #-already-read */ - - i = (cmd->SCp.this_residual - read_3393_count(hostdata)) - cmd->SCp.have_data_in; - i >>= 1; /* Gulp. We assume this will always be modulo 2 */ - f = hostdata->io_base + IO_FIFO; - -#ifdef FAST_READ_IO - - FAST_READ2_IO(); -#else - while (i--) - *sp++ = read2_io(IO_FIFO); - -#endif - - hostdata->fifo = FI_FIFO_UNUSED; - length = cmd->SCp.this_residual; - cmd->SCp.this_residual = read_3393_count(hostdata); - cmd->SCp.ptr += (length - cmd->SCp.this_residual); - - DB(DB_TRANSFER, printk("(%p,%d)", cmd->SCp.ptr, cmd->SCp.this_residual)) - - } - - else if (hostdata->fifo == FI_FIFO_WRITING) { - hostdata->fifo = FI_FIFO_UNUSED; - length = cmd->SCp.this_residual; - cmd->SCp.this_residual = read_3393_count(hostdata); - cmd->SCp.ptr += (length - cmd->SCp.this_residual); - - DB(DB_TRANSFER, printk("(%p,%d)", cmd->SCp.ptr, cmd->SCp.this_residual)) - - } - -/* Respond to the specific WD3393 interrupt - there are quite a few! */ - - switch (sr) { - - case CSR_TIMEOUT: - DB(DB_INTR, printk("TIMEOUT")) - - if (hostdata->state == S_RUNNING_LEVEL2) - hostdata->connected = NULL; - else { - cmd = (Scsi_Cmnd *) hostdata->selecting; /* get a valid cmd */ - CHECK_NULL(cmd, "csr_timeout") - hostdata->selecting = NULL; - } - - cmd->result = DID_NO_CONNECT << 16; - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); - hostdata->state = S_UNCONNECTED; - cmd->scsi_done(cmd); - -/* We are not connected to a target - check to see if there - * are commands waiting to be executed. - */ - - in2000_execute(instance); - break; - - -/* Note: this interrupt should not occur in a LEVEL2 command */ - - case CSR_SELECT: - DB(DB_INTR, printk("SELECT")) - hostdata->connected = cmd = (Scsi_Cmnd *) hostdata->selecting; - CHECK_NULL(cmd, "csr_select") - hostdata->selecting = NULL; - - /* construct an IDENTIFY message with correct disconnect bit */ - - hostdata->outgoing_msg[0] = (0x80 | 0x00 | cmd->device->lun); - if (cmd->SCp.phase) - hostdata->outgoing_msg[0] |= 0x40; - - if (hostdata->sync_stat[cmd->device->id] == SS_FIRST) { -#ifdef SYNC_DEBUG - printk(" sending SDTR "); -#endif - - hostdata->sync_stat[cmd->device->id] = SS_WAITING; - - /* tack on a 2nd message to ask about synchronous transfers */ - - hostdata->outgoing_msg[1] = EXTENDED_MESSAGE; - hostdata->outgoing_msg[2] = 3; - hostdata->outgoing_msg[3] = EXTENDED_SDTR; - hostdata->outgoing_msg[4] = OPTIMUM_SX_PER / 4; - hostdata->outgoing_msg[5] = OPTIMUM_SX_OFF; - hostdata->outgoing_len = 6; - } else - hostdata->outgoing_len = 1; - - hostdata->state = S_CONNECTED; - break; - - - case CSR_XFER_DONE | PHS_DATA_IN: - case CSR_UNEXP | PHS_DATA_IN: - case CSR_SRV_REQ | PHS_DATA_IN: - DB(DB_INTR, printk("IN-%d.%d", cmd->SCp.this_residual, cmd->SCp.buffers_residual)) - transfer_bytes(cmd, DATA_IN_DIR); - if (hostdata->state != S_RUNNING_LEVEL2) - hostdata->state = S_CONNECTED; - break; - - - case CSR_XFER_DONE | PHS_DATA_OUT: - case CSR_UNEXP | PHS_DATA_OUT: - case CSR_SRV_REQ | PHS_DATA_OUT: - DB(DB_INTR, printk("OUT-%d.%d", cmd->SCp.this_residual, cmd->SCp.buffers_residual)) - transfer_bytes(cmd, DATA_OUT_DIR); - if (hostdata->state != S_RUNNING_LEVEL2) - hostdata->state = S_CONNECTED; - break; - - -/* Note: this interrupt should not occur in a LEVEL2 command */ - - case CSR_XFER_DONE | PHS_COMMAND: - case CSR_UNEXP | PHS_COMMAND: - case CSR_SRV_REQ | PHS_COMMAND: - DB(DB_INTR, printk("CMND-%02x", cmd->cmnd[0])) - transfer_pio(cmd->cmnd, cmd->cmd_len, DATA_OUT_DIR, hostdata); - hostdata->state = S_CONNECTED; - break; - - - case CSR_XFER_DONE | PHS_STATUS: - case CSR_UNEXP | PHS_STATUS: - case CSR_SRV_REQ | PHS_STATUS: - DB(DB_INTR, printk("STATUS=")) - - cmd->SCp.Status = read_1_byte(hostdata); - DB(DB_INTR, printk("%02x", cmd->SCp.Status)) - if (hostdata->level2 >= L2_BASIC) { - sr = read_3393(hostdata, WD_SCSI_STATUS); /* clear interrupt */ - hostdata->state = S_RUNNING_LEVEL2; - write_3393(hostdata, WD_COMMAND_PHASE, 0x50); - write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER); - } else { - hostdata->state = S_CONNECTED; - } - break; - - - case CSR_XFER_DONE | PHS_MESS_IN: - case CSR_UNEXP | PHS_MESS_IN: - case CSR_SRV_REQ | PHS_MESS_IN: - DB(DB_INTR, printk("MSG_IN=")) - - msg = read_1_byte(hostdata); - sr = read_3393(hostdata, WD_SCSI_STATUS); /* clear interrupt */ - - hostdata->incoming_msg[hostdata->incoming_ptr] = msg; - if (hostdata->incoming_msg[0] == EXTENDED_MESSAGE) - msg = EXTENDED_MESSAGE; - else - hostdata->incoming_ptr = 0; - - cmd->SCp.Message = msg; - switch (msg) { - - case COMMAND_COMPLETE: - DB(DB_INTR, printk("CCMP")) - write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK); - hostdata->state = S_PRE_CMP_DISC; - break; - - case SAVE_POINTERS: - DB(DB_INTR, printk("SDP")) - write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK); - hostdata->state = S_CONNECTED; - break; - - case RESTORE_POINTERS: - DB(DB_INTR, printk("RDP")) - if (hostdata->level2 >= L2_BASIC) { - write_3393(hostdata, WD_COMMAND_PHASE, 0x45); - write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER); - hostdata->state = S_RUNNING_LEVEL2; - } else { - write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK); - hostdata->state = S_CONNECTED; - } - break; - - case DISCONNECT: - DB(DB_INTR, printk("DIS")) - cmd->device->disconnect = 1; - write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK); - hostdata->state = S_PRE_TMP_DISC; - break; - - case MESSAGE_REJECT: - DB(DB_INTR, printk("REJ")) -#ifdef SYNC_DEBUG - printk("-REJ-"); -#endif - if (hostdata->sync_stat[cmd->device->id] == SS_WAITING) - hostdata->sync_stat[cmd->device->id] = SS_SET; - write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK); - hostdata->state = S_CONNECTED; - break; - - case EXTENDED_MESSAGE: - DB(DB_INTR, printk("EXT")) - - ucp = hostdata->incoming_msg; - -#ifdef SYNC_DEBUG - printk("%02x", ucp[hostdata->incoming_ptr]); -#endif - /* Is this the last byte of the extended message? */ - - if ((hostdata->incoming_ptr >= 2) && (hostdata->incoming_ptr == (ucp[1] + 1))) { - - switch (ucp[2]) { /* what's the EXTENDED code? */ - case EXTENDED_SDTR: - id = calc_sync_xfer(ucp[3], ucp[4]); - if (hostdata->sync_stat[cmd->device->id] != SS_WAITING) { - -/* A device has sent an unsolicited SDTR message; rather than go - * through the effort of decoding it and then figuring out what - * our reply should be, we're just gonna say that we have a - * synchronous fifo depth of 0. This will result in asynchronous - * transfers - not ideal but so much easier. - * Actually, this is OK because it assures us that if we don't - * specifically ask for sync transfers, we won't do any. - */ - - write_3393_cmd(hostdata, WD_CMD_ASSERT_ATN); /* want MESS_OUT */ - hostdata->outgoing_msg[0] = EXTENDED_MESSAGE; - hostdata->outgoing_msg[1] = 3; - hostdata->outgoing_msg[2] = EXTENDED_SDTR; - hostdata->outgoing_msg[3] = hostdata->default_sx_per / 4; - hostdata->outgoing_msg[4] = 0; - hostdata->outgoing_len = 5; - hostdata->sync_xfer[cmd->device->id] = calc_sync_xfer(hostdata->default_sx_per / 4, 0); - } else { - hostdata->sync_xfer[cmd->device->id] = id; - } -#ifdef SYNC_DEBUG - printk("sync_xfer=%02x", hostdata->sync_xfer[cmd->device->id]); -#endif - hostdata->sync_stat[cmd->device->id] = SS_SET; - write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK); - hostdata->state = S_CONNECTED; - break; - case EXTENDED_WDTR: - write_3393_cmd(hostdata, WD_CMD_ASSERT_ATN); /* want MESS_OUT */ - printk("sending WDTR "); - hostdata->outgoing_msg[0] = EXTENDED_MESSAGE; - hostdata->outgoing_msg[1] = 2; - hostdata->outgoing_msg[2] = EXTENDED_WDTR; - hostdata->outgoing_msg[3] = 0; /* 8 bit transfer width */ - hostdata->outgoing_len = 4; - write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK); - hostdata->state = S_CONNECTED; - break; - default: - write_3393_cmd(hostdata, WD_CMD_ASSERT_ATN); /* want MESS_OUT */ - printk("Rejecting Unknown Extended Message(%02x). ", ucp[2]); - hostdata->outgoing_msg[0] = MESSAGE_REJECT; - hostdata->outgoing_len = 1; - write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK); - hostdata->state = S_CONNECTED; - break; - } - hostdata->incoming_ptr = 0; - } - - /* We need to read more MESS_IN bytes for the extended message */ - - else { - hostdata->incoming_ptr++; - write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK); - hostdata->state = S_CONNECTED; - } - break; - - default: - printk("Rejecting Unknown Message(%02x) ", msg); - write_3393_cmd(hostdata, WD_CMD_ASSERT_ATN); /* want MESS_OUT */ - hostdata->outgoing_msg[0] = MESSAGE_REJECT; - hostdata->outgoing_len = 1; - write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK); - hostdata->state = S_CONNECTED; - } - break; - - -/* Note: this interrupt will occur only after a LEVEL2 command */ - - case CSR_SEL_XFER_DONE: - -/* Make sure that reselection is enabled at this point - it may - * have been turned off for the command that just completed. - */ - - write_3393(hostdata, WD_SOURCE_ID, SRCID_ER); - if (phs == 0x60) { - DB(DB_INTR, printk("SX-DONE")) - cmd->SCp.Message = COMMAND_COMPLETE; - lun = read_3393(hostdata, WD_TARGET_LUN); - DB(DB_INTR, printk(":%d.%d", cmd->SCp.Status, lun)) - hostdata->connected = NULL; - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); - hostdata->state = S_UNCONNECTED; - if (cmd->SCp.Status == ILLEGAL_STATUS_BYTE) - cmd->SCp.Status = lun; - if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD) - cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); - else - cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); - cmd->scsi_done(cmd); - -/* We are no longer connected to a target - check to see if - * there are commands waiting to be executed. - */ - - in2000_execute(instance); - } else { - printk("%02x:%02x:%02x: Unknown SEL_XFER_DONE phase!!---", asr, sr, phs); - } - break; - - -/* Note: this interrupt will occur only after a LEVEL2 command */ - - case CSR_SDP: - DB(DB_INTR, printk("SDP")) - hostdata->state = S_RUNNING_LEVEL2; - write_3393(hostdata, WD_COMMAND_PHASE, 0x41); - write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER); - break; - - - case CSR_XFER_DONE | PHS_MESS_OUT: - case CSR_UNEXP | PHS_MESS_OUT: - case CSR_SRV_REQ | PHS_MESS_OUT: - DB(DB_INTR, printk("MSG_OUT=")) - -/* To get here, we've probably requested MESSAGE_OUT and have - * already put the correct bytes in outgoing_msg[] and filled - * in outgoing_len. We simply send them out to the SCSI bus. - * Sometimes we get MESSAGE_OUT phase when we're not expecting - * it - like when our SDTR message is rejected by a target. Some - * targets send the REJECT before receiving all of the extended - * message, and then seem to go back to MESSAGE_OUT for a byte - * or two. Not sure why, or if I'm doing something wrong to - * cause this to happen. Regardless, it seems that sending - * NOP messages in these situations results in no harm and - * makes everyone happy. - */ - if (hostdata->outgoing_len == 0) { - hostdata->outgoing_len = 1; - hostdata->outgoing_msg[0] = NOP; - } - transfer_pio(hostdata->outgoing_msg, hostdata->outgoing_len, DATA_OUT_DIR, hostdata); - DB(DB_INTR, printk("%02x", hostdata->outgoing_msg[0])) - hostdata->outgoing_len = 0; - hostdata->state = S_CONNECTED; - break; - - - case CSR_UNEXP_DISC: - -/* I think I've seen this after a request-sense that was in response - * to an error condition, but not sure. We certainly need to do - * something when we get this interrupt - the question is 'what?'. - * Let's think positively, and assume some command has finished - * in a legal manner (like a command that provokes a request-sense), - * so we treat it as a normal command-complete-disconnect. - */ - - -/* Make sure that reselection is enabled at this point - it may - * have been turned off for the command that just completed. - */ - - write_3393(hostdata, WD_SOURCE_ID, SRCID_ER); - if (cmd == NULL) { - printk(" - Already disconnected! "); - hostdata->state = S_UNCONNECTED; - -/* release the SMP spin_lock and restore irq state */ - spin_unlock_irqrestore(instance->host_lock, flags); - return IRQ_HANDLED; - } - DB(DB_INTR, printk("UNEXP_DISC")) - hostdata->connected = NULL; - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); - hostdata->state = S_UNCONNECTED; - if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD) - cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); - else - cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); - cmd->scsi_done(cmd); - -/* We are no longer connected to a target - check to see if - * there are commands waiting to be executed. - */ - - in2000_execute(instance); - break; - - - case CSR_DISC: - -/* Make sure that reselection is enabled at this point - it may - * have been turned off for the command that just completed. - */ - - write_3393(hostdata, WD_SOURCE_ID, SRCID_ER); - DB(DB_INTR, printk("DISC")) - if (cmd == NULL) { - printk(" - Already disconnected! "); - hostdata->state = S_UNCONNECTED; - } - switch (hostdata->state) { - case S_PRE_CMP_DISC: - hostdata->connected = NULL; - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); - hostdata->state = S_UNCONNECTED; - DB(DB_INTR, printk(":%d", cmd->SCp.Status)) - if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD) - cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); - else - cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); - cmd->scsi_done(cmd); - break; - case S_PRE_TMP_DISC: - case S_RUNNING_LEVEL2: - cmd->host_scribble = (uchar *) hostdata->disconnected_Q; - hostdata->disconnected_Q = cmd; - hostdata->connected = NULL; - hostdata->state = S_UNCONNECTED; - -#ifdef PROC_STATISTICS - hostdata->disc_done_cnt[cmd->device->id]++; -#endif - - break; - default: - printk("*** Unexpected DISCONNECT interrupt! ***"); - hostdata->state = S_UNCONNECTED; - } - -/* We are no longer connected to a target - check to see if - * there are commands waiting to be executed. - */ - - in2000_execute(instance); - break; - - - case CSR_RESEL_AM: - DB(DB_INTR, printk("RESEL")) - - /* First we have to make sure this reselection didn't */ - /* happen during Arbitration/Selection of some other device. */ - /* If yes, put losing command back on top of input_Q. */ - if (hostdata->level2 <= L2_NONE) { - - if (hostdata->selecting) { - cmd = (Scsi_Cmnd *) hostdata->selecting; - hostdata->selecting = NULL; - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); - cmd->host_scribble = (uchar *) hostdata->input_Q; - hostdata->input_Q = cmd; - } - } - - else { - - if (cmd) { - if (phs == 0x00) { - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); - cmd->host_scribble = (uchar *) hostdata->input_Q; - hostdata->input_Q = cmd; - } else { - printk("---%02x:%02x:%02x-TROUBLE: Intrusive ReSelect!---", asr, sr, phs); - while (1) - printk("\r"); - } - } - - } - - /* OK - find out which device reselected us. */ - - id = read_3393(hostdata, WD_SOURCE_ID); - id &= SRCID_MASK; - - /* and extract the lun from the ID message. (Note that we don't - * bother to check for a valid message here - I guess this is - * not the right way to go, but....) - */ - - lun = read_3393(hostdata, WD_DATA); - if (hostdata->level2 < L2_RESELECT) - write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK); - lun &= 7; - - /* Now we look for the command that's reconnecting. */ - - cmd = (Scsi_Cmnd *) hostdata->disconnected_Q; - patch = NULL; - while (cmd) { - if (id == cmd->device->id && lun == cmd->device->lun) - break; - patch = cmd; - cmd = (Scsi_Cmnd *) cmd->host_scribble; - } - - /* Hmm. Couldn't find a valid command.... What to do? */ - - if (!cmd) { - printk("---TROUBLE: target %d.%d not in disconnect queue---", id, lun); - break; - } - - /* Ok, found the command - now start it up again. */ - - if (patch) - patch->host_scribble = cmd->host_scribble; - else - hostdata->disconnected_Q = (Scsi_Cmnd *) cmd->host_scribble; - hostdata->connected = cmd; - - /* We don't need to worry about 'initialize_SCp()' or 'hostdata->busy[]' - * because these things are preserved over a disconnect. - * But we DO need to fix the DPD bit so it's correct for this command. - */ - - if (is_dir_out(cmd)) - write_3393(hostdata, WD_DESTINATION_ID, cmd->device->id); - else - write_3393(hostdata, WD_DESTINATION_ID, cmd->device->id | DSTID_DPD); - if (hostdata->level2 >= L2_RESELECT) { - write_3393_count(hostdata, 0); /* we want a DATA_PHASE interrupt */ - write_3393(hostdata, WD_COMMAND_PHASE, 0x45); - write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER); - hostdata->state = S_RUNNING_LEVEL2; - } else - hostdata->state = S_CONNECTED; - - break; - - default: - printk("--UNKNOWN INTERRUPT:%02x:%02x:%02x--", asr, sr, phs); - } - - write1_io(0, IO_LED_OFF); - - DB(DB_INTR, printk("} ")) - -/* release the SMP spin_lock and restore irq state */ - spin_unlock_irqrestore(instance->host_lock, flags); - return IRQ_HANDLED; -} - - - -#define RESET_CARD 0 -#define RESET_CARD_AND_BUS 1 -#define B_FLAG 0x80 - -/* - * Caller must hold instance lock! - */ - -static int reset_hardware(struct Scsi_Host *instance, int type) -{ - struct IN2000_hostdata *hostdata; - int qt, x; - - hostdata = (struct IN2000_hostdata *) instance->hostdata; - - write1_io(0, IO_LED_ON); - if (type == RESET_CARD_AND_BUS) { - write1_io(0, IO_CARD_RESET); - x = read1_io(IO_HARDWARE); - } - x = read_3393(hostdata, WD_SCSI_STATUS); /* clear any WD intrpt */ - write_3393(hostdata, WD_OWN_ID, instance->this_id | OWNID_EAF | OWNID_RAF | OWNID_FS_8); - write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED); - write_3393(hostdata, WD_SYNCHRONOUS_TRANSFER, calc_sync_xfer(hostdata->default_sx_per / 4, DEFAULT_SX_OFF)); - - write1_io(0, IO_FIFO_WRITE); /* clear fifo counter */ - write1_io(0, IO_FIFO_READ); /* start fifo out in read mode */ - write_3393(hostdata, WD_COMMAND, WD_CMD_RESET); - /* FIXME: timeout ?? */ - while (!(READ_AUX_STAT() & ASR_INT)) - cpu_relax(); /* wait for RESET to complete */ - - x = read_3393(hostdata, WD_SCSI_STATUS); /* clear interrupt */ - - write_3393(hostdata, WD_QUEUE_TAG, 0xa5); /* any random number */ - qt = read_3393(hostdata, WD_QUEUE_TAG); - if (qt == 0xa5) { - x |= B_FLAG; - write_3393(hostdata, WD_QUEUE_TAG, 0); - } - write_3393(hostdata, WD_TIMEOUT_PERIOD, TIMEOUT_PERIOD_VALUE); - write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED); - write1_io(0, IO_LED_OFF); - return x; -} - - - -static int in2000_bus_reset(Scsi_Cmnd * cmd) -{ - struct Scsi_Host *instance; - struct IN2000_hostdata *hostdata; - int x; - unsigned long flags; - - instance = cmd->device->host; - hostdata = (struct IN2000_hostdata *) instance->hostdata; - - printk(KERN_WARNING "scsi%d: Reset. ", instance->host_no); - - spin_lock_irqsave(instance->host_lock, flags); - - /* do scsi-reset here */ - reset_hardware(instance, RESET_CARD_AND_BUS); - for (x = 0; x < 8; x++) { - hostdata->busy[x] = 0; - hostdata->sync_xfer[x] = calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF); - hostdata->sync_stat[x] = SS_UNSET; /* using default sync values */ - } - hostdata->input_Q = NULL; - hostdata->selecting = NULL; - hostdata->connected = NULL; - hostdata->disconnected_Q = NULL; - hostdata->state = S_UNCONNECTED; - hostdata->fifo = FI_FIFO_UNUSED; - hostdata->incoming_ptr = 0; - hostdata->outgoing_len = 0; - - cmd->result = DID_RESET << 16; - - spin_unlock_irqrestore(instance->host_lock, flags); - return SUCCESS; -} - -static int __in2000_abort(Scsi_Cmnd * cmd) -{ - struct Scsi_Host *instance; - struct IN2000_hostdata *hostdata; - Scsi_Cmnd *tmp, *prev; - uchar sr, asr; - unsigned long timeout; - - instance = cmd->device->host; - hostdata = (struct IN2000_hostdata *) instance->hostdata; - - printk(KERN_DEBUG "scsi%d: Abort-", instance->host_no); - printk("(asr=%02x,count=%ld,resid=%d,buf_resid=%d,have_data=%d,FC=%02x)- ", READ_AUX_STAT(), read_3393_count(hostdata), cmd->SCp.this_residual, cmd->SCp.buffers_residual, cmd->SCp.have_data_in, read1_io(IO_FIFO_COUNT)); - -/* - * Case 1 : If the command hasn't been issued yet, we simply remove it - * from the inout_Q. - */ - - tmp = (Scsi_Cmnd *) hostdata->input_Q; - prev = NULL; - while (tmp) { - if (tmp == cmd) { - if (prev) - prev->host_scribble = cmd->host_scribble; - cmd->host_scribble = NULL; - cmd->result = DID_ABORT << 16; - printk(KERN_WARNING "scsi%d: Abort - removing command from input_Q. ", instance->host_no); - cmd->scsi_done(cmd); - return SUCCESS; - } - prev = tmp; - tmp = (Scsi_Cmnd *) tmp->host_scribble; - } - -/* - * Case 2 : If the command is connected, we're going to fail the abort - * and let the high level SCSI driver retry at a later time or - * issue a reset. - * - * Timeouts, and therefore aborted commands, will be highly unlikely - * and handling them cleanly in this situation would make the common - * case of noresets less efficient, and would pollute our code. So, - * we fail. - */ - - if (hostdata->connected == cmd) { - - printk(KERN_WARNING "scsi%d: Aborting connected command - ", instance->host_no); - - printk("sending wd33c93 ABORT command - "); - write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED); - write_3393_cmd(hostdata, WD_CMD_ABORT); - -/* Now we have to attempt to flush out the FIFO... */ - - printk("flushing fifo - "); - timeout = 1000000; - do { - asr = READ_AUX_STAT(); - if (asr & ASR_DBR) - read_3393(hostdata, WD_DATA); - } while (!(asr & ASR_INT) && timeout-- > 0); - sr = read_3393(hostdata, WD_SCSI_STATUS); - printk("asr=%02x, sr=%02x, %ld bytes un-transferred (timeout=%ld) - ", asr, sr, read_3393_count(hostdata), timeout); - - /* - * Abort command processed. - * Still connected. - * We must disconnect. - */ - - printk("sending wd33c93 DISCONNECT command - "); - write_3393_cmd(hostdata, WD_CMD_DISCONNECT); - - timeout = 1000000; - asr = READ_AUX_STAT(); - while ((asr & ASR_CIP) && timeout-- > 0) - asr = READ_AUX_STAT(); - sr = read_3393(hostdata, WD_SCSI_STATUS); - printk("asr=%02x, sr=%02x.", asr, sr); - - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); - hostdata->connected = NULL; - hostdata->state = S_UNCONNECTED; - cmd->result = DID_ABORT << 16; - cmd->scsi_done(cmd); - - in2000_execute(instance); - - return SUCCESS; - } - -/* - * Case 3: If the command is currently disconnected from the bus, - * we're not going to expend much effort here: Let's just return - * an ABORT_SNOOZE and hope for the best... - */ - - for (tmp = (Scsi_Cmnd *) hostdata->disconnected_Q; tmp; tmp = (Scsi_Cmnd *) tmp->host_scribble) - if (cmd == tmp) { - printk(KERN_DEBUG "scsi%d: unable to abort disconnected command.\n", instance->host_no); - return FAILED; - } - -/* - * Case 4 : If we reached this point, the command was not found in any of - * the queues. - * - * We probably reached this point because of an unlikely race condition - * between the command completing successfully and the abortion code, - * so we won't panic, but we will notify the user in case something really - * broke. - */ - - in2000_execute(instance); - - printk("scsi%d: warning : SCSI command probably completed successfully" " before abortion. ", instance->host_no); - return SUCCESS; -} - -static int in2000_abort(Scsi_Cmnd * cmd) -{ - int rc; - - spin_lock_irq(cmd->device->host->host_lock); - rc = __in2000_abort(cmd); - spin_unlock_irq(cmd->device->host->host_lock); - - return rc; -} - - -#define MAX_IN2000_HOSTS 3 -#define MAX_SETUP_ARGS ARRAY_SIZE(setup_args) -#define SETUP_BUFFER_SIZE 200 -static char setup_buffer[SETUP_BUFFER_SIZE]; -static char setup_used[MAX_SETUP_ARGS]; -static int done_setup = 0; - -static void __init in2000_setup(char *str, int *ints) -{ - int i; - char *p1, *p2; - - strlcpy(setup_buffer, str, SETUP_BUFFER_SIZE); - p1 = setup_buffer; - i = 0; - while (*p1 && (i < MAX_SETUP_ARGS)) { - p2 = strchr(p1, ','); - if (p2) { - *p2 = '\0'; - if (p1 != p2) - setup_args[i] = p1; - p1 = p2 + 1; - i++; - } else { - setup_args[i] = p1; - break; - } - } - for (i = 0; i < MAX_SETUP_ARGS; i++) - setup_used[i] = 0; - done_setup = 1; -} - - -/* check_setup_args() returns index if key found, 0 if not - */ - -static int __init check_setup_args(char *key, int *val, char *buf) -{ - int x; - char *cp; - - for (x = 0; x < MAX_SETUP_ARGS; x++) { - if (setup_used[x]) - continue; - if (!strncmp(setup_args[x], key, strlen(key))) - break; - } - if (x == MAX_SETUP_ARGS) - return 0; - setup_used[x] = 1; - cp = setup_args[x] + strlen(key); - *val = -1; - if (*cp != ':') - return ++x; - cp++; - if ((*cp >= '0') && (*cp <= '9')) { - *val = simple_strtoul(cp, NULL, 0); - } - return ++x; -} - - - -/* The "correct" (ie portable) way to access memory-mapped hardware - * such as the IN2000 EPROM and dip switch is through the use of - * special macros declared in 'asm/io.h'. We use readb() and readl() - * when reading from the card's BIOS area in in2000_detect(). - */ -static u32 bios_tab[] in2000__INITDATA = { - 0xc8000, - 0xd0000, - 0xd8000, - 0 -}; - -static unsigned short base_tab[] in2000__INITDATA = { - 0x220, - 0x200, - 0x110, - 0x100, -}; - -static int int_tab[] in2000__INITDATA = { - 15, - 14, - 11, - 10 -}; - -static int probe_bios(u32 addr, u32 *s1, uchar *switches) -{ - void __iomem *p = ioremap(addr, 0x34); - if (!p) - return 0; - *s1 = readl(p + 0x10); - if (*s1 == 0x41564f4e || readl(p + 0x30) == 0x61776c41) { - /* Read the switch image that's mapped into EPROM space */ - *switches = ~readb(p + 0x20); - iounmap(p); - return 1; - } - iounmap(p); - return 0; -} - -static int __init in2000_detect(struct scsi_host_template * tpnt) -{ - struct Scsi_Host *instance; - struct IN2000_hostdata *hostdata; - int detect_count; - int bios; - int x; - unsigned short base; - uchar switches; - uchar hrev; - unsigned long flags; - int val; - char buf[32]; - -/* Thanks to help from Bill Earnest, probing for IN2000 cards is a - * pretty straightforward and fool-proof operation. There are 3 - * possible locations for the IN2000 EPROM in memory space - if we - * find a BIOS signature, we can read the dip switch settings from - * the byte at BIOS+32 (shadowed in by logic on the card). From 2 - * of the switch bits we get the card's address in IO space. There's - * an image of the dip switch there, also, so we have a way to back- - * check that this really is an IN2000 card. Very nifty. Use the - * 'ioport:xx' command-line parameter if your BIOS EPROM is absent - * or disabled. - */ - - if (!done_setup && setup_strings) - in2000_setup(setup_strings, NULL); - - detect_count = 0; - for (bios = 0; bios_tab[bios]; bios++) { - u32 s1 = 0; - if (check_setup_args("ioport", &val, buf)) { - base = val; - switches = ~inb(base + IO_SWITCHES) & 0xff; - printk("Forcing IN2000 detection at IOport 0x%x ", base); - bios = 2; - } -/* - * There have been a couple of BIOS versions with different layouts - * for the obvious ID strings. We look for the 2 most common ones and - * hope that they cover all the cases... - */ - else if (probe_bios(bios_tab[bios], &s1, &switches)) { - printk("Found IN2000 BIOS at 0x%x ", (unsigned int) bios_tab[bios]); - -/* Find out where the IO space is */ - - x = switches & (SW_ADDR0 | SW_ADDR1); - base = base_tab[x]; - -/* Check for the IN2000 signature in IO space. */ - - x = ~inb(base + IO_SWITCHES) & 0xff; - if (x != switches) { - printk("Bad IO signature: %02x vs %02x.\n", x, switches); - continue; - } - } else - continue; - -/* OK. We have a base address for the IO ports - run a few safety checks */ - - if (!(switches & SW_BIT7)) { /* I _think_ all cards do this */ - printk("There is no IN-2000 SCSI card at IOport 0x%03x!\n", base); - continue; - } - -/* Let's assume any hardware version will work, although the driver - * has only been tested on 0x21, 0x22, 0x25, 0x26, and 0x27. We'll - * print out the rev number for reference later, but accept them all. - */ - - hrev = inb(base + IO_HARDWARE); - - /* Bit 2 tells us if interrupts are disabled */ - if (switches & SW_DISINT) { - printk("The IN-2000 SCSI card at IOport 0x%03x ", base); - printk("is not configured for interrupt operation!\n"); - printk("This driver requires an interrupt: cancelling detection.\n"); - continue; - } - -/* Ok. We accept that there's an IN2000 at ioaddr 'base'. Now - * initialize it. - */ - - tpnt->proc_name = "in2000"; - instance = scsi_register(tpnt, sizeof(struct IN2000_hostdata)); - if (instance == NULL) - continue; - detect_count++; - hostdata = (struct IN2000_hostdata *) instance->hostdata; - instance->io_port = hostdata->io_base = base; - hostdata->dip_switch = switches; - hostdata->hrev = hrev; - - write1_io(0, IO_FIFO_WRITE); /* clear fifo counter */ - write1_io(0, IO_FIFO_READ); /* start fifo out in read mode */ - write1_io(0, IO_INTR_MASK); /* allow all ints */ - x = int_tab[(switches & (SW_INT0 | SW_INT1)) >> SW_INT_SHIFT]; - if (request_irq(x, in2000_intr, 0, "in2000", instance)) { - printk("in2000_detect: Unable to allocate IRQ.\n"); - detect_count--; - continue; - } - instance->irq = x; - instance->n_io_port = 13; - request_region(base, 13, "in2000"); /* lock in this IO space for our use */ - - for (x = 0; x < 8; x++) { - hostdata->busy[x] = 0; - hostdata->sync_xfer[x] = calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF); - hostdata->sync_stat[x] = SS_UNSET; /* using default sync values */ -#ifdef PROC_STATISTICS - hostdata->cmd_cnt[x] = 0; - hostdata->disc_allowed_cnt[x] = 0; - hostdata->disc_done_cnt[x] = 0; -#endif - } - hostdata->input_Q = NULL; - hostdata->selecting = NULL; - hostdata->connected = NULL; - hostdata->disconnected_Q = NULL; - hostdata->state = S_UNCONNECTED; - hostdata->fifo = FI_FIFO_UNUSED; - hostdata->level2 = L2_BASIC; - hostdata->disconnect = DIS_ADAPTIVE; - hostdata->args = DEBUG_DEFAULTS; - hostdata->incoming_ptr = 0; - hostdata->outgoing_len = 0; - hostdata->default_sx_per = DEFAULT_SX_PER; - -/* Older BIOS's had a 'sync on/off' switch - use its setting */ - - if (s1 == 0x41564f4e && (switches & SW_SYNC_DOS5)) - hostdata->sync_off = 0x00; /* sync defaults to on */ - else - hostdata->sync_off = 0xff; /* sync defaults to off */ - -#ifdef PROC_INTERFACE - hostdata->proc = PR_VERSION | PR_INFO | PR_STATISTICS | PR_CONNECTED | PR_INPUTQ | PR_DISCQ | PR_STOP; -#ifdef PROC_STATISTICS - hostdata->int_cnt = 0; -#endif -#endif - - if (check_setup_args("nosync", &val, buf)) - hostdata->sync_off = val; - - if (check_setup_args("period", &val, buf)) - hostdata->default_sx_per = sx_table[round_period((unsigned int) val)].period_ns; - - if (check_setup_args("disconnect", &val, buf)) { - if ((val >= DIS_NEVER) && (val <= DIS_ALWAYS)) - hostdata->disconnect = val; - else - hostdata->disconnect = DIS_ADAPTIVE; - } - - if (check_setup_args("noreset", &val, buf)) - hostdata->args ^= A_NO_SCSI_RESET; - - if (check_setup_args("level2", &val, buf)) - hostdata->level2 = val; - - if (check_setup_args("debug", &val, buf)) - hostdata->args = (val & DB_MASK); - -#ifdef PROC_INTERFACE - if (check_setup_args("proc", &val, buf)) - hostdata->proc = val; -#endif - - - /* FIXME: not strictly needed I think but the called code expects - to be locked */ - spin_lock_irqsave(instance->host_lock, flags); - x = reset_hardware(instance, (hostdata->args & A_NO_SCSI_RESET) ? RESET_CARD : RESET_CARD_AND_BUS); - spin_unlock_irqrestore(instance->host_lock, flags); - - hostdata->microcode = read_3393(hostdata, WD_CDB_1); - if (x & 0x01) { - if (x & B_FLAG) - hostdata->chip = C_WD33C93B; - else - hostdata->chip = C_WD33C93A; - } else - hostdata->chip = C_WD33C93; - - printk("dip_switch=%02x irq=%d ioport=%02x floppy=%s sync/DOS5=%s ", (switches & 0x7f), instance->irq, hostdata->io_base, (switches & SW_FLOPPY) ? "Yes" : "No", (switches & SW_SYNC_DOS5) ? "Yes" : "No"); - printk("hardware_ver=%02x chip=%s microcode=%02x\n", hrev, (hostdata->chip == C_WD33C93) ? "WD33c93" : (hostdata->chip == C_WD33C93A) ? "WD33c93A" : (hostdata->chip == C_WD33C93B) ? "WD33c93B" : "unknown", hostdata->microcode); -#ifdef DEBUGGING_ON - printk("setup_args = "); - for (x = 0; x < MAX_SETUP_ARGS; x++) - printk("%s,", setup_args[x]); - printk("\n"); -#endif - if (hostdata->sync_off == 0xff) - printk("Sync-transfer DISABLED on all devices: ENABLE from command-line\n"); - printk("IN2000 driver version %s - %s\n", IN2000_VERSION, IN2000_DATE); - } - - return detect_count; -} - -static int in2000_release(struct Scsi_Host *shost) -{ - if (shost->irq) - free_irq(shost->irq, shost); - if (shost->io_port && shost->n_io_port) - release_region(shost->io_port, shost->n_io_port); - return 0; -} - -/* NOTE: I lifted this function straight out of the old driver, - * and have not tested it. Presumably it does what it's - * supposed to do... - */ - -static int in2000_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int *iinfo) -{ - int size; - - size = capacity; - iinfo[0] = 64; - iinfo[1] = 32; - iinfo[2] = size >> 11; - -/* This should approximate the large drive handling that the DOS ASPI manager - uses. Drives very near the boundaries may not be handled correctly (i.e. - near 2.0 Gb and 4.0 Gb) */ - - if (iinfo[2] > 1024) { - iinfo[0] = 64; - iinfo[1] = 63; - iinfo[2] = (unsigned long) capacity / (iinfo[0] * iinfo[1]); - } - if (iinfo[2] > 1024) { - iinfo[0] = 128; - iinfo[1] = 63; - iinfo[2] = (unsigned long) capacity / (iinfo[0] * iinfo[1]); - } - if (iinfo[2] > 1024) { - iinfo[0] = 255; - iinfo[1] = 63; - iinfo[2] = (unsigned long) capacity / (iinfo[0] * iinfo[1]); - } - return 0; -} - - -static int in2000_write_info(struct Scsi_Host *instance, char *buf, int len) -{ - -#ifdef PROC_INTERFACE - - char *bp; - struct IN2000_hostdata *hd; - int x, i; - - hd = (struct IN2000_hostdata *) instance->hostdata; - - buf[len] = '\0'; - bp = buf; - if (!strncmp(bp, "debug:", 6)) { - bp += 6; - hd->args = simple_strtoul(bp, NULL, 0) & DB_MASK; - } else if (!strncmp(bp, "disconnect:", 11)) { - bp += 11; - x = simple_strtoul(bp, NULL, 0); - if (x < DIS_NEVER || x > DIS_ALWAYS) - x = DIS_ADAPTIVE; - hd->disconnect = x; - } else if (!strncmp(bp, "period:", 7)) { - bp += 7; - x = simple_strtoul(bp, NULL, 0); - hd->default_sx_per = sx_table[round_period((unsigned int) x)].period_ns; - } else if (!strncmp(bp, "resync:", 7)) { - bp += 7; - x = simple_strtoul(bp, NULL, 0); - for (i = 0; i < 7; i++) - if (x & (1 << i)) - hd->sync_stat[i] = SS_UNSET; - } else if (!strncmp(bp, "proc:", 5)) { - bp += 5; - hd->proc = simple_strtoul(bp, NULL, 0); - } else if (!strncmp(bp, "level2:", 7)) { - bp += 7; - hd->level2 = simple_strtoul(bp, NULL, 0); - } -#endif - return len; -} - -static int in2000_show_info(struct seq_file *m, struct Scsi_Host *instance) -{ - -#ifdef PROC_INTERFACE - unsigned long flags; - struct IN2000_hostdata *hd; - Scsi_Cmnd *cmd; - int x; - - hd = (struct IN2000_hostdata *) instance->hostdata; - - spin_lock_irqsave(instance->host_lock, flags); - if (hd->proc & PR_VERSION) - seq_printf(m, "\nVersion %s - %s.", IN2000_VERSION, IN2000_DATE); - - if (hd->proc & PR_INFO) { - seq_printf(m, "\ndip_switch=%02x: irq=%d io=%02x floppy=%s sync/DOS5=%s", (hd->dip_switch & 0x7f), instance->irq, hd->io_base, (hd->dip_switch & 0x40) ? "Yes" : "No", (hd->dip_switch & 0x20) ? "Yes" : "No"); - seq_puts(m, "\nsync_xfer[] = "); - for (x = 0; x < 7; x++) - seq_printf(m, "\t%02x", hd->sync_xfer[x]); - seq_puts(m, "\nsync_stat[] = "); - for (x = 0; x < 7; x++) - seq_printf(m, "\t%02x", hd->sync_stat[x]); - } -#ifdef PROC_STATISTICS - if (hd->proc & PR_STATISTICS) { - seq_puts(m, "\ncommands issued: "); - for (x = 0; x < 7; x++) - seq_printf(m, "\t%ld", hd->cmd_cnt[x]); - seq_puts(m, "\ndisconnects allowed:"); - for (x = 0; x < 7; x++) - seq_printf(m, "\t%ld", hd->disc_allowed_cnt[x]); - seq_puts(m, "\ndisconnects done: "); - for (x = 0; x < 7; x++) - seq_printf(m, "\t%ld", hd->disc_done_cnt[x]); - seq_printf(m, "\ninterrupts: \t%ld", hd->int_cnt); - } -#endif - if (hd->proc & PR_CONNECTED) { - seq_puts(m, "\nconnected: "); - if (hd->connected) { - cmd = (Scsi_Cmnd *) hd->connected; - seq_printf(m, " %d:%llu(%02x)", cmd->device->id, cmd->device->lun, cmd->cmnd[0]); - } - } - if (hd->proc & PR_INPUTQ) { - seq_puts(m, "\ninput_Q: "); - cmd = (Scsi_Cmnd *) hd->input_Q; - while (cmd) { - seq_printf(m, " %d:%llu(%02x)", cmd->device->id, cmd->device->lun, cmd->cmnd[0]); - cmd = (Scsi_Cmnd *) cmd->host_scribble; - } - } - if (hd->proc & PR_DISCQ) { - seq_puts(m, "\ndisconnected_Q:"); - cmd = (Scsi_Cmnd *) hd->disconnected_Q; - while (cmd) { - seq_printf(m, " %d:%llu(%02x)", cmd->device->id, cmd->device->lun, cmd->cmnd[0]); - cmd = (Scsi_Cmnd *) cmd->host_scribble; - } - } - if (hd->proc & PR_TEST) { - ; /* insert your own custom function here */ - } - seq_putc(m, '\n'); - spin_unlock_irqrestore(instance->host_lock, flags); -#endif /* PROC_INTERFACE */ - return 0; -} - -MODULE_LICENSE("GPL"); - - -static struct scsi_host_template driver_template = { - .proc_name = "in2000", - .write_info = in2000_write_info, - .show_info = in2000_show_info, - .name = "Always IN2000", - .detect = in2000_detect, - .release = in2000_release, - .queuecommand = in2000_queuecommand, - .eh_abort_handler = in2000_abort, - .eh_bus_reset_handler = in2000_bus_reset, - .bios_param = in2000_biosparam, - .can_queue = IN2000_CAN_Q, - .this_id = IN2000_HOST_ID, - .sg_tablesize = IN2000_SG, - .cmd_per_lun = IN2000_CPL, - .use_clustering = DISABLE_CLUSTERING, -}; -#include "scsi_module.c" diff --git a/drivers/scsi/in2000.h b/drivers/scsi/in2000.h deleted file mode 100644 index 5821e1fbce08..000000000000 --- a/drivers/scsi/in2000.h +++ /dev/null @@ -1,412 +0,0 @@ -/* - * in2000.h - Linux device driver definitions for the - * Always IN2000 ISA SCSI card. - * - * IMPORTANT: This file is for version 1.33 - 26/Aug/1998 - * - * Copyright (c) 1996 John Shifflett, GeoLog Consulting - * john@geolog.com - * jshiffle@netcom.com - * - * 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, or (at your option) - * any later version. - * - * 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. - * - */ - -#ifndef IN2000_H -#define IN2000_H - -#include <asm/io.h> - -#define PROC_INTERFACE /* add code for /proc/scsi/in2000/xxx interface */ -#ifdef PROC_INTERFACE -#define PROC_STATISTICS /* add code for keeping various real time stats */ -#endif - -#define SYNC_DEBUG /* extra info on sync negotiation printed */ -#define DEBUGGING_ON /* enable command-line debugging bitmask */ -#define DEBUG_DEFAULTS 0 /* default bitmask - change from command-line */ - -#ifdef __i386__ -#define FAST_READ_IO /* No problems with these on my machine */ -#define FAST_WRITE_IO -#endif - -#ifdef DEBUGGING_ON -#define DB(f,a) if (hostdata->args & (f)) a; -#define CHECK_NULL(p,s) /* if (!(p)) {printk("\n"); while (1) printk("NP:%s\r",(s));} */ -#else -#define DB(f,a) -#define CHECK_NULL(p,s) -#endif - -#define uchar unsigned char - -#define read1_io(a) (inb(hostdata->io_base+(a))) -#define read2_io(a) (inw(hostdata->io_base+(a))) -#define write1_io(b,a) (outb((b),hostdata->io_base+(a))) -#define write2_io(w,a) (outw((w),hostdata->io_base+(a))) - -#ifdef __i386__ -/* These inline assembly defines are derived from a patch - * sent to me by Bill Earnest. He's done a lot of very - * valuable thinking, testing, and coding during his effort - * to squeeze more speed out of this driver. I really think - * that we are doing IO at close to the maximum now with - * the fifo. (And yes, insw uses 'edi' while outsw uses - * 'esi'. Thanks Bill!) - */ - -#define FAST_READ2_IO() \ -({ \ -int __dummy_1,__dummy_2; \ - __asm__ __volatile__ ("\n \ - cld \n \ - orl %%ecx, %%ecx \n \ - jz 1f \n \ - rep \n \ - insw (%%dx),%%es:(%%edi) \n \ -1: " \ - : "=D" (sp) ,"=c" (__dummy_1) ,"=d" (__dummy_2) /* output */ \ - : "2" (f), "0" (sp), "1" (i) /* input */ \ - ); /* trashed */ \ -}) - -#define FAST_WRITE2_IO() \ -({ \ -int __dummy_1,__dummy_2; \ - __asm__ __volatile__ ("\n \ - cld \n \ - orl %%ecx, %%ecx \n \ - jz 1f \n \ - rep \n \ - outsw %%ds:(%%esi),(%%dx) \n \ -1: " \ - : "=S" (sp) ,"=c" (__dummy_1) ,"=d" (__dummy_2)/* output */ \ - : "2" (f), "0" (sp), "1" (i) /* input */ \ - ); /* trashed */ \ -}) -#endif - -/* IN2000 io_port offsets */ -#define IO_WD_ASR 0x00 /* R - 3393 auxstat reg */ -#define ASR_INT 0x80 -#define ASR_LCI 0x40 -#define ASR_BSY 0x20 -#define ASR_CIP 0x10 -#define ASR_PE 0x02 -#define ASR_DBR 0x01 -#define IO_WD_ADDR 0x00 /* W - 3393 address reg */ -#define IO_WD_DATA 0x01 /* R/W - rest of 3393 regs */ -#define IO_FIFO 0x02 /* R/W - in2000 dual-port fifo (16 bits) */ -#define IN2000_FIFO_SIZE 2048 /* fifo capacity in bytes */ -#define IO_CARD_RESET 0x03 /* W - in2000 start master reset */ -#define IO_FIFO_COUNT 0x04 /* R - in2000 fifo counter */ -#define IO_FIFO_WRITE 0x05 /* W - clear fifo counter, start write */ -#define IO_FIFO_READ 0x07 /* W - start fifo read */ -#define IO_LED_OFF 0x08 /* W - turn off in2000 activity LED */ -#define IO_SWITCHES 0x08 /* R - read in2000 dip switch */ -#define SW_ADDR0 0x01 /* bit 0 = bit 0 of index to io addr */ -#define SW_ADDR1 0x02 /* bit 1 = bit 1 of index io addr */ -#define SW_DISINT 0x04 /* bit 2 true if ints disabled */ -#define SW_INT0 0x08 /* bit 3 = bit 0 of index to interrupt */ -#define SW_INT1 0x10 /* bit 4 = bit 1 of index to interrupt */ -#define SW_INT_SHIFT 3 /* shift right this amount to right justify int bits */ -#define SW_SYNC_DOS5 0x20 /* bit 5 used by Always BIOS */ -#define SW_FLOPPY 0x40 /* bit 6 true if floppy enabled */ -#define SW_BIT7 0x80 /* bit 7 hardwired true (ground) */ -#define IO_LED_ON 0x09 /* W - turn on in2000 activity LED */ -#define IO_HARDWARE 0x0a /* R - read in2000 hardware rev, stop reset */ -#define IO_INTR_MASK 0x0c /* W - in2000 interrupt mask reg */ -#define IMASK_WD 0x01 /* WD33c93 interrupt mask */ -#define IMASK_FIFO 0x02 /* FIFO interrupt mask */ - -/* wd register names */ -#define WD_OWN_ID 0x00 -#define WD_CONTROL 0x01 -#define WD_TIMEOUT_PERIOD 0x02 -#define WD_CDB_1 0x03 -#define WD_CDB_2 0x04 -#define WD_CDB_3 0x05 -#define WD_CDB_4 0x06 -#define WD_CDB_5 0x07 -#define WD_CDB_6 0x08 -#define WD_CDB_7 0x09 -#define WD_CDB_8 0x0a -#define WD_CDB_9 0x0b -#define WD_CDB_10 0x0c -#define WD_CDB_11 0x0d -#define WD_CDB_12 0x0e -#define WD_TARGET_LUN 0x0f -#define WD_COMMAND_PHASE 0x10 -#define WD_SYNCHRONOUS_TRANSFER 0x11 -#define WD_TRANSFER_COUNT_MSB 0x12 -#define WD_TRANSFER_COUNT 0x13 -#define WD_TRANSFER_COUNT_LSB 0x14 -#define WD_DESTINATION_ID 0x15 -#define WD_SOURCE_ID 0x16 -#define WD_SCSI_STATUS 0x17 -#define WD_COMMAND 0x18 -#define WD_DATA 0x19 -#define WD_QUEUE_TAG 0x1a -#define WD_AUXILIARY_STATUS 0x1f - -/* WD commands */ -#define WD_CMD_RESET 0x00 -#define WD_CMD_ABORT 0x01 -#define WD_CMD_ASSERT_ATN 0x02 -#define WD_CMD_NEGATE_ACK 0x03 -#define WD_CMD_DISCONNECT 0x04 -#define WD_CMD_RESELECT 0x05 -#define WD_CMD_SEL_ATN 0x06 -#define WD_CMD_SEL 0x07 -#define WD_CMD_SEL_ATN_XFER 0x08 -#define WD_CMD_SEL_XFER 0x09 -#define WD_CMD_RESEL_RECEIVE 0x0a -#define WD_CMD_RESEL_SEND 0x0b -#define WD_CMD_WAIT_SEL_RECEIVE 0x0c -#define WD_CMD_TRANS_ADDR 0x18 -#define WD_CMD_TRANS_INFO 0x20 -#define WD_CMD_TRANSFER_PAD 0x21 -#define WD_CMD_SBT_MODE 0x80 - -/* SCSI Bus Phases */ -#define PHS_DATA_OUT 0x00 -#define PHS_DATA_IN 0x01 -#define PHS_COMMAND 0x02 -#define PHS_STATUS 0x03 -#define PHS_MESS_OUT 0x06 -#define PHS_MESS_IN 0x07 - -/* Command Status Register definitions */ - - /* reset state interrupts */ -#define CSR_RESET 0x00 -#define CSR_RESET_AF 0x01 - - /* successful completion interrupts */ -#define CSR_RESELECT 0x10 -#define CSR_SELECT 0x11 -#define CSR_SEL_XFER_DONE 0x16 -#define CSR_XFER_DONE 0x18 - - /* paused or aborted interrupts */ -#define CSR_MSGIN 0x20 -#define CSR_SDP 0x21 -#define CSR_SEL_ABORT 0x22 -#define CSR_RESEL_ABORT 0x25 -#define CSR_RESEL_ABORT_AM 0x27 -#define CSR_ABORT 0x28 - - /* terminated interrupts */ -#define CSR_INVALID 0x40 -#define CSR_UNEXP_DISC 0x41 -#define CSR_TIMEOUT 0x42 -#define CSR_PARITY 0x43 -#define CSR_PARITY_ATN 0x44 -#define CSR_BAD_STATUS 0x45 -#define CSR_UNEXP 0x48 - - /* service required interrupts */ -#define CSR_RESEL 0x80 -#define CSR_RESEL_AM 0x81 -#define CSR_DISC 0x85 -#define CSR_SRV_REQ 0x88 - - /* Own ID/CDB Size register */ -#define OWNID_EAF 0x08 -#define OWNID_EHP 0x10 -#define OWNID_RAF 0x20 -#define OWNID_FS_8 0x00 -#define OWNID_FS_12 0x40 -#define OWNID_FS_16 0x80 - - /* Control register */ -#define CTRL_HSP 0x01 -#define CTRL_HA 0x02 -#define CTRL_IDI 0x04 -#define CTRL_EDI 0x08 -#define CTRL_HHP 0x10 -#define CTRL_POLLED 0x00 -#define CTRL_BURST 0x20 -#define CTRL_BUS 0x40 -#define CTRL_DMA 0x80 - - /* Timeout Period register */ -#define TIMEOUT_PERIOD_VALUE 20 /* results in 200 ms. */ - - /* Synchronous Transfer Register */ -#define STR_FSS 0x80 - - /* Destination ID register */ -#define DSTID_DPD 0x40 -#define DATA_OUT_DIR 0 -#define DATA_IN_DIR 1 -#define DSTID_SCC 0x80 - - /* Source ID register */ -#define SRCID_MASK 0x07 -#define SRCID_SIV 0x08 -#define SRCID_DSP 0x20 -#define SRCID_ES 0x40 -#define SRCID_ER 0x80 - - - -#define ILLEGAL_STATUS_BYTE 0xff - - -#define DEFAULT_SX_PER 500 /* (ns) fairly safe */ -#define DEFAULT_SX_OFF 0 /* aka async */ - -#define OPTIMUM_SX_PER 252 /* (ns) best we can do (mult-of-4) */ -#define OPTIMUM_SX_OFF 12 /* size of in2000 fifo */ - -struct sx_period { - unsigned int period_ns; - uchar reg_value; - }; - - -struct IN2000_hostdata { - struct Scsi_Host *next; - uchar chip; /* what kind of wd33c93 chip? */ - uchar microcode; /* microcode rev if 'B' */ - unsigned short io_base; /* IO port base */ - unsigned int dip_switch; /* dip switch settings */ - unsigned int hrev; /* hardware revision of card */ - volatile uchar busy[8]; /* index = target, bit = lun */ - volatile Scsi_Cmnd *input_Q; /* commands waiting to be started */ - volatile Scsi_Cmnd *selecting; /* trying to select this command */ - volatile Scsi_Cmnd *connected; /* currently connected command */ - volatile Scsi_Cmnd *disconnected_Q;/* commands waiting for reconnect */ - uchar state; /* what we are currently doing */ - uchar fifo; /* what the FIFO is up to */ - uchar level2; /* extent to which Level-2 commands are used */ - uchar disconnect; /* disconnect/reselect policy */ - unsigned int args; /* set from command-line argument */ - uchar incoming_msg[8]; /* filled during message_in phase */ - int incoming_ptr; /* mainly used with EXTENDED messages */ - uchar outgoing_msg[8]; /* send this during next message_out */ - int outgoing_len; /* length of outgoing message */ - unsigned int default_sx_per; /* default transfer period for SCSI bus */ - uchar sync_xfer[8]; /* sync_xfer reg settings per target */ - uchar sync_stat[8]; /* status of sync negotiation per target */ - uchar sync_off; /* bit mask: don't use sync with these targets */ -#ifdef PROC_INTERFACE - uchar proc; /* bit mask: what's in proc output */ -#ifdef PROC_STATISTICS - unsigned long cmd_cnt[8]; /* # of commands issued per target */ - unsigned long int_cnt; /* # of interrupts serviced */ - unsigned long disc_allowed_cnt[8]; /* # of disconnects allowed per target */ - unsigned long disc_done_cnt[8]; /* # of disconnects done per target*/ -#endif -#endif - }; - - -/* defines for hostdata->chip */ - -#define C_WD33C93 0 -#define C_WD33C93A 1 -#define C_WD33C93B 2 -#define C_UNKNOWN_CHIP 100 - -/* defines for hostdata->state */ - -#define S_UNCONNECTED 0 -#define S_SELECTING 1 -#define S_RUNNING_LEVEL2 2 -#define S_CONNECTED 3 -#define S_PRE_TMP_DISC 4 -#define S_PRE_CMP_DISC 5 - -/* defines for hostdata->fifo */ - -#define FI_FIFO_UNUSED 0 -#define FI_FIFO_READING 1 -#define FI_FIFO_WRITING 2 - -/* defines for hostdata->level2 */ -/* NOTE: only the first 3 are trustworthy at this point - - * having trouble when more than 1 device is reading/writing - * at the same time... - */ - -#define L2_NONE 0 /* no combination commands - we get lots of ints */ -#define L2_SELECT 1 /* start with SEL_ATN_XFER, but never resume it */ -#define L2_BASIC 2 /* resume after STATUS ints & RDP messages */ -#define L2_DATA 3 /* resume after DATA_IN/OUT ints */ -#define L2_MOST 4 /* resume after anything except a RESELECT int */ -#define L2_RESELECT 5 /* resume after everything, including RESELECT ints */ -#define L2_ALL 6 /* always resume */ - -/* defines for hostdata->disconnect */ - -#define DIS_NEVER 0 -#define DIS_ADAPTIVE 1 -#define DIS_ALWAYS 2 - -/* defines for hostdata->args */ - -#define DB_TEST 1<<0 -#define DB_FIFO 1<<1 -#define DB_QUEUE_COMMAND 1<<2 -#define DB_EXECUTE 1<<3 -#define DB_INTR 1<<4 -#define DB_TRANSFER 1<<5 -#define DB_MASK 0x3f - -#define A_NO_SCSI_RESET 1<<15 - - -/* defines for hostdata->sync_xfer[] */ - -#define SS_UNSET 0 -#define SS_FIRST 1 -#define SS_WAITING 2 -#define SS_SET 3 - -/* defines for hostdata->proc */ - -#define PR_VERSION 1<<0 -#define PR_INFO 1<<1 -#define PR_STATISTICS 1<<2 -#define PR_CONNECTED 1<<3 -#define PR_INPUTQ 1<<4 -#define PR_DISCQ 1<<5 -#define PR_TEST 1<<6 -#define PR_STOP 1<<7 - - -# include <linux/init.h> -# include <linux/spinlock.h> -# define in2000__INITFUNC(function) __initfunc(function) -# define in2000__INIT __init -# define in2000__INITDATA __initdata -# define CLISPIN_LOCK(host,flags) spin_lock_irqsave(host->host_lock, flags) -# define CLISPIN_UNLOCK(host,flags) spin_unlock_irqrestore(host->host_lock, \ - flags) - -static int in2000_detect(struct scsi_host_template *) in2000__INIT; -static int in2000_queuecommand(struct Scsi_Host *, struct scsi_cmnd *); -static int in2000_abort(Scsi_Cmnd *); -static void in2000_setup(char *, int *) in2000__INIT; -static int in2000_biosparam(struct scsi_device *, struct block_device *, - sector_t, int *); -static int in2000_bus_reset(Scsi_Cmnd *); - - -#define IN2000_CAN_Q 16 -#define IN2000_SG SG_ALL -#define IN2000_CPL 2 -#define IN2000_HOST_ID 7 - -#endif /* IN2000_H */ diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 17d04c702e1b..a8762a3efeef 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -493,15 +493,15 @@ struct ipr_error_table_t ipr_error_table[] = { "9072: Link not operational transition"}, {0x066B8200, 0, IPR_DEFAULT_LOG_LEVEL, "9032: Array exposed but still protected"}, - {0x066B8300, 0, IPR_DEFAULT_LOG_LEVEL + 1, + {0x066B8300, 0, IPR_DEBUG_LOG_LEVEL, "70DD: Device forced failed by disrupt device command"}, {0x066B9100, 0, IPR_DEFAULT_LOG_LEVEL, "4061: Multipath redundancy level got better"}, {0x066B9200, 0, IPR_DEFAULT_LOG_LEVEL, "4060: Multipath redundancy level got worse"}, - {0x06808100, 0, IPR_DEFAULT_LOG_LEVEL, + {0x06808100, 0, IPR_DEBUG_LOG_LEVEL, "9083: Device raw mode enabled"}, - {0x06808200, 0, IPR_DEFAULT_LOG_LEVEL, + {0x06808200, 0, IPR_DEBUG_LOG_LEVEL, "9084: Device raw mode disabled"}, {0x07270000, 0, 0, "Failure due to other device"}, @@ -1473,7 +1473,7 @@ static void ipr_process_ccn(struct ipr_cmnd *ipr_cmd) struct ipr_hostrcb *hostrcb = ipr_cmd->u.hostrcb; u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc); - list_del(&hostrcb->queue); + list_del_init(&hostrcb->queue); list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q); if (ioasc) { @@ -2552,6 +2552,23 @@ static void ipr_handle_log_data(struct ipr_ioa_cfg *ioa_cfg, } } +static struct ipr_hostrcb *ipr_get_free_hostrcb(struct ipr_ioa_cfg *ioa) +{ + struct ipr_hostrcb *hostrcb; + + hostrcb = list_first_entry_or_null(&ioa->hostrcb_free_q, + struct ipr_hostrcb, queue); + + if (unlikely(!hostrcb)) { + dev_info(&ioa->pdev->dev, "Reclaiming async error buffers."); + hostrcb = list_first_entry_or_null(&ioa->hostrcb_report_q, + struct ipr_hostrcb, queue); + } + + list_del_init(&hostrcb->queue); + return hostrcb; +} + /** * ipr_process_error - Op done function for an adapter error log. * @ipr_cmd: ipr command struct @@ -2569,13 +2586,14 @@ static void ipr_process_error(struct ipr_cmnd *ipr_cmd) struct ipr_hostrcb *hostrcb = ipr_cmd->u.hostrcb; u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc); u32 fd_ioasc; + char *envp[] = { "ASYNC_ERR_LOG=1", NULL }; if (ioa_cfg->sis64) fd_ioasc = be32_to_cpu(hostrcb->hcam.u.error64.fd_ioasc); else fd_ioasc = be32_to_cpu(hostrcb->hcam.u.error.fd_ioasc); - list_del(&hostrcb->queue); + list_del_init(&hostrcb->queue); list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q); if (!ioasc) { @@ -2588,6 +2606,10 @@ static void ipr_process_error(struct ipr_cmnd *ipr_cmd) "Host RCB failed with IOASC: 0x%08X\n", ioasc); } + list_add_tail(&hostrcb->queue, &ioa_cfg->hostrcb_report_q); + hostrcb = ipr_get_free_hostrcb(ioa_cfg); + kobject_uevent_env(&ioa_cfg->host->shost_dev.kobj, KOBJ_CHANGE, envp); + ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_LOG_DATA, hostrcb); } @@ -4095,6 +4117,64 @@ static struct device_attribute ipr_ioa_fw_type_attr = { .show = ipr_show_fw_type }; +static ssize_t ipr_read_async_err_log(struct file *filep, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct device *cdev = container_of(kobj, struct device, kobj); + struct Scsi_Host *shost = class_to_shost(cdev); + struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata; + struct ipr_hostrcb *hostrcb; + unsigned long lock_flags = 0; + int ret; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + hostrcb = list_first_entry_or_null(&ioa_cfg->hostrcb_report_q, + struct ipr_hostrcb, queue); + if (!hostrcb) { + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return 0; + } + ret = memory_read_from_buffer(buf, count, &off, &hostrcb->hcam, + sizeof(hostrcb->hcam)); + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return ret; +} + +static ssize_t ipr_next_async_err_log(struct file *filep, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct device *cdev = container_of(kobj, struct device, kobj); + struct Scsi_Host *shost = class_to_shost(cdev); + struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)shost->hostdata; + struct ipr_hostrcb *hostrcb; + unsigned long lock_flags = 0; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + hostrcb = list_first_entry_or_null(&ioa_cfg->hostrcb_report_q, + struct ipr_hostrcb, queue); + if (!hostrcb) { + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return count; + } + + /* Reclaim hostrcb before exit */ + list_move_tail(&hostrcb->queue, &ioa_cfg->hostrcb_free_q); + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return count; +} + +static struct bin_attribute ipr_ioa_async_err_log = { + .attr = { + .name = "async_err_log", + .mode = S_IRUGO | S_IWUSR, + }, + .size = 0, + .read = ipr_read_async_err_log, + .write = ipr_next_async_err_log +}; + static struct device_attribute *ipr_ioa_attrs[] = { &ipr_fw_version_attr, &ipr_log_level_attr, @@ -7026,8 +7106,7 @@ static int ipr_ioa_reset_done(struct ipr_cmnd *ipr_cmd) { struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; struct ipr_resource_entry *res; - struct ipr_hostrcb *hostrcb, *temp; - int i = 0, j; + int j; ENTER; ioa_cfg->in_reset_reload = 0; @@ -7048,12 +7127,16 @@ static int ipr_ioa_reset_done(struct ipr_cmnd *ipr_cmd) } schedule_work(&ioa_cfg->work_q); - list_for_each_entry_safe(hostrcb, temp, &ioa_cfg->hostrcb_free_q, queue) { - list_del(&hostrcb->queue); - if (i++ < IPR_NUM_LOG_HCAMS) - ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_LOG_DATA, hostrcb); + for (j = 0; j < IPR_NUM_HCAMS; j++) { + list_del_init(&ioa_cfg->hostrcb[j]->queue); + if (j < IPR_NUM_LOG_HCAMS) + ipr_send_hcam(ioa_cfg, + IPR_HCAM_CDB_OP_CODE_LOG_DATA, + ioa_cfg->hostrcb[j]); else - ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE, hostrcb); + ipr_send_hcam(ioa_cfg, + IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE, + ioa_cfg->hostrcb[j]); } scsi_report_bus_reset(ioa_cfg->host, IPR_VSET_BUS); @@ -7966,7 +8049,8 @@ static int ipr_ioafp_identify_hrrq(struct ipr_cmnd *ipr_cmd) ENTER; ipr_cmd->job_step = ipr_ioafp_std_inquiry; - dev_info(&ioa_cfg->pdev->dev, "Starting IOA initialization sequence.\n"); + if (ioa_cfg->identify_hrrq_index == 0) + dev_info(&ioa_cfg->pdev->dev, "Starting IOA initialization sequence.\n"); if (ioa_cfg->identify_hrrq_index < ioa_cfg->hrrq_num) { hrrq = &ioa_cfg->hrrq[ioa_cfg->identify_hrrq_index]; @@ -8335,7 +8419,7 @@ static void ipr_get_unit_check_buffer(struct ipr_ioa_cfg *ioa_cfg) hostrcb = list_entry(ioa_cfg->hostrcb_free_q.next, struct ipr_hostrcb, queue); - list_del(&hostrcb->queue); + list_del_init(&hostrcb->queue); memset(&hostrcb->hcam, 0, sizeof(hostrcb->hcam)); rc = ipr_get_ldump_data_section(ioa_cfg, @@ -9332,7 +9416,7 @@ static void ipr_free_mem(struct ipr_ioa_cfg *ioa_cfg) dma_free_coherent(&ioa_cfg->pdev->dev, ioa_cfg->cfg_table_size, ioa_cfg->u.cfg_table, ioa_cfg->cfg_table_dma); - for (i = 0; i < IPR_NUM_HCAMS; i++) { + for (i = 0; i < IPR_MAX_HCAMS; i++) { dma_free_coherent(&ioa_cfg->pdev->dev, sizeof(struct ipr_hostrcb), ioa_cfg->hostrcb[i], @@ -9572,7 +9656,7 @@ static int ipr_alloc_mem(struct ipr_ioa_cfg *ioa_cfg) if (!ioa_cfg->u.cfg_table) goto out_free_host_rrq; - for (i = 0; i < IPR_NUM_HCAMS; i++) { + for (i = 0; i < IPR_MAX_HCAMS; i++) { ioa_cfg->hostrcb[i] = dma_alloc_coherent(&pdev->dev, sizeof(struct ipr_hostrcb), &ioa_cfg->hostrcb_dma[i], @@ -9714,6 +9798,7 @@ static void ipr_init_ioa_cfg(struct ipr_ioa_cfg *ioa_cfg, INIT_LIST_HEAD(&ioa_cfg->hostrcb_free_q); INIT_LIST_HEAD(&ioa_cfg->hostrcb_pending_q); + INIT_LIST_HEAD(&ioa_cfg->hostrcb_report_q); INIT_LIST_HEAD(&ioa_cfg->free_res_q); INIT_LIST_HEAD(&ioa_cfg->used_res_q); INIT_WORK(&ioa_cfg->work_q, ipr_worker_thread); @@ -10352,6 +10437,8 @@ static void ipr_remove(struct pci_dev *pdev) &ipr_trace_attr); ipr_remove_dump_file(&ioa_cfg->host->shost_dev.kobj, &ipr_dump_attr); + sysfs_remove_bin_file(&ioa_cfg->host->shost_dev.kobj, + &ipr_ioa_async_err_log); scsi_remove_host(ioa_cfg->host); __ipr_remove(pdev); @@ -10400,10 +10487,25 @@ static int ipr_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id) return rc; } + rc = sysfs_create_bin_file(&ioa_cfg->host->shost_dev.kobj, + &ipr_ioa_async_err_log); + + if (rc) { + ipr_remove_dump_file(&ioa_cfg->host->shost_dev.kobj, + &ipr_dump_attr); + ipr_remove_trace_file(&ioa_cfg->host->shost_dev.kobj, + &ipr_trace_attr); + scsi_remove_host(ioa_cfg->host); + __ipr_remove(pdev); + return rc; + } + rc = ipr_create_dump_file(&ioa_cfg->host->shost_dev.kobj, &ipr_dump_attr); if (rc) { + sysfs_remove_bin_file(&ioa_cfg->host->shost_dev.kobj, + &ipr_ioa_async_err_log); ipr_remove_trace_file(&ioa_cfg->host->shost_dev.kobj, &ipr_trace_attr); scsi_remove_host(ioa_cfg->host); diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h index cdb51960b53c..8995053d01b3 100644 --- a/drivers/scsi/ipr.h +++ b/drivers/scsi/ipr.h @@ -154,7 +154,9 @@ #define IPR_DEFAULT_MAX_ERROR_DUMP 984 #define IPR_NUM_LOG_HCAMS 2 #define IPR_NUM_CFG_CHG_HCAMS 2 +#define IPR_NUM_HCAM_QUEUE 12 #define IPR_NUM_HCAMS (IPR_NUM_LOG_HCAMS + IPR_NUM_CFG_CHG_HCAMS) +#define IPR_MAX_HCAMS (IPR_NUM_HCAMS + IPR_NUM_HCAM_QUEUE) #define IPR_MAX_SIS64_TARGETS_PER_BUS 1024 #define IPR_MAX_SIS64_LUNS_PER_TARGET 0xffffffff @@ -1504,6 +1506,7 @@ struct ipr_ioa_cfg { u8 log_level; #define IPR_MAX_LOG_LEVEL 4 #define IPR_DEFAULT_LOG_LEVEL 2 +#define IPR_DEBUG_LOG_LEVEL 3 #define IPR_NUM_TRACE_INDEX_BITS 8 #define IPR_NUM_TRACE_ENTRIES (1 << IPR_NUM_TRACE_INDEX_BITS) @@ -1532,10 +1535,11 @@ struct ipr_ioa_cfg { char ipr_hcam_label[8]; #define IPR_HCAM_LABEL "hcams" - struct ipr_hostrcb *hostrcb[IPR_NUM_HCAMS]; - dma_addr_t hostrcb_dma[IPR_NUM_HCAMS]; + struct ipr_hostrcb *hostrcb[IPR_MAX_HCAMS]; + dma_addr_t hostrcb_dma[IPR_MAX_HCAMS]; struct list_head hostrcb_free_q; struct list_head hostrcb_pending_q; + struct list_head hostrcb_report_q; struct ipr_hrr_queue hrrq[IPR_MAX_HRRQ_NUM]; u32 hrrq_num; diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index e72673b0a8fb..16ca31ad5ec0 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c @@ -1837,7 +1837,6 @@ static void fc_exch_reset(struct fc_exch *ep) int rc = 1; spin_lock_bh(&ep->ex_lock); - fc_exch_abort_locked(ep, 0); ep->state |= FC_EX_RST_CLEANUP; fc_exch_timer_cancel(ep); if (ep->esb_stat & ESB_ST_REC_QUAL) diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c index 93f596182145..97aeaddd600d 100644 --- a/drivers/scsi/libfc/fc_rport.c +++ b/drivers/scsi/libfc/fc_rport.c @@ -457,6 +457,9 @@ static void fc_rport_enter_delete(struct fc_rport_priv *rdata, */ static int fc_rport_logoff(struct fc_rport_priv *rdata) { + struct fc_lport *lport = rdata->local_port; + u32 port_id = rdata->ids.port_id; + mutex_lock(&rdata->rp_mutex); FC_RPORT_DBG(rdata, "Remove port\n"); @@ -466,6 +469,15 @@ static int fc_rport_logoff(struct fc_rport_priv *rdata) FC_RPORT_DBG(rdata, "Port in Delete state, not removing\n"); goto out; } + /* + * FC-LS states: + * To explicitly Logout, the initiating Nx_Port shall terminate + * other open Sequences that it initiated with the destination + * Nx_Port prior to performing Logout. + */ + lport->tt.exch_mgr_reset(lport, 0, port_id); + lport->tt.exch_mgr_reset(lport, port_id, 0); + fc_rport_enter_logo(rdata); /* @@ -547,16 +559,24 @@ static void fc_rport_timeout(struct work_struct *work) */ static void fc_rport_error(struct fc_rport_priv *rdata, struct fc_frame *fp) { + struct fc_lport *lport = rdata->local_port; + FC_RPORT_DBG(rdata, "Error %ld in state %s, retries %d\n", IS_ERR(fp) ? -PTR_ERR(fp) : 0, fc_rport_state(rdata), rdata->retries); switch (rdata->rp_state) { case RPORT_ST_FLOGI: - case RPORT_ST_PLOGI: rdata->flags &= ~FC_RP_STARTED; fc_rport_enter_delete(rdata, RPORT_EV_FAILED); break; + case RPORT_ST_PLOGI: + if (lport->point_to_multipoint) { + rdata->flags &= ~FC_RP_STARTED; + fc_rport_enter_delete(rdata, RPORT_EV_FAILED); + } else + fc_rport_enter_logo(rdata); + break; case RPORT_ST_RTV: fc_rport_enter_ready(rdata); break; @@ -1877,7 +1897,7 @@ static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata, spp->spp_type_ext = rspp->spp_type_ext; spp->spp_flags = FC_SPP_RESP_ACK; - fc_rport_enter_delete(rdata, RPORT_EV_LOGO); + fc_rport_enter_prli(rdata); fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0); lport->tt.frame_send(lport, fp); @@ -1915,7 +1935,7 @@ static void fc_rport_recv_logo_req(struct fc_lport *lport, struct fc_frame *fp) FC_RPORT_DBG(rdata, "Received LOGO request while in state %s\n", fc_rport_state(rdata)); - fc_rport_enter_delete(rdata, RPORT_EV_LOGO); + fc_rport_enter_delete(rdata, RPORT_EV_STOP); mutex_unlock(&rdata->rp_mutex); kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy); } else diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c index 63e48d4277b0..4ac03b16d17f 100644 --- a/drivers/scsi/lpfc/lpfc_ct.c +++ b/drivers/scsi/lpfc/lpfc_ct.c @@ -1535,7 +1535,7 @@ lpfc_fdmi_num_disc_check(struct lpfc_vport *vport) } /* Routines for all individual HBA attributes */ -int +static int lpfc_fdmi_hba_attr_wwnn(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { struct lpfc_fdmi_attr_entry *ae; @@ -1551,7 +1551,7 @@ lpfc_fdmi_hba_attr_wwnn(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) ad->AttrType = cpu_to_be16(RHBA_NODENAME); return size; } -int +static int lpfc_fdmi_hba_attr_manufacturer(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1573,7 +1573,7 @@ lpfc_fdmi_hba_attr_manufacturer(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_sn(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { struct lpfc_hba *phba = vport->phba; @@ -1594,7 +1594,7 @@ lpfc_fdmi_hba_attr_sn(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) return size; } -int +static int lpfc_fdmi_hba_attr_model(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1615,7 +1615,7 @@ lpfc_fdmi_hba_attr_model(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_description(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1637,7 +1637,7 @@ lpfc_fdmi_hba_attr_description(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_hdw_ver(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1669,7 +1669,7 @@ lpfc_fdmi_hba_attr_hdw_ver(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_drvr_ver(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1690,7 +1690,7 @@ lpfc_fdmi_hba_attr_drvr_ver(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_rom_ver(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1715,7 +1715,7 @@ lpfc_fdmi_hba_attr_rom_ver(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_fmw_ver(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1736,7 +1736,7 @@ lpfc_fdmi_hba_attr_fmw_ver(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_os_ver(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1759,7 +1759,7 @@ lpfc_fdmi_hba_attr_os_ver(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_ct_len(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1775,7 +1775,7 @@ lpfc_fdmi_hba_attr_ct_len(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_symbolic_name(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1794,7 +1794,7 @@ lpfc_fdmi_hba_attr_symbolic_name(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_vendor_info(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1811,7 +1811,7 @@ lpfc_fdmi_hba_attr_vendor_info(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_num_ports(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1828,7 +1828,7 @@ lpfc_fdmi_hba_attr_num_ports(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_fabric_wwnn(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1846,7 +1846,7 @@ lpfc_fdmi_hba_attr_fabric_wwnn(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_bios_ver(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1867,7 +1867,7 @@ lpfc_fdmi_hba_attr_bios_ver(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_bios_state(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1884,7 +1884,7 @@ lpfc_fdmi_hba_attr_bios_state(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_hba_attr_vendor_id(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1906,7 +1906,7 @@ lpfc_fdmi_hba_attr_vendor_id(struct lpfc_vport *vport, } /* Routines for all individual PORT attributes */ -int +static int lpfc_fdmi_port_attr_fc4type(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1925,7 +1925,7 @@ lpfc_fdmi_port_attr_fc4type(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_support_speed(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -1975,7 +1975,7 @@ lpfc_fdmi_port_attr_support_speed(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_speed(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2039,7 +2039,7 @@ lpfc_fdmi_port_attr_speed(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_max_frame(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2059,7 +2059,7 @@ lpfc_fdmi_port_attr_max_frame(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_os_devname(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2081,7 +2081,7 @@ lpfc_fdmi_port_attr_os_devname(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_host_name(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2102,7 +2102,7 @@ lpfc_fdmi_port_attr_host_name(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_wwnn(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2120,7 +2120,7 @@ lpfc_fdmi_port_attr_wwnn(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_wwpn(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2138,7 +2138,7 @@ lpfc_fdmi_port_attr_wwpn(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_symbolic_name(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2156,7 +2156,7 @@ lpfc_fdmi_port_attr_symbolic_name(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_port_type(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2175,7 +2175,7 @@ lpfc_fdmi_port_attr_port_type(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_class(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2190,7 +2190,7 @@ lpfc_fdmi_port_attr_class(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_fabric_wwpn(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2208,7 +2208,7 @@ lpfc_fdmi_port_attr_fabric_wwpn(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_active_fc4type(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2227,7 +2227,7 @@ lpfc_fdmi_port_attr_active_fc4type(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_port_state(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2243,7 +2243,7 @@ lpfc_fdmi_port_attr_port_state(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_num_disc(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2259,7 +2259,7 @@ lpfc_fdmi_port_attr_num_disc(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_port_attr_nportid(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2274,7 +2274,7 @@ lpfc_fdmi_port_attr_nportid(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_smart_attr_service(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2295,7 +2295,7 @@ lpfc_fdmi_smart_attr_service(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_smart_attr_guid(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2316,7 +2316,7 @@ lpfc_fdmi_smart_attr_guid(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_smart_attr_version(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2337,7 +2337,7 @@ lpfc_fdmi_smart_attr_version(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_smart_attr_model(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2358,7 +2358,7 @@ lpfc_fdmi_smart_attr_model(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_smart_attr_port_info(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2378,7 +2378,7 @@ lpfc_fdmi_smart_attr_port_info(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_smart_attr_qos(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { @@ -2393,7 +2393,7 @@ lpfc_fdmi_smart_attr_qos(struct lpfc_vport *vport, return size; } -int +static int lpfc_fdmi_smart_attr_security(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) { diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index c0af32f24954..b7d54bfb1df9 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -4617,7 +4617,7 @@ lpfc_els_disc_plogi(struct lpfc_vport *vport) return sentplogi; } -uint32_t +static uint32_t lpfc_rdp_res_link_service(struct fc_rdp_link_service_desc *desc, uint32_t word0) { @@ -4629,7 +4629,7 @@ lpfc_rdp_res_link_service(struct fc_rdp_link_service_desc *desc, return sizeof(struct fc_rdp_link_service_desc); } -uint32_t +static uint32_t lpfc_rdp_res_sfp_desc(struct fc_rdp_sfp_desc *desc, uint8_t *page_a0, uint8_t *page_a2) { @@ -4694,7 +4694,7 @@ lpfc_rdp_res_sfp_desc(struct fc_rdp_sfp_desc *desc, return sizeof(struct fc_rdp_sfp_desc); } -uint32_t +static uint32_t lpfc_rdp_res_link_error(struct fc_rdp_link_error_status_desc *desc, READ_LNK_VAR *stat) { @@ -4723,7 +4723,7 @@ lpfc_rdp_res_link_error(struct fc_rdp_link_error_status_desc *desc, return sizeof(struct fc_rdp_link_error_status_desc); } -uint32_t +static uint32_t lpfc_rdp_res_bbc_desc(struct fc_rdp_bbc_desc *desc, READ_LNK_VAR *stat, struct lpfc_vport *vport) { @@ -4748,7 +4748,7 @@ lpfc_rdp_res_bbc_desc(struct fc_rdp_bbc_desc *desc, READ_LNK_VAR *stat, return sizeof(struct fc_rdp_bbc_desc); } -uint32_t +static uint32_t lpfc_rdp_res_oed_temp_desc(struct lpfc_hba *phba, struct fc_rdp_oed_sfp_desc *desc, uint8_t *page_a2) { @@ -4776,7 +4776,7 @@ lpfc_rdp_res_oed_temp_desc(struct lpfc_hba *phba, return sizeof(struct fc_rdp_oed_sfp_desc); } -uint32_t +static uint32_t lpfc_rdp_res_oed_voltage_desc(struct lpfc_hba *phba, struct fc_rdp_oed_sfp_desc *desc, uint8_t *page_a2) @@ -4805,7 +4805,7 @@ lpfc_rdp_res_oed_voltage_desc(struct lpfc_hba *phba, return sizeof(struct fc_rdp_oed_sfp_desc); } -uint32_t +static uint32_t lpfc_rdp_res_oed_txbias_desc(struct lpfc_hba *phba, struct fc_rdp_oed_sfp_desc *desc, uint8_t *page_a2) @@ -4834,7 +4834,7 @@ lpfc_rdp_res_oed_txbias_desc(struct lpfc_hba *phba, return sizeof(struct fc_rdp_oed_sfp_desc); } -uint32_t +static uint32_t lpfc_rdp_res_oed_txpower_desc(struct lpfc_hba *phba, struct fc_rdp_oed_sfp_desc *desc, uint8_t *page_a2) @@ -4864,7 +4864,7 @@ lpfc_rdp_res_oed_txpower_desc(struct lpfc_hba *phba, } -uint32_t +static uint32_t lpfc_rdp_res_oed_rxpower_desc(struct lpfc_hba *phba, struct fc_rdp_oed_sfp_desc *desc, uint8_t *page_a2) @@ -4893,7 +4893,7 @@ lpfc_rdp_res_oed_rxpower_desc(struct lpfc_hba *phba, return sizeof(struct fc_rdp_oed_sfp_desc); } -uint32_t +static uint32_t lpfc_rdp_res_opd_desc(struct fc_rdp_opd_sfp_desc *desc, uint8_t *page_a0, struct lpfc_vport *vport) { @@ -4907,7 +4907,7 @@ lpfc_rdp_res_opd_desc(struct fc_rdp_opd_sfp_desc *desc, return sizeof(struct fc_rdp_opd_sfp_desc); } -uint32_t +static uint32_t lpfc_rdp_res_fec_desc(struct fc_fec_rdp_desc *desc, READ_LNK_VAR *stat) { if (bf_get(lpfc_read_link_stat_gec2, stat) == 0) @@ -4924,7 +4924,7 @@ lpfc_rdp_res_fec_desc(struct fc_fec_rdp_desc *desc, READ_LNK_VAR *stat) return sizeof(struct fc_fec_rdp_desc); } -uint32_t +static uint32_t lpfc_rdp_res_speed(struct fc_rdp_port_speed_desc *desc, struct lpfc_hba *phba) { uint16_t rdp_cap = 0; @@ -4986,7 +4986,7 @@ lpfc_rdp_res_speed(struct fc_rdp_port_speed_desc *desc, struct lpfc_hba *phba) return sizeof(struct fc_rdp_port_speed_desc); } -uint32_t +static uint32_t lpfc_rdp_res_diag_port_names(struct fc_rdp_port_name_desc *desc, struct lpfc_hba *phba) { @@ -5003,7 +5003,7 @@ lpfc_rdp_res_diag_port_names(struct fc_rdp_port_name_desc *desc, return sizeof(struct fc_rdp_port_name_desc); } -uint32_t +static uint32_t lpfc_rdp_res_attach_port_names(struct fc_rdp_port_name_desc *desc, struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) { @@ -5027,7 +5027,7 @@ lpfc_rdp_res_attach_port_names(struct fc_rdp_port_name_desc *desc, return sizeof(struct fc_rdp_port_name_desc); } -void +static void lpfc_els_rdp_cmpl(struct lpfc_hba *phba, struct lpfc_rdp_context *rdp_context, int status) { @@ -5165,7 +5165,7 @@ free_rdp_context: kfree(rdp_context); } -int +static int lpfc_get_rdp_info(struct lpfc_hba *phba, struct lpfc_rdp_context *rdp_context) { LPFC_MBOXQ_t *mbox = NULL; @@ -7995,7 +7995,7 @@ lpfc_els_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, } } -void +static void lpfc_start_fdmi(struct lpfc_vport *vport) { struct lpfc_hba *phba = vport->phba; diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c index 12dbe99ccc50..b234c50c255f 100644 --- a/drivers/scsi/lpfc/lpfc_mbox.c +++ b/drivers/scsi/lpfc/lpfc_mbox.c @@ -2260,7 +2260,7 @@ lpfc_sli4_dump_cfg_rg23(struct lpfc_hba *phba, struct lpfcMboxq *mbox) return 0; } -void +static void lpfc_mbx_cmpl_rdp_link_stat(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) { MAILBOX_t *mb; @@ -2281,7 +2281,7 @@ mbx_failed: rdp_context->cmpl(phba, rdp_context, rc); } -void +static void lpfc_mbx_cmpl_rdp_page_a2(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox) { struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) mbox->context1; diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 7080ce2920fd..c5326055beee 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -5689,7 +5689,7 @@ lpfc_sli4_dealloc_extent(struct lpfc_hba *phba, uint16_t type) return rc; } -void +static void lpfc_set_features(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox, uint32_t feature) { @@ -8968,7 +8968,7 @@ lpfc_sli_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp) * Since ABORTS must go on the same WQ of the command they are * aborting, we use command's fcp_wqidx. */ -int +static int lpfc_sli_calc_ring(struct lpfc_hba *phba, uint32_t ring_number, struct lpfc_iocbq *piocb) { diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index c1ed25adb17e..9ff57dee72d7 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -189,25 +189,12 @@ u32 megasas_build_and_issue_cmd(struct megasas_instance *instance, struct scsi_cmnd *scmd); static void megasas_complete_cmd_dpc(unsigned long instance_addr); -void -megasas_release_fusion(struct megasas_instance *instance); -int -megasas_ioc_init_fusion(struct megasas_instance *instance); -void -megasas_free_cmds_fusion(struct megasas_instance *instance); -u8 -megasas_get_map_info(struct megasas_instance *instance); -int -megasas_sync_map_info(struct megasas_instance *instance); int wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd, int seconds); -void megasas_reset_reply_desc(struct megasas_instance *instance); void megasas_fusion_ocr_wq(struct work_struct *work); static int megasas_get_ld_vf_affiliation(struct megasas_instance *instance, int initial); -int megasas_check_mpio_paths(struct megasas_instance *instance, - struct scsi_cmnd *scmd); int megasas_issue_dcmd(struct megasas_instance *instance, struct megasas_cmd *cmd) @@ -5036,7 +5023,7 @@ static int megasas_init_fw(struct megasas_instance *instance) /* Find first memory bar */ bar_list = pci_select_bars(instance->pdev, IORESOURCE_MEM); - instance->bar = find_first_bit(&bar_list, sizeof(unsigned long)); + instance->bar = find_first_bit(&bar_list, BITS_PER_LONG); if (pci_request_selected_regions(instance->pdev, 1<<instance->bar, "megasas: LSI")) { dev_printk(KERN_DEBUG, &instance->pdev->dev, "IO memory region busy!\n"); @@ -5782,7 +5769,7 @@ static int megasas_probe_one(struct pci_dev *pdev, &instance->consumer_h); if (!instance->producer || !instance->consumer) { - dev_printk(KERN_DEBUG, &pdev->dev, "Failed to allocate" + dev_printk(KERN_DEBUG, &pdev->dev, "Failed to allocate " "memory for producer, consumer\n"); goto fail_alloc_dma_buf; } @@ -6711,14 +6698,9 @@ static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg) unsigned long flags; u32 wait_time = MEGASAS_RESET_WAIT_TIME; - ioc = kmalloc(sizeof(*ioc), GFP_KERNEL); - if (!ioc) - return -ENOMEM; - - if (copy_from_user(ioc, user_ioc, sizeof(*ioc))) { - error = -EFAULT; - goto out_kfree_ioc; - } + ioc = memdup_user(user_ioc, sizeof(*ioc)); + if (IS_ERR(ioc)) + return PTR_ERR(ioc); instance = megasas_lookup_instance(ioc->host_no); if (!instance) { diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.h b/drivers/scsi/megaraid/megaraid_sas_fusion.h index 80eaee22f5bc..e3bee04c1eb1 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.h +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.h @@ -991,5 +991,14 @@ union desc_value { } u; }; +void megasas_free_cmds_fusion(struct megasas_instance *instance); +int megasas_ioc_init_fusion(struct megasas_instance *instance); +u8 megasas_get_map_info(struct megasas_instance *instance); +int megasas_sync_map_info(struct megasas_instance *instance); +void megasas_release_fusion(struct megasas_instance *instance); +void megasas_reset_reply_desc(struct megasas_instance *instance); +int megasas_check_mpio_paths(struct megasas_instance *instance, + struct scsi_cmnd *scmd); +void megasas_fusion_ocr_wq(struct work_struct *work); #endif /* _MEGARAID_SAS_FUSION_H_ */ diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index 750f82c339d4..a1a5ceb42ce6 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -98,7 +98,7 @@ MODULE_PARM_DESC(mpt3sas_fwfault_debug, " enable detection of firmware fault and halt firmware - (default=0)"); static int -_base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc, int sleep_flag); +_base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc); /** * _scsih_set_fwfault_debug - global setting of ioc->fwfault_debug. @@ -218,8 +218,7 @@ _base_fault_reset_work(struct work_struct *work) ioc->non_operational_loop = 0; if ((doorbell & MPI2_IOC_STATE_MASK) != MPI2_IOC_STATE_OPERATIONAL) { - rc = mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + rc = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); pr_warn(MPT3SAS_FMT "%s: hard reset: %s\n", ioc->name, __func__, (rc == 0) ? "success" : "failed"); doorbell = mpt3sas_base_get_iocstate(ioc, 0); @@ -2040,7 +2039,7 @@ _base_enable_msix(struct MPT3SAS_ADAPTER *ioc) * mpt3sas_base_unmap_resources - free controller resources * @ioc: per adapter object */ -void +static void mpt3sas_base_unmap_resources(struct MPT3SAS_ADAPTER *ioc) { struct pci_dev *pdev = ioc->pdev; @@ -2145,7 +2144,7 @@ mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc) _base_mask_interrupts(ioc); - r = _base_get_ioc_facts(ioc, CAN_SLEEP); + r = _base_get_ioc_facts(ioc); if (r) goto out_fail; @@ -3183,12 +3182,11 @@ _base_release_memory_pools(struct MPT3SAS_ADAPTER *ioc) /** * _base_allocate_memory_pools - allocate start of day memory pools * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 success, anything else error */ static int -_base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) +_base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc) { struct mpt3sas_facts *facts; u16 max_sge_elements; @@ -3658,29 +3656,25 @@ mpt3sas_base_get_iocstate(struct MPT3SAS_ADAPTER *ioc, int cooked) * _base_wait_on_iocstate - waiting on a particular ioc state * @ioc_state: controller state { READY, OPERATIONAL, or RESET } * @timeout: timeout in second - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_wait_on_iocstate(struct MPT3SAS_ADAPTER *ioc, u32 ioc_state, int timeout, - int sleep_flag) +_base_wait_on_iocstate(struct MPT3SAS_ADAPTER *ioc, u32 ioc_state, int timeout) { u32 count, cntdn; u32 current_state; count = 0; - cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; + cntdn = 1000 * timeout; do { current_state = mpt3sas_base_get_iocstate(ioc, 1); if (current_state == ioc_state) return 0; if (count && current_state == MPI2_IOC_STATE_FAULT) break; - if (sleep_flag == CAN_SLEEP) - usleep_range(1000, 1500); - else - udelay(500); + + usleep_range(1000, 1500); count++; } while (--cntdn); @@ -3692,24 +3686,22 @@ _base_wait_on_iocstate(struct MPT3SAS_ADAPTER *ioc, u32 ioc_state, int timeout, * a write to the doorbell) * @ioc: per adapter object * @timeout: timeout in second - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. * * Notes: MPI2_HIS_IOC2SYS_DB_STATUS - set to one when IOC writes to doorbell. */ static int -_base_diag_reset(struct MPT3SAS_ADAPTER *ioc, int sleep_flag); +_base_diag_reset(struct MPT3SAS_ADAPTER *ioc); static int -_base_wait_for_doorbell_int(struct MPT3SAS_ADAPTER *ioc, int timeout, - int sleep_flag) +_base_wait_for_doorbell_int(struct MPT3SAS_ADAPTER *ioc, int timeout) { u32 cntdn, count; u32 int_status; count = 0; - cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; + cntdn = 1000 * timeout; do { int_status = readl(&ioc->chip->HostInterruptStatus); if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) { @@ -3718,10 +3710,35 @@ _base_wait_for_doorbell_int(struct MPT3SAS_ADAPTER *ioc, int timeout, ioc->name, __func__, count, timeout)); return 0; } - if (sleep_flag == CAN_SLEEP) - usleep_range(1000, 1500); - else - udelay(500); + + usleep_range(1000, 1500); + count++; + } while (--cntdn); + + pr_err(MPT3SAS_FMT + "%s: failed due to timeout count(%d), int_status(%x)!\n", + ioc->name, __func__, count, int_status); + return -EFAULT; +} + +static int +_base_spin_on_doorbell_int(struct MPT3SAS_ADAPTER *ioc, int timeout) +{ + u32 cntdn, count; + u32 int_status; + + count = 0; + cntdn = 2000 * timeout; + do { + int_status = readl(&ioc->chip->HostInterruptStatus); + if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) { + dhsprintk(ioc, pr_info(MPT3SAS_FMT + "%s: successful count(%d), timeout(%d)\n", + ioc->name, __func__, count, timeout)); + return 0; + } + + udelay(500); count++; } while (--cntdn); @@ -3729,13 +3746,13 @@ _base_wait_for_doorbell_int(struct MPT3SAS_ADAPTER *ioc, int timeout, "%s: failed due to timeout count(%d), int_status(%x)!\n", ioc->name, __func__, count, int_status); return -EFAULT; + } /** * _base_wait_for_doorbell_ack - waiting for controller to read the doorbell. * @ioc: per adapter object * @timeout: timeout in second - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. * @@ -3743,15 +3760,14 @@ _base_wait_for_doorbell_int(struct MPT3SAS_ADAPTER *ioc, int timeout, * doorbell. */ static int -_base_wait_for_doorbell_ack(struct MPT3SAS_ADAPTER *ioc, int timeout, - int sleep_flag) +_base_wait_for_doorbell_ack(struct MPT3SAS_ADAPTER *ioc, int timeout) { u32 cntdn, count; u32 int_status; u32 doorbell; count = 0; - cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; + cntdn = 1000 * timeout; do { int_status = readl(&ioc->chip->HostInterruptStatus); if (!(int_status & MPI2_HIS_SYS2IOC_DB_STATUS)) { @@ -3769,10 +3785,7 @@ _base_wait_for_doorbell_ack(struct MPT3SAS_ADAPTER *ioc, int timeout, } else if (int_status == 0xFFFFFFFF) goto out; - if (sleep_flag == CAN_SLEEP) - usleep_range(1000, 1500); - else - udelay(500); + usleep_range(1000, 1500); count++; } while (--cntdn); @@ -3787,20 +3800,18 @@ _base_wait_for_doorbell_ack(struct MPT3SAS_ADAPTER *ioc, int timeout, * _base_wait_for_doorbell_not_used - waiting for doorbell to not be in use * @ioc: per adapter object * @timeout: timeout in second - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. * */ static int -_base_wait_for_doorbell_not_used(struct MPT3SAS_ADAPTER *ioc, int timeout, - int sleep_flag) +_base_wait_for_doorbell_not_used(struct MPT3SAS_ADAPTER *ioc, int timeout) { u32 cntdn, count; u32 doorbell_reg; count = 0; - cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; + cntdn = 1000 * timeout; do { doorbell_reg = readl(&ioc->chip->Doorbell); if (!(doorbell_reg & MPI2_DOORBELL_USED)) { @@ -3809,10 +3820,8 @@ _base_wait_for_doorbell_not_used(struct MPT3SAS_ADAPTER *ioc, int timeout, ioc->name, __func__, count, timeout)); return 0; } - if (sleep_flag == CAN_SLEEP) - usleep_range(1000, 1500); - else - udelay(500); + + usleep_range(1000, 1500); count++; } while (--cntdn); @@ -3827,13 +3836,11 @@ _base_wait_for_doorbell_not_used(struct MPT3SAS_ADAPTER *ioc, int timeout, * @ioc: per adapter object * @reset_type: currently only supports: MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET * @timeout: timeout in second - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_send_ioc_reset(struct MPT3SAS_ADAPTER *ioc, u8 reset_type, int timeout, - int sleep_flag) +_base_send_ioc_reset(struct MPT3SAS_ADAPTER *ioc, u8 reset_type, int timeout) { u32 ioc_state; int r = 0; @@ -3852,12 +3859,11 @@ _base_send_ioc_reset(struct MPT3SAS_ADAPTER *ioc, u8 reset_type, int timeout, writel(reset_type << MPI2_DOORBELL_FUNCTION_SHIFT, &ioc->chip->Doorbell); - if ((_base_wait_for_doorbell_ack(ioc, 15, sleep_flag))) { + if ((_base_wait_for_doorbell_ack(ioc, 15))) { r = -EFAULT; goto out; } - ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, - timeout, sleep_flag); + ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, timeout); if (ioc_state) { pr_err(MPT3SAS_FMT "%s: failed going to ready state (ioc_state=0x%x)\n", @@ -3879,18 +3885,16 @@ _base_send_ioc_reset(struct MPT3SAS_ADAPTER *ioc, u8 reset_type, int timeout, * @reply_bytes: reply length * @reply: pointer to reply payload * @timeout: timeout in second - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, - u32 *request, int reply_bytes, u16 *reply, int timeout, int sleep_flag) + u32 *request, int reply_bytes, u16 *reply, int timeout) { MPI2DefaultReply_t *default_reply = (MPI2DefaultReply_t *)reply; int i; u8 failed; - u16 dummy; __le32 *mfp; /* make sure doorbell is not in use */ @@ -3911,7 +3915,7 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, ((request_bytes/4)<<MPI2_DOORBELL_ADD_DWORDS_SHIFT)), &ioc->chip->Doorbell); - if ((_base_wait_for_doorbell_int(ioc, 5, NO_SLEEP))) { + if ((_base_spin_on_doorbell_int(ioc, 5))) { pr_err(MPT3SAS_FMT "doorbell handshake int failed (line=%d)\n", ioc->name, __LINE__); @@ -3919,7 +3923,7 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, } writel(0, &ioc->chip->HostInterruptStatus); - if ((_base_wait_for_doorbell_ack(ioc, 5, sleep_flag))) { + if ((_base_wait_for_doorbell_ack(ioc, 5))) { pr_err(MPT3SAS_FMT "doorbell handshake ack failed (line=%d)\n", ioc->name, __LINE__); @@ -3929,7 +3933,7 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, /* send message 32-bits at a time */ for (i = 0, failed = 0; i < request_bytes/4 && !failed; i++) { writel(cpu_to_le32(request[i]), &ioc->chip->Doorbell); - if ((_base_wait_for_doorbell_ack(ioc, 5, sleep_flag))) + if ((_base_wait_for_doorbell_ack(ioc, 5))) failed = 1; } @@ -3941,7 +3945,7 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, } /* now wait for the reply */ - if ((_base_wait_for_doorbell_int(ioc, timeout, sleep_flag))) { + if ((_base_wait_for_doorbell_int(ioc, timeout))) { pr_err(MPT3SAS_FMT "doorbell handshake int failed (line=%d)\n", ioc->name, __LINE__); @@ -3952,7 +3956,7 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, reply[0] = le16_to_cpu(readl(&ioc->chip->Doorbell) & MPI2_DOORBELL_DATA_MASK); writel(0, &ioc->chip->HostInterruptStatus); - if ((_base_wait_for_doorbell_int(ioc, 5, sleep_flag))) { + if ((_base_wait_for_doorbell_int(ioc, 5))) { pr_err(MPT3SAS_FMT "doorbell handshake int failed (line=%d)\n", ioc->name, __LINE__); @@ -3963,22 +3967,22 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, writel(0, &ioc->chip->HostInterruptStatus); for (i = 2; i < default_reply->MsgLength * 2; i++) { - if ((_base_wait_for_doorbell_int(ioc, 5, sleep_flag))) { + if ((_base_wait_for_doorbell_int(ioc, 5))) { pr_err(MPT3SAS_FMT "doorbell handshake int failed (line=%d)\n", ioc->name, __LINE__); return -EFAULT; } if (i >= reply_bytes/2) /* overflow case */ - dummy = readl(&ioc->chip->Doorbell); + readl(&ioc->chip->Doorbell); else reply[i] = le16_to_cpu(readl(&ioc->chip->Doorbell) & MPI2_DOORBELL_DATA_MASK); writel(0, &ioc->chip->HostInterruptStatus); } - _base_wait_for_doorbell_int(ioc, 5, sleep_flag); - if (_base_wait_for_doorbell_not_used(ioc, 5, sleep_flag) != 0) { + _base_wait_for_doorbell_int(ioc, 5); + if (_base_wait_for_doorbell_not_used(ioc, 5) != 0) { dhsprintk(ioc, pr_info(MPT3SAS_FMT "doorbell is in use (line=%d)\n", ioc->name, __LINE__)); } @@ -4015,7 +4019,6 @@ mpt3sas_base_sas_iounit_control(struct MPT3SAS_ADAPTER *ioc, { u16 smid; u32 ioc_state; - unsigned long timeleft; bool issue_reset = false; int rc; void *request; @@ -4068,7 +4071,7 @@ mpt3sas_base_sas_iounit_control(struct MPT3SAS_ADAPTER *ioc, ioc->ioc_link_reset_in_progress = 1; init_completion(&ioc->base_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, + wait_for_completion_timeout(&ioc->base_cmds.done, msecs_to_jiffies(10000)); if ((mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET || mpi_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET) && @@ -4093,8 +4096,7 @@ mpt3sas_base_sas_iounit_control(struct MPT3SAS_ADAPTER *ioc, issue_host_reset: if (issue_reset) - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); ioc->base_cmds.status = MPT3_CMD_NOT_USED; rc = -EFAULT; out: @@ -4119,7 +4121,6 @@ mpt3sas_base_scsi_enclosure_processor(struct MPT3SAS_ADAPTER *ioc, { u16 smid; u32 ioc_state; - unsigned long timeleft; bool issue_reset = false; int rc; void *request; @@ -4170,7 +4171,7 @@ mpt3sas_base_scsi_enclosure_processor(struct MPT3SAS_ADAPTER *ioc, memcpy(request, mpi_request, sizeof(Mpi2SepReply_t)); init_completion(&ioc->base_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, + wait_for_completion_timeout(&ioc->base_cmds.done, msecs_to_jiffies(10000)); if (!(ioc->base_cmds.status & MPT3_CMD_COMPLETE)) { pr_err(MPT3SAS_FMT "%s: timeout\n", @@ -4191,8 +4192,7 @@ mpt3sas_base_scsi_enclosure_processor(struct MPT3SAS_ADAPTER *ioc, issue_host_reset: if (issue_reset) - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); ioc->base_cmds.status = MPT3_CMD_NOT_USED; rc = -EFAULT; out: @@ -4203,12 +4203,11 @@ mpt3sas_base_scsi_enclosure_processor(struct MPT3SAS_ADAPTER *ioc, /** * _base_get_port_facts - obtain port facts reply and save in ioc * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_get_port_facts(struct MPT3SAS_ADAPTER *ioc, int port, int sleep_flag) +_base_get_port_facts(struct MPT3SAS_ADAPTER *ioc, int port) { Mpi2PortFactsRequest_t mpi_request; Mpi2PortFactsReply_t mpi_reply; @@ -4224,7 +4223,7 @@ _base_get_port_facts(struct MPT3SAS_ADAPTER *ioc, int port, int sleep_flag) mpi_request.Function = MPI2_FUNCTION_PORT_FACTS; mpi_request.PortNumber = port; r = _base_handshake_req_reply_wait(ioc, mpi_request_sz, - (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply, 5, CAN_SLEEP); + (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply, 5); if (r != 0) { pr_err(MPT3SAS_FMT "%s: handshake failed (r=%d)\n", @@ -4247,13 +4246,11 @@ _base_get_port_facts(struct MPT3SAS_ADAPTER *ioc, int port, int sleep_flag) * _base_wait_for_iocstate - Wait until the card is in READY or OPERATIONAL * @ioc: per adapter object * @timeout: - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_wait_for_iocstate(struct MPT3SAS_ADAPTER *ioc, int timeout, - int sleep_flag) +_base_wait_for_iocstate(struct MPT3SAS_ADAPTER *ioc, int timeout) { u32 ioc_state; int rc; @@ -4287,8 +4284,7 @@ _base_wait_for_iocstate(struct MPT3SAS_ADAPTER *ioc, int timeout, goto issue_diag_reset; } - ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, - timeout, sleep_flag); + ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, timeout); if (ioc_state) { dfailprintk(ioc, printk(MPT3SAS_FMT "%s: failed going to ready state (ioc_state=0x%x)\n", @@ -4297,19 +4293,18 @@ _base_wait_for_iocstate(struct MPT3SAS_ADAPTER *ioc, int timeout, } issue_diag_reset: - rc = _base_diag_reset(ioc, sleep_flag); + rc = _base_diag_reset(ioc); return rc; } /** * _base_get_ioc_facts - obtain ioc facts reply and save in ioc * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) +_base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc) { Mpi2IOCFactsRequest_t mpi_request; Mpi2IOCFactsReply_t mpi_reply; @@ -4319,7 +4314,7 @@ _base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, __func__)); - r = _base_wait_for_iocstate(ioc, 10, sleep_flag); + r = _base_wait_for_iocstate(ioc, 10); if (r) { dfailprintk(ioc, printk(MPT3SAS_FMT "%s: failed getting to correct state\n", @@ -4331,7 +4326,7 @@ _base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) memset(&mpi_request, 0, mpi_request_sz); mpi_request.Function = MPI2_FUNCTION_IOC_FACTS; r = _base_handshake_req_reply_wait(ioc, mpi_request_sz, - (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply, 5, CAN_SLEEP); + (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply, 5); if (r != 0) { pr_err(MPT3SAS_FMT "%s: handshake failed (r=%d)\n", @@ -4391,12 +4386,11 @@ _base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) /** * _base_send_ioc_init - send ioc_init to firmware * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_send_ioc_init(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) +_base_send_ioc_init(struct MPT3SAS_ADAPTER *ioc) { Mpi2IOCInitRequest_t mpi_request; Mpi2IOCInitReply_t mpi_reply; @@ -4479,8 +4473,7 @@ _base_send_ioc_init(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) r = _base_handshake_req_reply_wait(ioc, sizeof(Mpi2IOCInitRequest_t), (u32 *)&mpi_request, - sizeof(Mpi2IOCInitReply_t), (u16 *)&mpi_reply, 10, - sleep_flag); + sizeof(Mpi2IOCInitReply_t), (u16 *)&mpi_reply, 10); if (r != 0) { pr_err(MPT3SAS_FMT "%s: handshake failed (r=%d)\n", @@ -4555,16 +4548,14 @@ mpt3sas_port_enable_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, /** * _base_send_port_enable - send port_enable(discovery stuff) to firmware * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_send_port_enable(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) +_base_send_port_enable(struct MPT3SAS_ADAPTER *ioc) { Mpi2PortEnableRequest_t *mpi_request; Mpi2PortEnableReply_t *mpi_reply; - unsigned long timeleft; int r = 0; u16 smid; u16 ioc_status; @@ -4592,8 +4583,7 @@ _base_send_port_enable(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) init_completion(&ioc->port_enable_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->port_enable_cmds.done, - 300*HZ); + wait_for_completion_timeout(&ioc->port_enable_cmds.done, 300*HZ); if (!(ioc->port_enable_cmds.status & MPT3_CMD_COMPLETE)) { pr_err(MPT3SAS_FMT "%s: timeout\n", ioc->name, __func__); @@ -4737,15 +4727,13 @@ _base_unmask_events(struct MPT3SAS_ADAPTER *ioc, u16 event) /** * _base_event_notification - send event notification * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_event_notification(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) +_base_event_notification(struct MPT3SAS_ADAPTER *ioc) { Mpi2EventNotificationRequest_t *mpi_request; - unsigned long timeleft; u16 smid; int r = 0; int i; @@ -4777,7 +4765,7 @@ _base_event_notification(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) cpu_to_le32(ioc->event_masks[i]); init_completion(&ioc->base_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, 30*HZ); + wait_for_completion_timeout(&ioc->base_cmds.done, 30*HZ); if (!(ioc->base_cmds.status & MPT3_CMD_COMPLETE)) { pr_err(MPT3SAS_FMT "%s: timeout\n", ioc->name, __func__); @@ -4827,19 +4815,18 @@ mpt3sas_base_validate_event_type(struct MPT3SAS_ADAPTER *ioc, u32 *event_type) return; mutex_lock(&ioc->base_cmds.mutex); - _base_event_notification(ioc, CAN_SLEEP); + _base_event_notification(ioc); mutex_unlock(&ioc->base_cmds.mutex); } /** * _base_diag_reset - the "big hammer" start of day reset * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_diag_reset(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) +_base_diag_reset(struct MPT3SAS_ADAPTER *ioc) { u32 host_diagnostic; u32 ioc_state; @@ -4867,10 +4854,7 @@ _base_diag_reset(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) writel(MPI2_WRSEQ_6TH_KEY_VALUE, &ioc->chip->WriteSequence); /* wait 100 msec */ - if (sleep_flag == CAN_SLEEP) - msleep(100); - else - mdelay(100); + msleep(100); if (count++ > 20) goto out; @@ -4890,10 +4874,7 @@ _base_diag_reset(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) &ioc->chip->HostDiagnostic); /*This delay allows the chip PCIe hardware time to finish reset tasks*/ - if (sleep_flag == CAN_SLEEP) - msleep(MPI2_HARD_RESET_PCIE_FIRST_READ_DELAY_MICRO_SEC/1000); - else - mdelay(MPI2_HARD_RESET_PCIE_FIRST_READ_DELAY_MICRO_SEC/1000); + msleep(MPI2_HARD_RESET_PCIE_FIRST_READ_DELAY_MICRO_SEC/1000); /* Approximately 300 second max wait */ for (count = 0; count < (300000000 / @@ -4906,13 +4887,7 @@ _base_diag_reset(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) if (!(host_diagnostic & MPI2_DIAG_RESET_ADAPTER)) break; - /* Wait to pass the second read delay window */ - if (sleep_flag == CAN_SLEEP) - msleep(MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC - / 1000); - else - mdelay(MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC - / 1000); + msleep(MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC / 1000); } if (host_diagnostic & MPI2_DIAG_HCB_MODE) { @@ -4941,8 +4916,7 @@ _base_diag_reset(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) drsprintk(ioc, pr_info(MPT3SAS_FMT "Wait for FW to go to the READY state\n", ioc->name)); - ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, 20, - sleep_flag); + ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, 20); if (ioc_state) { pr_err(MPT3SAS_FMT "%s: failed going to ready state (ioc_state=0x%x)\n", @@ -4961,14 +4935,12 @@ _base_diag_reset(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) /** * _base_make_ioc_ready - put controller in READY state * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP * @type: FORCE_BIG_HAMMER or SOFT_RESET * * Returns 0 for success, non-zero for failure. */ static int -_base_make_ioc_ready(struct MPT3SAS_ADAPTER *ioc, int sleep_flag, - enum reset_type type) +_base_make_ioc_ready(struct MPT3SAS_ADAPTER *ioc, enum reset_type type) { u32 ioc_state; int rc; @@ -4995,10 +4967,7 @@ _base_make_ioc_ready(struct MPT3SAS_ADAPTER *ioc, int sleep_flag, ioc->name, __func__, ioc_state); return -EFAULT; } - if (sleep_flag == CAN_SLEEP) - ssleep(1); - else - mdelay(1000); + ssleep(1); ioc_state = mpt3sas_base_get_iocstate(ioc, 0); } } @@ -5024,24 +4993,23 @@ _base_make_ioc_ready(struct MPT3SAS_ADAPTER *ioc, int sleep_flag, if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_OPERATIONAL) if (!(_base_send_ioc_reset(ioc, - MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET, 15, CAN_SLEEP))) { + MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET, 15))) { return 0; } issue_diag_reset: - rc = _base_diag_reset(ioc, CAN_SLEEP); + rc = _base_diag_reset(ioc); return rc; } /** * _base_make_ioc_operational - put controller in OPERATIONAL state * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) +_base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc) { int r, i, index; unsigned long flags; @@ -5160,7 +5128,7 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) } skip_init_reply_post_free_queue: - r = _base_send_ioc_init(ioc, sleep_flag); + r = _base_send_ioc_init(ioc); if (r) return r; @@ -5186,13 +5154,11 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) skip_init_reply_post_host_index: _base_unmask_interrupts(ioc); - r = _base_event_notification(ioc, sleep_flag); + r = _base_event_notification(ioc); if (r) return r; - if (sleep_flag == CAN_SLEEP) - _base_static_config_pages(ioc); - + _base_static_config_pages(ioc); if (ioc->is_driver_loading) { @@ -5211,7 +5177,7 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) return r; /* scan_start and scan_finished support */ } - r = _base_send_port_enable(ioc, sleep_flag); + r = _base_send_port_enable(ioc); if (r) return r; @@ -5235,7 +5201,7 @@ mpt3sas_base_free_resources(struct MPT3SAS_ADAPTER *ioc) if (ioc->chip_phys && ioc->chip) { _base_mask_interrupts(ioc); ioc->shost_recovery = 1; - _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET); + _base_make_ioc_ready(ioc, SOFT_RESET); ioc->shost_recovery = 0; } @@ -5292,7 +5258,7 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) goto out_free_resources; pci_set_drvdata(ioc->pdev, ioc->shost); - r = _base_get_ioc_facts(ioc, CAN_SLEEP); + r = _base_get_ioc_facts(ioc); if (r) goto out_free_resources; @@ -5326,7 +5292,7 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) ioc->build_sg_mpi = &_base_build_sg; ioc->build_zero_len_sge_mpi = &_base_build_zero_len_sge; - r = _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET); + r = _base_make_ioc_ready(ioc, SOFT_RESET); if (r) goto out_free_resources; @@ -5338,12 +5304,12 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) } for (i = 0 ; i < ioc->facts.NumberOfPorts; i++) { - r = _base_get_port_facts(ioc, i, CAN_SLEEP); + r = _base_get_port_facts(ioc, i); if (r) goto out_free_resources; } - r = _base_allocate_memory_pools(ioc, CAN_SLEEP); + r = _base_allocate_memory_pools(ioc); if (r) goto out_free_resources; @@ -5429,7 +5395,7 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) if (ioc->hba_mpi_version_belonged == MPI26_VERSION) _base_unmask_events(ioc, MPI2_EVENT_ACTIVE_CABLE_EXCEPTION); - r = _base_make_ioc_operational(ioc, CAN_SLEEP); + r = _base_make_ioc_operational(ioc); if (r) goto out_free_resources; @@ -5565,21 +5531,18 @@ _base_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase) /** * _wait_for_commands_to_complete - reset controller * @ioc: Pointer to MPT_ADAPTER structure - * @sleep_flag: CAN_SLEEP or NO_SLEEP * * This function waiting(3s) for all pending commands to complete * prior to putting controller in reset. */ static void -_wait_for_commands_to_complete(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) +_wait_for_commands_to_complete(struct MPT3SAS_ADAPTER *ioc) { u32 ioc_state; unsigned long flags; u16 i; ioc->pending_io_count = 0; - if (sleep_flag != CAN_SLEEP) - return; ioc_state = mpt3sas_base_get_iocstate(ioc, 0); if ((ioc_state & MPI2_IOC_STATE_MASK) != MPI2_IOC_STATE_OPERATIONAL) @@ -5602,13 +5565,12 @@ _wait_for_commands_to_complete(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) /** * mpt3sas_base_hard_reset_handler - reset controller * @ioc: Pointer to MPT_ADAPTER structure - * @sleep_flag: CAN_SLEEP or NO_SLEEP * @type: FORCE_BIG_HAMMER or SOFT_RESET * * Returns 0 for success, non-zero for failure. */ int -mpt3sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, int sleep_flag, +mpt3sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, enum reset_type type) { int r; @@ -5629,13 +5591,6 @@ mpt3sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, int sleep_flag, if (mpt3sas_fwfault_debug) mpt3sas_halt_firmware(ioc); - /* TODO - What we really should be doing is pulling - * out all the code associated with NO_SLEEP; its never used. - * That is legacy code from mpt fusion driver, ported over. - * I will leave this BUG_ON here for now till its been resolved. - */ - BUG_ON(sleep_flag == NO_SLEEP); - /* wait for an active reset in progress to complete */ if (!mutex_trylock(&ioc->reset_in_progress_mutex)) { do { @@ -5660,9 +5615,9 @@ mpt3sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, int sleep_flag, is_fault = 1; } _base_reset_handler(ioc, MPT3_IOC_PRE_RESET); - _wait_for_commands_to_complete(ioc, sleep_flag); + _wait_for_commands_to_complete(ioc); _base_mask_interrupts(ioc); - r = _base_make_ioc_ready(ioc, sleep_flag, type); + r = _base_make_ioc_ready(ioc, type); if (r) goto out; _base_reset_handler(ioc, MPT3_IOC_AFTER_RESET); @@ -5675,7 +5630,7 @@ mpt3sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, int sleep_flag, r = -EFAULT; goto out; } - r = _base_get_ioc_facts(ioc, CAN_SLEEP); + r = _base_get_ioc_facts(ioc); if (r) goto out; @@ -5684,7 +5639,7 @@ mpt3sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, int sleep_flag, "Please reboot the system and ensure that the correct" " firmware version is running\n", ioc->name); - r = _base_make_ioc_operational(ioc, sleep_flag); + r = _base_make_ioc_operational(ioc); if (!r) _base_reset_handler(ioc, MPT3_IOC_DONE_RESET); diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h index 892c9be008b5..3e71bc1b4a80 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.h +++ b/drivers/scsi/mpt3sas/mpt3sas_base.h @@ -119,10 +119,6 @@ #define MPT_MAX_CALLBACKS 32 - -#define CAN_SLEEP 1 -#define NO_SLEEP 0 - #define INTERNAL_CMDS_COUNT 10 /* reserved cmds */ /* reserved for issuing internally framed scsi io cmds */ #define INTERNAL_SCSIIO_CMDS_COUNT 3 @@ -478,7 +474,7 @@ struct _sas_device { u8 pfa_led_on; u8 pend_sas_rphy_add; u8 enclosure_level; - u8 connector_name[4]; + u8 connector_name[5]; struct kref refcount; }; @@ -794,16 +790,6 @@ struct reply_post_struct { dma_addr_t reply_post_free_dma; }; -/** - * enum mutex_type - task management mutex type - * @TM_MUTEX_OFF: mutex is not required becuase calling function is acquiring it - * @TM_MUTEX_ON: mutex is required - */ -enum mutex_type { - TM_MUTEX_OFF = 0, - TM_MUTEX_ON = 1, -}; - typedef void (*MPT3SAS_FLUSH_RUNNING_CMDS)(struct MPT3SAS_ADAPTER *ioc); /** * struct MPT3SAS_ADAPTER - per adapter struct @@ -1229,7 +1215,7 @@ int mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc); void mpt3sas_base_detach(struct MPT3SAS_ADAPTER *ioc); int mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc); void mpt3sas_base_free_resources(struct MPT3SAS_ADAPTER *ioc); -int mpt3sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, int sleep_flag, +int mpt3sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, enum reset_type type); void *mpt3sas_base_get_msg_frame(struct MPT3SAS_ADAPTER *ioc, u16 smid); @@ -1291,7 +1277,11 @@ void mpt3sas_scsih_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase); int mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, uint channel, uint id, uint lun, u8 type, u16 smid_task, - ulong timeout, enum mutex_type m_type); + ulong timeout); +int mpt3sas_scsih_issue_locked_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, + uint channel, uint id, uint lun, u8 type, u16 smid_task, + ulong timeout); + void mpt3sas_scsih_set_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle); void mpt3sas_scsih_clear_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle); void mpt3sas_expander_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address); diff --git a/drivers/scsi/mpt3sas/mpt3sas_config.c b/drivers/scsi/mpt3sas/mpt3sas_config.c index a6914ec99cc0..cebfd734fd76 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_config.c +++ b/drivers/scsi/mpt3sas/mpt3sas_config.c @@ -285,7 +285,6 @@ _config_request(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigRequest_t { u16 smid; u32 ioc_state; - unsigned long timeleft; Mpi2ConfigRequest_t *config_request; int r; u8 retry_count, issue_host_reset = 0; @@ -386,8 +385,7 @@ _config_request(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigRequest_t _config_display_some_debug(ioc, smid, "config_request", NULL); init_completion(&ioc->config_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->config_cmds.done, - timeout*HZ); + wait_for_completion_timeout(&ioc->config_cmds.done, timeout*HZ); if (!(ioc->config_cmds.status & MPT3_CMD_COMPLETE)) { pr_err(MPT3SAS_FMT "%s: timeout\n", ioc->name, __func__); @@ -491,8 +489,7 @@ _config_request(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigRequest_t mutex_unlock(&ioc->config_cmds.mutex); if (issue_host_reset) - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); return r; } diff --git a/drivers/scsi/mpt3sas/mpt3sas_ctl.c b/drivers/scsi/mpt3sas/mpt3sas_ctl.c index 7d00f09666b6..26cdc127ac89 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_ctl.c +++ b/drivers/scsi/mpt3sas/mpt3sas_ctl.c @@ -518,7 +518,7 @@ mpt3sas_ctl_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase) * * Called when application request fasyn callback handler. */ -int +static int _ctl_fasync(int fd, struct file *filep, int mode) { return fasync_helper(fd, filep, mode, &async_queue); @@ -530,7 +530,7 @@ _ctl_fasync(int fd, struct file *filep, int mode) * @wait - * */ -unsigned int +static unsigned int _ctl_poll(struct file *filep, poll_table *wait) { struct MPT3SAS_ADAPTER *ioc; @@ -641,9 +641,8 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, MPI2RequestHeader_t *mpi_request = NULL, *request; MPI2DefaultReply_t *mpi_reply; u32 ioc_state; - u16 ioc_status; u16 smid; - unsigned long timeout, timeleft; + unsigned long timeout; u8 issue_reset; u32 sz; void *psge; @@ -914,8 +913,7 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, timeout = MPT3_IOCTL_DEFAULT_TIMEOUT; else timeout = karg.timeout; - timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, - timeout*HZ); + wait_for_completion_timeout(&ioc->ctl_cmds.done, timeout*HZ); if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) { Mpi2SCSITaskManagementRequest_t *tm_request = (Mpi2SCSITaskManagementRequest_t *)mpi_request; @@ -938,7 +936,6 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, } mpi_reply = ioc->ctl_cmds.reply; - ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; if (mpi_reply->Function == MPI2_FUNCTION_SCSI_TASK_MGMT && (ioc->logging_level & MPT_DEBUG_TM)) { @@ -1001,13 +998,11 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, ioc->name, le16_to_cpu(mpi_request->FunctionDependent1)); mpt3sas_halt_firmware(ioc); - mpt3sas_scsih_issue_tm(ioc, + mpt3sas_scsih_issue_locked_tm(ioc, le16_to_cpu(mpi_request->FunctionDependent1), 0, 0, - 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 30, - TM_MUTEX_ON); + 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 30); } else - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); } out: @@ -1220,8 +1215,7 @@ _ctl_do_reset(struct MPT3SAS_ADAPTER *ioc, void __user *arg) dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n", ioc->name, __func__)); - retval = mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + retval = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); pr_info(MPT3SAS_FMT "host reset: %s\n", ioc->name, ((!retval) ? "SUCCESS" : "FAILED")); return 0; @@ -1381,7 +1375,6 @@ _ctl_diag_register_2(struct MPT3SAS_ADAPTER *ioc, Mpi2DiagBufferPostRequest_t *mpi_request; Mpi2DiagBufferPostReply_t *mpi_reply; u8 buffer_type; - unsigned long timeleft; u16 smid; u16 ioc_status; u32 ioc_state; @@ -1499,7 +1492,7 @@ _ctl_diag_register_2(struct MPT3SAS_ADAPTER *ioc, init_completion(&ioc->ctl_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, + wait_for_completion_timeout(&ioc->ctl_cmds.done, MPT3_IOCTL_DEFAULT_TIMEOUT*HZ); if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) { @@ -1538,8 +1531,7 @@ _ctl_diag_register_2(struct MPT3SAS_ADAPTER *ioc, issue_host_reset: if (issue_reset) - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); out: @@ -1800,7 +1792,6 @@ mpt3sas_send_diag_release(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type, u16 ioc_status; u32 ioc_state; int rc; - unsigned long timeleft; dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, __func__)); @@ -1848,7 +1839,7 @@ mpt3sas_send_diag_release(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type, init_completion(&ioc->ctl_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, + wait_for_completion_timeout(&ioc->ctl_cmds.done, MPT3_IOCTL_DEFAULT_TIMEOUT*HZ); if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) { @@ -1974,8 +1965,7 @@ _ctl_diag_release(struct MPT3SAS_ADAPTER *ioc, void __user *arg) rc = mpt3sas_send_diag_release(ioc, buffer_type, &issue_reset); if (issue_reset) - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); return rc; } @@ -1995,7 +1985,7 @@ _ctl_diag_read_buffer(struct MPT3SAS_ADAPTER *ioc, void __user *arg) Mpi2DiagBufferPostReply_t *mpi_reply; int rc, i; u8 buffer_type; - unsigned long timeleft, request_size, copy_size; + unsigned long request_size, copy_size; u16 smid; u16 ioc_status; u8 issue_reset = 0; @@ -2116,7 +2106,7 @@ _ctl_diag_read_buffer(struct MPT3SAS_ADAPTER *ioc, void __user *arg) init_completion(&ioc->ctl_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, + wait_for_completion_timeout(&ioc->ctl_cmds.done, MPT3_IOCTL_DEFAULT_TIMEOUT*HZ); if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) { @@ -2155,8 +2145,7 @@ _ctl_diag_read_buffer(struct MPT3SAS_ADAPTER *ioc, void __user *arg) issue_host_reset: if (issue_reset) - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); out: @@ -2352,7 +2341,7 @@ out_unlock_pciaccess: * @cmd - ioctl opcode * @arg - */ -long +static long _ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { long ret; @@ -2372,7 +2361,7 @@ _ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) * @cmd - ioctl opcode * @arg - */ -long +static long _ctl_mpt2_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { long ret; @@ -2392,7 +2381,7 @@ _ctl_mpt2_ioctl(struct file *file, unsigned int cmd, unsigned long arg) * * This routine handles 32 bit applications in 64bit os. */ -long +static long _ctl_ioctl_compat(struct file *file, unsigned cmd, unsigned long arg) { long ret; @@ -2410,7 +2399,7 @@ _ctl_ioctl_compat(struct file *file, unsigned cmd, unsigned long arg) * * This routine handles 32 bit applications in 64bit os. */ -long +static long _ctl_mpt2_ioctl_compat(struct file *file, unsigned cmd, unsigned long arg) { long ret; diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index cd91a684c945..209a969a979d 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -1195,7 +1195,7 @@ _scsih_scsi_lookup_find_by_lun(struct MPT3SAS_ADAPTER *ioc, int id, * * Returns queue depth. */ -int +static int scsih_change_queue_depth(struct scsi_device *sdev, int qdepth) { struct Scsi_Host *shost = sdev->host; @@ -1244,7 +1244,7 @@ scsih_change_queue_depth(struct scsi_device *sdev, int qdepth) * Returns 0 if ok. Any other return is assumed to be an error and * the device is ignored. */ -int +static int scsih_target_alloc(struct scsi_target *starget) { struct Scsi_Host *shost = dev_to_shost(&starget->dev); @@ -1311,7 +1311,7 @@ scsih_target_alloc(struct scsi_target *starget) * * Returns nothing. */ -void +static void scsih_target_destroy(struct scsi_target *starget) { struct Scsi_Host *shost = dev_to_shost(&starget->dev); @@ -1320,7 +1320,6 @@ scsih_target_destroy(struct scsi_target *starget) struct _sas_device *sas_device; struct _raid_device *raid_device; unsigned long flags; - struct sas_rphy *rphy; sas_target_priv_data = starget->hostdata; if (!sas_target_priv_data) @@ -1339,7 +1338,6 @@ scsih_target_destroy(struct scsi_target *starget) } spin_lock_irqsave(&ioc->sas_device_lock, flags); - rphy = dev_to_rphy(starget->dev.parent); sas_device = __mpt3sas_get_sdev_from_target(ioc, sas_target_priv_data); if (sas_device && (sas_device->starget == starget) && (sas_device->id == starget->id) && @@ -1369,7 +1367,7 @@ scsih_target_destroy(struct scsi_target *starget) * Returns 0 if ok. Any other return is assumed to be an error and * the device is ignored. */ -int +static int scsih_slave_alloc(struct scsi_device *sdev) { struct Scsi_Host *shost; @@ -1434,7 +1432,7 @@ scsih_slave_alloc(struct scsi_device *sdev) * * Returns nothing. */ -void +static void scsih_slave_destroy(struct scsi_device *sdev) { struct MPT3SAS_TARGET *sas_target_priv_data; @@ -1527,7 +1525,7 @@ _scsih_display_sata_capabilities(struct MPT3SAS_ADAPTER *ioc, * scsih_is_raid - return boolean indicating device is raid volume * @dev the device struct object */ -int +static int scsih_is_raid(struct device *dev) { struct scsi_device *sdev = to_scsi_device(dev); @@ -1542,7 +1540,7 @@ scsih_is_raid(struct device *dev) * scsih_get_resync - get raid volume resync percent complete * @dev the device struct object */ -void +static void scsih_get_resync(struct device *dev) { struct scsi_device *sdev = to_scsi_device(dev); @@ -1603,7 +1601,7 @@ scsih_get_resync(struct device *dev) * scsih_get_state - get raid volume level * @dev the device struct object */ -void +static void scsih_get_state(struct device *dev) { struct scsi_device *sdev = to_scsi_device(dev); @@ -1805,7 +1803,7 @@ _scsih_enable_tlr(struct MPT3SAS_ADAPTER *ioc, struct scsi_device *sdev) * Returns 0 if ok. Any other return is assumed to be an error and * the device is ignored. */ -int +static int scsih_slave_configure(struct scsi_device *sdev) { struct Scsi_Host *shost = sdev->host; @@ -2021,7 +2019,7 @@ scsih_slave_configure(struct scsi_device *sdev) * * Return nothing. */ -int +static int scsih_bios_param(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int params[]) { @@ -2201,7 +2199,6 @@ mpt3sas_scsih_clear_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle) * @type: MPI2_SCSITASKMGMT_TASKTYPE__XXX (defined in mpi2_init.h) * @smid_task: smid assigned to the task * @timeout: timeout in seconds - * @m_type: TM_MUTEX_ON or TM_MUTEX_OFF * Context: user * * A generic API for sending task management requests to firmware. @@ -2212,60 +2209,51 @@ mpt3sas_scsih_clear_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle) */ int mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, uint channel, - uint id, uint lun, u8 type, u16 smid_task, ulong timeout, - enum mutex_type m_type) + uint id, uint lun, u8 type, u16 smid_task, ulong timeout) { Mpi2SCSITaskManagementRequest_t *mpi_request; Mpi2SCSITaskManagementReply_t *mpi_reply; u16 smid = 0; u32 ioc_state; - unsigned long timeleft; struct scsiio_tracker *scsi_lookup = NULL; int rc; u16 msix_task = 0; - if (m_type == TM_MUTEX_ON) - mutex_lock(&ioc->tm_cmds.mutex); + lockdep_assert_held(&ioc->tm_cmds.mutex); + if (ioc->tm_cmds.status != MPT3_CMD_NOT_USED) { pr_info(MPT3SAS_FMT "%s: tm_cmd busy!!!\n", __func__, ioc->name); - rc = FAILED; - goto err_out; + return FAILED; } if (ioc->shost_recovery || ioc->remove_host || ioc->pci_error_recovery) { pr_info(MPT3SAS_FMT "%s: host reset in progress!\n", __func__, ioc->name); - rc = FAILED; - goto err_out; + return FAILED; } ioc_state = mpt3sas_base_get_iocstate(ioc, 0); if (ioc_state & MPI2_DOORBELL_USED) { dhsprintk(ioc, pr_info(MPT3SAS_FMT "unexpected doorbell active!\n", ioc->name)); - rc = mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); - rc = (!rc) ? SUCCESS : FAILED; - goto err_out; + rc = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); + return (!rc) ? SUCCESS : FAILED; } if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { mpt3sas_base_fault_info(ioc, ioc_state & MPI2_DOORBELL_DATA_MASK); - rc = mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); - rc = (!rc) ? SUCCESS : FAILED; - goto err_out; + rc = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); + return (!rc) ? SUCCESS : FAILED; } smid = mpt3sas_base_get_smid_hpr(ioc, ioc->tm_cb_idx); if (!smid) { pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", ioc->name, __func__); - rc = FAILED; - goto err_out; + return FAILED; } if (type == MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK) @@ -2292,19 +2280,17 @@ mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, uint channel, else msix_task = 0; mpt3sas_base_put_smid_hi_priority(ioc, smid, msix_task); - timeleft = wait_for_completion_timeout(&ioc->tm_cmds.done, timeout*HZ); + wait_for_completion_timeout(&ioc->tm_cmds.done, timeout*HZ); if (!(ioc->tm_cmds.status & MPT3_CMD_COMPLETE)) { pr_err(MPT3SAS_FMT "%s: timeout\n", ioc->name, __func__); _debug_dump_mf(mpi_request, sizeof(Mpi2SCSITaskManagementRequest_t)/4); if (!(ioc->tm_cmds.status & MPT3_CMD_RESET)) { - rc = mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + rc = mpt3sas_base_hard_reset_handler(ioc, + FORCE_BIG_HAMMER); rc = (!rc) ? SUCCESS : FAILED; - ioc->tm_cmds.status = MPT3_CMD_NOT_USED; - mpt3sas_scsih_clear_tm_flag(ioc, handle); - goto err_out; + goto out; } } @@ -2356,17 +2342,23 @@ mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, uint channel, break; } +out: mpt3sas_scsih_clear_tm_flag(ioc, handle); ioc->tm_cmds.status = MPT3_CMD_NOT_USED; - if (m_type == TM_MUTEX_ON) - mutex_unlock(&ioc->tm_cmds.mutex); - return rc; +} - err_out: - if (m_type == TM_MUTEX_ON) - mutex_unlock(&ioc->tm_cmds.mutex); - return rc; +int mpt3sas_scsih_issue_locked_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, + uint channel, uint id, uint lun, u8 type, u16 smid_task, ulong timeout) +{ + int ret; + + mutex_lock(&ioc->tm_cmds.mutex); + ret = mpt3sas_scsih_issue_tm(ioc, handle, channel, id, lun, type, + smid_task, timeout); + mutex_unlock(&ioc->tm_cmds.mutex); + + return ret; } /** @@ -2439,7 +2431,7 @@ _scsih_tm_display_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd) * * Returns SUCCESS if command aborted else FAILED */ -int +static int scsih_abort(struct scsi_cmnd *scmd) { struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host); @@ -2482,9 +2474,9 @@ scsih_abort(struct scsi_cmnd *scmd) mpt3sas_halt_firmware(ioc); handle = sas_device_priv_data->sas_target->handle; - r = mpt3sas_scsih_issue_tm(ioc, handle, scmd->device->channel, + r = mpt3sas_scsih_issue_locked_tm(ioc, handle, scmd->device->channel, scmd->device->id, scmd->device->lun, - MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30, TM_MUTEX_ON); + MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30); out: sdev_printk(KERN_INFO, scmd->device, "task abort: %s scmd(%p)\n", @@ -2498,7 +2490,7 @@ scsih_abort(struct scsi_cmnd *scmd) * * Returns SUCCESS if command aborted else FAILED */ -int +static int scsih_dev_reset(struct scsi_cmnd *scmd) { struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host); @@ -2541,9 +2533,9 @@ scsih_dev_reset(struct scsi_cmnd *scmd) goto out; } - r = mpt3sas_scsih_issue_tm(ioc, handle, scmd->device->channel, + r = mpt3sas_scsih_issue_locked_tm(ioc, handle, scmd->device->channel, scmd->device->id, scmd->device->lun, - MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, 0, 30, TM_MUTEX_ON); + MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, 0, 30); out: sdev_printk(KERN_INFO, scmd->device, "device reset: %s scmd(%p)\n", @@ -2561,7 +2553,7 @@ scsih_dev_reset(struct scsi_cmnd *scmd) * * Returns SUCCESS if command aborted else FAILED */ -int +static int scsih_target_reset(struct scsi_cmnd *scmd) { struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host); @@ -2603,9 +2595,9 @@ scsih_target_reset(struct scsi_cmnd *scmd) goto out; } - r = mpt3sas_scsih_issue_tm(ioc, handle, scmd->device->channel, + r = mpt3sas_scsih_issue_locked_tm(ioc, handle, scmd->device->channel, scmd->device->id, 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, - 30, TM_MUTEX_ON); + 30); out: starget_printk(KERN_INFO, starget, "target reset: %s scmd(%p)\n", @@ -2624,7 +2616,7 @@ scsih_target_reset(struct scsi_cmnd *scmd) * * Returns SUCCESS if command aborted else FAILED */ -int +static int scsih_host_reset(struct scsi_cmnd *scmd) { struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host); @@ -2641,8 +2633,7 @@ scsih_host_reset(struct scsi_cmnd *scmd) goto out; } - retval = mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + retval = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); r = (retval < 0) ? FAILED : SUCCESS; out: pr_info(MPT3SAS_FMT "host reset: %s scmd(%p)\n", @@ -3455,7 +3446,7 @@ _scsih_tm_volume_tr_complete(struct MPT3SAS_ADAPTER *ioc, u16 smid, * * Context - processed in interrupt context. */ -void +static void _scsih_issue_delayed_event_ack(struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 event, u32 event_context) { @@ -3494,7 +3485,7 @@ _scsih_issue_delayed_event_ack(struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 event, * * Context - processed in interrupt context. */ -void +static void _scsih_issue_delayed_sas_io_unit_ctrl(struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 handle) { @@ -4032,7 +4023,7 @@ _scsih_eedp_error_handling(struct scsi_cmnd *scmd, u16 ioc_status) * SCSI_MLQUEUE_DEVICE_BUSY if the device queue is full, or * SCSI_MLQUEUE_HOST_BUSY if the entire host queue is full */ -int +static int scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) { struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); @@ -4701,7 +4692,7 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) le16_to_cpu(mpi_reply->DevHandle)); mpt3sas_trigger_scsi(ioc, data.skey, data.asc, data.ascq); - if (!(ioc->logging_level & MPT_DEBUG_REPLY) && + if ((ioc->logging_level & MPT_DEBUG_REPLY) && ((scmd->sense_buffer[2] == UNIT_ATTENTION) || (scmd->sense_buffer[2] == MEDIUM_ERROR) || (scmd->sense_buffer[2] == HARDWARE_ERROR))) @@ -5380,8 +5371,9 @@ _scsih_check_device(struct MPT3SAS_ADAPTER *ioc, MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) { sas_device->enclosure_level = le16_to_cpu(sas_device_pg0.EnclosureLevel); - memcpy(&sas_device->connector_name[0], - &sas_device_pg0.ConnectorName[0], 4); + memcpy(sas_device->connector_name, + sas_device_pg0.ConnectorName, 4); + sas_device->connector_name[4] = '\0'; } else { sas_device->enclosure_level = 0; sas_device->connector_name[0] = '\0'; @@ -5508,8 +5500,9 @@ _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num, if (sas_device_pg0.Flags & MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) { sas_device->enclosure_level = le16_to_cpu(sas_device_pg0.EnclosureLevel); - memcpy(&sas_device->connector_name[0], - &sas_device_pg0.ConnectorName[0], 4); + memcpy(sas_device->connector_name, + sas_device_pg0.ConnectorName, 4); + sas_device->connector_name[4] = '\0'; } else { sas_device->enclosure_level = 0; sas_device->connector_name[0] = '\0'; @@ -6087,8 +6080,7 @@ _scsih_sas_broadcast_primitive_event(struct MPT3SAS_ADAPTER *ioc, spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); r = mpt3sas_scsih_issue_tm(ioc, handle, 0, 0, lun, - MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30, - TM_MUTEX_OFF); + MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30); if (r == FAILED) { sdev_printk(KERN_WARNING, sdev, "mpt3sas_scsih_issue_tm: FAILED when sending " @@ -6128,8 +6120,8 @@ _scsih_sas_broadcast_primitive_event(struct MPT3SAS_ADAPTER *ioc, goto out_no_lock; r = mpt3sas_scsih_issue_tm(ioc, handle, sdev->channel, sdev->id, - sdev->lun, MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30, - TM_MUTEX_OFF); + sdev->lun, MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, + 30); if (r == FAILED) { sdev_printk(KERN_WARNING, sdev, "mpt3sas_scsih_issue_tm: ABORT_TASK: FAILED : " @@ -6297,8 +6289,7 @@ _scsih_ir_fastpath(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phys_disk_num) mutex_unlock(&ioc->scsih_cmds.mutex); if (issue_reset) - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); return rc; } @@ -6311,11 +6302,10 @@ _scsih_ir_fastpath(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phys_disk_num) static void _scsih_reprobe_lun(struct scsi_device *sdev, void *no_uld_attach) { - int rc; sdev->no_uld_attach = no_uld_attach ? 1 : 0; sdev_printk(KERN_INFO, sdev, "%s raid component\n", sdev->no_uld_attach ? "hidding" : "exposing"); - rc = scsi_device_reprobe(sdev); + WARN_ON(scsi_device_reprobe(sdev)); } /** @@ -8137,7 +8127,7 @@ _scsih_ir_shutdown(struct MPT3SAS_ADAPTER *ioc) * Routine called when unloading the driver. * Return nothing. */ -void scsih_remove(struct pci_dev *pdev) +static void scsih_remove(struct pci_dev *pdev) { struct Scsi_Host *shost = pci_get_drvdata(pdev); struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); @@ -8210,7 +8200,7 @@ void scsih_remove(struct pci_dev *pdev) * * Return nothing. */ -void +static void scsih_shutdown(struct pci_dev *pdev) { struct Scsi_Host *shost = pci_get_drvdata(pdev); @@ -8451,7 +8441,7 @@ _scsih_probe_devices(struct MPT3SAS_ADAPTER *ioc) * of scanning the entire bus. In our implemention, we will kick off * firmware discovery. */ -void +static void scsih_scan_start(struct Scsi_Host *shost) { struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); @@ -8478,7 +8468,7 @@ scsih_scan_start(struct Scsi_Host *shost) * scsi_host and the elapsed time of the scan in jiffies. In our implemention, * we wait for firmware discovery to complete, then return 1. */ -int +static int scsih_scan_finished(struct Scsi_Host *shost, unsigned long time) { struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); @@ -8608,7 +8598,7 @@ static struct raid_function_template mpt3sas_raid_functions = { * MPI25_VERSION for SAS 3.0 HBA devices, and * MPI26 VERSION for Cutlass & Invader SAS 3.0 HBA devices */ -u16 +static u16 _scsih_determine_hba_mpi_version(struct pci_dev *pdev) { @@ -8660,7 +8650,7 @@ _scsih_determine_hba_mpi_version(struct pci_dev *pdev) * * Returns 0 success, anything else error. */ -int +static int _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct MPT3SAS_ADAPTER *ioc; @@ -8869,7 +8859,7 @@ out_add_shost_fail: * * Returns 0 success, anything else error. */ -int +static int scsih_suspend(struct pci_dev *pdev, pm_message_t state) { struct Scsi_Host *shost = pci_get_drvdata(pdev); @@ -8896,7 +8886,7 @@ scsih_suspend(struct pci_dev *pdev, pm_message_t state) * * Returns 0 success, anything else error. */ -int +static int scsih_resume(struct pci_dev *pdev) { struct Scsi_Host *shost = pci_get_drvdata(pdev); @@ -8916,7 +8906,7 @@ scsih_resume(struct pci_dev *pdev) if (r) return r; - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, SOFT_RESET); + mpt3sas_base_hard_reset_handler(ioc, SOFT_RESET); scsi_unblock_requests(shost); mpt3sas_base_start_watchdog(ioc); return 0; @@ -8933,7 +8923,7 @@ scsih_resume(struct pci_dev *pdev) * Return value: * PCI_ERS_RESULT_NEED_RESET or PCI_ERS_RESULT_DISCONNECT */ -pci_ers_result_t +static pci_ers_result_t scsih_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) { struct Scsi_Host *shost = pci_get_drvdata(pdev); @@ -8970,7 +8960,7 @@ scsih_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) * code after the PCI slot has been reset, just before we * should resume normal operations. */ -pci_ers_result_t +static pci_ers_result_t scsih_pci_slot_reset(struct pci_dev *pdev) { struct Scsi_Host *shost = pci_get_drvdata(pdev); @@ -8987,8 +8977,7 @@ scsih_pci_slot_reset(struct pci_dev *pdev) if (rc) return PCI_ERS_RESULT_DISCONNECT; - rc = mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + rc = mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); pr_warn(MPT3SAS_FMT "hard reset: %s\n", ioc->name, (rc == 0) ? "success" : "failed"); @@ -9007,7 +8996,7 @@ scsih_pci_slot_reset(struct pci_dev *pdev) * OK to resume normal operation. Use completion to allow * halted scsi ops to resume. */ -void +static void scsih_pci_resume(struct pci_dev *pdev) { struct Scsi_Host *shost = pci_get_drvdata(pdev); @@ -9024,7 +9013,7 @@ scsih_pci_resume(struct pci_dev *pdev) * scsih_pci_mmio_enabled - Enable MMIO and dump debug registers * @pdev: pointer to PCI device */ -pci_ers_result_t +static pci_ers_result_t scsih_pci_mmio_enabled(struct pci_dev *pdev) { struct Scsi_Host *shost = pci_get_drvdata(pdev); @@ -9152,7 +9141,7 @@ static struct pci_driver mpt3sas_driver = { * * Returns 0 success, anything else error. */ -int +static int scsih_init(void) { mpt2_ids = 0; @@ -9202,7 +9191,7 @@ scsih_init(void) * * Returns 0 success, anything else error. */ -void +static void scsih_exit(void) { diff --git a/drivers/scsi/mpt3sas/mpt3sas_transport.c b/drivers/scsi/mpt3sas/mpt3sas_transport.c index ff93286bc32f..b74faf1a69b2 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_transport.c +++ b/drivers/scsi/mpt3sas/mpt3sas_transport.c @@ -300,7 +300,6 @@ _transport_expander_report_manufacture(struct MPT3SAS_ADAPTER *ioc, int rc; u16 smid; u32 ioc_state; - unsigned long timeleft; void *psge; u8 issue_reset = 0; void *data_out = NULL; @@ -394,8 +393,7 @@ _transport_expander_report_manufacture(struct MPT3SAS_ADAPTER *ioc, ioc->name, (unsigned long long)sas_address)); init_completion(&ioc->transport_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, - 10*HZ); + wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ); if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) { pr_err(MPT3SAS_FMT "%s: timeout\n", @@ -446,8 +444,7 @@ _transport_expander_report_manufacture(struct MPT3SAS_ADAPTER *ioc, issue_host_reset: if (issue_reset) - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); out: ioc->transport_cmds.status = MPT3_CMD_NOT_USED; if (data_out) @@ -1107,7 +1104,6 @@ _transport_get_expander_phy_error_log(struct MPT3SAS_ADAPTER *ioc, int rc; u16 smid; u32 ioc_state; - unsigned long timeleft; void *psge; u8 issue_reset = 0; void *data_out = NULL; @@ -1203,8 +1199,7 @@ _transport_get_expander_phy_error_log(struct MPT3SAS_ADAPTER *ioc, phy->number)); init_completion(&ioc->transport_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, - 10*HZ); + wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ); if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) { pr_err(MPT3SAS_FMT "%s: timeout\n", @@ -1253,8 +1248,7 @@ _transport_get_expander_phy_error_log(struct MPT3SAS_ADAPTER *ioc, issue_host_reset: if (issue_reset) - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); out: ioc->transport_cmds.status = MPT3_CMD_NOT_USED; if (data_out) @@ -1421,7 +1415,6 @@ _transport_expander_phy_control(struct MPT3SAS_ADAPTER *ioc, int rc; u16 smid; u32 ioc_state; - unsigned long timeleft; void *psge; u8 issue_reset = 0; void *data_out = NULL; @@ -1522,8 +1515,7 @@ _transport_expander_phy_control(struct MPT3SAS_ADAPTER *ioc, phy->number, phy_operation)); init_completion(&ioc->transport_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, - 10*HZ); + wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ); if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) { pr_err(MPT3SAS_FMT "%s: timeout\n", @@ -1564,8 +1556,7 @@ _transport_expander_phy_control(struct MPT3SAS_ADAPTER *ioc, issue_host_reset: if (issue_reset) - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); out: ioc->transport_cmds.status = MPT3_CMD_NOT_USED; if (data_out) @@ -1899,7 +1890,6 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, int rc; u16 smid; u32 ioc_state; - unsigned long timeleft; void *psge; u8 issue_reset = 0; dma_addr_t dma_addr_in = 0; @@ -2043,8 +2033,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, init_completion(&ioc->transport_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, - 10*HZ); + wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ); if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) { pr_err(MPT3SAS_FMT "%s : timeout\n", @@ -2103,8 +2092,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, issue_host_reset: if (issue_reset) { - mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); + mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); rc = -ETIMEDOUT; } diff --git a/drivers/scsi/mvsas/mv_64xx.c b/drivers/scsi/mvsas/mv_64xx.c index 8bb06995adfb..b757d389e32f 100644 --- a/drivers/scsi/mvsas/mv_64xx.c +++ b/drivers/scsi/mvsas/mv_64xx.c @@ -136,7 +136,8 @@ static void mvs_64xx_phy_reset(struct mvs_info *mvi, u32 phy_id, int hard) } } -void mvs_64xx_clear_srs_irq(struct mvs_info *mvi, u8 reg_set, u8 clear_all) +static void +mvs_64xx_clear_srs_irq(struct mvs_info *mvi, u8 reg_set, u8 clear_all) { void __iomem *regs = mvi->regs; u32 tmp; @@ -563,7 +564,7 @@ static u8 mvs_64xx_assign_reg_set(struct mvs_info *mvi, u8 *tfs) return MVS_ID_NOT_MAPPED; } -void mvs_64xx_make_prd(struct scatterlist *scatter, int nr, void *prd) +static void mvs_64xx_make_prd(struct scatterlist *scatter, int nr, void *prd) { int i; struct scatterlist *sg; @@ -633,7 +634,7 @@ static void mvs_64xx_phy_work_around(struct mvs_info *mvi, int i) mvs_write_port_vsr_data(mvi, i, tmp); } -void mvs_64xx_phy_set_link_rate(struct mvs_info *mvi, u32 phy_id, +static void mvs_64xx_phy_set_link_rate(struct mvs_info *mvi, u32 phy_id, struct sas_phy_linkrates *rates) { u32 lrmin = 0, lrmax = 0; @@ -668,20 +669,20 @@ static void mvs_64xx_clear_active_cmds(struct mvs_info *mvi) } -u32 mvs_64xx_spi_read_data(struct mvs_info *mvi) +static u32 mvs_64xx_spi_read_data(struct mvs_info *mvi) { void __iomem *regs = mvi->regs_ex; return ior32(SPI_DATA_REG_64XX); } -void mvs_64xx_spi_write_data(struct mvs_info *mvi, u32 data) +static void mvs_64xx_spi_write_data(struct mvs_info *mvi, u32 data) { void __iomem *regs = mvi->regs_ex; iow32(SPI_DATA_REG_64XX, data); } -int mvs_64xx_spi_buildcmd(struct mvs_info *mvi, +static int mvs_64xx_spi_buildcmd(struct mvs_info *mvi, u32 *dwCmd, u8 cmd, u8 read, @@ -705,7 +706,7 @@ int mvs_64xx_spi_buildcmd(struct mvs_info *mvi, } -int mvs_64xx_spi_issuecmd(struct mvs_info *mvi, u32 cmd) +static int mvs_64xx_spi_issuecmd(struct mvs_info *mvi, u32 cmd) { void __iomem *regs = mvi->regs_ex; int retry; @@ -720,7 +721,7 @@ int mvs_64xx_spi_issuecmd(struct mvs_info *mvi, u32 cmd) return 0; } -int mvs_64xx_spi_waitdataready(struct mvs_info *mvi, u32 timeout) +static int mvs_64xx_spi_waitdataready(struct mvs_info *mvi, u32 timeout) { void __iomem *regs = mvi->regs_ex; u32 i, dwTmp; @@ -735,7 +736,7 @@ int mvs_64xx_spi_waitdataready(struct mvs_info *mvi, u32 timeout) return -1; } -void mvs_64xx_fix_dma(struct mvs_info *mvi, u32 phy_mask, +static void mvs_64xx_fix_dma(struct mvs_info *mvi, u32 phy_mask, int buf_len, int from, void *prd) { int i; diff --git a/drivers/scsi/mvsas/mv_94xx.c b/drivers/scsi/mvsas/mv_94xx.c index f6fc4a705924..4c57d9abce7b 100644 --- a/drivers/scsi/mvsas/mv_94xx.c +++ b/drivers/scsi/mvsas/mv_94xx.c @@ -48,8 +48,8 @@ static void mvs_94xx_detect_porttype(struct mvs_info *mvi, int i) } } -void set_phy_tuning(struct mvs_info *mvi, int phy_id, - struct phy_tuning phy_tuning) +static void set_phy_tuning(struct mvs_info *mvi, int phy_id, + struct phy_tuning phy_tuning) { u32 tmp, setting_0 = 0, setting_1 = 0; u8 i; @@ -110,8 +110,8 @@ void set_phy_tuning(struct mvs_info *mvi, int phy_id, } } -void set_phy_ffe_tuning(struct mvs_info *mvi, int phy_id, - struct ffe_control ffe) +static void set_phy_ffe_tuning(struct mvs_info *mvi, int phy_id, + struct ffe_control ffe) { u32 tmp; @@ -177,7 +177,7 @@ void set_phy_ffe_tuning(struct mvs_info *mvi, int phy_id, } /*Notice: this function must be called when phy is disabled*/ -void set_phy_rate(struct mvs_info *mvi, int phy_id, u8 rate) +static void set_phy_rate(struct mvs_info *mvi, int phy_id, u8 rate) { union reg_phy_cfg phy_cfg, phy_cfg_tmp; mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_MODE2); @@ -679,7 +679,8 @@ static void mvs_94xx_command_active(struct mvs_info *mvi, u32 slot_idx) } } -void mvs_94xx_clear_srs_irq(struct mvs_info *mvi, u8 reg_set, u8 clear_all) +static void +mvs_94xx_clear_srs_irq(struct mvs_info *mvi, u8 reg_set, u8 clear_all) { void __iomem *regs = mvi->regs; u32 tmp; @@ -906,8 +907,8 @@ static void mvs_94xx_fix_phy_info(struct mvs_info *mvi, int i, } -void mvs_94xx_phy_set_link_rate(struct mvs_info *mvi, u32 phy_id, - struct sas_phy_linkrates *rates) +static void mvs_94xx_phy_set_link_rate(struct mvs_info *mvi, u32 phy_id, + struct sas_phy_linkrates *rates) { u32 lrmax = 0; u32 tmp; @@ -936,25 +937,25 @@ static void mvs_94xx_clear_active_cmds(struct mvs_info *mvi) } -u32 mvs_94xx_spi_read_data(struct mvs_info *mvi) +static u32 mvs_94xx_spi_read_data(struct mvs_info *mvi) { void __iomem *regs = mvi->regs_ex - 0x10200; return mr32(SPI_RD_DATA_REG_94XX); } -void mvs_94xx_spi_write_data(struct mvs_info *mvi, u32 data) +static void mvs_94xx_spi_write_data(struct mvs_info *mvi, u32 data) { void __iomem *regs = mvi->regs_ex - 0x10200; mw32(SPI_RD_DATA_REG_94XX, data); } -int mvs_94xx_spi_buildcmd(struct mvs_info *mvi, - u32 *dwCmd, - u8 cmd, - u8 read, - u8 length, - u32 addr +static int mvs_94xx_spi_buildcmd(struct mvs_info *mvi, + u32 *dwCmd, + u8 cmd, + u8 read, + u8 length, + u32 addr ) { void __iomem *regs = mvi->regs_ex - 0x10200; @@ -974,7 +975,7 @@ int mvs_94xx_spi_buildcmd(struct mvs_info *mvi, } -int mvs_94xx_spi_issuecmd(struct mvs_info *mvi, u32 cmd) +static int mvs_94xx_spi_issuecmd(struct mvs_info *mvi, u32 cmd) { void __iomem *regs = mvi->regs_ex - 0x10200; mw32(SPI_CTRL_REG_94XX, cmd | SPI_CTRL_SpiStart_94XX); @@ -982,7 +983,7 @@ int mvs_94xx_spi_issuecmd(struct mvs_info *mvi, u32 cmd) return 0; } -int mvs_94xx_spi_waitdataready(struct mvs_info *mvi, u32 timeout) +static int mvs_94xx_spi_waitdataready(struct mvs_info *mvi, u32 timeout) { void __iomem *regs = mvi->regs_ex - 0x10200; u32 i, dwTmp; @@ -997,8 +998,8 @@ int mvs_94xx_spi_waitdataready(struct mvs_info *mvi, u32 timeout) return -1; } -void mvs_94xx_fix_dma(struct mvs_info *mvi, u32 phy_mask, - int buf_len, int from, void *prd) +static void mvs_94xx_fix_dma(struct mvs_info *mvi, u32 phy_mask, + int buf_len, int from, void *prd) { int i; struct mvs_prd *buf_prd = prd; diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c index 5b9fcff6cd94..86eb19902bac 100644 --- a/drivers/scsi/mvsas/mv_sas.c +++ b/drivers/scsi/mvsas/mv_sas.c @@ -74,7 +74,7 @@ void mvs_tag_init(struct mvs_info *mvi) mvs_tag_clear(mvi, i); } -struct mvs_info *mvs_find_dev_mvi(struct domain_device *dev) +static struct mvs_info *mvs_find_dev_mvi(struct domain_device *dev) { unsigned long i = 0, j = 0, hi = 0; struct sas_ha_struct *sha = dev->port->ha; @@ -102,7 +102,7 @@ struct mvs_info *mvs_find_dev_mvi(struct domain_device *dev) } -int mvs_find_dev_phyno(struct domain_device *dev, int *phyno) +static int mvs_find_dev_phyno(struct domain_device *dev, int *phyno) { unsigned long i = 0, j = 0, n = 0, num = 0; struct mvs_device *mvi_dev = (struct mvs_device *)dev->lldd_dev; @@ -1158,7 +1158,7 @@ void mvs_port_deformed(struct asd_sas_phy *sas_phy) mvs_port_notify_deformed(sas_phy, 1); } -struct mvs_device *mvs_alloc_dev(struct mvs_info *mvi) +static struct mvs_device *mvs_alloc_dev(struct mvs_info *mvi) { u32 dev; for (dev = 0; dev < MVS_MAX_DEVICES; dev++) { @@ -1175,7 +1175,7 @@ struct mvs_device *mvs_alloc_dev(struct mvs_info *mvi) return NULL; } -void mvs_free_dev(struct mvs_device *mvi_dev) +static void mvs_free_dev(struct mvs_device *mvi_dev) { u32 id = mvi_dev->device_id; memset(mvi_dev, 0, sizeof(*mvi_dev)); @@ -1185,7 +1185,7 @@ void mvs_free_dev(struct mvs_device *mvi_dev) mvi_dev->taskfileset = MVS_ID_NOT_MAPPED; } -int mvs_dev_found_notify(struct domain_device *dev, int lock) +static int mvs_dev_found_notify(struct domain_device *dev, int lock) { unsigned long flags = 0; int res = 0; @@ -1241,7 +1241,7 @@ int mvs_dev_found(struct domain_device *dev) return mvs_dev_found_notify(dev, 1); } -void mvs_dev_gone_notify(struct domain_device *dev) +static void mvs_dev_gone_notify(struct domain_device *dev) { unsigned long flags = 0; struct mvs_device *mvi_dev = dev->lldd_dev; @@ -1611,7 +1611,7 @@ static int mvs_sata_done(struct mvs_info *mvi, struct sas_task *task, return stat; } -void mvs_set_sense(u8 *buffer, int len, int d_sense, +static void mvs_set_sense(u8 *buffer, int len, int d_sense, int key, int asc, int ascq) { memset(buffer, 0, len); @@ -1650,7 +1650,7 @@ void mvs_set_sense(u8 *buffer, int len, int d_sense, return; } -void mvs_fill_ssp_resp_iu(struct ssp_response_iu *iu, +static void mvs_fill_ssp_resp_iu(struct ssp_response_iu *iu, u8 key, u8 asc, u8 asc_q) { iu->datapres = 2; diff --git a/drivers/scsi/pas16.c b/drivers/scsi/pas16.c deleted file mode 100644 index 2f689ae7a803..000000000000 --- a/drivers/scsi/pas16.c +++ /dev/null @@ -1,565 +0,0 @@ -/* - * This driver adapted from Drew Eckhardt's Trantor T128 driver - * - * Copyright 1993, Drew Eckhardt - * Visionary Computing - * (Unix and Linux consulting and custom programming) - * drew@colorado.edu - * +1 (303) 666-5836 - * - * ( Based on T128 - DISTRIBUTION RELEASE 3. ) - * - * Modified to work with the Pro Audio Spectrum/Studio 16 - * by John Weidman. - * - * - * For more information, please consult - * - * Media Vision - * (510) 770-8600 - * (800) 348-7116 - */ - -/* - * The card is detected and initialized in one of several ways : - * 1. Autoprobe (default) - There are many different models of - * the Pro Audio Spectrum/Studio 16, and I only have one of - * them, so this may require a little tweaking. An interrupt - * is triggered to autoprobe for the interrupt line. Note: - * with the newer model boards, the interrupt is set via - * software after reset using the default_irq for the - * current board number. - * - * 2. With command line overrides - pas16=port,irq may be - * used on the LILO command line to override the defaults. - * - * 3. With the PAS16_OVERRIDE compile time define. This is - * specified as an array of address, irq tuples. Ie, for - * one board at the default 0x388 address, IRQ10, I could say - * -DPAS16_OVERRIDE={{0x388, 10}} - * NOTE: Untested. - * - * 4. When included as a module, with arguments passed on the command line: - * pas16_irq=xx the interrupt - * pas16_addr=xx the port - * e.g. "modprobe pas16 pas16_addr=0x388 pas16_irq=5" - * - * Note that if the override methods are used, place holders must - * be specified for other boards in the system. - * - * - * Configuration notes : - * The current driver does not support interrupt sharing with the - * sound portion of the card. If you use the same irq for the - * scsi port and sound you will have problems. Either use - * a different irq for the scsi port or don't use interrupts - * for the scsi port. - * - * If you have problems with your card not being recognized, use - * the LILO command line override. Try to get it recognized without - * interrupts. Ie, for a board at the default 0x388 base port, - * boot: linux pas16=0x388,0 - * - * NO_IRQ (0) should be specified for no interrupt, - * IRQ_AUTO (254) to autoprobe for an IRQ line if overridden - * on the command line. - */ - -#include <linux/module.h> - -#include <asm/io.h> -#include <asm/dma.h> -#include <linux/blkdev.h> -#include <linux/interrupt.h> -#include <linux/init.h> - -#include <scsi/scsi_host.h> -#include "pas16.h" -#include "NCR5380.h" - - -static unsigned short pas16_addr; -static int pas16_irq; - - -static const int scsi_irq_translate[] = - { 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 0, 10, 11 }; - -/* The default_irqs array contains values used to set the irq into the - * board via software (as must be done on newer model boards without - * irq jumpers on the board). The first value in the array will be - * assigned to logical board 0, the next to board 1, etc. - */ -static int default_irqs[] __initdata = - { PAS16_DEFAULT_BOARD_1_IRQ, - PAS16_DEFAULT_BOARD_2_IRQ, - PAS16_DEFAULT_BOARD_3_IRQ, - PAS16_DEFAULT_BOARD_4_IRQ - }; - -static struct override { - unsigned short io_port; - int irq; -} overrides -#ifdef PAS16_OVERRIDE - [] __initdata = PAS16_OVERRIDE; -#else - [4] __initdata = {{0,IRQ_AUTO}, {0,IRQ_AUTO}, {0,IRQ_AUTO}, - {0,IRQ_AUTO}}; -#endif - -#define NO_OVERRIDES ARRAY_SIZE(overrides) - -static struct base { - unsigned short io_port; - int noauto; -} bases[] __initdata = - { {PAS16_DEFAULT_BASE_1, 0}, - {PAS16_DEFAULT_BASE_2, 0}, - {PAS16_DEFAULT_BASE_3, 0}, - {PAS16_DEFAULT_BASE_4, 0} - }; - -#define NO_BASES ARRAY_SIZE(bases) - -static const unsigned short pas16_offset[ 8 ] = - { - 0x1c00, /* OUTPUT_DATA_REG */ - 0x1c01, /* INITIATOR_COMMAND_REG */ - 0x1c02, /* MODE_REG */ - 0x1c03, /* TARGET_COMMAND_REG */ - 0x3c00, /* STATUS_REG ro, SELECT_ENABLE_REG wo */ - 0x3c01, /* BUS_AND_STATUS_REG ro, START_DMA_SEND_REG wo */ - 0x3c02, /* INPUT_DATA_REGISTER ro, (N/A on PAS16 ?) - * START_DMA_TARGET_RECEIVE_REG wo - */ - 0x3c03, /* RESET_PARITY_INTERRUPT_REG ro, - * START_DMA_INITIATOR_RECEIVE_REG wo - */ - }; - - -/* - * Function : enable_board( int board_num, unsigned short port ) - * - * Purpose : set address in new model board - * - * Inputs : board_num - logical board number 0-3, port - base address - * - */ - -static void __init - enable_board( int board_num, unsigned short port ) -{ - outb( 0xbc + board_num, MASTER_ADDRESS_PTR ); - outb( port >> 2, MASTER_ADDRESS_PTR ); -} - - - -/* - * Function : init_board( unsigned short port, int irq ) - * - * Purpose : Set the board up to handle the SCSI interface - * - * Inputs : port - base address of the board, - * irq - irq to assign to the SCSI port - * force_irq - set it even if it conflicts with sound driver - * - */ - -static void __init - init_board( unsigned short io_port, int irq, int force_irq ) -{ - unsigned int tmp; - unsigned int pas_irq_code; - - /* Initialize the SCSI part of the board */ - - outb( 0x30, io_port + P_TIMEOUT_COUNTER_REG ); /* Timeout counter */ - outb( 0x01, io_port + P_TIMEOUT_STATUS_REG_OFFSET ); /* Reset TC */ - outb( 0x01, io_port + WAIT_STATE ); /* 1 Wait state */ - - inb(io_port + pas16_offset[RESET_PARITY_INTERRUPT_REG]); - - /* Set the SCSI interrupt pointer without mucking up the sound - * interrupt pointer in the same byte. - */ - pas_irq_code = ( irq < 16 ) ? scsi_irq_translate[irq] : 0; - tmp = inb( io_port + IO_CONFIG_3 ); - - if( (( tmp & 0x0f ) == pas_irq_code) && pas_irq_code > 0 - && !force_irq ) - { - printk( "pas16: WARNING: Can't use same irq as sound " - "driver -- interrupts disabled\n" ); - /* Set up the drive parameters, disable 5380 interrupts */ - outb( 0x4d, io_port + SYS_CONFIG_4 ); - } - else - { - tmp = ( tmp & 0x0f ) | ( pas_irq_code << 4 ); - outb( tmp, io_port + IO_CONFIG_3 ); - - /* Set up the drive parameters and enable 5380 interrupts */ - outb( 0x6d, io_port + SYS_CONFIG_4 ); - } -} - - -/* - * Function : pas16_hw_detect( unsigned short board_num ) - * - * Purpose : determine if a pas16 board is present - * - * Inputs : board_num - logical board number ( 0 - 3 ) - * - * Returns : 0 if board not found, 1 if found. - */ - -static int __init - pas16_hw_detect( unsigned short board_num ) -{ - unsigned char board_rev, tmp; - unsigned short io_port = bases[ board_num ].io_port; - - /* See if we can find a PAS16 board at the address associated - * with this logical board number. - */ - - /* First, attempt to take a newer model board out of reset and - * give it a base address. This shouldn't affect older boards. - */ - enable_board( board_num, io_port ); - - /* Now see if it looks like a PAS16 board */ - board_rev = inb( io_port + PCB_CONFIG ); - - if( board_rev == 0xff ) - return 0; - - tmp = board_rev ^ 0xe0; - - outb( tmp, io_port + PCB_CONFIG ); - tmp = inb( io_port + PCB_CONFIG ); - outb( board_rev, io_port + PCB_CONFIG ); - - if( board_rev != tmp ) /* Not a PAS-16 */ - return 0; - - if( ( inb( io_port + OPERATION_MODE_1 ) & 0x03 ) != 0x03 ) - return 0; /* return if no SCSI interface found */ - - /* Mediavision has some new model boards that return ID bits - * that indicate a SCSI interface, but they're not (LMS). We'll - * put in an additional test to try to weed them out. - */ - - outb(0x01, io_port + WAIT_STATE); /* 1 Wait state */ - outb(0x20, io_port + pas16_offset[MODE_REG]); /* Is it really SCSI? */ - if (inb(io_port + pas16_offset[MODE_REG]) != 0x20) /* Write to a reg. */ - return 0; /* and try to read */ - outb(0x00, io_port + pas16_offset[MODE_REG]); /* it back. */ - if (inb(io_port + pas16_offset[MODE_REG]) != 0x00) - return 0; - - return 1; -} - - -#ifndef MODULE -/* - * Function : pas16_setup(char *str, int *ints) - * - * Purpose : LILO command line initialization of the overrides array, - * - * Inputs : str - unused, ints - array of integer parameters with ints[0] - * equal to the number of ints. - * - */ - -static int __init pas16_setup(char *str) -{ - static int commandline_current; - int i; - int ints[10]; - - get_options(str, ARRAY_SIZE(ints), ints); - if (ints[0] != 2) - printk("pas16_setup : usage pas16=io_port,irq\n"); - else - if (commandline_current < NO_OVERRIDES) { - overrides[commandline_current].io_port = (unsigned short) ints[1]; - overrides[commandline_current].irq = ints[2]; - for (i = 0; i < NO_BASES; ++i) - if (bases[i].io_port == (unsigned short) ints[1]) { - bases[i].noauto = 1; - break; - } - ++commandline_current; - } - return 1; -} - -__setup("pas16=", pas16_setup); -#endif - -/* - * Function : int pas16_detect(struct scsi_host_template * tpnt) - * - * Purpose : detects and initializes PAS16 controllers - * that were autoprobed, overridden on the LILO command line, - * or specified at compile time. - * - * Inputs : tpnt - template for this SCSI adapter. - * - * Returns : 1 if a host adapter was found, 0 if not. - * - */ - -static int __init pas16_detect(struct scsi_host_template *tpnt) -{ - static int current_override; - static unsigned short current_base; - struct Scsi_Host *instance; - unsigned short io_port; - int count; - - if (pas16_addr != 0) { - overrides[0].io_port = pas16_addr; - /* - * This is how we avoid seeing more than - * one host adapter at the same I/O port. - * Cribbed shamelessly from pas16_setup(). - */ - for (count = 0; count < NO_BASES; ++count) - if (bases[count].io_port == pas16_addr) { - bases[count].noauto = 1; - break; - } - } - if (pas16_irq != 0) - overrides[0].irq = pas16_irq; - - for (count = 0; current_override < NO_OVERRIDES; ++current_override) { - io_port = 0; - - if (overrides[current_override].io_port) - { - io_port = overrides[current_override].io_port; - enable_board( current_override, io_port ); - init_board( io_port, overrides[current_override].irq, 1 ); - } - else - for (; !io_port && (current_base < NO_BASES); ++current_base) { - dprintk(NDEBUG_INIT, "pas16: probing io_port 0x%04x\n", - (unsigned int)bases[current_base].io_port); - if ( !bases[current_base].noauto && - pas16_hw_detect( current_base ) ){ - io_port = bases[current_base].io_port; - init_board( io_port, default_irqs[ current_base ], 0 ); - dprintk(NDEBUG_INIT, "pas16: detected board\n"); - } - } - - dprintk(NDEBUG_INIT, "pas16: io_port = 0x%04x\n", - (unsigned int)io_port); - - if (!io_port) - break; - - instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata)); - if(instance == NULL) - goto out; - - instance->io_port = io_port; - - if (NCR5380_init(instance, FLAG_DMA_FIXUP | FLAG_LATE_DMA_SETUP)) - goto out_unregister; - - NCR5380_maybe_reset_bus(instance); - - if (overrides[current_override].irq != IRQ_AUTO) - instance->irq = overrides[current_override].irq; - else - instance->irq = NCR5380_probe_irq(instance, PAS16_IRQS); - - /* Compatibility with documented NCR5380 kernel parameters */ - if (instance->irq == 255) - instance->irq = NO_IRQ; - - if (instance->irq != NO_IRQ) - if (request_irq(instance->irq, pas16_intr, 0, - "pas16", instance)) { - printk("scsi%d : IRQ%d not free, interrupts disabled\n", - instance->host_no, instance->irq); - instance->irq = NO_IRQ; - } - - if (instance->irq == NO_IRQ) { - printk("scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no); - printk("scsi%d : please jumper the board for a free IRQ.\n", instance->host_no); - /* Disable 5380 interrupts, leave drive params the same */ - outb( 0x4d, io_port + SYS_CONFIG_4 ); - outb( (inb(io_port + IO_CONFIG_3) & 0x0f), io_port + IO_CONFIG_3 ); - } - - dprintk(NDEBUG_INIT, "scsi%d : irq = %d\n", - instance->host_no, instance->irq); - - ++current_override; - ++count; - } - return count; - -out_unregister: - scsi_unregister(instance); -out: - return count; -} - -/* - * Function : int pas16_biosparam(Disk *disk, struct block_device *dev, int *ip) - * - * Purpose : Generates a BIOS / DOS compatible H-C-S mapping for - * the specified device / size. - * - * Inputs : size = size of device in sectors (512 bytes), dev = block device - * major / minor, ip[] = {heads, sectors, cylinders} - * - * Returns : always 0 (success), initializes ip - * - */ - -/* - * XXX Most SCSI boards use this mapping, I could be incorrect. Some one - * using hard disks on a trantor should verify that this mapping corresponds - * to that used by the BIOS / ASPI driver by running the linux fdisk program - * and matching the H_C_S coordinates to what DOS uses. - */ - -static int pas16_biosparam(struct scsi_device *sdev, struct block_device *dev, - sector_t capacity, int *ip) -{ - int size = capacity; - ip[0] = 64; - ip[1] = 32; - ip[2] = size >> 11; /* I think I have it as /(32*64) */ - if( ip[2] > 1024 ) { /* yes, >, not >= */ - ip[0]=255; - ip[1]=63; - ip[2]=size/(63*255); - if( ip[2] > 1023 ) /* yes >1023... */ - ip[2] = 1023; - } - - return 0; -} - -/* - * Function : int pas16_pread (struct Scsi_Host *instance, - * unsigned char *dst, int len) - * - * Purpose : Fast 5380 pseudo-dma read function, transfers len bytes to - * dst - * - * Inputs : dst = destination, len = length in bytes - * - * Returns : 0 on success, non zero on a failure such as a watchdog - * timeout. - */ - -static inline int pas16_pread(struct Scsi_Host *instance, - unsigned char *dst, int len) -{ - register unsigned char *d = dst; - register unsigned short reg = (unsigned short) (instance->io_port + - P_DATA_REG_OFFSET); - register int i = len; - int ii = 0; - - while ( !(inb(instance->io_port + P_STATUS_REG_OFFSET) & P_ST_RDY) ) - ++ii; - - insb( reg, d, i ); - - if ( inb(instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET) & P_TS_TIM) { - outb( P_TS_CT, instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET); - printk("scsi%d : watchdog timer fired in NCR5380_pread()\n", - instance->host_no); - return -1; - } - return 0; -} - -/* - * Function : int pas16_pwrite (struct Scsi_Host *instance, - * unsigned char *src, int len) - * - * Purpose : Fast 5380 pseudo-dma write function, transfers len bytes from - * src - * - * Inputs : src = source, len = length in bytes - * - * Returns : 0 on success, non zero on a failure such as a watchdog - * timeout. - */ - -static inline int pas16_pwrite(struct Scsi_Host *instance, - unsigned char *src, int len) -{ - register unsigned char *s = src; - register unsigned short reg = (instance->io_port + P_DATA_REG_OFFSET); - register int i = len; - int ii = 0; - - while ( !((inb(instance->io_port + P_STATUS_REG_OFFSET)) & P_ST_RDY) ) - ++ii; - - outsb( reg, s, i ); - - if (inb(instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET) & P_TS_TIM) { - outb( P_TS_CT, instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET); - printk("scsi%d : watchdog timer fired in NCR5380_pwrite()\n", - instance->host_no); - return -1; - } - return 0; -} - -#include "NCR5380.c" - -static int pas16_release(struct Scsi_Host *shost) -{ - if (shost->irq != NO_IRQ) - free_irq(shost->irq, shost); - NCR5380_exit(shost); - scsi_unregister(shost); - return 0; -} - -static struct scsi_host_template driver_template = { - .name = "Pro Audio Spectrum-16 SCSI", - .detect = pas16_detect, - .release = pas16_release, - .proc_name = "pas16", - .info = pas16_info, - .queuecommand = pas16_queue_command, - .eh_abort_handler = pas16_abort, - .eh_bus_reset_handler = pas16_bus_reset, - .bios_param = pas16_biosparam, - .can_queue = 32, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 2, - .use_clustering = DISABLE_CLUSTERING, - .cmd_size = NCR5380_CMD_SIZE, - .max_sectors = 128, -}; -#include "scsi_module.c" - -#ifdef MODULE -module_param(pas16_addr, ushort, 0); -module_param(pas16_irq, int, 0); -#endif -MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/pas16.h b/drivers/scsi/pas16.h deleted file mode 100644 index 9fe7f33660b4..000000000000 --- a/drivers/scsi/pas16.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * This driver adapted from Drew Eckhardt's Trantor T128 driver - * - * Copyright 1993, Drew Eckhardt - * Visionary Computing - * (Unix and Linux consulting and custom programming) - * drew@colorado.edu - * +1 (303) 666-5836 - * - * ( Based on T128 - DISTRIBUTION RELEASE 3. ) - * - * Modified to work with the Pro Audio Spectrum/Studio 16 - * by John Weidman. - * - * - * For more information, please consult - * - * Media Vision - * (510) 770-8600 - * (800) 348-7116 - */ - - -#ifndef PAS16_H -#define PAS16_H - -#define PAS16_DEFAULT_BASE_1 0x388 -#define PAS16_DEFAULT_BASE_2 0x384 -#define PAS16_DEFAULT_BASE_3 0x38c -#define PAS16_DEFAULT_BASE_4 0x288 - -#define PAS16_DEFAULT_BOARD_1_IRQ 10 -#define PAS16_DEFAULT_BOARD_2_IRQ 12 -#define PAS16_DEFAULT_BOARD_3_IRQ 14 -#define PAS16_DEFAULT_BOARD_4_IRQ 15 - - -/* - * The Pro Audio Spectrum boards are I/O mapped. They use a Zilog 5380 - * SCSI controller, which is the equivalent of NCR's 5380. "Pseudo-DMA" - * architecture is used, where a PAL drives the DMA signals on the 5380 - * allowing fast, blind transfers with proper handshaking. - */ - - -/* The Time-out Counter register is used to safe-guard against a stuck - * bus (in the case of RDY driven handshake) or a stuck byte (if 16-Bit - * DMA conversion is used). The counter uses a 28.224MHz clock - * divided by 14 as its clock source. In the case of a stuck byte in - * the holding register, an interrupt is generated (and mixed with the - * one with the drive) using the CD-ROM interrupt pointer. - */ - -#define P_TIMEOUT_COUNTER_REG 0x4000 -#define P_TC_DISABLE 0x80 /* Set to 0 to enable timeout int. */ - /* Bits D6-D0 contain timeout count */ - - -#define P_TIMEOUT_STATUS_REG_OFFSET 0x4001 -#define P_TS_TIM 0x80 /* check timeout status */ - /* Bits D6-D4 N/U */ -#define P_TS_ARM_DRQ_INT 0x08 /* Arm DRQ Int. When set high, - * the next rising edge will - * cause a CD-ROM interrupt. - * When set low, the interrupt - * will be cleared. There is - * no status available for - * this interrupt. - */ -#define P_TS_ENABLE_TO_ERR_INTERRUPT /* Enable timeout error int. */ -#define P_TS_ENABLE_WAIT /* Enable Wait */ - -#define P_TS_CT 0x01 /* clear timeout. Note: writing - * to this register clears the - * timeout error int. or status - */ - - -/* - * The data register reads/writes to/from the 5380 in pseudo-DMA mode - */ - -#define P_DATA_REG_OFFSET 0x5c00 /* rw */ - -#define P_STATUS_REG_OFFSET 0x5c01 /* ro */ -#define P_ST_RDY 0x80 /* 5380 DDRQ Status */ - -#define P_IRQ_STATUS 0x5c03 -#define P_IS_IRQ 0x80 /* DIRQ status */ - -#define PCB_CONFIG 0x803 -#define MASTER_ADDRESS_PTR 0x9a01 /* Fixed position - no relo */ -#define SYS_CONFIG_4 0x8003 -#define WAIT_STATE 0xbc00 -#define OPERATION_MODE_1 0xec03 -#define IO_CONFIG_3 0xf002 - -#define NCR5380_implementation_fields /* none */ - -#define PAS16_io_port(reg) (instance->io_port + pas16_offset[(reg)]) - -#define NCR5380_read(reg) ( inb(PAS16_io_port(reg)) ) -#define NCR5380_write(reg, value) ( outb((value),PAS16_io_port(reg)) ) - -#define NCR5380_dma_xfer_len(instance, cmd, phase) (cmd->transfersize) -#define NCR5380_dma_recv_setup pas16_pread -#define NCR5380_dma_send_setup pas16_pwrite -#define NCR5380_dma_residual(instance) (0) - -#define NCR5380_intr pas16_intr -#define NCR5380_queue_command pas16_queue_command -#define NCR5380_abort pas16_abort -#define NCR5380_bus_reset pas16_bus_reset -#define NCR5380_info pas16_info - -/* 15 14 12 10 7 5 3 - 1101 0100 1010 1000 */ - -#define PAS16_IRQS 0xd4a8 - -#endif /* PAS16_H */ diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c index 04e67a190652..10546faac58c 100644 --- a/drivers/scsi/pm8001/pm8001_hwi.c +++ b/drivers/scsi/pm8001/pm8001_hwi.c @@ -4492,8 +4492,8 @@ pm8001_chip_phy_start_req(struct pm8001_hba_info *pm8001_ha, u8 phy_id) * @num: the inbound queue number * @phy_id: the phy id which we wanted to start up. */ -int pm8001_chip_phy_stop_req(struct pm8001_hba_info *pm8001_ha, - u8 phy_id) +static int pm8001_chip_phy_stop_req(struct pm8001_hba_info *pm8001_ha, + u8 phy_id) { struct phy_stop_req payload; struct inbound_queue_table *circularQ; diff --git a/drivers/scsi/pm8001/pm8001_sas.c b/drivers/scsi/pm8001/pm8001_sas.c index dc33dfa8f994..ce584c31d36e 100644 --- a/drivers/scsi/pm8001/pm8001_sas.c +++ b/drivers/scsi/pm8001/pm8001_sas.c @@ -527,7 +527,7 @@ void pm8001_ccb_task_free(struct pm8001_hba_info *pm8001_ha, * pm8001_alloc_dev - find a empty pm8001_device * @pm8001_ha: our hba card information */ -struct pm8001_device *pm8001_alloc_dev(struct pm8001_hba_info *pm8001_ha) +static struct pm8001_device *pm8001_alloc_dev(struct pm8001_hba_info *pm8001_ha) { u32 dev; for (dev = 0; dev < PM8001_MAX_DEVICES; dev++) { diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index b2a88200fe54..68a5c347fae9 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -306,7 +306,7 @@ static int pmcraid_change_queue_depth(struct scsi_device *scsi_dev, int depth) * Return Value * None */ -void pmcraid_init_cmdblk(struct pmcraid_cmd *cmd, int index) +static void pmcraid_init_cmdblk(struct pmcraid_cmd *cmd, int index) { struct pmcraid_ioarcb *ioarcb = &(cmd->ioa_cb->ioarcb); dma_addr_t dma_addr = cmd->ioa_cb_bus_addr; @@ -401,7 +401,7 @@ static struct pmcraid_cmd *pmcraid_get_free_cmd( * Return Value: * nothing */ -void pmcraid_return_cmd(struct pmcraid_cmd *cmd) +static void pmcraid_return_cmd(struct pmcraid_cmd *cmd) { struct pmcraid_instance *pinstance = cmd->drv_inst; unsigned long lock_flags; @@ -1710,7 +1710,7 @@ static struct pmcraid_ioasc_error *pmcraid_get_error_info(u32 ioasc) * @ioasc: ioasc code * @cmd: pointer to command that resulted in 'ioasc' */ -void pmcraid_ioasc_logger(u32 ioasc, struct pmcraid_cmd *cmd) +static void pmcraid_ioasc_logger(u32 ioasc, struct pmcraid_cmd *cmd) { struct pmcraid_ioasc_error *error_info = pmcraid_get_error_info(ioasc); @@ -3137,7 +3137,7 @@ static int pmcraid_eh_host_reset_handler(struct scsi_cmnd *scmd) * returns pointer pmcraid_ioadl_desc, initialized to point to internal * or external IOADLs */ -struct pmcraid_ioadl_desc * +static struct pmcraid_ioadl_desc * pmcraid_init_ioadls(struct pmcraid_cmd *cmd, int sgcount) { struct pmcraid_ioadl_desc *ioadl; diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index ae4a74756128..73b12e41d992 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -279,16 +279,6 @@ struct req_que; struct qla_tgt_sess; /* - * (sd.h is not exported, hence local inclusion) - * Data Integrity Field tuple. - */ -struct sd_dif_tuple { - __be16 guard_tag; /* Checksum */ - __be16 app_tag; /* Opaque storage */ - __be32 ref_tag; /* Target LBA or indirect LBA */ -}; - -/* * SCSI Request Block */ struct srb_cmd { diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 987f1c729e9c..068c4e47fac9 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -1828,7 +1828,7 @@ qla2x00_handle_dif_error(srb_t *sp, struct sts_entry_24xx *sts24) if (scsi_prot_sg_count(cmd)) { uint32_t i, j = 0, k = 0, num_ent; struct scatterlist *sg; - struct sd_dif_tuple *spt; + struct t10_pi_tuple *spt; /* Patch the corresponding protection tags */ scsi_for_each_prot_sg(cmd, sg, diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 2674f4c16bc3..ace65db1d2a2 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -899,12 +899,12 @@ qla2x00_wait_for_hba_ready(scsi_qla_host_t *vha) struct qla_hw_data *ha = vha->hw; scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); - while (((qla2x00_reset_active(vha)) || ha->dpc_active || - ha->flags.mbox_busy) || - test_bit(FX00_RESET_RECOVERY, &vha->dpc_flags) || - test_bit(FX00_TARGET_SCAN, &vha->dpc_flags)) { - if (test_bit(UNLOADING, &base_vha->dpc_flags)) - break; + while ((qla2x00_reset_active(vha) || ha->dpc_active || + ha->flags.mbox_busy) || + test_bit(FX00_RESET_RECOVERY, &vha->dpc_flags) || + test_bit(FX00_TARGET_SCAN, &vha->dpc_flags)) { + if (test_bit(UNLOADING, &base_vha->dpc_flags)) + break; msleep(1000); } } @@ -4694,7 +4694,7 @@ retry_unlock: qla83xx_wait_logic(); retry++; ql_dbg(ql_dbg_p3p, base_vha, 0xb064, - "Failed to release IDC lock, retyring=%d\n", retry); + "Failed to release IDC lock, retrying=%d\n", retry); goto retry_unlock; } } else if (retry < 10) { @@ -4702,7 +4702,7 @@ retry_unlock: qla83xx_wait_logic(); retry++; ql_dbg(ql_dbg_p3p, base_vha, 0xb065, - "Failed to read drv-lockid, retyring=%d\n", retry); + "Failed to read drv-lockid, retrying=%d\n", retry); goto retry_unlock; } @@ -4718,7 +4718,7 @@ retry_unlock2: qla83xx_wait_logic(); retry++; ql_dbg(ql_dbg_p3p, base_vha, 0xb066, - "Failed to release IDC lock, retyring=%d\n", retry); + "Failed to release IDC lock, retrying=%d\n", retry); goto retry_unlock2; } } diff --git a/drivers/scsi/qla4xxx/ql4_nx.c b/drivers/scsi/qla4xxx/ql4_nx.c index ae87d6c19f17..06ddd13cb7cc 100644 --- a/drivers/scsi/qla4xxx/ql4_nx.c +++ b/drivers/scsi/qla4xxx/ql4_nx.c @@ -1843,7 +1843,7 @@ static uint32_t ql4_84xx_poll_wait_for_ready(struct scsi_qla_host *ha, return rval; } -uint32_t ql4_84xx_ipmdio_rd_reg(struct scsi_qla_host *ha, uint32_t addr1, +static uint32_t ql4_84xx_ipmdio_rd_reg(struct scsi_qla_host *ha, uint32_t addr1, uint32_t addr3, uint32_t mask, uint32_t addr, uint32_t *data_ptr) { diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 6a219a0844d3..c905709707f0 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -42,6 +42,7 @@ #include <linux/atomic.h> #include <linux/hrtimer.h> #include <linux/uuid.h> +#include <linux/t10-pi.h> #include <net/checksum.h> @@ -627,7 +628,7 @@ static LIST_HEAD(sdebug_host_list); static DEFINE_SPINLOCK(sdebug_host_list_lock); static unsigned char *fake_storep; /* ramdisk storage */ -static struct sd_dif_tuple *dif_storep; /* protection info */ +static struct t10_pi_tuple *dif_storep; /* protection info */ static void *map_storep; /* provisioning map */ static unsigned long map_size; @@ -682,7 +683,7 @@ static void *fake_store(unsigned long long lba) return fake_storep + lba * sdebug_sector_size; } -static struct sd_dif_tuple *dif_store(sector_t sector) +static struct t10_pi_tuple *dif_store(sector_t sector) { sector = sector_div(sector, sdebug_store_sectors); @@ -1349,7 +1350,7 @@ static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) } else if (0x86 == cmd[2]) { /* extended inquiry */ arr[1] = cmd[2]; /*sanity */ arr[3] = 0x3c; /* number of following entries */ - if (sdebug_dif == SD_DIF_TYPE3_PROTECTION) + if (sdebug_dif == T10_PI_TYPE3_PROTECTION) arr[4] = 0x4; /* SPT: GRD_CHK:1 */ else if (have_dif_prot) arr[4] = 0x5; /* SPT: GRD_CHK:1, REF_CHK:1 */ @@ -2430,7 +2431,7 @@ static __be16 dif_compute_csum(const void *buf, int len) return csum; } -static int dif_verify(struct sd_dif_tuple *sdt, const void *data, +static int dif_verify(struct t10_pi_tuple *sdt, const void *data, sector_t sector, u32 ei_lba) { __be16 csum = dif_compute_csum(data, sdebug_sector_size); @@ -2442,13 +2443,13 @@ static int dif_verify(struct sd_dif_tuple *sdt, const void *data, be16_to_cpu(csum)); return 0x01; } - if (sdebug_dif == SD_DIF_TYPE1_PROTECTION && + if (sdebug_dif == T10_PI_TYPE1_PROTECTION && be32_to_cpu(sdt->ref_tag) != (sector & 0xffffffff)) { pr_err("REF check failed on sector %lu\n", (unsigned long)sector); return 0x03; } - if (sdebug_dif == SD_DIF_TYPE2_PROTECTION && + if (sdebug_dif == T10_PI_TYPE2_PROTECTION && be32_to_cpu(sdt->ref_tag) != ei_lba) { pr_err("REF check failed on sector %lu\n", (unsigned long)sector); @@ -2504,7 +2505,7 @@ static int prot_verify_read(struct scsi_cmnd *SCpnt, sector_t start_sec, unsigned int sectors, u32 ei_lba) { unsigned int i; - struct sd_dif_tuple *sdt; + struct t10_pi_tuple *sdt; sector_t sector; for (i = 0; i < sectors; i++, ei_lba++) { @@ -2580,13 +2581,13 @@ static int resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) break; } if (unlikely(have_dif_prot && check_prot)) { - if (sdebug_dif == SD_DIF_TYPE2_PROTECTION && + if (sdebug_dif == T10_PI_TYPE2_PROTECTION && (cmd[1] & 0xe0)) { mk_sense_invalid_opcode(scp); return check_condition_result; } - if ((sdebug_dif == SD_DIF_TYPE1_PROTECTION || - sdebug_dif == SD_DIF_TYPE3_PROTECTION) && + if ((sdebug_dif == T10_PI_TYPE1_PROTECTION || + sdebug_dif == T10_PI_TYPE3_PROTECTION) && (cmd[1] & 0xe0) == 0) sdev_printk(KERN_ERR, scp->device, "Unprotected RD " "to DIF device\n"); @@ -2696,7 +2697,7 @@ static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec, unsigned int sectors, u32 ei_lba) { int ret; - struct sd_dif_tuple *sdt; + struct t10_pi_tuple *sdt; void *daddr; sector_t sector = start_sec; int ppage_offset; @@ -2722,7 +2723,7 @@ static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec, } for (ppage_offset = 0; ppage_offset < piter.length; - ppage_offset += sizeof(struct sd_dif_tuple)) { + ppage_offset += sizeof(struct t10_pi_tuple)) { /* If we're at the end of the current * data page advance to the next one */ @@ -2893,13 +2894,13 @@ static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) break; } if (unlikely(have_dif_prot && check_prot)) { - if (sdebug_dif == SD_DIF_TYPE2_PROTECTION && + if (sdebug_dif == T10_PI_TYPE2_PROTECTION && (cmd[1] & 0xe0)) { mk_sense_invalid_opcode(scp); return check_condition_result; } - if ((sdebug_dif == SD_DIF_TYPE1_PROTECTION || - sdebug_dif == SD_DIF_TYPE3_PROTECTION) && + if ((sdebug_dif == T10_PI_TYPE1_PROTECTION || + sdebug_dif == T10_PI_TYPE3_PROTECTION) && (cmd[1] & 0xe0) == 0) sdev_printk(KERN_ERR, scp->device, "Unprotected WR " "to DIF device\n"); @@ -3135,13 +3136,13 @@ static int resp_comp_write(struct scsi_cmnd *scp, num = cmd[13]; /* 1 to a maximum of 255 logical blocks */ if (0 == num) return 0; /* degenerate case, not an error */ - if (sdebug_dif == SD_DIF_TYPE2_PROTECTION && + if (sdebug_dif == T10_PI_TYPE2_PROTECTION && (cmd[1] & 0xe0)) { mk_sense_invalid_opcode(scp); return check_condition_result; } - if ((sdebug_dif == SD_DIF_TYPE1_PROTECTION || - sdebug_dif == SD_DIF_TYPE3_PROTECTION) && + if ((sdebug_dif == T10_PI_TYPE1_PROTECTION || + sdebug_dif == T10_PI_TYPE3_PROTECTION) && (cmd[1] & 0xe0) == 0) sdev_printk(KERN_ERR, scp->device, "Unprotected WR " "to DIF device\n"); @@ -4939,12 +4940,11 @@ static int __init scsi_debug_init(void) } switch (sdebug_dif) { - - case SD_DIF_TYPE0_PROTECTION: + case T10_PI_TYPE0_PROTECTION: break; - case SD_DIF_TYPE1_PROTECTION: - case SD_DIF_TYPE2_PROTECTION: - case SD_DIF_TYPE3_PROTECTION: + case T10_PI_TYPE1_PROTECTION: + case T10_PI_TYPE2_PROTECTION: + case T10_PI_TYPE3_PROTECTION: have_dif_prot = true; break; @@ -5026,7 +5026,7 @@ static int __init scsi_debug_init(void) if (sdebug_dix) { int dif_size; - dif_size = sdebug_store_sectors * sizeof(struct sd_dif_tuple); + dif_size = sdebug_store_sectors * sizeof(struct t10_pi_tuple); dif_storep = vmalloc(dif_size); pr_err("dif_storep %u bytes @ %p\n", dif_size, dif_storep); @@ -5480,19 +5480,19 @@ static int sdebug_driver_probe(struct device * dev) switch (sdebug_dif) { - case SD_DIF_TYPE1_PROTECTION: + case T10_PI_TYPE1_PROTECTION: hprot = SHOST_DIF_TYPE1_PROTECTION; if (sdebug_dix) hprot |= SHOST_DIX_TYPE1_PROTECTION; break; - case SD_DIF_TYPE2_PROTECTION: + case T10_PI_TYPE2_PROTECTION: hprot = SHOST_DIF_TYPE2_PROTECTION; if (sdebug_dix) hprot |= SHOST_DIX_TYPE2_PROTECTION; break; - case SD_DIF_TYPE3_PROTECTION: + case T10_PI_TYPE3_PROTECTION: hprot = SHOST_DIF_TYPE3_PROTECTION; if (sdebug_dix) hprot |= SHOST_DIX_TYPE3_PROTECTION; diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index 85c8a51bc563..193636a59adf 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -86,12 +86,14 @@ extern void scsi_device_unbusy(struct scsi_device *sdev); extern void scsi_queue_insert(struct scsi_cmnd *cmd, int reason); extern void scsi_io_completion(struct scsi_cmnd *, unsigned int); extern void scsi_run_host_queues(struct Scsi_Host *shost); +extern void scsi_requeue_run_queue(struct work_struct *work); extern struct request_queue *scsi_alloc_queue(struct scsi_device *sdev); extern struct request_queue *scsi_mq_alloc_queue(struct scsi_device *sdev); extern int scsi_mq_setup_tags(struct Scsi_Host *shost); extern void scsi_mq_destroy_tags(struct Scsi_Host *shost); extern int scsi_init_queue(void); extern void scsi_exit_queue(void); +extern void scsi_evt_thread(struct work_struct *work); struct request_queue; struct request; extern struct kmem_cache *scsi_sdb_cache; diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index e0a78f53d809..212e98d940bc 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -217,8 +217,6 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget, struct scsi_device *sdev; int display_failure_msg = 1, ret; struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); - extern void scsi_evt_thread(struct work_struct *work); - extern void scsi_requeue_run_queue(struct work_struct *work); sdev = kzalloc(sizeof(*sdev) + shost->transportt->device_size, GFP_ATOMIC); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index d3e852ad5aa3..51e56296f465 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -52,6 +52,7 @@ #include <linux/slab.h> #include <linux/pm_runtime.h> #include <linux/pr.h> +#include <linux/t10-pi.h> #include <asm/uaccess.h> #include <asm/unaligned.h> @@ -314,7 +315,7 @@ protection_type_store(struct device *dev, struct device_attribute *attr, if (err) return err; - if (val >= 0 && val <= SD_DIF_TYPE3_PROTECTION) + if (val >= 0 && val <= T10_PI_TYPE3_PROTECTION) sdkp->protection_type = val; return count; @@ -332,7 +333,7 @@ protection_mode_show(struct device *dev, struct device_attribute *attr, dif = scsi_host_dif_capable(sdp->host, sdkp->protection_type); dix = scsi_host_dix_capable(sdp->host, sdkp->protection_type); - if (!dix && scsi_host_dix_capable(sdp->host, SD_DIF_TYPE0_PROTECTION)) { + if (!dix && scsi_host_dix_capable(sdp->host, T10_PI_TYPE0_PROTECTION)) { dif = 0; dix = 1; } @@ -608,7 +609,7 @@ static unsigned char sd_setup_protect_cmnd(struct scsi_cmnd *scmd, scmd->prot_flags |= SCSI_PROT_GUARD_CHECK; } - if (dif != SD_DIF_TYPE3_PROTECTION) { /* DIX/DIF Type 0, 1, 2 */ + if (dif != T10_PI_TYPE3_PROTECTION) { /* DIX/DIF Type 0, 1, 2 */ scmd->prot_flags |= SCSI_PROT_REF_INCREMENT; if (bio_integrity_flagged(bio, BIP_CTRL_NOCHECK) == false) @@ -1031,7 +1032,7 @@ static int sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) else protect = 0; - if (protect && sdkp->protection_type == SD_DIF_TYPE2_PROTECTION) { + if (protect && sdkp->protection_type == T10_PI_TYPE2_PROTECTION) { SCpnt->cmnd = mempool_alloc(sd_cdb_pool, GFP_ATOMIC); if (unlikely(SCpnt->cmnd == NULL)) { @@ -1997,7 +1998,7 @@ static int sd_read_protection_type(struct scsi_disk *sdkp, unsigned char *buffer type = ((buffer[12] >> 1) & 7) + 1; /* P_TYPE 0 = Type 1 */ - if (type > SD_DIF_TYPE3_PROTECTION) + if (type > T10_PI_TYPE3_PROTECTION) ret = -ENODEV; else if (scsi_host_dif_capable(sdp->host, type)) ret = 1; diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index 765a6f1ac1b7..c8d986368da9 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -157,27 +157,6 @@ static inline unsigned int logical_to_bytes(struct scsi_device *sdev, sector_t b } /* - * A DIF-capable target device can be formatted with different - * protection schemes. Currently 0 through 3 are defined: - * - * Type 0 is regular (unprotected) I/O - * - * Type 1 defines the contents of the guard and reference tags - * - * Type 2 defines the contents of the guard and reference tags and - * uses 32-byte commands to seed the latter - * - * Type 3 defines the contents of the guard tag only - */ - -enum sd_dif_target_protection_types { - SD_DIF_TYPE0_PROTECTION = 0x0, - SD_DIF_TYPE1_PROTECTION = 0x1, - SD_DIF_TYPE2_PROTECTION = 0x2, - SD_DIF_TYPE3_PROTECTION = 0x3, -}; - -/* * Look up the DIX operation based on whether the command is read or * write and whether dix and dif are enabled. */ @@ -239,15 +218,6 @@ static inline unsigned int sd_prot_flag_mask(unsigned int prot_op) return flag_mask[prot_op]; } -/* - * Data Integrity Field tuple. - */ -struct sd_dif_tuple { - __be16 guard_tag; /* Checksum */ - __be16 app_tag; /* Opaque storage */ - __be32 ref_tag; /* Target LBA or indirect LBA */ -}; - #ifdef CONFIG_BLK_DEV_INTEGRITY extern void sd_dif_config_host(struct scsi_disk *); diff --git a/drivers/scsi/sd_dif.c b/drivers/scsi/sd_dif.c index 987bf392c336..9035380c0dda 100644 --- a/drivers/scsi/sd_dif.c +++ b/drivers/scsi/sd_dif.c @@ -60,14 +60,14 @@ void sd_dif_config_host(struct scsi_disk *sdkp) /* Enable DMA of protection information */ if (scsi_host_get_guard(sdkp->device->host) & SHOST_DIX_GUARD_IP) { - if (type == SD_DIF_TYPE3_PROTECTION) + if (type == T10_PI_TYPE3_PROTECTION) bi.profile = &t10_pi_type3_ip; else bi.profile = &t10_pi_type1_ip; bi.flags |= BLK_INTEGRITY_IP_CHECKSUM; } else - if (type == SD_DIF_TYPE3_PROTECTION) + if (type == T10_PI_TYPE3_PROTECTION) bi.profile = &t10_pi_type3_crc; else bi.profile = &t10_pi_type1_crc; @@ -82,7 +82,7 @@ void sd_dif_config_host(struct scsi_disk *sdkp) if (!sdkp->ATO) goto out; - if (type == SD_DIF_TYPE3_PROTECTION) + if (type == T10_PI_TYPE3_PROTECTION) bi.tag_size = sizeof(u16) + sizeof(u32); else bi.tag_size = sizeof(u16); @@ -121,7 +121,7 @@ void sd_dif_prepare(struct scsi_cmnd *scmd) sdkp = scsi_disk(scmd->request->rq_disk); - if (sdkp->protection_type == SD_DIF_TYPE3_PROTECTION) + if (sdkp->protection_type == T10_PI_TYPE3_PROTECTION) return; phys = scsi_prot_ref_tag(scmd); @@ -172,7 +172,7 @@ void sd_dif_complete(struct scsi_cmnd *scmd, unsigned int good_bytes) sdkp = scsi_disk(scmd->request->rq_disk); - if (sdkp->protection_type == SD_DIF_TYPE3_PROTECTION || good_bytes == 0) + if (sdkp->protection_type == T10_PI_TYPE3_PROTECTION || good_bytes == 0) return; intervals = good_bytes / scsi_prot_interval(scmd); diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index ae7d9bdf409c..070332eb41f3 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -79,18 +79,7 @@ static void sg_proc_cleanup(void); */ #define SG_MAX_CDB_SIZE 252 -/* - * Suppose you want to calculate the formula muldiv(x,m,d)=int(x * m / d) - * Then when using 32 bit integers x * m may overflow during the calculation. - * Replacing muldiv(x) by muldiv(x)=((x % d) * m) / d + int(x / d) * m - * calculates the same, but prevents the overflow when both m and d - * are "small" numbers (like HZ and USER_HZ). - * Of course an overflow is inavoidable if the result of muldiv doesn't fit - * in 32 bits. - */ -#define MULDIV(X,MUL,DIV) ((((X % DIV) * MUL) / DIV) + ((X / DIV) * MUL)) - -#define SG_DEFAULT_TIMEOUT MULDIV(SG_DEFAULT_TIMEOUT_USER, HZ, USER_HZ) +#define SG_DEFAULT_TIMEOUT mult_frac(SG_DEFAULT_TIMEOUT_USER, HZ, USER_HZ) int sg_big_buff = SG_DEF_RESERVED_SIZE; /* N.B. This variable is readable and writeable via @@ -884,10 +873,11 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) return result; if (val < 0) return -EIO; - if (val >= MULDIV (INT_MAX, USER_HZ, HZ)) - val = MULDIV (INT_MAX, USER_HZ, HZ); + if (val >= mult_frac((s64)INT_MAX, USER_HZ, HZ)) + val = min_t(s64, mult_frac((s64)INT_MAX, USER_HZ, HZ), + INT_MAX); sfp->timeout_user = val; - sfp->timeout = MULDIV (val, HZ, USER_HZ); + sfp->timeout = mult_frac(val, HZ, USER_HZ); return 0; case SG_GET_TIMEOUT: /* N.B. User receives timeout as return value */ diff --git a/drivers/scsi/smartpqi/Kconfig b/drivers/scsi/smartpqi/Kconfig new file mode 100644 index 000000000000..97e159c2cecd --- /dev/null +++ b/drivers/scsi/smartpqi/Kconfig @@ -0,0 +1,54 @@ +# +# Kernel configuration file for the SMARTPQI +# +# Copyright (c) 2016 Microsemi Corporation +# Copyright (c) 2016 PMC-Sierra, Inc. +# (mailto:esc.storagedev@microsemi.com) + +# 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 + +config SCSI_SMARTPQI + tristate "Microsemi PQI Driver" + depends on PCI && SCSI && !S390 + select SCSI_SAS_ATTRS + select RAID_ATTRS + ---help--- + This driver supports Microsemi PQI controllers. + + <http://www.microsemi.com> + + To compile this driver as a module, choose M here: the + module will be called smartpqi. + + Note: the aacraid driver will not manage a smartpqi + controller. You need to enable smartpqi for smartpqi + controllers. For more information, please see + Documentation/scsi/smartpqi.txt diff --git a/drivers/scsi/smartpqi/Makefile b/drivers/scsi/smartpqi/Makefile new file mode 100644 index 000000000000..0f42a225a664 --- /dev/null +++ b/drivers/scsi/smartpqi/Makefile @@ -0,0 +1,3 @@ +ccflags-y += -I. +obj-m += smartpqi.o +smartpqi-objs := smartpqi_init.o smartpqi_sis.o smartpqi_sas_transport.o diff --git a/drivers/scsi/smartpqi/smartpqi.h b/drivers/scsi/smartpqi/smartpqi.h new file mode 100644 index 000000000000..07b6444d3e0a --- /dev/null +++ b/drivers/scsi/smartpqi/smartpqi.h @@ -0,0 +1,1136 @@ +/* + * driver for Microsemi PQI-based storage controllers + * Copyright (c) 2016 Microsemi Corporation + * Copyright (c) 2016 PMC-Sierra, Inc. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more details. + * + * Questions/Comments/Bugfixes to esc.storagedev@microsemi.com + * + */ + +#if !defined(_SMARTPQI_H) +#define _SMARTPQI_H + +#pragma pack(1) + +#define PQI_DEVICE_SIGNATURE "PQI DREG" + +/* This structure is defined by the PQI specification. */ +struct pqi_device_registers { + __le64 signature; + u8 function_and_status_code; + u8 reserved[7]; + u8 max_admin_iq_elements; + u8 max_admin_oq_elements; + u8 admin_iq_element_length; /* in 16-byte units */ + u8 admin_oq_element_length; /* in 16-byte units */ + __le16 max_reset_timeout; /* in 100-millisecond units */ + u8 reserved1[2]; + __le32 legacy_intx_status; + __le32 legacy_intx_mask_set; + __le32 legacy_intx_mask_clear; + u8 reserved2[28]; + __le32 device_status; + u8 reserved3[4]; + __le64 admin_iq_pi_offset; + __le64 admin_oq_ci_offset; + __le64 admin_iq_element_array_addr; + __le64 admin_oq_element_array_addr; + __le64 admin_iq_ci_addr; + __le64 admin_oq_pi_addr; + u8 admin_iq_num_elements; + u8 admin_oq_num_elements; + __le16 admin_queue_int_msg_num; + u8 reserved4[4]; + __le32 device_error; + u8 reserved5[4]; + __le64 error_details; + __le32 device_reset; + __le32 power_action; + u8 reserved6[104]; +}; + +/* + * controller registers + * + * These are defined by the PMC implementation. + * + * Some registers (those named sis_*) are only used when in + * legacy SIS mode before we transition the controller into + * PQI mode. There are a number of other SIS mode registers, + * but we don't use them, so only the SIS registers that we + * care about are defined here. The offsets mentioned in the + * comments are the offsets from the PCIe BAR 0. + */ +struct pqi_ctrl_registers { + u8 reserved[0x20]; + __le32 sis_host_to_ctrl_doorbell; /* 20h */ + u8 reserved1[0x34 - (0x20 + sizeof(__le32))]; + __le32 sis_interrupt_mask; /* 34h */ + u8 reserved2[0x9c - (0x34 + sizeof(__le32))]; + __le32 sis_ctrl_to_host_doorbell; /* 9Ch */ + u8 reserved3[0xa0 - (0x9c + sizeof(__le32))]; + __le32 sis_ctrl_to_host_doorbell_clear; /* A0h */ + u8 reserved4[0xb0 - (0xa0 + sizeof(__le32))]; + __le32 sis_driver_scratch; /* B0h */ + u8 reserved5[0xbc - (0xb0 + sizeof(__le32))]; + __le32 sis_firmware_status; /* BCh */ + u8 reserved6[0x1000 - (0xbc + sizeof(__le32))]; + __le32 sis_mailbox[8]; /* 1000h */ + u8 reserved7[0x4000 - (0x1000 + (sizeof(__le32) * 8))]; + /* + * The PQI spec states that the PQI registers should be at + * offset 0 from the PCIe BAR 0. However, we can't map + * them at offset 0 because that would break compatibility + * with the SIS registers. So we map them at offset 4000h. + */ + struct pqi_device_registers pqi_registers; /* 4000h */ +}; + +#define PQI_DEVICE_REGISTERS_OFFSET 0x4000 + +enum pqi_io_path { + RAID_PATH = 0, + AIO_PATH = 1 +}; + +struct pqi_sg_descriptor { + __le64 address; + __le32 length; + __le32 flags; +}; + +/* manifest constants for the flags field of pqi_sg_descriptor */ +#define CISS_SG_LAST 0x40000000 +#define CISS_SG_CHAIN 0x80000000 + +struct pqi_iu_header { + u8 iu_type; + u8 reserved; + __le16 iu_length; /* in bytes - does not include the length */ + /* of this header */ + __le16 response_queue_id; /* specifies the OQ where the */ + /* response IU is to be delivered */ + u8 work_area[2]; /* reserved for driver use */ +}; + +/* + * According to the PQI spec, the IU header is only the first 4 bytes of our + * pqi_iu_header structure. + */ +#define PQI_REQUEST_HEADER_LENGTH 4 + +struct pqi_general_admin_request { + struct pqi_iu_header header; + __le16 request_id; + u8 function_code; + union { + struct { + u8 reserved[33]; + __le32 buffer_length; + struct pqi_sg_descriptor sg_descriptor; + } report_device_capability; + + struct { + u8 reserved; + __le16 queue_id; + u8 reserved1[2]; + __le64 element_array_addr; + __le64 ci_addr; + __le16 num_elements; + __le16 element_length; + u8 queue_protocol; + u8 reserved2[23]; + __le32 vendor_specific; + } create_operational_iq; + + struct { + u8 reserved; + __le16 queue_id; + u8 reserved1[2]; + __le64 element_array_addr; + __le64 pi_addr; + __le16 num_elements; + __le16 element_length; + u8 queue_protocol; + u8 reserved2[3]; + __le16 int_msg_num; + __le16 coalescing_count; + __le32 min_coalescing_time; + __le32 max_coalescing_time; + u8 reserved3[8]; + __le32 vendor_specific; + } create_operational_oq; + + struct { + u8 reserved; + __le16 queue_id; + u8 reserved1[50]; + } delete_operational_queue; + + struct { + u8 reserved; + __le16 queue_id; + u8 reserved1[46]; + __le32 vendor_specific; + } change_operational_iq_properties; + + } data; +}; + +struct pqi_general_admin_response { + struct pqi_iu_header header; + __le16 request_id; + u8 function_code; + u8 status; + union { + struct { + u8 status_descriptor[4]; + __le64 iq_pi_offset; + u8 reserved[40]; + } create_operational_iq; + + struct { + u8 status_descriptor[4]; + __le64 oq_ci_offset; + u8 reserved[40]; + } create_operational_oq; + } data; +}; + +struct pqi_iu_layer_descriptor { + u8 inbound_spanning_supported : 1; + u8 reserved : 7; + u8 reserved1[5]; + __le16 max_inbound_iu_length; + u8 outbound_spanning_supported : 1; + u8 reserved2 : 7; + u8 reserved3[5]; + __le16 max_outbound_iu_length; +}; + +struct pqi_device_capability { + __le16 data_length; + u8 reserved[6]; + u8 iq_arbitration_priority_support_bitmask; + u8 maximum_aw_a; + u8 maximum_aw_b; + u8 maximum_aw_c; + u8 max_arbitration_burst : 3; + u8 reserved1 : 4; + u8 iqa : 1; + u8 reserved2[2]; + u8 iq_freeze : 1; + u8 reserved3 : 7; + __le16 max_inbound_queues; + __le16 max_elements_per_iq; + u8 reserved4[4]; + __le16 max_iq_element_length; + __le16 min_iq_element_length; + u8 reserved5[2]; + __le16 max_outbound_queues; + __le16 max_elements_per_oq; + __le16 intr_coalescing_time_granularity; + __le16 max_oq_element_length; + __le16 min_oq_element_length; + u8 reserved6[24]; + struct pqi_iu_layer_descriptor iu_layer_descriptors[32]; +}; + +#define PQI_MAX_EMBEDDED_SG_DESCRIPTORS 4 + +struct pqi_raid_path_request { + struct pqi_iu_header header; + __le16 request_id; + __le16 nexus_id; + __le32 buffer_length; + u8 lun_number[8]; + __le16 protocol_specific; + u8 data_direction : 2; + u8 partial : 1; + u8 reserved1 : 4; + u8 fence : 1; + __le16 error_index; + u8 reserved2; + u8 task_attribute : 3; + u8 command_priority : 4; + u8 reserved3 : 1; + u8 reserved4 : 2; + u8 additional_cdb_bytes_usage : 3; + u8 reserved5 : 3; + u8 cdb[32]; + struct pqi_sg_descriptor + sg_descriptors[PQI_MAX_EMBEDDED_SG_DESCRIPTORS]; +}; + +struct pqi_aio_path_request { + struct pqi_iu_header header; + __le16 request_id; + u8 reserved1[2]; + __le32 nexus_id; + __le32 buffer_length; + u8 data_direction : 2; + u8 partial : 1; + u8 memory_type : 1; + u8 fence : 1; + u8 encryption_enable : 1; + u8 reserved2 : 2; + u8 task_attribute : 3; + u8 command_priority : 4; + u8 reserved3 : 1; + __le16 data_encryption_key_index; + __le32 encrypt_tweak_lower; + __le32 encrypt_tweak_upper; + u8 cdb[16]; + __le16 error_index; + u8 num_sg_descriptors; + u8 cdb_length; + u8 lun_number[8]; + u8 reserved4[4]; + struct pqi_sg_descriptor + sg_descriptors[PQI_MAX_EMBEDDED_SG_DESCRIPTORS]; +}; + +struct pqi_io_response { + struct pqi_iu_header header; + __le16 request_id; + __le16 error_index; + u8 reserved2[4]; +}; + +struct pqi_general_management_request { + struct pqi_iu_header header; + __le16 request_id; + union { + struct { + u8 reserved[2]; + __le32 buffer_length; + struct pqi_sg_descriptor sg_descriptors[3]; + } report_event_configuration; + + struct { + __le16 global_event_oq_id; + __le32 buffer_length; + struct pqi_sg_descriptor sg_descriptors[3]; + } set_event_configuration; + } data; +}; + +struct pqi_event_descriptor { + u8 event_type; + u8 reserved; + __le16 oq_id; +}; + +struct pqi_event_config { + u8 reserved[2]; + u8 num_event_descriptors; + u8 reserved1; + struct pqi_event_descriptor descriptors[1]; +}; + +#define PQI_MAX_EVENT_DESCRIPTORS 255 + +struct pqi_event_response { + struct pqi_iu_header header; + u8 event_type; + u8 reserved2 : 7; + u8 request_acknowlege : 1; + __le16 event_id; + __le32 additional_event_id; + u8 data[16]; +}; + +struct pqi_event_acknowledge_request { + struct pqi_iu_header header; + u8 event_type; + u8 reserved2; + __le16 event_id; + __le32 additional_event_id; +}; + +struct pqi_task_management_request { + struct pqi_iu_header header; + __le16 request_id; + __le16 nexus_id; + u8 reserved[4]; + u8 lun_number[8]; + __le16 protocol_specific; + __le16 outbound_queue_id_to_manage; + __le16 request_id_to_manage; + u8 task_management_function; + u8 reserved2 : 7; + u8 fence : 1; +}; + +#define SOP_TASK_MANAGEMENT_LUN_RESET 0x8 + +struct pqi_task_management_response { + struct pqi_iu_header header; + __le16 request_id; + __le16 nexus_id; + u8 additional_response_info[3]; + u8 response_code; +}; + +struct pqi_aio_error_info { + u8 status; + u8 service_response; + u8 data_present; + u8 reserved; + __le32 residual_count; + __le16 data_length; + __le16 reserved1; + u8 data[256]; +}; + +struct pqi_raid_error_info { + u8 data_in_result; + u8 data_out_result; + u8 reserved[3]; + u8 status; + __le16 status_qualifier; + __le16 sense_data_length; + __le16 response_data_length; + __le32 data_in_transferred; + __le32 data_out_transferred; + u8 data[256]; +}; + +#define PQI_REQUEST_IU_TASK_MANAGEMENT 0x13 +#define PQI_REQUEST_IU_RAID_PATH_IO 0x14 +#define PQI_REQUEST_IU_AIO_PATH_IO 0x15 +#define PQI_REQUEST_IU_GENERAL_ADMIN 0x60 +#define PQI_REQUEST_IU_REPORT_VENDOR_EVENT_CONFIG 0x72 +#define PQI_REQUEST_IU_SET_VENDOR_EVENT_CONFIG 0x73 +#define PQI_REQUEST_IU_ACKNOWLEDGE_VENDOR_EVENT 0xf6 + +#define PQI_RESPONSE_IU_GENERAL_MANAGEMENT 0x81 +#define PQI_RESPONSE_IU_TASK_MANAGEMENT 0x93 +#define PQI_RESPONSE_IU_GENERAL_ADMIN 0xe0 +#define PQI_RESPONSE_IU_RAID_PATH_IO_SUCCESS 0xf0 +#define PQI_RESPONSE_IU_AIO_PATH_IO_SUCCESS 0xf1 +#define PQI_RESPONSE_IU_RAID_PATH_IO_ERROR 0xf2 +#define PQI_RESPONSE_IU_AIO_PATH_IO_ERROR 0xf3 +#define PQI_RESPONSE_IU_AIO_PATH_DISABLED 0xf4 +#define PQI_RESPONSE_IU_VENDOR_EVENT 0xf5 + +#define PQI_GENERAL_ADMIN_FUNCTION_REPORT_DEVICE_CAPABILITY 0x0 +#define PQI_GENERAL_ADMIN_FUNCTION_CREATE_IQ 0x10 +#define PQI_GENERAL_ADMIN_FUNCTION_CREATE_OQ 0x11 +#define PQI_GENERAL_ADMIN_FUNCTION_DELETE_IQ 0x12 +#define PQI_GENERAL_ADMIN_FUNCTION_DELETE_OQ 0x13 +#define PQI_GENERAL_ADMIN_FUNCTION_CHANGE_IQ_PROPERTY 0x14 + +#define PQI_GENERAL_ADMIN_STATUS_SUCCESS 0x0 + +#define PQI_IQ_PROPERTY_IS_AIO_QUEUE 0x1 + +#define PQI_GENERAL_ADMIN_IU_LENGTH 0x3c +#define PQI_PROTOCOL_SOP 0x0 + +#define PQI_DATA_IN_OUT_GOOD 0x0 +#define PQI_DATA_IN_OUT_UNDERFLOW 0x1 +#define PQI_DATA_IN_OUT_BUFFER_ERROR 0x40 +#define PQI_DATA_IN_OUT_BUFFER_OVERFLOW 0x41 +#define PQI_DATA_IN_OUT_BUFFER_OVERFLOW_DESCRIPTOR_AREA 0x42 +#define PQI_DATA_IN_OUT_BUFFER_OVERFLOW_BRIDGE 0x43 +#define PQI_DATA_IN_OUT_PCIE_FABRIC_ERROR 0x60 +#define PQI_DATA_IN_OUT_PCIE_COMPLETION_TIMEOUT 0x61 +#define PQI_DATA_IN_OUT_PCIE_COMPLETER_ABORT_RECEIVED 0x62 +#define PQI_DATA_IN_OUT_PCIE_UNSUPPORTED_REQUEST_RECEIVED 0x63 +#define PQI_DATA_IN_OUT_PCIE_ECRC_CHECK_FAILED 0x64 +#define PQI_DATA_IN_OUT_PCIE_UNSUPPORTED_REQUEST 0x65 +#define PQI_DATA_IN_OUT_PCIE_ACS_VIOLATION 0x66 +#define PQI_DATA_IN_OUT_PCIE_TLP_PREFIX_BLOCKED 0x67 +#define PQI_DATA_IN_OUT_PCIE_POISONED_MEMORY_READ 0x6F +#define PQI_DATA_IN_OUT_ERROR 0xf0 +#define PQI_DATA_IN_OUT_PROTOCOL_ERROR 0xf1 +#define PQI_DATA_IN_OUT_HARDWARE_ERROR 0xf2 +#define PQI_DATA_IN_OUT_UNSOLICITED_ABORT 0xf3 +#define PQI_DATA_IN_OUT_ABORTED 0xf4 +#define PQI_DATA_IN_OUT_TIMEOUT 0xf5 + +#define CISS_CMD_STATUS_SUCCESS 0x0 +#define CISS_CMD_STATUS_TARGET_STATUS 0x1 +#define CISS_CMD_STATUS_DATA_UNDERRUN 0x2 +#define CISS_CMD_STATUS_DATA_OVERRUN 0x3 +#define CISS_CMD_STATUS_INVALID 0x4 +#define CISS_CMD_STATUS_PROTOCOL_ERROR 0x5 +#define CISS_CMD_STATUS_HARDWARE_ERROR 0x6 +#define CISS_CMD_STATUS_CONNECTION_LOST 0x7 +#define CISS_CMD_STATUS_ABORTED 0x8 +#define CISS_CMD_STATUS_ABORT_FAILED 0x9 +#define CISS_CMD_STATUS_UNSOLICITED_ABORT 0xa +#define CISS_CMD_STATUS_TIMEOUT 0xb +#define CISS_CMD_STATUS_UNABORTABLE 0xc +#define CISS_CMD_STATUS_TMF 0xd +#define CISS_CMD_STATUS_AIO_DISABLED 0xe + +#define PQI_NUM_EVENT_QUEUE_ELEMENTS 32 +#define PQI_EVENT_OQ_ELEMENT_LENGTH sizeof(struct pqi_event_response) + +#define PQI_EVENT_TYPE_HOTPLUG 0x1 +#define PQI_EVENT_TYPE_HARDWARE 0x2 +#define PQI_EVENT_TYPE_PHYSICAL_DEVICE 0x4 +#define PQI_EVENT_TYPE_LOGICAL_DEVICE 0x5 +#define PQI_EVENT_TYPE_AIO_STATE_CHANGE 0xfd +#define PQI_EVENT_TYPE_AIO_CONFIG_CHANGE 0xfe +#define PQI_EVENT_TYPE_HEARTBEAT 0xff + +#pragma pack() + +#define PQI_ERROR_BUFFER_ELEMENT_LENGTH \ + sizeof(struct pqi_raid_error_info) + +/* these values are based on our implementation */ +#define PQI_ADMIN_IQ_NUM_ELEMENTS 8 +#define PQI_ADMIN_OQ_NUM_ELEMENTS 20 +#define PQI_ADMIN_IQ_ELEMENT_LENGTH 64 +#define PQI_ADMIN_OQ_ELEMENT_LENGTH 64 + +#define PQI_OPERATIONAL_IQ_ELEMENT_LENGTH 128 +#define PQI_OPERATIONAL_OQ_ELEMENT_LENGTH 16 + +#define PQI_MIN_MSIX_VECTORS 1 +#define PQI_MAX_MSIX_VECTORS 64 + +/* these values are defined by the PQI spec */ +#define PQI_MAX_NUM_ELEMENTS_ADMIN_QUEUE 255 +#define PQI_MAX_NUM_ELEMENTS_OPERATIONAL_QUEUE 65535 +#define PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT 64 +#define PQI_QUEUE_ELEMENT_LENGTH_ALIGNMENT 16 +#define PQI_ADMIN_INDEX_ALIGNMENT 64 +#define PQI_OPERATIONAL_INDEX_ALIGNMENT 4 + +#define PQI_MIN_OPERATIONAL_QUEUE_ID 1 +#define PQI_MAX_OPERATIONAL_QUEUE_ID 65535 + +#define PQI_AIO_SERV_RESPONSE_COMPLETE 0 +#define PQI_AIO_SERV_RESPONSE_FAILURE 1 +#define PQI_AIO_SERV_RESPONSE_TMF_COMPLETE 2 +#define PQI_AIO_SERV_RESPONSE_TMF_SUCCEEDED 3 +#define PQI_AIO_SERV_RESPONSE_TMF_REJECTED 4 +#define PQI_AIO_SERV_RESPONSE_TMF_INCORRECT_LUN 5 + +#define PQI_AIO_STATUS_IO_ERROR 0x1 +#define PQI_AIO_STATUS_IO_ABORTED 0x2 +#define PQI_AIO_STATUS_NO_PATH_TO_DEVICE 0x3 +#define PQI_AIO_STATUS_INVALID_DEVICE 0x4 +#define PQI_AIO_STATUS_AIO_PATH_DISABLED 0xe +#define PQI_AIO_STATUS_UNDERRUN 0x51 +#define PQI_AIO_STATUS_OVERRUN 0x75 + +typedef u32 pqi_index_t; + +/* SOP data direction flags */ +#define SOP_NO_DIRECTION_FLAG 0 +#define SOP_WRITE_FLAG 1 /* host writes data to Data-Out */ + /* buffer */ +#define SOP_READ_FLAG 2 /* host receives data from Data-In */ + /* buffer */ +#define SOP_BIDIRECTIONAL 3 /* data is transferred from the */ + /* Data-Out buffer and data is */ + /* transferred to the Data-In buffer */ + +#define SOP_TASK_ATTRIBUTE_SIMPLE 0 +#define SOP_TASK_ATTRIBUTE_HEAD_OF_QUEUE 1 +#define SOP_TASK_ATTRIBUTE_ORDERED 2 +#define SOP_TASK_ATTRIBUTE_ACA 4 + +#define SOP_TMF_COMPLETE 0x0 +#define SOP_TMF_FUNCTION_SUCCEEDED 0x8 + +/* additional CDB bytes usage field codes */ +#define SOP_ADDITIONAL_CDB_BYTES_0 0 /* 16-byte CDB */ +#define SOP_ADDITIONAL_CDB_BYTES_4 1 /* 20-byte CDB */ +#define SOP_ADDITIONAL_CDB_BYTES_8 2 /* 24-byte CDB */ +#define SOP_ADDITIONAL_CDB_BYTES_12 3 /* 28-byte CDB */ +#define SOP_ADDITIONAL_CDB_BYTES_16 4 /* 32-byte CDB */ + +/* + * The purpose of this structure is to obtain proper alignment of objects in + * an admin queue pair. + */ +struct pqi_admin_queues_aligned { + __aligned(PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT) + u8 iq_element_array[PQI_ADMIN_IQ_ELEMENT_LENGTH] + [PQI_ADMIN_IQ_NUM_ELEMENTS]; + __aligned(PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT) + u8 oq_element_array[PQI_ADMIN_OQ_ELEMENT_LENGTH] + [PQI_ADMIN_OQ_NUM_ELEMENTS]; + __aligned(PQI_ADMIN_INDEX_ALIGNMENT) pqi_index_t iq_ci; + __aligned(PQI_ADMIN_INDEX_ALIGNMENT) pqi_index_t oq_pi; +}; + +struct pqi_admin_queues { + void *iq_element_array; + void *oq_element_array; + volatile pqi_index_t *iq_ci; + volatile pqi_index_t *oq_pi; + dma_addr_t iq_element_array_bus_addr; + dma_addr_t oq_element_array_bus_addr; + dma_addr_t iq_ci_bus_addr; + dma_addr_t oq_pi_bus_addr; + __le32 __iomem *iq_pi; + pqi_index_t iq_pi_copy; + __le32 __iomem *oq_ci; + pqi_index_t oq_ci_copy; + struct task_struct *task; + u16 int_msg_num; +}; + +struct pqi_queue_group { + struct pqi_ctrl_info *ctrl_info; /* backpointer */ + u16 iq_id[2]; + u16 oq_id; + u16 int_msg_num; + void *iq_element_array[2]; + void *oq_element_array; + dma_addr_t iq_element_array_bus_addr[2]; + dma_addr_t oq_element_array_bus_addr; + __le32 __iomem *iq_pi[2]; + pqi_index_t iq_pi_copy[2]; + volatile pqi_index_t *iq_ci[2]; + volatile pqi_index_t *oq_pi; + dma_addr_t iq_ci_bus_addr[2]; + dma_addr_t oq_pi_bus_addr; + __le32 __iomem *oq_ci; + pqi_index_t oq_ci_copy; + spinlock_t submit_lock[2]; /* protect submission queue */ + struct list_head request_list[2]; +}; + +struct pqi_event_queue { + u16 oq_id; + u16 int_msg_num; + void *oq_element_array; + volatile pqi_index_t *oq_pi; + dma_addr_t oq_element_array_bus_addr; + dma_addr_t oq_pi_bus_addr; + __le32 __iomem *oq_ci; + pqi_index_t oq_ci_copy; +}; + +#define PQI_DEFAULT_QUEUE_GROUP 0 +#define PQI_MAX_QUEUE_GROUPS PQI_MAX_MSIX_VECTORS + +struct pqi_encryption_info { + u16 data_encryption_key_index; + u32 encrypt_tweak_lower; + u32 encrypt_tweak_upper; +}; + +#define PQI_MAX_OUTSTANDING_REQUESTS ((u32)~0) +#define PQI_MAX_TRANSFER_SIZE (4 * 1024U * 1024U) + +#define RAID_MAP_MAX_ENTRIES 1024 + +#define PQI_PHYSICAL_DEVICE_BUS 0 +#define PQI_RAID_VOLUME_BUS 1 +#define PQI_HBA_BUS 2 +#define PQI_MAX_BUS PQI_HBA_BUS + +#pragma pack(1) + +struct report_lun_header { + __be32 list_length; + u8 extended_response; + u8 reserved[3]; +}; + +struct report_log_lun_extended_entry { + u8 lunid[8]; + u8 volume_id[16]; +}; + +struct report_log_lun_extended { + struct report_lun_header header; + struct report_log_lun_extended_entry lun_entries[1]; +}; + +struct report_phys_lun_extended_entry { + u8 lunid[8]; + __be64 wwid; + u8 device_type; + u8 device_flags; + u8 lun_count; /* number of LUNs in a multi-LUN device */ + u8 redundant_paths; + u32 aio_handle; +}; + +/* for device_flags field of struct report_phys_lun_extended_entry */ +#define REPORT_PHYS_LUN_DEV_FLAG_NON_DISK 0x1 +#define REPORT_PHYS_LUN_DEV_FLAG_AIO_ENABLED 0x8 + +struct report_phys_lun_extended { + struct report_lun_header header; + struct report_phys_lun_extended_entry lun_entries[1]; +}; + +struct raid_map_disk_data { + u32 aio_handle; + u8 xor_mult[2]; + u8 reserved[2]; +}; + +/* constants for flags field of RAID map */ +#define RAID_MAP_ENCRYPTION_ENABLED 0x1 + +struct raid_map { + __le32 structure_size; /* size of entire structure in bytes */ + __le32 volume_blk_size; /* bytes / block in the volume */ + __le64 volume_blk_cnt; /* logical blocks on the volume */ + u8 phys_blk_shift; /* shift factor to convert between */ + /* units of logical blocks and */ + /* physical disk blocks */ + u8 parity_rotation_shift; /* shift factor to convert between */ + /* units of logical stripes and */ + /* physical stripes */ + __le16 strip_size; /* blocks used on each disk / stripe */ + __le64 disk_starting_blk; /* first disk block used in volume */ + __le64 disk_blk_cnt; /* disk blocks used by volume / disk */ + __le16 data_disks_per_row; /* data disk entries / row in the map */ + __le16 metadata_disks_per_row; /* mirror/parity disk entries / row */ + /* in the map */ + __le16 row_cnt; /* rows in each layout map */ + __le16 layout_map_count; /* layout maps (1 map per */ + /* mirror parity group) */ + __le16 flags; + __le16 data_encryption_key_index; + u8 reserved[16]; + struct raid_map_disk_data disk_data[RAID_MAP_MAX_ENTRIES]; +}; + +#pragma pack() + +#define RAID_CTLR_LUNID "\0\0\0\0\0\0\0\0" + +struct pqi_scsi_dev { + int devtype; /* as reported by INQUIRY commmand */ + u8 device_type; /* as reported by */ + /* BMIC_IDENTIFY_PHYSICAL_DEVICE */ + /* only valid for devtype = TYPE_DISK */ + int bus; + int target; + int lun; + u8 scsi3addr[8]; + __be64 wwid; + u8 volume_id[16]; + u8 is_physical_device : 1; + u8 target_lun_valid : 1; + u8 expose_device : 1; + u8 no_uld_attach : 1; + u8 aio_enabled : 1; /* only valid for physical disks */ + u8 device_gone : 1; + u8 new_device : 1; + u8 keep_device : 1; + u8 volume_offline : 1; + u8 vendor[8]; /* bytes 8-15 of inquiry data */ + u8 model[16]; /* bytes 16-31 of inquiry data */ + u64 sas_address; + u8 raid_level; + u16 queue_depth; /* max. queue_depth for this device */ + u16 advertised_queue_depth; + u32 aio_handle; + u8 volume_status; + u8 active_path_index; + u8 path_map; + u8 bay; + u8 box[8]; + u16 phys_connector[8]; + int offload_configured; /* I/O accel RAID offload configured */ + int offload_enabled; /* I/O accel RAID offload enabled */ + int offload_enabled_pending; + int offload_to_mirror; /* Send next I/O accelerator RAID */ + /* offload request to mirror drive. */ + struct raid_map *raid_map; /* I/O accelerator RAID map */ + + struct pqi_sas_port *sas_port; + struct scsi_device *sdev; + + struct list_head scsi_device_list_entry; + struct list_head new_device_list_entry; + struct list_head add_list_entry; + struct list_head delete_list_entry; +}; + +/* VPD inquiry pages */ +#define SCSI_VPD_SUPPORTED_PAGES 0x0 /* standard page */ +#define SCSI_VPD_DEVICE_ID 0x83 /* standard page */ +#define CISS_VPD_LV_DEVICE_GEOMETRY 0xc1 /* vendor-specific page */ +#define CISS_VPD_LV_OFFLOAD_STATUS 0xc2 /* vendor-specific page */ +#define CISS_VPD_LV_STATUS 0xc3 /* vendor-specific page */ + +#define VPD_PAGE (1 << 8) + +#pragma pack(1) + +/* structure for CISS_VPD_LV_STATUS */ +struct ciss_vpd_logical_volume_status { + u8 peripheral_info; + u8 page_code; + u8 reserved; + u8 page_length; + u8 volume_status; + u8 reserved2[3]; + __be32 flags; +}; + +#pragma pack() + +/* constants for volume_status field of ciss_vpd_logical_volume_status */ +#define CISS_LV_OK 0 +#define CISS_LV_FAILED 1 +#define CISS_LV_NOT_CONFIGURED 2 +#define CISS_LV_DEGRADED 3 +#define CISS_LV_READY_FOR_RECOVERY 4 +#define CISS_LV_UNDERGOING_RECOVERY 5 +#define CISS_LV_WRONG_PHYSICAL_DRIVE_REPLACED 6 +#define CISS_LV_PHYSICAL_DRIVE_CONNECTION_PROBLEM 7 +#define CISS_LV_HARDWARE_OVERHEATING 8 +#define CISS_LV_HARDWARE_HAS_OVERHEATED 9 +#define CISS_LV_UNDERGOING_EXPANSION 10 +#define CISS_LV_NOT_AVAILABLE 11 +#define CISS_LV_QUEUED_FOR_EXPANSION 12 +#define CISS_LV_DISABLED_SCSI_ID_CONFLICT 13 +#define CISS_LV_EJECTED 14 +#define CISS_LV_UNDERGOING_ERASE 15 +/* state 16 not used */ +#define CISS_LV_READY_FOR_PREDICTIVE_SPARE_REBUILD 17 +#define CISS_LV_UNDERGOING_RPI 18 +#define CISS_LV_PENDING_RPI 19 +#define CISS_LV_ENCRYPTED_NO_KEY 20 +/* state 21 not used */ +#define CISS_LV_UNDERGOING_ENCRYPTION 22 +#define CISS_LV_UNDERGOING_ENCRYPTION_REKEYING 23 +#define CISS_LV_ENCRYPTED_IN_NON_ENCRYPTED_CONTROLLER 24 +#define CISS_LV_PENDING_ENCRYPTION 25 +#define CISS_LV_PENDING_ENCRYPTION_REKEYING 26 +#define CISS_LV_NOT_SUPPORTED 27 +#define CISS_LV_STATUS_UNAVAILABLE 255 + +/* constants for flags field of ciss_vpd_logical_volume_status */ +#define CISS_LV_FLAGS_NO_HOST_IO 0x1 /* volume not available for */ + /* host I/O */ + +/* for SAS hosts and SAS expanders */ +struct pqi_sas_node { + struct device *parent_dev; + struct list_head port_list_head; +}; + +struct pqi_sas_port { + struct list_head port_list_entry; + u64 sas_address; + struct sas_port *port; + int next_phy_index; + struct list_head phy_list_head; + struct pqi_sas_node *parent_node; + struct sas_rphy *rphy; +}; + +struct pqi_sas_phy { + struct list_head phy_list_entry; + struct sas_phy *phy; + struct pqi_sas_port *parent_port; + bool added_to_port; +}; + +struct pqi_io_request { + atomic_t refcount; + u16 index; + void (*io_complete_callback)(struct pqi_io_request *io_request, + void *context); + void *context; + int status; + struct scsi_cmnd *scmd; + void *error_info; + struct pqi_sg_descriptor *sg_chain_buffer; + dma_addr_t sg_chain_buffer_dma_handle; + void *iu; + struct list_head request_list_entry; +}; + +/* for indexing into the pending_events[] field of struct pqi_ctrl_info */ +#define PQI_EVENT_HEARTBEAT 0 +#define PQI_EVENT_HOTPLUG 1 +#define PQI_EVENT_HARDWARE 2 +#define PQI_EVENT_PHYSICAL_DEVICE 3 +#define PQI_EVENT_LOGICAL_DEVICE 4 +#define PQI_EVENT_AIO_STATE_CHANGE 5 +#define PQI_EVENT_AIO_CONFIG_CHANGE 6 +#define PQI_NUM_SUPPORTED_EVENTS 7 + +struct pqi_event { + bool pending; + u8 event_type; + __le16 event_id; + __le32 additional_event_id; +}; + +#define PQI_RESERVED_IO_SLOTS_LUN_RESET 1 +#define PQI_RESERVED_IO_SLOTS_EVENT_ACK PQI_NUM_SUPPORTED_EVENTS +#define PQI_RESERVED_IO_SLOTS_SYNCHRONOUS_REQUESTS 3 +#define PQI_RESERVED_IO_SLOTS \ + (PQI_RESERVED_IO_SLOTS_LUN_RESET + PQI_RESERVED_IO_SLOTS_EVENT_ACK + \ + PQI_RESERVED_IO_SLOTS_SYNCHRONOUS_REQUESTS) + +struct pqi_ctrl_info { + unsigned int ctrl_id; + struct pci_dev *pci_dev; + char firmware_version[11]; + void __iomem *iomem_base; + struct pqi_ctrl_registers __iomem *registers; + struct pqi_device_registers __iomem *pqi_registers; + u32 max_sg_entries; + u32 config_table_offset; + u32 config_table_length; + u16 max_inbound_queues; + u16 max_elements_per_iq; + u16 max_iq_element_length; + u16 max_outbound_queues; + u16 max_elements_per_oq; + u16 max_oq_element_length; + u32 max_transfer_size; + u32 max_outstanding_requests; + u32 max_io_slots; + unsigned int scsi_ml_can_queue; + unsigned short sg_tablesize; + unsigned int max_sectors; + u32 error_buffer_length; + void *error_buffer; + dma_addr_t error_buffer_dma_handle; + size_t sg_chain_buffer_length; + unsigned int num_queue_groups; + unsigned int num_active_queue_groups; + u16 num_elements_per_iq; + u16 num_elements_per_oq; + u16 max_inbound_iu_length_per_firmware; + u16 max_inbound_iu_length; + unsigned int max_sg_per_iu; + void *admin_queue_memory_base; + u32 admin_queue_memory_length; + dma_addr_t admin_queue_memory_base_dma_handle; + void *queue_memory_base; + u32 queue_memory_length; + dma_addr_t queue_memory_base_dma_handle; + struct pqi_admin_queues admin_queues; + struct pqi_queue_group queue_groups[PQI_MAX_QUEUE_GROUPS]; + struct pqi_event_queue event_queue; + int max_msix_vectors; + int num_msix_vectors_enabled; + int num_msix_vectors_initialized; + u32 msix_vectors[PQI_MAX_MSIX_VECTORS]; + void *intr_data[PQI_MAX_MSIX_VECTORS]; + int event_irq; + struct Scsi_Host *scsi_host; + + struct mutex scan_mutex; + u8 inbound_spanning_supported : 1; + u8 outbound_spanning_supported : 1; + u8 pqi_mode_enabled : 1; + u8 controller_online : 1; + u8 heartbeat_timer_started : 1; + + struct list_head scsi_device_list; + spinlock_t scsi_device_list_lock; + + struct delayed_work rescan_work; + struct delayed_work update_time_work; + + struct pqi_sas_node *sas_host; + u64 sas_address; + + struct pqi_io_request *io_request_pool; + u16 next_io_request_slot; + + struct pqi_event pending_events[PQI_NUM_SUPPORTED_EVENTS]; + struct work_struct event_work; + + atomic_t num_interrupts; + int previous_num_interrupts; + unsigned int num_heartbeats_requested; + struct timer_list heartbeat_timer; + + struct semaphore sync_request_sem; + struct semaphore lun_reset_sem; +}; + +enum pqi_ctrl_mode { + UNKNOWN, + PQI_MODE +}; + +/* + * assume worst case: SATA queue depth of 31 minus 4 internal firmware commands + */ +#define PQI_PHYSICAL_DISK_DEFAULT_MAX_QUEUE_DEPTH 27 + +/* 0 = no limit */ +#define PQI_LOGICAL_DRIVE_DEFAULT_MAX_QUEUE_DEPTH 0 + +/* CISS commands */ +#define CISS_READ 0xc0 +#define CISS_REPORT_LOG 0xc2 /* Report Logical LUNs */ +#define CISS_REPORT_PHYS 0xc3 /* Report Physical LUNs */ +#define CISS_GET_RAID_MAP 0xc8 + +/* constants for CISS_REPORT_LOG/CISS_REPORT_PHYS commands */ +#define CISS_REPORT_LOG_EXTENDED 0x1 +#define CISS_REPORT_PHYS_EXTENDED 0x2 + +/* BMIC commands */ +#define BMIC_IDENTIFY_CONTROLLER 0x11 +#define BMIC_IDENTIFY_PHYSICAL_DEVICE 0x15 +#define BMIC_READ 0x26 +#define BMIC_WRITE 0x27 +#define BMIC_SENSE_CONTROLLER_PARAMETERS 0x64 +#define BMIC_SENSE_SUBSYSTEM_INFORMATION 0x66 +#define BMIC_WRITE_HOST_WELLNESS 0xa5 +#define BMIC_CACHE_FLUSH 0xc2 + +#define SA_CACHE_FLUSH 0x01 + +#define MASKED_DEVICE(lunid) ((lunid)[3] & 0xc0) +#define CISS_GET_BUS(lunid) ((lunid)[7] & 0x3f) +#define CISS_GET_LEVEL_2_TARGET(lunid) ((lunid)[6]) +#define CISS_GET_DRIVE_NUMBER(lunid) \ + (((CISS_GET_BUS((lunid)) - 1) << 8) + \ + CISS_GET_LEVEL_2_TARGET((lunid))) + +#define NO_TIMEOUT ((unsigned long) -1) + +#pragma pack(1) + +struct bmic_identify_controller { + u8 configured_logical_drive_count; + __le32 configuration_signature; + u8 firmware_version[4]; + u8 reserved[145]; + __le16 extended_logical_unit_count; + u8 reserved1[34]; + __le16 firmware_build_number; + u8 reserved2[100]; + u8 controller_mode; + u8 reserved3[32]; +}; + +struct bmic_identify_physical_device { + u8 scsi_bus; /* SCSI Bus number on controller */ + u8 scsi_id; /* SCSI ID on this bus */ + __le16 block_size; /* sector size in bytes */ + __le32 total_blocks; /* number for sectors on drive */ + __le32 reserved_blocks; /* controller reserved (RIS) */ + u8 model[40]; /* Physical Drive Model */ + u8 serial_number[40]; /* Drive Serial Number */ + u8 firmware_revision[8]; /* drive firmware revision */ + u8 scsi_inquiry_bits; /* inquiry byte 7 bits */ + u8 compaq_drive_stamp; /* 0 means drive not stamped */ + u8 last_failure_reason; + u8 flags; + u8 more_flags; + u8 scsi_lun; /* SCSI LUN for phys drive */ + u8 yet_more_flags; + u8 even_more_flags; + __le32 spi_speed_rules; + u8 phys_connector[2]; /* connector number on controller */ + u8 phys_box_on_bus; /* phys enclosure this drive resides */ + u8 phys_bay_in_box; /* phys drv bay this drive resides */ + __le32 rpm; /* drive rotational speed in RPM */ + u8 device_type; /* type of drive */ + u8 sata_version; /* only valid when device_type = */ + /* BMIC_DEVICE_TYPE_SATA */ + __le64 big_total_block_count; + __le64 ris_starting_lba; + __le32 ris_size; + u8 wwid[20]; + u8 controller_phy_map[32]; + __le16 phy_count; + u8 phy_connected_dev_type[256]; + u8 phy_to_drive_bay_num[256]; + __le16 phy_to_attached_dev_index[256]; + u8 box_index; + u8 reserved; + __le16 extra_physical_drive_flags; + u8 negotiated_link_rate[256]; + u8 phy_to_phy_map[256]; + u8 redundant_path_present_map; + u8 redundant_path_failure_map; + u8 active_path_number; + __le16 alternate_paths_phys_connector[8]; + u8 alternate_paths_phys_box_on_port[8]; + u8 multi_lun_device_lun_count; + u8 minimum_good_fw_revision[8]; + u8 unique_inquiry_bytes[20]; + u8 current_temperature_degreesC; + u8 temperature_threshold_degreesC; + u8 max_temperature_degreesC; + u8 logical_blocks_per_phys_block_exp; + __le16 current_queue_depth_limit; + u8 switch_name[10]; + __le16 switch_port; + u8 alternate_paths_switch_name[40]; + u8 alternate_paths_switch_port[8]; + __le16 power_on_hours; + __le16 percent_endurance_used; + u8 drive_authentication; + u8 smart_carrier_authentication; + u8 smart_carrier_app_fw_version; + u8 smart_carrier_bootloader_fw_version; + u8 encryption_key_name[64]; + __le32 misc_drive_flags; + __le16 dek_index; + u8 padding[112]; +}; + +#pragma pack() + +int pqi_add_sas_host(struct Scsi_Host *shost, struct pqi_ctrl_info *ctrl_info); +void pqi_delete_sas_host(struct pqi_ctrl_info *ctrl_info); +int pqi_add_sas_device(struct pqi_sas_node *pqi_sas_node, + struct pqi_scsi_dev *device); +void pqi_remove_sas_device(struct pqi_scsi_dev *device); +struct pqi_scsi_dev *pqi_find_device_by_sas_rphy( + struct pqi_ctrl_info *ctrl_info, struct sas_rphy *rphy); + +extern struct sas_function_template pqi_sas_transport_functions; + +#if !defined(readq) +#define readq readq +static inline u64 readq(const volatile void __iomem *addr) +{ + u32 lower32; + u32 upper32; + + lower32 = readl(addr); + upper32 = readl(addr + 4); + + return ((u64)upper32 << 32) | lower32; +} +#endif + +#if !defined(writeq) +#define writeq writeq +static inline void writeq(u64 value, volatile void __iomem *addr) +{ + u32 lower32; + u32 upper32; + + lower32 = lower_32_bits(value); + upper32 = upper_32_bits(value); + + writel(lower32, addr); + writel(upper32, addr + 4); +} +#endif + +#endif /* _SMARTPQI_H */ diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c new file mode 100644 index 000000000000..a535b2661f38 --- /dev/null +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -0,0 +1,6303 @@ +/* + * driver for Microsemi PQI-based storage controllers + * Copyright (c) 2016 Microsemi Corporation + * Copyright (c) 2016 PMC-Sierra, Inc. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more details. + * + * Questions/Comments/Bugfixes to esc.storagedev@microsemi.com + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include <linux/rtc.h> +#include <linux/bcd.h> +#include <linux/cciss_ioctl.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_eh.h> +#include <scsi/scsi_transport_sas.h> +#include <asm/unaligned.h> +#include "smartpqi.h" +#include "smartpqi_sis.h" + +#if !defined(BUILD_TIMESTAMP) +#define BUILD_TIMESTAMP +#endif + +#define DRIVER_VERSION "0.9.13-370" +#define DRIVER_MAJOR 0 +#define DRIVER_MINOR 9 +#define DRIVER_RELEASE 13 +#define DRIVER_REVISION 370 + +#define DRIVER_NAME "Microsemi PQI Driver (v" DRIVER_VERSION ")" +#define DRIVER_NAME_SHORT "smartpqi" + +MODULE_AUTHOR("Microsemi"); +MODULE_DESCRIPTION("Driver for Microsemi Smart Family Controller version " + DRIVER_VERSION); +MODULE_SUPPORTED_DEVICE("Microsemi Smart Family Controllers"); +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); + +#define PQI_ENABLE_MULTI_QUEUE_SUPPORT 0 + +static char *hpe_branded_controller = "HPE Smart Array Controller"; +static char *microsemi_branded_controller = "Microsemi Smart Family Controller"; + +static void pqi_take_ctrl_offline(struct pqi_ctrl_info *ctrl_info); +static int pqi_scan_scsi_devices(struct pqi_ctrl_info *ctrl_info); +static void pqi_scan_start(struct Scsi_Host *shost); +static void pqi_start_io(struct pqi_ctrl_info *ctrl_info, + struct pqi_queue_group *queue_group, enum pqi_io_path path, + struct pqi_io_request *io_request); +static int pqi_submit_raid_request_synchronous(struct pqi_ctrl_info *ctrl_info, + struct pqi_iu_header *request, unsigned int flags, + struct pqi_raid_error_info *error_info, unsigned long timeout_msecs); +static int pqi_aio_submit_io(struct pqi_ctrl_info *ctrl_info, + struct scsi_cmnd *scmd, u32 aio_handle, u8 *cdb, + unsigned int cdb_length, struct pqi_queue_group *queue_group, + struct pqi_encryption_info *encryption_info); + +/* for flags argument to pqi_submit_raid_request_synchronous() */ +#define PQI_SYNC_FLAGS_INTERRUPTABLE 0x1 + +static struct scsi_transport_template *pqi_sas_transport_template; + +static atomic_t pqi_controller_count = ATOMIC_INIT(0); + +static int pqi_disable_device_id_wildcards; +module_param_named(disable_device_id_wildcards, + pqi_disable_device_id_wildcards, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(disable_device_id_wildcards, + "Disable device ID wildcards."); + +static char *raid_levels[] = { + "RAID-0", + "RAID-4", + "RAID-1(1+0)", + "RAID-5", + "RAID-5+1", + "RAID-ADG", + "RAID-1(ADM)", +}; + +static char *pqi_raid_level_to_string(u8 raid_level) +{ + if (raid_level < ARRAY_SIZE(raid_levels)) + return raid_levels[raid_level]; + + return ""; +} + +#define SA_RAID_0 0 +#define SA_RAID_4 1 +#define SA_RAID_1 2 /* also used for RAID 10 */ +#define SA_RAID_5 3 /* also used for RAID 50 */ +#define SA_RAID_51 4 +#define SA_RAID_6 5 /* also used for RAID 60 */ +#define SA_RAID_ADM 6 /* also used for RAID 1+0 ADM */ +#define SA_RAID_MAX SA_RAID_ADM +#define SA_RAID_UNKNOWN 0xff + +static inline void pqi_scsi_done(struct scsi_cmnd *scmd) +{ + scmd->scsi_done(scmd); +} + +static inline bool pqi_scsi3addr_equal(u8 *scsi3addr1, u8 *scsi3addr2) +{ + return memcmp(scsi3addr1, scsi3addr2, 8) == 0; +} + +static inline struct pqi_ctrl_info *shost_to_hba(struct Scsi_Host *shost) +{ + void *hostdata = shost_priv(shost); + + return *((struct pqi_ctrl_info **)hostdata); +} + +static inline bool pqi_is_logical_device(struct pqi_scsi_dev *device) +{ + return !device->is_physical_device; +} + +static inline bool pqi_ctrl_offline(struct pqi_ctrl_info *ctrl_info) +{ + return !ctrl_info->controller_online; +} + +static inline void pqi_check_ctrl_health(struct pqi_ctrl_info *ctrl_info) +{ + if (ctrl_info->controller_online) + if (!sis_is_firmware_running(ctrl_info)) + pqi_take_ctrl_offline(ctrl_info); +} + +static inline bool pqi_is_hba_lunid(u8 *scsi3addr) +{ + return pqi_scsi3addr_equal(scsi3addr, RAID_CTLR_LUNID); +} + +static inline enum pqi_ctrl_mode pqi_get_ctrl_mode( + struct pqi_ctrl_info *ctrl_info) +{ + return sis_read_driver_scratch(ctrl_info); +} + +static inline void pqi_save_ctrl_mode(struct pqi_ctrl_info *ctrl_info, + enum pqi_ctrl_mode mode) +{ + sis_write_driver_scratch(ctrl_info, mode); +} + +#define PQI_RESCAN_WORK_INTERVAL (10 * HZ) + +static inline void pqi_schedule_rescan_worker(struct pqi_ctrl_info *ctrl_info) +{ + schedule_delayed_work(&ctrl_info->rescan_work, + PQI_RESCAN_WORK_INTERVAL); +} + +static int pqi_map_single(struct pci_dev *pci_dev, + struct pqi_sg_descriptor *sg_descriptor, void *buffer, + size_t buffer_length, int data_direction) +{ + dma_addr_t bus_address; + + if (!buffer || buffer_length == 0 || data_direction == PCI_DMA_NONE) + return 0; + + bus_address = pci_map_single(pci_dev, buffer, buffer_length, + data_direction); + if (pci_dma_mapping_error(pci_dev, bus_address)) + return -ENOMEM; + + put_unaligned_le64((u64)bus_address, &sg_descriptor->address); + put_unaligned_le32(buffer_length, &sg_descriptor->length); + put_unaligned_le32(CISS_SG_LAST, &sg_descriptor->flags); + + return 0; +} + +static void pqi_pci_unmap(struct pci_dev *pci_dev, + struct pqi_sg_descriptor *descriptors, int num_descriptors, + int data_direction) +{ + int i; + + if (data_direction == PCI_DMA_NONE) + return; + + for (i = 0; i < num_descriptors; i++) + pci_unmap_single(pci_dev, + (dma_addr_t)get_unaligned_le64(&descriptors[i].address), + get_unaligned_le32(&descriptors[i].length), + data_direction); +} + +static int pqi_build_raid_path_request(struct pqi_ctrl_info *ctrl_info, + struct pqi_raid_path_request *request, u8 cmd, + u8 *scsi3addr, void *buffer, size_t buffer_length, + u16 vpd_page, int *pci_direction) +{ + u8 *cdb; + int pci_dir; + + memset(request, 0, sizeof(*request)); + + request->header.iu_type = PQI_REQUEST_IU_RAID_PATH_IO; + put_unaligned_le16(offsetof(struct pqi_raid_path_request, + sg_descriptors[1]) - PQI_REQUEST_HEADER_LENGTH, + &request->header.iu_length); + put_unaligned_le32(buffer_length, &request->buffer_length); + memcpy(request->lun_number, scsi3addr, sizeof(request->lun_number)); + request->task_attribute = SOP_TASK_ATTRIBUTE_SIMPLE; + request->additional_cdb_bytes_usage = SOP_ADDITIONAL_CDB_BYTES_0; + + cdb = request->cdb; + + switch (cmd) { + case INQUIRY: + request->data_direction = SOP_READ_FLAG; + cdb[0] = INQUIRY; + if (vpd_page & VPD_PAGE) { + cdb[1] = 0x1; + cdb[2] = (u8)vpd_page; + } + cdb[4] = (u8)buffer_length; + break; + case CISS_REPORT_LOG: + case CISS_REPORT_PHYS: + request->data_direction = SOP_READ_FLAG; + cdb[0] = cmd; + if (cmd == CISS_REPORT_PHYS) + cdb[1] = CISS_REPORT_PHYS_EXTENDED; + else + cdb[1] = CISS_REPORT_LOG_EXTENDED; + put_unaligned_be32(buffer_length, &cdb[6]); + break; + case CISS_GET_RAID_MAP: + request->data_direction = SOP_READ_FLAG; + cdb[0] = CISS_READ; + cdb[1] = CISS_GET_RAID_MAP; + put_unaligned_be32(buffer_length, &cdb[6]); + break; + case SA_CACHE_FLUSH: + request->data_direction = SOP_WRITE_FLAG; + cdb[0] = BMIC_WRITE; + cdb[6] = BMIC_CACHE_FLUSH; + put_unaligned_be16(buffer_length, &cdb[7]); + break; + case BMIC_IDENTIFY_CONTROLLER: + case BMIC_IDENTIFY_PHYSICAL_DEVICE: + request->data_direction = SOP_READ_FLAG; + cdb[0] = BMIC_READ; + cdb[6] = cmd; + put_unaligned_be16(buffer_length, &cdb[7]); + break; + case BMIC_WRITE_HOST_WELLNESS: + request->data_direction = SOP_WRITE_FLAG; + cdb[0] = BMIC_WRITE; + cdb[6] = cmd; + put_unaligned_be16(buffer_length, &cdb[7]); + break; + default: + dev_err(&ctrl_info->pci_dev->dev, "unknown command 0x%c\n", + cmd); + WARN_ON(cmd); + break; + } + + switch (request->data_direction) { + case SOP_READ_FLAG: + pci_dir = PCI_DMA_FROMDEVICE; + break; + case SOP_WRITE_FLAG: + pci_dir = PCI_DMA_TODEVICE; + break; + case SOP_NO_DIRECTION_FLAG: + pci_dir = PCI_DMA_NONE; + break; + default: + pci_dir = PCI_DMA_BIDIRECTIONAL; + break; + } + + *pci_direction = pci_dir; + + return pqi_map_single(ctrl_info->pci_dev, &request->sg_descriptors[0], + buffer, buffer_length, pci_dir); +} + +static struct pqi_io_request *pqi_alloc_io_request( + struct pqi_ctrl_info *ctrl_info) +{ + struct pqi_io_request *io_request; + u16 i = ctrl_info->next_io_request_slot; /* benignly racy */ + + while (1) { + io_request = &ctrl_info->io_request_pool[i]; + if (atomic_inc_return(&io_request->refcount) == 1) + break; + atomic_dec(&io_request->refcount); + i = (i + 1) % ctrl_info->max_io_slots; + } + + /* benignly racy */ + ctrl_info->next_io_request_slot = (i + 1) % ctrl_info->max_io_slots; + + io_request->scmd = NULL; + io_request->status = 0; + io_request->error_info = NULL; + + return io_request; +} + +static void pqi_free_io_request(struct pqi_io_request *io_request) +{ + atomic_dec(&io_request->refcount); +} + +static int pqi_identify_controller(struct pqi_ctrl_info *ctrl_info, + struct bmic_identify_controller *buffer) +{ + int rc; + int pci_direction; + struct pqi_raid_path_request request; + + rc = pqi_build_raid_path_request(ctrl_info, &request, + BMIC_IDENTIFY_CONTROLLER, RAID_CTLR_LUNID, buffer, + sizeof(*buffer), 0, &pci_direction); + if (rc) + return rc; + + rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, 0, + NULL, NO_TIMEOUT); + + pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, + pci_direction); + + return rc; +} + +static int pqi_scsi_inquiry(struct pqi_ctrl_info *ctrl_info, + u8 *scsi3addr, u16 vpd_page, void *buffer, size_t buffer_length) +{ + int rc; + int pci_direction; + struct pqi_raid_path_request request; + + rc = pqi_build_raid_path_request(ctrl_info, &request, + INQUIRY, scsi3addr, buffer, buffer_length, vpd_page, + &pci_direction); + if (rc) + return rc; + + rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, 0, + NULL, NO_TIMEOUT); + + pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, + pci_direction); + + return rc; +} + +static int pqi_identify_physical_device(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device, + struct bmic_identify_physical_device *buffer, + size_t buffer_length) +{ + int rc; + int pci_direction; + u16 bmic_device_index; + struct pqi_raid_path_request request; + + rc = pqi_build_raid_path_request(ctrl_info, &request, + BMIC_IDENTIFY_PHYSICAL_DEVICE, RAID_CTLR_LUNID, buffer, + buffer_length, 0, &pci_direction); + if (rc) + return rc; + + bmic_device_index = CISS_GET_DRIVE_NUMBER(device->scsi3addr); + request.cdb[2] = (u8)bmic_device_index; + request.cdb[9] = (u8)(bmic_device_index >> 8); + + rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, + 0, NULL, NO_TIMEOUT); + + pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, + pci_direction); + + return rc; +} + +#define SA_CACHE_FLUSH_BUFFER_LENGTH 4 + +static int pqi_flush_cache(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + struct pqi_raid_path_request request; + int pci_direction; + u8 *buffer; + + /* + * Don't bother trying to flush the cache if the controller is + * locked up. + */ + if (pqi_ctrl_offline(ctrl_info)) + return -ENXIO; + + buffer = kzalloc(SA_CACHE_FLUSH_BUFFER_LENGTH, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + rc = pqi_build_raid_path_request(ctrl_info, &request, + SA_CACHE_FLUSH, RAID_CTLR_LUNID, buffer, + SA_CACHE_FLUSH_BUFFER_LENGTH, 0, &pci_direction); + if (rc) + goto out; + + rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, + 0, NULL, NO_TIMEOUT); + + pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, + pci_direction); + +out: + kfree(buffer); + + return rc; +} + +static int pqi_write_host_wellness(struct pqi_ctrl_info *ctrl_info, + void *buffer, size_t buffer_length) +{ + int rc; + struct pqi_raid_path_request request; + int pci_direction; + + rc = pqi_build_raid_path_request(ctrl_info, &request, + BMIC_WRITE_HOST_WELLNESS, RAID_CTLR_LUNID, buffer, + buffer_length, 0, &pci_direction); + if (rc) + return rc; + + rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, + 0, NULL, NO_TIMEOUT); + + pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, + pci_direction); + + return rc; +} + +#pragma pack(1) + +struct bmic_host_wellness_driver_version { + u8 start_tag[4]; + u8 driver_version_tag[2]; + __le16 driver_version_length; + char driver_version[32]; + u8 end_tag[2]; +}; + +#pragma pack() + +static int pqi_write_driver_version_to_host_wellness( + struct pqi_ctrl_info *ctrl_info) +{ + int rc; + struct bmic_host_wellness_driver_version *buffer; + size_t buffer_length; + + buffer_length = sizeof(*buffer); + + buffer = kmalloc(buffer_length, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + buffer->start_tag[0] = '<'; + buffer->start_tag[1] = 'H'; + buffer->start_tag[2] = 'W'; + buffer->start_tag[3] = '>'; + buffer->driver_version_tag[0] = 'D'; + buffer->driver_version_tag[1] = 'V'; + put_unaligned_le16(sizeof(buffer->driver_version), + &buffer->driver_version_length); + strncpy(buffer->driver_version, DRIVER_VERSION, + sizeof(buffer->driver_version) - 1); + buffer->driver_version[sizeof(buffer->driver_version) - 1] = '\0'; + buffer->end_tag[0] = 'Z'; + buffer->end_tag[1] = 'Z'; + + rc = pqi_write_host_wellness(ctrl_info, buffer, buffer_length); + + kfree(buffer); + + return rc; +} + +#pragma pack(1) + +struct bmic_host_wellness_time { + u8 start_tag[4]; + u8 time_tag[2]; + __le16 time_length; + u8 time[8]; + u8 dont_write_tag[2]; + u8 end_tag[2]; +}; + +#pragma pack() + +static int pqi_write_current_time_to_host_wellness( + struct pqi_ctrl_info *ctrl_info) +{ + int rc; + struct bmic_host_wellness_time *buffer; + size_t buffer_length; + time64_t local_time; + unsigned int year; + struct timeval time; + struct rtc_time tm; + + buffer_length = sizeof(*buffer); + + buffer = kmalloc(buffer_length, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + buffer->start_tag[0] = '<'; + buffer->start_tag[1] = 'H'; + buffer->start_tag[2] = 'W'; + buffer->start_tag[3] = '>'; + buffer->time_tag[0] = 'T'; + buffer->time_tag[1] = 'D'; + put_unaligned_le16(sizeof(buffer->time), + &buffer->time_length); + + do_gettimeofday(&time); + local_time = time.tv_sec - (sys_tz.tz_minuteswest * 60); + rtc_time64_to_tm(local_time, &tm); + year = tm.tm_year + 1900; + + buffer->time[0] = bin2bcd(tm.tm_hour); + buffer->time[1] = bin2bcd(tm.tm_min); + buffer->time[2] = bin2bcd(tm.tm_sec); + buffer->time[3] = 0; + buffer->time[4] = bin2bcd(tm.tm_mon + 1); + buffer->time[5] = bin2bcd(tm.tm_mday); + buffer->time[6] = bin2bcd(year / 100); + buffer->time[7] = bin2bcd(year % 100); + + buffer->dont_write_tag[0] = 'D'; + buffer->dont_write_tag[1] = 'W'; + buffer->end_tag[0] = 'Z'; + buffer->end_tag[1] = 'Z'; + + rc = pqi_write_host_wellness(ctrl_info, buffer, buffer_length); + + kfree(buffer); + + return rc; +} + +#define PQI_UPDATE_TIME_WORK_INTERVAL (24UL * 60 * 60 * HZ) + +static void pqi_update_time_worker(struct work_struct *work) +{ + int rc; + struct pqi_ctrl_info *ctrl_info; + + ctrl_info = container_of(to_delayed_work(work), struct pqi_ctrl_info, + update_time_work); + + rc = pqi_write_current_time_to_host_wellness(ctrl_info); + if (rc) + dev_warn(&ctrl_info->pci_dev->dev, + "error updating time on controller\n"); + + schedule_delayed_work(&ctrl_info->update_time_work, + PQI_UPDATE_TIME_WORK_INTERVAL); +} + +static inline void pqi_schedule_update_time_worker( + struct pqi_ctrl_info *ctrl_info) +{ + schedule_delayed_work(&ctrl_info->update_time_work, 0); +} + +static int pqi_report_luns(struct pqi_ctrl_info *ctrl_info, u8 cmd, + void *buffer, size_t buffer_length) +{ + int rc; + int pci_direction; + struct pqi_raid_path_request request; + + rc = pqi_build_raid_path_request(ctrl_info, &request, + cmd, RAID_CTLR_LUNID, buffer, buffer_length, 0, &pci_direction); + if (rc) + return rc; + + rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, 0, + NULL, NO_TIMEOUT); + + pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, + pci_direction); + + return rc; +} + +static int pqi_report_phys_logical_luns(struct pqi_ctrl_info *ctrl_info, u8 cmd, + void **buffer) +{ + int rc; + size_t lun_list_length; + size_t lun_data_length; + size_t new_lun_list_length; + void *lun_data = NULL; + struct report_lun_header *report_lun_header; + + report_lun_header = kmalloc(sizeof(*report_lun_header), GFP_KERNEL); + if (!report_lun_header) { + rc = -ENOMEM; + goto out; + } + + rc = pqi_report_luns(ctrl_info, cmd, report_lun_header, + sizeof(*report_lun_header)); + if (rc) + goto out; + + lun_list_length = get_unaligned_be32(&report_lun_header->list_length); + +again: + lun_data_length = sizeof(struct report_lun_header) + lun_list_length; + + lun_data = kmalloc(lun_data_length, GFP_KERNEL); + if (!lun_data) { + rc = -ENOMEM; + goto out; + } + + if (lun_list_length == 0) { + memcpy(lun_data, report_lun_header, sizeof(*report_lun_header)); + goto out; + } + + rc = pqi_report_luns(ctrl_info, cmd, lun_data, lun_data_length); + if (rc) + goto out; + + new_lun_list_length = get_unaligned_be32( + &((struct report_lun_header *)lun_data)->list_length); + + if (new_lun_list_length > lun_list_length) { + lun_list_length = new_lun_list_length; + kfree(lun_data); + goto again; + } + +out: + kfree(report_lun_header); + + if (rc) { + kfree(lun_data); + lun_data = NULL; + } + + *buffer = lun_data; + + return rc; +} + +static inline int pqi_report_phys_luns(struct pqi_ctrl_info *ctrl_info, + void **buffer) +{ + return pqi_report_phys_logical_luns(ctrl_info, CISS_REPORT_PHYS, + buffer); +} + +static inline int pqi_report_logical_luns(struct pqi_ctrl_info *ctrl_info, + void **buffer) +{ + return pqi_report_phys_logical_luns(ctrl_info, CISS_REPORT_LOG, buffer); +} + +static int pqi_get_device_lists(struct pqi_ctrl_info *ctrl_info, + struct report_phys_lun_extended **physdev_list, + struct report_log_lun_extended **logdev_list) +{ + int rc; + size_t logdev_list_length; + size_t logdev_data_length; + struct report_log_lun_extended *internal_logdev_list; + struct report_log_lun_extended *logdev_data; + struct report_lun_header report_lun_header; + + rc = pqi_report_phys_luns(ctrl_info, (void **)physdev_list); + if (rc) + dev_err(&ctrl_info->pci_dev->dev, + "report physical LUNs failed\n"); + + rc = pqi_report_logical_luns(ctrl_info, (void **)logdev_list); + if (rc) + dev_err(&ctrl_info->pci_dev->dev, + "report logical LUNs failed\n"); + + /* + * Tack the controller itself onto the end of the logical device list. + */ + + logdev_data = *logdev_list; + + if (logdev_data) { + logdev_list_length = + get_unaligned_be32(&logdev_data->header.list_length); + } else { + memset(&report_lun_header, 0, sizeof(report_lun_header)); + logdev_data = + (struct report_log_lun_extended *)&report_lun_header; + logdev_list_length = 0; + } + + logdev_data_length = sizeof(struct report_lun_header) + + logdev_list_length; + + internal_logdev_list = kmalloc(logdev_data_length + + sizeof(struct report_log_lun_extended), GFP_KERNEL); + if (!internal_logdev_list) { + kfree(*logdev_list); + *logdev_list = NULL; + return -ENOMEM; + } + + memcpy(internal_logdev_list, logdev_data, logdev_data_length); + memset((u8 *)internal_logdev_list + logdev_data_length, 0, + sizeof(struct report_log_lun_extended_entry)); + put_unaligned_be32(logdev_list_length + + sizeof(struct report_log_lun_extended_entry), + &internal_logdev_list->header.list_length); + + kfree(*logdev_list); + *logdev_list = internal_logdev_list; + + return 0; +} + +static inline void pqi_set_bus_target_lun(struct pqi_scsi_dev *device, + int bus, int target, int lun) +{ + device->bus = bus; + device->target = target; + device->lun = lun; +} + +static void pqi_assign_bus_target_lun(struct pqi_scsi_dev *device) +{ + u8 *scsi3addr; + u32 lunid; + + scsi3addr = device->scsi3addr; + lunid = get_unaligned_le32(scsi3addr); + + if (pqi_is_hba_lunid(scsi3addr)) { + /* The specified device is the controller. */ + pqi_set_bus_target_lun(device, PQI_HBA_BUS, 0, lunid & 0x3fff); + device->target_lun_valid = true; + return; + } + + if (pqi_is_logical_device(device)) { + pqi_set_bus_target_lun(device, PQI_RAID_VOLUME_BUS, 0, + lunid & 0x3fff); + device->target_lun_valid = true; + return; + } + + /* + * Defer target and LUN assignment for non-controller physical devices + * because the SAS transport layer will make these assignments later. + */ + pqi_set_bus_target_lun(device, PQI_PHYSICAL_DEVICE_BUS, 0, 0); +} + +static void pqi_get_raid_level(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device) +{ + int rc; + u8 raid_level; + u8 *buffer; + + raid_level = SA_RAID_UNKNOWN; + + buffer = kmalloc(64, GFP_KERNEL); + if (buffer) { + rc = pqi_scsi_inquiry(ctrl_info, device->scsi3addr, + VPD_PAGE | CISS_VPD_LV_DEVICE_GEOMETRY, buffer, 64); + if (rc == 0) { + raid_level = buffer[8]; + if (raid_level > SA_RAID_MAX) + raid_level = SA_RAID_UNKNOWN; + } + kfree(buffer); + } + + device->raid_level = raid_level; +} + +static int pqi_validate_raid_map(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device, struct raid_map *raid_map) +{ + char *err_msg; + u32 raid_map_size; + u32 r5or6_blocks_per_row; + unsigned int num_phys_disks; + unsigned int num_raid_map_entries; + + raid_map_size = get_unaligned_le32(&raid_map->structure_size); + + if (raid_map_size < offsetof(struct raid_map, disk_data)) { + err_msg = "RAID map too small"; + goto bad_raid_map; + } + + if (raid_map_size > sizeof(*raid_map)) { + err_msg = "RAID map too large"; + goto bad_raid_map; + } + + num_phys_disks = get_unaligned_le16(&raid_map->layout_map_count) * + (get_unaligned_le16(&raid_map->data_disks_per_row) + + get_unaligned_le16(&raid_map->metadata_disks_per_row)); + num_raid_map_entries = num_phys_disks * + get_unaligned_le16(&raid_map->row_cnt); + + if (num_raid_map_entries > RAID_MAP_MAX_ENTRIES) { + err_msg = "invalid number of map entries in RAID map"; + goto bad_raid_map; + } + + if (device->raid_level == SA_RAID_1) { + if (get_unaligned_le16(&raid_map->layout_map_count) != 2) { + err_msg = "invalid RAID-1 map"; + goto bad_raid_map; + } + } else if (device->raid_level == SA_RAID_ADM) { + if (get_unaligned_le16(&raid_map->layout_map_count) != 3) { + err_msg = "invalid RAID-1(ADM) map"; + goto bad_raid_map; + } + } else if ((device->raid_level == SA_RAID_5 || + device->raid_level == SA_RAID_6) && + get_unaligned_le16(&raid_map->layout_map_count) > 1) { + /* RAID 50/60 */ + r5or6_blocks_per_row = + get_unaligned_le16(&raid_map->strip_size) * + get_unaligned_le16(&raid_map->data_disks_per_row); + if (r5or6_blocks_per_row == 0) { + err_msg = "invalid RAID-5 or RAID-6 map"; + goto bad_raid_map; + } + } + + return 0; + +bad_raid_map: + dev_warn(&ctrl_info->pci_dev->dev, "%s\n", err_msg); + + return -EINVAL; +} + +static int pqi_get_raid_map(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device) +{ + int rc; + int pci_direction; + struct pqi_raid_path_request request; + struct raid_map *raid_map; + + raid_map = kmalloc(sizeof(*raid_map), GFP_KERNEL); + if (!raid_map) + return -ENOMEM; + + rc = pqi_build_raid_path_request(ctrl_info, &request, + CISS_GET_RAID_MAP, device->scsi3addr, raid_map, + sizeof(*raid_map), 0, &pci_direction); + if (rc) + goto error; + + rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, 0, + NULL, NO_TIMEOUT); + + pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, + pci_direction); + + if (rc) + goto error; + + rc = pqi_validate_raid_map(ctrl_info, device, raid_map); + if (rc) + goto error; + + device->raid_map = raid_map; + + return 0; + +error: + kfree(raid_map); + + return rc; +} + +static void pqi_get_offload_status(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device) +{ + int rc; + u8 *buffer; + u8 offload_status; + + buffer = kmalloc(64, GFP_KERNEL); + if (!buffer) + return; + + rc = pqi_scsi_inquiry(ctrl_info, device->scsi3addr, + VPD_PAGE | CISS_VPD_LV_OFFLOAD_STATUS, buffer, 64); + if (rc) + goto out; + +#define OFFLOAD_STATUS_BYTE 4 +#define OFFLOAD_CONFIGURED_BIT 0x1 +#define OFFLOAD_ENABLED_BIT 0x2 + + offload_status = buffer[OFFLOAD_STATUS_BYTE]; + device->offload_configured = + !!(offload_status & OFFLOAD_CONFIGURED_BIT); + if (device->offload_configured) { + device->offload_enabled_pending = + !!(offload_status & OFFLOAD_ENABLED_BIT); + if (pqi_get_raid_map(ctrl_info, device)) + device->offload_enabled_pending = false; + } + +out: + kfree(buffer); +} + +/* + * Use vendor-specific VPD to determine online/offline status of a volume. + */ + +static void pqi_get_volume_status(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device) +{ + int rc; + size_t page_length; + u8 volume_status = CISS_LV_STATUS_UNAVAILABLE; + bool volume_offline = true; + u32 volume_flags; + struct ciss_vpd_logical_volume_status *vpd; + + vpd = kmalloc(sizeof(*vpd), GFP_KERNEL); + if (!vpd) + goto no_buffer; + + rc = pqi_scsi_inquiry(ctrl_info, device->scsi3addr, + VPD_PAGE | CISS_VPD_LV_STATUS, vpd, sizeof(*vpd)); + if (rc) + goto out; + + page_length = offsetof(struct ciss_vpd_logical_volume_status, + volume_status) + vpd->page_length; + if (page_length < sizeof(*vpd)) + goto out; + + volume_status = vpd->volume_status; + volume_flags = get_unaligned_be32(&vpd->flags); + volume_offline = (volume_flags & CISS_LV_FLAGS_NO_HOST_IO) != 0; + +out: + kfree(vpd); +no_buffer: + device->volume_status = volume_status; + device->volume_offline = volume_offline; +} + +static int pqi_get_device_info(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device) +{ + int rc; + u8 *buffer; + + buffer = kmalloc(64, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + /* Send an inquiry to the device to see what it is. */ + rc = pqi_scsi_inquiry(ctrl_info, device->scsi3addr, 0, buffer, 64); + if (rc) + goto out; + + scsi_sanitize_inquiry_string(&buffer[8], 8); + scsi_sanitize_inquiry_string(&buffer[16], 16); + + device->devtype = buffer[0] & 0x1f; + memcpy(device->vendor, &buffer[8], + sizeof(device->vendor)); + memcpy(device->model, &buffer[16], + sizeof(device->model)); + + if (pqi_is_logical_device(device) && device->devtype == TYPE_DISK) { + pqi_get_raid_level(ctrl_info, device); + pqi_get_offload_status(ctrl_info, device); + pqi_get_volume_status(ctrl_info, device); + } + +out: + kfree(buffer); + + return rc; +} + +static void pqi_get_physical_disk_info(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device, + struct bmic_identify_physical_device *id_phys) +{ + int rc; + + memset(id_phys, 0, sizeof(*id_phys)); + + rc = pqi_identify_physical_device(ctrl_info, device, + id_phys, sizeof(*id_phys)); + if (rc) { + device->queue_depth = PQI_PHYSICAL_DISK_DEFAULT_MAX_QUEUE_DEPTH; + return; + } + + device->queue_depth = + get_unaligned_le16(&id_phys->current_queue_depth_limit); + device->device_type = id_phys->device_type; + device->active_path_index = id_phys->active_path_number; + device->path_map = id_phys->redundant_path_present_map; + memcpy(&device->box, + &id_phys->alternate_paths_phys_box_on_port, + sizeof(device->box)); + memcpy(&device->phys_connector, + &id_phys->alternate_paths_phys_connector, + sizeof(device->phys_connector)); + device->bay = id_phys->phys_bay_in_box; +} + +static void pqi_show_volume_status(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device) +{ + char *status; + static const char unknown_state_str[] = + "Volume is in an unknown state (%u)"; + char unknown_state_buffer[sizeof(unknown_state_str) + 10]; + + switch (device->volume_status) { + case CISS_LV_OK: + status = "Volume online"; + break; + case CISS_LV_FAILED: + status = "Volume failed"; + break; + case CISS_LV_NOT_CONFIGURED: + status = "Volume not configured"; + break; + case CISS_LV_DEGRADED: + status = "Volume degraded"; + break; + case CISS_LV_READY_FOR_RECOVERY: + status = "Volume ready for recovery operation"; + break; + case CISS_LV_UNDERGOING_RECOVERY: + status = "Volume undergoing recovery"; + break; + case CISS_LV_WRONG_PHYSICAL_DRIVE_REPLACED: + status = "Wrong physical drive was replaced"; + break; + case CISS_LV_PHYSICAL_DRIVE_CONNECTION_PROBLEM: + status = "A physical drive not properly connected"; + break; + case CISS_LV_HARDWARE_OVERHEATING: + status = "Hardware is overheating"; + break; + case CISS_LV_HARDWARE_HAS_OVERHEATED: + status = "Hardware has overheated"; + break; + case CISS_LV_UNDERGOING_EXPANSION: + status = "Volume undergoing expansion"; + break; + case CISS_LV_NOT_AVAILABLE: + status = "Volume waiting for transforming volume"; + break; + case CISS_LV_QUEUED_FOR_EXPANSION: + status = "Volume queued for expansion"; + break; + case CISS_LV_DISABLED_SCSI_ID_CONFLICT: + status = "Volume disabled due to SCSI ID conflict"; + break; + case CISS_LV_EJECTED: + status = "Volume has been ejected"; + break; + case CISS_LV_UNDERGOING_ERASE: + status = "Volume undergoing background erase"; + break; + case CISS_LV_READY_FOR_PREDICTIVE_SPARE_REBUILD: + status = "Volume ready for predictive spare rebuild"; + break; + case CISS_LV_UNDERGOING_RPI: + status = "Volume undergoing rapid parity initialization"; + break; + case CISS_LV_PENDING_RPI: + status = "Volume queued for rapid parity initialization"; + break; + case CISS_LV_ENCRYPTED_NO_KEY: + status = "Encrypted volume inaccessible - key not present"; + break; + case CISS_LV_UNDERGOING_ENCRYPTION: + status = "Volume undergoing encryption process"; + break; + case CISS_LV_UNDERGOING_ENCRYPTION_REKEYING: + status = "Volume undergoing encryption re-keying process"; + break; + case CISS_LV_ENCRYPTED_IN_NON_ENCRYPTED_CONTROLLER: + status = + "Encrypted volume inaccessible - disabled on ctrl"; + break; + case CISS_LV_PENDING_ENCRYPTION: + status = "Volume pending migration to encrypted state"; + break; + case CISS_LV_PENDING_ENCRYPTION_REKEYING: + status = "Volume pending encryption rekeying"; + break; + case CISS_LV_NOT_SUPPORTED: + status = "Volume not supported on this controller"; + break; + case CISS_LV_STATUS_UNAVAILABLE: + status = "Volume status not available"; + break; + default: + snprintf(unknown_state_buffer, sizeof(unknown_state_buffer), + unknown_state_str, device->volume_status); + status = unknown_state_buffer; + break; + } + + dev_info(&ctrl_info->pci_dev->dev, + "scsi %d:%d:%d:%d %s\n", + ctrl_info->scsi_host->host_no, + device->bus, device->target, device->lun, status); +} + +static struct pqi_scsi_dev *pqi_find_disk_by_aio_handle( + struct pqi_ctrl_info *ctrl_info, u32 aio_handle) +{ + struct pqi_scsi_dev *device; + + list_for_each_entry(device, &ctrl_info->scsi_device_list, + scsi_device_list_entry) { + if (device->devtype != TYPE_DISK && device->devtype != TYPE_ZBC) + continue; + if (pqi_is_logical_device(device)) + continue; + if (device->aio_handle == aio_handle) + return device; + } + + return NULL; +} + +static void pqi_update_logical_drive_queue_depth( + struct pqi_ctrl_info *ctrl_info, struct pqi_scsi_dev *logical_drive) +{ + unsigned int i; + struct raid_map *raid_map; + struct raid_map_disk_data *disk_data; + struct pqi_scsi_dev *phys_disk; + unsigned int num_phys_disks; + unsigned int num_raid_map_entries; + unsigned int queue_depth; + + logical_drive->queue_depth = PQI_LOGICAL_DRIVE_DEFAULT_MAX_QUEUE_DEPTH; + + raid_map = logical_drive->raid_map; + if (!raid_map) + return; + + disk_data = raid_map->disk_data; + num_phys_disks = get_unaligned_le16(&raid_map->layout_map_count) * + (get_unaligned_le16(&raid_map->data_disks_per_row) + + get_unaligned_le16(&raid_map->metadata_disks_per_row)); + num_raid_map_entries = num_phys_disks * + get_unaligned_le16(&raid_map->row_cnt); + + queue_depth = 0; + for (i = 0; i < num_raid_map_entries; i++) { + phys_disk = pqi_find_disk_by_aio_handle(ctrl_info, + disk_data[i].aio_handle); + + if (!phys_disk) { + dev_warn(&ctrl_info->pci_dev->dev, + "failed to find physical disk for logical drive %016llx\n", + get_unaligned_be64(logical_drive->scsi3addr)); + logical_drive->offload_enabled = false; + logical_drive->offload_enabled_pending = false; + kfree(raid_map); + logical_drive->raid_map = NULL; + return; + } + + queue_depth += phys_disk->queue_depth; + } + + logical_drive->queue_depth = queue_depth; +} + +static void pqi_update_all_logical_drive_queue_depths( + struct pqi_ctrl_info *ctrl_info) +{ + struct pqi_scsi_dev *device; + + list_for_each_entry(device, &ctrl_info->scsi_device_list, + scsi_device_list_entry) { + if (device->devtype != TYPE_DISK && device->devtype != TYPE_ZBC) + continue; + if (!pqi_is_logical_device(device)) + continue; + pqi_update_logical_drive_queue_depth(ctrl_info, device); + } +} + +static void pqi_rescan_worker(struct work_struct *work) +{ + struct pqi_ctrl_info *ctrl_info; + + ctrl_info = container_of(to_delayed_work(work), struct pqi_ctrl_info, + rescan_work); + + pqi_scan_scsi_devices(ctrl_info); +} + +static int pqi_add_device(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device) +{ + int rc; + + if (pqi_is_logical_device(device)) + rc = scsi_add_device(ctrl_info->scsi_host, device->bus, + device->target, device->lun); + else + rc = pqi_add_sas_device(ctrl_info->sas_host, device); + + return rc; +} + +static inline void pqi_remove_device(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device) +{ + if (pqi_is_logical_device(device)) + scsi_remove_device(device->sdev); + else + pqi_remove_sas_device(device); +} + +/* Assumes the SCSI device list lock is held. */ + +static struct pqi_scsi_dev *pqi_find_scsi_dev(struct pqi_ctrl_info *ctrl_info, + int bus, int target, int lun) +{ + struct pqi_scsi_dev *device; + + list_for_each_entry(device, &ctrl_info->scsi_device_list, + scsi_device_list_entry) + if (device->bus == bus && device->target == target && + device->lun == lun) + return device; + + return NULL; +} + +static inline bool pqi_device_equal(struct pqi_scsi_dev *dev1, + struct pqi_scsi_dev *dev2) +{ + if (dev1->is_physical_device != dev2->is_physical_device) + return false; + + if (dev1->is_physical_device) + return dev1->wwid == dev2->wwid; + + return memcmp(dev1->volume_id, dev2->volume_id, + sizeof(dev1->volume_id)) == 0; +} + +enum pqi_find_result { + DEVICE_NOT_FOUND, + DEVICE_CHANGED, + DEVICE_SAME, +}; + +static enum pqi_find_result pqi_scsi_find_entry(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device_to_find, + struct pqi_scsi_dev **matching_device) +{ + struct pqi_scsi_dev *device; + + list_for_each_entry(device, &ctrl_info->scsi_device_list, + scsi_device_list_entry) { + if (pqi_scsi3addr_equal(device_to_find->scsi3addr, + device->scsi3addr)) { + *matching_device = device; + if (pqi_device_equal(device_to_find, device)) { + if (device_to_find->volume_offline) + return DEVICE_CHANGED; + return DEVICE_SAME; + } + return DEVICE_CHANGED; + } + } + + return DEVICE_NOT_FOUND; +} + +static void pqi_dev_info(struct pqi_ctrl_info *ctrl_info, + char *action, struct pqi_scsi_dev *device) +{ + dev_info(&ctrl_info->pci_dev->dev, + "%s scsi %d:%d:%d:%d: %s %.8s %.16s %-12s SSDSmartPathCap%c En%c Exp%c qd=%d\n", + action, + ctrl_info->scsi_host->host_no, + device->bus, + device->target, + device->lun, + scsi_device_type(device->devtype), + device->vendor, + device->model, + pqi_raid_level_to_string(device->raid_level), + device->offload_configured ? '+' : '-', + device->offload_enabled_pending ? '+' : '-', + device->expose_device ? '+' : '-', + device->queue_depth); +} + +/* Assumes the SCSI device list lock is held. */ + +static void pqi_scsi_update_device(struct pqi_scsi_dev *existing_device, + struct pqi_scsi_dev *new_device) +{ + existing_device->devtype = new_device->devtype; + existing_device->device_type = new_device->device_type; + existing_device->bus = new_device->bus; + if (new_device->target_lun_valid) { + existing_device->target = new_device->target; + existing_device->lun = new_device->lun; + existing_device->target_lun_valid = true; + } + + /* By definition, the scsi3addr and wwid fields are already the same. */ + + existing_device->is_physical_device = new_device->is_physical_device; + existing_device->expose_device = new_device->expose_device; + existing_device->no_uld_attach = new_device->no_uld_attach; + existing_device->aio_enabled = new_device->aio_enabled; + memcpy(existing_device->vendor, new_device->vendor, + sizeof(existing_device->vendor)); + memcpy(existing_device->model, new_device->model, + sizeof(existing_device->model)); + existing_device->sas_address = new_device->sas_address; + existing_device->raid_level = new_device->raid_level; + existing_device->queue_depth = new_device->queue_depth; + existing_device->aio_handle = new_device->aio_handle; + existing_device->volume_status = new_device->volume_status; + existing_device->active_path_index = new_device->active_path_index; + existing_device->path_map = new_device->path_map; + existing_device->bay = new_device->bay; + memcpy(existing_device->box, new_device->box, + sizeof(existing_device->box)); + memcpy(existing_device->phys_connector, new_device->phys_connector, + sizeof(existing_device->phys_connector)); + existing_device->offload_configured = new_device->offload_configured; + existing_device->offload_enabled = false; + existing_device->offload_enabled_pending = + new_device->offload_enabled_pending; + existing_device->offload_to_mirror = 0; + kfree(existing_device->raid_map); + existing_device->raid_map = new_device->raid_map; + + /* To prevent this from being freed later. */ + new_device->raid_map = NULL; +} + +static inline void pqi_free_device(struct pqi_scsi_dev *device) +{ + if (device) { + kfree(device->raid_map); + kfree(device); + } +} + +/* + * Called when exposing a new device to the OS fails in order to re-adjust + * our internal SCSI device list to match the SCSI ML's view. + */ + +static inline void pqi_fixup_botched_add(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device) +{ + unsigned long flags; + + spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); + list_del(&device->scsi_device_list_entry); + spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); + + /* Allow the device structure to be freed later. */ + device->keep_device = false; +} + +static void pqi_update_device_list(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *new_device_list[], unsigned int num_new_devices) +{ + int rc; + unsigned int i; + unsigned long flags; + enum pqi_find_result find_result; + struct pqi_scsi_dev *device; + struct pqi_scsi_dev *next; + struct pqi_scsi_dev *matching_device; + struct list_head add_list; + struct list_head delete_list; + + INIT_LIST_HEAD(&add_list); + INIT_LIST_HEAD(&delete_list); + + /* + * The idea here is to do as little work as possible while holding the + * spinlock. That's why we go to great pains to defer anything other + * than updating the internal device list until after we release the + * spinlock. + */ + + spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); + + /* Assume that all devices in the existing list have gone away. */ + list_for_each_entry(device, &ctrl_info->scsi_device_list, + scsi_device_list_entry) + device->device_gone = true; + + for (i = 0; i < num_new_devices; i++) { + device = new_device_list[i]; + + find_result = pqi_scsi_find_entry(ctrl_info, device, + &matching_device); + + switch (find_result) { + case DEVICE_SAME: + /* + * The newly found device is already in the existing + * device list. + */ + device->new_device = false; + matching_device->device_gone = false; + pqi_scsi_update_device(matching_device, device); + break; + case DEVICE_NOT_FOUND: + /* + * The newly found device is NOT in the existing device + * list. + */ + device->new_device = true; + break; + case DEVICE_CHANGED: + /* + * The original device has gone away and we need to add + * the new device. + */ + device->new_device = true; + break; + default: + WARN_ON(find_result); + break; + } + } + + /* Process all devices that have gone away. */ + list_for_each_entry_safe(device, next, &ctrl_info->scsi_device_list, + scsi_device_list_entry) { + if (device->device_gone) { + list_del(&device->scsi_device_list_entry); + list_add_tail(&device->delete_list_entry, &delete_list); + } + } + + /* Process all new devices. */ + for (i = 0; i < num_new_devices; i++) { + device = new_device_list[i]; + if (!device->new_device) + continue; + if (device->volume_offline) + continue; + list_add_tail(&device->scsi_device_list_entry, + &ctrl_info->scsi_device_list); + list_add_tail(&device->add_list_entry, &add_list); + /* To prevent this device structure from being freed later. */ + device->keep_device = true; + } + + pqi_update_all_logical_drive_queue_depths(ctrl_info); + + list_for_each_entry(device, &ctrl_info->scsi_device_list, + scsi_device_list_entry) + device->offload_enabled = + device->offload_enabled_pending; + + spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); + + /* Remove all devices that have gone away. */ + list_for_each_entry_safe(device, next, &delete_list, + delete_list_entry) { + if (device->sdev) + pqi_remove_device(ctrl_info, device); + if (device->volume_offline) { + pqi_dev_info(ctrl_info, "offline", device); + pqi_show_volume_status(ctrl_info, device); + } else { + pqi_dev_info(ctrl_info, "removed", device); + } + list_del(&device->delete_list_entry); + pqi_free_device(device); + } + + /* + * Notify the SCSI ML if the queue depth of any existing device has + * changed. + */ + list_for_each_entry(device, &ctrl_info->scsi_device_list, + scsi_device_list_entry) { + if (device->sdev && device->queue_depth != + device->advertised_queue_depth) { + device->advertised_queue_depth = device->queue_depth; + scsi_change_queue_depth(device->sdev, + device->advertised_queue_depth); + } + } + + /* Expose any new devices. */ + list_for_each_entry_safe(device, next, &add_list, add_list_entry) { + if (device->expose_device && !device->sdev) { + rc = pqi_add_device(ctrl_info, device); + if (rc) { + dev_warn(&ctrl_info->pci_dev->dev, + "scsi %d:%d:%d:%d addition failed, device not added\n", + ctrl_info->scsi_host->host_no, + device->bus, device->target, + device->lun); + pqi_fixup_botched_add(ctrl_info, device); + continue; + } + } + pqi_dev_info(ctrl_info, "added", device); + } +} + +static bool pqi_is_supported_device(struct pqi_scsi_dev *device) +{ + bool is_supported = false; + + switch (device->devtype) { + case TYPE_DISK: + case TYPE_ZBC: + case TYPE_TAPE: + case TYPE_MEDIUM_CHANGER: + case TYPE_ENCLOSURE: + is_supported = true; + break; + case TYPE_RAID: + /* + * Only support the HBA controller itself as a RAID + * controller. If it's a RAID controller other than + * the HBA itself (an external RAID controller, MSA500 + * or similar), we don't support it. + */ + if (pqi_is_hba_lunid(device->scsi3addr)) + is_supported = true; + break; + } + + return is_supported; +} + +static inline bool pqi_skip_device(u8 *scsi3addr, + struct report_phys_lun_extended_entry *phys_lun_ext_entry) +{ + u8 device_flags; + + if (!MASKED_DEVICE(scsi3addr)) + return false; + + /* The device is masked. */ + + device_flags = phys_lun_ext_entry->device_flags; + + if (device_flags & REPORT_PHYS_LUN_DEV_FLAG_NON_DISK) { + /* + * It's a non-disk device. We ignore all devices of this type + * when they're masked. + */ + return true; + } + + return false; +} + +static inline bool pqi_expose_device(struct pqi_scsi_dev *device) +{ + /* Expose all devices except for physical devices that are masked. */ + if (device->is_physical_device && MASKED_DEVICE(device->scsi3addr)) + return false; + + return true; +} + +static int pqi_update_scsi_devices(struct pqi_ctrl_info *ctrl_info) +{ + int i; + int rc; + struct list_head new_device_list_head; + struct report_phys_lun_extended *physdev_list = NULL; + struct report_log_lun_extended *logdev_list = NULL; + struct report_phys_lun_extended_entry *phys_lun_ext_entry; + struct report_log_lun_extended_entry *log_lun_ext_entry; + struct bmic_identify_physical_device *id_phys = NULL; + u32 num_physicals; + u32 num_logicals; + struct pqi_scsi_dev **new_device_list = NULL; + struct pqi_scsi_dev *device; + struct pqi_scsi_dev *next; + unsigned int num_new_devices; + unsigned int num_valid_devices; + bool is_physical_device; + u8 *scsi3addr; + static char *out_of_memory_msg = + "out of memory, device discovery stopped"; + + INIT_LIST_HEAD(&new_device_list_head); + + rc = pqi_get_device_lists(ctrl_info, &physdev_list, &logdev_list); + if (rc) + goto out; + + if (physdev_list) + num_physicals = + get_unaligned_be32(&physdev_list->header.list_length) + / sizeof(physdev_list->lun_entries[0]); + else + num_physicals = 0; + + if (logdev_list) + num_logicals = + get_unaligned_be32(&logdev_list->header.list_length) + / sizeof(logdev_list->lun_entries[0]); + else + num_logicals = 0; + + if (num_physicals) { + /* + * We need this buffer for calls to pqi_get_physical_disk_info() + * below. We allocate it here instead of inside + * pqi_get_physical_disk_info() because it's a fairly large + * buffer. + */ + id_phys = kmalloc(sizeof(*id_phys), GFP_KERNEL); + if (!id_phys) { + dev_warn(&ctrl_info->pci_dev->dev, "%s\n", + out_of_memory_msg); + rc = -ENOMEM; + goto out; + } + } + + num_new_devices = num_physicals + num_logicals; + + new_device_list = kmalloc(sizeof(*new_device_list) * + num_new_devices, GFP_KERNEL); + if (!new_device_list) { + dev_warn(&ctrl_info->pci_dev->dev, "%s\n", out_of_memory_msg); + rc = -ENOMEM; + goto out; + } + + for (i = 0; i < num_new_devices; i++) { + device = kzalloc(sizeof(*device), GFP_KERNEL); + if (!device) { + dev_warn(&ctrl_info->pci_dev->dev, "%s\n", + out_of_memory_msg); + rc = -ENOMEM; + goto out; + } + list_add_tail(&device->new_device_list_entry, + &new_device_list_head); + } + + device = NULL; + num_valid_devices = 0; + + for (i = 0; i < num_new_devices; i++) { + + if (i < num_physicals) { + is_physical_device = true; + phys_lun_ext_entry = &physdev_list->lun_entries[i]; + log_lun_ext_entry = NULL; + scsi3addr = phys_lun_ext_entry->lunid; + } else { + is_physical_device = false; + phys_lun_ext_entry = NULL; + log_lun_ext_entry = + &logdev_list->lun_entries[i - num_physicals]; + scsi3addr = log_lun_ext_entry->lunid; + } + + if (is_physical_device && + pqi_skip_device(scsi3addr, phys_lun_ext_entry)) + continue; + + if (device) + device = list_next_entry(device, new_device_list_entry); + else + device = list_first_entry(&new_device_list_head, + struct pqi_scsi_dev, new_device_list_entry); + + memcpy(device->scsi3addr, scsi3addr, sizeof(device->scsi3addr)); + device->is_physical_device = is_physical_device; + device->raid_level = SA_RAID_UNKNOWN; + + /* Gather information about the device. */ + rc = pqi_get_device_info(ctrl_info, device); + if (rc == -ENOMEM) { + dev_warn(&ctrl_info->pci_dev->dev, "%s\n", + out_of_memory_msg); + goto out; + } + if (rc) { + dev_warn(&ctrl_info->pci_dev->dev, + "obtaining device info failed, skipping device %016llx\n", + get_unaligned_be64(device->scsi3addr)); + rc = 0; + continue; + } + + if (!pqi_is_supported_device(device)) + continue; + + pqi_assign_bus_target_lun(device); + + device->expose_device = pqi_expose_device(device); + + if (device->is_physical_device) { + device->wwid = phys_lun_ext_entry->wwid; + if ((phys_lun_ext_entry->device_flags & + REPORT_PHYS_LUN_DEV_FLAG_AIO_ENABLED) && + phys_lun_ext_entry->aio_handle) + device->aio_enabled = true; + } else { + memcpy(device->volume_id, log_lun_ext_entry->volume_id, + sizeof(device->volume_id)); + } + + switch (device->devtype) { + case TYPE_DISK: + case TYPE_ZBC: + case TYPE_ENCLOSURE: + if (device->is_physical_device) { + device->sas_address = + get_unaligned_be64(&device->wwid); + if (device->devtype == TYPE_DISK || + device->devtype == TYPE_ZBC) { + device->aio_handle = + phys_lun_ext_entry->aio_handle; + pqi_get_physical_disk_info(ctrl_info, + device, id_phys); + } + } + break; + } + + new_device_list[num_valid_devices++] = device; + } + + pqi_update_device_list(ctrl_info, new_device_list, num_valid_devices); + +out: + list_for_each_entry_safe(device, next, &new_device_list_head, + new_device_list_entry) { + if (device->keep_device) + continue; + list_del(&device->new_device_list_entry); + pqi_free_device(device); + } + + kfree(new_device_list); + kfree(physdev_list); + kfree(logdev_list); + kfree(id_phys); + + return rc; +} + +static void pqi_remove_all_scsi_devices(struct pqi_ctrl_info *ctrl_info) +{ + unsigned long flags; + struct pqi_scsi_dev *device; + struct pqi_scsi_dev *next; + + spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); + + list_for_each_entry_safe(device, next, &ctrl_info->scsi_device_list, + scsi_device_list_entry) { + if (device->sdev) + pqi_remove_device(ctrl_info, device); + list_del(&device->scsi_device_list_entry); + pqi_free_device(device); + } + + spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); +} + +static int pqi_scan_scsi_devices(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + + if (pqi_ctrl_offline(ctrl_info)) + return -ENXIO; + + mutex_lock(&ctrl_info->scan_mutex); + + rc = pqi_update_scsi_devices(ctrl_info); + if (rc) + pqi_schedule_rescan_worker(ctrl_info); + + mutex_unlock(&ctrl_info->scan_mutex); + + return rc; +} + +static void pqi_scan_start(struct Scsi_Host *shost) +{ + pqi_scan_scsi_devices(shost_to_hba(shost)); +} + +/* Returns TRUE if scan is finished. */ + +static int pqi_scan_finished(struct Scsi_Host *shost, + unsigned long elapsed_time) +{ + struct pqi_ctrl_info *ctrl_info; + + ctrl_info = shost_priv(shost); + + return !mutex_is_locked(&ctrl_info->scan_mutex); +} + +static inline void pqi_set_encryption_info( + struct pqi_encryption_info *encryption_info, struct raid_map *raid_map, + u64 first_block) +{ + u32 volume_blk_size; + + /* + * Set the encryption tweak values based on logical block address. + * If the block size is 512, the tweak value is equal to the LBA. + * For other block sizes, tweak value is (LBA * block size) / 512. + */ + volume_blk_size = get_unaligned_le32(&raid_map->volume_blk_size); + if (volume_blk_size != 512) + first_block = (first_block * volume_blk_size) / 512; + + encryption_info->data_encryption_key_index = + get_unaligned_le16(&raid_map->data_encryption_key_index); + encryption_info->encrypt_tweak_lower = lower_32_bits(first_block); + encryption_info->encrypt_tweak_upper = upper_32_bits(first_block); +} + +/* + * Attempt to perform offload RAID mapping for a logical volume I/O. + */ + +#define PQI_RAID_BYPASS_INELIGIBLE 1 + +static int pqi_raid_bypass_submit_scsi_cmd(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device, struct scsi_cmnd *scmd, + struct pqi_queue_group *queue_group) +{ + struct raid_map *raid_map; + bool is_write = false; + u32 map_index; + u64 first_block; + u64 last_block; + u32 block_cnt; + u32 blocks_per_row; + u64 first_row; + u64 last_row; + u32 first_row_offset; + u32 last_row_offset; + u32 first_column; + u32 last_column; + u64 r0_first_row; + u64 r0_last_row; + u32 r5or6_blocks_per_row; + u64 r5or6_first_row; + u64 r5or6_last_row; + u32 r5or6_first_row_offset; + u32 r5or6_last_row_offset; + u32 r5or6_first_column; + u32 r5or6_last_column; + u16 data_disks_per_row; + u32 total_disks_per_row; + u16 layout_map_count; + u32 stripesize; + u16 strip_size; + u32 first_group; + u32 last_group; + u32 current_group; + u32 map_row; + u32 aio_handle; + u64 disk_block; + u32 disk_block_cnt; + u8 cdb[16]; + u8 cdb_length; + int offload_to_mirror; + struct pqi_encryption_info *encryption_info_ptr; + struct pqi_encryption_info encryption_info; +#if BITS_PER_LONG == 32 + u64 tmpdiv; +#endif + + /* Check for valid opcode, get LBA and block count. */ + switch (scmd->cmnd[0]) { + case WRITE_6: + is_write = true; + /* fall through */ + case READ_6: + first_block = (u64)(((scmd->cmnd[1] & 0x1f) << 16) | + (scmd->cmnd[2] << 8) | scmd->cmnd[3]); + block_cnt = (u32)scmd->cmnd[4]; + if (block_cnt == 0) + block_cnt = 256; + break; + case WRITE_10: + is_write = true; + /* fall through */ + case READ_10: + first_block = (u64)get_unaligned_be32(&scmd->cmnd[2]); + block_cnt = (u32)get_unaligned_be16(&scmd->cmnd[7]); + break; + case WRITE_12: + is_write = true; + /* fall through */ + case READ_12: + first_block = (u64)get_unaligned_be32(&scmd->cmnd[2]); + block_cnt = get_unaligned_be32(&scmd->cmnd[6]); + break; + case WRITE_16: + is_write = true; + /* fall through */ + case READ_16: + first_block = get_unaligned_be64(&scmd->cmnd[2]); + block_cnt = get_unaligned_be32(&scmd->cmnd[10]); + break; + default: + /* Process via normal I/O path. */ + return PQI_RAID_BYPASS_INELIGIBLE; + } + + /* Check for write to non-RAID-0. */ + if (is_write && device->raid_level != SA_RAID_0) + return PQI_RAID_BYPASS_INELIGIBLE; + + if (unlikely(block_cnt == 0)) + return PQI_RAID_BYPASS_INELIGIBLE; + + last_block = first_block + block_cnt - 1; + raid_map = device->raid_map; + + /* Check for invalid block or wraparound. */ + if (last_block >= get_unaligned_le64(&raid_map->volume_blk_cnt) || + last_block < first_block) + return PQI_RAID_BYPASS_INELIGIBLE; + + data_disks_per_row = get_unaligned_le16(&raid_map->data_disks_per_row); + strip_size = get_unaligned_le16(&raid_map->strip_size); + layout_map_count = get_unaligned_le16(&raid_map->layout_map_count); + + /* Calculate stripe information for the request. */ + blocks_per_row = data_disks_per_row * strip_size; +#if BITS_PER_LONG == 32 + tmpdiv = first_block; + do_div(tmpdiv, blocks_per_row); + first_row = tmpdiv; + tmpdiv = last_block; + do_div(tmpdiv, blocks_per_row); + last_row = tmpdiv; + first_row_offset = (u32)(first_block - (first_row * blocks_per_row)); + last_row_offset = (u32)(last_block - (last_row * blocks_per_row)); + tmpdiv = first_row_offset; + do_div(tmpdiv, strip_size); + first_column = tmpdiv; + tmpdiv = last_row_offset; + do_div(tmpdiv, strip_size); + last_column = tmpdiv; +#else + first_row = first_block / blocks_per_row; + last_row = last_block / blocks_per_row; + first_row_offset = (u32)(first_block - (first_row * blocks_per_row)); + last_row_offset = (u32)(last_block - (last_row * blocks_per_row)); + first_column = first_row_offset / strip_size; + last_column = last_row_offset / strip_size; +#endif + + /* If this isn't a single row/column then give to the controller. */ + if (first_row != last_row || first_column != last_column) + return PQI_RAID_BYPASS_INELIGIBLE; + + /* Proceeding with driver mapping. */ + total_disks_per_row = data_disks_per_row + + get_unaligned_le16(&raid_map->metadata_disks_per_row); + map_row = ((u32)(first_row >> raid_map->parity_rotation_shift)) % + get_unaligned_le16(&raid_map->row_cnt); + map_index = (map_row * total_disks_per_row) + first_column; + + /* RAID 1 */ + if (device->raid_level == SA_RAID_1) { + if (device->offload_to_mirror) + map_index += data_disks_per_row; + device->offload_to_mirror = !device->offload_to_mirror; + } else if (device->raid_level == SA_RAID_ADM) { + /* RAID ADM */ + /* + * Handles N-way mirrors (R1-ADM) and R10 with # of drives + * divisible by 3. + */ + offload_to_mirror = device->offload_to_mirror; + if (offload_to_mirror == 0) { + /* use physical disk in the first mirrored group. */ + map_index %= data_disks_per_row; + } else { + do { + /* + * Determine mirror group that map_index + * indicates. + */ + current_group = map_index / data_disks_per_row; + + if (offload_to_mirror != current_group) { + if (current_group < + layout_map_count - 1) { + /* + * Select raid index from + * next group. + */ + map_index += data_disks_per_row; + current_group++; + } else { + /* + * Select raid index from first + * group. + */ + map_index %= data_disks_per_row; + current_group = 0; + } + } + } while (offload_to_mirror != current_group); + } + + /* Set mirror group to use next time. */ + offload_to_mirror = + (offload_to_mirror >= layout_map_count - 1) ? + 0 : offload_to_mirror + 1; + WARN_ON(offload_to_mirror >= layout_map_count); + device->offload_to_mirror = offload_to_mirror; + /* + * Avoid direct use of device->offload_to_mirror within this + * function since multiple threads might simultaneously + * increment it beyond the range of device->layout_map_count -1. + */ + } else if ((device->raid_level == SA_RAID_5 || + device->raid_level == SA_RAID_6) && layout_map_count > 1) { + /* RAID 50/60 */ + /* Verify first and last block are in same RAID group */ + r5or6_blocks_per_row = strip_size * data_disks_per_row; + stripesize = r5or6_blocks_per_row * layout_map_count; +#if BITS_PER_LONG == 32 + tmpdiv = first_block; + first_group = do_div(tmpdiv, stripesize); + tmpdiv = first_group; + do_div(tmpdiv, r5or6_blocks_per_row); + first_group = tmpdiv; + tmpdiv = last_block; + last_group = do_div(tmpdiv, stripesize); + tmpdiv = last_group; + do_div(tmpdiv, r5or6_blocks_per_row); + last_group = tmpdiv; +#else + first_group = (first_block % stripesize) / r5or6_blocks_per_row; + last_group = (last_block % stripesize) / r5or6_blocks_per_row; +#endif + if (first_group != last_group) + return PQI_RAID_BYPASS_INELIGIBLE; + + /* Verify request is in a single row of RAID 5/6 */ +#if BITS_PER_LONG == 32 + tmpdiv = first_block; + do_div(tmpdiv, stripesize); + first_row = r5or6_first_row = r0_first_row = tmpdiv; + tmpdiv = last_block; + do_div(tmpdiv, stripesize); + r5or6_last_row = r0_last_row = tmpdiv; +#else + first_row = r5or6_first_row = r0_first_row = + first_block / stripesize; + r5or6_last_row = r0_last_row = last_block / stripesize; +#endif + if (r5or6_first_row != r5or6_last_row) + return PQI_RAID_BYPASS_INELIGIBLE; + + /* Verify request is in a single column */ +#if BITS_PER_LONG == 32 + tmpdiv = first_block; + first_row_offset = do_div(tmpdiv, stripesize); + tmpdiv = first_row_offset; + first_row_offset = (u32)do_div(tmpdiv, r5or6_blocks_per_row); + r5or6_first_row_offset = first_row_offset; + tmpdiv = last_block; + r5or6_last_row_offset = do_div(tmpdiv, stripesize); + tmpdiv = r5or6_last_row_offset; + r5or6_last_row_offset = do_div(tmpdiv, r5or6_blocks_per_row); + tmpdiv = r5or6_first_row_offset; + do_div(tmpdiv, strip_size); + first_column = r5or6_first_column = tmpdiv; + tmpdiv = r5or6_last_row_offset; + do_div(tmpdiv, strip_size); + r5or6_last_column = tmpdiv; +#else + first_row_offset = r5or6_first_row_offset = + (u32)((first_block % stripesize) % + r5or6_blocks_per_row); + + r5or6_last_row_offset = + (u32)((last_block % stripesize) % + r5or6_blocks_per_row); + + first_column = r5or6_first_row_offset / strip_size; + r5or6_first_column = first_column; + r5or6_last_column = r5or6_last_row_offset / strip_size; +#endif + if (r5or6_first_column != r5or6_last_column) + return PQI_RAID_BYPASS_INELIGIBLE; + + /* Request is eligible */ + map_row = + ((u32)(first_row >> raid_map->parity_rotation_shift)) % + get_unaligned_le16(&raid_map->row_cnt); + + map_index = (first_group * + (get_unaligned_le16(&raid_map->row_cnt) * + total_disks_per_row)) + + (map_row * total_disks_per_row) + first_column; + } + + if (unlikely(map_index >= RAID_MAP_MAX_ENTRIES)) + return PQI_RAID_BYPASS_INELIGIBLE; + + aio_handle = raid_map->disk_data[map_index].aio_handle; + disk_block = get_unaligned_le64(&raid_map->disk_starting_blk) + + first_row * strip_size + + (first_row_offset - first_column * strip_size); + disk_block_cnt = block_cnt; + + /* Handle differing logical/physical block sizes. */ + if (raid_map->phys_blk_shift) { + disk_block <<= raid_map->phys_blk_shift; + disk_block_cnt <<= raid_map->phys_blk_shift; + } + + if (unlikely(disk_block_cnt > 0xffff)) + return PQI_RAID_BYPASS_INELIGIBLE; + + /* Build the new CDB for the physical disk I/O. */ + if (disk_block > 0xffffffff) { + cdb[0] = is_write ? WRITE_16 : READ_16; + cdb[1] = 0; + put_unaligned_be64(disk_block, &cdb[2]); + put_unaligned_be32(disk_block_cnt, &cdb[10]); + cdb[14] = 0; + cdb[15] = 0; + cdb_length = 16; + } else { + cdb[0] = is_write ? WRITE_10 : READ_10; + cdb[1] = 0; + put_unaligned_be32((u32)disk_block, &cdb[2]); + cdb[6] = 0; + put_unaligned_be16((u16)disk_block_cnt, &cdb[7]); + cdb[9] = 0; + cdb_length = 10; + } + + if (get_unaligned_le16(&raid_map->flags) & + RAID_MAP_ENCRYPTION_ENABLED) { + pqi_set_encryption_info(&encryption_info, raid_map, + first_block); + encryption_info_ptr = &encryption_info; + } else { + encryption_info_ptr = NULL; + } + + return pqi_aio_submit_io(ctrl_info, scmd, aio_handle, + cdb, cdb_length, queue_group, encryption_info_ptr); +} + +#define PQI_STATUS_IDLE 0x0 + +#define PQI_CREATE_ADMIN_QUEUE_PAIR 1 +#define PQI_DELETE_ADMIN_QUEUE_PAIR 2 + +#define PQI_DEVICE_STATE_POWER_ON_AND_RESET 0x0 +#define PQI_DEVICE_STATE_STATUS_AVAILABLE 0x1 +#define PQI_DEVICE_STATE_ALL_REGISTERS_READY 0x2 +#define PQI_DEVICE_STATE_ADMIN_QUEUE_PAIR_READY 0x3 +#define PQI_DEVICE_STATE_ERROR 0x4 + +#define PQI_MODE_READY_TIMEOUT_SECS 30 +#define PQI_MODE_READY_POLL_INTERVAL_MSECS 1 + +static int pqi_wait_for_pqi_mode_ready(struct pqi_ctrl_info *ctrl_info) +{ + struct pqi_device_registers __iomem *pqi_registers; + unsigned long timeout; + u64 signature; + u8 status; + + pqi_registers = ctrl_info->pqi_registers; + timeout = (PQI_MODE_READY_TIMEOUT_SECS * HZ) + jiffies; + + while (1) { + signature = readq(&pqi_registers->signature); + if (memcmp(&signature, PQI_DEVICE_SIGNATURE, + sizeof(signature)) == 0) + break; + if (time_after(jiffies, timeout)) { + dev_err(&ctrl_info->pci_dev->dev, + "timed out waiting for PQI signature\n"); + return -ETIMEDOUT; + } + msleep(PQI_MODE_READY_POLL_INTERVAL_MSECS); + } + + while (1) { + status = readb(&pqi_registers->function_and_status_code); + if (status == PQI_STATUS_IDLE) + break; + if (time_after(jiffies, timeout)) { + dev_err(&ctrl_info->pci_dev->dev, + "timed out waiting for PQI IDLE\n"); + return -ETIMEDOUT; + } + msleep(PQI_MODE_READY_POLL_INTERVAL_MSECS); + } + + while (1) { + if (readl(&pqi_registers->device_status) == + PQI_DEVICE_STATE_ALL_REGISTERS_READY) + break; + if (time_after(jiffies, timeout)) { + dev_err(&ctrl_info->pci_dev->dev, + "timed out waiting for PQI all registers ready\n"); + return -ETIMEDOUT; + } + msleep(PQI_MODE_READY_POLL_INTERVAL_MSECS); + } + + return 0; +} + +static inline void pqi_aio_path_disabled(struct pqi_io_request *io_request) +{ + struct pqi_scsi_dev *device; + + device = io_request->scmd->device->hostdata; + device->offload_enabled = false; +} + +static inline void pqi_take_device_offline(struct scsi_device *sdev) +{ + struct pqi_ctrl_info *ctrl_info; + struct pqi_scsi_dev *device; + + if (scsi_device_online(sdev)) { + scsi_device_set_state(sdev, SDEV_OFFLINE); + ctrl_info = shost_to_hba(sdev->host); + schedule_delayed_work(&ctrl_info->rescan_work, 0); + device = sdev->hostdata; + dev_err(&ctrl_info->pci_dev->dev, "offlined scsi %d:%d:%d:%d\n", + ctrl_info->scsi_host->host_no, device->bus, + device->target, device->lun); + } +} + +static void pqi_process_raid_io_error(struct pqi_io_request *io_request) +{ + u8 scsi_status; + u8 host_byte; + struct scsi_cmnd *scmd; + struct pqi_raid_error_info *error_info; + size_t sense_data_length; + int residual_count; + int xfer_count; + struct scsi_sense_hdr sshdr; + + scmd = io_request->scmd; + if (!scmd) + return; + + error_info = io_request->error_info; + scsi_status = error_info->status; + host_byte = DID_OK; + + if (error_info->data_out_result == PQI_DATA_IN_OUT_UNDERFLOW) { + xfer_count = + get_unaligned_le32(&error_info->data_out_transferred); + residual_count = scsi_bufflen(scmd) - xfer_count; + scsi_set_resid(scmd, residual_count); + if (xfer_count < scmd->underflow) + host_byte = DID_SOFT_ERROR; + } + + sense_data_length = get_unaligned_le16(&error_info->sense_data_length); + if (sense_data_length == 0) + sense_data_length = + get_unaligned_le16(&error_info->response_data_length); + if (sense_data_length) { + if (sense_data_length > sizeof(error_info->data)) + sense_data_length = sizeof(error_info->data); + + if (scsi_status == SAM_STAT_CHECK_CONDITION && + scsi_normalize_sense(error_info->data, + sense_data_length, &sshdr) && + sshdr.sense_key == HARDWARE_ERROR && + sshdr.asc == 0x3e && + sshdr.ascq == 0x1) { + pqi_take_device_offline(scmd->device); + host_byte = DID_NO_CONNECT; + } + + if (sense_data_length > SCSI_SENSE_BUFFERSIZE) + sense_data_length = SCSI_SENSE_BUFFERSIZE; + memcpy(scmd->sense_buffer, error_info->data, + sense_data_length); + } + + scmd->result = scsi_status; + set_host_byte(scmd, host_byte); +} + +static void pqi_process_aio_io_error(struct pqi_io_request *io_request) +{ + u8 scsi_status; + u8 host_byte; + struct scsi_cmnd *scmd; + struct pqi_aio_error_info *error_info; + size_t sense_data_length; + int residual_count; + int xfer_count; + bool device_offline; + + scmd = io_request->scmd; + error_info = io_request->error_info; + host_byte = DID_OK; + sense_data_length = 0; + device_offline = false; + + switch (error_info->service_response) { + case PQI_AIO_SERV_RESPONSE_COMPLETE: + scsi_status = error_info->status; + break; + case PQI_AIO_SERV_RESPONSE_FAILURE: + switch (error_info->status) { + case PQI_AIO_STATUS_IO_ABORTED: + scsi_status = SAM_STAT_TASK_ABORTED; + break; + case PQI_AIO_STATUS_UNDERRUN: + scsi_status = SAM_STAT_GOOD; + residual_count = get_unaligned_le32( + &error_info->residual_count); + scsi_set_resid(scmd, residual_count); + xfer_count = scsi_bufflen(scmd) - residual_count; + if (xfer_count < scmd->underflow) + host_byte = DID_SOFT_ERROR; + break; + case PQI_AIO_STATUS_OVERRUN: + scsi_status = SAM_STAT_GOOD; + break; + case PQI_AIO_STATUS_AIO_PATH_DISABLED: + pqi_aio_path_disabled(io_request); + scsi_status = SAM_STAT_GOOD; + io_request->status = -EAGAIN; + break; + case PQI_AIO_STATUS_NO_PATH_TO_DEVICE: + case PQI_AIO_STATUS_INVALID_DEVICE: + device_offline = true; + pqi_take_device_offline(scmd->device); + host_byte = DID_NO_CONNECT; + scsi_status = SAM_STAT_CHECK_CONDITION; + break; + case PQI_AIO_STATUS_IO_ERROR: + default: + scsi_status = SAM_STAT_CHECK_CONDITION; + break; + } + break; + case PQI_AIO_SERV_RESPONSE_TMF_COMPLETE: + case PQI_AIO_SERV_RESPONSE_TMF_SUCCEEDED: + scsi_status = SAM_STAT_GOOD; + break; + case PQI_AIO_SERV_RESPONSE_TMF_REJECTED: + case PQI_AIO_SERV_RESPONSE_TMF_INCORRECT_LUN: + default: + scsi_status = SAM_STAT_CHECK_CONDITION; + break; + } + + if (error_info->data_present) { + sense_data_length = + get_unaligned_le16(&error_info->data_length); + if (sense_data_length) { + if (sense_data_length > sizeof(error_info->data)) + sense_data_length = sizeof(error_info->data); + if (sense_data_length > SCSI_SENSE_BUFFERSIZE) + sense_data_length = SCSI_SENSE_BUFFERSIZE; + memcpy(scmd->sense_buffer, error_info->data, + sense_data_length); + } + } + + if (device_offline && sense_data_length == 0) + scsi_build_sense_buffer(0, scmd->sense_buffer, HARDWARE_ERROR, + 0x3e, 0x1); + + scmd->result = scsi_status; + set_host_byte(scmd, host_byte); +} + +static void pqi_process_io_error(unsigned int iu_type, + struct pqi_io_request *io_request) +{ + switch (iu_type) { + case PQI_RESPONSE_IU_RAID_PATH_IO_ERROR: + pqi_process_raid_io_error(io_request); + break; + case PQI_RESPONSE_IU_AIO_PATH_IO_ERROR: + pqi_process_aio_io_error(io_request); + break; + } +} + +static int pqi_interpret_task_management_response( + struct pqi_task_management_response *response) +{ + int rc; + + switch (response->response_code) { + case SOP_TMF_COMPLETE: + case SOP_TMF_FUNCTION_SUCCEEDED: + rc = 0; + break; + default: + rc = -EIO; + break; + } + + return rc; +} + +static unsigned int pqi_process_io_intr(struct pqi_ctrl_info *ctrl_info, + struct pqi_queue_group *queue_group) +{ + unsigned int num_responses; + pqi_index_t oq_pi; + pqi_index_t oq_ci; + struct pqi_io_request *io_request; + struct pqi_io_response *response; + u16 request_id; + + num_responses = 0; + oq_ci = queue_group->oq_ci_copy; + + while (1) { + oq_pi = *queue_group->oq_pi; + if (oq_pi == oq_ci) + break; + + num_responses++; + response = queue_group->oq_element_array + + (oq_ci * PQI_OPERATIONAL_OQ_ELEMENT_LENGTH); + + request_id = get_unaligned_le16(&response->request_id); + WARN_ON(request_id >= ctrl_info->max_io_slots); + + io_request = &ctrl_info->io_request_pool[request_id]; + WARN_ON(atomic_read(&io_request->refcount) == 0); + + switch (response->header.iu_type) { + case PQI_RESPONSE_IU_RAID_PATH_IO_SUCCESS: + case PQI_RESPONSE_IU_AIO_PATH_IO_SUCCESS: + case PQI_RESPONSE_IU_GENERAL_MANAGEMENT: + break; + case PQI_RESPONSE_IU_TASK_MANAGEMENT: + io_request->status = + pqi_interpret_task_management_response( + (void *)response); + break; + case PQI_RESPONSE_IU_AIO_PATH_DISABLED: + pqi_aio_path_disabled(io_request); + io_request->status = -EAGAIN; + break; + case PQI_RESPONSE_IU_RAID_PATH_IO_ERROR: + case PQI_RESPONSE_IU_AIO_PATH_IO_ERROR: + io_request->error_info = ctrl_info->error_buffer + + (get_unaligned_le16(&response->error_index) * + PQI_ERROR_BUFFER_ELEMENT_LENGTH); + pqi_process_io_error(response->header.iu_type, + io_request); + break; + default: + dev_err(&ctrl_info->pci_dev->dev, + "unexpected IU type: 0x%x\n", + response->header.iu_type); + WARN_ON(response->header.iu_type); + break; + } + + io_request->io_complete_callback(io_request, + io_request->context); + + /* + * Note that the I/O request structure CANNOT BE TOUCHED after + * returning from the I/O completion callback! + */ + + oq_ci = (oq_ci + 1) % ctrl_info->num_elements_per_oq; + } + + if (num_responses) { + queue_group->oq_ci_copy = oq_ci; + writel(oq_ci, queue_group->oq_ci); + } + + return num_responses; +} + +static inline unsigned int pqi_num_elements_free(unsigned int pi, + unsigned int ci, unsigned int elements_in_queue) +{ + unsigned int num_elements_used; + + if (pi >= ci) + num_elements_used = pi - ci; + else + num_elements_used = elements_in_queue - ci + pi; + + return elements_in_queue - num_elements_used - 1; +} + +#define PQI_EVENT_ACK_TIMEOUT 30 + +static void pqi_start_event_ack(struct pqi_ctrl_info *ctrl_info, + struct pqi_event_acknowledge_request *iu, size_t iu_length) +{ + pqi_index_t iq_pi; + pqi_index_t iq_ci; + unsigned long flags; + void *next_element; + unsigned long timeout; + struct pqi_queue_group *queue_group; + + queue_group = &ctrl_info->queue_groups[PQI_DEFAULT_QUEUE_GROUP]; + put_unaligned_le16(queue_group->oq_id, &iu->header.response_queue_id); + + timeout = (PQI_EVENT_ACK_TIMEOUT * HZ) + jiffies; + + while (1) { + spin_lock_irqsave(&queue_group->submit_lock[RAID_PATH], flags); + + iq_pi = queue_group->iq_pi_copy[RAID_PATH]; + iq_ci = *queue_group->iq_ci[RAID_PATH]; + + if (pqi_num_elements_free(iq_pi, iq_ci, + ctrl_info->num_elements_per_iq)) + break; + + spin_unlock_irqrestore( + &queue_group->submit_lock[RAID_PATH], flags); + + if (time_after(jiffies, timeout)) { + dev_err(&ctrl_info->pci_dev->dev, + "sending event acknowledge timed out\n"); + return; + } + } + + next_element = queue_group->iq_element_array[RAID_PATH] + + (iq_pi * PQI_OPERATIONAL_IQ_ELEMENT_LENGTH); + + memcpy(next_element, iu, iu_length); + + iq_pi = (iq_pi + 1) % ctrl_info->num_elements_per_iq; + + queue_group->iq_pi_copy[RAID_PATH] = iq_pi; + + /* + * This write notifies the controller that an IU is available to be + * processed. + */ + writel(iq_pi, queue_group->iq_pi[RAID_PATH]); + + spin_unlock_irqrestore(&queue_group->submit_lock[RAID_PATH], flags); +} + +static void pqi_acknowledge_event(struct pqi_ctrl_info *ctrl_info, + struct pqi_event *event) +{ + struct pqi_event_acknowledge_request request; + + memset(&request, 0, sizeof(request)); + + request.header.iu_type = PQI_REQUEST_IU_ACKNOWLEDGE_VENDOR_EVENT; + put_unaligned_le16(sizeof(request) - PQI_REQUEST_HEADER_LENGTH, + &request.header.iu_length); + request.event_type = event->event_type; + request.event_id = event->event_id; + request.additional_event_id = event->additional_event_id; + + pqi_start_event_ack(ctrl_info, &request, sizeof(request)); +} + +static void pqi_event_worker(struct work_struct *work) +{ + unsigned int i; + struct pqi_ctrl_info *ctrl_info; + struct pqi_event *pending_event; + bool got_non_heartbeat_event = false; + + ctrl_info = container_of(work, struct pqi_ctrl_info, event_work); + + pending_event = ctrl_info->pending_events; + for (i = 0; i < PQI_NUM_SUPPORTED_EVENTS; i++) { + if (pending_event->pending) { + pending_event->pending = false; + pqi_acknowledge_event(ctrl_info, pending_event); + if (i != PQI_EVENT_HEARTBEAT) + got_non_heartbeat_event = true; + } + pending_event++; + } + + if (got_non_heartbeat_event) + pqi_schedule_rescan_worker(ctrl_info); +} + +static void pqi_take_ctrl_offline(struct pqi_ctrl_info *ctrl_info) +{ + unsigned int i; + unsigned int path; + struct pqi_queue_group *queue_group; + unsigned long flags; + struct pqi_io_request *io_request; + struct pqi_io_request *next; + struct scsi_cmnd *scmd; + + ctrl_info->controller_online = false; + dev_err(&ctrl_info->pci_dev->dev, "controller offline\n"); + + for (i = 0; i < ctrl_info->num_queue_groups; i++) { + queue_group = &ctrl_info->queue_groups[i]; + + for (path = 0; path < 2; path++) { + spin_lock_irqsave( + &queue_group->submit_lock[path], flags); + + list_for_each_entry_safe(io_request, next, + &queue_group->request_list[path], + request_list_entry) { + + scmd = io_request->scmd; + if (scmd) { + set_host_byte(scmd, DID_NO_CONNECT); + pqi_scsi_done(scmd); + } + + list_del(&io_request->request_list_entry); + } + + spin_unlock_irqrestore( + &queue_group->submit_lock[path], flags); + } + } +} + +#define PQI_HEARTBEAT_TIMER_INTERVAL (5 * HZ) +#define PQI_MAX_HEARTBEAT_REQUESTS 5 + +static void pqi_heartbeat_timer_handler(unsigned long data) +{ + int num_interrupts; + struct pqi_ctrl_info *ctrl_info = (struct pqi_ctrl_info *)data; + + num_interrupts = atomic_read(&ctrl_info->num_interrupts); + + if (num_interrupts == ctrl_info->previous_num_interrupts) { + ctrl_info->num_heartbeats_requested++; + if (ctrl_info->num_heartbeats_requested > + PQI_MAX_HEARTBEAT_REQUESTS) { + pqi_take_ctrl_offline(ctrl_info); + return; + } + ctrl_info->pending_events[PQI_EVENT_HEARTBEAT].pending = true; + schedule_work(&ctrl_info->event_work); + } else { + ctrl_info->num_heartbeats_requested = 0; + } + + ctrl_info->previous_num_interrupts = num_interrupts; + mod_timer(&ctrl_info->heartbeat_timer, + jiffies + PQI_HEARTBEAT_TIMER_INTERVAL); +} + +static void pqi_start_heartbeat_timer(struct pqi_ctrl_info *ctrl_info) +{ + ctrl_info->previous_num_interrupts = + atomic_read(&ctrl_info->num_interrupts); + + init_timer(&ctrl_info->heartbeat_timer); + ctrl_info->heartbeat_timer.expires = + jiffies + PQI_HEARTBEAT_TIMER_INTERVAL; + ctrl_info->heartbeat_timer.data = (unsigned long)ctrl_info; + ctrl_info->heartbeat_timer.function = pqi_heartbeat_timer_handler; + add_timer(&ctrl_info->heartbeat_timer); + ctrl_info->heartbeat_timer_started = true; +} + +static inline void pqi_stop_heartbeat_timer(struct pqi_ctrl_info *ctrl_info) +{ + if (ctrl_info->heartbeat_timer_started) + del_timer_sync(&ctrl_info->heartbeat_timer); +} + +static int pqi_event_type_to_event_index(unsigned int event_type) +{ + int index; + + switch (event_type) { + case PQI_EVENT_TYPE_HEARTBEAT: + index = PQI_EVENT_HEARTBEAT; + break; + case PQI_EVENT_TYPE_HOTPLUG: + index = PQI_EVENT_HOTPLUG; + break; + case PQI_EVENT_TYPE_HARDWARE: + index = PQI_EVENT_HARDWARE; + break; + case PQI_EVENT_TYPE_PHYSICAL_DEVICE: + index = PQI_EVENT_PHYSICAL_DEVICE; + break; + case PQI_EVENT_TYPE_LOGICAL_DEVICE: + index = PQI_EVENT_LOGICAL_DEVICE; + break; + case PQI_EVENT_TYPE_AIO_STATE_CHANGE: + index = PQI_EVENT_AIO_STATE_CHANGE; + break; + case PQI_EVENT_TYPE_AIO_CONFIG_CHANGE: + index = PQI_EVENT_AIO_CONFIG_CHANGE; + break; + default: + index = -1; + break; + } + + return index; +} + +static unsigned int pqi_process_event_intr(struct pqi_ctrl_info *ctrl_info) +{ + unsigned int num_events; + pqi_index_t oq_pi; + pqi_index_t oq_ci; + struct pqi_event_queue *event_queue; + struct pqi_event_response *response; + struct pqi_event *pending_event; + bool need_delayed_work; + int event_index; + + event_queue = &ctrl_info->event_queue; + num_events = 0; + need_delayed_work = false; + oq_ci = event_queue->oq_ci_copy; + + while (1) { + oq_pi = *event_queue->oq_pi; + if (oq_pi == oq_ci) + break; + + num_events++; + response = event_queue->oq_element_array + + (oq_ci * PQI_EVENT_OQ_ELEMENT_LENGTH); + + event_index = + pqi_event_type_to_event_index(response->event_type); + + if (event_index >= 0) { + if (response->request_acknowlege) { + pending_event = + &ctrl_info->pending_events[event_index]; + pending_event->event_type = + response->event_type; + pending_event->event_id = response->event_id; + pending_event->additional_event_id = + response->additional_event_id; + if (event_index != PQI_EVENT_HEARTBEAT) { + pending_event->pending = true; + need_delayed_work = true; + } + } + } + + oq_ci = (oq_ci + 1) % PQI_NUM_EVENT_QUEUE_ELEMENTS; + } + + if (num_events) { + event_queue->oq_ci_copy = oq_ci; + writel(oq_ci, event_queue->oq_ci); + + if (need_delayed_work) + schedule_work(&ctrl_info->event_work); + } + + return num_events; +} + +static irqreturn_t pqi_irq_handler(int irq, void *data) +{ + struct pqi_ctrl_info *ctrl_info; + struct pqi_queue_group *queue_group; + unsigned int num_responses_handled; + + queue_group = data; + ctrl_info = queue_group->ctrl_info; + + if (!ctrl_info || !queue_group->oq_ci) + return IRQ_NONE; + + num_responses_handled = pqi_process_io_intr(ctrl_info, queue_group); + + if (irq == ctrl_info->event_irq) + num_responses_handled += pqi_process_event_intr(ctrl_info); + + if (num_responses_handled) + atomic_inc(&ctrl_info->num_interrupts); + + pqi_start_io(ctrl_info, queue_group, RAID_PATH, NULL); + pqi_start_io(ctrl_info, queue_group, AIO_PATH, NULL); + + return IRQ_HANDLED; +} + +static int pqi_request_irqs(struct pqi_ctrl_info *ctrl_info) +{ + int i; + int rc; + + ctrl_info->event_irq = ctrl_info->msix_vectors[0]; + + for (i = 0; i < ctrl_info->num_msix_vectors_enabled; i++) { + rc = request_irq(ctrl_info->msix_vectors[i], + pqi_irq_handler, 0, + DRIVER_NAME_SHORT, ctrl_info->intr_data[i]); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "irq %u init failed with error %d\n", + ctrl_info->msix_vectors[i], rc); + return rc; + } + ctrl_info->num_msix_vectors_initialized++; + } + + return 0; +} + +static void pqi_free_irqs(struct pqi_ctrl_info *ctrl_info) +{ + int i; + + for (i = 0; i < ctrl_info->num_msix_vectors_initialized; i++) + free_irq(ctrl_info->msix_vectors[i], + ctrl_info->intr_data[i]); +} + +static int pqi_enable_msix_interrupts(struct pqi_ctrl_info *ctrl_info) +{ + unsigned int i; + int max_vectors; + int num_vectors_enabled; + struct msix_entry msix_entries[PQI_MAX_MSIX_VECTORS]; + + max_vectors = ctrl_info->num_queue_groups; + + for (i = 0; i < max_vectors; i++) + msix_entries[i].entry = i; + + num_vectors_enabled = pci_enable_msix_range(ctrl_info->pci_dev, + msix_entries, PQI_MIN_MSIX_VECTORS, max_vectors); + + if (num_vectors_enabled < 0) { + dev_err(&ctrl_info->pci_dev->dev, + "MSI-X init failed with error %d\n", + num_vectors_enabled); + return num_vectors_enabled; + } + + ctrl_info->num_msix_vectors_enabled = num_vectors_enabled; + for (i = 0; i < num_vectors_enabled; i++) { + ctrl_info->msix_vectors[i] = msix_entries[i].vector; + ctrl_info->intr_data[i] = &ctrl_info->queue_groups[i]; + } + + return 0; +} + +static void pqi_irq_set_affinity_hint(struct pqi_ctrl_info *ctrl_info) +{ + int i; + int rc; + int cpu; + + cpu = cpumask_first(cpu_online_mask); + for (i = 0; i < ctrl_info->num_msix_vectors_initialized; i++) { + rc = irq_set_affinity_hint(ctrl_info->msix_vectors[i], + get_cpu_mask(cpu)); + if (rc) + dev_err(&ctrl_info->pci_dev->dev, + "error %d setting affinity hint for irq vector %u\n", + rc, ctrl_info->msix_vectors[i]); + cpu = cpumask_next(cpu, cpu_online_mask); + } +} + +static void pqi_irq_unset_affinity_hint(struct pqi_ctrl_info *ctrl_info) +{ + int i; + + for (i = 0; i < ctrl_info->num_msix_vectors_initialized; i++) + irq_set_affinity_hint(ctrl_info->msix_vectors[i], NULL); +} + +static int pqi_alloc_operational_queues(struct pqi_ctrl_info *ctrl_info) +{ + unsigned int i; + size_t alloc_length; + size_t element_array_length_per_iq; + size_t element_array_length_per_oq; + void *element_array; + void *next_queue_index; + void *aligned_pointer; + unsigned int num_inbound_queues; + unsigned int num_outbound_queues; + unsigned int num_queue_indexes; + struct pqi_queue_group *queue_group; + + element_array_length_per_iq = + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH * + ctrl_info->num_elements_per_iq; + element_array_length_per_oq = + PQI_OPERATIONAL_OQ_ELEMENT_LENGTH * + ctrl_info->num_elements_per_oq; + num_inbound_queues = ctrl_info->num_queue_groups * 2; + num_outbound_queues = ctrl_info->num_queue_groups; + num_queue_indexes = (ctrl_info->num_queue_groups * 3) + 1; + + aligned_pointer = NULL; + + for (i = 0; i < num_inbound_queues; i++) { + aligned_pointer = PTR_ALIGN(aligned_pointer, + PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT); + aligned_pointer += element_array_length_per_iq; + } + + for (i = 0; i < num_outbound_queues; i++) { + aligned_pointer = PTR_ALIGN(aligned_pointer, + PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT); + aligned_pointer += element_array_length_per_oq; + } + + aligned_pointer = PTR_ALIGN(aligned_pointer, + PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT); + aligned_pointer += PQI_NUM_EVENT_QUEUE_ELEMENTS * + PQI_EVENT_OQ_ELEMENT_LENGTH; + + for (i = 0; i < num_queue_indexes; i++) { + aligned_pointer = PTR_ALIGN(aligned_pointer, + PQI_OPERATIONAL_INDEX_ALIGNMENT); + aligned_pointer += sizeof(pqi_index_t); + } + + alloc_length = (size_t)aligned_pointer + + PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT; + + ctrl_info->queue_memory_base = + dma_zalloc_coherent(&ctrl_info->pci_dev->dev, + alloc_length, + &ctrl_info->queue_memory_base_dma_handle, GFP_KERNEL); + + if (!ctrl_info->queue_memory_base) { + dev_err(&ctrl_info->pci_dev->dev, + "failed to allocate memory for PQI admin queues\n"); + return -ENOMEM; + } + + ctrl_info->queue_memory_length = alloc_length; + + element_array = PTR_ALIGN(ctrl_info->queue_memory_base, + PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT); + + for (i = 0; i < ctrl_info->num_queue_groups; i++) { + queue_group = &ctrl_info->queue_groups[i]; + queue_group->iq_element_array[RAID_PATH] = element_array; + queue_group->iq_element_array_bus_addr[RAID_PATH] = + ctrl_info->queue_memory_base_dma_handle + + (element_array - ctrl_info->queue_memory_base); + element_array += element_array_length_per_iq; + element_array = PTR_ALIGN(element_array, + PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT); + queue_group->iq_element_array[AIO_PATH] = element_array; + queue_group->iq_element_array_bus_addr[AIO_PATH] = + ctrl_info->queue_memory_base_dma_handle + + (element_array - ctrl_info->queue_memory_base); + element_array += element_array_length_per_iq; + element_array = PTR_ALIGN(element_array, + PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT); + } + + for (i = 0; i < ctrl_info->num_queue_groups; i++) { + queue_group = &ctrl_info->queue_groups[i]; + queue_group->oq_element_array = element_array; + queue_group->oq_element_array_bus_addr = + ctrl_info->queue_memory_base_dma_handle + + (element_array - ctrl_info->queue_memory_base); + element_array += element_array_length_per_oq; + element_array = PTR_ALIGN(element_array, + PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT); + } + + ctrl_info->event_queue.oq_element_array = element_array; + ctrl_info->event_queue.oq_element_array_bus_addr = + ctrl_info->queue_memory_base_dma_handle + + (element_array - ctrl_info->queue_memory_base); + element_array += PQI_NUM_EVENT_QUEUE_ELEMENTS * + PQI_EVENT_OQ_ELEMENT_LENGTH; + + next_queue_index = PTR_ALIGN(element_array, + PQI_OPERATIONAL_INDEX_ALIGNMENT); + + for (i = 0; i < ctrl_info->num_queue_groups; i++) { + queue_group = &ctrl_info->queue_groups[i]; + queue_group->iq_ci[RAID_PATH] = next_queue_index; + queue_group->iq_ci_bus_addr[RAID_PATH] = + ctrl_info->queue_memory_base_dma_handle + + (next_queue_index - ctrl_info->queue_memory_base); + next_queue_index += sizeof(pqi_index_t); + next_queue_index = PTR_ALIGN(next_queue_index, + PQI_OPERATIONAL_INDEX_ALIGNMENT); + queue_group->iq_ci[AIO_PATH] = next_queue_index; + queue_group->iq_ci_bus_addr[AIO_PATH] = + ctrl_info->queue_memory_base_dma_handle + + (next_queue_index - ctrl_info->queue_memory_base); + next_queue_index += sizeof(pqi_index_t); + next_queue_index = PTR_ALIGN(next_queue_index, + PQI_OPERATIONAL_INDEX_ALIGNMENT); + queue_group->oq_pi = next_queue_index; + queue_group->oq_pi_bus_addr = + ctrl_info->queue_memory_base_dma_handle + + (next_queue_index - ctrl_info->queue_memory_base); + next_queue_index += sizeof(pqi_index_t); + next_queue_index = PTR_ALIGN(next_queue_index, + PQI_OPERATIONAL_INDEX_ALIGNMENT); + } + + ctrl_info->event_queue.oq_pi = next_queue_index; + ctrl_info->event_queue.oq_pi_bus_addr = + ctrl_info->queue_memory_base_dma_handle + + (next_queue_index - ctrl_info->queue_memory_base); + + return 0; +} + +static void pqi_init_operational_queues(struct pqi_ctrl_info *ctrl_info) +{ + unsigned int i; + u16 next_iq_id = PQI_MIN_OPERATIONAL_QUEUE_ID; + u16 next_oq_id = PQI_MIN_OPERATIONAL_QUEUE_ID; + + /* + * Initialize the backpointers to the controller structure in + * each operational queue group structure. + */ + for (i = 0; i < ctrl_info->num_queue_groups; i++) + ctrl_info->queue_groups[i].ctrl_info = ctrl_info; + + /* + * Assign IDs to all operational queues. Note that the IDs + * assigned to operational IQs are independent of the IDs + * assigned to operational OQs. + */ + ctrl_info->event_queue.oq_id = next_oq_id++; + for (i = 0; i < ctrl_info->num_queue_groups; i++) { + ctrl_info->queue_groups[i].iq_id[RAID_PATH] = next_iq_id++; + ctrl_info->queue_groups[i].iq_id[AIO_PATH] = next_iq_id++; + ctrl_info->queue_groups[i].oq_id = next_oq_id++; + } + + /* + * Assign MSI-X table entry indexes to all queues. Note that the + * interrupt for the event queue is shared with the first queue group. + */ + ctrl_info->event_queue.int_msg_num = 0; + for (i = 0; i < ctrl_info->num_queue_groups; i++) + ctrl_info->queue_groups[i].int_msg_num = i; + + for (i = 0; i < ctrl_info->num_queue_groups; i++) { + spin_lock_init(&ctrl_info->queue_groups[i].submit_lock[0]); + spin_lock_init(&ctrl_info->queue_groups[i].submit_lock[1]); + INIT_LIST_HEAD(&ctrl_info->queue_groups[i].request_list[0]); + INIT_LIST_HEAD(&ctrl_info->queue_groups[i].request_list[1]); + } +} + +static int pqi_alloc_admin_queues(struct pqi_ctrl_info *ctrl_info) +{ + size_t alloc_length; + struct pqi_admin_queues_aligned *admin_queues_aligned; + struct pqi_admin_queues *admin_queues; + + alloc_length = sizeof(struct pqi_admin_queues_aligned) + + PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT; + + ctrl_info->admin_queue_memory_base = + dma_zalloc_coherent(&ctrl_info->pci_dev->dev, + alloc_length, + &ctrl_info->admin_queue_memory_base_dma_handle, + GFP_KERNEL); + + if (!ctrl_info->admin_queue_memory_base) + return -ENOMEM; + + ctrl_info->admin_queue_memory_length = alloc_length; + + admin_queues = &ctrl_info->admin_queues; + admin_queues_aligned = PTR_ALIGN(ctrl_info->admin_queue_memory_base, + PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT); + admin_queues->iq_element_array = + &admin_queues_aligned->iq_element_array; + admin_queues->oq_element_array = + &admin_queues_aligned->oq_element_array; + admin_queues->iq_ci = &admin_queues_aligned->iq_ci; + admin_queues->oq_pi = &admin_queues_aligned->oq_pi; + + admin_queues->iq_element_array_bus_addr = + ctrl_info->admin_queue_memory_base_dma_handle + + (admin_queues->iq_element_array - + ctrl_info->admin_queue_memory_base); + admin_queues->oq_element_array_bus_addr = + ctrl_info->admin_queue_memory_base_dma_handle + + (admin_queues->oq_element_array - + ctrl_info->admin_queue_memory_base); + admin_queues->iq_ci_bus_addr = + ctrl_info->admin_queue_memory_base_dma_handle + + ((void *)admin_queues->iq_ci - + ctrl_info->admin_queue_memory_base); + admin_queues->oq_pi_bus_addr = + ctrl_info->admin_queue_memory_base_dma_handle + + ((void *)admin_queues->oq_pi - + ctrl_info->admin_queue_memory_base); + + return 0; +} + +#define PQI_ADMIN_QUEUE_CREATE_TIMEOUT_JIFFIES HZ +#define PQI_ADMIN_QUEUE_CREATE_POLL_INTERVAL_MSECS 1 + +static int pqi_create_admin_queues(struct pqi_ctrl_info *ctrl_info) +{ + struct pqi_device_registers __iomem *pqi_registers; + struct pqi_admin_queues *admin_queues; + unsigned long timeout; + u8 status; + u32 reg; + + pqi_registers = ctrl_info->pqi_registers; + admin_queues = &ctrl_info->admin_queues; + + writeq((u64)admin_queues->iq_element_array_bus_addr, + &pqi_registers->admin_iq_element_array_addr); + writeq((u64)admin_queues->oq_element_array_bus_addr, + &pqi_registers->admin_oq_element_array_addr); + writeq((u64)admin_queues->iq_ci_bus_addr, + &pqi_registers->admin_iq_ci_addr); + writeq((u64)admin_queues->oq_pi_bus_addr, + &pqi_registers->admin_oq_pi_addr); + + reg = PQI_ADMIN_IQ_NUM_ELEMENTS | + (PQI_ADMIN_OQ_NUM_ELEMENTS) << 8 | + (admin_queues->int_msg_num << 16); + writel(reg, &pqi_registers->admin_iq_num_elements); + writel(PQI_CREATE_ADMIN_QUEUE_PAIR, + &pqi_registers->function_and_status_code); + + timeout = PQI_ADMIN_QUEUE_CREATE_TIMEOUT_JIFFIES + jiffies; + while (1) { + status = readb(&pqi_registers->function_and_status_code); + if (status == PQI_STATUS_IDLE) + break; + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + msleep(PQI_ADMIN_QUEUE_CREATE_POLL_INTERVAL_MSECS); + } + + /* + * The offset registers are not initialized to the correct + * offsets until *after* the create admin queue pair command + * completes successfully. + */ + admin_queues->iq_pi = ctrl_info->iomem_base + + PQI_DEVICE_REGISTERS_OFFSET + + readq(&pqi_registers->admin_iq_pi_offset); + admin_queues->oq_ci = ctrl_info->iomem_base + + PQI_DEVICE_REGISTERS_OFFSET + + readq(&pqi_registers->admin_oq_ci_offset); + + return 0; +} + +static void pqi_submit_admin_request(struct pqi_ctrl_info *ctrl_info, + struct pqi_general_admin_request *request) +{ + struct pqi_admin_queues *admin_queues; + void *next_element; + pqi_index_t iq_pi; + + admin_queues = &ctrl_info->admin_queues; + iq_pi = admin_queues->iq_pi_copy; + + next_element = admin_queues->iq_element_array + + (iq_pi * PQI_ADMIN_IQ_ELEMENT_LENGTH); + + memcpy(next_element, request, sizeof(*request)); + + iq_pi = (iq_pi + 1) % PQI_ADMIN_IQ_NUM_ELEMENTS; + admin_queues->iq_pi_copy = iq_pi; + + /* + * This write notifies the controller that an IU is available to be + * processed. + */ + writel(iq_pi, admin_queues->iq_pi); +} + +static int pqi_poll_for_admin_response(struct pqi_ctrl_info *ctrl_info, + struct pqi_general_admin_response *response) +{ + struct pqi_admin_queues *admin_queues; + pqi_index_t oq_pi; + pqi_index_t oq_ci; + unsigned long timeout; + + admin_queues = &ctrl_info->admin_queues; + oq_ci = admin_queues->oq_ci_copy; + + timeout = (3 * HZ) + jiffies; + + while (1) { + oq_pi = *admin_queues->oq_pi; + if (oq_pi != oq_ci) + break; + if (time_after(jiffies, timeout)) { + dev_err(&ctrl_info->pci_dev->dev, + "timed out waiting for admin response\n"); + return -ETIMEDOUT; + } + usleep_range(1000, 2000); + } + + memcpy(response, admin_queues->oq_element_array + + (oq_ci * PQI_ADMIN_OQ_ELEMENT_LENGTH), sizeof(*response)); + + oq_ci = (oq_ci + 1) % PQI_ADMIN_OQ_NUM_ELEMENTS; + admin_queues->oq_ci_copy = oq_ci; + writel(oq_ci, admin_queues->oq_ci); + + return 0; +} + +static void pqi_start_io(struct pqi_ctrl_info *ctrl_info, + struct pqi_queue_group *queue_group, enum pqi_io_path path, + struct pqi_io_request *io_request) +{ + struct pqi_io_request *next; + void *next_element; + pqi_index_t iq_pi; + pqi_index_t iq_ci; + size_t iu_length; + unsigned long flags; + unsigned int num_elements_needed; + unsigned int num_elements_to_end_of_queue; + size_t copy_count; + struct pqi_iu_header *request; + + spin_lock_irqsave(&queue_group->submit_lock[path], flags); + + if (io_request) + list_add_tail(&io_request->request_list_entry, + &queue_group->request_list[path]); + + iq_pi = queue_group->iq_pi_copy[path]; + + list_for_each_entry_safe(io_request, next, + &queue_group->request_list[path], request_list_entry) { + + request = io_request->iu; + + iu_length = get_unaligned_le16(&request->iu_length) + + PQI_REQUEST_HEADER_LENGTH; + num_elements_needed = + DIV_ROUND_UP(iu_length, + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH); + + iq_ci = *queue_group->iq_ci[path]; + + if (num_elements_needed > pqi_num_elements_free(iq_pi, iq_ci, + ctrl_info->num_elements_per_iq)) + break; + + put_unaligned_le16(queue_group->oq_id, + &request->response_queue_id); + + next_element = queue_group->iq_element_array[path] + + (iq_pi * PQI_OPERATIONAL_IQ_ELEMENT_LENGTH); + + num_elements_to_end_of_queue = + ctrl_info->num_elements_per_iq - iq_pi; + + if (num_elements_needed <= num_elements_to_end_of_queue) { + memcpy(next_element, request, iu_length); + } else { + copy_count = num_elements_to_end_of_queue * + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH; + memcpy(next_element, request, copy_count); + memcpy(queue_group->iq_element_array[path], + (u8 *)request + copy_count, + iu_length - copy_count); + } + + iq_pi = (iq_pi + num_elements_needed) % + ctrl_info->num_elements_per_iq; + + list_del(&io_request->request_list_entry); + } + + if (iq_pi != queue_group->iq_pi_copy[path]) { + queue_group->iq_pi_copy[path] = iq_pi; + /* + * This write notifies the controller that one or more IUs are + * available to be processed. + */ + writel(iq_pi, queue_group->iq_pi[path]); + } + + spin_unlock_irqrestore(&queue_group->submit_lock[path], flags); +} + +static void pqi_raid_synchronous_complete(struct pqi_io_request *io_request, + void *context) +{ + struct completion *waiting = context; + + complete(waiting); +} + +static int pqi_submit_raid_request_synchronous_with_io_request( + struct pqi_ctrl_info *ctrl_info, struct pqi_io_request *io_request, + unsigned long timeout_msecs) +{ + int rc = 0; + DECLARE_COMPLETION_ONSTACK(wait); + + io_request->io_complete_callback = pqi_raid_synchronous_complete; + io_request->context = &wait; + + pqi_start_io(ctrl_info, + &ctrl_info->queue_groups[PQI_DEFAULT_QUEUE_GROUP], RAID_PATH, + io_request); + + if (timeout_msecs == NO_TIMEOUT) { + wait_for_completion_io(&wait); + } else { + if (!wait_for_completion_io_timeout(&wait, + msecs_to_jiffies(timeout_msecs))) { + dev_warn(&ctrl_info->pci_dev->dev, + "command timed out\n"); + rc = -ETIMEDOUT; + } + } + + return rc; +} + +static int pqi_submit_raid_request_synchronous(struct pqi_ctrl_info *ctrl_info, + struct pqi_iu_header *request, unsigned int flags, + struct pqi_raid_error_info *error_info, unsigned long timeout_msecs) +{ + int rc; + struct pqi_io_request *io_request; + unsigned long start_jiffies; + unsigned long msecs_blocked; + size_t iu_length; + + /* + * Note that specifying PQI_SYNC_FLAGS_INTERRUPTABLE and a timeout value + * are mutually exclusive. + */ + + if (flags & PQI_SYNC_FLAGS_INTERRUPTABLE) { + if (down_interruptible(&ctrl_info->sync_request_sem)) + return -ERESTARTSYS; + } else { + if (timeout_msecs == NO_TIMEOUT) { + down(&ctrl_info->sync_request_sem); + } else { + start_jiffies = jiffies; + if (down_timeout(&ctrl_info->sync_request_sem, + msecs_to_jiffies(timeout_msecs))) + return -ETIMEDOUT; + msecs_blocked = + jiffies_to_msecs(jiffies - start_jiffies); + if (msecs_blocked >= timeout_msecs) + return -ETIMEDOUT; + timeout_msecs -= msecs_blocked; + } + } + + io_request = pqi_alloc_io_request(ctrl_info); + + put_unaligned_le16(io_request->index, + &(((struct pqi_raid_path_request *)request)->request_id)); + + if (request->iu_type == PQI_REQUEST_IU_RAID_PATH_IO) + ((struct pqi_raid_path_request *)request)->error_index = + ((struct pqi_raid_path_request *)request)->request_id; + + iu_length = get_unaligned_le16(&request->iu_length) + + PQI_REQUEST_HEADER_LENGTH; + memcpy(io_request->iu, request, iu_length); + + rc = pqi_submit_raid_request_synchronous_with_io_request(ctrl_info, + io_request, timeout_msecs); + + if (error_info) { + if (io_request->error_info) + memcpy(error_info, io_request->error_info, + sizeof(*error_info)); + else + memset(error_info, 0, sizeof(*error_info)); + } else if (rc == 0 && io_request->error_info) { + u8 scsi_status; + struct pqi_raid_error_info *raid_error_info; + + raid_error_info = io_request->error_info; + scsi_status = raid_error_info->status; + + if (scsi_status == SAM_STAT_CHECK_CONDITION && + raid_error_info->data_out_result == + PQI_DATA_IN_OUT_UNDERFLOW) + scsi_status = SAM_STAT_GOOD; + + if (scsi_status != SAM_STAT_GOOD) + rc = -EIO; + } + + pqi_free_io_request(io_request); + + up(&ctrl_info->sync_request_sem); + + return rc; +} + +static int pqi_validate_admin_response( + struct pqi_general_admin_response *response, u8 expected_function_code) +{ + if (response->header.iu_type != PQI_RESPONSE_IU_GENERAL_ADMIN) + return -EINVAL; + + if (get_unaligned_le16(&response->header.iu_length) != + PQI_GENERAL_ADMIN_IU_LENGTH) + return -EINVAL; + + if (response->function_code != expected_function_code) + return -EINVAL; + + if (response->status != PQI_GENERAL_ADMIN_STATUS_SUCCESS) + return -EINVAL; + + return 0; +} + +static int pqi_submit_admin_request_synchronous( + struct pqi_ctrl_info *ctrl_info, + struct pqi_general_admin_request *request, + struct pqi_general_admin_response *response) +{ + int rc; + + pqi_submit_admin_request(ctrl_info, request); + + rc = pqi_poll_for_admin_response(ctrl_info, response); + + if (rc == 0) + rc = pqi_validate_admin_response(response, + request->function_code); + + return rc; +} + +static int pqi_report_device_capability(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + struct pqi_general_admin_request request; + struct pqi_general_admin_response response; + struct pqi_device_capability *capability; + struct pqi_iu_layer_descriptor *sop_iu_layer_descriptor; + + capability = kmalloc(sizeof(*capability), GFP_KERNEL); + if (!capability) + return -ENOMEM; + + memset(&request, 0, sizeof(request)); + + request.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN; + put_unaligned_le16(PQI_GENERAL_ADMIN_IU_LENGTH, + &request.header.iu_length); + request.function_code = + PQI_GENERAL_ADMIN_FUNCTION_REPORT_DEVICE_CAPABILITY; + put_unaligned_le32(sizeof(*capability), + &request.data.report_device_capability.buffer_length); + + rc = pqi_map_single(ctrl_info->pci_dev, + &request.data.report_device_capability.sg_descriptor, + capability, sizeof(*capability), + PCI_DMA_FROMDEVICE); + if (rc) + goto out; + + rc = pqi_submit_admin_request_synchronous(ctrl_info, &request, + &response); + + pqi_pci_unmap(ctrl_info->pci_dev, + &request.data.report_device_capability.sg_descriptor, 1, + PCI_DMA_FROMDEVICE); + + if (rc) + goto out; + + if (response.status != PQI_GENERAL_ADMIN_STATUS_SUCCESS) { + rc = -EIO; + goto out; + } + + ctrl_info->max_inbound_queues = + get_unaligned_le16(&capability->max_inbound_queues); + ctrl_info->max_elements_per_iq = + get_unaligned_le16(&capability->max_elements_per_iq); + ctrl_info->max_iq_element_length = + get_unaligned_le16(&capability->max_iq_element_length) + * 16; + ctrl_info->max_outbound_queues = + get_unaligned_le16(&capability->max_outbound_queues); + ctrl_info->max_elements_per_oq = + get_unaligned_le16(&capability->max_elements_per_oq); + ctrl_info->max_oq_element_length = + get_unaligned_le16(&capability->max_oq_element_length) + * 16; + + sop_iu_layer_descriptor = + &capability->iu_layer_descriptors[PQI_PROTOCOL_SOP]; + + ctrl_info->max_inbound_iu_length_per_firmware = + get_unaligned_le16( + &sop_iu_layer_descriptor->max_inbound_iu_length); + ctrl_info->inbound_spanning_supported = + sop_iu_layer_descriptor->inbound_spanning_supported; + ctrl_info->outbound_spanning_supported = + sop_iu_layer_descriptor->outbound_spanning_supported; + +out: + kfree(capability); + + return rc; +} + +static int pqi_validate_device_capability(struct pqi_ctrl_info *ctrl_info) +{ + if (ctrl_info->max_iq_element_length < + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH) { + dev_err(&ctrl_info->pci_dev->dev, + "max. inbound queue element length of %d is less than the required length of %d\n", + ctrl_info->max_iq_element_length, + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH); + return -EINVAL; + } + + if (ctrl_info->max_oq_element_length < + PQI_OPERATIONAL_OQ_ELEMENT_LENGTH) { + dev_err(&ctrl_info->pci_dev->dev, + "max. outbound queue element length of %d is less than the required length of %d\n", + ctrl_info->max_oq_element_length, + PQI_OPERATIONAL_OQ_ELEMENT_LENGTH); + return -EINVAL; + } + + if (ctrl_info->max_inbound_iu_length_per_firmware < + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH) { + dev_err(&ctrl_info->pci_dev->dev, + "max. inbound IU length of %u is less than the min. required length of %d\n", + ctrl_info->max_inbound_iu_length_per_firmware, + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH); + return -EINVAL; + } + + if (!ctrl_info->inbound_spanning_supported) { + dev_err(&ctrl_info->pci_dev->dev, + "the controller does not support inbound spanning\n"); + return -EINVAL; + } + + if (ctrl_info->outbound_spanning_supported) { + dev_err(&ctrl_info->pci_dev->dev, + "the controller supports outbound spanning but this driver does not\n"); + return -EINVAL; + } + + return 0; +} + +static int pqi_delete_operational_queue(struct pqi_ctrl_info *ctrl_info, + bool inbound_queue, u16 queue_id) +{ + struct pqi_general_admin_request request; + struct pqi_general_admin_response response; + + memset(&request, 0, sizeof(request)); + request.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN; + put_unaligned_le16(PQI_GENERAL_ADMIN_IU_LENGTH, + &request.header.iu_length); + if (inbound_queue) + request.function_code = + PQI_GENERAL_ADMIN_FUNCTION_DELETE_IQ; + else + request.function_code = + PQI_GENERAL_ADMIN_FUNCTION_DELETE_OQ; + put_unaligned_le16(queue_id, + &request.data.delete_operational_queue.queue_id); + + return pqi_submit_admin_request_synchronous(ctrl_info, &request, + &response); +} + +static int pqi_create_event_queue(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + struct pqi_event_queue *event_queue; + struct pqi_general_admin_request request; + struct pqi_general_admin_response response; + + event_queue = &ctrl_info->event_queue; + + /* + * Create OQ (Outbound Queue - device to host queue) to dedicate + * to events. + */ + memset(&request, 0, sizeof(request)); + request.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN; + put_unaligned_le16(PQI_GENERAL_ADMIN_IU_LENGTH, + &request.header.iu_length); + request.function_code = PQI_GENERAL_ADMIN_FUNCTION_CREATE_OQ; + put_unaligned_le16(event_queue->oq_id, + &request.data.create_operational_oq.queue_id); + put_unaligned_le64((u64)event_queue->oq_element_array_bus_addr, + &request.data.create_operational_oq.element_array_addr); + put_unaligned_le64((u64)event_queue->oq_pi_bus_addr, + &request.data.create_operational_oq.pi_addr); + put_unaligned_le16(PQI_NUM_EVENT_QUEUE_ELEMENTS, + &request.data.create_operational_oq.num_elements); + put_unaligned_le16(PQI_EVENT_OQ_ELEMENT_LENGTH / 16, + &request.data.create_operational_oq.element_length); + request.data.create_operational_oq.queue_protocol = PQI_PROTOCOL_SOP; + put_unaligned_le16(event_queue->int_msg_num, + &request.data.create_operational_oq.int_msg_num); + + rc = pqi_submit_admin_request_synchronous(ctrl_info, &request, + &response); + if (rc) + return rc; + + event_queue->oq_ci = ctrl_info->iomem_base + + PQI_DEVICE_REGISTERS_OFFSET + + get_unaligned_le64( + &response.data.create_operational_oq.oq_ci_offset); + + return 0; +} + +static int pqi_create_queue_group(struct pqi_ctrl_info *ctrl_info) +{ + unsigned int i; + int rc; + struct pqi_queue_group *queue_group; + struct pqi_general_admin_request request; + struct pqi_general_admin_response response; + + i = ctrl_info->num_active_queue_groups; + queue_group = &ctrl_info->queue_groups[i]; + + /* + * Create IQ (Inbound Queue - host to device queue) for + * RAID path. + */ + memset(&request, 0, sizeof(request)); + request.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN; + put_unaligned_le16(PQI_GENERAL_ADMIN_IU_LENGTH, + &request.header.iu_length); + request.function_code = PQI_GENERAL_ADMIN_FUNCTION_CREATE_IQ; + put_unaligned_le16(queue_group->iq_id[RAID_PATH], + &request.data.create_operational_iq.queue_id); + put_unaligned_le64( + (u64)queue_group->iq_element_array_bus_addr[RAID_PATH], + &request.data.create_operational_iq.element_array_addr); + put_unaligned_le64((u64)queue_group->iq_ci_bus_addr[RAID_PATH], + &request.data.create_operational_iq.ci_addr); + put_unaligned_le16(ctrl_info->num_elements_per_iq, + &request.data.create_operational_iq.num_elements); + put_unaligned_le16(PQI_OPERATIONAL_IQ_ELEMENT_LENGTH / 16, + &request.data.create_operational_iq.element_length); + request.data.create_operational_iq.queue_protocol = PQI_PROTOCOL_SOP; + + rc = pqi_submit_admin_request_synchronous(ctrl_info, &request, + &response); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error creating inbound RAID queue\n"); + return rc; + } + + queue_group->iq_pi[RAID_PATH] = ctrl_info->iomem_base + + PQI_DEVICE_REGISTERS_OFFSET + + get_unaligned_le64( + &response.data.create_operational_iq.iq_pi_offset); + + /* + * Create IQ (Inbound Queue - host to device queue) for + * Advanced I/O (AIO) path. + */ + memset(&request, 0, sizeof(request)); + request.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN; + put_unaligned_le16(PQI_GENERAL_ADMIN_IU_LENGTH, + &request.header.iu_length); + request.function_code = PQI_GENERAL_ADMIN_FUNCTION_CREATE_IQ; + put_unaligned_le16(queue_group->iq_id[AIO_PATH], + &request.data.create_operational_iq.queue_id); + put_unaligned_le64((u64)queue_group-> + iq_element_array_bus_addr[AIO_PATH], + &request.data.create_operational_iq.element_array_addr); + put_unaligned_le64((u64)queue_group->iq_ci_bus_addr[AIO_PATH], + &request.data.create_operational_iq.ci_addr); + put_unaligned_le16(ctrl_info->num_elements_per_iq, + &request.data.create_operational_iq.num_elements); + put_unaligned_le16(PQI_OPERATIONAL_IQ_ELEMENT_LENGTH / 16, + &request.data.create_operational_iq.element_length); + request.data.create_operational_iq.queue_protocol = PQI_PROTOCOL_SOP; + + rc = pqi_submit_admin_request_synchronous(ctrl_info, &request, + &response); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error creating inbound AIO queue\n"); + goto delete_inbound_queue_raid; + } + + queue_group->iq_pi[AIO_PATH] = ctrl_info->iomem_base + + PQI_DEVICE_REGISTERS_OFFSET + + get_unaligned_le64( + &response.data.create_operational_iq.iq_pi_offset); + + /* + * Designate the 2nd IQ as the AIO path. By default, all IQs are + * assumed to be for RAID path I/O unless we change the queue's + * property. + */ + memset(&request, 0, sizeof(request)); + request.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN; + put_unaligned_le16(PQI_GENERAL_ADMIN_IU_LENGTH, + &request.header.iu_length); + request.function_code = PQI_GENERAL_ADMIN_FUNCTION_CHANGE_IQ_PROPERTY; + put_unaligned_le16(queue_group->iq_id[AIO_PATH], + &request.data.change_operational_iq_properties.queue_id); + put_unaligned_le32(PQI_IQ_PROPERTY_IS_AIO_QUEUE, + &request.data.change_operational_iq_properties.vendor_specific); + + rc = pqi_submit_admin_request_synchronous(ctrl_info, &request, + &response); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error changing queue property\n"); + goto delete_inbound_queue_aio; + } + + /* + * Create OQ (Outbound Queue - device to host queue). + */ + memset(&request, 0, sizeof(request)); + request.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN; + put_unaligned_le16(PQI_GENERAL_ADMIN_IU_LENGTH, + &request.header.iu_length); + request.function_code = PQI_GENERAL_ADMIN_FUNCTION_CREATE_OQ; + put_unaligned_le16(queue_group->oq_id, + &request.data.create_operational_oq.queue_id); + put_unaligned_le64((u64)queue_group->oq_element_array_bus_addr, + &request.data.create_operational_oq.element_array_addr); + put_unaligned_le64((u64)queue_group->oq_pi_bus_addr, + &request.data.create_operational_oq.pi_addr); + put_unaligned_le16(ctrl_info->num_elements_per_oq, + &request.data.create_operational_oq.num_elements); + put_unaligned_le16(PQI_OPERATIONAL_OQ_ELEMENT_LENGTH / 16, + &request.data.create_operational_oq.element_length); + request.data.create_operational_oq.queue_protocol = PQI_PROTOCOL_SOP; + put_unaligned_le16(queue_group->int_msg_num, + &request.data.create_operational_oq.int_msg_num); + + rc = pqi_submit_admin_request_synchronous(ctrl_info, &request, + &response); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error creating outbound queue\n"); + goto delete_inbound_queue_aio; + } + + queue_group->oq_ci = ctrl_info->iomem_base + + PQI_DEVICE_REGISTERS_OFFSET + + get_unaligned_le64( + &response.data.create_operational_oq.oq_ci_offset); + + ctrl_info->num_active_queue_groups++; + + return 0; + +delete_inbound_queue_aio: + pqi_delete_operational_queue(ctrl_info, true, + queue_group->iq_id[AIO_PATH]); + +delete_inbound_queue_raid: + pqi_delete_operational_queue(ctrl_info, true, + queue_group->iq_id[RAID_PATH]); + + return rc; +} + +static int pqi_create_queues(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + unsigned int i; + + rc = pqi_create_event_queue(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error creating event queue\n"); + return rc; + } + + for (i = 0; i < ctrl_info->num_queue_groups; i++) { + rc = pqi_create_queue_group(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error creating queue group number %u/%u\n", + i, ctrl_info->num_queue_groups); + return rc; + } + } + + return 0; +} + +#define PQI_REPORT_EVENT_CONFIG_BUFFER_LENGTH \ + (offsetof(struct pqi_event_config, descriptors) + \ + (PQI_MAX_EVENT_DESCRIPTORS * sizeof(struct pqi_event_descriptor))) + +static int pqi_configure_events(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + unsigned int i; + struct pqi_event_config *event_config; + struct pqi_general_management_request request; + + event_config = kmalloc(PQI_REPORT_EVENT_CONFIG_BUFFER_LENGTH, + GFP_KERNEL); + if (!event_config) + return -ENOMEM; + + memset(&request, 0, sizeof(request)); + + request.header.iu_type = PQI_REQUEST_IU_REPORT_VENDOR_EVENT_CONFIG; + put_unaligned_le16(offsetof(struct pqi_general_management_request, + data.report_event_configuration.sg_descriptors[1]) - + PQI_REQUEST_HEADER_LENGTH, &request.header.iu_length); + put_unaligned_le32(PQI_REPORT_EVENT_CONFIG_BUFFER_LENGTH, + &request.data.report_event_configuration.buffer_length); + + rc = pqi_map_single(ctrl_info->pci_dev, + request.data.report_event_configuration.sg_descriptors, + event_config, PQI_REPORT_EVENT_CONFIG_BUFFER_LENGTH, + PCI_DMA_FROMDEVICE); + if (rc) + goto out; + + rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, + 0, NULL, NO_TIMEOUT); + + pqi_pci_unmap(ctrl_info->pci_dev, + request.data.report_event_configuration.sg_descriptors, 1, + PCI_DMA_FROMDEVICE); + + if (rc) + goto out; + + for (i = 0; i < event_config->num_event_descriptors; i++) + put_unaligned_le16(ctrl_info->event_queue.oq_id, + &event_config->descriptors[i].oq_id); + + memset(&request, 0, sizeof(request)); + + request.header.iu_type = PQI_REQUEST_IU_SET_VENDOR_EVENT_CONFIG; + put_unaligned_le16(offsetof(struct pqi_general_management_request, + data.report_event_configuration.sg_descriptors[1]) - + PQI_REQUEST_HEADER_LENGTH, &request.header.iu_length); + put_unaligned_le32(PQI_REPORT_EVENT_CONFIG_BUFFER_LENGTH, + &request.data.report_event_configuration.buffer_length); + + rc = pqi_map_single(ctrl_info->pci_dev, + request.data.report_event_configuration.sg_descriptors, + event_config, PQI_REPORT_EVENT_CONFIG_BUFFER_LENGTH, + PCI_DMA_TODEVICE); + if (rc) + goto out; + + rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, 0, + NULL, NO_TIMEOUT); + + pqi_pci_unmap(ctrl_info->pci_dev, + request.data.report_event_configuration.sg_descriptors, 1, + PCI_DMA_TODEVICE); + +out: + kfree(event_config); + + return rc; +} + +static void pqi_free_all_io_requests(struct pqi_ctrl_info *ctrl_info) +{ + unsigned int i; + struct device *dev; + size_t sg_chain_buffer_length; + struct pqi_io_request *io_request; + + if (!ctrl_info->io_request_pool) + return; + + dev = &ctrl_info->pci_dev->dev; + sg_chain_buffer_length = ctrl_info->sg_chain_buffer_length; + io_request = ctrl_info->io_request_pool; + + for (i = 0; i < ctrl_info->max_io_slots; i++) { + kfree(io_request->iu); + if (!io_request->sg_chain_buffer) + break; + dma_free_coherent(dev, sg_chain_buffer_length, + io_request->sg_chain_buffer, + io_request->sg_chain_buffer_dma_handle); + io_request++; + } + + kfree(ctrl_info->io_request_pool); + ctrl_info->io_request_pool = NULL; +} + +static inline int pqi_alloc_error_buffer(struct pqi_ctrl_info *ctrl_info) +{ + ctrl_info->error_buffer = dma_zalloc_coherent(&ctrl_info->pci_dev->dev, + ctrl_info->error_buffer_length, + &ctrl_info->error_buffer_dma_handle, GFP_KERNEL); + + if (!ctrl_info->error_buffer) + return -ENOMEM; + + return 0; +} + +static int pqi_alloc_io_resources(struct pqi_ctrl_info *ctrl_info) +{ + unsigned int i; + void *sg_chain_buffer; + size_t sg_chain_buffer_length; + dma_addr_t sg_chain_buffer_dma_handle; + struct device *dev; + struct pqi_io_request *io_request; + + ctrl_info->io_request_pool = kzalloc(ctrl_info->max_io_slots * + sizeof(ctrl_info->io_request_pool[0]), GFP_KERNEL); + + if (!ctrl_info->io_request_pool) { + dev_err(&ctrl_info->pci_dev->dev, + "failed to allocate I/O request pool\n"); + goto error; + } + + dev = &ctrl_info->pci_dev->dev; + sg_chain_buffer_length = ctrl_info->sg_chain_buffer_length; + io_request = ctrl_info->io_request_pool; + + for (i = 0; i < ctrl_info->max_io_slots; i++) { + io_request->iu = + kmalloc(ctrl_info->max_inbound_iu_length, GFP_KERNEL); + + if (!io_request->iu) { + dev_err(&ctrl_info->pci_dev->dev, + "failed to allocate IU buffers\n"); + goto error; + } + + sg_chain_buffer = dma_alloc_coherent(dev, + sg_chain_buffer_length, &sg_chain_buffer_dma_handle, + GFP_KERNEL); + + if (!sg_chain_buffer) { + dev_err(&ctrl_info->pci_dev->dev, + "failed to allocate PQI scatter-gather chain buffers\n"); + goto error; + } + + io_request->index = i; + io_request->sg_chain_buffer = sg_chain_buffer; + io_request->sg_chain_buffer_dma_handle = + sg_chain_buffer_dma_handle; + io_request++; + } + + return 0; + +error: + pqi_free_all_io_requests(ctrl_info); + + return -ENOMEM; +} + +/* + * Calculate required resources that are sized based on max. outstanding + * requests and max. transfer size. + */ + +static void pqi_calculate_io_resources(struct pqi_ctrl_info *ctrl_info) +{ + u32 max_transfer_size; + u32 max_sg_entries; + + ctrl_info->scsi_ml_can_queue = + ctrl_info->max_outstanding_requests - PQI_RESERVED_IO_SLOTS; + ctrl_info->max_io_slots = ctrl_info->max_outstanding_requests; + + ctrl_info->error_buffer_length = + ctrl_info->max_io_slots * PQI_ERROR_BUFFER_ELEMENT_LENGTH; + + max_transfer_size = + min(ctrl_info->max_transfer_size, PQI_MAX_TRANSFER_SIZE); + + max_sg_entries = max_transfer_size / PAGE_SIZE; + + /* +1 to cover when the buffer is not page-aligned. */ + max_sg_entries++; + + max_sg_entries = min(ctrl_info->max_sg_entries, max_sg_entries); + + max_transfer_size = (max_sg_entries - 1) * PAGE_SIZE; + + ctrl_info->sg_chain_buffer_length = + max_sg_entries * sizeof(struct pqi_sg_descriptor); + ctrl_info->sg_tablesize = max_sg_entries; + ctrl_info->max_sectors = max_transfer_size / 512; +} + +static void pqi_calculate_queue_resources(struct pqi_ctrl_info *ctrl_info) +{ + int num_cpus; + int max_queue_groups; + int num_queue_groups; + u16 num_elements_per_iq; + u16 num_elements_per_oq; + + max_queue_groups = min(ctrl_info->max_inbound_queues / 2, + ctrl_info->max_outbound_queues - 1); + max_queue_groups = min(max_queue_groups, PQI_MAX_QUEUE_GROUPS); + + num_cpus = num_online_cpus(); + num_queue_groups = min(num_cpus, ctrl_info->max_msix_vectors); + num_queue_groups = min(num_queue_groups, max_queue_groups); + + ctrl_info->num_queue_groups = num_queue_groups; + + /* + * Make sure that the max. inbound IU length is an even multiple + * of our inbound element length. + */ + ctrl_info->max_inbound_iu_length = + (ctrl_info->max_inbound_iu_length_per_firmware / + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH) * + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH; + + num_elements_per_iq = + (ctrl_info->max_inbound_iu_length / + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH); + + /* Add one because one element in each queue is unusable. */ + num_elements_per_iq++; + + num_elements_per_iq = min(num_elements_per_iq, + ctrl_info->max_elements_per_iq); + + num_elements_per_oq = ((num_elements_per_iq - 1) * 2) + 1; + num_elements_per_oq = min(num_elements_per_oq, + ctrl_info->max_elements_per_oq); + + ctrl_info->num_elements_per_iq = num_elements_per_iq; + ctrl_info->num_elements_per_oq = num_elements_per_oq; + + ctrl_info->max_sg_per_iu = + ((ctrl_info->max_inbound_iu_length - + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH) / + sizeof(struct pqi_sg_descriptor)) + + PQI_MAX_EMBEDDED_SG_DESCRIPTORS; +} + +static inline void pqi_set_sg_descriptor( + struct pqi_sg_descriptor *sg_descriptor, struct scatterlist *sg) +{ + u64 address = (u64)sg_dma_address(sg); + unsigned int length = sg_dma_len(sg); + + put_unaligned_le64(address, &sg_descriptor->address); + put_unaligned_le32(length, &sg_descriptor->length); + put_unaligned_le32(0, &sg_descriptor->flags); +} + +static int pqi_build_raid_sg_list(struct pqi_ctrl_info *ctrl_info, + struct pqi_raid_path_request *request, struct scsi_cmnd *scmd, + struct pqi_io_request *io_request) +{ + int i; + u16 iu_length; + int sg_count; + bool chained; + unsigned int num_sg_in_iu; + unsigned int max_sg_per_iu; + struct scatterlist *sg; + struct pqi_sg_descriptor *sg_descriptor; + + sg_count = scsi_dma_map(scmd); + if (sg_count < 0) + return sg_count; + + iu_length = offsetof(struct pqi_raid_path_request, sg_descriptors) - + PQI_REQUEST_HEADER_LENGTH; + + if (sg_count == 0) + goto out; + + sg = scsi_sglist(scmd); + sg_descriptor = request->sg_descriptors; + max_sg_per_iu = ctrl_info->max_sg_per_iu - 1; + chained = false; + num_sg_in_iu = 0; + i = 0; + + while (1) { + pqi_set_sg_descriptor(sg_descriptor, sg); + if (!chained) + num_sg_in_iu++; + i++; + if (i == sg_count) + break; + sg_descriptor++; + if (i == max_sg_per_iu) { + put_unaligned_le64( + (u64)io_request->sg_chain_buffer_dma_handle, + &sg_descriptor->address); + put_unaligned_le32((sg_count - num_sg_in_iu) + * sizeof(*sg_descriptor), + &sg_descriptor->length); + put_unaligned_le32(CISS_SG_CHAIN, + &sg_descriptor->flags); + chained = true; + num_sg_in_iu++; + sg_descriptor = io_request->sg_chain_buffer; + } + sg = sg_next(sg); + } + + put_unaligned_le32(CISS_SG_LAST, &sg_descriptor->flags); + request->partial = chained; + iu_length += num_sg_in_iu * sizeof(*sg_descriptor); + +out: + put_unaligned_le16(iu_length, &request->header.iu_length); + + return 0; +} + +static int pqi_build_aio_sg_list(struct pqi_ctrl_info *ctrl_info, + struct pqi_aio_path_request *request, struct scsi_cmnd *scmd, + struct pqi_io_request *io_request) +{ + int i; + u16 iu_length; + int sg_count; + bool chained; + unsigned int num_sg_in_iu; + unsigned int max_sg_per_iu; + struct scatterlist *sg; + struct pqi_sg_descriptor *sg_descriptor; + + sg_count = scsi_dma_map(scmd); + if (sg_count < 0) + return sg_count; + + iu_length = offsetof(struct pqi_aio_path_request, sg_descriptors) - + PQI_REQUEST_HEADER_LENGTH; + num_sg_in_iu = 0; + + if (sg_count == 0) + goto out; + + sg = scsi_sglist(scmd); + sg_descriptor = request->sg_descriptors; + max_sg_per_iu = ctrl_info->max_sg_per_iu - 1; + chained = false; + i = 0; + + while (1) { + pqi_set_sg_descriptor(sg_descriptor, sg); + if (!chained) + num_sg_in_iu++; + i++; + if (i == sg_count) + break; + sg_descriptor++; + if (i == max_sg_per_iu) { + put_unaligned_le64( + (u64)io_request->sg_chain_buffer_dma_handle, + &sg_descriptor->address); + put_unaligned_le32((sg_count - num_sg_in_iu) + * sizeof(*sg_descriptor), + &sg_descriptor->length); + put_unaligned_le32(CISS_SG_CHAIN, + &sg_descriptor->flags); + chained = true; + num_sg_in_iu++; + sg_descriptor = io_request->sg_chain_buffer; + } + sg = sg_next(sg); + } + + put_unaligned_le32(CISS_SG_LAST, &sg_descriptor->flags); + request->partial = chained; + iu_length += num_sg_in_iu * sizeof(*sg_descriptor); + +out: + put_unaligned_le16(iu_length, &request->header.iu_length); + request->num_sg_descriptors = num_sg_in_iu; + + return 0; +} + +static void pqi_raid_io_complete(struct pqi_io_request *io_request, + void *context) +{ + struct scsi_cmnd *scmd; + + scmd = io_request->scmd; + pqi_free_io_request(io_request); + scsi_dma_unmap(scmd); + pqi_scsi_done(scmd); +} + +static int pqi_raid_submit_scsi_cmd(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device, struct scsi_cmnd *scmd, + struct pqi_queue_group *queue_group) +{ + int rc; + size_t cdb_length; + struct pqi_io_request *io_request; + struct pqi_raid_path_request *request; + + io_request = pqi_alloc_io_request(ctrl_info); + io_request->io_complete_callback = pqi_raid_io_complete; + io_request->scmd = scmd; + + scmd->host_scribble = (unsigned char *)io_request; + + request = io_request->iu; + memset(request, 0, + offsetof(struct pqi_raid_path_request, sg_descriptors)); + + request->header.iu_type = PQI_REQUEST_IU_RAID_PATH_IO; + put_unaligned_le32(scsi_bufflen(scmd), &request->buffer_length); + request->task_attribute = SOP_TASK_ATTRIBUTE_SIMPLE; + put_unaligned_le16(io_request->index, &request->request_id); + request->error_index = request->request_id; + memcpy(request->lun_number, device->scsi3addr, + sizeof(request->lun_number)); + + cdb_length = min_t(size_t, scmd->cmd_len, sizeof(request->cdb)); + memcpy(request->cdb, scmd->cmnd, cdb_length); + + switch (cdb_length) { + case 6: + case 10: + case 12: + case 16: + /* No bytes in the Additional CDB bytes field */ + request->additional_cdb_bytes_usage = + SOP_ADDITIONAL_CDB_BYTES_0; + break; + case 20: + /* 4 bytes in the Additional cdb field */ + request->additional_cdb_bytes_usage = + SOP_ADDITIONAL_CDB_BYTES_4; + break; + case 24: + /* 8 bytes in the Additional cdb field */ + request->additional_cdb_bytes_usage = + SOP_ADDITIONAL_CDB_BYTES_8; + break; + case 28: + /* 12 bytes in the Additional cdb field */ + request->additional_cdb_bytes_usage = + SOP_ADDITIONAL_CDB_BYTES_12; + break; + case 32: + default: + /* 16 bytes in the Additional cdb field */ + request->additional_cdb_bytes_usage = + SOP_ADDITIONAL_CDB_BYTES_16; + break; + } + + switch (scmd->sc_data_direction) { + case DMA_TO_DEVICE: + request->data_direction = SOP_READ_FLAG; + break; + case DMA_FROM_DEVICE: + request->data_direction = SOP_WRITE_FLAG; + break; + case DMA_NONE: + request->data_direction = SOP_NO_DIRECTION_FLAG; + break; + case DMA_BIDIRECTIONAL: + request->data_direction = SOP_BIDIRECTIONAL; + break; + default: + dev_err(&ctrl_info->pci_dev->dev, + "unknown data direction: %d\n", + scmd->sc_data_direction); + WARN_ON(scmd->sc_data_direction); + break; + } + + rc = pqi_build_raid_sg_list(ctrl_info, request, scmd, io_request); + if (rc) { + pqi_free_io_request(io_request); + return SCSI_MLQUEUE_HOST_BUSY; + } + + pqi_start_io(ctrl_info, queue_group, RAID_PATH, io_request); + + return 0; +} + +static void pqi_aio_io_complete(struct pqi_io_request *io_request, + void *context) +{ + struct scsi_cmnd *scmd; + + scmd = io_request->scmd; + scsi_dma_unmap(scmd); + if (io_request->status == -EAGAIN) + set_host_byte(scmd, DID_IMM_RETRY); + pqi_free_io_request(io_request); + pqi_scsi_done(scmd); +} + +static inline int pqi_aio_submit_scsi_cmd(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device, struct scsi_cmnd *scmd, + struct pqi_queue_group *queue_group) +{ + return pqi_aio_submit_io(ctrl_info, scmd, device->aio_handle, + scmd->cmnd, scmd->cmd_len, queue_group, NULL); +} + +static int pqi_aio_submit_io(struct pqi_ctrl_info *ctrl_info, + struct scsi_cmnd *scmd, u32 aio_handle, u8 *cdb, + unsigned int cdb_length, struct pqi_queue_group *queue_group, + struct pqi_encryption_info *encryption_info) +{ + int rc; + struct pqi_io_request *io_request; + struct pqi_aio_path_request *request; + + io_request = pqi_alloc_io_request(ctrl_info); + io_request->io_complete_callback = pqi_aio_io_complete; + io_request->scmd = scmd; + + scmd->host_scribble = (unsigned char *)io_request; + + request = io_request->iu; + memset(request, 0, + offsetof(struct pqi_raid_path_request, sg_descriptors)); + + request->header.iu_type = PQI_REQUEST_IU_AIO_PATH_IO; + put_unaligned_le32(aio_handle, &request->nexus_id); + put_unaligned_le32(scsi_bufflen(scmd), &request->buffer_length); + request->task_attribute = SOP_TASK_ATTRIBUTE_SIMPLE; + put_unaligned_le16(io_request->index, &request->request_id); + request->error_index = request->request_id; + if (cdb_length > sizeof(request->cdb)) + cdb_length = sizeof(request->cdb); + request->cdb_length = cdb_length; + memcpy(request->cdb, cdb, cdb_length); + + switch (scmd->sc_data_direction) { + case DMA_TO_DEVICE: + request->data_direction = SOP_READ_FLAG; + break; + case DMA_FROM_DEVICE: + request->data_direction = SOP_WRITE_FLAG; + break; + case DMA_NONE: + request->data_direction = SOP_NO_DIRECTION_FLAG; + break; + case DMA_BIDIRECTIONAL: + request->data_direction = SOP_BIDIRECTIONAL; + break; + default: + dev_err(&ctrl_info->pci_dev->dev, + "unknown data direction: %d\n", + scmd->sc_data_direction); + WARN_ON(scmd->sc_data_direction); + break; + } + + if (encryption_info) { + request->encryption_enable = true; + put_unaligned_le16(encryption_info->data_encryption_key_index, + &request->data_encryption_key_index); + put_unaligned_le32(encryption_info->encrypt_tweak_lower, + &request->encrypt_tweak_lower); + put_unaligned_le32(encryption_info->encrypt_tweak_upper, + &request->encrypt_tweak_upper); + } + + rc = pqi_build_aio_sg_list(ctrl_info, request, scmd, io_request); + if (rc) { + pqi_free_io_request(io_request); + return SCSI_MLQUEUE_HOST_BUSY; + } + + pqi_start_io(ctrl_info, queue_group, AIO_PATH, io_request); + + return 0; +} + +static int pqi_scsi_queue_command(struct Scsi_Host *shost, + struct scsi_cmnd *scmd) +{ + int rc; + struct pqi_ctrl_info *ctrl_info; + struct pqi_scsi_dev *device; + u16 hwq; + struct pqi_queue_group *queue_group; + bool raid_bypassed; + + device = scmd->device->hostdata; + ctrl_info = shost_to_hba(shost); + + if (pqi_ctrl_offline(ctrl_info)) { + set_host_byte(scmd, DID_NO_CONNECT); + pqi_scsi_done(scmd); + return 0; + } + + /* + * This is necessary because the SML doesn't zero out this field during + * error recovery. + */ + scmd->result = 0; + + hwq = blk_mq_unique_tag_to_hwq(blk_mq_unique_tag(scmd->request)); + if (hwq >= ctrl_info->num_queue_groups) + hwq = 0; + + queue_group = &ctrl_info->queue_groups[hwq]; + + if (pqi_is_logical_device(device)) { + raid_bypassed = false; + if (device->offload_enabled && + scmd->request->cmd_type == REQ_TYPE_FS) { + rc = pqi_raid_bypass_submit_scsi_cmd(ctrl_info, device, + scmd, queue_group); + if (rc == 0 || + rc == SCSI_MLQUEUE_HOST_BUSY || + rc == SAM_STAT_CHECK_CONDITION || + rc == SAM_STAT_RESERVATION_CONFLICT) + raid_bypassed = true; + } + if (!raid_bypassed) + rc = pqi_raid_submit_scsi_cmd(ctrl_info, device, scmd, + queue_group); + } else { + if (device->aio_enabled) + rc = pqi_aio_submit_scsi_cmd(ctrl_info, device, scmd, + queue_group); + else + rc = pqi_raid_submit_scsi_cmd(ctrl_info, device, scmd, + queue_group); + } + + return rc; +} + +static void pqi_lun_reset_complete(struct pqi_io_request *io_request, + void *context) +{ + struct completion *waiting = context; + + complete(waiting); +} + +#define PQI_LUN_RESET_TIMEOUT_SECS 10 + +static int pqi_wait_for_lun_reset_completion(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device, struct completion *wait) +{ + int rc; + unsigned int wait_secs = 0; + + while (1) { + if (wait_for_completion_io_timeout(wait, + PQI_LUN_RESET_TIMEOUT_SECS * HZ)) { + rc = 0; + break; + } + + pqi_check_ctrl_health(ctrl_info); + if (pqi_ctrl_offline(ctrl_info)) { + rc = -ETIMEDOUT; + break; + } + + wait_secs += PQI_LUN_RESET_TIMEOUT_SECS; + + dev_err(&ctrl_info->pci_dev->dev, + "resetting scsi %d:%d:%d:%d - waiting %u seconds\n", + ctrl_info->scsi_host->host_no, device->bus, + device->target, device->lun, wait_secs); + } + + return rc; +} + +static int pqi_lun_reset(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device) +{ + int rc; + struct pqi_io_request *io_request; + DECLARE_COMPLETION_ONSTACK(wait); + struct pqi_task_management_request *request; + + down(&ctrl_info->lun_reset_sem); + + io_request = pqi_alloc_io_request(ctrl_info); + io_request->io_complete_callback = pqi_lun_reset_complete; + io_request->context = &wait; + + request = io_request->iu; + memset(request, 0, sizeof(*request)); + + request->header.iu_type = PQI_REQUEST_IU_TASK_MANAGEMENT; + put_unaligned_le16(sizeof(*request) - PQI_REQUEST_HEADER_LENGTH, + &request->header.iu_length); + put_unaligned_le16(io_request->index, &request->request_id); + memcpy(request->lun_number, device->scsi3addr, + sizeof(request->lun_number)); + request->task_management_function = SOP_TASK_MANAGEMENT_LUN_RESET; + + pqi_start_io(ctrl_info, + &ctrl_info->queue_groups[PQI_DEFAULT_QUEUE_GROUP], RAID_PATH, + io_request); + + rc = pqi_wait_for_lun_reset_completion(ctrl_info, device, &wait); + if (rc == 0) + rc = io_request->status; + + pqi_free_io_request(io_request); + up(&ctrl_info->lun_reset_sem); + + return rc; +} + +/* Performs a reset at the LUN level. */ + +static int pqi_device_reset(struct pqi_ctrl_info *ctrl_info, + struct pqi_scsi_dev *device) +{ + int rc; + + pqi_check_ctrl_health(ctrl_info); + if (pqi_ctrl_offline(ctrl_info)) + return FAILED; + + rc = pqi_lun_reset(ctrl_info, device); + + return rc == 0 ? SUCCESS : FAILED; +} + +static int pqi_eh_device_reset_handler(struct scsi_cmnd *scmd) +{ + int rc; + struct pqi_ctrl_info *ctrl_info; + struct pqi_scsi_dev *device; + + ctrl_info = shost_to_hba(scmd->device->host); + device = scmd->device->hostdata; + + dev_err(&ctrl_info->pci_dev->dev, + "resetting scsi %d:%d:%d:%d\n", + ctrl_info->scsi_host->host_no, + device->bus, device->target, device->lun); + + rc = pqi_device_reset(ctrl_info, device); + + dev_err(&ctrl_info->pci_dev->dev, + "reset of scsi %d:%d:%d:%d: %s\n", + ctrl_info->scsi_host->host_no, + device->bus, device->target, device->lun, + rc == SUCCESS ? "SUCCESS" : "FAILED"); + + return rc; +} + +static int pqi_slave_alloc(struct scsi_device *sdev) +{ + struct pqi_scsi_dev *device; + unsigned long flags; + struct pqi_ctrl_info *ctrl_info; + struct scsi_target *starget; + struct sas_rphy *rphy; + + ctrl_info = shost_to_hba(sdev->host); + + spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); + + if (sdev_channel(sdev) == PQI_PHYSICAL_DEVICE_BUS) { + starget = scsi_target(sdev); + rphy = target_to_rphy(starget); + device = pqi_find_device_by_sas_rphy(ctrl_info, rphy); + if (device) { + device->target = sdev_id(sdev); + device->lun = sdev->lun; + device->target_lun_valid = true; + } + } else { + device = pqi_find_scsi_dev(ctrl_info, sdev_channel(sdev), + sdev_id(sdev), sdev->lun); + } + + if (device && device->expose_device) { + sdev->hostdata = device; + device->sdev = sdev; + if (device->queue_depth) { + device->advertised_queue_depth = device->queue_depth; + scsi_change_queue_depth(sdev, + device->advertised_queue_depth); + } + } + + spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); + + return 0; +} + +static int pqi_slave_configure(struct scsi_device *sdev) +{ + struct pqi_scsi_dev *device; + + device = sdev->hostdata; + if (!device->expose_device) + sdev->no_uld_attach = true; + + return 0; +} + +static int pqi_getpciinfo_ioctl(struct pqi_ctrl_info *ctrl_info, + void __user *arg) +{ + struct pci_dev *pci_dev; + u32 subsystem_vendor; + u32 subsystem_device; + cciss_pci_info_struct pciinfo; + + if (!arg) + return -EINVAL; + + pci_dev = ctrl_info->pci_dev; + + pciinfo.domain = pci_domain_nr(pci_dev->bus); + pciinfo.bus = pci_dev->bus->number; + pciinfo.dev_fn = pci_dev->devfn; + subsystem_vendor = pci_dev->subsystem_vendor; + subsystem_device = pci_dev->subsystem_device; + pciinfo.board_id = ((subsystem_device << 16) & 0xffff0000) | + subsystem_vendor; + + if (copy_to_user(arg, &pciinfo, sizeof(pciinfo))) + return -EFAULT; + + return 0; +} + +static int pqi_getdrivver_ioctl(void __user *arg) +{ + u32 version; + + if (!arg) + return -EINVAL; + + version = (DRIVER_MAJOR << 28) | (DRIVER_MINOR << 24) | + (DRIVER_RELEASE << 16) | DRIVER_REVISION; + + if (copy_to_user(arg, &version, sizeof(version))) + return -EFAULT; + + return 0; +} + +struct ciss_error_info { + u8 scsi_status; + int command_status; + size_t sense_data_length; +}; + +static void pqi_error_info_to_ciss(struct pqi_raid_error_info *pqi_error_info, + struct ciss_error_info *ciss_error_info) +{ + int ciss_cmd_status; + size_t sense_data_length; + + switch (pqi_error_info->data_out_result) { + case PQI_DATA_IN_OUT_GOOD: + ciss_cmd_status = CISS_CMD_STATUS_SUCCESS; + break; + case PQI_DATA_IN_OUT_UNDERFLOW: + ciss_cmd_status = CISS_CMD_STATUS_DATA_UNDERRUN; + break; + case PQI_DATA_IN_OUT_BUFFER_OVERFLOW: + ciss_cmd_status = CISS_CMD_STATUS_DATA_OVERRUN; + break; + case PQI_DATA_IN_OUT_PROTOCOL_ERROR: + case PQI_DATA_IN_OUT_BUFFER_ERROR: + case PQI_DATA_IN_OUT_BUFFER_OVERFLOW_DESCRIPTOR_AREA: + case PQI_DATA_IN_OUT_BUFFER_OVERFLOW_BRIDGE: + case PQI_DATA_IN_OUT_ERROR: + ciss_cmd_status = CISS_CMD_STATUS_PROTOCOL_ERROR; + break; + case PQI_DATA_IN_OUT_HARDWARE_ERROR: + case PQI_DATA_IN_OUT_PCIE_FABRIC_ERROR: + case PQI_DATA_IN_OUT_PCIE_COMPLETION_TIMEOUT: + case PQI_DATA_IN_OUT_PCIE_COMPLETER_ABORT_RECEIVED: + case PQI_DATA_IN_OUT_PCIE_UNSUPPORTED_REQUEST_RECEIVED: + case PQI_DATA_IN_OUT_PCIE_ECRC_CHECK_FAILED: + case PQI_DATA_IN_OUT_PCIE_UNSUPPORTED_REQUEST: + case PQI_DATA_IN_OUT_PCIE_ACS_VIOLATION: + case PQI_DATA_IN_OUT_PCIE_TLP_PREFIX_BLOCKED: + case PQI_DATA_IN_OUT_PCIE_POISONED_MEMORY_READ: + ciss_cmd_status = CISS_CMD_STATUS_HARDWARE_ERROR; + break; + case PQI_DATA_IN_OUT_UNSOLICITED_ABORT: + ciss_cmd_status = CISS_CMD_STATUS_UNSOLICITED_ABORT; + break; + case PQI_DATA_IN_OUT_ABORTED: + ciss_cmd_status = CISS_CMD_STATUS_ABORTED; + break; + case PQI_DATA_IN_OUT_TIMEOUT: + ciss_cmd_status = CISS_CMD_STATUS_TIMEOUT; + break; + default: + ciss_cmd_status = CISS_CMD_STATUS_TARGET_STATUS; + break; + } + + sense_data_length = + get_unaligned_le16(&pqi_error_info->sense_data_length); + if (sense_data_length == 0) + sense_data_length = + get_unaligned_le16(&pqi_error_info->response_data_length); + if (sense_data_length) + if (sense_data_length > sizeof(pqi_error_info->data)) + sense_data_length = sizeof(pqi_error_info->data); + + ciss_error_info->scsi_status = pqi_error_info->status; + ciss_error_info->command_status = ciss_cmd_status; + ciss_error_info->sense_data_length = sense_data_length; +} + +static int pqi_passthru_ioctl(struct pqi_ctrl_info *ctrl_info, void __user *arg) +{ + int rc; + char *kernel_buffer = NULL; + u16 iu_length; + size_t sense_data_length; + IOCTL_Command_struct iocommand; + struct pqi_raid_path_request request; + struct pqi_raid_error_info pqi_error_info; + struct ciss_error_info ciss_error_info; + + if (pqi_ctrl_offline(ctrl_info)) + return -ENXIO; + if (!arg) + return -EINVAL; + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + if (copy_from_user(&iocommand, arg, sizeof(iocommand))) + return -EFAULT; + if (iocommand.buf_size < 1 && + iocommand.Request.Type.Direction != XFER_NONE) + return -EINVAL; + if (iocommand.Request.CDBLen > sizeof(request.cdb)) + return -EINVAL; + if (iocommand.Request.Type.Type != TYPE_CMD) + return -EINVAL; + + switch (iocommand.Request.Type.Direction) { + case XFER_NONE: + case XFER_WRITE: + case XFER_READ: + break; + default: + return -EINVAL; + } + + if (iocommand.buf_size > 0) { + kernel_buffer = kmalloc(iocommand.buf_size, GFP_KERNEL); + if (!kernel_buffer) + return -ENOMEM; + if (iocommand.Request.Type.Direction & XFER_WRITE) { + if (copy_from_user(kernel_buffer, iocommand.buf, + iocommand.buf_size)) { + rc = -EFAULT; + goto out; + } + } else { + memset(kernel_buffer, 0, iocommand.buf_size); + } + } + + memset(&request, 0, sizeof(request)); + + request.header.iu_type = PQI_REQUEST_IU_RAID_PATH_IO; + iu_length = offsetof(struct pqi_raid_path_request, sg_descriptors) - + PQI_REQUEST_HEADER_LENGTH; + memcpy(request.lun_number, iocommand.LUN_info.LunAddrBytes, + sizeof(request.lun_number)); + memcpy(request.cdb, iocommand.Request.CDB, iocommand.Request.CDBLen); + request.additional_cdb_bytes_usage = SOP_ADDITIONAL_CDB_BYTES_0; + + switch (iocommand.Request.Type.Direction) { + case XFER_NONE: + request.data_direction = SOP_NO_DIRECTION_FLAG; + break; + case XFER_WRITE: + request.data_direction = SOP_WRITE_FLAG; + break; + case XFER_READ: + request.data_direction = SOP_READ_FLAG; + break; + } + + request.task_attribute = SOP_TASK_ATTRIBUTE_SIMPLE; + + if (iocommand.buf_size > 0) { + put_unaligned_le32(iocommand.buf_size, &request.buffer_length); + + rc = pqi_map_single(ctrl_info->pci_dev, + &request.sg_descriptors[0], kernel_buffer, + iocommand.buf_size, PCI_DMA_BIDIRECTIONAL); + if (rc) + goto out; + + iu_length += sizeof(request.sg_descriptors[0]); + } + + put_unaligned_le16(iu_length, &request.header.iu_length); + + rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, + PQI_SYNC_FLAGS_INTERRUPTABLE, &pqi_error_info, NO_TIMEOUT); + + if (iocommand.buf_size > 0) + pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, + PCI_DMA_BIDIRECTIONAL); + + memset(&iocommand.error_info, 0, sizeof(iocommand.error_info)); + + if (rc == 0) { + pqi_error_info_to_ciss(&pqi_error_info, &ciss_error_info); + iocommand.error_info.ScsiStatus = ciss_error_info.scsi_status; + iocommand.error_info.CommandStatus = + ciss_error_info.command_status; + sense_data_length = ciss_error_info.sense_data_length; + if (sense_data_length) { + if (sense_data_length > + sizeof(iocommand.error_info.SenseInfo)) + sense_data_length = + sizeof(iocommand.error_info.SenseInfo); + memcpy(iocommand.error_info.SenseInfo, + pqi_error_info.data, sense_data_length); + iocommand.error_info.SenseLen = sense_data_length; + } + } + + if (copy_to_user(arg, &iocommand, sizeof(iocommand))) { + rc = -EFAULT; + goto out; + } + + if (rc == 0 && iocommand.buf_size > 0 && + (iocommand.Request.Type.Direction & XFER_READ)) { + if (copy_to_user(iocommand.buf, kernel_buffer, + iocommand.buf_size)) { + rc = -EFAULT; + } + } + +out: + kfree(kernel_buffer); + + return rc; +} + +static int pqi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) +{ + int rc; + struct pqi_ctrl_info *ctrl_info; + + ctrl_info = shost_to_hba(sdev->host); + + switch (cmd) { + case CCISS_DEREGDISK: + case CCISS_REGNEWDISK: + case CCISS_REGNEWD: + rc = pqi_scan_scsi_devices(ctrl_info); + break; + case CCISS_GETPCIINFO: + rc = pqi_getpciinfo_ioctl(ctrl_info, arg); + break; + case CCISS_GETDRIVVER: + rc = pqi_getdrivver_ioctl(arg); + break; + case CCISS_PASSTHRU: + rc = pqi_passthru_ioctl(ctrl_info, arg); + break; + default: + rc = -EINVAL; + break; + } + + return rc; +} + +static ssize_t pqi_version_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + ssize_t count = 0; + struct Scsi_Host *shost; + struct pqi_ctrl_info *ctrl_info; + + shost = class_to_shost(dev); + ctrl_info = shost_to_hba(shost); + + count += snprintf(buffer + count, PAGE_SIZE - count, + " driver: %s\n", DRIVER_VERSION BUILD_TIMESTAMP); + + count += snprintf(buffer + count, PAGE_SIZE - count, + "firmware: %s\n", ctrl_info->firmware_version); + + return count; +} + +static ssize_t pqi_host_rescan_store(struct device *dev, + struct device_attribute *attr, const char *buffer, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(dev); + + pqi_scan_start(shost); + + return count; +} + +static DEVICE_ATTR(version, S_IRUGO, pqi_version_show, NULL); +static DEVICE_ATTR(rescan, S_IWUSR, NULL, pqi_host_rescan_store); + +static struct device_attribute *pqi_shost_attrs[] = { + &dev_attr_version, + &dev_attr_rescan, + NULL +}; + +static ssize_t pqi_sas_address_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + struct pqi_ctrl_info *ctrl_info; + struct scsi_device *sdev; + struct pqi_scsi_dev *device; + unsigned long flags; + u64 sas_address; + + sdev = to_scsi_device(dev); + ctrl_info = shost_to_hba(sdev->host); + + spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); + + device = sdev->hostdata; + if (pqi_is_logical_device(device)) { + spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, + flags); + return -ENODEV; + } + sas_address = device->sas_address; + + spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); + + return snprintf(buffer, PAGE_SIZE, "0x%016llx\n", sas_address); +} + +static ssize_t pqi_ssd_smart_path_enabled_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + struct pqi_ctrl_info *ctrl_info; + struct scsi_device *sdev; + struct pqi_scsi_dev *device; + unsigned long flags; + + sdev = to_scsi_device(dev); + ctrl_info = shost_to_hba(sdev->host); + + spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); + + device = sdev->hostdata; + buffer[0] = device->offload_enabled ? '1' : '0'; + buffer[1] = '\n'; + buffer[2] = '\0'; + + spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); + + return 2; +} + +static DEVICE_ATTR(sas_address, S_IRUGO, pqi_sas_address_show, NULL); +static DEVICE_ATTR(ssd_smart_path_enabled, S_IRUGO, + pqi_ssd_smart_path_enabled_show, NULL); + +static struct device_attribute *pqi_sdev_attrs[] = { + &dev_attr_sas_address, + &dev_attr_ssd_smart_path_enabled, + NULL +}; + +static struct scsi_host_template pqi_driver_template = { + .module = THIS_MODULE, + .name = DRIVER_NAME_SHORT, + .proc_name = DRIVER_NAME_SHORT, + .queuecommand = pqi_scsi_queue_command, + .scan_start = pqi_scan_start, + .scan_finished = pqi_scan_finished, + .this_id = -1, + .use_clustering = ENABLE_CLUSTERING, + .eh_device_reset_handler = pqi_eh_device_reset_handler, + .ioctl = pqi_ioctl, + .slave_alloc = pqi_slave_alloc, + .slave_configure = pqi_slave_configure, + .sdev_attrs = pqi_sdev_attrs, + .shost_attrs = pqi_shost_attrs, +}; + +static int pqi_register_scsi(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + struct Scsi_Host *shost; + + shost = scsi_host_alloc(&pqi_driver_template, sizeof(ctrl_info)); + if (!shost) { + dev_err(&ctrl_info->pci_dev->dev, + "scsi_host_alloc failed for controller %u\n", + ctrl_info->ctrl_id); + return -ENOMEM; + } + + shost->io_port = 0; + shost->n_io_port = 0; + shost->this_id = -1; + shost->max_channel = PQI_MAX_BUS; + shost->max_cmd_len = MAX_COMMAND_SIZE; + shost->max_lun = ~0; + shost->max_id = ~0; + shost->max_sectors = ctrl_info->max_sectors; + shost->can_queue = ctrl_info->scsi_ml_can_queue; + shost->cmd_per_lun = shost->can_queue; + shost->sg_tablesize = ctrl_info->sg_tablesize; + shost->transportt = pqi_sas_transport_template; + shost->irq = ctrl_info->msix_vectors[0]; + shost->unique_id = shost->irq; + shost->nr_hw_queues = ctrl_info->num_queue_groups; + shost->hostdata[0] = (unsigned long)ctrl_info; + + rc = scsi_add_host(shost, &ctrl_info->pci_dev->dev); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "scsi_add_host failed for controller %u\n", + ctrl_info->ctrl_id); + goto free_host; + } + + rc = pqi_add_sas_host(shost, ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "add SAS host failed for controller %u\n", + ctrl_info->ctrl_id); + goto remove_host; + } + + ctrl_info->scsi_host = shost; + + return 0; + +remove_host: + scsi_remove_host(shost); +free_host: + scsi_host_put(shost); + + return rc; +} + +static void pqi_unregister_scsi(struct pqi_ctrl_info *ctrl_info) +{ + struct Scsi_Host *shost; + + pqi_delete_sas_host(ctrl_info); + + shost = ctrl_info->scsi_host; + if (!shost) + return; + + scsi_remove_host(shost); + scsi_host_put(shost); +} + +#define PQI_RESET_ACTION_RESET 0x1 + +#define PQI_RESET_TYPE_NO_RESET 0x0 +#define PQI_RESET_TYPE_SOFT_RESET 0x1 +#define PQI_RESET_TYPE_FIRM_RESET 0x2 +#define PQI_RESET_TYPE_HARD_RESET 0x3 + +static int pqi_reset(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + u32 reset_params; + + reset_params = (PQI_RESET_ACTION_RESET << 5) | + PQI_RESET_TYPE_HARD_RESET; + + writel(reset_params, + &ctrl_info->pqi_registers->device_reset); + + rc = pqi_wait_for_pqi_mode_ready(ctrl_info); + if (rc) + dev_err(&ctrl_info->pci_dev->dev, + "PQI reset failed\n"); + + return rc; +} + +static int pqi_get_ctrl_firmware_version(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + struct bmic_identify_controller *identify; + + identify = kmalloc(sizeof(*identify), GFP_KERNEL); + if (!identify) + return -ENOMEM; + + rc = pqi_identify_controller(ctrl_info, identify); + if (rc) + goto out; + + memcpy(ctrl_info->firmware_version, identify->firmware_version, + sizeof(identify->firmware_version)); + ctrl_info->firmware_version[sizeof(identify->firmware_version)] = '\0'; + snprintf(ctrl_info->firmware_version + + strlen(ctrl_info->firmware_version), + sizeof(ctrl_info->firmware_version), + "-%u", get_unaligned_le16(&identify->firmware_build_number)); + +out: + kfree(identify); + + return rc; +} + +static int pqi_kdump_init(struct pqi_ctrl_info *ctrl_info) +{ + if (!sis_is_firmware_running(ctrl_info)) + return -ENXIO; + + if (pqi_get_ctrl_mode(ctrl_info) == PQI_MODE) { + sis_disable_msix(ctrl_info); + if (pqi_reset(ctrl_info) == 0) + sis_reenable_sis_mode(ctrl_info); + } + + return 0; +} + +static int pqi_ctrl_init(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + + if (reset_devices) { + rc = pqi_kdump_init(ctrl_info); + if (rc) + return rc; + } + + /* + * When the controller comes out of reset, it is always running + * in legacy SIS mode. This is so that it can be compatible + * with legacy drivers shipped with OSes. So we have to talk + * to it using SIS commands at first. Once we are satisified + * that the controller supports PQI, we transition it into PQI + * mode. + */ + + /* + * Wait until the controller is ready to start accepting SIS + * commands. + */ + rc = sis_wait_for_ctrl_ready(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error initializing SIS interface\n"); + return rc; + } + + /* + * Get the controller properties. This allows us to determine + * whether or not it supports PQI mode. + */ + rc = sis_get_ctrl_properties(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error obtaining controller properties\n"); + return rc; + } + + rc = sis_get_pqi_capabilities(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error obtaining controller capabilities\n"); + return rc; + } + + if (ctrl_info->max_outstanding_requests > PQI_MAX_OUTSTANDING_REQUESTS) + ctrl_info->max_outstanding_requests = + PQI_MAX_OUTSTANDING_REQUESTS; + + pqi_calculate_io_resources(ctrl_info); + + rc = pqi_alloc_error_buffer(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "failed to allocate PQI error buffer\n"); + return rc; + } + + /* + * If the function we are about to call succeeds, the + * controller will transition from legacy SIS mode + * into PQI mode. + */ + rc = sis_init_base_struct_addr(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error initializing PQI mode\n"); + return rc; + } + + /* Wait for the controller to complete the SIS -> PQI transition. */ + rc = pqi_wait_for_pqi_mode_ready(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "transition to PQI mode failed\n"); + return rc; + } + + /* From here on, we are running in PQI mode. */ + ctrl_info->pqi_mode_enabled = true; + pqi_save_ctrl_mode(ctrl_info, PQI_MODE); + + rc = pqi_alloc_admin_queues(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error allocating admin queues\n"); + return rc; + } + + rc = pqi_create_admin_queues(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error creating admin queues\n"); + return rc; + } + + rc = pqi_report_device_capability(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "obtaining device capability failed\n"); + return rc; + } + + rc = pqi_validate_device_capability(ctrl_info); + if (rc) + return rc; + + pqi_calculate_queue_resources(ctrl_info); + + rc = pqi_enable_msix_interrupts(ctrl_info); + if (rc) + return rc; + + if (ctrl_info->num_msix_vectors_enabled < ctrl_info->num_queue_groups) { + ctrl_info->max_msix_vectors = + ctrl_info->num_msix_vectors_enabled; + pqi_calculate_queue_resources(ctrl_info); + } + + rc = pqi_alloc_io_resources(ctrl_info); + if (rc) + return rc; + + rc = pqi_alloc_operational_queues(ctrl_info); + if (rc) + return rc; + + pqi_init_operational_queues(ctrl_info); + + rc = pqi_request_irqs(ctrl_info); + if (rc) + return rc; + + pqi_irq_set_affinity_hint(ctrl_info); + + rc = pqi_create_queues(ctrl_info); + if (rc) + return rc; + + sis_enable_msix(ctrl_info); + + rc = pqi_configure_events(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error configuring events\n"); + return rc; + } + + pqi_start_heartbeat_timer(ctrl_info); + + ctrl_info->controller_online = true; + + /* Register with the SCSI subsystem. */ + rc = pqi_register_scsi(ctrl_info); + if (rc) + return rc; + + rc = pqi_get_ctrl_firmware_version(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error obtaining firmware version\n"); + return rc; + } + + rc = pqi_write_driver_version_to_host_wellness(ctrl_info); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "error updating host wellness\n"); + return rc; + } + + pqi_schedule_update_time_worker(ctrl_info); + + pqi_scan_scsi_devices(ctrl_info); + + return 0; +} + +static int pqi_pci_init(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + u64 mask; + + rc = pci_enable_device(ctrl_info->pci_dev); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "failed to enable PCI device\n"); + return rc; + } + + if (sizeof(dma_addr_t) > 4) + mask = DMA_BIT_MASK(64); + else + mask = DMA_BIT_MASK(32); + + rc = dma_set_mask(&ctrl_info->pci_dev->dev, mask); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, "failed to set DMA mask\n"); + goto disable_device; + } + + rc = pci_request_regions(ctrl_info->pci_dev, DRIVER_NAME_SHORT); + if (rc) { + dev_err(&ctrl_info->pci_dev->dev, + "failed to obtain PCI resources\n"); + goto disable_device; + } + + ctrl_info->iomem_base = ioremap_nocache(pci_resource_start( + ctrl_info->pci_dev, 0), + sizeof(struct pqi_ctrl_registers)); + if (!ctrl_info->iomem_base) { + dev_err(&ctrl_info->pci_dev->dev, + "failed to map memory for controller registers\n"); + rc = -ENOMEM; + goto release_regions; + } + + ctrl_info->registers = ctrl_info->iomem_base; + ctrl_info->pqi_registers = &ctrl_info->registers->pqi_registers; + + /* Enable bus mastering. */ + pci_set_master(ctrl_info->pci_dev); + + pci_set_drvdata(ctrl_info->pci_dev, ctrl_info); + + return 0; + +release_regions: + pci_release_regions(ctrl_info->pci_dev); +disable_device: + pci_disable_device(ctrl_info->pci_dev); + + return rc; +} + +static void pqi_cleanup_pci_init(struct pqi_ctrl_info *ctrl_info) +{ + iounmap(ctrl_info->iomem_base); + pci_release_regions(ctrl_info->pci_dev); + pci_disable_device(ctrl_info->pci_dev); + pci_set_drvdata(ctrl_info->pci_dev, NULL); +} + +static struct pqi_ctrl_info *pqi_alloc_ctrl_info(int numa_node) +{ + struct pqi_ctrl_info *ctrl_info; + + ctrl_info = kzalloc_node(sizeof(struct pqi_ctrl_info), + GFP_KERNEL, numa_node); + if (!ctrl_info) + return NULL; + + mutex_init(&ctrl_info->scan_mutex); + + INIT_LIST_HEAD(&ctrl_info->scsi_device_list); + spin_lock_init(&ctrl_info->scsi_device_list_lock); + + INIT_WORK(&ctrl_info->event_work, pqi_event_worker); + atomic_set(&ctrl_info->num_interrupts, 0); + + INIT_DELAYED_WORK(&ctrl_info->rescan_work, pqi_rescan_worker); + INIT_DELAYED_WORK(&ctrl_info->update_time_work, pqi_update_time_worker); + + sema_init(&ctrl_info->sync_request_sem, + PQI_RESERVED_IO_SLOTS_SYNCHRONOUS_REQUESTS); + sema_init(&ctrl_info->lun_reset_sem, PQI_RESERVED_IO_SLOTS_LUN_RESET); + + ctrl_info->ctrl_id = atomic_inc_return(&pqi_controller_count) - 1; + ctrl_info->max_msix_vectors = PQI_MAX_MSIX_VECTORS; + + return ctrl_info; +} + +static inline void pqi_free_ctrl_info(struct pqi_ctrl_info *ctrl_info) +{ + kfree(ctrl_info); +} + +static void pqi_free_interrupts(struct pqi_ctrl_info *ctrl_info) +{ + pqi_irq_unset_affinity_hint(ctrl_info); + pqi_free_irqs(ctrl_info); + if (ctrl_info->num_msix_vectors_enabled) + pci_disable_msix(ctrl_info->pci_dev); +} + +static void pqi_free_ctrl_resources(struct pqi_ctrl_info *ctrl_info) +{ + pqi_stop_heartbeat_timer(ctrl_info); + pqi_free_interrupts(ctrl_info); + if (ctrl_info->queue_memory_base) + dma_free_coherent(&ctrl_info->pci_dev->dev, + ctrl_info->queue_memory_length, + ctrl_info->queue_memory_base, + ctrl_info->queue_memory_base_dma_handle); + if (ctrl_info->admin_queue_memory_base) + dma_free_coherent(&ctrl_info->pci_dev->dev, + ctrl_info->admin_queue_memory_length, + ctrl_info->admin_queue_memory_base, + ctrl_info->admin_queue_memory_base_dma_handle); + pqi_free_all_io_requests(ctrl_info); + if (ctrl_info->error_buffer) + dma_free_coherent(&ctrl_info->pci_dev->dev, + ctrl_info->error_buffer_length, + ctrl_info->error_buffer, + ctrl_info->error_buffer_dma_handle); + if (ctrl_info->iomem_base) + pqi_cleanup_pci_init(ctrl_info); + pqi_free_ctrl_info(ctrl_info); +} + +static void pqi_remove_ctrl(struct pqi_ctrl_info *ctrl_info) +{ + cancel_delayed_work_sync(&ctrl_info->rescan_work); + cancel_delayed_work_sync(&ctrl_info->update_time_work); + pqi_remove_all_scsi_devices(ctrl_info); + pqi_unregister_scsi(ctrl_info); + + if (ctrl_info->pqi_mode_enabled) { + sis_disable_msix(ctrl_info); + if (pqi_reset(ctrl_info) == 0) + sis_reenable_sis_mode(ctrl_info); + } + pqi_free_ctrl_resources(ctrl_info); +} + +static void pqi_print_ctrl_info(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + char *ctrl_description; + + if (id->driver_data) { + ctrl_description = (char *)id->driver_data; + } else { + switch (id->subvendor) { + case PCI_VENDOR_ID_HP: + ctrl_description = hpe_branded_controller; + break; + case PCI_VENDOR_ID_ADAPTEC2: + default: + ctrl_description = microsemi_branded_controller; + break; + } + } + + dev_info(&pdev->dev, "%s found\n", ctrl_description); +} + +static int pqi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int rc; + int node; + struct pqi_ctrl_info *ctrl_info; + + pqi_print_ctrl_info(pdev, id); + + if (pqi_disable_device_id_wildcards && + id->subvendor == PCI_ANY_ID && + id->subdevice == PCI_ANY_ID) { + dev_warn(&pdev->dev, + "controller not probed because device ID wildcards are disabled\n"); + return -ENODEV; + } + + if (id->subvendor == PCI_ANY_ID || id->subdevice == PCI_ANY_ID) + dev_warn(&pdev->dev, + "controller device ID matched using wildcards\n"); + + node = dev_to_node(&pdev->dev); + if (node == NUMA_NO_NODE) + set_dev_node(&pdev->dev, 0); + + ctrl_info = pqi_alloc_ctrl_info(node); + if (!ctrl_info) { + dev_err(&pdev->dev, + "failed to allocate controller info block\n"); + return -ENOMEM; + } + + ctrl_info->pci_dev = pdev; + + rc = pqi_pci_init(ctrl_info); + if (rc) + goto error; + + rc = pqi_ctrl_init(ctrl_info); + if (rc) + goto error; + + return 0; + +error: + pqi_remove_ctrl(ctrl_info); + + return rc; +} + +static void pqi_pci_remove(struct pci_dev *pdev) +{ + struct pqi_ctrl_info *ctrl_info; + + ctrl_info = pci_get_drvdata(pdev); + if (!ctrl_info) + return; + + pqi_remove_ctrl(ctrl_info); +} + +static void pqi_shutdown(struct pci_dev *pdev) +{ + int rc; + struct pqi_ctrl_info *ctrl_info; + + ctrl_info = pci_get_drvdata(pdev); + if (!ctrl_info) + goto error; + + /* + * Write all data in the controller's battery-backed cache to + * storage. + */ + rc = pqi_flush_cache(ctrl_info); + if (rc == 0) + return; + +error: + dev_warn(&pdev->dev, + "unable to flush controller cache\n"); +} + +/* Define the PCI IDs for the controllers that we support. */ +static const struct pci_device_id pqi_pci_id_table[] = { + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0110) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x0600) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x0601) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x0602) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x0603) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x0650) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x0651) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x0652) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x0653) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x0654) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x0655) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x0700) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x0701) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0800) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0801) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0802) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0803) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0804) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0805) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0900) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0901) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0902) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0903) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0904) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0905) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0906) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x1001) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x1100) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x1101) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x1102) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_HP, 0x1150) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_ANY_ID, PCI_ANY_ID) + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(pci, pqi_pci_id_table); + +static struct pci_driver pqi_pci_driver = { + .name = DRIVER_NAME_SHORT, + .id_table = pqi_pci_id_table, + .probe = pqi_pci_probe, + .remove = pqi_pci_remove, + .shutdown = pqi_shutdown, +}; + +static int __init pqi_init(void) +{ + int rc; + + pr_info(DRIVER_NAME "\n"); + + pqi_sas_transport_template = + sas_attach_transport(&pqi_sas_transport_functions); + if (!pqi_sas_transport_template) + return -ENODEV; + + rc = pci_register_driver(&pqi_pci_driver); + if (rc) + sas_release_transport(pqi_sas_transport_template); + + return rc; +} + +static void __exit pqi_cleanup(void) +{ + pci_unregister_driver(&pqi_pci_driver); + sas_release_transport(pqi_sas_transport_template); +} + +module_init(pqi_init); +module_exit(pqi_cleanup); + +static void __attribute__((unused)) verify_structures(void) +{ + BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers, + sis_host_to_ctrl_doorbell) != 0x20); + BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers, + sis_interrupt_mask) != 0x34); + BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers, + sis_ctrl_to_host_doorbell) != 0x9c); + BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers, + sis_ctrl_to_host_doorbell_clear) != 0xa0); + BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers, + sis_driver_scratch) != 0xb0); + BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers, + sis_firmware_status) != 0xbc); + BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers, + sis_mailbox) != 0x1000); + BUILD_BUG_ON(offsetof(struct pqi_ctrl_registers, + pqi_registers) != 0x4000); + + BUILD_BUG_ON(offsetof(struct pqi_iu_header, + iu_type) != 0x0); + BUILD_BUG_ON(offsetof(struct pqi_iu_header, + iu_length) != 0x2); + BUILD_BUG_ON(offsetof(struct pqi_iu_header, + response_queue_id) != 0x4); + BUILD_BUG_ON(offsetof(struct pqi_iu_header, + work_area) != 0x6); + BUILD_BUG_ON(sizeof(struct pqi_iu_header) != 0x8); + + BUILD_BUG_ON(offsetof(struct pqi_aio_error_info, + status) != 0x0); + BUILD_BUG_ON(offsetof(struct pqi_aio_error_info, + service_response) != 0x1); + BUILD_BUG_ON(offsetof(struct pqi_aio_error_info, + data_present) != 0x2); + BUILD_BUG_ON(offsetof(struct pqi_aio_error_info, + reserved) != 0x3); + BUILD_BUG_ON(offsetof(struct pqi_aio_error_info, + residual_count) != 0x4); + BUILD_BUG_ON(offsetof(struct pqi_aio_error_info, + data_length) != 0x8); + BUILD_BUG_ON(offsetof(struct pqi_aio_error_info, + reserved1) != 0xa); + BUILD_BUG_ON(offsetof(struct pqi_aio_error_info, + data) != 0xc); + BUILD_BUG_ON(sizeof(struct pqi_aio_error_info) != 0x10c); + + BUILD_BUG_ON(offsetof(struct pqi_raid_error_info, + data_in_result) != 0x0); + BUILD_BUG_ON(offsetof(struct pqi_raid_error_info, + data_out_result) != 0x1); + BUILD_BUG_ON(offsetof(struct pqi_raid_error_info, + reserved) != 0x2); + BUILD_BUG_ON(offsetof(struct pqi_raid_error_info, + status) != 0x5); + BUILD_BUG_ON(offsetof(struct pqi_raid_error_info, + status_qualifier) != 0x6); + BUILD_BUG_ON(offsetof(struct pqi_raid_error_info, + sense_data_length) != 0x8); + BUILD_BUG_ON(offsetof(struct pqi_raid_error_info, + response_data_length) != 0xa); + BUILD_BUG_ON(offsetof(struct pqi_raid_error_info, + data_in_transferred) != 0xc); + BUILD_BUG_ON(offsetof(struct pqi_raid_error_info, + data_out_transferred) != 0x10); + BUILD_BUG_ON(offsetof(struct pqi_raid_error_info, + data) != 0x14); + BUILD_BUG_ON(sizeof(struct pqi_raid_error_info) != 0x114); + + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + signature) != 0x0); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + function_and_status_code) != 0x8); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + max_admin_iq_elements) != 0x10); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + max_admin_oq_elements) != 0x11); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + admin_iq_element_length) != 0x12); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + admin_oq_element_length) != 0x13); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + max_reset_timeout) != 0x14); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + legacy_intx_status) != 0x18); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + legacy_intx_mask_set) != 0x1c); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + legacy_intx_mask_clear) != 0x20); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + device_status) != 0x40); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + admin_iq_pi_offset) != 0x48); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + admin_oq_ci_offset) != 0x50); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + admin_iq_element_array_addr) != 0x58); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + admin_oq_element_array_addr) != 0x60); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + admin_iq_ci_addr) != 0x68); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + admin_oq_pi_addr) != 0x70); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + admin_iq_num_elements) != 0x78); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + admin_oq_num_elements) != 0x79); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + admin_queue_int_msg_num) != 0x7a); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + device_error) != 0x80); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + error_details) != 0x88); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + device_reset) != 0x90); + BUILD_BUG_ON(offsetof(struct pqi_device_registers, + power_action) != 0x94); + BUILD_BUG_ON(sizeof(struct pqi_device_registers) != 0x100); + + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + header.iu_type) != 0); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + header.iu_length) != 2); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + header.work_area) != 6); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + request_id) != 8); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + function_code) != 10); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.report_device_capability.buffer_length) != 44); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.report_device_capability.sg_descriptor) != 48); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_iq.queue_id) != 12); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_iq.element_array_addr) != 16); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_iq.ci_addr) != 24); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_iq.num_elements) != 32); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_iq.element_length) != 34); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_iq.queue_protocol) != 36); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_oq.queue_id) != 12); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_oq.element_array_addr) != 16); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_oq.pi_addr) != 24); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_oq.num_elements) != 32); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_oq.element_length) != 34); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_oq.queue_protocol) != 36); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_oq.int_msg_num) != 40); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_oq.coalescing_count) != 42); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_oq.min_coalescing_time) != 44); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.create_operational_oq.max_coalescing_time) != 48); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, + data.delete_operational_queue.queue_id) != 12); + BUILD_BUG_ON(sizeof(struct pqi_general_admin_request) != 64); + BUILD_BUG_ON(FIELD_SIZEOF(struct pqi_general_admin_request, + data.create_operational_iq) != 64 - 11); + BUILD_BUG_ON(FIELD_SIZEOF(struct pqi_general_admin_request, + data.create_operational_oq) != 64 - 11); + BUILD_BUG_ON(FIELD_SIZEOF(struct pqi_general_admin_request, + data.delete_operational_queue) != 64 - 11); + + BUILD_BUG_ON(offsetof(struct pqi_general_admin_response, + header.iu_type) != 0); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_response, + header.iu_length) != 2); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_response, + header.work_area) != 6); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_response, + request_id) != 8); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_response, + function_code) != 10); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_response, + status) != 11); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_response, + data.create_operational_iq.status_descriptor) != 12); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_response, + data.create_operational_iq.iq_pi_offset) != 16); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_response, + data.create_operational_oq.status_descriptor) != 12); + BUILD_BUG_ON(offsetof(struct pqi_general_admin_response, + data.create_operational_oq.oq_ci_offset) != 16); + BUILD_BUG_ON(sizeof(struct pqi_general_admin_response) != 64); + + BUILD_BUG_ON(offsetof(struct pqi_raid_path_request, + header.iu_type) != 0); + BUILD_BUG_ON(offsetof(struct pqi_raid_path_request, + header.iu_length) != 2); + BUILD_BUG_ON(offsetof(struct pqi_raid_path_request, + header.response_queue_id) != 4); + BUILD_BUG_ON(offsetof(struct pqi_raid_path_request, + header.work_area) != 6); + BUILD_BUG_ON(offsetof(struct pqi_raid_path_request, + request_id) != 8); + BUILD_BUG_ON(offsetof(struct pqi_raid_path_request, + nexus_id) != 10); + BUILD_BUG_ON(offsetof(struct pqi_raid_path_request, + buffer_length) != 12); + BUILD_BUG_ON(offsetof(struct pqi_raid_path_request, + lun_number) != 16); + BUILD_BUG_ON(offsetof(struct pqi_raid_path_request, + protocol_specific) != 24); + BUILD_BUG_ON(offsetof(struct pqi_raid_path_request, + error_index) != 27); + BUILD_BUG_ON(offsetof(struct pqi_raid_path_request, + cdb) != 32); + BUILD_BUG_ON(offsetof(struct pqi_raid_path_request, + sg_descriptors) != 64); + BUILD_BUG_ON(sizeof(struct pqi_raid_path_request) != + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH); + + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + header.iu_type) != 0); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + header.iu_length) != 2); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + header.response_queue_id) != 4); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + header.work_area) != 6); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + request_id) != 8); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + nexus_id) != 12); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + buffer_length) != 16); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + data_encryption_key_index) != 22); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + encrypt_tweak_lower) != 24); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + encrypt_tweak_upper) != 28); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + cdb) != 32); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + error_index) != 48); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + num_sg_descriptors) != 50); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + cdb_length) != 51); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + lun_number) != 52); + BUILD_BUG_ON(offsetof(struct pqi_aio_path_request, + sg_descriptors) != 64); + BUILD_BUG_ON(sizeof(struct pqi_aio_path_request) != + PQI_OPERATIONAL_IQ_ELEMENT_LENGTH); + + BUILD_BUG_ON(offsetof(struct pqi_io_response, + header.iu_type) != 0); + BUILD_BUG_ON(offsetof(struct pqi_io_response, + header.iu_length) != 2); + BUILD_BUG_ON(offsetof(struct pqi_io_response, + request_id) != 8); + BUILD_BUG_ON(offsetof(struct pqi_io_response, + error_index) != 10); + + BUILD_BUG_ON(offsetof(struct pqi_general_management_request, + header.iu_type) != 0); + BUILD_BUG_ON(offsetof(struct pqi_general_management_request, + header.iu_length) != 2); + BUILD_BUG_ON(offsetof(struct pqi_general_management_request, + header.response_queue_id) != 4); + BUILD_BUG_ON(offsetof(struct pqi_general_management_request, + request_id) != 8); + BUILD_BUG_ON(offsetof(struct pqi_general_management_request, + data.report_event_configuration.buffer_length) != 12); + BUILD_BUG_ON(offsetof(struct pqi_general_management_request, + data.report_event_configuration.sg_descriptors) != 16); + BUILD_BUG_ON(offsetof(struct pqi_general_management_request, + data.set_event_configuration.global_event_oq_id) != 10); + BUILD_BUG_ON(offsetof(struct pqi_general_management_request, + data.set_event_configuration.buffer_length) != 12); + BUILD_BUG_ON(offsetof(struct pqi_general_management_request, + data.set_event_configuration.sg_descriptors) != 16); + + BUILD_BUG_ON(offsetof(struct pqi_iu_layer_descriptor, + max_inbound_iu_length) != 6); + BUILD_BUG_ON(offsetof(struct pqi_iu_layer_descriptor, + max_outbound_iu_length) != 14); + BUILD_BUG_ON(sizeof(struct pqi_iu_layer_descriptor) != 16); + + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + data_length) != 0); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + iq_arbitration_priority_support_bitmask) != 8); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + maximum_aw_a) != 9); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + maximum_aw_b) != 10); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + maximum_aw_c) != 11); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + max_inbound_queues) != 16); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + max_elements_per_iq) != 18); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + max_iq_element_length) != 24); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + min_iq_element_length) != 26); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + max_outbound_queues) != 30); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + max_elements_per_oq) != 32); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + intr_coalescing_time_granularity) != 34); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + max_oq_element_length) != 36); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + min_oq_element_length) != 38); + BUILD_BUG_ON(offsetof(struct pqi_device_capability, + iu_layer_descriptors) != 64); + BUILD_BUG_ON(sizeof(struct pqi_device_capability) != 576); + + BUILD_BUG_ON(offsetof(struct pqi_event_descriptor, + event_type) != 0); + BUILD_BUG_ON(offsetof(struct pqi_event_descriptor, + oq_id) != 2); + BUILD_BUG_ON(sizeof(struct pqi_event_descriptor) != 4); + + BUILD_BUG_ON(offsetof(struct pqi_event_config, + num_event_descriptors) != 2); + BUILD_BUG_ON(offsetof(struct pqi_event_config, + descriptors) != 4); + + BUILD_BUG_ON(offsetof(struct pqi_event_response, + header.iu_type) != 0); + BUILD_BUG_ON(offsetof(struct pqi_event_response, + header.iu_length) != 2); + BUILD_BUG_ON(offsetof(struct pqi_event_response, + event_type) != 8); + BUILD_BUG_ON(offsetof(struct pqi_event_response, + event_id) != 10); + BUILD_BUG_ON(offsetof(struct pqi_event_response, + additional_event_id) != 12); + BUILD_BUG_ON(offsetof(struct pqi_event_response, + data) != 16); + BUILD_BUG_ON(sizeof(struct pqi_event_response) != 32); + + BUILD_BUG_ON(offsetof(struct pqi_event_acknowledge_request, + header.iu_type) != 0); + BUILD_BUG_ON(offsetof(struct pqi_event_acknowledge_request, + header.iu_length) != 2); + BUILD_BUG_ON(offsetof(struct pqi_event_acknowledge_request, + event_type) != 8); + BUILD_BUG_ON(offsetof(struct pqi_event_acknowledge_request, + event_id) != 10); + BUILD_BUG_ON(offsetof(struct pqi_event_acknowledge_request, + additional_event_id) != 12); + BUILD_BUG_ON(sizeof(struct pqi_event_acknowledge_request) != 16); + + BUILD_BUG_ON(offsetof(struct pqi_task_management_request, + header.iu_type) != 0); + BUILD_BUG_ON(offsetof(struct pqi_task_management_request, + header.iu_length) != 2); + BUILD_BUG_ON(offsetof(struct pqi_task_management_request, + request_id) != 8); + BUILD_BUG_ON(offsetof(struct pqi_task_management_request, + nexus_id) != 10); + BUILD_BUG_ON(offsetof(struct pqi_task_management_request, + lun_number) != 16); + BUILD_BUG_ON(offsetof(struct pqi_task_management_request, + protocol_specific) != 24); + BUILD_BUG_ON(offsetof(struct pqi_task_management_request, + outbound_queue_id_to_manage) != 26); + BUILD_BUG_ON(offsetof(struct pqi_task_management_request, + request_id_to_manage) != 28); + BUILD_BUG_ON(offsetof(struct pqi_task_management_request, + task_management_function) != 30); + BUILD_BUG_ON(sizeof(struct pqi_task_management_request) != 32); + + BUILD_BUG_ON(offsetof(struct pqi_task_management_response, + header.iu_type) != 0); + BUILD_BUG_ON(offsetof(struct pqi_task_management_response, + header.iu_length) != 2); + BUILD_BUG_ON(offsetof(struct pqi_task_management_response, + request_id) != 8); + BUILD_BUG_ON(offsetof(struct pqi_task_management_response, + nexus_id) != 10); + BUILD_BUG_ON(offsetof(struct pqi_task_management_response, + additional_response_info) != 12); + BUILD_BUG_ON(offsetof(struct pqi_task_management_response, + response_code) != 15); + BUILD_BUG_ON(sizeof(struct pqi_task_management_response) != 16); + + BUILD_BUG_ON(offsetof(struct bmic_identify_controller, + configured_logical_drive_count) != 0); + BUILD_BUG_ON(offsetof(struct bmic_identify_controller, + configuration_signature) != 1); + BUILD_BUG_ON(offsetof(struct bmic_identify_controller, + firmware_version) != 5); + BUILD_BUG_ON(offsetof(struct bmic_identify_controller, + extended_logical_unit_count) != 154); + BUILD_BUG_ON(offsetof(struct bmic_identify_controller, + firmware_build_number) != 190); + BUILD_BUG_ON(offsetof(struct bmic_identify_controller, + controller_mode) != 292); + + BUILD_BUG_ON(PQI_ADMIN_IQ_NUM_ELEMENTS > 255); + BUILD_BUG_ON(PQI_ADMIN_OQ_NUM_ELEMENTS > 255); + BUILD_BUG_ON(PQI_ADMIN_IQ_ELEMENT_LENGTH % + PQI_QUEUE_ELEMENT_LENGTH_ALIGNMENT != 0); + BUILD_BUG_ON(PQI_ADMIN_OQ_ELEMENT_LENGTH % + PQI_QUEUE_ELEMENT_LENGTH_ALIGNMENT != 0); + BUILD_BUG_ON(PQI_OPERATIONAL_IQ_ELEMENT_LENGTH > 1048560); + BUILD_BUG_ON(PQI_OPERATIONAL_IQ_ELEMENT_LENGTH % + PQI_QUEUE_ELEMENT_LENGTH_ALIGNMENT != 0); + BUILD_BUG_ON(PQI_OPERATIONAL_OQ_ELEMENT_LENGTH > 1048560); + BUILD_BUG_ON(PQI_OPERATIONAL_OQ_ELEMENT_LENGTH % + PQI_QUEUE_ELEMENT_LENGTH_ALIGNMENT != 0); + + BUILD_BUG_ON(PQI_RESERVED_IO_SLOTS >= PQI_MAX_OUTSTANDING_REQUESTS); +} diff --git a/drivers/scsi/smartpqi/smartpqi_sas_transport.c b/drivers/scsi/smartpqi/smartpqi_sas_transport.c new file mode 100644 index 000000000000..52ca4f93f1b2 --- /dev/null +++ b/drivers/scsi/smartpqi/smartpqi_sas_transport.c @@ -0,0 +1,350 @@ +/* + * driver for Microsemi PQI-based storage controllers + * Copyright (c) 2016 Microsemi Corporation + * Copyright (c) 2016 PMC-Sierra, Inc. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more details. + * + * Questions/Comments/Bugfixes to esc.storagedev@microsemi.com + * + */ + +#include <linux/kernel.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_transport_sas.h> +#include "smartpqi.h" + +static struct pqi_sas_phy *pqi_alloc_sas_phy(struct pqi_sas_port *pqi_sas_port) +{ + struct pqi_sas_phy *pqi_sas_phy; + struct sas_phy *phy; + + pqi_sas_phy = kzalloc(sizeof(*pqi_sas_phy), GFP_KERNEL); + if (!pqi_sas_phy) + return NULL; + + phy = sas_phy_alloc(pqi_sas_port->parent_node->parent_dev, + pqi_sas_port->next_phy_index); + if (!phy) { + kfree(pqi_sas_phy); + return NULL; + } + + pqi_sas_port->next_phy_index++; + pqi_sas_phy->phy = phy; + pqi_sas_phy->parent_port = pqi_sas_port; + + return pqi_sas_phy; +} + +static void pqi_free_sas_phy(struct pqi_sas_phy *pqi_sas_phy) +{ + struct sas_phy *phy = pqi_sas_phy->phy; + + sas_port_delete_phy(pqi_sas_phy->parent_port->port, phy); + sas_phy_free(phy); + if (pqi_sas_phy->added_to_port) + list_del(&pqi_sas_phy->phy_list_entry); + kfree(pqi_sas_phy); +} + +static int pqi_sas_port_add_phy(struct pqi_sas_phy *pqi_sas_phy) +{ + int rc; + struct pqi_sas_port *pqi_sas_port; + struct sas_phy *phy; + struct sas_identify *identify; + + pqi_sas_port = pqi_sas_phy->parent_port; + phy = pqi_sas_phy->phy; + + identify = &phy->identify; + memset(identify, 0, sizeof(*identify)); + identify->sas_address = pqi_sas_port->sas_address; + identify->device_type = SAS_END_DEVICE; + identify->initiator_port_protocols = SAS_PROTOCOL_STP; + identify->target_port_protocols = SAS_PROTOCOL_STP; + phy->minimum_linkrate_hw = SAS_LINK_RATE_UNKNOWN; + phy->maximum_linkrate_hw = SAS_LINK_RATE_UNKNOWN; + phy->minimum_linkrate = SAS_LINK_RATE_UNKNOWN; + phy->maximum_linkrate = SAS_LINK_RATE_UNKNOWN; + phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN; + + rc = sas_phy_add(pqi_sas_phy->phy); + if (rc) + return rc; + + sas_port_add_phy(pqi_sas_port->port, pqi_sas_phy->phy); + list_add_tail(&pqi_sas_phy->phy_list_entry, + &pqi_sas_port->phy_list_head); + pqi_sas_phy->added_to_port = true; + + return 0; +} + +static int pqi_sas_port_add_rphy(struct pqi_sas_port *pqi_sas_port, + struct sas_rphy *rphy) +{ + struct sas_identify *identify; + + identify = &rphy->identify; + identify->sas_address = pqi_sas_port->sas_address; + identify->initiator_port_protocols = SAS_PROTOCOL_STP; + identify->target_port_protocols = SAS_PROTOCOL_STP; + + return sas_rphy_add(rphy); +} + +static struct pqi_sas_port *pqi_alloc_sas_port( + struct pqi_sas_node *pqi_sas_node, u64 sas_address) +{ + int rc; + struct pqi_sas_port *pqi_sas_port; + struct sas_port *port; + + pqi_sas_port = kzalloc(sizeof(*pqi_sas_port), GFP_KERNEL); + if (!pqi_sas_port) + return NULL; + + INIT_LIST_HEAD(&pqi_sas_port->phy_list_head); + pqi_sas_port->parent_node = pqi_sas_node; + + port = sas_port_alloc_num(pqi_sas_node->parent_dev); + if (!port) + goto free_pqi_port; + + rc = sas_port_add(port); + if (rc) + goto free_sas_port; + + pqi_sas_port->port = port; + pqi_sas_port->sas_address = sas_address; + list_add_tail(&pqi_sas_port->port_list_entry, + &pqi_sas_node->port_list_head); + + return pqi_sas_port; + +free_sas_port: + sas_port_free(port); +free_pqi_port: + kfree(pqi_sas_port); + + return NULL; +} + +static void pqi_free_sas_port(struct pqi_sas_port *pqi_sas_port) +{ + struct pqi_sas_phy *pqi_sas_phy; + struct pqi_sas_phy *next; + + list_for_each_entry_safe(pqi_sas_phy, next, + &pqi_sas_port->phy_list_head, phy_list_entry) + pqi_free_sas_phy(pqi_sas_phy); + + sas_port_delete(pqi_sas_port->port); + list_del(&pqi_sas_port->port_list_entry); + kfree(pqi_sas_port); +} + +static struct pqi_sas_node *pqi_alloc_sas_node(struct device *parent_dev) +{ + struct pqi_sas_node *pqi_sas_node; + + pqi_sas_node = kzalloc(sizeof(*pqi_sas_node), GFP_KERNEL); + if (pqi_sas_node) { + pqi_sas_node->parent_dev = parent_dev; + INIT_LIST_HEAD(&pqi_sas_node->port_list_head); + } + + return pqi_sas_node; +} + +static void pqi_free_sas_node(struct pqi_sas_node *pqi_sas_node) +{ + struct pqi_sas_port *pqi_sas_port; + struct pqi_sas_port *next; + + if (!pqi_sas_node) + return; + + list_for_each_entry_safe(pqi_sas_port, next, + &pqi_sas_node->port_list_head, port_list_entry) + pqi_free_sas_port(pqi_sas_port); + + kfree(pqi_sas_node); +} + +struct pqi_scsi_dev *pqi_find_device_by_sas_rphy( + struct pqi_ctrl_info *ctrl_info, struct sas_rphy *rphy) +{ + struct pqi_scsi_dev *device; + + list_for_each_entry(device, &ctrl_info->scsi_device_list, + scsi_device_list_entry) { + if (!device->sas_port) + continue; + if (device->sas_port->rphy == rphy) + return device; + } + + return NULL; +} + +int pqi_add_sas_host(struct Scsi_Host *shost, struct pqi_ctrl_info *ctrl_info) +{ + int rc; + struct device *parent_dev; + struct pqi_sas_node *pqi_sas_node; + struct pqi_sas_port *pqi_sas_port; + struct pqi_sas_phy *pqi_sas_phy; + + parent_dev = &shost->shost_gendev; + + pqi_sas_node = pqi_alloc_sas_node(parent_dev); + if (!pqi_sas_node) + return -ENOMEM; + + pqi_sas_port = pqi_alloc_sas_port(pqi_sas_node, ctrl_info->sas_address); + if (!pqi_sas_port) { + rc = -ENODEV; + goto free_sas_node; + } + + pqi_sas_phy = pqi_alloc_sas_phy(pqi_sas_port); + if (!pqi_sas_phy) { + rc = -ENODEV; + goto free_sas_port; + } + + rc = pqi_sas_port_add_phy(pqi_sas_phy); + if (rc) + goto free_sas_phy; + + ctrl_info->sas_host = pqi_sas_node; + + return 0; + +free_sas_phy: + pqi_free_sas_phy(pqi_sas_phy); +free_sas_port: + pqi_free_sas_port(pqi_sas_port); +free_sas_node: + pqi_free_sas_node(pqi_sas_node); + + return rc; +} + +void pqi_delete_sas_host(struct pqi_ctrl_info *ctrl_info) +{ + pqi_free_sas_node(ctrl_info->sas_host); +} + +int pqi_add_sas_device(struct pqi_sas_node *pqi_sas_node, + struct pqi_scsi_dev *device) +{ + int rc; + struct pqi_sas_port *pqi_sas_port; + struct sas_rphy *rphy; + + pqi_sas_port = pqi_alloc_sas_port(pqi_sas_node, device->sas_address); + if (!pqi_sas_port) + return -ENOMEM; + + rphy = sas_end_device_alloc(pqi_sas_port->port); + if (!rphy) { + rc = -ENODEV; + goto free_sas_port; + } + + pqi_sas_port->rphy = rphy; + device->sas_port = pqi_sas_port; + + rc = pqi_sas_port_add_rphy(pqi_sas_port, rphy); + if (rc) + goto free_sas_port; + + return 0; + +free_sas_port: + pqi_free_sas_port(pqi_sas_port); + device->sas_port = NULL; + + return rc; +} + +void pqi_remove_sas_device(struct pqi_scsi_dev *device) +{ + if (device->sas_port) { + pqi_free_sas_port(device->sas_port); + device->sas_port = NULL; + } +} + +static int pqi_sas_get_linkerrors(struct sas_phy *phy) +{ + return 0; +} + +static int pqi_sas_get_enclosure_identifier(struct sas_rphy *rphy, + u64 *identifier) +{ + return 0; +} + +static int pqi_sas_get_bay_identifier(struct sas_rphy *rphy) +{ + return -ENXIO; +} + +static int pqi_sas_phy_reset(struct sas_phy *phy, int hard_reset) +{ + return 0; +} + +static int pqi_sas_phy_enable(struct sas_phy *phy, int enable) +{ + return 0; +} + +static int pqi_sas_phy_setup(struct sas_phy *phy) +{ + return 0; +} + +static void pqi_sas_phy_release(struct sas_phy *phy) +{ +} + +static int pqi_sas_phy_speed(struct sas_phy *phy, + struct sas_phy_linkrates *rates) +{ + return -EINVAL; +} + +/* SMP = Serial Management Protocol */ + +static int pqi_sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, + struct request *req) +{ + return -EINVAL; +} + +struct sas_function_template pqi_sas_transport_functions = { + .get_linkerrors = pqi_sas_get_linkerrors, + .get_enclosure_identifier = pqi_sas_get_enclosure_identifier, + .get_bay_identifier = pqi_sas_get_bay_identifier, + .phy_reset = pqi_sas_phy_reset, + .phy_enable = pqi_sas_phy_enable, + .phy_setup = pqi_sas_phy_setup, + .phy_release = pqi_sas_phy_release, + .set_phy_speed = pqi_sas_phy_speed, + .smp_handler = pqi_sas_smp_handler, +}; diff --git a/drivers/scsi/smartpqi/smartpqi_sis.c b/drivers/scsi/smartpqi/smartpqi_sis.c new file mode 100644 index 000000000000..71408f9e8f75 --- /dev/null +++ b/drivers/scsi/smartpqi/smartpqi_sis.c @@ -0,0 +1,404 @@ +/* + * driver for Microsemi PQI-based storage controllers + * Copyright (c) 2016 Microsemi Corporation + * Copyright (c) 2016 PMC-Sierra, Inc. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more details. + * + * Questions/Comments/Bugfixes to esc.storagedev@microsemi.com + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <scsi/scsi_device.h> +#include <asm/unaligned.h> +#include "smartpqi.h" +#include "smartpqi_sis.h" + +/* legacy SIS interface commands */ +#define SIS_CMD_GET_ADAPTER_PROPERTIES 0x19 +#define SIS_CMD_INIT_BASE_STRUCT_ADDRESS 0x1b +#define SIS_CMD_GET_PQI_CAPABILITIES 0x3000 + +/* for submission of legacy SIS commands */ +#define SIS_REENABLE_SIS_MODE 0x1 +#define SIS_ENABLE_MSIX 0x40 +#define SIS_SOFT_RESET 0x100 +#define SIS_CMD_READY 0x200 +#define SIS_CMD_COMPLETE 0x1000 +#define SIS_CLEAR_CTRL_TO_HOST_DOORBELL 0x1000 +#define SIS_CMD_STATUS_SUCCESS 0x1 +#define SIS_CMD_COMPLETE_TIMEOUT_SECS 30 +#define SIS_CMD_COMPLETE_POLL_INTERVAL_MSECS 10 + +/* used with SIS_CMD_GET_ADAPTER_PROPERTIES command */ +#define SIS_EXTENDED_PROPERTIES_SUPPORTED 0x800000 +#define SIS_SMARTARRAY_FEATURES_SUPPORTED 0x2 +#define SIS_PQI_MODE_SUPPORTED 0x4 +#define SIS_REQUIRED_EXTENDED_PROPERTIES \ + (SIS_SMARTARRAY_FEATURES_SUPPORTED | SIS_PQI_MODE_SUPPORTED) + +/* used with SIS_CMD_INIT_BASE_STRUCT_ADDRESS command */ +#define SIS_BASE_STRUCT_REVISION 9 +#define SIS_BASE_STRUCT_ALIGNMENT 16 + +#define SIS_CTRL_KERNEL_UP 0x80 +#define SIS_CTRL_KERNEL_PANIC 0x100 +#define SIS_CTRL_READY_TIMEOUT_SECS 30 +#define SIS_CTRL_READY_POLL_INTERVAL_MSECS 10 + +#pragma pack(1) + +/* for use with SIS_CMD_INIT_BASE_STRUCT_ADDRESS command */ +struct sis_base_struct { + __le32 revision; /* revision of this structure */ + __le32 flags; /* reserved */ + __le32 error_buffer_paddr_low; /* lower 32 bits of physical memory */ + /* buffer for PQI error response */ + /* data */ + __le32 error_buffer_paddr_high; /* upper 32 bits of physical */ + /* memory buffer for PQI */ + /* error response data */ + __le32 error_buffer_element_length; /* length of each PQI error */ + /* response buffer element */ + /* in bytes */ + __le32 error_buffer_num_elements; /* total number of PQI error */ + /* response buffers available */ +}; + +#pragma pack() + +int sis_wait_for_ctrl_ready(struct pqi_ctrl_info *ctrl_info) +{ + unsigned long timeout; + u32 status; + + timeout = (SIS_CTRL_READY_TIMEOUT_SECS * HZ) + jiffies; + + while (1) { + status = readl(&ctrl_info->registers->sis_firmware_status); + if (status != ~0) { + if (status & SIS_CTRL_KERNEL_PANIC) { + dev_err(&ctrl_info->pci_dev->dev, + "controller is offline: status code 0x%x\n", + readl( + &ctrl_info->registers->sis_mailbox[7])); + return -ENODEV; + } + if (status & SIS_CTRL_KERNEL_UP) + break; + } + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + msleep(SIS_CTRL_READY_POLL_INTERVAL_MSECS); + } + + return 0; +} + +bool sis_is_firmware_running(struct pqi_ctrl_info *ctrl_info) +{ + bool running; + u32 status; + + status = readl(&ctrl_info->registers->sis_firmware_status); + + if (status & SIS_CTRL_KERNEL_PANIC) + running = false; + else + running = true; + + if (!running) + dev_err(&ctrl_info->pci_dev->dev, + "controller is offline: status code 0x%x\n", + readl(&ctrl_info->registers->sis_mailbox[7])); + + return running; +} + +/* used for passing command parameters/results when issuing SIS commands */ +struct sis_sync_cmd_params { + u32 mailbox[6]; /* mailboxes 0-5 */ +}; + +static int sis_send_sync_cmd(struct pqi_ctrl_info *ctrl_info, + u32 cmd, struct sis_sync_cmd_params *params) +{ + struct pqi_ctrl_registers __iomem *registers; + unsigned int i; + unsigned long timeout; + u32 doorbell; + u32 cmd_status; + + registers = ctrl_info->registers; + + /* Write the command to mailbox 0. */ + writel(cmd, ®isters->sis_mailbox[0]); + + /* + * Write the command parameters to mailboxes 1-4 (mailbox 5 is not used + * when sending a command to the controller). + */ + for (i = 1; i <= 4; i++) + writel(params->mailbox[i], ®isters->sis_mailbox[i]); + + /* Clear the command doorbell. */ + writel(SIS_CLEAR_CTRL_TO_HOST_DOORBELL, + ®isters->sis_ctrl_to_host_doorbell_clear); + + /* Disable doorbell interrupts by masking all interrupts. */ + writel(~0, ®isters->sis_interrupt_mask); + + /* + * Force the completion of the interrupt mask register write before + * submitting the command. + */ + readl(®isters->sis_interrupt_mask); + + /* Submit the command to the controller. */ + writel(SIS_CMD_READY, ®isters->sis_host_to_ctrl_doorbell); + + /* + * Poll for command completion. Note that the call to msleep() is at + * the top of the loop in order to give the controller time to start + * processing the command before we start polling. + */ + timeout = (SIS_CMD_COMPLETE_TIMEOUT_SECS * HZ) + jiffies; + while (1) { + msleep(SIS_CMD_COMPLETE_POLL_INTERVAL_MSECS); + doorbell = readl(®isters->sis_ctrl_to_host_doorbell); + if (doorbell & SIS_CMD_COMPLETE) + break; + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + } + + /* Read the command status from mailbox 0. */ + cmd_status = readl(®isters->sis_mailbox[0]); + if (cmd_status != SIS_CMD_STATUS_SUCCESS) { + dev_err(&ctrl_info->pci_dev->dev, + "SIS command failed for command 0x%x: status = 0x%x\n", + cmd, cmd_status); + return -EINVAL; + } + + /* + * The command completed successfully, so save the command status and + * read the values returned in mailboxes 1-5. + */ + params->mailbox[0] = cmd_status; + for (i = 1; i < ARRAY_SIZE(params->mailbox); i++) + params->mailbox[i] = readl(®isters->sis_mailbox[i]); + + return 0; +} + +/* + * This function verifies that we are talking to a controller that speaks PQI. + */ + +int sis_get_ctrl_properties(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + u32 properties; + u32 extended_properties; + struct sis_sync_cmd_params params; + + memset(¶ms, 0, sizeof(params)); + + rc = sis_send_sync_cmd(ctrl_info, SIS_CMD_GET_ADAPTER_PROPERTIES, + ¶ms); + if (rc) + return rc; + + properties = params.mailbox[1]; + + if (!(properties & SIS_EXTENDED_PROPERTIES_SUPPORTED)) + return -ENODEV; + + extended_properties = params.mailbox[4]; + + if ((extended_properties & SIS_REQUIRED_EXTENDED_PROPERTIES) != + SIS_REQUIRED_EXTENDED_PROPERTIES) + return -ENODEV; + + return 0; +} + +int sis_get_pqi_capabilities(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + struct sis_sync_cmd_params params; + + memset(¶ms, 0, sizeof(params)); + + rc = sis_send_sync_cmd(ctrl_info, SIS_CMD_GET_PQI_CAPABILITIES, + ¶ms); + if (rc) + return rc; + + ctrl_info->max_sg_entries = params.mailbox[1]; + ctrl_info->max_transfer_size = params.mailbox[2]; + ctrl_info->max_outstanding_requests = params.mailbox[3]; + ctrl_info->config_table_offset = params.mailbox[4]; + ctrl_info->config_table_length = params.mailbox[5]; + + return 0; +} + +int sis_init_base_struct_addr(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + void *base_struct_unaligned; + struct sis_base_struct *base_struct; + struct sis_sync_cmd_params params; + unsigned long error_buffer_paddr; + dma_addr_t bus_address; + + base_struct_unaligned = kzalloc(sizeof(*base_struct) + + SIS_BASE_STRUCT_ALIGNMENT - 1, GFP_KERNEL); + if (!base_struct_unaligned) + return -ENOMEM; + + base_struct = PTR_ALIGN(base_struct_unaligned, + SIS_BASE_STRUCT_ALIGNMENT); + error_buffer_paddr = (unsigned long)ctrl_info->error_buffer_dma_handle; + + put_unaligned_le32(SIS_BASE_STRUCT_REVISION, &base_struct->revision); + put_unaligned_le32(lower_32_bits(error_buffer_paddr), + &base_struct->error_buffer_paddr_low); + put_unaligned_le32(upper_32_bits(error_buffer_paddr), + &base_struct->error_buffer_paddr_high); + put_unaligned_le32(PQI_ERROR_BUFFER_ELEMENT_LENGTH, + &base_struct->error_buffer_element_length); + put_unaligned_le32(ctrl_info->max_io_slots, + &base_struct->error_buffer_num_elements); + + bus_address = pci_map_single(ctrl_info->pci_dev, base_struct, + sizeof(*base_struct), PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(ctrl_info->pci_dev, bus_address)) { + rc = -ENOMEM; + goto out; + } + + memset(¶ms, 0, sizeof(params)); + params.mailbox[1] = lower_32_bits((u64)bus_address); + params.mailbox[2] = upper_32_bits((u64)bus_address); + params.mailbox[3] = sizeof(*base_struct); + + rc = sis_send_sync_cmd(ctrl_info, SIS_CMD_INIT_BASE_STRUCT_ADDRESS, + ¶ms); + + pci_unmap_single(ctrl_info->pci_dev, bus_address, sizeof(*base_struct), + PCI_DMA_TODEVICE); + +out: + kfree(base_struct_unaligned); + + return rc; +} + +/* Enable MSI-X interrupts on the controller. */ + +void sis_enable_msix(struct pqi_ctrl_info *ctrl_info) +{ + u32 doorbell_register; + + doorbell_register = + readl(&ctrl_info->registers->sis_host_to_ctrl_doorbell); + doorbell_register |= SIS_ENABLE_MSIX; + + writel(doorbell_register, + &ctrl_info->registers->sis_host_to_ctrl_doorbell); +} + +/* Disable MSI-X interrupts on the controller. */ + +void sis_disable_msix(struct pqi_ctrl_info *ctrl_info) +{ + u32 doorbell_register; + + doorbell_register = + readl(&ctrl_info->registers->sis_host_to_ctrl_doorbell); + doorbell_register &= ~SIS_ENABLE_MSIX; + + writel(doorbell_register, + &ctrl_info->registers->sis_host_to_ctrl_doorbell); +} + +void sis_soft_reset(struct pqi_ctrl_info *ctrl_info) +{ + writel(SIS_SOFT_RESET, + &ctrl_info->registers->sis_host_to_ctrl_doorbell); +} + +#define SIS_MODE_READY_TIMEOUT_SECS 30 + +int sis_reenable_sis_mode(struct pqi_ctrl_info *ctrl_info) +{ + int rc; + unsigned long timeout; + struct pqi_ctrl_registers __iomem *registers; + u32 doorbell; + + registers = ctrl_info->registers; + + writel(SIS_REENABLE_SIS_MODE, + ®isters->sis_host_to_ctrl_doorbell); + + rc = 0; + timeout = (SIS_MODE_READY_TIMEOUT_SECS * HZ) + jiffies; + + while (1) { + doorbell = readl(®isters->sis_ctrl_to_host_doorbell); + if ((doorbell & SIS_REENABLE_SIS_MODE) == 0) + break; + if (time_after(jiffies, timeout)) { + rc = -ETIMEDOUT; + break; + } + } + + if (rc) + dev_err(&ctrl_info->pci_dev->dev, + "re-enabling SIS mode failed\n"); + + return rc; +} + +void sis_write_driver_scratch(struct pqi_ctrl_info *ctrl_info, u32 value) +{ + writel(value, &ctrl_info->registers->sis_driver_scratch); +} + +u32 sis_read_driver_scratch(struct pqi_ctrl_info *ctrl_info) +{ + return readl(&ctrl_info->registers->sis_driver_scratch); +} + +static void __attribute__((unused)) verify_structures(void) +{ + BUILD_BUG_ON(offsetof(struct sis_base_struct, + revision) != 0x0); + BUILD_BUG_ON(offsetof(struct sis_base_struct, + flags) != 0x4); + BUILD_BUG_ON(offsetof(struct sis_base_struct, + error_buffer_paddr_low) != 0x8); + BUILD_BUG_ON(offsetof(struct sis_base_struct, + error_buffer_paddr_high) != 0xc); + BUILD_BUG_ON(offsetof(struct sis_base_struct, + error_buffer_element_length) != 0x10); + BUILD_BUG_ON(offsetof(struct sis_base_struct, + error_buffer_num_elements) != 0x14); + BUILD_BUG_ON(sizeof(struct sis_base_struct) != 0x18); +} diff --git a/drivers/scsi/smartpqi/smartpqi_sis.h b/drivers/scsi/smartpqi/smartpqi_sis.h new file mode 100644 index 000000000000..bd6e7b08338e --- /dev/null +++ b/drivers/scsi/smartpqi/smartpqi_sis.h @@ -0,0 +1,34 @@ +/* + * driver for Microsemi PQI-based storage controllers + * Copyright (c) 2016 Microsemi Corporation + * Copyright (c) 2016 PMC-Sierra, Inc. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more details. + * + * Questions/Comments/Bugfixes to esc.storagedev@microsemi.com + * + */ + +#if !defined(_SMARTPQI_SIS_H) +#define _SMARTPQI_SIS_H + +int sis_wait_for_ctrl_ready(struct pqi_ctrl_info *ctrl_info); +bool sis_is_firmware_running(struct pqi_ctrl_info *ctrl_info); +int sis_get_ctrl_properties(struct pqi_ctrl_info *ctrl_info); +int sis_get_pqi_capabilities(struct pqi_ctrl_info *ctrl_info); +int sis_init_base_struct_addr(struct pqi_ctrl_info *ctrl_info); +void sis_enable_msix(struct pqi_ctrl_info *ctrl_info); +void sis_disable_msix(struct pqi_ctrl_info *ctrl_info); +void sis_soft_reset(struct pqi_ctrl_info *ctrl_info); +int sis_reenable_sis_mode(struct pqi_ctrl_info *ctrl_info); +void sis_write_driver_scratch(struct pqi_ctrl_info *ctrl_info, u32 value); +u32 sis_read_driver_scratch(struct pqi_ctrl_info *ctrl_info); + +#endif /* _SMARTPQI_SIS_H */ diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index ed179348de80..bed2bbd6b923 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -83,7 +83,7 @@ static int sr_init_command(struct scsi_cmnd *SCpnt); static int sr_done(struct scsi_cmnd *); static int sr_runtime_suspend(struct device *dev); -static struct dev_pm_ops sr_pm_ops = { +static const struct dev_pm_ops sr_pm_ops = { .runtime_suspend = sr_runtime_suspend, }; diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c index 5d00e514ff28..d32e3ba8863e 100644 --- a/drivers/scsi/sym53c8xx_2/sym_glue.c +++ b/drivers/scsi/sym53c8xx_2/sym_glue.c @@ -1874,7 +1874,7 @@ static void sym2_io_resume(struct pci_dev *pdev) spin_lock_irq(shost->host_lock); if (sym_data->io_reset) - complete_all(sym_data->io_reset); + complete(sym_data->io_reset); spin_unlock_irq(shost->host_lock); } diff --git a/drivers/scsi/t128.c b/drivers/scsi/t128.c deleted file mode 100644 index 8a8608ac62e6..000000000000 --- a/drivers/scsi/t128.c +++ /dev/null @@ -1,407 +0,0 @@ -/* - * Trantor T128/T128F/T228 driver - * Note : architecturally, the T100 and T130 are different and won't - * work - * - * Copyright 1993, Drew Eckhardt - * Visionary Computing - * (Unix and Linux consulting and custom programming) - * drew@colorado.edu - * +1 (303) 440-4894 - * - * For more information, please consult - * - * Trantor Systems, Ltd. - * T128/T128F/T228 SCSI Host Adapter - * Hardware Specifications - * - * Trantor Systems, Ltd. - * 5415 Randall Place - * Fremont, CA 94538 - * 1+ (415) 770-1400, FAX 1+ (415) 770-9910 - */ - -/* - * The card is detected and initialized in one of several ways : - * 1. Autoprobe (default) - since the board is memory mapped, - * a BIOS signature is scanned for to locate the registers. - * An interrupt is triggered to autoprobe for the interrupt - * line. - * - * 2. With command line overrides - t128=address,irq may be - * used on the LILO command line to override the defaults. - * - * 3. With the T128_OVERRIDE compile time define. This is - * specified as an array of address, irq tuples. Ie, for - * one board at the default 0xcc000 address, IRQ5, I could say - * -DT128_OVERRIDE={{0xcc000, 5}} - * - * Note that if the override methods are used, place holders must - * be specified for other boards in the system. - * - * T128/T128F jumper/dipswitch settings (note : on my sample, the switches - * were epoxy'd shut, meaning I couldn't change the 0xcc000 base address) : - * - * T128 Sw7 Sw8 Sw6 = 0ws Sw5 = boot - * T128F Sw6 Sw7 Sw5 = 0ws Sw4 = boot Sw8 = floppy disable - * cc000 off off - * c8000 off on - * dc000 on off - * d8000 on on - * - * - * Interrupts - * There is a 12 pin jumper block, jp1, numbered as follows : - * T128 (JP1) T128F (J5) - * 2 4 6 8 10 12 11 9 7 5 3 1 - * 1 3 5 7 9 11 12 10 8 6 4 2 - * - * 3 2-4 - * 5 1-3 - * 7 3-5 - * T128F only - * 10 8-10 - * 12 7-9 - * 14 10-12 - * 15 9-11 - */ - -#include <linux/io.h> -#include <linux/blkdev.h> -#include <linux/interrupt.h> -#include <linux/init.h> -#include <linux/module.h> - -#include <scsi/scsi_host.h> -#include "t128.h" -#include "NCR5380.h" - -static struct override { - unsigned long address; - int irq; -} overrides -#ifdef T128_OVERRIDE - [] __initdata = T128_OVERRIDE; -#else - [4] __initdata = {{0, IRQ_AUTO}, {0, IRQ_AUTO}, - {0 ,IRQ_AUTO}, {0, IRQ_AUTO}}; -#endif - -#define NO_OVERRIDES ARRAY_SIZE(overrides) - -static struct base { - unsigned int address; - int noauto; -} bases[] __initdata = { - { 0xcc000, 0}, { 0xc8000, 0}, { 0xdc000, 0}, { 0xd8000, 0} -}; - -#define NO_BASES ARRAY_SIZE(bases) - -static struct signature { - const char *string; - int offset; -} signatures[] __initdata = { -{"TSROM: SCSI BIOS, Version 1.12", 0x36}, -}; - -#define NO_SIGNATURES ARRAY_SIZE(signatures) - -#ifndef MODULE -/* - * Function : t128_setup(char *str, int *ints) - * - * Purpose : LILO command line initialization of the overrides array, - * - * Inputs : str - unused, ints - array of integer parameters with ints[0] - * equal to the number of ints. - * - */ - -static int __init t128_setup(char *str) -{ - static int commandline_current; - int i; - int ints[10]; - - get_options(str, ARRAY_SIZE(ints), ints); - if (ints[0] != 2) - printk("t128_setup : usage t128=address,irq\n"); - else - if (commandline_current < NO_OVERRIDES) { - overrides[commandline_current].address = ints[1]; - overrides[commandline_current].irq = ints[2]; - for (i = 0; i < NO_BASES; ++i) - if (bases[i].address == ints[1]) { - bases[i].noauto = 1; - break; - } - ++commandline_current; - } - return 1; -} - -__setup("t128=", t128_setup); -#endif - -/* - * Function : int t128_detect(struct scsi_host_template * tpnt) - * - * Purpose : detects and initializes T128,T128F, or T228 controllers - * that were autoprobed, overridden on the LILO command line, - * or specified at compile time. - * - * Inputs : tpnt - template for this SCSI adapter. - * - * Returns : 1 if a host adapter was found, 0 if not. - * - */ - -static int __init t128_detect(struct scsi_host_template *tpnt) -{ - static int current_override, current_base; - struct Scsi_Host *instance; - unsigned long base; - void __iomem *p; - int sig, count; - - for (count = 0; current_override < NO_OVERRIDES; ++current_override) { - base = 0; - p = NULL; - - if (overrides[current_override].address) { - base = overrides[current_override].address; - p = ioremap(bases[current_base].address, 0x2000); - if (!p) - base = 0; - } else - for (; !base && (current_base < NO_BASES); ++current_base) { - dprintk(NDEBUG_INIT, "t128: probing address 0x%08x\n", - bases[current_base].address); - if (bases[current_base].noauto) - continue; - p = ioremap(bases[current_base].address, 0x2000); - if (!p) - continue; - for (sig = 0; sig < NO_SIGNATURES; ++sig) - if (check_signature(p + signatures[sig].offset, - signatures[sig].string, - strlen(signatures[sig].string))) { - base = bases[current_base].address; - dprintk(NDEBUG_INIT, "t128: detected board\n"); - goto found; - } - iounmap(p); - } - - dprintk(NDEBUG_INIT, "t128: base = 0x%08x\n", (unsigned int)base); - - if (!base) - break; - -found: - instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata)); - if(instance == NULL) - goto out_unmap; - - instance->base = base; - ((struct NCR5380_hostdata *)instance->hostdata)->base = p; - - if (NCR5380_init(instance, FLAG_DMA_FIXUP | FLAG_LATE_DMA_SETUP)) - goto out_unregister; - - NCR5380_maybe_reset_bus(instance); - - if (overrides[current_override].irq != IRQ_AUTO) - instance->irq = overrides[current_override].irq; - else - instance->irq = NCR5380_probe_irq(instance, T128_IRQS); - - /* Compatibility with documented NCR5380 kernel parameters */ - if (instance->irq == 255) - instance->irq = NO_IRQ; - - if (instance->irq != NO_IRQ) - if (request_irq(instance->irq, t128_intr, 0, "t128", - instance)) { - printk("scsi%d : IRQ%d not free, interrupts disabled\n", - instance->host_no, instance->irq); - instance->irq = NO_IRQ; - } - - if (instance->irq == NO_IRQ) { - printk("scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no); - printk("scsi%d : please jumper the board for a free IRQ.\n", instance->host_no); - } - - dprintk(NDEBUG_INIT, "scsi%d: irq = %d\n", - instance->host_no, instance->irq); - - ++current_override; - ++count; - } - return count; - -out_unregister: - scsi_unregister(instance); -out_unmap: - iounmap(p); - return count; -} - -static int t128_release(struct Scsi_Host *shost) -{ - struct NCR5380_hostdata *hostdata = shost_priv(shost); - - if (shost->irq != NO_IRQ) - free_irq(shost->irq, shost); - NCR5380_exit(shost); - scsi_unregister(shost); - iounmap(hostdata->base); - return 0; -} - -/* - * Function : int t128_biosparam(Disk * disk, struct block_device *dev, int *ip) - * - * Purpose : Generates a BIOS / DOS compatible H-C-S mapping for - * the specified device / size. - * - * Inputs : size = size of device in sectors (512 bytes), dev = block device - * major / minor, ip[] = {heads, sectors, cylinders} - * - * Returns : always 0 (success), initializes ip - * - */ - -/* - * XXX Most SCSI boards use this mapping, I could be incorrect. Some one - * using hard disks on a trantor should verify that this mapping corresponds - * to that used by the BIOS / ASPI driver by running the linux fdisk program - * and matching the H_C_S coordinates to what DOS uses. - */ - -static int t128_biosparam(struct scsi_device *sdev, struct block_device *bdev, - sector_t capacity, int *ip) -{ - ip[0] = 64; - ip[1] = 32; - ip[2] = capacity >> 11; - return 0; -} - -/* - * Function : int t128_pread (struct Scsi_Host *instance, - * unsigned char *dst, int len) - * - * Purpose : Fast 5380 pseudo-dma read function, transfers len bytes to - * dst - * - * Inputs : dst = destination, len = length in bytes - * - * Returns : 0 on success, non zero on a failure such as a watchdog - * timeout. - */ - -static inline int t128_pread(struct Scsi_Host *instance, - unsigned char *dst, int len) -{ - struct NCR5380_hostdata *hostdata = shost_priv(instance); - void __iomem *reg, *base = hostdata->base; - unsigned char *d = dst; - register int i = len; - - reg = base + T_DATA_REG_OFFSET; - -#if 0 - for (; i; --i) { - while (!(readb(base+T_STATUS_REG_OFFSET) & T_ST_RDY)) barrier(); -#else - while (!(readb(base+T_STATUS_REG_OFFSET) & T_ST_RDY)) barrier(); - for (; i; --i) { -#endif - *d++ = readb(reg); - } - - if (readb(base + T_STATUS_REG_OFFSET) & T_ST_TIM) { - unsigned char tmp; - void __iomem *foo = base + T_CONTROL_REG_OFFSET; - tmp = readb(foo); - writeb(tmp | T_CR_CT, foo); - writeb(tmp, foo); - printk("scsi%d : watchdog timer fired in NCR5380_pread()\n", - instance->host_no); - return -1; - } else - return 0; -} - -/* - * Function : int t128_pwrite (struct Scsi_Host *instance, - * unsigned char *src, int len) - * - * Purpose : Fast 5380 pseudo-dma write function, transfers len bytes from - * src - * - * Inputs : src = source, len = length in bytes - * - * Returns : 0 on success, non zero on a failure such as a watchdog - * timeout. - */ - -static inline int t128_pwrite(struct Scsi_Host *instance, - unsigned char *src, int len) -{ - struct NCR5380_hostdata *hostdata = shost_priv(instance); - void __iomem *reg, *base = hostdata->base; - unsigned char *s = src; - register int i = len; - - reg = base + T_DATA_REG_OFFSET; - -#if 0 - for (; i; --i) { - while (!(readb(base+T_STATUS_REG_OFFSET) & T_ST_RDY)) barrier(); -#else - while (!(readb(base+T_STATUS_REG_OFFSET) & T_ST_RDY)) barrier(); - for (; i; --i) { -#endif - writeb(*s++, reg); - } - - if (readb(base + T_STATUS_REG_OFFSET) & T_ST_TIM) { - unsigned char tmp; - void __iomem *foo = base + T_CONTROL_REG_OFFSET; - tmp = readb(foo); - writeb(tmp | T_CR_CT, foo); - writeb(tmp, foo); - printk("scsi%d : watchdog timer fired in NCR5380_pwrite()\n", - instance->host_no); - return -1; - } else - return 0; -} - -MODULE_LICENSE("GPL"); - -#include "NCR5380.c" - -static struct scsi_host_template driver_template = { - .name = "Trantor T128/T128F/T228", - .detect = t128_detect, - .release = t128_release, - .proc_name = "t128", - .info = t128_info, - .queuecommand = t128_queue_command, - .eh_abort_handler = t128_abort, - .eh_bus_reset_handler = t128_bus_reset, - .bios_param = t128_biosparam, - .can_queue = 32, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 2, - .use_clustering = DISABLE_CLUSTERING, - .cmd_size = NCR5380_CMD_SIZE, - .max_sectors = 128, -}; -#include "scsi_module.c" diff --git a/drivers/scsi/t128.h b/drivers/scsi/t128.h deleted file mode 100644 index c95bcd839109..000000000000 --- a/drivers/scsi/t128.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Trantor T128/T128F/T228 defines - * Note : architecturally, the T100 and T128 are different and won't work - * - * Copyright 1993, Drew Eckhardt - * Visionary Computing - * (Unix and Linux consulting and custom programming) - * drew@colorado.edu - * +1 (303) 440-4894 - * - * For more information, please consult - * - * Trantor Systems, Ltd. - * T128/T128F/T228 SCSI Host Adapter - * Hardware Specifications - * - * Trantor Systems, Ltd. - * 5415 Randall Place - * Fremont, CA 94538 - * 1+ (415) 770-1400, FAX 1+ (415) 770-9910 - */ - -#ifndef T128_H -#define T128_H - -/* - * The trantor boards are memory mapped. They use an NCR5380 or - * equivalent (my sample board had part second sourced from ZILOG). - * NCR's recommended "Pseudo-DMA" architecture is used, where - * a PAL drives the DMA signals on the 5380 allowing fast, blind - * transfers with proper handshaking. - */ - -/* - * Note : a boot switch is provided for the purpose of informing the - * firmware to boot or not boot from attached SCSI devices. So, I imagine - * there are fewer people who've yanked the ROM like they do on the Seagate - * to make bootup faster, and I'll probably use this for autodetection. - */ -#define T_ROM_OFFSET 0 - -/* - * Note : my sample board *WAS NOT* populated with the SRAM, so this - * can't be used for autodetection without a ROM present. - */ -#define T_RAM_OFFSET 0x1800 - -/* - * All of the registers are allocated 32 bytes of address space, except - * for the data register (read/write to/from the 5380 in pseudo-DMA mode) - */ -#define T_CONTROL_REG_OFFSET 0x1c00 /* rw */ -#define T_CR_INT 0x10 /* Enable interrupts */ -#define T_CR_CT 0x02 /* Reset watchdog timer */ - -#define T_STATUS_REG_OFFSET 0x1c20 /* ro */ -#define T_ST_BOOT 0x80 /* Boot switch */ -#define T_ST_S3 0x40 /* User settable switches, */ -#define T_ST_S2 0x20 /* read 0 when switch is on, 1 off */ -#define T_ST_S1 0x10 -#define T_ST_PS2 0x08 /* Set for Microchannel 228 */ -#define T_ST_RDY 0x04 /* 5380 DRQ */ -#define T_ST_TIM 0x02 /* indicates 40us watchdog timer fired */ -#define T_ST_ZERO 0x01 /* Always zero */ - -#define T_5380_OFFSET 0x1d00 /* 8 registers here, see NCR5380.h */ - -#define T_DATA_REG_OFFSET 0x1e00 /* rw 512 bytes long */ - -#define NCR5380_implementation_fields \ - void __iomem *base - -#define T128_address(reg) \ - (((struct NCR5380_hostdata *)shost_priv(instance))->base + T_5380_OFFSET + ((reg) * 0x20)) - -#define NCR5380_read(reg) readb(T128_address(reg)) -#define NCR5380_write(reg, value) writeb((value),(T128_address(reg))) - -#define NCR5380_dma_xfer_len(instance, cmd, phase) (cmd->transfersize) -#define NCR5380_dma_recv_setup t128_pread -#define NCR5380_dma_send_setup t128_pwrite -#define NCR5380_dma_residual(instance) (0) - -#define NCR5380_intr t128_intr -#define NCR5380_queue_command t128_queue_command -#define NCR5380_abort t128_abort -#define NCR5380_bus_reset t128_bus_reset -#define NCR5380_info t128_info - -#define NCR5380_io_delay(x) udelay(x) - -/* 15 14 12 10 7 5 3 - 1101 0100 1010 1000 */ - -#define T128_IRQS 0xc4a8 - -#endif /* T128_H */ diff --git a/drivers/scsi/u14-34f.c b/drivers/scsi/u14-34f.c deleted file mode 100644 index 14eb50b95a1e..000000000000 --- a/drivers/scsi/u14-34f.c +++ /dev/null @@ -1,1971 +0,0 @@ -/* - * u14-34f.c - Low-level driver for UltraStor 14F/34F SCSI host adapters. - * - * 03 Jun 2003 Rev. 8.10 for linux-2.5.70 - * + Update for new IRQ API. - * + Use "goto" when appropriate. - * + Drop u14-34f.h. - * + Update for new module_param API. - * + Module parameters can now be specified only in the - * same format as the kernel boot options. - * - * boot option old module param - * ----------- ------------------ - * addr,... io_port=addr,... - * lc:[y|n] linked_comm=[1|0] - * mq:xx max_queue_depth=xx - * tm:[0|1|2] tag_mode=[0|1|2] - * et:[y|n] ext_tran=[1|0] - * of:[y|n] have_old_firmware=[1|0] - * - * A valid example using the new parameter format is: - * modprobe u14-34f "u14-34f=0x340,0x330,lc:y,tm:0,mq:4" - * - * which is equivalent to the old format: - * modprobe u14-34f io_port=0x340,0x330 linked_comm=1 tag_mode=0 \ - * max_queue_depth=4 - * - * With actual module code, u14-34f and u14_34f are equivalent - * as module parameter names. - * - * 12 Feb 2003 Rev. 8.04 for linux 2.5.60 - * + Release irq before calling scsi_register. - * - * 12 Nov 2002 Rev. 8.02 for linux 2.5.47 - * + Release driver_lock before calling scsi_register. - * - * 11 Nov 2002 Rev. 8.01 for linux 2.5.47 - * + Fixed bios_param and scsicam_bios_param calling parameters. - * - * 28 Oct 2002 Rev. 8.00 for linux 2.5.44-ac4 - * + Use new tcq and adjust_queue_depth api. - * + New command line option (tm:[0-2]) to choose the type of tags: - * 0 -> disable tagging ; 1 -> simple tags ; 2 -> ordered tags. - * Default is tm:0 (tagged commands disabled). - * For compatibility the "tc:" option is an alias of the "tm:" - * option; tc:n is equivalent to tm:0 and tc:y is equivalent to - * tm:1. - * - * 10 Oct 2002 Rev. 7.70 for linux 2.5.42 - * + Foreport from revision 6.70. - * - * 25 Jun 2002 Rev. 6.70 for linux 2.4.19 - * + Fixed endian-ness problem due to bitfields. - * - * 21 Feb 2002 Rev. 6.52 for linux 2.4.18 - * + Backport from rev. 7.22 (use io_request_lock). - * - * 20 Feb 2002 Rev. 7.22 for linux 2.5.5 - * + Remove any reference to virt_to_bus(). - * + Fix pio hang while detecting multiple HBAs. - * - * 01 Jan 2002 Rev. 7.20 for linux 2.5.1 - * + Use the dynamic DMA mapping API. - * - * 19 Dec 2001 Rev. 7.02 for linux 2.5.1 - * + Use SCpnt->sc_data_direction if set. - * + Use sglist.page instead of sglist.address. - * - * 11 Dec 2001 Rev. 7.00 for linux 2.5.1 - * + Use host->host_lock instead of io_request_lock. - * - * 1 May 2001 Rev. 6.05 for linux 2.4.4 - * + Fix data transfer direction for opcode SEND_CUE_SHEET (0x5d) - * - * 25 Jan 2001 Rev. 6.03 for linux 2.4.0 - * + "check_region" call replaced by "request_region". - * - * 22 Nov 2000 Rev. 6.02 for linux 2.4.0-test11 - * + Removed old scsi error handling support. - * + The obsolete boot option flag eh:n is silently ignored. - * + Removed error messages while a disk drive is powered up at - * boot time. - * + Improved boot messages: all tagged capable device are - * indicated as "tagged". - * - * 16 Sep 1999 Rev. 5.11 for linux 2.2.12 and 2.3.18 - * + Updated to the new __setup interface for boot command line options. - * + When loaded as a module, accepts the new parameter boot_options - * which value is a string with the same format of the kernel boot - * command line options. A valid example is: - * modprobe u14-34f 'boot_options="0x230,0x340,lc:y,mq:4"' - * - * 22 Jul 1999 Rev. 5.00 for linux 2.2.10 and 2.3.11 - * + Removed pre-2.2 source code compatibility. - * - * 26 Jul 1998 Rev. 4.33 for linux 2.0.35 and 2.1.111 - * Added command line option (et:[y|n]) to use the existing - * translation (returned by scsicam_bios_param) as disk geometry. - * The default is et:n, which uses the disk geometry jumpered - * on the board. - * The default value et:n is compatible with all previous revisions - * of this driver. - * - * 28 May 1998 Rev. 4.32 for linux 2.0.33 and 2.1.104 - * Increased busy timeout from 10 msec. to 200 msec. while - * processing interrupts. - * - * 18 May 1998 Rev. 4.31 for linux 2.0.33 and 2.1.102 - * Improved abort handling during the eh recovery process. - * - * 13 May 1998 Rev. 4.30 for linux 2.0.33 and 2.1.101 - * The driver is now fully SMP safe, including the - * abort and reset routines. - * Added command line options (eh:[y|n]) to choose between - * new_eh_code and the old scsi code. - * If linux version >= 2.1.101 the default is eh:y, while the eh - * option is ignored for previous releases and the old scsi code - * is used. - * - * 18 Apr 1998 Rev. 4.20 for linux 2.0.33 and 2.1.97 - * Reworked interrupt handler. - * - * 11 Apr 1998 rev. 4.05 for linux 2.0.33 and 2.1.95 - * Major reliability improvement: when a batch with overlapping - * requests is detected, requests are queued one at a time - * eliminating any possible board or drive reordering. - * - * 10 Apr 1998 rev. 4.04 for linux 2.0.33 and 2.1.95 - * Improved SMP support (if linux version >= 2.1.95). - * - * 9 Apr 1998 rev. 4.03 for linux 2.0.33 and 2.1.94 - * Performance improvement: when sequential i/o is detected, - * always use direct sort instead of reverse sort. - * - * 4 Apr 1998 rev. 4.02 for linux 2.0.33 and 2.1.92 - * io_port is now unsigned long. - * - * 17 Mar 1998 rev. 4.01 for linux 2.0.33 and 2.1.88 - * Use new scsi error handling code (if linux version >= 2.1.88). - * Use new interrupt code. - * - * 12 Sep 1997 rev. 3.11 for linux 2.0.30 and 2.1.55 - * Use of udelay inside the wait loops to avoid timeout - * problems with fast cpus. - * Removed check about useless calls to the interrupt service - * routine (reported on SMP systems only). - * At initialization time "sorted/unsorted" is displayed instead - * of "linked/unlinked" to reinforce the fact that "linking" is - * nothing but "elevator sorting" in the actual implementation. - * - * 17 May 1997 rev. 3.10 for linux 2.0.30 and 2.1.38 - * Use of serial_number_at_timeout in abort and reset processing. - * Use of the __initfunc and __initdata macro in setup code. - * Minor cleanups in the list_statistics code. - * - * 24 Feb 1997 rev. 3.00 for linux 2.0.29 and 2.1.26 - * When loading as a module, parameter passing is now supported - * both in 2.0 and in 2.1 style. - * Fixed data transfer direction for some SCSI opcodes. - * Immediate acknowledge to request sense commands. - * Linked commands to each disk device are now reordered by elevator - * sorting. Rare cases in which reordering of write requests could - * cause wrong results are managed. - * - * 18 Jan 1997 rev. 2.60 for linux 2.1.21 and 2.0.28 - * Added command line options to enable/disable linked commands - * (lc:[y|n]), old firmware support (of:[y|n]) and to set the max - * queue depth (mq:xx). Default is "u14-34f=lc:n,of:n,mq:8". - * Improved command linking. - * - * 8 Jan 1997 rev. 2.50 for linux 2.1.20 and 2.0.27 - * Added linked command support. - * - * 3 Dec 1996 rev. 2.40 for linux 2.1.14 and 2.0.27 - * Added queue depth adjustment. - * - * 22 Nov 1996 rev. 2.30 for linux 2.1.12 and 2.0.26 - * The list of i/o ports to be probed can be overwritten by the - * "u14-34f=port0,port1,...." boot command line option. - * Scatter/gather lists are now allocated by a number of kmalloc - * calls, in order to avoid the previous size limit of 64Kb. - * - * 16 Nov 1996 rev. 2.20 for linux 2.1.10 and 2.0.25 - * Added multichannel support. - * - * 27 Sep 1996 rev. 2.12 for linux 2.1.0 - * Portability cleanups (virtual/bus addressing, little/big endian - * support). - * - * 09 Jul 1996 rev. 2.11 for linux 2.0.4 - * "Data over/under-run" no longer implies a redo on all targets. - * Number of internal retries is now limited. - * - * 16 Apr 1996 rev. 2.10 for linux 1.3.90 - * New argument "reset_flags" to the reset routine. - * - * 21 Jul 1995 rev. 2.02 for linux 1.3.11 - * Fixed Data Transfer Direction for some SCSI commands. - * - * 13 Jun 1995 rev. 2.01 for linux 1.2.10 - * HAVE_OLD_UX4F_FIRMWARE should be defined for U34F boards when - * the firmware prom is not the latest one (28008-006). - * - * 11 Mar 1995 rev. 2.00 for linux 1.2.0 - * Fixed a bug which prevented media change detection for removable - * disk drives. - * - * 23 Feb 1995 rev. 1.18 for linux 1.1.94 - * Added a check for scsi_register returning NULL. - * - * 11 Feb 1995 rev. 1.17 for linux 1.1.91 - * U14F qualified to run with 32 sglists. - * Now DEBUG_RESET is disabled by default. - * - * 9 Feb 1995 rev. 1.16 for linux 1.1.90 - * Use host->wish_block instead of host->block. - * - * 8 Feb 1995 rev. 1.15 for linux 1.1.89 - * Cleared target_time_out counter while performing a reset. - * - * 28 Jan 1995 rev. 1.14 for linux 1.1.86 - * Added module support. - * Log and do a retry when a disk drive returns a target status - * different from zero on a recovered error. - * Auto detects if U14F boards have an old firmware revision. - * Max number of scatter/gather lists set to 16 for all boards - * (most installation run fine using 33 sglists, while other - * has problems when using more than 16). - * - * 16 Jan 1995 rev. 1.13 for linux 1.1.81 - * Display a message if check_region detects a port address - * already in use. - * - * 15 Dec 1994 rev. 1.12 for linux 1.1.74 - * The host->block flag is set for all the detected ISA boards. - * - * 30 Nov 1994 rev. 1.11 for linux 1.1.68 - * Redo i/o on target status CHECK_CONDITION for TYPE_DISK only. - * Added optional support for using a single board at a time. - * - * 14 Nov 1994 rev. 1.10 for linux 1.1.63 - * - * 28 Oct 1994 rev. 1.09 for linux 1.1.58 Final BETA release. - * 16 Jul 1994 rev. 1.00 for linux 1.1.29 Initial ALPHA release. - * - * This driver is a total replacement of the original UltraStor - * scsi driver, but it supports ONLY the 14F and 34F boards. - * It can be configured in the same kernel in which the original - * ultrastor driver is configured to allow the original U24F - * support. - * - * Multiple U14F and/or U34F host adapters are supported. - * - * Copyright (C) 1994-2003 Dario Ballabio (ballabio_dario@emc.com) - * - * Alternate email: dario.ballabio@inwind.it, dario.ballabio@tiscalinet.it - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that redistributions of source - * code retain the above copyright notice and this comment without - * modification. - * - * WARNING: if your 14/34F board has an old firmware revision (see below) - * you must change "#undef" into "#define" in the following - * statement. - */ -#undef HAVE_OLD_UX4F_FIRMWARE -/* - * The UltraStor 14F, 24F, and 34F are a family of intelligent, high - * performance SCSI-2 host adapters. - * Here is the scoop on the various models: - * - * 14F - ISA first-party DMA HA with floppy support and WD1003 emulation. - * 24F - EISA Bus Master HA with floppy support and WD1003 emulation. - * 34F - VESA Local-Bus Bus Master HA (no WD1003 emulation). - * - * This code has been tested with up to two U14F boards, using both - * firmware 28004-005/38004-004 (BIOS rev. 2.00) and the latest firmware - * 28004-006/38004-005 (BIOS rev. 2.01). - * - * The latest firmware is required in order to get reliable operations when - * clustering is enabled. ENABLE_CLUSTERING provides a performance increase - * up to 50% on sequential access. - * - * Since the struct scsi_host_template structure is shared among all 14F and 34F, - * the last setting of use_clustering is in effect for all of these boards. - * - * Here a sample configuration using two U14F boards: - * - U14F0: ISA 0x330, BIOS 0xc8000, IRQ 11, DMA 5, SG 32, MB 16, of:n, lc:y, mq:8. - U14F1: ISA 0x340, BIOS 0x00000, IRQ 10, DMA 6, SG 32, MB 16, of:n, lc:y, mq:8. - * - * The boot controller must have its BIOS enabled, while other boards can - * have their BIOS disabled, or enabled to an higher address. - * Boards are named Ux4F0, Ux4F1..., according to the port address order in - * the io_port[] array. - * - * The following facts are based on real testing results (not on - * documentation) on the above U14F board. - * - * - The U14F board should be jumpered for bus on time less or equal to 7 - * microseconds, while the default is 11 microseconds. This is order to - * get acceptable performance while using floppy drive and hard disk - * together. The jumpering for 7 microseconds is: JP13 pin 15-16, - * JP14 pin 7-8 and pin 9-10. - * The reduction has a little impact on scsi performance. - * - * - If scsi bus length exceeds 3m., the scsi bus speed needs to be reduced - * from 10Mhz to 5Mhz (do this by inserting a jumper on JP13 pin 7-8). - * - * - If U14F on board firmware is older than 28004-006/38004-005, - * the U14F board is unable to provide reliable operations if the scsi - * request length exceeds 16Kbyte. When this length is exceeded the - * behavior is: - * - adapter_status equal 0x96 or 0xa3 or 0x93 or 0x94; - * - adapter_status equal 0 and target_status equal 2 on for all targets - * in the next operation following the reset. - * This sequence takes a long time (>3 seconds), so in the meantime - * the SD_TIMEOUT in sd.c could expire giving rise to scsi aborts - * (SD_TIMEOUT has been increased from 3 to 6 seconds in 1.1.31). - * Because of this I had to DISABLE_CLUSTERING and to work around the - * bus reset in the interrupt service routine, returning DID_BUS_BUSY - * so that the operations are retried without complains from the scsi.c - * code. - * Any reset of the scsi bus is going to kill tape operations, since - * no retry is allowed for tapes. Bus resets are more likely when the - * scsi bus is under heavy load. - * Requests using scatter/gather have a maximum length of 16 x 1024 bytes - * when DISABLE_CLUSTERING is in effect, but unscattered requests could be - * larger than 16Kbyte. - * - * The new firmware has fixed all the above problems. - * - * For U34F boards the latest bios prom is 38008-002 (BIOS rev. 2.01), - * the latest firmware prom is 28008-006. Older firmware 28008-005 has - * problems when using more than 16 scatter/gather lists. - * - * The list of i/o ports to be probed can be totally replaced by the - * boot command line option: "u14-34f=port0,port1,port2,...", where the - * port0, port1... arguments are ISA/VESA addresses to be probed. - * For example using "u14-34f=0x230,0x340", the driver probes only the two - * addresses 0x230 and 0x340 in this order; "u14-34f=0" totally disables - * this driver. - * - * After the optional list of detection probes, other possible command line - * options are: - * - * et:y use disk geometry returned by scsicam_bios_param; - * et:n use disk geometry jumpered on the board; - * lc:y enables linked commands; - * lc:n disables linked commands; - * tm:0 disables tagged commands (same as tc:n); - * tm:1 use simple queue tags (same as tc:y); - * tm:2 use ordered queue tags (same as tc:2); - * of:y enables old firmware support; - * of:n disables old firmware support; - * mq:xx set the max queue depth to the value xx (2 <= xx <= 8). - * - * The default value is: "u14-34f=lc:n,of:n,mq:8,tm:0,et:n". - * An example using the list of detection probes could be: - * "u14-34f=0x230,0x340,lc:y,tm:2,of:n,mq:4,et:n". - * - * When loading as a module, parameters can be specified as well. - * The above example would be (use 1 in place of y and 0 in place of n): - * - * modprobe u14-34f io_port=0x230,0x340 linked_comm=1 have_old_firmware=0 \ - * max_queue_depth=4 ext_tran=0 tag_mode=2 - * - * ---------------------------------------------------------------------------- - * In this implementation, linked commands are designed to work with any DISK - * or CD-ROM, since this linking has only the intent of clustering (time-wise) - * and reordering by elevator sorting commands directed to each device, - * without any relation with the actual SCSI protocol between the controller - * and the device. - * If Q is the queue depth reported at boot time for each device (also named - * cmds/lun) and Q > 2, whenever there is already an active command to the - * device all other commands to the same device (up to Q-1) are kept waiting - * in the elevator sorting queue. When the active command completes, the - * commands in this queue are sorted by sector address. The sort is chosen - * between increasing or decreasing by minimizing the seek distance between - * the sector of the commands just completed and the sector of the first - * command in the list to be sorted. - * Trivial math assures that the unsorted average seek distance when doing - * random seeks over S sectors is S/3. - * When (Q-1) requests are uniformly distributed over S sectors, the average - * distance between two adjacent requests is S/((Q-1) + 1), so the sorted - * average seek distance for (Q-1) random requests over S sectors is S/Q. - * The elevator sorting hence divides the seek distance by a factor Q/3. - * The above pure geometric remarks are valid in all cases and the - * driver effectively reduces the seek distance by the predicted factor - * when there are Q concurrent read i/o operations on the device, but this - * does not necessarily results in a noticeable performance improvement: - * your mileage may vary.... - * - * Note: command reordering inside a batch of queued commands could cause - * wrong results only if there is at least one write request and the - * intersection (sector-wise) of all requests is not empty. - * When the driver detects a batch including overlapping requests - * (a really rare event) strict serial (pid) order is enforced. - * ---------------------------------------------------------------------------- - * - * The boards are named Ux4F0, Ux4F1,... according to the detection order. - * - * In order to support multiple ISA boards in a reliable way, - * the driver sets host->wish_block = TRUE for all ISA boards. - */ - -#include <linux/string.h> -#include <linux/kernel.h> -#include <linux/ioport.h> -#include <linux/delay.h> -#include <asm/io.h> -#include <asm/byteorder.h> -#include <linux/proc_fs.h> -#include <linux/blkdev.h> -#include <linux/interrupt.h> -#include <linux/stat.h> -#include <linux/pci.h> -#include <linux/init.h> -#include <linux/ctype.h> -#include <linux/spinlock.h> -#include <linux/slab.h> -#include <asm/dma.h> -#include <asm/irq.h> - -#include <scsi/scsi.h> -#include <scsi/scsi_cmnd.h> -#include <scsi/scsi_device.h> -#include <scsi/scsi_host.h> -#include <scsi/scsi_tcq.h> -#include <scsi/scsicam.h> - -static int u14_34f_detect(struct scsi_host_template *); -static int u14_34f_release(struct Scsi_Host *); -static int u14_34f_queuecommand(struct Scsi_Host *, struct scsi_cmnd *); -static int u14_34f_eh_abort(struct scsi_cmnd *); -static int u14_34f_eh_host_reset(struct scsi_cmnd *); -static int u14_34f_bios_param(struct scsi_device *, struct block_device *, - sector_t, int *); -static int u14_34f_slave_configure(struct scsi_device *); - -static struct scsi_host_template driver_template = { - .name = "UltraStor 14F/34F rev. 8.10.00 ", - .detect = u14_34f_detect, - .release = u14_34f_release, - .queuecommand = u14_34f_queuecommand, - .eh_abort_handler = u14_34f_eh_abort, - .eh_host_reset_handler = u14_34f_eh_host_reset, - .bios_param = u14_34f_bios_param, - .slave_configure = u14_34f_slave_configure, - .this_id = 7, - .unchecked_isa_dma = 1, - .use_clustering = ENABLE_CLUSTERING, - }; - -#if !defined(__BIG_ENDIAN_BITFIELD) && !defined(__LITTLE_ENDIAN_BITFIELD) -#error "Adjust your <asm/byteorder.h> defines" -#endif - -/* Values for the PRODUCT_ID ports for the 14/34F */ -#define PRODUCT_ID1 0x56 -#define PRODUCT_ID2 0x40 /* NOTE: Only upper nibble is used */ - -/* Subversion values */ -#define ISA 0 -#define ESA 1 - -#define OP_HOST_ADAPTER 0x1 -#define OP_SCSI 0x2 -#define OP_RESET 0x4 -#define DTD_SCSI 0x0 -#define DTD_IN 0x1 -#define DTD_OUT 0x2 -#define DTD_NONE 0x3 -#define HA_CMD_INQUIRY 0x1 -#define HA_CMD_SELF_DIAG 0x2 -#define HA_CMD_READ_BUFF 0x3 -#define HA_CMD_WRITE_BUFF 0x4 - -#undef DEBUG_LINKED_COMMANDS -#undef DEBUG_DETECT -#undef DEBUG_INTERRUPT -#undef DEBUG_RESET -#undef DEBUG_GENERATE_ERRORS -#undef DEBUG_GENERATE_ABORTS -#undef DEBUG_GEOMETRY - -#define MAX_ISA 3 -#define MAX_VESA 1 -#define MAX_EISA 0 -#define MAX_PCI 0 -#define MAX_BOARDS (MAX_ISA + MAX_VESA + MAX_EISA + MAX_PCI) -#define MAX_CHANNEL 1 -#define MAX_LUN 8 -#define MAX_TARGET 8 -#define MAX_MAILBOXES 16 -#define MAX_SGLIST 32 -#define MAX_SAFE_SGLIST 16 -#define MAX_INTERNAL_RETRIES 64 -#define MAX_CMD_PER_LUN 2 -#define MAX_TAGGED_CMD_PER_LUN (MAX_MAILBOXES - MAX_CMD_PER_LUN) - -#define SKIP ULONG_MAX -#define FALSE 0 -#define TRUE 1 -#define FREE 0 -#define IN_USE 1 -#define LOCKED 2 -#define IN_RESET 3 -#define IGNORE 4 -#define READY 5 -#define ABORTING 6 -#define NO_DMA 0xff -#define MAXLOOP 10000 -#define TAG_DISABLED 0 -#define TAG_SIMPLE 1 -#define TAG_ORDERED 2 - -#define REG_LCL_MASK 0 -#define REG_LCL_INTR 1 -#define REG_SYS_MASK 2 -#define REG_SYS_INTR 3 -#define REG_PRODUCT_ID1 4 -#define REG_PRODUCT_ID2 5 -#define REG_CONFIG1 6 -#define REG_CONFIG2 7 -#define REG_OGM 8 -#define REG_ICM 12 -#define REGION_SIZE 13UL -#define BSY_ASSERTED 0x01 -#define IRQ_ASSERTED 0x01 -#define CMD_RESET 0xc0 -#define CMD_OGM_INTR 0x01 -#define CMD_CLR_INTR 0x01 -#define CMD_ENA_INTR 0x81 -#define ASOK 0x00 -#define ASST 0x91 - -#define YESNO(a) ((a) ? 'y' : 'n') -#define TLDEV(type) ((type) == TYPE_DISK || (type) == TYPE_ROM) - -#define PACKED __attribute__((packed)) - -struct sg_list { - unsigned int address; /* Segment Address */ - unsigned int num_bytes; /* Segment Length */ - }; - -/* MailBox SCSI Command Packet */ -struct mscp { - -#if defined(__BIG_ENDIAN_BITFIELD) - unsigned char sg:1, ca:1, dcn:1, xdir:2, opcode:3; - unsigned char lun: 3, channel:2, target:3; -#else - unsigned char opcode: 3, /* type of command */ - xdir: 2, /* data transfer direction */ - dcn: 1, /* disable disconnect */ - ca: 1, /* use cache (if available) */ - sg: 1; /* scatter/gather operation */ - unsigned char target: 3, /* SCSI target id */ - channel: 2, /* SCSI channel number */ - lun: 3; /* SCSI logical unit number */ -#endif - - unsigned int data_address PACKED; /* transfer data pointer */ - unsigned int data_len PACKED; /* length in bytes */ - unsigned int link_address PACKED; /* for linking command chains */ - unsigned char clink_id; /* identifies command in chain */ - unsigned char use_sg; /* (if sg is set) 8 bytes per list */ - unsigned char sense_len; - unsigned char cdb_len; /* 6, 10, or 12 */ - unsigned char cdb[12]; /* SCSI Command Descriptor Block */ - unsigned char adapter_status; /* non-zero indicates HA error */ - unsigned char target_status; /* non-zero indicates target error */ - unsigned int sense_addr PACKED; - - /* Additional fields begin here. */ - struct scsi_cmnd *SCpnt; - unsigned int cpp_index; /* cp index */ - - /* All the cp structure is zero filled by queuecommand except the - following CP_TAIL_SIZE bytes, initialized by detect */ - dma_addr_t cp_dma_addr; /* dma handle for this cp structure */ - struct sg_list *sglist; /* pointer to the allocated SG list */ - }; - -#define CP_TAIL_SIZE (sizeof(struct sglist *) + sizeof(dma_addr_t)) - -struct hostdata { - struct mscp cp[MAX_MAILBOXES]; /* Mailboxes for this board */ - unsigned int cp_stat[MAX_MAILBOXES]; /* FREE, IN_USE, LOCKED, IN_RESET */ - unsigned int last_cp_used; /* Index of last mailbox used */ - unsigned int iocount; /* Total i/o done for this board */ - int board_number; /* Number of this board */ - char board_name[16]; /* Name of this board */ - int in_reset; /* True if board is doing a reset */ - int target_to[MAX_TARGET][MAX_CHANNEL]; /* N. of timeout errors on target */ - int target_redo[MAX_TARGET][MAX_CHANNEL]; /* If TRUE redo i/o on target */ - unsigned int retries; /* Number of internal retries */ - unsigned long last_retried_pid; /* Pid of last retried command */ - unsigned char subversion; /* Bus type, either ISA or ESA */ - struct pci_dev *pdev; /* Always NULL */ - unsigned char heads; - unsigned char sectors; - char board_id[256]; /* data from INQUIRY on this board */ - }; - -static struct Scsi_Host *sh[MAX_BOARDS + 1]; -static const char *driver_name = "Ux4F"; -static char sha[MAX_BOARDS]; -static DEFINE_SPINLOCK(driver_lock); - -/* Initialize num_boards so that ihdlr can work while detect is in progress */ -static unsigned int num_boards = MAX_BOARDS; - -static unsigned long io_port[] = { - - /* Space for MAX_INT_PARAM ports usable while loading as a module */ - SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, - SKIP, SKIP, - - /* Possible ISA/VESA ports */ - 0x330, 0x340, 0x230, 0x240, 0x210, 0x130, 0x140, - - /* End of list */ - 0x0 - }; - -#define HD(board) ((struct hostdata *) &sh[board]->hostdata) -#define BN(board) (HD(board)->board_name) - -/* Device is Little Endian */ -#define H2DEV(x) cpu_to_le32(x) -#define DEV2H(x) le32_to_cpu(x) - -static irqreturn_t do_interrupt_handler(int, void *); -static void flush_dev(struct scsi_device *, unsigned long, unsigned int, unsigned int); -static int do_trace = FALSE; -static int setup_done = FALSE; -static int link_statistics; -static int ext_tran = FALSE; - -#if defined(HAVE_OLD_UX4F_FIRMWARE) -static int have_old_firmware = TRUE; -#else -static int have_old_firmware = FALSE; -#endif - -#if defined(CONFIG_SCSI_U14_34F_TAGGED_QUEUE) -static int tag_mode = TAG_SIMPLE; -#else -static int tag_mode = TAG_DISABLED; -#endif - -#if defined(CONFIG_SCSI_U14_34F_LINKED_COMMANDS) -static int linked_comm = TRUE; -#else -static int linked_comm = FALSE; -#endif - -#if defined(CONFIG_SCSI_U14_34F_MAX_TAGS) -static int max_queue_depth = CONFIG_SCSI_U14_34F_MAX_TAGS; -#else -static int max_queue_depth = MAX_CMD_PER_LUN; -#endif - -#define MAX_INT_PARAM 10 -#define MAX_BOOT_OPTIONS_SIZE 256 -static char boot_options[MAX_BOOT_OPTIONS_SIZE]; - -#if defined(MODULE) -#include <linux/module.h> -#include <linux/moduleparam.h> - -module_param_string(u14_34f, boot_options, MAX_BOOT_OPTIONS_SIZE, 0); -MODULE_PARM_DESC(u14_34f, " equivalent to the \"u14-34f=...\" kernel boot " \ -"option." \ -" Example: modprobe u14-34f \"u14_34f=0x340,0x330,lc:y,tm:0,mq:4\""); -MODULE_AUTHOR("Dario Ballabio"); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("UltraStor 14F/34F SCSI Driver"); - -#endif - -static int u14_34f_slave_configure(struct scsi_device *dev) { - int j, tqd, utqd; - char *tag_suffix, *link_suffix; - struct Scsi_Host *host = dev->host; - - j = ((struct hostdata *) host->hostdata)->board_number; - - utqd = MAX_CMD_PER_LUN; - tqd = max_queue_depth; - - if (TLDEV(dev->type) && dev->tagged_supported) - - if (tag_mode == TAG_SIMPLE) { - scsi_change_queue_depth(dev, tqd); - tag_suffix = ", simple tags"; - } - else if (tag_mode == TAG_ORDERED) { - scsi_change_queue_depth(dev, tqd); - tag_suffix = ", ordered tags"; - } - else { - scsi_change_queue_depth(dev, tqd); - tag_suffix = ", no tags"; - } - - else if (TLDEV(dev->type) && linked_comm) { - scsi_change_queue_depth(dev, tqd); - tag_suffix = ", untagged"; - } - - else { - scsi_change_queue_depth(dev, utqd); - tag_suffix = ""; - } - - if (TLDEV(dev->type) && linked_comm && dev->queue_depth > 2) - link_suffix = ", sorted"; - else if (TLDEV(dev->type)) - link_suffix = ", unsorted"; - else - link_suffix = ""; - - sdev_printk(KERN_INFO, dev, "cmds/lun %d%s%s.\n", - dev->queue_depth, link_suffix, tag_suffix); - - return FALSE; -} - -static int wait_on_busy(unsigned long iobase, unsigned int loop) { - - while (inb(iobase + REG_LCL_INTR) & BSY_ASSERTED) { - udelay(1L); - if (--loop == 0) return TRUE; - } - - return FALSE; -} - -static int board_inquiry(unsigned int j) { - struct mscp *cpp; - dma_addr_t id_dma_addr; - unsigned int limit = 0; - unsigned long time; - - id_dma_addr = pci_map_single(HD(j)->pdev, HD(j)->board_id, - sizeof(HD(j)->board_id), PCI_DMA_BIDIRECTIONAL); - cpp = &HD(j)->cp[0]; - cpp->cp_dma_addr = pci_map_single(HD(j)->pdev, cpp, sizeof(struct mscp), - PCI_DMA_BIDIRECTIONAL); - memset(cpp, 0, sizeof(struct mscp) - CP_TAIL_SIZE); - cpp->opcode = OP_HOST_ADAPTER; - cpp->xdir = DTD_IN; - cpp->data_address = H2DEV(id_dma_addr); - cpp->data_len = H2DEV(sizeof(HD(j)->board_id)); - cpp->cdb_len = 6; - cpp->cdb[0] = HA_CMD_INQUIRY; - - if (wait_on_busy(sh[j]->io_port, MAXLOOP)) { - printk("%s: board_inquiry, adapter busy.\n", BN(j)); - return TRUE; - } - - HD(j)->cp_stat[0] = IGNORE; - - /* Clear the interrupt indication */ - outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR); - - /* Store pointer in OGM address bytes */ - outl(H2DEV(cpp->cp_dma_addr), sh[j]->io_port + REG_OGM); - - /* Issue OGM interrupt */ - outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR); - - spin_unlock_irq(&driver_lock); - time = jiffies; - while ((jiffies - time) < HZ && limit++ < 20000) udelay(100L); - spin_lock_irq(&driver_lock); - - if (cpp->adapter_status || HD(j)->cp_stat[0] != FREE) { - HD(j)->cp_stat[0] = FREE; - printk("%s: board_inquiry, err 0x%x.\n", BN(j), cpp->adapter_status); - return TRUE; - } - - pci_unmap_single(HD(j)->pdev, cpp->cp_dma_addr, sizeof(struct mscp), - PCI_DMA_BIDIRECTIONAL); - pci_unmap_single(HD(j)->pdev, id_dma_addr, sizeof(HD(j)->board_id), - PCI_DMA_BIDIRECTIONAL); - return FALSE; -} - -static int port_detect \ - (unsigned long port_base, unsigned int j, struct scsi_host_template *tpnt) { - unsigned char irq, dma_channel, subversion, i; - unsigned char in_byte; - char *bus_type, dma_name[16]; - - /* Allowed BIOS base addresses (NULL indicates reserved) */ - unsigned long bios_segment_table[8] = { - 0, - 0xc4000, 0xc8000, 0xcc000, 0xd0000, - 0xd4000, 0xd8000, 0xdc000 - }; - - /* Allowed IRQs */ - unsigned char interrupt_table[4] = { 15, 14, 11, 10 }; - - /* Allowed DMA channels for ISA (0 indicates reserved) */ - unsigned char dma_channel_table[4] = { 5, 6, 7, 0 }; - - /* Head/sector mappings */ - struct { - unsigned char heads; - unsigned char sectors; - } mapping_table[4] = { - { 16, 63 }, { 64, 32 }, { 64, 63 }, { 64, 32 } - }; - - struct config_1 { - -#if defined(__BIG_ENDIAN_BITFIELD) - unsigned char dma_channel: 2, interrupt:2, - removable_disks_as_fixed:1, bios_segment: 3; -#else - unsigned char bios_segment: 3, removable_disks_as_fixed: 1, - interrupt: 2, dma_channel: 2; -#endif - - } config_1; - - struct config_2 { - -#if defined(__BIG_ENDIAN_BITFIELD) - unsigned char tfr_port: 2, bios_drive_number: 1, - mapping_mode: 2, ha_scsi_id: 3; -#else - unsigned char ha_scsi_id: 3, mapping_mode: 2, - bios_drive_number: 1, tfr_port: 2; -#endif - - } config_2; - - char name[16]; - - sprintf(name, "%s%d", driver_name, j); - - if (!request_region(port_base, REGION_SIZE, driver_name)) { -#if defined(DEBUG_DETECT) - printk("%s: address 0x%03lx in use, skipping probe.\n", name, port_base); -#endif - goto fail; - } - - spin_lock_irq(&driver_lock); - - if (inb(port_base + REG_PRODUCT_ID1) != PRODUCT_ID1) goto freelock; - - in_byte = inb(port_base + REG_PRODUCT_ID2); - - if ((in_byte & 0xf0) != PRODUCT_ID2) goto freelock; - - *(char *)&config_1 = inb(port_base + REG_CONFIG1); - *(char *)&config_2 = inb(port_base + REG_CONFIG2); - - irq = interrupt_table[config_1.interrupt]; - dma_channel = dma_channel_table[config_1.dma_channel]; - subversion = (in_byte & 0x0f); - - /* Board detected, allocate its IRQ */ - if (request_irq(irq, do_interrupt_handler, - (subversion == ESA) ? IRQF_SHARED : 0, - driver_name, (void *) &sha[j])) { - printk("%s: unable to allocate IRQ %u, detaching.\n", name, irq); - goto freelock; - } - - if (subversion == ISA && request_dma(dma_channel, driver_name)) { - printk("%s: unable to allocate DMA channel %u, detaching.\n", - name, dma_channel); - goto freeirq; - } - - if (have_old_firmware) tpnt->use_clustering = DISABLE_CLUSTERING; - - spin_unlock_irq(&driver_lock); - sh[j] = scsi_register(tpnt, sizeof(struct hostdata)); - spin_lock_irq(&driver_lock); - - if (sh[j] == NULL) { - printk("%s: unable to register host, detaching.\n", name); - goto freedma; - } - - sh[j]->io_port = port_base; - sh[j]->unique_id = port_base; - sh[j]->n_io_port = REGION_SIZE; - sh[j]->base = bios_segment_table[config_1.bios_segment]; - sh[j]->irq = irq; - sh[j]->sg_tablesize = MAX_SGLIST; - sh[j]->this_id = config_2.ha_scsi_id; - sh[j]->can_queue = MAX_MAILBOXES; - sh[j]->cmd_per_lun = MAX_CMD_PER_LUN; - -#if defined(DEBUG_DETECT) - { - unsigned char sys_mask, lcl_mask; - - sys_mask = inb(sh[j]->io_port + REG_SYS_MASK); - lcl_mask = inb(sh[j]->io_port + REG_LCL_MASK); - printk("SYS_MASK 0x%x, LCL_MASK 0x%x.\n", sys_mask, lcl_mask); - } -#endif - - /* Probably a bogus host scsi id, set it to the dummy value */ - if (sh[j]->this_id == 0) sh[j]->this_id = -1; - - /* If BIOS is disabled, force enable interrupts */ - if (sh[j]->base == 0) outb(CMD_ENA_INTR, sh[j]->io_port + REG_SYS_MASK); - - memset(HD(j), 0, sizeof(struct hostdata)); - HD(j)->heads = mapping_table[config_2.mapping_mode].heads; - HD(j)->sectors = mapping_table[config_2.mapping_mode].sectors; - HD(j)->subversion = subversion; - HD(j)->pdev = NULL; - HD(j)->board_number = j; - - if (have_old_firmware) sh[j]->sg_tablesize = MAX_SAFE_SGLIST; - - if (HD(j)->subversion == ESA) { - sh[j]->unchecked_isa_dma = FALSE; - sh[j]->dma_channel = NO_DMA; - sprintf(BN(j), "U34F%d", j); - bus_type = "VESA"; - } - else { - unsigned long flags; - sh[j]->unchecked_isa_dma = TRUE; - - flags=claim_dma_lock(); - disable_dma(dma_channel); - clear_dma_ff(dma_channel); - set_dma_mode(dma_channel, DMA_MODE_CASCADE); - enable_dma(dma_channel); - release_dma_lock(flags); - - sh[j]->dma_channel = dma_channel; - sprintf(BN(j), "U14F%d", j); - bus_type = "ISA"; - } - - sh[j]->max_channel = MAX_CHANNEL - 1; - sh[j]->max_id = MAX_TARGET; - sh[j]->max_lun = MAX_LUN; - - if (HD(j)->subversion == ISA && !board_inquiry(j)) { - HD(j)->board_id[40] = 0; - - if (strcmp(&HD(j)->board_id[32], "06000600")) { - printk("%s: %s.\n", BN(j), &HD(j)->board_id[8]); - printk("%s: firmware %s is outdated, FW PROM should be 28004-006.\n", - BN(j), &HD(j)->board_id[32]); - sh[j]->hostt->use_clustering = DISABLE_CLUSTERING; - sh[j]->sg_tablesize = MAX_SAFE_SGLIST; - } - } - - if (dma_channel == NO_DMA) sprintf(dma_name, "%s", "BMST"); - else sprintf(dma_name, "DMA %u", dma_channel); - - spin_unlock_irq(&driver_lock); - - for (i = 0; i < sh[j]->can_queue; i++) - HD(j)->cp[i].cp_dma_addr = pci_map_single(HD(j)->pdev, - &HD(j)->cp[i], sizeof(struct mscp), PCI_DMA_BIDIRECTIONAL); - - for (i = 0; i < sh[j]->can_queue; i++) - if (! ((&HD(j)->cp[i])->sglist = kmalloc( - sh[j]->sg_tablesize * sizeof(struct sg_list), - (sh[j]->unchecked_isa_dma ? GFP_DMA : 0) | GFP_ATOMIC))) { - printk("%s: kmalloc SGlist failed, mbox %d, detaching.\n", BN(j), i); - goto release; - } - - if (max_queue_depth > MAX_TAGGED_CMD_PER_LUN) - max_queue_depth = MAX_TAGGED_CMD_PER_LUN; - - if (max_queue_depth < MAX_CMD_PER_LUN) max_queue_depth = MAX_CMD_PER_LUN; - - if (tag_mode != TAG_DISABLED && tag_mode != TAG_SIMPLE) - tag_mode = TAG_ORDERED; - - if (j == 0) { - printk("UltraStor 14F/34F: Copyright (C) 1994-2003 Dario Ballabio.\n"); - printk("%s config options -> of:%c, tm:%d, lc:%c, mq:%d, et:%c.\n", - driver_name, YESNO(have_old_firmware), tag_mode, - YESNO(linked_comm), max_queue_depth, YESNO(ext_tran)); - } - - printk("%s: %s 0x%03lx, BIOS 0x%05x, IRQ %u, %s, SG %d, MB %d.\n", - BN(j), bus_type, (unsigned long)sh[j]->io_port, (int)sh[j]->base, - sh[j]->irq, dma_name, sh[j]->sg_tablesize, sh[j]->can_queue); - - if (sh[j]->max_id > 8 || sh[j]->max_lun > 8) - printk("%s: wide SCSI support enabled, max_id %u, max_lun %llu.\n", - BN(j), sh[j]->max_id, sh[j]->max_lun); - - for (i = 0; i <= sh[j]->max_channel; i++) - printk("%s: SCSI channel %u enabled, host target ID %d.\n", - BN(j), i, sh[j]->this_id); - - return TRUE; - -freedma: - if (subversion == ISA) free_dma(dma_channel); -freeirq: - free_irq(irq, &sha[j]); -freelock: - spin_unlock_irq(&driver_lock); - release_region(port_base, REGION_SIZE); -fail: - return FALSE; - -release: - u14_34f_release(sh[j]); - return FALSE; -} - -static void internal_setup(char *str, int *ints) { - int i, argc = ints[0]; - char *cur = str, *pc; - - if (argc > 0) { - - if (argc > MAX_INT_PARAM) argc = MAX_INT_PARAM; - - for (i = 0; i < argc; i++) io_port[i] = ints[i + 1]; - - io_port[i] = 0; - setup_done = TRUE; - } - - while (cur && (pc = strchr(cur, ':'))) { - int val = 0, c = *++pc; - - if (c == 'n' || c == 'N') val = FALSE; - else if (c == 'y' || c == 'Y') val = TRUE; - else val = (int) simple_strtoul(pc, NULL, 0); - - if (!strncmp(cur, "lc:", 3)) linked_comm = val; - else if (!strncmp(cur, "of:", 3)) have_old_firmware = val; - else if (!strncmp(cur, "tm:", 3)) tag_mode = val; - else if (!strncmp(cur, "tc:", 3)) tag_mode = val; - else if (!strncmp(cur, "mq:", 3)) max_queue_depth = val; - else if (!strncmp(cur, "ls:", 3)) link_statistics = val; - else if (!strncmp(cur, "et:", 3)) ext_tran = val; - - if ((cur = strchr(cur, ','))) ++cur; - } - - return; -} - -static int option_setup(char *str) { - int ints[MAX_INT_PARAM]; - char *cur = str; - int i = 1; - - while (cur && isdigit(*cur) && i < MAX_INT_PARAM) { - ints[i++] = simple_strtoul(cur, NULL, 0); - - if ((cur = strchr(cur, ',')) != NULL) cur++; - } - - ints[0] = i - 1; - internal_setup(cur, ints); - return 1; -} - -static int u14_34f_detect(struct scsi_host_template *tpnt) { - unsigned int j = 0, k; - - tpnt->proc_name = "u14-34f"; - - if(strlen(boot_options)) option_setup(boot_options); - -#if defined(MODULE) - /* io_port could have been modified when loading as a module */ - if(io_port[0] != SKIP) { - setup_done = TRUE; - io_port[MAX_INT_PARAM] = 0; - } -#endif - - for (k = 0; k < MAX_BOARDS + 1; k++) sh[k] = NULL; - - for (k = 0; io_port[k]; k++) { - - if (io_port[k] == SKIP) continue; - - if (j < MAX_BOARDS && port_detect(io_port[k], j, tpnt)) j++; - } - - num_boards = j; - return j; -} - -static void map_dma(unsigned int i, unsigned int j) { - unsigned int data_len = 0; - unsigned int k, pci_dir; - int count; - struct scatterlist *sg; - struct mscp *cpp; - struct scsi_cmnd *SCpnt; - - cpp = &HD(j)->cp[i]; SCpnt = cpp->SCpnt; - pci_dir = SCpnt->sc_data_direction; - - if (SCpnt->sense_buffer) - cpp->sense_addr = H2DEV(pci_map_single(HD(j)->pdev, SCpnt->sense_buffer, - SCSI_SENSE_BUFFERSIZE, PCI_DMA_FROMDEVICE)); - - cpp->sense_len = SCSI_SENSE_BUFFERSIZE; - - if (scsi_bufflen(SCpnt)) { - count = scsi_dma_map(SCpnt); - BUG_ON(count < 0); - - scsi_for_each_sg(SCpnt, sg, count, k) { - cpp->sglist[k].address = H2DEV(sg_dma_address(sg)); - cpp->sglist[k].num_bytes = H2DEV(sg_dma_len(sg)); - data_len += sg->length; - } - - cpp->sg = TRUE; - cpp->use_sg = scsi_sg_count(SCpnt); - cpp->data_address = - H2DEV(pci_map_single(HD(j)->pdev, cpp->sglist, - cpp->use_sg * sizeof(struct sg_list), - pci_dir)); - cpp->data_len = H2DEV(data_len); - - } else { - pci_dir = PCI_DMA_BIDIRECTIONAL; - cpp->data_len = H2DEV(scsi_bufflen(SCpnt)); - } -} - -static void unmap_dma(unsigned int i, unsigned int j) { - unsigned int pci_dir; - struct mscp *cpp; - struct scsi_cmnd *SCpnt; - - cpp = &HD(j)->cp[i]; SCpnt = cpp->SCpnt; - pci_dir = SCpnt->sc_data_direction; - - if (DEV2H(cpp->sense_addr)) - pci_unmap_single(HD(j)->pdev, DEV2H(cpp->sense_addr), - DEV2H(cpp->sense_len), PCI_DMA_FROMDEVICE); - - scsi_dma_unmap(SCpnt); - - if (!DEV2H(cpp->data_len)) pci_dir = PCI_DMA_BIDIRECTIONAL; - - if (DEV2H(cpp->data_address)) - pci_unmap_single(HD(j)->pdev, DEV2H(cpp->data_address), - DEV2H(cpp->data_len), pci_dir); -} - -static void sync_dma(unsigned int i, unsigned int j) { - unsigned int pci_dir; - struct mscp *cpp; - struct scsi_cmnd *SCpnt; - - cpp = &HD(j)->cp[i]; SCpnt = cpp->SCpnt; - pci_dir = SCpnt->sc_data_direction; - - if (DEV2H(cpp->sense_addr)) - pci_dma_sync_single_for_cpu(HD(j)->pdev, DEV2H(cpp->sense_addr), - DEV2H(cpp->sense_len), PCI_DMA_FROMDEVICE); - - if (scsi_sg_count(SCpnt)) - pci_dma_sync_sg_for_cpu(HD(j)->pdev, scsi_sglist(SCpnt), - scsi_sg_count(SCpnt), pci_dir); - - if (!DEV2H(cpp->data_len)) pci_dir = PCI_DMA_BIDIRECTIONAL; - - if (DEV2H(cpp->data_address)) - pci_dma_sync_single_for_cpu(HD(j)->pdev, DEV2H(cpp->data_address), - DEV2H(cpp->data_len), pci_dir); -} - -static void scsi_to_dev_dir(unsigned int i, unsigned int j) { - unsigned int k; - - static const unsigned char data_out_cmds[] = { - 0x0a, 0x2a, 0x15, 0x55, 0x04, 0x07, 0x18, 0x1d, 0x24, 0x2e, - 0x30, 0x31, 0x32, 0x38, 0x39, 0x3a, 0x3b, 0x3d, 0x3f, 0x40, - 0x41, 0x4c, 0xaa, 0xae, 0xb0, 0xb1, 0xb2, 0xb6, 0xea, 0x1b, 0x5d - }; - - static const unsigned char data_none_cmds[] = { - 0x01, 0x0b, 0x10, 0x11, 0x13, 0x16, 0x17, 0x19, 0x2b, 0x1e, - 0x2c, 0xac, 0x2f, 0xaf, 0x33, 0xb3, 0x35, 0x36, 0x45, 0x47, - 0x48, 0x49, 0xa9, 0x4b, 0xa5, 0xa6, 0xb5, 0x00 - }; - - struct mscp *cpp; - struct scsi_cmnd *SCpnt; - - cpp = &HD(j)->cp[i]; SCpnt = cpp->SCpnt; - - if (SCpnt->sc_data_direction == DMA_FROM_DEVICE) { - cpp->xdir = DTD_IN; - return; - } - else if (SCpnt->sc_data_direction == DMA_TO_DEVICE) { - cpp->xdir = DTD_OUT; - return; - } - else if (SCpnt->sc_data_direction == DMA_NONE) { - cpp->xdir = DTD_NONE; - return; - } - - if (SCpnt->sc_data_direction != DMA_BIDIRECTIONAL) - panic("%s: qcomm, invalid SCpnt->sc_data_direction.\n", BN(j)); - - cpp->xdir = DTD_IN; - - for (k = 0; k < ARRAY_SIZE(data_out_cmds); k++) - if (SCpnt->cmnd[0] == data_out_cmds[k]) { - cpp->xdir = DTD_OUT; - break; - } - - if (cpp->xdir == DTD_IN) - for (k = 0; k < ARRAY_SIZE(data_none_cmds); k++) - if (SCpnt->cmnd[0] == data_none_cmds[k]) { - cpp->xdir = DTD_NONE; - break; - } - -} - -static int u14_34f_queuecommand_lck(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) { - unsigned int i, j, k; - struct mscp *cpp; - - /* j is the board number */ - j = ((struct hostdata *) SCpnt->device->host->hostdata)->board_number; - - if (SCpnt->host_scribble) - panic("%s: qcomm, SCpnt %p already active.\n", - BN(j), SCpnt); - - /* i is the mailbox number, look for the first free mailbox - starting from last_cp_used */ - i = HD(j)->last_cp_used + 1; - - for (k = 0; k < sh[j]->can_queue; k++, i++) { - - if (i >= sh[j]->can_queue) i = 0; - - if (HD(j)->cp_stat[i] == FREE) { - HD(j)->last_cp_used = i; - break; - } - } - - if (k == sh[j]->can_queue) { - printk("%s: qcomm, no free mailbox.\n", BN(j)); - return 1; - } - - /* Set pointer to control packet structure */ - cpp = &HD(j)->cp[i]; - - memset(cpp, 0, sizeof(struct mscp) - CP_TAIL_SIZE); - SCpnt->scsi_done = done; - cpp->cpp_index = i; - SCpnt->host_scribble = (unsigned char *) &cpp->cpp_index; - - if (do_trace) printk("%s: qcomm, mbox %d, target %d.%d:%u.\n", - BN(j), i, SCpnt->device->channel, SCpnt->device->id, - (u8)SCpnt->device->lun); - - cpp->opcode = OP_SCSI; - cpp->channel = SCpnt->device->channel; - cpp->target = SCpnt->device->id; - cpp->lun = (u8)SCpnt->device->lun; - cpp->SCpnt = SCpnt; - cpp->cdb_len = SCpnt->cmd_len; - memcpy(cpp->cdb, SCpnt->cmnd, SCpnt->cmd_len); - - /* Use data transfer direction SCpnt->sc_data_direction */ - scsi_to_dev_dir(i, j); - - /* Map DMA buffers and SG list */ - map_dma(i, j); - - if (linked_comm && SCpnt->device->queue_depth > 2 - && TLDEV(SCpnt->device->type)) { - HD(j)->cp_stat[i] = READY; - flush_dev(SCpnt->device, blk_rq_pos(SCpnt->request), j, FALSE); - return 0; - } - - if (wait_on_busy(sh[j]->io_port, MAXLOOP)) { - unmap_dma(i, j); - SCpnt->host_scribble = NULL; - scmd_printk(KERN_INFO, SCpnt, - "qcomm, adapter busy.\n"); - return 1; - } - - /* Store pointer in OGM address bytes */ - outl(H2DEV(cpp->cp_dma_addr), sh[j]->io_port + REG_OGM); - - /* Issue OGM interrupt */ - outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR); - - HD(j)->cp_stat[i] = IN_USE; - return 0; -} - -static DEF_SCSI_QCMD(u14_34f_queuecommand) - -static int u14_34f_eh_abort(struct scsi_cmnd *SCarg) { - unsigned int i, j; - - j = ((struct hostdata *) SCarg->device->host->hostdata)->board_number; - - if (SCarg->host_scribble == NULL) { - scmd_printk(KERN_INFO, SCarg, "abort, command inactive.\n"); - return SUCCESS; - } - - i = *(unsigned int *)SCarg->host_scribble; - scmd_printk(KERN_INFO, SCarg, "abort, mbox %d.\n", i); - - if (i >= sh[j]->can_queue) - panic("%s: abort, invalid SCarg->host_scribble.\n", BN(j)); - - if (wait_on_busy(sh[j]->io_port, MAXLOOP)) { - printk("%s: abort, timeout error.\n", BN(j)); - return FAILED; - } - - if (HD(j)->cp_stat[i] == FREE) { - printk("%s: abort, mbox %d is free.\n", BN(j), i); - return SUCCESS; - } - - if (HD(j)->cp_stat[i] == IN_USE) { - printk("%s: abort, mbox %d is in use.\n", BN(j), i); - - if (SCarg != HD(j)->cp[i].SCpnt) - panic("%s: abort, mbox %d, SCarg %p, cp SCpnt %p.\n", - BN(j), i, SCarg, HD(j)->cp[i].SCpnt); - - if (inb(sh[j]->io_port + REG_SYS_INTR) & IRQ_ASSERTED) - printk("%s: abort, mbox %d, interrupt pending.\n", BN(j), i); - - return FAILED; - } - - if (HD(j)->cp_stat[i] == IN_RESET) { - printk("%s: abort, mbox %d is in reset.\n", BN(j), i); - return FAILED; - } - - if (HD(j)->cp_stat[i] == LOCKED) { - printk("%s: abort, mbox %d is locked.\n", BN(j), i); - return SUCCESS; - } - - if (HD(j)->cp_stat[i] == READY || HD(j)->cp_stat[i] == ABORTING) { - unmap_dma(i, j); - SCarg->result = DID_ABORT << 16; - SCarg->host_scribble = NULL; - HD(j)->cp_stat[i] = FREE; - printk("%s, abort, mbox %d ready, DID_ABORT, done.\n", BN(j), i); - SCarg->scsi_done(SCarg); - return SUCCESS; - } - - panic("%s: abort, mbox %d, invalid cp_stat.\n", BN(j), i); -} - -static int u14_34f_eh_host_reset(struct scsi_cmnd *SCarg) { - unsigned int i, j, k, c, limit = 0; - unsigned long time; - int arg_done = FALSE; - struct scsi_cmnd *SCpnt; - - j = ((struct hostdata *) SCarg->device->host->hostdata)->board_number; - scmd_printk(KERN_INFO, SCarg, "reset, enter.\n"); - - spin_lock_irq(sh[j]->host_lock); - - if (SCarg->host_scribble == NULL) - printk("%s: reset, inactive.\n", BN(j)); - - if (HD(j)->in_reset) { - printk("%s: reset, exit, already in reset.\n", BN(j)); - spin_unlock_irq(sh[j]->host_lock); - return FAILED; - } - - if (wait_on_busy(sh[j]->io_port, MAXLOOP)) { - printk("%s: reset, exit, timeout error.\n", BN(j)); - spin_unlock_irq(sh[j]->host_lock); - return FAILED; - } - - HD(j)->retries = 0; - - for (c = 0; c <= sh[j]->max_channel; c++) - for (k = 0; k < sh[j]->max_id; k++) { - HD(j)->target_redo[k][c] = TRUE; - HD(j)->target_to[k][c] = 0; - } - - for (i = 0; i < sh[j]->can_queue; i++) { - - if (HD(j)->cp_stat[i] == FREE) continue; - - if (HD(j)->cp_stat[i] == LOCKED) { - HD(j)->cp_stat[i] = FREE; - printk("%s: reset, locked mbox %d forced free.\n", BN(j), i); - continue; - } - - if (!(SCpnt = HD(j)->cp[i].SCpnt)) - panic("%s: reset, mbox %d, SCpnt == NULL.\n", BN(j), i); - - if (HD(j)->cp_stat[i] == READY || HD(j)->cp_stat[i] == ABORTING) { - HD(j)->cp_stat[i] = ABORTING; - printk("%s: reset, mbox %d aborting.\n", BN(j), i); - } - - else { - HD(j)->cp_stat[i] = IN_RESET; - printk("%s: reset, mbox %d in reset.\n", BN(j), i); - } - - if (SCpnt->host_scribble == NULL) - panic("%s: reset, mbox %d, garbled SCpnt.\n", BN(j), i); - - if (*(unsigned int *)SCpnt->host_scribble != i) - panic("%s: reset, mbox %d, index mismatch.\n", BN(j), i); - - if (SCpnt->scsi_done == NULL) - panic("%s: reset, mbox %d, SCpnt->scsi_done == NULL.\n", BN(j), i); - - if (SCpnt == SCarg) arg_done = TRUE; - } - - if (wait_on_busy(sh[j]->io_port, MAXLOOP)) { - printk("%s: reset, cannot reset, timeout error.\n", BN(j)); - spin_unlock_irq(sh[j]->host_lock); - return FAILED; - } - - outb(CMD_RESET, sh[j]->io_port + REG_LCL_INTR); - printk("%s: reset, board reset done, enabling interrupts.\n", BN(j)); - -#if defined(DEBUG_RESET) - do_trace = TRUE; -#endif - - HD(j)->in_reset = TRUE; - - spin_unlock_irq(sh[j]->host_lock); - time = jiffies; - while ((jiffies - time) < (10 * HZ) && limit++ < 200000) udelay(100L); - spin_lock_irq(sh[j]->host_lock); - - printk("%s: reset, interrupts disabled, loops %d.\n", BN(j), limit); - - for (i = 0; i < sh[j]->can_queue; i++) { - - if (HD(j)->cp_stat[i] == IN_RESET) { - SCpnt = HD(j)->cp[i].SCpnt; - unmap_dma(i, j); - SCpnt->result = DID_RESET << 16; - SCpnt->host_scribble = NULL; - - /* This mailbox is still waiting for its interrupt */ - HD(j)->cp_stat[i] = LOCKED; - - printk("%s, reset, mbox %d locked, DID_RESET, done.\n", BN(j), i); - } - - else if (HD(j)->cp_stat[i] == ABORTING) { - SCpnt = HD(j)->cp[i].SCpnt; - unmap_dma(i, j); - SCpnt->result = DID_RESET << 16; - SCpnt->host_scribble = NULL; - - /* This mailbox was never queued to the adapter */ - HD(j)->cp_stat[i] = FREE; - - printk("%s, reset, mbox %d aborting, DID_RESET, done.\n", BN(j), i); - } - - else - - /* Any other mailbox has already been set free by interrupt */ - continue; - - SCpnt->scsi_done(SCpnt); - } - - HD(j)->in_reset = FALSE; - do_trace = FALSE; - - if (arg_done) printk("%s: reset, exit, done.\n", BN(j)); - else printk("%s: reset, exit.\n", BN(j)); - - spin_unlock_irq(sh[j]->host_lock); - return SUCCESS; -} - -static int u14_34f_bios_param(struct scsi_device *disk, - struct block_device *bdev, sector_t capacity, int *dkinfo) { - unsigned int j = 0; - unsigned int size = capacity; - - dkinfo[0] = HD(j)->heads; - dkinfo[1] = HD(j)->sectors; - dkinfo[2] = size / (HD(j)->heads * HD(j)->sectors); - - if (ext_tran && (scsicam_bios_param(bdev, capacity, dkinfo) < 0)) { - dkinfo[0] = 255; - dkinfo[1] = 63; - dkinfo[2] = size / (dkinfo[0] * dkinfo[1]); - } - -#if defined (DEBUG_GEOMETRY) - printk ("%s: bios_param, head=%d, sec=%d, cyl=%d.\n", driver_name, - dkinfo[0], dkinfo[1], dkinfo[2]); -#endif - - return FALSE; -} - -static void sort(unsigned long sk[], unsigned int da[], unsigned int n, - unsigned int rev) { - unsigned int i, j, k, y; - unsigned long x; - - for (i = 0; i < n - 1; i++) { - k = i; - - for (j = k + 1; j < n; j++) - if (rev) { - if (sk[j] > sk[k]) k = j; - } - else { - if (sk[j] < sk[k]) k = j; - } - - if (k != i) { - x = sk[k]; sk[k] = sk[i]; sk[i] = x; - y = da[k]; da[k] = da[i]; da[i] = y; - } - } - - return; - } - -static int reorder(unsigned int j, unsigned long cursec, - unsigned int ihdlr, unsigned int il[], unsigned int n_ready) { - struct scsi_cmnd *SCpnt; - struct mscp *cpp; - unsigned int k, n; - unsigned int rev = FALSE, s = TRUE, r = TRUE; - unsigned int input_only = TRUE, overlap = FALSE; - unsigned long sl[n_ready], pl[n_ready], ll[n_ready]; - unsigned long maxsec = 0, minsec = ULONG_MAX, seek = 0, iseek = 0; - unsigned long ioseek = 0; - - static unsigned int flushcount = 0, batchcount = 0, sortcount = 0; - static unsigned int readycount = 0, ovlcount = 0, inputcount = 0; - static unsigned int readysorted = 0, revcount = 0; - static unsigned long seeksorted = 0, seeknosort = 0; - - if (link_statistics && !(++flushcount % link_statistics)) - printk("fc %d bc %d ic %d oc %d rc %d rs %d sc %d re %d"\ - " av %ldK as %ldK.\n", flushcount, batchcount, inputcount, - ovlcount, readycount, readysorted, sortcount, revcount, - seeknosort / (readycount + 1), - seeksorted / (readycount + 1)); - - if (n_ready <= 1) return FALSE; - - for (n = 0; n < n_ready; n++) { - k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt; - - if (!(cpp->xdir == DTD_IN)) input_only = FALSE; - - if (blk_rq_pos(SCpnt->request) < minsec) - minsec = blk_rq_pos(SCpnt->request); - if (blk_rq_pos(SCpnt->request) > maxsec) - maxsec = blk_rq_pos(SCpnt->request); - - sl[n] = blk_rq_pos(SCpnt->request); - ioseek += blk_rq_sectors(SCpnt->request); - - if (!n) continue; - - if (sl[n] < sl[n - 1]) s = FALSE; - if (sl[n] > sl[n - 1]) r = FALSE; - - if (link_statistics) { - if (sl[n] > sl[n - 1]) - seek += sl[n] - sl[n - 1]; - else - seek += sl[n - 1] - sl[n]; - } - - } - - if (link_statistics) { - if (cursec > sl[0]) seek += cursec - sl[0]; else seek += sl[0] - cursec; - } - - if (cursec > ((maxsec + minsec) / 2)) rev = TRUE; - - if (ioseek > ((maxsec - minsec) / 2)) rev = FALSE; - - if (!((rev && r) || (!rev && s))) sort(sl, il, n_ready, rev); - - if (!input_only) for (n = 0; n < n_ready; n++) { - k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt; - ll[n] = blk_rq_sectors(SCpnt->request); pl[n] = SCpnt->serial_number; - - if (!n) continue; - - if ((sl[n] == sl[n - 1]) || (!rev && ((sl[n - 1] + ll[n - 1]) > sl[n])) - || (rev && ((sl[n] + ll[n]) > sl[n - 1]))) overlap = TRUE; - } - - if (overlap) sort(pl, il, n_ready, FALSE); - - if (link_statistics) { - if (cursec > sl[0]) iseek = cursec - sl[0]; else iseek = sl[0] - cursec; - batchcount++; readycount += n_ready; seeknosort += seek / 1024; - if (input_only) inputcount++; - if (overlap) { ovlcount++; seeksorted += iseek / 1024; } - else seeksorted += (iseek + maxsec - minsec) / 1024; - if (rev && !r) { revcount++; readysorted += n_ready; } - if (!rev && !s) { sortcount++; readysorted += n_ready; } - } - -#if defined(DEBUG_LINKED_COMMANDS) - if (link_statistics && (overlap || !(flushcount % link_statistics))) - for (n = 0; n < n_ready; n++) { - k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt; - printk("%s %d.%d:%llu mb %d fc %d nr %d sec %ld ns %u"\ - " cur %ld s:%c r:%c rev:%c in:%c ov:%c xd %d.\n", - (ihdlr ? "ihdlr" : "qcomm"), SCpnt->channel, SCpnt->target, - (u8)SCpnt->lun, k, flushcount, n_ready, - blk_rq_pos(SCpnt->request), blk_rq_sectors(SCpnt->request), - cursec, YESNO(s), YESNO(r), YESNO(rev), YESNO(input_only), - YESNO(overlap), cpp->xdir); - } -#endif - return overlap; -} - -static void flush_dev(struct scsi_device *dev, unsigned long cursec, unsigned int j, - unsigned int ihdlr) { - struct scsi_cmnd *SCpnt; - struct mscp *cpp; - unsigned int k, n, n_ready = 0, il[MAX_MAILBOXES]; - - for (k = 0; k < sh[j]->can_queue; k++) { - - if (HD(j)->cp_stat[k] != READY && HD(j)->cp_stat[k] != IN_USE) continue; - - cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt; - - if (SCpnt->device != dev) continue; - - if (HD(j)->cp_stat[k] == IN_USE) return; - - il[n_ready++] = k; - } - - if (reorder(j, cursec, ihdlr, il, n_ready)) n_ready = 1; - - for (n = 0; n < n_ready; n++) { - k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt; - - if (wait_on_busy(sh[j]->io_port, MAXLOOP)) { - scmd_printk(KERN_INFO, SCpnt, - "%s, mbox %d, adapter" - " busy, will abort.\n", (ihdlr ? "ihdlr" : "qcomm"), - k); - HD(j)->cp_stat[k] = ABORTING; - continue; - } - - outl(H2DEV(cpp->cp_dma_addr), sh[j]->io_port + REG_OGM); - outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR); - HD(j)->cp_stat[k] = IN_USE; - } - -} - -static irqreturn_t ihdlr(unsigned int j) -{ - struct scsi_cmnd *SCpnt; - unsigned int i, k, c, status, tstatus, reg, ret; - struct mscp *spp, *cpp; - int irq = sh[j]->irq; - - /* Check if this board need to be serviced */ - if (!((reg = inb(sh[j]->io_port + REG_SYS_INTR)) & IRQ_ASSERTED)) goto none; - - HD(j)->iocount++; - - if (do_trace) printk("%s: ihdlr, enter, irq %d, count %d.\n", BN(j), irq, - HD(j)->iocount); - - /* Check if this board is still busy */ - if (wait_on_busy(sh[j]->io_port, 20 * MAXLOOP)) { - outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR); - printk("%s: ihdlr, busy timeout error, irq %d, reg 0x%x, count %d.\n", - BN(j), irq, reg, HD(j)->iocount); - goto none; - } - - ret = inl(sh[j]->io_port + REG_ICM); - - /* Clear interrupt pending flag */ - outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR); - - /* Find the mailbox to be serviced on this board */ - for (i = 0; i < sh[j]->can_queue; i++) - if (H2DEV(HD(j)->cp[i].cp_dma_addr) == ret) break; - - if (i >= sh[j]->can_queue) - panic("%s: ihdlr, invalid mscp bus address %p, cp0 %p.\n", BN(j), - (void *)ret, (void *)H2DEV(HD(j)->cp[0].cp_dma_addr)); - - cpp = &(HD(j)->cp[i]); - spp = cpp; - -#if defined(DEBUG_GENERATE_ABORTS) - if ((HD(j)->iocount > 500) && ((HD(j)->iocount % 500) < 3)) goto handled; -#endif - - if (HD(j)->cp_stat[i] == IGNORE) { - HD(j)->cp_stat[i] = FREE; - goto handled; - } - else if (HD(j)->cp_stat[i] == LOCKED) { - HD(j)->cp_stat[i] = FREE; - printk("%s: ihdlr, mbox %d unlocked, count %d.\n", BN(j), i, - HD(j)->iocount); - goto handled; - } - else if (HD(j)->cp_stat[i] == FREE) { - printk("%s: ihdlr, mbox %d is free, count %d.\n", BN(j), i, - HD(j)->iocount); - goto handled; - } - else if (HD(j)->cp_stat[i] == IN_RESET) - printk("%s: ihdlr, mbox %d is in reset.\n", BN(j), i); - else if (HD(j)->cp_stat[i] != IN_USE) - panic("%s: ihdlr, mbox %d, invalid cp_stat: %d.\n", - BN(j), i, HD(j)->cp_stat[i]); - - HD(j)->cp_stat[i] = FREE; - SCpnt = cpp->SCpnt; - - if (SCpnt == NULL) panic("%s: ihdlr, mbox %d, SCpnt == NULL.\n", BN(j), i); - - if (SCpnt->host_scribble == NULL) - panic("%s: ihdlr, mbox %d, SCpnt %p garbled.\n", BN(j), i, - SCpnt); - - if (*(unsigned int *)SCpnt->host_scribble != i) - panic("%s: ihdlr, mbox %d, index mismatch %d.\n", - BN(j), i, *(unsigned int *)SCpnt->host_scribble); - - sync_dma(i, j); - - if (linked_comm && SCpnt->device->queue_depth > 2 - && TLDEV(SCpnt->device->type)) - flush_dev(SCpnt->device, blk_rq_pos(SCpnt->request), j, TRUE); - - tstatus = status_byte(spp->target_status); - -#if defined(DEBUG_GENERATE_ERRORS) - if ((HD(j)->iocount > 500) && ((HD(j)->iocount % 200) < 2)) - spp->adapter_status = 0x01; -#endif - - switch (spp->adapter_status) { - case ASOK: /* status OK */ - - /* Forces a reset if a disk drive keeps returning BUSY */ - if (tstatus == BUSY && SCpnt->device->type != TYPE_TAPE) - status = DID_ERROR << 16; - - /* If there was a bus reset, redo operation on each target */ - else if (tstatus != GOOD && SCpnt->device->type == TYPE_DISK - && HD(j)->target_redo[scmd_id(SCpnt)][scmd_channel(SCpnt)]) - status = DID_BUS_BUSY << 16; - - /* Works around a flaw in scsi.c */ - else if (tstatus == CHECK_CONDITION - && SCpnt->device->type == TYPE_DISK - && (SCpnt->sense_buffer[2] & 0xf) == RECOVERED_ERROR) - status = DID_BUS_BUSY << 16; - - else - status = DID_OK << 16; - - if (tstatus == GOOD) - HD(j)->target_redo[scmd_id(SCpnt)][scmd_channel(SCpnt)] = FALSE; - - if (spp->target_status && SCpnt->device->type == TYPE_DISK && - (!(tstatus == CHECK_CONDITION && HD(j)->iocount <= 1000 && - (SCpnt->sense_buffer[2] & 0xf) == NOT_READY))) - scmd_printk(KERN_INFO, SCpnt, - "ihdlr, target_status 0x%x, sense key 0x%x.\n", - spp->target_status, - SCpnt->sense_buffer[2]); - - HD(j)->target_to[scmd_id(SCpnt)][scmd_channel(SCpnt)] = 0; - - if (HD(j)->last_retried_pid == SCpnt->serial_number) HD(j)->retries = 0; - - break; - case ASST: /* Selection Time Out */ - - if (HD(j)->target_to[scmd_id(SCpnt)][scmd_channel(SCpnt)] > 1) - status = DID_ERROR << 16; - else { - status = DID_TIME_OUT << 16; - HD(j)->target_to[scmd_id(SCpnt)][scmd_channel(SCpnt)]++; - } - - break; - - /* Perform a limited number of internal retries */ - case 0x93: /* Unexpected bus free */ - case 0x94: /* Target bus phase sequence failure */ - case 0x96: /* Illegal SCSI command */ - case 0xa3: /* SCSI bus reset error */ - - for (c = 0; c <= sh[j]->max_channel; c++) - for (k = 0; k < sh[j]->max_id; k++) - HD(j)->target_redo[k][c] = TRUE; - - - case 0x92: /* Data over/under-run */ - - if (SCpnt->device->type != TYPE_TAPE - && HD(j)->retries < MAX_INTERNAL_RETRIES) { - -#if defined(DID_SOFT_ERROR) - status = DID_SOFT_ERROR << 16; -#else - status = DID_BUS_BUSY << 16; -#endif - - HD(j)->retries++; - HD(j)->last_retried_pid = SCpnt->serial_number; - } - else - status = DID_ERROR << 16; - - break; - case 0x01: /* Invalid command */ - case 0x02: /* Invalid parameters */ - case 0x03: /* Invalid data list */ - case 0x84: /* SCSI bus abort error */ - case 0x9b: /* Auto request sense error */ - case 0x9f: /* Unexpected command complete message error */ - case 0xff: /* Invalid parameter in the S/G list */ - default: - status = DID_ERROR << 16; - break; - } - - SCpnt->result = status | spp->target_status; - -#if defined(DEBUG_INTERRUPT) - if (SCpnt->result || do_trace) -#else - if ((spp->adapter_status != ASOK && HD(j)->iocount > 1000) || - (spp->adapter_status != ASOK && - spp->adapter_status != ASST && HD(j)->iocount <= 1000) || - do_trace || msg_byte(spp->target_status)) -#endif - scmd_printk(KERN_INFO, SCpnt, "ihdlr, mbox %2d, err 0x%x:%x,"\ - " reg 0x%x, count %d.\n", - i, spp->adapter_status, spp->target_status, - reg, HD(j)->iocount); - - unmap_dma(i, j); - - /* Set the command state to inactive */ - SCpnt->host_scribble = NULL; - - SCpnt->scsi_done(SCpnt); - - if (do_trace) printk("%s: ihdlr, exit, irq %d, count %d.\n", BN(j), irq, - HD(j)->iocount); - -handled: - return IRQ_HANDLED; -none: - return IRQ_NONE; -} - -static irqreturn_t do_interrupt_handler(int irq, void *shap) { - unsigned int j; - unsigned long spin_flags; - irqreturn_t ret; - - /* Check if the interrupt must be processed by this handler */ - if ((j = (unsigned int)((char *)shap - sha)) >= num_boards) return IRQ_NONE; - - spin_lock_irqsave(sh[j]->host_lock, spin_flags); - ret = ihdlr(j); - spin_unlock_irqrestore(sh[j]->host_lock, spin_flags); - return ret; -} - -static int u14_34f_release(struct Scsi_Host *shpnt) { - unsigned int i, j; - - for (j = 0; sh[j] != NULL && sh[j] != shpnt; j++); - - if (sh[j] == NULL) - panic("%s: release, invalid Scsi_Host pointer.\n", driver_name); - - for (i = 0; i < sh[j]->can_queue; i++) - kfree((&HD(j)->cp[i])->sglist); - - for (i = 0; i < sh[j]->can_queue; i++) - pci_unmap_single(HD(j)->pdev, HD(j)->cp[i].cp_dma_addr, - sizeof(struct mscp), PCI_DMA_BIDIRECTIONAL); - - free_irq(sh[j]->irq, &sha[j]); - - if (sh[j]->dma_channel != NO_DMA) - free_dma(sh[j]->dma_channel); - - release_region(sh[j]->io_port, sh[j]->n_io_port); - scsi_unregister(sh[j]); - return FALSE; -} - -#include "scsi_module.c" - -#ifndef MODULE -__setup("u14-34f=", option_setup); -#endif /* end MODULE */ diff --git a/drivers/scsi/ufs/tc-dwc-g210.c b/drivers/scsi/ufs/tc-dwc-g210.c index 70db6d999ca3..dc03e47f7c58 100644 --- a/drivers/scsi/ufs/tc-dwc-g210.c +++ b/drivers/scsi/ufs/tc-dwc-g210.c @@ -15,6 +15,7 @@ #include "ufshcd-dwc.h" #include "ufshci-dwc.h" +#include "tc-dwc-g210.h" /** * tc_dwc_g210_setup_40bit_rmmi() diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index b291fa6ed2ad..845b874e2977 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -327,6 +327,7 @@ enum { MASK_QUERY_DATA_SEG_LEN = 0xFFFF, MASK_RSP_UPIU_DATA_SEG_LEN = 0xFFFF, MASK_RSP_EXCEPTION_EVENT = 0x10000, + MASK_TM_SERVICE_RESP = 0xFF, }; /* Task management service response */ diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index f08d41a2d70b..37f3c51e9d92 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -2568,7 +2568,7 @@ static int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd) status = ufshcd_get_upmcrs(hba); if (status != PWR_LOCAL) { dev_err(hba->dev, - "pwr ctrl cmd 0x%0x failed, host umpcrs:0x%x\n", + "pwr ctrl cmd 0x%0x failed, host upmcrs:0x%x\n", cmd->command, status); ret = (status != PWR_OK) ? status : -1; } @@ -3364,8 +3364,8 @@ static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index, u8 *resp) if (ocs_value == OCS_SUCCESS) { task_rsp_upiup = (struct utp_upiu_task_rsp *) task_req_descp[index].task_rsp_upiu; - task_result = be32_to_cpu(task_rsp_upiup->header.dword_1); - task_result = ((task_result & MASK_TASK_RESPONSE) >> 8); + task_result = be32_to_cpu(task_rsp_upiup->output_param1); + task_result = task_result & MASK_TM_SERVICE_RESP; if (resp) *resp = (u8)task_result; } else { diff --git a/drivers/scsi/ultrastor.c b/drivers/scsi/ultrastor.c deleted file mode 100644 index 14e0c40a68c9..000000000000 --- a/drivers/scsi/ultrastor.c +++ /dev/null @@ -1,1210 +0,0 @@ -/* - * ultrastor.c Copyright (C) 1992 David B. Gentzel - * Low-level SCSI driver for UltraStor 14F, 24F, and 34F - * by David B. Gentzel, Whitfield Software Services, Carnegie, PA - * (gentzel@nova.enet.dec.com) - * scatter/gather added by Scott Taylor (n217cg@tamuts.tamu.edu) - * 24F and multiple command support by John F. Carr (jfc@athena.mit.edu) - * John's work modified by Caleb Epstein (cae@jpmorgan.com) and - * Eric Youngdale (ericy@cais.com). - * Thanks to UltraStor for providing the necessary documentation - * - * This is an old driver, for the 14F and 34F you should be using the - * u14-34f driver instead. - */ - -/* - * TODO: - * 1. Find out why scatter/gather is limited to 16 requests per command. - * This is fixed, at least on the 24F, as of version 1.12 - CAE. - * 2. Look at command linking (mscp.command_link and - * mscp.command_link_id). (Does not work with many disks, - * and no performance increase. ERY). - * 3. Allow multiple adapters. - */ - -/* - * NOTES: - * The UltraStor 14F, 24F, and 34F are a family of intelligent, high - * performance SCSI-2 host adapters. They all support command queueing - * and scatter/gather I/O. Some of them can also emulate the standard - * WD1003 interface for use with OS's which don't support SCSI. Here - * is the scoop on the various models: - * 14F - ISA first-party DMA HA with floppy support and WD1003 emulation. - * 14N - ISA HA with floppy support. I think that this is a non-DMA - * HA. Nothing further known. - * 24F - EISA Bus Master HA with floppy support and WD1003 emulation. - * 34F - VL-Bus Bus Master HA with floppy support (no WD1003 emulation). - * - * The 14F, 24F, and 34F are supported by this driver. - * - * Places flagged with a triple question-mark are things which are either - * unfinished, questionable, or wrong. - */ - -/* Changes from version 1.11 alpha to 1.12 - * - * Increased the size of the scatter-gather list to 33 entries for - * the 24F adapter (it was 16). I don't have the specs for the 14F - * or the 34F, so they may support larger s-g lists as well. - * - * Caleb Epstein <cae@jpmorgan.com> - */ - -/* Changes from version 1.9 to 1.11 - * - * Patches to bring this driver up to speed with the default kernel - * driver which supports only the 14F and 34F adapters. This version - * should compile cleanly into 0.99.13, 0.99.12 and probably 0.99.11. - * - * Fixes from Eric Youngdale to fix a few possible race conditions and - * several problems with bit testing operations (insufficient - * parentheses). - * - * Removed the ultrastor_abort() and ultrastor_reset() functions - * (enclosed them in #if 0 / #endif). These functions, at least on - * the 24F, cause the SCSI bus to do odd things and generally lead to - * kernel panics and machine hangs. This is like the Adaptec code. - * - * Use check/snarf_region for 14f, 34f to avoid I/O space address conflicts. - */ - -/* Changes from version 1.8 to version 1.9 - * - * 0.99.11 patches (cae@jpmorgan.com) */ - -/* Changes from version 1.7 to version 1.8 - * - * Better error reporting. - */ - -/* Changes from version 1.6 to version 1.7 - * - * Removed CSIR command code. - * - * Better race condition avoidance (xchgb function added). - * - * Set ICM and OGM status to zero at probe (24F) - * - * reset sends soft reset to UltraStor adapter - * - * reset adapter if adapter interrupts with an invalid MSCP address - * - * handle aborted command interrupt (24F) - * - */ - -/* Changes from version 1.5 to version 1.6: - * - * Read MSCP address from ICM _before_ clearing the interrupt flag. - * This fixes a race condition. - */ - -/* Changes from version 1.4 to version 1.5: - * - * Abort now calls done when multiple commands are enabled. - * - * Clear busy when aborted command finishes, not when abort is called. - * - * More debugging messages for aborts. - */ - -/* Changes from version 1.3 to version 1.4: - * - * Enable automatic request of sense data on error (requires newer version - * of scsi.c to be useful). - * - * Fix PORT_OVERRIDE for 14F. - * - * Fix abort and reset to work properly (config.aborted wasn't cleared - * after it was tested, so after a command abort no further commands would - * work). - * - * Boot time test to enable SCSI bus reset (defaults to not allowing reset). - * - * Fix test for OGM busy -- the busy bit is in different places on the 24F. - * - * Release ICM slot by clearing first byte on 24F. - */ - -#include <linux/module.h> -#include <linux/blkdev.h> -#include <linux/interrupt.h> -#include <linux/stddef.h> -#include <linux/string.h> -#include <linux/kernel.h> -#include <linux/ioport.h> -#include <linux/proc_fs.h> -#include <linux/spinlock.h> -#include <linux/stat.h> -#include <linux/bitops.h> -#include <linux/delay.h> - -#include <asm/io.h> -#include <asm/dma.h> - -#define ULTRASTOR_PRIVATE /* Get the private stuff from ultrastor.h */ -#include "scsi.h" -#include <scsi/scsi_host.h> -#include "ultrastor.h" - -#define FALSE 0 -#define TRUE 1 - -#ifndef ULTRASTOR_DEBUG -#define ULTRASTOR_DEBUG (UD_ABORT|UD_CSIR|UD_RESET) -#endif - -#define VERSION "1.12" - -#define PACKED __attribute__((packed)) -#define ALIGNED(x) __attribute__((aligned(x))) - - -/* The 14F uses an array of 4-byte ints for its scatter/gather list. - The data can be unaligned, but need not be. It's easier to give - the list normal alignment since it doesn't need to fit into a - packed structure. */ - -typedef struct { - u32 address; - u32 num_bytes; -} ultrastor_sg_list; - - -/* MailBox SCSI Command Packet. Basic command structure for communicating - with controller. */ -struct mscp { - unsigned char opcode: 3; /* type of command */ - unsigned char xdir: 2; /* data transfer direction */ - unsigned char dcn: 1; /* disable disconnect */ - unsigned char ca: 1; /* use cache (if available) */ - unsigned char sg: 1; /* scatter/gather operation */ - unsigned char target_id: 3; /* target SCSI id */ - unsigned char ch_no: 2; /* SCSI channel (always 0 for 14f) */ - unsigned char lun: 3; /* logical unit number */ - unsigned int transfer_data PACKED; /* transfer data pointer */ - unsigned int transfer_data_length PACKED; /* length in bytes */ - unsigned int command_link PACKED; /* for linking command chains */ - unsigned char scsi_command_link_id; /* identifies command in chain */ - unsigned char number_of_sg_list; /* (if sg is set) 8 bytes per list */ - unsigned char length_of_sense_byte; - unsigned char length_of_scsi_cdbs; /* 6, 10, or 12 */ - unsigned char scsi_cdbs[12]; /* SCSI commands */ - unsigned char adapter_status; /* non-zero indicates HA error */ - unsigned char target_status; /* non-zero indicates target error */ - u32 sense_data PACKED; - /* The following fields are for software only. They are included in - the MSCP structure because they are associated with SCSI requests. */ - void (*done) (struct scsi_cmnd *); - struct scsi_cmnd *SCint; - ultrastor_sg_list sglist[ULTRASTOR_24F_MAX_SG]; /* use larger size for 24F */ -}; - - -/* Port addresses (relative to the base address) */ -#define U14F_PRODUCT_ID(port) ((port) + 0x4) -#define CONFIG(port) ((port) + 0x6) - -/* Port addresses relative to the doorbell base address. */ -#define LCL_DOORBELL_MASK(port) ((port) + 0x0) -#define LCL_DOORBELL_INTR(port) ((port) + 0x1) -#define SYS_DOORBELL_MASK(port) ((port) + 0x2) -#define SYS_DOORBELL_INTR(port) ((port) + 0x3) - - -/* Used to store configuration info read from config i/o registers. Most of - this is not used yet, but might as well save it. - - This structure also holds port addresses that are not at the same offset - on the 14F and 24F. - - This structure holds all data that must be duplicated to support multiple - adapters. */ - -static struct ultrastor_config -{ - unsigned short port_address; /* base address of card */ - unsigned short doorbell_address; /* base address of doorbell CSRs */ - unsigned short ogm_address; /* base address of OGM */ - unsigned short icm_address; /* base address of ICM */ - const void *bios_segment; - unsigned char interrupt: 4; - unsigned char dma_channel: 3; - unsigned char bios_drive_number: 1; - unsigned char heads; - unsigned char sectors; - unsigned char ha_scsi_id: 3; - unsigned char subversion: 4; - unsigned char revision; - /* The slot number is used to distinguish the 24F (slot != 0) from - the 14F and 34F (slot == 0). */ - unsigned char slot; - -#ifdef PRINT_U24F_VERSION - volatile int csir_done; -#endif - - /* A pool of MSCP structures for this adapter, and a bitmask of - busy structures. (If ULTRASTOR_14F_MAX_CMDS == 1, a 1 byte - busy flag is used instead.) */ - -#if ULTRASTOR_MAX_CMDS == 1 - unsigned char mscp_busy; -#else - unsigned long mscp_free; -#endif - volatile unsigned char aborted[ULTRASTOR_MAX_CMDS]; - struct mscp mscp[ULTRASTOR_MAX_CMDS]; -} config = {0}; - -/* Set this to 1 to reset the SCSI bus on error. */ -static int ultrastor_bus_reset; - - -/* Allowed BIOS base addresses (NULL indicates reserved) */ -static const void *const bios_segment_table[8] = { - NULL, (void *)0xC4000, (void *)0xC8000, (void *)0xCC000, - (void *)0xD0000, (void *)0xD4000, (void *)0xD8000, (void *)0xDC000, -}; - -/* Allowed IRQs for 14f */ -static const unsigned char interrupt_table_14f[4] = { 15, 14, 11, 10 }; - -/* Allowed DMA channels for 14f (0 indicates reserved) */ -static const unsigned char dma_channel_table_14f[4] = { 5, 6, 7, 0 }; - -/* Head/sector mappings allowed by 14f */ -static const struct { - unsigned char heads; - unsigned char sectors; -} mapping_table[4] = { { 16, 63 }, { 64, 32 }, { 64, 63 }, { 64, 32 } }; - -#ifndef PORT_OVERRIDE -/* ??? A probe of address 0x310 screws up NE2000 cards */ -static const unsigned short ultrastor_ports_14f[] = { - 0x330, 0x340, /*0x310,*/ 0x230, 0x240, 0x210, 0x130, 0x140, -}; -#endif - -static void ultrastor_interrupt(void *); -static irqreturn_t do_ultrastor_interrupt(int, void *); -static inline void build_sg_list(struct mscp *, struct scsi_cmnd *SCpnt); - - -/* Always called with host lock held */ - -static inline int find_and_clear_bit_16(unsigned long *field) -{ - int rv; - - if (*field == 0) - panic("No free mscp"); - - asm volatile ( - "xorl %0,%0\n\t" - "0: bsfw %1,%w0\n\t" - "btr %0,%1\n\t" - "jnc 0b" - : "=&r" (rv), "+m" (*field) :); - - return rv; -} - -/* This has been re-implemented with the help of Richard Earnshaw, - <rwe@pegasus.esprit.ec.org> and works with gcc-2.5.8 and gcc-2.6.0. - The instability noted by jfc below appears to be a bug in - gcc-2.5.x when compiling w/o optimization. --Caleb - - This asm is fragile: it doesn't work without the casts and it may - not work without optimization. Maybe I should add a swap builtin - to gcc. --jfc */ -static inline unsigned char xchgb(unsigned char reg, - volatile unsigned char *mem) -{ - __asm__ ("xchgb %0,%1" : "=q" (reg), "=m" (*mem) : "0" (reg)); - return reg; -} - -#if ULTRASTOR_DEBUG & (UD_COMMAND | UD_ABORT) - -/* Always called with the host lock held */ -static void log_ultrastor_abort(struct ultrastor_config *config, - int command) -{ - static char fmt[80] = "abort %d (%x); MSCP free pool: %x;"; - int i; - - for (i = 0; i < ULTRASTOR_MAX_CMDS; i++) - { - fmt[20 + i*2] = ' '; - if (! (config->mscp_free & (1 << i))) - fmt[21 + i*2] = '0' + config->mscp[i].target_id; - else - fmt[21 + i*2] = '-'; - } - fmt[20 + ULTRASTOR_MAX_CMDS * 2] = '\n'; - fmt[21 + ULTRASTOR_MAX_CMDS * 2] = 0; - printk(fmt, command, &config->mscp[command], config->mscp_free); - -} -#endif - -static int ultrastor_14f_detect(struct scsi_host_template * tpnt) -{ - size_t i; - unsigned char in_byte, version_byte = 0; - struct config_1 { - unsigned char bios_segment: 3; - unsigned char removable_disks_as_fixed: 1; - unsigned char interrupt: 2; - unsigned char dma_channel: 2; - } config_1; - struct config_2 { - unsigned char ha_scsi_id: 3; - unsigned char mapping_mode: 2; - unsigned char bios_drive_number: 1; - unsigned char tfr_port: 2; - } config_2; - -#if (ULTRASTOR_DEBUG & UD_DETECT) - printk("US14F: detect: called\n"); -#endif - - /* If a 24F has already been configured, don't look for a 14F. */ - if (config.bios_segment) - return FALSE; - -#ifdef PORT_OVERRIDE - if(!request_region(PORT_OVERRIDE, 0xc, "ultrastor")) { - printk("Ultrastor I/O space already in use\n"); - return FALSE; - }; - config.port_address = PORT_OVERRIDE; -#else - for (i = 0; i < ARRAY_SIZE(ultrastor_ports_14f); i++) { - if(!request_region(ultrastor_ports_14f[i], 0x0c, "ultrastor")) continue; - config.port_address = ultrastor_ports_14f[i]; -#endif - -#if (ULTRASTOR_DEBUG & UD_DETECT) - printk("US14F: detect: testing port address %03X\n", config.port_address); -#endif - - in_byte = inb(U14F_PRODUCT_ID(config.port_address)); - if (in_byte != US14F_PRODUCT_ID_0) { -#if (ULTRASTOR_DEBUG & UD_DETECT) -# ifdef PORT_OVERRIDE - printk("US14F: detect: wrong product ID 0 - %02X\n", in_byte); -# else - printk("US14F: detect: no adapter at port %03X\n", config.port_address); -# endif -#endif -#ifdef PORT_OVERRIDE - goto out_release_port; -#else - release_region(config.port_address, 0x0c); - continue; -#endif - } - in_byte = inb(U14F_PRODUCT_ID(config.port_address) + 1); - /* Only upper nibble is significant for Product ID 1 */ - if ((in_byte & 0xF0) != US14F_PRODUCT_ID_1) { -#if (ULTRASTOR_DEBUG & UD_DETECT) -# ifdef PORT_OVERRIDE - printk("US14F: detect: wrong product ID 1 - %02X\n", in_byte); -# else - printk("US14F: detect: no adapter at port %03X\n", config.port_address); -# endif -#endif -#ifdef PORT_OVERRIDE - goto out_release_port; -#else - release_region(config.port_address, 0x0c); - continue; -#endif - } - version_byte = in_byte; -#ifndef PORT_OVERRIDE - break; - } - if (i == ARRAY_SIZE(ultrastor_ports_14f)) { -# if (ULTRASTOR_DEBUG & UD_DETECT) - printk("US14F: detect: no port address found!\n"); -# endif - /* all ports probed already released - we can just go straight out */ - return FALSE; - } -#endif - -#if (ULTRASTOR_DEBUG & UD_DETECT) - printk("US14F: detect: adapter found at port address %03X\n", - config.port_address); -#endif - - /* Set local doorbell mask to disallow bus reset unless - ultrastor_bus_reset is true. */ - outb(ultrastor_bus_reset ? 0xc2 : 0x82, LCL_DOORBELL_MASK(config.port_address)); - - /* All above tests passed, must be the right thing. Get some useful - info. */ - - /* Register the I/O space that we use */ - - *(char *)&config_1 = inb(CONFIG(config.port_address + 0)); - *(char *)&config_2 = inb(CONFIG(config.port_address + 1)); - config.bios_segment = bios_segment_table[config_1.bios_segment]; - config.doorbell_address = config.port_address; - config.ogm_address = config.port_address + 0x8; - config.icm_address = config.port_address + 0xC; - config.interrupt = interrupt_table_14f[config_1.interrupt]; - config.ha_scsi_id = config_2.ha_scsi_id; - config.heads = mapping_table[config_2.mapping_mode].heads; - config.sectors = mapping_table[config_2.mapping_mode].sectors; - config.bios_drive_number = config_2.bios_drive_number; - config.subversion = (version_byte & 0x0F); - if (config.subversion == U34F) - config.dma_channel = 0; - else - config.dma_channel = dma_channel_table_14f[config_1.dma_channel]; - - if (!config.bios_segment) { -#if (ULTRASTOR_DEBUG & UD_DETECT) - printk("US14F: detect: not detected.\n"); -#endif - goto out_release_port; - } - - /* Final consistency check, verify previous info. */ - if (config.subversion != U34F) - if (!config.dma_channel || !(config_2.tfr_port & 0x2)) { -#if (ULTRASTOR_DEBUG & UD_DETECT) - printk("US14F: detect: consistency check failed\n"); -#endif - goto out_release_port; - } - - /* If we were TRULY paranoid, we could issue a host adapter inquiry - command here and verify the data returned. But frankly, I'm - exhausted! */ - - /* Finally! Now I'm satisfied... */ -#if (ULTRASTOR_DEBUG & UD_DETECT) - printk("US14F: detect: detect succeeded\n" - " Port address: %03X\n" - " BIOS segment: %05X\n" - " Interrupt: %u\n" - " DMA channel: %u\n" - " H/A SCSI ID: %u\n" - " Subversion: %u\n", - config.port_address, config.bios_segment, config.interrupt, - config.dma_channel, config.ha_scsi_id, config.subversion); -#endif - tpnt->this_id = config.ha_scsi_id; - tpnt->unchecked_isa_dma = (config.subversion != U34F); - -#if ULTRASTOR_MAX_CMDS > 1 - config.mscp_free = ~0; -#endif - - /* - * Brrr, &config.mscp[0].SCint->host) it is something magical.... - * XXX and FIXME - */ - if (request_irq(config.interrupt, do_ultrastor_interrupt, 0, "Ultrastor", &config.mscp[0].SCint->device->host)) { - printk("Unable to allocate IRQ%u for UltraStor controller.\n", - config.interrupt); - goto out_release_port; - } - if (config.dma_channel && request_dma(config.dma_channel,"Ultrastor")) { - printk("Unable to allocate DMA channel %u for UltraStor controller.\n", - config.dma_channel); - free_irq(config.interrupt, NULL); - goto out_release_port; - } - tpnt->sg_tablesize = ULTRASTOR_14F_MAX_SG; - printk("UltraStor driver version" VERSION ". Using %d SG lists.\n", - ULTRASTOR_14F_MAX_SG); - - return TRUE; -out_release_port: - release_region(config.port_address, 0x0c); - return FALSE; -} - -static int ultrastor_24f_detect(struct scsi_host_template * tpnt) -{ - int i; - struct Scsi_Host * shpnt = NULL; - -#if (ULTRASTOR_DEBUG & UD_DETECT) - printk("US24F: detect"); -#endif - - /* probe each EISA slot at slot address C80 */ - for (i = 1; i < 15; i++) - { - unsigned char config_1, config_2; - unsigned short addr = (i << 12) | ULTRASTOR_24F_PORT; - - if (inb(addr) != US24F_PRODUCT_ID_0 && - inb(addr+1) != US24F_PRODUCT_ID_1 && - inb(addr+2) != US24F_PRODUCT_ID_2) - continue; - - config.revision = inb(addr+3); - config.slot = i; - if (! (inb(addr+4) & 1)) - { -#if (ULTRASTOR_DEBUG & UD_DETECT) - printk("U24F: found disabled card in slot %u\n", i); -#endif - continue; - } -#if (ULTRASTOR_DEBUG & UD_DETECT) - printk("U24F: found card in slot %u\n", i); -#endif - config_1 = inb(addr + 5); - config.bios_segment = bios_segment_table[config_1 & 7]; - switch(config_1 >> 4) - { - case 1: - config.interrupt = 15; - break; - case 2: - config.interrupt = 14; - break; - case 4: - config.interrupt = 11; - break; - case 8: - config.interrupt = 10; - break; - default: - printk("U24F: invalid IRQ\n"); - return FALSE; - } - - /* BIOS addr set */ - /* base port set */ - config.port_address = addr; - config.doorbell_address = addr + 12; - config.ogm_address = addr + 0x17; - config.icm_address = addr + 0x1C; - config_2 = inb(addr + 7); - config.ha_scsi_id = config_2 & 7; - config.heads = mapping_table[(config_2 >> 3) & 3].heads; - config.sectors = mapping_table[(config_2 >> 3) & 3].sectors; -#if (ULTRASTOR_DEBUG & UD_DETECT) - printk("US24F: detect: detect succeeded\n" - " Port address: %03X\n" - " BIOS segment: %05X\n" - " Interrupt: %u\n" - " H/A SCSI ID: %u\n", - config.port_address, config.bios_segment, - config.interrupt, config.ha_scsi_id); -#endif - tpnt->this_id = config.ha_scsi_id; - tpnt->unchecked_isa_dma = 0; - tpnt->sg_tablesize = ULTRASTOR_24F_MAX_SG; - - shpnt = scsi_register(tpnt, 0); - if (!shpnt) { - printk(KERN_WARNING "(ultrastor:) Could not register scsi device. Aborting registration.\n"); - free_irq(config.interrupt, do_ultrastor_interrupt); - return FALSE; - } - - if (request_irq(config.interrupt, do_ultrastor_interrupt, 0, "Ultrastor", shpnt)) - { - printk("Unable to allocate IRQ%u for UltraStor controller.\n", - config.interrupt); - return FALSE; - } - - shpnt->irq = config.interrupt; - shpnt->dma_channel = config.dma_channel; - shpnt->io_port = config.port_address; - -#if ULTRASTOR_MAX_CMDS > 1 - config.mscp_free = ~0; -#endif - /* Mark ICM and OGM free */ - outb(0, addr + 0x16); - outb(0, addr + 0x1B); - - /* Set local doorbell mask to disallow bus reset unless - ultrastor_bus_reset is true. */ - outb(ultrastor_bus_reset ? 0xc2 : 0x82, LCL_DOORBELL_MASK(addr+12)); - outb(0x02, SYS_DOORBELL_MASK(addr+12)); - printk("UltraStor driver version " VERSION ". Using %d SG lists.\n", - tpnt->sg_tablesize); - return TRUE; - } - return FALSE; -} - -static int ultrastor_detect(struct scsi_host_template * tpnt) -{ - tpnt->proc_name = "ultrastor"; - return ultrastor_14f_detect(tpnt) || ultrastor_24f_detect(tpnt); -} - -static int ultrastor_release(struct Scsi_Host *shost) -{ - if (shost->irq) - free_irq(shost->irq, NULL); - if (shost->dma_channel != 0xff) - free_dma(shost->dma_channel); - if (shost->io_port && shost->n_io_port) - release_region(shost->io_port, shost->n_io_port); - scsi_unregister(shost); - return 0; -} - -static const char *ultrastor_info(struct Scsi_Host * shpnt) -{ - static char buf[64]; - - if (config.slot) - sprintf(buf, "UltraStor 24F SCSI @ Slot %u IRQ%u", - config.slot, config.interrupt); - else if (config.subversion) - sprintf(buf, "UltraStor 34F SCSI @ Port %03X BIOS %05X IRQ%u", - config.port_address, (int)config.bios_segment, - config.interrupt); - else - sprintf(buf, "UltraStor 14F SCSI @ Port %03X BIOS %05X IRQ%u DMA%u", - config.port_address, (int)config.bios_segment, - config.interrupt, config.dma_channel); - return buf; -} - -static inline void build_sg_list(struct mscp *mscp, struct scsi_cmnd *SCpnt) -{ - struct scatterlist *sg; - long transfer_length = 0; - int i, max; - - max = scsi_sg_count(SCpnt); - scsi_for_each_sg(SCpnt, sg, max, i) { - mscp->sglist[i].address = isa_page_to_bus(sg_page(sg)) + sg->offset; - mscp->sglist[i].num_bytes = sg->length; - transfer_length += sg->length; - } - mscp->number_of_sg_list = max; - mscp->transfer_data = isa_virt_to_bus(mscp->sglist); - /* ??? May not be necessary. Docs are unclear as to whether transfer - length field is ignored or whether it should be set to the total - number of bytes of the transfer. */ - mscp->transfer_data_length = transfer_length; -} - -static int ultrastor_queuecommand_lck(struct scsi_cmnd *SCpnt, - void (*done) (struct scsi_cmnd *)) -{ - struct mscp *my_mscp; -#if ULTRASTOR_MAX_CMDS > 1 - int mscp_index; -#endif - unsigned int status; - - /* Next test is for debugging; "can't happen" */ - if ((config.mscp_free & ((1U << ULTRASTOR_MAX_CMDS) - 1)) == 0) - panic("ultrastor_queuecommand: no free MSCP\n"); - mscp_index = find_and_clear_bit_16(&config.mscp_free); - - /* Has the command been aborted? */ - if (xchgb(0xff, &config.aborted[mscp_index]) != 0) - { - status = DID_ABORT << 16; - goto aborted; - } - - my_mscp = &config.mscp[mscp_index]; - - *(unsigned char *)my_mscp = OP_SCSI | (DTD_SCSI << 3); - - /* Tape drives don't work properly if the cache is used. The SCSI - READ command for a tape doesn't have a block offset, and the adapter - incorrectly assumes that all reads from the tape read the same - blocks. Results will depend on read buffer size and other disk - activity. - - ??? Which other device types should never use the cache? */ - my_mscp->ca = SCpnt->device->type != TYPE_TAPE; - my_mscp->target_id = SCpnt->device->id; - my_mscp->ch_no = 0; - my_mscp->lun = SCpnt->device->lun; - if (scsi_sg_count(SCpnt)) { - /* Set scatter/gather flag in SCSI command packet */ - my_mscp->sg = TRUE; - build_sg_list(my_mscp, SCpnt); - } else { - /* Unset scatter/gather flag in SCSI command packet */ - my_mscp->sg = FALSE; - my_mscp->transfer_data = isa_virt_to_bus(scsi_sglist(SCpnt)); - my_mscp->transfer_data_length = scsi_bufflen(SCpnt); - } - my_mscp->command_link = 0; /*???*/ - my_mscp->scsi_command_link_id = 0; /*???*/ - my_mscp->length_of_sense_byte = SCSI_SENSE_BUFFERSIZE; - my_mscp->length_of_scsi_cdbs = SCpnt->cmd_len; - memcpy(my_mscp->scsi_cdbs, SCpnt->cmnd, my_mscp->length_of_scsi_cdbs); - my_mscp->adapter_status = 0; - my_mscp->target_status = 0; - my_mscp->sense_data = isa_virt_to_bus(&SCpnt->sense_buffer); - my_mscp->done = done; - my_mscp->SCint = SCpnt; - SCpnt->host_scribble = (unsigned char *)my_mscp; - - /* Find free OGM slot. On 24F, look for OGM status byte == 0. - On 14F and 34F, wait for local interrupt pending flag to clear. - - FIXME: now we are using new_eh we should punt here and let the - midlayer sort it out */ - -retry: - if (config.slot) - while (inb(config.ogm_address - 1) != 0 && config.aborted[mscp_index] == 0xff) - barrier(); - - /* else??? */ - - while ((inb(LCL_DOORBELL_INTR(config.doorbell_address)) & (config.slot ? 2 : 1)) && config.aborted[mscp_index] == 0xff) - barrier(); - - /* To avoid race conditions, keep the code to write to the adapter - atomic. This simplifies the abort code. Right now the - scsi mid layer has the host_lock already held - */ - - if (inb(LCL_DOORBELL_INTR(config.doorbell_address)) & (config.slot ? 2 : 1)) - goto retry; - - status = xchgb(0, &config.aborted[mscp_index]); - if (status != 0xff) { - -#if ULTRASTOR_DEBUG & (UD_COMMAND | UD_ABORT) - printk("USx4F: queuecommand: aborted\n"); -#if ULTRASTOR_MAX_CMDS > 1 - log_ultrastor_abort(&config, mscp_index); -#endif -#endif - status <<= 16; - - aborted: - set_bit(mscp_index, &config.mscp_free); - /* If the driver queues commands, call the done proc here. Otherwise - return an error. */ -#if ULTRASTOR_MAX_CMDS > 1 - SCpnt->result = status; - done(SCpnt); - return 0; -#else - return status; -#endif - } - - /* Store pointer in OGM address bytes */ - outl(isa_virt_to_bus(my_mscp), config.ogm_address); - - /* Issue OGM interrupt */ - if (config.slot) { - /* Write OGM command register on 24F */ - outb(1, config.ogm_address - 1); - outb(0x2, LCL_DOORBELL_INTR(config.doorbell_address)); - } else { - outb(0x1, LCL_DOORBELL_INTR(config.doorbell_address)); - } - -#if (ULTRASTOR_DEBUG & UD_COMMAND) - printk("USx4F: queuecommand: returning\n"); -#endif - - return 0; -} - -static DEF_SCSI_QCMD(ultrastor_queuecommand) - -/* This code must deal with 2 cases: - - 1. The command has not been written to the OGM. In this case, set - the abort flag and return. - - 2. The command has been written to the OGM and is stuck somewhere in - the adapter. - - 2a. On a 24F, ask the adapter to abort the command. It will interrupt - when it does. - - 2b. Call the command's done procedure. - - */ - -static int ultrastor_abort(struct scsi_cmnd *SCpnt) -{ -#if ULTRASTOR_DEBUG & UD_ABORT - char out[108]; - unsigned char icm_status = 0, ogm_status = 0; - unsigned int icm_addr = 0, ogm_addr = 0; -#endif - unsigned int mscp_index; - unsigned char old_aborted; - unsigned long flags; - void (*done)(struct scsi_cmnd *); - struct Scsi_Host *host = SCpnt->device->host; - - if(config.slot) - return FAILED; /* Do not attempt an abort for the 24f */ - - /* Simple consistency checking */ - if(!SCpnt->host_scribble) - return FAILED; - - mscp_index = ((struct mscp *)SCpnt->host_scribble) - config.mscp; - if (mscp_index >= ULTRASTOR_MAX_CMDS) - panic("Ux4F aborting invalid MSCP"); - -#if ULTRASTOR_DEBUG & UD_ABORT - if (config.slot) - { - int port0 = (config.slot << 12) | 0xc80; - int i; - unsigned long flags; - - spin_lock_irqsave(host->host_lock, flags); - strcpy(out, "OGM %d:%x ICM %d:%x ports: "); - for (i = 0; i < 16; i++) - { - unsigned char p = inb(port0 + i); - out[28 + i * 3] = "0123456789abcdef"[p >> 4]; - out[29 + i * 3] = "0123456789abcdef"[p & 15]; - out[30 + i * 3] = ' '; - } - out[28 + i * 3] = '\n'; - out[29 + i * 3] = 0; - ogm_status = inb(port0 + 22); - ogm_addr = (unsigned int)isa_bus_to_virt(inl(port0 + 23)); - icm_status = inb(port0 + 27); - icm_addr = (unsigned int)isa_bus_to_virt(inl(port0 + 28)); - spin_unlock_irqrestore(host->host_lock, flags); - } - - /* First check to see if an interrupt is pending. I suspect the SiS - chipset loses interrupts. (I also suspect is mangles data, but - one bug at a time... */ - if (config.slot ? inb(config.icm_address - 1) == 2 : - (inb(SYS_DOORBELL_INTR(config.doorbell_address)) & 1)) - { - printk("Ux4F: abort while completed command pending\n"); - - spin_lock_irqsave(host->host_lock, flags); - /* FIXME: Ewww... need to think about passing host around properly */ - ultrastor_interrupt(NULL); - spin_unlock_irqrestore(host->host_lock, flags); - return SUCCESS; - } -#endif - - old_aborted = xchgb(DID_ABORT, &config.aborted[mscp_index]); - - /* aborted == 0xff is the signal that queuecommand has not yet sent - the command. It will notice the new abort flag and fail. */ - if (old_aborted == 0xff) - return SUCCESS; - - /* On 24F, send an abort MSCP request. The adapter will interrupt - and the interrupt handler will call done. */ - if (config.slot && inb(config.ogm_address - 1) == 0) - { - unsigned long flags; - - spin_lock_irqsave(host->host_lock, flags); - outl(isa_virt_to_bus(&config.mscp[mscp_index]), config.ogm_address); - udelay(8); - outb(0x80, config.ogm_address - 1); - outb(0x2, LCL_DOORBELL_INTR(config.doorbell_address)); -#if ULTRASTOR_DEBUG & UD_ABORT - log_ultrastor_abort(&config, mscp_index); - printk(out, ogm_status, ogm_addr, icm_status, icm_addr); -#endif - spin_unlock_irqrestore(host->host_lock, flags); - /* FIXME: add a wait for the abort to complete */ - return SUCCESS; - } - -#if ULTRASTOR_DEBUG & UD_ABORT - log_ultrastor_abort(&config, mscp_index); -#endif - - /* Can't request a graceful abort. Either this is not a 24F or - the OGM is busy. Don't free the command -- the adapter might - still be using it. Setting SCint = 0 causes the interrupt - handler to ignore the command. */ - - /* FIXME - devices that implement soft resets will still be running - the command after a bus reset. We would probably rather leave - the command in the queue. The upper level code will automatically - leave the command in the active state instead of requeueing it. ERY */ - -#if ULTRASTOR_DEBUG & UD_ABORT - if (config.mscp[mscp_index].SCint != SCpnt) - printk("abort: command mismatch, %p != %p\n", - config.mscp[mscp_index].SCint, SCpnt); -#endif - if (config.mscp[mscp_index].SCint == NULL) - return FAILED; - - if (config.mscp[mscp_index].SCint != SCpnt) panic("Bad abort"); - config.mscp[mscp_index].SCint = NULL; - done = config.mscp[mscp_index].done; - config.mscp[mscp_index].done = NULL; - SCpnt->result = DID_ABORT << 16; - - /* Take the host lock to guard against scsi layer re-entry */ - done(SCpnt); - - /* Need to set a timeout here in case command never completes. */ - return SUCCESS; -} - -static int ultrastor_host_reset(struct scsi_cmnd * SCpnt) -{ - unsigned long flags; - int i; - struct Scsi_Host *host = SCpnt->device->host; - -#if (ULTRASTOR_DEBUG & UD_RESET) - printk("US14F: reset: called\n"); -#endif - - if(config.slot) - return FAILED; - - spin_lock_irqsave(host->host_lock, flags); - /* Reset the adapter and SCSI bus. The SCSI bus reset can be - inhibited by clearing ultrastor_bus_reset before probe. */ - outb(0xc0, LCL_DOORBELL_INTR(config.doorbell_address)); - if (config.slot) - { - outb(0, config.ogm_address - 1); - outb(0, config.icm_address - 1); - } - -#if ULTRASTOR_MAX_CMDS == 1 - if (config.mscp_busy && config.mscp->done && config.mscp->SCint) - { - config.mscp->SCint->result = DID_RESET << 16; - config.mscp->done(config.mscp->SCint); - } - config.mscp->SCint = 0; -#else - for (i = 0; i < ULTRASTOR_MAX_CMDS; i++) - { - if (! (config.mscp_free & (1 << i)) && - config.mscp[i].done && config.mscp[i].SCint) - { - config.mscp[i].SCint->result = DID_RESET << 16; - config.mscp[i].done(config.mscp[i].SCint); - config.mscp[i].done = NULL; - } - config.mscp[i].SCint = NULL; - } -#endif - - /* FIXME - if the device implements soft resets, then the command - will still be running. ERY - - Even bigger deal with new_eh! - */ - - memset((unsigned char *)config.aborted, 0, sizeof config.aborted); -#if ULTRASTOR_MAX_CMDS == 1 - config.mscp_busy = 0; -#else - config.mscp_free = ~0; -#endif - - spin_unlock_irqrestore(host->host_lock, flags); - return SUCCESS; - -} - -int ultrastor_biosparam(struct scsi_device *sdev, struct block_device *bdev, - sector_t capacity, int * dkinfo) -{ - int size = capacity; - unsigned int s = config.heads * config.sectors; - - dkinfo[0] = config.heads; - dkinfo[1] = config.sectors; - dkinfo[2] = size / s; /* Ignore partial cylinders */ -#if 0 - if (dkinfo[2] > 1024) - dkinfo[2] = 1024; -#endif - return 0; -} - -static void ultrastor_interrupt(void *dev_id) -{ - unsigned int status; -#if ULTRASTOR_MAX_CMDS > 1 - unsigned int mscp_index; -#endif - struct mscp *mscp; - void (*done) (struct scsi_cmnd *); - struct scsi_cmnd *SCtmp; - -#if ULTRASTOR_MAX_CMDS == 1 - mscp = &config.mscp[0]; -#else - mscp = (struct mscp *)isa_bus_to_virt(inl(config.icm_address)); - mscp_index = mscp - config.mscp; - if (mscp_index >= ULTRASTOR_MAX_CMDS) { - printk("Ux4F interrupt: bad MSCP address %x\n", (unsigned int) mscp); - /* A command has been lost. Reset and report an error - for all commands. */ - ultrastor_host_reset(dev_id); - return; - } -#endif - - /* Clean ICM slot (set ICMINT bit to 0) */ - if (config.slot) { - unsigned char icm_status = inb(config.icm_address - 1); -#if ULTRASTOR_DEBUG & (UD_INTERRUPT|UD_ERROR|UD_ABORT) - if (icm_status != 1 && icm_status != 2) - printk("US24F: ICM status %x for MSCP %d (%x)\n", icm_status, - mscp_index, (unsigned int) mscp); -#endif - /* The manual says clear interrupt then write 0 to ICM status. - This seems backwards, but I'll do it anyway. --jfc */ - outb(2, SYS_DOORBELL_INTR(config.doorbell_address)); - outb(0, config.icm_address - 1); - if (icm_status == 4) { - printk("UltraStor abort command failed\n"); - return; - } - if (icm_status == 3) { - void (*done)(struct scsi_cmnd *) = mscp->done; - if (done) { - mscp->done = NULL; - mscp->SCint->result = DID_ABORT << 16; - done(mscp->SCint); - } - return; - } - } else { - outb(1, SYS_DOORBELL_INTR(config.doorbell_address)); - } - - SCtmp = mscp->SCint; - mscp->SCint = NULL; - - if (!SCtmp) - { -#if ULTRASTOR_DEBUG & (UD_ABORT|UD_INTERRUPT) - printk("MSCP %d (%x): no command\n", mscp_index, (unsigned int) mscp); -#endif -#if ULTRASTOR_MAX_CMDS == 1 - config.mscp_busy = FALSE; -#else - set_bit(mscp_index, &config.mscp_free); -#endif - config.aborted[mscp_index] = 0; - return; - } - - /* Save done locally and zero before calling. This is needed as - once we call done, we may get another command queued before this - interrupt service routine can return. */ - done = mscp->done; - mscp->done = NULL; - - /* Let the higher levels know that we're done */ - switch (mscp->adapter_status) - { - case 0: - status = DID_OK << 16; - break; - case 0x01: /* invalid command */ - case 0x02: /* invalid parameters */ - case 0x03: /* invalid data list */ - default: - status = DID_ERROR << 16; - break; - case 0x84: /* SCSI bus abort */ - status = DID_ABORT << 16; - break; - case 0x91: - status = DID_TIME_OUT << 16; - break; - } - - SCtmp->result = status | mscp->target_status; - - SCtmp->host_scribble = NULL; - - /* Free up mscp block for next command */ -#if ULTRASTOR_MAX_CMDS == 1 - config.mscp_busy = FALSE; -#else - set_bit(mscp_index, &config.mscp_free); -#endif - -#if ULTRASTOR_DEBUG & (UD_ABORT|UD_INTERRUPT) - if (config.aborted[mscp_index]) - printk("Ux4 interrupt: MSCP %d (%x) aborted = %d\n", - mscp_index, (unsigned int) mscp, config.aborted[mscp_index]); -#endif - config.aborted[mscp_index] = 0; - - if (done) - done(SCtmp); - else - printk("US14F: interrupt: unexpected interrupt\n"); - - if (config.slot ? inb(config.icm_address - 1) : - (inb(SYS_DOORBELL_INTR(config.doorbell_address)) & 1)) -#if (ULTRASTOR_DEBUG & UD_MULTI_CMD) - printk("Ux4F: multiple commands completed\n"); -#else - ; -#endif - -#if (ULTRASTOR_DEBUG & UD_INTERRUPT) - printk("USx4F: interrupt: returning\n"); -#endif -} - -static irqreturn_t do_ultrastor_interrupt(int irq, void *dev_id) -{ - unsigned long flags; - struct Scsi_Host *dev = dev_id; - - spin_lock_irqsave(dev->host_lock, flags); - ultrastor_interrupt(dev_id); - spin_unlock_irqrestore(dev->host_lock, flags); - return IRQ_HANDLED; -} - -MODULE_LICENSE("GPL"); - -static struct scsi_host_template driver_template = { - .name = "UltraStor 14F/24F/34F", - .detect = ultrastor_detect, - .release = ultrastor_release, - .info = ultrastor_info, - .queuecommand = ultrastor_queuecommand, - .eh_abort_handler = ultrastor_abort, - .eh_host_reset_handler = ultrastor_host_reset, - .bios_param = ultrastor_biosparam, - .can_queue = ULTRASTOR_MAX_CMDS, - .sg_tablesize = ULTRASTOR_14F_MAX_SG, - .cmd_per_lun = ULTRASTOR_MAX_CMDS_PER_LUN, - .unchecked_isa_dma = 1, - .use_clustering = ENABLE_CLUSTERING, -}; -#include "scsi_module.c" diff --git a/drivers/scsi/ultrastor.h b/drivers/scsi/ultrastor.h deleted file mode 100644 index 165c18b5cf5f..000000000000 --- a/drivers/scsi/ultrastor.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * ultrastor.c (C) 1991 David B. Gentzel - * Low-level scsi driver for UltraStor 14F - * by David B. Gentzel, Whitfield Software Services, Carnegie, PA - * (gentzel@nova.enet.dec.com) - * scatter/gather added by Scott Taylor (n217cg@tamuts.tamu.edu) - * 24F support by John F. Carr (jfc@athena.mit.edu) - * John's work modified by Caleb Epstein (cae@jpmorgan.com) and - * Eric Youngdale (eric@tantalus.nrl.navy.mil). - * Thanks to UltraStor for providing the necessary documentation - */ - -#ifndef _ULTRASTOR_H -#define _ULTRASTOR_H - -static int ultrastor_detect(struct scsi_host_template *); -static const char *ultrastor_info(struct Scsi_Host *shpnt); -static int ultrastor_queuecommand(struct Scsi_Host *, struct scsi_cmnd *); -static int ultrastor_abort(struct scsi_cmnd *); -static int ultrastor_host_reset(struct scsi_cmnd *); -static int ultrastor_biosparam(struct scsi_device *, struct block_device *, - sector_t, int *); - - -#define ULTRASTOR_14F_MAX_SG 16 -#define ULTRASTOR_24F_MAX_SG 33 - -#define ULTRASTOR_MAX_CMDS_PER_LUN 5 -#define ULTRASTOR_MAX_CMDS 16 - -#define ULTRASTOR_24F_PORT 0xC80 - - -#ifdef ULTRASTOR_PRIVATE - -#define UD_ABORT 0x0001 -#define UD_COMMAND 0x0002 -#define UD_DETECT 0x0004 -#define UD_INTERRUPT 0x0008 -#define UD_RESET 0x0010 -#define UD_MULTI_CMD 0x0020 -#define UD_CSIR 0x0040 -#define UD_ERROR 0x0080 - -/* #define PORT_OVERRIDE 0x330 */ - -/* Values for the PRODUCT_ID ports for the 14F */ -#define US14F_PRODUCT_ID_0 0x56 -#define US14F_PRODUCT_ID_1 0x40 /* NOTE: Only upper nibble is used */ - -#define US24F_PRODUCT_ID_0 0x56 -#define US24F_PRODUCT_ID_1 0x63 -#define US24F_PRODUCT_ID_2 0x02 - -/* Subversion values */ -#define U14F 0 -#define U34F 1 - -/* MSCP field values */ - -/* Opcode */ -#define OP_HOST_ADAPTER 0x1 -#define OP_SCSI 0x2 -#define OP_RESET 0x4 - -/* Date Transfer Direction */ -#define DTD_SCSI 0x0 -#define DTD_IN 0x1 -#define DTD_OUT 0x2 -#define DTD_NONE 0x3 - -/* Host Adapter command subcodes */ -#define HA_CMD_INQUIRY 0x1 -#define HA_CMD_SELF_DIAG 0x2 -#define HA_CMD_READ_BUFF 0x3 -#define HA_CMD_WRITE_BUFF 0x4 - -#endif - -#endif diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index deefab3a94d0..ec91bd07f00a 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -259,7 +259,7 @@ static void virtscsi_complete_free(struct virtio_scsi *vscsi, void *buf) struct virtio_scsi_cmd *cmd = buf; if (cmd->comp) - complete_all(cmd->comp); + complete(cmd->comp); } static void virtscsi_ctrl_done(struct virtqueue *vq) diff --git a/drivers/scsi/wd7000.c b/drivers/scsi/wd7000.c deleted file mode 100644 index 409f959845c4..000000000000 --- a/drivers/scsi/wd7000.c +++ /dev/null @@ -1,1657 +0,0 @@ -/* $Id: $ - * linux/drivers/scsi/wd7000.c - * - * Copyright (C) 1992 Thomas Wuensche - * closely related to the aha1542 driver from Tommy Thorn - * ( as close as different hardware allows on a lowlevel-driver :-) ) - * - * Revised (and renamed) by John Boyd <boyd@cis.ohio-state.edu> to - * accommodate Eric Youngdale's modifications to scsi.c. Nov 1992. - * - * Additional changes to support scatter/gather. Dec. 1992. tw/jb - * - * No longer tries to reset SCSI bus at boot (it wasn't working anyway). - * Rewritten to support multiple host adapters. - * Miscellaneous cleanup. - * So far, still doesn't do reset or abort correctly, since I have no idea - * how to do them with this board (8^(. Jan 1994 jb - * - * This driver now supports both of the two standard configurations (per - * the 3.36 Owner's Manual, my latest reference) by the same method as - * before; namely, by looking for a BIOS signature. Thus, the location of - * the BIOS signature determines the board configuration. Until I have - * time to do something more flexible, users should stick to one of the - * following: - * - * Standard configuration for single-adapter systems: - * - BIOS at CE00h - * - I/O base address 350h - * - IRQ level 15 - * - DMA channel 6 - * Standard configuration for a second adapter in a system: - * - BIOS at C800h - * - I/O base address 330h - * - IRQ level 11 - * - DMA channel 5 - * - * Anyone who can recompile the kernel is welcome to add others as need - * arises, but unpredictable results may occur if there are conflicts. - * In any event, if there are multiple adapters in a system, they MUST - * use different I/O bases, IRQ levels, and DMA channels, since they will be - * indistinguishable (and in direct conflict) otherwise. - * - * As a point of information, the NO_OP command toggles the CMD_RDY bit - * of the status port, and this fact could be used as a test for the I/O - * base address (or more generally, board detection). There is an interrupt - * status port, so IRQ probing could also be done. I suppose the full - * DMA diagnostic could be used to detect the DMA channel being used. I - * haven't done any of this, though, because I think there's too much of - * a chance that such explorations could be destructive, if some other - * board's resources are used inadvertently. So, call me a wimp, but I - * don't want to try it. The only kind of exploration I trust is memory - * exploration, since it's more certain that reading memory won't be - * destructive. - * - * More to my liking would be a LILO boot command line specification, such - * as is used by the aha152x driver (and possibly others). I'll look into - * it, as I have time... - * - * I get mail occasionally from people who either are using or are - * considering using a WD7000 with Linux. There is a variety of - * nomenclature describing WD7000's. To the best of my knowledge, the - * following is a brief summary (from an old WD doc - I don't work for - * them or anything like that): - * - * WD7000-FASST2: This is a WD7000 board with the real-mode SST ROM BIOS - * installed. Last I heard, the BIOS was actually done by Columbia - * Data Products. The BIOS is only used by this driver (and thus - * by Linux) to identify the board; none of it can be executed under - * Linux. - * - * WD7000-ASC: This is the original adapter board, with or without BIOS. - * The board uses a WD33C93 or WD33C93A SBIC, which in turn is - * controlled by an onboard Z80 processor. The board interface - * visible to the host CPU is defined effectively by the Z80's - * firmware, and it is this firmware's revision level that is - * determined and reported by this driver. (The version of the - * on-board BIOS is of no interest whatsoever.) The host CPU has - * no access to the SBIC; hence the fact that it is a WD33C93 is - * also of no interest to this driver. - * - * WD7000-AX: - * WD7000-MX: - * WD7000-EX: These are newer versions of the WD7000-ASC. The -ASC is - * largely built from discrete components; these boards use more - * integration. The -AX is an ISA bus board (like the -ASC), - * the -MX is an MCA (i.e., PS/2) bus board), and the -EX is an - * EISA bus board. - * - * At the time of my documentation, the -?X boards were "future" products, - * and were not yet available. However, I vaguely recall that Thomas - * Wuensche had an -AX, so I believe at least it is supported by this - * driver. I have no personal knowledge of either -MX or -EX boards. - * - * P.S. Just recently, I've discovered (directly from WD and Future - * Domain) that all but the WD7000-EX have been out of production for - * two years now. FD has production rights to the 7000-EX, and are - * producing it under a new name, and with a new BIOS. If anyone has - * one of the FD boards, it would be nice to come up with a signature - * for it. - * J.B. Jan 1994. - * - * - * Revisions by Miroslav Zagorac <zaga@fly.cc.fer.hr> - * - * 08/24/1996. - * - * Enhancement for wd7000_detect function has been made, so you don't have - * to enter BIOS ROM address in initialisation data (see struct Config). - * We cannot detect IRQ, DMA and I/O base address for now, so we have to - * enter them as arguments while wd_7000 is detected. If someone has IRQ, - * DMA or I/O base address set to some other value, he can enter them in - * configuration without any problem. Also I wrote a function wd7000_setup, - * so now you can enter WD-7000 definition as kernel arguments, - * as in lilo.conf: - * - * append="wd7000=IRQ,DMA,IO" - * - * PS: If card BIOS ROM is disabled, function wd7000_detect now will recognize - * adapter, unlike the old one. Anyway, BIOS ROM from WD7000 adapter is - * useless for Linux. B^) - * - * - * 09/06/1996. - * - * Autodetecting of I/O base address from wd7000_detect function is removed, - * some little bugs removed, etc... - * - * Thanks to Roger Scott for driver debugging. - * - * 06/07/1997 - * - * Added support for /proc file system (/proc/scsi/wd7000/[0...] files). - * Now, driver can handle hard disks with capacity >1GB. - * - * 01/15/1998 - * - * Added support for BUS_ON and BUS_OFF parameters in config line. - * Miscellaneous cleanup. - * - * 03/01/1998 - * - * WD7000 driver now work on kernels >= 2.1.x - * - * - * 12/31/2001 - Arnaldo Carvalho de Melo <acme@conectiva.com.br> - * - * use host->host_lock, not io_request_lock, cleanups - * - * 2002/10/04 - Alan Cox <alan@lxorguk.ukuu.org.uk> - * - * Use dev_id for interrupts, kill __func__ pasting - * Add a lock for the scb pool, clean up all other cli/sti usage stuff - * Use the adapter lock for the other places we had the cli's - * - * 2002/10/06 - Alan Cox <alan@lxorguk.ukuu.org.uk> - * - * Switch to new style error handling - * Clean up delay to udelay, and yielding sleeps - * Make host reset actually reset the card - * Make everything static - * - * 2003/02/12 - Christoph Hellwig <hch@infradead.org> - * - * Cleaned up host template definition - * Removed now obsolete wd7000.h - */ - -#include <linux/delay.h> -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/types.h> -#include <linux/string.h> -#include <linux/spinlock.h> -#include <linux/ioport.h> -#include <linux/proc_fs.h> -#include <linux/blkdev.h> -#include <linux/init.h> -#include <linux/stat.h> -#include <linux/io.h> - -#include <asm/dma.h> - -#include <scsi/scsi.h> -#include <scsi/scsi_cmnd.h> -#include <scsi/scsi_device.h> -#include <scsi/scsi_host.h> -#include <scsi/scsicam.h> - - -#undef WD7000_DEBUG /* general debug */ -#ifdef WD7000_DEBUG -#define dprintk printk -#else -#define dprintk no_printk -#endif - -/* - * Mailbox structure sizes. - * I prefer to keep the number of ICMBs much larger than the number of - * OGMBs. OGMBs are used very quickly by the driver to start one or - * more commands, while ICMBs are used by the host adapter per command. - */ -#define OGMB_CNT 16 -#define ICMB_CNT 32 - -/* - * Scb's are shared by all active adapters. So, if they all become busy, - * callers may be made to wait in alloc_scbs for them to free. That can - * be avoided by setting MAX_SCBS to NUM_CONFIG * WD7000_Q. If you'd - * rather conserve memory, use a smaller number (> 0, of course) - things - * will should still work OK. - */ -#define MAX_SCBS 32 - -/* - * In this version, sg_tablesize now defaults to WD7000_SG, and will - * be set to SG_NONE for older boards. This is the reverse of the - * previous default, and was changed so that the driver-level - * scsi_host_template would reflect the driver's support for scatter/ - * gather. - * - * Also, it has been reported that boards at Revision 6 support scatter/ - * gather, so the new definition of an "older" board has been changed - * accordingly. - */ -#define WD7000_Q 16 -#define WD7000_SG 16 - - -/* - * WD7000-specific mailbox structure - * - */ -typedef volatile struct mailbox { - unchar status; - unchar scbptr[3]; /* SCSI-style - MSB first (big endian) */ -} Mailbox; - -/* - * This structure should contain all per-adapter global data. I.e., any - * new global per-adapter data should put in here. - */ -typedef struct adapter { - struct Scsi_Host *sh; /* Pointer to Scsi_Host structure */ - int iobase; /* This adapter's I/O base address */ - int irq; /* This adapter's IRQ level */ - int dma; /* This adapter's DMA channel */ - int int_counter; /* This adapter's interrupt counter */ - int bus_on; /* This adapter's BUS_ON time */ - int bus_off; /* This adapter's BUS_OFF time */ - struct { /* This adapter's mailboxes */ - Mailbox ogmb[OGMB_CNT]; /* Outgoing mailboxes */ - Mailbox icmb[ICMB_CNT]; /* Incoming mailboxes */ - } mb; - int next_ogmb; /* to reduce contention at mailboxes */ - unchar control; /* shadows CONTROL port value */ - unchar rev1, rev2; /* filled in by wd7000_revision */ -} Adapter; - -/* - * (linear) base address for ROM BIOS - */ -static const long wd7000_biosaddr[] = { - 0xc0000, 0xc2000, 0xc4000, 0xc6000, 0xc8000, 0xca000, 0xcc000, 0xce000, - 0xd0000, 0xd2000, 0xd4000, 0xd6000, 0xd8000, 0xda000, 0xdc000, 0xde000 -}; -#define NUM_ADDRS ARRAY_SIZE(wd7000_biosaddr) - -static const unsigned short wd7000_iobase[] = { - 0x0300, 0x0308, 0x0310, 0x0318, 0x0320, 0x0328, 0x0330, 0x0338, - 0x0340, 0x0348, 0x0350, 0x0358, 0x0360, 0x0368, 0x0370, 0x0378, - 0x0380, 0x0388, 0x0390, 0x0398, 0x03a0, 0x03a8, 0x03b0, 0x03b8, - 0x03c0, 0x03c8, 0x03d0, 0x03d8, 0x03e0, 0x03e8, 0x03f0, 0x03f8 -}; -#define NUM_IOPORTS ARRAY_SIZE(wd7000_iobase) - -static const short wd7000_irq[] = { 3, 4, 5, 7, 9, 10, 11, 12, 14, 15 }; -#define NUM_IRQS ARRAY_SIZE(wd7000_irq) - -static const short wd7000_dma[] = { 5, 6, 7 }; -#define NUM_DMAS ARRAY_SIZE(wd7000_dma) - -/* - * The following is set up by wd7000_detect, and used thereafter for - * proc and other global ookups - */ - -#define UNITS 8 -static struct Scsi_Host *wd7000_host[UNITS]; - -#define BUS_ON 64 /* x 125ns = 8000ns (BIOS default) */ -#define BUS_OFF 15 /* x 125ns = 1875ns (BIOS default) */ - -/* - * Standard Adapter Configurations - used by wd7000_detect - */ -typedef struct { - short irq; /* IRQ level */ - short dma; /* DMA channel */ - unsigned iobase; /* I/O base address */ - short bus_on; /* Time that WD7000 spends on the AT-bus when */ - /* transferring data. BIOS default is 8000ns. */ - short bus_off; /* Time that WD7000 spends OFF THE BUS after */ - /* while it is transferring data. */ - /* BIOS default is 1875ns */ -} Config; - -/* - * Add here your configuration... - */ -static Config configs[] = { - {15, 6, 0x350, BUS_ON, BUS_OFF}, /* defaults for single adapter */ - {11, 5, 0x320, BUS_ON, BUS_OFF}, /* defaults for second adapter */ - {7, 6, 0x350, BUS_ON, BUS_OFF}, /* My configuration (Zaga) */ - {-1, -1, 0x0, BUS_ON, BUS_OFF} /* Empty slot */ -}; -#define NUM_CONFIGS ARRAY_SIZE(configs) - -/* - * The following list defines strings to look for in the BIOS that identify - * it as the WD7000-FASST2 SST BIOS. I suspect that something should be - * added for the Future Domain version. - */ -typedef struct signature { - const char *sig; /* String to look for */ - unsigned long ofs; /* offset from BIOS base address */ - unsigned len; /* length of string */ -} Signature; - -static const Signature signatures[] = { - {"SSTBIOS", 0x0000d, 7} /* "SSTBIOS" @ offset 0x0000d */ -}; -#define NUM_SIGNATURES ARRAY_SIZE(signatures) - - -/* - * I/O Port Offsets and Bit Definitions - * 4 addresses are used. Those not defined here are reserved. - */ -#define ASC_STAT 0 /* Status, Read */ -#define ASC_COMMAND 0 /* Command, Write */ -#define ASC_INTR_STAT 1 /* Interrupt Status, Read */ -#define ASC_INTR_ACK 1 /* Acknowledge, Write */ -#define ASC_CONTROL 2 /* Control, Write */ - -/* - * ASC Status Port - */ -#define INT_IM 0x80 /* Interrupt Image Flag */ -#define CMD_RDY 0x40 /* Command Port Ready */ -#define CMD_REJ 0x20 /* Command Port Byte Rejected */ -#define ASC_INIT 0x10 /* ASC Initialized Flag */ -#define ASC_STATMASK 0xf0 /* The lower 4 Bytes are reserved */ - -/* - * COMMAND opcodes - * - * Unfortunately, I have no idea how to properly use some of these commands, - * as the OEM manual does not make it clear. I have not been able to use - * enable/disable unsolicited interrupts or the reset commands with any - * discernible effect whatsoever. I think they may be related to certain - * ICB commands, but again, the OEM manual doesn't make that clear. - */ -#define NO_OP 0 /* NO-OP toggles CMD_RDY bit in ASC_STAT */ -#define INITIALIZATION 1 /* initialization (10 bytes) */ -#define DISABLE_UNS_INTR 2 /* disable unsolicited interrupts */ -#define ENABLE_UNS_INTR 3 /* enable unsolicited interrupts */ -#define INTR_ON_FREE_OGMB 4 /* interrupt on free OGMB */ -#define SOFT_RESET 5 /* SCSI bus soft reset */ -#define HARD_RESET_ACK 6 /* SCSI bus hard reset acknowledge */ -#define START_OGMB 0x80 /* start command in OGMB (n) */ -#define SCAN_OGMBS 0xc0 /* start multiple commands, signature (n) */ - /* where (n) = lower 6 bits */ -/* - * For INITIALIZATION: - */ -typedef struct initCmd { - unchar op; /* command opcode (= 1) */ - unchar ID; /* Adapter's SCSI ID */ - unchar bus_on; /* Bus on time, x 125ns (see below) */ - unchar bus_off; /* Bus off time, "" "" */ - unchar rsvd; /* Reserved */ - unchar mailboxes[3]; /* Address of Mailboxes, MSB first */ - unchar ogmbs; /* Number of outgoing MBs, max 64, 0,1 = 1 */ - unchar icmbs; /* Number of incoming MBs, "" "" */ -} InitCmd; - -/* - * Interrupt Status Port - also returns diagnostic codes at ASC reset - * - * if msb is zero, the lower bits are diagnostic status - * Diagnostics: - * 01 No diagnostic error occurred - * 02 RAM failure - * 03 FIFO R/W failed - * 04 SBIC register read/write failed - * 05 Initialization D-FF failed - * 06 Host IRQ D-FF failed - * 07 ROM checksum error - * Interrupt status (bitwise): - * 10NNNNNN outgoing mailbox NNNNNN is free - * 11NNNNNN incoming mailbox NNNNNN needs service - */ -#define MB_INTR 0xC0 /* Mailbox Service possible/required */ -#define IMB_INTR 0x40 /* 1 Incoming / 0 Outgoing */ -#define MB_MASK 0x3f /* mask for mailbox number */ - -/* - * CONTROL port bits - */ -#define INT_EN 0x08 /* Interrupt Enable */ -#define DMA_EN 0x04 /* DMA Enable */ -#define SCSI_RES 0x02 /* SCSI Reset */ -#define ASC_RES 0x01 /* ASC Reset */ - -/* - * Driver data structures: - * - mb and scbs are required for interfacing with the host adapter. - * An SCB has extra fields not visible to the adapter; mb's - * _cannot_ do this, since the adapter assumes they are contiguous in - * memory, 4 bytes each, with ICMBs following OGMBs, and uses this fact - * to access them. - * - An icb is for host-only (non-SCSI) commands. ICBs are 16 bytes each; - * the additional bytes are used only by the driver. - * - For now, a pool of SCBs are kept in global storage by this driver, - * and are allocated and freed as needed. - * - * The 7000-FASST2 marks OGMBs empty as soon as it has _started_ a command, - * not when it has finished. Since the SCB must be around for completion, - * problems arise when SCBs correspond to OGMBs, which may be reallocated - * earlier (or delayed unnecessarily until a command completes). - * Mailboxes are used as transient data structures, simply for - * carrying SCB addresses to/from the 7000-FASST2. - * - * Note also since SCBs are not "permanently" associated with mailboxes, - * there is no need to keep a global list of scsi_cmnd pointers indexed - * by OGMB. Again, SCBs reference their scsi_cmnds directly, so mailbox - * indices need not be involved. - */ - -/* - * WD7000-specific scatter/gather element structure - */ -typedef struct sgb { - unchar len[3]; - unchar ptr[3]; /* Also SCSI-style - MSB first */ -} Sgb; - -typedef struct scb { /* Command Control Block 5.4.1 */ - unchar op; /* Command Control Block Operation Code */ - unchar idlun; /* op=0,2:Target Id, op=1:Initiator Id */ - /* Outbound data transfer, length is checked */ - /* Inbound data transfer, length is checked */ - /* Logical Unit Number */ - unchar cdb[12]; /* SCSI Command Block */ - volatile unchar status; /* SCSI Return Status */ - volatile unchar vue; /* Vendor Unique Error Code */ - unchar maxlen[3]; /* Maximum Data Transfer Length */ - unchar dataptr[3]; /* SCSI Data Block Pointer */ - unchar linkptr[3]; /* Next Command Link Pointer */ - unchar direc; /* Transfer Direction */ - unchar reserved2[6]; /* SCSI Command Descriptor Block */ - /* end of hardware SCB */ - struct scsi_cmnd *SCpnt;/* scsi_cmnd using this SCB */ - Sgb sgb[WD7000_SG]; /* Scatter/gather list for this SCB */ - Adapter *host; /* host adapter */ - struct scb *next; /* for lists of scbs */ -} Scb; - -/* - * This driver is written to allow host-only commands to be executed. - * These use a 16-byte block called an ICB. The format is extended by the - * driver to 18 bytes, to support the status returned in the ICMB and - * an execution phase code. - * - * There are other formats besides these; these are the ones I've tried - * to use. Formats for some of the defined ICB opcodes are not defined - * (notably, get/set unsolicited interrupt status) in my copy of the OEM - * manual, and others are ambiguous/hard to follow. - */ -#define ICB_OP_MASK 0x80 /* distinguishes scbs from icbs */ -#define ICB_OP_OPEN_RBUF 0x80 /* open receive buffer */ -#define ICB_OP_RECV_CMD 0x81 /* receive command from initiator */ -#define ICB_OP_RECV_DATA 0x82 /* receive data from initiator */ -#define ICB_OP_RECV_SDATA 0x83 /* receive data with status from init. */ -#define ICB_OP_SEND_DATA 0x84 /* send data with status to initiator */ -#define ICB_OP_SEND_STAT 0x86 /* send command status to initiator */ - /* 0x87 is reserved */ -#define ICB_OP_READ_INIT 0x88 /* read initialization bytes */ -#define ICB_OP_READ_ID 0x89 /* read adapter's SCSI ID */ -#define ICB_OP_SET_UMASK 0x8A /* set unsolicited interrupt mask */ -#define ICB_OP_GET_UMASK 0x8B /* read unsolicited interrupt mask */ -#define ICB_OP_GET_REVISION 0x8C /* read firmware revision level */ -#define ICB_OP_DIAGNOSTICS 0x8D /* execute diagnostics */ -#define ICB_OP_SET_EPARMS 0x8E /* set execution parameters */ -#define ICB_OP_GET_EPARMS 0x8F /* read execution parameters */ - -typedef struct icbRecvCmd { - unchar op; - unchar IDlun; /* Initiator SCSI ID/lun */ - unchar len[3]; /* command buffer length */ - unchar ptr[3]; /* command buffer address */ - unchar rsvd[7]; /* reserved */ - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ -} IcbRecvCmd; - -typedef struct icbSendStat { - unchar op; - unchar IDlun; /* Target SCSI ID/lun */ - unchar stat; /* (outgoing) completion status byte 1 */ - unchar rsvd[12]; /* reserved */ - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ -} IcbSendStat; - -typedef struct icbRevLvl { - unchar op; - volatile unchar primary; /* primary revision level (returned) */ - volatile unchar secondary; /* secondary revision level (returned) */ - unchar rsvd[12]; /* reserved */ - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ -} IcbRevLvl; - -typedef struct icbUnsMask { /* I'm totally guessing here */ - unchar op; - volatile unchar mask[14]; /* mask bits */ -#if 0 - unchar rsvd[12]; /* reserved */ -#endif - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ -} IcbUnsMask; - -typedef struct icbDiag { - unchar op; - unchar type; /* diagnostics type code (0-3) */ - unchar len[3]; /* buffer length */ - unchar ptr[3]; /* buffer address */ - unchar rsvd[7]; /* reserved */ - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ -} IcbDiag; - -#define ICB_DIAG_POWERUP 0 /* Power-up diags only */ -#define ICB_DIAG_WALKING 1 /* walking 1's pattern */ -#define ICB_DIAG_DMA 2 /* DMA - system memory diags */ -#define ICB_DIAG_FULL 3 /* do both 1 & 2 */ - -typedef struct icbParms { - unchar op; - unchar rsvd1; /* reserved */ - unchar len[3]; /* parms buffer length */ - unchar ptr[3]; /* parms buffer address */ - unchar idx[2]; /* index (MSB-LSB) */ - unchar rsvd2[5]; /* reserved */ - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ -} IcbParms; - -typedef struct icbAny { - unchar op; - unchar data[14]; /* format-specific data */ - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ -} IcbAny; - -typedef union icb { - unchar op; /* ICB opcode */ - IcbRecvCmd recv_cmd; /* format for receive command */ - IcbSendStat send_stat; /* format for send status */ - IcbRevLvl rev_lvl; /* format for get revision level */ - IcbDiag diag; /* format for execute diagnostics */ - IcbParms eparms; /* format for get/set exec parms */ - IcbAny icb; /* generic format */ - unchar data[18]; -} Icb; - -#ifdef MODULE -static char *wd7000; -module_param(wd7000, charp, 0); -#endif - -/* - * Driver SCB structure pool. - * - * The SCBs declared here are shared by all host adapters; hence, this - * structure is not part of the Adapter structure. - */ -static Scb scbs[MAX_SCBS]; -static Scb *scbfree; /* free list */ -static int freescbs = MAX_SCBS; /* free list counter */ -static spinlock_t scbpool_lock; /* guards the scb free list and count */ - -/* - * END of data/declarations - code follows. - */ -static void __init setup_error(char *mesg, int *ints) -{ - if (ints[0] == 3) - printk(KERN_ERR "wd7000_setup: \"wd7000=%d,%d,0x%x\" -> %s\n", ints[1], ints[2], ints[3], mesg); - else if (ints[0] == 4) - printk(KERN_ERR "wd7000_setup: \"wd7000=%d,%d,0x%x,%d\" -> %s\n", ints[1], ints[2], ints[3], ints[4], mesg); - else - printk(KERN_ERR "wd7000_setup: \"wd7000=%d,%d,0x%x,%d,%d\" -> %s\n", ints[1], ints[2], ints[3], ints[4], ints[5], mesg); -} - - -/* - * Note: You can now set these options from the kernel's "command line". - * The syntax is: - * - * wd7000=<IRQ>,<DMA>,<IO>[,<BUS_ON>[,<BUS_OFF>]] - * - * , where BUS_ON and BUS_OFF are in nanoseconds. BIOS default values - * are 8000ns for BUS_ON and 1875ns for BUS_OFF. - * eg: - * wd7000=7,6,0x350 - * - * will configure the driver for a WD-7000 controller - * using IRQ 15 with a DMA channel 6, at IO base address 0x350. - */ -static int __init wd7000_setup(char *str) -{ - static short wd7000_card_num; /* .bss will zero this */ - short i; - int ints[6]; - - (void) get_options(str, ARRAY_SIZE(ints), ints); - - if (wd7000_card_num >= NUM_CONFIGS) { - printk(KERN_ERR "%s: Too many \"wd7000=\" configurations in " "command line!\n", __func__); - return 0; - } - - if ((ints[0] < 3) || (ints[0] > 5)) { - printk(KERN_ERR "%s: Error in command line! " "Usage: wd7000=<IRQ>,<DMA>,IO>[,<BUS_ON>" "[,<BUS_OFF>]]\n", __func__); - } else { - for (i = 0; i < NUM_IRQS; i++) - if (ints[1] == wd7000_irq[i]) - break; - - if (i == NUM_IRQS) { - setup_error("invalid IRQ.", ints); - return 0; - } else - configs[wd7000_card_num].irq = ints[1]; - - for (i = 0; i < NUM_DMAS; i++) - if (ints[2] == wd7000_dma[i]) - break; - - if (i == NUM_DMAS) { - setup_error("invalid DMA channel.", ints); - return 0; - } else - configs[wd7000_card_num].dma = ints[2]; - - for (i = 0; i < NUM_IOPORTS; i++) - if (ints[3] == wd7000_iobase[i]) - break; - - if (i == NUM_IOPORTS) { - setup_error("invalid I/O base address.", ints); - return 0; - } else - configs[wd7000_card_num].iobase = ints[3]; - - if (ints[0] > 3) { - if ((ints[4] < 500) || (ints[4] > 31875)) { - setup_error("BUS_ON value is out of range (500" " to 31875 nanoseconds)!", ints); - configs[wd7000_card_num].bus_on = BUS_ON; - } else - configs[wd7000_card_num].bus_on = ints[4] / 125; - } else - configs[wd7000_card_num].bus_on = BUS_ON; - - if (ints[0] > 4) { - if ((ints[5] < 500) || (ints[5] > 31875)) { - setup_error("BUS_OFF value is out of range (500" " to 31875 nanoseconds)!", ints); - configs[wd7000_card_num].bus_off = BUS_OFF; - } else - configs[wd7000_card_num].bus_off = ints[5] / 125; - } else - configs[wd7000_card_num].bus_off = BUS_OFF; - - if (wd7000_card_num) { - for (i = 0; i < (wd7000_card_num - 1); i++) { - int j = i + 1; - - for (; j < wd7000_card_num; j++) - if (configs[i].irq == configs[j].irq) { - setup_error("duplicated IRQ!", ints); - return 0; - } - if (configs[i].dma == configs[j].dma) { - setup_error("duplicated DMA " "channel!", ints); - return 0; - } - if (configs[i].iobase == configs[j].iobase) { - setup_error("duplicated I/O " "base address!", ints); - return 0; - } - } - } - - dprintk(KERN_DEBUG "wd7000_setup: IRQ=%d, DMA=%d, I/O=0x%x, " - "BUS_ON=%dns, BUS_OFF=%dns\n", configs[wd7000_card_num].irq, configs[wd7000_card_num].dma, configs[wd7000_card_num].iobase, configs[wd7000_card_num].bus_on * 125, configs[wd7000_card_num].bus_off * 125); - - wd7000_card_num++; - } - return 1; -} - -__setup("wd7000=", wd7000_setup); - -static inline void any2scsi(unchar * scsi, int any) -{ - *scsi++ = (unsigned)any >> 16; - *scsi++ = (unsigned)any >> 8; - *scsi++ = any; -} - -static inline int scsi2int(unchar * scsi) -{ - return (scsi[0] << 16) | (scsi[1] << 8) | scsi[2]; -} - -static inline void wd7000_enable_intr(Adapter * host) -{ - host->control |= INT_EN; - outb(host->control, host->iobase + ASC_CONTROL); -} - - -static inline void wd7000_enable_dma(Adapter * host) -{ - unsigned long flags; - host->control |= DMA_EN; - outb(host->control, host->iobase + ASC_CONTROL); - - flags = claim_dma_lock(); - set_dma_mode(host->dma, DMA_MODE_CASCADE); - enable_dma(host->dma); - release_dma_lock(flags); - -} - - -#define WAITnexttimeout 200 /* 2 seconds */ - -static inline short WAIT(unsigned port, unsigned mask, unsigned allof, unsigned noneof) -{ - unsigned WAITbits; - unsigned long WAITtimeout = jiffies + WAITnexttimeout; - - while (time_before_eq(jiffies, WAITtimeout)) { - WAITbits = inb(port) & mask; - - if (((WAITbits & allof) == allof) && ((WAITbits & noneof) == 0)) - return (0); - } - - return (1); -} - - -static inline int command_out(Adapter * host, unchar * cmd, int len) -{ - if (!WAIT(host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) { - while (len--) { - do { - outb(*cmd, host->iobase + ASC_COMMAND); - WAIT(host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0); - } while (inb(host->iobase + ASC_STAT) & CMD_REJ); - - cmd++; - } - - return (1); - } - - printk(KERN_WARNING "wd7000 command_out: WAIT failed(%d)\n", len + 1); - - return (0); -} - - -/* - * This version of alloc_scbs is in preparation for supporting multiple - * commands per lun and command chaining, by queueing pending commands. - * We will need to allocate Scbs in blocks since they will wait to be - * executed so there is the possibility of deadlock otherwise. - * Also, to keep larger requests from being starved by smaller requests, - * we limit access to this routine with an internal busy flag, so that - * the satisfiability of a request is not dependent on the size of the - * request. - */ -static inline Scb *alloc_scbs(struct Scsi_Host *host, int needed) -{ - Scb *scb, *p = NULL; - unsigned long flags; - unsigned long timeout = jiffies + WAITnexttimeout; - unsigned long now; - int i; - - if (needed <= 0) - return (NULL); /* sanity check */ - - spin_unlock_irq(host->host_lock); - - retry: - while (freescbs < needed) { - timeout = jiffies + WAITnexttimeout; - do { - /* FIXME: can we actually just yield here ?? */ - for (now = jiffies; now == jiffies;) - cpu_relax(); /* wait a jiffy */ - } while (freescbs < needed && time_before_eq(jiffies, timeout)); - /* - * If we get here with enough free Scbs, we can take them. - * Otherwise, we timed out and didn't get enough. - */ - if (freescbs < needed) { - printk(KERN_ERR "wd7000: can't get enough free SCBs.\n"); - return (NULL); - } - } - - /* Take the lock, then check we didn't get beaten, if so try again */ - spin_lock_irqsave(&scbpool_lock, flags); - if (freescbs < needed) { - spin_unlock_irqrestore(&scbpool_lock, flags); - goto retry; - } - - scb = scbfree; - freescbs -= needed; - for (i = 0; i < needed; i++) { - p = scbfree; - scbfree = p->next; - } - p->next = NULL; - - spin_unlock_irqrestore(&scbpool_lock, flags); - - spin_lock_irq(host->host_lock); - return (scb); -} - - -static inline void free_scb(Scb * scb) -{ - unsigned long flags; - - spin_lock_irqsave(&scbpool_lock, flags); - - memset(scb, 0, sizeof(Scb)); - scb->next = scbfree; - scbfree = scb; - freescbs++; - - spin_unlock_irqrestore(&scbpool_lock, flags); -} - - -static inline void init_scbs(void) -{ - int i; - - spin_lock_init(&scbpool_lock); - - /* This is only ever called before the SCB pool is active */ - - scbfree = &(scbs[0]); - memset(scbs, 0, sizeof(scbs)); - for (i = 0; i < MAX_SCBS - 1; i++) { - scbs[i].next = &(scbs[i + 1]); - scbs[i].SCpnt = NULL; - } - scbs[MAX_SCBS - 1].next = NULL; - scbs[MAX_SCBS - 1].SCpnt = NULL; -} - - -static int mail_out(Adapter * host, Scb * scbptr) -/* - * Note: this can also be used for ICBs; just cast to the parm type. - */ -{ - int i, ogmb; - unsigned long flags; - unchar start_ogmb; - Mailbox *ogmbs = host->mb.ogmb; - int *next_ogmb = &(host->next_ogmb); - - dprintk("wd7000_mail_out: 0x%06lx", (long) scbptr); - - /* We first look for a free outgoing mailbox */ - spin_lock_irqsave(host->sh->host_lock, flags); - ogmb = *next_ogmb; - for (i = 0; i < OGMB_CNT; i++) { - if (ogmbs[ogmb].status == 0) { - dprintk(" using OGMB 0x%x", ogmb); - ogmbs[ogmb].status = 1; - any2scsi((unchar *) ogmbs[ogmb].scbptr, (int) scbptr); - - *next_ogmb = (ogmb + 1) % OGMB_CNT; - break; - } else - ogmb = (ogmb + 1) % OGMB_CNT; - } - spin_unlock_irqrestore(host->sh->host_lock, flags); - - dprintk(", scb is 0x%06lx", (long) scbptr); - - if (i >= OGMB_CNT) { - /* - * Alternatively, we might issue the "interrupt on free OGMB", - * and sleep, but it must be ensured that it isn't the init - * task running. Instead, this version assumes that the caller - * will be persistent, and try again. Since it's the adapter - * that marks OGMB's free, waiting even with interrupts off - * should work, since they are freed very quickly in most cases. - */ - dprintk(", no free OGMBs.\n"); - return (0); - } - - wd7000_enable_intr(host); - - start_ogmb = START_OGMB | ogmb; - command_out(host, &start_ogmb, 1); - - dprintk(", awaiting interrupt.\n"); - - return (1); -} - - -static int make_code(unsigned hosterr, unsigned scsierr) -{ -#ifdef WD7000_DEBUG - int in_error = hosterr; -#endif - - switch ((hosterr >> 8) & 0xff) { - case 0: /* Reserved */ - hosterr = DID_ERROR; - break; - case 1: /* Command Complete, no errors */ - hosterr = DID_OK; - break; - case 2: /* Command complete, error logged in scb status (scsierr) */ - hosterr = DID_OK; - break; - case 4: /* Command failed to complete - timeout */ - hosterr = DID_TIME_OUT; - break; - case 5: /* Command terminated; Bus reset by external device */ - hosterr = DID_RESET; - break; - case 6: /* Unexpected Command Received w/ host as target */ - hosterr = DID_BAD_TARGET; - break; - case 80: /* Unexpected Reselection */ - case 81: /* Unexpected Selection */ - hosterr = DID_BAD_INTR; - break; - case 82: /* Abort Command Message */ - hosterr = DID_ABORT; - break; - case 83: /* SCSI Bus Software Reset */ - case 84: /* SCSI Bus Hardware Reset */ - hosterr = DID_RESET; - break; - default: /* Reserved */ - hosterr = DID_ERROR; - } -#ifdef WD7000_DEBUG - if (scsierr || hosterr) - dprintk("\nSCSI command error: SCSI 0x%02x host 0x%04x return %d\n", scsierr, in_error, hosterr); -#endif - return (scsierr | (hosterr << 16)); -} - -#define wd7000_intr_ack(host) outb (0, host->iobase + ASC_INTR_ACK) - - -static irqreturn_t wd7000_intr(int irq, void *dev_id) -{ - Adapter *host = (Adapter *) dev_id; - int flag, icmb, errstatus, icmb_status; - int host_error, scsi_error; - Scb *scb; /* for SCSI commands */ - IcbAny *icb; /* for host commands */ - struct scsi_cmnd *SCpnt; - Mailbox *icmbs = host->mb.icmb; - unsigned long flags; - - spin_lock_irqsave(host->sh->host_lock, flags); - host->int_counter++; - - dprintk("wd7000_intr: irq = %d, host = 0x%06lx\n", irq, (long) host); - - flag = inb(host->iobase + ASC_INTR_STAT); - - dprintk("wd7000_intr: intr stat = 0x%02x\n", flag); - - if (!(inb(host->iobase + ASC_STAT) & INT_IM)) { - /* NB: these are _very_ possible if IRQ 15 is being used, since - * it's the "garbage collector" on the 2nd 8259 PIC. Specifically, - * any interrupt signal into the 8259 which can't be identified - * comes out as 7 from the 8259, which is 15 to the host. Thus, it - * is a good thing the WD7000 has an interrupt status port, so we - * can sort these out. Otherwise, electrical noise and other such - * problems would be indistinguishable from valid interrupts... - */ - dprintk("wd7000_intr: phantom interrupt...\n"); - goto ack; - } - - if (!(flag & MB_INTR)) - goto ack; - - /* The interrupt is for a mailbox */ - if (!(flag & IMB_INTR)) { - dprintk("wd7000_intr: free outgoing mailbox\n"); - /* - * If sleep_on() and the "interrupt on free OGMB" command are - * used in mail_out(), wake_up() should correspondingly be called - * here. For now, we don't need to do anything special. - */ - goto ack; - } - - /* The interrupt is for an incoming mailbox */ - icmb = flag & MB_MASK; - icmb_status = icmbs[icmb].status; - if (icmb_status & 0x80) { /* unsolicited - result in ICMB */ - dprintk("wd7000_intr: unsolicited interrupt 0x%02x\n", icmb_status); - goto ack; - } - - /* Aaaargh! (Zaga) */ - scb = isa_bus_to_virt(scsi2int((unchar *) icmbs[icmb].scbptr)); - icmbs[icmb].status = 0; - if (scb->op & ICB_OP_MASK) { /* an SCB is done */ - icb = (IcbAny *) scb; - icb->status = icmb_status; - icb->phase = 0; - goto ack; - } - - SCpnt = scb->SCpnt; - if (--(SCpnt->SCp.phase) <= 0) { /* all scbs are done */ - host_error = scb->vue | (icmb_status << 8); - scsi_error = scb->status; - errstatus = make_code(host_error, scsi_error); - SCpnt->result = errstatus; - - free_scb(scb); - - SCpnt->scsi_done(SCpnt); - } - - ack: - dprintk("wd7000_intr: return from interrupt handler\n"); - wd7000_intr_ack(host); - - spin_unlock_irqrestore(host->sh->host_lock, flags); - return IRQ_HANDLED; -} - -static int wd7000_queuecommand_lck(struct scsi_cmnd *SCpnt, - void (*done)(struct scsi_cmnd *)) -{ - Scb *scb; - Sgb *sgb; - unchar *cdb = (unchar *) SCpnt->cmnd; - unchar idlun; - short cdblen; - int nseg; - Adapter *host = (Adapter *) SCpnt->device->host->hostdata; - - cdblen = SCpnt->cmd_len; - idlun = ((SCpnt->device->id << 5) & 0xe0) | (SCpnt->device->lun & 7); - SCpnt->scsi_done = done; - SCpnt->SCp.phase = 1; - scb = alloc_scbs(SCpnt->device->host, 1); - scb->idlun = idlun; - memcpy(scb->cdb, cdb, cdblen); - scb->direc = 0x40; /* Disable direction check */ - - scb->SCpnt = SCpnt; /* so we can find stuff later */ - SCpnt->host_scribble = (unchar *) scb; - scb->host = host; - - nseg = scsi_sg_count(SCpnt); - if (nseg > 1) { - struct scatterlist *sg; - unsigned i; - - dprintk("Using scatter/gather with %d elements.\n", nseg); - - sgb = scb->sgb; - scb->op = 1; - any2scsi(scb->dataptr, (int) sgb); - any2scsi(scb->maxlen, nseg * sizeof(Sgb)); - - scsi_for_each_sg(SCpnt, sg, nseg, i) { - any2scsi(sgb[i].ptr, isa_page_to_bus(sg_page(sg)) + sg->offset); - any2scsi(sgb[i].len, sg->length); - } - } else { - scb->op = 0; - if (nseg) { - struct scatterlist *sg = scsi_sglist(SCpnt); - any2scsi(scb->dataptr, isa_page_to_bus(sg_page(sg)) + sg->offset); - } - any2scsi(scb->maxlen, scsi_bufflen(SCpnt)); - } - - /* FIXME: drop lock and yield here ? */ - - while (!mail_out(host, scb)) - cpu_relax(); /* keep trying */ - - return 0; -} - -static DEF_SCSI_QCMD(wd7000_queuecommand) - -static int wd7000_diagnostics(Adapter * host, int code) -{ - static IcbDiag icb = { ICB_OP_DIAGNOSTICS }; - static unchar buf[256]; - unsigned long timeout; - - icb.type = code; - any2scsi(icb.len, sizeof(buf)); - any2scsi(icb.ptr, (int) &buf); - icb.phase = 1; - /* - * This routine is only called at init, so there should be OGMBs - * available. I'm assuming so here. If this is going to - * fail, I can just let the timeout catch the failure. - */ - mail_out(host, (struct scb *) &icb); - timeout = jiffies + WAITnexttimeout; /* wait up to 2 seconds */ - while (icb.phase && time_before(jiffies, timeout)) { - cpu_relax(); /* wait for completion */ - barrier(); - } - - if (icb.phase) { - printk("wd7000_diagnostics: timed out.\n"); - return (0); - } - if (make_code(icb.vue | (icb.status << 8), 0)) { - printk("wd7000_diagnostics: failed (0x%02x,0x%02x)\n", icb.vue, icb.status); - return (0); - } - - return (1); -} - - -static int wd7000_adapter_reset(Adapter * host) -{ - InitCmd init_cmd = { - INITIALIZATION, - 7, - host->bus_on, - host->bus_off, - 0, - {0, 0, 0}, - OGMB_CNT, - ICMB_CNT - }; - int diag; - /* - * Reset the adapter - only. The SCSI bus was initialized at power-up, - * and we need to do this just so we control the mailboxes, etc. - */ - outb(ASC_RES, host->iobase + ASC_CONTROL); - udelay(40); /* reset pulse: this is 40us, only need 25us */ - outb(0, host->iobase + ASC_CONTROL); - host->control = 0; /* this must always shadow ASC_CONTROL */ - - if (WAIT(host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) { - printk(KERN_ERR "wd7000_init: WAIT timed out.\n"); - return -1; /* -1 = not ok */ - } - - if ((diag = inb(host->iobase + ASC_INTR_STAT)) != 1) { - printk("wd7000_init: "); - - switch (diag) { - case 2: - printk(KERN_ERR "RAM failure.\n"); - break; - case 3: - printk(KERN_ERR "FIFO R/W failed\n"); - break; - case 4: - printk(KERN_ERR "SBIC register R/W failed\n"); - break; - case 5: - printk(KERN_ERR "Initialization D-FF failed.\n"); - break; - case 6: - printk(KERN_ERR "Host IRQ D-FF failed.\n"); - break; - case 7: - printk(KERN_ERR "ROM checksum error.\n"); - break; - default: - printk(KERN_ERR "diagnostic code 0x%02Xh received.\n", diag); - } - return -1; - } - /* Clear mailboxes */ - memset(&(host->mb), 0, sizeof(host->mb)); - - /* Execute init command */ - any2scsi((unchar *) & (init_cmd.mailboxes), (int) &(host->mb)); - if (!command_out(host, (unchar *) & init_cmd, sizeof(init_cmd))) { - printk(KERN_ERR "wd7000_adapter_reset: adapter initialization failed.\n"); - return -1; - } - - if (WAIT(host->iobase + ASC_STAT, ASC_STATMASK, ASC_INIT, 0)) { - printk("wd7000_adapter_reset: WAIT timed out.\n"); - return -1; - } - return 0; -} - -static int wd7000_init(Adapter * host) -{ - if (wd7000_adapter_reset(host) == -1) - return 0; - - - if (request_irq(host->irq, wd7000_intr, 0, "wd7000", host)) { - printk("wd7000_init: can't get IRQ %d.\n", host->irq); - return (0); - } - if (request_dma(host->dma, "wd7000")) { - printk("wd7000_init: can't get DMA channel %d.\n", host->dma); - free_irq(host->irq, host); - return (0); - } - wd7000_enable_dma(host); - wd7000_enable_intr(host); - - if (!wd7000_diagnostics(host, ICB_DIAG_FULL)) { - free_dma(host->dma); - free_irq(host->irq, NULL); - return (0); - } - - return (1); -} - - -static void wd7000_revision(Adapter * host) -{ - static IcbRevLvl icb = { ICB_OP_GET_REVISION }; - - icb.phase = 1; - /* - * Like diagnostics, this is only done at init time, in fact, from - * wd7000_detect, so there should be OGMBs available. If it fails, - * the only damage will be that the revision will show up as 0.0, - * which in turn means that scatter/gather will be disabled. - */ - mail_out(host, (struct scb *) &icb); - while (icb.phase) { - cpu_relax(); /* wait for completion */ - barrier(); - } - host->rev1 = icb.primary; - host->rev2 = icb.secondary; -} - - -static int wd7000_set_info(struct Scsi_Host *host, char *buffer, int length) -{ - dprintk("Buffer = <%.*s>, length = %d\n", length, buffer, length); - - /* - * Currently this is a no-op - */ - dprintk("Sorry, this function is currently out of order...\n"); - return (length); -} - - -static int wd7000_show_info(struct seq_file *m, struct Scsi_Host *host) -{ - Adapter *adapter = (Adapter *)host->hostdata; - unsigned long flags; -#ifdef WD7000_DEBUG - Mailbox *ogmbs, *icmbs; - short count; -#endif - - spin_lock_irqsave(host->host_lock, flags); - seq_printf(m, "Host scsi%d: Western Digital WD-7000 (rev %d.%d)\n", host->host_no, adapter->rev1, adapter->rev2); - seq_printf(m, " IO base: 0x%x\n", adapter->iobase); - seq_printf(m, " IRQ: %d\n", adapter->irq); - seq_printf(m, " DMA channel: %d\n", adapter->dma); - seq_printf(m, " Interrupts: %d\n", adapter->int_counter); - seq_printf(m, " BUS_ON time: %d nanoseconds\n", adapter->bus_on * 125); - seq_printf(m, " BUS_OFF time: %d nanoseconds\n", adapter->bus_off * 125); - -#ifdef WD7000_DEBUG - ogmbs = adapter->mb.ogmb; - icmbs = adapter->mb.icmb; - - seq_printf(m, "\nControl port value: 0x%x\n", adapter->control); - seq_puts(m, "Incoming mailbox:\n"); - seq_printf(m, " size: %d\n", ICMB_CNT); - seq_puts(m, " queued messages: "); - - for (i = count = 0; i < ICMB_CNT; i++) - if (icmbs[i].status) { - count++; - seq_printf(m, "0x%x ", i); - } - - seq_puts(m, count ? "\n" : "none\n"); - - seq_puts(m, "Outgoing mailbox:\n"); - seq_printf(m, " size: %d\n", OGMB_CNT); - seq_printf(m, " next message: 0x%x\n", adapter->next_ogmb); - seq_puts(m, " queued messages: "); - - for (i = count = 0; i < OGMB_CNT; i++) - if (ogmbs[i].status) { - count++; - seq_printf(m, "0x%x ", i); - } - - seq_puts(m, count ? "\n" : "none\n"); -#endif - - spin_unlock_irqrestore(host->host_lock, flags); - - return 0; -} - - -/* - * Returns the number of adapters this driver is supporting. - * - * The source for hosts.c says to wait to call scsi_register until 100% - * sure about an adapter. We need to do it a little sooner here; we - * need the storage set up by scsi_register before wd7000_init, and - * changing the location of an Adapter structure is more trouble than - * calling scsi_unregister. - * - */ - -static __init int wd7000_detect(struct scsi_host_template *tpnt) -{ - short present = 0, biosaddr_ptr, sig_ptr, i, pass; - short biosptr[NUM_CONFIGS]; - unsigned iobase; - Adapter *host = NULL; - struct Scsi_Host *sh; - int unit = 0; - - dprintk("wd7000_detect: started\n"); - -#ifdef MODULE - if (wd7000) - wd7000_setup(wd7000); -#endif - - for (i = 0; i < UNITS; wd7000_host[i++] = NULL); - for (i = 0; i < NUM_CONFIGS; biosptr[i++] = -1); - - tpnt->proc_name = "wd7000"; - tpnt->show_info = &wd7000_show_info; - tpnt->write_info = wd7000_set_info; - - /* - * Set up SCB free list, which is shared by all adapters - */ - init_scbs(); - - for (pass = 0; pass < NUM_CONFIGS; pass++) { - /* - * First, search for BIOS SIGNATURE... - */ - for (biosaddr_ptr = 0; biosaddr_ptr < NUM_ADDRS; biosaddr_ptr++) - for (sig_ptr = 0; sig_ptr < NUM_SIGNATURES; sig_ptr++) { - for (i = 0; i < pass; i++) - if (biosptr[i] == biosaddr_ptr) - break; - - if (i == pass) { - void __iomem *biosaddr = ioremap(wd7000_biosaddr[biosaddr_ptr] + signatures[sig_ptr].ofs, - signatures[sig_ptr].len); - short bios_match = 1; - - if (biosaddr) - bios_match = check_signature(biosaddr, signatures[sig_ptr].sig, signatures[sig_ptr].len); - - iounmap(biosaddr); - - if (bios_match) - goto bios_matched; - } - } - - bios_matched: - /* - * BIOS SIGNATURE has been found. - */ -#ifdef WD7000_DEBUG - dprintk("wd7000_detect: pass %d\n", pass + 1); - - if (biosaddr_ptr == NUM_ADDRS) - dprintk("WD-7000 SST BIOS not detected...\n"); - else - dprintk("WD-7000 SST BIOS detected at 0x%lx: checking...\n", wd7000_biosaddr[biosaddr_ptr]); -#endif - - if (configs[pass].irq < 0) - continue; - - if (unit == UNITS) - continue; - - iobase = configs[pass].iobase; - - dprintk("wd7000_detect: check IO 0x%x region...\n", iobase); - - if (request_region(iobase, 4, "wd7000")) { - - dprintk("wd7000_detect: ASC reset (IO 0x%x) ...", iobase); - /* - * ASC reset... - */ - outb(ASC_RES, iobase + ASC_CONTROL); - msleep(10); - outb(0, iobase + ASC_CONTROL); - - if (WAIT(iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) { - dprintk("failed!\n"); - goto err_release; - } else - dprintk("ok!\n"); - - if (inb(iobase + ASC_INTR_STAT) == 1) { - /* - * We register here, to get a pointer to the extra space, - * which we'll use as the Adapter structure (host) for - * this adapter. It is located just after the registered - * Scsi_Host structure (sh), and is located by the empty - * array hostdata. - */ - sh = scsi_register(tpnt, sizeof(Adapter)); - if (sh == NULL) - goto err_release; - - host = (Adapter *) sh->hostdata; - - dprintk("wd7000_detect: adapter allocated at 0x%x\n", (int) host); - memset(host, 0, sizeof(Adapter)); - - host->irq = configs[pass].irq; - host->dma = configs[pass].dma; - host->iobase = iobase; - host->int_counter = 0; - host->bus_on = configs[pass].bus_on; - host->bus_off = configs[pass].bus_off; - host->sh = wd7000_host[unit] = sh; - unit++; - - dprintk("wd7000_detect: Trying init WD-7000 card at IO " "0x%x, IRQ %d, DMA %d...\n", host->iobase, host->irq, host->dma); - - if (!wd7000_init(host)) /* Initialization failed */ - goto err_unregister; - - /* - * OK from here - we'll use this adapter/configuration. - */ - wd7000_revision(host); /* important for scatter/gather */ - - /* - * For boards before rev 6.0, scatter/gather isn't supported. - */ - if (host->rev1 < 6) - sh->sg_tablesize = 1; - - present++; /* count it */ - - if (biosaddr_ptr != NUM_ADDRS) - biosptr[pass] = biosaddr_ptr; - - printk(KERN_INFO "Western Digital WD-7000 (rev %d.%d) ", host->rev1, host->rev2); - printk("using IO 0x%x, IRQ %d, DMA %d.\n", host->iobase, host->irq, host->dma); - printk(" BUS_ON time: %dns, BUS_OFF time: %dns\n", host->bus_on * 125, host->bus_off * 125); - } - } else - dprintk("wd7000_detect: IO 0x%x region already allocated!\n", iobase); - - continue; - - err_unregister: - scsi_unregister(sh); - err_release: - release_region(iobase, 4); - - } - - if (!present) - printk("Failed initialization of WD-7000 SCSI card!\n"); - - return (present); -} - -static int wd7000_release(struct Scsi_Host *shost) -{ - if (shost->irq) - free_irq(shost->irq, NULL); - if (shost->io_port && shost->n_io_port) - release_region(shost->io_port, shost->n_io_port); - scsi_unregister(shost); - return 0; -} - -#if 0 -/* - * I have absolutely NO idea how to do an abort with the WD7000... - */ -static int wd7000_abort(Scsi_Cmnd * SCpnt) -{ - Adapter *host = (Adapter *) SCpnt->device->host->hostdata; - - if (inb(host->iobase + ASC_STAT) & INT_IM) { - printk("wd7000_abort: lost interrupt\n"); - wd7000_intr_handle(host->irq, NULL, NULL); - return FAILED; - } - return FAILED; -} -#endif - -/* - * Last resort. Reinitialize the board. - */ - -static int wd7000_host_reset(struct scsi_cmnd *SCpnt) -{ - Adapter *host = (Adapter *) SCpnt->device->host->hostdata; - - spin_lock_irq(SCpnt->device->host->host_lock); - - if (wd7000_adapter_reset(host) < 0) { - spin_unlock_irq(SCpnt->device->host->host_lock); - return FAILED; - } - - wd7000_enable_intr(host); - - spin_unlock_irq(SCpnt->device->host->host_lock); - return SUCCESS; -} - -/* - * This was borrowed directly from aha1542.c. (Zaga) - */ - -static int wd7000_biosparam(struct scsi_device *sdev, - struct block_device *bdev, sector_t capacity, int *ip) -{ - char b[BDEVNAME_SIZE]; - - dprintk("wd7000_biosparam: dev=%s, size=%llu, ", - bdevname(bdev, b), (u64)capacity); - (void)b; /* unused var warning? */ - - /* - * try default translation - */ - ip[0] = 64; - ip[1] = 32; - ip[2] = capacity >> 11; - - /* - * for disks >1GB do some guessing - */ - if (ip[2] >= 1024) { - int info[3]; - - /* - * try to figure out the geometry from the partition table - */ - if ((scsicam_bios_param(bdev, capacity, info) < 0) || !(((info[0] == 64) && (info[1] == 32)) || ((info[0] == 255) && (info[1] == 63)))) { - printk("wd7000_biosparam: unable to verify geometry for disk with >1GB.\n" " using extended translation.\n"); - - ip[0] = 255; - ip[1] = 63; - ip[2] = (unsigned long) capacity / (255 * 63); - } else { - ip[0] = info[0]; - ip[1] = info[1]; - ip[2] = info[2]; - - if (info[0] == 255) - printk(KERN_INFO "%s: current partition table is " "using extended translation.\n", __func__); - } - } - - dprintk("bios geometry: head=%d, sec=%d, cyl=%d\n", ip[0], ip[1], ip[2]); - dprintk("WARNING: check, if the bios geometry is correct.\n"); - - return (0); -} - -MODULE_AUTHOR("Thomas Wuensche, John Boyd, Miroslav Zagorac"); -MODULE_DESCRIPTION("Driver for the WD7000 series ISA controllers"); -MODULE_LICENSE("GPL"); - -static struct scsi_host_template driver_template = { - .proc_name = "wd7000", - .show_info = wd7000_show_info, - .write_info = wd7000_set_info, - .name = "Western Digital WD-7000", - .detect = wd7000_detect, - .release = wd7000_release, - .queuecommand = wd7000_queuecommand, - .eh_host_reset_handler = wd7000_host_reset, - .bios_param = wd7000_biosparam, - .can_queue = WD7000_Q, - .this_id = 7, - .sg_tablesize = WD7000_SG, - .unchecked_isa_dma = 1, - .use_clustering = ENABLE_CLUSTERING, -}; - -#include "scsi_module.c" |