Commit 6b8b8c7e authored by James Bottomley's avatar James Bottomley

Import qla2xxx driver

From: "Andrew Vasquez" <andrew.vasquez@qlogic.com>

With additional changes from: "James Bottomley" <James.Bottomley@SteelEye.com>,
                              "Christoph Hellwig" <hch@infradead.org>

This is the qlogic driver version 8.00.00b7 with the ioctl and failover code stripped
out and a few associated fixes put in.
parent fe51a64f
/*
* QLogic ISP2200 and ISP2300 Linux Driver Revision List File.
*
********************************************************************
*
* Revision History
*
* Rev 8.00.00b8 December 5, 2003 AV
* - Instruct mid-layer to perform initial scan.
*
* Rev 8.00.00b7 December 5, 2003 AV
* - Resync with Linux Kernel 2.6.0-test11.
* - Add basic NVRAM parser (extras/qla_nvr).
*
* Rev 8.00.00b7-pre11 December 3, 2003 AV
* - Sanitize the scsi_qla_host structure:
* - Purge unused elements.
* - Reorganize high-priority members (cache coherency).
* - Add support for NVRAM access via a sysfs binary attribute:
* - Consolidate semaphore locking access.
* - Fix more PCI posting issues.
* - Add extras directory for dump/NVRAM tools.
* - Remove unused qla_vendor.c file.
*
* Rev 8.00.00b7-pre11 November 26, 2003 DG/AV
* - Merge several patches from Christoph Hellwig [hch@lst.de]:
* - in Linux 2.6 both pci and the scsi layer use the generic
* dma direction bits, use them directly instead of the scsi
* and pci variants and the (noop) conversion routines.
* - Fix _IOXX_BAD() usage for external IOCTL interface.
* - Use atomic construct for HA loop_state member.
* - Add generic model description text for HBA types.
*
* Rev 8.00.00b7-pre5 November 17, 2003 AV
* - Merge several patches from Christoph Hellwig [hch@lst.de]:
* - patch to split the driver into a common qla2xxx.ko and a
* qla2?00.ko for each HBA type - the latter modules are
* only very small wrappers, mostly for the firmware
* images, all the meat is in the common qla2xxx.ko.
* - make the failover code optional.
* - kill useless lock_kernel in dpc thread startup.
* - no need for modversions hacks in 2.6 (or 2.4).
* - kill qla2x00_register_with_Linux.
* - simplify EH code, cmd or it's hostdata can't be NULL, no
* need to search whether the host it's ours, the midlayer
* makes sure it won't call into a driver for some else
* host.
* - Merge several patches from Jes Sorensen
* [jes@wildopensource.com]:
* - Call qla2x00_config_dma_addressing() before performing
* any consistent allocations. This is required since the
* dma mask settings will affect the memory
* pci_alloc_consistent() will return.
* - Call pci_set_consistent_dma_mask() to allow for 64 bit
* consistent allocations, required on some platforms such
* as the SN2.
* - Wait 20 usecs (not sure how long is really necessary,
* but this seems safe) after setting CSR_ISP_SOFT_RESET in
* the ctrl_status register as the card doesn't respond to
* PCI reads while in reset state. This causes a machine
* check on some architectures.
* - Flush PCI writes before calling udelay() to ensure the
* write is not sitting idle in-flight for a while before
* hitting the hardware.
* - Include linux/vmalloc.h in qla_os.c since it uses
* vmalloc().
* - Use auto-negotiate link speed when using default
* parameters rather than NVRAM settings. Disable NVRAM
* reading on SN2 since it's not possible to execute the
* HBA's BIOS on an SN2. I suggest doing something similar
* for all architectures that do not provide x86 BIOS
* emulation.
* - Clean-up slab-cache allocations:
* - locking.
* - mempool allocations in case of low-memory situations.
* - Fallback to GA_NXT scan if GID_PT call returns more than
* MAX_FIBRE_DEVICES.
* - Preserve iterating port ID across GA_NXT calls in
* qla2x00_find_all_fabric_devs().
* - Pre-calculate ASCII firmware dump length as to not incur the
* cost-to-calculate at each invocation of a read().
*
* Rev 8.00.00b6 November 4, 2003 AV
* - Add new 2300 TPX firmware (3.02.18).
*
* Rev 8.00.00b6-pre25 October 20, 2003 RA/AV
* - Resync with Linux Kernel 2.6.0-test9.
* - Rework firmware dump process:
* - Use binary attribute within sysfs tree.
* - Add user-space tool (gdump.sh) to retrieve formatted
* buffer.
* - Add ISP2100 support.
* - Use a slab cache for SRB allocations to reduce memory
* pressure.
* - Initial conversion of driver logging methods to a new
* qla_printk() function which uses dev_printk (Daniel
* Stekloff, IBM).
* - Further reduce stack usage in qla2x00_configure_local_loop()
* and qla2x00_find_all_fabric_devs().
* - Separate port state used for routing of I/O's from port
* mgmt-login retry etc.
*
* Rev 8.00.00b6-pre19 October 13, 2003 AV
* - Resync with Linux Kernel 2.6.0-test7-bk5.
* - Add intelligent RSCN event handling:
* - reduce scan time during 'port' RSCN events by only
* querying specified port ids.
* - Available on ISP23xx cards only.
* - Increase maximum number of recognizable targets from 256
* to 512.
* - Backend changes were previously added to support TPX
* (2K logins) firmware. Mid-layer can now scan for targets
* (H, B, T, L) where 512 < T >= 0.
* - Remove IP support from driver.
* - Switch firmware types from IP->TP for ISP22xx and
* IPX->TPX for ISP23xx cards.
* - Remove files qla_ip.[ch].
* - Remove type designations from firmware filenames.
*
* Rev 8.00.00b6-pre11 September 15, 2003 DG/AV
* - Resync with 6.06.00.
* - Resync with Linux Kernel 2.6.0-test5-bk3.
* - Add new 2300 IPX firmware (3.02.15).
*
* Rev 8.00.00b5 July 31, 2003 AV
* - Always create an fc_lun_t entry for lun 0 - as the mid-
* layer requires access to this lun for discovery to occur.
* - General sanitizing:
* - Add generic firmware option definitions.
* - Generalize retrieval/update of firmware options.
* - Fix compile errors which occur with extended debug.
* - Handle failure cases for scsi_add_host() and
* down_interruptible().
* - Host template updates:
* - Use standard bios_param callback function.
* - Disable clustering.
* - Remove unchecked_is_dma entry.
*
* Rev 8.00.00b5-pre5 July 29, 2003 DG/AV
* - Resync with 6.06.00b13.
* - Resync with Linux Kernel 2.6.0-test2.
* - Pass the complete loop_id, not the masked (0xff) value
* while issuing mailbox commands (qla_mbx.c/qla_fo.c/
* qla_iocb.c/qla_init.c).
* - Properly handle zero-length return status for an RLC CDB.
* - Create an fclun_t structure for 'disconnected' luns,
* peripheral-qualifier of 001b.
* - Remove unused LIP-sequence register access during AE 8010.
* - Generalize qla2x00_mark_device_lost() to handle forced
* login request -- modify all direct/indirect invocations
* with proper flag.
* - Save RSCN notification (AE 8015h) data in a proper and
* consistent format (domain, area, al_pa).
* - General sanitizing:
* - scsi_qla_host structure member reordering for cache-line
* coherency.
* - Remove unused SCSI opcodes, endian-swap definitions.
* - Remove CMD_* pre-processor defines.
* - Remove unused SCSIFCHOTSWAP/GAMAP/MULTIHOST codes.
* - Backout patch which added a per-scsi_qla_host scsi host
* spinlock, since mid-layer already defines one.
* - Add new 2300 IPX firmware (3.02.15).
*
* Rev 8.00.00b4 July 14, 2003 RA/DG/AV
* - Resync with 6.06.00b12.
* - Resync with Linux Kernel 2.6.0-test1.
* - Remove IOCB throttling code -- originally #if'd.
* - Remove apidev_*() routines since proc_mknod() has been
* removed -- need alternate IOCTL interface.
* - Merge several performance/fix patches from Arjan van de
* Ven:
* - Undefined operation >> 32.
* - No need to acquire mid-layer lock during command
* callback.
* - Use a per-HBA mid-layer lock.
* - Use a non-locked cycle for setting the count of the
* newly allocated sp (qla2x00_get_new_sp()).
* - Modify semantic behavior of qla2x00_queuecommand():
* - Reduce cacheline bouncing by having I/Os submitted
* by the IRQ handler.
* - Remove extraneous calls to qla2x00_next() during I/O
* queuing.
* - Use list_splice_init() during qla2x00_done() handling
* of commands to reduce list_lock contention.
* - RIO mode support for ISP2200:
* - Implementation differs slightly from original patch.
* - Do not use bottom-half handler (tasklet/work queue)
* for qla2x00_done() processing.
*
* Rev 8.00.00b4-pre22 July 12, 2003 AV
* - Check for 'Process Response Queue' requests early during
* the Host Status check.
* - General sanitizing:
* - srb_t structure rewrite, removal of unused members.
* - Remove unused fcdev array, fabricid, and PORT_*
* definitions.
* - Remove unused config_reg_t PCI definitions.
* - Add new 2200 IP firmware (2.02.06).
* - Add new 2300 IPX firmware (3.02.14).
*
* Rev 8.00.00b4-pre19 June 30, 2003 AV
* - Resync with Linux Kernel 2.5.73-bk8.
* - Rework IOCB command queuing methods:
* - Upper-layer driver *MUST* properly set the direction
* bit of SCSI commands.
* - Generalize 32bit/64bit queuing path functions.
* - Remove costly page-boundary cross check when using
* 64bit address capable IOCBs.
*
* Rev 8.00.00b4-pre15 June 19, 2003 AV
* - Resync with 6.06.00b11.
* - Continue fcport list consolidation work:
* - Updated IOCTL implementations to use new fcports
* list.
* - Modified product ID check to not verify ISP chip
* revision -- ISP2312 v3 (qla2x00_chip_diag()).
* - Add new 2300 IPX firmware (3.02.13):
*
* Rev 8.00.00b4-pre13 June 19, 2003 AV
* - Fix build process for qla2100 driver -- no support
* for IP.
* - SCSI host template modifications:
* - Set sg_tablesize based on the derived DMA mask.
* - Increase max_sectors since only limit within RISC
* is transfer of (((2^32) - 1) >> 9) sectors.
*
* Rev 8.00.00b4-pre12 June 18, 2003 RA, DG, RL, AV
* - Resync with 6.06.00b10.
* - Resync with Linux Kernel 2.5.72.
* - Initial fcport list consolidation work:
* - fcports/fcinitiators/fcdev/fc_ip --> ha->fcports
* list.
*
* Rev 8.00.00b4-pre7 June 05, 2003 AV
* - Properly release PCI resouces in init-failure case.
* - Reconcile disparite function return code definitions.
*
* Rev 8.00.00b4-pre4 June 03, 2003 AV
* - Resync with Linux Kernel 2.5.70-bk8:
* - SHT proc_info() changes.
* - Restructure SNS Generic Services routines:
* - Add qla_gs.c file to driver distribution.
* - Configure PCI latency timer for ISP23xx.
*
* Rev 8.00.00b4-pre3 June 02, 2003 RA, DG, RL, AV
* - Resync with 6.06.00b5.
* - Rework (again) PCI I/O space configuration
* (Anton Blanchard):
* - Use pci_set_mwi() routine;
* - Remove uneeded qla2x00_set_cache_line() function.
* - Remove extraneous modification of PCI_COMMAND word.
*
* Rev 8.00.00b3 May 29, 2003 AV
* - Resync with Linux Kernel 2.5.70.
* - Move RISC paused check from ISR fast-path.
*
* Rev 8.00.00b3-pre8 May 26, 2003 AV
* - Add new 2300 IPX firmware (3.02.12):
* - Rework PCI I/O space configuration.
*
* Rev 8.00.00b3-pre6 May 22, 2003 RA, DG, RL, AV
* - Resync with 6.06.00b3.
*
* Rev 8.00.00b3-pre4 May 21 2003 AV
* - Add new 2300 IPX firmware (3.02.11):
* - Remove 2300 TPX firmware from distribution.
*
* Rev 8.00.00b3-pre3 May 21 2003 AV
* - Properly setup PCI configuation space during
* initialization:
* - Properly configure Memory-Mapped I/O during early
* configuration stage.
* - Rework IP functionality to support 2k logins.
* - Add new 2300 IPX firmware (3.02.11):
* - Remove 2300 TPX firmware from distribution.
*
* Rev 8.00.00b3-pre2 May ??, 2003 RA, DG, RL, AV
* - Resync with 6.06.00b1.
*
* Rev 8.00.00b3-pre1 May ??, 2003 RA, DG, RL, AV
* - Resync with 6.05.00.
*
* Rev 8.00.00b2 May 19, 2003 AV
* - Simplify dma_addr_t handling during command queuing given
* new block-layer defined restrictions:
* - Physical addresses not spanning 4GB boundaries.
* - Firmware versions: 2100 TP (1.19.24), 2200 IP (2.02.05),
* 2300 TPX (3.02.10).
*
* Rev 8.00.00b2-pre1 May 13, 2003 AV
* - Add support for new 'Hotplug initialization' model.
* - Simplify host template by removing unused callbacks.
* - Use scsicam facilities to determine geometry.
* - Fix compilation issues for non-ISP23xx builds:
* - Correct register references in qla_dbg.c.
* - Correct Makefile build process.
*
* Rev 8.00.00b1 May 05, 2003 AV
* - Resync with Linux Kernel 2.5.69.
* - Firmware versions: 2100 TP (1.19.24), 2200 TP (2.02.05),
* 2300 TPX (3.02.10).
*
* Rev 8.00.00b1-pre45 April ??, 2003 AV
* - Resync with Linux Kernel 2.5.68-bk11:
* - Fix improper return-code assignment during fabric
* discovery.
* - Remove additional extraneous #defines from
* qla_settings.h.
* - USE_PORTNAME -- FO will always use portname.
* - Default queue depth size set to 64.
*
* Rev 8.00.00b1-pre42 April ??, 2003 AV
* - Convert bottom-half tasklet to a work_queue.
* - Initial basic coding of dynamic queue depth handling
* during QUEUE FULL statuses.
* - Fix mailbox interface problem with
* qla2x00_get_retry_cnt().
*
* Rev 8.00.00b1-pre41 April ??, 2003 AV
* - Convert build defines qla2[1|2|3]00 macros to
* qla2[1|2|3]xx due to module name stringification clashes.
* - Add additional ISP2322 checks during board configuration.
*
* Rev 8.00.00b1-pre40 April ??, 2003 AV
* - Resync with Linux Kernel 2.5.68-bk8:
* - Updated IRQ handler interface.
* - Add ISP dump code (stub) in case of SYSTEM_ERROR on
* ISP2100.
* - Add new 2200 IP firmware (2.02.05).
*
* Rev 8.00.00b1-pre39 April ??, 2003 AV
* - Resync with Linux Kernel 2.5.68.
* - Add simple build.sh script to aid in external compilation.
* - Clean-break with Kernel 2.4 compatibility.
* - Rework DPC routine -- completion routines for signaling.
* - Re-add HBAAPI character device node for IOCTL support.
* - Remove residual QLA2X_PERFORMANCE defines.
* - Allocate SP pool via __get_free_pages() rather than
* individual kmalloc()'s.
* - Inform SCSI mid-layer of 16-byte CDB support
* (host->max_cmd_len):
* - Remove unecessary 'more_cdb' handling code from
* qla_iocb.c and qla_xioct.c.
* - Reduce duplicate code in fabric scanning logic (MS IOCB
* preparation).
* - Add ISP dump code in case of SYSTEM_ERROR.
* - Remove 2300 VIX firmware from distribution:
* - Add initial code for IPX support.
* - Add new 2300 TPX firmware (3.02.10).
*
* Rev 8.00.00b1-pre34 April ??, 2003 AV
* - Resync with Linux Kernel 2.5.67.
* - Use domain/area/al_pa fields when displaying PortID
* values -- addresses endianess issues.
* - Rework large case statement to check 'common' CDB commands
* early in qla2x00_get_cmd_direction().
*
* Rev 8.00.00b1-pre31 April ??, 2003 AV
* - Update makefile to support PPC64 build.
* - Retool NVRAM configuration routine and structures:
* - Consoldate ISP21xx/ISP22xx/ISP23xx configuration
* (struct nvram_t).
* - Remove big/little endian support structures in favor of
* simplified bit-operations within byte fields.
* - Fix long-standing 'static' buffer sharing problem in
* qla2x00_configure_fabric().
*
* Rev 8.00.00b1-pre30 April ??, 2003 AV
* - Complete implementation of GID_PT scan.
* - Use consistent MS IOCB invocation method to query SNS:
* - Add RNN_ID and RSNN_NN registrations in a fabric.
* - Remove unused Mailbox Command 6Eh (Send SNS) support
* structures.
* - Use 64bit safe IOCBs while issuing INQUIRY and RLC during
* topology scan.
* - Until reimplementation of fcdev_t/fcport list
* consolidation, valid loop_id ranges are still limited from
* 0x00 through 0xFF -- enforce this within the code.
*
* Rev 8.00.00b1-pre27 March ??, 2003 AV
* - Resync with 6.05.00b9.
* - Retool HBA PCI configuration -- qla2x00_pci_config().
* - Remove inconsistent use of delay routines (UDELAY/SYS*).
* - Continue to teardown/clean/add comments and debug
* routines.
* - Properly swap bytes of the device's nodename in
* qla2x00_configure_local_loop().
*
* Rev 8.00.00b1-pre25 March ??, 2003 AV
* - Resync with 6.05.00b8.
*
* Rev 8.00.00b1-pre23 March ??, 2003 AV
* - Remove (#define) IOCB usage throttling.
* - Abstract interrupt polling with qla2x00_poll().
* - Modify lun scanning logic:
* - If the device does not support the SCSI Report Luns
* command, the driver will now only scan from 0 to the
* max#-luns as defined in the NVRAM (BIOS), rather than
* blindly scanning from 0 to 255 -- which could result in
* an increase in startup time when running against slow
* (JBOD) devices.
* - Rework reset logic in qla2x00_reset_chip() (spec).
*
* Rev 8.00.00b1-pre22 March ??, 2003 AV
* - Resync with 6.05.00b7.
* - Cleanup (rewrite) ISR handler.
* - Rename kmem_zalloc --> qla2x00_kmem_zalloc():
* - This function will eventually be removed.
* - Add new 2300 VIX firmware (3.02.09):
* - Support for Tape, Fabric, 2K logins, IP, and VI.
*
* Rev 8.00.00b1-pre18 March ??, 2003 AV
* - Support 232x type ISPs.
* - Support single firmware for each ISP type:
* - Restructure brd_info/fw_info methods.
* - Streamline firmware load process.
* - Properly query firmware for version information.
* - Remove extraneous scsi_qla_host members:
* - device_id ==> pdev->device
* - Fix fc4 features (RFF_ID) registration.
* - Convert kmem_zalloc --> qla2x00_kmem_zalloc().
* - Remove unused/extraneous #defines (USE_PORTNAME).
*
* Rev 8.00.00b1-pre14 March ??, 2003 AV
* - Resync with 6.05.00b6.
* - Initial source-code restructuring effort.
* - Build procedure.
* - Source file layout -- intuitive component layout.
* - Remove unused #defines (*PERFORMANCE, WORD_FW_LOAD, etc).
* - Add support for 2K logins (TPX -- firmware).
* - Add module parameter ql2xsuspendcount.
* - Add new 2200 IP/TP firmware (2.02.04).
*
* Rev 8.00.00b1-pre9 March ??, 2003 RL/DG/RA/AV
* - Use kernel struct list_head for fcport and fclun lists.
* - Remove extraneous (L|M)S_64BITS() and QL21_64*() defines.
*
* Rev 8.00.00b1-pre8 February 28, 2003 RL/DG/RA/AV
* - Resync with 6.05.00b3.
*
* Rev 8.00.00b1-pre7 February 23, 2003 RL/DG/RA/AV
* - Add alternate fabric scanning logic (GID_PT/GNN_ID/GPN_ID).
* - Remove use of deprecated function check_region().
* - Add new 2300 IP/TP firmware (3.02.08).
*
* Rev 8.00.00b1-pre5 January 28, 2003 RL/DG/RA/AV
* - Resync with 6.05.00b3.
* - Consolidate device_reg structure definitions for ISP types.
* - Add support for new queue-depth selection.
* - Add new 2300 IP/TP firmware (3.02.07).
*
* Rev 8.00.00b1-pre1 January 17, 2003 AV
* - Initial branch from 6.04.00b8 driver.
* - Remove VMWARE specific code.
* - Add support for pci_driver interface.
*
********************************************************************/
...@@ -1210,6 +1210,8 @@ config SCSI_QLOGICPTI ...@@ -1210,6 +1210,8 @@ config SCSI_QLOGICPTI
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called qlogicpti. module will be called qlogicpti.
source "drivers/scsi/qla2xxx/Kconfig"
config SCSI_SEAGATE config SCSI_SEAGATE
tristate "Seagate ST-02 and Future Domain TMC-8xx SCSI support" tristate "Seagate ST-02 and Future Domain TMC-8xx SCSI support"
depends on X86 && ISA && SCSI && BROKEN depends on X86 && ISA && SCSI && BROKEN
......
...@@ -71,6 +71,7 @@ obj-$(CONFIG_SCSI_QLOGIC_FAS) += qlogicfas.o ...@@ -71,6 +71,7 @@ obj-$(CONFIG_SCSI_QLOGIC_FAS) += qlogicfas.o
obj-$(CONFIG_SCSI_QLOGIC_ISP) += qlogicisp.o obj-$(CONFIG_SCSI_QLOGIC_ISP) += qlogicisp.o
obj-$(CONFIG_SCSI_QLOGIC_FC) += qlogicfc.o obj-$(CONFIG_SCSI_QLOGIC_FC) += qlogicfc.o
obj-$(CONFIG_SCSI_QLOGIC_1280) += qla1280.o obj-$(CONFIG_SCSI_QLOGIC_1280) += qla1280.o
obj-$(CONFIG_SCSI_QLA2XXX) += qla2xxx/
obj-$(CONFIG_SCSI_PAS16) += pas16.o obj-$(CONFIG_SCSI_PAS16) += pas16.o
obj-$(CONFIG_SCSI_SEAGATE) += seagate.o obj-$(CONFIG_SCSI_SEAGATE) += seagate.o
obj-$(CONFIG_SCSI_FD_8xx) += seagate.o obj-$(CONFIG_SCSI_FD_8xx) += seagate.o
......
config SCSI_QLA2XXX
tristate
depends on PCI && SCSI
config SCSI_QLA21XX
tristate "Qlogic ISP2100 host adapter family support"
select SCSI_QLA2XXX
---help---
This driver supports the QLogic 21xx (ISP2100) host adapter family.
config SCSI_QLA22XX
tristate "Qlogic ISP2200 host adapter family support"
select SCSI_QLA2XXX
---help---
This driver supports the QLogic 22xx (ISP2200) host adapter family.
config SCSI_QLA23XX
tristate "Qlogic ISP23xx host adapter family support"
select SCSI_QLA2XXX
---help---
This driver supports the QLogic 23xx (ISP2300, ISP2312, and ISP2322)
host adapter family.
EXTRA_CFLAGS += -g -Idrivers/scsi -DUNIQUE_FW_NAME
qla2xxx-y := qla_os.o qla_init.o qla_mbx.o qla_iocb.o qla_isr.o qla_gs.o \
qla_dbg.o qla_sup.o qla_rscn.o
qla2100-y := ql2100.o ql2100_fw.o
qla2200-y := ql2200.o ql2200_fw.o
qla2300-y := ql2300.o ql2300_fw.o #ql2322_fw.o
obj-$(CONFIG_SCSI_QLA21XX) += qla2xxx.o qla2100.o
obj-$(CONFIG_SCSI_QLA22XX) += qla2xxx.o qla2200.o
obj-$(CONFIG_SCSI_QLA23XX) += qla2xxx.o qla2300.o
/*
* QLogic ISP2100 device driver for Linux 2.6.x
* Copyright (C) 2003 Christoph Hellwig.
* Copyright (C) 2003 QLogic Corporation (www.qlogic.com)
*
* Released under GPL v2.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pci.h>
#include "qla_os.h"
#include "qla_def.h"
static char qla_driver_name[] = "qla2100";
extern unsigned char fw2100tp_version[];
extern unsigned char fw2100tp_version_str[];
extern unsigned short fw2100tp_addr01;
extern unsigned short fw2100tp_code01[];
extern unsigned short fw2100tp_length01;
static struct qla_fw_info qla_fw_tbl[] = {
{
.addressing = FW_INFO_ADDR_NORMAL,
.fwcode = &fw2100tp_code01[0],
.fwlen = &fw2100tp_length01,
.fwstart = &fw2100tp_addr01,
},
{ FW_INFO_ADDR_NOMORE, },
};
static struct qla_board_info qla_board_tbl = {
.drv_name = qla_driver_name,
.isp_name = "ISP2100",
.fw_info = qla_fw_tbl,
};
static struct pci_device_id qla2100_pci_tbl[] = {
{
.vendor = PCI_VENDOR_ID_QLOGIC,
.device = PCI_DEVICE_ID_QLOGIC_ISP2100,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (unsigned long)&qla_board_tbl,
},
{0, 0},
};
MODULE_DEVICE_TABLE(pci, qla2100_pci_tbl);
static int __devinit
qla2100_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
{
return qla2x00_probe_one(pdev,
(struct qla_board_info *)id->driver_data);
}
static void __devexit
qla2100_remove_one(struct pci_dev *pdev)
{
qla2x00_remove_one(pdev);
}
static struct pci_driver qla2100_pci_driver = {
.name = "qla2100",
.id_table = qla2100_pci_tbl,
.probe = qla2100_probe_one,
.remove = __devexit_p(qla2100_remove_one),
};
static int __init
qla2100_init(void)
{
return pci_module_init(&qla2100_pci_driver);
}
static void __exit
qla2100_exit(void)
{
pci_unregister_driver(&qla2100_pci_driver);
}
module_init(qla2100_init);
module_exit(qla2100_exit);
MODULE_AUTHOR("QLogic Corporation");
MODULE_DESCRIPTION("QLogic ISP21xx FC-SCSI Host Bus Adapter driver");
MODULE_LICENSE("GPL");
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* QLogic ISP2200 device driver for Linux 2.6.x
* Copyright (C) 2003 Christoph Hellwig.
* Copyright (C) 2003 QLogic Corporation (www.qlogic.com)
*
* Released under GPL v2.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pci.h>
#include "qla_os.h"
#include "qla_def.h"
static char qla_driver_name[] = "qla2200";
extern unsigned char fw2200tp_version[];
extern unsigned char fw2200tp_version_str[];
extern unsigned short fw2200tp_addr01;
extern unsigned short fw2200tp_code01[];
extern unsigned short fw2200tp_length01;
static struct qla_fw_info qla_fw_tbl[] = {
{
.addressing = FW_INFO_ADDR_NORMAL,
.fwcode = &fw2200tp_code01[0],
.fwlen = &fw2200tp_length01,
.fwstart = &fw2200tp_addr01,
},
{ FW_INFO_ADDR_NOMORE, },
};
static struct qla_board_info qla_board_tbl = {
.drv_name = qla_driver_name,
.isp_name = "ISP2200",
.fw_info = qla_fw_tbl,
};
static struct pci_device_id qla2200_pci_tbl[] = {
{
.vendor = PCI_VENDOR_ID_QLOGIC,
.device = PCI_DEVICE_ID_QLOGIC_ISP2200,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (unsigned long)&qla_board_tbl,
},
{0, 0},
};
MODULE_DEVICE_TABLE(pci, qla2200_pci_tbl);
static int __devinit
qla2200_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
{
return qla2x00_probe_one(pdev,
(struct qla_board_info *)id->driver_data);
}
static void __devexit
qla2200_remove_one(struct pci_dev *pdev)
{
qla2x00_remove_one(pdev);
}
static struct pci_driver qla2200_pci_driver = {
.name = "qla2200",
.id_table = qla2200_pci_tbl,
.probe = qla2200_probe_one,
.remove = __devexit_p(qla2200_remove_one),
};
static int __init
qla2200_init(void)
{
return pci_module_init(&qla2200_pci_driver);
}
static void __exit
qla2200_exit(void)
{
pci_unregister_driver(&qla2200_pci_driver);
}
module_init(qla2200_init);
module_exit(qla2200_exit);
MODULE_AUTHOR("QLogic Corporation");
MODULE_DESCRIPTION("QLogic ISP22xx FC-SCSI Host Bus Adapter driver");
MODULE_LICENSE("GPL");
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* QLogic ISP23XX device driver for Linux 2.6.x
* Copyright (C) 2003 Christoph Hellwig.
* Copyright (C) 2003 QLogic Corporation (www.qlogic.com)
*
* Released under GPL v2.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pci.h>
#include "qla_os.h"
#include "qla_def.h"
static char qla_driver_name[] = "qla2300";
extern unsigned char fw2300tpx_version[];
extern unsigned char fw2300tpx_version_str[];
extern unsigned short fw2300tpx_addr01;
extern unsigned short fw2300tpx_code01[];
extern unsigned short fw2300tpx_length01;
extern unsigned char fw2322tpx_version[];
extern unsigned char fw2322tpx_version_str[];
extern unsigned short fw2322tpx_addr01;
extern unsigned short fw2322tpx_code01[];
extern unsigned short fw2322tpx_length01;
extern unsigned long rseqtpx_code_addr01;
extern unsigned short rseqtpx_code01[];
extern unsigned short rseqtpx_code_length01;
extern unsigned long xseqtpx_code_addr01;
extern unsigned short xseqtpx_code01[];
extern unsigned short xseqtpx_code_length01;
static struct qla_fw_info qla_fw_tbl[] = {
{
.addressing = FW_INFO_ADDR_NORMAL,
.fwcode = &fw2300tpx_code01[0],
.fwlen = &fw2300tpx_length01,
.fwstart = &fw2300tpx_addr01,
},
#if defined(ISP2322)
/* End of 23xx firmware list */
{ FW_INFO_ADDR_NOMORE, },
/* Start of 232x firmware list */
{
.addressing = FW_INFO_ADDR_NORMAL,
.fwcode = &fw2322tpx_code01[0],
.fwlen = &fw2322tpx_length01,
.fwstart = &fw2322tpx_addr01,
},
{
.addressing = FW_INFO_ADDR_EXTENDED,
.fwcode = &rseqtpx_code01[0],
.fwlen = &rseqtpx_code_length01,
.lfwstart = &rseqtpx_code_addr01,
},
{
.addressing = FW_INFO_ADDR_EXTENDED,
.fwcode = &xseqtpx_code01[0],
.fwlen = &xseqtpx_code_length01,
.lfwstart = &xseqtpx_code_addr01,
},
#endif
{ FW_INFO_ADDR_NOMORE, },
};
static struct qla_board_info qla_board_tbl[] = {
{
.drv_name = qla_driver_name,
.isp_name = "ISP2300",
.fw_info = qla_fw_tbl,
},
{
.drv_name = qla_driver_name,
.isp_name = "ISP2312",
.fw_info = qla_fw_tbl,
},
#if defined(ISP2322)
{
.drv_name = qla_driver_name,
.isp_name = "ISP2322",
.fw_info = &qla_fw_tbl[2],
},
#endif
};
static struct pci_device_id qla2300_pci_tbl[] = {
{
.vendor = PCI_VENDOR_ID_QLOGIC,
.device = PCI_DEVICE_ID_QLOGIC_ISP2300,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (unsigned long)&qla_board_tbl[0],
},
{
.vendor = PCI_VENDOR_ID_QLOGIC,
.device = PCI_DEVICE_ID_QLOGIC_ISP2312,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (unsigned long)&qla_board_tbl[1],
},
#if defined(ISP2322)
{
.vendor = PCI_VENDOR_ID_QLOGIC,
.device = PCI_DEVICE_ID_QLOGIC_ISP2322,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (unsigned long)&qla_board_tbl[2],
},
#endif
{0, 0},
};
MODULE_DEVICE_TABLE(pci, qla2300_pci_tbl);
static int __devinit
qla2300_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
{
return qla2x00_probe_one(pdev,
(struct qla_board_info *)id->driver_data);
}
static void __devexit
qla2300_remove_one(struct pci_dev *pdev)
{
qla2x00_remove_one(pdev);
}
static struct pci_driver qla2300_pci_driver = {
.name = "qla2300",
.id_table = qla2300_pci_tbl,
.probe = qla2300_probe_one,
.remove = __devexit_p(qla2300_remove_one),
};
static int __init
qla2300_init(void)
{
return pci_module_init(&qla2300_pci_driver);
}
static void __exit
qla2300_exit(void)
{
pci_unregister_driver(&qla2300_pci_driver);
}
module_init(qla2300_init);
module_exit(qla2300_exit);
MODULE_AUTHOR("QLogic Corporation");
MODULE_DESCRIPTION("QLogic ISP23xx FC-SCSI Host Bus Adapter driver");
MODULE_LICENSE("GPL");
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* QLOGIC LINUX SOFTWARE
*
* QLogic ISP2x00 device driver for Linux 2.6.x
* Copyright (C) 2003 QLogic Corporation
* (www.qlogic.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.
*
*/
#include "qla_os.h"
#include "qla_def.h"
static int qla_uprintf(char **, char *, ...);
/**
* qla2300_fw_dump() - Dumps binary data from the 2300 firmware.
* @ha: HA context
* @hardware_locked: Called with the hardware_lock
*/
void
qla2300_fw_dump(scsi_qla_host_t *ha, int hardware_locked)
{
int rval;
uint32_t cnt, timer;
uint32_t risc_address;
uint16_t mb0, mb2;
uint32_t stat;
device_reg_t *reg;
uint16_t *dmp_reg;
unsigned long flags;
struct qla2300_fw_dump *fw;
reg = ha->iobase;
risc_address = 0;
mb0 = mb2 = 0;
flags = 0;
if (!hardware_locked)
spin_lock_irqsave(&ha->hardware_lock, flags);
if (ha->fw_dump != NULL) {
qla_printk(KERN_WARNING, ha,
"Firmware has been previously dumped (%p) -- ignoring "
"request...\n", ha->fw_dump);
return;
}
/* Allocate (large) dump buffer. */
ha->fw_dump_order = get_order(sizeof(struct qla2300_fw_dump));
ha->fw_dump = (struct qla2300_fw_dump *) __get_free_pages(GFP_ATOMIC,
ha->fw_dump_order);
if (ha->fw_dump == NULL) {
qla_printk(KERN_WARNING, ha,
"Unable to allocated memory for firmware dump (%d/%d).\n",
ha->fw_dump_order, sizeof(struct qla2300_fw_dump));
return;
}
fw = ha->fw_dump;
rval = QLA_SUCCESS;
fw->hccr = RD_REG_WORD(&reg->hccr);
/* Pause RISC. */
WRT_REG_WORD(&reg->hccr, HCCR_PAUSE_RISC);
if (!IS_QLA2312(ha) && !IS_QLA2322(ha)) {
for (cnt = 30000;
(RD_REG_WORD(&reg->hccr) & HCCR_RISC_PAUSE) == 0 &&
rval == QLA_SUCCESS; cnt--) {
if (cnt)
udelay(100);
else
rval = QLA_FUNCTION_TIMEOUT;
}
} else {
udelay(10);
}
if (rval == QLA_SUCCESS) {
dmp_reg = (uint16_t *)(reg + 0);
for (cnt = 0; cnt < sizeof(fw->pbiu_reg) / 2; cnt++)
fw->pbiu_reg[cnt] = RD_REG_WORD(dmp_reg++);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x10);
for (cnt = 0; cnt < sizeof(fw->risc_host_reg) / 2; cnt++)
fw->risc_host_reg[cnt] = RD_REG_WORD(dmp_reg++);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x40);
for (cnt = 0; cnt < sizeof(fw->mailbox_reg) / 2; cnt++)
fw->mailbox_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->ctrl_status, 0x40);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x80);
for (cnt = 0; cnt < sizeof(fw->resp_dma_reg) / 2; cnt++)
fw->resp_dma_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->ctrl_status, 0x50);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x80);
for (cnt = 0; cnt < sizeof(fw->dma_reg) / 2; cnt++)
fw->dma_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->ctrl_status, 0x00);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0xA0);
for (cnt = 0; cnt < sizeof(fw->risc_hdw_reg) / 2; cnt++)
fw->risc_hdw_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->pcr, 0x2000);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x80);
for (cnt = 0; cnt < sizeof(fw->risc_gp0_reg) / 2; cnt++)
fw->risc_gp0_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->pcr, 0x2200);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x80);
for (cnt = 0; cnt < sizeof(fw->risc_gp1_reg) / 2; cnt++)
fw->risc_gp1_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->pcr, 0x2400);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x80);
for (cnt = 0; cnt < sizeof(fw->risc_gp2_reg) / 2; cnt++)
fw->risc_gp2_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->pcr, 0x2600);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x80);
for (cnt = 0; cnt < sizeof(fw->risc_gp3_reg) / 2; cnt++)
fw->risc_gp3_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->pcr, 0x2800);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x80);
for (cnt = 0; cnt < sizeof(fw->risc_gp4_reg) / 2; cnt++)
fw->risc_gp4_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->pcr, 0x2A00);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x80);
for (cnt = 0; cnt < sizeof(fw->risc_gp5_reg) / 2; cnt++)
fw->risc_gp5_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->pcr, 0x2C00);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x80);
for (cnt = 0; cnt < sizeof(fw->risc_gp6_reg) / 2; cnt++)
fw->risc_gp6_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->pcr, 0x2E00);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x80);
for (cnt = 0; cnt < sizeof(fw->risc_gp7_reg) / 2; cnt++)
fw->risc_gp7_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->ctrl_status, 0x10);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x80);
for (cnt = 0; cnt < sizeof(fw->frame_buf_hdw_reg) / 2; cnt++)
fw->frame_buf_hdw_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->ctrl_status, 0x20);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x80);
for (cnt = 0; cnt < sizeof(fw->fpm_b0_reg) / 2; cnt++)
fw->fpm_b0_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->ctrl_status, 0x30);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x80);
for (cnt = 0; cnt < sizeof(fw->fpm_b1_reg) / 2; cnt++)
fw->fpm_b1_reg[cnt] = RD_REG_WORD(dmp_reg++);
/* Reset RISC. */
WRT_REG_WORD(&reg->ctrl_status, CSR_ISP_SOFT_RESET);
for (cnt = 0; cnt < 30000; cnt++) {
if ((RD_REG_WORD(&reg->ctrl_status) &
CSR_ISP_SOFT_RESET) == 0)
break;
udelay(10);
}
}
if (IS_QLA2312(ha) || IS_QLA2322(ha)) {
for (cnt = 30000; RD_MAILBOX_REG(ha, reg, 0) != 0 &&
rval == QLA_SUCCESS; cnt--) {
if (cnt)
udelay(100);
else
rval = QLA_FUNCTION_TIMEOUT;
}
}
if (rval == QLA_SUCCESS) {
/* Get RISC SRAM. */
risc_address = 0x800;
WRT_MAILBOX_REG(ha, reg, 0, MBC_READ_RAM_WORD);
clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
}
for (cnt = 0; cnt < sizeof(fw->risc_ram) / 2 && rval == QLA_SUCCESS;
cnt++, risc_address++) {
WRT_MAILBOX_REG(ha, reg, 1, (uint16_t)risc_address);
WRT_REG_WORD(&reg->hccr, HCCR_SET_HOST_INT);
for (timer = 6000000; timer; timer--) {
/* Check for pending interrupts. */
stat = RD_REG_DWORD(&reg->u.isp2300.host_status);
if (stat & HSR_RISC_INT) {
stat &= 0xff;
if (stat == 0x1 || stat == 0x2) {
set_bit(MBX_INTERRUPT,
&ha->mbx_cmd_flags);
mb0 = RD_MAILBOX_REG(ha, reg, 0);
mb2 = RD_MAILBOX_REG(ha, reg, 2);
/* Release mailbox registers. */
WRT_REG_WORD(&reg->semaphore, 0);
WRT_REG_WORD(&reg->hccr,
HCCR_CLR_RISC_INT);
break;
} else if (stat == 0x10 || stat == 0x11) {
set_bit(MBX_INTERRUPT,
&ha->mbx_cmd_flags);
mb0 = RD_MAILBOX_REG(ha, reg, 0);
mb2 = RD_MAILBOX_REG(ha, reg, 2);
WRT_REG_WORD(&reg->hccr,
HCCR_CLR_RISC_INT);
break;
}
/* clear this intr; it wasn't a mailbox intr */
WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
}
udelay(5);
}
if (test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) {
rval = mb0 & MBS_MASK;
fw->risc_ram[cnt] = mb2;
} else {
rval = QLA_FUNCTION_FAILED;
}
}
if (rval == QLA_SUCCESS) {
/* Get stack SRAM. */
risc_address = 0x10000;
WRT_MAILBOX_REG(ha, reg, 0, MBC_READ_RAM_EXTENDED);
clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
}
for (cnt = 0; cnt < sizeof(fw->stack_ram) / 2 && rval == QLA_SUCCESS;
cnt++, risc_address++) {
WRT_MAILBOX_REG(ha, reg, 1, LSW(risc_address));
WRT_MAILBOX_REG(ha, reg, 8, MSW(risc_address));
WRT_REG_WORD(&reg->hccr, HCCR_SET_HOST_INT);
for (timer = 6000000; timer; timer--) {
/* Check for pending interrupts. */
stat = RD_REG_DWORD(&reg->u.isp2300.host_status);
if (stat & HSR_RISC_INT) {
stat &= 0xff;
if (stat == 0x1 || stat == 0x2) {
set_bit(MBX_INTERRUPT,
&ha->mbx_cmd_flags);
mb0 = RD_MAILBOX_REG(ha, reg, 0);
mb2 = RD_MAILBOX_REG(ha, reg, 2);
/* Release mailbox registers. */
WRT_REG_WORD(&reg->semaphore, 0);
WRT_REG_WORD(&reg->hccr,
HCCR_CLR_RISC_INT);
break;
} else if (stat == 0x10 || stat == 0x11) {
set_bit(MBX_INTERRUPT,
&ha->mbx_cmd_flags);
mb0 = RD_MAILBOX_REG(ha, reg, 0);
mb2 = RD_MAILBOX_REG(ha, reg, 2);
WRT_REG_WORD(&reg->hccr,
HCCR_CLR_RISC_INT);
break;
}
/* clear this intr; it wasn't a mailbox intr */
WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
}
udelay(5);
}
if (test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) {
rval = mb0 & MBS_MASK;
fw->stack_ram[cnt] = mb2;
} else {
rval = QLA_FUNCTION_FAILED;
}
}
if (rval == QLA_SUCCESS) {
/* Get data SRAM. */
risc_address = 0x11000;
WRT_MAILBOX_REG(ha, reg, 0, MBC_READ_RAM_EXTENDED);
clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
}
for (cnt = 0; cnt < sizeof(fw->data_ram) / 2 && rval == QLA_SUCCESS;
cnt++, risc_address++) {
WRT_MAILBOX_REG(ha, reg, 1, LSW(risc_address));
WRT_MAILBOX_REG(ha, reg, 8, MSW(risc_address));
WRT_REG_WORD(&reg->hccr, HCCR_SET_HOST_INT);
for (timer = 6000000; timer; timer--) {
/* Check for pending interrupts. */
stat = RD_REG_DWORD(&reg->u.isp2300.host_status);
if (stat & HSR_RISC_INT) {
stat &= 0xff;
if (stat == 0x1 || stat == 0x2) {
set_bit(MBX_INTERRUPT,
&ha->mbx_cmd_flags);
mb0 = RD_MAILBOX_REG(ha, reg, 0);
mb2 = RD_MAILBOX_REG(ha, reg, 2);
/* Release mailbox registers. */
WRT_REG_WORD(&reg->semaphore, 0);
WRT_REG_WORD(&reg->hccr,
HCCR_CLR_RISC_INT);
break;
} else if (stat == 0x10 || stat == 0x11) {
set_bit(MBX_INTERRUPT,
&ha->mbx_cmd_flags);
mb0 = RD_MAILBOX_REG(ha, reg, 0);
mb2 = RD_MAILBOX_REG(ha, reg, 2);
WRT_REG_WORD(&reg->hccr,
HCCR_CLR_RISC_INT);
break;
}
/* clear this intr; it wasn't a mailbox intr */
WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
}
udelay(5);
}
if (test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) {
rval = mb0 & MBS_MASK;
fw->data_ram[cnt] = mb2;
} else {
rval = QLA_FUNCTION_FAILED;
}
}
if (rval != QLA_SUCCESS) {
qla_printk(KERN_WARNING, ha,
"Failed to dump firmware (%d)!!!\n", rval);
free_pages((unsigned long)ha->fw_dump, ha->fw_dump_order);
ha->fw_dump = NULL;
} else {
qla_printk(KERN_INFO, ha,
"Firmware dump saved to temp buffer (%ld/%p).\n",
ha->host_no, ha->fw_dump);
}
if (!hardware_locked)
spin_unlock_irqrestore(&ha->hardware_lock, flags);
}
/**
* qla2300_ascii_fw_dump() - Converts a binary firmware dump to ASCII.
* @ha: HA context
*/
void
qla2300_ascii_fw_dump(scsi_qla_host_t *ha)
{
uint32_t cnt;
char *uiter;
char fw_info[30];
struct qla2300_fw_dump *fw;
uiter = ha->fw_dump_buffer;
fw = ha->fw_dump;
qla_uprintf(&uiter, "%s Firmware Version %s\n", ha->model_number,
qla2x00_get_fw_version_str(ha, fw_info));
qla_uprintf(&uiter, "\n[==>BEG]\n");
qla_uprintf(&uiter, "HCCR Register:\n%04x\n\n", fw->hccr);
qla_uprintf(&uiter, "PBIU Registers:");
for (cnt = 0; cnt < sizeof (fw->pbiu_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->pbiu_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nReqQ-RspQ-Risc2Host Status registers:");
for (cnt = 0; cnt < sizeof (fw->risc_host_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->risc_host_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nMailbox Registers:");
for (cnt = 0; cnt < sizeof (fw->mailbox_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->mailbox_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nAuto Request Response DMA Registers:");
for (cnt = 0; cnt < sizeof (fw->resp_dma_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->resp_dma_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nDMA Registers:");
for (cnt = 0; cnt < sizeof (fw->dma_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->dma_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nRISC Hardware Registers:");
for (cnt = 0; cnt < sizeof (fw->risc_hdw_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->risc_hdw_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nRISC GP0 Registers:");
for (cnt = 0; cnt < sizeof (fw->risc_gp0_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->risc_gp0_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nRISC GP1 Registers:");
for (cnt = 0; cnt < sizeof (fw->risc_gp1_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->risc_gp1_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nRISC GP2 Registers:");
for (cnt = 0; cnt < sizeof (fw->risc_gp2_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->risc_gp2_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nRISC GP3 Registers:");
for (cnt = 0; cnt < sizeof (fw->risc_gp3_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->risc_gp3_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nRISC GP4 Registers:");
for (cnt = 0; cnt < sizeof (fw->risc_gp4_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->risc_gp4_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nRISC GP5 Registers:");
for (cnt = 0; cnt < sizeof (fw->risc_gp5_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->risc_gp5_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nRISC GP6 Registers:");
for (cnt = 0; cnt < sizeof (fw->risc_gp6_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->risc_gp6_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nRISC GP7 Registers:");
for (cnt = 0; cnt < sizeof (fw->risc_gp7_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->risc_gp7_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nFrame Buffer Hardware Registers:");
for (cnt = 0; cnt < sizeof (fw->frame_buf_hdw_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->frame_buf_hdw_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nFPM B0 Registers:");
for (cnt = 0; cnt < sizeof (fw->fpm_b0_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->fpm_b0_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nFPM B1 Registers:");
for (cnt = 0; cnt < sizeof (fw->fpm_b1_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->fpm_b1_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nCode RAM Dump:");
for (cnt = 0; cnt < sizeof (fw->risc_ram) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n%04x: ", cnt + 0x0800);
}
qla_uprintf(&uiter, "%04x ", fw->risc_ram[cnt]);
}
qla_uprintf(&uiter, "\n\nStack RAM Dump:");
for (cnt = 0; cnt < sizeof (fw->stack_ram) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n%05x: ", cnt + 0x10000);
}
qla_uprintf(&uiter, "%04x ", fw->stack_ram[cnt]);
}
qla_uprintf(&uiter, "\n\nData RAM Dump:");
for (cnt = 0; cnt < sizeof (fw->data_ram) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n%05x: ", cnt + 0x11000);
}
qla_uprintf(&uiter, "%04x ", fw->data_ram[cnt]);
}
qla_uprintf(&uiter, "\n\n[<==END] ISP Debug Dump.");
}
/**
* qla2100_fw_dump() - Dumps binary data from the 2100/2200 firmware.
* @ha: HA context
* @hardware_locked: Called with the hardware_lock
*/
void
qla2100_fw_dump(scsi_qla_host_t *ha, int hardware_locked)
{
int rval;
uint32_t cnt, timer;
uint32_t risc_address;
uint16_t mb0, mb2;
device_reg_t *reg;
uint16_t *dmp_reg;
unsigned long flags;
struct qla2100_fw_dump *fw;
reg = ha->iobase;
risc_address = 0;
mb0 = mb2 = 0;
flags = 0;
if (!hardware_locked)
spin_lock_irqsave(&ha->hardware_lock, flags);
if (ha->fw_dump != NULL) {
qla_printk(KERN_WARNING, ha,
"Firmware has been previously dumped (%p) -- ignoring "
"request...\n", ha->fw_dump);
return;
}
/* Allocate (large) dump buffer. */
ha->fw_dump_order = get_order(sizeof(struct qla2100_fw_dump));
ha->fw_dump = (struct qla2100_fw_dump *) __get_free_pages(GFP_ATOMIC,
ha->fw_dump_order);
if (ha->fw_dump == NULL) {
qla_printk(KERN_WARNING, ha,
"Unable to allocated memory for firmware dump (%d/%d).\n",
ha->fw_dump_order, sizeof(struct qla2100_fw_dump));
return;
}
fw = ha->fw_dump;
rval = QLA_SUCCESS;
fw->hccr = RD_REG_WORD(&reg->hccr);
/* Pause RISC. */
WRT_REG_WORD(&reg->hccr, HCCR_PAUSE_RISC);
for (cnt = 30000; (RD_REG_WORD(&reg->hccr) & HCCR_RISC_PAUSE) == 0 &&
rval == QLA_SUCCESS; cnt--) {
if (cnt)
udelay(100);
else
rval = QLA_FUNCTION_TIMEOUT;
}
if (rval == QLA_SUCCESS) {
dmp_reg = (uint16_t *)(reg + 0);
for (cnt = 0; cnt < sizeof(fw->pbiu_reg) / 2; cnt++)
fw->pbiu_reg[cnt] = RD_REG_WORD(dmp_reg++);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x10);
for (cnt = 0; cnt < ha->mbx_count; cnt++) {
if (cnt == 8) {
dmp_reg = (uint16_t *)((uint8_t *)reg + 0xe0);
}
fw->mailbox_reg[cnt] = RD_REG_WORD(dmp_reg++);
}
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x20);
for (cnt = 0; cnt < sizeof(fw->dma_reg) / 2; cnt++)
fw->dma_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->ctrl_status, 0x00);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0xA0);
for (cnt = 0; cnt < sizeof(fw->risc_hdw_reg) / 2; cnt++)
fw->risc_hdw_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->pcr, 0x2000);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x80);
for (cnt = 0; cnt < sizeof(fw->risc_gp0_reg) / 2; cnt++)
fw->risc_gp0_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->pcr, 0x2100);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x80);
for (cnt = 0; cnt < sizeof(fw->risc_gp1_reg) / 2; cnt++)
fw->risc_gp1_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->pcr, 0x2200);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x80);
for (cnt = 0; cnt < sizeof(fw->risc_gp2_reg) / 2; cnt++)
fw->risc_gp2_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->pcr, 0x2300);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x80);
for (cnt = 0; cnt < sizeof(fw->risc_gp3_reg) / 2; cnt++)
fw->risc_gp3_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->pcr, 0x2400);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x80);
for (cnt = 0; cnt < sizeof(fw->risc_gp4_reg) / 2; cnt++)
fw->risc_gp4_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->pcr, 0x2500);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x80);
for (cnt = 0; cnt < sizeof(fw->risc_gp5_reg) / 2; cnt++)
fw->risc_gp5_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->pcr, 0x2600);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x80);
for (cnt = 0; cnt < sizeof(fw->risc_gp6_reg) / 2; cnt++)
fw->risc_gp6_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->pcr, 0x2700);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x80);
for (cnt = 0; cnt < sizeof(fw->risc_gp7_reg) / 2; cnt++)
fw->risc_gp7_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->ctrl_status, 0x10);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x80);
for (cnt = 0; cnt < sizeof(fw->frame_buf_hdw_reg) / 2; cnt++)
fw->frame_buf_hdw_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->ctrl_status, 0x20);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x80);
for (cnt = 0; cnt < sizeof(fw->fpm_b0_reg) / 2; cnt++)
fw->fpm_b0_reg[cnt] = RD_REG_WORD(dmp_reg++);
WRT_REG_WORD(&reg->ctrl_status, 0x30);
dmp_reg = (uint16_t *)((uint8_t *)reg + 0x80);
for (cnt = 0; cnt < sizeof(fw->fpm_b1_reg) / 2; cnt++)
fw->fpm_b1_reg[cnt] = RD_REG_WORD(dmp_reg++);
/* Disable ISP interrupts. */
WRT_REG_WORD(&reg->ictrl, 0);
/* Reset RISC module. */
WRT_REG_WORD(&reg->hccr, HCCR_RESET_RISC);
/* Release RISC module. */
WRT_REG_WORD(&reg->hccr, HCCR_RELEASE_RISC);
/* Insure mailbox registers are free. */
WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
WRT_REG_WORD(&reg->hccr, HCCR_CLR_HOST_INT);
}
for (cnt = 30000; RD_MAILBOX_REG(ha, reg, 0) != 0 &&
rval == QLA_SUCCESS; cnt--) {
if (cnt)
udelay(100);
else
rval = QLA_FUNCTION_TIMEOUT;
}
/* Pause RISC. */
if (rval == QLA_SUCCESS && (IS_QLA2200(ha) || (IS_QLA2100(ha) &&
(RD_REG_WORD(&reg->mctr) & (BIT_1 | BIT_0)) != 0))) {
WRT_REG_WORD(&reg->hccr, HCCR_PAUSE_RISC);
for (cnt = 30000;
(RD_REG_WORD(&reg->hccr) & HCCR_RISC_PAUSE) == 0 &&
rval == QLA_SUCCESS; cnt--) {
if (cnt)
udelay(100);
else
rval = QLA_FUNCTION_TIMEOUT;
}
if (rval == QLA_SUCCESS) {
/* Set memory configuration and timing. */
if (IS_QLA2100(ha))
WRT_REG_WORD(&reg->mctr, 0xf1);
else
WRT_REG_WORD(&reg->mctr, 0xf2);
/* Release RISC. */
WRT_REG_WORD(&reg->hccr, HCCR_RELEASE_RISC);
}
}
if (rval == QLA_SUCCESS) {
/* Get RISC SRAM. */
risc_address = 0x1000;
WRT_MAILBOX_REG(ha, reg, 0, MBC_READ_RAM_WORD);
clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
}
for (cnt = 0; cnt < sizeof(fw->risc_ram) / 2 && rval == QLA_SUCCESS;
cnt++, risc_address++) {
WRT_MAILBOX_REG(ha, reg, 1, (uint16_t)risc_address);
WRT_REG_WORD(&reg->hccr, HCCR_SET_HOST_INT);
for (timer = 6000000; timer != 0; timer--) {
/* Check for pending interrupts. */
if (RD_REG_WORD(&reg->istatus) & ISR_RISC_INT) {
if (RD_REG_WORD(&reg->semaphore) & BIT_0) {
set_bit(MBX_INTERRUPT,
&ha->mbx_cmd_flags);
mb0 = RD_MAILBOX_REG(ha, reg, 0);
mb2 = RD_MAILBOX_REG(ha, reg, 2);
WRT_REG_WORD(&reg->semaphore, 0);
WRT_REG_WORD(&reg->hccr,
HCCR_CLR_RISC_INT);
break;
}
WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
}
udelay(5);
}
if (test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) {
rval = mb0 & MBS_MASK;
fw->risc_ram[cnt] = mb2;
} else {
rval = QLA_FUNCTION_FAILED;
}
}
if (rval != QLA_SUCCESS) {
qla_printk(KERN_WARNING, ha,
"Failed to dump firmware (%d)!!!\n", rval);
free_pages((unsigned long)ha->fw_dump, ha->fw_dump_order);
ha->fw_dump = NULL;
} else {
qla_printk(KERN_INFO, ha,
"Firmware dump saved to temp buffer (%ld/%p).\n",
ha->host_no, ha->fw_dump);
}
if (!hardware_locked)
spin_unlock_irqrestore(&ha->hardware_lock, flags);
}
/**
* qla2100_ascii_fw_dump() - Converts a binary firmware dump to ASCII.
* @ha: HA context
*/
void
qla2100_ascii_fw_dump(scsi_qla_host_t *ha)
{
uint32_t cnt;
char *uiter;
char fw_info[30];
struct qla2100_fw_dump *fw;
uiter = ha->fw_dump_buffer;
fw = ha->fw_dump;
qla_uprintf(&uiter, "%s Firmware Version %s\n", ha->model_number,
qla2x00_get_fw_version_str(ha, fw_info));
qla_uprintf(&uiter, "\n[==>BEG]\n");
qla_uprintf(&uiter, "HCCR Register:\n%04x\n\n", fw->hccr);
qla_uprintf(&uiter, "PBIU Registers:");
for (cnt = 0; cnt < sizeof (fw->pbiu_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->pbiu_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nMailbox Registers:");
for (cnt = 0; cnt < sizeof (fw->mailbox_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->mailbox_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nDMA Registers:");
for (cnt = 0; cnt < sizeof (fw->dma_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->dma_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nRISC Hardware Registers:");
for (cnt = 0; cnt < sizeof (fw->risc_hdw_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->risc_hdw_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nRISC GP0 Registers:");
for (cnt = 0; cnt < sizeof (fw->risc_gp0_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->risc_gp0_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nRISC GP1 Registers:");
for (cnt = 0; cnt < sizeof (fw->risc_gp1_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->risc_gp1_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nRISC GP2 Registers:");
for (cnt = 0; cnt < sizeof (fw->risc_gp2_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->risc_gp2_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nRISC GP3 Registers:");
for (cnt = 0; cnt < sizeof (fw->risc_gp3_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->risc_gp3_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nRISC GP4 Registers:");
for (cnt = 0; cnt < sizeof (fw->risc_gp4_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->risc_gp4_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nRISC GP5 Registers:");
for (cnt = 0; cnt < sizeof (fw->risc_gp5_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->risc_gp5_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nRISC GP6 Registers:");
for (cnt = 0; cnt < sizeof (fw->risc_gp6_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->risc_gp6_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nRISC GP7 Registers:");
for (cnt = 0; cnt < sizeof (fw->risc_gp7_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->risc_gp7_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nFrame Buffer Hardware Registers:");
for (cnt = 0; cnt < sizeof (fw->frame_buf_hdw_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->frame_buf_hdw_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nFPM B0 Registers:");
for (cnt = 0; cnt < sizeof (fw->fpm_b0_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->fpm_b0_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nFPM B1 Registers:");
for (cnt = 0; cnt < sizeof (fw->fpm_b1_reg) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n");
}
qla_uprintf(&uiter, "%04x ", fw->fpm_b1_reg[cnt]);
}
qla_uprintf(&uiter, "\n\nRISC SRAM:");
for (cnt = 0; cnt < sizeof (fw->risc_ram) / 2; cnt++) {
if (cnt % 8 == 0) {
qla_uprintf(&uiter, "\n%04x: ", cnt + 0x1000);
}
qla_uprintf(&uiter, "%04x ", fw->risc_ram[cnt]);
}
qla_uprintf(&uiter, "\n\n[<==END] ISP Debug Dump.");
return;
}
static int
qla_uprintf(char **uiter, char *fmt, ...)
{
int iter, len;
char buf[128];
va_list args;
va_start(args, fmt);
len = vsprintf(buf, fmt, args);
va_end(args);
for (iter = 0; iter < len; iter++, *uiter += 1)
*uiter[0] = buf[iter];
return (len);
}
//FIXME
/****************************************************************************/
/* Driver Debug Functions. */
/****************************************************************************/
void
qla2x00_dump_regs(scsi_qla_host_t *ha)
{
device_reg_t *reg;
reg = ha->iobase;
printk("Mailbox registers:\n");
printk("scsi(%ld): mbox 0 0x%04x \n",
ha->host_no, RD_MAILBOX_REG(ha, reg, 0));
printk("scsi(%ld): mbox 1 0x%04x \n",
ha->host_no, RD_MAILBOX_REG(ha, reg, 1));
printk("scsi(%ld): mbox 2 0x%04x \n",
ha->host_no, RD_MAILBOX_REG(ha, reg, 2));
printk("scsi(%ld): mbox 3 0x%04x \n",
ha->host_no, RD_MAILBOX_REG(ha, reg, 3));
printk("scsi(%ld): mbox 4 0x%04x \n",
ha->host_no, RD_MAILBOX_REG(ha, reg, 4));
printk("scsi(%ld): mbox 5 0x%04x \n",
ha->host_no, RD_MAILBOX_REG(ha, reg, 5));
}
void
qla2x00_dump_buffer(uint8_t * b, uint32_t size)
{
uint32_t cnt;
uint8_t c;
printk(" 0 1 2 3 4 5 6 7 8 9 "
"Ah Bh Ch Dh Eh Fh\n");
printk("----------------------------------------"
"----------------------\n");
for (cnt = 0; cnt < size;) {
c = *b++;
printk("%02x",(uint32_t) c);
cnt++;
if (!(cnt % 16))
printk("\n");
else
printk(" ");
}
if (cnt % 16)
printk("\n");
}
/**************************************************************************
* qla2x00_print_scsi_cmd
* Dumps out info about the scsi cmd and srb.
* Input
* cmd : struct scsi_cmnd
**************************************************************************/
void
qla2x00_print_scsi_cmd(struct scsi_cmnd * cmd)
{
int i;
struct scsi_qla_host *ha;
srb_t *sp;
ha = (struct scsi_qla_host *)cmd->device->host->hostdata;
sp = (srb_t *) cmd->SCp.ptr;
printk("SCSI Command @=0x%p, Handle=0x%p\n", cmd, cmd->host_scribble);
printk(" chan=0x%02x, target=0x%02x, lun=0x%02x, cmd_len=0x%02x\n",
cmd->device->channel, cmd->device->id, cmd->device->lun,
cmd->cmd_len);
printk(" CDB: ");
for (i = 0; i < cmd->cmd_len; i++) {
printk("0x%02x ", cmd->cmnd[i]);
}
printk("\n seg_cnt=%d, allowed=%d, retries=%d, "
"serial_number_at_timeout=0x%lx\n",
cmd->use_sg, cmd->allowed, cmd->retries,
cmd->serial_number_at_timeout);
printk(" request buffer=0x%p, request buffer len=0x%x\n",
cmd->request_buffer, cmd->request_bufflen);
printk(" tag=%d, transfersize=0x%x\n",
cmd->tag, cmd->transfersize);
printk(" serial_number=%lx, SP=%p\n", cmd->serial_number, sp);
printk(" data direction=%d\n", cmd->sc_data_direction);
if (!sp)
return;
printk(" sp flags=0x%x\n", sp->flags);
printk(" r_start=0x%lx, u_start=0x%lx, f_start=0x%lx, state=%d\n",
sp->r_start, sp->u_start, sp->f_start, sp->state);
printk(" e_start= 0x%lx, ext_history=%d, fo retry=%d, loopid=%x, "
"port path=%d\n", sp->e_start, sp->ext_history, sp->fo_retry_cnt,
sp->lun_queue->fclun->fcport->loop_id,
sp->lun_queue->fclun->fcport->cur_path);
}
/*
* qla2x00_print_q_info
* Prints queue info
* Input
* q: lun queue
*/
void
qla2x00_print_q_info(struct os_lun *q)
{
printk("Queue info: flags=0x%lx\n", q->q_flag);
}
#if defined(QL_DEBUG_ROUTINES)
/*
* qla2x00_formatted_dump_buffer
* Prints string plus buffer.
*
* Input:
* string = Null terminated string (no newline at end).
* buffer = buffer address.
* wd_size = word size 8, 16, 32 or 64 bits
* count = number of words.
*/
void
qla2x00_formatted_dump_buffer(char *string, uint8_t * buffer,
uint8_t wd_size, uint32_t count)
{
uint32_t cnt;
uint16_t *buf16;
uint32_t *buf32;
if (strcmp(string, "") != 0)
printk("%s\n",string);
switch (wd_size) {
case 8:
printk(" 0 1 2 3 4 5 6 7 "
"8 9 Ah Bh Ch Dh Eh Fh\n");
printk("-----------------------------------------"
"-------------------------------------\n");
for (cnt = 1; cnt <= count; cnt++, buffer++) {
printk("%02x",*buffer);
if (cnt % 16 == 0)
printk("\n");
else
printk(" ");
}
if (cnt % 16 != 0)
printk("\n");
break;
case 16:
printk(" 0 2 4 6 8 Ah "
" Ch Eh\n");
printk("-----------------------------------------"
"-------------\n");
buf16 = (uint16_t *) buffer;
for (cnt = 1; cnt <= count; cnt++, buf16++) {
printk("%4x",*buf16);
if (cnt % 8 == 0)
printk("\n");
else if (*buf16 < 10)
printk(" ");
else
printk(" ");
}
if (cnt % 8 != 0)
printk("\n");
break;
case 32:
printk(" 0 4 8 Ch\n");
printk("------------------------------------------\n");
buf32 = (uint32_t *) buffer;
for (cnt = 1; cnt <= count; cnt++, buf32++) {
printk("%8x", *buf32);
if (cnt % 4 == 0)
printk("\n");
else if (*buf32 < 10)
printk(" ");
else
printk(" ");
}
if (cnt % 4 != 0)
printk("\n");
break;
default:
break;
}
}
#endif
#if STOP_ON_ERROR
/**************************************************************************
* qla2x00_panic
*
**************************************************************************/
static void
qla2x00_panic(char *cp, struct Scsi_Host *host)
{
struct scsi_qla_host *ha;
long *fp;
ha = (struct scsi_qla_host *) host->hostdata;
DEBUG2(ql2x_debug_print = 1;);
printk("qla2100 - PANIC: %s\n", cp);
printk("Current time=0x%lx\n", jiffies);
printk("Number of pending commands =0x%lx\n", ha->actthreads);
printk("Number of queued commands =0x%lx\n", ha->qthreads);
printk("Number of free entries = (%d)\n", ha->req_q_cnt);
printk("Request Queue @ 0x%lx, Response Queue @ 0x%lx\n",
ha->request_dma, ha->response_dma);
printk("Request In Ptr %d\n", ha->req_ring_index);
fp = (long *) &ha->flags;
printk("HA flags =0x%lx\n", *fp);
qla2x00_dump_requests(ha);
qla2x00_dump_regs(ha);
cli();
for (;;) {
udelay(2);
barrier();
/* cpu_relax();*/
}
sti();
}
#endif
/**************************************************************************
* qla2x00_dump_requests
*
**************************************************************************/
void
qla2x00_dump_requests(scsi_qla_host_t *ha)
{
struct scsi_cmnd *cp;
srb_t *sp;
int i;
printk("Outstanding Commands on controller:\n");
for (i = 1; i < MAX_OUTSTANDING_COMMANDS; i++) {
if ((sp = ha->outstanding_cmds[i]) == NULL)
continue;
if ((cp = sp->cmd) == NULL)
continue;
printk("(%d): Pid=%ld, sp flags=0x%x, cmd=0x%p\n",
i, sp->cmd->serial_number, sp->flags, CMD_SP(sp->cmd));
}
}
/******************************************************************************
* QLOGIC LINUX SOFTWARE
*
* QLogic ISP2x00 device driver for Linux 2.6.x
* Copyright (C) 2003 QLogic Corporation
* (www.qlogic.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.
*
******************************************************************************/
/*
* Firmware Dump structure definition
*/
#define FW_DUMP_SIZE 0xBC000 /* bytes */
struct qla2300_fw_dump {
uint16_t hccr;
uint16_t pbiu_reg[8];
uint16_t risc_host_reg[8];
uint16_t mailbox_reg[32];
uint16_t resp_dma_reg[32];
uint16_t dma_reg[48];
uint16_t risc_hdw_reg[16];
uint16_t risc_gp0_reg[16];
uint16_t risc_gp1_reg[16];
uint16_t risc_gp2_reg[16];
uint16_t risc_gp3_reg[16];
uint16_t risc_gp4_reg[16];
uint16_t risc_gp5_reg[16];
uint16_t risc_gp6_reg[16];
uint16_t risc_gp7_reg[16];
uint16_t frame_buf_hdw_reg[64];
uint16_t fpm_b0_reg[64];
uint16_t fpm_b1_reg[64];
uint16_t risc_ram[0xf800];
uint16_t stack_ram[0x1000];
uint16_t data_ram[0xF000];
};
struct qla2100_fw_dump {
uint16_t hccr;
uint16_t pbiu_reg[8];
uint16_t mailbox_reg[32];
uint16_t dma_reg[48];
uint16_t risc_hdw_reg[16];
uint16_t risc_gp0_reg[16];
uint16_t risc_gp1_reg[16];
uint16_t risc_gp2_reg[16];
uint16_t risc_gp3_reg[16];
uint16_t risc_gp4_reg[16];
uint16_t risc_gp5_reg[16];
uint16_t risc_gp6_reg[16];
uint16_t risc_gp7_reg[16];
uint16_t frame_buf_hdw_reg[16];
uint16_t fpm_b0_reg[64];
uint16_t fpm_b1_reg[64];
uint16_t risc_ram[0xf000];
};
/*
* Driver debug definitions.
*/
/* #define QL_DEBUG_LEVEL_1 */ /* Output register accesses to COM1 */
/* #define QL_DEBUG_LEVEL_2 */ /* Output error msgs to COM1 */
/* #define QL_DEBUG_LEVEL_3 */ /* Output function trace msgs to COM1 */
/* #define QL_DEBUG_LEVEL_4 */ /* Output NVRAM trace msgs to COM1 */
/* #define QL_DEBUG_LEVEL_5 */ /* Output ring trace msgs to COM1 */
/* #define QL_DEBUG_LEVEL_6 */ /* Output WATCHDOG timer trace to COM1 */
/* #define QL_DEBUG_LEVEL_7 */ /* Output RISC load trace msgs to COM1 */
/* #define QL_DEBUG_LEVEL_8 */ /* Output ring saturation msgs to COM1 */
/* #define QL_DEBUG_LEVEL_9 */ /* Output IOCTL trace msgs */
/* #define QL_DEBUG_LEVEL_10 */ /* Output IOCTL error msgs */
/* #define QL_DEBUG_LEVEL_11 */ /* Output Mbx Cmd trace msgs */
/* #define QL_DEBUG_LEVEL_12 */ /* Output IP trace msgs */
/* #define QL_DEBUG_LEVEL_13 */ /* Output fdmi function trace msgs */
/* #define QL_DEBUG_LEVEL_14 */ /* Output RSCN trace msgs */
/*
* Local Macro Definitions.
*/
#if defined(QL_DEBUG_LEVEL_1) || defined(QL_DEBUG_LEVEL_2) || \
defined(QL_DEBUG_LEVEL_3) || defined(QL_DEBUG_LEVEL_4) || \
defined(QL_DEBUG_LEVEL_5) || defined(QL_DEBUG_LEVEL_6) || \
defined(QL_DEBUG_LEVEL_7) || defined(QL_DEBUG_LEVEL_8) || \
defined(QL_DEBUG_LEVEL_9) || defined(QL_DEBUG_LEVEL_10) || \
defined(QL_DEBUG_LEVEL_11) || defined(QL_DEBUG_LEVEL_12) || \
defined(QL_DEBUG_LEVEL_13) || defined(QL_DEBUG_LEVEL_14)
#define QL_DEBUG_ROUTINES
#endif
/*
* Macros use for debugging the driver.
*/
#undef ENTER_TRACE
#if defined(ENTER_TRACE)
#define ENTER(x) do { printk("qla2100 : Entering %s()\n", x); } while (0)
#define LEAVE(x) do { printk("qla2100 : Leaving %s()\n", x); } while (0)
#define ENTER_INTR(x) do { printk("qla2100 : Entering %s()\n", x); } while (0)
#define LEAVE_INTR(x) do { printk("qla2100 : Leaving %s()\n", x); } while (0)
#else
#define ENTER(x) do {} while (0)
#define LEAVE(x) do {} while (0)
#define ENTER_INTR(x) do {} while (0)
#define LEAVE_INTR(x) do {} while (0)
#endif
#if DEBUG_QLA2100
#define DEBUG(x) do {x;} while (0);
#else
#define DEBUG(x) do {} while (0);
#endif
#if defined(QL_DEBUG_LEVEL_1)
#define DEBUG1(x) do {x;} while (0);
#else
#define DEBUG1(x) do {} while (0);
#endif
#if defined(QL_DEBUG_LEVEL_2)
#define DEBUG2(x) do {x;} while (0);
#define DEBUG2_3(x) do {x;} while (0);
#define DEBUG2_3_11(x) do {x;} while (0);
#define DEBUG2_9_10(x) do {x;} while (0);
#define DEBUG2_11(x) do {x;} while (0);
#else
#define DEBUG2(x) do {} while (0);
#endif
#if defined(QL_DEBUG_LEVEL_3)
#define DEBUG3(x) do {x;} while (0);
#define DEBUG2_3(x) do {x;} while (0);
#define DEBUG2_3_11(x) do {x;} while (0);
#define DEBUG3_11(x) do {x;} while (0);
#else
#define DEBUG3(x) do {} while (0);
#if !defined(QL_DEBUG_LEVEL_2)
#define DEBUG2_3(x) do {} while (0);
#endif
#endif
#if defined(QL_DEBUG_LEVEL_4)
#define DEBUG4(x) do {x;} while (0);
#else
#define DEBUG4(x) do {} while (0);
#endif
#if defined(QL_DEBUG_LEVEL_5)
#define DEBUG5(x) do {x;} while (0);
#else
#define DEBUG5(x) do {} while (0);
#endif
#if defined(QL_DEBUG_LEVEL_7)
#define DEBUG7(x) do {x;} while (0);
#else
#define DEBUG7(x) do {} while (0);
#endif
#if defined(QL_DEBUG_LEVEL_9)
#define DEBUG9(x) do {x;} while (0);
#define DEBUG9_10(x) do {x;} while (0);
#define DEBUG2_9_10(x) do {x;} while (0);
#else
#define DEBUG9(x) do {} while (0);
#endif
#if defined(QL_DEBUG_LEVEL_10)
#define DEBUG10(x) do {x;} while (0);
#define DEBUG2_9_10(x) do {x;} while (0);
#define DEBUG9_10(x) do {x;} while (0);
#else
#define DEBUG10(x) do {} while (0);
#if !defined(DEBUG2_9_10)
#define DEBUG2_9_10(x) do {} while (0);
#endif
#if !defined(DEBUG9_10)
#define DEBUG9_10(x) do {} while (0);
#endif
#endif
#if defined(QL_DEBUG_LEVEL_11)
#define DEBUG11(x) do{x;} while(0);
#if !defined(DEBUG2_11)
#define DEBUG2_11(x) do{x;} while(0);
#endif
#if !defined(DEBUG2_3_11)
#define DEBUG2_3_11(x) do{x;} while(0);
#endif
#if !defined(DEBUG3_11)
#define DEBUG3_11(x) do{x;} while(0);
#endif
#else
#define DEBUG11(x) do{} while(0);
#if !defined(QL_DEBUG_LEVEL_2)
#define DEBUG2_11(x) do{} while(0);
#if !defined(QL_DEBUG_LEVEL_3)
#define DEBUG2_3_11(x) do{} while(0);
#endif
#endif
#if !defined(QL_DEBUG_LEVEL_3)
#define DEBUG3_11(x) do{} while(0);
#endif
#endif
#if defined(QL_DEBUG_LEVEL_12)
#define DEBUG12(x) do {x;} while (0);
#else
#define DEBUG12(x) do {} while (0);
#endif
#if defined(QL_DEBUG_LEVEL_13)
#define DEBUG13(x) do {x;} while (0)
#else
#define DEBUG13(x) do {} while (0)
#endif
#if defined(QL_DEBUG_LEVEL_14)
#define DEBUG14(x) do {x;} while (0)
#else
#define DEBUG14(x) do {} while (0)
#endif
/********************************************************************************
* QLOGIC LINUX SOFTWARE
*
* QLogic ISP2x00 device driver for Linux 2.6.x
* Copyright (C) 2003 QLogic Corporation
* (www.qlogic.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 __QLA_DEF_H
#define __QLA_DEF_H
/* XXX(hch): move to pci_ids.h */
#ifndef PCI_DEVICE_ID_QLOGIC_ISP2300
#define PCI_DEVICE_ID_QLOGIC_ISP2300 0x2300
#endif
#ifndef PCI_DEVICE_ID_QLOGIC_ISP2312
#define PCI_DEVICE_ID_QLOGIC_ISP2312 0x2312
#endif
#ifndef PCI_DEVICE_ID_QLOGIC_ISP2322
#define PCI_DEVICE_ID_QLOGIC_ISP2322 0x2322
#endif
#if defined(CONFIG_SCSI_QLA21XX) || defined(CONFIG_SCSI_QLA21XX_MODULE)
#define IS_QLA2100(ha) ((ha)->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2100)
#else
#define IS_QLA2100(ha) 0
#endif
#if defined(CONFIG_SCSI_QLA22XX) || defined(CONFIG_SCSI_QLA22XX_MODULE)
#define IS_QLA2200(ha) ((ha)->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2200)
#else
#define IS_QLA2200(ha) 0
#endif
#if defined(CONFIG_SCSI_QLA23XX) || defined(CONFIG_SCSI_QLA23XX_MODULE)
#define IS_QLA2300(ha) ((ha)->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2300)
#define IS_QLA2312(ha) ((ha)->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2312)
#define IS_QLA2322(ha) ((ha)->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2322)
#define IS_QLA23XX(ha) (IS_QLA2300(ha) || IS_QLA2312(ha) || IS_QLA2322(ha))
#else
#define IS_QLA2300(ha) 0
#define IS_QLA2312(ha) 0
#define IS_QLA2322(ha) 0
#define IS_QLA23XX(ha) 0
#endif
/*
* Only ISP23XX has extended addressing support in the firmware.
*/
#define HAS_EXTENDED_IDS(ha) IS_QLA23XX(ha)
/*
* We have MAILBOX_REGISTER_COUNT sized arrays in a few places,
* but that's fine as we don't look at the last 24 ones for
* ISP2100 HBAs.
*/
#define MAILBOX_REGISTER_COUNT_2100 8
#define MAILBOX_REGISTER_COUNT 32
#define QLA2200A_RISC_ROM_VER 4
#define FPM_2300 6
#define FPM_2310 7
#include "qla_settings.h"
/*
* Data bit definitions
*/
#define BIT_0 0x1
#define BIT_1 0x2
#define BIT_2 0x4
#define BIT_3 0x8
#define BIT_4 0x10
#define BIT_5 0x20
#define BIT_6 0x40
#define BIT_7 0x80
#define BIT_8 0x100
#define BIT_9 0x200
#define BIT_10 0x400
#define BIT_11 0x800
#define BIT_12 0x1000
#define BIT_13 0x2000
#define BIT_14 0x4000
#define BIT_15 0x8000
#define BIT_16 0x10000
#define BIT_17 0x20000
#define BIT_18 0x40000
#define BIT_19 0x80000
#define BIT_20 0x100000
#define BIT_21 0x200000
#define BIT_22 0x400000
#define BIT_23 0x800000
#define BIT_24 0x1000000
#define BIT_25 0x2000000
#define BIT_26 0x4000000
#define BIT_27 0x8000000
#define BIT_28 0x10000000
#define BIT_29 0x20000000
#define BIT_30 0x40000000
#define BIT_31 0x80000000
#define LSB(x) ((uint8_t)(x))
#define MSB(x) ((uint8_t)((uint16_t)(x) >> 8))
#define LSW(x) ((uint16_t)(x))
#define MSW(x) ((uint16_t)((uint32_t)(x) >> 16))
#define LSD(x) ((uint32_t)((uint64_t)(x)))
#define MSD(x) ((uint32_t)((((uint64_t)(x)) >> 16) >> 16))
/*
* I/O register
*/
#if MEMORY_MAPPED_IO
#define RD_REG_BYTE(addr) readb(addr)
#define RD_REG_WORD(addr) readw(addr)
#define RD_REG_DWORD(addr) readl(addr)
#define WRT_REG_BYTE(addr, data) writeb(data,addr)
#define WRT_REG_WORD(addr, data) writew(data,addr)
#define WRT_REG_DWORD(addr, data) writel(data,addr)
#else /* MEMORY_MAPPED_IO */
#define RD_REG_BYTE(addr) (inb((unsigned long)addr))
#define RD_REG_WORD(addr) (inw((unsigned long)addr))
#define RD_REG_DWORD(addr) (inl((unsigned long)addr))
#define WRT_REG_BYTE(addr, data) (outb(data,(unsigned long)addr))
#define WRT_REG_WORD(addr, data) (outw(data,(unsigned long)addr))
#define WRT_REG_DWORD(addr, data) (outl(data,(unsigned long)addr))
#endif /* MEMORY_MAPPED_IO */
/*
* Fibre Channel device definitions.
*/
#define WWN_SIZE 8 /* Size of WWPN, WWN & WWNN */
#define MAX_FIBRE_DEVICES 512
#define MAX_FIBRE_LUNS 256
#define MAX_RSCN_COUNT 32
#define MAX_HOST_COUNT 16
/*
* Host adapter default definitions.
*/
#define MAX_BUSES 1 /* We only have one bus today */
#define MAX_TARGETS_2100 MAX_FIBRE_DEVICES
#define MAX_TARGETS_2200 MAX_FIBRE_DEVICES
#define MAX_TARGETS MAX_FIBRE_DEVICES
#define MIN_LUNS 8
#define MAX_LUNS MAX_FIBRE_LUNS
#define MAX_CMDS_PER_LUN 255
/*
* Fibre Channel device definitions.
*/
#define SNS_LAST_LOOP_ID_2100 0xfe
#define SNS_LAST_LOOP_ID_2300 0x7ff
#define LAST_LOCAL_LOOP_ID 0x7d
#define SNS_FL_PORT 0x7e
#define FABRIC_CONTROLLER 0x7f
#define SIMPLE_NAME_SERVER 0x80
#define SNS_FIRST_LOOP_ID 0x81
#define MANAGEMENT_SERVER 0xfe
#define BROADCAST 0xff
#define RESERVED_LOOP_ID(x) ((x > LAST_LOCAL_LOOP_ID && \
x < SNS_FIRST_LOOP_ID) || \
x == MANAGEMENT_SERVER || \
x == BROADCAST)
/*
* Timeout timer counts in seconds
*/
#define PORT_RETRY_TIME 2
#define LOOP_DOWN_TIMEOUT 60
#define LOOP_DOWN_TIME 255 /* 240 */
#define LOOP_DOWN_RESET (LOOP_DOWN_TIME - 30)
/* Maximum outstanding commands in ISP queues (1-65535) */
#define MAX_OUTSTANDING_COMMANDS 1024
/* ISP request and response entry counts (37-65535) */
#define REQUEST_ENTRY_CNT 1024 /* Number of request entries. */
#define RESPONSE_ENTRY_CNT_2100 64 /* Number of response entries.*/
#define RESPONSE_ENTRY_CNT_2300 512 /* Number of response entries.*/
/* Calculations for SG segments */
#define SEGS_PER_REQUEST_32 3
#define SEGS_PER_CONT_32 7
#define SG_SEGMENTS_32 (SEGS_PER_REQUEST_32 + \
(SEGS_PER_CONT_32 * (REQUEST_ENTRY_CNT - 2)))
#define SEGS_PER_REQUEST_64 2
#define SEGS_PER_CONT_64 5
#define SG_SEGMENTS_64 (SEGS_PER_REQUEST_64 + \
(SEGS_PER_CONT_64 * (REQUEST_ENTRY_CNT - 2)))
/*
* SCSI Request Block
*/
typedef struct srb {
struct list_head list;
struct scsi_qla_host *ha; /* HA the SP is queued on */
struct scsi_cmnd *cmd; /* Linux SCSI command pkt */
struct timer_list timer; /* Command timer */
atomic_t ref_count; /* Reference count for this structure */
uint16_t flags;
/* Request state */
uint16_t state;
/* Target/LUN queue pointers. */
struct os_tgt *tgt_queue; /* ptr to visible ha's target */
struct os_lun *lun_queue; /* ptr to visible ha's lun */
struct fc_lun *fclun; /* FC LUN context pointer. */
/* Timing counts. */
unsigned long e_start; /* Start of extend timeout */
unsigned long r_start; /* Start of request */
unsigned long u_start; /* When sent to RISC */
unsigned long f_start; /* When placed in FO queue*/
/* Single transfer DMA context */
dma_addr_t dma_handle;
uint32_t request_sense_length;
uint8_t *request_sense_ptr;
int ext_history;
/* Suspend delay */
int delay;
/* Raw completion info for use by failover ? */
uint8_t fo_retry_cnt; /* Retry count this request */
uint8_t err_id; /* error id */
/* SRB magic number */
uint16_t magic;
#define SRB_MAGIC 0x10CB
} srb_t;
/*
* SRB flag definitions
*/
#define SRB_TIMEOUT BIT_0 /* Command timed out */
#define SRB_DMA_VALID BIT_1 /* Command sent to ISP */
#define SRB_WATCHDOG BIT_2 /* Command on watchdog list */
#define SRB_ABORT_PENDING BIT_3 /* Command abort sent to device */
#define SRB_ABORTED BIT_4 /* Command aborted command already */
#define SRB_RETRY BIT_5 /* Command needs retrying */
#define SRB_GOT_SENSE BIT_6 /* Command has sense data */
#define SRB_FAILOVER BIT_7 /* Command in failover state */
#define SRB_BUSY BIT_8 /* Command is in busy retry state */
#define SRB_FO_CANCEL BIT_9 /* Command don't need to do failover */
#define SRB_IOCTL BIT_10 /* IOCTL command. */
/*
* SRB state definitions
*/
#define SRB_FREE_STATE 0 /* returned back */
#define SRB_PENDING_STATE 1 /* queued in LUN Q */
#define SRB_ACTIVE_STATE 2 /* in Active Array */
#define SRB_DONE_STATE 3 /* queued in Done Queue */
#define SRB_RETRY_STATE 4 /* in Retry Queue */
#define SRB_SUSPENDED_STATE 5 /* in suspended state */
#define SRB_NO_QUEUE_STATE 6 /* is in between states */
#define SRB_ACTIVE_TIMEOUT_STATE 7 /* in Active Array but timed out */
#define SRB_FAILOVER_STATE 8 /* in Failover Queue */
#define SRB_SCSI_RETRY_STATE 9 /* in Scsi Retry Queue */
/*
* ISP I/O Register Set structure definitions.
*/
typedef volatile struct {
volatile uint16_t flash_address; /* Flash BIOS address */
volatile uint16_t flash_data; /* Flash BIOS data */
uint16_t unused_1[1]; /* Gap */
volatile uint16_t ctrl_status; /* Control/Status */
#define CSR_FLASH_64K_BANK BIT_3 /* Flash upper 64K bank select */
#define CSR_FLASH_ENABLE BIT_1 /* Flash BIOS Read/Write enable */
#define CSR_ISP_SOFT_RESET BIT_0 /* ISP soft reset */
volatile uint16_t ictrl; /* Interrupt control */
#define ICR_EN_INT BIT_15 /* ISP enable interrupts. */
#define ICR_EN_RISC BIT_3 /* ISP enable RISC interrupts. */
volatile uint16_t istatus; /* Interrupt status */
#define ISR_RISC_INT BIT_3 /* RISC interrupt */
volatile uint16_t semaphore; /* Semaphore */
volatile uint16_t nvram; /* NVRAM register. */
#define NVR_DESELECT 0
#define NVR_BUSY BIT_15
#define NVR_DATA_IN BIT_3
#define NVR_DATA_OUT BIT_2
#define NVR_SELECT BIT_1
#define NVR_CLOCK BIT_0
union {
struct {
volatile uint16_t mailbox0;
volatile uint16_t mailbox1;
volatile uint16_t mailbox2;
volatile uint16_t mailbox3;
volatile uint16_t mailbox4;
volatile uint16_t mailbox5;
volatile uint16_t mailbox6;
volatile uint16_t mailbox7;
uint16_t unused_2[59]; /* Gap */
} __attribute__((packed)) isp2100;
struct {
/* Request Queue */
volatile uint16_t req_q_in; /* In-Pointer */
volatile uint16_t req_q_out; /* Out-Pointer */
/* Response Queue */
volatile uint16_t rsp_q_in; /* In-Pointer */
volatile uint16_t rsp_q_out; /* Out-Pointer */
/* RISC to Host Status */
volatile uint32_t host_status;
#define HSR_RISC_INT BIT_15 /* RISC interrupt */
#define HSR_RISC_PAUSED BIT_8 /* RISC Paused */
/* Host to Host Semaphore */
volatile uint16_t host_semaphore;
uint16_t unused_3[17]; /* Gap */
volatile uint16_t mailbox0;
volatile uint16_t mailbox1;
volatile uint16_t mailbox2;
volatile uint16_t mailbox3;
volatile uint16_t mailbox4;
volatile uint16_t mailbox5;
volatile uint16_t mailbox6;
volatile uint16_t mailbox7;
volatile uint16_t mailbox8;
volatile uint16_t mailbox9;
volatile uint16_t mailbox10;
volatile uint16_t mailbox11;
volatile uint16_t mailbox12;
volatile uint16_t mailbox13;
volatile uint16_t mailbox14;
volatile uint16_t mailbox15;
volatile uint16_t mailbox16;
volatile uint16_t mailbox17;
volatile uint16_t mailbox18;
volatile uint16_t mailbox19;
volatile uint16_t mailbox20;
volatile uint16_t mailbox21;
volatile uint16_t mailbox22;
volatile uint16_t mailbox23;
volatile uint16_t mailbox24;
volatile uint16_t mailbox25;
volatile uint16_t mailbox26;
volatile uint16_t mailbox27;
volatile uint16_t mailbox28;
volatile uint16_t mailbox29;
volatile uint16_t mailbox30;
volatile uint16_t mailbox31;
volatile uint16_t fb_cmd;
uint16_t unused_4[10]; /* Gap */
} __attribute__((packed)) isp2300;
} u;
volatile uint16_t fpm_diag_config;
uint16_t unused_5[0x6]; /* Gap */
volatile uint16_t pcr; /* Processor Control Register. */
uint16_t unused_6[0x5]; /* Gap */
volatile uint16_t mctr; /* Memory Configuration and Timing. */
uint16_t unused_7[0x3]; /* Gap */
volatile uint16_t fb_cmd_2100; /* Unused on 23XX */
uint16_t unused_8[0x3]; /* Gap */
volatile uint16_t hccr; /* Host command & control register. */
#define HCCR_HOST_INT BIT_7 /* Host interrupt bit */
#define HCCR_RISC_PAUSE BIT_5 /* Pause mode bit */
/* HCCR commands */
#define HCCR_RESET_RISC 0x1000 /* Reset RISC */
#define HCCR_PAUSE_RISC 0x2000 /* Pause RISC */
#define HCCR_RELEASE_RISC 0x3000 /* Release RISC from reset. */
#define HCCR_SET_HOST_INT 0x5000 /* Set host interrupt */
#define HCCR_CLR_HOST_INT 0x6000 /* Clear HOST interrupt */
#define HCCR_CLR_RISC_INT 0x7000 /* Clear RISC interrupt */
#define HCCR_DISABLE_PARITY_PAUSE 0x4001 /* Disable parity error RISC pause. */
#define HCCR_ENABLE_PARITY 0xA000 /* Enable PARITY interrupt */
uint16_t unused_9[5]; /* Gap */
volatile uint16_t gpiod; /* GPIO Data register. */
volatile uint16_t gpioe; /* GPIO Enable register. */
#define GPIO_LED_MASK 0x00C0
#define GPIO_LED_GREEN_OFF_AMBER_OFF 0x0000
#define GPIO_LED_GREEN_ON_AMBER_OFF 0x0040
#define GPIO_LED_GREEN_OFF_AMBER_ON 0x0080
#define GPIO_LED_GREEN_ON_AMBER_ON 0x00C0
union {
struct {
uint16_t unused_10[8]; /* Gap */
volatile uint16_t mailbox8;
volatile uint16_t mailbox9;
volatile uint16_t mailbox10;
volatile uint16_t mailbox11;
volatile uint16_t mailbox12;
volatile uint16_t mailbox13;
volatile uint16_t mailbox14;
volatile uint16_t mailbox15;
volatile uint16_t mailbox16;
volatile uint16_t mailbox17;
volatile uint16_t mailbox18;
volatile uint16_t mailbox19;
volatile uint16_t mailbox20;
volatile uint16_t mailbox21;
volatile uint16_t mailbox22;
volatile uint16_t mailbox23; /* Also probe reg. */
} __attribute__((packed)) isp2200;
} u_end;
} device_reg_t;
#define ISP_REQ_Q_IN(ha, reg) \
(IS_QLA23XX(ha) ? \
&(reg)->u.isp2300.req_q_in : \
&(reg)->u.isp2100.mailbox4)
#define ISP_REQ_Q_OUT(ha, reg) \
(IS_QLA23XX(ha) ? \
&(reg)->u.isp2300.req_q_out : \
&(reg)->u.isp2100.mailbox4)
#define ISP_RSP_Q_IN(ha, reg) \
(IS_QLA23XX(ha) ? \
&(reg)->u.isp2300.rsp_q_in : \
&(reg)->u.isp2100.mailbox5)
#define ISP_RSP_Q_OUT(ha, reg) \
(IS_QLA23XX(ha) ? \
&(reg)->u.isp2300.rsp_q_out : \
&(reg)->u.isp2100.mailbox5)
#define MAILBOX_REG(ha, reg, num) \
(IS_QLA23XX(ha) ? \
&(reg)->u.isp2300.mailbox0 + (num) : \
((num < 8) ? \
&(reg)->u.isp2100.mailbox0 + (num) : \
&(reg)->u_end.isp2200.mailbox8 + (num) - 8)) /* only for isp2200 */
#define RD_MAILBOX_REG(ha, reg, num) \
RD_REG_WORD(MAILBOX_REG(ha, reg, num))
#define WRT_MAILBOX_REG(ha, reg, num, data) \
WRT_REG_WORD(MAILBOX_REG(ha, reg, num), data)
#define FB_CMD_REG(ha, reg) \
(IS_QLA23XX(ha) ? &(reg)->u.isp2300.fb_cmd : &(reg)->fb_cmd_2100)
#define RD_FB_CMD_REG(ha, reg) \
RD_REG_WORD(FB_CMD_REG(ha, reg))
#define WRT_FB_CMD_REG(ha, reg, data) \
WRT_REG_WORD(FB_CMD_REG(ha, reg), data)
typedef struct {
uint32_t out_mb; /* outbound from driver */
uint32_t in_mb; /* Incoming from RISC */
uint16_t mb[MAILBOX_REGISTER_COUNT];
long buf_size;
void *bufp;
uint32_t tov;
uint8_t flags;
#define MBX_DMA_IN BIT_0
#define MBX_DMA_OUT BIT_1
#define IOCTL_CMD BIT_2
} mbx_cmd_t;
#define MBX_TOV_SECONDS 30
/*
* ISP product identification definitions in mailboxes after reset.
*/
#define PROD_ID_1 0x4953
#define PROD_ID_2 0x0000
#define PROD_ID_2a 0x5020
#define PROD_ID_3 0x2020
/*
* ISP mailbox Self-Test status codes
*/
#define MBS_FRM_ALIVE 0 /* Firmware Alive. */
#define MBS_CHKSUM_ERR 1 /* Checksum Error. */
#define MBS_BUSY 4 /* Busy. */
/*
* ISP mailbox command complete status codes
*/
#define MBS_COMMAND_COMPLETE 0x4000
#define MBS_INVALID_COMMAND 0x4001
#define MBS_HOST_INTERFACE_ERROR 0x4002
#define MBS_TEST_FAILED 0x4003
#define MBS_COMMAND_ERROR 0x4005
#define MBS_COMMAND_PARAMETER_ERROR 0x4006
#define MBS_PORT_ID_USED 0x4007
#define MBS_LOOP_ID_USED 0x4008
#define MBS_ALL_IDS_IN_USE 0x4009
#define MBS_NOT_LOGGED_IN 0x400A
/*
* ISP mailbox asynchronous event status codes
*/
#define MBA_ASYNC_EVENT 0x8000 /* Asynchronous event. */
#define MBA_RESET 0x8001 /* Reset Detected. */
#define MBA_SYSTEM_ERR 0x8002 /* System Error. */
#define MBA_REQ_TRANSFER_ERR 0x8003 /* Request Transfer Error. */
#define MBA_RSP_TRANSFER_ERR 0x8004 /* Response Transfer Error. */
#define MBA_WAKEUP_THRES 0x8005 /* Request Queue Wake-up. */
#define MBA_LIP_OCCURRED 0x8010 /* Loop Initialization Procedure */
/* occurred. */
#define MBA_LOOP_UP 0x8011 /* FC Loop UP. */
#define MBA_LOOP_DOWN 0x8012 /* FC Loop Down. */
#define MBA_LIP_RESET 0x8013 /* LIP reset occurred. */
#define MBA_PORT_UPDATE 0x8014 /* Port Database update. */
#define MBA_RSCN_UPDATE 0x8015 /* Register State Chg Notification. */
#define MBA_LIP_F8 0x8016 /* Received a LIP F8. */
#define MBA_LOOP_INIT_ERR 0x8017 /* Loop Initialization Error. */
#define MBA_FABRIC_AUTH_REQ 0x801b /* Fabric Authentication Required. */
#define MBA_SCSI_COMPLETION 0x8020 /* SCSI Command Complete. */
#define MBA_CTIO_COMPLETION 0x8021 /* CTIO Complete. */
#define MBA_IP_COMPLETION 0x8022 /* IP Transmit Command Complete. */
#define MBA_IP_RECEIVE 0x8023 /* IP Received. */
#define MBA_IP_BROADCAST 0x8024 /* IP Broadcast Received. */
#define MBA_IP_LOW_WATER_MARK 0x8025 /* IP Low Water Mark reached. */
#define MBA_IP_RCV_BUFFER_EMPTY 0x8026 /* IP receive buffer queue empty. */
#define MBA_IP_HDR_DATA_SPLIT 0x8027 /* IP header/data splitting feature */
/* used. */
#define MBA_POINT_TO_POINT 0x8030 /* Point to point mode. */
#define MBA_CMPLT_1_16BIT 0x8031 /* Completion 1 16bit IOSB. */
#define MBA_CMPLT_2_16BIT 0x8032 /* Completion 2 16bit IOSB. */
#define MBA_CMPLT_3_16BIT 0x8033 /* Completion 3 16bit IOSB. */
#define MBA_CMPLT_4_16BIT 0x8034 /* Completion 4 16bit IOSB. */
#define MBA_CMPLT_5_16BIT 0x8035 /* Completion 5 16bit IOSB. */
#define MBA_CHG_IN_CONNECTION 0x8036 /* Change in connection mode. */
#define MBA_RIO_RESPONSE 0x8040 /* RIO response queue update. */
#define MBA_ZIO_RESPONSE 0x8040 /* ZIO response queue update. */
#define MBA_CMPLT_2_32BIT 0x8042 /* Completion 2 32bit IOSB. */
#define MBA_BYPASS_NOTIFICATION 0x8043 /* Auto bypass notification. */
#define MBA_DISCARD_RND_FRAME 0x8048 /* discard RND frame due to error. */
#define MBA_REJECTED_FCP_CMD 0x8049 /* rejected FCP_CMD. */
/*
* Firmware options 1, 2, 3.
*/
#define FO1_AE_ON_LIPF8 BIT_0
#define FO1_AE_ALL_LIP_RESET BIT_1
#define FO1_CTIO_RETRY BIT_3
#define FO1_DISABLE_LIP_F7_SW BIT_4
#define FO1_DISABLE_100MS_LOS_WAIT BIT_5
#define FO1_DISABLE_GPIO6_7 BIT_6
#define FO1_AE_ON_LOOP_INIT_ERR BIT_7
#define FO1_SET_EMPHASIS_SWING BIT_8
#define FO1_AE_AUTO_BYPASS BIT_9
#define FO1_ENABLE_PURE_IOCB BIT_10
#define FO1_AE_PLOGI_RJT BIT_11
#define FO1_ENABLE_ABORT_SEQUENCE BIT_12
#define FO1_AE_QUEUE_FULL BIT_13
#define FO2_ENABLE_ATIO_TYPE_3 BIT_0
#define FO2_REV_LOOPBACK BIT_1
#define FO3_ENABLE_EMERG_IOCB BIT_0
#define FO3_AE_RND_ERROR BIT_1
/*
* ISP mailbox commands
*/
#define MBC_LOAD_RAM 1 /* Load RAM. */
#define MBC_EXECUTE_FIRMWARE 2 /* Execute firmware. */
#define MBC_WRITE_RAM_WORD 4 /* Write RAM word. */
#define MBC_READ_RAM_WORD 5 /* Read RAM word. */
#define MBC_MAILBOX_REGISTER_TEST 6 /* Wrap incoming mailboxes */
#define MBC_VERIFY_CHECKSUM 7 /* Verify checksum. */
#define MBC_GET_FIRMWARE_VERSION 8 /* Get firmware revision. */
#define MBC_LOAD_RISC_RAM 9 /* Load RAM command. */
#define MBC_DUMP_RISC_RAM 0xa /* Dump RAM command. */
#define MBC_LOAD_RISC_RAM_EXTENDED 0xb /* Load RAM extended. */
#define MBC_DUMP_RISC_RAM_EXTENDED 0xc /* Dump RAM extended. */
#define MBC_WRITE_RAM_WORD_EXTENDED 0xd /* Write RAM word extended */
#define MBC_READ_RAM_EXTENDED 0xf /* Read RAM extended. */
#define MBC_IOCB_COMMAND 0x12 /* Execute IOCB command. */
#define MBC_ABORT_COMMAND 0x15 /* Abort IOCB command. */
#define MBC_ABORT_DEVICE 0x16 /* Abort device (ID/LUN). */
#define MBC_ABORT_TARGET 0x17 /* Abort target (ID). */
#define MBC_RESET 0x18 /* Reset. */
#define MBC_GET_ADAPTER_LOOP_ID 0x20 /* Get loop id of ISP2200. */
#define MBC_GET_RETRY_COUNT 0x22 /* Get f/w retry cnt/delay. */
#define MBC_DISABLE_VI 0x24 /* Disable VI operation. */
#define MBC_ENABLE_VI 0x25 /* Enable VI operation. */
#define MBC_GET_FIRMWARE_OPTION 0x28 /* Get Firmware Options. */
#define MBC_SET_FIRMWARE_OPTION 0x38 /* Set Firmware Options. */
#define MBC_LOOP_PORT_BYPASS 0x40 /* Loop Port Bypass. */
#define MBC_LOOP_PORT_ENABLE 0x41 /* Loop Port Enable. */
#define MBC_GET_RESOURCE_COUNTS 0x42 /* Get Resource Counts. */
#define MBC_NON_PARTICIPATE 0x43 /* Non-Participating Mode. */
#define MBC_DIAGNOSTIC_ECHO 0x44 /* Diagnostic echo. */
#define MBC_DIAGNOSTIC_LOOP_BACK 0x45 /* Diagnostic loop back. */
#define MBC_ONLINE_SELF_TEST 0x46 /* Online self-test. */
#define MBC_ENHANCED_GET_PORT_DATABASE 0x47 /* Get port database + login */
#define MBC_RESET_LINK_STATUS 0x52 /* Reset Link Error Status */
#define MBC_IOCB_COMMAND_A64 0x54 /* Execute IOCB command (64) */
#define MBC_SEND_RNID_ELS 0x57 /* Send RNID ELS request */
#define MBC_SET_RNID_PARAMS 0x59 /* Set RNID parameters */
#define MBC_GET_RNID_PARAMS 0x5a /* Data Rate */
#define MBC_DATA_RATE 0x5d /* Get RNID parameters */
#define MBC_INITIALIZE_FIRMWARE 0x60 /* Initialize firmware */
#define MBC_INITIATE_LIP 0x62 /* Initiate Loop */
/* Initialization Procedure */
#define MBC_GET_FC_AL_POSITION_MAP 0x63 /* Get FC_AL Position Map. */
#define MBC_GET_PORT_DATABASE 0x64 /* Get Port Database. */
#define MBC_CLEAR_ACA 0x65 /* Clear ACA. */
#define MBC_TARGET_RESET 0x66 /* Target Reset. */
#define MBC_CLEAR_TASK_SET 0x67 /* Clear Task Set. */
#define MBC_ABORT_TASK_SET 0x68 /* Abort Task Set. */
#define MBC_GET_FIRMWARE_STATE 0x69 /* Get firmware state. */
#define MBC_GET_PORT_NAME 0x6a /* Get port name. */
#define MBC_GET_LINK_STATUS 0x6b /* Get port link status. */
#define MBC_LIP_RESET 0x6c /* LIP reset. */
#define MBC_SEND_SNS_COMMAND 0x6e /* Send Simple Name Server */
/* commandd. */
#define MBC_LOGIN_FABRIC_PORT 0x6f /* Login fabric port. */
#define MBC_SEND_CHANGE_REQUEST 0x70 /* Send Change Request. */
#define MBC_LOGOUT_FABRIC_PORT 0x71 /* Logout fabric port. */
#define MBC_LIP_FULL_LOGIN 0x72 /* Full login LIP. */
#define MBC_LOGIN_LOOP_PORT 0x74 /* Login Loop Port. */
#define MBC_PORT_NODE_NAME_LIST 0x75 /* Get port/node name list. */
#define MBC_INITIALIZE_RECEIVE_QUEUE 0x77 /* Initialize receive queue */
#define MBC_UNLOAD_IP 0x79 /* Shutdown IP */
#define MBC_GET_ID_LIST 0x7C /* Get Port ID list. */
#define MBC_SEND_LFA_COMMAND 0x7D /* Send Loop Fabric Address */
#define MBC_LUN_RESET 0x7E /* Send LUN reset */
/* Firmware return data sizes */
#define FCAL_MAP_SIZE 128
/* Mailbox bit definitions for out_mb and in_mb */
#define MBX_31 BIT_31
#define MBX_30 BIT_30
#define MBX_29 BIT_29
#define MBX_28 BIT_28
#define MBX_27 BIT_27
#define MBX_26 BIT_26
#define MBX_25 BIT_25
#define MBX_24 BIT_24
#define MBX_23 BIT_23
#define MBX_22 BIT_22
#define MBX_21 BIT_21
#define MBX_20 BIT_20
#define MBX_19 BIT_19
#define MBX_18 BIT_18
#define MBX_17 BIT_17
#define MBX_16 BIT_16
#define MBX_15 BIT_15
#define MBX_14 BIT_14
#define MBX_13 BIT_13
#define MBX_12 BIT_12
#define MBX_11 BIT_11
#define MBX_10 BIT_10
#define MBX_9 BIT_9
#define MBX_8 BIT_8
#define MBX_7 BIT_7
#define MBX_6 BIT_6
#define MBX_5 BIT_5
#define MBX_4 BIT_4
#define MBX_3 BIT_3
#define MBX_2 BIT_2
#define MBX_1 BIT_1
#define MBX_0 BIT_0
/*
* Firmware state codes from get firmware state mailbox command
*/
#define FSTATE_CONFIG_WAIT 0
#define FSTATE_WAIT_AL_PA 1
#define FSTATE_WAIT_LOGIN 2
#define FSTATE_READY 3
#define FSTATE_LOSS_OF_SYNC 4
#define FSTATE_ERROR 5
#define FSTATE_REINIT 6
#define FSTATE_NON_PART 7
#define FSTATE_CONFIG_CORRECT 0
#define FSTATE_P2P_RCV_LIP 1
#define FSTATE_P2P_CHOOSE_LOOP 2
#define FSTATE_P2P_RCV_UNIDEN_LIP 3
#define FSTATE_FATAL_ERROR 4
#define FSTATE_LOOP_BACK_CONN 5
/*
* Port Database structure definition
* Little endian except where noted.
*/
#define PORT_DATABASE_SIZE 128 /* bytes */
typedef struct {
uint8_t options;
uint8_t control;
uint8_t master_state;
uint8_t slave_state;
uint8_t reserved[2];
uint8_t hard_address;
uint8_t reserved_1;
uint8_t port_id[4];
uint8_t node_name[WWN_SIZE]; /* Big endian. */
uint8_t port_name[WWN_SIZE]; /* Big endian. */
uint16_t execution_throttle;
uint16_t execution_count;
uint8_t reset_count;
uint8_t reserved_2;
uint16_t resource_allocation;
uint16_t current_allocation;
uint16_t queue_head;
uint16_t queue_tail;
uint16_t transmit_execution_list_next;
uint16_t transmit_execution_list_previous;
uint16_t common_features;
uint16_t total_concurrent_sequences;
uint16_t RO_by_information_category;
uint8_t recipient;
uint8_t initiator;
uint16_t receive_data_size;
uint16_t concurrent_sequences;
uint16_t open_sequences_per_exchange;
uint16_t lun_abort_flags;
uint16_t lun_stop_flags;
uint16_t stop_queue_head;
uint16_t stop_queue_tail;
uint16_t port_retry_timer;
uint16_t next_sequence_id;
uint16_t frame_count;
uint16_t PRLI_payload_length;
uint8_t prli_svc_param_word_0[2]; /* Big endian */
/* Bits 15-0 of word 0 */
uint8_t prli_svc_param_word_3[2]; /* Big endian */
/* Bits 15-0 of word 3 */
uint16_t loop_id;
uint16_t extended_lun_info_list_pointer;
uint16_t extended_lun_stop_list_pointer;
} port_database_t;
/*
* Port database slave/master states
*/
#define PD_STATE_DISCOVERY 0
#define PD_STATE_WAIT_DISCOVERY_ACK 1
#define PD_STATE_PORT_LOGIN 2
#define PD_STATE_WAIT_PORT_LOGIN_ACK 3
#define PD_STATE_PROCESS_LOGIN 4
#define PD_STATE_WAIT_PROCESS_LOGIN_ACK 5
#define PD_STATE_PORT_LOGGED_IN 6
#define PD_STATE_PORT_UNAVAILABLE 7
#define PD_STATE_PROCESS_LOGOUT 8
#define PD_STATE_WAIT_PROCESS_LOGOUT_ACK 9
#define PD_STATE_PORT_LOGOUT 10
#define PD_STATE_WAIT_PORT_LOGOUT_ACK 11
/*
* ISP Initialization Control Block.
* Little endian except where noted.
*/
#define ICB_VERSION 1
typedef struct {
uint8_t version;
uint8_t reserved_1;
/*
* LSB BIT 0 = Enable Hard Loop Id
* LSB BIT 1 = Enable Fairness
* LSB BIT 2 = Enable Full-Duplex
* LSB BIT 3 = Enable Fast Posting
* LSB BIT 4 = Enable Target Mode
* LSB BIT 5 = Disable Initiator Mode
* LSB BIT 6 = Enable ADISC
* LSB BIT 7 = Enable Target Inquiry Data
*
* MSB BIT 0 = Enable PDBC Notify
* MSB BIT 1 = Non Participating LIP
* MSB BIT 2 = Descending Loop ID Search
* MSB BIT 3 = Acquire Loop ID in LIPA
* MSB BIT 4 = Stop PortQ on Full Status
* MSB BIT 5 = Full Login after LIP
* MSB BIT 6 = Node Name Option
* MSB BIT 7 = Ext IFWCB enable bit
*/
uint8_t firmware_options[2];
uint16_t frame_payload_size;
uint16_t max_iocb_allocation;
uint16_t execution_throttle;
uint8_t retry_count;
uint8_t retry_delay; /* unused */
uint8_t port_name[WWN_SIZE]; /* Big endian. */
uint16_t hard_address;
uint8_t inquiry_data;
uint8_t login_timeout;
uint8_t node_name[WWN_SIZE]; /* Big endian. */
uint16_t request_q_outpointer;
uint16_t response_q_inpointer;
uint16_t request_q_length;
uint16_t response_q_length;
uint32_t request_q_address[2];
uint32_t response_q_address[2];
uint16_t lun_enables;
uint8_t command_resource_count;
uint8_t immediate_notify_resource_count;
uint16_t timeout;
uint8_t reserved_2[2];
/*
* LSB BIT 0 = Timer Operation mode bit 0
* LSB BIT 1 = Timer Operation mode bit 1
* LSB BIT 2 = Timer Operation mode bit 2
* LSB BIT 3 = Timer Operation mode bit 3
* LSB BIT 4 = Init Config Mode bit 0
* LSB BIT 5 = Init Config Mode bit 1
* LSB BIT 6 = Init Config Mode bit 2
* LSB BIT 7 = Enable Non part on LIHA failure
*
* MSB BIT 0 = Enable class 2
* MSB BIT 1 = Enable ACK0
* MSB BIT 2 =
* MSB BIT 3 =
* MSB BIT 4 = FC Tape Enable
* MSB BIT 5 = Enable FC Confirm
* MSB BIT 6 = Enable command queuing in target mode
* MSB BIT 7 = No Logo On Link Down
*/
uint8_t add_firmware_options[2];
uint8_t response_accumulation_timer;
uint8_t interrupt_delay_timer;
/*
* LSB BIT 0 = Enable Read xfr_rdy
* LSB BIT 1 = Soft ID only
* LSB BIT 2 =
* LSB BIT 3 =
* LSB BIT 4 = FCP RSP Payload [0]
* LSB BIT 5 = FCP RSP Payload [1] / Sbus enable - 2200
* LSB BIT 6 = Enable Out-of-Order frame handling
* LSB BIT 7 = Disable Automatic PLOGI on Local Loop
*
* MSB BIT 0 = Sbus enable - 2300
* MSB BIT 1 =
* MSB BIT 2 =
* MSB BIT 3 =
* MSB BIT 4 =
* MSB BIT 5 = enable 50 ohm termination
* MSB BIT 6 = Data Rate (2300 only)
* MSB BIT 7 = Data Rate (2300 only)
*/
uint8_t special_options[2];
uint8_t reserved_3[26];
} init_cb_t;
/*
* Get Link Status mailbox command return buffer.
*/
typedef struct {
uint32_t link_fail_cnt;
uint32_t loss_sync_cnt;
uint32_t loss_sig_cnt;
uint32_t prim_seq_err_cnt;
uint32_t inval_xmit_word_cnt;
uint32_t inval_crc_cnt;
} link_stat_t;
/*
* NVRAM Command values.
*/
#define NV_START_BIT BIT_2
#define NV_WRITE_OP (BIT_26+BIT_24)
#define NV_READ_OP (BIT_26+BIT_25)
#define NV_ERASE_OP (BIT_26+BIT_25+BIT_24)
#define NV_MASK_OP (BIT_26+BIT_25+BIT_24)
#define NV_DELAY_COUNT 10
/*
* QLogic ISP2100, ISP2200 and ISP2300 NVRAM structure definition.
*/
typedef struct {
/*
* NVRAM header
*/
uint8_t id[4];
uint8_t nvram_version;
uint8_t reserved_0;
/*
* NVRAM RISC parameter block
*/
uint8_t parameter_block_version;
uint8_t reserved_1;
/*
* LSB BIT 0 = Enable Hard Loop Id
* LSB BIT 1 = Enable Fairness
* LSB BIT 2 = Enable Full-Duplex
* LSB BIT 3 = Enable Fast Posting
* LSB BIT 4 = Enable Target Mode
* LSB BIT 5 = Disable Initiator Mode
* LSB BIT 6 = Enable ADISC
* LSB BIT 7 = Enable Target Inquiry Data
*
* MSB BIT 0 = Enable PDBC Notify
* MSB BIT 1 = Non Participating LIP
* MSB BIT 2 = Descending Loop ID Search
* MSB BIT 3 = Acquire Loop ID in LIPA
* MSB BIT 4 = Stop PortQ on Full Status
* MSB BIT 5 = Full Login after LIP
* MSB BIT 6 = Node Name Option
* MSB BIT 7 = Ext IFWCB enable bit
*/
uint8_t firmware_options[2];
uint16_t frame_payload_size;
uint16_t max_iocb_allocation;
uint16_t execution_throttle;
uint8_t retry_count;
uint8_t retry_delay; /* unused */
uint8_t port_name[WWN_SIZE]; /* Big endian. */
uint16_t hard_address;
uint8_t inquiry_data;
uint8_t login_timeout;
uint8_t node_name[WWN_SIZE]; /* Big endian. */
/*
* LSB BIT 0 = Timer Operation mode bit 0
* LSB BIT 1 = Timer Operation mode bit 1
* LSB BIT 2 = Timer Operation mode bit 2
* LSB BIT 3 = Timer Operation mode bit 3
* LSB BIT 4 = Init Config Mode bit 0
* LSB BIT 5 = Init Config Mode bit 1
* LSB BIT 6 = Init Config Mode bit 2
* LSB BIT 7 = Enable Non part on LIHA failure
*
* MSB BIT 0 = Enable class 2
* MSB BIT 1 = Enable ACK0
* MSB BIT 2 =
* MSB BIT 3 =
* MSB BIT 4 = FC Tape Enable
* MSB BIT 5 = Enable FC Confirm
* MSB BIT 6 = Enable command queuing in target mode
* MSB BIT 7 = No Logo On Link Down
*/
uint8_t add_firmware_options[2];
uint8_t response_accumulation_timer;
uint8_t interrupt_delay_timer;
/*
* LSB BIT 0 = Enable Read xfr_rdy
* LSB BIT 1 = Soft ID only
* LSB BIT 2 =
* LSB BIT 3 =
* LSB BIT 4 = FCP RSP Payload [0]
* LSB BIT 5 = FCP RSP Payload [1] / Sbus enable - 2200
* LSB BIT 6 = Enable Out-of-Order frame handling
* LSB BIT 7 = Disable Automatic PLOGI on Local Loop
*
* MSB BIT 0 = Sbus enable - 2300
* MSB BIT 1 =
* MSB BIT 2 =
* MSB BIT 3 =
* MSB BIT 4 =
* MSB BIT 5 = enable 50 ohm termination
* MSB BIT 6 = Data Rate (2300 only)
* MSB BIT 7 = Data Rate (2300 only)
*/
uint8_t special_options[2];
/* Reserved for expanded RISC parameter block */
uint8_t reserved_2[24];
/*
* LSB BIT 0 = Output Swing 1G bit 0
* LSB BIT 1 = Output Swing 1G bit 1
* LSB BIT 2 = Output Swing 1G bit 2
* LSB BIT 3 = Output Emphasis 1G bit 0
* LSB BIT 4 = Output Emphasis 1G bit 1
* LSB BIT 5 = Output Swing 2G bit 0
* LSB BIT 6 = Output Swing 2G bit 1
* LSB BIT 7 = Output Swing 2G bit 2
*
* MSB BIT 0 = Output Emphasis 2G bit 0
* MSB BIT 1 = Output Emphasis 2G bit 1
* MSB BIT 2 = Output Enable
* MSB BIT 3 =
* MSB BIT 4 =
* MSB BIT 5 =
* MSB BIT 6 =
* MSB BIT 7 =
*/
uint8_t seriallink_options[2];
/*
* NVRAM host parameter block
*
* LSB BIT 0 = Enable spinup delay
* LSB BIT 1 = Disable BIOS
* LSB BIT 2 = Enable Memory Map BIOS
* LSB BIT 3 = Enable Selectable Boot
* LSB BIT 4 = Disable RISC code load
* LSB BIT 5 = Set cache line size 1
* LSB BIT 6 = PCI Parity Disable
* LSB BIT 7 = Enable extended logging
*
* MSB BIT 0 = Enable 64bit addressing
* MSB BIT 1 = Enable lip reset
* MSB BIT 2 = Enable lip full login
* MSB BIT 3 = Enable target reset
* MSB BIT 4 = Enable database storage
* MSB BIT 5 = Enable cache flush read
* MSB BIT 6 = Enable database load
* MSB BIT 7 = Enable alternate WWN
*/
uint8_t host_p[2];
uint8_t boot_node_name[WWN_SIZE];
uint8_t boot_lun_number;
uint8_t reset_delay;
uint8_t port_down_retry_count;
uint8_t boot_id_number;
uint16_t max_luns_per_target;
uint8_t fcode_boot_port_name[WWN_SIZE];
uint8_t alternate_port_name[WWN_SIZE];
uint8_t alternate_node_name[WWN_SIZE];
/*
* BIT 0 = Boot Zoning
* BIT 1 = Alt-Boot Enable
* BIT 2 = Report SCSI Path
* BIT 3 = unused
* BIT 4 = unused
* BIT 5 = unused
* BIT 6 = unused
* BIT 7 = unused
*/
uint8_t efi_parameters;
uint8_t link_down_timeout;
uint8_t adapter_id_0[4];
uint8_t adapter_id_1[4];
uint8_t adapter_id_2[4];
uint8_t adapter_id_3[4];
uint8_t alt1_boot_node_name[WWN_SIZE];
uint16_t alt1_boot_lun_number;
uint8_t alt2_boot_node_name[WWN_SIZE];
uint16_t alt2_boot_lun_number;
uint8_t alt3_boot_node_name[WWN_SIZE];
uint16_t alt3_boot_lun_number;
uint8_t alt4_boot_node_name[WWN_SIZE];
uint16_t alt4_boot_lun_number;
uint8_t alt5_boot_node_name[WWN_SIZE];
uint16_t alt5_boot_lun_number;
uint8_t alt6_boot_node_name[WWN_SIZE];
uint16_t alt6_boot_lun_number;
uint8_t alt7_boot_node_name[WWN_SIZE];
uint16_t alt7_boot_lun_number;
uint8_t reserved_3[2];
/* Offset 200-215 : Model Number */
uint8_t model_number[16];
/* OEM related items */
uint8_t oem_specific[16];
/*
* NVRAM Adapter Features offset 232-239
*
* LSB BIT 0 = External GBIC
* LSB BIT 1 = Risc RAM parity
* LSB BIT 2 = Buffer Plus Module
* LSB BIT 3 = Multi Chip Adapter
* LSB BIT 4 = Internal connector
* LSB BIT 5 =
* LSB BIT 6 =
* LSB BIT 7 =
*
* MSB BIT 0 =
* MSB BIT 1 =
* MSB BIT 2 =
* MSB BIT 3 =
* MSB BIT 4 =
* MSB BIT 5 =
* MSB BIT 6 =
* MSB BIT 7 =
*/
uint8_t adapter_features[2];
uint8_t reserved_4[16];
/* Subsystem vendor ID for ISP2200 */
uint16_t subsystem_vendor_id_2200;
/* Subsystem device ID for ISP2200 */
uint16_t subsystem_device_id_2200;
uint8_t reserved_5;
uint8_t checksum;
} nvram_t;
/*
* ISP queue - response queue entry definition.
*/
typedef struct {
uint8_t data[60];
uint32_t signature;
#define RESPONSE_PROCESSED 0xDEADDEAD /* Signature */
} response_t;
typedef union {
uint16_t extended;
struct {
uint8_t reserved;
uint8_t standard;;
};
} target_id_t;
#define SET_TARGET_ID(ha, to, from) \
do { \
if (HAS_EXTENDED_IDS(ha)) \
to.extended = cpu_to_le16(from); \
else \
to.standard = (uint8_t)from; \
} while (0)
/*
* ISP queue - command entry structure definition.
*/
#define COMMAND_TYPE 0x11 /* Command entry */
#define MAX_CMDSZ 16 /* SCSI maximum CDB size. */
typedef struct {
uint8_t entry_type; /* Entry type. */
uint8_t entry_count; /* Entry count. */
uint8_t sys_define; /* System defined. */
uint8_t entry_status; /* Entry Status. */
uint32_t handle; /* System handle. */
target_id_t target; /* SCSI ID */
uint16_t lun; /* SCSI LUN */
uint16_t control_flags; /* Control flags. */
#define CF_WRITE BIT_6
#define CF_READ BIT_5
#define CF_SIMPLE_TAG BIT_3
#define CF_ORDERED_TAG BIT_2
#define CF_HEAD_TAG BIT_1
uint16_t reserved_1;
uint16_t timeout; /* Command timeout. */
uint16_t dseg_count; /* Data segment count. */
uint8_t scsi_cdb[MAX_CMDSZ]; /* SCSI command words. */
uint32_t byte_count; /* Total byte count. */
uint32_t dseg_0_address; /* Data segment 0 address. */
uint32_t dseg_0_length; /* Data segment 0 length. */
uint32_t dseg_1_address; /* Data segment 1 address. */
uint32_t dseg_1_length; /* Data segment 1 length. */
uint32_t dseg_2_address; /* Data segment 2 address. */
uint32_t dseg_2_length; /* Data segment 2 length. */
} cmd_entry_t;
/*
* ISP queue - 64-Bit addressing, command entry structure definition.
*/
#define COMMAND_A64_TYPE 0x19 /* Command A64 entry */
typedef struct {
uint8_t entry_type; /* Entry type. */
uint8_t entry_count; /* Entry count. */
uint8_t sys_define; /* System defined. */
uint8_t entry_status; /* Entry Status. */
uint32_t handle; /* System handle. */
target_id_t target; /* SCSI ID */
uint16_t lun; /* SCSI LUN */
uint16_t control_flags; /* Control flags. */
uint16_t reserved_1;
uint16_t timeout; /* Command timeout. */
uint16_t dseg_count; /* Data segment count. */
uint8_t scsi_cdb[MAX_CMDSZ]; /* SCSI command words. */
uint32_t byte_count; /* Total byte count. */
uint32_t dseg_0_address[2]; /* Data segment 0 address. */
uint32_t dseg_0_length; /* Data segment 0 length. */
uint32_t dseg_1_address[2]; /* Data segment 1 address. */
uint32_t dseg_1_length; /* Data segment 1 length. */
} cmd_a64_entry_t, request_t;
/*
* ISP queue - continuation entry structure definition.
*/
#define CONTINUE_TYPE 0x02 /* Continuation entry. */
typedef struct {
uint8_t entry_type; /* Entry type. */
uint8_t entry_count; /* Entry count. */
uint8_t sys_define; /* System defined. */
uint8_t entry_status; /* Entry Status. */
uint32_t reserved;
uint32_t dseg_0_address; /* Data segment 0 address. */
uint32_t dseg_0_length; /* Data segment 0 length. */
uint32_t dseg_1_address; /* Data segment 1 address. */
uint32_t dseg_1_length; /* Data segment 1 length. */
uint32_t dseg_2_address; /* Data segment 2 address. */
uint32_t dseg_2_length; /* Data segment 2 length. */
uint32_t dseg_3_address; /* Data segment 3 address. */
uint32_t dseg_3_length; /* Data segment 3 length. */
uint32_t dseg_4_address; /* Data segment 4 address. */
uint32_t dseg_4_length; /* Data segment 4 length. */
uint32_t dseg_5_address; /* Data segment 5 address. */
uint32_t dseg_5_length; /* Data segment 5 length. */
uint32_t dseg_6_address; /* Data segment 6 address. */
uint32_t dseg_6_length; /* Data segment 6 length. */
} cont_entry_t;
/*
* ISP queue - 64-Bit addressing, continuation entry structure definition.
*/
#define CONTINUE_A64_TYPE 0x0A /* Continuation A64 entry. */
typedef struct {
uint8_t entry_type; /* Entry type. */
uint8_t entry_count; /* Entry count. */
uint8_t sys_define; /* System defined. */
uint8_t entry_status; /* Entry Status. */
uint32_t dseg_0_address[2]; /* Data segment 0 address. */
uint32_t dseg_0_length; /* Data segment 0 length. */
uint32_t dseg_1_address[2]; /* Data segment 1 address. */
uint32_t dseg_1_length; /* Data segment 1 length. */
uint32_t dseg_2_address [2]; /* Data segment 2 address. */
uint32_t dseg_2_length; /* Data segment 2 length. */
uint32_t dseg_3_address[2]; /* Data segment 3 address. */
uint32_t dseg_3_length; /* Data segment 3 length. */
uint32_t dseg_4_address[2]; /* Data segment 4 address. */
uint32_t dseg_4_length; /* Data segment 4 length. */
} cont_a64_entry_t;
/*
* ISP queue - status entry structure definition.
*/
#define STATUS_TYPE 0x03 /* Status entry. */
typedef struct {
uint8_t entry_type; /* Entry type. */
uint8_t entry_count; /* Entry count. */
uint8_t sys_define; /* System defined. */
uint8_t entry_status; /* Entry Status. */
uint32_t handle; /* System handle. */
uint16_t scsi_status; /* SCSI status. */
uint16_t comp_status; /* Completion status. */
uint16_t state_flags; /* State flags. */
uint16_t status_flags; /* Status flags. */
uint16_t rsp_info_len; /* Response Info Length. */
uint16_t req_sense_length; /* Request sense data length. */
uint32_t residual_length; /* Residual transfer length. */
uint8_t rsp_info[8]; /* FCP response information. */
uint8_t req_sense_data[32]; /* Request sense data. */
} sts_entry_t;
/*
* Status entry entry status
*/
#define RF_INV_E_ORDER BIT_5 /* Invalid entry order. */
#define RF_INV_E_COUNT BIT_4 /* Invalid entry count. */
#define RF_INV_E_PARAM BIT_3 /* Invalid entry parameter. */
#define RF_INV_E_TYPE BIT_2 /* Invalid entry type. */
#define RF_BUSY BIT_1 /* Busy */
/*
* Status entry SCSI status bit definitions.
*/
#define SS_MASK 0xfff /* Reserved bits BIT_12-BIT_15*/
#define SS_RESIDUAL_UNDER BIT_11
#define SS_RESIDUAL_OVER BIT_10
#define SS_SENSE_LEN_VALID BIT_9
#define SS_RESIDUAL_LEN_VALID BIT_8 /* ISP2100 only */
#define SS_RESPONSE_INFO_LEN_VALID BIT_8 /* ISP2200 and 23xx */
#define SS_RESERVE_CONFLICT (BIT_4 | BIT_3)
#define SS_BUSY_CONDITION BIT_3
#define SS_CONDITION_MET BIT_2
#define SS_CHECK_CONDITION BIT_1
/*
* Status entry completion status
*/
#define CS_COMPLETE 0x0 /* No errors */
#define CS_INCOMPLETE 0x1 /* Incomplete transfer of cmd. */
#define CS_DMA 0x2 /* A DMA direction error. */
#define CS_TRANSPORT 0x3 /* Transport error. */
#define CS_RESET 0x4 /* SCSI bus reset occurred */
#define CS_ABORTED 0x5 /* System aborted command. */
#define CS_TIMEOUT 0x6 /* Timeout error. */
#define CS_DATA_OVERRUN 0x7 /* Data overrun. */
#define CS_DATA_UNDERRUN 0x15 /* Data Underrun. */
#define CS_QUEUE_FULL 0x1C /* Queue Full. */
#define CS_PORT_UNAVAILABLE 0x28 /* Port unavailable */
/* (selection timeout) */
#define CS_PORT_LOGGED_OUT 0x29 /* Port Logged Out */
#define CS_PORT_CONFIG_CHG 0x2A /* Port Configuration Changed */
#define CS_PORT_BUSY 0x2B /* Port Busy */
#define CS_COMPLETE_CHKCOND 0x30 /* Error? */
#define CS_BAD_PAYLOAD 0x80 /* Driver defined */
#define CS_UNKNOWN 0x81 /* Driver defined */
#define CS_RETRY 0x82 /* Driver defined */
#define CS_LOOP_DOWN_ABORT 0x83 /* Driver defined */
/*
* Status entry status flags
*/
#define SF_LOGOUT_SENT BIT_13
/*
* ISP queue - status continuation entry structure definition.
*/
#define STATUS_CONT_TYPE 0x10 /* Status continuation entry. */
typedef struct {
uint8_t entry_type; /* Entry type. */
uint8_t entry_count; /* Entry count. */
uint8_t sys_define; /* System defined. */
uint8_t entry_status; /* Entry Status. */
uint8_t data[60]; /* data */
} sts_cont_entry_t;
/*
* ISP queue - RIO Type 1 status entry (32 bit I/O entry handles)
* structure definition.
*/
#define STATUS_TYPE_21 0x21 /* Status entry. */
typedef struct {
uint8_t entry_type; /* Entry type. */
uint8_t entry_count; /* Entry count. */
uint8_t handle_count; /* Handle count. */
uint8_t entry_status; /* Entry Status. */
uint32_t handle[15]; /* System handles. */
} sts21_entry_t;
/*
* ISP queue - RIO Type 2 status entry (16 bit I/O entry handles)
* structure definition.
*/
#define STATUS_TYPE_22 0x22 /* Status entry. */
typedef struct {
uint8_t entry_type; /* Entry type. */
uint8_t entry_count; /* Entry count. */
uint8_t handle_count; /* Handle count. */
uint8_t entry_status; /* Entry Status. */
uint16_t handle[30]; /* System handles. */
} sts22_entry_t;
/*
* ISP queue - marker entry structure definition.
*/
#define MARKER_TYPE 0x04 /* Marker entry. */
typedef struct {
uint8_t entry_type; /* Entry type. */
uint8_t entry_count; /* Entry count. */
uint8_t handle_count; /* Handle count. */
uint8_t entry_status; /* Entry Status. */
uint32_t sys_define_2; /* System defined. */
target_id_t target; /* SCSI ID */
uint8_t modifier; /* Modifier (7-0). */
#define MK_SYNC_ID_LUN 0 /* Synchronize ID/LUN */
#define MK_SYNC_ID 1 /* Synchronize ID */
#define MK_SYNC_ALL 2 /* Synchronize all ID/LUN */
#define MK_SYNC_LIP 3 /* Synchronize all ID/LUN, */
/* clear port changed, */
/* use sequence number. */
uint8_t reserved_1;
uint16_t sequence_number; /* Sequence number of event */
uint16_t lun; /* SCSI LUN */
uint8_t reserved_2[48];
} mrk_entry_t;
/*
* ISP queue - Management Server entry structure definition.
*/
#define MS_IOCB_TYPE 0x29 /* Management Server IOCB entry */
typedef struct {
uint8_t entry_type; /* Entry type. */
uint8_t entry_count; /* Entry count. */
uint8_t handle_count; /* Handle count. */
uint8_t entry_status; /* Entry Status. */
uint32_t handle1; /* System handle. */
target_id_t loop_id;
uint16_t status;
uint16_t control_flags; /* Control flags. */
uint16_t reserved2;
uint16_t timeout;
uint16_t cmd_dsd_count;
uint16_t total_dsd_count;
uint8_t type;
uint8_t r_ctl;
uint16_t rx_id;
uint16_t reserved3;
uint32_t handle2;
uint32_t rsp_bytecount;
uint32_t req_bytecount;
uint32_t dseg_req_address[2]; /* Data segment 0 address. */
uint32_t dseg_req_length; /* Data segment 0 length. */
uint32_t dseg_rsp_address[2]; /* Data segment 1 address. */
uint32_t dseg_rsp_length; /* Data segment 1 length. */
} ms_iocb_entry_t;
/*
* ISP queue - Mailbox Command entry structure definition.
*/
#define MBX_IOCB_TYPE 0x39
struct mbx_entry {
uint8_t entry_type;
uint8_t entry_count;
uint8_t sys_define1;
/* Use sys_define1 for source type */
#define SOURCE_SCSI 0x00
#define SOURCE_IP 0x01
#define SOURCE_VI 0x02
#define SOURCE_SCTP 0x03
#define SOURCE_MP 0x04
#define SOURCE_MPIOCTL 0x05
#define SOURCE_ASYNC_IOCB 0x07
uint8_t entry_status;
uint32_t handle;
target_id_t loop_id;
uint16_t status;
uint16_t state_flags;
uint16_t status_flags;
uint32_t sys_define2[2];
uint16_t mb0;
uint16_t mb1;
uint16_t mb2;
uint16_t mb3;
uint16_t mb6;
uint16_t mb7;
uint16_t mb9;
uint16_t mb10;
uint32_t reserved_2[2];
uint8_t node_name[WWN_SIZE];
uint8_t port_name[WWN_SIZE];
};
/*
* ISP request and response queue entry sizes
*/
#define RESPONSE_ENTRY_SIZE (sizeof(response_t))
#define REQUEST_ENTRY_SIZE (sizeof(request_t))
/*
* 24 bit port ID type definition.
*/
typedef union {
uint32_t b24 : 24;
struct {
uint8_t d_id[3];
uint8_t rsvd_1;
} r;
struct {
uint8_t al_pa;
uint8_t area;
uint8_t domain;
uint8_t rsvd_1;
} b;
} port_id_t;
#define INVALID_PORT_ID 0xFFFFFF
/*
* Switch info gathering structure.
*/
typedef struct {
port_id_t d_id;
uint8_t node_name[WWN_SIZE];
uint8_t port_name[WWN_SIZE];
uint32_t type;
#define SW_TYPE_IP BIT_1
#define SW_TYPE_SCSI BIT_0
} sw_info_t;
/*
* Inquiry command structure.
*/
#define INQ_DATA_SIZE 8
/*
* Inquiry mailbox IOCB packet definition.
*/
typedef struct {
union {
cmd_a64_entry_t cmd;
sts_entry_t rsp;
} p;
uint8_t inq[INQ_DATA_SIZE];
} inq_cmd_rsp_t;
/*
* Report LUN command structure.
*/
#define CHAR_TO_SHORT(a, b) (uint16_t)((uint8_t)b << 8 | (uint8_t)a)
typedef struct {
uint32_t len;
uint32_t rsrv;
} rpt_hdr_t;
typedef struct {
struct {
uint8_t b : 6;
uint8_t address_method : 2;
} msb;
uint8_t lsb;
uint8_t unused[6];
} rpt_lun_t;
typedef struct {
rpt_hdr_t hdr;
rpt_lun_t lst[MAX_LUNS];
} rpt_lun_lst_t;
/*
* Report Lun mailbox IOCB packet definition.
*/
typedef struct {
union {
cmd_a64_entry_t cmd;
sts_entry_t rsp;
} p;
rpt_lun_lst_t list;
} rpt_lun_cmd_rsp_t;
/*
* SCSI Target Queue structure
*/
typedef struct os_tgt {
struct os_lun *olun[MAX_LUNS]; /* LUN context pointer. */
struct fc_port *fcport;
uint32_t flags;
uint8_t port_down_retry_count;
uint32_t down_timer;
struct scsi_qla_host *ha;
/* Persistent binding information */
port_id_t d_id;
uint8_t node_name[WWN_SIZE];
uint8_t port_name[WWN_SIZE];
} os_tgt_t;
/*
* SCSI Target Queue flags
*/
#define TQF_QUEUE_SUSPENDED BIT_0 /* Queue suspended. */
#define TQF_BOOT_DEVICE BIT_1 /* Boot device. */
#define TQF_ONLINE BIT_2 /* Device online to OS. */
#define TQF_TGT_RST_NEEDED BIT_3
/*
* SCSI LUN Queue structure
*/
typedef struct os_lun {
struct fc_lun *fclun; /* FC LUN context pointer. */
spinlock_t q_lock; /* Lun Lock */
unsigned long q_flag;
#define LUN_MPIO_BUSY 2 /* Lun is changing paths */
#define LUN_EXEC_DELAYED 7 /* Lun execution is delayed */
u_long q_timeout; /* total command timeouts */
atomic_t q_timer; /* suspend timer */
uint32_t q_count; /* current count */
uint32_t q_max; /* maxmum count lun can be suspended */
uint8_t q_state; /* lun State */
#define LUN_STATE_READY 1 /* lun is ready for i/o */
#define LUN_STATE_RUN 2 /* lun has a timer running */
#define LUN_STATE_WAIT 3 /* lun is suspended */
#define LUN_STATE_TIMEOUT 4 /* lun has timed out */
u_long io_cnt; /* total xfer count since boot */
u_long out_cnt; /* total outstanding IO count */
u_long w_cnt; /* total writes */
u_long r_cnt; /* total reads */
u_long avg_time; /* */
} os_lun_t;
/* LUN BitMask structure definition, array of 32bit words,
* 1 bit per lun. When bit == 1, the lun is masked.
* Most significant bit of mask[0] is lun 0, bit 24 is lun 7.
*/
typedef struct lun_bit_mask {
/* Must allocate at least enough bits to accomodate all LUNs */
#if ((MAX_FIBRE_LUNS & 0x7) == 0)
uint8_t mask[MAX_FIBRE_LUNS >> 3];
#else
uint8_t mask[(MAX_FIBRE_LUNS + 8) >> 3];
#endif
} lun_bit_mask_t;
/*
* Fibre channel port type.
*/
typedef enum {
FCT_UNKNOWN,
FCT_RSCN,
FCT_SWITCH,
FCT_BROADCAST,
FCT_INITIATOR,
FCT_TARGET
} fc_port_type_t;
/*
* Fibre channel port structure.
*/
typedef struct fc_port {
struct list_head list;
struct list_head fcluns;
struct scsi_qla_host *ha;
struct scsi_qla_host *vis_ha; /* only used when suspending lun */
uint8_t node_name[WWN_SIZE];
uint8_t port_name[WWN_SIZE];
port_id_t d_id;
uint16_t loop_id;
uint16_t old_loop_id;
fc_port_type_t port_type;
atomic_t state;
uint32_t flags;
os_tgt_t *tgt_queue;
uint16_t os_target_id;
uint16_t iodesc_idx_sent;
int port_login_retry_count;
int login_retry;
atomic_t port_down_timer;
uint8_t device_type;
uint8_t unused;
uint8_t mp_byte; /* multi-path byte (not used) */
uint8_t cur_path; /* current path id */
lun_bit_mask_t lun_mask;
} fc_port_t;
/*
* Fibre channel port/lun states.
*/
#define FCS_UNCONFIGURED 1
#define FCS_DEVICE_DEAD 2
#define FCS_DEVICE_LOST 3
#define FCS_ONLINE 4
#define FCS_NOT_SUPPORTED 5
#define FCS_FAILOVER 6
#define FCS_FAILOVER_FAILED 7
/*
* FC port flags.
*/
#define FCF_FABRIC_DEVICE BIT_0
#define FCF_LOGIN_NEEDED BIT_1
#define FCF_FO_MASKED BIT_2
#define FCF_FAILOVER_NEEDED BIT_3
#define FCF_RESET_NEEDED BIT_4
#define FCF_PERSISTENT_BOUND BIT_5
#define FCF_TAPE_PRESENT BIT_6
#define FCF_FARP_DONE BIT_7
#define FCF_FARP_FAILED BIT_8
#define FCF_FARP_REPLY_NEEDED BIT_9
#define FCF_AUTH_REQ BIT_10
#define FCF_SEND_AUTH_REQ BIT_11
#define FCF_RECEIVE_AUTH_REQ BIT_12
#define FCF_AUTH_SUCCESS BIT_13
#define FCF_RLC_SUPPORT BIT_14
#define FCF_CONFIG BIT_15 /* Needed? */
#define FCF_RESCAN_NEEDED BIT_16
/* No loop ID flag. */
#define FC_NO_LOOP_ID 0x1000
/*
* Fibre channel LUN structure.
*/
typedef struct fc_lun {
struct list_head list;
fc_port_t *fcport;
fc_port_t *o_fcport;
uint16_t lun;
atomic_t state;
uint8_t device_type;
uint8_t max_path_retries;
} fc_lun_t;
/*
* FC-CT interface
*
* NOTE: All structures are big-endian in form.
*/
#define CT_REJECT_RESPONSE 0x8001
#define CT_ACCEPT_RESPONSE 0x8002
#define NS_N_PORT_TYPE 0x01
#define NS_NL_PORT_TYPE 0x02
#define NS_NX_PORT_TYPE 0x7F
#define GA_NXT_CMD 0x100
#define GA_NXT_REQ_SIZE (16 + 4)
#define GA_NXT_RSP_SIZE (16 + 620)
#define GID_PT_CMD 0x1A1
#define GID_PT_REQ_SIZE (16 + 4)
#define GID_PT_RSP_SIZE (16 + (MAX_FIBRE_DEVICES * 4))
#define GPN_ID_CMD 0x112
#define GPN_ID_REQ_SIZE (16 + 4)
#define GPN_ID_RSP_SIZE (16 + 8)
#define GNN_ID_CMD 0x113
#define GNN_ID_REQ_SIZE (16 + 4)
#define GNN_ID_RSP_SIZE (16 + 8)
#define GFT_ID_CMD 0x117
#define GFT_ID_REQ_SIZE (16 + 4)
#define GFT_ID_RSP_SIZE (16 + 32)
#define RFT_ID_CMD 0x217
#define RFT_ID_REQ_SIZE (16 + 4 + 32)
#define RFT_ID_RSP_SIZE 16
#define RFF_ID_CMD 0x21F
#define RFF_ID_REQ_SIZE (16 + 4 + 2 + 1 + 1)
#define RFF_ID_RSP_SIZE 16
#define RNN_ID_CMD 0x213
#define RNN_ID_REQ_SIZE (16 + 4 + 8)
#define RNN_ID_RSP_SIZE 16
#define RSNN_NN_CMD 0x239
#define RSNN_NN_REQ_SIZE (16 + 8 + 1 + 255)
#define RSNN_NN_RSP_SIZE 16
/* CT command header -- request/response common fields */
struct ct_cmd_hdr {
uint8_t revision;
uint8_t in_id[3];
uint8_t gs_type;
uint8_t gs_subtype;
uint8_t options;
uint8_t reserved;
};
/* CT command request */
struct ct_sns_req {
struct ct_cmd_hdr header;
uint16_t command;
uint16_t max_rsp_size;
uint8_t fragment_id;
uint8_t reserved[3];
union {
/* GA_NXT, GPN_ID, GNN_ID, GFT_ID */
struct {
uint8_t reserved;
uint8_t port_id[3];
} port_id;
struct {
uint8_t port_type;
uint8_t domain;
uint8_t area;
uint8_t reserved;
} gid_pt;
struct {
uint8_t reserved;
uint8_t port_id[3];
uint8_t fc4_types[32];
} rft_id;
struct {
uint8_t reserved;
uint8_t port_id[3];
uint16_t reserved2;
uint8_t fc4_feature;
uint8_t fc4_type;
} rff_id;
struct {
uint8_t reserved;
uint8_t port_id[3];
uint8_t node_name[8];
} rnn_id;
struct {
uint8_t node_name[8];
uint8_t name_len;
uint8_t sym_node_name[255];
} rsnn_nn;
} req;
};
/* CT command response header */
struct ct_rsp_hdr {
struct ct_cmd_hdr header;
uint16_t response;
uint16_t residual;
uint8_t fragment_id;
uint8_t reason_code;
uint8_t explanation_code;
uint8_t vendor_unique;
};
struct ct_sns_gid_pt_data {
uint8_t control_byte;
uint8_t port_id[3];
};
struct ct_sns_rsp {
struct ct_rsp_hdr header;
union {
struct {
uint8_t port_type;
uint8_t port_id[3];
uint8_t port_name[8];
uint8_t sym_port_name_len;
uint8_t sym_port_name[255];
uint8_t node_name[8];
uint8_t sym_node_name_len;
uint8_t sym_node_name[255];
uint8_t init_proc_assoc[8];
uint8_t node_ip_addr[16];
uint8_t class_of_service[4];
uint8_t fc4_types[32];
uint8_t ip_address[16];
uint8_t fabric_port_name[8];
uint8_t reserved;
uint8_t hard_address[3];
} ga_nxt;
struct {
struct ct_sns_gid_pt_data entries[MAX_FIBRE_DEVICES];
} gid_pt;
struct {
uint8_t port_name[8];
} gpn_id;
struct {
uint8_t node_name[8];
} gnn_id;
struct {
uint8_t fc4_types[32];
} gft_id;
} rsp;
};
struct ct_sns_pkt {
union {
struct ct_sns_req req;
struct ct_sns_rsp rsp;
} p;
};
/* IO descriptors */
#define MAX_IO_DESCRIPTORS 32
#define ABORT_IOCB_CB 0
#define ADISC_PORT_IOCB_CB 1
#define LOGOUT_PORT_IOCB_CB 2
#define LOGIN_PORT_IOCB_CB 3
#define LAST_IOCB_CB 4
#define IODESC_INVALID_INDEX 0xFFFF
#define IODESC_ADISC_NEEDED 0xFFFE
#define IODESC_LOGIN_NEEDED 0xFFFD
struct io_descriptor {
uint16_t used:1;
uint16_t idx:11;
uint16_t cb_idx:4;
struct timer_list timer;
struct scsi_qla_host *ha;
port_id_t d_id;
fc_port_t *remote_fcport;
uint32_t signature;
};
/* Mailbox command semaphore queue for command serialization */
typedef struct _mbx_cmdq_t {
struct semaphore cmd_sem;
struct _mbx_cmdq_t *pnext;
} mbx_cmdq_t;
struct qla_fw_info {
unsigned short addressing; /* addressing method used to load fw */
#define FW_INFO_ADDR_NORMAL 0
#define FW_INFO_ADDR_EXTENDED 1
#define FW_INFO_ADDR_NOMORE 0xffff
unsigned short *fwcode; /* pointer to FW array */
unsigned short *fwlen; /* number of words in array */
unsigned short *fwstart; /* start address for F/W */
unsigned long *lfwstart; /* start address (long) for F/W */
};
struct qla_board_info {
char *drv_name;
char isp_name[8];
struct qla_fw_info *fw_info;
};
/*
* Linux Host Adapter structure
*/
typedef struct scsi_qla_host {
struct list_head list;
/* Commonly used flags and state information. */
struct Scsi_Host *host;
struct pci_dev *pdev;
unsigned long host_no;
unsigned long instance;
volatile struct {
uint32_t init_done :1;
uint32_t online :1;
uint32_t mbox_int :1;
uint32_t mbox_busy :1;
uint32_t rscn_queue_overflow :1;
uint32_t reset_active :1;
uint32_t management_server_logged_in :1;
uint32_t process_response_queue :1;
uint32_t disable_risc_code_load :1;
uint32_t enable_64bit_addressing :1;
uint32_t enable_lip_reset :1;
uint32_t enable_lip_full_login :1;
uint32_t enable_target_reset :1;
} flags;
atomic_t loop_state;
#define LOOP_TIMEOUT 1
#define LOOP_DOWN 2
#define LOOP_UP 3
#define LOOP_UPDATE 4
#define LOOP_READY 5
#define LOOP_DEAD 6
unsigned long dpc_flags;
#define RESET_MARKER_NEEDED 0 /* Send marker to ISP. */
#define RESET_ACTIVE 1
#define ISP_ABORT_NEEDED 2 /* Initiate ISP abort. */
#define ABORT_ISP_ACTIVE 3 /* ISP abort in progress. */
#define LOOP_RESYNC_NEEDED 4 /* Device Resync needed. */
#define LOOP_RESYNC_ACTIVE 5
#define LOCAL_LOOP_UPDATE 6 /* Perform a local loop update. */
#define RSCN_UPDATE 7 /* Perform an RSCN update. */
#define MAILBOX_RETRY 8
#define ISP_RESET_NEEDED 9 /* Initiate a ISP reset. */
#define FAILOVER_EVENT_NEEDED 10
#define FAILOVER_EVENT 11
#define FAILOVER_NEEDED 12
#define SCSI_RESTART_NEEDED 13 /* Processes SCSI retry queue. */
#define PORT_RESTART_NEEDED 14 /* Processes Retry queue. */
#define RESTART_QUEUES_NEEDED 15 /* Restarts the Lun queue. */
#define ABORT_QUEUES_NEEDED 16
#define RELOGIN_NEEDED 17
#define LOGIN_RETRY_NEEDED 18 /* Initiate required fabric logins. */
#define REGISTER_FC4_NEEDED 19 /* SNS FC4 registration required. */
#define ISP_ABORT_RETRY 20 /* ISP aborted. */
#define FCPORT_RESCAN_NEEDED 21 /* IO descriptor processing needed */
#define IODESC_PROCESS_NEEDED 22 /* IO descriptor processing needed */
uint32_t device_flags;
#define DFLG_LOCAL_DEVICES BIT_0
#define DFLG_RETRY_LOCAL_DEVICES BIT_1
#define DFLG_FABRIC_DEVICES BIT_2
#define SWITCH_FOUND BIT_3
#define DFLG_NO_CABLE BIT_4
/* SRB cache. */
#define SRB_MIN_REQ 128
mempool_t *srb_mempool;
/* This spinlock is used to protect "io transactions", you must
* aquire it before doing any IO to the card, eg with RD_REG*() and
* WRT_REG*() for the duration of your entire commandtransaction.
*
* This spinlock is of lower priority than the io request lock.
*/
spinlock_t hardware_lock ____cacheline_aligned;
device_reg_t *iobase; /* Base I/O address */
unsigned long pio_address;
unsigned long pio_length;
void * mmio_address;
unsigned long mmio_length;
#define MIN_IOBASE_LEN 0x100
/* ISP ring lock, rings, and indexes */
dma_addr_t request_dma; /* Physical address. */
request_t *request_ring; /* Base virtual address */
request_t *request_ring_ptr; /* Current address. */
uint16_t req_ring_index; /* Current index. */
uint16_t req_q_cnt; /* Number of available entries. */
dma_addr_t response_dma; /* Physical address. */
response_t *response_ring; /* Base virtual address */
response_t *response_ring_ptr; /* Current address. */
uint16_t rsp_ring_index; /* Current index. */
uint16_t response_q_length;
uint16_t (*calc_request_entries)(uint16_t);
void (*build_scsi_iocbs)(srb_t *, cmd_entry_t *, uint16_t);
/* Outstandings ISP commands. */
srb_t *outstanding_cmds[MAX_OUTSTANDING_COMMANDS];
uint32_t current_outstanding_cmd;
srb_t *status_srb; /* Status continuation entry. */
/*
* Need to hold the list_lock with irq's disabled in order to access
* the following list.
*
* This list_lock is of lower priority than the host_lock.
*/
spinlock_t list_lock ____cacheline_aligned;
/* lock to guard lists which
* hold srb_t's */
struct list_head retry_queue; /* watchdog queue */
struct list_head done_queue; /* job on done queue */
struct list_head failover_queue; /* failover list link. */
struct list_head scsi_retry_queue; /* SCSI retry queue */
struct list_head pending_queue; /* SCSI command pending queue */
unsigned long done_q_cnt;
unsigned long pending_in_q;
uint32_t retry_q_cnt;
uint32_t scsi_retry_q_cnt;
uint32_t failover_cnt;
unsigned long last_irq_cpu; /* cpu where we got our last irq */
uint16_t revision;
uint8_t ports;
u_long actthreads;
u_long ipreq_cnt;
u_long qthreads;
uint32_t total_isr_cnt; /* Interrupt count */
uint32_t total_isp_aborts; /* controller err cnt */
uint32_t total_lip_cnt; /* LIP cnt */
uint32_t total_dev_errs; /* device error cnt */
uint32_t total_ios; /* IO cnt */
uint64_t total_bytes; /* xfr byte cnt */
uint32_t total_mbx_timeout; /* mailbox timeout cnt */
uint32_t total_loop_resync; /* loop resyn cnt */
uint32_t dropped_frame_error_cnt;
/* ISP configuration data. */
uint16_t loop_id; /* Host adapter loop id */
uint16_t fb_rev;
port_id_t d_id; /* Host adapter port id */
uint16_t max_public_loop_ids;
uint16_t min_external_loopid; /* First external loop Id */
uint8_t current_topology;
uint8_t prev_topology;
#define ISP_CFG_NL 1
#define ISP_CFG_N 2
#define ISP_CFG_FL 4
#define ISP_CFG_F 8
uint8_t operating_mode; /* F/W operating mode */
#define LOOP 0
#define P2P 1
#define LOOP_P2P 2
#define P2P_LOOP 3
uint8_t active_fc4_types; /* Active fc4 types */
uint8_t current_speed; /* F/W operating speed */
uint8_t marker_needed;
uint8_t sns_retry_cnt;
uint8_t mem_err;
uint8_t interrupts_on;
/* HBA serial number */
uint8_t serial0;
uint8_t serial1;
uint8_t serial2;
/* NVRAM configuration data */
uint16_t nvram_base;
uint16_t loop_reset_delay;
uint16_t minimum_timeout;
uint8_t retry_count;
uint8_t login_timeout;
uint16_t r_a_tov;
int port_down_retry_count;
uint8_t loop_down_timeout;
uint8_t mbx_count;
uint16_t max_probe_luns;
uint16_t max_luns;
uint16_t max_targets;
uint16_t last_loop_id;
uint32_t login_retry_count;
/* Fibre Channel Device List. */
struct list_head fcports;
struct list_head rscn_fcports;
struct io_descriptor io_descriptors[MAX_IO_DESCRIPTORS];
uint16_t iodesc_signature;
port_database_t *iodesc_pd;
dma_addr_t iodesc_pd_dma;
/* OS target queue pointers. */
os_tgt_t *otgt[MAX_FIBRE_DEVICES];
/* RSCN queue. */
uint32_t rscn_queue[MAX_RSCN_COUNT];
uint8_t rscn_in_ptr;
uint8_t rscn_out_ptr;
ms_iocb_entry_t *ms_iocb;
dma_addr_t ms_iocb_dma;
struct ct_sns_pkt *ct_sns;
dma_addr_t ct_sns_dma;
pid_t dpc_pid;
int dpc_should_die;
struct completion dpc_inited;
struct completion dpc_exited;
struct semaphore *dpc_wait;
uint8_t dpc_active; /* DPC routine is active */
/* Timeout timers. */
uint8_t queue_restart_timer;
uint8_t loop_down_abort_time; /* port down timer */
atomic_t loop_down_timer; /* loop down timer */
uint8_t link_down_timeout; /* link down timeout */
uint32_t timer_active;
struct timer_list timer;
/* Firmware Initialization Control Block data */
dma_addr_t init_cb_dma; /* Physical address. */
init_cb_t *init_cb;
/* These are used by mailbox operations. */
volatile uint16_t mailbox_out[MAILBOX_REGISTER_COUNT];
mbx_cmd_t *mcp;
unsigned long mbx_cmd_flags;
#define MBX_CMD_ACTIVE 1
#define MBX_CMD_WANT 2
#define MBX_INTERRUPT 3
#define MBX_INTR_WAIT 4
spinlock_t mbx_reg_lock; /* Mbx Cmd Register Lock */
spinlock_t mbx_q_lock; /* Mbx Active Cmd Queue Lock */
spinlock_t mbx_bits_lock; /* Mailbox access bits Lock */
struct semaphore mbx_intr_sem; /* Used for completion notification */
mbx_cmdq_t *mbx_sem_pool_head; /* Head Pointer to a list of
* recyclable mbx semaphore pool
* to be used during run time.
*/
mbx_cmdq_t *mbx_sem_pool_tail; /* Tail Pointer to semaphore pool*/
#define MBQ_INIT_LEN 16 /* initial mbx sem pool q len. actual len may vary */
mbx_cmdq_t *mbx_q_head; /* Head Pointer to sem q for active cmds */
mbx_cmdq_t *mbx_q_tail; /* Tail Pointer to sem q for active cmds */
uint32_t mbx_flags;
#define MBX_IN_PROGRESS BIT_0
#define MBX_BUSY BIT_1 /* Got the Access */
#define MBX_SLEEPING_ON_SEM BIT_2
#define MBX_POLLING_FOR_COMP BIT_3
#define MBX_COMPLETED BIT_4
#define MBX_TIMEDOUT BIT_5
#define MBX_ACCESS_TIMEDOUT BIT_6
mbx_cmd_t mc;
uint8_t *cmdline;
uint32_t failover_type;
uint32_t failback_delay;
unsigned long cfg_flags;
#define CFG_ACTIVE 0 /* CFG during a failover, event update, or ioctl */
uint32_t binding_type;
#define BIND_BY_PORT_NAME 0
#define BIND_BY_PORT_ID 1
/* Basic firmware related information. */
struct qla_board_info *brd_info;
uint16_t fw_major_version;
uint16_t fw_minor_version;
uint16_t fw_subminor_version;
uint16_t fw_attributes;
uint32_t fw_transfer_size;
uint16_t fw_options[16]; /* slots: 1,2,3,10,11 */
uint8_t fw_seriallink_options[2];
/* Firmware dump information. */
void *fw_dump;
int fw_dump_order;
int fw_dump_reading;
char *fw_dump_buffer;
int fw_dump_buffer_len;
uint8_t host_str[16];
uint16_t pci_attr;
uint16_t xchg_buf_cnt;
uint16_t iocb_buf_cnt;
uint8_t model_number[16+1];
#define BINZERO "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
char *model_desc;
/* following are new and needed for IOCTL support */
#ifdef CONFIG_SCSI_QLA2XXX_IOCTL
struct hba_ioctl *ioctl;
void *ioctl_mem;
dma_addr_t ioctl_mem_phys;
uint32_t ioctl_mem_size;
#endif
uint8_t node_name[WWN_SIZE];
uint8_t nvram_version;
uint8_t optrom_major;
uint8_t optrom_minor;
uint32_t isp_abort_cnt;
/* Adapter I/O statistics for failover */
uint64_t IosRequested;
uint64_t BytesRequested;
uint64_t IosExecuted;
uint64_t BytesExecuted;
/* Needed for BEACON */
uint16_t beacon_blink_led;
uint16_t beacon_green_on;
} scsi_qla_host_t;
/*
* Macros to help code, maintain, etc.
*/
#define LOOP_TRANSITION(ha) \
(test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags) || \
test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags))
#define LOOP_NOT_READY(ha) \
((test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags) || \
test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags) || \
test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags) || \
test_bit(LOOP_RESYNC_ACTIVE, &ha->dpc_flags)) || \
atomic_read(&ha->loop_state) == LOOP_DOWN)
#define LOOP_RDY(ha) (!LOOP_NOT_READY(ha))
#define TGT_Q(ha, t) (ha->otgt[t])
#define LUN_Q(ha, t, l) (TGT_Q(ha, t)->olun[l])
#define GET_LU_Q(ha, t, l) ((TGT_Q(ha,t) != NULL)? TGT_Q(ha, t)->olun[l] : NULL)
#define to_qla_host(x) ((scsi_qla_host_t *) (x)->hostdata)
#define qla_printk(level, ha, format, arg...) \
dev_printk(level , &((ha)->pdev->dev) , format , ## arg)
/*
* qla2x00 local function return status codes
*/
#define MBS_MASK 0x3fff
#define QLA_SUCCESS (MBS_COMMAND_COMPLETE & MBS_MASK)
#define QLA_INVALID_COMMAND (MBS_INVALID_COMMAND & MBS_MASK)
#define QLA_INTERFACE_ERROR (MBS_HOST_INTERFACE_ERROR & MBS_MASK)
#define QLA_TEST_FAILED (MBS_TEST_FAILED & MBS_MASK)
#define QLA_COMMAND_ERROR (MBS_COMMAND_ERROR & MBS_MASK)
#define QLA_PARAMETER_ERROR (MBS_COMMAND_PARAMETER_ERROR & MBS_MASK)
#define QLA_PORT_ID_USED (MBS_PORT_ID_USED & MBS_MASK)
#define QLA_LOOP_ID_USED (MBS_LOOP_ID_USED & MBS_MASK)
#define QLA_ALL_IDS_IN_USE (MBS_ALL_IDS_IN_USE & MBS_MASK)
#define QLA_NOT_LOGGED_IN (MBS_NOT_LOGGED_IN & MBS_MASK)
#define QLA_FUNCTION_TIMEOUT 0x100
#define QLA_FUNCTION_PARAMETER_ERROR 0x101
#define QLA_FUNCTION_FAILED 0x102
#define QLA_MEMORY_ALLOC_FAILED 0x103
#define QLA_LOCK_TIMEOUT 0x104
#define QLA_ABORTED 0x105
#define QLA_SUSPENDED 0x106
#define QLA_BUSY 0x107
#define QLA_RSCNS_HANDLED 0x108
/*
* Stat info for all adpaters
*/
struct _qla2x00stats {
unsigned long mboxtout; /* mailbox timeouts */
unsigned long mboxerr; /* mailbox errors */
unsigned long ispAbort; /* ISP aborts */
unsigned long debugNo;
unsigned long loop_resync;
unsigned long outarray_full;
unsigned long retry_q_cnt;
};
#define NVRAM_DELAY() udelay(10)
#define INVALID_HANDLE (MAX_OUTSTANDING_COMMANDS+1)
/*
* Flash support definitions
*/
#define FLASH_IMAGE_SIZE 131072
#include "qla_gbl.h"
#include "qla_dbg.h"
#include "qla_inline.h"
#include "qla_listops.h"
#endif
#define QLA_MODEL_NAMES 0x1B
/*
* Adapter model names.
*/
char *qla2x00_model_name[QLA_MODEL_NAMES] = {
"QLA2340", /* 0x100 */
"QLA2342", /* 0x101 */
"QLA2344", /* 0x102 */
"QCP2342", /* 0x103 */
"QSB2340", /* 0x104 */
"QSB2342", /* 0x105 */
"QLA2310", /* 0x106 */
"QLA2332", /* 0x107 */
"QCP2332", /* 0x108 */
"QCP2340", /* 0x109 */
"QLA2342", /* 0x10a */
"QCP2342", /* 0x10b */
"QLA2350", /* 0x10c */
"QLA2352", /* 0x10d */
"QLA2352", /* 0x10e */
"HPQSVS ", /* 0x10f */
"HPQSVS ", /* 0x110 */
"QLA4010", /* 0x111 */
"QLA4010", /* 0x112 */
"QLA4010C", /* 0x113 */
"QLA4010C", /* 0x114 */
"QLA2360", /* 0x115 */
"QLA2362", /* 0x116 */
" ", /* 0x117 */
" ", /* 0x118 */
"QLA200", /* 0x119 */
"QLA200C" /* 0x11A */
};
char *qla2x00_model_desc[QLA_MODEL_NAMES] = {
"133MHz PCI-X to 2Gb FC, Single Channel", /* 0x100 */
"133MHz PCI-X to 2Gb FC, Dual Channel", /* 0x101 */
"133MHz PCI-X to 2Gb FC, Quad Channel", /* 0x102 */
" ", /* 0x103 */
" ", /* 0x104 */
" ", /* 0x105 */
" ", /* 0x106 */
" ", /* 0x107 */
" ", /* 0x108 */
" ", /* 0x109 */
" ", /* 0x10a */
" ", /* 0x10b */
"133MHz PCI-X to 2Gb FC, Single Channel", /* 0x10c */
"133MHz PCI-X to 2Gb FC, Dual Channel", /* 0x10d */
" ", /* 0x10e */
"HPQ SVS HBA- Initiator device", /* 0x10f */
"HPQ SVS HBA- Target device", /* 0x110 */
"Optical- 133MHz to 1Gb iSCSI- networking", /* 0x111 */
"Optical- 133MHz to 1Gb iSCSI- storage", /* 0x112 */
"Copper- 133MHz to 1Gb iSCSI- networking", /* 0x113 */
"Copper- 133MHz to 1Gb iSCSI- storage", /* 0x114 */
"133MHz PCI-X to 2Gb FC Single Channel", /* 0x115 */
"133MHz PCI-X to 2Gb FC Dual Channel", /* 0x116 */
" ", /* 0x117 */
" ", /* 0x118 */
"133MHz PCI-X to 2Gb FC Optical", /* 0x119 */
"133MHz PCI-X to 2Gb FC Copper" /* 0x11A */
};
/********************************************************************************
* QLOGIC LINUX SOFTWARE
*
* QLogic ISP2x00 device driver for Linux 2.6.x
* Copyright (C) 2003 QLogic Corporation
* (www.qlogic.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.
*
******************************************************************************
* Global include file.
******************************************************************************/
#ifndef __QLA_GBL_H
#define __QLA_GBL_H
extern void qla2x00_remove_one(struct pci_dev *);
extern int qla2x00_probe_one(struct pci_dev *, struct qla_board_info *);
/*
* Global Function Prototypes in qla_init.c source file.
*/
extern int qla2x00_initialize_adapter(scsi_qla_host_t *);
extern fc_port_t *qla2x00_alloc_fcport(scsi_qla_host_t *, int);
extern int qla2x00_loop_resync(scsi_qla_host_t *);
extern int qla2x00_find_new_loop_id(scsi_qla_host_t *, fc_port_t *);
extern int qla2x00_fabric_login(scsi_qla_host_t *, fc_port_t *, uint16_t *);
extern int qla2x00_local_device_login(scsi_qla_host_t *, uint16_t);
extern void qla2x00_restart_queues(scsi_qla_host_t *, uint8_t);
extern void qla2x00_rescan_fcports(scsi_qla_host_t *);
extern void qla2x00_tgt_free(scsi_qla_host_t *ha, uint16_t t);
extern os_tgt_t *qla2x00_tgt_alloc(scsi_qla_host_t *, uint16_t);
extern os_lun_t * qla2x00_lun_alloc(scsi_qla_host_t *, uint16_t, uint16_t);
extern int qla2x00_abort_isp(scsi_qla_host_t *);
/*
* Global Data in qla_os.c source file.
*/
extern char qla2x00_version_str[];
extern int num_hosts;
extern int apiHBAInstance;
extern struct _qla2x00stats qla2x00_stats;
extern int ql2xretrycount;
extern int ql2xlogintimeout;
extern int qlport_down_retry;
extern int ql2xmaxqdepth;
extern int displayConfig;
extern int ql2xplogiabsentdevice;
extern int ql2xintrdelaytimer;
extern int ConfigRequired;
extern int Bind;
extern int ql2xsuspendcount;
#if defined(MODULE)
extern char *ql2xopts;
#endif
extern struct list_head qla_hostlist;
extern rwlock_t qla_hostlist_lock;
extern char *qla2x00_get_fw_version_str(struct scsi_qla_host *, char *);
extern int qla2x00_queuecommand(struct scsi_cmnd *,
void (*)(struct scsi_cmnd *));
extern int __qla2x00_suspend_lun(scsi_qla_host_t *, os_lun_t *, int, int, int);
extern void qla2x00_done(scsi_qla_host_t *);
extern void qla2x00_next(scsi_qla_host_t *);
extern void qla2x00_flush_failover_q(scsi_qla_host_t *, os_lun_t *);
extern void qla2x00_reset_lun_fo_counts(scsi_qla_host_t *, os_lun_t *);
extern int qla2x00_check_tgt_status(scsi_qla_host_t *, struct scsi_cmnd *);
extern int qla2x00_check_port_status(scsi_qla_host_t *, fc_port_t *);
extern void qla2x00_extend_timeout(struct scsi_cmnd *, int);
extern srb_t * qla2x00_get_new_sp (scsi_qla_host_t *ha);
extern void qla2x00_mark_device_lost(scsi_qla_host_t *, fc_port_t *, int);
extern void qla2x00_mark_all_devices_lost(scsi_qla_host_t *);
extern int qla2x00_get_prop_xstr(scsi_qla_host_t *, char *, uint8_t *, int);
extern void qla2x00_abort_queues(scsi_qla_host_t *, uint8_t);
extern void qla2x00_blink_led(scsi_qla_host_t *);
/*
* Global Function Prototypes in qla_iocb.c source file.
*/
extern request_t *qla2x00_req_pkt(scsi_qla_host_t *);
extern request_t *qla2x00_ms_req_pkt(scsi_qla_host_t *, srb_t *);
extern void qla2x00_isp_cmd(scsi_qla_host_t *);
extern uint16_t qla2x00_calc_iocbs_32(uint16_t);
extern uint16_t qla2x00_calc_iocbs_64(uint16_t);
extern void qla2x00_build_scsi_iocbs_32(srb_t *, cmd_entry_t *, uint16_t);
extern void qla2x00_build_scsi_iocbs_64(srb_t *, cmd_entry_t *, uint16_t);
extern int qla2x00_start_scsi(srb_t *sp);
int qla2x00_marker(scsi_qla_host_t *, uint16_t, uint16_t, uint8_t);
int __qla2x00_marker(scsi_qla_host_t *, uint16_t, uint16_t, uint8_t);
/*
* Global Function Prototypes in qla_mbx.c source file.
*/
extern int
qla2x00_mailbox_command(scsi_qla_host_t *, mbx_cmd_t *);
extern int
qla2x00_load_ram(scsi_qla_host_t *, dma_addr_t, uint16_t, uint16_t);
extern int
qla2x00_load_ram_ext(scsi_qla_host_t *, dma_addr_t, uint32_t, uint16_t);
extern int
qla2x00_execute_fw(scsi_qla_host_t *);
extern void
qla2x00_get_fw_version(scsi_qla_host_t *, uint16_t *,
uint16_t *, uint16_t *, uint16_t *);
extern int
qla2x00_get_fw_options(scsi_qla_host_t *, uint16_t *);
extern int
qla2x00_set_fw_options(scsi_qla_host_t *, uint16_t *);
extern int
qla2x00_read_ram_word(scsi_qla_host_t *, uint16_t, uint16_t *);
extern int
qla2x00_write_ram_word(scsi_qla_host_t *, uint16_t, uint16_t);
extern int
qla2x00_write_ram_word_ext(scsi_qla_host_t *, uint32_t, uint16_t);
extern int
qla2x00_mbx_reg_test(scsi_qla_host_t *);
extern int
qla2x00_verify_checksum(scsi_qla_host_t *);
extern int
qla2x00_issue_iocb(scsi_qla_host_t *, void *, dma_addr_t, size_t);
extern int
qla2x00_abort_command(scsi_qla_host_t *, srb_t *);
extern int
qla2x00_abort_device(scsi_qla_host_t *, uint16_t, uint16_t);
#if USE_ABORT_TGT
extern int
qla2x00_abort_target(fc_port_t *fcport);
#endif
extern int
qla2x00_target_reset(scsi_qla_host_t *, uint16_t, uint16_t);
extern int
qla2x00_get_adapter_id(scsi_qla_host_t *, uint16_t *, uint8_t *, uint8_t *,
uint8_t *, uint16_t *);
extern int
qla2x00_get_retry_cnt(scsi_qla_host_t *, uint8_t *, uint8_t *, uint16_t *);
extern int
qla2x00_init_firmware(scsi_qla_host_t *, uint16_t);
extern int
qla2x00_get_port_database(scsi_qla_host_t *, fc_port_t *, uint8_t);
extern int
qla2x00_get_firmware_state(scsi_qla_host_t *, uint16_t *);
extern int
qla2x00_get_port_name(scsi_qla_host_t *, uint16_t, uint8_t *, uint8_t);
extern uint8_t
qla2x00_get_link_status(scsi_qla_host_t *, uint8_t, void *, uint16_t *);
extern int
qla2x00_lip_reset(scsi_qla_host_t *);
extern int
qla2x00_send_sns(scsi_qla_host_t *, dma_addr_t, uint16_t, size_t);
extern int
qla2x00_login_fabric(scsi_qla_host_t *, uint16_t, uint8_t, uint8_t, uint8_t,
uint16_t *, uint8_t);
extern int
qla2x00_login_local_device(scsi_qla_host_t *, uint16_t, uint16_t *, uint8_t);
extern int
qla2x00_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id);
extern int
qla2x00_full_login_lip(scsi_qla_host_t *ha);
extern int
qla2x00_get_id_list(scsi_qla_host_t *, void *, dma_addr_t, uint16_t *);
#if 0 /* not yet needed */
extern int
qla2x00_dump_ram(scsi_qla_host_t *, uint32_t, dma_addr_t, uint32_t);
#endif
extern int
qla2x00_lun_reset(scsi_qla_host_t *, uint16_t, uint16_t);
extern int
qla2x00_send_rnid_mbx(scsi_qla_host_t *, uint16_t, uint8_t, dma_addr_t,
size_t, uint16_t *);
extern int
qla2x00_set_rnid_params_mbx(scsi_qla_host_t *, dma_addr_t, size_t, uint16_t *);
extern int
qla2x00_get_rnid_params_mbx(scsi_qla_host_t *, dma_addr_t, size_t, uint16_t *);
extern int
qla2x00_get_resource_cnts(scsi_qla_host_t *, uint16_t *, uint16_t *, uint16_t *,
uint16_t *);
#if defined(QL_DEBUG_LEVEL_3)
extern int
qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map);
#endif
/*
* Global Function Prototypes in qla_isr.c source file.
*/
extern irqreturn_t qla2x00_intr_handler(int, void *, struct pt_regs *);
extern void qla2x00_process_response_queue(struct scsi_qla_host *);
/*
* Global Function Prototypes in qla_sup.c source file.
*/
extern void qla2x00_lock_nvram_access(scsi_qla_host_t *);
extern void qla2x00_unlock_nvram_access(scsi_qla_host_t *);
extern uint16_t qla2x00_get_nvram_word(scsi_qla_host_t *, uint32_t);
extern void qla2x00_write_nvram_word(scsi_qla_host_t *, uint32_t, uint16_t);
extern void qla2x00_flash_enable(scsi_qla_host_t *);
extern void qla2x00_flash_disable(scsi_qla_host_t *);
extern uint8_t qla2x00_read_flash_byte(scsi_qla_host_t *, uint32_t);
extern uint8_t qla2x00_get_flash_manufacturer(scsi_qla_host_t *);
extern uint16_t qla2x00_get_flash_version(scsi_qla_host_t *);
extern uint16_t qla2x00_get_flash_image(scsi_qla_host_t *, uint8_t *);
extern uint16_t qla2x00_set_flash_image(scsi_qla_host_t *, uint8_t *);
/*
* Global Function Prototypes in qla_dbg.c source file.
*/
extern void qla2100_fw_dump(scsi_qla_host_t *, int);
extern void qla2300_fw_dump(scsi_qla_host_t *, int);
extern void qla2100_ascii_fw_dump(scsi_qla_host_t *);
extern void qla2300_ascii_fw_dump(scsi_qla_host_t *);
extern void qla2x00_dump_regs(scsi_qla_host_t *);
extern void qla2x00_dump_buffer(uint8_t *, uint32_t);
extern void qla2x00_print_scsi_cmd(struct scsi_cmnd *);
extern void qla2x00_print_q_info(struct os_lun *);
/*
* Global Function Prototypes in qla_ip.c source file.
*/
extern int qla2x00_ip_initialize(scsi_qla_host_t *);
extern int qla2x00_update_ip_device_data(scsi_qla_host_t *, fc_port_t *);
extern void qla2x00_ip_send_complete(scsi_qla_host_t *, uint32_t, uint16_t);
extern void qla2x00_ip_receive(scsi_qla_host_t *, sts_entry_t *);
extern void qla2x00_ip_receive_fastpost(scsi_qla_host_t *, uint16_t);
extern void qla2x00_ip_mailbox_iocb_done(scsi_qla_host_t *, struct mbx_entry *);
/*
* Global Function Prototypes in qla_gs.c source file.
*/
extern int qla2x00_ga_nxt(scsi_qla_host_t *, fc_port_t *);
extern int qla2x00_gid_pt(scsi_qla_host_t *, sw_info_t *);
extern int qla2x00_gpn_id(scsi_qla_host_t *, sw_info_t *);
extern int qla2x00_gnn_id(scsi_qla_host_t *, sw_info_t *);
extern int qla2x00_gft_id(scsi_qla_host_t *, sw_info_t *);
extern int qla2x00_rft_id(scsi_qla_host_t *);
extern int qla2x00_rff_id(scsi_qla_host_t *);
extern int qla2x00_rnn_id(scsi_qla_host_t *);
extern int qla2x00_rsnn_nn(scsi_qla_host_t *);
/*
* Global Function Prototypes in qla_rscn.c source file.
*/
extern fc_port_t *qla2x00_alloc_rscn_fcport(scsi_qla_host_t *, int);
extern int qla2x00_handle_port_rscn(scsi_qla_host_t *, uint32_t, fc_port_t *,
int);
extern void qla2x00_process_iodesc(scsi_qla_host_t *, struct mbx_entry *);
extern void qla2x00_cancel_io_descriptors(scsi_qla_host_t *);
/*
* Global Function Prototypes in qla_xioctl.c source file.
*/
#ifdef CONFIG_SCSI_QLA2XXX_IOCTL
extern void qla2x00_enqueue_aen(scsi_qla_host_t *, uint16_t, void *);
extern int qla2x00_alloc_ioctl_mem(scsi_qla_host_t *);
extern void qla2x00_free_ioctl_mem(scsi_qla_host_t *);
extern int qla2x00_get_ioctl_scrap_mem(scsi_qla_host_t *, void **, uint32_t);
extern void qla2x00_free_ioctl_scrap_mem(scsi_qla_host_t *);
#else
#define qla2x00_enqueue_aen(ha, cmd, mode) do { } while (0)
#define qla2x00_alloc_ioctl_mem(ha) (0)
#define qla2x00_free_ioctl_mem(ha) do { } while (0)
#endif
#endif /* _QLA_GBL_H */
/*
* QLOGIC LINUX SOFTWARE
*
* QLogic ISP2x00 device driver for Linux 2.6.x
* Copyright (C) 2003 QLogic Corporation
* (www.qlogic.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.
*
*/
#include "qla_os.h"
#include "qla_def.h"
/* XXX(hch): this is ugly, but we don't want to pull in exioctl.h */
#ifndef EXT_DEF_FC4_TYPE_SCSI
#define EXT_DEF_FC4_TYPE_SCSI 0x1
#endif
static inline ms_iocb_entry_t *
qla2x00_prep_ms_iocb(scsi_qla_host_t *, uint32_t, uint32_t);
static inline struct ct_sns_req *
qla2x00_prep_ct_req(struct ct_sns_req *, uint16_t, uint16_t);
/**
* qla2x00_prep_ms_iocb() - Prepare common MS IOCB fields for SNS CT query.
* @ha: HA context
* @req_size: request size in bytes
* @rsp_size: response size in bytes
*
* Returns a pointer to the @ha's ms_iocb.
*/
static inline ms_iocb_entry_t *
qla2x00_prep_ms_iocb(scsi_qla_host_t *ha, uint32_t req_size, uint32_t rsp_size)
{
ms_iocb_entry_t *ms_pkt;
ms_pkt = ha->ms_iocb;
memset(ms_pkt, 0, sizeof(ms_iocb_entry_t));
ms_pkt->entry_type = MS_IOCB_TYPE;
ms_pkt->entry_count = 1;
SET_TARGET_ID(ha, ms_pkt->loop_id, SIMPLE_NAME_SERVER);
ms_pkt->control_flags = __constant_cpu_to_le16(CF_READ | CF_HEAD_TAG);
ms_pkt->timeout = __constant_cpu_to_le16(25);
ms_pkt->cmd_dsd_count = __constant_cpu_to_le16(1);
ms_pkt->total_dsd_count = __constant_cpu_to_le16(2);
ms_pkt->rsp_bytecount = cpu_to_le32(rsp_size);
ms_pkt->req_bytecount = cpu_to_le32(req_size);
ms_pkt->dseg_req_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma));
ms_pkt->dseg_req_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma));
ms_pkt->dseg_req_length = ms_pkt->req_bytecount;
ms_pkt->dseg_rsp_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma));
ms_pkt->dseg_rsp_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma));
ms_pkt->dseg_rsp_length = ms_pkt->rsp_bytecount;
return (ms_pkt);
}
/**
* qla2x00_prep_ct_req() - Prepare common CT request fields for SNS query.
* @ct_req: CT request buffer
* @cmd: GS command
* @rsp_size: response size in bytes
*
* Returns a pointer to the intitialized @ct_req.
*/
static inline struct ct_sns_req *
qla2x00_prep_ct_req(struct ct_sns_req *ct_req, uint16_t cmd, uint16_t rsp_size)
{
memset(ct_req, 0, sizeof(struct ct_sns_pkt));
ct_req->header.revision = 0x01;
ct_req->header.gs_type = 0xFC;
ct_req->header.gs_subtype = 0x02;
ct_req->command = cpu_to_be16(cmd);
ct_req->max_rsp_size = cpu_to_be16((rsp_size - 16) / 4);
return (ct_req);
}
/**
* qla2x00_ga_nxt() - SNS scan for fabric devices via GA_NXT command.
* @ha: HA context
* @fcport: fcport entry to updated
*
* Returns 0 on success.
*/
int
qla2x00_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport)
{
int rval;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
/* Issue GA_NXT */
/* Prepare common MS IOCB */
ms_pkt = qla2x00_prep_ms_iocb(ha, GA_NXT_REQ_SIZE, GA_NXT_RSP_SIZE);
/* Prepare CT request */
ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GA_NXT_CMD,
GA_NXT_RSP_SIZE);
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare CT arguments -- port_id */
ct_req->req.port_id.port_id[0] = fcport->d_id.b.domain;
ct_req->req.port_id.port_id[1] = fcport->d_id.b.area;
ct_req->req.port_id.port_id[2] = fcport->d_id.b.al_pa;
/* Execute MS IOCB */
rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): GA_NXT issue IOCB failed (%d).\n",
ha->host_no, rval));
} else if (ct_rsp->header.response !=
__constant_cpu_to_be16(CT_ACCEPT_RESPONSE)) {
DEBUG2_3(printk("scsi(%ld): GA_NXT failed, rejected request, "
"ga_nxt_rsp:\n", ha->host_no));
DEBUG2_3(qla2x00_dump_buffer((uint8_t *)&ct_rsp->header,
sizeof(struct ct_rsp_hdr)));
rval = QLA_FUNCTION_FAILED;
} else {
/* Populate fc_port_t entry. */
fcport->d_id.b.domain = ct_rsp->rsp.ga_nxt.port_id[0];
fcport->d_id.b.area = ct_rsp->rsp.ga_nxt.port_id[1];
fcport->d_id.b.al_pa = ct_rsp->rsp.ga_nxt.port_id[2];
memcpy(fcport->node_name, ct_rsp->rsp.ga_nxt.node_name,
WWN_SIZE);
memcpy(fcport->port_name, ct_rsp->rsp.ga_nxt.port_name,
WWN_SIZE);
if (ct_rsp->rsp.ga_nxt.port_type != NS_N_PORT_TYPE &&
ct_rsp->rsp.ga_nxt.port_type != NS_NL_PORT_TYPE)
fcport->d_id.b.domain = 0xf0;
DEBUG2_3(printk("scsi(%ld): GA_NXT entry - "
"nn %02x%02x%02x%02x%02x%02x%02x%02x "
"pn %02x%02x%02x%02x%02x%02x%02x%02x "
"portid=%02x%02x%02x.\n",
ha->host_no,
fcport->node_name[0], fcport->node_name[1],
fcport->node_name[2], fcport->node_name[3],
fcport->node_name[4], fcport->node_name[5],
fcport->node_name[6], fcport->node_name[7],
fcport->port_name[0], fcport->port_name[1],
fcport->port_name[2], fcport->port_name[3],
fcport->port_name[4], fcport->port_name[5],
fcport->port_name[6], fcport->port_name[7],
fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.al_pa));
}
return (rval);
}
/**
* qla2x00_gid_pt() - SNS scan for fabric devices via GID_PT command.
* @ha: HA context
* @list: switch info entries to populate
*
* NOTE: Non-Nx_Ports are not requested.
*
* Returns 0 on success.
*/
int
qla2x00_gid_pt(scsi_qla_host_t *ha, sw_info_t *list)
{
int rval;
uint16_t i;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
struct ct_sns_gid_pt_data *gid_data;
gid_data = NULL;
/* Issue GID_PT */
/* Prepare common MS IOCB */
ms_pkt = qla2x00_prep_ms_iocb(ha, GID_PT_REQ_SIZE, GID_PT_RSP_SIZE);
/* Prepare CT request */
ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GID_PT_CMD,
GID_PT_RSP_SIZE);
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare CT arguments -- port_type */
ct_req->req.gid_pt.port_type = NS_NX_PORT_TYPE;
/* Execute MS IOCB */
rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): GID_PT issue IOCB failed (%d).\n",
ha->host_no, rval));
} else if (ct_rsp->header.response !=
__constant_cpu_to_be16(CT_ACCEPT_RESPONSE)) {
DEBUG2_3(printk("scsi(%ld): GID_PT failed, rejected request, "
"gid_pt_rsp:\n", ha->host_no));
DEBUG2_3(qla2x00_dump_buffer((uint8_t *)&ct_rsp->header,
sizeof(struct ct_rsp_hdr)));
rval = QLA_FUNCTION_FAILED;
} else {
/* Set port IDs in switch info list. */
for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
gid_data = &ct_rsp->rsp.gid_pt.entries[i];
list[i].d_id.b.domain = gid_data->port_id[0];
list[i].d_id.b.area = gid_data->port_id[1];
list[i].d_id.b.al_pa = gid_data->port_id[2];
/* Last one exit. */
if (gid_data->control_byte & BIT_7) {
list[i].d_id.b.rsvd_1 = gid_data->control_byte;
break;
}
}
/*
* If we've used all available slots, then the switch is
* reporting back more devices that we can handle with this
* single call. Return a failed status, and let GA_NXT handle
* the overload.
*/
if (i == MAX_FIBRE_DEVICES)
rval = QLA_FUNCTION_FAILED;
}
return (rval);
}
/**
* qla2x00_gpn_id() - SNS Get Port Name (GPN_ID) query.
* @ha: HA context
* @list: switch info entries to populate
*
* Returns 0 on success.
*/
int
qla2x00_gpn_id(scsi_qla_host_t *ha, sw_info_t *list)
{
int rval;
uint16_t i;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
/* Issue GPN_ID */
/* Prepare common MS IOCB */
ms_pkt = qla2x00_prep_ms_iocb(ha, GPN_ID_REQ_SIZE,
GPN_ID_RSP_SIZE);
/* Prepare CT request */
ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GPN_ID_CMD,
GPN_ID_RSP_SIZE);
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare CT arguments -- port_id */
ct_req->req.port_id.port_id[0] = list[i].d_id.b.domain;
ct_req->req.port_id.port_id[1] = list[i].d_id.b.area;
ct_req->req.port_id.port_id[2] = list[i].d_id.b.al_pa;
/* Execute MS IOCB */
rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): GPN_ID issue IOCB failed "
"(%d).\n", ha->host_no, rval));
} else if (ct_rsp->header.response !=
__constant_cpu_to_be16(CT_ACCEPT_RESPONSE)) {
DEBUG2_3(printk("scsi(%ld): GPN_ID failed, rejected "
"request, gpn_id_rsp:\n", ha->host_no));
DEBUG2_3(qla2x00_dump_buffer((uint8_t *)&ct_rsp->header,
sizeof(struct ct_rsp_hdr)));
rval = QLA_FUNCTION_FAILED;
} else {
/* Save portname */
memcpy(list[i].port_name,
ct_rsp->rsp.gpn_id.port_name, WWN_SIZE);
}
/* Last device exit. */
if (list[i].d_id.b.rsvd_1 != 0)
break;
}
return (rval);
}
/**
* qla2x00_gnn_id() - SNS Get Node Name (GPN_ID) query.
* @ha: HA context
* @list: switch info entries to populate
*
* Returns 0 on success.
*/
int
qla2x00_gnn_id(scsi_qla_host_t *ha, sw_info_t *list)
{
int rval;
uint16_t i;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
/* Issue GNN_ID */
/* Prepare common MS IOCB */
ms_pkt = qla2x00_prep_ms_iocb(ha, GNN_ID_REQ_SIZE,
GNN_ID_RSP_SIZE);
/* Prepare CT request */
ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GNN_ID_CMD,
GNN_ID_RSP_SIZE);
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare CT arguments -- port_id */
ct_req->req.port_id.port_id[0] = list[i].d_id.b.domain;
ct_req->req.port_id.port_id[1] = list[i].d_id.b.area;
ct_req->req.port_id.port_id[2] = list[i].d_id.b.al_pa;
/* Execute MS IOCB */
rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): GNN_ID issue IOCB failed "
"(%d).\n", ha->host_no, rval));
} else if (ct_rsp->header.response !=
__constant_cpu_to_be16(CT_ACCEPT_RESPONSE)) {
DEBUG2_3(printk("scsi(%ld): GNN_ID failed, rejected "
"request, gnn_id_rsp:\n", ha->host_no));
DEBUG2_3(qla2x00_dump_buffer((uint8_t *)&ct_rsp->header,
sizeof(struct ct_rsp_hdr)));
rval = QLA_FUNCTION_FAILED;
} else {
/* Save nodename */
memcpy(list[i].node_name,
ct_rsp->rsp.gnn_id.node_name, WWN_SIZE);
DEBUG2_3(printk("scsi(%ld): GID_PT entry - "
"nn %02x%02x%02x%02x%02x%02x%02x%02x "
"pn %02x%02x%02x%02x%02x%02x%02x%02x "
"portid=%02x%02x%02x.\n",
ha->host_no,
list[i].node_name[0], list[i].node_name[1],
list[i].node_name[2], list[i].node_name[3],
list[i].node_name[4], list[i].node_name[5],
list[i].node_name[6], list[i].node_name[7],
list[i].port_name[0], list[i].port_name[1],
list[i].port_name[2], list[i].port_name[3],
list[i].port_name[4], list[i].port_name[5],
list[i].port_name[6], list[i].port_name[7],
list[i].d_id.b.domain, list[i].d_id.b.area,
list[i].d_id.b.al_pa));
}
/* Last device exit. */
if (list[i].d_id.b.rsvd_1 != 0)
break;
}
return (rval);
}
/**
* qla2x00_gft_id() - SNS Get FC-4 TYPEs (GFT_ID) query.
* @ha: HA context
* @list: switch info entries to populate
*
* Returns 0 on success.
*/
int
qla2x00_gft_id(scsi_qla_host_t *ha, sw_info_t *list)
{
int rval;
uint16_t i;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
/* Issue GFT_ID */
/* Prepare common MS IOCB */
ms_pkt = qla2x00_prep_ms_iocb(ha, GFT_ID_REQ_SIZE,
GFT_ID_RSP_SIZE);
/* Prepare CT request */
ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GFT_ID_CMD,
GFT_ID_RSP_SIZE);
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare CT arguments -- port_id */
ct_req->req.port_id.port_id[0] = list[i].d_id.b.domain;
ct_req->req.port_id.port_id[1] = list[i].d_id.b.area;
ct_req->req.port_id.port_id[2] = list[i].d_id.b.al_pa;
/* Execute MS IOCB */
rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): GFT_ID issue IOCB failed "
"(%d).\n", ha->host_no, rval));
} else if (ct_rsp->header.response !=
__constant_cpu_to_be16(CT_ACCEPT_RESPONSE)) {
DEBUG2_3(printk("scsi(%ld): GFT_ID failed, rejected "
"request, gft_id_rsp:\n", ha->host_no));
DEBUG2_3(qla2x00_dump_buffer((uint8_t *)&ct_rsp->header,
sizeof(struct ct_rsp_hdr)));
rval = QLA_FUNCTION_FAILED;
} else {
/* FCP-3 check necessary? No, assume FCP-3 */
/*if (ct_rsp->rsp.gft_id.fc4_types[2] & 0x01)*/
list[i].type = SW_TYPE_SCSI;
if (ct_rsp->rsp.gft_id.fc4_types[3] & 0x20)
list[i].type |= SW_TYPE_IP;
}
/* Last device exit. */
if (list[i].d_id.b.rsvd_1 != 0)
break;
}
return (rval);
}
/**
* qla2x00_rft_id() - SNS Register FC-4 TYPEs (RFT_ID) supported by the HBA.
* @ha: HA context
*
* Returns 0 on success.
*/
int
qla2x00_rft_id(scsi_qla_host_t *ha)
{
int rval;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
/* Issue RFT_ID */
/* Prepare common MS IOCB */
ms_pkt = qla2x00_prep_ms_iocb(ha, RFT_ID_REQ_SIZE, RFT_ID_RSP_SIZE);
/* Prepare CT request */
ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RFT_ID_CMD,
RFT_ID_RSP_SIZE);
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare CT arguments -- port_id, FC-4 types */
ct_req->req.rft_id.port_id[0] = ha->d_id.b.domain;
ct_req->req.rft_id.port_id[1] = ha->d_id.b.area;
ct_req->req.rft_id.port_id[2] = ha->d_id.b.al_pa;
ct_req->req.rft_id.fc4_types[2] = 0x01; /* FCP-3 */
ha->active_fc4_types = EXT_DEF_FC4_TYPE_SCSI;
/* Execute MS IOCB */
rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): RFT_ID issue IOCB failed (%d).\n",
ha->host_no, rval));
} else if (ct_rsp->header.response !=
__constant_cpu_to_be16(CT_ACCEPT_RESPONSE)) {
DEBUG2_3(printk("scsi(%ld): RFT_ID failed, rejected "
"request, rft_id_rsp:\n", ha->host_no));
DEBUG2_3(qla2x00_dump_buffer((uint8_t *)&ct_rsp->header,
sizeof(struct ct_rsp_hdr)));
rval = QLA_FUNCTION_FAILED;
} else {
DEBUG2(printk("scsi(%ld): RFT_ID exiting normally.\n",
ha->host_no));
}
return (rval);
}
/**
* qla2x00_rff_id() - SNS Register FC-4 Features (RFF_ID) supported by the HBA.
* @ha: HA context
*
* Returns 0 on success.
*/
int
qla2x00_rff_id(scsi_qla_host_t *ha)
{
int rval;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
/* Issue RFF_ID */
/* Prepare common MS IOCB */
ms_pkt = qla2x00_prep_ms_iocb(ha, RFF_ID_REQ_SIZE, RFF_ID_RSP_SIZE);
/* Prepare CT request */
ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RFF_ID_CMD,
RFF_ID_RSP_SIZE);
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare CT arguments -- port_id, FC-4 feature, FC-4 type */
ct_req->req.rff_id.port_id[0] = ha->d_id.b.domain;
ct_req->req.rff_id.port_id[1] = ha->d_id.b.area;
ct_req->req.rff_id.port_id[2] = ha->d_id.b.al_pa;
ct_req->req.rff_id.fc4_type = 0x08; /* SCSI - FCP */
/* Execute MS IOCB */
rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): RFF_ID issue IOCB failed (%d).\n",
ha->host_no, rval));
} else if (ct_rsp->header.response !=
__constant_cpu_to_be16(CT_ACCEPT_RESPONSE)) {
DEBUG2_3(printk("scsi(%ld): RFF_ID failed, rejected "
"request, rff_id_rsp:\n", ha->host_no));
DEBUG2_3(qla2x00_dump_buffer((uint8_t *)&ct_rsp->header,
sizeof(struct ct_rsp_hdr)));
rval = QLA_FUNCTION_FAILED;
} else {
DEBUG2(printk("scsi(%ld): RFF_ID exiting normally.\n",
ha->host_no));
}
return (rval);
}
/**
* qla2x00_rnn_id() - SNS Register Node Name (RNN_ID) of the HBA.
* @ha: HA context
*
* Returns 0 on success.
*/
int
qla2x00_rnn_id(scsi_qla_host_t *ha)
{
int rval;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
/* Issue RNN_ID */
/* Prepare common MS IOCB */
ms_pkt = qla2x00_prep_ms_iocb(ha, RNN_ID_REQ_SIZE, RNN_ID_RSP_SIZE);
/* Prepare CT request */
ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RNN_ID_CMD,
RNN_ID_RSP_SIZE);
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare CT arguments -- port_id, node_name */
ct_req->req.rnn_id.port_id[0] = ha->d_id.b.domain;
ct_req->req.rnn_id.port_id[1] = ha->d_id.b.area;
ct_req->req.rnn_id.port_id[2] = ha->d_id.b.al_pa;
memcpy(ct_req->req.rnn_id.node_name, ha->init_cb->node_name, WWN_SIZE);
/* Execute MS IOCB */
rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): RNN_ID issue IOCB failed (%d).\n",
ha->host_no, rval));
} else if (ct_rsp->header.response !=
__constant_cpu_to_be16(CT_ACCEPT_RESPONSE)) {
DEBUG2_3(printk("scsi(%ld): RNN_ID failed, rejected "
"request, rnn_id_rsp:\n", ha->host_no));
DEBUG2_3(qla2x00_dump_buffer((uint8_t *)&ct_rsp->header,
sizeof(struct ct_rsp_hdr)));
rval = QLA_FUNCTION_FAILED;
} else {
DEBUG2(printk("scsi(%ld): RNN_ID exiting normally.\n",
ha->host_no));
}
return (rval);
}
/**
* qla2x00_rsnn_nn() - SNS Register Symbolic Node Name (RSNN_NN) of the HBA.
* @ha: HA context
*
* Returns 0 on success.
*/
int
qla2x00_rsnn_nn(scsi_qla_host_t *ha)
{
int rval;
uint8_t *snn;
uint8_t version[20];
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
/* Issue RSNN_NN */
/* Prepare common MS IOCB */
/* Request size adjusted after CT preparation */
ms_pkt = qla2x00_prep_ms_iocb(ha, 0, RSNN_NN_RSP_SIZE);
/* Prepare CT request */
ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RSNN_NN_CMD,
RSNN_NN_RSP_SIZE);
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare CT arguments -- node_name, symbolic node_name, size */
memcpy(ct_req->req.rsnn_nn.node_name, ha->init_cb->node_name, WWN_SIZE);
/* Prepare the Symbolic Node Name */
/* Board type */
snn = ct_req->req.rsnn_nn.sym_node_name;
strcpy(snn, ha->model_number);
/* Firmware version */
strcat(snn, " FW:v");
sprintf(version, "%d.%02d.%02d", ha->fw_major_version,
ha->fw_minor_version, ha->fw_subminor_version);
strcat(snn, version);
/* Driver version */
strcat(snn, " DVR:v");
strcat(snn, qla2x00_version_str);
/* Calculate SNN length */
ct_req->req.rsnn_nn.name_len = (uint8_t)strlen(snn);
/* Update MS IOCB request */
ms_pkt->req_bytecount =
cpu_to_le32(24 + 1 + ct_req->req.rsnn_nn.name_len);
ms_pkt->dseg_req_length = ms_pkt->req_bytecount;
/* Execute MS IOCB */
rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): RSNN_NN issue IOCB failed (%d).\n",
ha->host_no, rval));
} else if (ct_rsp->header.response !=
__constant_cpu_to_be16(CT_ACCEPT_RESPONSE)) {
DEBUG2_3(printk("scsi(%ld): RSNN_NN failed, rejected "
"request, rsnn_id_rsp:\n", ha->host_no));
DEBUG2_3(qla2x00_dump_buffer((uint8_t *)&ct_rsp->header,
sizeof(struct ct_rsp_hdr)));
rval = QLA_FUNCTION_FAILED;
} else {
DEBUG2(printk("scsi(%ld): RSNN_NN exiting normally.\n",
ha->host_no));
}
return (rval);
}
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* QLOGIC LINUX SOFTWARE
*
* QLogic ISP2x00 device driver for Linux 2.6.x
* Copyright (C) 2003 QLogic Corporation
* (www.qlogic.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.
*
*/
static __inline__ uint16_t qla2x00_debounce_register(volatile uint16_t *);
/*
* qla2x00_debounce_register
* Debounce register.
*
* Input:
* port = register address.
*
* Returns:
* register value.
*/
static __inline__ uint16_t
qla2x00_debounce_register(volatile uint16_t *addr)
{
volatile uint16_t first;
volatile uint16_t second;
do {
first = RD_REG_WORD(addr);
barrier();
cpu_relax();
second = RD_REG_WORD(addr);
} while (first != second);
return (first);
}
static __inline__ int qla2x00_normalize_dma_addr(
dma_addr_t *e_addr, uint32_t *e_len,
dma_addr_t *ne_addr, uint32_t *ne_len);
/**
* qla2x00_normalize_dma_addr() - Normalize an DMA address.
* @e_addr: Raw DMA address
* @e_len: Raw DMA length
* @ne_addr: Normalized second DMA address
* @ne_len: Normalized second DMA length
*
* If the address does not span a 4GB page boundary, the contents of @ne_addr
* and @ne_len are undefined. @e_len is updated to reflect a normalization.
*
* Example:
*
* ffffabc0ffffeeee (e_addr) start of DMA address
* 0000000020000000 (e_len) length of DMA transfer
* ffffabc11fffeeed end of DMA transfer
*
* Is the 4GB boundary crossed?
*
* ffffabc0ffffeeee (e_addr)
* ffffabc11fffeeed (e_addr + e_len - 1)
* 00000001e0000003 ((e_addr ^ (e_addr + e_len - 1))
* 0000000100000000 ((e_addr ^ (e_addr + e_len - 1)) & ~(0xffffffff)
*
* Compute start of second DMA segment:
*
* ffffabc0ffffeeee (e_addr)
* ffffabc1ffffeeee (0x100000000 + e_addr)
* ffffabc100000000 (0x100000000 + e_addr) & ~(0xffffffff)
* ffffabc100000000 (ne_addr)
*
* Compute length of second DMA segment:
*
* 00000000ffffeeee (e_addr & 0xffffffff)
* 0000000000001112 (0x100000000 - (e_addr & 0xffffffff))
* 000000001fffeeee (e_len - (0x100000000 - (e_addr & 0xffffffff))
* 000000001fffeeee (ne_len)
*
* Adjust length of first DMA segment
*
* 0000000020000000 (e_len)
* 0000000000001112 (e_len - ne_len)
* 0000000000001112 (e_len)
*
* Returns non-zero if the specified address was normalized, else zero.
*/
static __inline__ int
qla2x00_normalize_dma_addr(
dma_addr_t *e_addr, uint32_t *e_len,
dma_addr_t *ne_addr, uint32_t *ne_len)
{
int normalized;
normalized = 0;
if ((*e_addr ^ (*e_addr + *e_len - 1)) & ~(0xFFFFFFFFULL)) {
/* Compute normalized crossed address and len */
*ne_addr = (0x100000000ULL + *e_addr) & ~(0xFFFFFFFFULL);
*ne_len = *e_len - (0x100000000ULL - (*e_addr & 0xFFFFFFFFULL));
*e_len -= *ne_len;
normalized++;
}
return (normalized);
}
static __inline__ void qla2x00_poll(scsi_qla_host_t *);
static inline void
qla2x00_poll(scsi_qla_host_t *ha)
{
qla2x00_intr_handler(0, ha, NULL);
}
static __inline__ void qla2x00_enable_intrs(scsi_qla_host_t *);
static __inline__ void qla2x00_disable_intrs(scsi_qla_host_t *);
static inline void
qla2x00_enable_intrs(scsi_qla_host_t *ha)
{
unsigned long flags = 0;
device_reg_t *reg;
spin_lock_irqsave(&ha->hardware_lock, flags);
reg = ha->iobase;
ha->interrupts_on = 1;
/* enable risc and host interrupts */
WRT_REG_WORD(&reg->ictrl, ICR_EN_INT | ICR_EN_RISC);
RD_REG_WORD(&reg->ictrl);
spin_unlock_irqrestore(&ha->hardware_lock, flags);
}
static inline void
qla2x00_disable_intrs(scsi_qla_host_t *ha)
{
unsigned long flags = 0;
device_reg_t *reg;
spin_lock_irqsave(&ha->hardware_lock, flags);
reg = ha->iobase;
ha->interrupts_on = 0;
/* disable risc and host interrupts */
WRT_REG_WORD(&reg->ictrl, 0);
RD_REG_WORD(&reg->ictrl);
spin_unlock_irqrestore(&ha->hardware_lock, flags);
}
static __inline__ int qla2x00_is_wwn_zero(uint8_t *);
/*
* qla2x00_is_wwn_zero - Check for zero node name
*
* Input:
* wwn = Pointer to WW name to check
*
* Returns:
* TRUE if name is 0 else FALSE
*
* Context:
* Kernel context.
*/
static __inline__ int
qla2x00_is_wwn_zero(uint8_t *wwn)
{
int cnt;
for (cnt = 0; cnt < WWN_SIZE ; cnt++, wwn++) {
if (*wwn != 0)
break;
}
/* if zero return TRUE */
if (cnt == WWN_SIZE)
return (1);
else
return (0);
}
static __inline__ uint8_t
qla2x00_suspend_lun(scsi_qla_host_t *, os_lun_t *, int, int);
static __inline__ uint8_t
qla2x00_delay_lun(scsi_qla_host_t *, os_lun_t *, int);
static __inline__ uint8_t
qla2x00_suspend_lun(scsi_qla_host_t *ha, os_lun_t *lq, int time, int count)
{
return (__qla2x00_suspend_lun(ha, lq, time, count, 0));
}
static __inline__ uint8_t
qla2x00_delay_lun(scsi_qla_host_t *ha, os_lun_t *lq, int time)
{
return (__qla2x00_suspend_lun(ha, lq, time, 1, 1));
}
static __inline__ void qla2x00_check_fabric_devices(scsi_qla_host_t *);
/*
* This routine will wait for fabric devices for
* the reset delay.
*/
static __inline__ void qla2x00_check_fabric_devices(scsi_qla_host_t *ha)
{
uint16_t fw_state;
qla2x00_get_firmware_state(ha, &fw_state);
}
/**
* qla2x00_issue_marker() - Issue a Marker IOCB if necessary.
* @ha: HA context
* @ha_locked: is function called with the hardware lock
*
* Returns non-zero if a failure occured, else zero.
*/
static inline int
qla2x00_issue_marker(scsi_qla_host_t *ha, int ha_locked)
{
/* Send marker if required */
if (ha->marker_needed != 0) {
if (ha_locked) {
if (__qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) !=
QLA_SUCCESS)
return (QLA_FUNCTION_FAILED);
} else {
if (qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) !=
QLA_SUCCESS)
return (QLA_FUNCTION_FAILED);
}
ha->marker_needed = 0;
}
return (QLA_SUCCESS);
}
/******************************************************************************
* QLOGIC LINUX SOFTWARE
*
* QLogic ISP2x00 device driver for Linux 2.6.x
* Copyright (C) 2003 QLogic Corporation
* (www.qlogic.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.
*
******************************************************************************/
#include "qla_os.h"
#include "qla_def.h"
static inline uint16_t qla2x00_get_cmd_direction(struct scsi_cmnd *cmd);
static inline cont_entry_t *qla2x00_prep_cont_type0_iocb(scsi_qla_host_t *);
static inline cont_a64_entry_t *qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *);
/**
* qla2x00_get_cmd_direction() - Determine control_flag data direction.
* @cmd: SCSI command
*
* Returns the proper CF_* direction based on CDB.
*/
static inline uint16_t
qla2x00_get_cmd_direction(struct scsi_cmnd *cmd)
{
uint16_t cflags;
cflags = 0;
/* Set transfer direction */
if (cmd->sc_data_direction == DMA_TO_DEVICE)
cflags = CF_WRITE;
else if (cmd->sc_data_direction == DMA_FROM_DEVICE)
cflags = CF_READ;
else {
switch (cmd->data_cmnd[0]) {
case WRITE_6:
case WRITE_10:
case WRITE_12:
case WRITE_BUFFER:
case WRITE_LONG:
case WRITE_SAME:
case WRITE_VERIFY:
case WRITE_VERIFY_12:
case FORMAT_UNIT:
case SEND_VOLUME_TAG:
case MODE_SELECT:
case SEND_DIAGNOSTIC:
case MODE_SELECT_10:
cflags = CF_WRITE;
break;
default:
cflags = CF_READ;
break;
}
}
return (cflags);
}
/**
* qla2x00_calc_iocbs_32() - Determine number of Command Type 2 and
* Continuation Type 0 IOCBs to allocate.
*
* @dsds: number of data segment decriptors needed
*
* Returns the number of IOCB entries needed to store @dsds.
*/
uint16_t
qla2x00_calc_iocbs_32(uint16_t dsds)
{
uint16_t iocbs;
iocbs = 1;
if (dsds > 3) {
iocbs += (dsds - 3) / 7;
if ((dsds - 3) % 7)
iocbs++;
}
return (iocbs);
}
/**
* qla2x00_calc_iocbs_64() - Determine number of Command Type 3 and
* Continuation Type 1 IOCBs to allocate.
*
* @dsds: number of data segment decriptors needed
*
* Returns the number of IOCB entries needed to store @dsds.
*/
uint16_t
qla2x00_calc_iocbs_64(uint16_t dsds)
{
uint16_t iocbs;
iocbs = 1;
if (dsds > 2) {
iocbs += (dsds - 2) / 5;
if ((dsds - 2) % 5)
iocbs++;
}
return (iocbs);
}
/**
* qla2x00_prep_cont_type0_iocb() - Initialize a Continuation Type 0 IOCB.
* @ha: HA context
*
* Returns a pointer to the Continuation Type 0 IOCB packet.
*/
static inline cont_entry_t *
qla2x00_prep_cont_type0_iocb(scsi_qla_host_t *ha)
{
cont_entry_t *cont_pkt;
/* Adjust ring index. */
ha->req_ring_index++;
if (ha->req_ring_index == REQUEST_ENTRY_CNT) {
ha->req_ring_index = 0;
ha->request_ring_ptr = ha->request_ring;
} else {
ha->request_ring_ptr++;
}
cont_pkt = (cont_entry_t *)ha->request_ring_ptr;
/* Load packet defaults. */
*((uint32_t *)(&cont_pkt->entry_type)) =
__constant_cpu_to_le32(CONTINUE_TYPE);
return (cont_pkt);
}
/**
* qla2x00_prep_cont_type1_iocb() - Initialize a Continuation Type 1 IOCB.
* @ha: HA context
*
* Returns a pointer to the continuation type 1 IOCB packet.
*/
static inline cont_a64_entry_t *
qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *ha)
{
cont_a64_entry_t *cont_pkt;
/* Adjust ring index. */
ha->req_ring_index++;
if (ha->req_ring_index == REQUEST_ENTRY_CNT) {
ha->req_ring_index = 0;
ha->request_ring_ptr = ha->request_ring;
} else {
ha->request_ring_ptr++;
}
cont_pkt = (cont_a64_entry_t *)ha->request_ring_ptr;
/* Load packet defaults. */
*((uint32_t *)(&cont_pkt->entry_type)) =
__constant_cpu_to_le32(CONTINUE_A64_TYPE);
return (cont_pkt);
}
/**
* qla2x00_build_scsi_iocbs_32() - Build IOCB command utilizing 32bit
* capable IOCB types.
*
* @sp: SRB command to process
* @cmd_pkt: Command type 2 IOCB
* @tot_dsds: Total number of segments to transfer
*/
void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt,
uint16_t tot_dsds)
{
uint16_t avail_dsds;
uint32_t *cur_dsd;
scsi_qla_host_t *ha;
struct scsi_cmnd *cmd;
cmd = sp->cmd;
/* Update entry type to indicate Command Type 2 IOCB */
*((uint32_t *)(&cmd_pkt->entry_type)) =
__constant_cpu_to_le32(COMMAND_TYPE);
/* No data transfer */
if (cmd->request_bufflen == 0 || cmd->sc_data_direction == DMA_NONE) {
cmd_pkt->byte_count = __constant_cpu_to_le32(0);
return;
}
ha = sp->ha;
cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(cmd));
/* Three DSDs are available in the Command Type 2 IOCB */
avail_dsds = 3;
cur_dsd = (uint32_t *)&cmd_pkt->dseg_0_address;
/* Load data segments */
if (cmd->use_sg != 0) {
struct scatterlist *cur_seg;
struct scatterlist *end_seg;
cur_seg = (struct scatterlist *)cmd->request_buffer;
end_seg = cur_seg + tot_dsds;
while (cur_seg < end_seg) {
cont_entry_t *cont_pkt;
/* Allocate additional continuation packets? */
if (avail_dsds == 0) {
/*
* Seven DSDs are available in the Continuation
* Type 0 IOCB.
*/
cont_pkt = qla2x00_prep_cont_type0_iocb(ha);
cur_dsd = (uint32_t *)&cont_pkt->dseg_0_address;
avail_dsds = 7;
}
*cur_dsd++ = cpu_to_le32(sg_dma_address(cur_seg));
*cur_dsd++ = cpu_to_le32(sg_dma_len(cur_seg));
avail_dsds--;
cur_seg++;
}
} else {
dma_addr_t req_dma;
struct page *page;
unsigned long offset;
page = virt_to_page(cmd->request_buffer);
offset = ((unsigned long)cmd->request_buffer & ~PAGE_MASK);
req_dma = pci_map_page(ha->pdev, page, offset,
cmd->request_bufflen, cmd->sc_data_direction);
sp->dma_handle = req_dma;
*cur_dsd++ = cpu_to_le32(req_dma);
*cur_dsd++ = cpu_to_le32(cmd->request_bufflen);
}
}
/**
* qla2x00_build_scsi_iocbs_64() - Build IOCB command utilizing 64bit
* capable IOCB types.
*
* @sp: SRB command to process
* @cmd_pkt: Command type 3 IOCB
* @tot_dsds: Total number of segments to transfer
*/
void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt,
uint16_t tot_dsds)
{
uint16_t avail_dsds;
uint32_t *cur_dsd;
scsi_qla_host_t *ha;
struct scsi_cmnd *cmd;
cmd = sp->cmd;
/* Update entry type to indicate Command Type 3 IOCB */
*((uint32_t *)(&cmd_pkt->entry_type)) =
__constant_cpu_to_le32(COMMAND_A64_TYPE);
/* No data transfer */
if (cmd->request_bufflen == 0 || cmd->sc_data_direction == DMA_NONE) {
cmd_pkt->byte_count = __constant_cpu_to_le32(0);
return;
}
ha = sp->ha;
cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(cmd));
/* Two DSDs are available in the Command Type 3 IOCB */
avail_dsds = 2;
cur_dsd = (uint32_t *)&cmd_pkt->dseg_0_address;
/* Load data segments */
if (cmd->use_sg != 0) {
struct scatterlist *cur_seg;
struct scatterlist *end_seg;
cur_seg = (struct scatterlist *)cmd->request_buffer;
end_seg = cur_seg + tot_dsds;
while (cur_seg < end_seg) {
dma_addr_t sle_dma;
cont_a64_entry_t *cont_pkt;
/* Allocate additional continuation packets? */
if (avail_dsds == 0) {
/*
* Five DSDs are available in the Continuation
* Type 1 IOCB.
*/
cont_pkt = qla2x00_prep_cont_type1_iocb(ha);
cur_dsd = (uint32_t *)cont_pkt->dseg_0_address;
avail_dsds = 5;
}
sle_dma = sg_dma_address(cur_seg);
*cur_dsd++ = cpu_to_le32(LSD(sle_dma));
*cur_dsd++ = cpu_to_le32(MSD(sle_dma));
*cur_dsd++ = cpu_to_le32(sg_dma_len(cur_seg));
avail_dsds--;
cur_seg++;
}
} else {
dma_addr_t req_dma;
struct page *page;
unsigned long offset;
page = virt_to_page(cmd->request_buffer);
offset = ((unsigned long)cmd->request_buffer & ~PAGE_MASK);
req_dma = pci_map_page(ha->pdev, page, offset,
cmd->request_bufflen, cmd->sc_data_direction);
sp->dma_handle = req_dma;
*cur_dsd++ = cpu_to_le32(LSD(req_dma));
*cur_dsd++ = cpu_to_le32(MSD(req_dma));
*cur_dsd++ = cpu_to_le32(cmd->request_bufflen);
}
}
/**
* qla2x00_start_scsi() - Send a SCSI command to the ISP
* @sp: command to send to the ISP
*
* Returns non-zero if a failure occured, else zero.
*/
int
qla2x00_start_scsi(srb_t *sp)
{
int ret;
unsigned long flags;
scsi_qla_host_t *ha;
fc_lun_t *fclun;
struct scsi_cmnd *cmd;
uint32_t *clr_ptr;
uint32_t index;
uint32_t handle;
uint16_t cnt, tot_dsds, req_cnt;
cmd_entry_t *cmd_pkt;
uint32_t timeout;
struct scatterlist *sg;
device_reg_t *reg;
/* Setup device pointers. */
ret = 0;
fclun = sp->lun_queue->fclun;
ha = fclun->fcport->ha;
cmd = sp->cmd;
reg = ha->iobase;
/* Send marker if required */
if (ha->marker_needed != 0) {
if (qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) != QLA_SUCCESS) {
return (QLA_FUNCTION_FAILED);
}
ha->marker_needed = 0;
}
/* Calculate number of segments and entries required */
tot_dsds = 0;
if (cmd->use_sg) {
sg = (struct scatterlist *) cmd->request_buffer;
tot_dsds = pci_map_sg(ha->pdev, sg, cmd->use_sg,
cmd->sc_data_direction);
} else if (cmd->request_bufflen) { /* Single segment transfer */
tot_dsds++;
}
req_cnt = (ha->calc_request_entries)(tot_dsds);
/* Acquire ring specific lock */
spin_lock_irqsave(&ha->hardware_lock, flags);
if (ha->req_q_cnt < (req_cnt + 2)) {
/* Calculate number of free request entries */
cnt = RD_REG_WORD(ISP_REQ_Q_OUT(ha, reg));
if (ha->req_ring_index < cnt)
ha->req_q_cnt = cnt - ha->req_ring_index;
else
ha->req_q_cnt = REQUEST_ENTRY_CNT -
(ha->req_ring_index - cnt);
}
/* If no room for request in request ring */
if (ha->req_q_cnt < (req_cnt + 2)) {
DEBUG5(printk("scsi(%ld): in-ptr=%x req_q_cnt=%x "
"tot_dsds=%x.\n",
ha->host_no, ha->req_ring_index, ha->req_q_cnt, tot_dsds));
goto queuing_error;
}
/* Check for room in outstanding command list. */
handle = ha->current_outstanding_cmd;
for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) {
handle++;
if (handle == MAX_OUTSTANDING_COMMANDS)
handle = 1;
if (ha->outstanding_cmds[handle] == 0) {
ha->current_outstanding_cmd = handle;
break;
}
}
if (index == MAX_OUTSTANDING_COMMANDS) {
DEBUG5(printk("scsi(%ld): Unable to queue command -- NO ROOM "
"IN OUTSTANDING ARRAY (req_q_cnt=%x).\n",
ha->host_no, ha->req_q_cnt));
goto queuing_error;
}
/* Build command packet */
ha->outstanding_cmds[handle] = sp;
sp->ha = ha;
sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle;
ha->req_q_cnt -= req_cnt;
cmd_pkt = (cmd_entry_t *)ha->request_ring_ptr;
cmd_pkt->handle = handle;
/* Zero out remaining portion of packet. */
clr_ptr = (uint32_t *)cmd_pkt + 2;
memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
cmd_pkt->dseg_count = cpu_to_le16(tot_dsds);
/* Set target ID */
SET_TARGET_ID(ha, cmd_pkt->target, fclun->fcport->loop_id);
/* Set LUN number*/
cmd_pkt->lun = cpu_to_le16(fclun->lun);
/* Update tagged queuing modifier */
cmd_pkt->control_flags = __constant_cpu_to_le16(CF_SIMPLE_TAG);
if (cmd->device->tagged_supported) {
switch (cmd->tag) {
case HEAD_OF_QUEUE_TAG:
cmd_pkt->control_flags =
__constant_cpu_to_le16(CF_HEAD_TAG);
break;
case ORDERED_QUEUE_TAG:
cmd_pkt->control_flags =
__constant_cpu_to_le16(CF_ORDERED_TAG);
break;
}
}
/*
* Allocate at least 5 (+ QLA_CMD_TIMER_DELTA) seconds for RISC timeout.
*/
timeout = (uint32_t)(cmd->timeout_per_command / HZ);
if (timeout > 65535)
cmd_pkt->timeout = __constant_cpu_to_le16(0);
else if (timeout > 25)
cmd_pkt->timeout = cpu_to_le16((uint16_t)timeout -
(5 + QLA_CMD_TIMER_DELTA));
else
cmd_pkt->timeout = cpu_to_le16((uint16_t)timeout);
/* Load SCSI command packet. */
memcpy(cmd_pkt->scsi_cdb, cmd->cmnd, cmd->cmd_len);
cmd_pkt->byte_count = cpu_to_le32((uint32_t)cmd->request_bufflen);
/* Build IOCB segments */
(ha->build_scsi_iocbs)(sp, cmd_pkt, tot_dsds);
/* Set total data segment count. */
cmd_pkt->entry_count = (uint8_t)req_cnt;
/* Adjust ring index. */
ha->req_ring_index++;
if (ha->req_ring_index == REQUEST_ENTRY_CNT) {
ha->req_ring_index = 0;
ha->request_ring_ptr = ha->request_ring;
} else
ha->request_ring_ptr++;
ha->actthreads++;
ha->total_ios++;
sp->lun_queue->out_cnt++;
sp->flags |= SRB_DMA_VALID;
sp->state = SRB_ACTIVE_STATE;
sp->u_start = jiffies;
/* Set chip new ring index. */
WRT_REG_WORD(ISP_REQ_Q_IN(ha, reg), ha->req_ring_index);
RD_REG_WORD(ISP_REQ_Q_IN(ha, reg)); /* PCI Posting. */
spin_unlock_irqrestore(&ha->hardware_lock, flags);
return (QLA_SUCCESS);
queuing_error:
spin_unlock_irqrestore(&ha->hardware_lock, flags);
if (cmd->use_sg)
pci_unmap_sg(ha->pdev, sg, cmd->use_sg, cmd->sc_data_direction);
return (QLA_FUNCTION_FAILED);
}
/**
* qla2x00_marker() - Send a marker IOCB to the firmware.
* @ha: HA context
* @loop_id: loop ID
* @lun: LUN
* @type: marker modifier
*
* Can be called from both normal and interrupt context.
*
* Returns non-zero if a failure occured, else zero.
*/
int
__qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun,
uint8_t type)
{
mrk_entry_t *pkt;
ENTER(__func__);
pkt = (mrk_entry_t *)qla2x00_req_pkt(ha);
if (pkt == NULL) {
DEBUG2_3(printk("%s(): **** FAILED ****\n", __func__));
return (QLA_FUNCTION_FAILED);
}
pkt->entry_type = MARKER_TYPE;
pkt->modifier = type;
if (type != MK_SYNC_ALL) {
pkt->lun = cpu_to_le16(lun);
SET_TARGET_ID(ha, pkt->target, loop_id);
}
/* Issue command to ISP */
qla2x00_isp_cmd(ha);
LEAVE(__func__);
return (QLA_SUCCESS);
}
int
qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun,
uint8_t type)
{
int ret;
unsigned long flags = 0;
spin_lock_irqsave(&ha->hardware_lock, flags);
ret = __qla2x00_marker(ha, loop_id, lun, type);
spin_unlock_irqrestore(&ha->hardware_lock, flags);
return (ret);
}
/**
* qla2x00_req_pkt() - Retrieve a request packet from the request ring.
* @ha: HA context
*
* Note: The caller must hold the hardware lock before calling this routine.
*
* Returns NULL if function failed, else, a pointer to the request packet.
*/
request_t *
qla2x00_req_pkt(scsi_qla_host_t *ha)
{
device_reg_t *reg = ha->iobase;
request_t *pkt = NULL;
uint16_t cnt;
uint32_t *dword_ptr;
uint32_t timer;
uint16_t req_cnt = 1;
ENTER(__func__);
/* Wait 1 second for slot. */
for (timer = HZ; timer; timer--) {
if ((req_cnt + 2) >= ha->req_q_cnt) {
/* Calculate number of free request entries. */
cnt = qla2x00_debounce_register(ISP_REQ_Q_OUT(ha, reg));
if (ha->req_ring_index < cnt)
ha->req_q_cnt = cnt - ha->req_ring_index;
else
ha->req_q_cnt = REQUEST_ENTRY_CNT -
(ha->req_ring_index - cnt);
}
/* If room for request in request ring. */
if ((req_cnt + 2) < ha->req_q_cnt) {
ha->req_q_cnt--;
pkt = ha->request_ring_ptr;
/* Zero out packet. */
dword_ptr = (uint32_t *)pkt;
for (cnt = 0; cnt < REQUEST_ENTRY_SIZE / 4; cnt++)
*dword_ptr++ = 0;
/* Set system defined field. */
pkt->sys_define = (uint8_t)ha->req_ring_index;
/* Set entry count. */
pkt->entry_count = 1;
break;
}
/* Release ring specific lock */
spin_unlock(&ha->hardware_lock);
udelay(2); /* 2 us */
/* Check for pending interrupts. */
/* During init we issue marker directly */
if (!ha->marker_needed)
qla2x00_poll(ha);
spin_lock_irq(&ha->hardware_lock);
}
if (!pkt) {
DEBUG2_3(printk("%s(): **** FAILED ****\n", __func__));
}
LEAVE(__func__);
return (pkt);
}
/**
* qla2x00_ms_req_pkt() - Retrieve a Management Server request packet from
* the request ring.
* @ha: HA context
* @sp: pointer to handle post function call
*
* Note: The caller must hold the hardware lock before calling this routine.
*
* Returns NULL if function failed, else, a pointer to the request packet.
*/
request_t *
qla2x00_ms_req_pkt(scsi_qla_host_t *ha, srb_t *sp)
{
device_reg_t *reg = ha->iobase;
request_t *pkt = NULL;
uint16_t cnt, i, index;
uint32_t *dword_ptr;
uint32_t timer;
uint8_t found = 0;
uint16_t req_cnt = 1;
ENTER(__func__);
/* Wait 1 second for slot. */
for (timer = HZ; timer; timer--) {
if ((req_cnt + 2) >= ha->req_q_cnt) {
/* Calculate number of free request entries. */
cnt = qla2x00_debounce_register(ISP_REQ_Q_OUT(ha, reg));
if (ha->req_ring_index < cnt) {
ha->req_q_cnt = cnt - ha->req_ring_index;
} else {
ha->req_q_cnt = REQUEST_ENTRY_CNT -
(ha->req_ring_index - cnt);
}
}
/* Check for room in outstanding command list. */
cnt = ha->current_outstanding_cmd;
for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) {
cnt++;
if (cnt == MAX_OUTSTANDING_COMMANDS)
cnt = 1;
if (ha->outstanding_cmds[cnt] == 0) {
found = 1;
ha->current_outstanding_cmd = cnt;
break;
}
}
/* If room for request in request ring. */
if (found && (req_cnt + 2) < ha->req_q_cnt) {
pkt = ha->request_ring_ptr;
/* Zero out packet. */
dword_ptr = (uint32_t *)pkt;
for (i = 0; i < REQUEST_ENTRY_SIZE / 4; i++ )
*dword_ptr++ = 0;
DEBUG5(printk("%s(): putting sp=%p in "
"outstanding_cmds[%x]\n",
__func__,
sp, cnt));
ha->outstanding_cmds[cnt] = sp;
/* save the handle */
sp->cmd->host_scribble = (unsigned char *) (u_long) cnt;
CMD_SP(sp->cmd) = (void *)sp;
ha->req_q_cnt--;
pkt->handle = (uint32_t)cnt;
/* Set system defined field. */
pkt->sys_define = (uint8_t)ha->req_ring_index;
pkt->entry_status = 0;
break;
}
/* Release ring specific lock */
spin_unlock(&ha->hardware_lock);
udelay(20);
/* Check for pending interrupts. */
qla2x00_poll(ha);
spin_lock_irq(&ha->hardware_lock);
}
if (!pkt) {
DEBUG2_3(printk("%s(): **** FAILED ****\n", __func__));
}
LEAVE(__func__);
return (pkt);
}
/**
* qla2x00_isp_cmd() - Modify the request ring pointer.
* @ha: HA context
*
* Note: The caller must hold the hardware lock before calling this routine.
*/
void
qla2x00_isp_cmd(scsi_qla_host_t *ha)
{
device_reg_t *reg = ha->iobase;
DEBUG5(printk("%s(): IOCB data:\n", __func__));
DEBUG5(qla2x00_dump_buffer(
(uint8_t *)ha->request_ring_ptr, REQUEST_ENTRY_SIZE));
/* Adjust ring index. */
ha->req_ring_index++;
if (ha->req_ring_index == REQUEST_ENTRY_CNT) {
ha->req_ring_index = 0;
ha->request_ring_ptr = ha->request_ring;
} else
ha->request_ring_ptr++;
/* Set chip new ring index. */
WRT_REG_WORD(ISP_REQ_Q_IN(ha, reg), ha->req_ring_index);
RD_REG_WORD(ISP_REQ_Q_IN(ha, reg)); /* PCI Posting. */
}
/*
* QLOGIC LINUX SOFTWARE
*
* QLogic ISP2x00 device driver for Linux 2.6.x
* Copyright (C) 2003 QLogic Corporation
* (www.qlogic.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.
*
*/
#include "qla_os.h"
#include "qla_def.h"
/* XXX(hch): this is ugly, but we don't want to pull in exioctl.h */
#ifndef EXT_DEF_PORTSPEED_1GBIT
#define EXT_DEF_PORTSPEED_1GBIT 1
#define EXT_DEF_PORTSPEED_2GBIT 2
#endif
static void qla2x00_mbx_completion(scsi_qla_host_t *, uint16_t);
static void qla2x00_async_event(scsi_qla_host_t *, uint32_t);
static void qla2x00_process_completed_request(struct scsi_qla_host *, uint32_t);
void qla2x00_process_response_queue(struct scsi_qla_host *);
static void qla2x00_status_entry(scsi_qla_host_t *, sts_entry_t *);
static void qla2x00_status_cont_entry(scsi_qla_host_t *, sts_cont_entry_t *);
static void qla2x00_error_entry(scsi_qla_host_t *, sts_entry_t *);
static void qla2x00_ms_entry(scsi_qla_host_t *, ms_iocb_entry_t *);
static int qla2x00_check_sense(struct scsi_cmnd *cp, os_lun_t *);
/**
* qla2x00_intr_handler() - Process interrupts for the ISP.
* @irq:
* @dev_id: SCSI driver HA context
* @regs:
*
* Called by system whenever the host adapter generates an interrupt.
*
* Returns handled flag.
*/
irqreturn_t
qla2x00_intr_handler(int irq, void *dev_id, struct pt_regs *regs)
{
scsi_qla_host_t *ha;
device_reg_t *reg;
uint32_t mbx;
int status = 0;
unsigned long flags = 0;
unsigned long mbx_flags = 0;
unsigned long intr_iter;
uint32_t stat;
uint16_t hccr;
/* Don't loop forever, interrupt are OFF */
intr_iter = 50;
ha = (scsi_qla_host_t *) dev_id;
if (!ha) {
printk(KERN_INFO
"%s(): NULL host pointer\n", __func__);
return (IRQ_NONE);
}
reg = ha->iobase;
spin_lock_irqsave(&ha->hardware_lock, flags);
for (;;) {
/* Relax CPU! */
if (!(intr_iter--))
break;
if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
if ((RD_REG_WORD(&reg->istatus) & ISR_RISC_INT) == 0)
break;
if (RD_REG_WORD(&reg->semaphore) & BIT_0) {
WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
RD_REG_WORD(&reg->hccr);
/* Get mailbox data. */
mbx = RD_MAILBOX_REG(ha, reg, 0);
if (mbx > 0x3fff && mbx < 0x8000) {
qla2x00_mbx_completion(ha,
(uint16_t)mbx);
status |= MBX_INTERRUPT;
} else if (mbx > 0x7fff && mbx < 0xc000) {
qla2x00_async_event(ha, mbx);
} else {
/*EMPTY*/
DEBUG2(printk("scsi(%ld): Unrecognized "
"interrupt type (%d)\n",
ha->host_no, mbx));
}
/* Release mailbox registers. */
WRT_REG_WORD(&reg->semaphore, 0);
/* Workaround for ISP2100 chip. */
if (IS_QLA2100(ha))
RD_REG_WORD(&reg->semaphore);
} else {
qla2x00_process_response_queue(ha);
WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
RD_REG_WORD(&reg->hccr);
}
} else /* IS_QLA23XX(ha) */ {
stat = RD_REG_DWORD(&reg->u.isp2300.host_status);
if ((stat & HSR_RISC_INT) == 0)
break;
mbx = MSW(stat);
switch (stat & 0xff) {
case 0x13:
qla2x00_process_response_queue(ha);
break;
case 0x1:
case 0x2:
case 0x10:
case 0x11:
qla2x00_mbx_completion(ha, (uint16_t)mbx);
status |= MBX_INTERRUPT;
/* Release mailbox registers. */
WRT_REG_WORD(&reg->semaphore, 0);
break;
case 0x12:
qla2x00_async_event(ha, mbx);
break;
case 0x15:
mbx = mbx << 16 | MBA_CMPLT_1_16BIT;
qla2x00_async_event(ha, mbx);
break;
case 0x16:
mbx = mbx << 16 | MBA_SCSI_COMPLETION;
qla2x00_async_event(ha, mbx);
break;
default:
hccr = RD_REG_WORD(&reg->hccr);
if (hccr & HCCR_RISC_PAUSE) {
qla_printk(KERN_INFO, ha,
"RISC paused, dumping HCCR=%x\n",
hccr);
/*
* Issue a "HARD" reset in order for
* the RISC interrupt bit to be
* cleared. Schedule a big hammmer to
* get out of the RISC PAUSED state.
*/
WRT_REG_WORD(&reg->hccr,
HCCR_RESET_RISC);
RD_REG_WORD(&reg->hccr);
set_bit(ISP_ABORT_NEEDED,
&ha->dpc_flags);
break;
} else {
DEBUG2(printk("scsi(%ld): Unrecognized "
"interrupt type (%d)\n",
ha->host_no, stat & 0xff));
}
break;
}
WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
RD_REG_WORD(&reg->hccr);
}
}
spin_unlock_irqrestore(&ha->hardware_lock, flags);
qla2x00_next(ha);
ha->last_irq_cpu = smp_processor_id();
ha->total_isr_cnt++;
if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags) &&
(status & MBX_INTERRUPT) && ha->flags.mbox_int) {
/* There was a mailbox completion */
DEBUG3(printk("%s(%ld): Going to get mbx reg lock.\n",
__func__, ha->host_no));
spin_lock_irqsave(&ha->mbx_reg_lock, mbx_flags);
if (ha->mcp == NULL) {
DEBUG3(printk("%s(%ld): Error mbx pointer.\n",
__func__, ha->host_no));
} else {
DEBUG3(printk("%s(%ld): Going to set mbx intr flags. "
"cmd=%x.\n",
__func__, ha->host_no, ha->mcp->mb[0]));
}
set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
DEBUG3(printk("%s(%ld): Going to wake up mbx function for "
"completion.\n",
__func__, ha->host_no));
up(&ha->mbx_intr_sem);
DEBUG3(printk("%s(%ld): Going to release mbx reg lock.\n",
__func__, ha->host_no));
spin_unlock_irqrestore(&ha->mbx_reg_lock, mbx_flags);
}
if (!list_empty(&ha->done_queue))
qla2x00_done(ha);
/* Wakeup the DPC routine */
if ((!ha->flags.mbox_busy &&
(test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags) ||
test_bit(RESET_MARKER_NEEDED, &ha->dpc_flags) ||
test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags))) &&
ha->dpc_wait && !ha->dpc_active) {
up(ha->dpc_wait);
}
return (IRQ_HANDLED);
}
/**
* qla2x00_mbx_completion() - Process mailbox command completions.
* @ha: SCSI driver HA context
* @mb0: Mailbox0 register
*/
static void
qla2x00_mbx_completion(scsi_qla_host_t *ha, uint16_t mb0)
{
uint16_t cnt;
uint16_t *wptr;
device_reg_t *reg = ha->iobase;
/* Load return mailbox registers. */
ha->flags.mbox_int = TRUE;
ha->mailbox_out[0] = mb0;
wptr = (uint16_t *)MAILBOX_REG(ha, reg, 1);
for (cnt = 1; cnt < ha->mbx_count; cnt++) {
if (IS_QLA2200(ha) && cnt == 8)
wptr = (uint16_t *)MAILBOX_REG(ha, reg, 8);
if (cnt == 4 || cnt == 5)
ha->mailbox_out[cnt] = qla2x00_debounce_register(wptr);
else
ha->mailbox_out[cnt] = RD_REG_WORD(wptr);
wptr++;
}
if (ha->mcp) {
DEBUG3(printk("%s(%ld): Got mailbox completion. cmd=%x.\n",
__func__, ha->host_no, ha->mcp->mb[0]));
} else {
DEBUG2_3(printk("%s(%ld): MBX pointer ERROR!\n",
__func__, ha->host_no));
}
}
/**
* qla2x00_async_event() - Process aynchronous events.
* @ha: SCSI driver HA context
* @mb0: Mailbox0 register
*/
static void
qla2x00_async_event(scsi_qla_host_t *ha, uint32_t mbx)
{
static char *link_speeds[5] = { "1", "2", "4", "?", "10" };
char *link_speed;
uint16_t mb[4];
uint16_t handle_cnt;
uint16_t cnt;
uint32_t handles[5];
device_reg_t *reg = ha->iobase;
uint32_t rscn_entry, host_pid;
uint8_t rscn_queue_index;
/* Setup to process RIO completion. */
handle_cnt = 0;
mb[0] = LSW(mbx);
switch (mb[0]) {
case MBA_SCSI_COMPLETION:
if (IS_QLA2100(ha) || IS_QLA2200(ha))
handles[0] = RD_MAILBOX_REG(ha, reg, 1);
else
handles[0] = MSW(mbx);
handles[0] |= (uint32_t)(RD_MAILBOX_REG(ha, reg, 2) << 16);
handle_cnt = 1;
break;
case MBA_CMPLT_1_16BIT:
if (IS_QLA2100(ha) || IS_QLA2200(ha))
handles[0] = (uint32_t)RD_MAILBOX_REG(ha, reg, 1);
else
handles[0] = MSW(mbx);
handle_cnt = 1;
mb[0] = MBA_SCSI_COMPLETION;
break;
case MBA_CMPLT_2_16BIT:
handles[0] = (uint32_t)RD_MAILBOX_REG(ha, reg, 1);
handles[1] = (uint32_t)RD_MAILBOX_REG(ha, reg, 2);
handle_cnt = 2;
mb[0] = MBA_SCSI_COMPLETION;
break;
case MBA_CMPLT_3_16BIT:
handles[0] = (uint32_t)RD_MAILBOX_REG(ha, reg, 1);
handles[1] = (uint32_t)RD_MAILBOX_REG(ha, reg, 2);
handles[2] = (uint32_t)RD_MAILBOX_REG(ha, reg, 3);
handle_cnt = 3;
mb[0] = MBA_SCSI_COMPLETION;
break;
case MBA_CMPLT_4_16BIT:
handles[0] = (uint32_t)RD_MAILBOX_REG(ha, reg, 1);
handles[1] = (uint32_t)RD_MAILBOX_REG(ha, reg, 2);
handles[2] = (uint32_t)RD_MAILBOX_REG(ha, reg, 3);
handles[3] = (uint32_t)RD_MAILBOX_REG(ha, reg, 6);
handle_cnt = 4;
mb[0] = MBA_SCSI_COMPLETION;
break;
case MBA_CMPLT_5_16BIT:
handles[0] = (uint32_t)RD_MAILBOX_REG(ha, reg, 1);
handles[1] = (uint32_t)RD_MAILBOX_REG(ha, reg, 2);
handles[2] = (uint32_t)RD_MAILBOX_REG(ha, reg, 3);
handles[3] = (uint32_t)RD_MAILBOX_REG(ha, reg, 6);
handles[4] = (uint32_t)RD_MAILBOX_REG(ha, reg, 7);
handle_cnt = 5;
mb[0] = MBA_SCSI_COMPLETION;
break;
case MBA_CMPLT_2_32BIT:
handles[0] = (uint32_t)((RD_MAILBOX_REG(ha, reg, 2) << 16) |
RD_MAILBOX_REG(ha, reg, 1));
handles[1] = (uint32_t)((RD_MAILBOX_REG(ha, reg, 7) << 16) |
RD_MAILBOX_REG(ha, reg, 6));
handle_cnt = 2;
mb[0] = MBA_SCSI_COMPLETION;
break;
default:
break;
}
mb[0] = LSW(mbx);
switch (mb[0]) {
case MBA_SCSI_COMPLETION: /* Fast Post */
if (!ha->flags.online)
break;
for (cnt = 0; cnt < handle_cnt; cnt++)
qla2x00_process_completed_request(ha, handles[cnt]);
break;
case MBA_RESET: /* Reset */
DEBUG2(printk("scsi(%ld): Asynchronous RESET.\n", ha->host_no));
set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
break;
case MBA_SYSTEM_ERR: /* System Error */
mb[1] = RD_MAILBOX_REG(ha, reg, 1);
mb[2] = RD_MAILBOX_REG(ha, reg, 2);
mb[3] = RD_MAILBOX_REG(ha, reg, 3);
qla_printk(KERN_INFO, ha,
"ISP System Error - mbx1=%xh mbx2=%xh mbx3=%xh.\n",
mb[1], mb[2], mb[3]);
if (IS_QLA2100(ha) || IS_QLA2200(ha))
qla2100_fw_dump(ha, 1);
else
qla2300_fw_dump(ha, 1);
set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
break;
case MBA_REQ_TRANSFER_ERR: /* Request Transfer Error */
DEBUG2(printk("scsi(%ld): ISP Request Transfer Error.\n",
ha->host_no));
qla_printk(KERN_WARNING, ha, "ISP Request Transfer Error.\n");
set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
break;
case MBA_RSP_TRANSFER_ERR: /* Response Transfer Error */
DEBUG2(printk("scsi(%ld): ISP Response Transfer Error.\n",
ha->host_no));
qla_printk(KERN_WARNING, ha, "ISP Response Transfer Error.\n");
set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
break;
case MBA_WAKEUP_THRES: /* Request Queue Wake-up */
DEBUG2(printk("scsi(%ld): Asynchronous WAKEUP_THRES.\n",
ha->host_no));
break;
case MBA_LIP_OCCURRED: /* Loop Initialization Procedure */
mb[1] = RD_MAILBOX_REG(ha, reg, 1);
DEBUG2(printk("scsi(%ld): LIP occured (%x).\n", ha->host_no,
mb[1]));
qla_printk(KERN_INFO, ha, "LIP occured (%x).\n", mb[1]);
if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
atomic_set(&ha->loop_state, LOOP_DOWN);
atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME);
qla2x00_mark_all_devices_lost(ha);
}
set_bit(REGISTER_FC4_NEEDED, &ha->dpc_flags);
ha->flags.management_server_logged_in = 0;
/* Update AEN queue. */
qla2x00_enqueue_aen(ha, MBA_LIP_OCCURRED, NULL);
ha->total_lip_cnt++;
break;
case MBA_LOOP_UP: /* Loop Up Event */
mb[1] = RD_MAILBOX_REG(ha, reg, 1);
ha->current_speed = EXT_DEF_PORTSPEED_1GBIT;
if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
link_speed = link_speeds[0];
} else {
link_speed = link_speeds[3];
if (mb[1] < 5)
link_speed = link_speeds[mb[1]];
if (mb[1] == 1)
ha->current_speed = EXT_DEF_PORTSPEED_2GBIT;
}
DEBUG2(printk("scsi(%ld): Asynchronous LOOP UP (%s Gbps).\n",
ha->host_no, link_speed));
qla_printk(KERN_INFO, ha, "LOOP UP detected (%s Gbps).\n",
link_speed);
ha->flags.management_server_logged_in = 0;
/* Update AEN queue. */
qla2x00_enqueue_aen(ha, MBA_LOOP_UP, NULL);
break;
case MBA_LOOP_DOWN: /* Loop Down Event */
DEBUG2(printk("scsi(%ld): Asynchronous LOOP DOWN.\n",
ha->host_no));
qla_printk(KERN_INFO, ha, "LOOP DOWN detected.\n");
if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
atomic_set(&ha->loop_state, LOOP_DOWN);
atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME);
qla2x00_mark_all_devices_lost(ha);
}
ha->flags.management_server_logged_in = 0;
ha->current_speed = 0; /* reset value */
/* Update AEN queue. */
qla2x00_enqueue_aen(ha, MBA_LOOP_DOWN, NULL);
break;
case MBA_LIP_RESET: /* LIP reset occurred */
mb[1] = RD_MAILBOX_REG(ha, reg, 1);
DEBUG2(printk("scsi(%ld): Asynchronous LIP RESET (%x).\n",
ha->host_no, mb[1]));
qla_printk(KERN_INFO, ha,
"LIP reset occured (%x).\n", mb[1]);
if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
atomic_set(&ha->loop_state, LOOP_DOWN);
atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME);
qla2x00_mark_all_devices_lost(ha);
}
set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
ha->operating_mode = LOOP;
ha->flags.management_server_logged_in = 0;
/* Update AEN queue. */
qla2x00_enqueue_aen(ha, MBA_LIP_RESET, NULL);
ha->total_lip_cnt++;
break;
case MBA_POINT_TO_POINT: /* Point-to-Point */
if (IS_QLA2100(ha))
break;
DEBUG2(printk("scsi(%ld): Asynchronous P2P MODE received.\n",
ha->host_no));
/*
* Until there's a transition from loop down to loop up, treat
* this as loop down only.
*/
if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
atomic_set(&ha->loop_state, LOOP_DOWN);
if (!atomic_read(&ha->loop_down_timer))
atomic_set(&ha->loop_down_timer,
LOOP_DOWN_TIME);
qla2x00_mark_all_devices_lost(ha);
}
if (!(test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags))) {
set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
}
set_bit(REGISTER_FC4_NEEDED, &ha->dpc_flags);
break;
case MBA_CHG_IN_CONNECTION: /* Change in connection mode */
if (IS_QLA2100(ha))
break;
mb[1] = RD_MAILBOX_REG(ha, reg, 1);
DEBUG2(printk("scsi(%ld): Asynchronous Change In Connection "
"received.\n",
ha->host_no));
qla_printk(KERN_INFO, ha,
"Configuration change detected: value=%x.\n", mb[1]);
if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
atomic_set(&ha->loop_state, LOOP_DOWN);
if (!atomic_read(&ha->loop_down_timer))
atomic_set(&ha->loop_down_timer,
LOOP_DOWN_TIME);
qla2x00_mark_all_devices_lost(ha);
}
set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags);
break;
case MBA_PORT_UPDATE: /* Port database update */
mb[1] = RD_MAILBOX_REG(ha, reg, 1);
mb[2] = RD_MAILBOX_REG(ha, reg, 2);
/*
* If a single remote port just logged into (or logged out of)
* us, create a new entry in our rscn fcports list and handle
* the event like an RSCN.
*/
if (IS_QLA23XX(ha) && ha->flags.init_done && mb[1] != 0xffff &&
((ha->operating_mode == P2P && mb[1] != 0) ||
(ha->operating_mode != P2P && mb[1] !=
SNS_FIRST_LOOP_ID)) && (mb[2] == 6 || mb[2] == 7)) {
int rval;
fc_port_t *rscn_fcport;
/* Create new fcport for login. */
rscn_fcport = qla2x00_alloc_rscn_fcport(ha, GFP_ATOMIC);
if (rscn_fcport) {
DEBUG14(printk("scsi(%ld): Port Update -- "
"creating RSCN fcport %p for login.\n",
ha->host_no, rscn_fcport));
rscn_fcport->loop_id = mb[1];
rscn_fcport->d_id.b24 = INVALID_PORT_ID;
atomic_set(&rscn_fcport->state,
FCS_DEVICE_LOST);
list_add_tail(&rscn_fcport->list,
&ha->rscn_fcports);
rval = qla2x00_handle_port_rscn(ha, 0,
rscn_fcport, 1);
if (rval == QLA_SUCCESS)
break;
} else {
DEBUG14(printk("scsi(%ld): Port Update -- "
"-- unable to allocate RSCN fcport "
"login.\n", ha->host_no));
}
}
/*
* If PORT UPDATE is global (recieved LIP_OCCURED/LIP_RESET
* event etc. earlier indicating loop is down) then process
* it. Otherwise ignore it and Wait for RSCN to come in.
*/
if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
DEBUG2(printk("scsi(%ld): Asynchronous PORT UPDATE "
"ignored.\n", ha->host_no));
break;
}
DEBUG2(printk("scsi(%ld): Asynchronous PORT UPDATE.\n",
ha->host_no));
DEBUG(printk(KERN_INFO
"scsi(%ld): Port database changed %04x %04x.\n",
ha->host_no, mb[1], mb[2]));
/*
* Mark all devices as missing so we will login again.
*/
atomic_set(&ha->loop_state, LOOP_UP);
atomic_set(&ha->loop_down_timer, 0);
qla2x00_mark_all_devices_lost(ha);
ha->flags.rscn_queue_overflow = 1;
set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags);
/* Update AEN queue. */
qla2x00_enqueue_aen(ha, MBA_PORT_UPDATE, NULL);
break;
case MBA_RSCN_UPDATE: /* State Change Registration */
mb[1] = RD_MAILBOX_REG(ha, reg, 1);
mb[2] = RD_MAILBOX_REG(ha, reg, 2);
DEBUG2(printk("scsi(%ld): Asynchronous RSCR UPDATE.\n",
ha->host_no));
DEBUG(printk(KERN_INFO
"scsi(%ld): RSCN database changed -- %04x %04x.\n",
ha->host_no, mb[1], mb[2]));
rscn_entry = (mb[1] << 16) | mb[2];
host_pid = (ha->d_id.b.domain << 16) | (ha->d_id.b.area << 8) |
ha->d_id.b.al_pa;
if (rscn_entry == host_pid) {
DEBUG(printk(KERN_INFO
"scsi(%ld): Ignoring RSCN update to local host "
"port ID (%06x)\n",
ha->host_no, host_pid));
break;
}
rscn_queue_index = ha->rscn_in_ptr + 1;
if (rscn_queue_index == MAX_RSCN_COUNT)
rscn_queue_index = 0;
if (rscn_queue_index != ha->rscn_out_ptr) {
ha->rscn_queue[ha->rscn_in_ptr] = rscn_entry;
ha->rscn_in_ptr = rscn_queue_index;
} else {
ha->flags.rscn_queue_overflow = 1;
}
atomic_set(&ha->loop_state, LOOP_UPDATE);
atomic_set(&ha->loop_down_timer, 0);
ha->flags.management_server_logged_in = 0;
set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
set_bit(RSCN_UPDATE, &ha->dpc_flags);
/* Update AEN queue. */
qla2x00_enqueue_aen(ha, MBA_RSCN_UPDATE, &mb[0]);
break;
/* case MBA_RIO_RESPONSE: */
case MBA_ZIO_RESPONSE:
DEBUG2(printk("scsi(%ld): [R|Z]IO update completion.\n",
ha->host_no));
DEBUG(printk(KERN_INFO
"scsi(%ld): [R|Z]IO update completion.\n",
ha->host_no));
qla2x00_process_response_queue(ha);
break;
}
}
/**
* qla2x00_process_completed_request() - Process a Fast Post response.
* @ha: SCSI driver HA context
* @index: SRB index
*/
static void
qla2x00_process_completed_request(struct scsi_qla_host *ha, uint32_t index)
{
srb_t *sp;
/* Validate handle. */
if (index >= MAX_OUTSTANDING_COMMANDS) {
DEBUG2(printk("scsi(%ld): Invalid SCSI completion handle %d.\n",
ha->host_no, index));
qla_printk(KERN_WARNING, ha,
"Invalid SCSI completion handle %d.\n", index);
set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
return;
}
sp = ha->outstanding_cmds[index];
if (sp) {
/* Free outstanding command slot. */
ha->outstanding_cmds[index] = 0;
if (ha->actthreads)
ha->actthreads--;
sp->lun_queue->out_cnt--;
CMD_COMPL_STATUS(sp->cmd) = 0L;
CMD_SCSI_STATUS(sp->cmd) = 0L;
/* Save ISP completion status */
sp->cmd->result = DID_OK << 16;
sp->fo_retry_cnt = 0;
add_to_done_queue(ha, sp);
} else {
DEBUG2(printk("scsi(%ld): Invalid ISP SCSI completion handle\n",
ha->host_no));
qla_printk(KERN_WARNING, ha,
"Invalid ISP SCSI completion handle\n");
set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
}
}
/**
* qla2x00_process_response_queue() - Process response queue entries.
* @ha: SCSI driver HA context
*/
void
qla2x00_process_response_queue(struct scsi_qla_host *ha)
{
device_reg_t *reg = ha->iobase;
sts_entry_t *pkt;
uint16_t handle_cnt;
uint16_t cnt;
if (!ha->flags.online)
return;
while (ha->response_ring_ptr->signature != RESPONSE_PROCESSED) {
pkt = (sts_entry_t *)ha->response_ring_ptr;
ha->rsp_ring_index++;
if (ha->rsp_ring_index == ha->response_q_length) {
ha->rsp_ring_index = 0;
ha->response_ring_ptr = ha->response_ring;
} else {
ha->response_ring_ptr++;
}
if (pkt->entry_status != 0) {
DEBUG3(printk(KERN_INFO
"scsi(%ld): Process error entry.\n", ha->host_no));
qla2x00_error_entry(ha, pkt);
((response_t *)pkt)->signature = RESPONSE_PROCESSED;
wmb();
continue;
}
switch (pkt->entry_type) {
case STATUS_TYPE:
qla2x00_status_entry(ha, pkt);
break;
case STATUS_TYPE_21:
handle_cnt = ((sts21_entry_t *)pkt)->handle_count;
for (cnt = 0; cnt < handle_cnt; cnt++) {
qla2x00_process_completed_request(ha,
((sts21_entry_t *)pkt)->handle[cnt]);
}
break;
case STATUS_TYPE_22:
handle_cnt = ((sts22_entry_t *)pkt)->handle_count;
for (cnt = 0; cnt < handle_cnt; cnt++) {
qla2x00_process_completed_request(ha,
((sts22_entry_t *)pkt)->handle[cnt]);
}
break;
case STATUS_CONT_TYPE:
qla2x00_status_cont_entry(ha, (sts_cont_entry_t *)pkt);
break;
case MS_IOCB_TYPE:
qla2x00_ms_entry(ha, (ms_iocb_entry_t *)pkt);
break;
case MBX_IOCB_TYPE:
if (IS_QLA23XX(ha)) {
if (pkt->sys_define == SOURCE_ASYNC_IOCB) {
qla2x00_process_iodesc(ha,
(struct mbx_entry *)pkt);
} else {
/* MBX IOCB Type Not Supported. */
DEBUG4(printk(KERN_WARNING
"scsi(%ld): Received unknown MBX "
"IOCB response pkt type=%x "
"source=%x entry status=%x.\n",
ha->host_no, pkt->entry_type,
pkt->sys_define,
pkt->entry_status));
}
break;
}
/* Fallthrough. */
default:
/* Type Not Supported. */
DEBUG4(printk(KERN_WARNING
"scsi(%ld): Received unknown response pkt type %x "
"entry status=%x.\n",
ha->host_no, pkt->entry_type, pkt->entry_status));
break;
}
((response_t *)pkt)->signature = RESPONSE_PROCESSED;
wmb();
}
/* Adjust ring index */
WRT_REG_WORD(ISP_RSP_Q_OUT(ha, reg), ha->rsp_ring_index);
}
/**
* qla2x00_status_entry() - Process a Status IOCB entry.
* @ha: SCSI driver HA context
* @pkt: Entry pointer
*/
static void
qla2x00_status_entry(scsi_qla_host_t *ha, sts_entry_t *pkt)
{
int ret;
unsigned b, t, l;
srb_t *sp;
os_lun_t *lq;
os_tgt_t *tq;
fc_port_t *fcport;
struct scsi_cmnd *cp;
uint16_t comp_status;
uint16_t scsi_status;
uint8_t lscsi_status;
uint32_t resid;
uint8_t sense_sz = 0;
/* Fast path completion. */
if (le16_to_cpu(pkt->comp_status) == CS_COMPLETE &&
(le16_to_cpu(pkt->scsi_status) & SS_MASK) == 0) {
qla2x00_process_completed_request(ha, pkt->handle);
return;
}
/* Validate handle. */
if (pkt->handle < MAX_OUTSTANDING_COMMANDS) {
sp = ha->outstanding_cmds[pkt->handle];
ha->outstanding_cmds[pkt->handle] = 0;
} else
sp = NULL;
if (sp == NULL) {
DEBUG2(printk("scsi(%ld): Status Entry invalid handle.\n",
ha->host_no));
qla_printk(KERN_WARNING, ha, "Status Entry invalid handle.\n");
set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
if (ha->dpc_wait && !ha->dpc_active)
up(ha->dpc_wait);
return;
}
cp = sp->cmd;
if (cp == NULL) {
DEBUG2(printk("scsi(%ld): Command already returned back to OS "
"pkt->handle=%d sp=%p sp->state:%d\n",
ha->host_no, pkt->handle, sp, sp->state));
qla_printk(KERN_WARNING, ha,
"Command is NULL: already returned to OS (sp=%p)\n", sp);
return;
}
if (ha->actthreads)
ha->actthreads--;
if (sp->lun_queue == NULL) {
DEBUG2(printk("scsi(%ld): Status Entry invalid lun pointer.\n",
ha->host_no));
qla_printk(KERN_WARNING, ha,
"Status Entry invalid lun pointer.\n");
set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
if (ha->dpc_wait && !ha->dpc_active)
up(ha->dpc_wait);
return;
}
sp->lun_queue->out_cnt--;
comp_status = le16_to_cpu(pkt->comp_status);
/* Mask of reserved bits 12-15, before we examine the scsi status */
scsi_status = le16_to_cpu(pkt->scsi_status) & SS_MASK;
lscsi_status = scsi_status & STATUS_MASK;
CMD_ENTRY_STATUS(cp) = pkt->entry_status;
CMD_COMPL_STATUS(cp) = comp_status;
CMD_SCSI_STATUS(cp) = scsi_status;
/* Generate LU queue on cntrl, target, LUN */
b = cp->device->channel;
t = cp->device->id;
l = cp->device->lun,
tq = sp->tgt_queue;
lq = sp->lun_queue;
/*
* If loop is in transient state Report DID_BUS_BUSY
*/
if ((comp_status != CS_COMPLETE || scsi_status != 0)) {
if (!(sp->flags & SRB_IOCTL) &&
(atomic_read(&ha->loop_down_timer) ||
atomic_read(&ha->loop_state) != LOOP_READY)) {
DEBUG2(printk("scsi(%ld:%d:%d:%d): Loop Not Ready - "
"pid=%lx.\n",
ha->host_no, b, t, l, cp->serial_number));
qla2x00_extend_timeout(cp, EXTEND_CMD_TIMEOUT);
add_to_retry_queue(ha, sp);
return;
}
}
/*
* Based on Host and scsi status generate status code for Linux
*/
switch (comp_status) {
case CS_COMPLETE:
if (scsi_status == 0) {
cp->result = DID_OK << 16;
break;
}
if (lscsi_status == SS_BUSY_CONDITION) {
cp->result = DID_BUS_BUSY << 16 | lscsi_status;
break;
}
cp->result = DID_OK << 16 | lscsi_status;
if (lscsi_status != SS_CHECK_CONDITION)
break;
/*
* Copy Sense Data into sense buffer
*/
memset(cp->sense_buffer, 0, sizeof(cp->sense_buffer));
if (!(scsi_status & SS_SENSE_LEN_VALID))
break;
if (le16_to_cpu(pkt->req_sense_length) <
sizeof(cp->sense_buffer))
sense_sz = le16_to_cpu(pkt->req_sense_length);
else
sense_sz = sizeof(cp->sense_buffer) - 1;
CMD_ACTUAL_SNSLEN(cp) = sense_sz;
sp->request_sense_length = sense_sz;
sp->request_sense_ptr = cp->sense_buffer;
if (sp->request_sense_length > 32)
sense_sz = 32;
memcpy(cp->sense_buffer, pkt->req_sense_data, sense_sz);
sp->request_sense_ptr += sense_sz;
sp->request_sense_length -= sense_sz;
if (sp->request_sense_length != 0)
ha->status_srb = sp;
if (!(sp->flags & SRB_IOCTL) &&
qla2x00_check_sense(cp, lq) == QLA_SUCCESS) {
/* Throw away status_cont if any */
ha->status_srb = NULL;
add_to_scsi_retry_queue(ha, sp);
return;
}
DEBUG5(printk("%s(): Check condition Sense data, "
"scsi(%ld:%d:%d:%d) cmd=%p pid=%ld\n",
__func__, ha->host_no, b, t, l, cp,
cp->serial_number));
if (sense_sz)
DEBUG5(qla2x00_dump_buffer(cp->sense_buffer,
CMD_ACTUAL_SNSLEN(cp)));
break;
case CS_DATA_UNDERRUN:
DEBUG2(printk(KERN_INFO
"scsi(%ld:%d:%d) UNDERRUN status detected 0x%x-0x%x.\n",
ha->host_no, t, l, comp_status, scsi_status));
resid = le32_to_cpu(pkt->residual_length);
CMD_RESID_LEN(cp) = resid;
/*
* Check to see if SCSI Status is non zero. If so report SCSI
* Status.
*/
if (lscsi_status != 0) {
if (lscsi_status == SS_BUSY_CONDITION) {
cp->result = DID_BUS_BUSY << 16 |
lscsi_status;
break;
}
cp->result = DID_OK << 16 | lscsi_status;
if (lscsi_status != SS_CHECK_CONDITION)
break;
/* Copy Sense Data into sense buffer */
memset(cp->sense_buffer, 0, sizeof(cp->sense_buffer));
if (!(scsi_status & SS_SENSE_LEN_VALID))
break;
if (le16_to_cpu(pkt->req_sense_length) <
sizeof(cp->sense_buffer))
sense_sz = le16_to_cpu(pkt->req_sense_length);
else
sense_sz = sizeof(cp->sense_buffer) - 1;
CMD_ACTUAL_SNSLEN(cp) = sense_sz;
sp->request_sense_length = sense_sz;
sp->request_sense_ptr = cp->sense_buffer;
if (sp->request_sense_length > 32)
sense_sz = 32;
memcpy(cp->sense_buffer, pkt->req_sense_data, sense_sz);
sp->request_sense_ptr += sense_sz;
sp->request_sense_length -= sense_sz;
if (sp->request_sense_length != 0)
ha->status_srb = sp;
if (!(sp->flags & SRB_IOCTL) &&
(qla2x00_check_sense(cp, lq) == QLA_SUCCESS)) {
ha->status_srb = NULL;
add_to_scsi_retry_queue(ha, sp);
return;
}
DEBUG5(printk("%s(): Check condition Sense data, "
"scsi(%ld:%d:%d:%d) cmd=%p pid=%ld\n",
__func__, ha->host_no, b, t, l, cp,
cp->serial_number));
if (sense_sz)
DEBUG5(qla2x00_dump_buffer(cp->sense_buffer,
CMD_ACTUAL_SNSLEN(cp)));
} else {
/*
* If RISC reports underrun and target does not report
* it then we must have a lost frame, so tell upper
* layer to retry it by reporting a bus busy.
*/
if (!(scsi_status & SS_RESIDUAL_UNDER)) {
DEBUG2(printk("scsi(%ld:%d:%d:%d) Dropped "
"frame(s) detected (%x of %x bytes)..."
"retrying command.\n",
ha->host_no, b, t, l, resid,
cp->request_bufflen));
cp->result = DID_BUS_BUSY << 16;
ha->dropped_frame_error_cnt++;
break;
}
/* Handle mid-layer underflow */
cp->resid = resid;
if ((unsigned)(cp->request_bufflen - resid) <
cp->underflow) {
qla_printk(KERN_INFO, ha,
"scsi(%ld:%d:%d:%d): Mid-layer underflow "
"detected (%x of %x bytes)...returning "
"error status.\n",
ha->host_no, b, t, l, resid,
cp->request_bufflen);
cp->result = DID_ERROR << 16;
break;
}
/* Everybody online, looking good... */
cp->result = DID_OK << 16;
}
break;
case CS_DATA_OVERRUN:
DEBUG2(printk(KERN_INFO
"scsi(%ld:%d:%d): OVERRUN status detected 0x%x-0x%x\n",
ha->host_no, t, l, comp_status, scsi_status));
DEBUG2(printk(KERN_INFO
"CDB: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
cp->cmnd[0], cp->cmnd[1], cp->cmnd[2], cp->cmnd[3],
cp->cmnd[4], cp->cmnd[5]));
DEBUG2(printk(KERN_INFO
"PID=0x%lx req=0x%x xtra=0x%x -- returning DID_ERROR "
"status!\n",
cp->serial_number, cp->request_bufflen,
le32_to_cpu(pkt->residual_length)));
cp->result = DID_ERROR << 16;
break;
case CS_PORT_LOGGED_OUT:
case CS_PORT_CONFIG_CHG:
case CS_PORT_BUSY:
case CS_INCOMPLETE:
case CS_PORT_UNAVAILABLE:
/*
* If the port is in Target Down state, return all IOs for this
* Target with DID_NO_CONNECT ELSE Queue the IOs in the
* retry_queue.
*/
fcport = sp->fclun->fcport;
DEBUG2(printk("scsi(%ld:%d:%d): status_entry: Port Down "
"pid=%ld, compl status=0x%x, port state=0x%x\n",
ha->host_no, t, l, cp->serial_number, comp_status,
atomic_read(&fcport->state)));
if ((sp->flags & SRB_IOCTL) ||
atomic_read(&fcport->state) == FCS_DEVICE_DEAD) {
cp->result = DID_NO_CONNECT << 16;
add_to_done_queue(ha, sp);
} else {
qla2x00_extend_timeout(cp, EXTEND_CMD_TIMEOUT);
add_to_retry_queue(ha, sp);
}
if (atomic_read(&fcport->state) == FCS_ONLINE) {
qla2x00_mark_device_lost(ha, fcport, 1);
}
return;
break;
case CS_RESET:
DEBUG2(printk(KERN_INFO
"scsi(%ld): RESET status detected 0x%x-0x%x.\n",
ha->host_no, comp_status, scsi_status));
if (sp->flags & SRB_IOCTL) {
cp->result = DID_RESET << 16;
} else {
qla2x00_extend_timeout(cp, EXTEND_CMD_TIMEOUT);
add_to_retry_queue(ha, sp);
return;
}
break;
case CS_ABORTED:
/*
* hv2.19.12 - DID_ABORT does not retry the request if we
* aborted this request then abort otherwise it must be a
* reset.
*/
DEBUG2(printk(KERN_INFO
"scsi(%ld): ABORT status detected 0x%x-0x%x.\n",
ha->host_no, comp_status, scsi_status));
cp->result = DID_RESET << 16;
break;
case CS_TIMEOUT:
DEBUG2(printk(KERN_INFO
"scsi(%ld:%d:%d:%d): TIMEOUT status detected 0x%x-0x%x.\n",
ha->host_no, b, t, l, comp_status, scsi_status));
cp->result = DID_BUS_BUSY << 16;
fcport = lq->fclun->fcport;
/* Check to see if logout occurred */
if ((le16_to_cpu(pkt->status_flags) & SF_LOGOUT_SENT)) {
qla2x00_mark_device_lost(ha, fcport, 1);
}
break;
case CS_QUEUE_FULL:
DEBUG2(printk(KERN_INFO
"scsi(%ld): QUEUE FULL status detected 0x%x-0x%x.\n",
ha->host_no, comp_status, scsi_status));
/* SCSI Mid-Layer handles device queue full */
cp->result = DID_OK << 16 | lscsi_status;
/* TODO: ??? */
/* Adjust queue depth */
ret = scsi_track_queue_full(cp->device,
sp->lun_queue->out_cnt - 1);
if (ret) {
qla_printk(KERN_INFO, ha,
"scsi(%ld:%d:%d:%d): Queue depth adjusted to %d.\n",
ha->host_no, cp->device->channel, cp->device->id,
cp->device->lun, ret);
}
break;
default:
DEBUG3(printk("scsi(%ld): Error detected (unknown status) "
"0x%x-0x%x.\n",
ha->host_no, comp_status, scsi_status));
qla_printk(KERN_INFO, ha,
"Unknown status detected 0x%x-0x%x.\n",
comp_status, scsi_status);
cp->result = DID_ERROR << 16;
break;
}
/* Place command on done queue. */
if (ha->status_srb == NULL)
add_to_done_queue(ha, sp);
}
/**
* qla2x00_status_cont_entry() - Process a Status Continuations entry.
* @ha: SCSI driver HA context
* @pkt: Entry pointer
*
* Extended sense data.
*/
static void
qla2x00_status_cont_entry(scsi_qla_host_t *ha, sts_cont_entry_t *pkt)
{
uint8_t sense_sz = 0;
srb_t *sp = ha->status_srb;
struct scsi_cmnd *cp;
if (sp != NULL && sp->request_sense_length != 0) {
cp = sp->cmd;
if (cp == NULL) {
DEBUG2(printk("%s(): Cmd already returned back to OS "
"sp=%p sp->state:%d\n", __func__, sp, sp->state));
qla_printk(KERN_INFO, ha,
"cmd is NULL: already returned to OS (sp=%p)\n",
sp);
ha->status_srb = NULL;
return;
}
if (sp->request_sense_length > sizeof(pkt->data)) {
sense_sz = sizeof(pkt->data);
} else {
sense_sz = sp->request_sense_length;
}
/* Move sense data. */
memcpy(sp->request_sense_ptr, pkt->data, sense_sz);
DEBUG5(qla2x00_dump_buffer(sp->request_sense_ptr, sense_sz));
sp->request_sense_ptr += sense_sz;
sp->request_sense_length -= sense_sz;
/* Place command on done queue. */
if (sp->request_sense_length == 0) {
add_to_done_queue(ha, sp);
ha->status_srb = NULL;
}
}
}
/**
* qla2x00_error_entry() - Process an error entry.
* @ha: SCSI driver HA context
* @pkt: Entry pointer
*/
static void
qla2x00_error_entry(scsi_qla_host_t *ha, sts_entry_t *pkt)
{
srb_t *sp;
#if defined(QL_DEBUG_LEVEL_2)
if (pkt->entry_status & RF_INV_E_ORDER)
qla_printk(KERN_ERR, ha, "%s: Invalid Entry Order\n", __func__);
else if (pkt->entry_status & RF_INV_E_COUNT)
qla_printk(KERN_ERR, ha, "%s: Invalid Entry Count\n", __func__);
else if (pkt->entry_status & RF_INV_E_PARAM)
qla_printk(KERN_ERR, ha,
"%s: Invalid Entry Parameter\n", __func__);
else if (pkt->entry_status & RF_INV_E_TYPE)
qla_printk(KERN_ERR, ha, "%s: Invalid Entry Type\n", __func__);
else if (pkt->entry_status & RF_BUSY)
qla_printk(KERN_ERR, ha, "%s: Busy\n", __func__);
else
qla_printk(KERN_ERR, ha, "%s: UNKNOWN flag error\n", __func__);
#endif
/* Validate handle. */
if (pkt->handle < MAX_OUTSTANDING_COMMANDS)
sp = ha->outstanding_cmds[pkt->handle];
else
sp = NULL;
if (sp) {
/* Free outstanding command slot. */
ha->outstanding_cmds[pkt->handle] = 0;
if (ha->actthreads)
ha->actthreads--;
sp->lun_queue->out_cnt--;
/* Bad payload or header */
if (pkt->entry_status &
(RF_INV_E_ORDER | RF_INV_E_COUNT |
RF_INV_E_PARAM | RF_INV_E_TYPE)) {
sp->cmd->result = DID_ERROR << 16;
} else if (pkt->entry_status & RF_BUSY) {
sp->cmd->result = DID_BUS_BUSY << 16;
} else {
sp->cmd->result = DID_ERROR << 16;
}
/* Place command on done queue. */
add_to_done_queue(ha, sp);
} else if (pkt->entry_type == COMMAND_A64_TYPE ||
pkt->entry_type == COMMAND_TYPE) {
DEBUG2(printk("scsi(%ld): Error entry - invalid handle\n",
ha->host_no));
qla_printk(KERN_WARNING, ha,
"Error entry - invalid handle\n");
set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
if (ha->dpc_wait && !ha->dpc_active)
up(ha->dpc_wait);
}
}
/**
* qla2x00_ms_entry() - Process a Management Server entry.
* @ha: SCSI driver HA context
* @index: Response queue out pointer
*/
static void
qla2x00_ms_entry(scsi_qla_host_t *ha, ms_iocb_entry_t *pkt)
{
srb_t *sp;
DEBUG3(printk("%s(%ld): pkt=%p pkthandle=%d.\n",
__func__, ha->host_no, pkt, pkt->handle1));
/* Validate handle. */
if (pkt->handle1 < MAX_OUTSTANDING_COMMANDS)
sp = ha->outstanding_cmds[pkt->handle1];
else
sp = NULL;
if (sp == NULL) {
DEBUG2(printk("scsi(%ld): MS entry - invalid handle\n",
ha->host_no));
qla_printk(KERN_WARNING, ha, "MS entry - invalid handle\n");
set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
return;
}
CMD_COMPL_STATUS(sp->cmd) = le16_to_cpu(pkt->status);
CMD_ENTRY_STATUS(sp->cmd) = pkt->entry_status;
/* Free outstanding command slot. */
ha->outstanding_cmds[pkt->handle1] = 0;
add_to_done_queue(ha, sp);
}
/**
* qla2x00_check_sense() - Perform any sense data interrogation.
* @cp: SCSI Command
* @lq: Lun queue
*
* Returns QLA_SUCCESS if the lun queue is suspended, else
* QLA_FUNCTION_FAILED (lun queue not suspended).
*/
static int
qla2x00_check_sense(struct scsi_cmnd *cp, os_lun_t *lq)
{
scsi_qla_host_t *ha;
srb_t *sp;
fc_port_t *fcport;
ha = (scsi_qla_host_t *) cp->device->host->hostdata;
if ((cp->sense_buffer[0] & 0x70) != 0x70) {
return (QLA_FUNCTION_FAILED);
}
sp = (srb_t * )CMD_SP(cp);
sp->flags |= SRB_GOT_SENSE;
switch (cp->sense_buffer[2] & 0xf) {
case RECOVERED_ERROR:
cp->result = DID_OK << 16;
cp->sense_buffer[0] = 0;
break;
case NOT_READY:
fcport = lq->fclun->fcport;
/*
* Suspend the lun only for hard disk device type.
*/
if ((fcport->flags & FCF_TAPE_PRESENT) == 0 &&
lq->q_state != LUN_STATE_TIMEOUT) {
/*
* If target is in process of being ready then suspend
* lun for 6 secs and retry all the commands.
*/
if (cp->sense_buffer[12] == 0x4 &&
cp->sense_buffer[13] == 0x1) {
/* Suspend the lun for 6 secs */
qla2x00_suspend_lun(ha, lq, 6,
ql2xsuspendcount);
return (QLA_SUCCESS);
}
}
break;
}
return (QLA_FUNCTION_FAILED);
}
/******************************************************************************
* QLOGIC LINUX SOFTWARE
*
* QLogic ISP2x00 device driver for Linux 2.6.x
* Copyright (C) 2003 QLogic Corporation
* (www.qlogic.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.
*
******************************************************************************/
/* Management functions for various lists */
/* __add_to_done_queue()
*
* Place SRB command on done queue.
*
* Input:
* ha = host pointer
* sp = srb pointer.
* Locking:
* this function assumes the ha->list_lock is already taken
*/
static inline void
__add_to_done_queue(struct scsi_qla_host * ha, srb_t * sp)
{
/*
if (sp->state != SRB_NO_QUEUE_STATE &&
sp->state != SRB_ACTIVE_STATE)
BUG();
*/
/* Place block on done queue */
sp->cmd->host_scribble = (unsigned char *) NULL;
sp->state = SRB_DONE_STATE;
list_add_tail(&sp->list,&ha->done_queue);
ha->done_q_cnt++;
sp->ha = ha;
}
static inline void
__add_to_retry_queue(struct scsi_qla_host * ha, srb_t * sp)
{
/*
if( sp->state != SRB_NO_QUEUE_STATE &&
sp->state != SRB_ACTIVE_STATE)
BUG();
*/
/* Place block on retry queue */
list_add_tail(&sp->list,&ha->retry_queue);
ha->retry_q_cnt++;
sp->flags |= SRB_WATCHDOG;
sp->state = SRB_RETRY_STATE;
sp->ha = ha;
}
static inline void
__add_to_scsi_retry_queue(struct scsi_qla_host * ha, srb_t * sp)
{
/*
if( sp->state != SRB_NO_QUEUE_STATE &&
sp->state != SRB_ACTIVE_STATE)
BUG();
*/
/* Place block on retry queue */
list_add_tail(&sp->list,&ha->scsi_retry_queue);
ha->scsi_retry_q_cnt++;
sp->state = SRB_SCSI_RETRY_STATE;
sp->ha = ha;
}
static inline void
add_to_done_queue(struct scsi_qla_host * ha, srb_t * sp)
{
unsigned long flags;
spin_lock_irqsave(&ha->list_lock, flags);
__add_to_done_queue(ha,sp);
spin_unlock_irqrestore(&ha->list_lock, flags);
}
static inline void
add_to_free_queue(struct scsi_qla_host * ha, srb_t * sp)
{
mempool_free(sp, ha->srb_mempool);
}
static inline void
add_to_retry_queue(struct scsi_qla_host * ha, srb_t * sp)
{
unsigned long flags;
spin_lock_irqsave(&ha->list_lock, flags);
__add_to_retry_queue(ha,sp);
spin_unlock_irqrestore(&ha->list_lock, flags);
}
static inline void
add_to_scsi_retry_queue(struct scsi_qla_host * ha, srb_t * sp)
{
unsigned long flags;
spin_lock_irqsave(&ha->list_lock, flags);
__add_to_scsi_retry_queue(ha,sp);
spin_unlock_irqrestore(&ha->list_lock, flags);
}
/*
* __del_from_retry_queue
* Function used to remove a command block from the
* watchdog timer queue.
*
* Note: Must insure that command is on watchdog
* list before calling del_from_retry_queue
* if (sp->flags & SRB_WATCHDOG)
*
* Input:
* ha = adapter block pointer.
* sp = srb pointer.
* Locking:
* this function assumes the list_lock is already taken
*/
static inline void
__del_from_retry_queue(struct scsi_qla_host * ha, srb_t * sp)
{
list_del_init(&sp->list);
sp->flags &= ~(SRB_WATCHDOG | SRB_BUSY);
sp->state = SRB_NO_QUEUE_STATE;
ha->retry_q_cnt--;
}
/*
* __del_from_scsi_retry_queue
* Function used to remove a command block from the
* scsi retry queue.
*
* Input:
* ha = adapter block pointer.
* sp = srb pointer.
* Locking:
* this function assumes the list_lock is already taken
*/
static inline void
__del_from_scsi_retry_queue(struct scsi_qla_host * ha, srb_t * sp)
{
list_del_init(&sp->list);
ha->scsi_retry_q_cnt--;
sp->state = SRB_NO_QUEUE_STATE;
}
/*
* del_from_retry_queue
* Function used to remove a command block from the
* watchdog timer queue.
*
* Note: Must insure that command is on watchdog
* list before calling del_from_retry_queue
* if (sp->flags & SRB_WATCHDOG)
*
* Input:
* ha = adapter block pointer.
* sp = srb pointer.
* Locking:
* this function takes and releases the list_lock
*/
static inline void
del_from_retry_queue(struct scsi_qla_host * ha, srb_t * sp)
{
unsigned long flags;
/* if (unlikely(!(sp->flags & SRB_WATCHDOG)))
BUG();*/
spin_lock_irqsave(&ha->list_lock, flags);
/* if (unlikely(list_empty(&ha->retry_queue)))
BUG();*/
__del_from_retry_queue(ha,sp);
spin_unlock_irqrestore(&ha->list_lock, flags);
}
/*
* del_from_scsi_retry_queue
* Function used to remove a command block from the
* scsi retry queue.
*
* Input:
* ha = adapter block pointer.
* sp = srb pointer.
* Locking:
* this function takes and releases the list_lock
*/
static inline void
del_from_scsi_retry_queue(struct scsi_qla_host * ha, srb_t * sp)
{
unsigned long flags;
spin_lock_irqsave(&ha->list_lock, flags);
/* if (unlikely(list_empty(&ha->scsi_retry_queue)))
BUG();*/
__del_from_scsi_retry_queue(ha,sp);
spin_unlock_irqrestore(&ha->list_lock, flags);
}
/*
* __add_to_pending_queue
* Add the standard SCB job to the bottom of standard SCB commands.
*
* Input:
* COMPLETE!!!
* q = SCSI LU pointer.
* sp = srb pointer.
* SCSI_LU_Q lock must be already obtained.
*/
static inline int
__add_to_pending_queue(struct scsi_qla_host *ha, srb_t * sp)
{
int empty;
/*
if( sp->state != SRB_NO_QUEUE_STATE &&
sp->state != SRB_FREE_STATE &&
sp->state != SRB_ACTIVE_STATE)
BUG();
*/
empty = list_empty(&ha->pending_queue);
list_add_tail(&sp->list, &ha->pending_queue);
ha->qthreads++;
sp->state = SRB_PENDING_STATE;
return (empty);
}
static inline void
__add_to_pending_queue_head(struct scsi_qla_host *ha, srb_t * sp)
{
/*
if( sp->state != SRB_NO_QUEUE_STATE &&
sp->state != SRB_FREE_STATE &&
sp->state != SRB_ACTIVE_STATE)
BUG();
*/
list_add(&sp->list, &ha->pending_queue);
ha->qthreads++;
sp->state = SRB_PENDING_STATE;
}
static inline int
add_to_pending_queue(struct scsi_qla_host *ha, srb_t *sp)
{
int empty;
unsigned long flags;
spin_lock_irqsave(&ha->list_lock, flags);
empty = __add_to_pending_queue(ha, sp);
spin_unlock_irqrestore(&ha->list_lock, flags);
return (empty);
}
static inline void
add_to_pending_queue_head(struct scsi_qla_host *ha, srb_t *sp)
{
unsigned long flags;
spin_lock_irqsave(&ha->list_lock, flags);
__add_to_pending_queue_head(ha, sp);
spin_unlock_irqrestore(&ha->list_lock, flags);
}
static inline void
__del_from_pending_queue(struct scsi_qla_host *ha, srb_t *sp)
{
list_del_init(&sp->list);
ha->qthreads--;
sp->state = SRB_NO_QUEUE_STATE;
}
/*
* Failover Stuff.
*/
static inline void
__add_to_failover_queue(struct scsi_qla_host * ha, srb_t * sp)
{
/*
if( sp->state != SRB_NO_QUEUE_STATE &&
sp->state != SRB_ACTIVE_STATE)
BUG();
*/
list_add_tail(&sp->list,&ha->failover_queue);
ha->failover_cnt++;
sp->state = SRB_FAILOVER_STATE;
sp->ha = ha;
}
static inline void add_to_failover_queue(struct scsi_qla_host * ha, srb_t * sp)
{
unsigned long flags;
spin_lock_irqsave(&ha->list_lock, flags);
__add_to_failover_queue(ha,sp);
spin_unlock_irqrestore(&ha->list_lock, flags);
}
static inline void __del_from_failover_queue(struct scsi_qla_host * ha, srb_t *
sp)
{
ha->failover_cnt--;
list_del_init(&sp->list);
sp->state = SRB_NO_QUEUE_STATE;
}
static inline void del_from_failover_queue(struct scsi_qla_host * ha, srb_t * sp)
{
unsigned long flags;
spin_lock_irqsave(&ha->list_lock, flags);
__del_from_failover_queue(ha,sp);
spin_unlock_irqrestore(&ha->list_lock, flags);
}
static inline void
del_from_pending_queue(struct scsi_qla_host * ha, srb_t * sp)
{
unsigned long flags;
spin_lock_irqsave(&ha->list_lock, flags);
__del_from_pending_queue(ha,sp);
spin_unlock_irqrestore(&ha->list_lock, flags);
}
/*
* QLOGIC LINUX SOFTWARE
*
* QLogic ISP2x00 device driver for Linux 2.6.x
* Copyright (C) 2003 QLogic Corporation
* (www.qlogic.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.
*
*/
#include "qla_os.h"
#include "qla_def.h"
/*
* Local Function Prototypes.
*/
static void
qla2x00_mbx_sem_timeout(unsigned long);
static int
qla2x00_get_mbx_access(scsi_qla_host_t *, uint32_t);
static int
qla2x00_release_mbx_access(scsi_qla_host_t *, uint32_t);
static int
qla2x00_mbx_q_add(scsi_qla_host_t *, mbx_cmdq_t **);
static void
qla2x00_mbx_q_get(scsi_qla_host_t *, mbx_cmdq_t **);
static void
qla2x00_mbx_q_memb_alloc(scsi_qla_host_t *, mbx_cmdq_t **);
static void
qla2x00_mbx_q_memb_free(scsi_qla_host_t *, mbx_cmdq_t *);
/***************************/
/* Function implementation */
/***************************/
static void
qla2x00_mbx_sem_timeout(unsigned long data)
{
struct semaphore *sem_ptr = (struct semaphore *)data;
DEBUG11(printk("qla2x00_sem_timeout: entered.\n");)
if (sem_ptr != NULL) {
up(sem_ptr);
}
DEBUG11(printk("qla2x00_mbx_sem_timeout: exiting.\n");)
}
/*
* tov = timeout value in seconds
*/
static int
qla2x00_get_mbx_access(scsi_qla_host_t *ha, uint32_t tov)
{
int ret;
int prev_val = 1; /* assume no access yet */
mbx_cmdq_t *ptmp_mbq;
struct timer_list tmp_cmd_timer;
unsigned long cpu_flags;
DEBUG11(printk("qla2x00_get_mbx_access(%ld): entered.\n",
ha->host_no);)
while (1) {
if (test_bit(MBX_CMD_WANT, &ha->mbx_cmd_flags) == 0) {
DEBUG11(printk("qla2x00_get_mbx_access(%ld): going "
" to test access flags.\n", ha->host_no);)
/* No one else is waiting. Go ahead and try to
* get access.
*/
if ((prev_val = test_and_set_bit(MBX_CMD_ACTIVE,
&ha->mbx_cmd_flags)) == 0) {
break;
}
}
/* wait for previous command to finish */
DEBUG(printk("qla2x00_get_mbx_access(%ld): access "
"flags=%lx. busy. Waiting for access. curr time=0x%lx.\n",
ha->host_no, ha->mbx_cmd_flags, jiffies);)
DEBUG11(printk("qla2x00_get_mbx_access(%ld): access "
"flags=%lx. busy. Waiting for access. curr time=0x%lx.\n",
ha->host_no, ha->mbx_cmd_flags, jiffies);)
/*
* Init timer and get semaphore from mbx q. After we got valid
* semaphore pointer the MBX_CMD_WANT flag would also had
* been set.
*/
qla2x00_mbx_q_add(ha, &ptmp_mbq);
if (ptmp_mbq == NULL) {
/* queue full? problem? can't proceed. */
DEBUG2_3_11(printk("qla2x00_get_mbx_access(%ld): ERROR "
"no more mbx_q allowed. exiting.\n", ha->host_no);)
break;
}
/* init timer and semaphore */
init_timer(&tmp_cmd_timer);
tmp_cmd_timer.data = (unsigned long)&ptmp_mbq->cmd_sem;
tmp_cmd_timer.function =
(void (*)(unsigned long))qla2x00_mbx_sem_timeout;
tmp_cmd_timer.expires = jiffies + tov * HZ;
DEBUG11(printk("get_mbx_access(%ld): adding timer. "
"curr time=0x%lx timeoutval=0x%lx.\n",
ha->host_no, jiffies, tmp_cmd_timer.expires);)
/* wait. */
/* add_timer(&tmp_cmd_timer);*/
DEBUG11(printk("get_mbx_access(%ld): going to sleep. "
"current time=0x%lx.\n", ha->host_no, jiffies);)
down_interruptible(&ptmp_mbq->cmd_sem);
DEBUG11(printk("get_mbx_access(%ld): woke up. current "
"time=0x%lx.\n",
ha->host_no, jiffies);)
/* del_timer(&tmp_cmd_timer);*/
/* try to get lock again. we'll test later to see
* if we actually got the lock.
*/
prev_val = test_and_set_bit(MBX_CMD_ACTIVE,
&ha->mbx_cmd_flags);
/*
* After we tried to get access then we check to see
* if we need to clear the MBX_CMD_WANT flag. Don't clear
* this flag before trying to get access or else another
* new thread might grab it before we did.
*/
spin_lock_irqsave(&ha->mbx_q_lock, cpu_flags);
if (ha->mbx_q_head == NULL) {
/* We're the last thread in queue. */
clear_bit(MBX_CMD_WANT, &ha->mbx_cmd_flags);
}
qla2x00_mbx_q_memb_free(ha, ptmp_mbq);
spin_unlock_irqrestore(&ha->mbx_q_lock, cpu_flags);
break;
}
if (prev_val == 0) {
/* We got the lock */
DEBUG11(printk("qla2x00_get_mbx_access(%ld): success.\n",
ha->host_no);)
ret = QLA_SUCCESS;
} else {
/* Timeout or resource error. */
DEBUG2_3_11(printk("qla2x00_get_mbx_access(%ld): timed out.\n",
ha->host_no);)
ret = QLA_FUNCTION_TIMEOUT;
}
return ret;
}
static int
qla2x00_release_mbx_access(scsi_qla_host_t *ha, uint32_t tov)
{
mbx_cmdq_t *next_thread;
DEBUG11(printk("qla2x00_release_mbx_access:(%ld): entered.\n",
ha->host_no);)
clear_bit(MBX_CMD_ACTIVE, &ha->mbx_cmd_flags);
/* Wake up one pending mailbox cmd thread in queue. */
qla2x00_mbx_q_get(ha, &next_thread);
if (next_thread) {
DEBUG11(printk("qla2x00_release_mbx_access: found pending "
"mbx cmd. Waking up sem in %p.\n", &next_thread);)
up(&next_thread->cmd_sem);
}
DEBUG11(printk("qla2x00_release_mbx_access:(%ld): exiting.\n",
ha->host_no);)
return QLA_SUCCESS;
}
/* Allocates a mbx_cmdq_t struct and add to the mbx_q list. */
static int
qla2x00_mbx_q_add(scsi_qla_host_t *ha, mbx_cmdq_t **ret_mbq)
{
int ret;
unsigned long cpu_flags;
mbx_cmdq_t *ptmp = NULL;
spin_lock_irqsave(&ha->mbx_q_lock, cpu_flags);
DEBUG11(printk("qla2x00_mbx_q_add: got mbx_q spinlock. "
"Inst=%d.\n", apiHBAInstance);)
qla2x00_mbx_q_memb_alloc(ha, &ptmp);
if (ptmp == NULL) {
/* can't add any more threads */
DEBUG2_3_11(printk("qla2x00_mbx_q_add: ERROR no more "
"ioctl threads allowed. Inst=%d.\n", apiHBAInstance);)
ret = QLA_MEMORY_ALLOC_FAILED;
} else {
if (ha->mbx_q_tail == NULL) {
/* First thread to queue. */
set_bit(MBX_CMD_WANT, &ha->mbx_cmd_flags);
ha->mbx_q_head = ptmp;
} else {
ha->mbx_q_tail->pnext = ptmp;
}
ha->mbx_q_tail = ptmp;
/* Now init the semaphore */
init_MUTEX_LOCKED(&ptmp->cmd_sem);
ret = QLA_SUCCESS;
}
*ret_mbq = ptmp;
DEBUG11(printk("qla2x00_mbx_q_add: going to release spinlock. "
"ret_mbq=%p, ret=%d. Inst=%d.\n", *ret_mbq, ret, apiHBAInstance);)
spin_unlock_irqrestore(&ha->mbx_q_lock, cpu_flags);
return ret;
}
/* Just remove and return first member from mbx_cmdq. Don't free anything. */
static void
qla2x00_mbx_q_get(scsi_qla_host_t *ha, mbx_cmdq_t **ret_mbq)
{
unsigned long cpu_flags;
spin_lock_irqsave(&ha->mbx_q_lock, cpu_flags);
DEBUG11(printk("qla2x00_mbx_q_get: got mbx_q spinlock. "
"Inst=%d.\n", apiHBAInstance);)
/* Remove from head */
*ret_mbq = ha->mbx_q_head;
if (ha->mbx_q_head != NULL) {
ha->mbx_q_head = ha->mbx_q_head->pnext;
if (ha->mbx_q_head == NULL) {
/* That's the last one in queue. */
ha->mbx_q_tail = NULL;
}
(*ret_mbq)->pnext = NULL;
}
DEBUG11(printk("qla2x00_mbx_q_remove: return ret_mbq=%p. Going to "
"release spinlock. Inst=%d.\n", *ret_mbq, apiHBAInstance);)
spin_unlock_irqrestore(&ha->mbx_q_lock, cpu_flags);
}
/* Find a free mbx_q member from the array. Must already got the
* mbx_q_lock spinlock.
*/
static void
qla2x00_mbx_q_memb_alloc(scsi_qla_host_t *ha, mbx_cmdq_t **ret_mbx_q_memb)
{
mbx_cmdq_t *ptmp = NULL;
DEBUG11(printk("qla2x00_mbx_q_memb_alloc: entered. "
"Inst=%d.\n", apiHBAInstance);)
ptmp = ha->mbx_sem_pool_head;
if (ptmp != NULL) {
ha->mbx_sem_pool_head = ptmp->pnext;
ptmp->pnext = NULL;
if (ha->mbx_sem_pool_head == NULL) {
ha->mbx_sem_pool_tail = NULL;
}
} else {
/* We ran out of pre-allocated semaphores. Try to allocate
* a new one.
*/
ptmp = kmalloc(sizeof(mbx_cmdq_t), GFP_ATOMIC);
if(ptmp)
memset(ptmp, 0, sizeof(mbx_cmdq_t));
}
*ret_mbx_q_memb = ptmp;
DEBUG11(printk("qla2x00_mbx_q_memb_alloc: return waitq_memb=%p. "
"Inst=%d.\n", *ret_mbx_q_memb, apiHBAInstance);)
}
/* Add the specified mbx_q member back to the free semaphore pool. Must
* already got the mbx_q_lock spinlock.
*/
static void
qla2x00_mbx_q_memb_free(scsi_qla_host_t *ha, mbx_cmdq_t *pfree_mbx_q_memb)
{
DEBUG11(printk("qla2x00_mbx_q_memb_free: entered. Inst=%d.\n",
apiHBAInstance);)
if (pfree_mbx_q_memb != NULL) {
if (ha->mbx_sem_pool_tail != NULL) {
/* Add to tail */
ha->mbx_sem_pool_tail->pnext = pfree_mbx_q_memb;
} else {
ha->mbx_sem_pool_head = pfree_mbx_q_memb;
}
ha->mbx_sem_pool_tail = pfree_mbx_q_memb;
}
/* put it back to the free pool. */
DEBUG11(printk("qla2x00_mbx_q_memb_free: exiting. "
"Inst=%d.\n", apiHBAInstance);)
}
/*
* qla2x00_mailbox_command
* Issue mailbox command and waits for completion.
*
* Input:
* ha = adapter block pointer.
* mcp = driver internal mbx struct pointer.
*
* Output:
* mb[MAX_MAILBOX_REGISTER_COUNT] = returned mailbox data.
*
* Returns:
* 0 : QLA_SUCCESS = cmd performed success
* 1 : QLA_FUNCTION_FAILED (error encountered)
* 6 : QLA_FUNCTION_TIMEOUT (timeout condition encountered)
*
* Context:
* Kernel context.
*/
int
qla2x00_mailbox_command(scsi_qla_host_t *ha, mbx_cmd_t *mcp)
{
int rval;
unsigned long flags = 0;
device_reg_t *reg = ha->iobase;
struct timer_list tmp_intr_timer;
uint8_t abort_active = test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
uint8_t io_lock_on = ha->flags.init_done;
uint8_t tmp_stat = 0;
uint16_t command;
uint16_t *iptr, *optr;
uint32_t cnt;
uint32_t mboxes;
unsigned long mbx_flags = 0;
unsigned long wait_time;
rval = QLA_SUCCESS;
DEBUG11(printk("qla2x00_mailbox_command(%ld): entered.\n",
ha->host_no);)
/*
* Wait for active mailbox commands to finish by waiting at most
* tov seconds. This is to serialize actual issuing of mailbox cmds
* during non ISP abort time.
*/
if (!abort_active) {
tmp_stat = qla2x00_get_mbx_access(ha, mcp->tov);
if (tmp_stat != QLA_SUCCESS) {
/* Timeout occurred. Return error. */
DEBUG2_3_11(printk("qla2x00_mailbox_command(%ld): cmd "
"access timeout. Exiting.\n", ha->host_no);)
return QLA_FUNCTION_TIMEOUT;
}
}
ha->flags.mbox_busy = TRUE;
/* Save mailbox command for debug */
ha->mcp = mcp;
/* Try to get mailbox register access */
if (!abort_active)
spin_lock_irqsave(&ha->mbx_reg_lock, mbx_flags);
DEBUG11(printk("scsi%d: prepare to issue mbox cmd=0x%x.\n",
(int)ha->host_no, mcp->mb[0]);)
spin_lock_irqsave(&ha->hardware_lock, flags);
/* Load mailbox registers. */
optr = (uint16_t *)MAILBOX_REG(ha, reg, 0);
iptr = mcp->mb;
command = mcp->mb[0];
mboxes = mcp->out_mb;
for (cnt = 0; cnt < ha->mbx_count; cnt++) {
if (IS_QLA2200(ha) && cnt == 8)
optr = (uint16_t *)MAILBOX_REG(ha, reg, 8);
if (mboxes & BIT_0)
WRT_REG_WORD(optr, *iptr);
mboxes >>= 1;
optr++;
iptr++;
}
#if defined(QL_DEBUG_LEVEL_1)
printk("qla2x00_mailbox_command: Loaded MBX registers "
"(displayed in bytes) = \n");
qla2x00_dump_buffer((uint8_t *)mcp->mb, 16);
printk("\n");
qla2x00_dump_buffer(((uint8_t *)mcp->mb + 0x10), 16);
printk("\n");
qla2x00_dump_buffer(((uint8_t *)mcp->mb + 0x20), 8);
printk("\n");
printk("qla2x00_mailbox_command: I/O address = %lx.\n",
(u_long)optr);
qla2x00_dump_regs(ha);
#endif
/* Issue set host interrupt command to send cmd out. */
ha->flags.mbox_int = FALSE;
clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
/* Unlock mbx registers and wait for interrupt */
DEBUG11(printk("qla2x00_mailbox_command: going to unlock irq & "
"waiting for interrupt. jiffies=%lx.\n", jiffies);)
/* Wait for mbx cmd completion until timeout */
if (!abort_active && io_lock_on) {
/* sleep on completion semaphore */
DEBUG11(printk("qla2x00_mailbox_command(%ld): "
"INTERRUPT MODE. Initializing timer.\n",
ha->host_no);)
init_timer(&tmp_intr_timer);
tmp_intr_timer.data = (unsigned long)&ha->mbx_intr_sem;
tmp_intr_timer.expires = jiffies + mcp->tov * HZ;
tmp_intr_timer.function =
(void (*)(unsigned long))qla2x00_mbx_sem_timeout;
DEBUG11(printk("qla2x00_mailbox_command(%ld): "
"Adding timer.\n", ha->host_no);)
add_timer(&tmp_intr_timer);
DEBUG11(printk("qla2x00_mailbox_command: going to "
"unlock & sleep. time=0x%lx.\n", jiffies);)
set_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags);
WRT_REG_WORD(&reg->hccr, HCCR_SET_HOST_INT);
spin_unlock_irqrestore(&ha->hardware_lock, flags);
if (!abort_active)
spin_unlock_irqrestore(&ha->mbx_reg_lock, mbx_flags);
/* Wait for either the timer to expire
* or the mbox completion interrupt
*/
down(&ha->mbx_intr_sem);
DEBUG11(printk("qla2x00_mailbox_command:"
"waking up."
"time=0x%lx\n", jiffies);)
clear_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags);
/* delete the timer */
del_timer(&tmp_intr_timer);
} else {
DEBUG3_11(printk("qla2x00_mailbox_command(%ld): cmd=%x "
"POLLING MODE.\n", ha->host_no, command);)
WRT_REG_WORD(&reg->hccr, HCCR_SET_HOST_INT);
spin_unlock_irqrestore(&ha->hardware_lock, flags);
if (!abort_active)
spin_unlock_irqrestore(&ha->mbx_reg_lock, mbx_flags);
wait_time = jiffies + mcp->tov * HZ; /* wait at most tov secs */
while (!ha->flags.mbox_int) {
if (time_after(jiffies, wait_time))
break;
/* Check for pending interrupts. */
qla2x00_poll(ha);
udelay(10); /* v4.27 */
} /* while */
}
if (!abort_active)
spin_lock_irqsave(&ha->mbx_reg_lock, mbx_flags);
/* Check whether we timed out */
if (ha->flags.mbox_int) {
DEBUG3_11(printk("qla2x00_mailbox_cmd: cmd %x completed.\n",
command);)
/* Got interrupt. Clear the flag. */
ha->flags.mbox_int = FALSE;
clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
if (ha->mailbox_out[0] != MBS_COMMAND_COMPLETE) {
qla2x00_stats.mboxerr++;
rval = QLA_FUNCTION_FAILED;
}
/* Load return mailbox registers. */
optr = mcp->mb;
iptr = (uint16_t *)&ha->mailbox_out[0];
mboxes = mcp->in_mb;
for (cnt = 0; cnt < ha->mbx_count; cnt++) {
if (mboxes & BIT_0)
*optr = *iptr;
mboxes >>= 1;
optr++;
iptr++;
}
} else {
#if defined(QL_DEBUG_LEVEL_2) || defined(QL_DEBUG_LEVEL_3) || \
defined(QL_DEBUG_LEVEL_11)
printk("qla2x00_mailbox_command(%ld): **** MB Command Timeout "
"for cmd %x ****\n", ha->host_no, command);
printk("qla2x00_mailbox_command: icontrol=%x jiffies=%lx\n",
RD_REG_WORD(&reg->ictrl), jiffies);
printk("qla2x00_mailbox_command: *** mailbox[0] = 0x%x ***\n",
RD_REG_WORD(optr));
qla2x00_dump_regs(ha);
#endif
qla2x00_stats.mboxtout++;
ha->total_mbx_timeout++;
rval = QLA_FUNCTION_TIMEOUT;
}
if (!abort_active)
spin_unlock_irqrestore(&ha->mbx_reg_lock, mbx_flags);
ha->flags.mbox_busy = FALSE;
/* Clean up */
ha->mcp = NULL;
if (!abort_active) {
DEBUG11(printk("qla2x00_mailbox_cmd: checking for additional "
"resp interrupt.\n");)
/* polling mode for non isp_abort commands. */
qla2x00_poll(ha);
}
if (rval == QLA_FUNCTION_TIMEOUT) {
if (!io_lock_on || (mcp->flags & IOCTL_CMD)) {
/* not in dpc. schedule it for dpc to take over. */
DEBUG(printk("qla2x00_mailbox_command(%ld): timeout "
"schedule isp_abort_needed.\n",
ha->host_no);)
DEBUG2_3_11(printk("qla2x00_mailbox_command(%ld): "
"timeout schedule isp_abort_needed.\n",
ha->host_no);)
set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
if (ha->dpc_wait && !ha->dpc_active)
up(ha->dpc_wait);
} else if (!abort_active) {
/* call abort directly since we are in the DPC thread */
DEBUG(printk("qla2x00_mailbox_command(%ld): timeout "
"calling abort_isp\n", ha->host_no);)
DEBUG2_3_11(printk("qla2x00_mailbox_command(%ld): "
"timeout calling abort_isp\n", ha->host_no);)
set_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
clear_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
if (qla2x00_abort_isp(ha)) {
/* failed. retry later. */
set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
}
clear_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
DEBUG(printk("qla2x00_mailbox_command: finished "
"abort_isp\n");)
DEBUG2_3_11(printk("qla2x00_mailbox_command: finished "
"abort_isp\n");)
}
}
/* Allow next mbx cmd to come in. */
if (!abort_active) {
tmp_stat = qla2x00_release_mbx_access(ha, mcp->tov);
if (rval == 0)
rval = tmp_stat;
}
if (rval) {
DEBUG2_3_11(printk("qla2x00_mailbox_command(%ld): **** FAILED. "
"mbx0=%x, mbx1=%x, mbx2=%x, cmd=%x ****\n",
ha->host_no, mcp->mb[0], mcp->mb[1], mcp->mb[2], command);)
} else {
DEBUG11(printk("qla2x00_mailbox_command(%ld): done.\n",
ha->host_no);)
}
DEBUG11(printk("qla2x00_mailbox_command(%ld): exiting.\n",
ha->host_no);)
return rval;
}
/*
* qla2x00_load_ram
* Load adapter RAM using DMA.
*
* Input:
* ha = adapter block pointer.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_load_ram(scsi_qla_host_t *ha, dma_addr_t req_dma, uint16_t risc_addr,
uint16_t risc_code_size)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
uint32_t req_len;
dma_addr_t nml_dma;
uint32_t nml_len;
uint32_t normalized;
DEBUG11(printk("qla2x00_load_ram(%ld): entered.\n",
ha->host_no);)
req_len = risc_code_size;
nml_dma = 0;
nml_len = 0;
normalized = qla2x00_normalize_dma_addr(&req_dma, &req_len, &nml_dma,
&nml_len);
/* Load first segment */
mcp->mb[0] = MBC_LOAD_RISC_RAM;
mcp->mb[1] = risc_addr;
mcp->mb[2] = MSW(req_dma);
mcp->mb[3] = LSW(req_dma);
mcp->mb[4] = (uint16_t)req_len;
mcp->mb[6] = MSW(MSD(req_dma));
mcp->mb[7] = LSW(MSD(req_dma));
mcp->out_mb = MBX_7|MBX_6|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
/* Load second segment - if necessary */
if (normalized && (rval == QLA_SUCCESS)) {
mcp->mb[0] = MBC_LOAD_RISC_RAM;
mcp->mb[1] = risc_addr + (uint16_t)req_len;
mcp->mb[2] = MSW(nml_dma);
mcp->mb[3] = LSW(nml_dma);
mcp->mb[4] = (uint16_t)nml_len;
mcp->mb[6] = MSW(MSD(nml_dma));
mcp->mb[7] = LSW(MSD(nml_dma));
mcp->out_mb = MBX_7|MBX_6|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
}
if (rval == QLA_SUCCESS) {
/* Empty */
DEBUG11(printk("qla2x00_load_ram(%ld): done.\n", ha->host_no);)
} else {
/* Empty */
DEBUG2_3_11(printk("qla2x00_load_ram(%ld): failed. rval=%x "
"mb[0]=%x.\n", ha->host_no, rval, mcp->mb[0]);)
}
return rval;
}
/*
* qla2x00_load_ram_ext
* Load adapter extended RAM using DMA.
*
* Input:
* ha = adapter block pointer.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_load_ram_ext(scsi_qla_host_t *ha, dma_addr_t req_dma,
uint32_t risc_addr, uint16_t risc_code_size)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
uint32_t req_len;
dma_addr_t nml_dma;
uint32_t nml_len;
uint32_t normalized;
DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
req_len = risc_code_size;
nml_dma = 0;
nml_len = 0;
normalized = qla2x00_normalize_dma_addr(&req_dma, &req_len, &nml_dma,
&nml_len);
/* Load first segment */
mcp->mb[0] = MBC_LOAD_RISC_RAM_EXTENDED;
mcp->mb[1] = LSW(risc_addr);
mcp->mb[2] = MSW(req_dma);
mcp->mb[3] = LSW(req_dma);
mcp->mb[4] = (uint16_t)req_len;
mcp->mb[6] = MSW(MSD(req_dma));
mcp->mb[7] = LSW(MSD(req_dma));
mcp->mb[8] = MSW(risc_addr);
mcp->out_mb = MBX_8|MBX_7|MBX_6|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
/* Load second segment - if necessary */
if (normalized && (rval == QLA_SUCCESS)) {
risc_addr += req_len;
mcp->mb[0] = MBC_LOAD_RISC_RAM_EXTENDED;
mcp->mb[1] = LSW(risc_addr);
mcp->mb[2] = MSW(nml_dma);
mcp->mb[3] = LSW(nml_dma);
mcp->mb[4] = (uint16_t)nml_len;
mcp->mb[6] = MSW(MSD(nml_dma));
mcp->mb[7] = LSW(MSD(nml_dma));
mcp->mb[8] = MSW(risc_addr);
mcp->out_mb = MBX_8|MBX_7|MBX_6|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
}
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n",
__func__, ha->host_no, rval, mcp->mb[0]));
} else {
/*EMPTY*/
DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
}
return rval;
}
/*
* qla2x00_execute_fw
* Start adapter firmware.
*
* Input:
* ha = adapter block pointer.
* TARGET_QUEUE_LOCK must be released.
* ADAPTER_STATE_LOCK must be released.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_execute_fw(scsi_qla_host_t *ha)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_execute_fw(%ld): entered.\n", ha->host_no);)
mcp->mb[0] = MBC_EXECUTE_FIRMWARE;
mcp->mb[1] = *ha->brd_info->fw_info[0].fwstart;
mcp->out_mb = MBX_1|MBX_0;
if (IS_QLA2322(ha)) {
mcp->mb[2] = 0;
mcp->out_mb |= MBX_2;
}
mcp->in_mb = MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
DEBUG11(printk("qla2x00_execute_fw(%ld): done.\n", ha->host_no);)
return rval;
}
/*
* qla2x00_get_fw_version
* Get firmware version.
*
* Input:
* ha: adapter state pointer.
* major: pointer for major number.
* minor: pointer for minor number.
* subminor: pointer for subminor number.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
void
qla2x00_get_fw_version(scsi_qla_host_t *ha, uint16_t *major, uint16_t *minor,
uint16_t *subminor, uint16_t *attributes)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
mcp->mb[0] = MBC_GET_FIRMWARE_VERSION;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->flags = 0;
mcp->tov = 30;
rval = qla2x00_mailbox_command(ha, mcp);
/* Return mailbox data. */
*major = mcp->mb[1];
*minor = mcp->mb[2];
*subminor = mcp->mb[3];
*attributes = mcp->mb[6];
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
ha->host_no, rval));
} else {
/*EMPTY*/
DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
}
}
/*
* qla2x00_get_fw_options
* Set firmware options.
*
* Input:
* ha = adapter block pointer.
* fwopt = pointer for firmware options.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_get_fw_options(scsi_qla_host_t *ha, uint16_t *fwopts)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
mcp->mb[0] = MBC_GET_FIRMWARE_OPTION;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
ha->host_no, rval));
} else {
fwopts[1] = mcp->mb[1];
fwopts[2] = mcp->mb[2];
fwopts[3] = mcp->mb[3];
DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
}
return rval;
}
/*
* qla2x00_set_fw_options
* Set firmware options.
*
* Input:
* ha = adapter block pointer.
* fwopt = pointer for firmware options.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_set_fw_options(scsi_qla_host_t *ha, uint16_t *fwopts)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
mcp->mb[0] = MBC_SET_FIRMWARE_OPTION;
mcp->mb[1] = fwopts[1];
mcp->mb[2] = fwopts[2];
mcp->mb[3] = fwopts[3];
mcp->mb[10] = fwopts[10];
mcp->mb[11] = fwopts[11];
mcp->mb[12] = 0; /* Undocumented, but used */
mcp->out_mb = MBX_12|MBX_11|MBX_10|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
ha->host_no, rval));
} else {
/*EMPTY*/
DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
}
return rval;
}
/*
* qla2x00_read_ram_word
*
* Input:
* ha = adapter block pointer.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_read_ram_word(scsi_qla_host_t *ha, uint16_t addr, uint16_t *data)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_read_ram_word(%ld): entered.\n", ha->host_no);)
mcp->mb[0] = MBC_READ_RAM_WORD;
mcp->mb[1] = addr;
mcp->out_mb = MBX_1|MBX_0;
mcp->in_mb = MBX_0|MBX_2;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_read_ram_word(%ld): failed=%x.\n",
ha->host_no, rval);)
} else {
*data = mcp->mb[2];
DEBUG11(printk("qla2x00_read_ram_word(%ld): done.\n",
ha->host_no);)
}
return rval;
}
/*
* qla2x00_write_ram_word
*
* Input:
* ha = adapter block pointer.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_write_ram_word(scsi_qla_host_t *ha, uint16_t addr, uint16_t data)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_write_ram_word(%ld): entered.\n",
ha->host_no);)
mcp->mb[0] = MBC_WRITE_RAM_WORD;
mcp->mb[1] = addr;
mcp->mb[2] = data;
mcp->out_mb = MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_write_ram_word(%ld): failed=%x.\n",
ha->host_no, rval);)
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_write_ram_word(%ld): done.\n",
ha->host_no);)
}
return rval;
}
/*
* qla2x00_write_ram_word_ext
*
* Input:
* ha = adapter block pointer.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_write_ram_word_ext(scsi_qla_host_t *ha, uint32_t addr, uint16_t data)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
mcp->mb[0] = MBC_WRITE_RAM_WORD_EXTENDED;
mcp->mb[1] = LSW(addr);
mcp->mb[2] = data;
mcp->mb[8] = MSW(addr);
mcp->out_mb = MBX_8|MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
ha->host_no, rval));
} else {
/*EMPTY*/
DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
}
return rval;
}
/*
* qla2x00_mbx_reg_test
* Mailbox register wrap test.
*
* Input:
* ha = adapter block pointer.
* TARGET_QUEUE_LOCK must be released.
* ADAPTER_STATE_LOCK must be released.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_mbx_reg_test(scsi_qla_host_t *ha)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_mbx_reg_test(%ld): entered.\n", ha->host_no);)
mcp->mb[0] = MBC_MAILBOX_REGISTER_TEST;
mcp->mb[1] = 0xAAAA;
mcp->mb[2] = 0x5555;
mcp->mb[3] = 0xAA55;
mcp->mb[4] = 0x55AA;
mcp->mb[5] = 0xA5A5;
mcp->mb[6] = 0x5A5A;
mcp->mb[7] = 0x2525;
mcp->out_mb = MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval == QLA_SUCCESS) {
if (mcp->mb[1] != 0xAAAA || mcp->mb[2] != 0x5555 ||
mcp->mb[3] != 0xAA55 || mcp->mb[4] != 0x55AA)
rval = QLA_FUNCTION_FAILED;
if (mcp->mb[5] != 0xA5A5 || mcp->mb[6] != 0x5A5A ||
mcp->mb[7] != 0x2525)
rval = QLA_FUNCTION_FAILED;
}
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_mbx_reg_test(%ld): failed=%x.\n",
ha->host_no, rval);)
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_mbx_reg_test(%ld): done.\n",
ha->host_no);)
}
return rval;
}
/*
* qla2x00_verify_checksum
* Verify firmware checksum.
*
* Input:
* ha = adapter block pointer.
* TARGET_QUEUE_LOCK must be released.
* ADAPTER_STATE_LOCK must be released.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_verify_checksum(scsi_qla_host_t *ha)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_verify_checksum(%ld): entered.\n",
ha->host_no);)
mcp->mb[0] = MBC_VERIFY_CHECKSUM;
mcp->mb[1] = *ha->brd_info->fw_info[0].fwstart;
mcp->out_mb = MBX_1|MBX_0;
mcp->in_mb = MBX_2|MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_verify_checksum(%ld): failed=%x.\n",
ha->host_no, rval);)
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_verify_checksum(%ld): done.\n",
ha->host_no);)
}
return rval;
}
/*
* qla2x00_issue_iocb
* Issue IOCB using mailbox command
*
* Input:
* ha = adapter state pointer.
* buffer = buffer pointer.
* phys_addr = physical address of buffer.
* size = size of buffer.
* TARGET_QUEUE_LOCK must be released.
* ADAPTER_STATE_LOCK must be released.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_issue_iocb(scsi_qla_host_t *ha, void* buffer, dma_addr_t phys_addr,
size_t size)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
ENTER("qla2x00_issue_iocb: started");
mcp->mb[0] = MBC_IOCB_COMMAND_A64;
mcp->mb[1] = 0;
mcp->mb[2] = MSW(phys_addr);
mcp->mb[3] = LSW(phys_addr);
mcp->mb[6] = MSW(MSD(phys_addr));
mcp->mb[7] = LSW(MSD(phys_addr));
mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_2|MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG(printk("qla2x00_issue_iocb(%ld): failed rval 0x%x",
ha->host_no,rval);)
DEBUG2(printk("qla2x00_issue_iocb(%ld): failed rval 0x%x",
ha->host_no,rval);)
} else {
/*EMPTY*/
LEAVE("qla2x00_issue_iocb: exiting normally");
}
return rval;
}
/*
* qla2x00_abort_command
* Abort command aborts a specified IOCB.
*
* Input:
* ha = adapter block pointer.
* sp = SB structure pointer.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_abort_command(scsi_qla_host_t *ha, srb_t *sp)
{
unsigned long flags = 0;
fc_port_t *fcport;
int rval;
uint32_t handle;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_abort_command(%ld): entered.\n", ha->host_no);)
fcport = sp->fclun->fcport;
if (atomic_read(&ha->loop_state) == LOOP_DOWN ||
atomic_read(&fcport->state) == FCS_DEVICE_LOST) {
/* v2.19.8 Ignore abort request if port is down */
return 1;
}
spin_lock_irqsave(&ha->hardware_lock, flags);
for (handle = 1; handle < MAX_OUTSTANDING_COMMANDS; handle++) {
if (ha->outstanding_cmds[handle] == sp)
break;
}
spin_unlock_irqrestore(&ha->hardware_lock, flags);
if (handle == MAX_OUTSTANDING_COMMANDS) {
/* command not found */
return QLA_FUNCTION_FAILED;
}
mcp->mb[0] = MBC_ABORT_COMMAND;
if (HAS_EXTENDED_IDS(ha))
mcp->mb[1] = fcport->loop_id;
else
mcp->mb[1] = fcport->loop_id << 8;
mcp->mb[2] = (uint16_t)handle;
mcp->mb[3] = (uint16_t)(handle >> 16);
mcp->mb[6] = (uint16_t)sp->fclun->lun;
mcp->out_mb = MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("qla2x00_abort_command(%ld): failed=%x.\n",
ha->host_no, rval);)
} else {
sp->flags |= SRB_ABORT_PENDING;
DEBUG11(printk("qla2x00_abort_command(%ld): done.\n",
ha->host_no);)
}
return rval;
}
/*
* qla2x00_abort_device
*
* Input:
* ha = adapter block pointer.
* loop_id = FC loop ID
* lun = SCSI LUN.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_abort_device(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_abort_device(%ld): entered.\n", ha->host_no);)
mcp->mb[0] = MBC_ABORT_DEVICE;
if (HAS_EXTENDED_IDS(ha))
mcp->mb[1] = loop_id;
else
mcp->mb[1] = loop_id << 8;
mcp->mb[2] = lun;
mcp->out_mb = MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
/* Issue marker command. */
qla2x00_marker(ha, loop_id, lun, MK_SYNC_ID_LUN);
if (rval != QLA_SUCCESS) {
set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
if (ha->dpc_wait && !ha->dpc_active)
up(ha->dpc_wait);
DEBUG2_3_11(printk("qla2x00_abort_device(%ld): failed=%x.\n",
ha->host_no, rval);)
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_abort_device(%ld): done.\n",
ha->host_no);)
}
return rval;
}
#if USE_ABORT_TGT
/*
* qla2x00_abort_target
* Issue abort target mailbox command.
*
* Input:
* ha = adapter block pointer.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_abort_target(fc_port_t *fcport)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_abort_target(%ld): entered.\n",
fcport->ha->host_no);)
if (fcport == NULL) {
/* no target to abort */
return 0;
}
mcp->mb[0] = MBC_ABORT_TARGET;
mcp->out_mb = MBX_2|MBX_1|MBX_0;
if (HAS_EXTENDED_IDS(fcport->ha)) {
mcp->mb[1] = fcport->loop_id;
mcp->mb[10] = 0;
mcp->out_mb |= MBX_10;
} else {
mcp->mb[1] = fcport->loop_id << 8;
}
mcp->mb[2] = fcport->ha->loop_reset_delay;
mcp->in_mb = MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(fcport->ha, mcp);
/* Issue marker command. */
fcport->ha->marker_needed = 1;
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("qla2x00_abort_target(%ld): failed=%x.\n",
fcport->ha->host_no, rval);)
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_abort_target(%ld): done.\n",
fcport->ha->host_no);)
}
return rval;
}
#endif
/*
* qla2x00_target_reset
* Issue target reset mailbox command.
*
* Input:
* ha = adapter block pointer.
* TARGET_QUEUE_LOCK must be released.
* ADAPTER_STATE_LOCK must be released.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_target_reset(scsi_qla_host_t *ha, uint16_t b, uint16_t t)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
os_tgt_t *tgt;
DEBUG11(printk("qla2x00_target_reset(%ld): entered.\n", ha->host_no);)
tgt = TGT_Q(ha, t);
if (tgt->fcport == NULL) {
/* no target to abort */
return 0;
}
if (atomic_read(&tgt->fcport->state) != FCS_ONLINE) {
/* target not online */
return 0;
}
mcp->mb[0] = MBC_TARGET_RESET;
if (HAS_EXTENDED_IDS(ha))
mcp->mb[1] = tgt->fcport->loop_id;
else
mcp->mb[1] = tgt->fcport->loop_id << 8;
mcp->mb[2] = ha->loop_reset_delay;
mcp->out_mb = MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_target_reset(%ld): failed=%x.\n",
ha->host_no, rval);)
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_target_reset(%ld): done.\n",
ha->host_no);)
}
return rval;
}
/*
* qla2x00_get_adapter_id
* Get adapter ID and topology.
*
* Input:
* ha = adapter block pointer.
* id = pointer for loop ID.
* al_pa = pointer for AL_PA.
* area = pointer for area.
* domain = pointer for domain.
* top = pointer for topology.
* TARGET_QUEUE_LOCK must be released.
* ADAPTER_STATE_LOCK must be released.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_get_adapter_id(scsi_qla_host_t *ha, uint16_t *id, uint8_t *al_pa,
uint8_t *area, uint8_t *domain, uint16_t *top)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_get_adapter_id(%ld): entered.\n",
ha->host_no);)
mcp->mb[0] = MBC_GET_ADAPTER_LOOP_ID;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
/* Return data. */
*id = mcp->mb[1];
*al_pa = LSB(mcp->mb[2]);
*area = MSB(mcp->mb[2]);
*domain = LSB(mcp->mb[3]);
*top = mcp->mb[6];
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_get_adapter_id(%ld): failed=%x.\n",
ha->host_no, rval);)
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_get_adapter_id(%ld): done.\n",
ha->host_no);)
}
return rval;
}
/*
* qla2x00_get_retry_cnt
* Get current firmware login retry count and delay.
*
* Input:
* ha = adapter block pointer.
* retry_cnt = pointer to login retry count.
* tov = pointer to login timeout value.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_get_retry_cnt(scsi_qla_host_t *ha, uint8_t *retry_cnt, uint8_t *tov,
uint16_t *r_a_tov)
{
int rval;
uint16_t ratov;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_get_retry_cnt(%ld): entered.\n",
ha->host_no);)
mcp->mb[0] = MBC_GET_RETRY_COUNT;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_get_retry_cnt(%ld): failed = %x.\n",
ha->host_no, mcp->mb[0]);)
} else {
/* Convert returned data and check our values. */
*r_a_tov = mcp->mb[3] / 2;
ratov = (mcp->mb[3]/2) / 10; /* mb[3] value is in 100ms */
if (mcp->mb[1] * ratov > (*retry_cnt) * (*tov)) {
/* Update to the larger values */
*retry_cnt = (uint8_t)mcp->mb[1];
*tov = ratov;
}
DEBUG11(printk("qla2x00_get_retry_cnt(%ld): done. mb3=%d "
"ratov=%d.\n", ha->host_no, mcp->mb[3], ratov);)
}
return rval;
}
/*
* qla2x00_init_firmware
* Initialize adapter firmware.
*
* Input:
* ha = adapter block pointer.
* dptr = Initialization control block pointer.
* size = size of initialization control block.
* TARGET_QUEUE_LOCK must be released.
* ADAPTER_STATE_LOCK must be released.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_init_firmware(scsi_qla_host_t *ha, uint16_t size)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_init_firmware(%ld): entered.\n",
ha->host_no);)
mcp->mb[0] = MBC_INITIALIZE_FIRMWARE;
mcp->mb[2] = MSW(ha->init_cb_dma);
mcp->mb[3] = LSW(ha->init_cb_dma);
mcp->mb[4] = 0;
mcp->mb[5] = 0;
mcp->mb[6] = MSW(MSD(ha->init_cb_dma));
mcp->mb[7] = LSW(MSD(ha->init_cb_dma));
mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_0;
mcp->in_mb = MBX_5|MBX_4|MBX_0;
mcp->buf_size = size;
mcp->flags = MBX_DMA_OUT;
mcp->tov = 30;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_init_firmware(%ld): failed=%x "
"mb0=%x.\n",
ha->host_no, rval, mcp->mb[0]);)
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_init_firmware(%ld): done.\n",
ha->host_no);)
}
return rval;
}
/*
* qla2x00_get_port_database
* Issue normal/enhanced get port database mailbox command
* and copy device name as necessary.
*
* Input:
* ha = adapter state pointer.
* dev = structure pointer.
* opt = enhanced cmd option byte.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
port_database_t *pd;
dma_addr_t pd_dma;
DEBUG11(printk("qla2x00_get_port_database(%ld): entered.\n",
ha->host_no);)
pd = pci_alloc_consistent(ha->pdev, PORT_DATABASE_SIZE, &pd_dma);
if (pd == NULL) {
DEBUG2_3_11(printk("qla2x00_get_port_database(%ld): **** "
"Mem Alloc Failed ****", ha->host_no);)
return QLA_MEMORY_ALLOC_FAILED;
}
memset(pd, 0, PORT_DATABASE_SIZE);
if (opt != 0)
mcp->mb[0] = MBC_ENHANCED_GET_PORT_DATABASE;
else
mcp->mb[0] = MBC_GET_PORT_DATABASE;
mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
if (HAS_EXTENDED_IDS(ha)) {
mcp->mb[1] = fcport->loop_id;
mcp->mb[10] = opt;
mcp->out_mb |= MBX_10;
} else {
mcp->mb[1] = fcport->loop_id << 8 | opt;
}
mcp->mb[2] = MSW(pd_dma);
mcp->mb[3] = LSW(pd_dma);
mcp->mb[6] = MSW(MSD(pd_dma));
mcp->mb[7] = LSW(MSD(pd_dma));
mcp->in_mb = MBX_0;
mcp->buf_size = PORT_DATABASE_SIZE;
mcp->flags = MBX_DMA_IN;
mcp->tov = ha->login_timeout * 2;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval == QLA_SUCCESS) {
/* Names are little-endian. */
memcpy(fcport->node_name, pd->node_name, WWN_SIZE);
memcpy(fcport->port_name, pd->port_name, WWN_SIZE);
/* Get port_id of device. */
fcport->d_id.b.al_pa = pd->port_id[2];
fcport->d_id.b.area = pd->port_id[3];
fcport->d_id.b.domain = pd->port_id[0];
fcport->d_id.b.rsvd_1 = 0;
/* Check for device require authentication. */
pd->common_features & BIT_5 ? (fcport->flags |= FCF_AUTH_REQ) :
(fcport->flags &= ~FCF_AUTH_REQ);
/* If not target must be initiator or unknown type. */
if ((pd->prli_svc_param_word_3[0] & BIT_4) == 0) {
fcport->port_type = FCT_INITIATOR;
} else {
fcport->port_type = FCT_TARGET;
/* Check for logged in. */
if (pd->master_state != PD_STATE_PORT_LOGGED_IN &&
pd->slave_state != PD_STATE_PORT_LOGGED_IN)
rval = QLA_FUNCTION_FAILED;
}
}
pci_free_consistent(ha->pdev, PORT_DATABASE_SIZE, pd, pd_dma);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_get_port_database(%ld): "
"failed=%x.\n", ha->host_no, rval);)
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_get_port_database(%ld): done.\n",
ha->host_no);)
}
return rval;
}
/*
* qla2x00_get_firmware_state
* Get adapter firmware state.
*
* Input:
* ha = adapter block pointer.
* dptr = pointer for firmware state.
* TARGET_QUEUE_LOCK must be released.
* ADAPTER_STATE_LOCK must be released.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_get_firmware_state(scsi_qla_host_t *ha, uint16_t *dptr)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_get_firmware_state(%ld): entered.\n",
ha->host_no);)
mcp->mb[0] = MBC_GET_FIRMWARE_STATE;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_2|MBX_1|MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
/* Return firmware state. */
*dptr = mcp->mb[1];
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_get_firmware_state(%ld): "
"failed=%x.\n", ha->host_no, rval);)
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_get_firmware_state(%ld): done.\n",
ha->host_no);)
}
return rval;
}
/*
* qla2x00_get_port_name
* Issue get port name mailbox command.
* Returned name is in big endian format.
*
* Input:
* ha = adapter block pointer.
* loop_id = loop ID of device.
* name = pointer for name.
* TARGET_QUEUE_LOCK must be released.
* ADAPTER_STATE_LOCK must be released.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_get_port_name(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t *name,
uint8_t opt)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_get_port_name(%ld): entered.\n",
ha->host_no);)
mcp->mb[0] = MBC_GET_PORT_NAME;
mcp->out_mb = MBX_1|MBX_0;
if (HAS_EXTENDED_IDS(ha)) {
mcp->mb[1] = loop_id;
mcp->mb[10] = opt;
mcp->out_mb |= MBX_10;
} else {
mcp->mb[1] = loop_id << 8 | opt;
}
mcp->in_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_get_port_name(%ld): failed=%x.\n",
ha->host_no, rval);)
} else {
if (name != NULL) {
/* This function returns name in big endian. */
name[0] = LSB(mcp->mb[2]);
name[1] = MSB(mcp->mb[2]);
name[2] = LSB(mcp->mb[3]);
name[3] = MSB(mcp->mb[3]);
name[4] = LSB(mcp->mb[6]);
name[5] = MSB(mcp->mb[6]);
name[6] = LSB(mcp->mb[7]);
name[7] = MSB(mcp->mb[7]);
}
DEBUG11(printk("qla2x00_get_port_name(%ld): done.\n",
ha->host_no);)
}
return rval;
}
/*
* qla2x00_get_link_status
*
* Input:
* ha = adapter block pointer.
* loop_id = device loop ID.
* ret_buf = pointer to link status return buffer.
*
* Returns:
* 0 = success.
* BIT_0 = mem alloc error.
* BIT_1 = mailbox error.
*/
uint8_t
qla2x00_get_link_status(scsi_qla_host_t *ha, uint8_t loop_id, void *ret_buf,
uint16_t *status)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
link_stat_t *stat_buf;
dma_addr_t phys_address = 0;
DEBUG11(printk("qla2x00_get_link_status(%ld): entered.\n",
ha->host_no);)
stat_buf = pci_alloc_consistent(ha->pdev, sizeof(link_stat_t),
&phys_address);
if (stat_buf == NULL) {
DEBUG2_3_11(printk("qla2x00_get_link_status(%ld): Failed to "
"allocate memory.\n", ha->host_no));
return BIT_0;
}
memset(stat_buf, 0, sizeof(link_stat_t));
mcp->mb[0] = MBC_GET_LINK_STATUS;
mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
if (HAS_EXTENDED_IDS(ha)) {
mcp->mb[1] = loop_id;
mcp->mb[10] = 0;
mcp->out_mb |= MBX_10;
} else {
mcp->mb[1] = loop_id << 8;
}
mcp->mb[2] = MSW(phys_address);
mcp->mb[3] = LSW(phys_address);
mcp->mb[6] = MSW(MSD(phys_address));
mcp->mb[7] = LSW(MSD(phys_address));
mcp->in_mb = MBX_0;
mcp->tov = 30;
mcp->flags = IOCTL_CMD;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval == QLA_SUCCESS) {
if (mcp->mb[0] != MBS_COMMAND_COMPLETE) {
DEBUG2_3_11(printk("qla2x00_get_link_status(%ld): cmd "
"failed. mbx0=%x.\n", ha->host_no, mcp->mb[0]);)
status[0] = mcp->mb[0];
rval = BIT_1;
} else {
/* copy over data */
memcpy(ret_buf, stat_buf,sizeof(link_stat_t));
DEBUG(printk("qla2x00_get_link_status(%ld): stat dump: "
"fail_cnt=%d loss_sync=%d loss_sig=%d seq_err=%d "
"inval_xmt_word=%d inval_crc=%d.\n",
ha->host_no,
stat_buf->link_fail_cnt, stat_buf->loss_sync_cnt,
stat_buf->loss_sig_cnt, stat_buf->prim_seq_err_cnt,
stat_buf->inval_xmit_word_cnt,
stat_buf->inval_crc_cnt);)
DEBUG11(printk("qla2x00_get_link_status(%ld): stat "
"dump: fail_cnt=%d loss_sync=%d loss_sig=%d "
"seq_err=%d inval_xmt_word=%d inval_crc=%d.\n",
ha->host_no,
stat_buf->link_fail_cnt, stat_buf->loss_sync_cnt,
stat_buf->loss_sig_cnt, stat_buf->prim_seq_err_cnt,
stat_buf->inval_xmit_word_cnt,
stat_buf->inval_crc_cnt);)
}
} else {
/* Failed. */
DEBUG2_3_11(printk("qla2x00_get_link_status(%ld): failed=%x.\n",
ha->host_no, rval);)
rval = BIT_1;
}
pci_free_consistent(ha->pdev, sizeof(link_stat_t),
stat_buf, phys_address);
return rval;
}
/*
* qla2x00_lip_reset
* Issue LIP reset mailbox command.
*
* Input:
* ha = adapter block pointer.
* TARGET_QUEUE_LOCK must be released.
* ADAPTER_STATE_LOCK must be released.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_lip_reset(scsi_qla_host_t *ha)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_lip_reset(%ld): entered.\n",
ha->host_no);)
mcp->mb[0] = MBC_LIP_RESET;
mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
if (HAS_EXTENDED_IDS(ha)) {
mcp->mb[1] = 0x00ff;
mcp->mb[10] = 0;
mcp->out_mb |= MBX_10;
} else {
mcp->mb[1] = 0xff00;
}
mcp->mb[2] = ha->loop_reset_delay;
mcp->mb[3] = 0;
mcp->in_mb = MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_lip_reset(%ld): failed=%x.\n",
ha->host_no, rval);)
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_lip_reset(%ld): done.\n", ha->host_no);)
}
return rval;
}
/*
* qla2x00_send_sns
* Send SNS command.
*
* Input:
* ha = adapter block pointer.
* sns = pointer for command.
* cmd_size = command size.
* buf_size = response/command size.
* TARGET_QUEUE_LOCK must be released.
* ADAPTER_STATE_LOCK must be released.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_send_sns(scsi_qla_host_t *ha, dma_addr_t sns_phys_address,
uint16_t cmd_size, size_t buf_size)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_send_sns(%ld): entered.\n",
ha->host_no);)
DEBUG11(printk("qla2x00_send_sns: retry cnt=%d ratov=%d total "
"tov=%d.\n", ha->retry_count, ha->login_timeout, mcp->tov);)
mcp->mb[0] = MBC_SEND_SNS_COMMAND;
mcp->mb[1] = cmd_size;
mcp->mb[2] = MSW(sns_phys_address);
mcp->mb[3] = LSW(sns_phys_address);
mcp->mb[6] = MSW(MSD(sns_phys_address));
mcp->mb[7] = LSW(MSD(sns_phys_address));
mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_0|MBX_1;
mcp->buf_size = buf_size;
mcp->flags = MBX_DMA_OUT|MBX_DMA_IN;
/*mcp->tov = ha->retry_count * ha->login_timeout * 2;*/
mcp->tov = ha->login_timeout * 2;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG(printk("qla2x00_send_sns(%ld): failed=%x mb[0]=%x "
"mb[1]=%x.\n", ha->host_no, rval, mcp->mb[0], mcp->mb[1]);)
DEBUG2_3_11(printk("qla2x00_send_sns(%ld): failed=%x mb[0]=%x "
"mb[1]=%x.\n", ha->host_no, rval, mcp->mb[0], mcp->mb[1]);)
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_send_sns(%ld): done.\n", ha->host_no);)
}
return rval;
}
/*
* qla2x00_login_fabric
* Issue login fabric port mailbox command.
*
* Input:
* ha = adapter block pointer.
* loop_id = device loop ID.
* domain = device domain.
* area = device area.
* al_pa = device AL_PA.
* status = pointer for return status.
* opt = command options.
* TARGET_QUEUE_LOCK must be released.
* ADAPTER_STATE_LOCK must be released.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
uint8_t area, uint8_t al_pa, uint16_t *mb, uint8_t opt)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_login_fabric(%ld): entered.\n", ha->host_no);)
mcp->mb[0] = MBC_LOGIN_FABRIC_PORT;
mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
if (HAS_EXTENDED_IDS(ha)) {
mcp->mb[1] = loop_id;
mcp->mb[10] = opt;
mcp->out_mb |= MBX_10;
} else {
mcp->mb[1] = (loop_id << 8) | opt;
}
mcp->mb[2] = domain;
mcp->mb[3] = area << 8 | al_pa;
mcp->in_mb = MBX_7|MBX_6|MBX_2|MBX_1|MBX_0;
/*mcp->tov = ha->retry_count * ha->login_timeout * 2;*/
mcp->tov = ha->login_timeout * 2;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
/* Return mailbox statuses. */
if (mb != NULL) {
mb[0] = mcp->mb[0];
mb[1] = mcp->mb[1];
mb[2] = mcp->mb[2];
mb[6] = mcp->mb[6];
mb[7] = mcp->mb[7];
}
if (rval != QLA_SUCCESS) {
/* RLU tmp code: need to change main mailbox_command function to
* return ok even when the mailbox completion value is not
* SUCCESS. The caller needs to be responsible to interpret
* the return values of this mailbox command if we're not
* to change too much of the existing code.
*/
if (mcp->mb[0] == 0x4001 || mcp->mb[0] == 0x4002 ||
mcp->mb[0] == 0x4003 || mcp->mb[0] == 0x4005 ||
mcp->mb[0] == 0x4006)
rval = QLA_SUCCESS;
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_login_fabric(%ld): failed=%x "
"mb[0]=%x mb[1]=%x mb[2]=%x.\n", ha->host_no, rval,
mcp->mb[0], mcp->mb[1], mcp->mb[2]);)
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_login_fabric(%ld): done.\n",
ha->host_no);)
}
return rval;
}
/*
* qla2x00_login_local_device
* Issue login loop port mailbox command.
*
* Input:
* ha = adapter block pointer.
* loop_id = device loop ID.
* opt = command options.
*
* Returns:
* Return status code.
*
* Context:
* Kernel context.
*
*/
int
qla2x00_login_local_device(scsi_qla_host_t *ha, uint16_t loop_id,
uint16_t *mb_ret, uint8_t opt)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG3(printk("%s(%ld): entered.\n", __func__, ha->host_no);)
mcp->mb[0] = MBC_LOGIN_LOOP_PORT;
if (HAS_EXTENDED_IDS(ha))
mcp->mb[1] = loop_id;
else
mcp->mb[1] = loop_id << 8;
mcp->mb[2] = opt;
mcp->out_mb = MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_7|MBX_6|MBX_1|MBX_0;
mcp->tov = ha->login_timeout * 2;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
/* Return mailbox statuses. */
if (mb_ret != NULL) {
mb_ret[0] = mcp->mb[0];
mb_ret[1] = mcp->mb[1];
mb_ret[6] = mcp->mb[6];
mb_ret[7] = mcp->mb[7];
}
if (rval != QLA_SUCCESS) {
/* AV tmp code: need to change main mailbox_command function to
* return ok even when the mailbox completion value is not
* SUCCESS. The caller needs to be responsible to interpret
* the return values of this mailbox command if we're not
* to change too much of the existing code.
*/
if (mcp->mb[0] == 0x4005 || mcp->mb[0] == 0x4006)
rval = QLA_SUCCESS;
DEBUG(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x "
"mb[6]=%x mb[7]=%x.\n", __func__, ha->host_no, rval,
mcp->mb[0], mcp->mb[1], mcp->mb[6], mcp->mb[7]);)
DEBUG2_3(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x "
"mb[6]=%x mb[7]=%x.\n", __func__, ha->host_no, rval,
mcp->mb[0], mcp->mb[1], mcp->mb[6], mcp->mb[7]);)
} else {
/*EMPTY*/
DEBUG3(printk("%s(%ld): done.\n", __func__, ha->host_no);)
}
return (rval);
}
/*
* qla2x00_fabric_logout
* Issue logout fabric port mailbox command.
*
* Input:
* ha = adapter block pointer.
* loop_id = device loop ID.
* TARGET_QUEUE_LOCK must be released.
* ADAPTER_STATE_LOCK must be released.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_fabric_logout(%ld): entered.\n",
ha->host_no);)
mcp->mb[0] = MBC_LOGOUT_FABRIC_PORT;
mcp->out_mb = MBX_1|MBX_0;
if (HAS_EXTENDED_IDS(ha)) {
mcp->mb[1] = loop_id;
mcp->mb[10] = 0;
mcp->out_mb |= MBX_10;
} else {
mcp->mb[1] = loop_id << 8;
}
mcp->in_mb = MBX_1|MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_fabric_logout(%ld): failed=%x "
"mbx1=%x.\n", ha->host_no, rval, mcp->mb[1]);)
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_fabric_logout(%ld): done.\n",
ha->host_no);)
}
return rval;
}
/*
* qla2x00_full_login_lip
* Issue full login LIP mailbox command.
*
* Input:
* ha = adapter block pointer.
* TARGET_QUEUE_LOCK must be released.
* ADAPTER_STATE_LOCK must be released.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_full_login_lip(scsi_qla_host_t *ha)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_full_login_lip(%ld): entered.\n",
ha->host_no);)
mcp->mb[0] = MBC_LIP_FULL_LOGIN;
mcp->mb[1] = 0;
mcp->mb[2] = 0;
mcp->mb[3] = 0;
mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_full_login_lip(%ld): failed=%x.\n",
ha->instance, rval);)
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_full_login_lip(%ld): done.\n",
ha->host_no);)
}
return rval;
}
/*
* qla2x00_get_id_list
*
* Input:
* ha = adapter block pointer.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_get_id_list(scsi_qla_host_t *ha, void *id_list, dma_addr_t id_list_dma,
uint16_t *entries)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_get_id_list(%ld): entered.\n",
ha->host_no);)
if (id_list == NULL)
return QLA_FUNCTION_FAILED;
mcp->mb[0] = MBC_GET_ID_LIST;
mcp->mb[1] = MSW(id_list_dma);
mcp->mb[2] = LSW(id_list_dma);
mcp->mb[3] = MSW(MSD(id_list_dma));
mcp->mb[6] = LSW(MSD(id_list_dma));
mcp->out_mb = MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_1|MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_get_id_list(%ld): failed=%x.\n",
ha->host_no, rval);)
} else {
*entries = mcp->mb[1];
DEBUG11(printk("qla2x00_get_id_list(%ld): done.\n",
ha->host_no);)
}
return rval;
}
#if 0 /* not yet needed */
int
qla2x00_dump_ram(scsi_qla_host_t *ha, uint32_t risc_address,
dma_addr_t ispdump_dma, uint32_t size)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
mcp->mb[0] = MBC_DUMP_RAM;
mcp->mb[1] = risc_address & 0xffff;
mcp->mb[2] = MSW(ispdump_dma);
mcp->mb[3] = LSW(ispdump_dma);
mcp->mb[4] = 0;
mcp->mb[6] = MSW(MSD(ispdump_dma));
mcp->mb[7] = LSW(MSD(ispdump_dma));
mcp->out_mb = MBX_7|MBX_6|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
return rval;
}
#endif
/*
* qla2x00_lun_reset
* Issue lun reset mailbox command.
*
* Input:
* ha = adapter block pointer.
* loop_id = device loop ID.
* lun = lun to be reset.
* TARGET_QUEUE_LOCK must be released.
* ADAPTER_STATE_LOCK must be released.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_lun_reset(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
ENTER("qla2x00_lun_reset");
mcp->mb[0] = MBC_LUN_RESET;
if (HAS_EXTENDED_IDS(ha))
mcp->mb[1] = loop_id;
else
mcp->mb[1] = loop_id << 8;
mcp->mb[2] = lun;
mcp->out_mb = MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
printk(KERN_WARNING "qla2x00_lun_reset(%d): failed = %d",
(int)ha->instance, rval);
} else {
/*EMPTY*/
LEAVE("qla2x00_lun_reset: exiting normally");
}
return rval;
}
/*
* qla2x00_send_rnid_mbx
* Issue RNID ELS using mailbox command
*
* Input:
* ha = adapter state pointer.
* loop_id = loop ID of the target device.
* data_fmt = currently supports only 0xDF.
* buffer = buffer pointer.
* buf_size = size of buffer.
* mb_reg = pointer to return mailbox registers.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_send_rnid_mbx(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t data_fmt,
dma_addr_t buf_phys_addr, size_t buf_size, uint16_t *mb_reg)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_send_rnid_mbx(%ld): entered.\n",
ha->host_no);)
mcp->mb[0] = MBC_SEND_RNID_ELS;
mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
if (HAS_EXTENDED_IDS(ha)) {
mcp->mb[1] = loop_id;
mcp->mb[10] = data_fmt;
mcp->out_mb |= MBX_10;
} else {
mcp->mb[1] = (loop_id << 8) | data_fmt;
}
mcp->mb[2] = MSW(buf_phys_addr);
mcp->mb[3] = LSW(buf_phys_addr);
mcp->mb[6] = MSW(MSD(buf_phys_addr));
mcp->mb[7] = LSW(MSD(buf_phys_addr));
mcp->in_mb = MBX_1|MBX_0;
mcp->buf_size = buf_size;
mcp->flags = MBX_DMA_IN;
mcp->tov = 30;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval != QLA_SUCCESS) {
memcpy(mb_reg, mcp->mb, 2 * 2); /* 2 status regs */
DEBUG2_3_11(printk("qla2x00_send_rnid_mbx(%ld): failed=%x "
"mb[1]=%x.\n",
ha->host_no, mcp->mb[0], mcp->mb[1]);)
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_send_rnid_mbx(%ld): done.\n",
ha->host_no);)
}
return (rval);
}
/*
* qla2x00_set_rnid_params_mbx
* Set RNID parameters using mailbox command
*
* Input:
* ha = adapter state pointer.
* buffer = buffer pointer.
* buf_size = size of buffer.
* mb_reg = pointer to return mailbox registers.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_set_rnid_params_mbx(scsi_qla_host_t *ha, dma_addr_t buf_phys_addr,
size_t buf_size, uint16_t *mb_reg)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_set_rnid_params_mbx(%ld): entered.\n",
ha->host_no);)
mcp->mb[0] = MBC_SET_RNID_PARAMS;
mcp->mb[1] = 0;
mcp->mb[2] = MSW(buf_phys_addr);
mcp->mb[3] = LSW(buf_phys_addr);
mcp->mb[6] = MSW(MSD(buf_phys_addr));
mcp->mb[7] = LSW(MSD(buf_phys_addr));
mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_1|MBX_0;
mcp->buf_size = buf_size;
mcp->flags = MBX_DMA_OUT;
mcp->tov = 30;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval != QLA_SUCCESS) {
memcpy(mb_reg, mcp->mb, 2 * 2); /* 2 status regs */
DEBUG2_3_11(printk("qla2x00_set_rnid_params_mbx(%ld): "
"failed=%x mb[1]=%x.\n", ha->host_no, mcp->mb[0],
mcp->mb[1]);)
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_set_rnid_params_mbx(%ld): done.\n",
ha->host_no);)
}
return (rval);
}
/*
* qla2x00_get_rnid_params_mbx
* Get RNID parameters using mailbox command
*
* Input:
* ha = adapter state pointer.
* buffer = buffer pointer.
* buf_size = size of buffer.
* mb_reg = pointer to return mailbox registers.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_get_rnid_params_mbx(scsi_qla_host_t *ha, dma_addr_t buf_phys_addr,
size_t buf_size, uint16_t *mb_reg)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_get_rnid_params_mbx(%ld): entered.\n",
ha->host_no);)
mcp->mb[0] = MBC_GET_RNID_PARAMS;
mcp->mb[1] = 0;
mcp->mb[2] = MSW(buf_phys_addr);
mcp->mb[3] = LSW(buf_phys_addr);
mcp->mb[6] = MSW(MSD(buf_phys_addr));
mcp->mb[7] = LSW(MSD(buf_phys_addr));
mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_1|MBX_0;
mcp->buf_size = buf_size;
mcp->flags = MBX_DMA_IN;
mcp->tov = 30;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval != QLA_SUCCESS) {
memcpy(mb_reg, mcp->mb, 2 * 2); /* 2 status regs */
DEBUG2_3_11(printk("qla2x00_get_rnid_params_mbx(%ld): "
"failed=%x mb[1]=%x.\n", ha->host_no, mcp->mb[0],
mcp->mb[1]);)
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_get_rnid_params_mbx(%ld): done.\n",
ha->host_no);)
}
return (rval);
}
/*
* qla2x00_get_resource_cnts
* Get current firmware resource counts.
*
* Input:
* ha = adapter block pointer.
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_get_resource_cnts(scsi_qla_host_t *ha, uint16_t *cur_xchg_cnt,
uint16_t *orig_xchg_cnt, uint16_t *cur_iocb_cnt, uint16_t *orig_iocb_cnt)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
mcp->mb[0] = MBC_GET_RESOURCE_COUNTS;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_10|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->tov = 30;
mcp->flags = 0;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("%s(%ld): failed = %x.\n", __func__,
ha->host_no, mcp->mb[0]);)
} else {
DEBUG11(printk("%s(%ld): done. mb1=%x mb2=%x mb3=%x mb6=%x "
"mb7=%x mb10=%x.\n", __func__, ha->host_no,
mcp->mb[1], mcp->mb[2], mcp->mb[3], mcp->mb[6], mcp->mb[7],
mcp->mb[10]));
if (cur_xchg_cnt)
*cur_xchg_cnt = mcp->mb[3];
if (orig_xchg_cnt)
*orig_xchg_cnt = mcp->mb[6];
if (cur_iocb_cnt)
*cur_iocb_cnt = mcp->mb[7];
if (orig_iocb_cnt)
*orig_iocb_cnt = mcp->mb[10];
}
return (rval);
}
#if defined(QL_DEBUG_LEVEL_3)
/*
* qla2x00_get_fcal_position_map
* Get FCAL (LILP) position map using mailbox command
*
* Input:
* ha = adapter state pointer.
* pos_map = buffer pointer (can be NULL).
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int
qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
char *pmap;
dma_addr_t pmap_dma;
pmap = pci_alloc_consistent(ha->pdev, FCAL_MAP_SIZE, &pmap_dma);
if (pmap == NULL) {
DEBUG2_3_11(printk("%s(%ld): **** Mem Alloc Failed ****",
__func__, ha->host_no));
return QLA_MEMORY_ALLOC_FAILED;
}
memset(pmap, 0, FCAL_MAP_SIZE);
mcp->mb[0] = MBC_GET_FC_AL_POSITION_MAP;
mcp->mb[2] = MSW(pmap_dma);
mcp->mb[3] = LSW(pmap_dma);
mcp->mb[6] = MSW(MSD(pmap_dma));
mcp->mb[7] = LSW(MSD(pmap_dma));
mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_0;
mcp->in_mb = MBX_1|MBX_0;
mcp->buf_size = FCAL_MAP_SIZE;
mcp->flags = MBX_DMA_IN;
mcp->tov = ha->login_timeout * 2;
rval = qla2x00_mailbox_command(ha, mcp);
if (rval == QLA_SUCCESS) {
DEBUG11(printk("%s(%ld): (mb0=%x/mb1=%x) FC/AL Position Map "
"size (%x)\n", __func__, ha->host_no, mcp->mb[0],
mcp->mb[1], (unsigned)pmap[0]));
DEBUG11(qla2x00_dump_buffer(pmap, pmap[0] + 1));
if (pos_map)
memcpy(pos_map, pmap, FCAL_MAP_SIZE);
}
pci_free_consistent(ha->pdev, FCAL_MAP_SIZE, pmap, pmap_dma);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
ha->host_no, rval));
} else {
DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
}
return rval;
}
#endif
This source diff could not be displayed because it is too large. You can view the blob instead.
/******************************************************************************
* QLOGIC LINUX SOFTWARE
*
* QLogic ISP2x00 device driver for Linux 2.6.x
* Copyright (C) 2003 QLogic Corporation
* (www.qlogic.com)
*
* Portions (C) Arjan van de Ven <arjanv@redhat.com> for Red Hat, 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; 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 __QLA_OS_H
#define __QLA_OS_H
#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/sched.h>
#include <linux/pci.h>
#include <linux/proc_fs.h>
#include <linux/blkdev.h>
#include <linux/interrupt.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/mempool.h>
#include <linux/vmalloc.h>
#define __KERNEL_SYSCALLS__
#include <linux/unistd.h>
#include <linux/smp_lock.h>
#include <linux/bio.h>
#include <linux/moduleparam.h>
#include <linux/capability.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/segment.h>
#include <asm/byteorder.h>
#include <asm/pgtable.h>
#include <linux/ioctl.h>
#include <asm/uaccess.h>
#include "scsi.h"
#include "hosts.h"
#include <scsi/scsicam.h>
#include <scsi/scsi_ioctl.h>
//TODO Fix this!!!
/*
* String arrays
*/
#define LINESIZE 256
#define MAXARGS 26
/***********************************************************************
* We use the struct scsi_pointer structure that's included with each
* command SCSI_Cmnd as a scratchpad.
*
* SCp is defined as follows:
* - SCp.ptr -- > pointer to the SRB
* - SCp.this_residual -- > HBA completion status for ioctl code.
*
* Cmnd->host_scribble --> Used to hold the hba actived handle (1..255).
***********************************************************************/
#define CMD_SP(Cmnd) ((Cmnd)->SCp.ptr)
#define CMD_COMPL_STATUS(Cmnd) ((Cmnd)->SCp.this_residual)
/* Additional fields used by ioctl passthru */
#define CMD_RESID_LEN(Cmnd) ((Cmnd)->SCp.buffers_residual)
#define CMD_SCSI_STATUS(Cmnd) ((Cmnd)->SCp.Status)
#define CMD_ACTUAL_SNSLEN(Cmnd) ((Cmnd)->SCp.Message)
#define CMD_ENTRY_STATUS(Cmnd) ((Cmnd)->SCp.have_data_in)
#endif
/*
* QLOGIC LINUX SOFTWARE
*
* QLogic ISP2x00 device driver for Linux 2.6.x
* Copyright (C) 2003 QLogic Corporation
* (www.qlogic.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.
*
*/
#include "qla_os.h"
#include "qla_def.h"
/**
* IO descriptor handle definitions.
*
* Signature form:
*
* |31------28|27-------------------12|11-------0|
* | Type | Rolling Signature | Index |
* |----------|-----------------------|----------|
*
**/
#define HDL_TYPE_SCSI 0
#define HDL_TYPE_ASYNC_IOCB 0x0A
#define HDL_INDEX_BITS 12
#define HDL_ITER_BITS 16
#define HDL_TYPE_BITS 4
#define HDL_INDEX_MASK ((1UL << HDL_INDEX_BITS) - 1)
#define HDL_ITER_MASK ((1UL << HDL_ITER_BITS) - 1)
#define HDL_TYPE_MASK ((1UL << HDL_TYPE_BITS) - 1)
#define HDL_INDEX_SHIFT 0
#define HDL_ITER_SHIFT (HDL_INDEX_SHIFT + HDL_INDEX_BITS)
#define HDL_TYPE_SHIFT (HDL_ITER_SHIFT + HDL_ITER_BITS)
/* Local Prototypes. */
static inline uint32_t qla2x00_to_handle(uint16_t, uint16_t, uint16_t);
static inline uint16_t qla2x00_handle_to_idx(uint32_t);
static inline uint16_t qla2x00_handle_to_iter(uint32_t);
static inline uint16_t qla2x00_handle_to_type(uint32_t);
static inline uint32_t qla2x00_iodesc_to_handle(struct io_descriptor *);
static inline struct io_descriptor *qla2x00_handle_to_iodesc(scsi_qla_host_t *,
uint32_t);
static inline struct io_descriptor *qla2x00_alloc_iodesc(scsi_qla_host_t *);
static inline void qla2x00_free_iodesc(struct io_descriptor *);
static inline void qla2x00_init_io_descriptors(scsi_qla_host_t *);
static void qla2x00_iodesc_timeout(unsigned long);
static inline void qla2x00_add_iodesc_timer(struct io_descriptor *);
static inline void qla2x00_remove_iodesc_timer(struct io_descriptor *);
static inline void qla2x00_update_login_fcport(scsi_qla_host_t *,
struct mbx_entry *, fc_port_t *);
static int qla2x00_send_abort_iocb(scsi_qla_host_t *, struct io_descriptor *,
uint32_t, int);
static int qla2x00_send_abort_iocb_cb(scsi_qla_host_t *, struct io_descriptor *,
struct mbx_entry *);
static int qla2x00_send_adisc_iocb(scsi_qla_host_t *, struct io_descriptor *,
int);
static int qla2x00_send_adisc_iocb_cb(scsi_qla_host_t *, struct io_descriptor *,
struct mbx_entry *);
static int qla2x00_send_logout_iocb(scsi_qla_host_t *, struct io_descriptor *,
int);
static int qla2x00_send_logout_iocb_cb(scsi_qla_host_t *,
struct io_descriptor *, struct mbx_entry *);
static int qla2x00_send_login_iocb(scsi_qla_host_t *, struct io_descriptor *,
port_id_t *, int);
static int qla2x00_send_login_iocb_cb(scsi_qla_host_t *, struct io_descriptor *,
struct mbx_entry *);
/**
* Mailbox IOCB callback array.
**/
int (*iocb_function_cb_list[LAST_IOCB_CB])
(scsi_qla_host_t *, struct io_descriptor *, struct mbx_entry *) = {
qla2x00_send_abort_iocb_cb,
qla2x00_send_adisc_iocb_cb,
qla2x00_send_logout_iocb_cb,
qla2x00_send_login_iocb_cb,
};
/**
* Generic IO descriptor handle routines.
**/
/**
* qla2x00_to_handle() - Create a descriptor handle.
* @type: descriptor type
* @iter: descriptor rolling signature
* @idx: index to the descriptor array
*
* Returns a composite handle based in the @type, @iter, and @idx.
*/
static inline uint32_t
qla2x00_to_handle(uint16_t type, uint16_t iter, uint16_t idx)
{
return ((uint32_t)(((uint32_t)type << HDL_TYPE_SHIFT) |
((uint32_t)iter << HDL_ITER_SHIFT) |
((uint32_t)idx << HDL_INDEX_SHIFT)));
}
/**
* qla2x00_handle_to_idx() - Retrive the index for a given handle.
* @handle: descriptor handle
*
* Returns the index specified by the @handle.
*/
static inline uint16_t
qla2x00_handle_to_idx(uint32_t handle)
{
return ((uint16_t)(((handle) >> HDL_INDEX_SHIFT) & HDL_INDEX_MASK));
}
/**
* qla2x00_handle_to_type() - Retrive the descriptor type for a given handle.
* @handle: descriptor handle
*
* Returns the descriptor type specified by the @handle.
*/
static inline uint16_t
qla2x00_handle_to_type(uint32_t handle)
{
return ((uint16_t)(((handle) >> HDL_TYPE_SHIFT) & HDL_TYPE_MASK));
}
/**
* qla2x00_handle_to_iter() - Retrive the rolling signature for a given handle.
* @handle: descriptor handle
*
* Returns the signature specified by the @handle.
*/
static inline uint16_t
qla2x00_handle_to_iter(uint32_t handle)
{
return ((uint16_t)(((handle) >> HDL_ITER_SHIFT) & HDL_ITER_MASK));
}
/**
* qla2x00_iodesc_to_handle() - Convert an IO descriptor to a unique handle.
* @iodesc: io descriptor
*
* Returns a unique handle for @iodesc.
*/
static inline uint32_t
qla2x00_iodesc_to_handle(struct io_descriptor *iodesc)
{
uint32_t handle;
handle = qla2x00_to_handle(HDL_TYPE_ASYNC_IOCB,
++iodesc->ha->iodesc_signature, iodesc->idx);
iodesc->signature = handle;
return (handle);
}
/**
* qla2x00_handle_to_iodesc() - Retrieve an IO descriptor given a unique handle.
* @ha: HA context
* @handle: handle to io descriptor
*
* Returns a pointer to the io descriptor, or NULL, if the io descriptor does
* not exist or the io descriptors signature does not @handle.
*/
static inline struct io_descriptor *
qla2x00_handle_to_iodesc(scsi_qla_host_t *ha, uint32_t handle)
{
uint16_t idx;
struct io_descriptor *iodesc;
idx = qla2x00_handle_to_idx(handle);
iodesc = &ha->io_descriptors[idx];
if (iodesc)
if (iodesc->signature != handle)
iodesc = NULL;
return (iodesc);
}
/**
* IO descriptor allocation routines.
**/
/**
* qla2x00_alloc_iodesc() - Allocate an IO descriptor from the pool.
* @ha: HA context
*
* Returns a pointer to the allocated io descriptor, or NULL, if none available.
*/
static inline struct io_descriptor *
qla2x00_alloc_iodesc(scsi_qla_host_t *ha)
{
uint16_t iter;
struct io_descriptor *iodesc;
iodesc = NULL;
for (iter = 0; iter < MAX_IO_DESCRIPTORS; iter++) {
if (ha->io_descriptors[iter].used)
continue;
iodesc = &ha->io_descriptors[iter];
iodesc->used = 1;
iodesc->idx = iter;
init_timer(&iodesc->timer);
iodesc->ha = ha;
iodesc->signature = qla2x00_iodesc_to_handle(iodesc);
break;
}
return (iodesc);
}
/**
* qla2x00_free_iodesc() - Free an IO descriptor.
* @iodesc: io descriptor
*
* NOTE: The io descriptors timer *must* be stopped before it can be free'd.
*/
static inline void
qla2x00_free_iodesc(struct io_descriptor *iodesc)
{
iodesc->used = 0;
iodesc->signature = 0;
}
/**
* qla2x00_init_io_descriptors() - Initialize the pool of IO descriptors.
* @ha: HA context
*/
static inline void
qla2x00_init_io_descriptors(scsi_qla_host_t *ha)
{
uint16_t iter;
for (iter = 0; iter < MAX_IO_DESCRIPTORS; iter++) {
if (!ha->io_descriptors[iter].used)
continue;
qla2x00_remove_iodesc_timer(&ha->io_descriptors[iter]);
qla2x00_free_iodesc(&ha->io_descriptors[iter]);
}
}
/**
* IO descriptor timer routines.
**/
/**
* qla2x00_iodesc_timeout() - Timeout IO descriptor handler.
* @data: io descriptor
*/
static void
qla2x00_iodesc_timeout(unsigned long data)
{
struct io_descriptor *iodesc;
iodesc = (struct io_descriptor *) data;
DEBUG14(printk("scsi(%ld): IO descriptor timeout, index=%x "
"signature=%08x, scheduling ISP abort.\n", iodesc->ha->host_no,
iodesc->idx, iodesc->signature));
qla2x00_free_iodesc(iodesc);
set_bit(ISP_ABORT_NEEDED, &iodesc->ha->dpc_flags);
}
/**
* qla2x00_add_iodesc_timer() - Add and start a timer for an IO descriptor.
* @iodesc: io descriptor
*
* NOTE:
* The firmware shall timeout an outstanding mailbox IOCB in 2 * R_A_TOV (in
* tenths of a second). The driver will wait 2.5 * R_A_TOV before scheduling
* a recovery (big hammer).
*/
static inline void
qla2x00_add_iodesc_timer(struct io_descriptor *iodesc)
{
unsigned long timeout;
timeout = ((iodesc->ha->r_a_tov * 2) + (iodesc->ha->r_a_tov / 2)) / 10;
init_timer(&iodesc->timer);
iodesc->timer.data = (unsigned long) iodesc;
iodesc->timer.expires = jiffies + (timeout * HZ);
iodesc->timer.function =
(void (*) (unsigned long)) qla2x00_iodesc_timeout;
add_timer(&iodesc->timer);
}
/**
* qla2x00_remove_iodesc_timer() - Remove an active timer from an IO descriptor.
* @iodesc: io descriptor
*/
static inline void
qla2x00_remove_iodesc_timer(struct io_descriptor *iodesc)
{
if (iodesc->timer.function != NULL) {
del_timer_sync(&iodesc->timer);
iodesc->timer.data = (unsigned long) NULL;
iodesc->timer.function = NULL;
}
}
/**
* IO descriptor support routines.
**/
/**
* qla2x00_update_login_fcport() - Update fcport data after login processing.
* @ha: HA context
* @mbxstat: Mailbox command status IOCB
* @fcport: port to update
*/
static inline void
qla2x00_update_login_fcport(scsi_qla_host_t *ha, struct mbx_entry *mbxstat,
fc_port_t *fcport)
{
if (le16_to_cpu(mbxstat->mb1) & BIT_0) {
fcport->port_type = FCT_INITIATOR;
} else {
fcport->port_type = FCT_TARGET;
if (le16_to_cpu(mbxstat->mb1) & BIT_1) {
fcport->flags |= FCF_TAPE_PRESENT;
}
}
fcport->login_retry = 0;
fcport->port_login_retry_count = ha->port_down_retry_count *
PORT_RETRY_TIME;
atomic_set(&fcport->port_down_timer, ha->port_down_retry_count *
PORT_RETRY_TIME);
fcport->flags |= FCF_FABRIC_DEVICE;
fcport->flags &= ~FCF_FAILOVER_NEEDED;
fcport->iodesc_idx_sent = IODESC_INVALID_INDEX;
atomic_set(&fcport->state, FCS_ONLINE);
}
/**
* Mailbox IOCB commands.
**/
/**
* qla2x00_get_mbx_iocb_entry() - Retrieve an IOCB from the request queue.
* @ha: HA context
* @handle: handle to io descriptor
*
* Returns a pointer to the reqest entry, or NULL, if none were available.
*/
static inline struct mbx_entry *
qla2x00_get_mbx_iocb_entry(scsi_qla_host_t *ha, uint32_t handle)
{
uint16_t cnt;
device_reg_t *reg;
struct mbx_entry *mbxentry;
reg = ha->iobase;
mbxentry = NULL;
if (ha->req_q_cnt < 3) {
cnt = qla2x00_debounce_register(ISP_REQ_Q_OUT(ha, reg));
if (ha->req_ring_index < cnt)
ha->req_q_cnt = cnt - ha->req_ring_index;
else
ha->req_q_cnt = REQUEST_ENTRY_CNT -
(ha->req_ring_index - cnt);
}
if (ha->req_q_cnt >= 3) {
mbxentry = (struct mbx_entry *)ha->request_ring_ptr;
memset(mbxentry, 0, sizeof(struct mbx_entry));
mbxentry->entry_type = MBX_IOCB_TYPE;
mbxentry->entry_count = 1;
mbxentry->sys_define1 = SOURCE_ASYNC_IOCB;
mbxentry->handle = handle;
}
return (mbxentry);
}
/**
* qla2x00_send_abort_iocb() - Issue an abort IOCB to the firmware.
* @ha: HA context
* @iodesc: io descriptor
* @handle_to_abort: firmware handle to abort
* @ha_locked: is function called with the hardware lock
*
* Returns QLA_SUCCESS if the IOCB was issued.
*/
static int
qla2x00_send_abort_iocb(scsi_qla_host_t *ha, struct io_descriptor *iodesc,
uint32_t handle_to_abort, int ha_locked)
{
unsigned long flags = 0;
struct mbx_entry *mbxentry;
/* Send marker if required. */
if (qla2x00_issue_marker(ha, ha_locked) != QLA_SUCCESS)
return (QLA_FUNCTION_FAILED);
if (!ha_locked)
spin_lock_irqsave(&ha->hardware_lock, flags);
/* Build abort mailbox IOCB. */
mbxentry = qla2x00_get_mbx_iocb_entry(ha, iodesc->signature);
if (mbxentry == NULL) {
if (!ha_locked)
spin_unlock_irqrestore(&ha->hardware_lock, flags);
return (QLA_FUNCTION_FAILED);
}
mbxentry->mb0 = __constant_cpu_to_le16(MBC_ABORT_COMMAND);
mbxentry->mb1 = mbxentry->loop_id.extended =
cpu_to_le16(iodesc->remote_fcport->loop_id);
mbxentry->mb2 = LSW(handle_to_abort);
mbxentry->mb3 = MSW(handle_to_abort);
qla2x00_add_iodesc_timer(iodesc);
/* Issue command to ISP. */
qla2x00_isp_cmd(ha);
if (!ha_locked)
spin_unlock_irqrestore(&ha->hardware_lock, flags);
DEBUG14(printk("scsi(%ld): Sending Abort IOCB (%08x) to [%x], aborting "
"%08x.\n", ha->host_no, iodesc->signature,
iodesc->remote_fcport->loop_id, handle_to_abort));
return (QLA_SUCCESS);
}
/**
* qla2x00_send_abort_iocb_cb() - Abort IOCB callback.
* @ha: HA context
* @iodesc: io descriptor
* @mbxstat: mailbox status IOCB
*
* Returns QLA_SUCCESS if @iodesc can be freed by the caller, else, @iodesc
* will be used for a retry.
*/
static int
qla2x00_send_abort_iocb_cb(scsi_qla_host_t *ha, struct io_descriptor *iodesc,
struct mbx_entry *mbxstat)
{
DEBUG14(printk("scsi(%ld): Abort IOCB -- sent to [%x/%02x%02x%02x], "
"status=%x mb0=%x.\n", ha->host_no, iodesc->remote_fcport->loop_id,
iodesc->d_id.b.domain, iodesc->d_id.b.area, iodesc->d_id.b.al_pa,
le16_to_cpu(mbxstat->status), le16_to_cpu(mbxstat->mb0)));
return (QLA_SUCCESS);
}
/**
* qla2x00_send_adisc_iocb() - Issue a Get Port Database IOCB to the firmware.
* @ha: HA context
* @iodesc: io descriptor
* @ha_locked: is function called with the hardware lock
*
* Returns QLA_SUCCESS if the IOCB was issued.
*/
static int
qla2x00_send_adisc_iocb(scsi_qla_host_t *ha, struct io_descriptor *iodesc,
int ha_locked)
{
unsigned long flags = 0;
struct mbx_entry *mbxentry;
/* Send marker if required. */
if (qla2x00_issue_marker(ha, ha_locked) != QLA_SUCCESS)
return (QLA_FUNCTION_FAILED);
if (!ha_locked)
spin_lock_irqsave(&ha->hardware_lock, flags);
/* Build Get Port Database IOCB. */
mbxentry = qla2x00_get_mbx_iocb_entry(ha, iodesc->signature);
if (mbxentry == NULL) {
if (!ha_locked)
spin_unlock_irqrestore(&ha->hardware_lock, flags);
return (QLA_FUNCTION_FAILED);
}
mbxentry->mb0 = __constant_cpu_to_le16(MBC_GET_PORT_DATABASE);
mbxentry->mb1 = mbxentry->loop_id.extended =
cpu_to_le16(iodesc->remote_fcport->loop_id);
mbxentry->mb2 = cpu_to_le16(MSW(LSD(ha->iodesc_pd_dma)));
mbxentry->mb3 = cpu_to_le16(LSW(LSD(ha->iodesc_pd_dma)));
mbxentry->mb6 = cpu_to_le16(MSW(MSD(ha->iodesc_pd_dma)));
mbxentry->mb7 = cpu_to_le16(LSW(MSD(ha->iodesc_pd_dma)));
mbxentry->mb10 = __constant_cpu_to_le16(BIT_0);
qla2x00_add_iodesc_timer(iodesc);
/* Issue command to ISP. */
qla2x00_isp_cmd(ha);
if (!ha_locked)
spin_unlock_irqrestore(&ha->hardware_lock, flags);
DEBUG14(printk("scsi(%ld): Sending Adisc IOCB (%08x) to [%x].\n",
ha->host_no, iodesc->signature, iodesc->remote_fcport->loop_id));
return (QLA_SUCCESS);
}
/**
* qla2x00_send_adisc_iocb_cb() - Get Port Database IOCB callback.
* @ha: HA context
* @iodesc: io descriptor
* @mbxstat: mailbox status IOCB
*
* Returns QLA_SUCCESS if @iodesc can be freed by the caller, else, @iodesc
* will be used for a retry.
*/
static int
qla2x00_send_adisc_iocb_cb(scsi_qla_host_t *ha, struct io_descriptor *iodesc,
struct mbx_entry *mbxstat)
{
fc_port_t *remote_fcport;
remote_fcport = iodesc->remote_fcport;
/* Ensure the port IDs are consistent. */
if (remote_fcport->d_id.b24 != iodesc->d_id.b24) {
DEBUG14(printk("scsi(%ld): Adisc IOCB -- ignoring, remote port "
"id changed from [%02x%02x%02x] to [%02x%02x%02x].\n",
ha->host_no, remote_fcport->d_id.b.domain,
remote_fcport->d_id.b.area, remote_fcport->d_id.b.al_pa,
iodesc->d_id.b.domain, iodesc->d_id.b.area,
iodesc->d_id.b.al_pa));
return (QLA_SUCCESS);
}
/* Only process the last command. */
if (remote_fcport->iodesc_idx_sent != iodesc->idx) {
DEBUG14(printk("scsi(%ld): Adisc IOCB -- ignoring, sent to "
"[%02x%02x%02x], expected %x, received %x.\n", ha->host_no,
iodesc->d_id.b.domain, iodesc->d_id.b.area,
iodesc->d_id.b.al_pa, remote_fcport->iodesc_idx_sent,
iodesc->idx));
return (QLA_SUCCESS);
}
if (le16_to_cpu(mbxstat->status) == CS_COMPLETE) {
DEBUG14(printk("scsi(%ld): Adisc IOCB -- marking "
"[%x/%02x%02x%02x] online.\n", ha->host_no,
remote_fcport->loop_id, remote_fcport->d_id.b.domain,
remote_fcport->d_id.b.area, remote_fcport->d_id.b.al_pa));
atomic_set(&remote_fcport->state, FCS_ONLINE);
} else {
DEBUG14(printk("scsi(%ld): Adisc IOCB -- marking "
"[%x/%02x%02x%02x] lost, status=%x mb0=%x.\n", ha->host_no,
remote_fcport->loop_id, remote_fcport->d_id.b.domain,
remote_fcport->d_id.b.area, remote_fcport->d_id.b.al_pa,
le16_to_cpu(mbxstat->status), le16_to_cpu(mbxstat->mb0)));
if (atomic_read(&remote_fcport->state) != FCS_DEVICE_DEAD)
atomic_set(&remote_fcport->state, FCS_DEVICE_LOST);
}
remote_fcport->iodesc_idx_sent = IODESC_INVALID_INDEX;
return (QLA_SUCCESS);
}
/**
* qla2x00_send_logout_iocb() - Issue a fabric port logout IOCB to the firmware.
* @ha: HA context
* @iodesc: io descriptor
* @ha_locked: is function called with the hardware lock
*
* Returns QLA_SUCCESS if the IOCB was issued.
*/
static int
qla2x00_send_logout_iocb(scsi_qla_host_t *ha, struct io_descriptor *iodesc,
int ha_locked)
{
unsigned long flags = 0;
struct mbx_entry *mbxentry;
/* Send marker if required. */
if (qla2x00_issue_marker(ha, ha_locked) != QLA_SUCCESS)
return (QLA_FUNCTION_FAILED);
if (!ha_locked)
spin_lock_irqsave(&ha->hardware_lock, flags);
/* Build fabric port logout mailbox IOCB. */
mbxentry = qla2x00_get_mbx_iocb_entry(ha, iodesc->signature);
if (mbxentry == NULL) {
if (!ha_locked)
spin_unlock_irqrestore(&ha->hardware_lock, flags);
return (QLA_FUNCTION_FAILED);
}
mbxentry->mb0 = __constant_cpu_to_le16(MBC_LOGOUT_FABRIC_PORT);
mbxentry->mb1 = mbxentry->loop_id.extended =
cpu_to_le16(iodesc->remote_fcport->loop_id);
qla2x00_add_iodesc_timer(iodesc);
/* Issue command to ISP. */
qla2x00_isp_cmd(ha);
if (!ha_locked)
spin_unlock_irqrestore(&ha->hardware_lock, flags);
DEBUG14(printk("scsi(%ld): Sending Logout IOCB (%08x) to [%x].\n",
ha->host_no, iodesc->signature, iodesc->remote_fcport->loop_id));
return (QLA_SUCCESS);
}
/**
* qla2x00_send_logout_iocb_cb() - Fabric port logout IOCB callback.
* @ha: HA context
* @iodesc: io descriptor
* @mbxstat: mailbox status IOCB
*
* Returns QLA_SUCCESS if @iodesc can be freed by the caller, else, @iodesc
* will be used for a retry.
*/
static int
qla2x00_send_logout_iocb_cb(scsi_qla_host_t *ha, struct io_descriptor *iodesc,
struct mbx_entry *mbxstat)
{
DEBUG14(printk("scsi(%ld): Logout IOCB -- sent to [%x/%02x%02x%02x], "
"status=%x mb0=%x mb1=%x.\n", ha->host_no,
iodesc->remote_fcport->loop_id,
iodesc->remote_fcport->d_id.b.domain,
iodesc->remote_fcport->d_id.b.area,
iodesc->remote_fcport->d_id.b.al_pa, le16_to_cpu(mbxstat->status),
le16_to_cpu(mbxstat->mb0), le16_to_cpu(mbxstat->mb1)));
return (QLA_SUCCESS);
}
/**
* qla2x00_send_login_iocb() - Issue a fabric port login IOCB to the firmware.
* @ha: HA context
* @iodesc: io descriptor
* @d_id: port id for device
* @ha_locked: is function called with the hardware lock
*
* Returns QLA_SUCCESS if the IOCB was issued.
*/
static int
qla2x00_send_login_iocb(scsi_qla_host_t *ha, struct io_descriptor *iodesc,
port_id_t *d_id, int ha_locked)
{
unsigned long flags = 0;
struct mbx_entry *mbxentry;
/* Send marker if required. */
if (qla2x00_issue_marker(ha, ha_locked) != QLA_SUCCESS)
return (QLA_FUNCTION_FAILED);
if (!ha_locked)
spin_lock_irqsave(&ha->hardware_lock, flags);
/* Build fabric port login mailbox IOCB. */
mbxentry = qla2x00_get_mbx_iocb_entry(ha, iodesc->signature);
if (mbxentry == NULL) {
if (!ha_locked)
spin_unlock_irqrestore(&ha->hardware_lock, flags);
return (QLA_FUNCTION_FAILED);
}
mbxentry->mb0 = __constant_cpu_to_le16(MBC_LOGIN_FABRIC_PORT);
mbxentry->mb1 = mbxentry->loop_id.extended =
cpu_to_le16(iodesc->remote_fcport->loop_id);
mbxentry->mb2 = cpu_to_le16(d_id->b.domain);
mbxentry->mb3 = cpu_to_le16(d_id->b.area << 8 | d_id->b.al_pa);
mbxentry->mb10 = __constant_cpu_to_le16(BIT_0);
qla2x00_add_iodesc_timer(iodesc);
/* Issue command to ISP. */
qla2x00_isp_cmd(ha);
if (!ha_locked)
spin_unlock_irqrestore(&ha->hardware_lock, flags);
DEBUG14(printk("scsi(%ld): Sending Login IOCB (%08x) to "
"[%x/%02x%02x%02x].\n", ha->host_no, iodesc->signature,
iodesc->remote_fcport->loop_id, d_id->b.domain, d_id->b.area,
d_id->b.al_pa));
return (QLA_SUCCESS);
}
/**
* qla2x00_send_login_iocb_cb() - Fabric port logout IOCB callback.
* @ha: HA context
* @iodesc: io descriptor
* @mbxstat: mailbox status IOCB
*
* Returns QLA_SUCCESS if @iodesc can be freed by the caller, else, @iodesc
* will be used for a retry.
*/
static int
qla2x00_send_login_iocb_cb(scsi_qla_host_t *ha, struct io_descriptor *iodesc,
struct mbx_entry *mbxstat)
{
int rval;
fc_port_t *fcport, *remote_fcport, *exist_fcport;
struct io_descriptor *abort_iodesc, *login_iodesc;
uint16_t status, mb[8];
uint16_t reuse;
uint16_t remote_loopid;
port_id_t remote_did, inuse_did;
remote_fcport = iodesc->remote_fcport;
/* Only process the last command. */
if (remote_fcport->iodesc_idx_sent != iodesc->idx) {
DEBUG14(printk("scsi(%ld): Login IOCB -- ignoring, sent to "
"[%02x%02x%02x], expected %x, received %x.\n",
ha->host_no, iodesc->d_id.b.domain, iodesc->d_id.b.area,
iodesc->d_id.b.al_pa, remote_fcport->iodesc_idx_sent,
iodesc->idx));
/* Free RSCN fcport resources. */
if (remote_fcport->port_type == FCT_RSCN) {
DEBUG14(printk("scsi(%ld): Login IOCB -- Freeing RSCN "
"fcport %p [%x/%02x%02x%02x] given ignored Login "
"IOCB.\n", ha->host_no, remote_fcport,
remote_fcport->loop_id,
remote_fcport->d_id.b.domain,
remote_fcport->d_id.b.area,
remote_fcport->d_id.b.al_pa));
list_del(&remote_fcport->list);
kfree(remote_fcport);
}
return (QLA_SUCCESS);
}
status = le16_to_cpu(mbxstat->status);
mb[0] = le16_to_cpu(mbxstat->mb0);
mb[1] = le16_to_cpu(mbxstat->mb1);
mb[2] = le16_to_cpu(mbxstat->mb2);
mb[6] = le16_to_cpu(mbxstat->mb6);
mb[7] = le16_to_cpu(mbxstat->mb7);
/* Good status? */
if ((status == CS_COMPLETE || status == CS_COMPLETE_CHKCOND) &&
mb[0] == MBS_COMMAND_COMPLETE) {
DEBUG14(printk("scsi(%ld): Login IOCB -- status=%x mb1=%x pn="
"%02x%02x%02x%02x%02x%02x%02x%02x.\n", ha->host_no, status,
mb[1], mbxstat->port_name[0], mbxstat->port_name[1],
mbxstat->port_name[2], mbxstat->port_name[3],
mbxstat->port_name[4], mbxstat->port_name[5],
mbxstat->port_name[6], mbxstat->port_name[7]));
memcpy(remote_fcport->node_name, mbxstat->node_name, WWN_SIZE);
memcpy(remote_fcport->port_name, mbxstat->port_name, WWN_SIZE);
/* Is the device already in our fcports list? */
if (remote_fcport->port_type != FCT_RSCN) {
DEBUG14(printk("scsi(%ld): Login IOCB -- marking "
"[%x/%02x%02x%02x] online.\n", ha->host_no,
remote_fcport->loop_id,
remote_fcport->d_id.b.domain,
remote_fcport->d_id.b.area,
remote_fcport->d_id.b.al_pa));
qla2x00_update_login_fcport(ha, mbxstat, remote_fcport);
return (QLA_SUCCESS);
}
/* Does the RSCN portname already exist in our fcports list? */
exist_fcport = NULL;
list_for_each_entry(fcport, &ha->fcports, list) {
if (memcmp(remote_fcport->port_name, fcport->port_name,
WWN_SIZE) == 0) {
exist_fcport = fcport;
break;
}
}
if (exist_fcport != NULL) {
DEBUG14(printk("scsi(%ld): Login IOCB -- found RSCN "
"fcport in fcports list [%p].\n", ha->host_no,
exist_fcport));
/* Abort any ADISC that could have been sent. */
if (exist_fcport->iodesc_idx_sent != iodesc->idx &&
exist_fcport->iodesc_idx_sent <
MAX_IO_DESCRIPTORS &&
ha->io_descriptors[exist_fcport->iodesc_idx_sent].
cb_idx == ADISC_PORT_IOCB_CB) {
abort_iodesc = qla2x00_alloc_iodesc(ha);
if (abort_iodesc) {
DEBUG14(printk("scsi(%ld): Login IOCB "
"-- issuing abort to outstanding "
"Adisc [%x/%02x%02x%02x].\n",
ha->host_no, remote_fcport->loop_id,
exist_fcport->d_id.b.domain,
exist_fcport->d_id.b.area,
exist_fcport->d_id.b.al_pa));
abort_iodesc->cb_idx = ABORT_IOCB_CB;
abort_iodesc->d_id.b24 =
exist_fcport->d_id.b24;
abort_iodesc->remote_fcport =
exist_fcport;
exist_fcport->iodesc_idx_sent =
abort_iodesc->idx;
qla2x00_send_abort_iocb(ha,
abort_iodesc, ha->io_descriptors[
exist_fcport->iodesc_idx_sent].
signature, 1);
} else {
DEBUG14(printk("scsi(%ld): Login IOCB "
"-- unable to abort outstanding "
"Adisc [%x/%02x%02x%02x].\n",
ha->host_no, remote_fcport->loop_id,
exist_fcport->d_id.b.domain,
exist_fcport->d_id.b.area,
exist_fcport->d_id.b.al_pa));
}
}
/*
* If the existing fcport is waiting to send an ADISC
* or LOGIN, then reuse remote fcport (RSCN) to
* continue waiting.
*/
reuse = 0;
remote_loopid = remote_fcport->loop_id;
remote_did.b24 = remote_fcport->d_id.b24;
if (exist_fcport->iodesc_idx_sent ==
IODESC_ADISC_NEEDED ||
exist_fcport->iodesc_idx_sent ==
IODESC_LOGIN_NEEDED) {
DEBUG14(printk("scsi(%ld): Login IOCB -- "
"existing fcport [%x/%02x%02x%02x] "
"waiting for IO descriptor, reuse RSCN "
"fcport.\n", ha->host_no,
exist_fcport->loop_id,
exist_fcport->d_id.b.domain,
exist_fcport->d_id.b.area,
exist_fcport->d_id.b.al_pa));
reuse++;
remote_fcport->iodesc_idx_sent =
exist_fcport->iodesc_idx_sent;
exist_fcport->iodesc_idx_sent =
IODESC_INVALID_INDEX;
remote_fcport->loop_id = exist_fcport->loop_id;
remote_fcport->d_id.b24 =
exist_fcport->d_id.b24;
}
/* Logout the old loopid. */
if (!reuse &&
exist_fcport->loop_id != remote_fcport->loop_id &&
exist_fcport->loop_id != FC_NO_LOOP_ID) {
login_iodesc = qla2x00_alloc_iodesc(ha);
if (login_iodesc) {
DEBUG14(printk("scsi(%ld): Login IOCB "
"-- issuing logout to free old "
"loop id [%x/%02x%02x%02x].\n",
ha->host_no, exist_fcport->loop_id,
exist_fcport->d_id.b.domain,
exist_fcport->d_id.b.area,
exist_fcport->d_id.b.al_pa));
login_iodesc->cb_idx =
LOGOUT_PORT_IOCB_CB;
login_iodesc->d_id.b24 =
exist_fcport->d_id.b24;
login_iodesc->remote_fcport =
exist_fcport;
exist_fcport->iodesc_idx_sent =
login_iodesc->idx;
qla2x00_send_logout_iocb(ha,
login_iodesc, 1);
} else {
/* Ran out of IO descriptiors. */
DEBUG14(printk("scsi(%ld): Login IOCB "
"-- unable to logout to free old "
"loop id [%x/%02x%02x%02x].\n",
ha->host_no, exist_fcport->loop_id,
exist_fcport->d_id.b.domain,
exist_fcport->d_id.b.area,
exist_fcport->d_id.b.al_pa));
exist_fcport->iodesc_idx_sent =
IODESC_INVALID_INDEX;
}
}
/* Update existing fcport with remote fcport info. */
DEBUG14(printk("scsi(%ld): Login IOCB -- marking "
"existing fcport [%x/%02x%02x%02x] online.\n",
ha->host_no, remote_loopid, remote_did.b.domain,
remote_did.b.area, remote_did.b.al_pa));
memcpy(exist_fcport->node_name,
remote_fcport->node_name, WWN_SIZE);
exist_fcport->loop_id = remote_loopid;
exist_fcport->d_id.b24 = remote_did.b24;
qla2x00_update_login_fcport(ha, mbxstat, exist_fcport);
/* Finally, free the remote (RSCN) fcport. */
if (!reuse) {
DEBUG14(printk("scsi(%ld): Login IOCB -- "
"Freeing RSCN fcport %p "
"[%x/%02x%02x%02x].\n", ha->host_no,
remote_fcport, remote_fcport->loop_id,
remote_fcport->d_id.b.domain,
remote_fcport->d_id.b.area,
remote_fcport->d_id.b.al_pa));
list_del(&remote_fcport->list);
kfree(remote_fcport);
}
return (QLA_SUCCESS);
}
/*
* A new device has been added, move the RSCN fcport to our
* fcports list.
*/
DEBUG14(printk("scsi(%ld): Login IOCB -- adding RSCN fcport "
"[%x/%02x%02x%02x] to fcports list.\n", ha->host_no,
remote_fcport->loop_id, remote_fcport->d_id.b.domain,
remote_fcport->d_id.b.area, remote_fcport->d_id.b.al_pa));
list_del(&remote_fcport->list);
remote_fcport->flags = (FCF_RLC_SUPPORT | FCF_RESCAN_NEEDED);
qla2x00_update_login_fcport(ha, mbxstat, remote_fcport);
list_add_tail(&remote_fcport->list, &ha->fcports);
set_bit(FCPORT_RESCAN_NEEDED, &ha->dpc_flags);
} else {
/* Handle login failure. */
if (remote_fcport->login_retry != 0) {
if (mb[0] == MBS_LOOP_ID_USED) {
inuse_did.b.domain = LSB(mb[1]);
inuse_did.b.area = MSB(mb[2]);
inuse_did.b.al_pa = LSB(mb[2]);
DEBUG14(printk("scsi(%ld): Login IOCB -- loop "
"id [%x] used by port id [%02x%02x%02x].\n",
ha->host_no, remote_fcport->loop_id,
inuse_did.b.domain, inuse_did.b.area,
inuse_did.b.al_pa));
if (remote_fcport->d_id.b24 ==
INVALID_PORT_ID) {
/*
* Invalid port id means we are trying
* to login to a remote port with just
* a loop id without knowing about the
* port id. Copy the port id and try
* again.
*/
remote_fcport->d_id.b24 = inuse_did.b24;
iodesc->d_id.b24 = inuse_did.b24;
} else {
remote_fcport->loop_id++;
rval = qla2x00_find_new_loop_id(ha,
remote_fcport);
if (rval == QLA_FUNCTION_FAILED) {
/* No more loop ids. */
return (QLA_SUCCESS);
}
}
} else if (mb[0] == MBS_PORT_ID_USED) {
/*
* Device has another loop ID. The firmware
* group recommends the driver perform an
* implicit login with the specified ID.
*/
DEBUG14(printk("scsi(%ld): Login IOCB -- port "
"id [%02x%02x%02x] already assigned to "
"loop id [%x].\n", ha->host_no,
iodesc->d_id.b.domain, iodesc->d_id.b.area,
iodesc->d_id.b.al_pa, mb[1]));
remote_fcport->loop_id = mb[1];
} else {
/* Unable to perform login, try again. */
DEBUG14(printk("scsi(%ld): Login IOCB -- "
"failed login [%x/%02x%02x%02x], status=%x "
"mb0=%x mb1=%x mb2=%x mb6=%x mb7=%x.\n",
ha->host_no, remote_fcport->loop_id,
iodesc->d_id.b.domain, iodesc->d_id.b.area,
iodesc->d_id.b.al_pa, status, mb[0], mb[1],
mb[2], mb[6], mb[7]));
}
/* Reissue Login with the same IO descriptor. */
iodesc->signature =
qla2x00_iodesc_to_handle(iodesc);
iodesc->cb_idx = LOGIN_PORT_IOCB_CB;
iodesc->d_id.b24 = remote_fcport->d_id.b24;
remote_fcport->iodesc_idx_sent = iodesc->idx;
remote_fcport->login_retry--;
DEBUG14(printk("scsi(%ld): Login IOCB -- retrying "
"login to [%x/%02x%02x%02x] (%d).\n", ha->host_no,
remote_fcport->loop_id,
remote_fcport->d_id.b.domain,
remote_fcport->d_id.b.area,
remote_fcport->d_id.b.al_pa,
remote_fcport->login_retry));
qla2x00_send_login_iocb(ha, iodesc,
&remote_fcport->d_id, 1);
return (QLA_FUNCTION_FAILED);
} else {
/* No more logins, mark device dead. */
DEBUG14(printk("scsi(%ld): Login IOCB -- failed "
"login [%x/%02x%02x%02x] after retries, status=%x "
"mb0=%x mb1=%x mb2=%x mb6=%x mb7=%x.\n",
ha->host_no, remote_fcport->loop_id,
iodesc->d_id.b.domain, iodesc->d_id.b.area,
iodesc->d_id.b.al_pa, status, mb[0], mb[1],
mb[2], mb[6], mb[7]));
atomic_set(&remote_fcport->state, FCS_DEVICE_DEAD);
if (remote_fcport->port_type == FCT_RSCN) {
DEBUG14(printk("scsi(%ld): Login IOCB -- "
"Freeing dead RSCN fcport %p "
"[%x/%02x%02x%02x].\n", ha->host_no,
remote_fcport, remote_fcport->loop_id,
remote_fcport->d_id.b.domain,
remote_fcport->d_id.b.area,
remote_fcport->d_id.b.al_pa));
list_del(&remote_fcport->list);
kfree(remote_fcport);
}
}
}
return (QLA_SUCCESS);
}
/**
* IO descriptor processing routines.
**/
/**
* qla2x00_alloc_rscn_fcport() - Allocate an RSCN type fcport.
* @ha: HA context
* @flags: allocation flags
*
* Returns a pointer to the allocated RSCN fcport, or NULL, if none available.
*/
fc_port_t *
qla2x00_alloc_rscn_fcport(scsi_qla_host_t *ha, int flags)
{
fc_port_t *fcport;
fcport = qla2x00_alloc_fcport(ha, flags);
if (fcport == NULL)
return (fcport);
/* Setup RSCN fcport structure. */
fcport->port_type = FCT_RSCN;
return (fcport);
}
/**
* qla2x00_handle_port_rscn() - Handle port RSCN.
* @ha: HA context
* @rscn_entry: RSCN entry
* @fcport: fcport entry to updated
*
* Returns QLA_SUCCESS if the port RSCN was handled.
*/
int
qla2x00_handle_port_rscn(scsi_qla_host_t *ha, uint32_t rscn_entry,
fc_port_t *known_fcport, int ha_locked)
{
int rval;
port_id_t rscn_pid;
fc_port_t *fcport, *remote_fcport, *rscn_fcport;
struct io_descriptor *iodesc;
remote_fcport = NULL;
rscn_fcport = NULL;
/* Prepare port id based on incoming entries. */
if (known_fcport) {
rscn_pid.b24 = known_fcport->d_id.b24;
remote_fcport = known_fcport;
DEBUG14(printk("scsi(%ld): Handle RSCN -- process RSCN for "
"fcport [%02x%02x%02x].\n", ha->host_no,
remote_fcport->d_id.b.domain, remote_fcport->d_id.b.area,
remote_fcport->d_id.b.al_pa));
} else {
rscn_pid.b.domain = LSB(MSW(rscn_entry));
rscn_pid.b.area = MSB(LSW(rscn_entry));
rscn_pid.b.al_pa = LSB(LSW(rscn_entry));
DEBUG14(printk("scsi(%ld): Handle RSCN -- process RSCN for "
"port id [%02x%02x%02x].\n", ha->host_no,
rscn_pid.b.domain, rscn_pid.b.area, rscn_pid.b.al_pa));
/*
* Search fcport lists for a known entry at the specified port
* ID.
*/
list_for_each_entry(fcport, &ha->fcports, list) {
if (rscn_pid.b24 == fcport->d_id.b24) {
remote_fcport = fcport;
break;
}
}
list_for_each_entry(fcport, &ha->rscn_fcports, list) {
if (rscn_pid.b24 == fcport->d_id.b24) {
rscn_fcport = fcport;
break;
}
}
if (remote_fcport == NULL)
remote_fcport = rscn_fcport;
}
/*
* If the port is already in our fcport list and online, send an ADISC
* to see if it's still alive. Issue login if a new fcport or the known
* fcport is currently offline.
*/
if (remote_fcport) {
/*
* No need to send request if the remote fcport is currently
* waiting for an available io descriptor.
*/
if (known_fcport == NULL &&
(remote_fcport->iodesc_idx_sent == IODESC_ADISC_NEEDED ||
remote_fcport->iodesc_idx_sent == IODESC_LOGIN_NEEDED)) {
/*
* If previous waiting io descriptor is an ADISC, then
* the new RSCN may come from a new remote fcport being
* plugged into the same location.
*/
if (remote_fcport->port_type == FCT_RSCN) {
remote_fcport->iodesc_idx_sent =
IODESC_LOGIN_NEEDED;
} else if (remote_fcport->iodesc_idx_sent ==
IODESC_ADISC_NEEDED) {
fc_port_t *new_fcport;
remote_fcport->iodesc_idx_sent =
IODESC_INVALID_INDEX;
/* Create new fcport for later login. */
new_fcport = qla2x00_alloc_rscn_fcport(ha,
ha_locked ? GFP_ATOMIC: GFP_KERNEL);
if (new_fcport) {
DEBUG14(printk("scsi(%ld): Handle RSCN "
"-- creating RSCN fcport %p for "
"future login.\n", ha->host_no,
new_fcport));
new_fcport->d_id.b24 =
remote_fcport->d_id.b24;
new_fcport->iodesc_idx_sent =
IODESC_LOGIN_NEEDED;
list_add_tail(&new_fcport->list,
&ha->rscn_fcports);
set_bit(IODESC_PROCESS_NEEDED,
&ha->dpc_flags);
} else {
DEBUG14(printk("scsi(%ld): Handle RSCN "
"-- unable to allocate RSCN fcport "
"for future login.\n",
ha->host_no));
}
}
return (QLA_SUCCESS);
}
/* Send ADISC if the fcport is online */
if (atomic_read(&remote_fcport->state) == FCS_ONLINE ||
remote_fcport->iodesc_idx_sent == IODESC_ADISC_NEEDED) {
atomic_set(&remote_fcport->state, FCS_DEVICE_LOST);
iodesc = qla2x00_alloc_iodesc(ha);
if (iodesc == NULL) {
/* Mark fcport for later adisc processing */
DEBUG14(printk("scsi(%ld): Handle RSCN -- not "
"enough IO descriptors for Adisc, flag "
"for later processing.\n", ha->host_no));
remote_fcport->iodesc_idx_sent =
IODESC_ADISC_NEEDED;
set_bit(IODESC_PROCESS_NEEDED, &ha->dpc_flags);
return (QLA_SUCCESS);
}
iodesc->cb_idx = ADISC_PORT_IOCB_CB;
iodesc->d_id.b24 = rscn_pid.b24;
iodesc->remote_fcport = remote_fcport;
remote_fcport->iodesc_idx_sent = iodesc->idx;
qla2x00_send_adisc_iocb(ha, iodesc, ha_locked);
return (QLA_SUCCESS);
} else if (remote_fcport->iodesc_idx_sent <
MAX_IO_DESCRIPTORS &&
ha->io_descriptors[remote_fcport->iodesc_idx_sent].cb_idx ==
ADISC_PORT_IOCB_CB) {
/*
* Receiving another RSCN while an ADISC is pending,
* abort the IOCB. Use the same descriptor for the
* abort.
*/
uint32_t handle_to_abort;
iodesc = &ha->io_descriptors[
remote_fcport->iodesc_idx_sent];
qla2x00_remove_iodesc_timer(iodesc);
handle_to_abort = iodesc->signature;
iodesc->signature = qla2x00_iodesc_to_handle(iodesc);
iodesc->cb_idx = ABORT_IOCB_CB;
iodesc->d_id.b24 = remote_fcport->d_id.b24;
iodesc->remote_fcport = remote_fcport;
remote_fcport->iodesc_idx_sent = iodesc->idx;
DEBUG14(printk("scsi(%ld): Handle RSCN -- issuing "
"abort to outstanding Adisc [%x/%02x%02x%02x].\n",
ha->host_no, remote_fcport->loop_id,
iodesc->d_id.b.domain, iodesc->d_id.b.area,
iodesc->d_id.b.al_pa));
qla2x00_send_abort_iocb(ha, iodesc, handle_to_abort,
ha_locked);
}
}
/* We need to login to the remote port, find it. */
if (known_fcport) {
remote_fcport = known_fcport;
} else if (rscn_fcport && rscn_fcport->d_id.b24 != INVALID_PORT_ID &&
rscn_fcport->iodesc_idx_sent < MAX_IO_DESCRIPTORS &&
ha->io_descriptors[rscn_fcport->iodesc_idx_sent].cb_idx ==
LOGIN_PORT_IOCB_CB) {
/*
* Ignore duplicate RSCN on fcport which has already
* initiated a login IOCB.
*/
DEBUG14(printk("scsi(%ld): Handle RSCN -- ignoring, login "
"already sent to [%02x%02x%02x].\n", ha->host_no,
rscn_fcport->d_id.b.domain, rscn_fcport->d_id.b.area,
rscn_fcport->d_id.b.al_pa));
return (QLA_SUCCESS);
} else if (rscn_fcport && rscn_fcport->d_id.b24 != INVALID_PORT_ID &&
rscn_fcport != remote_fcport) {
/* Reuse same rscn fcport. */
DEBUG14(printk("scsi(%ld): Handle RSCN -- reusing RSCN fcport "
"[%02x%02x%02x].\n", ha->host_no,
rscn_fcport->d_id.b.domain, rscn_fcport->d_id.b.area,
rscn_fcport->d_id.b.al_pa));
remote_fcport = rscn_fcport;
} else {
/* Create new fcport for later login. */
remote_fcport = qla2x00_alloc_rscn_fcport(ha,
ha_locked ? GFP_ATOMIC: GFP_KERNEL);
list_add_tail(&remote_fcport->list, &ha->rscn_fcports);
}
if (remote_fcport == NULL)
return (QLA_SUCCESS);
/* Prepare fcport for login. */
atomic_set(&remote_fcport->state, FCS_DEVICE_LOST);
remote_fcport->login_retry = 3; /* ha->login_retry_count; */
remote_fcport->d_id.b24 = rscn_pid.b24;
iodesc = qla2x00_alloc_iodesc(ha);
if (iodesc == NULL) {
/* Mark fcport for later adisc processing. */
DEBUG14(printk("scsi(%ld): Handle RSCN -- not enough IO "
"descriptors for Login, flag for later processing.\n",
ha->host_no));
remote_fcport->iodesc_idx_sent = IODESC_LOGIN_NEEDED;
set_bit(IODESC_PROCESS_NEEDED, &ha->dpc_flags);
return (QLA_SUCCESS);
}
if (known_fcport == NULL || rscn_pid.b24 != INVALID_PORT_ID) {
remote_fcport->loop_id = ha->min_external_loopid;
rval = qla2x00_find_new_loop_id(ha, remote_fcport);
if (rval == QLA_FUNCTION_FAILED) {
/* No more loop ids, failed. */
DEBUG14(printk("scsi(%ld): Handle RSCN -- no available "
"loop id to perform Login, failed.\n",
ha->host_no));
return (rval);
}
}
iodesc->cb_idx = LOGIN_PORT_IOCB_CB;
iodesc->d_id.b24 = rscn_pid.b24;
iodesc->remote_fcport = remote_fcport;
remote_fcport->iodesc_idx_sent = iodesc->idx;
DEBUG14(printk("scsi(%ld): Handle RSCN -- attempting login to "
"[%x/%02x%02x%02x].\n", ha->host_no, remote_fcport->loop_id,
iodesc->d_id.b.domain, iodesc->d_id.b.area, iodesc->d_id.b.al_pa));
qla2x00_send_login_iocb(ha, iodesc, &rscn_pid, ha_locked);
return (QLA_SUCCESS);
}
/**
* qla2x00_process_iodesc() - Complete IO descriptor processing.
* @ha: HA context
* @mbxstat: Mailbox IOCB status
*/
void
qla2x00_process_iodesc(scsi_qla_host_t *ha, struct mbx_entry *mbxstat)
{
int rval;
uint32_t signature;
fc_port_t *fcport;
struct io_descriptor *iodesc;
signature = mbxstat->handle;
DEBUG14(printk("scsi(%ld): Process IODesc -- processing %08x.\n",
ha->host_no, signature));
/* Retrieve proper IO descriptor. */
iodesc = qla2x00_handle_to_iodesc(ha, signature);
if (iodesc == NULL) {
DEBUG14(printk("scsi(%ld): Process IODesc -- ignoring, "
"incorrect signature %08x.\n", ha->host_no, signature));
return;
}
/* Stop IO descriptor timer. */
qla2x00_remove_iodesc_timer(iodesc);
/* Verify signature match. */
if (iodesc->signature != signature) {
DEBUG14(printk("scsi(%ld): Process IODesc -- ignoring, "
"signature mismatch, sent %08x, received %08x.\n",
ha->host_no, iodesc->signature, signature));
return;
}
/* Go with IOCB callback. */
rval = iocb_function_cb_list[iodesc->cb_idx](ha, iodesc, mbxstat);
if (rval != QLA_SUCCESS) {
/* IO descriptor reused by callback. */
return;
}
qla2x00_free_iodesc(iodesc);
if (test_bit(IODESC_PROCESS_NEEDED, &ha->dpc_flags)) {
/* Scan our fcports list for any RSCN requests. */
list_for_each_entry(fcport, &ha->fcports, list) {
if (fcport->iodesc_idx_sent == IODESC_ADISC_NEEDED ||
fcport->iodesc_idx_sent == IODESC_LOGIN_NEEDED) {
qla2x00_handle_port_rscn(ha, 0, fcport, 1);
return;
}
}
/* Scan our RSCN fcports list for any RSCN requests. */
list_for_each_entry(fcport, &ha->rscn_fcports, list) {
if (fcport->iodesc_idx_sent == IODESC_ADISC_NEEDED ||
fcport->iodesc_idx_sent == IODESC_LOGIN_NEEDED) {
qla2x00_handle_port_rscn(ha, 0, fcport, 1);
return;
}
}
}
clear_bit(IODESC_PROCESS_NEEDED, &ha->dpc_flags);
}
/**
* qla2x00_cancel_io_descriptors() - Cancel all outstanding io descriptors.
* @ha: HA context
*
* This routine will also delete any RSCN entries related to the outstanding
* IO descriptors.
*/
void
qla2x00_cancel_io_descriptors(scsi_qla_host_t *ha)
{
fc_port_t *fcport, *fcptemp;
clear_bit(IODESC_PROCESS_NEEDED, &ha->dpc_flags);
/* Abort all IO descriptors. */
qla2x00_init_io_descriptors(ha);
/* Reset all pending IO descriptors in fcports list. */
list_for_each_entry(fcport, &ha->fcports, list) {
fcport->iodesc_idx_sent = IODESC_INVALID_INDEX;
}
/* Reset all pending IO descriptors in rscn fcports list. */
list_for_each_entry_safe(fcport, fcptemp, &ha->rscn_fcports, list) {
DEBUG14(printk("scsi(%ld): Cancel IOs -- Freeing RSCN fcport "
"%p [%x/%02x%02x%02x].\n", ha->host_no, fcport,
fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.al_pa));
list_del(&fcport->list);
kfree(fcport);
}
}
/******************************************************************************
* QLOGIC LINUX SOFTWARE
*
* QLogic ISP2x00 device driver for Linux 2.6.x
* Copyright (C) 2003 QLogic Corporation
* (www.qlogic.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.
*
******************************************************************************/
/*
* Compile time Options:
* 0 - Disable and 1 - Enable
*/
#define DEBUG_QLA2100 0 /* For Debug of qla2x00 */
#define MEMORY_MAPPED_IO 1
#define STOP_ON_ERROR 0 /* Stop on aborts and resets */
#define STOP_ON_RESET 0
#define USE_ABORT_TGT 1 /* Use Abort Target mbx cmd */
#define VSA 0 /* Volume Set Addressing */
/* Failover options */
#define MAX_RECOVERYTIME 10 /*
* Max suspend time for a lun recovery
* time
*/
#define MAX_FAILBACKTIME 5 /* Max suspend time before fail back */
#define QLA_CMD_TIMER_DELTA 3
/*
* When a lun is suspended for the "Not Ready" condition then it will suspend
* the lun for increments of 6 sec delays. SUSPEND_COUNT is that count.
*/
#define SUSPEND_COUNT 10 /* 6 secs * 10 retries = 60 secs */
/*
* Defines the time in seconds that the driver extends the command timeout to
* get around the problem where the mid-layer only allows 5 retries for
* commands that return BUS_BUSY
*/
#define EXTEND_CMD_TIMEOUT 60
#define MAX_RETRIES_OF_ISP_ABORT 5
/* Max time to wait for the loop to be in LOOP_READY state */
#define MAX_LOOP_TIMEOUT (60 * 5)
#define EH_ACTIVE 1 /* Error handler active */
/*
* Some vendor subsystems do not recover properly after a device reset. Define
* the following to force a logout after a successful device reset.
*/
#undef LOGOUT_AFTER_DEVICE_RESET
#include "qla_version.h"
/******************************************************************************
* QLOGIC LINUX SOFTWARE
*
* QLogic ISP2x00 device driver for Linux 2.6.x
* Copyright (C) 2003 QLogic Corporation
* (www.qlogic.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.
*
******************************************************************************/
#include "qla_os.h"
#include "qla_def.h"
static uint16_t qla2x00_nvram_request(scsi_qla_host_t *, uint32_t);
static void qla2x00_nv_deselect(scsi_qla_host_t *);
static void qla2x00_nv_write(scsi_qla_host_t *, uint16_t);
uint8_t qla2x00_read_flash_byte(scsi_qla_host_t *, uint32_t);
static void qla2x00_write_flash_byte(scsi_qla_host_t *, uint32_t, uint8_t);
static uint8_t qla2x00_poll_flash(scsi_qla_host_t *ha,
uint32_t addr, uint8_t poll_data, uint8_t mid);
static uint8_t qla2x00_program_flash_address(scsi_qla_host_t *ha,
uint32_t addr, uint8_t data, uint8_t mid);
static uint8_t qla2x00_erase_flash_sector(scsi_qla_host_t *ha,
uint32_t addr, uint32_t sec_mask, uint8_t mid);
uint8_t qla2x00_get_flash_manufacturer(scsi_qla_host_t *ha);
uint16_t qla2x00_get_flash_version(scsi_qla_host_t *);
uint16_t qla2x00_get_flash_image(scsi_qla_host_t *ha, uint8_t *image);
uint16_t qla2x00_set_flash_image(scsi_qla_host_t *ha, uint8_t *image);
/*
* NVRAM support routines
*/
/**
* qla2x00_lock_nvram_access() -
* @ha: HA context
*/
void
qla2x00_lock_nvram_access(scsi_qla_host_t *ha)
{
uint16_t data;
device_reg_t *reg;
reg = ha->iobase;
if (IS_QLA2312(ha) || IS_QLA2322(ha)) {
data = RD_REG_WORD(&reg->nvram);
while (data & NVR_BUSY) {
udelay(100);
data = RD_REG_WORD(&reg->nvram);
}
/* Lock resource */
WRT_REG_WORD(&reg->u.isp2300.host_semaphore, 0x1);
udelay(5);
data = RD_REG_WORD(&reg->u.isp2300.host_semaphore);
while ((data & BIT_0) == 0) {
/* Lock failed */
udelay(100);
WRT_REG_WORD(&reg->u.isp2300.host_semaphore, 0x1);
udelay(5);
data = RD_REG_WORD(&reg->u.isp2300.host_semaphore);
}
}
}
/**
* qla2x00_unlock_nvram_access() -
* @ha: HA context
*/
void
qla2x00_unlock_nvram_access(scsi_qla_host_t *ha)
{
device_reg_t *reg;
reg = ha->iobase;
if (IS_QLA2312(ha) || IS_QLA2322(ha))
WRT_REG_WORD(&reg->u.isp2300.host_semaphore, 0);
}
/**
* qla2x00_get_nvram_word() - Calculates word position in NVRAM and calls the
* request routine to get the word from NVRAM.
* @ha: HA context
* @addr: Address in NVRAM to read
*
* Returns the word read from nvram @addr.
*/
uint16_t
qla2x00_get_nvram_word(scsi_qla_host_t *ha, uint32_t addr)
{
uint16_t data;
uint32_t nv_cmd;
nv_cmd = addr << 16;
nv_cmd |= NV_READ_OP;
data = qla2x00_nvram_request(ha, nv_cmd);
return (data);
}
/**
* qla2x00_write_nvram_word() - Write NVRAM data.
* @ha: HA context
* @addr: Address in NVRAM to write
* @data: word to program
*/
void
qla2x00_write_nvram_word(scsi_qla_host_t *ha, uint32_t addr, uint16_t data)
{
int count;
uint16_t word;
uint32_t nv_cmd;
device_reg_t *reg = ha->iobase;
qla2x00_nv_write(ha, NVR_DATA_OUT);
qla2x00_nv_write(ha, 0);
qla2x00_nv_write(ha, 0);
for (word = 0; word < 8; word++)
qla2x00_nv_write(ha, NVR_DATA_OUT);
qla2x00_nv_deselect(ha);
/* Erase Location */
nv_cmd = (addr << 16) | NV_ERASE_OP;
nv_cmd <<= 5;
for (count = 0; count < 11; count++) {
if (nv_cmd & BIT_31)
qla2x00_nv_write(ha, NVR_DATA_OUT);
else
qla2x00_nv_write(ha, 0);
nv_cmd <<= 1;
}
qla2x00_nv_deselect(ha);
/* Wait for Erase to Finish */
WRT_REG_WORD(&reg->nvram, NVR_SELECT);
do {
NVRAM_DELAY();
word = RD_REG_WORD(&reg->nvram);
} while ((word & NVR_DATA_IN) == 0);
qla2x00_nv_deselect(ha);
/* Write data */
nv_cmd = (addr << 16) | NV_WRITE_OP;
nv_cmd |= data;
nv_cmd <<= 5;
for (count = 0; count < 27; count++) {
if (nv_cmd & BIT_31)
qla2x00_nv_write(ha, NVR_DATA_OUT);
else
qla2x00_nv_write(ha, 0);
nv_cmd <<= 1;
}
qla2x00_nv_deselect(ha);
/* Wait for NVRAM to become ready */
WRT_REG_WORD(&reg->nvram, NVR_SELECT);
do {
NVRAM_DELAY();
word = RD_REG_WORD(&reg->nvram);
} while ((word & NVR_DATA_IN) == 0);
qla2x00_nv_deselect(ha);
/* Disable writes */
qla2x00_nv_write(ha, NVR_DATA_OUT);
for (count = 0; count < 10; count++)
qla2x00_nv_write(ha, 0);
qla2x00_nv_deselect(ha);
}
/**
* qla2x00_nvram_request() - Sends read command to NVRAM and gets data from
* NVRAM.
* @ha: HA context
* @nv_cmd: NVRAM command
*
* Bit definitions for NVRAM command:
*
* Bit 26 = start bit
* Bit 25, 24 = opcode
* Bit 23-16 = address
* Bit 15-0 = write data
*
* Returns the word read from nvram @addr.
*/
static uint16_t
qla2x00_nvram_request(scsi_qla_host_t *ha, uint32_t nv_cmd)
{
uint8_t cnt;
device_reg_t *reg = ha->iobase;
uint16_t data = 0;
uint16_t reg_data;
/* Send command to NVRAM. */
nv_cmd <<= 5;
for (cnt = 0; cnt < 11; cnt++) {
if (nv_cmd & BIT_31)
qla2x00_nv_write(ha, NVR_DATA_OUT);
else
qla2x00_nv_write(ha, 0);
nv_cmd <<= 1;
}
/* Read data from NVRAM. */
for (cnt = 0; cnt < 16; cnt++) {
WRT_REG_WORD(&reg->nvram, NVR_SELECT | NVR_CLOCK);
NVRAM_DELAY();
data <<= 1;
reg_data = RD_REG_WORD(&reg->nvram);
if (reg_data & NVR_DATA_IN)
data |= BIT_0;
WRT_REG_WORD(&reg->nvram, NVR_SELECT);
NVRAM_DELAY();
RD_REG_WORD(&reg->nvram); /* PCI Posting. */
}
/* Deselect chip. */
WRT_REG_WORD(&reg->nvram, NVR_DESELECT);
NVRAM_DELAY();
RD_REG_WORD(&reg->nvram); /* PCI Posting. */
return (data);
}
/**
* qla2x00_nv_write() - Clean NVRAM operations.
* @ha: HA context
*/
void
qla2x00_nv_deselect(scsi_qla_host_t *ha)
{
device_reg_t *reg = ha->iobase;
WRT_REG_WORD(&reg->nvram, NVR_DESELECT);
NVRAM_DELAY();
RD_REG_WORD(&reg->nvram); /* PCI Posting. */
}
/**
* qla2x00_nv_write() - Prepare for NVRAM read/write operation.
* @ha: HA context
* @data: Serial interface selector
*/
void
qla2x00_nv_write(scsi_qla_host_t *ha, uint16_t data)
{
device_reg_t *reg = ha->iobase;
WRT_REG_WORD(&reg->nvram, data | NVR_SELECT);
NVRAM_DELAY();
RD_REG_WORD(&reg->nvram); /* PCI Posting. */
WRT_REG_WORD(&reg->nvram, data | NVR_SELECT | NVR_CLOCK);
NVRAM_DELAY();
RD_REG_WORD(&reg->nvram); /* PCI Posting. */
WRT_REG_WORD(&reg->nvram, data | NVR_SELECT);
NVRAM_DELAY();
RD_REG_WORD(&reg->nvram); /* PCI Posting. */
}
/*
* Flash support routines
*/
/**
* qla2x00_flash_enable() - Setup flash for reading and writing.
* @ha: HA context
*/
void
qla2x00_flash_enable(scsi_qla_host_t *ha)
{
uint16_t data;
device_reg_t *reg = ha->iobase;
data = RD_REG_WORD(&reg->ctrl_status);
data |= CSR_FLASH_ENABLE;
WRT_REG_WORD(&reg->ctrl_status, data);
}
/**
* qla2x00_flash_disable() - Disable flash and allow RISC to run.
* @ha: HA context
*/
void
qla2x00_flash_disable(scsi_qla_host_t *ha)
{
uint16_t data;
device_reg_t *reg = ha->iobase;
data = RD_REG_WORD(&reg->ctrl_status);
data &= ~(CSR_FLASH_ENABLE);
WRT_REG_WORD(&reg->ctrl_status, data);
}
/**
* qla2x00_read_flash_byte() - Reads a byte from flash
* @ha: HA context
* @addr: Address in flash to read
*
* A word is read from the chip, but, only the lower byte is valid.
*
* Returns the byte read from flash @addr.
*/
uint8_t
qla2x00_read_flash_byte(scsi_qla_host_t *ha, uint32_t addr)
{
uint16_t data;
uint16_t bank_select;
device_reg_t *reg = ha->iobase;
/* Setup bit 16 of flash address. */
bank_select = RD_REG_WORD(&reg->ctrl_status);
if ((addr & BIT_16) && ((bank_select & CSR_FLASH_64K_BANK) == 0)) {
bank_select |= CSR_FLASH_64K_BANK;
WRT_REG_WORD(&reg->ctrl_status, bank_select);
} else if (((addr & BIT_16) == 0) &&
(bank_select & CSR_FLASH_64K_BANK)) {
bank_select &= ~(CSR_FLASH_64K_BANK);
WRT_REG_WORD(&reg->ctrl_status, bank_select);
}
WRT_REG_WORD(&reg->flash_address, (uint16_t)addr);
data = qla2x00_debounce_register(&reg->flash_data);
return ((uint8_t)data);
}
/**
* qla2x00_write_flash_byte() - Write a byte to flash
* @ha: HA context
* @addr: Address in flash to write
* @data: Data to write
*/
static void
qla2x00_write_flash_byte(scsi_qla_host_t *ha, uint32_t addr, uint8_t data)
{
uint16_t bank_select;
device_reg_t *reg = ha->iobase;
/* Setup bit 16 of flash address. */
bank_select = RD_REG_WORD(&reg->ctrl_status);
if ((addr & BIT_16) && ((bank_select & CSR_FLASH_64K_BANK) == 0)) {
bank_select |= CSR_FLASH_64K_BANK;
WRT_REG_WORD(&reg->ctrl_status, bank_select);
} else if (((addr & BIT_16) == 0) &&
(bank_select & CSR_FLASH_64K_BANK)) {
bank_select &= ~(CSR_FLASH_64K_BANK);
WRT_REG_WORD(&reg->ctrl_status, bank_select);
}
WRT_REG_WORD(&reg->flash_address, (uint16_t)addr);
WRT_REG_WORD(&reg->flash_data, (uint16_t)data);
}
/**
* qla2x00_poll_flash() - Polls flash for completion.
* @ha: HA context
* @addr: Address in flash to poll
* @poll_data: Data to be polled
* @mid: Flash manufacturer ID
*
* This function polls the device until bit 7 of what is read matches data
* bit 7 or until data bit 5 becomes a 1. If that hapens, the flash ROM timed
* out (a fatal error). The flash book recommeds reading bit 7 again after
* reading bit 5 as a 1.
*
* Returns 0 on success, else non-zero.
*/
static uint8_t
qla2x00_poll_flash(scsi_qla_host_t *ha,
uint32_t addr, uint8_t poll_data, uint8_t mid)
{
uint8_t status;
uint8_t flash_data;
uint32_t cnt;
int failed_pass;
status = 1;
failed_pass = 1;
/* Wait for 30 seconds for command to finish. */
poll_data &= BIT_7;
for (cnt = 3000000; cnt; cnt--) {
flash_data = qla2x00_read_flash_byte(ha, addr);
if ((flash_data & BIT_7) == poll_data) {
status = 0;
break;
}
if (mid != 0x40 && mid != 0xda) {
if (flash_data & BIT_5)
failed_pass--;
if (failed_pass < 0)
break;
}
udelay(10);
barrier();
}
return (status);
}
/**
* qla2x00_program_flash_address() - Programs a flash address
* @ha: HA context
* @addr: Address in flash to program
* @data: Data to be written in flash
* @mid: Flash manufacturer ID
*
* Returns 0 on success, else non-zero.
*/
static uint8_t
qla2x00_program_flash_address(scsi_qla_host_t *ha,
uint32_t addr, uint8_t data, uint8_t mid)
{
/* Write Program Command Sequence */
qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);
qla2x00_write_flash_byte(ha, 0x5555, 0xa0);
qla2x00_write_flash_byte(ha, addr, data);
/* Wait for write to complete. */
return (qla2x00_poll_flash(ha, addr, data, mid));
}
/**
* qla2x00_erase_flash_sector() - Erase a flash sector.
* @ha: HA context
* @addr: Flash sector to erase
* @sec_mask: Sector address mask
* @mid: Flash manufacturer ID
*
* Returns 0 on success, else non-zero.
*/
static uint8_t
qla2x00_erase_flash_sector(scsi_qla_host_t *ha,
uint32_t addr, uint32_t sec_mask, uint8_t mid)
{
/* Individual Sector Erase Command Sequence */
qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);
qla2x00_write_flash_byte(ha, 0x5555, 0x80);
qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);
if (mid == 0xda)
qla2x00_write_flash_byte(ha, addr & sec_mask, 0x10);
else
qla2x00_write_flash_byte(ha, addr & sec_mask, 0x30);
udelay(150);
/* Wait for erase to complete. */
return (qla2x00_poll_flash(ha, addr, 0x80, mid));
}
/**
* qla2x00_get_flash_manufacturer() - Read manufacturer ID from flash chip.
* @ha: HA context
*
* Returns the manufacturer's ID read from the flash chip.
*/
uint8_t
qla2x00_get_flash_manufacturer(scsi_qla_host_t *ha)
{
uint8_t manuf_id;
qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);
qla2x00_write_flash_byte(ha, 0x5555, 0x90);
manuf_id = qla2x00_read_flash_byte(ha, 0x0001);
return (manuf_id);
}
/**
* qla2x00_get_flash_version() - Read version information from flash.
* @ha: HA context
*
* Returns QLA_SUCCESS on successful retrieval of flash version.
*/
uint16_t
qla2x00_get_flash_version(scsi_qla_host_t *ha)
{
uint16_t ret = QLA_SUCCESS;
uint32_t loop_cnt = 1; /* this is for error exit only */
uint32_t pcir_adr;
ENTER(__func__);
qla2x00_flash_enable(ha);
do { /* Loop once to provide quick error exit */
/* Match signature */
if (!(qla2x00_read_flash_byte(ha, 0) == 0x55 &&
qla2x00_read_flash_byte(ha, 1) == 0xaa)) {
/* No signature */
DEBUG2(printk("scsi(%ld): No matching FLASH "
"signature.\n", ha->host_no));
ret = QLA_FUNCTION_FAILED;
break;
}
pcir_adr = qla2x00_read_flash_byte(ha, 0x18) & 0xff;
/* validate signature of PCI data structure */
if ((qla2x00_read_flash_byte(ha, pcir_adr)) == 'P' &&
(qla2x00_read_flash_byte(ha, pcir_adr + 1)) == 'C' &&
(qla2x00_read_flash_byte(ha, pcir_adr + 2)) == 'I' &&
(qla2x00_read_flash_byte(ha, pcir_adr + 3)) == 'R') {
/* Read version */
ha->optrom_minor =
qla2x00_read_flash_byte(ha, pcir_adr + 0x12);
ha->optrom_major =
qla2x00_read_flash_byte(ha, pcir_adr + 0x13);
DEBUG3(printk("%s(): got %d.%d.\n",
__func__, ha->optrom_major, ha->optrom_minor));
} else {
/* error */
DEBUG2(printk("%s(): PCI data struct not found. "
"pcir_adr=%x.\n",
__func__, pcir_adr));
ret = QLA_FUNCTION_FAILED;
break;
}
} while (--loop_cnt);
qla2x00_flash_disable(ha);
LEAVE(__func__);
return (ret);
}
/**
* qla2x00_get_flash_image() - Read image from flash chip.
* @ha: HA context
* @image: Buffer to receive flash image
*
* Returns 0 on success, else non-zero.
*/
uint16_t
qla2x00_get_flash_image(scsi_qla_host_t *ha, uint8_t *image)
{
uint32_t addr;
uint32_t midpoint;
uint8_t *data;
device_reg_t *reg = ha->iobase;
midpoint = FLASH_IMAGE_SIZE / 2;
qla2x00_flash_enable(ha);
WRT_REG_WORD(&reg->nvram, 0);
for (addr = 0, data = image; addr < FLASH_IMAGE_SIZE; addr++, data++) {
if (addr == midpoint)
WRT_REG_WORD(&reg->nvram, NVR_SELECT);
*data = qla2x00_read_flash_byte(ha, addr);
}
qla2x00_flash_disable(ha);
return (0);
}
/**
* qla2x00_set_flash_image() - Write image to flash chip.
* @ha: HA context
* @image: Source image to write to flash
*
* Returns 0 on success, else non-zero.
*/
uint16_t
qla2x00_set_flash_image(scsi_qla_host_t *ha, uint8_t *image)
{
uint16_t status;
uint32_t addr;
uint32_t midpoint;
uint32_t sec_mask;
uint32_t rest_addr;
uint8_t mid;
uint8_t sec_number;
uint8_t data;
device_reg_t *reg = ha->iobase;
status = 0;
sec_number = 0;
/* Reset ISP chip. */
WRT_REG_WORD(&reg->ctrl_status, CSR_ISP_SOFT_RESET);
qla2x00_flash_enable(ha);
do { /* Loop once to provide quick error exit */
/* Structure of flash memory based on manufacturer */
mid = qla2x00_get_flash_manufacturer(ha);
if (mid == 0x6d) {
// Am29LV001 part
rest_addr = 0x1fff;
sec_mask = 0x1e000;
} else if (mid == 0x40) {
// Mostel v29c51001 part
rest_addr = 0x1ff;
sec_mask = 0x1fe00;
} else if (mid == 0xbf) {
// SST39sf10 part
rest_addr = 0xfff;
sec_mask = 0x1f000;
} else if (mid == 0xda) {
// Winbond W29EE011 part
rest_addr = 0x7f;
sec_mask = 0x1ff80;
addr = 0;
if (qla2x00_erase_flash_sector(ha, addr, sec_mask,
mid)) {
status = 1;
break;
}
} else {
// Am29F010 part
rest_addr = 0x3fff;
sec_mask = 0x1c000;
}
midpoint = FLASH_IMAGE_SIZE / 2;
for (addr = 0; addr < FLASH_IMAGE_SIZE; addr++) {
data = *image++;
/* Are we at the beginning of a sector? */
if (!(addr & rest_addr)) {
if (addr == midpoint)
WRT_REG_WORD(&reg->nvram, NVR_SELECT);
/* Then erase it */
if (qla2x00_erase_flash_sector(ha, addr,
sec_mask, mid)) {
status = 1;
break;
}
sec_number++;
}
if (mid == 0x6d) {
if (sec_number == 1 &&
(addr == (rest_addr - 1))) {
rest_addr = 0x0fff;
sec_mask = 0x1f000;
} else if (sec_number == 3 && (addr & 0x7ffe)) {
rest_addr = 0x3fff;
sec_mask = 0x1c000;
}
}
if (qla2x00_program_flash_address(ha, addr, data,
mid)) {
status = 1;
break;
}
}
} while (0);
qla2x00_flash_disable(ha);
return (status);
}
/******************************************************************************
* QLOGIC LINUX SOFTWARE
*
* QLogic ISP2x00 device driver for Linux 2.6.x
* Copyright (C) 2003 QLogic Corporation
* (www.qlogic.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.
*
******************************************************************************/
/*
* Driver version
*/
#define QLA2XXX_VERSION "8.00.00b8"
#define QLA_DRIVER_MAJOR_VER 8
#define QLA_DRIVER_MINOR_VER 0
#define QLA_DRIVER_PATCH_VER 0
#define QLA_DRIVER_BETA_VER 8
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment