diff options
Diffstat (limited to 'sys/dev/isp')
| -rw-r--r-- | sys/dev/isp/DriverManual.txt | 634 | ||||
| -rw-r--r-- | sys/dev/isp/Hardware.txt | 304 | ||||
| -rw-r--r-- | sys/dev/isp/isp.c | 7012 | ||||
| -rw-r--r-- | sys/dev/isp/isp_freebsd.c | 3257 | ||||
| -rw-r--r-- | sys/dev/isp/isp_freebsd.h | 521 | ||||
| -rw-r--r-- | sys/dev/isp/isp_ioctl.h | 195 | ||||
| -rw-r--r-- | sys/dev/isp/isp_library.c | 1725 | ||||
| -rw-r--r-- | sys/dev/isp/isp_library.h | 164 | ||||
| -rw-r--r-- | sys/dev/isp/isp_pci.c | 2506 | ||||
| -rw-r--r-- | sys/dev/isp/isp_sbus.c | 874 | ||||
| -rw-r--r-- | sys/dev/isp/isp_target.c | 1315 | ||||
| -rw-r--r-- | sys/dev/isp/isp_target.h | 703 | ||||
| -rw-r--r-- | sys/dev/isp/isp_tpublic.h | 372 | ||||
| -rw-r--r-- | sys/dev/isp/ispmbox.h | 986 | ||||
| -rw-r--r-- | sys/dev/isp/ispreg.h | 1035 | ||||
| -rw-r--r-- | sys/dev/isp/ispvar.h | 892 |
16 files changed, 22495 insertions, 0 deletions
diff --git a/sys/dev/isp/DriverManual.txt b/sys/dev/isp/DriverManual.txt new file mode 100644 index 000000000000..ea1968f9f883 --- /dev/null +++ b/sys/dev/isp/DriverManual.txt @@ -0,0 +1,634 @@ +/* $FreeBSD$ */ + + Driver Theory of Operation Manual + +1. Introduction + +This is a short text document that will describe the background, goals +for, and current theory of operation for the joint Fibre Channel/SCSI +HBA driver for QLogic hardware. + +Because this driver is an ongoing project, do not expect this manual +to remain entirely up to date. Like a lot of software engineering, the +ultimate documentation is the driver source. However, this manual should +serve as a solid basis for attempting to understand where the driver +started and what is trying to be accomplished with the current source. + +The reader is expected to understand the basics of SCSI and Fibre Channel +and to be familiar with the range of platforms that Solaris, Linux and +the variant "BSD" Open Source systems are available on. A glossary and +a few references will be placed at the end of the document. + +There will be references to functions and structures within the body of +this document. These can be easily found within the source using editor +tags or grep. There will be few code examples here as the code already +exists where the reader can easily find it. + +2. A Brief History for this Driver + +This driver originally started as part of work funded by NASA Ames +Research Center's Numerical Aerodynamic Simulation center ("NAS" for +short) for the QLogic PCI 1020 and 1040 SCSI Host Adapters as part of my +work at porting the NetBSD Operating System to the Alpha architectures +(specifically the AlphaServer 8200 and 8400 platforms). In short, it +started just as simple single SCSI HBA driver for just the purpose of +running off a SCSI disk. This work took place starting in January, 1997. + +Because the first implementation was for NetBSD, which runs on a very +large number of platforms, and because NetBSD supported both systems with +SBus cards (e.g., Sun SPARC systems) as well as systems with PCI cards, +and because the QLogic SCSI cards came in both SBus and PCI versions, the +initial implementation followed the very thoughtful NetBSD design tenet +of splitting drivers into what are called MI (for Machine Independent) +and MD (Machine Dependent) portions. The original design therefore was +from the premise that the driver would drive both SBus and PCI card +variants. These busses are similar but have quite different constraints, +and while the QLogic SBus and PCI cards are very similar, there are some +significant differences. + +After this initial goal had been met, there began to be some talk about +looking into implementing Fibre Channel mass storage at NAS. At this time +the QLogic 2100 FC/AL HBA was about to become available. After looking at +the way it was designed I concluded that it was so darned close to being +just like the SCSI HBAs that it would be insane to *not* leverage off of +the existing driver. So, we ended up with a driver for NetBSD that drove +PCI and SBus SCSI cards, and now also drove the QLogic 2100 FC-AL HBA. + +After this, ports to non-NetBSD platforms became interesting as well. +This took the driver out of the interest with NAS and into interested +support from a number of other places. Since the original NetBSD +development, the driver has been ported to FreeBSD, OpenBSD, Linux, +Solaris, and two proprietary systems. Following from the original MI/MD +design of NetBSD, a rather successful attempt has been made to keep the +Operating System Platform differences segregated and to a minimum. + +Along the way, support for the 2200 as well as full fabric and target +mode support has been added, and 2300 support as well as an FC-IP stack +are planned. + +3. Driver Design Goals + +The driver has not started out as one normally would do such an effort. +Normally you design via top-down methodologies and set an intial goal +and meet it. This driver has had a design goal that changes from almost +the very first. This has been an extremely peculiar, if not risque, +experience. As a consequence, this section of this document contains +a bit of "reconstruction after the fact" in that the design goals are +as I perceive them to be now- not necessarily what they started as. + +The primary design goal now is to have a driver that can run both the +SCSI and Fibre Channel SCSI prototocols on multiple OS platforms with +as little OS platform support code as possible. + +The intended support targets for SCSI HBAs is to support the single and +dual channel PCI Ultra2 and PCI Ultra3 cards as well as the older PCI +Ultra single channel cards and SBus cards. + +The intended support targets for Fibre Channel HBAs is the 2100, 2200 +and 2300 PCI cards. + +Fibre Channel support should include complete fabric and public loop +as well as private loop and private loop, direct-attach topologies. +FC-IP support is also a goal. + +For both SCSI and Fibre Channel, simultaneous target/initiator mode support +is a goal. + +Pure, raw, performance is not a primary goal of this design. This design, +because it has a tremendous amount of code common across multiple +platforms, will undoubtedly never be able to beat the performance of a +driver that is specifically designed for a single platform and a single +card. However, it is a good strong secondary goal to make the performance +penalties in this design as small as possible. + +Another primary aim, which almost need not be stated, is that the +implementation of platform differences must not clutter up the common +code with platform specific defines. Instead, some reasonable layering +semantics are defined such that platform specifics can be kept in the +platform specific code. + +4. QLogic Hardware Architecture + +In order to make the design of this driver more intelligible, some +description of the Qlogic hardware architecture is in order. This will +not be an exhaustive description of how this card works, but will +note enough of the important features so that the driver design is +hopefully clearer. + +4.1 Basic QLogic hardware + +The QLogic HBA cards all contain a tiny 16-bit RISC-like processor and +varying sizes of SRAM. Each card contains a Bus Interface Unit (BIU) +as appropriate for the host bus (SBus or PCI). The BIUs allow access +to a set of dual-ranked 16 bit incoming and outgoing mailbox registers +as well as access to control registers that control the RISC or access +other portions of the card (e.g., Flash BIOS). The term 'dual-ranked' +means that at the same host visible address if you write a mailbox +register, that is a write to an (incoming, to the HBA) mailbox register, +while a read to the same address reads another (outgoing, to the HBA) +mailbox register with completely different data. Each HBA also then has +core and auxillary logic which either is used to interface to a SCSI bus +(or to external bus drivers that connect to a SCSI bus), or to connect +to a Fibre Channel bus. + +4.2 Basic Control Interface + +There are two principle I/O control mechanisms by which the driver +communicates with and controls the QLogic HBA. The first mechanism is to +use the incoming mailbox registers to interrupt and issue commands to +the RISC processor (with results usually, but not always, ending up in +the ougtoing mailbox registers). The second mechanism is to establish, +via mailbox commands, circular request and response queues in system +memory that are then shared between the QLogic and the driver. The +request queue is used to queue requests (e.g., I/O requests) for the +QLogic HBA's RISC engine to copy into the HBA memory and process. The +result queue is used by the QLogic HBA's RISC engine to place results of +requests read from the request queue, as well as to place notification +of asynchronous events (e.g., incoming commands in target mode). + +To give a bit more precise scale to the preceding description, the QLogic +HBA has 8 dual-ranked 16 bit mailbox registers, mostly for out-of-band +control purposes. The QLogic HBA then utilizes a circular request queue +of 64 byte fixed size Queue Entries to receive normal initiator mode +I/O commands (or continue target mode requests). The request queue may +be up to 256 elements for the QLogic 1020 and 1040 chipsets, but may +be quite larger for the QLogic 12X0/12160 SCSI and QLogic 2X00 Fibre +Channel chipsets. + +In addition to synchronously initiated usage of mailbox commands by +the host system, the QLogic may also deliver asynchronous notifications +solely in outgoing mailbox registers. These asynchronous notifications in +mailboxes may be things like notification of SCSI Bus resets, or that the +Fabric Name server has sent a change notification, or even that a specific +I/O command completed without error (this is called 'Fast Posting' +and saves the QLogic HBA from having to write a response queue entry). + +The QLogic HBA is an interrupting card, and when servicing an interrupt +you really only have to check for either a mailbox interrupt or an +interrupt notification that the the response queue has an entry to +be dequeued. + +4.3 Fibre Channel SCSI out of SCSI + +QLogic took the approach in introducing the 2X00 cards to just treat +FC-AL as a 'fat' SCSI bus (a SCSI bus with more than 15 targets). All +of the things that you really need to do with Fibre Channel with respect +to providing FC-4 services on top of a Class 3 connection are performed +by the RISC engine on the QLogic card itself. This means that from +an HBA driver point of view, very little needs to change that would +distinguish addressing a Fibre Channel disk from addressing a plain +old SCSI disk. + +However, in the details it's not *quite* that simple. For example, in +order to manage Fabric Connections, the HBA driver has to do explicit +binding of entities it's queried from the name server to specific 'target' +ids (targets, in this case, being a virtual entity). + +Still- the HBA firmware does really nearly all of the tedious management +of Fibre Channel login state. The corollary to this sometimes is the +lack of ability to say why a particular login connection to a Fibre +Channel disk is not working well. + +There are clear limits with the QLogic card in managing fabric devices. +The QLogic manages local loop devices (LoopID or Target 0..126) itself, +but for the management of fabric devices, it has an absolute limit of +253 simultaneous connections (256 entries less 3 reserved entries). + +5. Driver Architecture + +5.1 Driver Assumptions + +The first basic assumption for this driver is that the requirements for +a SCSI HBA driver for any system is that of a 2 or 3 layer model where +there are SCSI target device drivers (drivers which drive SCSI disks, +SCSI tapes, and so on), possibly a middle services layer, and a bottom +layer that manages the transport of SCSI CDB's out a SCSI bus (or across +Fibre Channel) to a SCSI device. It's assumed that each SCSI command is +a separate structure (or pointer to a structure) that contains the SCSI +CDB and a place to store SCSI Status and SCSI Sense Data. + +This turns out to be a pretty good assumption. All of the Open Source +systems (*BSD and Linux) and most of the proprietary systems have this +kind of structure. This has been the way to manage SCSI subsystems for +at least ten years. + +There are some additional basic assumptions that this driver makes- primarily +in the arena of basic simple services like memory zeroing, memory copying, +delay, sleep, microtime functions. It doesn't assume much more than this. + +5.2 Overall Driver Architecture + +The driver is split into a core (machine independent) module and platform +and bus specific outer modules (machine dependent). + +The core code (in the files isp.c, isp_inline.h, ispvar.h, ispreg.h and +ispmbox.h) handles: + + + Chipset recognition and reset and firmware download (isp_reset) + + Board Initialization (isp_init) + + First level interrupt handling (response retrieval) (isp_intr) + + A SCSI command queueing entry point (isp_start) + + A set of control services accessed either via local requirements within + the core module or via an externally visible control entry point + (isp_control). + +The platform/bus specific modules (and definitions) depend on each +platform, and they provide both definitions and functions for the core +module's use. Generally a platform module set is split into a bus +dependent module (where configuration is begun from and bus specific +support functions reside) and relatively thin platform specific layer +which serves as the interconnect with the rest of this platform's SCSI +subsystem. + +For ease of bus specific access issues, a centralized soft state +structure is maintained for each HBA instance (struct ispsoftc). This +soft state structure contains a machine/bus dependent vector (mdvec) +for functions that read and write hardware registers, set up DMA for the +request/response queues and fibre channel scratch area, set up and tear +down DMA mappings for a SCSI command, provide a pointer to firmware to +load, and other minor things. + +The machine dependent outer module must provide functional entry points +for the core module: + + + A SCSI command completion handoff point (isp_done) + + An asynchronous event handler (isp_async) + + A logging/printing function (isp_prt) + +The machine dependent outer module code must also provide a set of +abstracting definitions which is what the core module utilizes heavily +to do its job. These are discussed in detail in the comments in the +file ispvar.h, but to give a sense of the range of what is required, +let's illustrate two basic classes of these defines. + +The first class are "structure definition/access" class. An +example of these would be: + + XS_T Platform SCSI transaction type (i.e., command for HBA) + .. + XS_TGT(xs) gets the target from an XS_T + .. + XS_TAG_TYPE(xs) which type of tag to use + .. + +The second class are 'functional' class definitions. Some examples of +this class are: + + MEMZERO(dst, src) platform zeroing function + .. + MBOX_WAIT_COMPLETE(struct ispsoftc *) wait for mailbox cmd to be done + +Note that the former is likely to be simple replacement with bzero or +memset on most systems, while the latter could be quite complex. + +This soft state structure also contains different parameter information +based upon whether this is a SCSI HBA or a Fibre Channel HBA (which is +filled in by the code module). + +In order to clear up what is undoubtedly a seeming confusion of +interconnects, a description of the typical flow of code that performs +boards initialization and command transactions may help. + +5.3 Initialization Code Flow + +Typically a bus specific module for a platform (e.g., one that wants +to configure a PCI card) is entered via that platform's configuration +methods. If this module recognizes a card and can utilize or construct the +space for the HBA instance softc, it does so, and initializes the machine +dependent vector as well as any other platform specific information that +can be hidden in or associated with this structure. + +Configuration at this point usually involves mapping in board registers +and registering an interrupt. It's quite possible that the core module's +isp_intr function is adequate to be the interrupt entry point, but often +it's more useful have a bus specific wrapper module that calls isp_intr. + +After mapping and interrupt registry is done, isp_reset is called. +Part of the isp_reset call may cause callbacks out to the bus dependent +module to perform allocation and/or mapping of Request and Response +queues (as well as a Fibre Channel scratch area if this is a Fibre +Channel HBA). The reason this is considered 'bus dependent' is that +only the bus dependent module may have the information that says how +one could perform I/O mapping and dependent (e.g., on a Solaris system) +on the Request and Reponse queues. Another callback can enable the *use* +of interrupts should this platform be able to finish configuration in +interrupt driven mode. + +If isp_reset is successful at resetting the QLogic chipset and downloading +new firmware (if available) and setting it running, isp_init is called. If +isp_init is successful in doing initial board setups (including reading +NVRAM from the QLogic card), then this bus specicic module will call the +platform dependent module that takes the appropriate steps to 'register' +this HBA with this platform's SCSI subsystem. Examining either the +OpenBSD or the NetBSD isp_pci.c or isp_sbus.c files may assist the reader +here in clarifying some of this. + +5.4 Initiator Mode Command Code Flow + +A succesful execution of isp_init will lead to the driver 'registering' +itself with this platform's SCSI subsystem. One assumed action for this +is the registry of a function the the SCSI subsystem for this platform +will call when it has a SCSI command to run. + +The platform specific module function that receives this will do whatever +it needs to to prepare this command for execution in the core module. This +sounds vague, but it's also very flexible. In principle, this could be +a complete marshalling/demarshalling of this platform's SCSI command +structure (should it be impossible to represent in an XS_T). In addition, +this function can also block commands from running (if, e.g., Fibre +Channel loop state would preclude successful starting of the command). + +When it's ready to do so, the function isp_start is called with this +command. This core module tries to allocate request queue space for +this command. It also calls through the machine dependent vector +function to make sure any DMA mapping for this command is done. + +Now, DMA mapping here is possibly a misnomer, as more than just +DMA mapping can be done in this bus dependent function. This is +also the place where any endian byte-swizzling will be done. At any +rate, this function is called last because the process of establishing +DMA addresses for any command may in fact consume more Request Queue +entries than there are currently available. If the mapping and other +functions are successful, the QLogic mailbox inbox pointer register +is updated to indicate to the QLogic that it has a new request to +read. + +If this function is unsuccessful, policy as to what to do at this point is +left to the machine dependent platform function which called isp_start. In +some platforms, temporary resource shortages can be handled by the main +SCSI subsystem. In other platforms, the machine dependent code has to +handle this. + +In order to keep track of commands that are in progress, the soft state +structure contains an array of 'handles' that are associated with each +active command. When you send a command to the QLogic firmware, a portion +of the Request Queue entry can contain a non-zero handle identifier so +that at a later point in time in reading either a Response Queue entry +or from a Fast Posting mailbox completion interrupt, you can take this +handle to find the command you were waiting on. It should be noted that +this is probably one of the most dangerous areas of this driver. Corrupted +handles will lead to system panics. + +At some later point in time an interrupt will occur. Eventually, +isp_intr will be called. This core module will determine what the cause +of the interrupt is, and if it is for a completing command. That is, +it'll determine the handle and fetch the pointer to the command out of +storage within the soft state structure. Skipping over a lot of details, +the machine dependent code supplied function isp_done is called with the +pointer to the completing command. This would then be the glue layer that +informs the SCSI subsystem for this platform that a command is complete. + +5.5 Asynchronous Events + +Interrupts occur for events other than commands (mailbox or request queue +started commands) completing. These are called Asynchronous Mailbox +interrupts. When some external event causes the SCSI bus to be reset, +or when a Fibre Channel loop changes state (e.g., a LIP is observed), +this generates such an asynchronous event. + +Each platform module has to provide an isp_async entry point that will +handle a set of these. This isp_async entry point also handles things +which aren't properly async events but are simply natural outgrowths +of code flow for another core function (see discussion on fabric device +management below). + +5.6 Target Mode Code Flow + +This section could use a lot of expansion, but this covers the basics. + +The QLogic cards, when operating in target mode, follow a code flow that is +essentially the inverse of that for intiator mode describe above. In this +scenario, an interrupt occurs, and present on the Response Queue is a +queue entry element defining a new command arriving from an initiator. + +This is passed to possibly external target mode handler. This driver +provides some handling for this in a core module, but also leaves +things open enough that a completely different target mode handler +may accept this incoming queue entry. + +The external target mode handler then turns around forms up a response +to this 'response' that just arrived which is then placed on the Request +Queue and handled very much like an initiator mode command (i.e., calling +the bus dependent DMA mapping function). If this entry completes the +command, no more need occur. But often this handles only part of the +requested command, so the QLogic firmware will rewrite the response +to the initial 'response' again onto the Response Queue, whereupon the +target mode handler will respond to that, and so on until the command +is completely handled. + +Because almost no platform provides basic SCSI Subsystem target mode +support, this design has been left extremely open ended, and as such +it's a bit hard to describe in more detail than this. + +5.7 Locking Assumptions + +The observant reader by now is likely to have asked the question, "but what +about locking? Or interrupt masking" by now. + +The basic assumption about this is that the core module does not know +anything directly about locking or interrupt masking. It may assume that +upon entry (e.g., via isp_start, isp_control, isp_intr) that appropriate +locking and interrupt masking has been done. + +The platform dependent code may also therefore assume that if it is +called (e.g., isp_done or isp_async) that any locking or masking that +was in place upon the entry to the core module is still there. It is up +to the platform dependent code to worry about avoiding any lock nesting +issues. As an example of this, the Linux implementation simply queues +up commands completed via the callout to isp_done, which it then pushes +out to the SCSI subsystem after a return from it's calling isp_intr is +executed (and locks dropped appropriately, as well as avoidance of deep +interrupt stacks). + +Recent changes in the design have now eased what had been an original +requirement that the while in the core module no locks or interrupt +masking could be dropped. It's now up to each platform to figure out how +to implement this. This is principally used in the execution of mailbox +commands (which are principally used for Loop and Fabric management via +the isp_control function). + +5.8 SCSI Specifics + +The driver core or platform dependent architecture issues that are specific +to SCSI are few. There is a basic assumption that the QLogic firmware +supported Automatic Request sense will work- there is no particular provision +for disabling it's usage on a per-command basis. + +5.9 Fibre Channel Specifics + +Fibre Channel presents an interesting challenge here. The QLogic firmware +architecture for dealing with Fibre Channel as just a 'fat' SCSI bus +is fine on the face of it, but there are some subtle and not so subtle +problems here. + +5.9.1 Firmware State + +Part of the initialization (isp_init) for Fibre Channel HBAs involves +sending a command (Initialize Control Block) that establishes Node +and Port WWNs as well as topology preferences. After this occurs, +the QLogic firmware tries to traverese through serveral states: + + FW_CONFIG_WAIT + FW_WAIT_AL_PA + FW_WAIT_LOGIN + FW_READY + FW_LOSS_OF_SYNC + FW_ERROR + FW_REINIT + FW_NON_PART + +It starts with FW_CONFIG_WAIT, attempts to get an AL_PA (if on an FC-AL +loop instead of being connected as an N-port), waits to log into all +FC-AL loop entities and then hopefully transitions to FW_READY state. + +Clearly, no command should be attempted prior to FW_READY state is +achieved. The core internal function isp_fclink_test (reachable via +isp_control with the ISPCTL_FCLINK_TEST function code). This function +also determines connection topology (i.e., whether we're attached to a +fabric or not). + +5.9.2. Loop State Transitions- From Nil to Ready + +Once the firmware has transitioned to a ready state, then the state of the +connection to either arbitrated loop or to a fabric has to be ascertained, +and the identity of all loop members (and fabric members validated). + +This can be very complicated, and it isn't made easy in that the QLogic +firmware manages PLOGI and PRLI to devices that are on a local loop, but +it is the driver that must manage PLOGI/PRLI with devices on the fabric. + +In order to manage this state an eight level staging of current "Loop" +(where "Loop" is taken to mean FC-AL or N- or F-port connections) states +in the following ascending order: + + LOOP_NIL + LOOP_LIP_RCVD + LOOP_PDB_RCVD + LOOP_SCANNING_FABRIC + LOOP_FSCAN_DONE + LOOP_SCANNING_LOOP + LOOP_LSCAN_DONE + LOOP_SYNCING_PDB + LOOP_READY + +When the core code initializes the QLogic firmware, it sets the loop +state to LOOP_NIL. The first 'LIP Received' asynchronous event sets state +to LOOP_LIP_RCVD. This should be followed by a "Port Database Changed" +asynchronous event which will set the state to LOOP_PDB_RCVD. Each of +these states, when entered, causes an isp_async event call to the +machine dependent layers with the ISPASYNC_CHANGE_NOTIFY code. + +After the state of LOOP_PDB_RCVD is reached, the internal core function +isp_scan_fabric (reachable via isp_control(..ISPCTL_SCAN_FABRIC)) will, +if the connection is to a fabric, use Simple Name Server mailbox mediated +commands to dump the entire fabric contents. For each new entity, an +isp_async event will be generated that says a Fabric device has arrived +(ISPASYNC_FABRIC_DEV). The function that isp_async must perform in this +step is to insert possibly remove devices that it wants to have the +QLogic firmware log into (at LOOP_SYNCING_PDB state level)). + +After this has occurred, the state LOOP_FSCAN_DONE is set, and then the +internal function isp_scan_loop (isp_control(...ISPCTL_SCAN_LOOP)) can +be called which will then scan for any local (FC-AL) entries by asking +for each possible local loop id the QLogic firmware for a Port Database +entry. It's at this level some entries cached locally are purged +or shifting loopids are managed (see section 5.9.4). + +The final step after this is to call the internal function isp_pdb_sync +(isp_control(..ISPCTL_PDB_SYNC)). The purpose of this function is to +then perform the PLOGI/PRLI functions for fabric devices. The next state +entered after this is LOOP_READY, which means that the driver is ready +to process commands to send to Fibre Channel devices. + +5.9.3 Fibre Channel variants of Initiator Mode Code Flow + +The code flow in isp_start for Fibre Channel devices is the same as it is +for SCSI devices, but with a notable exception. + +Maintained within the fibre channel specific portion of the driver soft +state structure is a distillation of the existing population of both +local loop and fabric devices. Because Loop IDs can shift on a local +loop but we wish to retain a 'constant' Target ID (see 5.9.4), this +is indexed directly via the Target ID for the command (XS_TGT(xs)). + +If there is a valid entry for this Target ID, the command is started +(with the stored 'Loop ID'). If not the command is completed with +the error that is just like a SCSI Selection Timeout error. + +This code is currently somewhat in transition. Some platforms to +do firmware and loop state management (as described above) at this +point. Other platforms manage this from the machine dependent layers. The +important function to watch in this respect is isp_fc_runstate (in +isp_inline.h). + +5.9.4 "Target" in Fibre Channel is a fixed virtual construct + +Very few systems can cope with the notion that "Target" for a disk +device can change while you're using it. But one of the properties of +for arbitrated loop is that the physical bus address for a loop member +(the AL_PA) can change depending on when and how things are inserted in +the loop. + +To illustrate this, let's take an example. Let's say you start with a +loop that has 5 disks in it. At boot time, the system will likely find +them and see them in this order: + +disk# Loop ID Target ID +disk0 0 0 +disk1 1 1 +disk2 2 2 +disk3 3 3 +disk4 4 4 + +The driver uses 'Loop ID' when it forms requests to send a comamnd to +each disk. However, it reports to NetBSD that things exist as 'Target +ID'. As you can see here, there is perfect correspondence between disk, +Loop ID and Target ID. + +Let's say you add a new disk between disk2 and disk3 while things are +running. You don't really often see this, but you *could* see this where +the loop has to renegotiate, and you end up with: + +disk# Loop ID Target ID +disk0 0 0 +disk1 1 1 +disk2 2 2 +diskN 3 ? +disk3 4 ? +disk4 5 ? + +Clearly, you don't want disk3 and disk4's "Target ID" to change while you're +running since currently mounted filesystems will get trashed. + +What the driver is supposed to do (this is the function of isp_scan_loop), +is regenerate things such that the following then occurs: + +disk# Loop ID Target ID +disk0 0 0 +disk1 1 1 +disk2 2 2 +diskN 3 5 +disk3 4 3 +disk4 5 4 + +So, "Target" is a virtual entity that is maintained while you're running. + +6. Glossary + +HBA - Host Bus Adapter + +SCSI - Small Computer + +7. References + +Various URLs of interest: + +http://www.netbsd.org - NetBSD's Web Page +http://www.openbsd.org - OpenBSD's Web Page +http://www.freebsd.org - FreeBSD's Web Page + +http://www.t10.org - ANSI SCSI Commitee's Web Page + (SCSI Specs) +http://www.t11.org - NCITS Device Interface Web Page + (Fibre Channel Specs) + diff --git a/sys/dev/isp/Hardware.txt b/sys/dev/isp/Hardware.txt new file mode 100644 index 000000000000..75f01bf7dd36 --- /dev/null +++ b/sys/dev/isp/Hardware.txt @@ -0,0 +1,304 @@ +/* $FreeBSD$ */ + + Hardware that is Known To or Should Work with This Driver + + +0. Intro + + This is not an endorsement for hardware vendors (there will be + no "where to buy" URLs here with a couple of exception). This + is simply a list of things I know work, or should work, plus + maybe a couple of notes as to what you should do to make it + work. Corrections accepted. Even better would be to send me + hardware to I can test it. + + I'll put a rough range of costs in US$ that I know about. No doubt + it'll differ from your expectations. + +1. HBAs + +Qlogic 2100, 2102 + 2200, 2202, 2204 + + There are various suffices that indicate copper or optical + connectors, or 33 vs. 66MHz PCI bus operation. None of these + have a software impact. + + Approx cost: 1K$ for a 2200 + +Qlogic 2300, 2312 + + These are the new 2-Gigabit cards. Optical only. + + Approx cost: ?????? + + +Antares P-0033, P-0034, P-0036 + + There many other vendors that use the Qlogic 2X00 chipset. Some older + 2100 boards (not on this list) have a bug in the ROM that causes a + failure to download newer firmware that is larger than 0x7fff words. + + Approx cost: 850$ for a P-0036 + + + + In general, the 2200 class chip is to be preferred. + + +2. Hubs + +Vixel 1000 +Vixel 2000 + Of the two, the 1000 (7 ports, vs. 12 ports) has had fewer problems- + it's an old workhorse. + + + Approx cost: 1.5K$ for Vixel 1000, 2.5K$ for 2000 + +Gadzoox Cappellix 3000 + Don't forget to use telnet to configure the Cappellix ports + to the role you're using them for- otherwise things don't + work well at all. + + (cost: I have no idea... certainly less than a switch) + +3. Switches + +Brocade Silkworm II +Brocade 2400 +(other brocades should be fine) + + Especially with revision 2 or higher f/w, this is now best + of breed for fabrics or segmented loop (which Brocade + calls "QuickLoop"). + + For the Silkworm II, set operating mode to "Tachyon" (mode 3). + + The web interace isn't good- but telnet is what I prefer anyhow. + + You can't connect a Silkworm II and the other Brocades together + as E-ports to make a large fabric (at least with the f/w *I* + had for the Silkworm II). + + Approx cost of a Brocade 2400 with no GBICs is about 8K$ when + I recently checked the US Government SEWP price list- no doubt + it'll be a bit more for others. I'd assume around 10K$. + +ANCOR SA-8 + + This also is a fine switch, but you have to use a browser + with working java to manage it- which is a bit of a pain. + This also supports fabric and segmented loop. + + These switches don't form E-ports with each other for a larger + fabric. + + (cost: no idea) + +McData (model unknown) + + I tried one exactly once for 30 minutes. Seemed to work once + I added the "register FC4 types" command to the driver. + + (cost: very very expensive, 40K$ plus) + +4. Cables/GBICs + + Multimode optical is adequate for Fibre Channel- the same cable is + used for Gigabit Ethernet. + + Copper DB-9 and Copper HSS-DC connectors are also fine. Copper && + Optical both are rated to 1.026Gbit- copper is naturally shorter + (the longest I've used is a 15meter cable but it's supposed to go + longer). + + The reason to use copper instead of optical is that if step on one of + the really fat DB-9 cables you can get, it'll survive. Optical usually + dies quickly if you step on it. + + Approx cost: I don't know what optical is- you can expect to pay maybe + a 100$ for a 3m copper cable. + +GBICs- + + I use Finisar copper and IBM Opticals. + + Approx Cost: Copper GBICs are 70$ each. Opticals are twice that or more. + + +Vendor: (this is the one exception I'll make because it turns out to be + an incredible pain to find FC copper cabling and GBICs- the source I + use for GBICs and copper cables is http://www.scsi-cables.com) + + +Other: + There now is apparently a source for little connector boards + to connect to bare drives: http://www.cinonic.com. + + +5. Storage JBODs/RAID + +JMR 4-Bay + + Rinky-tink, but a solid 4 bay loop only entry model. + + I paid 1000$ for mine- overprice, IMO. + +JMR Fortra + + I rather like this box. The blue LEDs are a very nice touch- you + can see them very clearly from 50 feet away. + + I paid 2000$ for one used. + +Sun A5X00 + + Very expensive (in my opinion) but well crafted. Has two SES + instances, so you can use the ses driver (and the example + code in /usr/share/examples) for power/thermal/slot monitoring. + + Approx Cost: The last I saw for a price list item on this was 22K$ + for an unpopulated (no disk drive) A5X00. + + +DataDirect E1000 RAID + + Don't connect both SCSI and FC interfaces at the same time- a SCSI + reset will cause the DataDirect to think you want to use the SCSI + interface and a LIP on the FC interface will cause it to think you + want to use the FC interface. Use only one connector at a time so + both you and the DataDirect are sure about what you want. + + Cost: I have no idea. + +Veritas ServPoint + + This is a software storage virtualization engine that + runs on Sparc/Solaris in target mode for frontend + and with other FC or SCSI as the backend storage. FreeBSD + has been used extensively to test it. + + + Cost: I have no idea. + +6. Disk Drives + + I have used lots of different Seagate and a few IBM drives and + typically have had few problems with them. These are the bare + drives with 40-pin SCA connectors in back. They go into the JBODs + you assemble. + + Seagate does make, but I can no longer find, a little paddleboard + single drive connector that goes from DB-9 FC to the 40-pin SCA + connector- primarily for you to try and evaluate a single FC drive. + + All FC-AL disk drives are dual ported (i.e., have separte 'A' and + 'B' ports- which are completely separate loops). This seems to work + reasonably enough, but I haven't tested it much. It really depends + on the JBOD you put them to carry this dual port to the outside + world. The JMR boxes have it. The Sun A5X00 you have to pay for + an extra IB card to carry it out. + + Approx Cost: You'll find that FC drives are the same cost if not + slightly cheaper than the equivalent Ultra3 SCSI drives. + +7. Recommended Configurations + +These are recommendations that are biased toward the cautious side. They +do not represent formal engineering commitments- just suggestions as to +what I would expect to work. + +A. The simpletst form of a connection topology I can suggest for +a small SAN (i.e., replacement for SCSI JBOD/RAID): + +HOST +2xxx <----------> Single Unit of Storage (JBOD, RAID) + +This is called a PL_DA (Private Loop, Direct Attach) topology. + +B. The next most simple form of a connection topology I can suggest for +a medium local SAN (where you do not plan to do dynamic insertion +and removal of devices while I/Os are active): + +HOST +2xxx <----------> +-------- + | Vixel | + | 1000 | + | +<---> Storage + | | + | +<---> Storage + | | + | +<---> Storage + -------- + +This is a Private Loop topology. Remember that this can get very unstable +if you make it too long. A good practice is to try it in a staged fashion. + +It is possible with some units to "daisy chain", e.g.: + +HOST +2xxx <----------> (JBOD, RAID) <--------> (JBOD, RAID) + +In practice I have had poor results with these configurations. They *should* +work fine, but for both the JMR and the Sun A5X00 I tend to get LIP storms +and so the second unit just isn't seen and the loop isn't stable. + +Now, this could simply be my lack of clean, newer, h/w (or, in general, +a lack of h/w), but I would recommend the use of a hub if you want to +stay with Private Loop and have more than one FC target. + +You should also note this can begin to be the basis for a shared SAN +solution. For example, the above configuration can be extended to be: + +HOST +2xxx <----------> +-------- + | Vixel | + | 1000 | + | +<---> Storage + | | + | +<---> Storage + | | + | +<---> Storage +HOST | | +2xxx <----------> +-------- + +However, note that there is nothing to mediate locking of devices, and +it is also conceivable that the reboot of one host can, by causing +a LIP storm, cause problems with the I/Os from the other host. +(in other words, this topology hasn't really been made safe yet for +this driver). + +D. You can repeat the topology in #B with a switch that is set to be +in segmented loop mode. This avoids LIPs propagating where you don't +want them to- and this makes for a much more reliable, if more expensive, +SAN. + +E. The next level of complexity is a Switched Fabric. The following topology +is good when you start to begin to get to want more performance. Private +and Public Arbitrated Loop, while 100MB/s, is a shared medium. Direct +connections to a switch can run full-duplex at full speed. + +HOST +2xxx <----------> +--------- + | Brocade| + | 2400 | + | +<---> Storage + | | + | +<---> Storage + | | + | +<---> Storage +HOST | | +2xxx <----------> +--------- + + +I would call this the best configuration available now. It can expand +substantially if you cascade switches. + +There is a hard limit of about 253 devices for each Qlogic HBA- and the +fabric login policy is simplistic (log them in as you find them). If +somebody actually runs into a configuration that's larger, let me know +and I'll work on some tools that would allow you some policy choices +as to which would be interesting devices to actually connect to. + + diff --git a/sys/dev/isp/isp.c b/sys/dev/isp/isp.c new file mode 100644 index 000000000000..b880a45dec30 --- /dev/null +++ b/sys/dev/isp/isp.c @@ -0,0 +1,7012 @@ +/*- + * Copyright (c) 1997-2006 by Matthew Jacob + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Machine and OS Independent (well, as best as possible) + * code for the Qlogic ISP SCSI adapters. + */ +/* + * Inspiration and ideas about this driver are from Erik Moe's Linux driver + * (qlogicisp.c) and Dave Miller's SBus version of same (qlogicisp.c). Some + * ideas dredged from the Solaris driver. + */ + +/* + * Include header file appropriate for platform we're building on. + */ +#ifdef __NetBSD__ +#include <dev/ic/isp_netbsd.h> +#endif +#ifdef __FreeBSD__ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); +#include <dev/isp/isp_freebsd.h> +#endif +#ifdef __OpenBSD__ +#include <dev/ic/isp_openbsd.h> +#endif +#ifdef __linux__ +#include "isp_linux.h" +#endif +#ifdef __svr4__ +#include "isp_solaris.h" +#endif + +/* + * General defines + */ + +#define MBOX_DELAY_COUNT 1000000 / 100 + +/* + * Local static data + */ +static const char portshift[] = + "Target %d Loop ID 0x%x (Port 0x%x) => Loop 0x%x (Port 0x%x)"; +static const char portdup[] = + "Target %d duplicates Target %d- killing off both"; +static const char retained[] = + "Retaining Loop ID 0x%x for Target %d (Port 0x%x)"; +static const char lretained[] = + "Retained login of Target %d (Loop ID 0x%x) Port 0x%x"; +static const char plogout[] = + "Logging out Target %d at Loop ID 0x%x (Port 0x%x)"; +static const char plogierr[] = + "Command Error in PLOGI for Port 0x%x (0x%x)"; +static const char nopdb[] = + "Could not get PDB for Device @ Port 0x%x"; +static const char pdbmfail1[] = + "PDB Loop ID info for Device @ Port 0x%x does not match up (0x%x)"; +static const char pdbmfail2[] = + "PDB Port info for Device @ Port 0x%x does not match up (0x%x)"; +static const char ldumped[] = + "Target %d (Loop ID 0x%x) Port 0x%x dumped after login info mismatch"; +static const char notresp[] = + "Not RESPONSE in RESPONSE Queue (type 0x%x) @ idx %d (next %d) nlooked %d"; +static const char xact1[] = + "HBA attempted queued transaction with disconnect not set for %d.%d.%d"; +static const char xact2[] = + "HBA attempted queued transaction to target routine %d on target %d bus %d"; +static const char xact3[] = + "HBA attempted queued cmd for %d.%d.%d when queueing disabled"; +static const char pskip[] = + "SCSI phase skipped for target %d.%d.%d"; +static const char topology[] = + "Loop ID %d, Port ID 0x%x, Loop State 0x%x, Topology '%s'"; +static const char swrej[] = + "Fabric Nameserver rejected %s (Reason=0x%x Expl=0x%x) for Port ID 0x%x"; +static const char finmsg[] = + "%d.%d.%d: FIN dl%d resid %d STS 0x%x SKEY %c XS_ERR=0x%x"; +static const char sc0[] = + "%s CHAN %d FTHRSH %d IID %d RESETD %d RETRYC %d RETRYD %d ASD 0x%x"; +static const char sc1[] = + "%s RAAN 0x%x DLAN 0x%x DDMAB 0x%x CDMAB 0x%x SELTIME %d MQD %d"; +static const char sc2[] = "%s CHAN %d TGT %d FLAGS 0x%x 0x%x/0x%x"; +static const char sc3[] = "Generated"; +static const char sc4[] = "NVRAM"; +static const char bun[] = + "bad underrun for %d.%d (count %d, resid %d, status %s)"; + +/* + * Local function prototypes. + */ +static int isp_parse_async(ispsoftc_t *, uint16_t); +static int isp_handle_other_response(ispsoftc_t *, int, isphdr_t *, + uint16_t *); +static void +isp_parse_status(ispsoftc_t *, ispstatusreq_t *, XS_T *); +static void isp_fastpost_complete(ispsoftc_t *, uint16_t); +static int isp_mbox_continue(ispsoftc_t *); +static void isp_scsi_init(ispsoftc_t *); +static void isp_scsi_channel_init(ispsoftc_t *, int); +static void isp_fibre_init(ispsoftc_t *); +static void isp_mark_getpdb_all(ispsoftc_t *); +static int isp_getmap(ispsoftc_t *, fcpos_map_t *); +static int isp_getpdb(ispsoftc_t *, int, isp_pdb_t *); +static uint64_t isp_get_portname(ispsoftc_t *, int, int); +static int isp_fclink_test(ispsoftc_t *, int); +static const char *isp2100_fw_statename(int); +static int isp_pdb_sync(ispsoftc_t *); +static int isp_scan_loop(ispsoftc_t *); +static int isp_fabric_mbox_cmd(ispsoftc_t *, mbreg_t *); +static int isp_scan_fabric(ispsoftc_t *, int); +static void isp_register_fc4_type(ispsoftc_t *); +static void isp_fw_state(ispsoftc_t *); +static void isp_mboxcmd_qnw(ispsoftc_t *, mbreg_t *, int); +static void isp_mboxcmd(ispsoftc_t *, mbreg_t *, int); + +static void isp_update(ispsoftc_t *); +static void isp_update_bus(ispsoftc_t *, int); +static void isp_setdfltparm(ispsoftc_t *, int); +static int isp_read_nvram(ispsoftc_t *); +static void isp_rdnvram_word(ispsoftc_t *, int, uint16_t *); +static void isp_parse_nvram_1020(ispsoftc_t *, uint8_t *); +static void isp_parse_nvram_1080(ispsoftc_t *, int, uint8_t *); +static void isp_parse_nvram_12160(ispsoftc_t *, int, uint8_t *); +static void isp_parse_nvram_2100(ispsoftc_t *, uint8_t *); + +/* + * Reset Hardware. + * + * Hit the chip over the head, download new f/w if available and set it running. + * + * Locking done elsewhere. + */ + +void +isp_reset(ispsoftc_t *isp) +{ + mbreg_t mbs; + uint32_t code_org; + int loops, i, dodnld = 1; + char *btype = "????"; + + isp->isp_state = ISP_NILSTATE; + + /* + * Basic types (SCSI, FibreChannel and PCI or SBus) + * have been set in the MD code. We figure out more + * here. Possibly more refined types based upon PCI + * identification. Chip revision has been gathered. + * + * After we've fired this chip up, zero out the conf1 register + * for SCSI adapters and do other settings for the 2100. + */ + + /* + * Get the current running firmware revision out of the + * chip before we hit it over the head (if this is our + * first time through). Note that we store this as the + * 'ROM' firmware revision- which it may not be. In any + * case, we don't really use this yet, but we may in + * the future. + */ + if (isp->isp_touched == 0) { + /* + * First see whether or not we're sitting in the ISP PROM. + * If we've just been reset, we'll have the string "ISP " + * spread through outgoing mailbox registers 1-3. We do + * this for PCI cards because otherwise we really don't + * know what state the card is in and we could hang if + * we try this command otherwise. + * + * For SBus cards, we just do this because they almost + * certainly will be running firmware by now. + */ + if (ISP_READ(isp, OUTMAILBOX1) != 0x4953 || + ISP_READ(isp, OUTMAILBOX2) != 0x5020 || + ISP_READ(isp, OUTMAILBOX3) != 0x2020) { + /* + * Just in case it was paused... + */ + ISP_WRITE(isp, HCCR, HCCR_CMD_RELEASE); + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_ABOUT_FIRMWARE; + isp_mboxcmd(isp, &mbs, MBLOGNONE); + if (mbs.param[0] == MBOX_COMMAND_COMPLETE) { + isp->isp_romfw_rev[0] = mbs.param[1]; + isp->isp_romfw_rev[1] = mbs.param[2]; + isp->isp_romfw_rev[2] = mbs.param[3]; + } + } + isp->isp_touched = 1; + } + + DISABLE_INTS(isp); + + /* + * Set up default request/response queue in-pointer/out-pointer + * register indices. + */ + if (IS_23XX(isp)) { + isp->isp_rqstinrp = BIU_REQINP; + isp->isp_rqstoutrp = BIU_REQOUTP; + isp->isp_respinrp = BIU_RSPINP; + isp->isp_respoutrp = BIU_RSPOUTP; + ISP_WRITE(isp, isp->isp_rqstinrp, 0); + ISP_WRITE(isp, isp->isp_rqstoutrp, 0); + ISP_WRITE(isp, isp->isp_respinrp, 0); + ISP_WRITE(isp, isp->isp_respoutrp, 0); + } else { + isp->isp_rqstinrp = INMAILBOX4; + isp->isp_rqstoutrp = OUTMAILBOX4; + isp->isp_respinrp = OUTMAILBOX5; + isp->isp_respoutrp = INMAILBOX5; + } + + /* + * Put the board into PAUSE mode (so we can read the SXP registers + * or write FPM/FBM registers). + */ + ISP_WRITE(isp, HCCR, HCCR_CMD_PAUSE); + + if (IS_FC(isp)) { + switch (isp->isp_type) { + case ISP_HA_FC_2100: + btype = "2100"; + break; + case ISP_HA_FC_2200: + btype = "2200"; + break; + case ISP_HA_FC_2300: + btype = "2300"; + break; + case ISP_HA_FC_2312: + btype = "2312"; + break; + case ISP_HA_FC_2322: + btype = "2322"; + break; + case ISP_HA_FC_2422: + btype = "2422"; + break; + default: + break; + } + /* + * While we're paused, reset the FPM module and FBM fifos. + */ + ISP_WRITE(isp, BIU2100_CSR, BIU2100_FPM0_REGS); + ISP_WRITE(isp, FPM_DIAG_CONFIG, FPM_SOFT_RESET); + ISP_WRITE(isp, BIU2100_CSR, BIU2100_FB_REGS); + ISP_WRITE(isp, FBM_CMD, FBMCMD_FIFO_RESET_ALL); + ISP_WRITE(isp, BIU2100_CSR, BIU2100_RISC_REGS); + } else if (IS_1240(isp)) { + sdparam *sdp = isp->isp_param; + btype = "1240"; + isp->isp_clock = 60; + sdp->isp_ultramode = 1; + sdp++; + sdp->isp_ultramode = 1; + /* + * XXX: Should probably do some bus sensing. + */ + } else if (IS_ULTRA2(isp)) { + static const char m[] = "bus %d is in %s Mode"; + uint16_t l; + sdparam *sdp = isp->isp_param; + + isp->isp_clock = 100; + + if (IS_1280(isp)) + btype = "1280"; + else if (IS_1080(isp)) + btype = "1080"; + else if (IS_10160(isp)) + btype = "10160"; + else if (IS_12160(isp)) + btype = "12160"; + else + btype = "<UNKLVD>"; + + l = ISP_READ(isp, SXP_PINS_DIFF) & ISP1080_MODE_MASK; + switch (l) { + case ISP1080_LVD_MODE: + sdp->isp_lvdmode = 1; + isp_prt(isp, ISP_LOGCONFIG, m, 0, "LVD"); + break; + case ISP1080_HVD_MODE: + sdp->isp_diffmode = 1; + isp_prt(isp, ISP_LOGCONFIG, m, 0, "Differential"); + break; + case ISP1080_SE_MODE: + sdp->isp_ultramode = 1; + isp_prt(isp, ISP_LOGCONFIG, m, 0, "Single-Ended"); + break; + default: + isp_prt(isp, ISP_LOGERR, + "unknown mode on bus %d (0x%x)", 0, l); + break; + } + + if (IS_DUALBUS(isp)) { + sdp++; + l = ISP_READ(isp, SXP_PINS_DIFF|SXP_BANK1_SELECT); + l &= ISP1080_MODE_MASK; + switch(l) { + case ISP1080_LVD_MODE: + sdp->isp_lvdmode = 1; + isp_prt(isp, ISP_LOGCONFIG, m, 1, "LVD"); + break; + case ISP1080_HVD_MODE: + sdp->isp_diffmode = 1; + isp_prt(isp, ISP_LOGCONFIG, + m, 1, "Differential"); + break; + case ISP1080_SE_MODE: + sdp->isp_ultramode = 1; + isp_prt(isp, ISP_LOGCONFIG, + m, 1, "Single-Ended"); + break; + default: + isp_prt(isp, ISP_LOGERR, + "unknown mode on bus %d (0x%x)", 1, l); + break; + } + } + } else { + sdparam *sdp = isp->isp_param; + i = ISP_READ(isp, BIU_CONF0) & BIU_CONF0_HW_MASK; + switch (i) { + default: + isp_prt(isp, ISP_LOGALL, "Unknown Chip Type 0x%x", i); + /* FALLTHROUGH */ + case 1: + btype = "1020"; + isp->isp_type = ISP_HA_SCSI_1020; + isp->isp_clock = 40; + break; + case 2: + /* + * Some 1020A chips are Ultra Capable, but don't + * run the clock rate up for that unless told to + * do so by the Ultra Capable bits being set. + */ + btype = "1020A"; + isp->isp_type = ISP_HA_SCSI_1020A; + isp->isp_clock = 40; + break; + case 3: + btype = "1040"; + isp->isp_type = ISP_HA_SCSI_1040; + isp->isp_clock = 60; + break; + case 4: + btype = "1040A"; + isp->isp_type = ISP_HA_SCSI_1040A; + isp->isp_clock = 60; + break; + case 5: + btype = "1040B"; + isp->isp_type = ISP_HA_SCSI_1040B; + isp->isp_clock = 60; + break; + case 6: + btype = "1040C"; + isp->isp_type = ISP_HA_SCSI_1040C; + isp->isp_clock = 60; + break; + } + /* + * Now, while we're at it, gather info about ultra + * and/or differential mode. + */ + if (ISP_READ(isp, SXP_PINS_DIFF) & SXP_PINS_DIFF_MODE) { + isp_prt(isp, ISP_LOGCONFIG, "Differential Mode"); + sdp->isp_diffmode = 1; + } else { + sdp->isp_diffmode = 0; + } + i = ISP_READ(isp, RISC_PSR); + if (isp->isp_bustype == ISP_BT_SBUS) { + i &= RISC_PSR_SBUS_ULTRA; + } else { + i &= RISC_PSR_PCI_ULTRA; + } + if (i != 0) { + isp_prt(isp, ISP_LOGCONFIG, "Ultra Mode Capable"); + sdp->isp_ultramode = 1; + /* + * If we're in Ultra Mode, we have to be 60MHz clock- + * even for the SBus version. + */ + isp->isp_clock = 60; + } else { + sdp->isp_ultramode = 0; + /* + * Clock is known. Gronk. + */ + } + + /* + * Machine dependent clock (if set) overrides + * our generic determinations. + */ + if (isp->isp_mdvec->dv_clock) { + if (isp->isp_mdvec->dv_clock < isp->isp_clock) { + isp->isp_clock = isp->isp_mdvec->dv_clock; + } + } + + } + + /* + * Clear instrumentation + */ + isp->isp_intcnt = isp->isp_intbogus = 0; + + /* + * Do MD specific pre initialization + */ + ISP_RESET0(isp); + +again: + + /* + * Hit the chip over the head with hammer, + * and give the ISP a chance to recover. + */ + + if (IS_SCSI(isp)) { + ISP_WRITE(isp, BIU_ICR, BIU_ICR_SOFT_RESET); + /* + * A slight delay... + */ + USEC_DELAY(100); + + /* + * Clear data && control DMA engines. + */ + ISP_WRITE(isp, CDMA_CONTROL, + DMA_CNTRL_CLEAR_CHAN | DMA_CNTRL_RESET_INT); + ISP_WRITE(isp, DDMA_CONTROL, + DMA_CNTRL_CLEAR_CHAN | DMA_CNTRL_RESET_INT); + + + } else { + ISP_WRITE(isp, BIU2100_CSR, BIU2100_SOFT_RESET); + /* + * A slight delay... + */ + USEC_DELAY(100); + + /* + * Clear data && control DMA engines. + */ + ISP_WRITE(isp, CDMA2100_CONTROL, + DMA_CNTRL2100_CLEAR_CHAN | DMA_CNTRL2100_RESET_INT); + ISP_WRITE(isp, TDMA2100_CONTROL, + DMA_CNTRL2100_CLEAR_CHAN | DMA_CNTRL2100_RESET_INT); + ISP_WRITE(isp, RDMA2100_CONTROL, + DMA_CNTRL2100_CLEAR_CHAN | DMA_CNTRL2100_RESET_INT); + } + + /* + * Wait for ISP to be ready to go... + */ + loops = MBOX_DELAY_COUNT; + for (;;) { + if (IS_SCSI(isp)) { + if (!(ISP_READ(isp, BIU_ICR) & BIU_ICR_SOFT_RESET)) + break; + } else { + if (!(ISP_READ(isp, BIU2100_CSR) & BIU2100_SOFT_RESET)) + break; + } + USEC_DELAY(100); + if (--loops < 0) { + ISP_DUMPREGS(isp, "chip reset timed out"); + return; + } + } + + /* + * After we've fired this chip up, zero out the conf1 register + * for SCSI adapters and other settings for the 2100. + */ + + if (IS_SCSI(isp)) { + ISP_WRITE(isp, BIU_CONF1, 0); + } else { + ISP_WRITE(isp, BIU2100_CSR, 0); + } + + /* + * Reset RISC Processor + */ + ISP_WRITE(isp, HCCR, HCCR_CMD_RESET); + USEC_DELAY(100); + /* Clear semaphore register (just to be sure) */ + ISP_WRITE(isp, BIU_SEMA, 0); + + /* + * Establish some initial burst rate stuff. + * (only for the 1XX0 boards). This really should + * be done later after fetching from NVRAM. + */ + if (IS_SCSI(isp)) { + uint16_t tmp = isp->isp_mdvec->dv_conf1; + /* + * Busted FIFO. Turn off all but burst enables. + */ + if (isp->isp_type == ISP_HA_SCSI_1040A) { + tmp &= BIU_BURST_ENABLE; + } + ISP_SETBITS(isp, BIU_CONF1, tmp); + if (tmp & BIU_BURST_ENABLE) { + ISP_SETBITS(isp, CDMA_CONF, DMA_ENABLE_BURST); + ISP_SETBITS(isp, DDMA_CONF, DMA_ENABLE_BURST); + } + ISP_WRITE(isp, RISC_MTR, 0x1212); + } else { + ISP_WRITE(isp, RISC_MTR2100, 0x1212); + if (IS_2200(isp) || IS_23XX(isp)) { + ISP_WRITE(isp, HCCR, HCCR_2X00_DISABLE_PARITY_PAUSE); + } + } + + ISP_WRITE(isp, HCCR, HCCR_CMD_RELEASE); /* release paused processor */ + + /* + * Do MD specific post initialization + */ + ISP_RESET1(isp); + + /* + * Wait for everything to finish firing up. + * + * Avoid doing this on the 2312 because you can generate a PCI + * parity error (chip breakage). + */ + if (IS_23XX(isp)) { + USEC_DELAY(5); + } else { + loops = MBOX_DELAY_COUNT; + while (ISP_READ(isp, OUTMAILBOX0) == MBOX_BUSY) { + USEC_DELAY(100); + if (--loops < 0) { + isp_prt(isp, ISP_LOGERR, + "MBOX_BUSY never cleared on reset"); + return; + } + } + } + + /* + * Up until this point we've done everything by just reading or + * setting registers. From this point on we rely on at least *some* + * kind of firmware running in the card. + */ + + /* + * Do some sanity checking. + */ + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_NO_OP; + isp_mboxcmd(isp, &mbs, MBLOGALL); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + return; + } + + if (IS_SCSI(isp)) { + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_MAILBOX_REG_TEST; + mbs.param[1] = 0xdead; + mbs.param[2] = 0xbeef; + mbs.param[3] = 0xffff; + mbs.param[4] = 0x1111; + mbs.param[5] = 0xa5a5; + isp_mboxcmd(isp, &mbs, MBLOGALL); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + return; + } + if (mbs.param[1] != 0xdead || mbs.param[2] != 0xbeef || + mbs.param[3] != 0xffff || mbs.param[4] != 0x1111 || + mbs.param[5] != 0xa5a5) { + isp_prt(isp, ISP_LOGERR, + "Register Test Failed (0x%x 0x%x 0x%x 0x%x 0x%x)", + mbs.param[1], mbs.param[2], mbs.param[3], + mbs.param[4], mbs.param[5]); + return; + } + + } + + /* + * Download new Firmware, unless requested not to do so. + * This is made slightly trickier in some cases where the + * firmware of the ROM revision is newer than the revision + * compiled into the driver. So, where we used to compare + * versions of our f/w and the ROM f/w, now we just see + * whether we have f/w at all and whether a config flag + * has disabled our download. + */ + if ((isp->isp_mdvec->dv_ispfw == NULL) || + (isp->isp_confopts & ISP_CFG_NORELOAD)) { + dodnld = 0; + } + + if (IS_23XX(isp)) { + code_org = ISP_CODE_ORG_2300; + } else { + code_org = ISP_CODE_ORG; + } + if (dodnld) { + uint16_t *ptr = isp->isp_mdvec->dv_ispfw; + + isp->isp_mbxworkp = &ptr[1]; + isp->isp_mbxwrk0 = ptr[3] - 1; + isp->isp_mbxwrk1 = code_org + 1; + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_WRITE_RAM_WORD; + mbs.param[1] = code_org; + mbs.param[2] = ptr[0]; + isp_mboxcmd(isp, &mbs, MBLOGNONE); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + isp_prt(isp, ISP_LOGERR, + "F/W download failed at word %d", + isp->isp_mbxwrk1 - code_org); + dodnld = 0; + goto again; + } + + /* + * If we're a 2322, the firmware actually comes in three chunks. + * We loaded the first at the code_org address. The other two + * chunks, which follow right after each other in memory here, + * get loaded at addresses specfied at offset 0x9..0xB. + */ + if (IS_2322(isp)) { + uint32_t nxtaddr; + uint32_t offset; + + nxtaddr = ptr[3]; + ptr = &ptr[nxtaddr]; + offset = ptr[5] | (((uint32_t)(ptr[4] & 0x3f)) << 16); + isp->isp_mbxworkp = &ptr[1]; + isp->isp_mbxwrk0 = ptr[3] - 1; + isp->isp_mbxwrk1 = offset + 1; + isp->isp_mbxwrk8 = (offset + 1) >> 16; + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_WRITE_RAM_WORD_EXTENDED; + mbs.param[1] = offset; + mbs.param[2] = ptr[0]; + mbs.param[8] = offset >> 16; + isp_mboxcmd(isp, &mbs, MBLOGNONE); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + isp_prt(isp, ISP_LOGERR, + "Receive Sequencer F/W Load Failed"); + return; + } + + nxtaddr = ptr[3]; + ptr = &ptr[nxtaddr]; + offset = ptr[5] | (((uint32_t)(ptr[4] & 0x3f)) << 16); + isp->isp_mbxworkp = &ptr[1]; + isp->isp_mbxwrk0 = ptr[3] - 1; + isp->isp_mbxwrk1 = (offset + 1); + isp->isp_mbxwrk8 = (offset + 1) >> 16; + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_WRITE_RAM_WORD_EXTENDED; + mbs.param[1] = offset; + mbs.param[2] = ptr[0]; + mbs.param[8] = offset >> 16; + isp_mboxcmd(isp, &mbs, MBLOGNONE); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + isp_prt(isp, ISP_LOGERR, + "Transmit Sequencer F/W Load Failed"); + return; + } + } else { + /* + * Verify that it downloaded correctly. + */ + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_VERIFY_CHECKSUM; + mbs.param[1] = code_org; + isp_mboxcmd(isp, &mbs, MBLOGNONE); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + isp_prt(isp, ISP_LOGERR, + "Downloaded RISC Code Checksum Failure"); + return; + } + + } + isp->isp_loaded_fw = 1; + } else { + isp->isp_loaded_fw = 0; + isp_prt(isp, ISP_LOGDEBUG2, "skipping f/w download"); + } + + /* + * Now start it rolling. + * + * If we didn't actually download f/w, + * we still need to (re)start it. + */ + + + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_EXEC_FIRMWARE; + mbs.param[1] = code_org; + if (IS_2322(isp) || IS_24XX(isp)) { + if (isp->isp_loaded_fw) { + mbs.param[2] = 0; + } else { + mbs.param[2] = 1; + } + } + isp_mboxcmd(isp, &mbs, MBLOGNONE); + if (IS_2322(isp) || IS_24XX(isp)) { + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + isp_prt(isp, ISP_LOGERR, "EXEC F/W failed: 0x%x", + mbs.param[0]); + return; + } + } + + /* + * Give it a chance to start. + */ + USEC_DELAY(500); + + if (IS_SCSI(isp)) { + /* + * Set CLOCK RATE, but only if asked to. + */ + if (isp->isp_clock) { + mbs.param[0] = MBOX_SET_CLOCK_RATE; + mbs.param[1] = isp->isp_clock; + isp_mboxcmd(isp, &mbs, MBLOGALL); + /* we will try not to care if this fails */ + } + } + + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_ABOUT_FIRMWARE; + isp_mboxcmd(isp, &mbs, MBLOGALL); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + return; + } + + /* + * The SBus firmware that we are using apparently does not return + * major, minor, micro revisions in the mailbox registers, which + * is really, really, annoying. + */ + if (ISP_SBUS_SUPPORTED && isp->isp_bustype == ISP_BT_SBUS) { + if (dodnld) { +#ifdef ISP_TARGET_MODE + isp->isp_fwrev[0] = 7; + isp->isp_fwrev[1] = 55; +#else + isp->isp_fwrev[0] = 1; + isp->isp_fwrev[1] = 37; +#endif + isp->isp_fwrev[2] = 0; + } + } else { + isp->isp_fwrev[0] = mbs.param[1]; + isp->isp_fwrev[1] = mbs.param[2]; + isp->isp_fwrev[2] = mbs.param[3]; + } + + isp_prt(isp, ISP_LOGCONFIG, + "Board Type %s, Chip Revision 0x%x, %s F/W Revision %d.%d.%d", + btype, isp->isp_revision, dodnld? "loaded" : "resident", + isp->isp_fwrev[0], isp->isp_fwrev[1], isp->isp_fwrev[2]); + + if (IS_FC(isp)) { + /* + * We do not believe firmware attributes for 2100 code less + * than 1.17.0, unless it's the firmware we specifically + * are loading. + * + * Note that all 22XX and 23XX f/w is greater than 1.X.0. + */ + if (!(ISP_FW_NEWER_THAN(isp, 1, 17, 0))) { +#ifdef USE_SMALLER_2100_FIRMWARE + FCPARAM(isp)->isp_fwattr = ISP_FW_ATTR_SCCLUN; +#else + FCPARAM(isp)->isp_fwattr = 0; +#endif + } else { + FCPARAM(isp)->isp_fwattr = mbs.param[6]; + isp_prt(isp, ISP_LOGDEBUG0, + "Firmware Attributes = 0x%x", mbs.param[6]); + } + if (IS_2KLOGIN(isp)) { + isp_prt(isp, ISP_LOGCONFIG, "2K Logins Supported"); + } + } + + if (isp->isp_romfw_rev[0] || isp->isp_romfw_rev[1] || + isp->isp_romfw_rev[2]) { + isp_prt(isp, ISP_LOGCONFIG, "Last F/W revision was %d.%d.%d", + isp->isp_romfw_rev[0], isp->isp_romfw_rev[1], + isp->isp_romfw_rev[2]); + } + + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_GET_FIRMWARE_STATUS; + isp_mboxcmd(isp, &mbs, MBLOGALL); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + return; + } + isp->isp_maxcmds = mbs.param[2]; + isp_prt(isp, ISP_LOGINFO, + "%d max I/O commands supported", mbs.param[2]); + isp_fw_state(isp); + + /* + * Set up DMA for the request and result mailboxes. + */ + if (ISP_MBOXDMASETUP(isp) != 0) { + isp_prt(isp, ISP_LOGERR, "Cannot setup DMA"); + return; + } + isp->isp_state = ISP_RESETSTATE; + + /* + * Okay- now that we have new firmware running, we now (re)set our + * notion of how many luns we support. This is somewhat tricky because + * if we haven't loaded firmware, we sometimes do not have an easy way + * of knowing how many luns we support. + * + * Expanded lun firmware gives you 32 luns for SCSI cards and + * 16384 luns for Fibre Channel cards. + * + * It turns out that even for QLogic 2100s with ROM 1.10 and above + * we do get a firmware attributes word returned in mailbox register 6. + * + * Because the lun is in a different position in the Request Queue + * Entry structure for Fibre Channel with expanded lun firmware, we + * can only support one lun (lun zero) when we don't know what kind + * of firmware we're running. + */ + if (IS_SCSI(isp)) { + if (dodnld) { + if (IS_ULTRA2(isp) || IS_ULTRA3(isp)) { + isp->isp_maxluns = 32; + } else { + isp->isp_maxluns = 8; + } + } else { + isp->isp_maxluns = 8; + } + } else { + if (FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) { + isp->isp_maxluns = 16384; + } else { + isp->isp_maxluns = 16; + } + } +} + +/* + * Initialize Parameters of Hardware to a known state. + * + * Locks are held before coming here. + */ + +void +isp_init(ispsoftc_t *isp) +{ + /* + * Must do this first to get defaults established. + */ + isp_setdfltparm(isp, 0); + if (IS_DUALBUS(isp)) { + isp_setdfltparm(isp, 1); + } + if (IS_FC(isp)) { + isp_fibre_init(isp); + } else { + isp_scsi_init(isp); + } +} + +static void +isp_scsi_init(ispsoftc_t *isp) +{ + sdparam *sdp_chan0, *sdp_chan1; + mbreg_t mbs; + + sdp_chan0 = isp->isp_param; + sdp_chan1 = sdp_chan0; + if (IS_DUALBUS(isp)) { + sdp_chan1++; + } + + /* + * If we have no role (neither target nor initiator), return. + */ + if (isp->isp_role == ISP_ROLE_NONE) { + return; + } + + /* First do overall per-card settings. */ + + /* + * If we have fast memory timing enabled, turn it on. + */ + if (sdp_chan0->isp_fast_mttr) { + ISP_WRITE(isp, RISC_MTR, 0x1313); + } + + /* + * Set Retry Delay and Count. + * You set both channels at the same time. + */ + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_SET_RETRY_COUNT; + mbs.param[1] = sdp_chan0->isp_retry_count; + mbs.param[2] = sdp_chan0->isp_retry_delay; + mbs.param[6] = sdp_chan1->isp_retry_count; + mbs.param[7] = sdp_chan1->isp_retry_delay; + + isp_mboxcmd(isp, &mbs, MBLOGALL); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + return; + } + + /* + * Set ASYNC DATA SETUP time. This is very important. + */ + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_SET_ASYNC_DATA_SETUP_TIME; + mbs.param[1] = sdp_chan0->isp_async_data_setup; + mbs.param[2] = sdp_chan1->isp_async_data_setup; + isp_mboxcmd(isp, &mbs, MBLOGALL); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + return; + } + + /* + * Set ACTIVE Negation State. + */ + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_SET_ACT_NEG_STATE; + mbs.param[1] = + (sdp_chan0->isp_req_ack_active_neg << 4) | + (sdp_chan0->isp_data_line_active_neg << 5); + mbs.param[2] = + (sdp_chan1->isp_req_ack_active_neg << 4) | + (sdp_chan1->isp_data_line_active_neg << 5); + + MEMZERO(&mbs, sizeof (mbs)); + isp_mboxcmd(isp, &mbs, MBLOGNONE); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + isp_prt(isp, ISP_LOGERR, + "failed to set active negation state (%d,%d), (%d,%d)", + sdp_chan0->isp_req_ack_active_neg, + sdp_chan0->isp_data_line_active_neg, + sdp_chan1->isp_req_ack_active_neg, + sdp_chan1->isp_data_line_active_neg); + /* + * But don't return. + */ + } + + /* + * Set the Tag Aging limit + */ + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_SET_TAG_AGE_LIMIT; + mbs.param[1] = sdp_chan0->isp_tag_aging; + mbs.param[2] = sdp_chan1->isp_tag_aging; + isp_mboxcmd(isp, &mbs, MBLOGALL); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + isp_prt(isp, ISP_LOGERR, "failed to set tag age limit (%d,%d)", + sdp_chan0->isp_tag_aging, sdp_chan1->isp_tag_aging); + return; + } + + /* + * Set selection timeout. + */ + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_SET_SELECT_TIMEOUT; + mbs.param[1] = sdp_chan0->isp_selection_timeout; + mbs.param[2] = sdp_chan1->isp_selection_timeout; + isp_mboxcmd(isp, &mbs, MBLOGALL); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + return; + } + + /* now do per-channel settings */ + isp_scsi_channel_init(isp, 0); + if (IS_DUALBUS(isp)) + isp_scsi_channel_init(isp, 1); + + /* + * Now enable request/response queues + */ + + if (IS_ULTRA2(isp) || IS_1240(isp)) { + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_INIT_RES_QUEUE_A64; + mbs.param[1] = RESULT_QUEUE_LEN(isp); + mbs.param[2] = DMA_WD1(isp->isp_result_dma); + mbs.param[3] = DMA_WD0(isp->isp_result_dma); + mbs.param[4] = 0; + mbs.param[6] = DMA_WD3(isp->isp_result_dma); + mbs.param[7] = DMA_WD2(isp->isp_result_dma); + isp_mboxcmd(isp, &mbs, MBLOGALL); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + return; + } + isp->isp_residx = mbs.param[5]; + + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_INIT_REQ_QUEUE_A64; + mbs.param[1] = RQUEST_QUEUE_LEN(isp); + mbs.param[2] = DMA_WD1(isp->isp_rquest_dma); + mbs.param[3] = DMA_WD0(isp->isp_rquest_dma); + mbs.param[5] = 0; + mbs.param[6] = DMA_WD3(isp->isp_result_dma); + mbs.param[7] = DMA_WD2(isp->isp_result_dma); + isp_mboxcmd(isp, &mbs, MBLOGALL); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + return; + } + isp->isp_reqidx = isp->isp_reqodx = mbs.param[4]; + } else { + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_INIT_RES_QUEUE; + mbs.param[1] = RESULT_QUEUE_LEN(isp); + mbs.param[2] = DMA_WD1(isp->isp_result_dma); + mbs.param[3] = DMA_WD0(isp->isp_result_dma); + mbs.param[4] = 0; + isp_mboxcmd(isp, &mbs, MBLOGALL); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + return; + } + isp->isp_residx = mbs.param[5]; + + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_INIT_REQ_QUEUE; + mbs.param[1] = RQUEST_QUEUE_LEN(isp); + mbs.param[2] = DMA_WD1(isp->isp_rquest_dma); + mbs.param[3] = DMA_WD0(isp->isp_rquest_dma); + mbs.param[5] = 0; + isp_mboxcmd(isp, &mbs, MBLOGALL); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + return; + } + isp->isp_reqidx = isp->isp_reqodx = mbs.param[4]; + } + + /* + * Turn on Fast Posting, LVD transitions + * + * Ultra2 F/W always has had fast posting (and LVD transitions) + * + * Ultra and older (i.e., SBus) cards may not. It's just safer + * to assume not for them. + */ + + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_SET_FW_FEATURES; + mbs.param[1] = 0; + if (IS_ULTRA2(isp)) + mbs.param[1] |= FW_FEATURE_LVD_NOTIFY; +#ifndef ISP_NO_RIO + if (IS_ULTRA2(isp) || IS_1240(isp)) + mbs.param[1] |= FW_FEATURE_RIO_16BIT; +#else + if (IS_ULTRA2(isp) || IS_1240(isp)) + mbs.param[1] |= FW_FEATURE_FAST_POST; +#endif + if (mbs.param[1] != 0) { + uint16_t sfeat = mbs.param[1]; + isp_mboxcmd(isp, &mbs, MBLOGALL); + if (mbs.param[0] == MBOX_COMMAND_COMPLETE) { + isp_prt(isp, ISP_LOGINFO, + "Enabled FW features (0x%x)", sfeat); + } + } + + /* + * Let the outer layers decide whether to issue a SCSI bus reset. + */ + isp->isp_state = ISP_INITSTATE; +} + +static void +isp_scsi_channel_init(ispsoftc_t *isp, int channel) +{ + sdparam *sdp; + mbreg_t mbs; + int tgt; + + sdp = isp->isp_param; + sdp += channel; + + /* + * Set (possibly new) Initiator ID. + */ + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_SET_INIT_SCSI_ID; + mbs.param[1] = (channel << 7) | sdp->isp_initiator_id; + isp_mboxcmd(isp, &mbs, MBLOGALL); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + return; + } + isp_prt(isp, ISP_LOGINFO, "Initiator ID is %d on Channel %d", + sdp->isp_initiator_id, channel); + + + /* + * Set current per-target parameters to an initial safe minimum. + */ + for (tgt = 0; tgt < MAX_TARGETS; tgt++) { + int lun; + uint16_t sdf; + + if (sdp->isp_devparam[tgt].dev_enable == 0) { + continue; + } +#ifndef ISP_TARGET_MODE + sdf = sdp->isp_devparam[tgt].goal_flags; + sdf &= DPARM_SAFE_DFLT; + /* + * It is not quite clear when this changed over so that + * we could force narrow and async for 1000/1020 cards, + * but assume that this is only the case for loaded + * firmware. + */ + if (isp->isp_loaded_fw) { + sdf |= DPARM_NARROW | DPARM_ASYNC; + } +#else + /* + * The !$*!)$!$)* f/w uses the same index into some + * internal table to decide how to respond to negotiations, + * so if we've said "let's be safe" for ID X, and ID X + * selects *us*, the negotiations will back to 'safe' + * (as in narrow/async). What the f/w *should* do is + * use the initiator id settings to decide how to respond. + */ + sdp->isp_devparam[tgt].goal_flags = sdf = DPARM_DEFAULT; +#endif + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_SET_TARGET_PARAMS; + mbs.param[1] = (channel << 15) | (tgt << 8); + mbs.param[2] = sdf; + if ((sdf & DPARM_SYNC) == 0) { + mbs.param[3] = 0; + } else { + mbs.param[3] = + (sdp->isp_devparam[tgt].goal_offset << 8) | + (sdp->isp_devparam[tgt].goal_period); + } + isp_prt(isp, ISP_LOGDEBUG0, + "Initial Settings bus%d tgt%d flags 0x%x off 0x%x per 0x%x", + channel, tgt, mbs.param[2], mbs.param[3] >> 8, + mbs.param[3] & 0xff); + isp_mboxcmd(isp, &mbs, MBLOGNONE); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + sdf = DPARM_SAFE_DFLT; + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_SET_TARGET_PARAMS; + mbs.param[1] = (tgt << 8) | (channel << 15); + mbs.param[2] = sdf; + mbs.param[3] = 0; + isp_mboxcmd(isp, &mbs, MBLOGALL); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + continue; + } + } + + /* + * We don't update any information directly from the f/w + * because we need to run at least one command to cause a + * new state to be latched up. So, we just assume that we + * converge to the values we just had set. + * + * Ensure that we don't believe tagged queuing is enabled yet. + * It turns out that sometimes the ISP just ignores our + * attempts to set parameters for devices that it hasn't + * seen yet. + */ + sdp->isp_devparam[tgt].actv_flags = sdf & ~DPARM_TQING; + for (lun = 0; lun < (int) isp->isp_maxluns; lun++) { + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_SET_DEV_QUEUE_PARAMS; + mbs.param[1] = (channel << 15) | (tgt << 8) | lun; + mbs.param[2] = sdp->isp_max_queue_depth; + mbs.param[3] = sdp->isp_devparam[tgt].exc_throttle; + isp_mboxcmd(isp, &mbs, MBLOGALL); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + break; + } + } + } + for (tgt = 0; tgt < MAX_TARGETS; tgt++) { + if (sdp->isp_devparam[tgt].dev_refresh) { + isp->isp_sendmarker |= (1 << channel); + isp->isp_update |= (1 << channel); + break; + } + } +} + +/* + * Fibre Channel specific initialization. + * + * Locks are held before coming here. + */ +static void +isp_fibre_init(ispsoftc_t *isp) +{ + fcparam *fcp; + isp_icb_t local, *icbp = &local; + mbreg_t mbs; + int loopid; + uint64_t nwwn, pwwn; + + fcp = isp->isp_param; + + /* + * Do this *before* initializing the firmware. + */ + isp_mark_getpdb_all(isp); + fcp->isp_fwstate = FW_CONFIG_WAIT; + fcp->isp_loopstate = LOOP_NIL; + + /* + * If we have no role (neither target nor initiator), return. + */ + if (isp->isp_role == ISP_ROLE_NONE) { + return; + } + + loopid = fcp->isp_loopid; + MEMZERO(icbp, sizeof (*icbp)); + icbp->icb_version = ICB_VERSION1; + + /* + * Firmware Options are either retrieved from NVRAM or + * are patched elsewhere. We check them for sanity here + * and make changes based on board revision, but otherwise + * let others decide policy. + */ + + /* + * If this is a 2100 < revision 5, we have to turn off FAIRNESS. + */ + if ((isp->isp_type == ISP_HA_FC_2100) && isp->isp_revision < 5) { + fcp->isp_fwoptions &= ~ICBOPT_FAIRNESS; + } + + /* + * We have to use FULL LOGIN even though it resets the loop too much + * because otherwise port database entries don't get updated after + * a LIP- this is a known f/w bug for 2100 f/w less than 1.17.0. + */ + if (!ISP_FW_NEWER_THAN(isp, 1, 17, 0)) { + fcp->isp_fwoptions |= ICBOPT_FULL_LOGIN; + } + + /* + * Insist on Port Database Update Async notifications + */ + fcp->isp_fwoptions |= ICBOPT_PDBCHANGE_AE; + + /* + * Make sure that target role reflects into fwoptions. + */ + if (isp->isp_role & ISP_ROLE_TARGET) { + fcp->isp_fwoptions |= ICBOPT_TGT_ENABLE; + } else { + fcp->isp_fwoptions &= ~ICBOPT_TGT_ENABLE; + } + + if (isp->isp_role & ISP_ROLE_INITIATOR) { + fcp->isp_fwoptions &= ~ICBOPT_INI_DISABLE; + } else { + fcp->isp_fwoptions |= ICBOPT_INI_DISABLE; + } + + /* + * Propagate all of this into the ICB structure. + */ + icbp->icb_fwoptions = fcp->isp_fwoptions; + icbp->icb_maxfrmlen = fcp->isp_maxfrmlen; + if (icbp->icb_maxfrmlen < ICB_MIN_FRMLEN || + icbp->icb_maxfrmlen > ICB_MAX_FRMLEN) { + isp_prt(isp, ISP_LOGERR, + "bad frame length (%d) from NVRAM- using %d", + fcp->isp_maxfrmlen, ICB_DFLT_FRMLEN); + icbp->icb_maxfrmlen = ICB_DFLT_FRMLEN; + } + icbp->icb_maxalloc = fcp->isp_maxalloc; + if (icbp->icb_maxalloc < 1) { + isp_prt(isp, ISP_LOGERR, + "bad maximum allocation (%d)- using 16", fcp->isp_maxalloc); + icbp->icb_maxalloc = 16; + } + icbp->icb_execthrottle = fcp->isp_execthrottle; + if (icbp->icb_execthrottle < 1) { + isp_prt(isp, ISP_LOGERR, + "bad execution throttle of %d- using 16", + fcp->isp_execthrottle); + icbp->icb_execthrottle = ICB_DFLT_THROTTLE; + } + icbp->icb_retry_delay = fcp->isp_retry_delay; + icbp->icb_retry_count = fcp->isp_retry_count; + icbp->icb_hardaddr = loopid; + if (icbp->icb_hardaddr > 125) { + /* + * We end up with these Loop IDs for F-Port topologies + */ + if (icbp->icb_hardaddr != 0xff && icbp->icb_hardaddr != 0x800) { + isp_prt(isp, ISP_LOGERR, + "bad hard address %u- resetting to zero", + icbp->icb_hardaddr); + } + icbp->icb_hardaddr = 0; + } + + /* + * Our life seems so much better with 2200s and later with + * the latest f/w if we set Hard Address. + */ + if (ISP_FW_NEWER_THAN(isp, 2, 2, 5)) { + icbp->icb_fwoptions |= ICBOPT_HARD_ADDRESS; + } + + /* + * Right now we just set extended options to prefer point-to-point + * over loop based upon some soft config options. + * + * NB: for the 2300, ICBOPT_EXTENDED is required. + */ + if (IS_2200(isp) || IS_23XX(isp)) { + icbp->icb_fwoptions |= ICBOPT_EXTENDED; + /* + * Prefer or force Point-To-Point instead Loop? + */ + switch(isp->isp_confopts & ISP_CFG_PORT_PREF) { + case ISP_CFG_NPORT: + icbp->icb_xfwoptions |= ICBXOPT_PTP_2_LOOP; + break; + case ISP_CFG_NPORT_ONLY: + icbp->icb_xfwoptions |= ICBXOPT_PTP_ONLY; + break; + case ISP_CFG_LPORT_ONLY: + icbp->icb_xfwoptions |= ICBXOPT_LOOP_ONLY; + break; + default: + icbp->icb_xfwoptions |= ICBXOPT_LOOP_2_PTP; + break; + } + if (IS_2200(isp)) { + if (ISP_FW_NEWER_THAN(isp, 1, 17, 0)) { + icbp->icb_xfwoptions |= ICBXOPT_RIO_16BIT; + icbp->icb_racctimer = 4; + icbp->icb_idelaytimer = 8; + } + icbp->icb_fwoptions |= ICBOPT_FAST_POST; + } else { + /* + * QLogic recommends that FAST Posting be turned + * off for 23XX cards and instead allow the HBA + * to write response queue entries and interrupt + * after a delay (ZIO). + */ + icbp->icb_fwoptions &= ~ICBOPT_FAST_POST; + if ((fcp->isp_xfwoptions & ICBXOPT_TIMER_MASK) == + ICBXOPT_ZIO) { + icbp->icb_xfwoptions |= ICBXOPT_ZIO; + icbp->icb_idelaytimer = 10; + } + if (isp->isp_confopts & ISP_CFG_ONEGB) { + icbp->icb_zfwoptions |= ICBZOPT_RATE_ONEGB; + } else if (isp->isp_confopts & ISP_CFG_TWOGB) { + icbp->icb_zfwoptions |= ICBZOPT_RATE_TWOGB; + } else { + icbp->icb_zfwoptions |= ICBZOPT_RATE_AUTO; + } + if (fcp->isp_zfwoptions & ICBZOPT_50_OHM) { + icbp->icb_zfwoptions |= ICBZOPT_50_OHM; + } + } + } + + + /* + * For 22XX > 2.1.26 && 23XX, set some options. + * XXX: Probably okay for newer 2100 f/w too. + */ + if (ISP_FW_NEWER_THAN(isp, 2, 26, 0)) { + /* + * Turn on LIP F8 async event (1) + * Turn on generate AE 8013 on all LIP Resets (2) + * Disable LIP F7 switching (8) + */ + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_SET_FIRMWARE_OPTIONS; + mbs.param[1] = 0xb; + mbs.param[2] = 0; + mbs.param[3] = 0; + isp_mboxcmd(isp, &mbs, MBLOGALL); + } + icbp->icb_logintime = ICB_LOGIN_TOV; + icbp->icb_lunetimeout = ICB_LUN_ENABLE_TOV; + + nwwn = ISP_NODEWWN(isp); + pwwn = ISP_PORTWWN(isp); + if (nwwn && pwwn) { + icbp->icb_fwoptions |= ICBOPT_BOTH_WWNS; + MAKE_NODE_NAME_FROM_WWN(icbp->icb_nodename, nwwn); + MAKE_NODE_NAME_FROM_WWN(icbp->icb_portname, pwwn); + isp_prt(isp, ISP_LOGDEBUG1, + "Setting ICB Node 0x%08x%08x Port 0x%08x%08x", + ((uint32_t) (nwwn >> 32)), + ((uint32_t) (nwwn & 0xffffffff)), + ((uint32_t) (pwwn >> 32)), + ((uint32_t) (pwwn & 0xffffffff))); + } else { + isp_prt(isp, ISP_LOGDEBUG1, "Not using any WWNs"); + icbp->icb_fwoptions &= ~(ICBOPT_BOTH_WWNS|ICBOPT_FULL_LOGIN); + } + icbp->icb_rqstqlen = RQUEST_QUEUE_LEN(isp); + if (icbp->icb_rqstqlen < 1) { + isp_prt(isp, ISP_LOGERR, "bad request queue length"); + } + icbp->icb_rsltqlen = RESULT_QUEUE_LEN(isp); + if (icbp->icb_rsltqlen < 1) { + isp_prt(isp, ISP_LOGERR, "bad result queue length"); + } + icbp->icb_rqstaddr[RQRSP_ADDR0015] = DMA_WD0(isp->isp_rquest_dma); + icbp->icb_rqstaddr[RQRSP_ADDR1631] = DMA_WD1(isp->isp_rquest_dma); + icbp->icb_rqstaddr[RQRSP_ADDR3247] = DMA_WD2(isp->isp_rquest_dma); + icbp->icb_rqstaddr[RQRSP_ADDR4863] = DMA_WD3(isp->isp_rquest_dma); + icbp->icb_respaddr[RQRSP_ADDR0015] = DMA_WD0(isp->isp_result_dma); + icbp->icb_respaddr[RQRSP_ADDR1631] = DMA_WD1(isp->isp_result_dma); + icbp->icb_respaddr[RQRSP_ADDR3247] = DMA_WD2(isp->isp_result_dma); + icbp->icb_respaddr[RQRSP_ADDR4863] = DMA_WD3(isp->isp_result_dma); + + isp_prt(isp, ISP_LOGDEBUG0, + "isp_fibre_init: fwopt 0x%x xfwopt 0x%x zfwopt 0x%x", + icbp->icb_fwoptions, icbp->icb_xfwoptions, icbp->icb_zfwoptions); + + FC_SCRATCH_ACQUIRE(isp); + isp_put_icb(isp, icbp, (isp_icb_t *)fcp->isp_scratch); + + /* + * Init the firmware + */ + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_INIT_FIRMWARE; + mbs.param[2] = DMA_WD1(fcp->isp_scdma); + mbs.param[3] = DMA_WD0(fcp->isp_scdma); + mbs.param[6] = DMA_WD3(fcp->isp_scdma); + mbs.param[7] = DMA_WD2(fcp->isp_scdma); + isp_prt(isp, ISP_LOGDEBUG0, "INIT F/W from %p (%08x%08x)", + fcp->isp_scratch, (uint32_t) ((uint64_t)fcp->isp_scdma >> 32), + (uint32_t) fcp->isp_scdma); + MEMORYBARRIER(isp, SYNC_SFORDEV, 0, sizeof (*icbp)); + isp_mboxcmd(isp, &mbs, MBLOGALL); + FC_SCRATCH_RELEASE(isp); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + return; + } + isp->isp_reqidx = isp->isp_reqodx = 0; + isp->isp_residx = 0; + isp->isp_sendmarker = 1; + + /* + * Whatever happens, we're now committed to being here. + */ + isp->isp_state = ISP_INITSTATE; +} + +/* + * Fibre Channel Support- get the port database for the id. + * + * Locks are held before coming here. Return 0 if success, + * else failure. + */ + +static int +isp_getmap(ispsoftc_t *isp, fcpos_map_t *map) +{ + fcparam *fcp = (fcparam *) isp->isp_param; + mbreg_t mbs; + + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_GET_FC_AL_POSITION_MAP; + mbs.param[2] = DMA_WD1(fcp->isp_scdma); + mbs.param[3] = DMA_WD0(fcp->isp_scdma); + mbs.param[6] = DMA_WD3(fcp->isp_scdma); + mbs.param[7] = DMA_WD2(fcp->isp_scdma); + FC_SCRATCH_ACQUIRE(isp); + MEMORYBARRIER(isp, SYNC_SFORDEV, 0, sizeof (fcpos_map_t)); + isp_mboxcmd(isp, &mbs, MBLOGALL & ~MBOX_COMMAND_PARAM_ERROR); + if (mbs.param[0] == MBOX_COMMAND_COMPLETE) { + MEMCPY(map, fcp->isp_scratch, sizeof (fcpos_map_t)); + map->fwmap = mbs.param[1] != 0; + FC_SCRATCH_RELEASE(isp); + return (0); + } + FC_SCRATCH_RELEASE(isp); + return (-1); +} + +static void +isp_mark_getpdb_all(ispsoftc_t *isp) +{ + fcparam *fcp = (fcparam *) isp->isp_param; + int i; + for (i = 0; i < MAX_FC_TARG; i++) { + fcp->portdb[i].valid = fcp->portdb[i].fabric_dev = 0; + } +} + +static int +isp_getpdb(ispsoftc_t *isp, int id, isp_pdb_t *pdbp) +{ + fcparam *fcp = (fcparam *) isp->isp_param; + mbreg_t mbs; + + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_GET_PORT_DB; + if (IS_2KLOGIN(isp)) { + mbs.param[1] = id; + mbs.ibits |= (1 << 10); + } else { + mbs.param[1] = id << 8; + } + mbs.param[2] = DMA_WD1(fcp->isp_scdma); + mbs.param[3] = DMA_WD0(fcp->isp_scdma); + mbs.param[6] = DMA_WD3(fcp->isp_scdma); + mbs.param[7] = DMA_WD2(fcp->isp_scdma); + FC_SCRATCH_ACQUIRE(isp); + MEMORYBARRIER(isp, SYNC_SFORDEV, 0, sizeof (isp_pdb_t)); + isp_mboxcmd(isp, &mbs, MBLOGALL & ~MBOX_COMMAND_PARAM_ERROR); + if (mbs.param[0] == MBOX_COMMAND_COMPLETE) { + isp_get_pdb(isp, (isp_pdb_t *)fcp->isp_scratch, pdbp); + FC_SCRATCH_RELEASE(isp); + return (0); + } + FC_SCRATCH_RELEASE(isp); + return (-1); +} + +static uint64_t +isp_get_portname(ispsoftc_t *isp, int loopid, int nodename) +{ + uint64_t wwn = 0; + mbreg_t mbs; + + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_GET_PORT_NAME; + if (IS_2KLOGIN(isp)) { + mbs.param[1] = loopid; + mbs.ibits |= (1 << 10); + if (nodename) { + mbs.param[10] = 1; + } + } else { + mbs.param[1] = loopid << 8; + if (nodename) { + mbs.param[1] |= 1; + } + } + isp_mboxcmd(isp, &mbs, MBLOGALL & ~MBOX_COMMAND_PARAM_ERROR); + if (mbs.param[0] == MBOX_COMMAND_COMPLETE) { + wwn = + (((uint64_t)(mbs.param[2] & 0xff)) << 56) | + (((uint64_t)(mbs.param[2] >> 8)) << 48) | + (((uint64_t)(mbs.param[3] & 0xff)) << 40) | + (((uint64_t)(mbs.param[3] >> 8)) << 32) | + (((uint64_t)(mbs.param[6] & 0xff)) << 24) | + (((uint64_t)(mbs.param[6] >> 8)) << 16) | + (((uint64_t)(mbs.param[7] & 0xff)) << 8) | + (((uint64_t)(mbs.param[7] >> 8))); + } + return (wwn); +} + +/* + * Make sure we have good FC link and know our Loop ID. + */ + +static int +isp_fclink_test(ispsoftc_t *isp, int usdelay) +{ + static char *toponames[] = { + "Private Loop", + "FL Port", + "N-Port to N-Port", + "F Port", + "F Port (no FLOGI_ACC response)" + }; + mbreg_t mbs; + int count, check_for_fabric; + uint8_t lwfs; + fcparam *fcp; + struct lportdb *lp; + isp_pdb_t pdb; + + fcp = isp->isp_param; + + isp_prt(isp, ISP_LOGDEBUG0, "FC Link Test Entry"); + + /* + * XXX: Here is where we would start a 'loop dead' timeout + */ + + /* + * Wait up to N microseconds for F/W to go to a ready state. + */ + lwfs = FW_CONFIG_WAIT; + count = 0; + while (count < usdelay) { + uint64_t enano; + uint32_t wrk; + NANOTIME_T hra, hrb; + + GET_NANOTIME(&hra); + isp_fw_state(isp); + if (lwfs != fcp->isp_fwstate) { + isp_prt(isp, ISP_LOGINFO, "Firmware State <%s->%s>", + isp2100_fw_statename((int)lwfs), + isp2100_fw_statename((int)fcp->isp_fwstate)); + lwfs = fcp->isp_fwstate; + } + if (fcp->isp_fwstate == FW_READY) { + break; + } + GET_NANOTIME(&hrb); + + /* + * Get the elapsed time in nanoseconds. + * Always guaranteed to be non-zero. + */ + enano = NANOTIME_SUB(&hrb, &hra); + + isp_prt(isp, ISP_LOGDEBUG1, + "usec%d: 0x%lx->0x%lx enano 0x%x%08x", + count, (long) GET_NANOSEC(&hra), (long) GET_NANOSEC(&hrb), + (uint32_t)(enano >> 32), (uint32_t)(enano & 0xffffffff)); + + /* + * If the elapsed time is less than 1 millisecond, + * delay a period of time up to that millisecond of + * waiting. + * + * This peculiar code is an attempt to try and avoid + * invoking uint64_t math support functions for some + * platforms where linkage is a problem. + */ + if (enano < (1000 * 1000)) { + count += 1000; + enano = (1000 * 1000) - enano; + while (enano > (uint64_t) 4000000000U) { + USEC_SLEEP(isp, 4000000); + enano -= (uint64_t) 4000000000U; + } + wrk = enano; + wrk /= 1000; + USEC_SLEEP(isp, wrk); + } else { + while (enano > (uint64_t) 4000000000U) { + count += 4000000; + enano -= (uint64_t) 4000000000U; + } + wrk = enano; + count += (wrk / 1000); + } + } + + /* + * If we haven't gone to 'ready' state, return. + */ + if (fcp->isp_fwstate != FW_READY) { + return (-1); + } + + /* + * Get our Loop ID (if possible). We really need to have it. + */ + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_GET_LOOP_ID; + isp_mboxcmd(isp, &mbs, MBLOGALL); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + return (-1); + } + if (IS_2KLOGIN(isp)) { + fcp->isp_loopid = mbs.param[1]; + } else { + fcp->isp_loopid = mbs.param[1] & 0xff; + } + if (fcp->isp_loopid == 0xffff) { /* happens with 2k login f/w */ + fcp->isp_loopid = MAX_FC_TARG-1; + } else if (fcp->isp_loopid >= MAX_FC_TARG) { + isp_prt(isp, ISP_LOGWARN, "bad initiator loopid (0x%x)", fcp->isp_loopid); + fcp->isp_loopid = MAX_FC_TARG-1; + } + if (IS_2200(isp) || IS_23XX(isp)) { + int topo = (int) mbs.param[6]; + if (topo < TOPO_NL_PORT || topo > TOPO_PTP_STUB) + topo = TOPO_PTP_STUB; + fcp->isp_topo = topo; + } else { + fcp->isp_topo = TOPO_NL_PORT; + } + /* + * Get the port id. + */ + fcp->isp_portid = mbs.param[2] | (mbs.param[3] << 16); + + /* + * Check to see if we're on a fabric by trying to see if we + * can talk to the fabric name server. This can be a bit + * tricky because if we're a 2100, we should check always + * (in case we're connected to a server doing aliasing). + */ + fcp->isp_onfabric = 0; + + if (IS_2100(isp)) { + /* + * Don't bother with fabric if we are using really old + * 2100 firmware. It's just not worth it. + */ + if (ISP_FW_NEWER_THAN(isp, 1, 15, 37)) { + check_for_fabric = 1; + } else { + check_for_fabric = 0; + } + } else if (fcp->isp_topo == TOPO_FL_PORT || + fcp->isp_topo == TOPO_F_PORT) { + check_for_fabric = 1; + } else + check_for_fabric = 0; + + if (check_for_fabric && isp_getpdb(isp, FL_PORT_ID, &pdb) == 0) { + int loopid = FL_PORT_ID; + if (IS_2100(isp)) { + fcp->isp_topo = TOPO_FL_PORT; + } + + if (BITS2WORD(pdb.pdb_portid_bits) == 0) { + /* + * Crock. + */ + fcp->isp_topo = TOPO_NL_PORT; + goto not_on_fabric; + } + fcp->isp_portid = mbs.param[2] | ((int) mbs.param[3] << 16); + + /* + * Save the Fabric controller's port database entry. + */ + lp = &fcp->portdb[loopid]; + lp->node_wwn = + (((uint64_t)pdb.pdb_nodename[0]) << 56) | + (((uint64_t)pdb.pdb_nodename[1]) << 48) | + (((uint64_t)pdb.pdb_nodename[2]) << 40) | + (((uint64_t)pdb.pdb_nodename[3]) << 32) | + (((uint64_t)pdb.pdb_nodename[4]) << 24) | + (((uint64_t)pdb.pdb_nodename[5]) << 16) | + (((uint64_t)pdb.pdb_nodename[6]) << 8) | + (((uint64_t)pdb.pdb_nodename[7])); + lp->port_wwn = + (((uint64_t)pdb.pdb_portname[0]) << 56) | + (((uint64_t)pdb.pdb_portname[1]) << 48) | + (((uint64_t)pdb.pdb_portname[2]) << 40) | + (((uint64_t)pdb.pdb_portname[3]) << 32) | + (((uint64_t)pdb.pdb_portname[4]) << 24) | + (((uint64_t)pdb.pdb_portname[5]) << 16) | + (((uint64_t)pdb.pdb_portname[6]) << 8) | + (((uint64_t)pdb.pdb_portname[7])); + lp->roles = + (pdb.pdb_prli_svc3 & SVC3_ROLE_MASK) >> SVC3_ROLE_SHIFT; + lp->portid = BITS2WORD(pdb.pdb_portid_bits); + lp->loopid = pdb.pdb_loopid; + lp->loggedin = lp->valid = 1; + fcp->isp_onfabric = 1; + (void) isp_async(isp, ISPASYNC_PROMENADE, &loopid); + isp_register_fc4_type(isp); + } else { +not_on_fabric: + fcp->isp_onfabric = 0; + fcp->portdb[FL_PORT_ID].valid = 0; + } + + fcp->isp_gbspeed = 1; + if (IS_23XX(isp)) { + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_GET_SET_DATA_RATE; + mbs.param[1] = MBGSD_GET_RATE; + /* mbs.param[2] undefined if we're just getting rate */ + isp_mboxcmd(isp, &mbs, MBLOGALL); + if (mbs.param[0] == MBOX_COMMAND_COMPLETE) { + if (mbs.param[1] == MBGSD_TWOGB) { + isp_prt(isp, ISP_LOGINFO, "2Gb link speed/s"); + fcp->isp_gbspeed = 2; + } + } + } + + isp_prt(isp, ISP_LOGCONFIG, topology, fcp->isp_loopid, + fcp->isp_portid, fcp->isp_loopstate, toponames[fcp->isp_topo]); + + /* + * Announce ourselves, too. This involves synthesizing an entry. + */ + if (fcp->isp_iid_set == 0) { + fcp->isp_iid_set = 1; + fcp->isp_iid = fcp->isp_loopid; + lp = &fcp->portdb[fcp->isp_iid]; + } else { + lp = &fcp->portdb[fcp->isp_iid]; + if (fcp->isp_portid != lp->portid || + fcp->isp_loopid != lp->loopid || + fcp->isp_nodewwn != ISP_NODEWWN(isp) || + fcp->isp_portwwn != ISP_PORTWWN(isp)) { + lp->valid = 0; + count = fcp->isp_iid; + (void) isp_async(isp, ISPASYNC_PROMENADE, &count); + } + } + lp->loopid = fcp->isp_loopid; + lp->portid = fcp->isp_portid; + lp->node_wwn = ISP_NODEWWN(isp); + lp->port_wwn = ISP_PORTWWN(isp); + switch (isp->isp_role) { + case ISP_ROLE_NONE: + lp->roles = 0; + break; + case ISP_ROLE_TARGET: + lp->roles = SVC3_TGT_ROLE >> SVC3_ROLE_SHIFT; + break; + case ISP_ROLE_INITIATOR: + lp->roles = SVC3_INI_ROLE >> SVC3_ROLE_SHIFT; + break; + case ISP_ROLE_BOTH: + lp->roles = (SVC3_INI_ROLE|SVC3_TGT_ROLE) >> SVC3_ROLE_SHIFT; + break; + } + lp->loggedin = lp->valid = 1; + count = fcp->isp_iid; + (void) isp_async(isp, ISPASYNC_PROMENADE, &count); + isp_prt(isp, ISP_LOGDEBUG0, "FC Link Test Complete"); + return (0); +} + +static const char * +isp2100_fw_statename(int state) +{ + switch(state) { + case FW_CONFIG_WAIT: return "Config Wait"; + case FW_WAIT_AL_PA: return "Waiting for AL_PA"; + case FW_WAIT_LOGIN: return "Wait Login"; + case FW_READY: return "Ready"; + case FW_LOSS_OF_SYNC: return "Loss Of Sync"; + case FW_ERROR: return "Error"; + case FW_REINIT: return "Re-Init"; + case FW_NON_PART: return "Nonparticipating"; + default: return "?????"; + } +} + +/* + * Synchronize our soft copy of the port database with what the f/w thinks + * (with a view toward possibly for a specific target....) + */ + +static int +isp_pdb_sync(ispsoftc_t *isp) +{ + struct lportdb *lp; + fcparam *fcp = isp->isp_param; + isp_pdb_t pdb; + int loopid, base, lim; + + /* + * Make sure we're okay for doing this right now. + */ + if (fcp->isp_loopstate != LOOP_PDB_RCVD && + fcp->isp_loopstate != LOOP_FSCAN_DONE && + fcp->isp_loopstate != LOOP_LSCAN_DONE) { + return (-1); + } + + if (fcp->isp_topo == TOPO_FL_PORT || fcp->isp_topo == TOPO_NL_PORT || + fcp->isp_topo == TOPO_N_PORT) { + if (fcp->isp_loopstate < LOOP_LSCAN_DONE) { + if (isp_scan_loop(isp) != 0) { + return (-1); + } + } + } + fcp->isp_loopstate = LOOP_SYNCING_PDB; + + /* + * If we get this far, we've settled our differences with the f/w + * (for local loop device) and we can say that the loop state is ready. + */ + + if (fcp->isp_topo == TOPO_NL_PORT) { + fcp->loop_seen_once = 1; + fcp->isp_loopstate = LOOP_READY; + return (0); + } + + /* + * Find all Fabric Entities that didn't make it from one scan to the + * next and let the world know they went away. Scan the whole database. + */ + for (lp = &fcp->portdb[0]; lp < &fcp->portdb[MAX_FC_TARG]; lp++) { + if (lp->was_fabric_dev && lp->fabric_dev == 0) { + loopid = lp - fcp->portdb; + lp->valid = 0; /* should already be set */ + (void) isp_async(isp, ISPASYNC_PROMENADE, &loopid); + MEMZERO((void *) lp, sizeof (*lp)); + continue; + } + lp->was_fabric_dev = lp->fabric_dev; + } + + if (fcp->isp_topo == TOPO_FL_PORT) + base = FC_SNS_ID+1; + else + base = 0; + + if (fcp->isp_topo == TOPO_N_PORT) + lim = 1; + else + lim = MAX_FC_TARG; + + /* + * Now log in any fabric devices that the outer layer has + * left for us to see. This seems the most sane policy + * for the moment. + */ + for (lp = &fcp->portdb[base]; lp < &fcp->portdb[lim]; lp++) { + uint32_t portid; + mbreg_t mbs; + + loopid = lp - fcp->portdb; + if (loopid >= FL_PORT_ID && loopid <= FC_SNS_ID) { + continue; + } + + /* + * Anything here? + */ + if (lp->port_wwn == 0) { + continue; + } + + /* + * Don't try to log into yourself. + */ + if ((portid = lp->portid) == fcp->isp_portid) { + continue; + } + + + /* + * If we'd been logged in- see if we still are and we haven't + * changed. If so, no need to log ourselves out, etc.. + * + * Unfortunately, our charming Qlogic f/w has decided to + * return a valid port database entry for a fabric device + * that has, in fact, gone away. And it hangs trying to + * log it out. + */ + if (lp->loggedin && lp->force_logout == 0 && + isp_getpdb(isp, lp->loopid, &pdb) == 0) { + int nrole; + uint64_t nwwnn, nwwpn; + nwwnn = + (((uint64_t)pdb.pdb_nodename[0]) << 56) | + (((uint64_t)pdb.pdb_nodename[1]) << 48) | + (((uint64_t)pdb.pdb_nodename[2]) << 40) | + (((uint64_t)pdb.pdb_nodename[3]) << 32) | + (((uint64_t)pdb.pdb_nodename[4]) << 24) | + (((uint64_t)pdb.pdb_nodename[5]) << 16) | + (((uint64_t)pdb.pdb_nodename[6]) << 8) | + (((uint64_t)pdb.pdb_nodename[7])); + nwwpn = + (((uint64_t)pdb.pdb_portname[0]) << 56) | + (((uint64_t)pdb.pdb_portname[1]) << 48) | + (((uint64_t)pdb.pdb_portname[2]) << 40) | + (((uint64_t)pdb.pdb_portname[3]) << 32) | + (((uint64_t)pdb.pdb_portname[4]) << 24) | + (((uint64_t)pdb.pdb_portname[5]) << 16) | + (((uint64_t)pdb.pdb_portname[6]) << 8) | + (((uint64_t)pdb.pdb_portname[7])); + nrole = (pdb.pdb_prli_svc3 & SVC3_ROLE_MASK) >> + SVC3_ROLE_SHIFT; + if (pdb.pdb_loopid == lp->loopid && lp->portid == + (uint32_t) BITS2WORD(pdb.pdb_portid_bits) && + nwwnn == lp->node_wwn && nwwpn == lp->port_wwn && + lp->roles == nrole && lp->force_logout == 0) { + lp->loggedin = lp->valid = 1; + isp_prt(isp, ISP_LOGCONFIG, lretained, + (int) (lp - fcp->portdb), + (int) lp->loopid, lp->portid); + continue; + } + } + + if (fcp->isp_fwstate != FW_READY || + fcp->isp_loopstate != LOOP_SYNCING_PDB) { + return (-1); + } + + /* + * Force a logout if we were logged in. + */ + if (lp->loggedin) { + if (lp->force_logout || + isp_getpdb(isp, lp->loopid, &pdb) == 0) { + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_FABRIC_LOGOUT; + if (IS_2KLOGIN(isp)) { + mbs.param[1] = lp->loopid; + mbs.ibits |= (1 << 10); + } else { + mbs.param[1] = lp->loopid << 8; + } + isp_mboxcmd(isp, &mbs, MBLOGNONE); + isp_prt(isp, ISP_LOGINFO, plogout, + (int) (lp - fcp->portdb), lp->loopid, + lp->portid); + } + lp->force_logout = lp->loggedin = 0; + if (fcp->isp_fwstate != FW_READY || + fcp->isp_loopstate != LOOP_SYNCING_PDB) { + return (-1); + } + } + + /* + * And log in.... + */ + loopid = lp - fcp->portdb; + lp->loopid = FL_PORT_ID; + do { + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_FABRIC_LOGIN; + if (IS_2KLOGIN(isp)) { + mbs.param[1] = loopid; + mbs.ibits |= (1 << 10); + } else { + mbs.param[1] = loopid << 8; + } + mbs.param[2] = portid >> 16; + mbs.param[3] = portid & 0xffff; + isp_mboxcmd(isp, &mbs, MBLOGALL & ~(MBOX_LOOP_ID_USED | + MBOX_PORT_ID_USED | MBOX_COMMAND_ERROR)); + if (fcp->isp_fwstate != FW_READY || + fcp->isp_loopstate != LOOP_SYNCING_PDB) { + return (-1); + } + switch (mbs.param[0]) { + case MBOX_LOOP_ID_USED: + /* + * Try the next available loop id. + */ + loopid++; + break; + case MBOX_PORT_ID_USED: + /* + * This port is already logged in. + * Snaffle the loop id it's using if it's + * nonzero, otherwise we're hosed. + */ + if (mbs.param[1] != 0) { + loopid = mbs.param[1]; + if (loopid >= MAX_FC_TARG) { + loopid = MAX_FC_TARG; + break; + } + isp_prt(isp, ISP_LOGINFO, retained, + loopid, (int) (lp - fcp->portdb), + lp->portid); + } else { + loopid = MAX_FC_TARG; + break; + } + /* FALLTHROUGH */ + case MBOX_COMMAND_COMPLETE: + lp->loggedin = 1; + lp->loopid = loopid; + break; + case MBOX_COMMAND_ERROR: + isp_prt(isp, ISP_LOGINFO, plogierr, + portid, mbs.param[1]); + /* FALLTHROUGH */ + case MBOX_ALL_IDS_USED: /* We're outta IDs */ + default: + loopid = MAX_FC_TARG; + break; + } + } while (lp->loopid == FL_PORT_ID && loopid < MAX_FC_TARG); + + /* + * If we get here and we haven't set a Loop ID, + * we failed to log into this device. + */ + + if (lp->loopid == FL_PORT_ID) { + lp->loopid = 0; + continue; + } + + /* + * Make sure we can get the approriate port information. + */ + if (isp_getpdb(isp, lp->loopid, &pdb) != 0) { + isp_prt(isp, ISP_LOGWARN, nopdb, lp->portid); + goto dump_em; + } + + if (fcp->isp_fwstate != FW_READY || + fcp->isp_loopstate != LOOP_SYNCING_PDB) { + return (-1); + } + + if (pdb.pdb_loopid != lp->loopid) { + isp_prt(isp, ISP_LOGWARN, pdbmfail1, + lp->portid, pdb.pdb_loopid); + goto dump_em; + } + + if (lp->portid != (uint32_t) BITS2WORD(pdb.pdb_portid_bits)) { + isp_prt(isp, ISP_LOGWARN, pdbmfail2, + lp->portid, BITS2WORD(pdb.pdb_portid_bits)); + goto dump_em; + } + + lp->roles = + (pdb.pdb_prli_svc3 & SVC3_ROLE_MASK) >> SVC3_ROLE_SHIFT; + lp->node_wwn = + (((uint64_t)pdb.pdb_nodename[0]) << 56) | + (((uint64_t)pdb.pdb_nodename[1]) << 48) | + (((uint64_t)pdb.pdb_nodename[2]) << 40) | + (((uint64_t)pdb.pdb_nodename[3]) << 32) | + (((uint64_t)pdb.pdb_nodename[4]) << 24) | + (((uint64_t)pdb.pdb_nodename[5]) << 16) | + (((uint64_t)pdb.pdb_nodename[6]) << 8) | + (((uint64_t)pdb.pdb_nodename[7])); + lp->port_wwn = + (((uint64_t)pdb.pdb_portname[0]) << 56) | + (((uint64_t)pdb.pdb_portname[1]) << 48) | + (((uint64_t)pdb.pdb_portname[2]) << 40) | + (((uint64_t)pdb.pdb_portname[3]) << 32) | + (((uint64_t)pdb.pdb_portname[4]) << 24) | + (((uint64_t)pdb.pdb_portname[5]) << 16) | + (((uint64_t)pdb.pdb_portname[6]) << 8) | + (((uint64_t)pdb.pdb_portname[7])); + /* + * Check to make sure this all makes sense. + */ + if (lp->node_wwn && lp->port_wwn) { + lp->valid = 1; + loopid = lp - fcp->portdb; + (void) isp_async(isp, ISPASYNC_PROMENADE, &loopid); + continue; + } +dump_em: + lp->valid = 0; + isp_prt(isp, ISP_LOGINFO, + ldumped, loopid, lp->loopid, lp->portid); + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_FABRIC_LOGOUT; + if (IS_2KLOGIN(isp)) { + mbs.param[1] = lp->loopid; + mbs.ibits |= (1 << 10); + } else { + mbs.param[1] = lp->loopid << 8; + } + isp_mboxcmd(isp, &mbs, MBLOGNONE); + if (fcp->isp_fwstate != FW_READY || + fcp->isp_loopstate != LOOP_SYNCING_PDB) { + return (-1); + } + } + /* + * If we get here, we've for sure seen not only a valid loop + * but know what is or isn't on it, so mark this for usage + * in isp_start. + */ + fcp->loop_seen_once = 1; + fcp->isp_loopstate = LOOP_READY; + return (0); +} + +static int +isp_scan_loop(ispsoftc_t *isp) +{ + struct lportdb *lp; + fcparam *fcp = isp->isp_param; + isp_pdb_t pdb; + int loopid, lim, hival; + + switch (fcp->isp_topo) { + case TOPO_NL_PORT: + hival = FL_PORT_ID; + break; + case TOPO_N_PORT: + hival = 2; + break; + case TOPO_FL_PORT: + hival = FC_PORT_ID; + break; + default: + isp_prt(isp, ISP_LOGDEBUG0, "no loop topology to scan"); + fcp->isp_loopstate = LOOP_LSCAN_DONE; + return (0); + } + fcp->isp_loopstate = LOOP_SCANNING_LOOP; + + isp_prt(isp, ISP_LOGDEBUG0, "scanning local loop 0..%d", hival); + + + /* + * make sure the temp port database is clean... + */ + MEMZERO((void *)fcp->tport, sizeof (fcp->tport)); + + /* + * Run through the local loop ports and get port database info + * for each loop ID. + * + * There's a somewhat unexplained situation where the f/w passes back + * the wrong database entity- if that happens, just restart (up to + * FL_PORT_ID times). + */ + for (lim = loopid = 0; loopid < hival; loopid++) { + lp = &fcp->tport[loopid]; + + /* + * Don't even try for ourselves... + */ + if (loopid == fcp->isp_loopid) { + continue; + } + + if (IS_2100(isp) || IS_2200(isp)) { + lp->node_wwn = isp_get_portname(isp, loopid, 1); + if (fcp->isp_loopstate < LOOP_SCANNING_LOOP) { + return (-1); + } + if (lp->node_wwn == 0) { + continue; + } + } + + /* + * Get an entry.... + */ + if (isp_getpdb(isp, loopid, &pdb) != 0) { + if (fcp->isp_loopstate < LOOP_SCANNING_LOOP) { + return (-1); + } + continue; + } + + if (fcp->isp_loopstate < LOOP_SCANNING_LOOP) { + return (-1); + } + + /* + * If the returned database element doesn't match what we + * asked for, restart the process entirely (up to a point...). + */ + if (pdb.pdb_loopid != loopid) { + loopid = 0; + if (lim++ < hival) { + continue; + } + isp_prt(isp, ISP_LOGWARN, + "giving up on synchronizing the port database"); + return (-1); + } + + /* + * Save the pertinent info locally. + */ + lp->node_wwn = + (((uint64_t)pdb.pdb_nodename[0]) << 56) | + (((uint64_t)pdb.pdb_nodename[1]) << 48) | + (((uint64_t)pdb.pdb_nodename[2]) << 40) | + (((uint64_t)pdb.pdb_nodename[3]) << 32) | + (((uint64_t)pdb.pdb_nodename[4]) << 24) | + (((uint64_t)pdb.pdb_nodename[5]) << 16) | + (((uint64_t)pdb.pdb_nodename[6]) << 8) | + (((uint64_t)pdb.pdb_nodename[7])); + lp->port_wwn = + (((uint64_t)pdb.pdb_portname[0]) << 56) | + (((uint64_t)pdb.pdb_portname[1]) << 48) | + (((uint64_t)pdb.pdb_portname[2]) << 40) | + (((uint64_t)pdb.pdb_portname[3]) << 32) | + (((uint64_t)pdb.pdb_portname[4]) << 24) | + (((uint64_t)pdb.pdb_portname[5]) << 16) | + (((uint64_t)pdb.pdb_portname[6]) << 8) | + (((uint64_t)pdb.pdb_portname[7])); + lp->roles = + (pdb.pdb_prli_svc3 & SVC3_ROLE_MASK) >> SVC3_ROLE_SHIFT; + lp->portid = BITS2WORD(pdb.pdb_portid_bits); + lp->loopid = pdb.pdb_loopid; + } + + /* + * Mark all of the permanent local loop database entries as invalid + * (except our own entry). + */ + for (loopid = 0; loopid < hival; loopid++) { + if (loopid == fcp->isp_iid) { + fcp->portdb[loopid].valid = 1; + fcp->portdb[loopid].loopid = fcp->isp_loopid; + continue; + } + fcp->portdb[loopid].valid = 0; + } + + /* + * Now merge our local copy of the port database into our saved copy. + * Notify the outer layers of new devices arriving. + */ + for (loopid = 0; loopid < hival; loopid++) { + int i; + + /* + * If we don't have a non-zero Port WWN, we're not here. + */ + if (fcp->tport[loopid].port_wwn == 0) { + continue; + } + + /* + * Skip ourselves. + */ + if (loopid == fcp->isp_iid) { + continue; + } + + /* + * For the purposes of deciding whether this is the + * 'same' device or not, we only search for an identical + * Port WWN. Node WWNs may or may not be the same as + * the Port WWN, and there may be multiple different + * Port WWNs with the same Node WWN. It would be chaos + * to have multiple identical Port WWNs, so we don't + * allow that. + */ + + for (i = 0; i < hival; i++) { + int j; + if (fcp->portdb[i].port_wwn == 0) + continue; + if (fcp->portdb[i].port_wwn != + fcp->tport[loopid].port_wwn) + continue; + /* + * We found this WWN elsewhere- it's changed + * loopids then. We don't change it's actual + * position in our cached port database- we + * just change the actual loop ID we'd use. + */ + if (fcp->portdb[i].loopid != loopid) { + isp_prt(isp, ISP_LOGINFO, portshift, i, + fcp->portdb[i].loopid, + fcp->portdb[i].portid, loopid, + fcp->tport[loopid].portid); + } + fcp->portdb[i].portid = fcp->tport[loopid].portid; + fcp->portdb[i].loopid = loopid; + fcp->portdb[i].valid = 1; + fcp->portdb[i].roles = fcp->tport[loopid].roles; + + /* + * Now make sure this Port WWN doesn't exist elsewhere + * in the port database. + */ + for (j = i+1; j < hival; j++) { + if (fcp->portdb[i].port_wwn != + fcp->portdb[j].port_wwn) { + continue; + } + isp_prt(isp, ISP_LOGWARN, portdup, j, i); + /* + * Invalidate the 'old' *and* 'new' ones. + * This is really harsh and not quite right, + * but if this happens, we really don't know + * who is what at this point. + */ + fcp->portdb[i].valid = 0; + fcp->portdb[j].valid = 0; + } + break; + } + + /* + * If we didn't traverse the entire port database, + * then we found (and remapped) an existing entry. + * No need to notify anyone- go for the next one. + */ + if (i < hival) { + isp_prt(isp, ISP_LOGINFO, retained, + fcp->portdb[i].loopid, i, fcp->portdb[i].portid); + continue; + } + + /* + * We've not found this Port WWN anywhere. It's a new entry. + * See if we can leave it where it is (with target == loopid). + */ + if (fcp->portdb[loopid].port_wwn != 0) { + for (lim = 0; lim < hival; lim++) { + if (fcp->portdb[lim].port_wwn == 0) + break; + } + /* "Cannot Happen" */ + if (lim == hival) { + isp_prt(isp, ISP_LOGWARN, "Remap Overflow"); + continue; + } + i = lim; + } else { + i = loopid; + } + + /* + * NB: The actual loopid we use here is loopid- we may + * in fact be at a completely different index (target). + */ + fcp->portdb[i].loopid = loopid; + fcp->portdb[i].port_wwn = fcp->tport[loopid].port_wwn; + fcp->portdb[i].node_wwn = fcp->tport[loopid].node_wwn; + fcp->portdb[i].roles = fcp->tport[loopid].roles; + fcp->portdb[i].portid = fcp->tport[loopid].portid; + fcp->portdb[i].valid = 1; + + /* + * Tell the outside world we've arrived. + */ + (void) isp_async(isp, ISPASYNC_PROMENADE, &i); + } + + /* + * Now find all previously used targets that are now invalid and + * notify the outer layers that they're gone. + */ + for (lp = &fcp->portdb[0]; lp < &fcp->portdb[hival]; lp++) { + if (lp->valid || lp->port_wwn == 0) { + continue; + } + + /* + * Tell the outside world we've gone + * away and erase our pdb entry. + * + */ + loopid = lp - fcp->portdb; + (void) isp_async(isp, ISPASYNC_PROMENADE, &loopid); + MEMZERO((void *) lp, sizeof (*lp)); + } + fcp->isp_loopstate = LOOP_LSCAN_DONE; + return (0); +} + + +static int +isp_fabric_mbox_cmd(ispsoftc_t *isp, mbreg_t *mbp) +{ + /* the caller sets up the mailbox */ + isp_mboxcmd(isp, mbp, MBLOGNONE); + if (mbp->param[0] != MBOX_COMMAND_COMPLETE) { + if (FCPARAM(isp)->isp_loopstate == LOOP_SCANNING_FABRIC) { + FCPARAM(isp)->isp_loopstate = LOOP_PDB_RCVD; + } + if (mbp->param[0] == MBOX_COMMAND_ERROR) { + char tbuf[16]; + char *m; + switch (mbp->param[1]) { + case 1: + m = "No Loop"; + break; + case 2: + m = "Failed to allocate IOCB buffer"; + break; + case 3: + m = "Failed to allocate XCB buffer"; + break; + case 4: + m = "timeout or transmit failed"; + break; + case 5: + m = "no fabric loop"; + break; + case 6: + m = "remote device not a target"; + break; + default: + SNPRINTF(tbuf, sizeof tbuf, "%x", + mbp->param[1]); + m = tbuf; + break; + } + isp_prt(isp, ISP_LOGERR, "SNS Failed- %s", m); + } + return (-1); + } + + if (FCPARAM(isp)->isp_fwstate != FW_READY || + FCPARAM(isp)->isp_loopstate < LOOP_SCANNING_FABRIC) { + return (-1); + } + return(0); +} + +#ifdef ISP_USE_GA_NXT +static int +isp_scan_fabric(ispsoftc_t *isp, int ftype) +{ + fcparam *fcp = isp->isp_param; + uint32_t portid, first_portid, last_portid; + int hicap, last_port_same; + + if (fcp->isp_onfabric == 0) { + fcp->isp_loopstate = LOOP_FSCAN_DONE; + return (0); + } + + isp_prt(isp, ISP_LOGDEBUG0, "scanning fabric (GA_NXT)"); + + FC_SCRATCH_ACQUIRE(isp); + + /* + * Since Port IDs are 24 bits, we can check against having seen + * anything yet with this value. + */ + last_port_same = 0; + last_portid = 0xffffffff; /* not a port */ + first_portid = portid = fcp->isp_portid; + fcp->isp_loopstate = LOOP_SCANNING_FABRIC; + + for (hicap = 0; hicap < GA_NXT_MAX; hicap++) { + mbreg_t mbs; + sns_screq_t *rq; + sns_ga_nxt_rsp_t *rs0, *rs1; + struct lportdb lcl; + uint8_t sc[SNS_GA_NXT_RESP_SIZE]; + + rq = (sns_screq_t *)sc; + MEMZERO((void *) rq, SNS_GA_NXT_REQ_SIZE); + rq->snscb_rblen = SNS_GA_NXT_RESP_SIZE >> 1; + rq->snscb_addr[RQRSP_ADDR0015] = DMA_WD0(fcp->isp_scdma+0x100); + rq->snscb_addr[RQRSP_ADDR1631] = DMA_WD1(fcp->isp_scdma+0x100); + rq->snscb_addr[RQRSP_ADDR3247] = DMA_WD2(fcp->isp_scdma+0x100); + rq->snscb_addr[RQRSP_ADDR4863] = DMA_WD3(fcp->isp_scdma+0x100); + rq->snscb_sblen = 6; + rq->snscb_data[0] = SNS_GA_NXT; + rq->snscb_data[4] = portid & 0xffff; + rq->snscb_data[5] = (portid >> 16) & 0xff; + isp_put_sns_request(isp, rq, (sns_screq_t *) fcp->isp_scratch); + MEMORYBARRIER(isp, SYNC_SFORDEV, 0, SNS_GA_NXT_REQ_SIZE); + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_SEND_SNS; + mbs.param[1] = SNS_GA_NXT_REQ_SIZE >> 1; + mbs.param[2] = DMA_WD1(fcp->isp_scdma); + mbs.param[3] = DMA_WD0(fcp->isp_scdma); + /* + * Leave 4 and 5 alone + */ + mbs.param[6] = DMA_WD3(fcp->isp_scdma); + mbs.param[7] = DMA_WD2(fcp->isp_scdma); + if (isp_fabric_mbox_cmd(isp, &mbs)) { + if (fcp->isp_loopstate >= LOOP_SCANNING_FABRIC) { + fcp->isp_loopstate = LOOP_PDB_RCVD; + } + FC_SCRATCH_RELEASE(isp); + return (-1); + } + MEMORYBARRIER(isp, SYNC_SFORCPU, 0x100, SNS_GA_NXT_RESP_SIZE); + rs1 = (sns_ga_nxt_rsp_t *) sc; + rs0 = (sns_ga_nxt_rsp_t *) ((uint8_t *)fcp->isp_scratch+0x100); + isp_get_ga_nxt_response(isp, rs0, rs1); + if (rs1->snscb_cthdr.ct_response != FS_ACC) { + int level; + if (rs1->snscb_cthdr.ct_reason == 9 && + rs1->snscb_cthdr.ct_explanation == 7) + level = ISP_LOGDEBUG0; + else + level = ISP_LOGWARN; + isp_prt(isp, level, swrej, "GA_NXT", + rs1->snscb_cthdr.ct_reason, + rs1->snscb_cthdr.ct_explanation, portid); + FC_SCRATCH_RELEASE(isp); + fcp->isp_loopstate = LOOP_FSCAN_DONE; + return (0); + } + portid = + (((uint32_t) rs1->snscb_port_id[0]) << 16) | + (((uint32_t) rs1->snscb_port_id[1]) << 8) | + (((uint32_t) rs1->snscb_port_id[2])); + + /* + * XXX: We should check to make sure that this entry + * XXX: supports the type(s) we are interested in. + */ + /* + * Okay, we now have information about a fabric object. + * If it is the type we're interested in, tell the outer layers + * about it. The outer layer needs to know: Port ID, WWNN, + * WWPN, FC4 type, and port type. + * + * The lportdb structure is adequate for this. + */ + MEMZERO(&lcl, sizeof (lcl)); + lcl.port_type = rs1->snscb_port_type; + lcl.fc4_type = ftype; + lcl.portid = portid; + lcl.node_wwn = + (((uint64_t)rs1->snscb_nodename[0]) << 56) | + (((uint64_t)rs1->snscb_nodename[1]) << 48) | + (((uint64_t)rs1->snscb_nodename[2]) << 40) | + (((uint64_t)rs1->snscb_nodename[3]) << 32) | + (((uint64_t)rs1->snscb_nodename[4]) << 24) | + (((uint64_t)rs1->snscb_nodename[5]) << 16) | + (((uint64_t)rs1->snscb_nodename[6]) << 8) | + (((uint64_t)rs1->snscb_nodename[7])); + lcl.port_wwn = + (((uint64_t)rs1->snscb_portname[0]) << 56) | + (((uint64_t)rs1->snscb_portname[1]) << 48) | + (((uint64_t)rs1->snscb_portname[2]) << 40) | + (((uint64_t)rs1->snscb_portname[3]) << 32) | + (((uint64_t)rs1->snscb_portname[4]) << 24) | + (((uint64_t)rs1->snscb_portname[5]) << 16) | + (((uint64_t)rs1->snscb_portname[6]) << 8) | + (((uint64_t)rs1->snscb_portname[7])); + + /* + * Does this fabric object support the type we want? + * If not, skip it. + */ + if (rs1->snscb_fc4_types[ftype >> 5] & (1 << (ftype & 0x1f))) { + if (first_portid == portid) { + lcl.last_fabric_dev = 1; + } else { + lcl.last_fabric_dev = 0; + } + (void) isp_async(isp, ISPASYNC_FABRIC_DEV, &lcl); + } else { + isp_prt(isp, ISP_LOGDEBUG0, + "PortID 0x%x doesn't support FC4 type 0x%x", + portid, ftype); + } + if (first_portid == portid) { + fcp->isp_loopstate = LOOP_FSCAN_DONE; + FC_SCRATCH_RELEASE(isp); + return (0); + } + if (portid == last_portid) { + if (last_port_same++ > 20) { + isp_prt(isp, ISP_LOGWARN, + "tangled fabric database detected"); + break; + } + } else { + last_port_same = 0 ; + last_portid = portid; + } + } + FC_SCRATCH_RELEASE(isp); + if (hicap >= GA_NXT_MAX) { + isp_prt(isp, ISP_LOGWARN, "fabric too big (> %d)", GA_NXT_MAX); + } + fcp->isp_loopstate = LOOP_FSCAN_DONE; + return (0); +} +#else +#define GIDLEN ((ISP2100_SCRLEN >> 1) + 16) +#define NGENT ((GIDLEN - 16) >> 2) + +#define IGPOFF (ISP2100_SCRLEN - GIDLEN) +#define GXOFF (256) + +static int +isp_scan_fabric(ispsoftc_t *isp, int ftype) +{ + fcparam *fcp = FCPARAM(isp); + mbreg_t mbs; + int i; + sns_gid_ft_req_t *rq; + sns_gid_ft_rsp_t *rs0, *rs1; + + if (fcp->isp_onfabric == 0) { + fcp->isp_loopstate = LOOP_FSCAN_DONE; + return (0); + } + + isp_prt(isp, ISP_LOGDEBUG0, "scanning fabric (GID_FT)"); + + FC_SCRATCH_ACQUIRE(isp); + fcp->isp_loopstate = LOOP_SCANNING_FABRIC; + + rq = (sns_gid_ft_req_t *)fcp->tport; + MEMZERO((void *) rq, SNS_GID_FT_REQ_SIZE); + rq->snscb_rblen = GIDLEN >> 1; + rq->snscb_addr[RQRSP_ADDR0015] = DMA_WD0(fcp->isp_scdma+IGPOFF); + rq->snscb_addr[RQRSP_ADDR1631] = DMA_WD1(fcp->isp_scdma+IGPOFF); + rq->snscb_addr[RQRSP_ADDR3247] = DMA_WD2(fcp->isp_scdma+IGPOFF); + rq->snscb_addr[RQRSP_ADDR4863] = DMA_WD3(fcp->isp_scdma+IGPOFF); + rq->snscb_sblen = 6; + rq->snscb_cmd = SNS_GID_FT; + rq->snscb_mword_div_2 = NGENT; + rq->snscb_fc4_type = ftype; + isp_put_gid_ft_request(isp, rq, (sns_gid_ft_req_t *) fcp->isp_scratch); + MEMORYBARRIER(isp, SYNC_SFORDEV, 0, SNS_GID_FT_REQ_SIZE); + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_SEND_SNS; + mbs.param[1] = SNS_GID_FT_REQ_SIZE >> 1; + mbs.param[2] = DMA_WD1(fcp->isp_scdma); + mbs.param[3] = DMA_WD0(fcp->isp_scdma); + + /* + * Leave 4 and 5 alone + */ + mbs.param[6] = DMA_WD3(fcp->isp_scdma); + mbs.param[7] = DMA_WD2(fcp->isp_scdma); + if (isp_fabric_mbox_cmd(isp, &mbs)) { + if (fcp->isp_loopstate >= LOOP_SCANNING_FABRIC) { + fcp->isp_loopstate = LOOP_PDB_RCVD; + } + FC_SCRATCH_RELEASE(isp); + return (-1); + } + if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) { + FC_SCRATCH_RELEASE(isp); + return (-1); + } + MEMORYBARRIER(isp, SYNC_SFORCPU, IGPOFF, GIDLEN); + rs1 = (sns_gid_ft_rsp_t *) fcp->tport; + rs0 = (sns_gid_ft_rsp_t *) ((uint8_t *)fcp->isp_scratch+IGPOFF); + isp_get_gid_ft_response(isp, rs0, rs1, NGENT); + if (rs1->snscb_cthdr.ct_response != FS_ACC) { + int level; + if (rs1->snscb_cthdr.ct_reason == 9 && + rs1->snscb_cthdr.ct_explanation == 7) + level = ISP_LOGDEBUG0; + else + level = ISP_LOGWARN; + isp_prt(isp, level, swrej, "GID_FT", + rs1->snscb_cthdr.ct_reason, + rs1->snscb_cthdr.ct_explanation, 0); + FC_SCRATCH_RELEASE(isp); + fcp->isp_loopstate = LOOP_FSCAN_DONE; + return (0); + } + + /* + * Okay, we now have a list of Port IDs for this class of device. + * Go through the list and for each one get the WWPN/WWNN for it + * and tell the outer layers about it. The outer layer needs to + * know: Port ID, WWNN, WWPN, FC4 type, and (possibly) port type. + * + * The lportdb structure is adequate for this. + */ + i = -1; + do { + sns_gxn_id_req_t grqbuf, *gq = &grqbuf; + sns_gxn_id_rsp_t *gs0, grsbuf, *gs1 = &grsbuf; + struct lportdb lcl; +#if 0 + sns_gff_id_rsp_t *fs0, ffsbuf, *fs1 = &ffsbuf; +#endif + + i++; + MEMZERO(&lcl, sizeof (lcl)); + lcl.fc4_type = ftype; + lcl.portid = + (((uint32_t) rs1->snscb_ports[i].portid[0]) << 16) | + (((uint32_t) rs1->snscb_ports[i].portid[1]) << 8) | + (((uint32_t) rs1->snscb_ports[i].portid[2])); + + MEMZERO((void *) gq, sizeof (sns_gxn_id_req_t)); + gq->snscb_rblen = SNS_GXN_ID_RESP_SIZE >> 1; + gq->snscb_addr[RQRSP_ADDR0015] = DMA_WD0(fcp->isp_scdma+GXOFF); + gq->snscb_addr[RQRSP_ADDR1631] = DMA_WD1(fcp->isp_scdma+GXOFF); + gq->snscb_addr[RQRSP_ADDR3247] = DMA_WD2(fcp->isp_scdma+GXOFF); + gq->snscb_addr[RQRSP_ADDR4863] = DMA_WD3(fcp->isp_scdma+GXOFF); + gq->snscb_sblen = 6; + gq->snscb_cmd = SNS_GPN_ID; + gq->snscb_portid = lcl.portid; + isp_put_gxn_id_request(isp, gq, + (sns_gxn_id_req_t *) fcp->isp_scratch); + MEMORYBARRIER(isp, SYNC_SFORDEV, 0, SNS_GXN_ID_REQ_SIZE); + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_SEND_SNS; + mbs.param[1] = SNS_GXN_ID_REQ_SIZE >> 1; + mbs.param[2] = DMA_WD1(fcp->isp_scdma); + mbs.param[3] = DMA_WD0(fcp->isp_scdma); + /* + * Leave 4 and 5 alone + */ + mbs.param[6] = DMA_WD3(fcp->isp_scdma); + mbs.param[7] = DMA_WD2(fcp->isp_scdma); + if (isp_fabric_mbox_cmd(isp, &mbs)) { + if (fcp->isp_loopstate >= LOOP_SCANNING_FABRIC) { + fcp->isp_loopstate = LOOP_PDB_RCVD; + } + FC_SCRATCH_RELEASE(isp); + return (-1); + } + if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) { + FC_SCRATCH_RELEASE(isp); + return (-1); + } + MEMORYBARRIER(isp, SYNC_SFORCPU, GXOFF, SNS_GXN_ID_RESP_SIZE); + gs0 = (sns_gxn_id_rsp_t *) ((uint8_t *)fcp->isp_scratch+GXOFF); + isp_get_gxn_id_response(isp, gs0, gs1); + if (gs1->snscb_cthdr.ct_response != FS_ACC) { + isp_prt(isp, ISP_LOGWARN, swrej, "GPN_ID", + gs1->snscb_cthdr.ct_reason, + gs1->snscb_cthdr.ct_explanation, lcl.portid); + if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) { + FC_SCRATCH_RELEASE(isp); + return (-1); + } + continue; + } + lcl.port_wwn = + (((uint64_t)gs1->snscb_wwn[0]) << 56) | + (((uint64_t)gs1->snscb_wwn[1]) << 48) | + (((uint64_t)gs1->snscb_wwn[2]) << 40) | + (((uint64_t)gs1->snscb_wwn[3]) << 32) | + (((uint64_t)gs1->snscb_wwn[4]) << 24) | + (((uint64_t)gs1->snscb_wwn[5]) << 16) | + (((uint64_t)gs1->snscb_wwn[6]) << 8) | + (((uint64_t)gs1->snscb_wwn[7])); + + MEMZERO((void *) gq, sizeof (sns_gxn_id_req_t)); + gq->snscb_rblen = SNS_GXN_ID_RESP_SIZE >> 1; + gq->snscb_addr[RQRSP_ADDR0015] = DMA_WD0(fcp->isp_scdma+GXOFF); + gq->snscb_addr[RQRSP_ADDR1631] = DMA_WD1(fcp->isp_scdma+GXOFF); + gq->snscb_addr[RQRSP_ADDR3247] = DMA_WD2(fcp->isp_scdma+GXOFF); + gq->snscb_addr[RQRSP_ADDR4863] = DMA_WD3(fcp->isp_scdma+GXOFF); + gq->snscb_sblen = 6; + gq->snscb_cmd = SNS_GNN_ID; + gq->snscb_portid = lcl.portid; + isp_put_gxn_id_request(isp, gq, + (sns_gxn_id_req_t *) fcp->isp_scratch); + MEMORYBARRIER(isp, SYNC_SFORDEV, 0, SNS_GXN_ID_REQ_SIZE); + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_SEND_SNS; + mbs.param[1] = SNS_GXN_ID_REQ_SIZE >> 1; + mbs.param[2] = DMA_WD1(fcp->isp_scdma); + mbs.param[3] = DMA_WD0(fcp->isp_scdma); + /* + * Leave 4 and 5 alone + */ + mbs.param[6] = DMA_WD3(fcp->isp_scdma); + mbs.param[7] = DMA_WD2(fcp->isp_scdma); + if (isp_fabric_mbox_cmd(isp, &mbs)) { + if (fcp->isp_loopstate >= LOOP_SCANNING_FABRIC) { + fcp->isp_loopstate = LOOP_PDB_RCVD; + } + FC_SCRATCH_RELEASE(isp); + return (-1); + } + if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) { + FC_SCRATCH_RELEASE(isp); + return (-1); + } + MEMORYBARRIER(isp, SYNC_SFORCPU, GXOFF, SNS_GXN_ID_RESP_SIZE); + gs0 = (sns_gxn_id_rsp_t *) ((uint8_t *)fcp->isp_scratch+GXOFF); + isp_get_gxn_id_response(isp, gs0, gs1); + if (gs1->snscb_cthdr.ct_response != FS_ACC) { + isp_prt(isp, ISP_LOGWARN, swrej, "GNN_ID", + gs1->snscb_cthdr.ct_reason, + gs1->snscb_cthdr.ct_explanation, lcl.portid); + if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) { + FC_SCRATCH_RELEASE(isp); + return (-1); + } + continue; + } + lcl.node_wwn = + (((uint64_t)gs1->snscb_wwn[0]) << 56) | + (((uint64_t)gs1->snscb_wwn[1]) << 48) | + (((uint64_t)gs1->snscb_wwn[2]) << 40) | + (((uint64_t)gs1->snscb_wwn[3]) << 32) | + (((uint64_t)gs1->snscb_wwn[4]) << 24) | + (((uint64_t)gs1->snscb_wwn[5]) << 16) | + (((uint64_t)gs1->snscb_wwn[6]) << 8) | + (((uint64_t)gs1->snscb_wwn[7])); + + /* + * The QLogic f/w is bouncing this with a parameter error. + */ +#if 0 + /* + * Try and get FC4 Features (FC-GS-3 only). + * We can use the sns_gxn_id_req_t for this request. + */ + MEMZERO((void *) gq, sizeof (sns_gxn_id_req_t)); + gq->snscb_rblen = SNS_GFF_ID_RESP_SIZE >> 1; + gq->snscb_addr[RQRSP_ADDR0015] = DMA_WD0(fcp->isp_scdma+GXOFF); + gq->snscb_addr[RQRSP_ADDR1631] = DMA_WD1(fcp->isp_scdma+GXOFF); + gq->snscb_addr[RQRSP_ADDR3247] = DMA_WD2(fcp->isp_scdma+GXOFF); + gq->snscb_addr[RQRSP_ADDR4863] = DMA_WD3(fcp->isp_scdma+GXOFF); + gq->snscb_sblen = 6; + gq->snscb_cmd = SNS_GFF_ID; + gq->snscb_portid = lcl.portid; + isp_put_gxn_id_request(isp, gq, + (sns_gxn_id_req_t *) fcp->isp_scratch); + MEMORYBARRIER(isp, SYNC_SFORDEV, 0, SNS_GXN_ID_REQ_SIZE); + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_SEND_SNS; + mbs.param[1] = SNS_GXN_ID_REQ_SIZE >> 1; + mbs.param[2] = DMA_WD1(fcp->isp_scdma); + mbs.param[3] = DMA_WD0(fcp->isp_scdma); + /* + * Leave 4 and 5 alone + */ + mbs.param[6] = DMA_WD3(fcp->isp_scdma); + mbs.param[7] = DMA_WD2(fcp->isp_scdma); + if (isp_fabric_mbox_cmd(isp, &mbs)) { + if (fcp->isp_loopstate >= LOOP_SCANNING_FABRIC) { + fcp->isp_loopstate = LOOP_PDB_RCVD; + } + FC_SCRATCH_RELEASE(isp); + return (-1); + } + if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) { + FC_SCRATCH_RELEASE(isp); + return (-1); + } + MEMORYBARRIER(isp, SYNC_SFORCPU, GXOFF, SNS_GFF_ID_RESP_SIZE); + fs0 = (sns_gff_id_rsp_t *) ((uint8_t *)fcp->isp_scratch+GXOFF); + isp_get_gff_id_response(isp, fs0, fs1); + if (fs1->snscb_cthdr.ct_response != FS_ACC) { + isp_prt(isp, /* ISP_LOGDEBUG0 */ ISP_LOGWARN, + swrej, "GFF_ID", + fs1->snscb_cthdr.ct_reason, + fs1->snscb_cthdr.ct_explanation, lcl.portid); + if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) { + FC_SCRATCH_RELEASE(isp); + return (-1); + } + } else { + int index = (ftype >> 3); + int bshft = (ftype & 0x7) * 4; + int fc4_fval = + (fs1->snscb_fc4_features[index] >> bshft) & 0xf; + if (fc4_fval & 0x1) { + lcl.roles |= + (SVC3_INI_ROLE >> SVC3_ROLE_SHIFT); + } + if (fc4_fval & 0x2) { + lcl.roles |= + (SVC3_TGT_ROLE >> SVC3_ROLE_SHIFT); + } + } +#endif + + /* + * If we really want to know what kind of port type this is, + * we have to run another CT command. Otherwise, we'll leave + * it as undefined. + * + lcl.port_type = 0; + */ + if (rs1->snscb_ports[i].control & 0x80) { + lcl.last_fabric_dev = 1; + } else { + lcl.last_fabric_dev = 0; + } + (void) isp_async(isp, ISPASYNC_FABRIC_DEV, &lcl); + + } while ((rs1->snscb_ports[i].control & 0x80) == 0 && i < NGENT-1); + + /* + * If we're not at the last entry, our list isn't big enough. + */ + if ((rs1->snscb_ports[i].control & 0x80) == 0) { + isp_prt(isp, ISP_LOGWARN, "fabric too big for scratch area"); + } + + FC_SCRATCH_RELEASE(isp); + fcp->isp_loopstate = LOOP_FSCAN_DONE; + return (0); +} +#endif + +static void +isp_register_fc4_type(ispsoftc_t *isp) +{ + fcparam *fcp = isp->isp_param; + uint8_t local[SNS_RFT_ID_REQ_SIZE]; + sns_screq_t *reqp = (sns_screq_t *) local; + mbreg_t mbs; + + MEMZERO((void *) reqp, SNS_RFT_ID_REQ_SIZE); + reqp->snscb_rblen = SNS_RFT_ID_RESP_SIZE >> 1; + reqp->snscb_addr[RQRSP_ADDR0015] = DMA_WD0(fcp->isp_scdma + 0x100); + reqp->snscb_addr[RQRSP_ADDR1631] = DMA_WD1(fcp->isp_scdma + 0x100); + reqp->snscb_addr[RQRSP_ADDR3247] = DMA_WD2(fcp->isp_scdma + 0x100); + reqp->snscb_addr[RQRSP_ADDR4863] = DMA_WD3(fcp->isp_scdma + 0x100); + reqp->snscb_sblen = 22; + reqp->snscb_data[0] = SNS_RFT_ID; + reqp->snscb_data[4] = fcp->isp_portid & 0xffff; + reqp->snscb_data[5] = (fcp->isp_portid >> 16) & 0xff; + reqp->snscb_data[6] = (1 << FC4_SCSI); +#if 0 + reqp->snscb_data[6] |= (1 << FC4_IP); /* ISO/IEC 8802-2 LLC/SNAP */ +#endif + FC_SCRATCH_ACQUIRE(isp); + isp_put_sns_request(isp, reqp, (sns_screq_t *) fcp->isp_scratch); + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_SEND_SNS; + mbs.param[1] = SNS_RFT_ID_REQ_SIZE >> 1; + mbs.param[2] = DMA_WD1(fcp->isp_scdma); + mbs.param[3] = DMA_WD0(fcp->isp_scdma); + /* + * Leave 4 and 5 alone + */ + mbs.param[6] = DMA_WD3(fcp->isp_scdma); + mbs.param[7] = DMA_WD2(fcp->isp_scdma); + MEMORYBARRIER(isp, SYNC_SFORDEV, 0, SNS_RFT_ID_REQ_SIZE); + isp_mboxcmd(isp, &mbs, MBLOGALL); + FC_SCRATCH_RELEASE(isp); + if (mbs.param[0] == MBOX_COMMAND_COMPLETE) { + isp_prt(isp, ISP_LOGDEBUG0, "Register FC4 types succeeded"); + } +} + +/* + * Start a command. Locking is assumed done in the caller. + */ + +int +isp_start(XS_T *xs) +{ + ispsoftc_t *isp; + uint16_t nxti, optr, handle; + uint8_t local[QENTRY_LEN]; + ispreq_t *reqp, *qep; + int target, i; + + XS_INITERR(xs); + isp = XS_ISP(xs); + + /* + * Check to make sure we're supporting initiator role. + */ + if ((isp->isp_role & ISP_ROLE_INITIATOR) == 0) { + XS_SETERR(xs, HBA_SELTIMEOUT); + return (CMD_COMPLETE); + } + + /* + * Now make sure we're running. + */ + + if (isp->isp_state != ISP_RUNSTATE) { + isp_prt(isp, ISP_LOGERR, "Adapter not at RUNSTATE"); + XS_SETERR(xs, HBA_BOTCH); + return (CMD_COMPLETE); + } + + /* + * Check command CDB length, etc.. We really are limited to 16 bytes + * for Fibre Channel, but can do up to 44 bytes in parallel SCSI, + * but probably only if we're running fairly new firmware (we'll + * let the old f/w choke on an extended command queue entry). + */ + + if (XS_CDBLEN(xs) > (IS_FC(isp)? 16 : 44) || XS_CDBLEN(xs) == 0) { + isp_prt(isp, ISP_LOGERR, + "unsupported cdb length (%d, CDB[0]=0x%x)", + XS_CDBLEN(xs), XS_CDBP(xs)[0] & 0xff); + XS_SETERR(xs, HBA_BOTCH); + return (CMD_COMPLETE); + } + + /* + * Check to see whether we have good firmware state still or + * need to refresh our port database for this target. + */ + target = XS_TGT(xs); + if (IS_FC(isp)) { + fcparam *fcp = isp->isp_param; + struct lportdb *lp; +#ifdef HANDLE_LOOPSTATE_IN_OUTER_LAYERS + if (fcp->isp_fwstate != FW_READY || + fcp->isp_loopstate != LOOP_READY) { + return (CMD_RQLATER); + } + + /* + * If we're not on a Fabric, we can't have a target + * above FL_PORT_ID-1. + * + * If we're on a fabric and *not* connected as an F-port, + * we can't have a target less than FC_SNS_ID+1. This + * keeps us from having to sort out the difference between + * local public loop devices and those which we might get + * from a switch's database. + */ + if (fcp->isp_onfabric == 0) { + if (target >= FL_PORT_ID) { + XS_SETERR(xs, HBA_SELTIMEOUT); + return (CMD_COMPLETE); + } + } else { + if (target >= FL_PORT_ID && target <= FC_SNS_ID) { + XS_SETERR(xs, HBA_SELTIMEOUT); + return (CMD_COMPLETE); + } + /* + * We used to exclude having local loop ports + * at the same time that we have fabric ports. + * That is, we used to exclude having ports + * at < FL_PORT_ID if we're FL-port. + * + * That's wrong. The only thing that could be + * dicey is if the switch you're connected to + * has these local loop ports appear on the + * fabric and we somehow attach them twice. + */ + } +#else + /* + * Check for f/w being in ready state. If the f/w + * isn't in ready state, then we don't know our + * loop ID and the f/w hasn't completed logging + * into all targets on the loop. If this is the + * case, then bounce the command. We pretend this is + * a SELECTION TIMEOUT error if we've never gone to + * FW_READY state at all- in this case we may not + * be hooked to a loop at all and we shouldn't hang + * the machine for this. Otherwise, defer this command + * until later. + */ + if (fcp->isp_fwstate != FW_READY) { + /* + * Give ourselves at most a 250ms delay. + */ + if (isp_fclink_test(isp, 250000)) { + XS_SETERR(xs, HBA_SELTIMEOUT); + if (fcp->loop_seen_once) { + return (CMD_RQLATER); + } else { + return (CMD_COMPLETE); + } + } + } + + /* + * If we're not on a Fabric, we can't have a target + * above FL_PORT_ID-1. + * + * If we're on a fabric and *not* connected as an F-port, + * we can't have a target less than FC_SNS_ID+1. This + * keeps us from having to sort out the difference between + * local public loop devices and those which we might get + * from a switch's database. + */ + if (fcp->isp_onfabric == 0) { + if (target >= FL_PORT_ID) { + XS_SETERR(xs, HBA_SELTIMEOUT); + return (CMD_COMPLETE); + } + } else { + if (target >= FL_PORT_ID && target <= FC_SNS_ID) { + XS_SETERR(xs, HBA_SELTIMEOUT); + return (CMD_COMPLETE); + } + if (fcp->isp_topo != TOPO_F_PORT && + target < FL_PORT_ID) { + XS_SETERR(xs, HBA_SELTIMEOUT); + return (CMD_COMPLETE); + } + } + + /* + * If our loop state is such that we haven't yet received + * a "Port Database Changed" notification (after a LIP or + * a Loop Reset or firmware initialization), then defer + * sending commands for a little while, but only if we've + * seen a valid loop at one point (otherwise we can get + * stuck at initialization time). + */ + if (fcp->isp_loopstate < LOOP_PDB_RCVD) { + XS_SETERR(xs, HBA_SELTIMEOUT); + if (fcp->loop_seen_once) { + return (CMD_RQLATER); + } else { + return (CMD_COMPLETE); + } + } + + /* + * If we're in the middle of loop or fabric scanning + * or merging the port databases, retry this command later. + */ + if (fcp->isp_loopstate == LOOP_SCANNING_FABRIC || + fcp->isp_loopstate == LOOP_SCANNING_LOOP || + fcp->isp_loopstate == LOOP_SYNCING_PDB) { + return (CMD_RQLATER); + } + + /* + * If our loop state is now such that we've just now + * received a Port Database Change notification, then + * we have to go off and (re)scan the fabric. We back + * out and try again later if this doesn't work. + */ + if (fcp->isp_loopstate == LOOP_PDB_RCVD && fcp->isp_onfabric) { + if (isp_scan_fabric(isp, FC4_SCSI)) { + return (CMD_RQLATER); + } + if (fcp->isp_fwstate != FW_READY || + fcp->isp_loopstate < LOOP_FSCAN_DONE) { + return (CMD_RQLATER); + } + } + + /* + * If our loop state is now such that we've just now + * received a Port Database Change notification, then + * we have to go off and (re)synchronize our port + * database. + */ + if (fcp->isp_loopstate < LOOP_READY) { + if (isp_pdb_sync(isp)) { + return (CMD_RQLATER); + } + if (fcp->isp_fwstate != FW_READY || + fcp->isp_loopstate != LOOP_READY) { + return (CMD_RQLATER); + } + } + + /* + * XXX: Here's were we would cancel any loop_dead flag + * XXX: also cancel in dead_loop timeout that's running + */ +#endif + + /* + * Now check whether we should even think about pursuing this. + */ + lp = &fcp->portdb[target]; + if (lp->valid == 0) { + XS_SETERR(xs, HBA_SELTIMEOUT); + return (CMD_COMPLETE); + } + if ((lp->roles & (SVC3_TGT_ROLE >> SVC3_ROLE_SHIFT)) == 0) { + isp_prt(isp, ISP_LOGDEBUG2, + "Target %d does not have target service", target); + XS_SETERR(xs, HBA_SELTIMEOUT); + return (CMD_COMPLETE); + } + /* + * Now turn target into what the actual Loop ID is. + */ + target = lp->loopid; + } + + /* + * Next check to see if any HBA or Device + * parameters need to be updated. + */ + if (isp->isp_update != 0) { + isp_update(isp); + } + + if (isp_getrqentry(isp, &nxti, &optr, (void *)&qep)) { + isp_prt(isp, ISP_LOGDEBUG0, "Request Queue Overflow"); + XS_SETERR(xs, HBA_BOTCH); + return (CMD_EAGAIN); + } + + /* + * Now see if we need to synchronize the ISP with respect to anything. + * We do dual duty here (cough) for synchronizing for busses other + * than which we got here to send a command to. + */ + reqp = (ispreq_t *) local; + if (isp->isp_sendmarker) { + uint8_t n = (IS_DUALBUS(isp)? 2: 1); + /* + * Check ports to send markers for... + */ + for (i = 0; i < n; i++) { + if ((isp->isp_sendmarker & (1 << i)) == 0) { + continue; + } + MEMZERO((void *) reqp, QENTRY_LEN); + reqp->req_header.rqs_entry_count = 1; + reqp->req_header.rqs_entry_type = RQSTYPE_MARKER; + reqp->req_modifier = SYNC_ALL; + reqp->req_target = i << 7; /* insert bus number */ + isp_put_request(isp, reqp, qep); + ISP_ADD_REQUEST(isp, nxti); + isp->isp_sendmarker &= ~(1 << i); + if (isp_getrqentry(isp, &nxti, &optr, (void *) &qep)) { + isp_prt(isp, ISP_LOGDEBUG0, + "Request Queue Overflow+"); + XS_SETERR(xs, HBA_BOTCH); + return (CMD_EAGAIN); + } + } + } + + MEMZERO((void *)reqp, QENTRY_LEN); + reqp->req_header.rqs_entry_count = 1; + if (IS_FC(isp)) { + reqp->req_header.rqs_entry_type = RQSTYPE_T2RQS; + } else { + if (XS_CDBLEN(xs) > 12) + reqp->req_header.rqs_entry_type = RQSTYPE_CMDONLY; + else + reqp->req_header.rqs_entry_type = RQSTYPE_REQUEST; + } + /* reqp->req_header.rqs_flags = 0; */ + /* reqp->req_header.rqs_seqno = 0; */ + if (IS_FC(isp)) { + /* + * See comment in isp_intr + */ + /* XS_RESID(xs) = 0; */ + + /* + * Fibre Channel always requires some kind of tag. + * The Qlogic drivers seem be happy not to use a tag, + * but this breaks for some devices (IBM drives). + */ + if (XS_TAG_P(xs)) { + ((ispreqt2_t *)reqp)->req_flags = XS_TAG_TYPE(xs); + } else { + /* + * If we don't know what tag to use, use HEAD OF QUEUE + * for Request Sense or Simple. + */ + if (XS_CDBP(xs)[0] == 0x3) /* REQUEST SENSE */ + ((ispreqt2_t *)reqp)->req_flags = REQFLAG_HTAG; + else + ((ispreqt2_t *)reqp)->req_flags = REQFLAG_STAG; + } + } else { + sdparam *sdp = (sdparam *)isp->isp_param; + sdp += XS_CHANNEL(xs); + if ((sdp->isp_devparam[target].actv_flags & DPARM_TQING) && + XS_TAG_P(xs)) { + reqp->req_flags = XS_TAG_TYPE(xs); + } + } + if (IS_SCSI(isp)) { + reqp->req_target = target | (XS_CHANNEL(xs) << 7); + reqp->req_lun_trn = XS_LUN(xs); + reqp->req_cdblen = XS_CDBLEN(xs); + } else if (IS_2KLOGIN(isp)) { + ((ispreqt2e_t *)reqp)->req_target = target; + ((ispreqt2e_t *)reqp)->req_scclun = XS_LUN(xs); + } else if (FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) { + ((ispreqt2_t *)reqp)->req_target = target; + ((ispreqt2_t *)reqp)->req_scclun = XS_LUN(xs); + } else { + ((ispreqt2_t *)reqp)->req_target = target; + ((ispreqt2_t *)reqp)->req_lun_trn = XS_LUN(xs); + } + MEMCPY(reqp->req_cdb, XS_CDBP(xs), XS_CDBLEN(xs)); + + reqp->req_time = XS_TIME(xs) / 1000; + if (reqp->req_time == 0 && XS_TIME(xs)) { + reqp->req_time = 1; + } + + if (isp_save_xs(isp, xs, &handle)) { + isp_prt(isp, ISP_LOGDEBUG0, "out of xflist pointers"); + XS_SETERR(xs, HBA_BOTCH); + return (CMD_EAGAIN); + } + reqp->req_handle = handle; + + /* + * Set up DMA and/or do any bus swizzling of the request entry + * so that the Qlogic F/W understands what is being asked of it. + */ + i = ISP_DMASETUP(isp, xs, reqp, &nxti, optr); + if (i != CMD_QUEUED) { + isp_destroy_handle(isp, handle); + /* + * dmasetup sets actual error in packet, and + * return what we were given to return. + */ + return (i); + } + XS_SETERR(xs, HBA_NOERROR); + isp_prt(isp, ISP_LOGDEBUG2, + "START cmd for %d.%d.%d cmd 0x%x datalen %ld", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs), XS_CDBP(xs)[0], + (long) XS_XFRLEN(xs)); + ISP_ADD_REQUEST(isp, nxti); + isp->isp_nactive++; + return (CMD_QUEUED); +} + +/* + * isp control + * Locks (ints blocked) assumed held. + */ + +int +isp_control(ispsoftc_t *isp, ispctl_t ctl, void *arg) +{ + XS_T *xs; + mbreg_t mbs; + int bus, tgt; + uint16_t handle; + + MEMZERO(&mbs, sizeof (mbs)); + + switch (ctl) { + default: + isp_prt(isp, ISP_LOGERR, "Unknown Control Opcode 0x%x", ctl); + break; + + case ISPCTL_RESET_BUS: + /* + * Issue a bus reset. + */ + mbs.param[0] = MBOX_BUS_RESET; + if (IS_SCSI(isp)) { + mbs.param[1] = + ((sdparam *) isp->isp_param)->isp_bus_reset_delay; + if (mbs.param[1] < 2) { + mbs.param[1] = 2; + } + bus = *((int *) arg); + if (IS_DUALBUS(isp)) { + mbs.param[2] = bus; + } + } else { + mbs.param[1] = 10; + bus = 0; + } + isp->isp_sendmarker |= (1 << bus); + isp_mboxcmd(isp, &mbs, MBLOGALL); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + break; + } + isp_prt(isp, ISP_LOGINFO, + "driver initiated bus reset of bus %d", bus); + return (0); + + case ISPCTL_RESET_DEV: + tgt = (*((int *) arg)) & 0xffff; + bus = (*((int *) arg)) >> 16; + mbs.param[0] = MBOX_ABORT_TARGET; + if (IS_SCSI(isp)) { + mbs.param[1] = (tgt << 8) | (bus << 15); + } else { + if (IS_2KLOGIN(isp)) { + mbs.param[1] = tgt; + mbs.ibits |= (1 << 10); + } else { + mbs.param[1] = (tgt << 8); + } + } + mbs.param[2] = 3; /* 'delay', in seconds */ + isp_mboxcmd(isp, &mbs, MBLOGALL); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + break; + } + isp_prt(isp, ISP_LOGINFO, + "Target %d on Bus %d Reset Succeeded", tgt, bus); + isp->isp_sendmarker |= (1 << bus); + return (0); + + case ISPCTL_ABORT_CMD: + xs = (XS_T *) arg; + tgt = XS_TGT(xs); + handle = isp_find_handle(isp, xs); + if (handle == 0) { + isp_prt(isp, ISP_LOGWARN, + "cannot find handle for command to abort"); + break; + } + bus = XS_CHANNEL(xs); + mbs.param[0] = MBOX_ABORT; + if (IS_FC(isp)) { + if (FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) { + if (IS_2KLOGIN(isp)) { + mbs.param[1] = tgt; + } else { + mbs.param[1] = tgt << 8; + } + mbs.param[6] = XS_LUN(xs); + } else { + mbs.param[1] = tgt << 8 | XS_LUN(xs); + } + } else { + mbs.param[1] = + (bus << 15) | (XS_TGT(xs) << 8) | XS_LUN(xs); + } + mbs.param[2] = handle; + isp_mboxcmd(isp, &mbs, MBLOGALL & ~MBOX_COMMAND_ERROR); + if (mbs.param[0] == MBOX_COMMAND_COMPLETE) { + return (0); + } + /* + * XXX: Look for command in the REQUEST QUEUE. That is, + * XXX: It hasen't been picked up by firmware yet. + */ + break; + + case ISPCTL_UPDATE_PARAMS: + + isp_update(isp); + return (0); + + case ISPCTL_FCLINK_TEST: + + if (IS_FC(isp)) { + int usdelay = *((int *) arg); + if (usdelay == 0) { + usdelay = 250000; + } + return (isp_fclink_test(isp, usdelay)); + } + break; + + case ISPCTL_SCAN_FABRIC: + + if (IS_FC(isp)) { + int ftype = (arg)? *((int *) arg) : FC4_SCSI; + return (isp_scan_fabric(isp, ftype)); + } + break; + + case ISPCTL_SCAN_LOOP: + + if (IS_FC(isp)) { + return (isp_scan_loop(isp)); + } + break; + + case ISPCTL_PDB_SYNC: + + if (IS_FC(isp)) { + return (isp_pdb_sync(isp)); + } + break; + + case ISPCTL_SEND_LIP: + + if (IS_FC(isp)) { + mbs.param[0] = MBOX_INIT_LIP; + if (IS_2KLOGIN(isp)) { + mbs.ibits |= (1 << 10); + } + isp_mboxcmd(isp, &mbs, MBLOGALL); + if (mbs.param[0] == MBOX_COMMAND_COMPLETE) { + return (0); + } + } + break; + + case ISPCTL_GET_POSMAP: + + if (IS_FC(isp) && arg) { + return (isp_getmap(isp, arg)); + } + break; + + + case ISPCTL_GET_PDB: + if (IS_FC(isp) && arg) { + int id = *((int *)arg); + isp_pdb_t *pdb = arg; + return (isp_getpdb(isp, id, pdb)); + } + break; + + case ISPCTL_RUN_MBOXCMD: + + isp_mboxcmd(isp, arg, MBLOGALL); + return(0); + +#ifdef ISP_TARGET_MODE + case ISPCTL_TOGGLE_TMODE: + { + + /* + * We don't check/set against role here- that's the + * responsibility for the outer layer to coordinate. + */ + if (IS_SCSI(isp)) { + int param = *(int *)arg; + mbs.param[0] = MBOX_ENABLE_TARGET_MODE; + mbs.param[1] = param & 0xffff; + mbs.param[2] = param >> 16; + isp_mboxcmd(isp, &mbs, MBLOGALL); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + break; + } + } + return (0); + } +#endif + } + return (-1); +} + +/* + * Interrupt Service Routine(s). + * + * External (OS) framework has done the appropriate locking, + * and the locking will be held throughout this function. + */ + +/* + * Limit our stack depth by sticking with the max likely number + * of completions on a request queue at any one time. + */ +#ifndef MAX_REQUESTQ_COMPLETIONS +#define MAX_REQUESTQ_COMPLETIONS 64 +#endif + +void +isp_intr(ispsoftc_t *isp, uint16_t isr, uint16_t sema, uint16_t mbox) +{ + XS_T *complist[MAX_REQUESTQ_COMPLETIONS], *xs; + uint16_t iptr, optr, junk; + int i, nlooked = 0, ndone = 0; + +again: + /* + * Is this a mailbox related interrupt? + * The mailbox semaphore will be nonzero if so. + */ + if (sema) { + if (mbox & 0x4000) { + isp->isp_intmboxc++; + if (isp->isp_mboxbsy) { + int i = 0, obits = isp->isp_obits; + isp->isp_mboxtmp[i++] = mbox; + for (i = 1; i < MAX_MAILBOX(isp); i++) { + if ((obits & (1 << i)) == 0) { + continue; + } + isp->isp_mboxtmp[i] = + ISP_READ(isp, MBOX_OFF(i)); + } + if (isp->isp_mbxwrk0) { + if (isp_mbox_continue(isp) == 0) { + return; + } + } + MBOX_NOTIFY_COMPLETE(isp); + } else { + isp_prt(isp, ISP_LOGWARN, + "Mbox Command Async (0x%x) with no waiters", + mbox); + } + } else if (isp_parse_async(isp, mbox) < 0) { + return; + } + if ((IS_FC(isp) && mbox != ASYNC_RIO_RESP) || + isp->isp_state != ISP_RUNSTATE) { + ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT); + ISP_WRITE(isp, BIU_SEMA, 0); + return; + } + } + + /* + * We can't be getting this now. + */ + if (isp->isp_state != ISP_RUNSTATE) { + isp_prt(isp, ISP_LOGWARN, + "interrupt (ISR=%x SEMA=%x) when not ready", isr, sema); + /* + * Thank you very much! *Burrrp*! + */ + WRITE_RESPONSE_QUEUE_OUT_POINTER(isp, + READ_RESPONSE_QUEUE_IN_POINTER(isp)); + + ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT); + ISP_WRITE(isp, BIU_SEMA, 0); + return; + } + + /* + * Get the current Response Queue Out Pointer. + * + * If we're a 2300, we can ask what hardware what it thinks. + */ + if (IS_23XX(isp)) { + optr = ISP_READ(isp, isp->isp_respoutrp); + /* + * Debug: to be taken out eventually + */ + if (isp->isp_residx != optr) { + isp_prt(isp, ISP_LOGWARN, "optr %x soft optr %x", + optr, isp->isp_residx); + } + } else { + optr = isp->isp_residx; + } + + /* + * You *must* read the Response Queue In Pointer + * prior to clearing the RISC interrupt. + * + * Debounce the 2300 if revision less than 2. + */ + if (IS_2100(isp) || (IS_2300(isp) && isp->isp_revision < 2)) { + i = 0; + do { + iptr = READ_RESPONSE_QUEUE_IN_POINTER(isp); + junk = READ_RESPONSE_QUEUE_IN_POINTER(isp); + } while (junk != iptr && ++i < 1000); + + if (iptr != junk) { + ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT); + isp_prt(isp, ISP_LOGWARN, + "Response Queue Out Pointer Unstable (%x, %x)", + iptr, junk); + return; + } + } else { + iptr = READ_RESPONSE_QUEUE_IN_POINTER(isp); + } + isp->isp_resodx = iptr; + + + if (optr == iptr && sema == 0) { + /* + * There are a lot of these- reasons unknown- mostly on + * faster Alpha machines. + * + * I tried delaying after writing HCCR_CMD_CLEAR_RISC_INT to + * make sure the old interrupt went away (to avoid 'ringing' + * effects), but that didn't stop this from occurring. + */ + if (IS_23XX(isp)) { + USEC_DELAY(100); + iptr = READ_RESPONSE_QUEUE_IN_POINTER(isp); + junk = ISP_READ(isp, BIU_R2HSTSLO); + } else { + junk = ISP_READ(isp, BIU_ISR); + } + if (optr == iptr) { + if (IS_23XX(isp)) { + ; + } else { + sema = ISP_READ(isp, BIU_SEMA); + mbox = ISP_READ(isp, OUTMAILBOX0); + if ((sema & 0x3) && (mbox & 0x8000)) { + goto again; + } + } + isp->isp_intbogus++; + isp_prt(isp, ISP_LOGDEBUG1, + "bogus intr- isr %x (%x) iptr %x optr %x", + isr, junk, iptr, optr); + } + } + isp->isp_resodx = iptr; + ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT); + ISP_WRITE(isp, BIU_SEMA, 0); + + if (isp->isp_rspbsy) { + return; + } + isp->isp_rspbsy = 1; + + while (optr != iptr) { + ispstatusreq_t local, *sp = &local; + isphdr_t *hp; + int type; + uint16_t oop; + int buddaboom = 0; + + hp = (isphdr_t *) ISP_QUEUE_ENTRY(isp->isp_result, optr); + oop = optr; + optr = ISP_NXT_QENTRY(optr, RESULT_QUEUE_LEN(isp)); + nlooked++; + read_again: + /* + * Synchronize our view of this response queue entry. + */ + MEMORYBARRIER(isp, SYNC_RESULT, oop, QENTRY_LEN); + + type = isp_get_response_type(isp, hp); + + if (type == RQSTYPE_RESPONSE) { + isp_get_response(isp, (ispstatusreq_t *) hp, sp); + } else if (type == RQSTYPE_RIO2) { + isp_rio2_t rio; + isp_get_rio2(isp, (isp_rio2_t *) hp, &rio); + for (i = 0; i < rio.req_header.rqs_seqno; i++) { + isp_fastpost_complete(isp, rio.req_handles[i]); + } + if (isp->isp_fpcchiwater < rio.req_header.rqs_seqno) + isp->isp_fpcchiwater = rio.req_header.rqs_seqno; + MEMZERO(hp, QENTRY_LEN); /* PERF */ + continue; + } else { + /* + * Somebody reachable via isp_handle_other_response + * may have updated the response queue pointers for + * us, so we reload our goal index. + */ + int i = isp_handle_other_response(isp, type, hp, &optr); + if (i < 0) { + goto read_again; + } else if (i > 0) { + iptr = isp->isp_resodx; + MEMZERO(hp, QENTRY_LEN); /* PERF */ + continue; + } + + /* + * After this point, we'll just look at the header as + * we don't know how to deal with the rest of the + * response. + */ + isp_get_response(isp, (ispstatusreq_t *) hp, sp); + + /* + * It really has to be a bounced request just copied + * from the request queue to the response queue. If + * not, something bad has happened. + */ + if (sp->req_header.rqs_entry_type != RQSTYPE_REQUEST) { + isp_prt(isp, ISP_LOGERR, notresp, + sp->req_header.rqs_entry_type, oop, optr, + nlooked); + if (isp->isp_dblev & ISP_LOGDEBUG0) { + isp_print_bytes(isp, "Queue Entry", + QENTRY_LEN, sp); + } + MEMZERO(hp, QENTRY_LEN); /* PERF */ + continue; + } + buddaboom = 1; + } + + if (sp->req_header.rqs_flags & 0xf) { +#define _RQS_OFLAGS \ + ~(RQSFLAG_CONTINUATION|RQSFLAG_FULL|RQSFLAG_BADHEADER|RQSFLAG_BADPACKET) + if (sp->req_header.rqs_flags & RQSFLAG_CONTINUATION) { + isp_prt(isp, ISP_LOGWARN, + "continuation segment"); + WRITE_RESPONSE_QUEUE_OUT_POINTER(isp, optr); + continue; + } + if (sp->req_header.rqs_flags & RQSFLAG_FULL) { + isp_prt(isp, ISP_LOGDEBUG1, + "internal queues full"); + /* + * We'll synthesize a QUEUE FULL message below. + */ + } + if (sp->req_header.rqs_flags & RQSFLAG_BADHEADER) { + isp_prt(isp, ISP_LOGERR, "bad header flag"); + buddaboom++; + } + if (sp->req_header.rqs_flags & RQSFLAG_BADPACKET) { + isp_prt(isp, ISP_LOGERR, "bad request packet"); + buddaboom++; + } + if (sp->req_header.rqs_flags & _RQS_OFLAGS) { + isp_prt(isp, ISP_LOGERR, + "unknown flags (0x%x) in response", + sp->req_header.rqs_flags); + buddaboom++; + } +#undef _RQS_OFLAGS + } + if (sp->req_handle > isp->isp_maxcmds || sp->req_handle < 1) { + MEMZERO(hp, QENTRY_LEN); /* PERF */ + isp_prt(isp, ISP_LOGERR, + "bad request handle %d (type 0x%x, flags 0x%x)", + sp->req_handle, sp->req_header.rqs_entry_type, + sp->req_header.rqs_flags); + WRITE_RESPONSE_QUEUE_OUT_POINTER(isp, optr); + continue; + } + xs = isp_find_xs(isp, sp->req_handle); + if (xs == NULL) { + uint8_t ts = sp->req_completion_status & 0xff; + MEMZERO(hp, QENTRY_LEN); /* PERF */ + /* + * Only whine if this isn't the expected fallout of + * aborting the command. + */ + if (sp->req_header.rqs_entry_type != RQSTYPE_RESPONSE) { + isp_prt(isp, ISP_LOGERR, + "cannot find handle 0x%x (type 0x%x)", + sp->req_handle, + sp->req_header.rqs_entry_type); + } else if (ts != RQCS_ABORTED) { + isp_prt(isp, ISP_LOGERR, + "cannot find handle 0x%x (status 0x%x)", + sp->req_handle, ts); + } + WRITE_RESPONSE_QUEUE_OUT_POINTER(isp, optr); + continue; + } + isp_destroy_handle(isp, sp->req_handle); + if (sp->req_status_flags & RQSTF_BUS_RESET) { + XS_SETERR(xs, HBA_BUSRESET); + isp->isp_sendmarker |= (1 << XS_CHANNEL(xs)); + } + if (buddaboom) { + XS_SETERR(xs, HBA_BOTCH); + } + + if (IS_FC(isp) && (sp->req_scsi_status & RQCS_SV)) { + /* + * Fibre Channel F/W doesn't say we got status + * if there's Sense Data instead. I guess they + * think it goes w/o saying. + */ + sp->req_state_flags |= RQSF_GOT_STATUS; + } + if (sp->req_state_flags & RQSF_GOT_STATUS) { + *XS_STSP(xs) = sp->req_scsi_status & 0xff; + } + + switch (sp->req_header.rqs_entry_type) { + case RQSTYPE_RESPONSE: + XS_SET_STATE_STAT(isp, xs, sp); + isp_parse_status(isp, sp, xs); + if ((XS_NOERR(xs) || XS_ERR(xs) == HBA_NOERROR) && + (*XS_STSP(xs) == SCSI_BUSY)) { + XS_SETERR(xs, HBA_TGTBSY); + } + if (IS_SCSI(isp)) { + XS_RESID(xs) = sp->req_resid; + if ((sp->req_state_flags & RQSF_GOT_STATUS) && + (*XS_STSP(xs) == SCSI_CHECK) && + (sp->req_state_flags & RQSF_GOT_SENSE)) { + XS_SAVE_SENSE(xs, sp); + } + /* + * A new synchronous rate was negotiated for + * this target. Mark state such that we'll go + * look up that which has changed later. + */ + if (sp->req_status_flags & RQSTF_NEGOTIATION) { + int t = XS_TGT(xs); + sdparam *sdp = isp->isp_param; + sdp += XS_CHANNEL(xs); + sdp->isp_devparam[t].dev_refresh = 1; + isp->isp_update |= + (1 << XS_CHANNEL(xs)); + } + } else { + if (sp->req_status_flags & RQSF_XFER_COMPLETE) { + XS_RESID(xs) = 0; + } else if (sp->req_scsi_status & RQCS_RESID) { + XS_RESID(xs) = sp->req_resid; + } else { + XS_RESID(xs) = 0; + } + if ((sp->req_state_flags & RQSF_GOT_STATUS) && + (*XS_STSP(xs) == SCSI_CHECK) && + (sp->req_scsi_status & RQCS_SV)) { + XS_SAVE_SENSE(xs, sp); + /* solely for the benefit of debug */ + sp->req_state_flags |= RQSF_GOT_SENSE; + } + } + isp_prt(isp, ISP_LOGDEBUG2, + "asked for %ld got raw resid %ld settled for %ld", + (long) XS_XFRLEN(xs), (long) sp->req_resid, + (long) XS_RESID(xs)); + break; + case RQSTYPE_REQUEST: + if (sp->req_header.rqs_flags & RQSFLAG_FULL) { + /* + * Force Queue Full status. + */ + *XS_STSP(xs) = SCSI_QFULL; + XS_SETERR(xs, HBA_NOERROR); + } else if (XS_NOERR(xs)) { + /* + * ???? + */ + isp_prt(isp, ISP_LOGDEBUG0, + "Request Queue Entry bounced back"); + XS_SETERR(xs, HBA_BOTCH); + } + XS_RESID(xs) = XS_XFRLEN(xs); + break; + default: + isp_prt(isp, ISP_LOGWARN, + "unhandled response queue type 0x%x", + sp->req_header.rqs_entry_type); + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_BOTCH); + } + break; + } + + /* + * Free any DMA resources. As a side effect, this may + * also do any cache flushing necessary for data coherence. */ + if (XS_XFRLEN(xs)) { + ISP_DMAFREE(isp, xs, sp->req_handle); + } + + if (((isp->isp_dblev & (ISP_LOGDEBUG2|ISP_LOGDEBUG3))) || + ((isp->isp_dblev & ISP_LOGDEBUG1) && ((!XS_NOERR(xs)) || + (*XS_STSP(xs) != SCSI_GOOD)))) { + char skey; + if (sp->req_state_flags & RQSF_GOT_SENSE) { + skey = XS_SNSKEY(xs) & 0xf; + if (skey < 10) + skey += '0'; + else + skey += 'a' - 10; + } else if (*XS_STSP(xs) == SCSI_CHECK) { + skey = '?'; + } else { + skey = '.'; + } + isp_prt(isp, ISP_LOGALL, finmsg, XS_CHANNEL(xs), + XS_TGT(xs), XS_LUN(xs), XS_XFRLEN(xs), XS_RESID(xs), + *XS_STSP(xs), skey, XS_ERR(xs)); + } + + if (isp->isp_nactive > 0) + isp->isp_nactive--; + complist[ndone++] = xs; /* defer completion call until later */ + MEMZERO(hp, QENTRY_LEN); /* PERF */ + if (ndone == MAX_REQUESTQ_COMPLETIONS) { + break; + } + } + + /* + * If we looked at any commands, then it's valid to find out + * what the outpointer is. It also is a trigger to update the + * ISP's notion of what we've seen so far. + */ + if (nlooked) { + WRITE_RESPONSE_QUEUE_OUT_POINTER(isp, optr); + /* + * While we're at it, read the requst queue out pointer. + */ + isp->isp_reqodx = READ_REQUEST_QUEUE_OUT_POINTER(isp); + if (isp->isp_rscchiwater < ndone) + isp->isp_rscchiwater = ndone; + } + + isp->isp_residx = optr; + isp->isp_rspbsy = 0; + for (i = 0; i < ndone; i++) { + xs = complist[i]; + if (xs) { + isp->isp_rsltccmplt++; + isp_done(xs); + } + } +} + +/* + * Support routines. + */ + +static int +isp_parse_async(ispsoftc_t *isp, uint16_t mbox) +{ + int rval = 0; + int bus; + + if (IS_DUALBUS(isp)) { + bus = ISP_READ(isp, OUTMAILBOX6); + } else { + bus = 0; + } + isp_prt(isp, ISP_LOGDEBUG2, "Async Mbox 0x%x", mbox); + + switch (mbox) { + case ASYNC_BUS_RESET: + isp->isp_sendmarker |= (1 << bus); +#ifdef ISP_TARGET_MODE + if (isp_target_async(isp, bus, mbox)) + rval = -1; +#endif + isp_async(isp, ISPASYNC_BUS_RESET, &bus); + break; + case ASYNC_SYSTEM_ERROR: + isp->isp_state = ISP_CRASHED; + if (IS_FC(isp)) { + FCPARAM(isp)->isp_loopstate = LOOP_NIL; + FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT; + } + /* + * Were we waiting for a mailbox command to complete? + * If so, it's dead, so wake up the waiter. + */ + if (isp->isp_mboxbsy) { + isp->isp_obits = 1; + isp->isp_mboxtmp[0] = MBOX_HOST_INTERFACE_ERROR; + MBOX_NOTIFY_COMPLETE(isp); + } +#ifdef ISP_FW_CRASH_DUMP + /* + * If we have crash dumps enabled, it's up to the handler + * for isp_async to reinit stuff and restart the firmware + * after performing the crash dump. The reason we do things + * this way is that we may need to activate a kernel thread + * to do all the crash dump goop. + */ + isp_async(isp, ISPASYNC_FW_CRASH, NULL); +#else + isp_async(isp, ISPASYNC_FW_CRASH, NULL); + isp_reinit(isp); + isp_async(isp, ISPASYNC_FW_RESTARTED, NULL); +#endif + rval = -1; + break; + + case ASYNC_RQS_XFER_ERR: + isp_prt(isp, ISP_LOGERR, "Request Queue Transfer Error"); + break; + + case ASYNC_RSP_XFER_ERR: + isp_prt(isp, ISP_LOGERR, "Response Queue Transfer Error"); + break; + + case ASYNC_QWAKEUP: + /* + * We've just been notified that the Queue has woken up. + * We don't need to be chatty about this- just unlatch things + * and move on. + */ + mbox = READ_REQUEST_QUEUE_OUT_POINTER(isp); + break; + + case ASYNC_TIMEOUT_RESET: + isp_prt(isp, ISP_LOGWARN, + "timeout initiated SCSI bus reset of bus %d", bus); + isp->isp_sendmarker |= (1 << bus); +#ifdef ISP_TARGET_MODE + if (isp_target_async(isp, bus, mbox)) + rval = -1; +#endif + break; + + case ASYNC_DEVICE_RESET: + isp_prt(isp, ISP_LOGINFO, "device reset on bus %d", bus); + isp->isp_sendmarker |= (1 << bus); +#ifdef ISP_TARGET_MODE + if (isp_target_async(isp, bus, mbox)) + rval = -1; +#endif + break; + + case ASYNC_EXTMSG_UNDERRUN: + isp_prt(isp, ISP_LOGWARN, "extended message underrun"); + break; + + case ASYNC_SCAM_INT: + isp_prt(isp, ISP_LOGINFO, "SCAM interrupt"); + break; + + case ASYNC_HUNG_SCSI: + isp_prt(isp, ISP_LOGERR, + "stalled SCSI Bus after DATA Overrun"); + /* XXX: Need to issue SCSI reset at this point */ + break; + + case ASYNC_KILLED_BUS: + isp_prt(isp, ISP_LOGERR, "SCSI Bus reset after DATA Overrun"); + break; + + case ASYNC_BUS_TRANSIT: + mbox = ISP_READ(isp, OUTMAILBOX2); + switch (mbox & 0x1c00) { + case SXP_PINS_LVD_MODE: + isp_prt(isp, ISP_LOGINFO, "Transition to LVD mode"); + SDPARAM(isp)->isp_diffmode = 0; + SDPARAM(isp)->isp_ultramode = 0; + SDPARAM(isp)->isp_lvdmode = 1; + break; + case SXP_PINS_HVD_MODE: + isp_prt(isp, ISP_LOGINFO, + "Transition to Differential mode"); + SDPARAM(isp)->isp_diffmode = 1; + SDPARAM(isp)->isp_ultramode = 0; + SDPARAM(isp)->isp_lvdmode = 0; + break; + case SXP_PINS_SE_MODE: + isp_prt(isp, ISP_LOGINFO, + "Transition to Single Ended mode"); + SDPARAM(isp)->isp_diffmode = 0; + SDPARAM(isp)->isp_ultramode = 1; + SDPARAM(isp)->isp_lvdmode = 0; + break; + default: + isp_prt(isp, ISP_LOGWARN, + "Transition to Unknown Mode 0x%x", mbox); + break; + } + /* + * XXX: Set up to renegotiate again! + */ + /* Can only be for a 1080... */ + isp->isp_sendmarker |= (1 << bus); + break; + + /* + * We can use bus, which will always be zero for FC cards, + * as a mailbox pattern accumulator to be checked below. + */ + case ASYNC_RIO5: + bus = 0x1ce; /* outgoing mailbox regs 1-3, 6-7 */ + break; + + case ASYNC_RIO4: + bus = 0x14e; /* outgoing mailbox regs 1-3, 6 */ + break; + + case ASYNC_RIO3: + bus = 0x10e; /* outgoing mailbox regs 1-3 */ + break; + + case ASYNC_RIO2: + bus = 0x106; /* outgoing mailbox regs 1-2 */ + break; + + case ASYNC_RIO1: + case ASYNC_CMD_CMPLT: + bus = 0x102; /* outgoing mailbox regs 1 */ + break; + + case ASYNC_RIO_RESP: + return (rval); + + case ASYNC_CTIO_DONE: + { +#ifdef ISP_TARGET_MODE + int handle = + (ISP_READ(isp, OUTMAILBOX2) << 16) | + (ISP_READ(isp, OUTMAILBOX1)); + if (isp_target_async(isp, handle, mbox)) { + rval = -1; + } else { + /* count it as a fast posting intr */ + isp->isp_fphccmplt++; + } +#else + isp_prt(isp, ISP_LOGINFO, "Fast Posting CTIO done"); + isp->isp_fphccmplt++; /* count it as a fast posting intr */ +#endif + break; + } + case ASYNC_LIP_F8: + case ASYNC_LIP_OCCURRED: + FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT; + FCPARAM(isp)->isp_loopstate = LOOP_LIP_RCVD; + isp->isp_sendmarker = 1; + isp_mark_getpdb_all(isp); + isp_async(isp, ISPASYNC_LIP, NULL); +#ifdef ISP_TARGET_MODE + if (isp_target_async(isp, bus, mbox)) + rval = -1; +#endif + /* + * We've had problems with data corruption occuring on + * commands that complete (with no apparent error) after + * we receive a LIP. This has been observed mostly on + * Local Loop topologies. To be safe, let's just mark + * all active commands as dead. + */ + if (FCPARAM(isp)->isp_topo == TOPO_NL_PORT || + FCPARAM(isp)->isp_topo == TOPO_FL_PORT) { + int i, j; + for (i = j = 0; i < isp->isp_maxcmds; i++) { + XS_T *xs; + xs = isp->isp_xflist[i]; + if (xs != NULL) { + j++; + XS_SETERR(xs, HBA_BUSRESET); + } + } + if (j) { + isp_prt(isp, ISP_LOGERR, + "LIP destroyed %d active commands", j); + } + } + break; + + case ASYNC_LOOP_UP: + isp->isp_sendmarker = 1; + FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT; + FCPARAM(isp)->isp_loopstate = LOOP_LIP_RCVD; + isp_mark_getpdb_all(isp); + isp_async(isp, ISPASYNC_LOOP_UP, NULL); +#ifdef ISP_TARGET_MODE + if (isp_target_async(isp, bus, mbox)) { + rval = -1; + } +#endif + break; + + case ASYNC_LOOP_DOWN: + isp->isp_sendmarker = 1; + FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT; + FCPARAM(isp)->isp_loopstate = LOOP_NIL; + isp_mark_getpdb_all(isp); + isp_async(isp, ISPASYNC_LOOP_DOWN, NULL); +#ifdef ISP_TARGET_MODE + if (isp_target_async(isp, bus, mbox)) { + rval = -1; + } +#endif + break; + + case ASYNC_LOOP_RESET: + isp->isp_sendmarker = 1; + FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT; + FCPARAM(isp)->isp_loopstate = LOOP_NIL; + isp_mark_getpdb_all(isp); + isp_async(isp, ISPASYNC_LOOP_RESET, NULL); +#ifdef ISP_TARGET_MODE + if (isp_target_async(isp, bus, mbox)) { + rval = -1; + } +#endif + break; + + case ASYNC_PDB_CHANGED: + isp->isp_sendmarker = 1; + FCPARAM(isp)->isp_loopstate = LOOP_PDB_RCVD; + isp_mark_getpdb_all(isp); + isp_async(isp, ISPASYNC_CHANGE_NOTIFY, ISPASYNC_CHANGE_PDB); + break; + + case ASYNC_CHANGE_NOTIFY: + /* + * Not correct, but it will force us to rescan the loop. + */ + FCPARAM(isp)->isp_loopstate = LOOP_PDB_RCVD; + isp_mark_getpdb_all(isp); + isp_async(isp, ISPASYNC_CHANGE_NOTIFY, ISPASYNC_CHANGE_SNS); + break; + + case ASYNC_PTPMODE: + if (FCPARAM(isp)->isp_onfabric) + FCPARAM(isp)->isp_topo = TOPO_F_PORT; + else + FCPARAM(isp)->isp_topo = TOPO_N_PORT; + isp_mark_getpdb_all(isp); + isp->isp_sendmarker = 1; + FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT; + FCPARAM(isp)->isp_loopstate = LOOP_LIP_RCVD; + isp_async(isp, ISPASYNC_CHANGE_NOTIFY, ISPASYNC_CHANGE_OTHER); +#ifdef ISP_TARGET_MODE + if (isp_target_async(isp, bus, mbox)) + rval = -1; +#endif + isp_prt(isp, ISP_LOGINFO, "Point-to-Point mode"); + break; + + case ASYNC_CONNMODE: + mbox = ISP_READ(isp, OUTMAILBOX1); + isp_mark_getpdb_all(isp); + switch (mbox) { + case ISP_CONN_LOOP: + isp_prt(isp, ISP_LOGINFO, + "Point-to-Point -> Loop mode"); + break; + case ISP_CONN_PTP: + isp_prt(isp, ISP_LOGINFO, + "Loop -> Point-to-Point mode"); + break; + case ISP_CONN_BADLIP: + isp_prt(isp, ISP_LOGWARN, + "Point-to-Point -> Loop mode (BAD LIP)"); + break; + case ISP_CONN_FATAL: + isp_prt(isp, ISP_LOGERR, "FATAL CONNECTION ERROR"); +#ifdef ISP_FW_CRASH_DUMP + isp_async(isp, ISPASYNC_FW_CRASH, NULL); +#else + isp_async(isp, ISPASYNC_FW_CRASH, NULL); + isp_reinit(isp); + isp_async(isp, ISPASYNC_FW_RESTARTED, NULL); +#endif + return (-1); + case ISP_CONN_LOOPBACK: + isp_prt(isp, ISP_LOGWARN, + "Looped Back in Point-to-Point mode"); + break; + default: + isp_prt(isp, ISP_LOGWARN, + "Unknown connection mode (0x%x)", mbox); + break; + } + isp_async(isp, ISPASYNC_CHANGE_NOTIFY, ISPASYNC_CHANGE_OTHER); + isp->isp_sendmarker = 1; + FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT; + FCPARAM(isp)->isp_loopstate = LOOP_LIP_RCVD; + break; + + default: + isp_prt(isp, ISP_LOGWARN, "Unknown Async Code 0x%x", mbox); + break; + } + + if (bus & 0x100) { + int i, nh; + uint16_t handles[16]; + + for (nh = 0, i = 1; i < MAX_MAILBOX(isp); i++) { + if ((bus & (1 << i)) == 0) { + continue; + } + handles[nh++] = ISP_READ(isp, MBOX_OFF(i)); + } + for (i = 0; i < nh; i++) { + isp_fastpost_complete(isp, handles[i]); + isp_prt(isp, ISP_LOGDEBUG3, + "fast post completion of %u", handles[i]); + } + if (isp->isp_fpcchiwater < nh) + isp->isp_fpcchiwater = nh; + } else { + isp->isp_intoasync++; + } + return (rval); +} + +/* + * Handle other response entries. A pointer to the request queue output + * index is here in case we want to eat several entries at once, although + * this is not used currently. + */ + +static int +isp_handle_other_response(ispsoftc_t *isp, int type, + isphdr_t *hp, uint16_t *optrp) +{ + switch (type) { + case RQSTYPE_STATUS_CONT: + isp_prt(isp, ISP_LOGINFO, "Ignored Continuation Response"); + return (1); + case RQSTYPE_ATIO: + case RQSTYPE_CTIO: + case RQSTYPE_ENABLE_LUN: + case RQSTYPE_MODIFY_LUN: + case RQSTYPE_NOTIFY: + case RQSTYPE_NOTIFY_ACK: + case RQSTYPE_CTIO1: + case RQSTYPE_ATIO2: + case RQSTYPE_CTIO2: + case RQSTYPE_CTIO3: + isp->isp_rsltccmplt++; /* count as a response completion */ +#ifdef ISP_TARGET_MODE + if (isp_target_notify(isp, (ispstatusreq_t *) hp, optrp)) { + return (1); + } +#endif + /* FALLTHROUGH */ + case RQSTYPE_REQUEST: + default: + USEC_DELAY(100); + if (type != isp_get_response_type(isp, hp)) { + /* + * This is questionable- we're just papering over + * something we've seen on SMP linux in target + * mode- we don't really know what's happening + * here that causes us to think we've gotten + * an entry, but that either the entry isn't + * filled out yet or our CPU read data is stale. + */ + isp_prt(isp, ISP_LOGINFO, + "unstable type in response queue"); + return (-1); + } + isp_prt(isp, ISP_LOGWARN, "Unhandled Response Type 0x%x", + isp_get_response_type(isp, hp)); + if (isp_async(isp, ISPASYNC_UNHANDLED_RESPONSE, hp)) { + return (1); + } + return (0); + } +} + +static void +isp_parse_status(ispsoftc_t *isp, ispstatusreq_t *sp, XS_T *xs) +{ + switch (sp->req_completion_status & 0xff) { + case RQCS_COMPLETE: + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_NOERROR); + } + return; + + case RQCS_INCOMPLETE: + if ((sp->req_state_flags & RQSF_GOT_TARGET) == 0) { + isp_prt(isp, ISP_LOGDEBUG1, + "Selection Timeout for %d.%d.%d", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_SELTIMEOUT); + } + return; + } + isp_prt(isp, ISP_LOGERR, + "command incomplete for %d.%d.%d, state 0x%x", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs), + sp->req_state_flags); + break; + + case RQCS_DMA_ERROR: + isp_prt(isp, ISP_LOGERR, "DMA error for command on %d.%d.%d", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_TRANSPORT_ERROR: + { + char buf[172]; + SNPRINTF(buf, sizeof (buf), "states=>"); + if (sp->req_state_flags & RQSF_GOT_BUS) { + SNPRINTF(buf, sizeof (buf), "%s GOT_BUS", buf); + } + if (sp->req_state_flags & RQSF_GOT_TARGET) { + SNPRINTF(buf, sizeof (buf), "%s GOT_TGT", buf); + } + if (sp->req_state_flags & RQSF_SENT_CDB) { + SNPRINTF(buf, sizeof (buf), "%s SENT_CDB", buf); + } + if (sp->req_state_flags & RQSF_XFRD_DATA) { + SNPRINTF(buf, sizeof (buf), "%s XFRD_DATA", buf); + } + if (sp->req_state_flags & RQSF_GOT_STATUS) { + SNPRINTF(buf, sizeof (buf), "%s GOT_STS", buf); + } + if (sp->req_state_flags & RQSF_GOT_SENSE) { + SNPRINTF(buf, sizeof (buf), "%s GOT_SNS", buf); + } + if (sp->req_state_flags & RQSF_XFER_COMPLETE) { + SNPRINTF(buf, sizeof (buf), "%s XFR_CMPLT", buf); + } + SNPRINTF(buf, sizeof (buf), "%s\nstatus=>", buf); + if (sp->req_status_flags & RQSTF_DISCONNECT) { + SNPRINTF(buf, sizeof (buf), "%s Disconnect", buf); + } + if (sp->req_status_flags & RQSTF_SYNCHRONOUS) { + SNPRINTF(buf, sizeof (buf), "%s Sync_xfr", buf); + } + if (sp->req_status_flags & RQSTF_PARITY_ERROR) { + SNPRINTF(buf, sizeof (buf), "%s Parity", buf); + } + if (sp->req_status_flags & RQSTF_BUS_RESET) { + SNPRINTF(buf, sizeof (buf), "%s Bus_Reset", buf); + } + if (sp->req_status_flags & RQSTF_DEVICE_RESET) { + SNPRINTF(buf, sizeof (buf), "%s Device_Reset", buf); + } + if (sp->req_status_flags & RQSTF_ABORTED) { + SNPRINTF(buf, sizeof (buf), "%s Aborted", buf); + } + if (sp->req_status_flags & RQSTF_TIMEOUT) { + SNPRINTF(buf, sizeof (buf), "%s Timeout", buf); + } + if (sp->req_status_flags & RQSTF_NEGOTIATION) { + SNPRINTF(buf, sizeof (buf), "%s Negotiation", buf); + } + isp_prt(isp, ISP_LOGERR, "%s", buf); + isp_prt(isp, ISP_LOGERR, "transport error for %d.%d.%d:\n%s", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs), buf); + break; + } + case RQCS_RESET_OCCURRED: + isp_prt(isp, ISP_LOGWARN, + "bus reset destroyed command for %d.%d.%d", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + isp->isp_sendmarker |= (1 << XS_CHANNEL(xs)); + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_BUSRESET); + } + return; + + case RQCS_ABORTED: + isp_prt(isp, ISP_LOGERR, "command aborted for %d.%d.%d", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + isp->isp_sendmarker |= (1 << XS_CHANNEL(xs)); + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_ABORTED); + } + return; + + case RQCS_TIMEOUT: + isp_prt(isp, ISP_LOGWARN, "command timed out for %d.%d.%d", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + /* + * Check to see if we logged out the device. + */ + if (IS_FC(isp)) { + if ((sp->req_completion_status & RQSTF_LOGOUT) && + FCPARAM(isp)->portdb[XS_TGT(xs)].valid && + FCPARAM(isp)->portdb[XS_TGT(xs)].fabric_dev) { + FCPARAM(isp)->portdb[XS_TGT(xs)].relogin = 1; + } + } + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_CMDTIMEOUT); + } + return; + + case RQCS_DATA_OVERRUN: + XS_RESID(xs) = sp->req_resid; + isp_prt(isp, ISP_LOGERR, "data overrun for command on %d.%d.%d", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_DATAOVR); + } + return; + + case RQCS_COMMAND_OVERRUN: + isp_prt(isp, ISP_LOGERR, + "command overrun for command on %d.%d.%d", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_STATUS_OVERRUN: + isp_prt(isp, ISP_LOGERR, + "status overrun for command on %d.%d.%d", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_BAD_MESSAGE: + isp_prt(isp, ISP_LOGERR, + "msg not COMMAND COMPLETE after status %d.%d.%d", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_NO_MESSAGE_OUT: + isp_prt(isp, ISP_LOGERR, + "No MESSAGE OUT phase after selection on %d.%d.%d", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_EXT_ID_FAILED: + isp_prt(isp, ISP_LOGERR, "EXTENDED IDENTIFY failed %d.%d.%d", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_IDE_MSG_FAILED: + isp_prt(isp, ISP_LOGERR, + "INITIATOR DETECTED ERROR rejected by %d.%d.%d", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_ABORT_MSG_FAILED: + isp_prt(isp, ISP_LOGERR, "ABORT OPERATION rejected by %d.%d.%d", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_REJECT_MSG_FAILED: + isp_prt(isp, ISP_LOGERR, "MESSAGE REJECT rejected by %d.%d.%d", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_NOP_MSG_FAILED: + isp_prt(isp, ISP_LOGERR, "NOP rejected by %d.%d.%d", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_PARITY_ERROR_MSG_FAILED: + isp_prt(isp, ISP_LOGERR, + "MESSAGE PARITY ERROR rejected by %d.%d.%d", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_DEVICE_RESET_MSG_FAILED: + isp_prt(isp, ISP_LOGWARN, + "BUS DEVICE RESET rejected by %d.%d.%d", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_ID_MSG_FAILED: + isp_prt(isp, ISP_LOGERR, "IDENTIFY rejected by %d.%d.%d", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_UNEXP_BUS_FREE: + isp_prt(isp, ISP_LOGERR, "%d.%d.%d had an unexpected bus free", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_DATA_UNDERRUN: + { + if (IS_FC(isp)) { + int ru_marked = (sp->req_scsi_status & RQCS_RU) != 0; + if (!ru_marked || sp->req_resid > XS_XFRLEN(xs)) { + isp_prt(isp, ISP_LOGWARN, bun, XS_TGT(xs), + XS_LUN(xs), XS_XFRLEN(xs), sp->req_resid, + (ru_marked)? "marked" : "not marked"); + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_BOTCH); + } + return; + } + } + XS_RESID(xs) = sp->req_resid; + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_NOERROR); + } + return; + } + + case RQCS_XACT_ERR1: + isp_prt(isp, ISP_LOGERR, xact1, XS_CHANNEL(xs), + XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_XACT_ERR2: + isp_prt(isp, ISP_LOGERR, xact2, + XS_LUN(xs), XS_TGT(xs), XS_CHANNEL(xs)); + break; + + case RQCS_XACT_ERR3: + isp_prt(isp, ISP_LOGERR, xact3, + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_BAD_ENTRY: + isp_prt(isp, ISP_LOGERR, "Invalid IOCB entry type detected"); + break; + + case RQCS_QUEUE_FULL: + isp_prt(isp, ISP_LOGDEBUG0, + "internal queues full for %d.%d.%d status 0x%x", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs), *XS_STSP(xs)); + + /* + * If QFULL or some other status byte is set, then this + * isn't an error, per se. + * + * Unfortunately, some QLogic f/w writers have, in + * some cases, ommitted to *set* status to QFULL. + * + + if (*XS_STSP(xs) != SCSI_GOOD && XS_NOERR(xs)) { + XS_SETERR(xs, HBA_NOERROR); + return; + } + + * + * + */ + + *XS_STSP(xs) = SCSI_QFULL; + XS_SETERR(xs, HBA_NOERROR); + return; + + case RQCS_PHASE_SKIPPED: + isp_prt(isp, ISP_LOGERR, pskip, XS_CHANNEL(xs), + XS_TGT(xs), XS_LUN(xs)); + break; + + case RQCS_ARQS_FAILED: + isp_prt(isp, ISP_LOGERR, + "Auto Request Sense failed for %d.%d.%d", + XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs)); + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_ARQFAIL); + } + return; + + case RQCS_WIDE_FAILED: + isp_prt(isp, ISP_LOGERR, + "Wide Negotiation failed for %d.%d.%d", + XS_TGT(xs), XS_LUN(xs), XS_CHANNEL(xs)); + if (IS_SCSI(isp)) { + sdparam *sdp = isp->isp_param; + sdp += XS_CHANNEL(xs); + sdp->isp_devparam[XS_TGT(xs)].goal_flags &= ~DPARM_WIDE; + sdp->isp_devparam[XS_TGT(xs)].dev_update = 1; + isp->isp_update |= (1 << XS_CHANNEL(xs)); + } + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_NOERROR); + } + return; + + case RQCS_SYNCXFER_FAILED: + isp_prt(isp, ISP_LOGERR, + "SDTR Message failed for target %d.%d.%d", + XS_TGT(xs), XS_LUN(xs), XS_CHANNEL(xs)); + if (IS_SCSI(isp)) { + sdparam *sdp = isp->isp_param; + sdp += XS_CHANNEL(xs); + sdp->isp_devparam[XS_TGT(xs)].goal_flags &= ~DPARM_SYNC; + sdp->isp_devparam[XS_TGT(xs)].dev_update = 1; + isp->isp_update |= (1 << XS_CHANNEL(xs)); + } + break; + + case RQCS_LVD_BUSERR: + isp_prt(isp, ISP_LOGERR, + "Bad LVD condition while talking to %d.%d.%d", + XS_TGT(xs), XS_LUN(xs), XS_CHANNEL(xs)); + break; + + case RQCS_PORT_UNAVAILABLE: + /* + * No such port on the loop. Moral equivalent of SELTIMEO + */ + case RQCS_PORT_LOGGED_OUT: + /* + * It was there (maybe)- treat as a selection timeout. + */ + if ((sp->req_completion_status & 0xff) == RQCS_PORT_UNAVAILABLE) + isp_prt(isp, ISP_LOGINFO, + "port unavailable for target %d", XS_TGT(xs)); + else + isp_prt(isp, ISP_LOGINFO, + "port logout for target %d", XS_TGT(xs)); + /* + * If we're on a local loop, force a LIP (which is overkill) + * to force a re-login of this unit. If we're on fabric, + * then we'll have to relogin as a matter of course. + */ + if (FCPARAM(isp)->isp_topo == TOPO_NL_PORT || + FCPARAM(isp)->isp_topo == TOPO_FL_PORT) { + mbreg_t mbs; + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_INIT_LIP; + if (IS_2KLOGIN(isp)) { + mbs.ibits |= (1 << 10); + } + isp_mboxcmd_qnw(isp, &mbs, 1); + } + + /* + * Probably overkill. + */ + isp->isp_sendmarker = 1; + FCPARAM(isp)->isp_loopstate = LOOP_PDB_RCVD; + isp_mark_getpdb_all(isp); + isp_async(isp, ISPASYNC_CHANGE_NOTIFY, ISPASYNC_CHANGE_OTHER); + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_SELTIMEOUT); + } + return; + + case RQCS_PORT_CHANGED: + isp_prt(isp, ISP_LOGWARN, + "port changed for target %d", XS_TGT(xs)); + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_SELTIMEOUT); + } + return; + + case RQCS_PORT_BUSY: + isp_prt(isp, ISP_LOGWARN, + "port busy for target %d", XS_TGT(xs)); + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_TGTBSY); + } + return; + + default: + isp_prt(isp, ISP_LOGERR, "Unknown Completion Status 0x%x", + sp->req_completion_status); + break; + } + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_BOTCH); + } +} + +static void +isp_fastpost_complete(ispsoftc_t *isp, uint16_t fph) +{ + XS_T *xs; + + if (fph == 0) { + return; + } + xs = isp_find_xs(isp, fph); + if (xs == NULL) { + isp_prt(isp, ISP_LOGWARN, + "Command for fast post handle 0x%x not found", fph); + return; + } + isp_destroy_handle(isp, fph); + + /* + * Since we don't have a result queue entry item, + * we must believe that SCSI status is zero and + * that all data transferred. + */ + XS_SET_STATE_STAT(isp, xs, NULL); + XS_RESID(xs) = 0; + *XS_STSP(xs) = SCSI_GOOD; + if (XS_XFRLEN(xs)) { + ISP_DMAFREE(isp, xs, fph); + } + if (isp->isp_nactive) + isp->isp_nactive--; + isp->isp_fphccmplt++; + isp_done(xs); +} + +static int +isp_mbox_continue(ispsoftc_t *isp) +{ + mbreg_t mbs; + uint16_t *ptr; + uint32_t offset; + + switch (isp->isp_lastmbxcmd) { + case MBOX_WRITE_RAM_WORD: + case MBOX_READ_RAM_WORD: + case MBOX_WRITE_RAM_WORD_EXTENDED: + case MBOX_READ_RAM_WORD_EXTENDED: + break; + default: + return (1); + } + if (isp->isp_mboxtmp[0] != MBOX_COMMAND_COMPLETE) { + isp->isp_mbxwrk0 = 0; + return (-1); + } + + /* + * Clear the previous interrupt. + */ + ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT); + ISP_WRITE(isp, BIU_SEMA, 0); + + /* + * Continue with next word. + */ + MEMZERO(&mbs, sizeof (mbs)); + ptr = isp->isp_mbxworkp; + switch (isp->isp_lastmbxcmd) { + case MBOX_WRITE_RAM_WORD: + mbs.param[1] = isp->isp_mbxwrk1++;; + mbs.param[2] = *ptr++;; + break; + case MBOX_READ_RAM_WORD: + *ptr++ = isp->isp_mboxtmp[2]; + mbs.param[1] = isp->isp_mbxwrk1++; + break; + case MBOX_WRITE_RAM_WORD_EXTENDED: + offset = isp->isp_mbxwrk1; + offset |= ((uint32_t) isp->isp_mbxwrk8 << 16); + + mbs.param[2] = *ptr++;; + mbs.param[1] = offset; + mbs.param[8] = offset >> 16; + isp->isp_mbxwrk1 = ++offset; + isp->isp_mbxwrk8 = offset >> 16; + break; + case MBOX_READ_RAM_WORD_EXTENDED: + offset = isp->isp_mbxwrk1; + offset |= ((uint32_t) isp->isp_mbxwrk8 << 16); + + *ptr++ = isp->isp_mboxtmp[2]; + mbs.param[1] = offset; + mbs.param[8] = offset >> 16; + isp->isp_mbxwrk1 = ++offset; + isp->isp_mbxwrk8 = offset >> 16; + break; + } + isp->isp_mbxworkp = ptr; + isp->isp_mbxwrk0--; + mbs.param[0] = isp->isp_lastmbxcmd; + isp_mboxcmd_qnw(isp, &mbs, 0); + return (0); +} + + +#define HIWRD(x) ((x) >> 16) +#define LOWRD(x) ((x) & 0xffff) +#define ISPOPMAP(a, b) (((a) << 16) | (b)) +static const uint32_t mbpscsi[] = { + ISPOPMAP(0x01, 0x01), /* 0x00: MBOX_NO_OP */ + ISPOPMAP(0x1f, 0x01), /* 0x01: MBOX_LOAD_RAM */ + ISPOPMAP(0x03, 0x01), /* 0x02: MBOX_EXEC_FIRMWARE */ + ISPOPMAP(0x1f, 0x01), /* 0x03: MBOX_DUMP_RAM */ + ISPOPMAP(0x07, 0x07), /* 0x04: MBOX_WRITE_RAM_WORD */ + ISPOPMAP(0x03, 0x07), /* 0x05: MBOX_READ_RAM_WORD */ + ISPOPMAP(0x3f, 0x3f), /* 0x06: MBOX_MAILBOX_REG_TEST */ + ISPOPMAP(0x07, 0x07), /* 0x07: MBOX_VERIFY_CHECKSUM */ + ISPOPMAP(0x01, 0x0f), /* 0x08: MBOX_ABOUT_FIRMWARE */ + ISPOPMAP(0x00, 0x00), /* 0x09: */ + ISPOPMAP(0x00, 0x00), /* 0x0a: */ + ISPOPMAP(0x00, 0x00), /* 0x0b: */ + ISPOPMAP(0x00, 0x00), /* 0x0c: */ + ISPOPMAP(0x00, 0x00), /* 0x0d: */ + ISPOPMAP(0x01, 0x05), /* 0x0e: MBOX_CHECK_FIRMWARE */ + ISPOPMAP(0x00, 0x00), /* 0x0f: */ + ISPOPMAP(0x1f, 0x1f), /* 0x10: MBOX_INIT_REQ_QUEUE */ + ISPOPMAP(0x3f, 0x3f), /* 0x11: MBOX_INIT_RES_QUEUE */ + ISPOPMAP(0x0f, 0x0f), /* 0x12: MBOX_EXECUTE_IOCB */ + ISPOPMAP(0x03, 0x03), /* 0x13: MBOX_WAKE_UP */ + ISPOPMAP(0x01, 0x3f), /* 0x14: MBOX_STOP_FIRMWARE */ + ISPOPMAP(0x0f, 0x0f), /* 0x15: MBOX_ABORT */ + ISPOPMAP(0x03, 0x03), /* 0x16: MBOX_ABORT_DEVICE */ + ISPOPMAP(0x07, 0x07), /* 0x17: MBOX_ABORT_TARGET */ + ISPOPMAP(0x07, 0x07), /* 0x18: MBOX_BUS_RESET */ + ISPOPMAP(0x03, 0x07), /* 0x19: MBOX_STOP_QUEUE */ + ISPOPMAP(0x03, 0x07), /* 0x1a: MBOX_START_QUEUE */ + ISPOPMAP(0x03, 0x07), /* 0x1b: MBOX_SINGLE_STEP_QUEUE */ + ISPOPMAP(0x03, 0x07), /* 0x1c: MBOX_ABORT_QUEUE */ + ISPOPMAP(0x03, 0x4f), /* 0x1d: MBOX_GET_DEV_QUEUE_STATUS */ + ISPOPMAP(0x00, 0x00), /* 0x1e: */ + ISPOPMAP(0x01, 0x07), /* 0x1f: MBOX_GET_FIRMWARE_STATUS */ + ISPOPMAP(0x01, 0x07), /* 0x20: MBOX_GET_INIT_SCSI_ID */ + ISPOPMAP(0x01, 0x07), /* 0x21: MBOX_GET_SELECT_TIMEOUT */ + ISPOPMAP(0x01, 0xc7), /* 0x22: MBOX_GET_RETRY_COUNT */ + ISPOPMAP(0x01, 0x07), /* 0x23: MBOX_GET_TAG_AGE_LIMIT */ + ISPOPMAP(0x01, 0x03), /* 0x24: MBOX_GET_CLOCK_RATE */ + ISPOPMAP(0x01, 0x07), /* 0x25: MBOX_GET_ACT_NEG_STATE */ + ISPOPMAP(0x01, 0x07), /* 0x26: MBOX_GET_ASYNC_DATA_SETUP_TIME */ + ISPOPMAP(0x01, 0x07), /* 0x27: MBOX_GET_PCI_PARAMS */ + ISPOPMAP(0x03, 0x4f), /* 0x28: MBOX_GET_TARGET_PARAMS */ + ISPOPMAP(0x03, 0x0f), /* 0x29: MBOX_GET_DEV_QUEUE_PARAMS */ + ISPOPMAP(0x01, 0x07), /* 0x2a: MBOX_GET_RESET_DELAY_PARAMS */ + ISPOPMAP(0x00, 0x00), /* 0x2b: */ + ISPOPMAP(0x00, 0x00), /* 0x2c: */ + ISPOPMAP(0x00, 0x00), /* 0x2d: */ + ISPOPMAP(0x00, 0x00), /* 0x2e: */ + ISPOPMAP(0x00, 0x00), /* 0x2f: */ + ISPOPMAP(0x03, 0x03), /* 0x30: MBOX_SET_INIT_SCSI_ID */ + ISPOPMAP(0x07, 0x07), /* 0x31: MBOX_SET_SELECT_TIMEOUT */ + ISPOPMAP(0xc7, 0xc7), /* 0x32: MBOX_SET_RETRY_COUNT */ + ISPOPMAP(0x07, 0x07), /* 0x33: MBOX_SET_TAG_AGE_LIMIT */ + ISPOPMAP(0x03, 0x03), /* 0x34: MBOX_SET_CLOCK_RATE */ + ISPOPMAP(0x07, 0x07), /* 0x35: MBOX_SET_ACT_NEG_STATE */ + ISPOPMAP(0x07, 0x07), /* 0x36: MBOX_SET_ASYNC_DATA_SETUP_TIME */ + ISPOPMAP(0x07, 0x07), /* 0x37: MBOX_SET_PCI_CONTROL_PARAMS */ + ISPOPMAP(0x4f, 0x4f), /* 0x38: MBOX_SET_TARGET_PARAMS */ + ISPOPMAP(0x0f, 0x0f), /* 0x39: MBOX_SET_DEV_QUEUE_PARAMS */ + ISPOPMAP(0x07, 0x07), /* 0x3a: MBOX_SET_RESET_DELAY_PARAMS */ + ISPOPMAP(0x00, 0x00), /* 0x3b: */ + ISPOPMAP(0x00, 0x00), /* 0x3c: */ + ISPOPMAP(0x00, 0x00), /* 0x3d: */ + ISPOPMAP(0x00, 0x00), /* 0x3e: */ + ISPOPMAP(0x00, 0x00), /* 0x3f: */ + ISPOPMAP(0x01, 0x03), /* 0x40: MBOX_RETURN_BIOS_BLOCK_ADDR */ + ISPOPMAP(0x3f, 0x01), /* 0x41: MBOX_WRITE_FOUR_RAM_WORDS */ + ISPOPMAP(0x03, 0x07), /* 0x42: MBOX_EXEC_BIOS_IOCB */ + ISPOPMAP(0x00, 0x00), /* 0x43: */ + ISPOPMAP(0x00, 0x00), /* 0x44: */ + ISPOPMAP(0x03, 0x03), /* 0x45: SET SYSTEM PARAMETER */ + ISPOPMAP(0x01, 0x03), /* 0x46: GET SYSTEM PARAMETER */ + ISPOPMAP(0x00, 0x00), /* 0x47: */ + ISPOPMAP(0x01, 0xcf), /* 0x48: GET SCAM CONFIGURATION */ + ISPOPMAP(0xcf, 0xcf), /* 0x49: SET SCAM CONFIGURATION */ + ISPOPMAP(0x03, 0x03), /* 0x4a: MBOX_SET_FIRMWARE_FEATURES */ + ISPOPMAP(0x01, 0x03), /* 0x4b: MBOX_GET_FIRMWARE_FEATURES */ + ISPOPMAP(0x00, 0x00), /* 0x4c: */ + ISPOPMAP(0x00, 0x00), /* 0x4d: */ + ISPOPMAP(0x00, 0x00), /* 0x4e: */ + ISPOPMAP(0x00, 0x00), /* 0x4f: */ + ISPOPMAP(0xdf, 0xdf), /* 0x50: LOAD RAM A64 */ + ISPOPMAP(0xdf, 0xdf), /* 0x51: DUMP RAM A64 */ + ISPOPMAP(0xdf, 0xff), /* 0x52: INITIALIZE REQUEST QUEUE A64 */ + ISPOPMAP(0xef, 0xff), /* 0x53: INITIALIZE RESPONSE QUEUE A64 */ + ISPOPMAP(0xcf, 0x01), /* 0x54: EXECUTE IOCB A64 */ + ISPOPMAP(0x07, 0x01), /* 0x55: ENABLE TARGET MODE */ + ISPOPMAP(0x03, 0x0f), /* 0x56: GET TARGET STATUS */ + ISPOPMAP(0x00, 0x00), /* 0x57: */ + ISPOPMAP(0x00, 0x00), /* 0x58: */ + ISPOPMAP(0x00, 0x00), /* 0x59: */ + ISPOPMAP(0x03, 0x03), /* 0x5a: SET DATA OVERRUN RECOVERY MODE */ + ISPOPMAP(0x01, 0x03), /* 0x5b: GET DATA OVERRUN RECOVERY MODE */ + ISPOPMAP(0x0f, 0x0f), /* 0x5c: SET HOST DATA */ + ISPOPMAP(0x01, 0x01) /* 0x5d: GET NOST DATA */ +}; + +#ifndef ISP_STRIPPED +static char *scsi_mbcmd_names[] = { + "NO-OP", + "LOAD RAM", + "EXEC FIRMWARE", + "DUMP RAM", + "WRITE RAM WORD", + "READ RAM WORD", + "MAILBOX REG TEST", + "VERIFY CHECKSUM", + "ABOUT FIRMWARE", + NULL, + NULL, + NULL, + NULL, + NULL, + "CHECK FIRMWARE", + NULL, + "INIT REQUEST QUEUE", + "INIT RESULT QUEUE", + "EXECUTE IOCB", + "WAKE UP", + "STOP FIRMWARE", + "ABORT", + "ABORT DEVICE", + "ABORT TARGET", + "BUS RESET", + "STOP QUEUE", + "START QUEUE", + "SINGLE STEP QUEUE", + "ABORT QUEUE", + "GET DEV QUEUE STATUS", + NULL, + "GET FIRMWARE STATUS", + "GET INIT SCSI ID", + "GET SELECT TIMEOUT", + "GET RETRY COUNT", + "GET TAG AGE LIMIT", + "GET CLOCK RATE", + "GET ACT NEG STATE", + "GET ASYNC DATA SETUP TIME", + "GET PCI PARAMS", + "GET TARGET PARAMS", + "GET DEV QUEUE PARAMS", + "GET RESET DELAY PARAMS", + NULL, + NULL, + NULL, + NULL, + NULL, + "SET INIT SCSI ID", + "SET SELECT TIMEOUT", + "SET RETRY COUNT", + "SET TAG AGE LIMIT", + "SET CLOCK RATE", + "SET ACT NEG STATE", + "SET ASYNC DATA SETUP TIME", + "SET PCI CONTROL PARAMS", + "SET TARGET PARAMS", + "SET DEV QUEUE PARAMS", + "SET RESET DELAY PARAMS", + NULL, + NULL, + NULL, + NULL, + NULL, + "RETURN BIOS BLOCK ADDR", + "WRITE FOUR RAM WORDS", + "EXEC BIOS IOCB", + NULL, + NULL, + "SET SYSTEM PARAMETER", + "GET SYSTEM PARAMETER", + NULL, + "GET SCAM CONFIGURATION", + "SET SCAM CONFIGURATION", + "SET FIRMWARE FEATURES", + "GET FIRMWARE FEATURES", + NULL, + NULL, + NULL, + NULL, + "LOAD RAM A64", + "DUMP RAM A64", + "INITIALIZE REQUEST QUEUE A64", + "INITIALIZE RESPONSE QUEUE A64", + "EXECUTE IOCB A64", + "ENABLE TARGET MODE", + "GET TARGET MODE STATE", + NULL, + NULL, + NULL, + "SET DATA OVERRUN RECOVERY MODE", + "GET DATA OVERRUN RECOVERY MODE", + "SET HOST DATA", + "GET NOST DATA", +}; +#endif + +static const uint32_t mbpfc[] = { + ISPOPMAP(0x01, 0x01), /* 0x00: MBOX_NO_OP */ + ISPOPMAP(0x1f, 0x01), /* 0x01: MBOX_LOAD_RAM */ + ISPOPMAP(0x07, 0x01), /* 0x02: MBOX_EXEC_FIRMWARE */ + ISPOPMAP(0xdf, 0x01), /* 0x03: MBOX_DUMP_RAM */ + ISPOPMAP(0x07, 0x07), /* 0x04: MBOX_WRITE_RAM_WORD */ + ISPOPMAP(0x03, 0x07), /* 0x05: MBOX_READ_RAM_WORD */ + ISPOPMAP(0xff, 0xff), /* 0x06: MBOX_MAILBOX_REG_TEST */ + ISPOPMAP(0x03, 0x05), /* 0x07: MBOX_VERIFY_CHECKSUM */ + ISPOPMAP(0x01, 0x4f), /* 0x08: MBOX_ABOUT_FIRMWARE */ + ISPOPMAP(0xdf, 0x01), /* 0x09: LOAD RAM */ + ISPOPMAP(0xdf, 0x01), /* 0x0a: DUMP RAM */ + ISPOPMAP(0x00, 0x00), /* 0x0b: */ + ISPOPMAP(0x00, 0x00), /* 0x0c: */ + ISPOPMAP(0x10f, 0x01), /* 0x0d: MBOX_WRITE_RAM_WORD_EXTENDED) */ + ISPOPMAP(0x01, 0x05), /* 0x0e: MBOX_CHECK_FIRMWARE */ + ISPOPMAP(0x10f, 0x05), /* 0x0f: MBOX_READ_RAM_WORD_EXTENDED */ + ISPOPMAP(0x1f, 0x11), /* 0x10: MBOX_INIT_REQ_QUEUE */ + ISPOPMAP(0x2f, 0x21), /* 0x11: MBOX_INIT_RES_QUEUE */ + ISPOPMAP(0x0f, 0x01), /* 0x12: MBOX_EXECUTE_IOCB */ + ISPOPMAP(0x03, 0x03), /* 0x13: MBOX_WAKE_UP */ + ISPOPMAP(0x01, 0xff), /* 0x14: MBOX_STOP_FIRMWARE */ + ISPOPMAP(0x4f, 0x01), /* 0x15: MBOX_ABORT */ + ISPOPMAP(0x07, 0x01), /* 0x16: MBOX_ABORT_DEVICE */ + ISPOPMAP(0x07, 0x01), /* 0x17: MBOX_ABORT_TARGET */ + ISPOPMAP(0x03, 0x03), /* 0x18: MBOX_BUS_RESET */ + ISPOPMAP(0x07, 0x05), /* 0x19: MBOX_STOP_QUEUE */ + ISPOPMAP(0x07, 0x05), /* 0x1a: MBOX_START_QUEUE */ + ISPOPMAP(0x07, 0x05), /* 0x1b: MBOX_SINGLE_STEP_QUEUE */ + ISPOPMAP(0x07, 0x05), /* 0x1c: MBOX_ABORT_QUEUE */ + ISPOPMAP(0x07, 0x03), /* 0x1d: MBOX_GET_DEV_QUEUE_STATUS */ + ISPOPMAP(0x00, 0x00), /* 0x1e: */ + ISPOPMAP(0x01, 0x07), /* 0x1f: MBOX_GET_FIRMWARE_STATUS */ + ISPOPMAP(0x01, 0x4f), /* 0x20: MBOX_GET_LOOP_ID */ + ISPOPMAP(0x00, 0x00), /* 0x21: */ + ISPOPMAP(0x01, 0x07), /* 0x22: MBOX_GET_RETRY_COUNT */ + ISPOPMAP(0x00, 0x00), /* 0x23: */ + ISPOPMAP(0x00, 0x00), /* 0x24: */ + ISPOPMAP(0x00, 0x00), /* 0x25: */ + ISPOPMAP(0x00, 0x00), /* 0x26: */ + ISPOPMAP(0x00, 0x00), /* 0x27: */ + ISPOPMAP(0x01, 0x03), /* 0x28: MBOX_GET_FIRMWARE_OPTIONS */ + ISPOPMAP(0x03, 0x07), /* 0x29: MBOX_GET_PORT_QUEUE_PARAMS */ + ISPOPMAP(0x00, 0x00), /* 0x2a: */ + ISPOPMAP(0x00, 0x00), /* 0x2b: */ + ISPOPMAP(0x00, 0x00), /* 0x2c: */ + ISPOPMAP(0x00, 0x00), /* 0x2d: */ + ISPOPMAP(0x00, 0x00), /* 0x2e: */ + ISPOPMAP(0x00, 0x00), /* 0x2f: */ + ISPOPMAP(0x00, 0x00), /* 0x30: */ + ISPOPMAP(0x00, 0x00), /* 0x31: */ + ISPOPMAP(0x07, 0x07), /* 0x32: MBOX_SET_RETRY_COUNT */ + ISPOPMAP(0x00, 0x00), /* 0x33: */ + ISPOPMAP(0x00, 0x00), /* 0x34: */ + ISPOPMAP(0x00, 0x00), /* 0x35: */ + ISPOPMAP(0x00, 0x00), /* 0x36: */ + ISPOPMAP(0x00, 0x00), /* 0x37: */ + ISPOPMAP(0x0f, 0x01), /* 0x38: MBOX_SET_FIRMWARE_OPTIONS */ + ISPOPMAP(0x0f, 0x07), /* 0x39: MBOX_SET_PORT_QUEUE_PARAMS */ + ISPOPMAP(0x00, 0x00), /* 0x3a: */ + ISPOPMAP(0x00, 0x00), /* 0x3b: */ + ISPOPMAP(0x00, 0x00), /* 0x3c: */ + ISPOPMAP(0x00, 0x00), /* 0x3d: */ + ISPOPMAP(0x00, 0x00), /* 0x3e: */ + ISPOPMAP(0x00, 0x00), /* 0x3f: */ + ISPOPMAP(0x03, 0x01), /* 0x40: MBOX_LOOP_PORT_BYPASS */ + ISPOPMAP(0x03, 0x01), /* 0x41: MBOX_LOOP_PORT_ENABLE */ + ISPOPMAP(0x03, 0x07), /* 0x42: MBOX_GET_RESOURCE_COUNTS */ + ISPOPMAP(0x01, 0x01), /* 0x43: MBOX_REQUEST_NON_PARTICIPATING_MODE */ + ISPOPMAP(0x00, 0x00), /* 0x44: */ + ISPOPMAP(0x00, 0x00), /* 0x45: */ + ISPOPMAP(0x00, 0x00), /* 0x46: */ + ISPOPMAP(0xcf, 0x03), /* 0x47: GET PORT_DATABASE ENHANCED */ + ISPOPMAP(0x00, 0x00), /* 0x48: */ + ISPOPMAP(0x00, 0x00), /* 0x49: */ + ISPOPMAP(0x00, 0x00), /* 0x4a: */ + ISPOPMAP(0x00, 0x00), /* 0x4b: */ + ISPOPMAP(0x00, 0x00), /* 0x4c: */ + ISPOPMAP(0x00, 0x00), /* 0x4d: */ + ISPOPMAP(0x00, 0x00), /* 0x4e: */ + ISPOPMAP(0x00, 0x00), /* 0x4f: */ + ISPOPMAP(0x00, 0x00), /* 0x50: */ + ISPOPMAP(0x00, 0x00), /* 0x51: */ + ISPOPMAP(0x00, 0x00), /* 0x52: */ + ISPOPMAP(0x00, 0x00), /* 0x53: */ + ISPOPMAP(0xcf, 0x01), /* 0x54: EXECUTE IOCB A64 */ + ISPOPMAP(0x00, 0x00), /* 0x55: */ + ISPOPMAP(0x00, 0x00), /* 0x56: */ + ISPOPMAP(0x00, 0x00), /* 0x57: */ + ISPOPMAP(0x00, 0x00), /* 0x58: */ + ISPOPMAP(0x00, 0x00), /* 0x59: */ + ISPOPMAP(0x00, 0x00), /* 0x5a: */ + ISPOPMAP(0x03, 0x01), /* 0x5b: MBOX_DRIVER_HEARTBEAT */ + ISPOPMAP(0xcf, 0x01), /* 0x5c: MBOX_FW_HEARTBEAT */ + ISPOPMAP(0x07, 0x03), /* 0x5d: MBOX_GET_SET_DATA_RATE */ + ISPOPMAP(0x00, 0x00), /* 0x5e: */ + ISPOPMAP(0x00, 0x00), /* 0x5f: */ + ISPOPMAP(0xcd, 0x31), /* 0x60: MBOX_INIT_FIRMWARE */ + ISPOPMAP(0x00, 0x00), /* 0x61: */ + ISPOPMAP(0x01, 0x01), /* 0x62: MBOX_INIT_LIP */ + ISPOPMAP(0xcd, 0x03), /* 0x63: MBOX_GET_FC_AL_POSITION_MAP */ + ISPOPMAP(0xcf, 0x01), /* 0x64: MBOX_GET_PORT_DB */ + ISPOPMAP(0x07, 0x01), /* 0x65: MBOX_CLEAR_ACA */ + ISPOPMAP(0x07, 0x01), /* 0x66: MBOX_TARGET_RESET */ + ISPOPMAP(0x07, 0x01), /* 0x67: MBOX_CLEAR_TASK_SET */ + ISPOPMAP(0x07, 0x01), /* 0x68: MBOX_ABORT_TASK_SET */ + ISPOPMAP(0x01, 0x07), /* 0x69: MBOX_GET_FW_STATE */ + ISPOPMAP(0x03, 0xcf), /* 0x6a: MBOX_GET_PORT_NAME */ + ISPOPMAP(0xcf, 0x01), /* 0x6b: MBOX_GET_LINK_STATUS */ + ISPOPMAP(0x0f, 0x01), /* 0x6c: MBOX_INIT_LIP_RESET */ + ISPOPMAP(0x00, 0x00), /* 0x6d: */ + ISPOPMAP(0xcf, 0x03), /* 0x6e: MBOX_SEND_SNS */ + ISPOPMAP(0x0f, 0x07), /* 0x6f: MBOX_FABRIC_LOGIN */ + ISPOPMAP(0x03, 0x01), /* 0x70: MBOX_SEND_CHANGE_REQUEST */ + ISPOPMAP(0x03, 0x03), /* 0x71: MBOX_FABRIC_LOGOUT */ + ISPOPMAP(0x0f, 0x0f), /* 0x72: MBOX_INIT_LIP_LOGIN */ + ISPOPMAP(0x00, 0x00), /* 0x73: */ + ISPOPMAP(0x07, 0x01), /* 0x74: LOGIN LOOP PORT */ + ISPOPMAP(0xcf, 0x03), /* 0x75: GET PORT/NODE NAME LIST */ + ISPOPMAP(0x4f, 0x01), /* 0x76: SET VENDOR ID */ + ISPOPMAP(0xcd, 0x01), /* 0x77: INITIALIZE IP MAILBOX */ + ISPOPMAP(0x00, 0x00), /* 0x78: */ + ISPOPMAP(0x00, 0x00), /* 0x79: */ + ISPOPMAP(0x00, 0x00), /* 0x7a: */ + ISPOPMAP(0x00, 0x00), /* 0x7b: */ + ISPOPMAP(0x4f, 0x03), /* 0x7c: Get ID List */ + ISPOPMAP(0xcf, 0x01), /* 0x7d: SEND LFA */ + ISPOPMAP(0x0f, 0x01) /* 0x7e: LUN RESET */ +}; +/* + * Footnotes + * + * (1): this sets bits 21..16 in mailbox register #8, which we nominally + * do not access at this time in the core driver. The caller is + * responsible for setting this register first (Gross!). The assumption + * is that we won't overflow. + */ + +#ifndef ISP_STRIPPED +static char *fc_mbcmd_names[] = { + "NO-OP", + "LOAD RAM", + "EXEC FIRMWARE", + "DUMP RAM", + "WRITE RAM WORD", + "READ RAM WORD", + "MAILBOX REG TEST", + "VERIFY CHECKSUM", + "ABOUT FIRMWARE", + "LOAD RAM", + "DUMP RAM", + "WRITE RAM WORD EXTENDED", + NULL, + "READ RAM WORD EXTENDED", + "CHECK FIRMWARE", + NULL, + "INIT REQUEST QUEUE", + "INIT RESULT QUEUE", + "EXECUTE IOCB", + "WAKE UP", + "STOP FIRMWARE", + "ABORT", + "ABORT DEVICE", + "ABORT TARGET", + "BUS RESET", + "STOP QUEUE", + "START QUEUE", + "SINGLE STEP QUEUE", + "ABORT QUEUE", + "GET DEV QUEUE STATUS", + NULL, + "GET FIRMWARE STATUS", + "GET LOOP ID", + NULL, + "GET RETRY COUNT", + NULL, + NULL, + NULL, + NULL, + NULL, + "GET FIRMWARE OPTIONS", + "GET PORT QUEUE PARAMS", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "SET RETRY COUNT", + NULL, + NULL, + NULL, + NULL, + NULL, + "SET FIRMWARE OPTIONS", + "SET PORT QUEUE PARAMS", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "LOOP PORT BYPASS", + "LOOP PORT ENABLE", + "GET RESOURCE COUNTS", + "REQUEST NON PARTICIPATING MODE", + NULL, + NULL, + NULL, + "GET PORT DATABASE ENHANCED", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "EXECUTE IOCB A64", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "DRIVER HEARTBEAT", + NULL, + "GET/SET DATA RATE", + NULL, + NULL, + "INIT FIRMWARE", + NULL, + "INIT LIP", + "GET FC-AL POSITION MAP", + "GET PORT DATABASE", + "CLEAR ACA", + "TARGET RESET", + "CLEAR TASK SET", + "ABORT TASK SET", + "GET FW STATE", + "GET PORT NAME", + "GET LINK STATUS", + "INIT LIP RESET", + NULL, + "SEND SNS", + "FABRIC LOGIN", + "SEND CHANGE REQUEST", + "FABRIC LOGOUT", + "INIT LIP LOGIN", + NULL, + "LOGIN LOOP PORT", + "GET PORT/NODE NAME LIST", + "SET VENDOR ID", + "INITIALIZE IP MAILBOX", + NULL, + NULL, + NULL, + NULL, + "Get ID List", + "SEND LFA", + "Lun RESET" +}; +#endif + +static void +isp_mboxcmd_qnw(ispsoftc_t *isp, mbreg_t *mbp, int nodelay) +{ + unsigned int ibits, obits, box, opcode; + const uint32_t *mcp; + + if (IS_FC(isp)) { + mcp = mbpfc; + } else { + mcp = mbpscsi; + } + opcode = mbp->param[0]; + ibits = HIWRD(mcp[opcode]) & NMBOX_BMASK(isp); + obits = LOWRD(mcp[opcode]) & NMBOX_BMASK(isp); + ibits |= mbp->ibits; + obits |= mbp->obits; + for (box = 0; box < MAX_MAILBOX(isp); box++) { + if (ibits & (1 << box)) { + ISP_WRITE(isp, MBOX_OFF(box), mbp->param[box]); + } + if (nodelay == 0) { + isp->isp_mboxtmp[box] = mbp->param[box] = 0; + } + } + if (nodelay == 0) { + isp->isp_lastmbxcmd = opcode; + isp->isp_obits = obits; + isp->isp_mboxbsy = 1; + } + ISP_WRITE(isp, HCCR, HCCR_CMD_SET_HOST_INT); + /* + * Oddly enough, if we're not delaying for an answer, + * delay a bit to give the f/w a chance to pick up the + * command. + */ + if (nodelay) { + USEC_DELAY(100); + } +} + +static void +isp_mboxcmd(ispsoftc_t *isp, mbreg_t *mbp, int logmask) +{ + char *cname, *xname, tname[16], mname[16]; + unsigned int lim, ibits, obits, box, opcode; + const uint32_t *mcp; + + if (IS_FC(isp)) { + mcp = mbpfc; + lim = (sizeof (mbpfc) / sizeof (mbpfc[0])); + } else { + mcp = mbpscsi; + lim = (sizeof (mbpscsi) / sizeof (mbpscsi[0])); + } + + if ((opcode = mbp->param[0]) >= lim) { + mbp->param[0] = MBOX_INVALID_COMMAND; + isp_prt(isp, ISP_LOGERR, "Unknown Command 0x%x", opcode); + return; + } + + ibits = HIWRD(mcp[opcode]) & NMBOX_BMASK(isp); + obits = LOWRD(mcp[opcode]) & NMBOX_BMASK(isp); + + /* + * Pick up any additional bits that the caller might have set. + */ + ibits |= mbp->ibits; + obits |= mbp->obits; + + if (ibits == 0 && obits == 0) { + mbp->param[0] = MBOX_COMMAND_PARAM_ERROR; + isp_prt(isp, ISP_LOGERR, "no parameters for 0x%x", opcode); + return; + } + + /* + * Get exclusive usage of mailbox registers. + */ + MBOX_ACQUIRE(isp); + + for (box = 0; box < MAX_MAILBOX(isp); box++) { + if (ibits & (1 << box)) { + isp_prt(isp, ISP_LOGDEBUG1, "IN mbox %d = 0x%x", box, + mbp->param[box]); + ISP_WRITE(isp, MBOX_OFF(box), mbp->param[box]); + } + isp->isp_mboxtmp[box] = mbp->param[box] = 0; + } + + isp->isp_lastmbxcmd = opcode; + + /* + * We assume that we can't overwrite a previous command. + */ + isp->isp_obits = obits; + isp->isp_mboxbsy = 1; + + /* + * Set Host Interrupt condition so that RISC will pick up mailbox regs. + */ + ISP_WRITE(isp, HCCR, HCCR_CMD_SET_HOST_INT); + + /* + * While we haven't finished the command, spin our wheels here. + */ + MBOX_WAIT_COMPLETE(isp); + + /* + * Did the command time out? + */ + if (isp->isp_mboxbsy) { + isp->isp_mboxbsy = 0; + MBOX_RELEASE(isp); + return; + } + + /* + * Copy back output registers. + */ + for (box = 0; box < MAX_MAILBOX(isp); box++) { + if (obits & (1 << box)) { + mbp->param[box] = isp->isp_mboxtmp[box]; + isp_prt(isp, ISP_LOGDEBUG1, "OUT mbox %d = 0x%x", box, + mbp->param[box]); + } + } + + MBOX_RELEASE(isp); + + if (logmask == 0 || opcode == MBOX_EXEC_FIRMWARE) { + return; + } +#ifdef ISP_STRIPPED + cname = NULL; +#else + cname = (IS_FC(isp))? fc_mbcmd_names[opcode] : scsi_mbcmd_names[opcode]; +#endif + if (cname == NULL) { + cname = tname; + SNPRINTF(tname, sizeof tname, "opcode %x", opcode); + } + + /* + * Just to be chatty here... + */ + xname = NULL; + switch (mbp->param[0]) { + case MBOX_COMMAND_COMPLETE: + break; + case MBOX_INVALID_COMMAND: + if (logmask & MBLOGMASK(MBOX_COMMAND_COMPLETE)) { + xname = "INVALID COMMAND"; + } + break; + case MBOX_HOST_INTERFACE_ERROR: + if (logmask & MBLOGMASK(MBOX_HOST_INTERFACE_ERROR)) { + xname = "HOST INTERFACE ERROR"; + } + break; + case MBOX_TEST_FAILED: + if (logmask & MBLOGMASK(MBOX_TEST_FAILED)) { + xname = "TEST FAILED"; + } + break; + case MBOX_COMMAND_ERROR: + if (logmask & MBLOGMASK(MBOX_COMMAND_ERROR)) { + xname = "COMMAND ERROR"; + } + break; + case MBOX_COMMAND_PARAM_ERROR: + if (logmask & MBLOGMASK(MBOX_COMMAND_PARAM_ERROR)) { + xname = "COMMAND PARAMETER ERROR"; + } + break; + case MBOX_LOOP_ID_USED: + if (logmask & MBLOGMASK(MBOX_LOOP_ID_USED)) { + xname = "LOOP ID ALREADY IN USE"; + } + break; + case MBOX_PORT_ID_USED: + if (logmask & MBLOGMASK(MBOX_PORT_ID_USED)) { + xname = "PORT ID ALREADY IN USE"; + } + break; + case MBOX_ALL_IDS_USED: + if (logmask & MBLOGMASK(MBOX_ALL_IDS_USED)) { + xname = "ALL LOOP IDS IN USE"; + } + break; + case 0: /* special case */ + xname = "TIMEOUT"; + break; + default: + SNPRINTF(mname, sizeof mname, "error 0x%x", mbp->param[0]); + xname = mname; + break; + } + if (xname) { + isp_prt(isp, ISP_LOGALL, "Mailbox Command '%s' failed (%s)", + cname, xname); + } +} + +static void +isp_fw_state(ispsoftc_t *isp) +{ + if (IS_FC(isp)) { + mbreg_t mbs; + fcparam *fcp = isp->isp_param; + + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_GET_FW_STATE; + isp_mboxcmd(isp, &mbs, MBLOGALL); + if (mbs.param[0] == MBOX_COMMAND_COMPLETE) { + fcp->isp_fwstate = mbs.param[1]; + } + } +} + +static void +isp_update(ispsoftc_t *isp) +{ + int bus, upmask; + + for (bus = 0, upmask = isp->isp_update; upmask != 0; bus++) { + if (upmask & (1 << bus)) { + isp_update_bus(isp, bus); + } + upmask &= ~(1 << bus); + } +} + +static void +isp_update_bus(ispsoftc_t *isp, int bus) +{ + int tgt; + mbreg_t mbs; + sdparam *sdp; + + isp->isp_update &= ~(1 << bus); + if (IS_FC(isp)) { + /* + * There are no 'per-bus' settings for Fibre Channel. + */ + return; + } + sdp = isp->isp_param; + sdp += bus; + + for (tgt = 0; tgt < MAX_TARGETS; tgt++) { + uint16_t flags, period, offset; + int get; + + if (sdp->isp_devparam[tgt].dev_enable == 0) { + sdp->isp_devparam[tgt].dev_update = 0; + sdp->isp_devparam[tgt].dev_refresh = 0; + isp_prt(isp, ISP_LOGDEBUG0, + "skipping target %d bus %d update", tgt, bus); + continue; + } + /* + * If the goal is to update the status of the device, + * take what's in goal_flags and try and set the device + * toward that. Otherwise, if we're just refreshing the + * current device state, get the current parameters. + */ + + MEMZERO(&mbs, sizeof (mbs)); + + /* + * Refresh overrides set + */ + if (sdp->isp_devparam[tgt].dev_refresh) { + mbs.param[0] = MBOX_GET_TARGET_PARAMS; + sdp->isp_devparam[tgt].dev_refresh = 0; + get = 1; + } else if (sdp->isp_devparam[tgt].dev_update) { + mbs.param[0] = MBOX_SET_TARGET_PARAMS; + /* + * Make sure goal_flags has "Renegotiate on Error" + * on and "Freeze Queue on Error" off. + */ + sdp->isp_devparam[tgt].goal_flags |= DPARM_RENEG; + sdp->isp_devparam[tgt].goal_flags &= ~DPARM_QFRZ; + + mbs.param[2] = sdp->isp_devparam[tgt].goal_flags; + + /* + * Insist that PARITY must be enabled + * if SYNC or WIDE is enabled. + */ + if ((mbs.param[2] & (DPARM_SYNC|DPARM_WIDE)) != 0) { + mbs.param[2] |= DPARM_PARITY; + } + + if (mbs.param[2] & DPARM_SYNC) { + mbs.param[3] = + (sdp->isp_devparam[tgt].goal_offset << 8) | + (sdp->isp_devparam[tgt].goal_period); + } + /* + * A command completion later that has + * RQSTF_NEGOTIATION set can cause + * the dev_refresh/announce cycle also. + * + * Note: It is really important to update our current + * flags with at least the state of TAG capabilities- + * otherwise we might try and send a tagged command + * when we have it all turned off. So change it here + * to say that current already matches goal. + */ + sdp->isp_devparam[tgt].actv_flags &= ~DPARM_TQING; + sdp->isp_devparam[tgt].actv_flags |= + (sdp->isp_devparam[tgt].goal_flags & DPARM_TQING); + isp_prt(isp, ISP_LOGDEBUG0, + "bus %d set tgt %d flags 0x%x off 0x%x period 0x%x", + bus, tgt, mbs.param[2], mbs.param[3] >> 8, + mbs.param[3] & 0xff); + sdp->isp_devparam[tgt].dev_update = 0; + sdp->isp_devparam[tgt].dev_refresh = 1; + get = 0; + } else { + continue; + } + mbs.param[1] = (bus << 15) | (tgt << 8); + isp_mboxcmd(isp, &mbs, MBLOGALL); + if (get == 0) { + isp->isp_sendmarker |= (1 << bus); + continue; + } + flags = mbs.param[2]; + period = mbs.param[3] & 0xff; + offset = mbs.param[3] >> 8; + sdp->isp_devparam[tgt].actv_flags = flags; + sdp->isp_devparam[tgt].actv_period = period; + sdp->isp_devparam[tgt].actv_offset = offset; + get = (bus << 16) | tgt; + (void) isp_async(isp, ISPASYNC_NEW_TGT_PARAMS, &get); + } + + for (tgt = 0; tgt < MAX_TARGETS; tgt++) { + if (sdp->isp_devparam[tgt].dev_update || + sdp->isp_devparam[tgt].dev_refresh) { + isp->isp_update |= (1 << bus); + break; + } + } +} + +#ifndef DEFAULT_FRAMESIZE +#define DEFAULT_FRAMESIZE(isp) ICB_DFLT_FRMLEN +#endif +#ifndef DEFAULT_EXEC_THROTTLE +#define DEFAULT_EXEC_THROTTLE(isp) ISP_EXEC_THROTTLE +#endif + +static void +isp_setdfltparm(ispsoftc_t *isp, int channel) +{ + int tgt; + sdparam *sdp; + + if (IS_FC(isp)) { + fcparam *fcp = (fcparam *) isp->isp_param; + int nvfail; + + fcp += channel; + if (fcp->isp_gotdparms) { + return; + } + fcp->isp_gotdparms = 1; + fcp->isp_maxfrmlen = DEFAULT_FRAMESIZE(isp); + fcp->isp_maxalloc = ICB_DFLT_ALLOC; + fcp->isp_execthrottle = DEFAULT_EXEC_THROTTLE(isp); + fcp->isp_retry_delay = ICB_DFLT_RDELAY; + fcp->isp_retry_count = ICB_DFLT_RCOUNT; + /* Platform specific.... */ + fcp->isp_loopid = DEFAULT_LOOPID(isp); + fcp->isp_nodewwn = DEFAULT_NODEWWN(isp); + fcp->isp_portwwn = DEFAULT_PORTWWN(isp); + fcp->isp_fwoptions = 0; + fcp->isp_fwoptions |= ICBOPT_FAIRNESS; + fcp->isp_fwoptions |= ICBOPT_PDBCHANGE_AE; + fcp->isp_fwoptions |= ICBOPT_HARD_ADDRESS; + fcp->isp_fwoptions |= ICBOPT_FAST_POST; + if (isp->isp_confopts & ISP_CFG_FULL_DUPLEX) + fcp->isp_fwoptions |= ICBOPT_FULL_DUPLEX; + + /* + * Make sure this is turned off now until we get + * extended options from NVRAM + */ + fcp->isp_fwoptions &= ~ICBOPT_EXTENDED; + + /* + * Now try and read NVRAM unless told to not do so. + * This will set fcparam's isp_nodewwn && isp_portwwn. + */ + if ((isp->isp_confopts & ISP_CFG_NONVRAM) == 0) { + nvfail = isp_read_nvram(isp); + if (nvfail) { + isp->isp_confopts |= ISP_CFG_NONVRAM; + } + } else { + nvfail = 1; + } + /* + * Set node && port to override platform set defaults + * unless the nvram read failed (or none was done), + * or the platform code wants to use what had been + * set in the defaults. + */ + if (nvfail) { + isp->isp_confopts |= ISP_CFG_OWNWWPN|ISP_CFG_OWNWWNN; + } + if (isp->isp_confopts & ISP_CFG_OWNWWNN) { + isp_prt(isp, ISP_LOGCONFIG, "Using Node WWN 0x%08x%08x", + (uint32_t) (DEFAULT_NODEWWN(isp) >> 32), + (uint32_t) (DEFAULT_NODEWWN(isp) & 0xffffffff)); + ISP_NODEWWN(isp) = DEFAULT_NODEWWN(isp); + } else { + /* + * We always start out with values derived + * from NVRAM or our platform default. + */ + ISP_NODEWWN(isp) = fcp->isp_nodewwn; + if (fcp->isp_nodewwn == 0) { + isp_prt(isp, ISP_LOGCONFIG, + "bad WWNN- using default\n"); + ISP_NODEWWN(isp) = DEFAULT_NODEWWN(isp); + } + } + if (isp->isp_confopts & ISP_CFG_OWNWWPN) { + isp_prt(isp, ISP_LOGCONFIG, "Using Port WWN 0x%08x%08x", + (uint32_t) (DEFAULT_PORTWWN(isp) >> 32), + (uint32_t) (DEFAULT_PORTWWN(isp) & 0xffffffff)); + ISP_PORTWWN(isp) = DEFAULT_PORTWWN(isp); + } else { + /* + * We always start out with values derived + * from NVRAM or our platform default. + */ + ISP_PORTWWN(isp) = fcp->isp_portwwn; + if (fcp->isp_portwwn == 0) { + isp_prt(isp, ISP_LOGCONFIG, + "bad WWPN- using default\n"); + ISP_PORTWWN(isp) = DEFAULT_PORTWWN(isp); + } + } + return; + } + + sdp = (sdparam *) isp->isp_param; + sdp += channel; + + /* + * Been there, done that, got the T-shirt... + */ + if (sdp->isp_gotdparms) { + return; + } + sdp->isp_gotdparms = 1; + + /* + * Establish some default parameters. + */ + sdp->isp_cmd_dma_burst_enable = 0; + sdp->isp_data_dma_burst_enabl = 1; + sdp->isp_fifo_threshold = 0; + sdp->isp_initiator_id = DEFAULT_IID(isp); + if (isp->isp_type >= ISP_HA_SCSI_1040) { + sdp->isp_async_data_setup = 9; + } else { + sdp->isp_async_data_setup = 6; + } + sdp->isp_selection_timeout = 250; + sdp->isp_max_queue_depth = MAXISPREQUEST(isp); + sdp->isp_tag_aging = 8; + sdp->isp_bus_reset_delay = 5; + /* + * Don't retry selection, busy or queue full automatically- reflect + * these back to us. + */ + sdp->isp_retry_count = 0; + sdp->isp_retry_delay = 0; + + for (tgt = 0; tgt < MAX_TARGETS; tgt++) { + sdp->isp_devparam[tgt].exc_throttle = ISP_EXEC_THROTTLE; + sdp->isp_devparam[tgt].dev_enable = 1; + } + + /* + * If we've not been told to avoid reading NVRAM, try and read it. + * If we're successful reading it, we can then return because NVRAM + * will tell us what the desired settings are. Otherwise, we establish + * some reasonable 'fake' nvram and goal defaults. + */ + + if ((isp->isp_confopts & ISP_CFG_NONVRAM) == 0) { + if (isp_read_nvram(isp) == 0) { + return; + } + } + + /* + * Now try and see whether we have specific values for them. + */ + if ((isp->isp_confopts & ISP_CFG_NONVRAM) == 0) { + mbreg_t mbs; + + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_GET_ACT_NEG_STATE; + isp_mboxcmd(isp, &mbs, MBLOGNONE); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + sdp->isp_req_ack_active_neg = 1; + sdp->isp_data_line_active_neg = 1; + } else { + sdp->isp_req_ack_active_neg = + (mbs.param[1+channel] >> 4) & 0x1; + sdp->isp_data_line_active_neg = + (mbs.param[1+channel] >> 5) & 0x1; + } + } + + isp_prt(isp, ISP_LOGDEBUG0, sc0, sc3, + 0, sdp->isp_fifo_threshold, sdp->isp_initiator_id, + sdp->isp_bus_reset_delay, sdp->isp_retry_count, + sdp->isp_retry_delay, sdp->isp_async_data_setup); + isp_prt(isp, ISP_LOGDEBUG0, sc1, sc3, + sdp->isp_req_ack_active_neg, sdp->isp_data_line_active_neg, + sdp->isp_data_dma_burst_enabl, sdp->isp_cmd_dma_burst_enable, + sdp->isp_selection_timeout, sdp->isp_max_queue_depth); + + /* + * The trick here is to establish a default for the default (honk!) + * state (goal_flags). Then try and get the current status from + * the card to fill in the current state. We don't, in fact, set + * the default to the SAFE default state- that's not the goal state. + */ + for (tgt = 0; tgt < MAX_TARGETS; tgt++) { + uint8_t off, per; + sdp->isp_devparam[tgt].actv_offset = 0; + sdp->isp_devparam[tgt].actv_period = 0; + sdp->isp_devparam[tgt].actv_flags = 0; + + sdp->isp_devparam[tgt].goal_flags = + sdp->isp_devparam[tgt].nvrm_flags = DPARM_DEFAULT; + + /* + * We default to Wide/Fast for versions less than a 1040 + * (unless it's SBus). + */ + if (IS_ULTRA3(isp)) { + off = ISP_80M_SYNCPARMS >> 8; + per = ISP_80M_SYNCPARMS & 0xff; + } else if (IS_ULTRA2(isp)) { + off = ISP_40M_SYNCPARMS >> 8; + per = ISP_40M_SYNCPARMS & 0xff; + } else if (IS_1240(isp)) { + off = ISP_20M_SYNCPARMS >> 8; + per = ISP_20M_SYNCPARMS & 0xff; + } else if ((isp->isp_bustype == ISP_BT_SBUS && + isp->isp_type < ISP_HA_SCSI_1020A) || + (isp->isp_bustype == ISP_BT_PCI && + isp->isp_type < ISP_HA_SCSI_1040) || + (isp->isp_clock && isp->isp_clock < 60) || + (sdp->isp_ultramode == 0)) { + off = ISP_10M_SYNCPARMS >> 8; + per = ISP_10M_SYNCPARMS & 0xff; + } else { + off = ISP_20M_SYNCPARMS_1040 >> 8; + per = ISP_20M_SYNCPARMS_1040 & 0xff; + } + sdp->isp_devparam[tgt].goal_offset = + sdp->isp_devparam[tgt].nvrm_offset = off; + sdp->isp_devparam[tgt].goal_period = + sdp->isp_devparam[tgt].nvrm_period = per; + + isp_prt(isp, ISP_LOGDEBUG0, sc2, sc3, + channel, tgt, sdp->isp_devparam[tgt].nvrm_flags, + sdp->isp_devparam[tgt].nvrm_offset, + sdp->isp_devparam[tgt].nvrm_period); + } +} + +/* + * Re-initialize the ISP and complete all orphaned commands + * with a 'botched' notice. The reset/init routines should + * not disturb an already active list of commands. + * + * Locks held prior to coming here. + */ + +void +isp_reinit(ispsoftc_t *isp) +{ + XS_T *xs; + int i; + + if (IS_FC(isp)) { + isp_mark_getpdb_all(isp); + } + isp_reset(isp); + if (isp->isp_state != ISP_RESETSTATE) { + isp_prt(isp, ISP_LOGERR, "isp_reinit cannot reset card"); + } else if (isp->isp_role != ISP_ROLE_NONE) { + isp_init(isp); + if (isp->isp_state == ISP_INITSTATE) { + isp->isp_state = ISP_RUNSTATE; + } + if (isp->isp_state != ISP_RUNSTATE) { + isp_prt(isp, ISP_LOGERR, + "isp_reinit cannot restart card"); + } + } + isp->isp_nactive = 0; + + for (i = 0; i < isp->isp_maxcmds; i++) { + uint16_t handle; + xs = isp->isp_xflist[i]; + if (xs == NULL) { + continue; + } + handle = isp_index_handle(i); + isp_destroy_handle(isp, handle); + if (XS_XFRLEN(xs)) { + ISP_DMAFREE(isp, xs, handle); + XS_RESID(xs) = XS_XFRLEN(xs); + } else { + XS_RESID(xs) = 0; + } + XS_SETERR(xs, HBA_BUSRESET); + isp_done(xs); + } +} + +/* + * NVRAM Routines + */ +static int +isp_read_nvram(ispsoftc_t *isp) +{ + int i, amt, retval; + uint8_t csum, minversion; + union { + uint8_t _x[ISP2100_NVRAM_SIZE]; + uint16_t _s[ISP2100_NVRAM_SIZE>>1]; + } _n; +#define nvram_data _n._x +#define nvram_words _n._s + + if (IS_FC(isp)) { + amt = ISP2100_NVRAM_SIZE; + minversion = 1; + } else if (IS_ULTRA2(isp)) { + amt = ISP1080_NVRAM_SIZE; + minversion = 0; + } else { + amt = ISP_NVRAM_SIZE; + minversion = 2; + } + + /* + * Just read the first two words first to see if we have a valid + * NVRAM to continue reading the rest with. + */ + for (i = 0; i < 2; i++) { + isp_rdnvram_word(isp, i, &nvram_words[i]); + } + if (nvram_data[0] != 'I' || nvram_data[1] != 'S' || + nvram_data[2] != 'P') { + if (isp->isp_bustype != ISP_BT_SBUS) { + isp_prt(isp, ISP_LOGWARN, "invalid NVRAM header"); + isp_prt(isp, ISP_LOGDEBUG0, "%x %x %x", + nvram_data[0], nvram_data[1], nvram_data[2]); + } + retval = -1; + goto out; + } + for (i = 2; i < amt>>1; i++) { + isp_rdnvram_word(isp, i, &nvram_words[i]); + } + for (csum = 0, i = 0; i < amt; i++) { + csum += nvram_data[i]; + } + if (csum != 0) { + isp_prt(isp, ISP_LOGWARN, "invalid NVRAM checksum"); + retval = -1; + goto out; + } + if (ISP_NVRAM_VERSION(nvram_data) < minversion) { + isp_prt(isp, ISP_LOGWARN, "version %d NVRAM not understood", + ISP_NVRAM_VERSION(nvram_data)); + retval = -1; + goto out; + } + + if (IS_ULTRA3(isp)) { + isp_parse_nvram_12160(isp, 0, nvram_data); + if (IS_12160(isp)) + isp_parse_nvram_12160(isp, 1, nvram_data); + } else if (IS_1080(isp)) { + isp_parse_nvram_1080(isp, 0, nvram_data); + } else if (IS_1280(isp) || IS_1240(isp)) { + isp_parse_nvram_1080(isp, 0, nvram_data); + isp_parse_nvram_1080(isp, 1, nvram_data); + } else if (IS_SCSI(isp)) { + isp_parse_nvram_1020(isp, nvram_data); + } else { + isp_parse_nvram_2100(isp, nvram_data); + } + retval = 0; +out: + return (retval); +#undef nvram_data +#undef nvram_words +} + +static void +isp_rdnvram_word(ispsoftc_t *isp, int wo, uint16_t *rp) +{ + int i, cbits; + uint16_t bit, rqst, junk; + + ISP_WRITE(isp, BIU_NVRAM, BIU_NVRAM_SELECT); + USEC_DELAY(10); + ISP_WRITE(isp, BIU_NVRAM, BIU_NVRAM_SELECT|BIU_NVRAM_CLOCK); + USEC_DELAY(10); + + if (IS_FC(isp)) { + wo &= ((ISP2100_NVRAM_SIZE >> 1) - 1); + if (IS_2312(isp) && isp->isp_port) { + wo += 128; + } + rqst = (ISP_NVRAM_READ << 8) | wo; + cbits = 10; + } else if (IS_ULTRA2(isp)) { + wo &= ((ISP1080_NVRAM_SIZE >> 1) - 1); + rqst = (ISP_NVRAM_READ << 8) | wo; + cbits = 10; + } else { + wo &= ((ISP_NVRAM_SIZE >> 1) - 1); + rqst = (ISP_NVRAM_READ << 6) | wo; + cbits = 8; + } + + /* + * Clock the word select request out... + */ + for (i = cbits; i >= 0; i--) { + if ((rqst >> i) & 1) { + bit = BIU_NVRAM_SELECT | BIU_NVRAM_DATAOUT; + } else { + bit = BIU_NVRAM_SELECT; + } + ISP_WRITE(isp, BIU_NVRAM, bit); + USEC_DELAY(10); + junk = ISP_READ(isp, BIU_NVRAM); /* force PCI flush */ + ISP_WRITE(isp, BIU_NVRAM, bit | BIU_NVRAM_CLOCK); + USEC_DELAY(10); + junk = ISP_READ(isp, BIU_NVRAM); /* force PCI flush */ + ISP_WRITE(isp, BIU_NVRAM, bit); + USEC_DELAY(10); + junk = ISP_READ(isp, BIU_NVRAM); /* force PCI flush */ + } + /* + * Now read the result back in (bits come back in MSB format). + */ + *rp = 0; + for (i = 0; i < 16; i++) { + uint16_t rv; + *rp <<= 1; + ISP_WRITE(isp, BIU_NVRAM, BIU_NVRAM_SELECT|BIU_NVRAM_CLOCK); + USEC_DELAY(10); + rv = ISP_READ(isp, BIU_NVRAM); + if (rv & BIU_NVRAM_DATAIN) { + *rp |= 1; + } + USEC_DELAY(10); + ISP_WRITE(isp, BIU_NVRAM, BIU_NVRAM_SELECT); + USEC_DELAY(10); + junk = ISP_READ(isp, BIU_NVRAM); /* force PCI flush */ + } + ISP_WRITE(isp, BIU_NVRAM, 0); + USEC_DELAY(10); + junk = ISP_READ(isp, BIU_NVRAM); /* force PCI flush */ + ISP_SWIZZLE_NVRAM_WORD(isp, rp); +} + +static void +isp_parse_nvram_1020(ispsoftc_t *isp, uint8_t *nvram_data) +{ + sdparam *sdp = (sdparam *) isp->isp_param; + int tgt; + + sdp->isp_fifo_threshold = + ISP_NVRAM_FIFO_THRESHOLD(nvram_data) | + (ISP_NVRAM_FIFO_THRESHOLD_128(nvram_data) << 2); + + if ((isp->isp_confopts & ISP_CFG_OWNLOOPID) == 0) + sdp->isp_initiator_id = + ISP_NVRAM_INITIATOR_ID(nvram_data); + + sdp->isp_bus_reset_delay = + ISP_NVRAM_BUS_RESET_DELAY(nvram_data); + + sdp->isp_retry_count = + ISP_NVRAM_BUS_RETRY_COUNT(nvram_data); + + sdp->isp_retry_delay = + ISP_NVRAM_BUS_RETRY_DELAY(nvram_data); + + sdp->isp_async_data_setup = + ISP_NVRAM_ASYNC_DATA_SETUP_TIME(nvram_data); + + if (isp->isp_type >= ISP_HA_SCSI_1040) { + if (sdp->isp_async_data_setup < 9) { + sdp->isp_async_data_setup = 9; + } + } else { + if (sdp->isp_async_data_setup != 6) { + sdp->isp_async_data_setup = 6; + } + } + + sdp->isp_req_ack_active_neg = + ISP_NVRAM_REQ_ACK_ACTIVE_NEGATION(nvram_data); + + sdp->isp_data_line_active_neg = + ISP_NVRAM_DATA_LINE_ACTIVE_NEGATION(nvram_data); + + sdp->isp_data_dma_burst_enabl = + ISP_NVRAM_DATA_DMA_BURST_ENABLE(nvram_data); + + sdp->isp_cmd_dma_burst_enable = + ISP_NVRAM_CMD_DMA_BURST_ENABLE(nvram_data); + + sdp->isp_tag_aging = + ISP_NVRAM_TAG_AGE_LIMIT(nvram_data); + + sdp->isp_selection_timeout = + ISP_NVRAM_SELECTION_TIMEOUT(nvram_data); + + sdp->isp_max_queue_depth = + ISP_NVRAM_MAX_QUEUE_DEPTH(nvram_data); + + sdp->isp_fast_mttr = ISP_NVRAM_FAST_MTTR_ENABLE(nvram_data); + + isp_prt(isp, ISP_LOGDEBUG0, sc0, sc4, + 0, sdp->isp_fifo_threshold, sdp->isp_initiator_id, + sdp->isp_bus_reset_delay, sdp->isp_retry_count, + sdp->isp_retry_delay, sdp->isp_async_data_setup); + isp_prt(isp, ISP_LOGDEBUG0, sc1, sc4, + sdp->isp_req_ack_active_neg, sdp->isp_data_line_active_neg, + sdp->isp_data_dma_burst_enabl, sdp->isp_cmd_dma_burst_enable, + sdp->isp_selection_timeout, sdp->isp_max_queue_depth); + + for (tgt = 0; tgt < MAX_TARGETS; tgt++) { + sdp->isp_devparam[tgt].dev_enable = + ISP_NVRAM_TGT_DEVICE_ENABLE(nvram_data, tgt); + sdp->isp_devparam[tgt].exc_throttle = + ISP_NVRAM_TGT_EXEC_THROTTLE(nvram_data, tgt); + sdp->isp_devparam[tgt].nvrm_offset = + ISP_NVRAM_TGT_SYNC_OFFSET(nvram_data, tgt); + sdp->isp_devparam[tgt].nvrm_period = + ISP_NVRAM_TGT_SYNC_PERIOD(nvram_data, tgt); + /* + * We probably shouldn't lie about this, but it + * it makes it much safer if we limit NVRAM values + * to sanity. + */ + if (isp->isp_type < ISP_HA_SCSI_1040) { + /* + * If we're not ultra, we can't possibly + * be a shorter period than this. + */ + if (sdp->isp_devparam[tgt].nvrm_period < 0x19) { + sdp->isp_devparam[tgt].nvrm_period = 0x19; + } + if (sdp->isp_devparam[tgt].nvrm_offset > 0xc) { + sdp->isp_devparam[tgt].nvrm_offset = 0x0c; + } + } else { + if (sdp->isp_devparam[tgt].nvrm_offset > 0x8) { + sdp->isp_devparam[tgt].nvrm_offset = 0x8; + } + } + sdp->isp_devparam[tgt].nvrm_flags = 0; + if (ISP_NVRAM_TGT_RENEG(nvram_data, tgt)) + sdp->isp_devparam[tgt].nvrm_flags |= DPARM_RENEG; + sdp->isp_devparam[tgt].nvrm_flags |= DPARM_ARQ; + if (ISP_NVRAM_TGT_TQING(nvram_data, tgt)) + sdp->isp_devparam[tgt].nvrm_flags |= DPARM_TQING; + if (ISP_NVRAM_TGT_SYNC(nvram_data, tgt)) + sdp->isp_devparam[tgt].nvrm_flags |= DPARM_SYNC; + if (ISP_NVRAM_TGT_WIDE(nvram_data, tgt)) + sdp->isp_devparam[tgt].nvrm_flags |= DPARM_WIDE; + if (ISP_NVRAM_TGT_PARITY(nvram_data, tgt)) + sdp->isp_devparam[tgt].nvrm_flags |= DPARM_PARITY; + if (ISP_NVRAM_TGT_DISC(nvram_data, tgt)) + sdp->isp_devparam[tgt].nvrm_flags |= DPARM_DISC; + sdp->isp_devparam[tgt].actv_flags = 0; /* we don't know */ + isp_prt(isp, ISP_LOGDEBUG0, sc2, sc4, + 0, tgt, sdp->isp_devparam[tgt].nvrm_flags, + sdp->isp_devparam[tgt].nvrm_offset, + sdp->isp_devparam[tgt].nvrm_period); + sdp->isp_devparam[tgt].goal_offset = + sdp->isp_devparam[tgt].nvrm_offset; + sdp->isp_devparam[tgt].goal_period = + sdp->isp_devparam[tgt].nvrm_period; + sdp->isp_devparam[tgt].goal_flags = + sdp->isp_devparam[tgt].nvrm_flags; + } +} + +static void +isp_parse_nvram_1080(ispsoftc_t *isp, int bus, uint8_t *nvram_data) +{ + sdparam *sdp = (sdparam *) isp->isp_param; + int tgt; + + sdp += bus; + + sdp->isp_fifo_threshold = + ISP1080_NVRAM_FIFO_THRESHOLD(nvram_data); + + if ((isp->isp_confopts & ISP_CFG_OWNLOOPID) == 0) + sdp->isp_initiator_id = + ISP1080_NVRAM_INITIATOR_ID(nvram_data, bus); + + sdp->isp_bus_reset_delay = + ISP1080_NVRAM_BUS_RESET_DELAY(nvram_data, bus); + + sdp->isp_retry_count = + ISP1080_NVRAM_BUS_RETRY_COUNT(nvram_data, bus); + + sdp->isp_retry_delay = + ISP1080_NVRAM_BUS_RETRY_DELAY(nvram_data, bus); + + sdp->isp_async_data_setup = + ISP1080_NVRAM_ASYNC_DATA_SETUP_TIME(nvram_data, bus); + + sdp->isp_req_ack_active_neg = + ISP1080_NVRAM_REQ_ACK_ACTIVE_NEGATION(nvram_data, bus); + + sdp->isp_data_line_active_neg = + ISP1080_NVRAM_DATA_LINE_ACTIVE_NEGATION(nvram_data, bus); + + sdp->isp_data_dma_burst_enabl = + ISP1080_NVRAM_BURST_ENABLE(nvram_data); + + sdp->isp_cmd_dma_burst_enable = + ISP1080_NVRAM_BURST_ENABLE(nvram_data); + + sdp->isp_selection_timeout = + ISP1080_NVRAM_SELECTION_TIMEOUT(nvram_data, bus); + + sdp->isp_max_queue_depth = + ISP1080_NVRAM_MAX_QUEUE_DEPTH(nvram_data, bus); + + isp_prt(isp, ISP_LOGDEBUG0, sc0, sc4, + bus, sdp->isp_fifo_threshold, sdp->isp_initiator_id, + sdp->isp_bus_reset_delay, sdp->isp_retry_count, + sdp->isp_retry_delay, sdp->isp_async_data_setup); + isp_prt(isp, ISP_LOGDEBUG0, sc1, sc4, + sdp->isp_req_ack_active_neg, sdp->isp_data_line_active_neg, + sdp->isp_data_dma_burst_enabl, sdp->isp_cmd_dma_burst_enable, + sdp->isp_selection_timeout, sdp->isp_max_queue_depth); + + + for (tgt = 0; tgt < MAX_TARGETS; tgt++) { + sdp->isp_devparam[tgt].dev_enable = + ISP1080_NVRAM_TGT_DEVICE_ENABLE(nvram_data, tgt, bus); + sdp->isp_devparam[tgt].exc_throttle = + ISP1080_NVRAM_TGT_EXEC_THROTTLE(nvram_data, tgt, bus); + sdp->isp_devparam[tgt].nvrm_offset = + ISP1080_NVRAM_TGT_SYNC_OFFSET(nvram_data, tgt, bus); + sdp->isp_devparam[tgt].nvrm_period = + ISP1080_NVRAM_TGT_SYNC_PERIOD(nvram_data, tgt, bus); + sdp->isp_devparam[tgt].nvrm_flags = 0; + if (ISP1080_NVRAM_TGT_RENEG(nvram_data, tgt, bus)) + sdp->isp_devparam[tgt].nvrm_flags |= DPARM_RENEG; + sdp->isp_devparam[tgt].nvrm_flags |= DPARM_ARQ; + if (ISP1080_NVRAM_TGT_TQING(nvram_data, tgt, bus)) + sdp->isp_devparam[tgt].nvrm_flags |= DPARM_TQING; + if (ISP1080_NVRAM_TGT_SYNC(nvram_data, tgt, bus)) + sdp->isp_devparam[tgt].nvrm_flags |= DPARM_SYNC; + if (ISP1080_NVRAM_TGT_WIDE(nvram_data, tgt, bus)) + sdp->isp_devparam[tgt].nvrm_flags |= DPARM_WIDE; + if (ISP1080_NVRAM_TGT_PARITY(nvram_data, tgt, bus)) + sdp->isp_devparam[tgt].nvrm_flags |= DPARM_PARITY; + if (ISP1080_NVRAM_TGT_DISC(nvram_data, tgt, bus)) + sdp->isp_devparam[tgt].nvrm_flags |= DPARM_DISC; + sdp->isp_devparam[tgt].actv_flags = 0; + isp_prt(isp, ISP_LOGDEBUG0, sc2, sc4, + bus, tgt, sdp->isp_devparam[tgt].nvrm_flags, + sdp->isp_devparam[tgt].nvrm_offset, + sdp->isp_devparam[tgt].nvrm_period); + sdp->isp_devparam[tgt].goal_offset = + sdp->isp_devparam[tgt].nvrm_offset; + sdp->isp_devparam[tgt].goal_period = + sdp->isp_devparam[tgt].nvrm_period; + sdp->isp_devparam[tgt].goal_flags = + sdp->isp_devparam[tgt].nvrm_flags; + } +} + +static void +isp_parse_nvram_12160(ispsoftc_t *isp, int bus, uint8_t *nvram_data) +{ + sdparam *sdp = (sdparam *) isp->isp_param; + int tgt; + + sdp += bus; + + sdp->isp_fifo_threshold = + ISP12160_NVRAM_FIFO_THRESHOLD(nvram_data); + + if ((isp->isp_confopts & ISP_CFG_OWNLOOPID) == 0) + sdp->isp_initiator_id = + ISP12160_NVRAM_INITIATOR_ID(nvram_data, bus); + + sdp->isp_bus_reset_delay = + ISP12160_NVRAM_BUS_RESET_DELAY(nvram_data, bus); + + sdp->isp_retry_count = + ISP12160_NVRAM_BUS_RETRY_COUNT(nvram_data, bus); + + sdp->isp_retry_delay = + ISP12160_NVRAM_BUS_RETRY_DELAY(nvram_data, bus); + + sdp->isp_async_data_setup = + ISP12160_NVRAM_ASYNC_DATA_SETUP_TIME(nvram_data, bus); + + sdp->isp_req_ack_active_neg = + ISP12160_NVRAM_REQ_ACK_ACTIVE_NEGATION(nvram_data, bus); + + sdp->isp_data_line_active_neg = + ISP12160_NVRAM_DATA_LINE_ACTIVE_NEGATION(nvram_data, bus); + + sdp->isp_data_dma_burst_enabl = + ISP12160_NVRAM_BURST_ENABLE(nvram_data); + + sdp->isp_cmd_dma_burst_enable = + ISP12160_NVRAM_BURST_ENABLE(nvram_data); + + sdp->isp_selection_timeout = + ISP12160_NVRAM_SELECTION_TIMEOUT(nvram_data, bus); + + sdp->isp_max_queue_depth = + ISP12160_NVRAM_MAX_QUEUE_DEPTH(nvram_data, bus); + + isp_prt(isp, ISP_LOGDEBUG0, sc0, sc4, + bus, sdp->isp_fifo_threshold, sdp->isp_initiator_id, + sdp->isp_bus_reset_delay, sdp->isp_retry_count, + sdp->isp_retry_delay, sdp->isp_async_data_setup); + isp_prt(isp, ISP_LOGDEBUG0, sc1, sc4, + sdp->isp_req_ack_active_neg, sdp->isp_data_line_active_neg, + sdp->isp_data_dma_burst_enabl, sdp->isp_cmd_dma_burst_enable, + sdp->isp_selection_timeout, sdp->isp_max_queue_depth); + + for (tgt = 0; tgt < MAX_TARGETS; tgt++) { + sdp->isp_devparam[tgt].dev_enable = + ISP12160_NVRAM_TGT_DEVICE_ENABLE(nvram_data, tgt, bus); + sdp->isp_devparam[tgt].exc_throttle = + ISP12160_NVRAM_TGT_EXEC_THROTTLE(nvram_data, tgt, bus); + sdp->isp_devparam[tgt].nvrm_offset = + ISP12160_NVRAM_TGT_SYNC_OFFSET(nvram_data, tgt, bus); + sdp->isp_devparam[tgt].nvrm_period = + ISP12160_NVRAM_TGT_SYNC_PERIOD(nvram_data, tgt, bus); + sdp->isp_devparam[tgt].nvrm_flags = 0; + if (ISP12160_NVRAM_TGT_RENEG(nvram_data, tgt, bus)) + sdp->isp_devparam[tgt].nvrm_flags |= DPARM_RENEG; + sdp->isp_devparam[tgt].nvrm_flags |= DPARM_ARQ; + if (ISP12160_NVRAM_TGT_TQING(nvram_data, tgt, bus)) + sdp->isp_devparam[tgt].nvrm_flags |= DPARM_TQING; + if (ISP12160_NVRAM_TGT_SYNC(nvram_data, tgt, bus)) + sdp->isp_devparam[tgt].nvrm_flags |= DPARM_SYNC; + if (ISP12160_NVRAM_TGT_WIDE(nvram_data, tgt, bus)) + sdp->isp_devparam[tgt].nvrm_flags |= DPARM_WIDE; + if (ISP12160_NVRAM_TGT_PARITY(nvram_data, tgt, bus)) + sdp->isp_devparam[tgt].nvrm_flags |= DPARM_PARITY; + if (ISP12160_NVRAM_TGT_DISC(nvram_data, tgt, bus)) + sdp->isp_devparam[tgt].nvrm_flags |= DPARM_DISC; + sdp->isp_devparam[tgt].actv_flags = 0; + isp_prt(isp, ISP_LOGDEBUG0, sc2, sc4, + bus, tgt, sdp->isp_devparam[tgt].nvrm_flags, + sdp->isp_devparam[tgt].nvrm_offset, + sdp->isp_devparam[tgt].nvrm_period); + sdp->isp_devparam[tgt].goal_offset = + sdp->isp_devparam[tgt].nvrm_offset; + sdp->isp_devparam[tgt].goal_period = + sdp->isp_devparam[tgt].nvrm_period; + sdp->isp_devparam[tgt].goal_flags = + sdp->isp_devparam[tgt].nvrm_flags; + } +} + +static void +isp_parse_nvram_2100(ispsoftc_t *isp, uint8_t *nvram_data) +{ + fcparam *fcp = (fcparam *) isp->isp_param; + uint64_t wwn; + + /* + * There is NVRAM storage for both Port and Node entities- + * but the Node entity appears to be unused on all the cards + * I can find. However, we should account for this being set + * at some point in the future. + * + * Qlogic WWNs have an NAA of 2, but usually nothing shows up in + * bits 48..60. In the case of the 2202, it appears that they do + * use bit 48 to distinguish between the two instances on the card. + * The 2204, which I've never seen, *probably* extends this method. + */ + wwn = ISP2100_NVRAM_PORT_NAME(nvram_data); + if (wwn) { + isp_prt(isp, ISP_LOGCONFIG, "NVRAM Port WWN 0x%08x%08x", + (uint32_t) (wwn >> 32), (uint32_t) (wwn & 0xffffffff)); + if ((wwn >> 60) == 0) { + wwn |= (((uint64_t) 2)<< 60); + } + } + fcp->isp_portwwn = wwn; + if (IS_2200(isp) || IS_23XX(isp)) { + wwn = ISP2100_NVRAM_NODE_NAME(nvram_data); + if (wwn) { + isp_prt(isp, ISP_LOGCONFIG, "NVRAM Node WWN 0x%08x%08x", + (uint32_t) (wwn >> 32), + (uint32_t) (wwn & 0xffffffff)); + if ((wwn >> 60) == 0) { + wwn |= (((uint64_t) 2)<< 60); + } + } + } else { + wwn &= ~((uint64_t) 0xfff << 48); + } + fcp->isp_nodewwn = wwn; + + /* + * Make sure we have both Node and Port as non-zero values. + */ + if (fcp->isp_nodewwn != 0 && fcp->isp_portwwn == 0) { + fcp->isp_portwwn = fcp->isp_nodewwn; + } else if (fcp->isp_nodewwn == 0 && fcp->isp_portwwn != 0) { + fcp->isp_nodewwn = fcp->isp_portwwn; + } + + /* + * Make the Node and Port values sane if they're NAA == 2. + * This means to clear bits 48..56 for the Node WWN and + * make sure that there's some non-zero value in 48..56 + * for the Port WWN. + */ + if (fcp->isp_nodewwn && fcp->isp_portwwn) { + if ((fcp->isp_nodewwn & (((uint64_t) 0xfff) << 48)) != 0 && + (fcp->isp_nodewwn >> 60) == 2) { + fcp->isp_nodewwn &= ~((uint64_t) 0xfff << 48); + } + if ((fcp->isp_portwwn & (((uint64_t) 0xfff) << 48)) == 0 && + (fcp->isp_portwwn >> 60) == 2) { + fcp->isp_portwwn |= ((uint64_t) 1 << 56); + } + } + + fcp->isp_maxalloc = + ISP2100_NVRAM_MAXIOCBALLOCATION(nvram_data); + if ((isp->isp_confopts & ISP_CFG_OWNFSZ) == 0) + fcp->isp_maxfrmlen = + ISP2100_NVRAM_MAXFRAMELENGTH(nvram_data); + fcp->isp_retry_delay = + ISP2100_NVRAM_RETRY_DELAY(nvram_data); + fcp->isp_retry_count = + ISP2100_NVRAM_RETRY_COUNT(nvram_data); + if ((isp->isp_confopts & ISP_CFG_OWNLOOPID) == 0) + fcp->isp_loopid = + ISP2100_NVRAM_HARDLOOPID(nvram_data); + if ((isp->isp_confopts & ISP_CFG_OWNEXCTHROTTLE) == 0) + fcp->isp_execthrottle = + ISP2100_NVRAM_EXECUTION_THROTTLE(nvram_data); + fcp->isp_fwoptions = ISP2100_NVRAM_OPTIONS(nvram_data); + isp_prt(isp, ISP_LOGDEBUG0, + "NVRAM 0x%08x%08x 0x%08x%08x maxalloc %d maxframelen %d", + (uint32_t) (fcp->isp_nodewwn >> 32), (uint32_t) fcp->isp_nodewwn, + (uint32_t) (fcp->isp_portwwn >> 32), (uint32_t) fcp->isp_portwwn, + ISP2100_NVRAM_MAXIOCBALLOCATION(nvram_data), + ISP2100_NVRAM_MAXFRAMELENGTH(nvram_data)); + isp_prt(isp, ISP_LOGDEBUG0, + "execthrottle %d fwoptions 0x%x hardloop %d tov %d", + ISP2100_NVRAM_EXECUTION_THROTTLE(nvram_data), + ISP2100_NVRAM_OPTIONS(nvram_data), + ISP2100_NVRAM_HARDLOOPID(nvram_data), + ISP2100_NVRAM_TOV(nvram_data)); + fcp->isp_xfwoptions = ISP2100_XFW_OPTIONS(nvram_data); + fcp->isp_zfwoptions = ISP2100_ZFW_OPTIONS(nvram_data); + isp_prt(isp, ISP_LOGDEBUG0, + "xfwoptions 0x%x zfw options 0x%x", + ISP2100_XFW_OPTIONS(nvram_data), ISP2100_ZFW_OPTIONS(nvram_data)); +} + +#ifdef ISP_FW_CRASH_DUMP +static void isp2200_fw_dump(ispsoftc_t *); +static void isp2300_fw_dump(ispsoftc_t *); + +static void +isp2200_fw_dump(ispsoftc_t *isp) +{ + int i, j; + mbreg_t mbs; + uint16_t *ptr; + + MEMZERO(&mbs, sizeof (mbs)); + ptr = FCPARAM(isp)->isp_dump_data; + if (ptr == NULL) { + isp_prt(isp, ISP_LOGERR, + "No place to dump RISC registers and SRAM"); + return; + } + if (*ptr++) { + isp_prt(isp, ISP_LOGERR, + "dump area for RISC registers and SRAM already used"); + return; + } + ISP_WRITE(isp, HCCR, HCCR_CMD_PAUSE); + for (i = 0; i < 100; i++) { + USEC_DELAY(100); + if (ISP_READ(isp, HCCR) & HCCR_PAUSE) { + break; + } + } + if (ISP_READ(isp, HCCR) & HCCR_PAUSE) { + /* + * PBIU Registers + */ + for (i = 0; i < 8; i++) { + *ptr++ = ISP_READ(isp, BIU_BLOCK + (i << 1)); + } + + /* + * Mailbox Registers + */ + for (i = 0; i < 8; i++) { + *ptr++ = ISP_READ(isp, MBOX_BLOCK + (i << 1)); + } + + /* + * DMA Registers + */ + for (i = 0; i < 48; i++) { + *ptr++ = ISP_READ(isp, DMA_BLOCK + 0x20 + (i << 1)); + } + + /* + * RISC H/W Registers + */ + ISP_WRITE(isp, BIU2100_CSR, 0); + for (i = 0; i < 16; i++) { + *ptr++ = ISP_READ(isp, BIU_BLOCK + 0xA0 + (i << 1)); + } + + /* + * RISC GP Registers + */ + for (j = 0; j < 8; j++) { + ISP_WRITE(isp, BIU_BLOCK + 0xA4, 0x2000 + (j << 8)); + for (i = 0; i < 16; i++) { + *ptr++ = + ISP_READ(isp, BIU_BLOCK + 0x80 + (i << 1)); + } + } + + /* + * Frame Buffer Hardware Registers + */ + ISP_WRITE(isp, BIU2100_CSR, 0x10); + for (i = 0; i < 16; i++) { + *ptr++ = ISP_READ(isp, BIU_BLOCK + 0x80 + (i << 1)); + } + + /* + * Fibre Protocol Module 0 Hardware Registers + */ + ISP_WRITE(isp, BIU2100_CSR, 0x20); + for (i = 0; i < 64; i++) { + *ptr++ = ISP_READ(isp, BIU_BLOCK + 0x80 + (i << 1)); + } + + /* + * Fibre Protocol Module 1 Hardware Registers + */ + ISP_WRITE(isp, BIU2100_CSR, 0x30); + for (i = 0; i < 64; i++) { + *ptr++ = ISP_READ(isp, BIU_BLOCK + 0x80 + (i << 1)); + } + } else { + isp_prt(isp, ISP_LOGERR, "RISC Would Not Pause"); + return; + } + isp_prt(isp, ISP_LOGALL, + "isp_fw_dump: RISC registers dumped successfully"); + ISP_WRITE(isp, BIU2100_CSR, BIU2100_SOFT_RESET); + for (i = 0; i < 100; i++) { + USEC_DELAY(100); + if (ISP_READ(isp, OUTMAILBOX0) == 0) { + break; + } + } + if (ISP_READ(isp, OUTMAILBOX0) != 0) { + isp_prt(isp, ISP_LOGERR, "Board Would Not Reset"); + return; + } + ISP_WRITE(isp, HCCR, HCCR_CMD_PAUSE); + for (i = 0; i < 100; i++) { + USEC_DELAY(100); + if (ISP_READ(isp, HCCR) & HCCR_PAUSE) { + break; + } + } + if ((ISP_READ(isp, HCCR) & HCCR_PAUSE) == 0) { + isp_prt(isp, ISP_LOGERR, "RISC Would Not Pause After Reset"); + return; + } + ISP_WRITE(isp, RISC_EMB, 0xf2); + ISP_WRITE(isp, HCCR, HCCR_CMD_RELEASE); + for (i = 0; i < 100; i++) { + USEC_DELAY(100); + if ((ISP_READ(isp, HCCR) & HCCR_PAUSE) == 0) { + break; + } + } + ENABLE_INTS(isp); + mbs.param[0] = MBOX_READ_RAM_WORD; + mbs.param[1] = 0x1000; + isp->isp_mbxworkp = (void *) ptr; + isp->isp_mbxwrk0 = 0xefff; /* continuation count */ + isp->isp_mbxwrk1 = 0x1001; /* next SRAM address */ + isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + isp_prt(isp, ISP_LOGWARN, + "RAM DUMP FAILED @ WORD %x", isp->isp_mbxwrk1); + return; + } + ptr = isp->isp_mbxworkp; /* finish fetch of final word */ + *ptr++ = isp->isp_mboxtmp[2]; + isp_prt(isp, ISP_LOGALL, "isp_fw_dump: SRAM dumped successfully"); + FCPARAM(isp)->isp_dump_data[0] = isp->isp_type; /* now used */ + (void) isp_async(isp, ISPASYNC_FW_DUMPED, 0); +} + +static void +isp2300_fw_dump(ispsoftc_t *isp) +{ + int i, j; + mbreg_t mbs; + uint16_t *ptr; + + MEMZERO(&mbs, sizeof (mbs)); + ptr = FCPARAM(isp)->isp_dump_data; + if (ptr == NULL) { + isp_prt(isp, ISP_LOGERR, + "No place to dump RISC registers and SRAM"); + return; + } + if (*ptr++) { + isp_prt(isp, ISP_LOGERR, + "dump area for RISC registers and SRAM already used"); + return; + } + ISP_WRITE(isp, HCCR, HCCR_CMD_PAUSE); + for (i = 0; i < 100; i++) { + USEC_DELAY(100); + if (ISP_READ(isp, HCCR) & HCCR_PAUSE) { + break; + } + } + if (ISP_READ(isp, HCCR) & HCCR_PAUSE) { + /* + * PBIU registers + */ + for (i = 0; i < 8; i++) { + *ptr++ = ISP_READ(isp, BIU_BLOCK + (i << 1)); + } + + /* + * ReqQ-RspQ-Risc2Host Status registers + */ + for (i = 0; i < 8; i++) { + *ptr++ = ISP_READ(isp, BIU_BLOCK + 0x10 + (i << 1)); + } + + /* + * Mailbox Registers + */ + for (i = 0; i < 32; i++) { + *ptr++ = + ISP_READ(isp, PCI_MBOX_REGS2300_OFF + (i << 1)); + } + + /* + * Auto Request Response DMA registers + */ + ISP_WRITE(isp, BIU2100_CSR, 0x40); + for (i = 0; i < 32; i++) { + *ptr++ = ISP_READ(isp, BIU_BLOCK + 0x80 + (i << 1)); + } + + /* + * DMA registers + */ + ISP_WRITE(isp, BIU2100_CSR, 0x50); + for (i = 0; i < 48; i++) { + *ptr++ = ISP_READ(isp, BIU_BLOCK + 0x80 + (i << 1)); + } + + /* + * RISC hardware registers + */ + ISP_WRITE(isp, BIU2100_CSR, 0); + for (i = 0; i < 16; i++) { + *ptr++ = ISP_READ(isp, BIU_BLOCK + 0xA0 + (i << 1)); + } + + /* + * RISC GP? registers + */ + for (j = 0; j < 8; j++) { + ISP_WRITE(isp, BIU_BLOCK + 0xA4, 0x2000 + (j << 9)); + for (i = 0; i < 16; i++) { + *ptr++ = + ISP_READ(isp, BIU_BLOCK + 0x80 + (i << 1)); + } + } + + /* + * frame buffer hardware registers + */ + ISP_WRITE(isp, BIU2100_CSR, 0x10); + for (i = 0; i < 64; i++) { + *ptr++ = ISP_READ(isp, BIU_BLOCK + 0x80 + (i << 1)); + } + + /* + * FPM B0 hardware registers + */ + ISP_WRITE(isp, BIU2100_CSR, 0x20); + for (i = 0; i < 64; i++) { + *ptr++ = ISP_READ(isp, BIU_BLOCK + 0x80 + (i << 1)); + } + + /* + * FPM B1 hardware registers + */ + ISP_WRITE(isp, BIU2100_CSR, 0x30); + for (i = 0; i < 64; i++) { + *ptr++ = ISP_READ(isp, BIU_BLOCK + 0x80 + (i << 1)); + } + } else { + isp_prt(isp, ISP_LOGERR, "RISC Would Not Pause"); + return; + } + isp_prt(isp, ISP_LOGALL, + "isp_fw_dump: RISC registers dumped successfully"); + ISP_WRITE(isp, BIU2100_CSR, BIU2100_SOFT_RESET); + for (i = 0; i < 100; i++) { + USEC_DELAY(100); + if (ISP_READ(isp, OUTMAILBOX0) == 0) { + break; + } + } + if (ISP_READ(isp, OUTMAILBOX0) != 0) { + isp_prt(isp, ISP_LOGERR, "Board Would Not Reset"); + return; + } + ENABLE_INTS(isp); + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_READ_RAM_WORD; + mbs.param[1] = 0x800; + isp->isp_mbxworkp = (void *) ptr; + isp->isp_mbxwrk0 = 0xf7ff; /* continuation count */ + isp->isp_mbxwrk1 = 0x801; /* next SRAM address */ + isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + isp_prt(isp, ISP_LOGWARN, + "RAM DUMP FAILED @ WORD %x", isp->isp_mbxwrk1); + return; + } + ptr = isp->isp_mbxworkp; /* finish fetch of final word */ + *ptr++ = isp->isp_mboxtmp[2]; + MEMZERO(&mbs, sizeof (mbs)); + mbs.param[0] = MBOX_READ_RAM_WORD_EXTENDED; + mbs.param[8] = 1; + isp->isp_mbxworkp = (void *) ptr; + isp->isp_mbxwrk0 = 0xffff; /* continuation count */ + isp->isp_mbxwrk1 = 0x1; /* next SRAM address */ + isp->isp_mbxwrk8 = 0x1; + isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs); + if (mbs.param[0] != MBOX_COMMAND_COMPLETE) { + isp_prt(isp, ISP_LOGWARN, + "RAM DUMP FAILED @ WORD %x", 0x10000 + isp->isp_mbxwrk1); + return; + } + ptr = isp->isp_mbxworkp; /* finish final word */ + *ptr++ = mbs.param[2]; + isp_prt(isp, ISP_LOGALL, "isp_fw_dump: SRAM dumped successfully"); + FCPARAM(isp)->isp_dump_data[0] = isp->isp_type; /* now used */ + (void) isp_async(isp, ISPASYNC_FW_DUMPED, 0); +} + +void +isp_fw_dump(ispsoftc_t *isp) +{ + if (IS_2200(isp)) + isp2200_fw_dump(isp); + else if (IS_23XX(isp)) + isp2300_fw_dump(isp); +} +#endif diff --git a/sys/dev/isp/isp_freebsd.c b/sys/dev/isp/isp_freebsd.c new file mode 100644 index 000000000000..af81b135acaa --- /dev/null +++ b/sys/dev/isp/isp_freebsd.c @@ -0,0 +1,3257 @@ +/*- + * + * Copyright (c) 1997-2006 by Matthew Jacob + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Platform (FreeBSD) dependent common attachment code for Qlogic adapters. + */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); +#include <dev/isp/isp_freebsd.h> +#include <sys/unistd.h> +#include <sys/kthread.h> +#include <machine/stdarg.h> /* for use by isp_prt below */ +#include <sys/conf.h> +#include <sys/module.h> +#include <sys/ioccom.h> +#include <dev/isp/isp_ioctl.h> + + +MODULE_VERSION(isp, 1); +MODULE_DEPEND(isp, cam, 1, 1, 1); +int isp_announced = 0; + +static d_ioctl_t ispioctl; +static void isp_intr_enable(void *); +static void isp_cam_async(void *, uint32_t, struct cam_path *, void *); +static void isp_poll(struct cam_sim *); +static timeout_t isp_watchdog; +static void isp_kthread(void *); +static void isp_action(struct cam_sim *, union ccb *); + +#if __FreeBSD_version < 700000 +ispfwfunc *isp_get_firmware_p = NULL; +#endif + +#if __FreeBSD_version < 500000 +#define ISP_CDEV_MAJOR 248 +static struct cdevsw isp_cdevsw = { + /* open */ nullopen, + /* close */ nullclose, + /* read */ noread, + /* write */ nowrite, + /* ioctl */ ispioctl, + /* poll */ nopoll, + /* mmap */ nommap, + /* strategy */ nostrategy, + /* name */ "isp", + /* maj */ ISP_CDEV_MAJOR, + /* dump */ nodump, + /* psize */ nopsize, + /* flags */ D_TAPE, +}; +#else +static struct cdevsw isp_cdevsw = { + .d_version = D_VERSION, + .d_flags = D_NEEDGIANT, + .d_ioctl = ispioctl, + .d_name = "isp", +}; +#endif + +static ispsoftc_t *isplist = NULL; + +void +isp_attach(ispsoftc_t *isp) +{ + int primary, secondary; + struct ccb_setasync csa; + struct cam_devq *devq; + struct cam_sim *sim; + struct cam_path *path; + + /* + * Establish (in case of 12X0) which bus is the primary. + */ + + primary = 0; + secondary = 1; + + /* + * Create the device queue for our SIM(s). + */ + devq = cam_simq_alloc(isp->isp_maxcmds); + if (devq == NULL) { + return; + } + + /* + * Construct our SIM entry. + */ + ISPLOCK_2_CAMLOCK(isp); + sim = cam_sim_alloc(isp_action, isp_poll, "isp", isp, + device_get_unit(isp->isp_dev), 1, isp->isp_maxcmds, devq); + if (sim == NULL) { + cam_simq_free(devq); + CAMLOCK_2_ISPLOCK(isp); + return; + } + CAMLOCK_2_ISPLOCK(isp); + + isp->isp_osinfo.ehook.ich_func = isp_intr_enable; + isp->isp_osinfo.ehook.ich_arg = isp; + ISPLOCK_2_CAMLOCK(isp); + if (config_intrhook_establish(&isp->isp_osinfo.ehook) != 0) { + cam_sim_free(sim, TRUE); + CAMLOCK_2_ISPLOCK(isp); + isp_prt(isp, ISP_LOGERR, + "could not establish interrupt enable hook"); + return; + } + + if (xpt_bus_register(sim, primary) != CAM_SUCCESS) { + cam_sim_free(sim, TRUE); + CAMLOCK_2_ISPLOCK(isp); + return; + } + + if (xpt_create_path(&path, NULL, cam_sim_path(sim), + CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { + xpt_bus_deregister(cam_sim_path(sim)); + cam_sim_free(sim, TRUE); + config_intrhook_disestablish(&isp->isp_osinfo.ehook); + CAMLOCK_2_ISPLOCK(isp); + return; + } + + xpt_setup_ccb(&csa.ccb_h, path, 5); + csa.ccb_h.func_code = XPT_SASYNC_CB; + csa.event_enable = AC_LOST_DEVICE; + csa.callback = isp_cam_async; + csa.callback_arg = sim; + xpt_action((union ccb *)&csa); + CAMLOCK_2_ISPLOCK(isp); + isp->isp_sim = sim; + isp->isp_path = path; + /* + * Create a kernel thread for fibre channel instances. We + * don't have dual channel FC cards. + */ + if (IS_FC(isp)) { + ISPLOCK_2_CAMLOCK(isp); +#if __FreeBSD_version >= 500000 + /* XXX: LOCK VIOLATION */ + cv_init(&isp->isp_osinfo.kthread_cv, "isp_kthread_cv"); + if (kthread_create(isp_kthread, isp, &isp->isp_osinfo.kproc, + RFHIGHPID, 0, "%s: fc_thrd", + device_get_nameunit(isp->isp_dev))) +#else + if (kthread_create(isp_kthread, isp, &isp->isp_osinfo.kproc, + "%s: fc_thrd", device_get_nameunit(isp->isp_dev))) +#endif + { + xpt_bus_deregister(cam_sim_path(sim)); + cam_sim_free(sim, TRUE); + config_intrhook_disestablish(&isp->isp_osinfo.ehook); + CAMLOCK_2_ISPLOCK(isp); + isp_prt(isp, ISP_LOGERR, "could not create kthread"); + return; + } + CAMLOCK_2_ISPLOCK(isp); + } + + + /* + * If we have a second channel, construct SIM entry for that. + */ + if (IS_DUALBUS(isp)) { + ISPLOCK_2_CAMLOCK(isp); + sim = cam_sim_alloc(isp_action, isp_poll, "isp", isp, + device_get_unit(isp->isp_dev), 1, isp->isp_maxcmds, devq); + if (sim == NULL) { + xpt_bus_deregister(cam_sim_path(isp->isp_sim)); + xpt_free_path(isp->isp_path); + cam_simq_free(devq); + config_intrhook_disestablish(&isp->isp_osinfo.ehook); + return; + } + if (xpt_bus_register(sim, secondary) != CAM_SUCCESS) { + xpt_bus_deregister(cam_sim_path(isp->isp_sim)); + xpt_free_path(isp->isp_path); + cam_sim_free(sim, TRUE); + config_intrhook_disestablish(&isp->isp_osinfo.ehook); + CAMLOCK_2_ISPLOCK(isp); + return; + } + + if (xpt_create_path(&path, NULL, cam_sim_path(sim), + CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { + xpt_bus_deregister(cam_sim_path(isp->isp_sim)); + xpt_free_path(isp->isp_path); + xpt_bus_deregister(cam_sim_path(sim)); + cam_sim_free(sim, TRUE); + config_intrhook_disestablish(&isp->isp_osinfo.ehook); + CAMLOCK_2_ISPLOCK(isp); + return; + } + + xpt_setup_ccb(&csa.ccb_h, path, 5); + csa.ccb_h.func_code = XPT_SASYNC_CB; + csa.event_enable = AC_LOST_DEVICE; + csa.callback = isp_cam_async; + csa.callback_arg = sim; + xpt_action((union ccb *)&csa); + CAMLOCK_2_ISPLOCK(isp); + isp->isp_sim2 = sim; + isp->isp_path2 = path; + } + + /* + * Create device nodes + */ + (void) make_dev(&isp_cdevsw, device_get_unit(isp->isp_dev), UID_ROOT, + GID_OPERATOR, 0600, "%s", device_get_nameunit(isp->isp_dev)); + + if (isp->isp_role != ISP_ROLE_NONE) { + isp->isp_state = ISP_RUNSTATE; + ENABLE_INTS(isp); + } + if (isplist == NULL) { + isplist = isp; + } else { + ispsoftc_t *tmp = isplist; + while (tmp->isp_osinfo.next) { + tmp = tmp->isp_osinfo.next; + } + tmp->isp_osinfo.next = isp; + } + +} + +static __inline void +isp_freeze_loopdown(ispsoftc_t *isp, char *msg) +{ + if (isp->isp_osinfo.simqfrozen == 0) { + isp_prt(isp, ISP_LOGDEBUG0, "%s: freeze simq (loopdown)", msg); + isp->isp_osinfo.simqfrozen |= SIMQFRZ_LOOPDOWN; + ISPLOCK_2_CAMLOCK(isp); + xpt_freeze_simq(isp->isp_sim, 1); + CAMLOCK_2_ISPLOCK(isp); + } else { + isp_prt(isp, ISP_LOGDEBUG0, "%s: mark frozen (loopdown)", msg); + isp->isp_osinfo.simqfrozen |= SIMQFRZ_LOOPDOWN; + } +} + + +#if __FreeBSD_version < 500000 +#define _DEV dev_t +#define _IOP struct proc +#else +#define _IOP struct thread +#define _DEV struct cdev * +#endif + +static int +ispioctl(_DEV dev, u_long c, caddr_t addr, int flags, _IOP *td) +{ + ispsoftc_t *isp; + int nr, retval = ENOTTY; + + isp = isplist; + while (isp) { + if (minor(dev) == device_get_unit(isp->isp_dev)) { + break; + } + isp = isp->isp_osinfo.next; + } + if (isp == NULL) + return (ENXIO); + + switch (c) { +#ifdef ISP_FW_CRASH_DUMP + case ISP_GET_FW_CRASH_DUMP: + if (IS_FC(isp)) { + uint16_t *ptr = FCPARAM(isp)->isp_dump_data; + size_t sz; + + retval = 0; + if (IS_2200(isp)) { + sz = QLA2200_RISC_IMAGE_DUMP_SIZE; + } else { + sz = QLA2300_RISC_IMAGE_DUMP_SIZE; + } + ISP_LOCK(isp); + if (ptr && *ptr) { + void *uaddr = *((void **) addr); + if (copyout(ptr, uaddr, sz)) { + retval = EFAULT; + } else { + *ptr = 0; + } + } else { + retval = ENXIO; + } + ISP_UNLOCK(isp); + } + break; + case ISP_FORCE_CRASH_DUMP: + if (IS_FC(isp)) { + ISP_LOCK(isp); + isp_freeze_loopdown(isp, + "ispioctl(ISP_FORCE_CRASH_DUMP)"); + isp_fw_dump(isp); + isp_reinit(isp); + ISP_UNLOCK(isp); + retval = 0; + } + break; +#endif + case ISP_SDBLEV: + { + int olddblev = isp->isp_dblev; + isp->isp_dblev = *(int *)addr; + *(int *)addr = olddblev; + retval = 0; + break; + } + case ISP_GETROLE: + *(int *)addr = isp->isp_role; + retval = 0; + break; + case ISP_SETROLE: + nr = *(int *)addr; + if (nr & ~(ISP_ROLE_INITIATOR|ISP_ROLE_TARGET)) { + retval = EINVAL; + break; + } + *(int *)addr = isp->isp_role; + isp->isp_role = nr; + /* FALLTHROUGH */ + case ISP_RESETHBA: + ISP_LOCK(isp); + isp_reinit(isp); + ISP_UNLOCK(isp); + retval = 0; + break; + case ISP_RESCAN: + if (IS_FC(isp)) { + ISP_LOCK(isp); + if (isp_fc_runstate(isp, 5 * 1000000)) { + retval = EIO; + } else { + retval = 0; + } + ISP_UNLOCK(isp); + } + break; + case ISP_FC_LIP: + if (IS_FC(isp)) { + ISP_LOCK(isp); + if (isp_control(isp, ISPCTL_SEND_LIP, 0)) { + retval = EIO; + } else { + retval = 0; + } + ISP_UNLOCK(isp); + } + break; + case ISP_FC_GETDINFO: + { + struct isp_fc_device *ifc = (struct isp_fc_device *) addr; + struct lportdb *lp; + + if (IS_SCSI(isp)) { + break; + } + if (ifc->loopid < 0 || ifc->loopid >= MAX_FC_TARG) { + retval = EINVAL; + break; + } + ISP_LOCK(isp); + lp = &FCPARAM(isp)->portdb[ifc->loopid]; + if (lp->valid) { + ifc->role = lp->roles; + ifc->loopid = lp->loopid; + ifc->portid = lp->portid; + ifc->node_wwn = lp->node_wwn; + ifc->port_wwn = lp->port_wwn; + retval = 0; + } else { + retval = ENODEV; + } + ISP_UNLOCK(isp); + break; + } + case ISP_GET_STATS: + { + isp_stats_t *sp = (isp_stats_t *) addr; + + MEMZERO(sp, sizeof (*sp)); + sp->isp_stat_version = ISP_STATS_VERSION; + sp->isp_type = isp->isp_type; + sp->isp_revision = isp->isp_revision; + ISP_LOCK(isp); + sp->isp_stats[ISP_INTCNT] = isp->isp_intcnt; + sp->isp_stats[ISP_INTBOGUS] = isp->isp_intbogus; + sp->isp_stats[ISP_INTMBOXC] = isp->isp_intmboxc; + sp->isp_stats[ISP_INGOASYNC] = isp->isp_intoasync; + sp->isp_stats[ISP_RSLTCCMPLT] = isp->isp_rsltccmplt; + sp->isp_stats[ISP_FPHCCMCPLT] = isp->isp_fphccmplt; + sp->isp_stats[ISP_RSCCHIWAT] = isp->isp_rscchiwater; + sp->isp_stats[ISP_FPCCHIWAT] = isp->isp_fpcchiwater; + ISP_UNLOCK(isp); + retval = 0; + break; + } + case ISP_CLR_STATS: + ISP_LOCK(isp); + isp->isp_intcnt = 0; + isp->isp_intbogus = 0; + isp->isp_intmboxc = 0; + isp->isp_intoasync = 0; + isp->isp_rsltccmplt = 0; + isp->isp_fphccmplt = 0; + isp->isp_rscchiwater = 0; + isp->isp_fpcchiwater = 0; + ISP_UNLOCK(isp); + retval = 0; + break; + case ISP_FC_GETHINFO: + { + struct isp_hba_device *hba = (struct isp_hba_device *) addr; + MEMZERO(hba, sizeof (*hba)); + + hba->fc_fw_major = ISP_FW_MAJORX(isp->isp_fwrev); + hba->fc_fw_minor = ISP_FW_MINORX(isp->isp_fwrev); + hba->fc_fw_micro = ISP_FW_MICROX(isp->isp_fwrev); + if (IS_FC(isp)) { + hba->fc_speed = FCPARAM(isp)->isp_gbspeed; + hba->fc_scsi_supported = 1; + hba->fc_topology = FCPARAM(isp)->isp_topo + 1; + hba->fc_loopid = FCPARAM(isp)->isp_loopid; + hba->nvram_node_wwn = FCPARAM(isp)->isp_nodewwn; + hba->nvram_port_wwn = FCPARAM(isp)->isp_portwwn; + hba->active_node_wwn = ISP_NODEWWN(isp); + hba->active_port_wwn = ISP_PORTWWN(isp); + } + retval = 0; + break; + } + case ISP_GET_FC_PARAM: + { + struct isp_fc_param *f = (struct isp_fc_param *) addr; + + if (IS_SCSI(isp)) { + break; + } + f->parameter = 0; + if (strcmp(f->param_name, "framelength") == 0) { + f->parameter = FCPARAM(isp)->isp_maxfrmlen; + retval = 0; + break; + } + if (strcmp(f->param_name, "exec_throttle") == 0) { + f->parameter = FCPARAM(isp)->isp_execthrottle; + retval = 0; + break; + } + if (strcmp(f->param_name, "fullduplex") == 0) { + if (FCPARAM(isp)->isp_fwoptions & ICBOPT_FULL_DUPLEX) + f->parameter = 1; + retval = 0; + break; + } + if (strcmp(f->param_name, "loopid") == 0) { + f->parameter = FCPARAM(isp)->isp_loopid; + retval = 0; + break; + } + retval = EINVAL; + break; + } + case ISP_SET_FC_PARAM: + { + struct isp_fc_param *f = (struct isp_fc_param *) addr; + uint32_t param = f->parameter; + + if (IS_SCSI(isp)) { + break; + } + f->parameter = 0; + if (strcmp(f->param_name, "framelength") == 0) { + if (param != 512 && param != 1024 && param != 1024) { + retval = EINVAL; + break; + } + FCPARAM(isp)->isp_maxfrmlen = param; + retval = 0; + break; + } + if (strcmp(f->param_name, "exec_throttle") == 0) { + if (param < 16 || param > 255) { + retval = EINVAL; + break; + } + FCPARAM(isp)->isp_execthrottle = param; + retval = 0; + break; + } + if (strcmp(f->param_name, "fullduplex") == 0) { + if (param != 0 && param != 1) { + retval = EINVAL; + break; + } + if (param) { + FCPARAM(isp)->isp_fwoptions |= + ICBOPT_FULL_DUPLEX; + } else { + FCPARAM(isp)->isp_fwoptions &= + ~ICBOPT_FULL_DUPLEX; + } + retval = 0; + break; + } + if (strcmp(f->param_name, "loopid") == 0) { + if (param < 0 || param > 125) { + retval = EINVAL; + break; + } + FCPARAM(isp)->isp_loopid = param; + retval = 0; + break; + } + retval = EINVAL; + break; + } + case ISP_TSK_MGMT: + { + int needmarker; + struct isp_fc_tsk_mgmt *fct = (struct isp_fc_tsk_mgmt *) addr; + uint16_t loopid; + mbreg_t mbs; + + if (IS_SCSI(isp)) { + break; + } + + memset(&mbs, 0, sizeof (mbs)); + needmarker = retval = 0; + loopid = fct->loopid; + if (IS_2KLOGIN(isp) == 0) { + loopid <<= 8; + } + switch (fct->action) { + case IPT_CLEAR_ACA: + mbs.param[0] = MBOX_CLEAR_ACA; + mbs.param[1] = loopid; + mbs.param[2] = fct->lun; + break; + case IPT_TARGET_RESET: + mbs.param[0] = MBOX_TARGET_RESET; + mbs.param[1] = loopid; + needmarker = 1; + break; + case IPT_LUN_RESET: + mbs.param[0] = MBOX_LUN_RESET; + mbs.param[1] = loopid; + mbs.param[2] = fct->lun; + needmarker = 1; + break; + case IPT_CLEAR_TASK_SET: + mbs.param[0] = MBOX_CLEAR_TASK_SET; + mbs.param[1] = loopid; + mbs.param[2] = fct->lun; + needmarker = 1; + break; + case IPT_ABORT_TASK_SET: + mbs.param[0] = MBOX_ABORT_TASK_SET; + mbs.param[1] = loopid; + mbs.param[2] = fct->lun; + needmarker = 1; + break; + default: + retval = EINVAL; + break; + } + if (retval == 0) { + ISP_LOCK(isp); + if (needmarker) { + isp->isp_sendmarker |= 1; + } + retval = isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs); + ISP_UNLOCK(isp); + if (retval) + retval = EIO; + } + break; + } + default: + break; + } + return (retval); +} + +static void +isp_intr_enable(void *arg) +{ + ispsoftc_t *isp = arg; + if (isp->isp_role != ISP_ROLE_NONE) { + ENABLE_INTS(isp); +#if 0 + isp->isp_osinfo.intsok = 1; +#endif + } + /* Release our hook so that the boot can continue. */ + config_intrhook_disestablish(&isp->isp_osinfo.ehook); +} + +/* + * Put the target mode functions here, because some are inlines + */ + +#ifdef ISP_TARGET_MODE + +static __inline int is_lun_enabled(ispsoftc_t *, int, lun_id_t); +static __inline int are_any_luns_enabled(ispsoftc_t *, int); +static __inline tstate_t *get_lun_statep(ispsoftc_t *, int, lun_id_t); +static __inline void rls_lun_statep(ispsoftc_t *, tstate_t *); +static __inline atio_private_data_t *isp_get_atpd(ispsoftc_t *, int); +static cam_status +create_lun_state(ispsoftc_t *, int, struct cam_path *, tstate_t **); +static void destroy_lun_state(ispsoftc_t *, tstate_t *); +static int isp_en_lun(ispsoftc_t *, union ccb *); +static void isp_ledone(ispsoftc_t *, lun_entry_t *); +static cam_status isp_abort_tgt_ccb(ispsoftc_t *, union ccb *); +static timeout_t isp_refire_putback_atio; +static void isp_complete_ctio(union ccb *); +static void isp_target_putback_atio(union ccb *); +static void isp_target_start_ctio(ispsoftc_t *, union ccb *); +static int isp_handle_platform_atio(ispsoftc_t *, at_entry_t *); +static int isp_handle_platform_atio2(ispsoftc_t *, at2_entry_t *); +static int isp_handle_platform_ctio(ispsoftc_t *, void *); +static int isp_handle_platform_notify_scsi(ispsoftc_t *, in_entry_t *); +static int isp_handle_platform_notify_fc(ispsoftc_t *, in_fcentry_t *); + +static __inline int +is_lun_enabled(ispsoftc_t *isp, int bus, lun_id_t lun) +{ + tstate_t *tptr; + tptr = isp->isp_osinfo.lun_hash[LUN_HASH_FUNC(isp, bus, lun)]; + if (tptr == NULL) { + return (0); + } + do { + if (tptr->lun == (lun_id_t) lun && tptr->bus == bus) { + return (1); + } + } while ((tptr = tptr->next) != NULL); + return (0); +} + +static __inline int +are_any_luns_enabled(ispsoftc_t *isp, int port) +{ + int lo, hi; + if (IS_DUALBUS(isp)) { + lo = (port * (LUN_HASH_SIZE >> 1)); + hi = lo + (LUN_HASH_SIZE >> 1); + } else { + lo = 0; + hi = LUN_HASH_SIZE; + } + for (lo = 0; lo < hi; lo++) { + if (isp->isp_osinfo.lun_hash[lo]) { + return (1); + } + } + return (0); +} + +static __inline tstate_t * +get_lun_statep(ispsoftc_t *isp, int bus, lun_id_t lun) +{ + tstate_t *tptr = NULL; + + if (lun == CAM_LUN_WILDCARD) { + if (isp->isp_osinfo.tmflags[bus] & TM_WILDCARD_ENABLED) { + tptr = &isp->isp_osinfo.tsdflt[bus]; + tptr->hold++; + return (tptr); + } + return (NULL); + } else { + tptr = isp->isp_osinfo.lun_hash[LUN_HASH_FUNC(isp, bus, lun)]; + if (tptr == NULL) { + return (NULL); + } + } + + do { + if (tptr->lun == lun && tptr->bus == bus) { + tptr->hold++; + return (tptr); + } + } while ((tptr = tptr->next) != NULL); + return (tptr); +} + +static __inline void +rls_lun_statep(ispsoftc_t *isp, tstate_t *tptr) +{ + if (tptr->hold) + tptr->hold--; +} + +static __inline atio_private_data_t * +isp_get_atpd(ispsoftc_t *isp, int tag) +{ + atio_private_data_t *atp; + for (atp = isp->isp_osinfo.atpdp; + atp < &isp->isp_osinfo.atpdp[ATPDPSIZE]; atp++) { + if (atp->tag == tag) + return (atp); + } + return (NULL); +} + +static cam_status +create_lun_state(ispsoftc_t *isp, int bus, + struct cam_path *path, tstate_t **rslt) +{ + cam_status status; + lun_id_t lun; + int hfx; + tstate_t *tptr, *new; + + lun = xpt_path_lun_id(path); + if (lun < 0) { + return (CAM_LUN_INVALID); + } + if (is_lun_enabled(isp, bus, lun)) { + return (CAM_LUN_ALRDY_ENA); + } + new = (tstate_t *) malloc(sizeof (tstate_t), M_DEVBUF, M_NOWAIT|M_ZERO); + if (new == NULL) { + return (CAM_RESRC_UNAVAIL); + } + + status = xpt_create_path(&new->owner, NULL, xpt_path_path_id(path), + xpt_path_target_id(path), xpt_path_lun_id(path)); + if (status != CAM_REQ_CMP) { + free(new, M_DEVBUF); + return (status); + } + new->bus = bus; + new->lun = lun; + SLIST_INIT(&new->atios); + SLIST_INIT(&new->inots); + new->hold = 1; + + hfx = LUN_HASH_FUNC(isp, new->bus, new->lun); + tptr = isp->isp_osinfo.lun_hash[hfx]; + if (tptr == NULL) { + isp->isp_osinfo.lun_hash[hfx] = new; + } else { + while (tptr->next) + tptr = tptr->next; + tptr->next = new; + } + *rslt = new; + return (CAM_REQ_CMP); +} + +static __inline void +destroy_lun_state(ispsoftc_t *isp, tstate_t *tptr) +{ + int hfx; + tstate_t *lw, *pw; + + if (tptr->hold) { + return; + } + hfx = LUN_HASH_FUNC(isp, tptr->bus, tptr->lun); + pw = isp->isp_osinfo.lun_hash[hfx]; + if (pw == NULL) { + return; + } else if (pw->lun == tptr->lun && pw->bus == tptr->bus) { + isp->isp_osinfo.lun_hash[hfx] = pw->next; + } else { + lw = pw; + pw = lw->next; + while (pw) { + if (pw->lun == tptr->lun && pw->bus == tptr->bus) { + lw->next = pw->next; + break; + } + lw = pw; + pw = pw->next; + } + if (pw == NULL) { + return; + } + } + free(tptr, M_DEVBUF); +} + +/* + * Enable luns. + */ +static int +isp_en_lun(ispsoftc_t *isp, union ccb *ccb) +{ + struct ccb_en_lun *cel = &ccb->cel; + tstate_t *tptr; + uint32_t seq; + int bus, cmd, av, wildcard, tm_on; + lun_id_t lun; + target_id_t tgt; + + bus = XS_CHANNEL(ccb); + if (bus > 1) { + xpt_print_path(ccb->ccb_h.path); + printf("illegal bus %d\n", bus); + ccb->ccb_h.status = CAM_PATH_INVALID; + return (-1); + } + tgt = ccb->ccb_h.target_id; + lun = ccb->ccb_h.target_lun; + + isp_prt(isp, ISP_LOGTDEBUG0, + "isp_en_lun: %sabling lun 0x%x on channel %d", + cel->enable? "en" : "dis", lun, bus); + + + if ((lun != CAM_LUN_WILDCARD) && + (lun < 0 || lun >= (lun_id_t) isp->isp_maxluns)) { + ccb->ccb_h.status = CAM_LUN_INVALID; + return (-1); + } + + if (IS_SCSI(isp)) { + sdparam *sdp = isp->isp_param; + sdp += bus; + if (tgt != CAM_TARGET_WILDCARD && + tgt != sdp->isp_initiator_id) { + ccb->ccb_h.status = CAM_TID_INVALID; + return (-1); + } + } else { + /* + * There's really no point in doing this yet w/o multi-tid + * capability. Even then, it's problematic. + */ +#if 0 + if (tgt != CAM_TARGET_WILDCARD && + tgt != FCPARAM(isp)->isp_iid) { + ccb->ccb_h.status = CAM_TID_INVALID; + return (-1); + } +#endif + /* + * This is as a good a place as any to check f/w capabilities. + */ + if ((FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_TMODE) == 0) { + isp_prt(isp, ISP_LOGERR, + "firmware does not support target mode"); + ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; + return (-1); + } + /* + * XXX: We *could* handle non-SCCLUN f/w, but we'd have to + * XXX: dorks with our already fragile enable/disable code. + */ + if ((FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) == 0) { + isp_prt(isp, ISP_LOGERR, + "firmware not SCCLUN capable"); + ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; + return (-1); + } + } + + if (tgt == CAM_TARGET_WILDCARD) { + if (lun == CAM_LUN_WILDCARD) { + wildcard = 1; + } else { + ccb->ccb_h.status = CAM_LUN_INVALID; + return (-1); + } + } else { + wildcard = 0; + } + + tm_on = (isp->isp_osinfo.tmflags[bus] & TM_TMODE_ENABLED) != 0; + + /* + * Next check to see whether this is a target/lun wildcard action. + * + * If so, we know that we can accept commands for luns that haven't + * been enabled yet and send them upstream. Otherwise, we have to + * handle them locally (if we see them at all). + */ + + if (wildcard) { + tptr = &isp->isp_osinfo.tsdflt[bus]; + if (cel->enable) { + if (tm_on) { + ccb->ccb_h.status = CAM_LUN_ALRDY_ENA; + return (-1); + } + ccb->ccb_h.status = + xpt_create_path(&tptr->owner, NULL, + xpt_path_path_id(ccb->ccb_h.path), + xpt_path_target_id(ccb->ccb_h.path), + xpt_path_lun_id(ccb->ccb_h.path)); + if (ccb->ccb_h.status != CAM_REQ_CMP) { + return (-1); + } + SLIST_INIT(&tptr->atios); + SLIST_INIT(&tptr->inots); + isp->isp_osinfo.tmflags[bus] |= TM_WILDCARD_ENABLED; + } else { + if (tm_on == 0) { + ccb->ccb_h.status = CAM_REQ_CMP; + return (-1); + } + if (tptr->hold) { + ccb->ccb_h.status = CAM_SCSI_BUSY; + return (-1); + } + xpt_free_path(tptr->owner); + isp->isp_osinfo.tmflags[bus] &= ~TM_WILDCARD_ENABLED; + } + } + + /* + * Now check to see whether this bus needs to be + * enabled/disabled with respect to target mode. + */ + av = bus << 31; + if (cel->enable && tm_on == 0) { + av |= ENABLE_TARGET_FLAG; + av = isp_control(isp, ISPCTL_TOGGLE_TMODE, &av); + if (av) { + ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; + if (wildcard) { + isp->isp_osinfo.tmflags[bus] &= + ~TM_WILDCARD_ENABLED; + xpt_free_path(tptr->owner); + } + return (-1); + } + isp->isp_osinfo.tmflags[bus] |= TM_TMODE_ENABLED; + isp_prt(isp, ISP_LOGINFO, + "Target Mode enabled on channel %d", bus); + } else if (cel->enable == 0 && tm_on && wildcard) { + if (are_any_luns_enabled(isp, bus)) { + ccb->ccb_h.status = CAM_SCSI_BUSY; + return (-1); + } + av = isp_control(isp, ISPCTL_TOGGLE_TMODE, &av); + if (av) { + ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; + return (-1); + } + isp->isp_osinfo.tmflags[bus] &= ~TM_TMODE_ENABLED; + isp_prt(isp, ISP_LOGINFO, + "Target Mode disabled on channel %d", bus); + } + + if (wildcard) { + ccb->ccb_h.status = CAM_REQ_CMP; + return (-1); + } + + /* + * Find an empty slot + */ + for (seq = 0; seq < NLEACT; seq++) { + if (isp->isp_osinfo.leact[seq] == 0) { + break; + } + } + if (seq >= NLEACT) { + ccb->ccb_h.status = CAM_RESRC_UNAVAIL; + return (-1); + + } + isp->isp_osinfo.leact[seq] = ccb; + + if (cel->enable) { + ccb->ccb_h.status = + create_lun_state(isp, bus, ccb->ccb_h.path, &tptr); + if (ccb->ccb_h.status != CAM_REQ_CMP) { + isp->isp_osinfo.leact[seq] = 0; + return (-1); + } + } else { + tptr = get_lun_statep(isp, bus, lun); + if (tptr == NULL) { + ccb->ccb_h.status = CAM_LUN_INVALID; + return (-1); + } + } + + if (cel->enable) { + int c, n, ulun = lun; + + cmd = RQSTYPE_ENABLE_LUN; + c = DFLT_CMND_CNT; + n = DFLT_INOT_CNT; + if (IS_FC(isp) && lun != 0) { + cmd = RQSTYPE_MODIFY_LUN; + n = 0; + /* + * For SCC firmware, we only deal with setting + * (enabling or modifying) lun 0. + */ + ulun = 0; + } + if (isp_lun_cmd(isp, cmd, bus, tgt, ulun, c, n, seq+1) == 0) { + rls_lun_statep(isp, tptr); + ccb->ccb_h.status = CAM_REQ_INPROG; + return (seq); + } + } else { + int c, n, ulun = lun; + + cmd = -RQSTYPE_MODIFY_LUN; + c = DFLT_CMND_CNT; + n = DFLT_INOT_CNT; + if (IS_FC(isp) && lun != 0) { + n = 0; + /* + * For SCC firmware, we only deal with setting + * (enabling or modifying) lun 0. + */ + ulun = 0; + } + if (isp_lun_cmd(isp, cmd, bus, tgt, ulun, c, n, seq+1) == 0) { + rls_lun_statep(isp, tptr); + ccb->ccb_h.status = CAM_REQ_INPROG; + return (seq); + } + } + rls_lun_statep(isp, tptr); + xpt_print_path(ccb->ccb_h.path); + printf("isp_lun_cmd failed\n"); + isp->isp_osinfo.leact[seq] = 0; + ccb->ccb_h.status = CAM_REQ_CMP_ERR; + return (-1); +} + +static void +isp_ledone(ispsoftc_t *isp, lun_entry_t *lep) +{ + const char lfmt[] = "lun %d now %sabled for target mode on channel %d"; + union ccb *ccb; + uint32_t seq; + tstate_t *tptr; + int av; + struct ccb_en_lun *cel; + + seq = lep->le_reserved - 1; + if (seq >= NLEACT) { + isp_prt(isp, ISP_LOGERR, + "seq out of range (%u) in isp_ledone", seq); + return; + } + ccb = isp->isp_osinfo.leact[seq]; + if (ccb == 0) { + isp_prt(isp, ISP_LOGERR, + "no ccb for seq %u in isp_ledone", seq); + return; + } + cel = &ccb->cel; + tptr = get_lun_statep(isp, XS_CHANNEL(ccb), XS_LUN(ccb)); + if (tptr == NULL) { + xpt_print_path(ccb->ccb_h.path); + printf("null tptr in isp_ledone\n"); + isp->isp_osinfo.leact[seq] = 0; + return; + } + + if (lep->le_status != LUN_OK) { + xpt_print_path(ccb->ccb_h.path); + printf("ENABLE/MODIFY LUN returned 0x%x\n", lep->le_status); +err: + ccb->ccb_h.status = CAM_REQ_CMP_ERR; + xpt_print_path(ccb->ccb_h.path); + rls_lun_statep(isp, tptr); + isp->isp_osinfo.leact[seq] = 0; + ISPLOCK_2_CAMLOCK(isp); + xpt_done(ccb); + CAMLOCK_2_ISPLOCK(isp); + return; + } else { + isp_prt(isp, ISP_LOGTDEBUG0, + "isp_ledone: ENABLE/MODIFY done okay"); + } + + + if (cel->enable) { + ccb->ccb_h.status = CAM_REQ_CMP; + isp_prt(isp, ISP_LOGINFO, lfmt, + XS_LUN(ccb), "en", XS_CHANNEL(ccb)); + rls_lun_statep(isp, tptr); + isp->isp_osinfo.leact[seq] = 0; + ISPLOCK_2_CAMLOCK(isp); + xpt_done(ccb); + CAMLOCK_2_ISPLOCK(isp); + return; + } + + if (lep->le_header.rqs_entry_type == RQSTYPE_MODIFY_LUN) { + if (isp_lun_cmd(isp, -RQSTYPE_ENABLE_LUN, XS_CHANNEL(ccb), + XS_TGT(ccb), XS_LUN(ccb), 0, 0, seq+1)) { + xpt_print_path(ccb->ccb_h.path); + printf("isp_ledone: isp_lun_cmd failed\n"); + goto err; + } + rls_lun_statep(isp, tptr); + return; + } + + isp_prt(isp, ISP_LOGINFO, lfmt, XS_LUN(ccb), "dis", XS_CHANNEL(ccb)); + rls_lun_statep(isp, tptr); + destroy_lun_state(isp, tptr); + ccb->ccb_h.status = CAM_REQ_CMP; + isp->isp_osinfo.leact[seq] = 0; + ISPLOCK_2_CAMLOCK(isp); + xpt_done(ccb); + CAMLOCK_2_ISPLOCK(isp); + if (are_any_luns_enabled(isp, XS_CHANNEL(ccb)) == 0) { + int bus = XS_CHANNEL(ccb); + av = bus << 31; + av = isp_control(isp, ISPCTL_TOGGLE_TMODE, &av); + if (av) { + isp_prt(isp, ISP_LOGWARN, + "disable target mode on channel %d failed", bus); + } else { + isp_prt(isp, ISP_LOGINFO, + "Target Mode disabled on channel %d", bus); + } + isp->isp_osinfo.tmflags[bus] &= ~TM_TMODE_ENABLED; + } +} + + +static cam_status +isp_abort_tgt_ccb(ispsoftc_t *isp, union ccb *ccb) +{ + tstate_t *tptr; + struct ccb_hdr_slist *lp; + struct ccb_hdr *curelm; + int found, *ctr; + union ccb *accb = ccb->cab.abort_ccb; + + isp_prt(isp, ISP_LOGTDEBUG0, "aborting ccb %p", accb); + if (accb->ccb_h.target_id != CAM_TARGET_WILDCARD) { + int badpath = 0; + if (IS_FC(isp) && (accb->ccb_h.target_id != + ((fcparam *) isp->isp_param)->isp_loopid)) { + badpath = 1; + } else if (IS_SCSI(isp) && (accb->ccb_h.target_id != + ((sdparam *) isp->isp_param)->isp_initiator_id)) { + badpath = 1; + } + if (badpath) { + /* + * Being restrictive about target ids is really about + * making sure we're aborting for the right multi-tid + * path. This doesn't really make much sense at present. + */ +#if 0 + return (CAM_PATH_INVALID); +#endif + } + } + tptr = get_lun_statep(isp, XS_CHANNEL(ccb), accb->ccb_h.target_lun); + if (tptr == NULL) { + isp_prt(isp, ISP_LOGTDEBUG0, + "isp_abort_tgt_ccb: can't get statep"); + return (CAM_PATH_INVALID); + } + if (accb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO) { + lp = &tptr->atios; + ctr = &tptr->atio_count; + } else if (accb->ccb_h.func_code == XPT_IMMED_NOTIFY) { + lp = &tptr->inots; + ctr = &tptr->inot_count; + } else { + rls_lun_statep(isp, tptr); + isp_prt(isp, ISP_LOGTDEBUG0, + "isp_abort_tgt_ccb: bad func %d\n", accb->ccb_h.func_code); + return (CAM_UA_ABORT); + } + curelm = SLIST_FIRST(lp); + found = 0; + if (curelm == &accb->ccb_h) { + found = 1; + SLIST_REMOVE_HEAD(lp, sim_links.sle); + } else { + while(curelm != NULL) { + struct ccb_hdr *nextelm; + + nextelm = SLIST_NEXT(curelm, sim_links.sle); + if (nextelm == &accb->ccb_h) { + found = 1; + SLIST_NEXT(curelm, sim_links.sle) = + SLIST_NEXT(nextelm, sim_links.sle); + break; + } + curelm = nextelm; + } + } + rls_lun_statep(isp, tptr); + if (found) { + (*ctr)--; + accb->ccb_h.status = CAM_REQ_ABORTED; + xpt_done(accb); + return (CAM_REQ_CMP); + } + isp_prt(isp, ISP_LOGTDEBUG0, + "isp_abort_tgt_ccb: CCB %p not found\n", ccb); + return (CAM_PATH_INVALID); +} + +static void +isp_target_start_ctio(ispsoftc_t *isp, union ccb *ccb) +{ + void *qe; + struct ccb_scsiio *cso = &ccb->csio; + uint16_t *hp, save_handle; + uint16_t nxti, optr; + uint8_t local[QENTRY_LEN]; + + + if (isp_getrqentry(isp, &nxti, &optr, &qe)) { + xpt_print_path(ccb->ccb_h.path); + printf("Request Queue Overflow in isp_target_start_ctio\n"); + XS_SETERR(ccb, CAM_REQUEUE_REQ); + goto out; + } + memset(local, 0, QENTRY_LEN); + + /* + * We're either moving data or completing a command here. + */ + + if (IS_FC(isp)) { + atio_private_data_t *atp; + ct2_entry_t *cto = (ct2_entry_t *) local; + + cto->ct_header.rqs_entry_type = RQSTYPE_CTIO2; + cto->ct_header.rqs_entry_count = 1; + if (IS_2KLOGIN(isp)) { + ((ct2e_entry_t *)cto)->ct_iid = cso->init_id; + } else { + cto->ct_iid = cso->init_id; + if (!(FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN)) { + cto->ct_lun = ccb->ccb_h.target_lun; + } + } + + atp = isp_get_atpd(isp, cso->tag_id); + if (atp == NULL) { + isp_prt(isp, ISP_LOGERR, + "cannot find private data adjunct for tag %x", + cso->tag_id); + XS_SETERR(ccb, CAM_REQ_CMP_ERR); + goto out; + } + + cto->ct_rxid = cso->tag_id; + if (cso->dxfer_len == 0) { + cto->ct_flags |= CT2_FLAG_MODE1 | CT2_NO_DATA; + if (ccb->ccb_h.flags & CAM_SEND_STATUS) { + cto->ct_flags |= CT2_SENDSTATUS; + cto->rsp.m1.ct_scsi_status = cso->scsi_status; + cto->ct_resid = + atp->orig_datalen - atp->bytes_xfered; + if (cto->ct_resid < 0) { + cto->rsp.m1.ct_scsi_status |= + CT2_DATA_OVER; + } else if (cto->ct_resid > 0) { + cto->rsp.m1.ct_scsi_status |= + CT2_DATA_UNDER; + } + } + if ((ccb->ccb_h.flags & CAM_SEND_SENSE) != 0) { + int m = min(cso->sense_len, MAXRESPLEN); + memcpy(cto->rsp.m1.ct_resp, + &cso->sense_data, m); + cto->rsp.m1.ct_senselen = m; + cto->rsp.m1.ct_scsi_status |= CT2_SNSLEN_VALID; + } + } else { + cto->ct_flags |= CT2_FLAG_MODE0; + if ((cso->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { + cto->ct_flags |= CT2_DATA_IN; + } else { + cto->ct_flags |= CT2_DATA_OUT; + } + cto->ct_reloff = atp->bytes_xfered; + if ((ccb->ccb_h.flags & CAM_SEND_STATUS) != 0) { + cto->ct_flags |= CT2_SENDSTATUS; + cto->rsp.m0.ct_scsi_status = cso->scsi_status; + cto->ct_resid = + atp->orig_datalen - + (atp->bytes_xfered + cso->dxfer_len); + if (cto->ct_resid < 0) { + cto->rsp.m0.ct_scsi_status |= + CT2_DATA_OVER; + } else if (cto->ct_resid > 0) { + cto->rsp.m0.ct_scsi_status |= + CT2_DATA_UNDER; + } + } else { + atp->last_xframt = cso->dxfer_len; + } + /* + * If we're sending data and status back together, + * we can't also send back sense data as well. + */ + ccb->ccb_h.flags &= ~CAM_SEND_SENSE; + } + + if (cto->ct_flags & CT2_SENDSTATUS) { + isp_prt(isp, ISP_LOGTDEBUG0, + "CTIO2[%x] STATUS %x origd %u curd %u resid %u", + cto->ct_rxid, cso->scsi_status, atp->orig_datalen, + cso->dxfer_len, cto->ct_resid); + cto->ct_flags |= CT2_CCINCR; + atp->state = ATPD_STATE_LAST_CTIO; + } else { + atp->state = ATPD_STATE_CTIO; + } + cto->ct_timeout = 10; + hp = &cto->ct_syshandle; + } else { + ct_entry_t *cto = (ct_entry_t *) local; + + cto->ct_header.rqs_entry_type = RQSTYPE_CTIO; + cto->ct_header.rqs_entry_count = 1; + cto->ct_iid = cso->init_id; + cto->ct_iid |= XS_CHANNEL(ccb) << 7; + cto->ct_tgt = ccb->ccb_h.target_id; + cto->ct_lun = ccb->ccb_h.target_lun; + cto->ct_fwhandle = AT_GET_HANDLE(cso->tag_id); + if (AT_HAS_TAG(cso->tag_id)) { + cto->ct_tag_val = (uint8_t) AT_GET_TAG(cso->tag_id); + cto->ct_flags |= CT_TQAE; + } + if (ccb->ccb_h.flags & CAM_DIS_DISCONNECT) { + cto->ct_flags |= CT_NODISC; + } + if (cso->dxfer_len == 0) { + cto->ct_flags |= CT_NO_DATA; + } else if ((cso->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { + cto->ct_flags |= CT_DATA_IN; + } else { + cto->ct_flags |= CT_DATA_OUT; + } + if (ccb->ccb_h.flags & CAM_SEND_STATUS) { + cto->ct_flags |= CT_SENDSTATUS|CT_CCINCR; + cto->ct_scsi_status = cso->scsi_status; + cto->ct_resid = cso->resid; + isp_prt(isp, ISP_LOGTDEBUG0, + "CTIO[%x] SCSI STATUS 0x%x resid %d tag_id %x", + cto->ct_fwhandle, cso->scsi_status, cso->resid, + cso->tag_id); + } + ccb->ccb_h.flags &= ~CAM_SEND_SENSE; + cto->ct_timeout = 10; + hp = &cto->ct_syshandle; + } + + if (isp_save_xs_tgt(isp, ccb, hp)) { + xpt_print_path(ccb->ccb_h.path); + printf("No XFLIST pointers for isp_target_start_ctio\n"); + XS_SETERR(ccb, CAM_REQUEUE_REQ); + goto out; + } + + + /* + * Call the dma setup routines for this entry (and any subsequent + * CTIOs) if there's data to move, and then tell the f/w it's got + * new things to play with. As with isp_start's usage of DMA setup, + * any swizzling is done in the machine dependent layer. Because + * of this, we put the request onto the queue area first in native + * format. + */ + + save_handle = *hp; + + switch (ISP_DMASETUP(isp, cso, (ispreq_t *) local, &nxti, optr)) { + case CMD_QUEUED: + ISP_ADD_REQUEST(isp, nxti); + ccb->ccb_h.status |= CAM_SIM_QUEUED; + return; + + case CMD_EAGAIN: + XS_SETERR(ccb, CAM_REQUEUE_REQ); + break; + + default: + break; + } + isp_destroy_tgt_handle(isp, save_handle); + +out: + ISPLOCK_2_CAMLOCK(isp); + xpt_done(ccb); + CAMLOCK_2_ISPLOCK(isp); +} + +static void +isp_refire_putback_atio(void *arg) +{ + int s = splcam(); + isp_target_putback_atio(arg); + splx(s); +} + +static void +isp_target_putback_atio(union ccb *ccb) +{ + ispsoftc_t *isp; + struct ccb_scsiio *cso; + uint16_t nxti, optr; + void *qe; + + isp = XS_ISP(ccb); + + if (isp_getrqentry(isp, &nxti, &optr, &qe)) { + (void) timeout(isp_refire_putback_atio, ccb, 10); + isp_prt(isp, ISP_LOGWARN, + "isp_target_putback_atio: Request Queue Overflow"); + return; + } + memset(qe, 0, QENTRY_LEN); + cso = &ccb->csio; + if (IS_FC(isp)) { + at2_entry_t local, *at = &local; + MEMZERO(at, sizeof (at2_entry_t)); + at->at_header.rqs_entry_type = RQSTYPE_ATIO2; + at->at_header.rqs_entry_count = 1; + if ((FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) != 0) { + at->at_scclun = (uint16_t) ccb->ccb_h.target_lun; + } else { + at->at_lun = (uint8_t) ccb->ccb_h.target_lun; + } + at->at_status = CT_OK; + at->at_rxid = cso->tag_id; + at->at_iid = cso->ccb_h.target_id; + isp_put_atio2(isp, at, qe); + } else { + at_entry_t local, *at = &local; + MEMZERO(at, sizeof (at_entry_t)); + at->at_header.rqs_entry_type = RQSTYPE_ATIO; + at->at_header.rqs_entry_count = 1; + at->at_iid = cso->init_id; + at->at_iid |= XS_CHANNEL(ccb) << 7; + at->at_tgt = cso->ccb_h.target_id; + at->at_lun = cso->ccb_h.target_lun; + at->at_status = CT_OK; + at->at_tag_val = AT_GET_TAG(cso->tag_id); + at->at_handle = AT_GET_HANDLE(cso->tag_id); + isp_put_atio(isp, at, qe); + } + ISP_TDQE(isp, "isp_target_putback_atio", (int) optr, qe); + ISP_ADD_REQUEST(isp, nxti); + isp_complete_ctio(ccb); +} + +static void +isp_complete_ctio(union ccb *ccb) +{ + ISPLOCK_2_CAMLOCK(isp); + if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG) { + ccb->ccb_h.status |= CAM_REQ_CMP; + } + ccb->ccb_h.status &= ~CAM_SIM_QUEUED; + xpt_done(ccb); + CAMLOCK_2_ISPLOCK(isp); +} + +/* + * Handle ATIO stuff that the generic code can't. + * This means handling CDBs. + */ + +static int +isp_handle_platform_atio(ispsoftc_t *isp, at_entry_t *aep) +{ + tstate_t *tptr; + int status, bus, iswildcard; + struct ccb_accept_tio *atiop; + + /* + * The firmware status (except for the QLTM_SVALID bit) + * indicates why this ATIO was sent to us. + * + * If QLTM_SVALID is set, the firware has recommended Sense Data. + * + * If the DISCONNECTS DISABLED bit is set in the flags field, + * we're still connected on the SCSI bus. + */ + status = aep->at_status; + if ((status & ~QLTM_SVALID) == AT_PHASE_ERROR) { + /* + * Bus Phase Sequence error. We should have sense data + * suggested by the f/w. I'm not sure quite yet what + * to do about this for CAM. + */ + isp_prt(isp, ISP_LOGWARN, "PHASE ERROR"); + isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); + return (0); + } + if ((status & ~QLTM_SVALID) != AT_CDB) { + isp_prt(isp, ISP_LOGWARN, "bad atio (0x%x) leaked to platform", + status); + isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); + return (0); + } + + bus = GET_BUS_VAL(aep->at_iid); + tptr = get_lun_statep(isp, bus, aep->at_lun); + if (tptr == NULL) { + tptr = get_lun_statep(isp, bus, CAM_LUN_WILDCARD); + if (tptr == NULL) { + /* + * Because we can't autofeed sense data back with + * a command for parallel SCSI, we can't give back + * a CHECK CONDITION. We'll give back a BUSY status + * instead. This works out okay because the only + * time we should, in fact, get this, is in the + * case that somebody configured us without the + * blackhole driver, so they get what they deserve. + */ + isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); + return (0); + } + iswildcard = 1; + } else { + iswildcard = 0; + } + + atiop = (struct ccb_accept_tio *) SLIST_FIRST(&tptr->atios); + if (atiop == NULL) { + /* + * Because we can't autofeed sense data back with + * a command for parallel SCSI, we can't give back + * a CHECK CONDITION. We'll give back a QUEUE FULL status + * instead. This works out okay because the only time we + * should, in fact, get this, is in the case that we've + * run out of ATIOS. + */ + xpt_print_path(tptr->owner); + isp_prt(isp, ISP_LOGWARN, + "no ATIOS for lun %d from initiator %d on channel %d", + aep->at_lun, GET_IID_VAL(aep->at_iid), bus); + if (aep->at_flags & AT_TQAE) + isp_endcmd(isp, aep, SCSI_STATUS_QUEUE_FULL, 0); + else + isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); + rls_lun_statep(isp, tptr); + return (0); + } + SLIST_REMOVE_HEAD(&tptr->atios, sim_links.sle); + tptr->atio_count--; + isp_prt(isp, ISP_LOGTDEBUG0, "Take FREE ATIO lun %d, count now %d", + aep->at_lun, tptr->atio_count); + if (iswildcard) { + atiop->ccb_h.target_id = aep->at_tgt; + atiop->ccb_h.target_lun = aep->at_lun; + } + if (aep->at_flags & AT_NODISC) { + atiop->ccb_h.flags = CAM_DIS_DISCONNECT; + } else { + atiop->ccb_h.flags = 0; + } + + if (status & QLTM_SVALID) { + size_t amt = imin(QLTM_SENSELEN, sizeof (atiop->sense_data)); + atiop->sense_len = amt; + MEMCPY(&atiop->sense_data, aep->at_sense, amt); + } else { + atiop->sense_len = 0; + } + + atiop->init_id = GET_IID_VAL(aep->at_iid); + atiop->cdb_len = aep->at_cdblen; + MEMCPY(atiop->cdb_io.cdb_bytes, aep->at_cdb, aep->at_cdblen); + atiop->ccb_h.status = CAM_CDB_RECVD; + /* + * Construct a tag 'id' based upon tag value (which may be 0..255) + * and the handle (which we have to preserve). + */ + AT_MAKE_TAGID(atiop->tag_id, device_get_unit(isp->isp_dev), aep); + if (aep->at_flags & AT_TQAE) { + atiop->tag_action = aep->at_tag_type; + atiop->ccb_h.status |= CAM_TAG_ACTION_VALID; + } + xpt_done((union ccb*)atiop); + isp_prt(isp, ISP_LOGTDEBUG0, + "ATIO[%x] CDB=0x%x bus %d iid%d->lun%d tag 0x%x ttype 0x%x %s", + aep->at_handle, aep->at_cdb[0] & 0xff, GET_BUS_VAL(aep->at_iid), + GET_IID_VAL(aep->at_iid), aep->at_lun, aep->at_tag_val & 0xff, + aep->at_tag_type, (aep->at_flags & AT_NODISC)? + "nondisc" : "disconnecting"); + rls_lun_statep(isp, tptr); + return (0); +} + +static int +isp_handle_platform_atio2(ispsoftc_t *isp, at2_entry_t *aep) +{ + lun_id_t lun; + tstate_t *tptr; + struct ccb_accept_tio *atiop; + atio_private_data_t *atp; + + /* + * The firmware status (except for the QLTM_SVALID bit) + * indicates why this ATIO was sent to us. + * + * If QLTM_SVALID is set, the firware has recommended Sense Data. + */ + if ((aep->at_status & ~QLTM_SVALID) != AT_CDB) { + isp_prt(isp, ISP_LOGWARN, + "bogus atio (0x%x) leaked to platform", aep->at_status); + isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); + return (0); + } + + if ((FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) != 0) { + lun = aep->at_scclun; + } else { + lun = aep->at_lun; + } + tptr = get_lun_statep(isp, 0, lun); + if (tptr == NULL) { + isp_prt(isp, ISP_LOGTDEBUG0, + "[0x%x] no state pointer for lun %d", aep->at_rxid, lun); + tptr = get_lun_statep(isp, 0, CAM_LUN_WILDCARD); + if (tptr == NULL) { + isp_endcmd(isp, aep, + SCSI_STATUS_CHECK_COND | ECMD_SVALID | + (0x5 << 12) | (0x25 << 16), 0); + return (0); + } + } + + atp = isp_get_atpd(isp, 0); + atiop = (struct ccb_accept_tio *) SLIST_FIRST(&tptr->atios); + if (atiop == NULL || atp == NULL) { + + /* + * Because we can't autofeed sense data back with + * a command for parallel SCSI, we can't give back + * a CHECK CONDITION. We'll give back a QUEUE FULL status + * instead. This works out okay because the only time we + * should, in fact, get this, is in the case that we've + * run out of ATIOS. + */ + xpt_print_path(tptr->owner); + isp_prt(isp, ISP_LOGWARN, + "no %s for lun %d from initiator %d", + (atp == NULL && atiop == NULL)? "ATIO2s *or* ATPS" : + ((atp == NULL)? "ATPs" : "ATIO2s"), lun, aep->at_iid); + rls_lun_statep(isp, tptr); + isp_endcmd(isp, aep, SCSI_STATUS_QUEUE_FULL, 0); + return (0); + } + atp->state = ATPD_STATE_ATIO; + SLIST_REMOVE_HEAD(&tptr->atios, sim_links.sle); + tptr->atio_count--; + isp_prt(isp, ISP_LOGTDEBUG0, "Take FREE ATIO lun %d, count now %d", + lun, tptr->atio_count); + + if (tptr == &isp->isp_osinfo.tsdflt[0]) { + atiop->ccb_h.target_id = + ((fcparam *)isp->isp_param)->isp_loopid; + atiop->ccb_h.target_lun = lun; + } + /* + * We don't get 'suggested' sense data as we do with SCSI cards. + */ + atiop->sense_len = 0; + + atiop->init_id = aep->at_iid; + atiop->cdb_len = ATIO2_CDBLEN; + MEMCPY(atiop->cdb_io.cdb_bytes, aep->at_cdb, ATIO2_CDBLEN); + atiop->ccb_h.status = CAM_CDB_RECVD; + atiop->tag_id = aep->at_rxid; + switch (aep->at_taskflags & ATIO2_TC_ATTR_MASK) { + case ATIO2_TC_ATTR_SIMPLEQ: + atiop->tag_action = MSG_SIMPLE_Q_TAG; + break; + case ATIO2_TC_ATTR_HEADOFQ: + atiop->tag_action = MSG_HEAD_OF_Q_TAG; + break; + case ATIO2_TC_ATTR_ORDERED: + atiop->tag_action = MSG_ORDERED_Q_TAG; + break; + case ATIO2_TC_ATTR_ACAQ: /* ?? */ + case ATIO2_TC_ATTR_UNTAGGED: + default: + atiop->tag_action = 0; + break; + } + atiop->ccb_h.flags = CAM_TAG_ACTION_VALID; + + atp->tag = atiop->tag_id; + atp->lun = lun; + atp->orig_datalen = aep->at_datalen; + atp->last_xframt = 0; + atp->bytes_xfered = 0; + atp->state = ATPD_STATE_CAM; + ISPLOCK_2_CAMLOCK(siP); + xpt_done((union ccb*)atiop); + + isp_prt(isp, ISP_LOGTDEBUG0, + "ATIO2[%x] CDB=0x%x iid%d->lun%d tattr 0x%x datalen %u", + aep->at_rxid, aep->at_cdb[0] & 0xff, aep->at_iid, + lun, aep->at_taskflags, aep->at_datalen); + rls_lun_statep(isp, tptr); + return (0); +} + +static int +isp_handle_platform_ctio(ispsoftc_t *isp, void *arg) +{ + union ccb *ccb; + int sentstatus, ok, notify_cam, resid = 0; + uint16_t tval; + + /* + * CTIO and CTIO2 are close enough.... + */ + + ccb = isp_find_xs_tgt(isp, ((ct_entry_t *)arg)->ct_syshandle); + KASSERT((ccb != NULL), ("null ccb in isp_handle_platform_ctio")); + isp_destroy_tgt_handle(isp, ((ct_entry_t *)arg)->ct_syshandle); + + if (IS_FC(isp)) { + ct2_entry_t *ct = arg; + atio_private_data_t *atp = isp_get_atpd(isp, ct->ct_rxid); + if (atp == NULL) { + isp_prt(isp, ISP_LOGERR, + "cannot find adjunct for %x after I/O", + ct->ct_rxid); + return (0); + } + sentstatus = ct->ct_flags & CT2_SENDSTATUS; + ok = (ct->ct_status & ~QLTM_SVALID) == CT_OK; + if (ok && sentstatus && (ccb->ccb_h.flags & CAM_SEND_SENSE)) { + ccb->ccb_h.status |= CAM_SENT_SENSE; + } + notify_cam = ct->ct_header.rqs_seqno & 0x1; + if ((ct->ct_flags & CT2_DATAMASK) != CT2_NO_DATA) { + resid = ct->ct_resid; + atp->bytes_xfered += (atp->last_xframt - resid); + atp->last_xframt = 0; + } + if (sentstatus || !ok) { + atp->tag = 0; + } + isp_prt(isp, ok? ISP_LOGTDEBUG0 : ISP_LOGWARN, + "CTIO2[%x] sts 0x%x flg 0x%x sns %d resid %d %s", + ct->ct_rxid, ct->ct_status, ct->ct_flags, + (ccb->ccb_h.status & CAM_SENT_SENSE) != 0, + resid, sentstatus? "FIN" : "MID"); + tval = ct->ct_rxid; + + /* XXX: should really come after isp_complete_ctio */ + atp->state = ATPD_STATE_PDON; + } else { + ct_entry_t *ct = arg; + sentstatus = ct->ct_flags & CT_SENDSTATUS; + ok = (ct->ct_status & ~QLTM_SVALID) == CT_OK; + /* + * We *ought* to be able to get back to the original ATIO + * here, but for some reason this gets lost. It's just as + * well because it's squirrelled away as part of periph + * private data. + * + * We can live without it as long as we continue to use + * the auto-replenish feature for CTIOs. + */ + notify_cam = ct->ct_header.rqs_seqno & 0x1; + if (ct->ct_status & QLTM_SVALID) { + char *sp = (char *)ct; + sp += CTIO_SENSE_OFFSET; + ccb->csio.sense_len = + min(sizeof (ccb->csio.sense_data), QLTM_SENSELEN); + MEMCPY(&ccb->csio.sense_data, sp, ccb->csio.sense_len); + ccb->ccb_h.status |= CAM_AUTOSNS_VALID; + } + if ((ct->ct_flags & CT_DATAMASK) != CT_NO_DATA) { + resid = ct->ct_resid; + } + isp_prt(isp, ISP_LOGTDEBUG0, + "CTIO[%x] tag %x iid %d lun %d sts %x flg %x resid %d %s", + ct->ct_fwhandle, ct->ct_tag_val, ct->ct_iid, ct->ct_lun, + ct->ct_status, ct->ct_flags, resid, + sentstatus? "FIN" : "MID"); + tval = ct->ct_fwhandle; + } + ccb->csio.resid += resid; + + /* + * We're here either because intermediate data transfers are done + * and/or the final status CTIO (which may have joined with a + * Data Transfer) is done. + * + * In any case, for this platform, the upper layers figure out + * what to do next, so all we do here is collect status and + * pass information along. Any DMA handles have already been + * freed. + */ + if (notify_cam == 0) { + isp_prt(isp, ISP_LOGTDEBUG0, " INTER CTIO[0x%x] done", tval); + return (0); + } + + isp_prt(isp, ISP_LOGTDEBUG0, "%s CTIO[0x%x] done", + (sentstatus)? " FINAL " : "MIDTERM ", tval); + + if (!ok) { + isp_target_putback_atio(ccb); + } else { + isp_complete_ctio(ccb); + + } + return (0); +} + +static int +isp_handle_platform_notify_scsi(ispsoftc_t *isp, in_entry_t *inp) +{ + return (0); /* XXXX */ +} + +static int +isp_handle_platform_notify_fc(ispsoftc_t *isp, in_fcentry_t *inp) +{ + + switch (inp->in_status) { + case IN_PORT_LOGOUT: + isp_prt(isp, ISP_LOGWARN, "port logout of iid %d", + inp->in_iid); + break; + case IN_PORT_CHANGED: + isp_prt(isp, ISP_LOGWARN, "port changed for iid %d", + inp->in_iid); + break; + case IN_GLOBAL_LOGO: + isp_prt(isp, ISP_LOGINFO, "all ports logged out"); + break; + case IN_ABORT_TASK: + { + atio_private_data_t *atp = isp_get_atpd(isp, inp->in_seqid); + struct ccb_immed_notify *inot = NULL; + + if (atp) { + tstate_t *tptr = get_lun_statep(isp, 0, atp->lun); + if (tptr) { + inot = (struct ccb_immed_notify *) + SLIST_FIRST(&tptr->inots); + if (inot) { + tptr->inot_count--; + SLIST_REMOVE_HEAD(&tptr->inots, + sim_links.sle); + isp_prt(isp, ISP_LOGTDEBUG0, + "Take FREE INOT count now %d", + tptr->inot_count); + } + } + isp_prt(isp, ISP_LOGWARN, + "abort task RX_ID %x IID %d state %d", + inp->in_seqid, inp->in_iid, atp->state); + } else { + isp_prt(isp, ISP_LOGWARN, + "abort task RX_ID %x from iid %d, state unknown", + inp->in_seqid, inp->in_iid); + } + if (inot) { + inot->initiator_id = inp->in_iid; + inot->sense_len = 0; + inot->message_args[0] = MSG_ABORT_TAG; + inot->message_args[1] = inp->in_seqid & 0xff; + inot->message_args[2] = (inp->in_seqid >> 8) & 0xff; + inot->ccb_h.status = CAM_MESSAGE_RECV; + xpt_done((union ccb *)inot); + } + break; + } + default: + break; + } + return (0); +} +#endif + +static void +isp_cam_async(void *cbarg, uint32_t code, struct cam_path *path, void *arg) +{ + struct cam_sim *sim; + ispsoftc_t *isp; + + sim = (struct cam_sim *)cbarg; + isp = (ispsoftc_t *) cam_sim_softc(sim); + switch (code) { + case AC_LOST_DEVICE: + if (IS_SCSI(isp)) { + uint16_t oflags, nflags; + sdparam *sdp = isp->isp_param; + int tgt; + + tgt = xpt_path_target_id(path); + if (tgt >= 0) { + sdp += cam_sim_bus(sim); + ISP_LOCK(isp); + nflags = sdp->isp_devparam[tgt].nvrm_flags; +#ifndef ISP_TARGET_MODE + nflags &= DPARM_SAFE_DFLT; + if (isp->isp_loaded_fw) { + nflags |= DPARM_NARROW | DPARM_ASYNC; + } +#else + nflags = DPARM_DEFAULT; +#endif + oflags = sdp->isp_devparam[tgt].goal_flags; + sdp->isp_devparam[tgt].goal_flags = nflags; + sdp->isp_devparam[tgt].dev_update = 1; + isp->isp_update |= (1 << cam_sim_bus(sim)); + (void) isp_control(isp, + ISPCTL_UPDATE_PARAMS, NULL); + sdp->isp_devparam[tgt].goal_flags = oflags; + ISP_UNLOCK(isp); + } + } + break; + default: + isp_prt(isp, ISP_LOGWARN, "isp_cam_async: Code 0x%x", code); + break; + } +} + +static void +isp_poll(struct cam_sim *sim) +{ + ispsoftc_t *isp = cam_sim_softc(sim); + uint16_t isr, sema, mbox; + + ISP_LOCK(isp); + if (ISP_READ_ISR(isp, &isr, &sema, &mbox)) { + isp_intr(isp, isr, sema, mbox); + } + ISP_UNLOCK(isp); +} + + +static void +isp_watchdog(void *arg) +{ + XS_T *xs = arg; + ispsoftc_t *isp = XS_ISP(xs); + uint32_t handle; + int iok; + + /* + * We've decided this command is dead. Make sure we're not trying + * to kill a command that's already dead by getting it's handle and + * and seeing whether it's still alive. + */ + ISP_LOCK(isp); + iok = isp->isp_osinfo.intsok; + isp->isp_osinfo.intsok = 0; + handle = isp_find_handle(isp, xs); + if (handle) { + uint16_t isr, sema, mbox; + + if (XS_CMD_DONE_P(xs)) { + isp_prt(isp, ISP_LOGDEBUG1, + "watchdog found done cmd (handle 0x%x)", handle); + ISP_UNLOCK(isp); + return; + } + + if (XS_CMD_WDOG_P(xs)) { + isp_prt(isp, ISP_LOGDEBUG2, + "recursive watchdog (handle 0x%x)", handle); + ISP_UNLOCK(isp); + return; + } + + XS_CMD_S_WDOG(xs); + if (ISP_READ_ISR(isp, &isr, &sema, &mbox)) { + isp_intr(isp, isr, sema, mbox); + } + if (XS_CMD_DONE_P(xs)) { + isp_prt(isp, ISP_LOGDEBUG2, + "watchdog cleanup for handle 0x%x", handle); + xpt_done((union ccb *) xs); + } else if (XS_CMD_GRACE_P(xs)) { + /* + * Make sure the command is *really* dead before we + * release the handle (and DMA resources) for reuse. + */ + (void) isp_control(isp, ISPCTL_ABORT_CMD, arg); + + /* + * After this point, the comamnd is really dead. + */ + if (XS_XFRLEN(xs)) { + ISP_DMAFREE(isp, xs, handle); + } + isp_destroy_handle(isp, handle); + xpt_print_path(xs->ccb_h.path); + isp_prt(isp, ISP_LOGWARN, + "watchdog timeout for handle 0x%x", handle); + XS_SETERR(xs, CAM_CMD_TIMEOUT); + XS_CMD_C_WDOG(xs); + isp_done(xs); + } else { + uint16_t nxti, optr; + ispreq_t local, *mp= &local, *qe; + + XS_CMD_C_WDOG(xs); + xs->ccb_h.timeout_ch = timeout(isp_watchdog, xs, hz); + if (isp_getrqentry(isp, &nxti, &optr, (void **) &qe)) { + ISP_UNLOCK(isp); + return; + } + XS_CMD_S_GRACE(xs); + MEMZERO((void *) mp, sizeof (*mp)); + mp->req_header.rqs_entry_count = 1; + mp->req_header.rqs_entry_type = RQSTYPE_MARKER; + mp->req_modifier = SYNC_ALL; + mp->req_target = XS_CHANNEL(xs) << 7; + isp_put_request(isp, mp, qe); + ISP_ADD_REQUEST(isp, nxti); + } + } else { + isp_prt(isp, ISP_LOGDEBUG2, "watchdog with no command"); + } + isp->isp_osinfo.intsok = iok; + ISP_UNLOCK(isp); +} + +static void +isp_kthread(void *arg) +{ + ispsoftc_t *isp = arg; + + +#if __FreeBSD_version < 500000 + int s; + + s = splcam(); + isp->isp_osinfo.intsok = 1; +#else +#ifdef ISP_SMPLOCK + mtx_lock(&isp->isp_lock); +#else + mtx_lock(&Giant); +#endif +#endif + /* + * The first loop is for our usage where we have yet to have + * gotten good fibre channel state. + */ + for (;;) { + int wasfrozen; + + isp_prt(isp, ISP_LOGDEBUG0, "kthread: checking FC state"); + while (isp_fc_runstate(isp, 2 * 1000000) != 0) { + isp_prt(isp, ISP_LOGDEBUG0, "kthread: FC state ungood"); + if (FCPARAM(isp)->isp_fwstate != FW_READY || + FCPARAM(isp)->isp_loopstate < LOOP_PDB_RCVD) { + if (FCPARAM(isp)->loop_seen_once == 0 || + isp->isp_osinfo.ktmature == 0) { + break; + } + } +#ifdef ISP_SMPLOCK + msleep(isp_kthread, &isp->isp_lock, + PRIBIO, "isp_fcthrd", hz); +#else + (void) tsleep(isp_kthread, PRIBIO, "isp_fcthrd", hz); +#endif + } + + /* + * Even if we didn't get good loop state we may be + * unfreezing the SIMQ so that we can kill off + * commands (if we've never seen loop before, for example). + */ + isp->isp_osinfo.ktmature = 1; + wasfrozen = isp->isp_osinfo.simqfrozen & SIMQFRZ_LOOPDOWN; + isp->isp_osinfo.simqfrozen &= ~SIMQFRZ_LOOPDOWN; + if (wasfrozen && isp->isp_osinfo.simqfrozen == 0) { + isp_prt(isp, ISP_LOGDEBUG0, "kthread: releasing simq"); + ISPLOCK_2_CAMLOCK(isp); + xpt_release_simq(isp->isp_sim, 1); + CAMLOCK_2_ISPLOCK(isp); + } + isp_prt(isp, ISP_LOGDEBUG0, "kthread: waiting until called"); +#if __FreeBSD_version < 500000 + tsleep(&isp->isp_osinfo.kproc, PRIBIO, "isp_fc_worker", 0); +#else +#ifdef ISP_SMPLOCK + cv_wait(&isp->isp_osinfo.kthread_cv, &isp->isp_lock); +#else + (void) tsleep(&isp->isp_osinfo.kthread_cv, PRIBIO, "fc_cv", 0); +#endif +#endif + } +} + +static void +isp_action(struct cam_sim *sim, union ccb *ccb) +{ + int bus, tgt, error; + ispsoftc_t *isp; + struct ccb_trans_settings *cts; + + CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("isp_action\n")); + + isp = (ispsoftc_t *)cam_sim_softc(sim); + ccb->ccb_h.sim_priv.entries[0].field = 0; + ccb->ccb_h.sim_priv.entries[1].ptr = isp; + if (isp->isp_state != ISP_RUNSTATE && + ccb->ccb_h.func_code == XPT_SCSI_IO) { + CAMLOCK_2_ISPLOCK(isp); + isp_init(isp); + if (isp->isp_state != ISP_INITSTATE) { + ISP_UNLOCK(isp); + /* + * Lie. Say it was a selection timeout. + */ + ccb->ccb_h.status = CAM_SEL_TIMEOUT | CAM_DEV_QFRZN; + xpt_freeze_devq(ccb->ccb_h.path, 1); + xpt_done(ccb); + return; + } + isp->isp_state = ISP_RUNSTATE; + ISPLOCK_2_CAMLOCK(isp); + } + isp_prt(isp, ISP_LOGDEBUG2, "isp_action code %x", ccb->ccb_h.func_code); + + + switch (ccb->ccb_h.func_code) { + case XPT_SCSI_IO: /* Execute the requested I/O operation */ + /* + * Do a couple of preliminary checks... + */ + if ((ccb->ccb_h.flags & CAM_CDB_POINTER) != 0) { + if ((ccb->ccb_h.flags & CAM_CDB_PHYS) != 0) { + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + } + } +#ifdef DIAGNOSTIC + if (ccb->ccb_h.target_id > (ISP_MAX_TARGETS(isp) - 1)) { + ccb->ccb_h.status = CAM_PATH_INVALID; + } else if (ccb->ccb_h.target_lun > (ISP_MAX_LUNS(isp) - 1)) { + ccb->ccb_h.status = CAM_PATH_INVALID; + } + if (ccb->ccb_h.status == CAM_PATH_INVALID) { + isp_prt(isp, ISP_LOGERR, + "invalid tgt/lun (%d.%d) in XPT_SCSI_IO", + ccb->ccb_h.target_id, ccb->ccb_h.target_lun); + xpt_done(ccb); + break; + } +#endif + ((struct ccb_scsiio *) ccb)->scsi_status = SCSI_STATUS_OK; + CAMLOCK_2_ISPLOCK(isp); + error = isp_start((XS_T *) ccb); + switch (error) { + case CMD_QUEUED: + ccb->ccb_h.status |= CAM_SIM_QUEUED; + if (ccb->ccb_h.timeout != CAM_TIME_INFINITY) { + uint64_t ticks = (uint64_t) hz; + if (ccb->ccb_h.timeout == CAM_TIME_DEFAULT) + ticks = 60 * 1000 * ticks; + else + ticks = ccb->ccb_h.timeout * hz; + ticks = ((ticks + 999) / 1000) + hz + hz; + if (ticks >= 0x80000000) { + isp_prt(isp, ISP_LOGERR, + "timeout overflow"); + ticks = 0x7fffffff; + } + ccb->ccb_h.timeout_ch = timeout(isp_watchdog, + (caddr_t)ccb, (int)ticks); + } else { + callout_handle_init(&ccb->ccb_h.timeout_ch); + } + ISPLOCK_2_CAMLOCK(isp); + break; + case CMD_RQLATER: + /* + * This can only happen for Fibre Channel + */ + KASSERT((IS_FC(isp)), ("CMD_RQLATER for FC only")); + if (FCPARAM(isp)->loop_seen_once == 0 && + isp->isp_osinfo.ktmature) { + ISPLOCK_2_CAMLOCK(isp); + XS_SETERR(ccb, CAM_SEL_TIMEOUT); + xpt_done(ccb); + break; + } +#if __FreeBSD_version < 500000 + wakeup(&isp->isp_osinfo.kproc); +#else +#ifdef ISP_SMPLOCK + cv_signal(&isp->isp_osinfo.kthread_cv); +#else + wakeup(&isp->isp_osinfo.kthread_cv); +#endif +#endif + isp_freeze_loopdown(isp, "isp_action(RQLATER)"); + XS_SETERR(ccb, CAM_REQUEUE_REQ); + ISPLOCK_2_CAMLOCK(isp); + xpt_done(ccb); + break; + case CMD_EAGAIN: + XS_SETERR(ccb, CAM_REQUEUE_REQ); + ISPLOCK_2_CAMLOCK(isp); + xpt_done(ccb); + break; + case CMD_COMPLETE: + isp_done((struct ccb_scsiio *) ccb); + ISPLOCK_2_CAMLOCK(isp); + break; + default: + isp_prt(isp, ISP_LOGERR, + "What's this? 0x%x at %d in file %s", + error, __LINE__, __FILE__); + XS_SETERR(ccb, CAM_REQ_CMP_ERR); + xpt_done(ccb); + ISPLOCK_2_CAMLOCK(isp); + } + break; + +#ifdef ISP_TARGET_MODE + case XPT_EN_LUN: /* Enable LUN as a target */ + { + int seq, iok, i; + CAMLOCK_2_ISPLOCK(isp); + iok = isp->isp_osinfo.intsok; + isp->isp_osinfo.intsok = 0; + seq = isp_en_lun(isp, ccb); + if (seq < 0) { + isp->isp_osinfo.intsok = iok; + ISPLOCK_2_CAMLOCK(isp); + xpt_done(ccb); + break; + } + for (i = 0; isp->isp_osinfo.leact[seq] && i < 30 * 1000; i++) { + uint16_t isr, sema, mbox; + if (ISP_READ_ISR(isp, &isr, &sema, &mbox)) { + isp_intr(isp, isr, sema, mbox); + } + DELAY(1000); + } + isp->isp_osinfo.intsok = iok; + ISPLOCK_2_CAMLOCK(isp); + break; + } + case XPT_NOTIFY_ACK: /* recycle notify ack */ + case XPT_IMMED_NOTIFY: /* Add Immediate Notify Resource */ + case XPT_ACCEPT_TARGET_IO: /* Add Accept Target IO Resource */ + { + tstate_t *tptr = + get_lun_statep(isp, XS_CHANNEL(ccb), ccb->ccb_h.target_lun); + if (tptr == NULL) { + ccb->ccb_h.status = CAM_LUN_INVALID; + xpt_done(ccb); + break; + } + ccb->ccb_h.sim_priv.entries[0].field = 0; + ccb->ccb_h.sim_priv.entries[1].ptr = isp; + ccb->ccb_h.flags = 0; + + CAMLOCK_2_ISPLOCK(isp); + if (ccb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO) { + /* + * Note that the command itself may not be done- + * it may not even have had the first CTIO sent. + */ + tptr->atio_count++; + isp_prt(isp, ISP_LOGTDEBUG0, + "Put FREE ATIO, lun %d, count now %d", + ccb->ccb_h.target_lun, tptr->atio_count); + SLIST_INSERT_HEAD(&tptr->atios, &ccb->ccb_h, + sim_links.sle); + } else if (ccb->ccb_h.func_code == XPT_IMMED_NOTIFY) { + tptr->inot_count++; + isp_prt(isp, ISP_LOGTDEBUG0, + "Put FREE INOT, lun %d, count now %d", + ccb->ccb_h.target_lun, tptr->inot_count); + SLIST_INSERT_HEAD(&tptr->inots, &ccb->ccb_h, + sim_links.sle); + } else { + isp_prt(isp, ISP_LOGWARN, "Got Notify ACK");; + } + rls_lun_statep(isp, tptr); + ccb->ccb_h.status = CAM_REQ_INPROG; + ISPLOCK_2_CAMLOCK(isp); + break; + } + case XPT_CONT_TARGET_IO: + { + CAMLOCK_2_ISPLOCK(isp); + isp_target_start_ctio(isp, ccb); + ISPLOCK_2_CAMLOCK(isp); + break; + } +#endif + case XPT_RESET_DEV: /* BDR the specified SCSI device */ + + bus = cam_sim_bus(xpt_path_sim(ccb->ccb_h.path)); + tgt = ccb->ccb_h.target_id; + tgt |= (bus << 16); + + CAMLOCK_2_ISPLOCK(isp); + error = isp_control(isp, ISPCTL_RESET_DEV, &tgt); + ISPLOCK_2_CAMLOCK(isp); + if (error) { + ccb->ccb_h.status = CAM_REQ_CMP_ERR; + } else { + ccb->ccb_h.status = CAM_REQ_CMP; + } + xpt_done(ccb); + break; + case XPT_ABORT: /* Abort the specified CCB */ + { + union ccb *accb = ccb->cab.abort_ccb; + CAMLOCK_2_ISPLOCK(isp); + switch (accb->ccb_h.func_code) { +#ifdef ISP_TARGET_MODE + case XPT_ACCEPT_TARGET_IO: + case XPT_IMMED_NOTIFY: + ccb->ccb_h.status = isp_abort_tgt_ccb(isp, ccb); + break; + case XPT_CONT_TARGET_IO: + isp_prt(isp, ISP_LOGERR, "cannot abort CTIOs yet"); + ccb->ccb_h.status = CAM_UA_ABORT; + break; +#endif + case XPT_SCSI_IO: + error = isp_control(isp, ISPCTL_ABORT_CMD, ccb); + if (error) { + ccb->ccb_h.status = CAM_UA_ABORT; + } else { + ccb->ccb_h.status = CAM_REQ_CMP; + } + break; + default: + ccb->ccb_h.status = CAM_REQ_INVALID; + break; + } + ISPLOCK_2_CAMLOCK(isp); + xpt_done(ccb); + break; + } +#ifdef CAM_NEW_TRAN_CODE +#define IS_CURRENT_SETTINGS(c) (c->type == CTS_TYPE_CURRENT_SETTINGS) +#else +#define IS_CURRENT_SETTINGS(c) (c->flags & CCB_TRANS_CURRENT_SETTINGS) +#endif + case XPT_SET_TRAN_SETTINGS: /* Nexus Settings */ + cts = &ccb->cts; + if (!IS_CURRENT_SETTINGS(cts)) { + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + } + tgt = cts->ccb_h.target_id; + CAMLOCK_2_ISPLOCK(isp); + if (IS_SCSI(isp)) { +#ifndef CAM_NEW_TRAN_CODE + sdparam *sdp = isp->isp_param; + uint16_t *dptr; + + bus = cam_sim_bus(xpt_path_sim(cts->ccb_h.path)); + + sdp += bus; + /* + * We always update (internally) from goal_flags + * so any request to change settings just gets + * vectored to that location. + */ + dptr = &sdp->isp_devparam[tgt].goal_flags; + + /* + * Note that these operations affect the + * the goal flags (goal_flags)- not + * the current state flags. Then we mark + * things so that the next operation to + * this HBA will cause the update to occur. + */ + if (cts->valid & CCB_TRANS_DISC_VALID) { + if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) { + *dptr |= DPARM_DISC; + } else { + *dptr &= ~DPARM_DISC; + } + } + if (cts->valid & CCB_TRANS_TQ_VALID) { + if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) { + *dptr |= DPARM_TQING; + } else { + *dptr &= ~DPARM_TQING; + } + } + if (cts->valid & CCB_TRANS_BUS_WIDTH_VALID) { + switch (cts->bus_width) { + case MSG_EXT_WDTR_BUS_16_BIT: + *dptr |= DPARM_WIDE; + break; + default: + *dptr &= ~DPARM_WIDE; + } + } + /* + * Any SYNC RATE of nonzero and SYNC_OFFSET + * of nonzero will cause us to go to the + * selected (from NVRAM) maximum value for + * this device. At a later point, we'll + * allow finer control. + */ + if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) && + (cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) && + (cts->sync_offset > 0)) { + *dptr |= DPARM_SYNC; + } else { + *dptr &= ~DPARM_SYNC; + } + *dptr |= DPARM_SAFE_DFLT; +#else + struct ccb_trans_settings_scsi *scsi = + &cts->proto_specific.scsi; + struct ccb_trans_settings_spi *spi = + &cts->xport_specific.spi; + sdparam *sdp = isp->isp_param; + uint16_t *dptr; + + bus = cam_sim_bus(xpt_path_sim(cts->ccb_h.path)); + sdp += bus; + /* + * We always update (internally) from goal_flags + * so any request to change settings just gets + * vectored to that location. + */ + dptr = &sdp->isp_devparam[tgt].goal_flags; + + if ((spi->valid & CTS_SPI_VALID_DISC) != 0) { + if ((spi->flags & CTS_SPI_FLAGS_DISC_ENB) != 0) + *dptr |= DPARM_DISC; + else + *dptr &= ~DPARM_DISC; + } + + if ((scsi->valid & CTS_SCSI_VALID_TQ) != 0) { + if ((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0) + *dptr |= DPARM_TQING; + else + *dptr &= ~DPARM_TQING; + } + + if ((spi->valid & CTS_SPI_VALID_BUS_WIDTH) != 0) { + if (spi->bus_width == MSG_EXT_WDTR_BUS_16_BIT) + *dptr |= DPARM_WIDE; + else + *dptr &= ~DPARM_WIDE; + } + + /* + * XXX: FIX ME + */ + if ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) && + (spi->valid & CTS_SPI_VALID_SYNC_RATE) && + (spi->sync_period && spi->sync_offset)) { + *dptr |= DPARM_SYNC; + /* + * XXX: CHECK FOR LEGALITY + */ + sdp->isp_devparam[tgt].goal_period = + spi->sync_period; + sdp->isp_devparam[tgt].goal_offset = + spi->sync_offset; + } else { + *dptr &= ~DPARM_SYNC; + } +#endif + isp_prt(isp, ISP_LOGDEBUG0, + "SET bus %d targ %d to flags %x off %x per %x", + bus, tgt, sdp->isp_devparam[tgt].goal_flags, + sdp->isp_devparam[tgt].goal_offset, + sdp->isp_devparam[tgt].goal_period); + sdp->isp_devparam[tgt].dev_update = 1; + isp->isp_update |= (1 << bus); + } + ISPLOCK_2_CAMLOCK(isp); + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + break; + case XPT_GET_TRAN_SETTINGS: + cts = &ccb->cts; + tgt = cts->ccb_h.target_id; + CAMLOCK_2_ISPLOCK(isp); + if (IS_FC(isp)) { +#ifndef CAM_NEW_TRAN_CODE + /* + * a lot of normal SCSI things don't make sense. + */ + cts->flags = CCB_TRANS_TAG_ENB | CCB_TRANS_DISC_ENB; + cts->valid = CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID; + /* + * How do you measure the width of a high + * speed serial bus? Well, in bytes. + * + * Offset and period make no sense, though, so we set + * (above) a 'base' transfer speed to be gigabit. + */ + cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT; +#else + fcparam *fcp = isp->isp_param; + struct ccb_trans_settings_fc *fc = + &cts->xport_specific.fc; + + cts->protocol = PROTO_SCSI; + cts->protocol_version = SCSI_REV_2; + cts->transport = XPORT_FC; + cts->transport_version = 0; + + fc->valid = CTS_FC_VALID_SPEED; + if (fcp->isp_gbspeed == 2) + fc->bitrate = 200000; + else + fc->bitrate = 100000; + if (tgt > 0 && tgt < MAX_FC_TARG) { + struct lportdb *lp = &fcp->portdb[tgt]; + fc->wwnn = lp->node_wwn; + fc->wwpn = lp->port_wwn; + fc->port = lp->portid; + fc->valid |= CTS_FC_VALID_WWNN | + CTS_FC_VALID_WWPN | CTS_FC_VALID_PORT; + } +#endif + } else { +#ifdef CAM_NEW_TRAN_CODE + struct ccb_trans_settings_scsi *scsi = + &cts->proto_specific.scsi; + struct ccb_trans_settings_spi *spi = + &cts->xport_specific.spi; +#endif + sdparam *sdp = isp->isp_param; + int bus = cam_sim_bus(xpt_path_sim(cts->ccb_h.path)); + uint16_t dval, pval, oval; + + sdp += bus; + + if (IS_CURRENT_SETTINGS(cts)) { + sdp->isp_devparam[tgt].dev_refresh = 1; + isp->isp_update |= (1 << bus); + (void) isp_control(isp, ISPCTL_UPDATE_PARAMS, + NULL); + dval = sdp->isp_devparam[tgt].actv_flags; + oval = sdp->isp_devparam[tgt].actv_offset; + pval = sdp->isp_devparam[tgt].actv_period; + } else { + dval = sdp->isp_devparam[tgt].nvrm_flags; + oval = sdp->isp_devparam[tgt].nvrm_offset; + pval = sdp->isp_devparam[tgt].nvrm_period; + } + +#ifndef CAM_NEW_TRAN_CODE + cts->flags &= ~(CCB_TRANS_DISC_ENB|CCB_TRANS_TAG_ENB); + + if (dval & DPARM_DISC) { + cts->flags |= CCB_TRANS_DISC_ENB; + } + if (dval & DPARM_TQING) { + cts->flags |= CCB_TRANS_TAG_ENB; + } + if (dval & DPARM_WIDE) { + cts->bus_width = MSG_EXT_WDTR_BUS_16_BIT; + } else { + cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT; + } + cts->valid = CCB_TRANS_BUS_WIDTH_VALID | + CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID; + + if ((dval & DPARM_SYNC) && oval != 0) { + cts->sync_period = pval; + cts->sync_offset = oval; + cts->valid |= + CCB_TRANS_SYNC_RATE_VALID | + CCB_TRANS_SYNC_OFFSET_VALID; + } +#else + cts->protocol = PROTO_SCSI; + cts->protocol_version = SCSI_REV_2; + cts->transport = XPORT_SPI; + cts->transport_version = 2; + + scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; + spi->flags &= ~CTS_SPI_FLAGS_DISC_ENB; + if (dval & DPARM_DISC) { + spi->flags |= CTS_SPI_FLAGS_DISC_ENB; + } + if (dval & DPARM_TQING) { + scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB; + } + if ((dval & DPARM_SYNC) && oval && pval) { + spi->sync_offset = oval; + spi->sync_period = pval; + spi->valid |= CTS_SPI_VALID_SYNC_OFFSET; + spi->valid |= CTS_SPI_VALID_SYNC_RATE; + } + spi->valid |= CTS_SPI_VALID_BUS_WIDTH; + if (dval & DPARM_WIDE) { + spi->bus_width = MSG_EXT_WDTR_BUS_16_BIT; + } else { + spi->bus_width = MSG_EXT_WDTR_BUS_8_BIT; + } + if (cts->ccb_h.target_lun != CAM_LUN_WILDCARD) { + scsi->valid = CTS_SCSI_VALID_TQ; + spi->valid |= CTS_SPI_VALID_DISC; + } else { + scsi->valid = 0; + } +#endif + isp_prt(isp, ISP_LOGDEBUG0, + "GET %s bus %d targ %d to flags %x off %x per %x", + IS_CURRENT_SETTINGS(cts)? "ACTIVE" : "NVRAM", + bus, tgt, dval, oval, pval); + } + ISPLOCK_2_CAMLOCK(isp); + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + break; + + case XPT_CALC_GEOMETRY: +#if __FreeBSD_version < 500000 + { + struct ccb_calc_geometry *ccg; + u_int32_t secs_per_cylinder; + u_int32_t size_mb; + + ccg = &ccb->ccg; + if (ccg->block_size == 0) { + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + } + size_mb = ccg->volume_size /((1024L * 1024L) / ccg->block_size); + if (size_mb > 1024) { + ccg->heads = 255; + ccg->secs_per_track = 63; + } else { + ccg->heads = 64; + ccg->secs_per_track = 32; + } + secs_per_cylinder = ccg->heads * ccg->secs_per_track; + ccg->cylinders = ccg->volume_size / secs_per_cylinder; + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + break; + } +#else + { + cam_calc_geometry(&ccb->ccg, /*extended*/1); + xpt_done(ccb); + break; + } +#endif + case XPT_RESET_BUS: /* Reset the specified bus */ + bus = cam_sim_bus(sim); + CAMLOCK_2_ISPLOCK(isp); + error = isp_control(isp, ISPCTL_RESET_BUS, &bus); + ISPLOCK_2_CAMLOCK(isp); + if (error) + ccb->ccb_h.status = CAM_REQ_CMP_ERR; + else { + if (cam_sim_bus(sim) && isp->isp_path2 != NULL) + xpt_async(AC_BUS_RESET, isp->isp_path2, NULL); + else if (isp->isp_path != NULL) + xpt_async(AC_BUS_RESET, isp->isp_path, NULL); + ccb->ccb_h.status = CAM_REQ_CMP; + } + xpt_done(ccb); + break; + + case XPT_TERM_IO: /* Terminate the I/O process */ + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + + case XPT_PATH_INQ: /* Path routing inquiry */ + { + struct ccb_pathinq *cpi = &ccb->cpi; + + cpi->version_num = 1; +#ifdef ISP_TARGET_MODE + cpi->target_sprt = PIT_PROCESSOR | PIT_DISCONNECT | PIT_TERM_IO; +#else + cpi->target_sprt = 0; +#endif + cpi->hba_eng_cnt = 0; + cpi->max_target = ISP_MAX_TARGETS(isp) - 1; + cpi->max_lun = ISP_MAX_LUNS(isp) - 1; + cpi->bus_id = cam_sim_bus(sim); + if (IS_FC(isp)) { + cpi->hba_misc = PIM_NOBUSRESET; + /* + * Because our loop ID can shift from time to time, + * make our initiator ID out of range of our bus. + */ + cpi->initiator_id = cpi->max_target + 1; + + /* + * Set base transfer capabilities for Fibre Channel. + * Technically not correct because we don't know + * what media we're running on top of- but we'll + * look good if we always say 100MB/s. + */ + if (FCPARAM(isp)->isp_gbspeed == 2) + cpi->base_transfer_speed = 200000; + else + cpi->base_transfer_speed = 100000; + cpi->hba_inquiry = PI_TAG_ABLE; +#ifdef CAM_NEW_TRAN_CODE + cpi->transport = XPORT_FC; + cpi->transport_version = 0; /* WHAT'S THIS FOR? */ +#endif + } else { + sdparam *sdp = isp->isp_param; + sdp += cam_sim_bus(xpt_path_sim(cpi->ccb_h.path)); + cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE|PI_WIDE_16; + cpi->hba_misc = 0; + cpi->initiator_id = sdp->isp_initiator_id; + cpi->base_transfer_speed = 3300; +#ifdef CAM_NEW_TRAN_CODE + cpi->transport = XPORT_SPI; + cpi->transport_version = 2; /* WHAT'S THIS FOR? */ +#endif + } +#ifdef CAM_NEW_TRAN_CODE + cpi->protocol = PROTO_SCSI; + cpi->protocol_version = SCSI_REV_2; +#endif + strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); + strncpy(cpi->hba_vid, "Qlogic", HBA_IDLEN); + strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); + cpi->unit_number = cam_sim_unit(sim); + cpi->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + break; + } + default: + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + } +} + +#define ISPDDB (CAM_DEBUG_INFO|CAM_DEBUG_TRACE|CAM_DEBUG_CDB) +void +isp_done(struct ccb_scsiio *sccb) +{ + ispsoftc_t *isp = XS_ISP(sccb); + + if (XS_NOERR(sccb)) + XS_SETERR(sccb, CAM_REQ_CMP); + + if ((sccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP && + (sccb->scsi_status != SCSI_STATUS_OK)) { + sccb->ccb_h.status &= ~CAM_STATUS_MASK; + if ((sccb->scsi_status == SCSI_STATUS_CHECK_COND) && + (sccb->ccb_h.status & CAM_AUTOSNS_VALID) == 0) { + sccb->ccb_h.status |= CAM_AUTOSENSE_FAIL; + } else { + sccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR; + } + } + + sccb->ccb_h.status &= ~CAM_SIM_QUEUED; + if ((sccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + if ((sccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { + sccb->ccb_h.status |= CAM_DEV_QFRZN; + xpt_freeze_devq(sccb->ccb_h.path, 1); + isp_prt(isp, ISP_LOGDEBUG0, + "freeze devq %d.%d cam sts %x scsi sts %x", + sccb->ccb_h.target_id, sccb->ccb_h.target_lun, + sccb->ccb_h.status, sccb->scsi_status); + } + } + + if ((CAM_DEBUGGED(sccb->ccb_h.path, ISPDDB)) && + (sccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + xpt_print_path(sccb->ccb_h.path); + isp_prt(isp, ISP_LOGINFO, + "cam completion status 0x%x", sccb->ccb_h.status); + } + + XS_CMD_S_DONE(sccb); + if (XS_CMD_WDOG_P(sccb) == 0) { + untimeout(isp_watchdog, (caddr_t)sccb, sccb->ccb_h.timeout_ch); + if (XS_CMD_GRACE_P(sccb)) { + isp_prt(isp, ISP_LOGDEBUG2, + "finished command on borrowed time"); + } + XS_CMD_S_CLEAR(sccb); + ISPLOCK_2_CAMLOCK(isp); + xpt_done((union ccb *) sccb); + CAMLOCK_2_ISPLOCK(isp); + } +} + +int +isp_async(ispsoftc_t *isp, ispasync_t cmd, void *arg) +{ + int bus, rv = 0; + switch (cmd) { + case ISPASYNC_NEW_TGT_PARAMS: + { +#ifdef CAM_NEW_TRAN_CODE + struct ccb_trans_settings_scsi *scsi; + struct ccb_trans_settings_spi *spi; +#endif + int flags, tgt; + sdparam *sdp = isp->isp_param; + struct ccb_trans_settings cts; + struct cam_path *tmppath; + + memset(&cts, 0, sizeof (struct ccb_trans_settings)); + + tgt = *((int *)arg); + bus = (tgt >> 16) & 0xffff; + tgt &= 0xffff; + sdp += bus; + ISPLOCK_2_CAMLOCK(isp); + if (xpt_create_path(&tmppath, NULL, + cam_sim_path(bus? isp->isp_sim2 : isp->isp_sim), + tgt, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { + CAMLOCK_2_ISPLOCK(isp); + isp_prt(isp, ISP_LOGWARN, + "isp_async cannot make temp path for %d.%d", + tgt, bus); + rv = -1; + break; + } + CAMLOCK_2_ISPLOCK(isp); + flags = sdp->isp_devparam[tgt].actv_flags; +#ifdef CAM_NEW_TRAN_CODE + cts.type = CTS_TYPE_CURRENT_SETTINGS; + cts.protocol = PROTO_SCSI; + cts.transport = XPORT_SPI; + + scsi = &cts.proto_specific.scsi; + spi = &cts.xport_specific.spi; + + if (flags & DPARM_TQING) { + scsi->valid |= CTS_SCSI_VALID_TQ; + scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB; + spi->flags |= CTS_SPI_FLAGS_TAG_ENB; + } + + if (flags & DPARM_DISC) { + spi->valid |= CTS_SPI_VALID_DISC; + spi->flags |= CTS_SPI_FLAGS_DISC_ENB; + } + spi->flags |= CTS_SPI_VALID_BUS_WIDTH; + if (flags & DPARM_WIDE) { + spi->bus_width = MSG_EXT_WDTR_BUS_16_BIT; + } else { + spi->bus_width = MSG_EXT_WDTR_BUS_8_BIT; + } + if (flags & DPARM_SYNC) { + spi->valid |= CTS_SPI_VALID_SYNC_RATE; + spi->valid |= CTS_SPI_VALID_SYNC_OFFSET; + spi->sync_period = sdp->isp_devparam[tgt].actv_period; + spi->sync_offset = sdp->isp_devparam[tgt].actv_offset; + } +#else + cts.flags = CCB_TRANS_CURRENT_SETTINGS; + cts.valid = CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID; + if (flags & DPARM_DISC) { + cts.flags |= CCB_TRANS_DISC_ENB; + } + if (flags & DPARM_TQING) { + cts.flags |= CCB_TRANS_TAG_ENB; + } + cts.valid |= CCB_TRANS_BUS_WIDTH_VALID; + cts.bus_width = (flags & DPARM_WIDE)? + MSG_EXT_WDTR_BUS_8_BIT : MSG_EXT_WDTR_BUS_16_BIT; + cts.sync_period = sdp->isp_devparam[tgt].actv_period; + cts.sync_offset = sdp->isp_devparam[tgt].actv_offset; + if (flags & DPARM_SYNC) { + cts.valid |= + CCB_TRANS_SYNC_RATE_VALID | + CCB_TRANS_SYNC_OFFSET_VALID; + } +#endif + isp_prt(isp, ISP_LOGDEBUG2, + "NEW_TGT_PARAMS bus %d tgt %d period %x offset %x flags %x", + bus, tgt, sdp->isp_devparam[tgt].actv_period, + sdp->isp_devparam[tgt].actv_offset, flags); + xpt_setup_ccb(&cts.ccb_h, tmppath, 1); + ISPLOCK_2_CAMLOCK(isp); + xpt_async(AC_TRANSFER_NEG, tmppath, &cts); + xpt_free_path(tmppath); + CAMLOCK_2_ISPLOCK(isp); + break; + } + case ISPASYNC_BUS_RESET: + bus = *((int *)arg); + isp_prt(isp, ISP_LOGINFO, "SCSI bus reset on bus %d detected", + bus); + if (bus > 0 && isp->isp_path2) { + ISPLOCK_2_CAMLOCK(isp); + xpt_async(AC_BUS_RESET, isp->isp_path2, NULL); + CAMLOCK_2_ISPLOCK(isp); + } else if (isp->isp_path) { + ISPLOCK_2_CAMLOCK(isp); + xpt_async(AC_BUS_RESET, isp->isp_path, NULL); + CAMLOCK_2_ISPLOCK(isp); + } + break; + case ISPASYNC_LIP: + if (isp->isp_path) { + isp_freeze_loopdown(isp, "ISPASYNC_LIP"); + } + isp_prt(isp, ISP_LOGINFO, "LIP Received"); + break; + case ISPASYNC_LOOP_RESET: + if (isp->isp_path) { + isp_freeze_loopdown(isp, "ISPASYNC_LOOP_RESET"); + } + isp_prt(isp, ISP_LOGINFO, "Loop Reset Received"); + break; + case ISPASYNC_LOOP_DOWN: + if (isp->isp_path) { + isp_freeze_loopdown(isp, "ISPASYNC_LOOP_DOWN"); + } + isp_prt(isp, ISP_LOGINFO, "Loop DOWN"); + break; + case ISPASYNC_LOOP_UP: + /* + * Now we just note that Loop has come up. We don't + * actually do anything because we're waiting for a + * Change Notify before activating the FC cleanup + * thread to look at the state of the loop again. + */ + isp_prt(isp, ISP_LOGINFO, "Loop UP"); + break; + case ISPASYNC_PROMENADE: + { + const char *fmt = "Target %d (Loop 0x%x) Port ID 0x%x " + "(role %s) %s\n Port WWN 0x%08x%08x\n Node WWN 0x%08x%08x"; + static const char *roles[4] = { + "(none)", "Target", "Initiator", "Target/Initiator" + }; + fcparam *fcp = isp->isp_param; + int tgt = *((int *) arg); +#if __FreeBSD_version >= 500000 + int is_tgt_mask = (SVC3_TGT_ROLE >> SVC3_ROLE_SHIFT); + struct cam_path *tmppath; +#endif + struct lportdb *lp = &fcp->portdb[tgt]; + + isp_prt(isp, ISP_LOGINFO, fmt, tgt, lp->loopid, lp->portid, + roles[lp->roles & 0x3], + (lp->valid)? "Arrived" : "Departed", + (uint32_t) (lp->port_wwn >> 32), + (uint32_t) (lp->port_wwn & 0xffffffffLL), + (uint32_t) (lp->node_wwn >> 32), + (uint32_t) (lp->node_wwn & 0xffffffffLL)); + + ISPLOCK_2_CAMLOCK(isp); +#if __FreeBSD_version >= 500000 + if (xpt_create_path(&tmppath, NULL, cam_sim_path(isp->isp_sim), + (target_id_t)tgt, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { + CAMLOCK_2_ISPLOCK(isp); + break; + } + /* + * Policy: only announce targets. + */ + if (lp->roles & is_tgt_mask) { + if (lp->valid) { + xpt_async(AC_FOUND_DEVICE, tmppath, NULL); + } else { + xpt_async(AC_LOST_DEVICE, tmppath, NULL); + } + } + xpt_free_path(tmppath); +#endif + CAMLOCK_2_ISPLOCK(isp); + break; + } + case ISPASYNC_CHANGE_NOTIFY: + if (arg == ISPASYNC_CHANGE_PDB) { + isp_prt(isp, ISP_LOGINFO, + "Port Database Changed"); + } else if (arg == ISPASYNC_CHANGE_SNS) { + isp_prt(isp, ISP_LOGINFO, + "Name Server Database Changed"); + } +#if __FreeBSD_version < 500000 + wakeup(&isp->isp_osinfo.kproc); +#else +#ifdef ISP_SMPLOCK + cv_signal(&isp->isp_osinfo.kthread_cv); +#else + wakeup(&isp->isp_osinfo.kthread_cv); +#endif +#endif + break; + case ISPASYNC_FABRIC_DEV: + { + int target, base, lim; + fcparam *fcp = isp->isp_param; + struct lportdb *lp = NULL; + struct lportdb *clp = (struct lportdb *) arg; + char *pt; + + switch (clp->port_type) { + case 1: + pt = " N_Port"; + break; + case 2: + pt = " NL_Port"; + break; + case 3: + pt = "F/NL_Port"; + break; + case 0x7f: + pt = " Nx_Port"; + break; + case 0x81: + pt = " F_port"; + break; + case 0x82: + pt = " FL_Port"; + break; + case 0x84: + pt = " E_port"; + break; + default: + pt = " "; + break; + } + + isp_prt(isp, ISP_LOGINFO, + "%s Fabric Device @ PortID 0x%x", pt, clp->portid); + + /* + * If we don't have an initiator role we bail. + * + * We just use ISPASYNC_FABRIC_DEV for announcement purposes. + */ + + if ((isp->isp_role & ISP_ROLE_INITIATOR) == 0) { + break; + } + + /* + * Is this entry for us? If so, we bail. + */ + + if (fcp->isp_portid == clp->portid) { + break; + } + + /* + * Else, the default policy is to find room for it in + * our local port database. Later, when we execute + * the call to isp_pdb_sync either this newly arrived + * or already logged in device will be (re)announced. + */ + + if (fcp->isp_topo == TOPO_FL_PORT) + base = FC_SNS_ID+1; + else + base = 0; + + if (fcp->isp_topo == TOPO_N_PORT) + lim = 1; + else + lim = MAX_FC_TARG; + + /* + * Is it already in our list? + */ + for (target = base; target < lim; target++) { + if (target >= FL_PORT_ID && target <= FC_SNS_ID) { + continue; + } + lp = &fcp->portdb[target]; + if (lp->port_wwn == clp->port_wwn && + lp->node_wwn == clp->node_wwn) { + lp->fabric_dev = 1; + break; + } + } + if (target < lim) { + break; + } + for (target = base; target < lim; target++) { + if (target >= FL_PORT_ID && target <= FC_SNS_ID) { + continue; + } + lp = &fcp->portdb[target]; + if (lp->port_wwn == 0) { + break; + } + } + if (target == lim) { + isp_prt(isp, ISP_LOGWARN, + "out of space for fabric devices"); + break; + } + lp->port_type = clp->port_type; + lp->fc4_type = clp->fc4_type; + lp->node_wwn = clp->node_wwn; + lp->port_wwn = clp->port_wwn; + lp->portid = clp->portid; + lp->fabric_dev = 1; + break; + } +#ifdef ISP_TARGET_MODE + case ISPASYNC_TARGET_NOTIFY: + { + tmd_notify_t *nt = arg; + isp_prt(isp, ISP_LOGALL, + "target notify code 0x%x", nt->nt_ncode); + break; + } + case ISPASYNC_TARGET_ACTION: + switch (((isphdr_t *)arg)->rqs_entry_type) { + default: + isp_prt(isp, ISP_LOGWARN, + "event 0x%x for unhandled target action", + ((isphdr_t *)arg)->rqs_entry_type); + break; + case RQSTYPE_NOTIFY: + if (IS_SCSI(isp)) { + rv = isp_handle_platform_notify_scsi(isp, + (in_entry_t *) arg); + } else { + rv = isp_handle_platform_notify_fc(isp, + (in_fcentry_t *) arg); + } + break; + case RQSTYPE_ATIO: + rv = isp_handle_platform_atio(isp, (at_entry_t *) arg); + break; + case RQSTYPE_ATIO2: + rv = isp_handle_platform_atio2(isp, (at2_entry_t *)arg); + break; + case RQSTYPE_CTIO3: + case RQSTYPE_CTIO2: + case RQSTYPE_CTIO: + rv = isp_handle_platform_ctio(isp, arg); + break; + case RQSTYPE_ENABLE_LUN: + case RQSTYPE_MODIFY_LUN: + isp_ledone(isp, (lun_entry_t *) arg); + break; + } + break; +#endif + case ISPASYNC_FW_CRASH: + { + uint16_t mbox1, mbox6; + mbox1 = ISP_READ(isp, OUTMAILBOX1); + if (IS_DUALBUS(isp)) { + mbox6 = ISP_READ(isp, OUTMAILBOX6); + } else { + mbox6 = 0; + } + isp_prt(isp, ISP_LOGERR, + "Internal Firmware Error on bus %d @ RISC Address 0x%x", + mbox6, mbox1); +#ifdef ISP_FW_CRASH_DUMP + /* + * XXX: really need a thread to do this right. + */ + if (IS_FC(isp)) { + FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT; + FCPARAM(isp)->isp_loopstate = LOOP_NIL; + isp_freeze_loopdown(isp, "f/w crash"); + isp_fw_dump(isp); + } + isp_reinit(isp); + isp_async(isp, ISPASYNC_FW_RESTARTED, NULL); +#endif + break; + } + case ISPASYNC_UNHANDLED_RESPONSE: + break; + default: + isp_prt(isp, ISP_LOGERR, "unknown isp_async event %d", cmd); + break; + } + return (rv); +} + + +/* + * Locks are held before coming here. + */ +void +isp_uninit(ispsoftc_t *isp) +{ + ISP_WRITE(isp, HCCR, HCCR_CMD_RESET); + DISABLE_INTS(isp); +} + +void +isp_prt(ispsoftc_t *isp, int level, const char *fmt, ...) +{ + va_list ap; + if (level != ISP_LOGALL && (level & isp->isp_dblev) == 0) { + return; + } + printf("%s: ", device_get_nameunit(isp->isp_dev)); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + printf("\n"); +} diff --git a/sys/dev/isp/isp_freebsd.h b/sys/dev/isp/isp_freebsd.h new file mode 100644 index 000000000000..6502dea00844 --- /dev/null +++ b/sys/dev/isp/isp_freebsd.h @@ -0,0 +1,521 @@ +/* $FreeBSD$ */ +/*- + * Qlogic ISP SCSI Host Adapter FreeBSD Wrapper Definitions + * + * Copyright (c) 1997-2006 by Matthew Jacob + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef _ISP_FREEBSD_H +#define _ISP_FREEBSD_H + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/endian.h> +#if __FreeBSD_version < 500000 +#include <sys/kernel.h> +#include <sys/queue.h> +#include <sys/malloc.h> +#else +#include <sys/lock.h> +#include <sys/kernel.h> +#include <sys/queue.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/condvar.h> +#endif + +#include <sys/proc.h> +#include <sys/bus.h> + +#include <machine/bus.h> +#if __FreeBSD_version < 500000 +#include <machine/clock.h> +#endif +#include <machine/cpu.h> + +#include <cam/cam.h> +#include <cam/cam_debug.h> +#include <cam/cam_ccb.h> +#include <cam/cam_sim.h> +#include <cam/cam_xpt.h> +#include <cam/cam_xpt_sim.h> +#include <cam/cam_debug.h> +#include <cam/scsi/scsi_all.h> +#include <cam/scsi/scsi_message.h> + +#include "opt_ddb.h" +#include "opt_isp.h" + +#if __FreeBSD_version < 500000 +#define ISP_PLATFORM_VERSION_MAJOR 4 +#define ISP_PLATFORM_VERSION_MINOR 17 +#else +#define ISP_PLATFORM_VERSION_MAJOR 5 +#define ISP_PLATFORM_VERSION_MINOR 9 +#endif + +/* + * Efficiency- get rid of SBus code && tests unless we need them. + */ +#ifdef __sparc64__ +#define ISP_SBUS_SUPPORTED 1 +#else +#define ISP_SBUS_SUPPORTED 0 +#endif + +#define HANDLE_LOOPSTATE_IN_OUTER_LAYERS 1 +/* #define ISP_SMPLOCK 1 */ + +#if __FreeBSD_version < 500000 +#define ISP_IFLAGS INTR_TYPE_CAM +#else +#ifdef ISP_SMPLOCK +#define ISP_IFLAGS INTR_TYPE_CAM | INTR_ENTROPY | INTR_MPSAFE +#else +#define ISP_IFLAGS INTR_TYPE_CAM | INTR_ENTROPY +#endif +#endif + +#if __FreeBSD_version < 700000 +typedef void ispfwfunc(int, int, int, uint16_t **); +#endif + +#ifdef ISP_TARGET_MODE +#define ISP_TARGET_FUNCTIONS 1 +#define ATPDPSIZE 256 +typedef struct { + uint32_t orig_datalen; + uint32_t bytes_xfered; + uint32_t last_xframt; + uint32_t tag : 16, + lun : 13, /* not enough */ + state : 3; +} atio_private_data_t; +#define ATPD_STATE_FREE 0 +#define ATPD_STATE_ATIO 1 +#define ATPD_STATE_CAM 2 +#define ATPD_STATE_CTIO 3 +#define ATPD_STATE_LAST_CTIO 4 +#define ATPD_STATE_PDON 5 + +typedef struct tstate { + struct tstate *next; + struct cam_path *owner; + struct ccb_hdr_slist atios; + struct ccb_hdr_slist inots; + lun_id_t lun; + int bus; + uint32_t hold; + int atio_count; + int inot_count; +} tstate_t; + +#define LUN_HASH_SIZE 32 +#define LUN_HASH_FUNC(isp, port, lun) \ + ((IS_DUALBUS(isp)) ? \ + (((lun) & ((LUN_HASH_SIZE >> 1) - 1)) << (port)) : \ + ((lun) & (LUN_HASH_SIZE - 1))) +#endif + +struct isposinfo { + struct ispsoftc * next; + uint64_t default_port_wwn; + uint64_t default_node_wwn; + uint32_t default_id; + device_t dev; + struct cam_sim *sim; + struct cam_path *path; + struct cam_sim *sim2; + struct cam_path *path2; + struct intr_config_hook ehook; + uint8_t + disabled : 1, + fcbsy : 1, + ktmature : 1, + mboxwaiting : 1, + intsok : 1, + simqfrozen : 3; +#if __FreeBSD_version >= 500000 + struct firmware * fw; + struct mtx lock; + struct cv kthread_cv; +#endif + struct proc *kproc; + bus_dma_tag_t cdmat; + bus_dmamap_t cdmap; +#define isp_cdmat isp_osinfo.cdmat +#define isp_cdmap isp_osinfo.cdmap +#ifdef ISP_TARGET_MODE +#define TM_WILDCARD_ENABLED 0x02 +#define TM_TMODE_ENABLED 0x01 + uint8_t tmflags[2]; /* two busses */ +#define NLEACT 4 + union ccb * leact[NLEACT]; + tstate_t tsdflt[2]; /* two busses */ + tstate_t *lun_hash[LUN_HASH_SIZE]; + atio_private_data_t atpdp[ATPDPSIZE]; +#endif +}; + +#define isp_lock isp_osinfo.lock + +/* + * Locking macros... + */ + +#ifdef ISP_SMPLOCK +#define ISP_LOCK(x) mtx_lock(&(x)->isp_lock) +#define ISP_UNLOCK(x) mtx_unlock(&(x)->isp_lock) +#define ISPLOCK_2_CAMLOCK(isp) \ + mtx_unlock(&(isp)->isp_lock); mtx_lock(&Giant) +#define CAMLOCK_2_ISPLOCK(isp) \ + mtx_unlock(&Giant); mtx_lock(&(isp)->isp_lock) +#else +#define ISP_LOCK(x) do { } while (0) +#define ISP_UNLOCK(x) do { } while (0) +#define ISPLOCK_2_CAMLOCK(isp) do { } while (0) +#define CAMLOCK_2_ISPLOCK(isp) do { } while (0) +#endif + +/* + * Required Macros/Defines + */ + +#define ISP2100_SCRLEN 0x800 + +#define MEMZERO(a, b) memset(a, 0, b) +#define MEMCPY memcpy +#define SNPRINTF snprintf +#define USEC_DELAY DELAY +#define USEC_SLEEP(isp, x) \ + if (isp->isp_osinfo.intsok) \ + ISP_UNLOCK(isp); \ + DELAY(x); \ + if (isp->isp_osinfo.intsok) \ + ISP_LOCK(isp) + +#define NANOTIME_T struct timespec +#define GET_NANOTIME nanotime +#define GET_NANOSEC(x) ((x)->tv_sec * 1000000000 + (x)->tv_nsec) +#define NANOTIME_SUB nanotime_sub + +#define MAXISPREQUEST(isp) ((IS_FC(isp) || IS_ULTRA2(isp))? 1024 : 256) + +#define MEMORYBARRIER(isp, type, offset, size) \ +switch (type) { \ +case SYNC_SFORDEV: \ +case SYNC_REQUEST: \ + bus_dmamap_sync(isp->isp_cdmat, isp->isp_cdmap, \ + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); \ + break; \ +case SYNC_SFORCPU: \ +case SYNC_RESULT: \ + bus_dmamap_sync(isp->isp_cdmat, isp->isp_cdmap, \ + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); \ + break; \ +default: \ + break; \ +} + +#define MBOX_ACQUIRE(isp) +#define MBOX_WAIT_COMPLETE isp_mbox_wait_complete +#define MBOX_NOTIFY_COMPLETE(isp) \ + if (isp->isp_osinfo.mboxwaiting) { \ + isp->isp_osinfo.mboxwaiting = 0; \ + wakeup(&isp->isp_mbxworkp); \ + } \ + isp->isp_mboxbsy = 0 +#define MBOX_RELEASE(isp) + +#define FC_SCRATCH_ACQUIRE(isp) \ + if (isp->isp_osinfo.fcbsy) { \ + isp_prt(isp, ISP_LOGWARN, \ + "FC scratch area busy (line %d)!", __LINE__); \ + } else \ + isp->isp_osinfo.fcbsy = 1 +#define FC_SCRATCH_RELEASE(isp) isp->isp_osinfo.fcbsy = 0 + +#ifndef SCSI_GOOD +#define SCSI_GOOD SCSI_STATUS_OK +#endif +#ifndef SCSI_CHECK +#define SCSI_CHECK SCSI_STATUS_CHECK_COND +#endif +#ifndef SCSI_BUSY +#define SCSI_BUSY SCSI_STATUS_BUSY +#endif +#ifndef SCSI_QFULL +#define SCSI_QFULL SCSI_STATUS_QUEUE_FULL +#endif + +#define XS_T struct ccb_scsiio +#define XS_DMA_ADDR_T bus_addr_t +#define XS_ISP(ccb) ((ispsoftc_t *) (ccb)->ccb_h.spriv_ptr1) +#define XS_CHANNEL(ccb) cam_sim_bus(xpt_path_sim((ccb)->ccb_h.path)) +#define XS_TGT(ccb) (ccb)->ccb_h.target_id +#define XS_LUN(ccb) (ccb)->ccb_h.target_lun + +#define XS_CDBP(ccb) \ + (((ccb)->ccb_h.flags & CAM_CDB_POINTER)? \ + (ccb)->cdb_io.cdb_ptr : (ccb)->cdb_io.cdb_bytes) + +#define XS_CDBLEN(ccb) (ccb)->cdb_len +#define XS_XFRLEN(ccb) (ccb)->dxfer_len +#define XS_TIME(ccb) (ccb)->ccb_h.timeout +#define XS_RESID(ccb) (ccb)->resid +#define XS_STSP(ccb) (&(ccb)->scsi_status) +#define XS_SNSP(ccb) (&(ccb)->sense_data) + +#define XS_SNSLEN(ccb) \ + imin((sizeof((ccb)->sense_data)), ccb->sense_len) + +#define XS_SNSKEY(ccb) ((ccb)->sense_data.flags & 0xf) +#define XS_TAG_P(ccb) \ + (((ccb)->ccb_h.flags & CAM_TAG_ACTION_VALID) && \ + (ccb)->tag_action != CAM_TAG_ACTION_NONE) + +#define XS_TAG_TYPE(ccb) \ + ((ccb->tag_action == MSG_SIMPLE_Q_TAG)? REQFLAG_STAG : \ + ((ccb->tag_action == MSG_HEAD_OF_Q_TAG)? REQFLAG_HTAG : REQFLAG_OTAG)) + + +#define XS_SETERR(ccb, v) (ccb)->ccb_h.status &= ~CAM_STATUS_MASK, \ + (ccb)->ccb_h.status |= v, \ + (ccb)->ccb_h.spriv_field0 |= ISP_SPRIV_ERRSET + +# define HBA_NOERROR CAM_REQ_INPROG +# define HBA_BOTCH CAM_UNREC_HBA_ERROR +# define HBA_CMDTIMEOUT CAM_CMD_TIMEOUT +# define HBA_SELTIMEOUT CAM_SEL_TIMEOUT +# define HBA_TGTBSY CAM_SCSI_STATUS_ERROR +# define HBA_BUSRESET CAM_SCSI_BUS_RESET +# define HBA_ABORTED CAM_REQ_ABORTED +# define HBA_DATAOVR CAM_DATA_RUN_ERR +# define HBA_ARQFAIL CAM_AUTOSENSE_FAIL + + +#define XS_ERR(ccb) ((ccb)->ccb_h.status & CAM_STATUS_MASK) + +#define XS_NOERR(ccb) \ + (((ccb)->ccb_h.spriv_field0 & ISP_SPRIV_ERRSET) == 0 || \ + ((ccb)->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG) + +#define XS_INITERR(ccb) \ + XS_SETERR(ccb, CAM_REQ_INPROG), (ccb)->ccb_h.spriv_field0 = 0 + +#define XS_SAVE_SENSE(xs, sp) \ + (xs)->ccb_h.status |= CAM_AUTOSNS_VALID, \ + memcpy(&(xs)->sense_data, sp->req_sense_data, \ + imin(XS_SNSLEN(xs), sp->req_sense_len)) + +#define XS_SET_STATE_STAT(a, b, c) + +#define DEFAULT_IID(x) (isp)->isp_osinfo.default_id +#define DEFAULT_LOOPID(x) (isp)->isp_osinfo.default_id +#define DEFAULT_NODEWWN(isp) (isp)->isp_osinfo.default_node_wwn +#define DEFAULT_PORTWWN(isp) (isp)->isp_osinfo.default_port_wwn +#define ISP_NODEWWN(isp) FCPARAM(isp)->isp_nodewwn +#define ISP_PORTWWN(isp) FCPARAM(isp)->isp_portwwn + +#if BYTE_ORDER == BIG_ENDIAN +#ifdef ISP_SBUS_SUPPORTED +#define ISP_IOXPUT_8(isp, s, d) *(d) = s +#define ISP_IOXPUT_16(isp, s, d) \ + *(d) = (isp->isp_bustype == ISP_BT_SBUS)? s : bswap16(s) +#define ISP_IOXPUT_32(isp, s, d) \ + *(d) = (isp->isp_bustype == ISP_BT_SBUS)? s : bswap32(s) +#define ISP_IOXGET_8(isp, s, d) d = (*((uint8_t *)s)) +#define ISP_IOXGET_16(isp, s, d) \ + d = (isp->isp_bustype == ISP_BT_SBUS)? \ + *((uint16_t *)s) : bswap16(*((uint16_t *)s)) +#define ISP_IOXGET_32(isp, s, d) \ + d = (isp->isp_bustype == ISP_BT_SBUS)? \ + *((uint32_t *)s) : bswap32(*((uint32_t *)s)) +#else +#define ISP_IOXPUT_8(isp, s, d) *(d) = s +#define ISP_IOXPUT_16(isp, s, d) *(d) = bswap16(s) +#define ISP_IOXPUT_32(isp, s, d) *(d) = bswap32(s) +#define ISP_IOXGET_8(isp, s, d) d = (*((uint8_t *)s)) +#define ISP_IOXGET_16(isp, s, d) d = bswap16(*((uint16_t *)s)) +#define ISP_IOXGET_32(isp, s, d) d = bswap32(*((uint32_t *)s)) +#endif +#define ISP_SWIZZLE_NVRAM_WORD(isp, rp) *rp = bswap16(*rp) +#else +#define ISP_IOXPUT_8(isp, s, d) *(d) = s +#define ISP_IOXPUT_16(isp, s, d) *(d) = s +#define ISP_IOXPUT_32(isp, s, d) *(d) = s +#define ISP_IOXGET_8(isp, s, d) d = *(s) +#define ISP_IOXGET_16(isp, s, d) d = *(s) +#define ISP_IOXGET_32(isp, s, d) d = *(s) +#define ISP_SWIZZLE_NVRAM_WORD(isp, rp) +#endif + +/* + * Includes of common header files + */ + +#include <dev/isp/ispreg.h> +#include <dev/isp/ispvar.h> +#include <dev/isp/ispmbox.h> + +#ifdef ISP_TARGET_MODE +#include <dev/isp/isp_tpublic.h> +#endif + +void isp_prt(ispsoftc_t *, int level, const char *, ...) + __printflike(3, 4); +/* + * isp_osinfo definiitions && shorthand + */ +#define SIMQFRZ_RESOURCE 0x1 +#define SIMQFRZ_LOOPDOWN 0x2 +#define SIMQFRZ_TIMED 0x4 + +#define isp_sim isp_osinfo.sim +#define isp_path isp_osinfo.path +#define isp_sim2 isp_osinfo.sim2 +#define isp_path2 isp_osinfo.path2 +#define isp_dev isp_osinfo.dev + +/* + * prototypes for isp_pci && isp_freebsd to share + */ +extern void isp_attach(ispsoftc_t *); +extern void isp_uninit(ispsoftc_t *); + +/* + * driver global data + */ +extern int isp_announced; + +/* + * Platform private flags + */ +#define ISP_SPRIV_ERRSET 0x1 +#define ISP_SPRIV_INWDOG 0x2 +#define ISP_SPRIV_GRACE 0x4 +#define ISP_SPRIV_DONE 0x8 + +#define XS_CMD_S_WDOG(sccb) (sccb)->ccb_h.spriv_field0 |= ISP_SPRIV_INWDOG +#define XS_CMD_C_WDOG(sccb) (sccb)->ccb_h.spriv_field0 &= ~ISP_SPRIV_INWDOG +#define XS_CMD_WDOG_P(sccb) ((sccb)->ccb_h.spriv_field0 & ISP_SPRIV_INWDOG) + +#define XS_CMD_S_GRACE(sccb) (sccb)->ccb_h.spriv_field0 |= ISP_SPRIV_GRACE +#define XS_CMD_C_GRACE(sccb) (sccb)->ccb_h.spriv_field0 &= ~ISP_SPRIV_GRACE +#define XS_CMD_GRACE_P(sccb) ((sccb)->ccb_h.spriv_field0 & ISP_SPRIV_GRACE) + +#define XS_CMD_S_DONE(sccb) (sccb)->ccb_h.spriv_field0 |= ISP_SPRIV_DONE +#define XS_CMD_C_DONE(sccb) (sccb)->ccb_h.spriv_field0 &= ~ISP_SPRIV_DONE +#define XS_CMD_DONE_P(sccb) ((sccb)->ccb_h.spriv_field0 & ISP_SPRIV_DONE) + +#define XS_CMD_S_CLEAR(sccb) (sccb)->ccb_h.spriv_field0 = 0 + +/* + * Platform specific inline functions + */ + +static __inline void isp_mbox_wait_complete(ispsoftc_t *); +static __inline void +isp_mbox_wait_complete(ispsoftc_t *isp) +{ + if (isp->isp_osinfo.intsok) { + int lim = ((isp->isp_mbxwrk0)? 120 : 20) * hz; + isp->isp_osinfo.mboxwaiting = 1; +#ifdef ISP_SMPLOCK + (void) msleep(&isp->isp_mbxworkp, + &isp->isp_lock, PRIBIO, "isp_mboxwaiting", lim); +#else + (void) tsleep(&isp->isp_mbxworkp, + PRIBIO, "isp_mboxwaiting", lim); +#endif + if (isp->isp_mboxbsy != 0) { + isp_prt(isp, ISP_LOGWARN, + "Interrupting Mailbox Command (0x%x) Timeout", + isp->isp_lastmbxcmd); + isp->isp_mboxbsy = 0; + } + isp->isp_osinfo.mboxwaiting = 0; + } else { + int lim = ((isp->isp_mbxwrk0)? 240 : 60) * 10000; + int j; + for (j = 0; j < lim; j++) { + uint16_t isr, sema, mbox; + if (isp->isp_mboxbsy == 0) { + break; + } + if (ISP_READ_ISR(isp, &isr, &sema, &mbox)) { + isp_intr(isp, isr, sema, mbox); + if (isp->isp_mboxbsy == 0) { + break; + } + } + USEC_DELAY(500); + } + if (isp->isp_mboxbsy != 0) { + isp_prt(isp, ISP_LOGWARN, + "Polled Mailbox Command (0x%x) Timeout", + isp->isp_lastmbxcmd); + } + } +} + +static __inline uint64_t nanotime_sub(struct timespec *, struct timespec *); +static __inline uint64_t +nanotime_sub(struct timespec *b, struct timespec *a) +{ + uint64_t elapsed; + struct timespec x = *b; + timespecsub(&x, a); + elapsed = GET_NANOSEC(&x); + if (elapsed == 0) + elapsed++; + return (elapsed); +} + +static __inline char *strncat(char *, const char *, size_t); +static __inline char * +strncat(char *d, const char *s, size_t c) +{ + char *t = d; + + if (c) { + while (*d) + d++; + while ((*d++ = *s++)) { + if (--c == 0) { + *d = '\0'; + break; + } + } + } + return (t); +} + +/* + * ISP Library functions + */ + +#include <dev/isp/isp_library.h> + +#endif /* _ISP_FREEBSD_H */ diff --git a/sys/dev/isp/isp_ioctl.h b/sys/dev/isp/isp_ioctl.h new file mode 100644 index 000000000000..4171466e29d2 --- /dev/null +++ b/sys/dev/isp/isp_ioctl.h @@ -0,0 +1,195 @@ +/* $FreeBSD$ */ +/*- + * + * Copyright (c) 1997-2006 by Matthew Jacob + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * ioctl definitions for Qlogic FC/SCSI HBA driver + */ +#define ISP_IOC (021) /* 'Ctrl-Q' */ + +/* + * This ioctl sets/retrieves the debugging level for this hba instance. + * Note that this is not a simple integer level- see ispvar.h for definitions. + * + * The arguments is a pointer to an integer with the new debugging level. + * The old value is written into this argument. + */ + +#define ISP_SDBLEV _IOWR(ISP_IOC, 1, int) + +/* + * This ioctl resets the HBA. Use with caution. + */ +#define ISP_RESETHBA _IO(ISP_IOC, 2) + +/* + * This ioctl performs a fibre channel rescan. + */ +#define ISP_RESCAN _IO(ISP_IOC, 3) + +/* + * This ioctl performs a reset and then will set the adapter to the + * role that was passed in (the old role will be returned). It almost + * goes w/o saying: use with caution. + */ +#define ISP_SETROLE _IOWR(ISP_IOC, 4, int) + +#define ISP_ROLE_NONE 0x0 +#define ISP_ROLE_TARGET 0x1 +#define ISP_ROLE_INITIATOR 0x2 +#define ISP_ROLE_BOTH (ISP_ROLE_TARGET|ISP_ROLE_INITIATOR) + +/* + * Get the current adapter role + */ +#define ISP_GETROLE _IOR(ISP_IOC, 5, int) + +/* + * Get/Clear Stats + */ +#define ISP_STATS_VERSION 0 +typedef struct { + uint8_t isp_stat_version; + uint8_t isp_type; /* (ro) reflects chip type */ + uint8_t isp_revision; /* (ro) reflects chip version */ + uint8_t unused1; + uint32_t unused2; + /* + * Statistics Counters + */ +#define ISP_NSTATS 16 +#define ISP_INTCNT 0 +#define ISP_INTBOGUS 1 +#define ISP_INTMBOXC 2 +#define ISP_INGOASYNC 3 +#define ISP_RSLTCCMPLT 4 +#define ISP_FPHCCMCPLT 5 +#define ISP_RSCCHIWAT 6 +#define ISP_FPCCHIWAT 7 + uint64_t isp_stats[ISP_NSTATS]; +} isp_stats_t; + +#define ISP_GET_STATS _IOR(ISP_IOC, 6, isp_stats_t) +#define ISP_CLR_STATS _IO(ISP_IOC, 7) + +/* + * Initiate a LIP + */ +#define ISP_FC_LIP _IO(ISP_IOC, 8) + +/* + * Return the Port Database structure for the named device, or ENODEV if none. + * Caller fills in virtual loopid (0..255), aka 'target'. The driver returns + * ENODEV (if nothing valid there) or the actual loopid (for local loop devices + * only), 24 bit Port ID and Node and Port WWNs. + */ +struct isp_fc_device { + uint32_t loopid; /* 0..255 */ + uint32_t : 6, + role : 2, + portid : 24; /* 24 bit Port ID */ + uint64_t node_wwn; + uint64_t port_wwn; +}; +#define ISP_FC_GETDINFO _IOWR(ISP_IOC, 9, struct isp_fc_device) + +/* + * Get F/W crash dump + */ +#define ISP_GET_FW_CRASH_DUMP _IO(ISP_IOC, 10) +#define ISP_FORCE_CRASH_DUMP _IO(ISP_IOC, 11) + +/* + * Get information about this Host Adapter, including current connection + * topology and capabilities. + */ +struct isp_hba_device { + uint32_t + : 8, + : 4, + fc_speed : 4, /* Gbps */ + : 2, + fc_class2 : 1, + fc_ip_supported : 1, + fc_scsi_supported : 1, + fc_topology : 3, + fc_loopid : 8; + uint8_t fc_fw_major; + uint8_t fc_fw_minor; + uint8_t fc_fw_micro; + uint8_t reserved; + uint64_t nvram_node_wwn; + uint64_t nvram_port_wwn; + uint64_t active_node_wwn; + uint64_t active_port_wwn; +}; + +#define ISP_TOPO_UNKNOWN 0 /* connection topology unknown */ +#define ISP_TOPO_FCAL 1 /* private or PL_DA */ +#define ISP_TOPO_LPORT 2 /* public loop */ +#define ISP_TOPO_NPORT 3 /* N-port */ +#define ISP_TOPO_FPORT 4 /* F-port */ + +#define ISP_FC_GETHINFO _IOR(ISP_IOC, 12, struct isp_hba_device) +/* + * Set some internal parameters. This doesn't take effect until + * the chip is reset. + * + * Each parameter is generalized to be a name string with an integer value. + * + * Known parameters are: + * + * Name Value Range + * + * "framelength" 512,1024,2048 + * "exec_throttle" 16..255 + * "fullduplex" 0,1 + * "loopid" 0..125 + */ + +struct isp_fc_param { + char param_name[16]; /* null terminated */ + uint32_t parameter; +}; + +#define ISP_GET_FC_PARAM _IOWR(ISP_IOC, 98, struct isp_fc_param) +#define ISP_SET_FC_PARAM _IOWR(ISP_IOC, 99, struct isp_fc_param) + +/* + * Various Reset Goodies + */ +struct isp_fc_tsk_mgmt { + uint32_t loopid; /* 0..255 */ + uint32_t lun; + enum { + IPT_CLEAR_ACA, + IPT_TARGET_RESET, + IPT_LUN_RESET, + IPT_CLEAR_TASK_SET, + IPT_ABORT_TASK_SET + } action; +}; +#define ISP_TSK_MGMT _IOWR(ISP_IOC, 97, struct isp_fc_tsk_mgmt) diff --git a/sys/dev/isp/isp_library.c b/sys/dev/isp/isp_library.c new file mode 100644 index 000000000000..298197f25b8a --- /dev/null +++ b/sys/dev/isp/isp_library.c @@ -0,0 +1,1725 @@ +/*- + * Copyright (c) 1999-2006 by Matthew Jacob + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +/* + * Qlogic Host Adapter Internal Library Functions + */ +#ifdef __NetBSD__ +#include <dev/ic/isp_netbsd.h> +#endif +#ifdef __FreeBSD__ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); +#include <dev/isp/isp_freebsd.h> +#endif +#ifdef __OpenBSD__ +#include <dev/ic/isp_openbsd.h> +#endif +#ifdef __linux__ +#include "isp_linux.h" +#endif +#ifdef __svr4__ +#include "isp_solaris.h" +#endif + +int +isp_save_xs(ispsoftc_t *isp, XS_T *xs, uint16_t *handlep) +{ + int i, j; + + for (j = isp->isp_lasthdls, i = 0; i < (int) isp->isp_maxcmds; i++) { + if (isp->isp_xflist[j] == NULL) { + break; + } + if (++j == isp->isp_maxcmds) { + j = 0; + } + } + if (i == isp->isp_maxcmds) { + return (-1); + } + isp->isp_xflist[j] = xs; + *handlep = j+1; + if (++j == isp->isp_maxcmds) + j = 0; + isp->isp_lasthdls = (uint16_t)j; + return (0); +} + +XS_T * +isp_find_xs(ispsoftc_t *isp, uint16_t handle) +{ + if (handle < 1 || handle > (uint16_t) isp->isp_maxcmds) { + return (NULL); + } else { + return (isp->isp_xflist[handle - 1]); + } +} + +uint16_t +isp_find_handle(ispsoftc_t *isp, XS_T *xs) +{ + int i; + if (xs != NULL) { + for (i = 0; i < isp->isp_maxcmds; i++) { + if (isp->isp_xflist[i] == xs) { + return ((uint16_t) i+1); + } + } + } + return (0); +} + +int +isp_handle_index(uint16_t handle) +{ + return (handle-1); +} + +uint16_t +isp_index_handle(int index) +{ + return (index+1); +} + +void +isp_destroy_handle(ispsoftc_t *isp, uint16_t handle) +{ + if (handle > 0 && handle <= (uint16_t) isp->isp_maxcmds) { + isp->isp_xflist[handle - 1] = NULL; + } +} + +int +isp_getrqentry(ispsoftc_t *isp, uint16_t *iptrp, + uint16_t *optrp, void **resultp) +{ + volatile uint16_t iptr, optr; + + optr = isp->isp_reqodx = READ_REQUEST_QUEUE_OUT_POINTER(isp); + iptr = isp->isp_reqidx; + *resultp = ISP_QUEUE_ENTRY(isp->isp_rquest, iptr); + iptr = ISP_NXT_QENTRY(iptr, RQUEST_QUEUE_LEN(isp)); + if (iptr == optr) { + return (1); + } + if (optrp) + *optrp = optr; + if (iptrp) + *iptrp = iptr; + return (0); +} + +#define TBA (4 * (((QENTRY_LEN >> 2) * 3) + 1) + 1) +void +isp_print_qentry(ispsoftc_t *isp, char *msg, int idx, void *arg) +{ + char buf[TBA]; + int amt, i, j; + uint8_t *ptr = arg; + + isp_prt(isp, ISP_LOGALL, "%s index %d=>", msg, idx); + for (buf[0] = 0, amt = i = 0; i < 4; i++) { + buf[0] = 0; + SNPRINTF(buf, TBA, " "); + for (j = 0; j < (QENTRY_LEN >> 2); j++) { + SNPRINTF(buf, TBA, "%s %02x", buf, ptr[amt++] & 0xff); + } + isp_prt(isp, ISP_LOGALL, buf); + } +} + +void +isp_print_bytes(ispsoftc_t *isp, char *msg, int amt, void *arg) +{ + char buf[128]; + uint8_t *ptr = arg; + int off; + + if (msg) + isp_prt(isp, ISP_LOGALL, "%s:", msg); + off = 0; + buf[0] = 0; + while (off < amt) { + int j, to; + to = off; + for (j = 0; j < 16; j++) { + SNPRINTF(buf, 128, "%s %02x", buf, ptr[off++] & 0xff); + if (off == amt) + break; + } + isp_prt(isp, ISP_LOGALL, "0x%08x:%s", to, buf); + buf[0] = 0; + } +} + +/* + * Do the common path to try and ensure that link is up, we've scanned + * the fabric (if we're on a fabric), and that we've synchronized this + * all with our own database and done the appropriate logins. + * + * We repeatedly check for firmware state and loop state after each + * action because things may have changed while we were doing this. + * Any failure or change of state causes us to return a nonzero value. + * + * We honor HBA roles in that if we're not in Initiator mode, we don't + * attempt to sync up the database (that's for somebody else to do, + * if ever). + * + * We assume we enter here with any locks held. + */ + +int +isp_fc_runstate(ispsoftc_t *isp, int tval) +{ + fcparam *fcp; + int *tptr; + + if (IS_SCSI(isp)) + return (0); + + tptr = &tval; + if (isp_control(isp, ISPCTL_FCLINK_TEST, tptr) != 0) { + return (-1); + } + fcp = FCPARAM(isp); + if (fcp->isp_fwstate != FW_READY || fcp->isp_loopstate < LOOP_PDB_RCVD) + return (-1); + if (isp_control(isp, ISPCTL_SCAN_FABRIC, NULL) != 0) { + return (-1); + } + if (isp_control(isp, ISPCTL_SCAN_LOOP, NULL) != 0) { + return (-1); + } + if ((isp->isp_role & ISP_ROLE_INITIATOR) == 0) { + return (0); + } + if (isp_control(isp, ISPCTL_PDB_SYNC, NULL) != 0) { + return (-1); + } + if (fcp->isp_fwstate != FW_READY || fcp->isp_loopstate != LOOP_READY) { + return (-1); + } + return (0); +} + +/* + * Functions to move stuff to a form that the QLogic RISC engine understands + * and functions to move stuff back to a form the processor understands. + * + * Each platform is required to provide the 8, 16 and 32 bit + * swizzle and unswizzle macros (ISP_IOX{PUT|GET}_{8,16,32}) + * + * The assumption is that swizzling and unswizzling is mostly done 'in place' + * (with a few exceptions for efficiency). + */ + +#define ISP_IS_SBUS(isp) \ + (ISP_SBUS_SUPPORTED && (isp)->isp_bustype == ISP_BT_SBUS) + +/* + * Swizzle/Copy Functions + */ +void +isp_copy_out_hdr(ispsoftc_t *isp, isphdr_t *hpsrc, isphdr_t *hpdst) +{ + if (ISP_IS_SBUS(isp)) { + ISP_IOXPUT_8(isp, hpsrc->rqs_entry_type, + &hpdst->rqs_entry_count); + ISP_IOXPUT_8(isp, hpsrc->rqs_entry_count, + &hpdst->rqs_entry_type); + ISP_IOXPUT_8(isp, hpsrc->rqs_seqno, + &hpdst->rqs_flags); + ISP_IOXPUT_8(isp, hpsrc->rqs_flags, + &hpdst->rqs_seqno); + } else { + ISP_IOXPUT_8(isp, hpsrc->rqs_entry_type, + &hpdst->rqs_entry_type); + ISP_IOXPUT_8(isp, hpsrc->rqs_entry_count, + &hpdst->rqs_entry_count); + ISP_IOXPUT_8(isp, hpsrc->rqs_seqno, + &hpdst->rqs_seqno); + ISP_IOXPUT_8(isp, hpsrc->rqs_flags, + &hpdst->rqs_flags); + } +} + +void +isp_copy_in_hdr(ispsoftc_t *isp, isphdr_t *hpsrc, isphdr_t *hpdst) +{ + if (ISP_IS_SBUS(isp)) { + ISP_IOXGET_8(isp, &hpsrc->rqs_entry_type, + hpdst->rqs_entry_count); + ISP_IOXGET_8(isp, &hpsrc->rqs_entry_count, + hpdst->rqs_entry_type); + ISP_IOXGET_8(isp, &hpsrc->rqs_seqno, + hpdst->rqs_flags); + ISP_IOXGET_8(isp, &hpsrc->rqs_flags, + hpdst->rqs_seqno); + } else { + ISP_IOXGET_8(isp, &hpsrc->rqs_entry_type, + hpdst->rqs_entry_type); + ISP_IOXGET_8(isp, &hpsrc->rqs_entry_count, + hpdst->rqs_entry_count); + ISP_IOXGET_8(isp, &hpsrc->rqs_seqno, + hpdst->rqs_seqno); + ISP_IOXGET_8(isp, &hpsrc->rqs_flags, + hpdst->rqs_flags); + } +} + +int +isp_get_response_type(ispsoftc_t *isp, isphdr_t *hp) +{ + uint8_t type; + if (ISP_IS_SBUS(isp)) { + ISP_IOXGET_8(isp, &hp->rqs_entry_count, type); + } else { + ISP_IOXGET_8(isp, &hp->rqs_entry_type, type); + } + return ((int)type); +} + +void +isp_put_request(ispsoftc_t *isp, ispreq_t *rqsrc, ispreq_t *rqdst) +{ + int i; + isp_copy_out_hdr(isp, &rqsrc->req_header, &rqdst->req_header); + ISP_IOXPUT_32(isp, rqsrc->req_handle, &rqdst->req_handle); + if (ISP_IS_SBUS(isp)) { + ISP_IOXPUT_8(isp, rqsrc->req_lun_trn, &rqdst->req_target); + ISP_IOXPUT_8(isp, rqsrc->req_target, &rqdst->req_lun_trn); + } else { + ISP_IOXPUT_8(isp, rqsrc->req_lun_trn, &rqdst->req_lun_trn); + ISP_IOXPUT_8(isp, rqsrc->req_target, &rqdst->req_target); + } + ISP_IOXPUT_16(isp, rqsrc->req_cdblen, &rqdst->req_cdblen); + ISP_IOXPUT_16(isp, rqsrc->req_flags, &rqdst->req_flags); + ISP_IOXPUT_16(isp, rqsrc->req_time, &rqdst->req_time); + ISP_IOXPUT_16(isp, rqsrc->req_seg_count, &rqdst->req_seg_count); + for (i = 0; i < 12; i++) { + ISP_IOXPUT_8(isp, rqsrc->req_cdb[i], &rqdst->req_cdb[i]); + } + for (i = 0; i < ISP_RQDSEG; i++) { + ISP_IOXPUT_32(isp, rqsrc->req_dataseg[i].ds_base, + &rqdst->req_dataseg[i].ds_base); + ISP_IOXPUT_32(isp, rqsrc->req_dataseg[i].ds_count, + &rqdst->req_dataseg[i].ds_count); + } +} + +void +isp_put_request_t2(ispsoftc_t *isp, ispreqt2_t *tqsrc, ispreqt2_t *tqdst) +{ + int i; + isp_copy_out_hdr(isp, &tqsrc->req_header, &tqdst->req_header); + ISP_IOXPUT_32(isp, tqsrc->req_handle, &tqdst->req_handle); + ISP_IOXPUT_8(isp, tqsrc->req_lun_trn, &tqdst->req_lun_trn); + ISP_IOXPUT_8(isp, tqsrc->req_target, &tqdst->req_target); + ISP_IOXPUT_16(isp, tqsrc->req_scclun, &tqdst->req_scclun); + ISP_IOXPUT_16(isp, tqsrc->req_flags, &tqdst->req_flags); + ISP_IOXPUT_16(isp, tqsrc->_res2, &tqdst->_res2); + ISP_IOXPUT_16(isp, tqsrc->req_time, &tqdst->req_time); + ISP_IOXPUT_16(isp, tqsrc->req_seg_count, &tqdst->req_seg_count); + for (i = 0; i < 16; i++) { + ISP_IOXPUT_8(isp, tqsrc->req_cdb[i], &tqdst->req_cdb[i]); + } + ISP_IOXPUT_32(isp, tqsrc->req_totalcnt, &tqdst->req_totalcnt); + for (i = 0; i < ISP_RQDSEG_T2; i++) { + ISP_IOXPUT_32(isp, tqsrc->req_dataseg[i].ds_base, + &tqdst->req_dataseg[i].ds_base); + ISP_IOXPUT_32(isp, tqsrc->req_dataseg[i].ds_count, + &tqdst->req_dataseg[i].ds_count); + } +} + +void +isp_put_request_t2e(ispsoftc_t *isp, ispreqt2e_t *tqsrc, ispreqt2e_t *tqdst) +{ + int i; + isp_copy_out_hdr(isp, &tqsrc->req_header, &tqdst->req_header); + ISP_IOXPUT_32(isp, tqsrc->req_handle, &tqdst->req_handle); + ISP_IOXPUT_16(isp, tqsrc->req_target, &tqdst->req_target); + ISP_IOXPUT_16(isp, tqsrc->req_scclun, &tqdst->req_scclun); + ISP_IOXPUT_16(isp, tqsrc->req_flags, &tqdst->req_flags); + ISP_IOXPUT_16(isp, tqsrc->_res2, &tqdst->_res2); + ISP_IOXPUT_16(isp, tqsrc->req_time, &tqdst->req_time); + ISP_IOXPUT_16(isp, tqsrc->req_seg_count, &tqdst->req_seg_count); + for (i = 0; i < 16; i++) { + ISP_IOXPUT_8(isp, tqsrc->req_cdb[i], &tqdst->req_cdb[i]); + } + ISP_IOXPUT_32(isp, tqsrc->req_totalcnt, &tqdst->req_totalcnt); + for (i = 0; i < ISP_RQDSEG_T2; i++) { + ISP_IOXPUT_32(isp, tqsrc->req_dataseg[i].ds_base, + &tqdst->req_dataseg[i].ds_base); + ISP_IOXPUT_32(isp, tqsrc->req_dataseg[i].ds_count, + &tqdst->req_dataseg[i].ds_count); + } +} + +void +isp_put_request_t3(ispsoftc_t *isp, ispreqt3_t *tqsrc, ispreqt3_t *tqdst) +{ + int i; + isp_copy_out_hdr(isp, &tqsrc->req_header, &tqdst->req_header); + ISP_IOXPUT_32(isp, tqsrc->req_handle, &tqdst->req_handle); + ISP_IOXPUT_8(isp, tqsrc->req_lun_trn, &tqdst->req_lun_trn); + ISP_IOXPUT_8(isp, tqsrc->req_target, &tqdst->req_target); + ISP_IOXPUT_16(isp, tqsrc->req_scclun, &tqdst->req_scclun); + ISP_IOXPUT_16(isp, tqsrc->req_flags, &tqdst->req_flags); + ISP_IOXPUT_16(isp, tqsrc->_res2, &tqdst->_res2); + ISP_IOXPUT_16(isp, tqsrc->req_time, &tqdst->req_time); + ISP_IOXPUT_16(isp, tqsrc->req_seg_count, &tqdst->req_seg_count); + for (i = 0; i < 16; i++) { + ISP_IOXPUT_8(isp, tqsrc->req_cdb[i], &tqdst->req_cdb[i]); + } + ISP_IOXPUT_32(isp, tqsrc->req_totalcnt, &tqdst->req_totalcnt); + for (i = 0; i < ISP_RQDSEG_T3; i++) { + ISP_IOXPUT_32(isp, tqsrc->req_dataseg[i].ds_base, + &tqdst->req_dataseg[i].ds_base); + ISP_IOXPUT_32(isp, tqsrc->req_dataseg[i].ds_basehi, + &tqdst->req_dataseg[i].ds_basehi); + ISP_IOXPUT_32(isp, tqsrc->req_dataseg[i].ds_count, + &tqdst->req_dataseg[i].ds_count); + } +} + +void +isp_put_request_t3e(ispsoftc_t *isp, ispreqt3e_t *tqsrc, ispreqt3e_t *tqdst) +{ + int i; + isp_copy_out_hdr(isp, &tqsrc->req_header, &tqdst->req_header); + ISP_IOXPUT_32(isp, tqsrc->req_handle, &tqdst->req_handle); + ISP_IOXPUT_16(isp, tqsrc->req_target, &tqdst->req_target); + ISP_IOXPUT_16(isp, tqsrc->req_scclun, &tqdst->req_scclun); + ISP_IOXPUT_16(isp, tqsrc->req_flags, &tqdst->req_flags); + ISP_IOXPUT_16(isp, tqsrc->_res2, &tqdst->_res2); + ISP_IOXPUT_16(isp, tqsrc->req_time, &tqdst->req_time); + ISP_IOXPUT_16(isp, tqsrc->req_seg_count, &tqdst->req_seg_count); + for (i = 0; i < 16; i++) { + ISP_IOXPUT_8(isp, tqsrc->req_cdb[i], &tqdst->req_cdb[i]); + } + ISP_IOXPUT_32(isp, tqsrc->req_totalcnt, &tqdst->req_totalcnt); + for (i = 0; i < ISP_RQDSEG_T3; i++) { + ISP_IOXPUT_32(isp, tqsrc->req_dataseg[i].ds_base, + &tqdst->req_dataseg[i].ds_base); + ISP_IOXPUT_32(isp, tqsrc->req_dataseg[i].ds_basehi, + &tqdst->req_dataseg[i].ds_basehi); + ISP_IOXPUT_32(isp, tqsrc->req_dataseg[i].ds_count, + &tqdst->req_dataseg[i].ds_count); + } +} + +void +isp_put_extended_request(ispsoftc_t *isp, ispextreq_t *xqsrc, + ispextreq_t *xqdst) +{ + int i; + isp_copy_out_hdr(isp, &xqsrc->req_header, &xqdst->req_header); + ISP_IOXPUT_32(isp, xqsrc->req_handle, &xqdst->req_handle); + if (ISP_IS_SBUS(isp)) { + ISP_IOXPUT_8(isp, xqsrc->req_lun_trn, &xqdst->req_target); + ISP_IOXPUT_8(isp, xqsrc->req_target, &xqdst->req_lun_trn); + } else { + ISP_IOXPUT_8(isp, xqsrc->req_lun_trn, &xqdst->req_lun_trn); + ISP_IOXPUT_8(isp, xqsrc->req_target, &xqdst->req_target); + } + ISP_IOXPUT_16(isp, xqsrc->req_cdblen, &xqdst->req_cdblen); + ISP_IOXPUT_16(isp, xqsrc->req_flags, &xqdst->req_flags); + ISP_IOXPUT_16(isp, xqsrc->req_time, &xqdst->req_time); + ISP_IOXPUT_16(isp, xqsrc->req_seg_count, &xqdst->req_seg_count); + for (i = 0; i < 44; i++) { + ISP_IOXPUT_8(isp, xqsrc->req_cdb[i], &xqdst->req_cdb[i]); + } +} + +void +isp_put_cont_req(ispsoftc_t *isp, ispcontreq_t *cqsrc, ispcontreq_t *cqdst) +{ + int i; + isp_copy_out_hdr(isp, &cqsrc->req_header, &cqdst->req_header); + for (i = 0; i < ISP_CDSEG; i++) { + ISP_IOXPUT_32(isp, cqsrc->req_dataseg[i].ds_base, + &cqdst->req_dataseg[i].ds_base); + ISP_IOXPUT_32(isp, cqsrc->req_dataseg[i].ds_count, + &cqdst->req_dataseg[i].ds_count); + } +} + +void +isp_put_cont64_req(ispsoftc_t *isp, ispcontreq64_t *cqsrc, + ispcontreq64_t *cqdst) +{ + int i; + isp_copy_out_hdr(isp, &cqsrc->req_header, &cqdst->req_header); + for (i = 0; i < ISP_CDSEG64; i++) { + ISP_IOXPUT_32(isp, cqsrc->req_dataseg[i].ds_base, + &cqdst->req_dataseg[i].ds_base); + ISP_IOXPUT_32(isp, cqsrc->req_dataseg[i].ds_basehi, + &cqdst->req_dataseg[i].ds_basehi); + ISP_IOXPUT_32(isp, cqsrc->req_dataseg[i].ds_count, + &cqdst->req_dataseg[i].ds_count); + } +} + +void +isp_get_response(ispsoftc_t *isp, ispstatusreq_t *spsrc, + ispstatusreq_t *spdst) +{ + int i; + isp_copy_in_hdr(isp, &spsrc->req_header, &spdst->req_header); + ISP_IOXGET_32(isp, &spsrc->req_handle, spdst->req_handle); + ISP_IOXGET_16(isp, &spsrc->req_scsi_status, spdst->req_scsi_status); + ISP_IOXGET_16(isp, &spsrc->req_completion_status, + spdst->req_completion_status); + ISP_IOXGET_16(isp, &spsrc->req_state_flags, spdst->req_state_flags); + ISP_IOXGET_16(isp, &spsrc->req_status_flags, spdst->req_status_flags); + ISP_IOXGET_16(isp, &spsrc->req_time, spdst->req_time); + ISP_IOXGET_16(isp, &spsrc->req_sense_len, spdst->req_sense_len); + ISP_IOXGET_32(isp, &spsrc->req_resid, spdst->req_resid); + for (i = 0; i < 8; i++) { + ISP_IOXGET_8(isp, &spsrc->req_response[i], + spdst->req_response[i]); + } + for (i = 0; i < 32; i++) { + ISP_IOXGET_8(isp, &spsrc->req_sense_data[i], + spdst->req_sense_data[i]); + } +} + +void +isp_get_response_x(ispsoftc_t *isp, ispstatus_cont_t *cpsrc, + ispstatus_cont_t *cpdst) +{ + int i; + isp_copy_in_hdr(isp, &cpsrc->req_header, &cpdst->req_header); + for (i = 0; i < 60; i++) { + ISP_IOXGET_8(isp, &cpsrc->req_sense_data[i], + cpdst->req_sense_data[i]); + } +} + +void +isp_get_rio2(ispsoftc_t *isp, isp_rio2_t *r2src, isp_rio2_t *r2dst) +{ + int i; + isp_copy_in_hdr(isp, &r2src->req_header, &r2dst->req_header); + if (r2dst->req_header.rqs_seqno > 30) + r2dst->req_header.rqs_seqno = 30; + for (i = 0; i < r2dst->req_header.rqs_seqno; i++) { + ISP_IOXGET_16(isp, &r2src->req_handles[i], + r2dst->req_handles[i]); + } + while (i < 30) { + r2dst->req_handles[i++] = 0; + } +} + +void +isp_put_icb(ispsoftc_t *isp, isp_icb_t *Is, isp_icb_t *Id) +{ + int i; + if (ISP_IS_SBUS(isp)) { + ISP_IOXPUT_8(isp, Is->icb_version, &Id->_reserved0); + ISP_IOXPUT_8(isp, Is->_reserved0, &Id->icb_version); + } else { + ISP_IOXPUT_8(isp, Is->icb_version, &Id->icb_version); + ISP_IOXPUT_8(isp, Is->_reserved0, &Id->_reserved0); + } + ISP_IOXPUT_16(isp, Is->icb_fwoptions, &Id->icb_fwoptions); + ISP_IOXPUT_16(isp, Is->icb_maxfrmlen, &Id->icb_maxfrmlen); + ISP_IOXPUT_16(isp, Is->icb_maxalloc, &Id->icb_maxalloc); + ISP_IOXPUT_16(isp, Is->icb_execthrottle, &Id->icb_execthrottle); + if (ISP_IS_SBUS(isp)) { + ISP_IOXPUT_8(isp, Is->icb_retry_count, &Id->icb_retry_delay); + ISP_IOXPUT_8(isp, Is->icb_retry_delay, &Id->icb_retry_count); + } else { + ISP_IOXPUT_8(isp, Is->icb_retry_count, &Id->icb_retry_count); + ISP_IOXPUT_8(isp, Is->icb_retry_delay, &Id->icb_retry_delay); + } + for (i = 0; i < 8; i++) { + ISP_IOXPUT_8(isp, Is->icb_portname[i], &Id->icb_portname[i]); + } + ISP_IOXPUT_16(isp, Is->icb_hardaddr, &Id->icb_hardaddr); + if (ISP_IS_SBUS(isp)) { + ISP_IOXPUT_8(isp, Is->icb_iqdevtype, &Id->icb_logintime); + ISP_IOXPUT_8(isp, Is->icb_logintime, &Id->icb_iqdevtype); + } else { + ISP_IOXPUT_8(isp, Is->icb_iqdevtype, &Id->icb_iqdevtype); + ISP_IOXPUT_8(isp, Is->icb_logintime, &Id->icb_logintime); + } + for (i = 0; i < 8; i++) { + ISP_IOXPUT_8(isp, Is->icb_nodename[i], &Id->icb_nodename[i]); + } + ISP_IOXPUT_16(isp, Is->icb_rqstout, &Id->icb_rqstout); + ISP_IOXPUT_16(isp, Is->icb_rspnsin, &Id->icb_rspnsin); + ISP_IOXPUT_16(isp, Is->icb_rqstqlen, &Id->icb_rqstqlen); + ISP_IOXPUT_16(isp, Is->icb_rsltqlen, &Id->icb_rsltqlen); + for (i = 0; i < 4; i++) { + ISP_IOXPUT_16(isp, Is->icb_rqstaddr[i], &Id->icb_rqstaddr[i]); + } + for (i = 0; i < 4; i++) { + ISP_IOXPUT_16(isp, Is->icb_respaddr[i], &Id->icb_respaddr[i]); + } + ISP_IOXPUT_16(isp, Is->icb_lunenables, &Id->icb_lunenables); + if (ISP_IS_SBUS(isp)) { + ISP_IOXPUT_8(isp, Is->icb_ccnt, &Id->icb_icnt); + ISP_IOXPUT_8(isp, Is->icb_icnt, &Id->icb_ccnt); + } else { + ISP_IOXPUT_8(isp, Is->icb_ccnt, &Id->icb_ccnt); + ISP_IOXPUT_8(isp, Is->icb_icnt, &Id->icb_icnt); + } + ISP_IOXPUT_16(isp, Is->icb_lunetimeout, &Id->icb_lunetimeout); + ISP_IOXPUT_16(isp, Is->icb_xfwoptions, &Id->icb_xfwoptions); + if (ISP_IS_SBUS(isp)) { + ISP_IOXPUT_8(isp, Is->icb_racctimer, &Id->icb_idelaytimer); + ISP_IOXPUT_8(isp, Is->icb_idelaytimer, &Id->icb_racctimer); + } else { + ISP_IOXPUT_8(isp, Is->icb_racctimer, &Id->icb_racctimer); + ISP_IOXPUT_8(isp, Is->icb_idelaytimer, &Id->icb_idelaytimer); + } + ISP_IOXPUT_16(isp, Is->icb_zfwoptions, &Id->icb_zfwoptions); +} + +void +isp_get_pdb(ispsoftc_t *isp, isp_pdb_t *src, isp_pdb_t *dst) +{ + int i; + ISP_IOXGET_16(isp, &src->pdb_options, dst->pdb_options); + ISP_IOXGET_8(isp, &src->pdb_mstate, dst->pdb_mstate); + ISP_IOXGET_8(isp, &src->pdb_sstate, dst->pdb_sstate); + for (i = 0; i < 4; i++) { + ISP_IOXGET_8(isp, &src->pdb_hardaddr_bits[i], + dst->pdb_hardaddr_bits[i]); + } + for (i = 0; i < 4; i++) { + ISP_IOXGET_8(isp, &src->pdb_portid_bits[i], + dst->pdb_portid_bits[i]); + } + for (i = 0; i < 8; i++) { + ISP_IOXGET_8(isp, &src->pdb_nodename[i], dst->pdb_nodename[i]); + } + for (i = 0; i < 8; i++) { + ISP_IOXGET_8(isp, &src->pdb_portname[i], dst->pdb_portname[i]); + } + ISP_IOXGET_16(isp, &src->pdb_execthrottle, dst->pdb_execthrottle); + ISP_IOXGET_16(isp, &src->pdb_exec_count, dst->pdb_exec_count); + ISP_IOXGET_8(isp, &src->pdb_retry_count, dst->pdb_retry_count); + ISP_IOXGET_8(isp, &src->pdb_retry_delay, dst->pdb_retry_delay); + ISP_IOXGET_16(isp, &src->pdb_resalloc, dst->pdb_resalloc); + ISP_IOXGET_16(isp, &src->pdb_curalloc, dst->pdb_curalloc); + ISP_IOXGET_16(isp, &src->pdb_qhead, dst->pdb_qhead); + ISP_IOXGET_16(isp, &src->pdb_qtail, dst->pdb_qtail); + ISP_IOXGET_16(isp, &src->pdb_tl_next, dst->pdb_tl_next); + ISP_IOXGET_16(isp, &src->pdb_tl_last, dst->pdb_tl_last); + ISP_IOXGET_16(isp, &src->pdb_features, dst->pdb_features); + ISP_IOXGET_16(isp, &src->pdb_pconcurrnt, dst->pdb_pconcurrnt); + ISP_IOXGET_16(isp, &src->pdb_roi, dst->pdb_roi); + ISP_IOXGET_8(isp, &src->pdb_target, dst->pdb_target); + ISP_IOXGET_8(isp, &src->pdb_initiator, dst->pdb_initiator); + ISP_IOXGET_16(isp, &src->pdb_rdsiz, dst->pdb_rdsiz); + ISP_IOXGET_16(isp, &src->pdb_ncseq, dst->pdb_ncseq); + ISP_IOXGET_16(isp, &src->pdb_noseq, dst->pdb_noseq); + ISP_IOXGET_16(isp, &src->pdb_labrtflg, dst->pdb_labrtflg); + ISP_IOXGET_16(isp, &src->pdb_lstopflg, dst->pdb_lstopflg); + ISP_IOXGET_16(isp, &src->pdb_sqhead, dst->pdb_sqhead); + ISP_IOXGET_16(isp, &src->pdb_sqtail, dst->pdb_sqtail); + ISP_IOXGET_16(isp, &src->pdb_ptimer, dst->pdb_ptimer); + ISP_IOXGET_16(isp, &src->pdb_nxt_seqid, dst->pdb_nxt_seqid); + ISP_IOXGET_16(isp, &src->pdb_fcount, dst->pdb_fcount); + ISP_IOXGET_16(isp, &src->pdb_prli_len, dst->pdb_prli_len); + ISP_IOXGET_16(isp, &src->pdb_prli_svc0, dst->pdb_prli_svc0); + ISP_IOXGET_16(isp, &src->pdb_prli_svc3, dst->pdb_prli_svc3); + ISP_IOXGET_16(isp, &src->pdb_loopid, dst->pdb_loopid); + ISP_IOXGET_16(isp, &src->pdb_il_ptr, dst->pdb_il_ptr); + ISP_IOXGET_16(isp, &src->pdb_sl_ptr, dst->pdb_sl_ptr); +} + + +/* + * CT_HDR canonicalization- only needed for SNS responses + */ +void +isp_get_ct_hdr(ispsoftc_t *isp, ct_hdr_t *src, ct_hdr_t *dst) +{ + ISP_IOXGET_8(isp, &src->ct_revision, dst->ct_revision); + ISP_IOXGET_8(isp, &src->ct_portid[0], dst->ct_portid[0]); + ISP_IOXGET_8(isp, &src->ct_portid[1], dst->ct_portid[1]); + ISP_IOXGET_8(isp, &src->ct_portid[2], dst->ct_portid[2]); + ISP_IOXGET_8(isp, &src->ct_fcs_type, dst->ct_fcs_type); + ISP_IOXGET_8(isp, &src->ct_fcs_subtype, dst->ct_fcs_subtype); + ISP_IOXGET_8(isp, &src->ct_options, dst->ct_options); + ISP_IOXGET_8(isp, &src->ct_res0, dst->ct_res0); + ISP_IOXGET_16(isp, &src->ct_response, dst->ct_response); + dst->ct_response = (dst->ct_response << 8) | (dst->ct_response >> 8); + ISP_IOXGET_16(isp, &src->ct_resid, dst->ct_resid); + dst->ct_resid = (dst->ct_resid << 8) | (dst->ct_resid >> 8); + ISP_IOXGET_8(isp, &src->ct_res1, dst->ct_res1); + ISP_IOXGET_8(isp, &src->ct_reason, dst->ct_reason); + ISP_IOXGET_8(isp, &src->ct_explanation, dst->ct_explanation); + ISP_IOXGET_8(isp, &src->ct_vunique, dst->ct_vunique); +} + +/* + * Generic SNS request - not particularly useful since the per-command data + * isn't always 16 bit words. + */ +void +isp_put_sns_request(ispsoftc_t *isp, sns_screq_t *src, sns_screq_t *dst) +{ + int i, nw = (int) src->snscb_sblen; + ISP_IOXPUT_16(isp, src->snscb_rblen, &dst->snscb_rblen); + for (i = 0; i < 4; i++) { + ISP_IOXPUT_16(isp, src->snscb_addr[i], &dst->snscb_addr[i]); + } + ISP_IOXPUT_16(isp, src->snscb_sblen, &dst->snscb_sblen); + for (i = 0; i < nw; i++) { + ISP_IOXPUT_16(isp, src->snscb_data[i], &dst->snscb_data[i]); + } + +} + +void +isp_put_gid_ft_request(ispsoftc_t *isp, sns_gid_ft_req_t *src, + sns_gid_ft_req_t *dst) +{ + ISP_IOXPUT_16(isp, src->snscb_rblen, &dst->snscb_rblen); + ISP_IOXPUT_16(isp, src->snscb_res0, &dst->snscb_res0); + ISP_IOXPUT_16(isp, src->snscb_addr[0], &dst->snscb_addr[0]); + ISP_IOXPUT_16(isp, src->snscb_addr[1], &dst->snscb_addr[1]); + ISP_IOXPUT_16(isp, src->snscb_addr[2], &dst->snscb_addr[2]); + ISP_IOXPUT_16(isp, src->snscb_addr[3], &dst->snscb_addr[3]); + ISP_IOXPUT_16(isp, src->snscb_sblen, &dst->snscb_sblen); + ISP_IOXPUT_16(isp, src->snscb_res1, &dst->snscb_res1); + ISP_IOXPUT_16(isp, src->snscb_cmd, &dst->snscb_cmd); + ISP_IOXPUT_16(isp, src->snscb_mword_div_2, &dst->snscb_mword_div_2); + ISP_IOXPUT_32(isp, src->snscb_res3, &dst->snscb_res3); + ISP_IOXPUT_32(isp, src->snscb_fc4_type, &dst->snscb_fc4_type); +} + +void +isp_put_gxn_id_request(ispsoftc_t *isp, sns_gxn_id_req_t *src, + sns_gxn_id_req_t *dst) +{ + ISP_IOXPUT_16(isp, src->snscb_rblen, &dst->snscb_rblen); + ISP_IOXPUT_16(isp, src->snscb_res0, &dst->snscb_res0); + ISP_IOXPUT_16(isp, src->snscb_addr[0], &dst->snscb_addr[0]); + ISP_IOXPUT_16(isp, src->snscb_addr[1], &dst->snscb_addr[1]); + ISP_IOXPUT_16(isp, src->snscb_addr[2], &dst->snscb_addr[2]); + ISP_IOXPUT_16(isp, src->snscb_addr[3], &dst->snscb_addr[3]); + ISP_IOXPUT_16(isp, src->snscb_sblen, &dst->snscb_sblen); + ISP_IOXPUT_16(isp, src->snscb_res1, &dst->snscb_res1); + ISP_IOXPUT_16(isp, src->snscb_cmd, &dst->snscb_cmd); + ISP_IOXPUT_16(isp, src->snscb_res2, &dst->snscb_res2); + ISP_IOXPUT_32(isp, src->snscb_res3, &dst->snscb_res3); + ISP_IOXPUT_32(isp, src->snscb_portid, &dst->snscb_portid); +} + +/* + * Generic SNS response - not particularly useful since the per-command data + * isn't always 16 bit words. + */ +void +isp_get_sns_response(ispsoftc_t *isp, sns_scrsp_t *src, + sns_scrsp_t *dst, int nwords) +{ + int i; + isp_get_ct_hdr(isp, &src->snscb_cthdr, &dst->snscb_cthdr); + ISP_IOXGET_8(isp, &src->snscb_port_type, dst->snscb_port_type); + for (i = 0; i < 3; i++) { + ISP_IOXGET_8(isp, &src->snscb_port_id[i], + dst->snscb_port_id[i]); + } + for (i = 0; i < 8; i++) { + ISP_IOXGET_8(isp, &src->snscb_portname[i], + dst->snscb_portname[i]); + } + for (i = 0; i < nwords; i++) { + ISP_IOXGET_16(isp, &src->snscb_data[i], dst->snscb_data[i]); + } +} + +void +isp_get_gid_ft_response(ispsoftc_t *isp, sns_gid_ft_rsp_t *src, + sns_gid_ft_rsp_t *dst, int nwords) +{ + int i; + isp_get_ct_hdr(isp, &src->snscb_cthdr, &dst->snscb_cthdr); + for (i = 0; i < nwords; i++) { + int j; + ISP_IOXGET_8(isp, + &src->snscb_ports[i].control, + dst->snscb_ports[i].control); + for (j = 0; j < 3; j++) { + ISP_IOXGET_8(isp, + &src->snscb_ports[i].portid[j], + dst->snscb_ports[i].portid[j]); + } + if (dst->snscb_ports[i].control & 0x80) { + break; + } + } +} + +void +isp_get_gxn_id_response(ispsoftc_t *isp, sns_gxn_id_rsp_t *src, + sns_gxn_id_rsp_t *dst) +{ + int i; + isp_get_ct_hdr(isp, &src->snscb_cthdr, &dst->snscb_cthdr); + for (i = 0; i < 8; i++) + ISP_IOXGET_8(isp, &src->snscb_wwn[i], dst->snscb_wwn[i]); +} + +void +isp_get_gff_id_response(ispsoftc_t *isp, sns_gff_id_rsp_t *src, + sns_gff_id_rsp_t *dst) +{ + int i; + isp_get_ct_hdr(isp, &src->snscb_cthdr, &dst->snscb_cthdr); + for (i = 0; i < 32; i++) { + ISP_IOXGET_32(isp, &src->snscb_fc4_features[i], + dst->snscb_fc4_features[i]); + } +} + +void +isp_get_ga_nxt_response(ispsoftc_t *isp, sns_ga_nxt_rsp_t *src, + sns_ga_nxt_rsp_t *dst) +{ + int i; + isp_get_ct_hdr(isp, &src->snscb_cthdr, &dst->snscb_cthdr); + ISP_IOXGET_8(isp, &src->snscb_port_type, dst->snscb_port_type); + for (i = 0; i < 3; i++) { + ISP_IOXGET_8(isp, &src->snscb_port_id[i], + dst->snscb_port_id[i]); + } + for (i = 0; i < 8; i++) { + ISP_IOXGET_8(isp, &src->snscb_portname[i], + dst->snscb_portname[i]); + } + ISP_IOXGET_8(isp, &src->snscb_pnlen, dst->snscb_pnlen); + for (i = 0; i < 255; i++) { + ISP_IOXGET_8(isp, &src->snscb_pname[i], dst->snscb_pname[i]); + } + for (i = 0; i < 8; i++) { + ISP_IOXGET_8(isp, &src->snscb_nodename[i], + dst->snscb_nodename[i]); + } + ISP_IOXGET_8(isp, &src->snscb_nnlen, dst->snscb_nnlen); + for (i = 0; i < 255; i++) { + ISP_IOXGET_8(isp, &src->snscb_nname[i], dst->snscb_nname[i]); + } + for (i = 0; i < 8; i++) { + ISP_IOXGET_8(isp, &src->snscb_ipassoc[i], + dst->snscb_ipassoc[i]); + } + for (i = 0; i < 16; i++) { + ISP_IOXGET_8(isp, &src->snscb_ipaddr[i], dst->snscb_ipaddr[i]); + } + for (i = 0; i < 4; i++) { + ISP_IOXGET_8(isp, &src->snscb_svc_class[i], + dst->snscb_svc_class[i]); + } + for (i = 0; i < 32; i++) { + ISP_IOXGET_8(isp, &src->snscb_fc4_types[i], + dst->snscb_fc4_types[i]); + } + for (i = 0; i < 8; i++) { + ISP_IOXGET_8(isp, &src->snscb_fpname[i], dst->snscb_fpname[i]); + } + ISP_IOXGET_8(isp, &src->snscb_reserved, dst->snscb_reserved); + for (i = 0; i < 3; i++) { + ISP_IOXGET_8(isp, &src->snscb_hardaddr[i], + dst->snscb_hardaddr[i]); + } +} + +#ifdef ISP_TARGET_MODE + +int +isp_save_xs_tgt(ispsoftc_t *isp, void *xs, uint16_t *handlep) +{ + int i; + + for (i = 0; i < (int) isp->isp_maxcmds; i++) { + if (isp->isp_tgtlist[i] == NULL) { + break; + } + } + if (i == isp->isp_maxcmds) { + return (-1); + } + isp->isp_tgtlist[i] = xs; + *handlep = i+1; + return (0); +} + +void * +isp_find_xs_tgt(ispsoftc_t *isp, uint16_t handle) +{ + if (handle < 1 || handle > (uint16_t) isp->isp_maxcmds) { + return (NULL); + } else { + return (isp->isp_tgtlist[handle - 1]); + } +} + +uint16_t +isp_find_tgt_handle(ispsoftc_t *isp, void *xs) +{ + int i; + if (xs != NULL) { + for (i = 0; i < isp->isp_maxcmds; i++) { + if (isp->isp_tgtlist[i] == xs) { + return ((uint16_t) i+1); + } + } + } + return (0); +} + +void +isp_destroy_tgt_handle(ispsoftc_t *isp, uint16_t handle) +{ + if (handle > 0 && handle <= (uint16_t) isp->isp_maxcmds) { + isp->isp_tgtlist[handle - 1] = NULL; + } +} +void +isp_put_atio(ispsoftc_t *isp, at_entry_t *atsrc, at_entry_t *atdst) +{ + int i; + isp_copy_out_hdr(isp, &atsrc->at_header, &atdst->at_header); + ISP_IOXPUT_16(isp, atsrc->at_reserved, &atdst->at_reserved); + ISP_IOXPUT_16(isp, atsrc->at_handle, &atdst->at_handle); + if (ISP_IS_SBUS(isp)) { + ISP_IOXPUT_8(isp, atsrc->at_lun, &atdst->at_iid); + ISP_IOXPUT_8(isp, atsrc->at_iid, &atdst->at_lun); + ISP_IOXPUT_8(isp, atsrc->at_cdblen, &atdst->at_tgt); + ISP_IOXPUT_8(isp, atsrc->at_tgt, &atdst->at_cdblen); + ISP_IOXPUT_8(isp, atsrc->at_status, &atdst->at_scsi_status); + ISP_IOXPUT_8(isp, atsrc->at_scsi_status, &atdst->at_status); + ISP_IOXPUT_8(isp, atsrc->at_tag_val, &atdst->at_tag_type); + ISP_IOXPUT_8(isp, atsrc->at_tag_type, &atdst->at_tag_val); + } else { + ISP_IOXPUT_8(isp, atsrc->at_lun, &atdst->at_lun); + ISP_IOXPUT_8(isp, atsrc->at_iid, &atdst->at_iid); + ISP_IOXPUT_8(isp, atsrc->at_cdblen, &atdst->at_cdblen); + ISP_IOXPUT_8(isp, atsrc->at_tgt, &atdst->at_tgt); + ISP_IOXPUT_8(isp, atsrc->at_status, &atdst->at_status); + ISP_IOXPUT_8(isp, atsrc->at_scsi_status, + &atdst->at_scsi_status); + ISP_IOXPUT_8(isp, atsrc->at_tag_val, &atdst->at_tag_val); + ISP_IOXPUT_8(isp, atsrc->at_tag_type, &atdst->at_tag_type); + } + ISP_IOXPUT_32(isp, atsrc->at_flags, &atdst->at_flags); + for (i = 0; i < ATIO_CDBLEN; i++) { + ISP_IOXPUT_8(isp, atsrc->at_cdb[i], &atdst->at_cdb[i]); + } + for (i = 0; i < QLTM_SENSELEN; i++) { + ISP_IOXPUT_8(isp, atsrc->at_sense[i], &atdst->at_sense[i]); + } +} + +void +isp_get_atio(ispsoftc_t *isp, at_entry_t *atsrc, at_entry_t *atdst) +{ + int i; + isp_copy_in_hdr(isp, &atsrc->at_header, &atdst->at_header); + ISP_IOXGET_16(isp, &atsrc->at_reserved, atdst->at_reserved); + ISP_IOXGET_16(isp, &atsrc->at_handle, atdst->at_handle); + if (ISP_IS_SBUS(isp)) { + ISP_IOXGET_8(isp, &atsrc->at_lun, atdst->at_iid); + ISP_IOXGET_8(isp, &atsrc->at_iid, atdst->at_lun); + ISP_IOXGET_8(isp, &atsrc->at_cdblen, atdst->at_tgt); + ISP_IOXGET_8(isp, &atsrc->at_tgt, atdst->at_cdblen); + ISP_IOXGET_8(isp, &atsrc->at_status, atdst->at_scsi_status); + ISP_IOXGET_8(isp, &atsrc->at_scsi_status, atdst->at_status); + ISP_IOXGET_8(isp, &atsrc->at_tag_val, atdst->at_tag_type); + ISP_IOXGET_8(isp, &atsrc->at_tag_type, atdst->at_tag_val); + } else { + ISP_IOXGET_8(isp, &atsrc->at_lun, atdst->at_lun); + ISP_IOXGET_8(isp, &atsrc->at_iid, atdst->at_iid); + ISP_IOXGET_8(isp, &atsrc->at_cdblen, atdst->at_cdblen); + ISP_IOXGET_8(isp, &atsrc->at_tgt, atdst->at_tgt); + ISP_IOXGET_8(isp, &atsrc->at_status, atdst->at_status); + ISP_IOXGET_8(isp, &atsrc->at_scsi_status, + atdst->at_scsi_status); + ISP_IOXGET_8(isp, &atsrc->at_tag_val, atdst->at_tag_val); + ISP_IOXGET_8(isp, &atsrc->at_tag_type, atdst->at_tag_type); + } + ISP_IOXGET_32(isp, &atsrc->at_flags, atdst->at_flags); + for (i = 0; i < ATIO_CDBLEN; i++) { + ISP_IOXGET_8(isp, &atsrc->at_cdb[i], atdst->at_cdb[i]); + } + for (i = 0; i < QLTM_SENSELEN; i++) { + ISP_IOXGET_8(isp, &atsrc->at_sense[i], atdst->at_sense[i]); + } +} + +void +isp_put_atio2(ispsoftc_t *isp, at2_entry_t *atsrc, at2_entry_t *atdst) +{ + int i; + isp_copy_out_hdr(isp, &atsrc->at_header, &atdst->at_header); + ISP_IOXPUT_32(isp, atsrc->at_reserved, &atdst->at_reserved); + ISP_IOXPUT_8(isp, atsrc->at_lun, &atdst->at_lun); + ISP_IOXPUT_8(isp, atsrc->at_iid, &atdst->at_iid); + ISP_IOXPUT_16(isp, atsrc->at_rxid, &atdst->at_rxid); + ISP_IOXPUT_16(isp, atsrc->at_flags, &atdst->at_flags); + ISP_IOXPUT_16(isp, atsrc->at_status, &atdst->at_status); + ISP_IOXPUT_8(isp, atsrc->at_crn, &atdst->at_crn); + ISP_IOXPUT_8(isp, atsrc->at_taskcodes, &atdst->at_taskcodes); + ISP_IOXPUT_8(isp, atsrc->at_taskflags, &atdst->at_taskflags); + ISP_IOXPUT_8(isp, atsrc->at_execodes, &atdst->at_execodes); + for (i = 0; i < ATIO2_CDBLEN; i++) { + ISP_IOXPUT_8(isp, atsrc->at_cdb[i], &atdst->at_cdb[i]); + } + ISP_IOXPUT_32(isp, atsrc->at_datalen, &atdst->at_datalen); + ISP_IOXPUT_16(isp, atsrc->at_scclun, &atdst->at_scclun); + for (i = 0; i < 4; i++) { + ISP_IOXPUT_16(isp, atsrc->at_wwpn[i], &atdst->at_wwpn[i]); + } + for (i = 0; i < 6; i++) { + ISP_IOXPUT_16(isp, atsrc->at_reserved2[i], + &atdst->at_reserved2[i]); + } + ISP_IOXPUT_16(isp, atsrc->at_oxid, &atdst->at_oxid); +} + +void +isp_put_atio2e(ispsoftc_t *isp, at2e_entry_t *atsrc, at2e_entry_t *atdst) +{ + int i; + isp_copy_out_hdr(isp, &atsrc->at_header, &atdst->at_header); + ISP_IOXPUT_32(isp, atsrc->at_reserved, &atdst->at_reserved); + ISP_IOXPUT_16(isp, atsrc->at_iid, &atdst->at_iid); + ISP_IOXPUT_16(isp, atsrc->at_rxid, &atdst->at_rxid); + ISP_IOXPUT_16(isp, atsrc->at_flags, &atdst->at_flags); + ISP_IOXPUT_16(isp, atsrc->at_status, &atdst->at_status); + ISP_IOXPUT_8(isp, atsrc->at_crn, &atdst->at_crn); + ISP_IOXPUT_8(isp, atsrc->at_taskcodes, &atdst->at_taskcodes); + ISP_IOXPUT_8(isp, atsrc->at_taskflags, &atdst->at_taskflags); + ISP_IOXPUT_8(isp, atsrc->at_execodes, &atdst->at_execodes); + for (i = 0; i < ATIO2_CDBLEN; i++) { + ISP_IOXPUT_8(isp, atsrc->at_cdb[i], &atdst->at_cdb[i]); + } + ISP_IOXPUT_32(isp, atsrc->at_datalen, &atdst->at_datalen); + ISP_IOXPUT_16(isp, atsrc->at_scclun, &atdst->at_scclun); + for (i = 0; i < 4; i++) { + ISP_IOXPUT_16(isp, atsrc->at_wwpn[i], &atdst->at_wwpn[i]); + } + for (i = 0; i < 6; i++) { + ISP_IOXPUT_16(isp, atsrc->at_reserved2[i], + &atdst->at_reserved2[i]); + } + ISP_IOXPUT_16(isp, atsrc->at_oxid, &atdst->at_oxid); +} + +void +isp_get_atio2(ispsoftc_t *isp, at2_entry_t *atsrc, at2_entry_t *atdst) +{ + int i; + isp_copy_in_hdr(isp, &atsrc->at_header, &atdst->at_header); + ISP_IOXGET_32(isp, &atsrc->at_reserved, atdst->at_reserved); + ISP_IOXGET_8(isp, &atsrc->at_lun, atdst->at_lun); + ISP_IOXGET_8(isp, &atsrc->at_iid, atdst->at_iid); + ISP_IOXGET_16(isp, &atsrc->at_rxid, atdst->at_rxid); + ISP_IOXGET_16(isp, &atsrc->at_flags, atdst->at_flags); + ISP_IOXGET_16(isp, &atsrc->at_status, atdst->at_status); + ISP_IOXGET_8(isp, &atsrc->at_crn, atdst->at_crn); + ISP_IOXGET_8(isp, &atsrc->at_taskcodes, atdst->at_taskcodes); + ISP_IOXGET_8(isp, &atsrc->at_taskflags, atdst->at_taskflags); + ISP_IOXGET_8(isp, &atsrc->at_execodes, atdst->at_execodes); + for (i = 0; i < ATIO2_CDBLEN; i++) { + ISP_IOXGET_8(isp, &atsrc->at_cdb[i], atdst->at_cdb[i]); + } + ISP_IOXGET_32(isp, &atsrc->at_datalen, atdst->at_datalen); + ISP_IOXGET_16(isp, &atsrc->at_scclun, atdst->at_scclun); + for (i = 0; i < 4; i++) { + ISP_IOXGET_16(isp, &atsrc->at_wwpn[i], atdst->at_wwpn[i]); + } + for (i = 0; i < 6; i++) { + ISP_IOXGET_16(isp, &atsrc->at_reserved2[i], + atdst->at_reserved2[i]); + } + ISP_IOXGET_16(isp, &atsrc->at_oxid, atdst->at_oxid); +} + +void +isp_get_atio2e(ispsoftc_t *isp, at2e_entry_t *atsrc, at2e_entry_t *atdst) +{ + int i; + isp_copy_in_hdr(isp, &atsrc->at_header, &atdst->at_header); + ISP_IOXGET_32(isp, &atsrc->at_reserved, atdst->at_reserved); + ISP_IOXGET_16(isp, &atsrc->at_iid, atdst->at_iid); + ISP_IOXGET_16(isp, &atsrc->at_rxid, atdst->at_rxid); + ISP_IOXGET_16(isp, &atsrc->at_flags, atdst->at_flags); + ISP_IOXGET_16(isp, &atsrc->at_status, atdst->at_status); + ISP_IOXGET_8(isp, &atsrc->at_crn, atdst->at_crn); + ISP_IOXGET_8(isp, &atsrc->at_taskcodes, atdst->at_taskcodes); + ISP_IOXGET_8(isp, &atsrc->at_taskflags, atdst->at_taskflags); + ISP_IOXGET_8(isp, &atsrc->at_execodes, atdst->at_execodes); + for (i = 0; i < ATIO2_CDBLEN; i++) { + ISP_IOXGET_8(isp, &atsrc->at_cdb[i], atdst->at_cdb[i]); + } + ISP_IOXGET_32(isp, &atsrc->at_datalen, atdst->at_datalen); + ISP_IOXGET_16(isp, &atsrc->at_scclun, atdst->at_scclun); + for (i = 0; i < 4; i++) { + ISP_IOXGET_16(isp, &atsrc->at_wwpn[i], atdst->at_wwpn[i]); + } + for (i = 0; i < 6; i++) { + ISP_IOXGET_16(isp, &atsrc->at_reserved2[i], + atdst->at_reserved2[i]); + } + ISP_IOXGET_16(isp, &atsrc->at_oxid, atdst->at_oxid); +} + +void +isp_put_ctio(ispsoftc_t *isp, ct_entry_t *ctsrc, ct_entry_t *ctdst) +{ + int i; + isp_copy_out_hdr(isp, &ctsrc->ct_header, &ctdst->ct_header); + ISP_IOXPUT_16(isp, ctsrc->ct_reserved, &ctdst->ct_reserved); + ISP_IOXPUT_16(isp, ctsrc->ct_fwhandle, &ctdst->ct_fwhandle); + if (ISP_IS_SBUS(isp)) { + ISP_IOXPUT_8(isp, ctsrc->ct_iid, &ctdst->ct_lun); + ISP_IOXPUT_8(isp, ctsrc->ct_lun, &ctdst->ct_iid); + ISP_IOXPUT_8(isp, ctsrc->ct_tgt, &ctdst->ct_reserved2); + ISP_IOXPUT_8(isp, ctsrc->ct_reserved2, &ctdst->ct_tgt); + ISP_IOXPUT_8(isp, ctsrc->ct_status, &ctdst->ct_scsi_status); + ISP_IOXPUT_8(isp, ctsrc->ct_scsi_status, &ctdst->ct_status); + ISP_IOXPUT_8(isp, ctsrc->ct_tag_type, &ctdst->ct_tag_val); + ISP_IOXPUT_8(isp, ctsrc->ct_tag_val, &ctdst->ct_tag_type); + } else { + ISP_IOXPUT_8(isp, ctsrc->ct_iid, &ctdst->ct_iid); + ISP_IOXPUT_8(isp, ctsrc->ct_lun, &ctdst->ct_lun); + ISP_IOXPUT_8(isp, ctsrc->ct_tgt, &ctdst->ct_tgt); + ISP_IOXPUT_8(isp, ctsrc->ct_reserved2, &ctdst->ct_reserved2); + ISP_IOXPUT_8(isp, ctsrc->ct_scsi_status, + &ctdst->ct_scsi_status); + ISP_IOXPUT_8(isp, ctsrc->ct_status, &ctdst->ct_status); + ISP_IOXPUT_8(isp, ctsrc->ct_tag_type, &ctdst->ct_tag_type); + ISP_IOXPUT_8(isp, ctsrc->ct_tag_val, &ctdst->ct_tag_val); + } + ISP_IOXPUT_32(isp, ctsrc->ct_flags, &ctdst->ct_flags); + ISP_IOXPUT_32(isp, ctsrc->ct_xfrlen, &ctdst->ct_xfrlen); + ISP_IOXPUT_32(isp, ctsrc->ct_resid, &ctdst->ct_resid); + ISP_IOXPUT_16(isp, ctsrc->ct_timeout, &ctdst->ct_timeout); + ISP_IOXPUT_16(isp, ctsrc->ct_seg_count, &ctdst->ct_seg_count); + for (i = 0; i < ISP_RQDSEG; i++) { + ISP_IOXPUT_32(isp, ctsrc->ct_dataseg[i].ds_base, + &ctdst->ct_dataseg[i].ds_base); + ISP_IOXPUT_32(isp, ctsrc->ct_dataseg[i].ds_count, + &ctdst->ct_dataseg[i].ds_count); + } +} + +void +isp_get_ctio(ispsoftc_t *isp, ct_entry_t *ctsrc, ct_entry_t *ctdst) +{ + int i; + isp_copy_in_hdr(isp, &ctsrc->ct_header, &ctdst->ct_header); + ISP_IOXGET_16(isp, &ctsrc->ct_reserved, ctdst->ct_reserved); + ISP_IOXGET_16(isp, &ctsrc->ct_fwhandle, ctdst->ct_fwhandle); + if (ISP_IS_SBUS(isp)) { + ISP_IOXGET_8(isp, &ctsrc->ct_lun, ctdst->ct_iid); + ISP_IOXGET_8(isp, &ctsrc->ct_iid, ctdst->ct_lun); + ISP_IOXGET_8(isp, &ctsrc->ct_reserved2, ctdst->ct_tgt); + ISP_IOXGET_8(isp, &ctsrc->ct_tgt, ctdst->ct_reserved2); + ISP_IOXGET_8(isp, &ctsrc->ct_status, ctdst->ct_scsi_status); + ISP_IOXGET_8(isp, &ctsrc->ct_scsi_status, ctdst->ct_status); + ISP_IOXGET_8(isp, &ctsrc->ct_tag_val, ctdst->ct_tag_type); + ISP_IOXGET_8(isp, &ctsrc->ct_tag_type, ctdst->ct_tag_val); + } else { + ISP_IOXGET_8(isp, &ctsrc->ct_lun, ctdst->ct_lun); + ISP_IOXGET_8(isp, &ctsrc->ct_iid, ctdst->ct_iid); + ISP_IOXGET_8(isp, &ctsrc->ct_reserved2, ctdst->ct_reserved2); + ISP_IOXGET_8(isp, &ctsrc->ct_tgt, ctdst->ct_tgt); + ISP_IOXGET_8(isp, &ctsrc->ct_status, ctdst->ct_status); + ISP_IOXGET_8(isp, &ctsrc->ct_scsi_status, + ctdst->ct_scsi_status); + ISP_IOXGET_8(isp, &ctsrc->ct_tag_val, ctdst->ct_tag_val); + ISP_IOXGET_8(isp, &ctsrc->ct_tag_type, ctdst->ct_tag_type); + } + ISP_IOXGET_32(isp, &ctsrc->ct_flags, ctdst->ct_flags); + ISP_IOXGET_32(isp, &ctsrc->ct_xfrlen, ctdst->ct_xfrlen); + ISP_IOXGET_32(isp, &ctsrc->ct_resid, ctdst->ct_resid); + ISP_IOXGET_16(isp, &ctsrc->ct_timeout, ctdst->ct_timeout); + ISP_IOXGET_16(isp, &ctsrc->ct_seg_count, ctdst->ct_seg_count); + for (i = 0; i < ISP_RQDSEG; i++) { + ISP_IOXGET_32(isp, + &ctsrc->ct_dataseg[i].ds_base, + ctdst->ct_dataseg[i].ds_base); + ISP_IOXGET_32(isp, + &ctsrc->ct_dataseg[i].ds_count, + ctdst->ct_dataseg[i].ds_count); + } +} + +void +isp_put_ctio2(ispsoftc_t *isp, ct2_entry_t *ctsrc, ct2_entry_t *ctdst) +{ + int i; + isp_copy_out_hdr(isp, &ctsrc->ct_header, &ctdst->ct_header); + ISP_IOXPUT_16(isp, ctsrc->ct_reserved, &ctdst->ct_reserved); + ISP_IOXPUT_16(isp, ctsrc->ct_fwhandle, &ctdst->ct_fwhandle); + ISP_IOXPUT_8(isp, ctsrc->ct_lun, &ctdst->ct_lun); + ISP_IOXPUT_8(isp, ctsrc->ct_iid, &ctdst->ct_iid); + ISP_IOXPUT_16(isp, ctsrc->ct_rxid, &ctdst->ct_rxid); + ISP_IOXPUT_16(isp, ctsrc->ct_flags, &ctdst->ct_flags); + ISP_IOXPUT_16(isp, ctsrc->ct_timeout, &ctdst->ct_timeout); + ISP_IOXPUT_16(isp, ctsrc->ct_seg_count, &ctdst->ct_seg_count); + ISP_IOXPUT_32(isp, ctsrc->ct_resid, &ctdst->ct_resid); + ISP_IOXPUT_32(isp, ctsrc->ct_reloff, &ctdst->ct_reloff); + if ((ctsrc->ct_flags & CT2_FLAG_MMASK) == CT2_FLAG_MODE0) { + ISP_IOXPUT_32(isp, ctsrc->rsp.m0._reserved, + &ctdst->rsp.m0._reserved); + ISP_IOXPUT_16(isp, ctsrc->rsp.m0._reserved2, + &ctdst->rsp.m0._reserved2); + ISP_IOXPUT_16(isp, ctsrc->rsp.m0.ct_scsi_status, + &ctdst->rsp.m0.ct_scsi_status); + ISP_IOXPUT_32(isp, ctsrc->rsp.m0.ct_xfrlen, + &ctdst->rsp.m0.ct_xfrlen); + if (ctsrc->ct_header.rqs_entry_type == RQSTYPE_CTIO2) { + for (i = 0; i < ISP_RQDSEG_T2; i++) { + ISP_IOXPUT_32(isp, + ctsrc->rsp.m0.ct_dataseg[i].ds_base, + &ctdst->rsp.m0.ct_dataseg[i].ds_base); + ISP_IOXPUT_32(isp, + ctsrc->rsp.m0.ct_dataseg[i].ds_count, + &ctdst->rsp.m0.ct_dataseg[i].ds_count); + } + } else if (ctsrc->ct_header.rqs_entry_type == RQSTYPE_CTIO3) { + for (i = 0; i < ISP_RQDSEG_T3; i++) { + ISP_IOXPUT_32(isp, + ctsrc->rsp.m0.ct_dataseg64[i].ds_base, + &ctdst->rsp.m0.ct_dataseg64[i].ds_base); + ISP_IOXPUT_32(isp, + ctsrc->rsp.m0.ct_dataseg64[i].ds_basehi, + &ctdst->rsp.m0.ct_dataseg64[i].ds_basehi); + ISP_IOXPUT_32(isp, + ctsrc->rsp.m0.ct_dataseg64[i].ds_count, + &ctdst->rsp.m0.ct_dataseg64[i].ds_count); + } + } else if (ctsrc->ct_header.rqs_entry_type == RQSTYPE_CTIO4) { + ISP_IOXPUT_16(isp, ctsrc->rsp.m0.ct_dslist.ds_type, + &ctdst->rsp.m0.ct_dslist.ds_type); + ISP_IOXPUT_32(isp, ctsrc->rsp.m0.ct_dslist.ds_segment, + &ctdst->rsp.m0.ct_dslist.ds_segment); + ISP_IOXPUT_32(isp, ctsrc->rsp.m0.ct_dslist.ds_base, + &ctdst->rsp.m0.ct_dslist.ds_base); + } + } else if ((ctsrc->ct_flags & CT2_FLAG_MMASK) == CT2_FLAG_MODE1) { + ISP_IOXPUT_16(isp, ctsrc->rsp.m1._reserved, + &ctdst->rsp.m1._reserved); + ISP_IOXPUT_16(isp, ctsrc->rsp.m1._reserved2, + &ctdst->rsp.m1._reserved2); + ISP_IOXPUT_16(isp, ctsrc->rsp.m1.ct_senselen, + &ctdst->rsp.m1.ct_senselen); + ISP_IOXPUT_16(isp, ctsrc->rsp.m1.ct_scsi_status, + &ctdst->rsp.m1.ct_scsi_status); + ISP_IOXPUT_16(isp, ctsrc->rsp.m1.ct_resplen, + &ctdst->rsp.m1.ct_resplen); + for (i = 0; i < MAXRESPLEN; i++) { + ISP_IOXPUT_8(isp, ctsrc->rsp.m1.ct_resp[i], + &ctdst->rsp.m1.ct_resp[i]); + } + } else { + ISP_IOXPUT_32(isp, ctsrc->rsp.m2._reserved, + &ctdst->rsp.m2._reserved); + ISP_IOXPUT_16(isp, ctsrc->rsp.m2._reserved2, + &ctdst->rsp.m2._reserved2); + ISP_IOXPUT_16(isp, ctsrc->rsp.m2._reserved3, + &ctdst->rsp.m2._reserved3); + ISP_IOXPUT_32(isp, ctsrc->rsp.m2.ct_datalen, + &ctdst->rsp.m2.ct_datalen); + ISP_IOXPUT_32(isp, ctsrc->rsp.m2.ct_fcp_rsp_iudata.ds_base, + &ctdst->rsp.m2.ct_fcp_rsp_iudata.ds_base); + ISP_IOXPUT_32(isp, ctsrc->rsp.m2.ct_fcp_rsp_iudata.ds_count, + &ctdst->rsp.m2.ct_fcp_rsp_iudata.ds_count); + } +} + +void +isp_put_ctio2e(ispsoftc_t *isp, ct2e_entry_t *ctsrc, ct2e_entry_t *ctdst) +{ + int i; + isp_copy_out_hdr(isp, &ctsrc->ct_header, &ctdst->ct_header); + ISP_IOXPUT_16(isp, ctsrc->ct_reserved, &ctdst->ct_reserved); + ISP_IOXPUT_16(isp, ctsrc->ct_fwhandle, &ctdst->ct_fwhandle); + ISP_IOXPUT_16(isp, ctsrc->ct_iid, &ctdst->ct_iid); + ISP_IOXPUT_16(isp, ctsrc->ct_rxid, &ctdst->ct_rxid); + ISP_IOXPUT_16(isp, ctsrc->ct_flags, &ctdst->ct_flags); + ISP_IOXPUT_16(isp, ctsrc->ct_timeout, &ctdst->ct_timeout); + ISP_IOXPUT_16(isp, ctsrc->ct_seg_count, &ctdst->ct_seg_count); + ISP_IOXPUT_32(isp, ctsrc->ct_resid, &ctdst->ct_resid); + ISP_IOXPUT_32(isp, ctsrc->ct_reloff, &ctdst->ct_reloff); + if ((ctsrc->ct_flags & CT2_FLAG_MMASK) == CT2_FLAG_MODE0) { + ISP_IOXPUT_32(isp, ctsrc->rsp.m0._reserved, + &ctdst->rsp.m0._reserved); + ISP_IOXPUT_16(isp, ctsrc->rsp.m0._reserved2, + &ctdst->rsp.m0._reserved2); + ISP_IOXPUT_16(isp, ctsrc->rsp.m0.ct_scsi_status, + &ctdst->rsp.m0.ct_scsi_status); + ISP_IOXPUT_32(isp, ctsrc->rsp.m0.ct_xfrlen, + &ctdst->rsp.m0.ct_xfrlen); + if (ctsrc->ct_header.rqs_entry_type == RQSTYPE_CTIO2) { + for (i = 0; i < ISP_RQDSEG_T2; i++) { + ISP_IOXPUT_32(isp, + ctsrc->rsp.m0.ct_dataseg[i].ds_base, + &ctdst->rsp.m0.ct_dataseg[i].ds_base); + ISP_IOXPUT_32(isp, + ctsrc->rsp.m0.ct_dataseg[i].ds_count, + &ctdst->rsp.m0.ct_dataseg[i].ds_count); + } + } else if (ctsrc->ct_header.rqs_entry_type == RQSTYPE_CTIO3) { + for (i = 0; i < ISP_RQDSEG_T3; i++) { + ISP_IOXPUT_32(isp, + ctsrc->rsp.m0.ct_dataseg64[i].ds_base, + &ctdst->rsp.m0.ct_dataseg64[i].ds_base); + ISP_IOXPUT_32(isp, + ctsrc->rsp.m0.ct_dataseg64[i].ds_basehi, + &ctdst->rsp.m0.ct_dataseg64[i].ds_basehi); + ISP_IOXPUT_32(isp, + ctsrc->rsp.m0.ct_dataseg64[i].ds_count, + &ctdst->rsp.m0.ct_dataseg64[i].ds_count); + } + } else if (ctsrc->ct_header.rqs_entry_type == RQSTYPE_CTIO4) { + ISP_IOXPUT_16(isp, ctsrc->rsp.m0.ct_dslist.ds_type, + &ctdst->rsp.m0.ct_dslist.ds_type); + ISP_IOXPUT_32(isp, ctsrc->rsp.m0.ct_dslist.ds_segment, + &ctdst->rsp.m0.ct_dslist.ds_segment); + ISP_IOXPUT_32(isp, ctsrc->rsp.m0.ct_dslist.ds_base, + &ctdst->rsp.m0.ct_dslist.ds_base); + } + } else if ((ctsrc->ct_flags & CT2_FLAG_MMASK) == CT2_FLAG_MODE1) { + ISP_IOXPUT_16(isp, ctsrc->rsp.m1._reserved, + &ctdst->rsp.m1._reserved); + ISP_IOXPUT_16(isp, ctsrc->rsp.m1._reserved2, + &ctdst->rsp.m1._reserved2); + ISP_IOXPUT_16(isp, ctsrc->rsp.m1.ct_senselen, + &ctdst->rsp.m1.ct_senselen); + ISP_IOXPUT_16(isp, ctsrc->rsp.m1.ct_scsi_status, + &ctdst->rsp.m1.ct_scsi_status); + ISP_IOXPUT_16(isp, ctsrc->rsp.m1.ct_resplen, + &ctdst->rsp.m1.ct_resplen); + for (i = 0; i < MAXRESPLEN; i++) { + ISP_IOXPUT_8(isp, ctsrc->rsp.m1.ct_resp[i], + &ctdst->rsp.m1.ct_resp[i]); + } + } else { + ISP_IOXPUT_32(isp, ctsrc->rsp.m2._reserved, + &ctdst->rsp.m2._reserved); + ISP_IOXPUT_16(isp, ctsrc->rsp.m2._reserved2, + &ctdst->rsp.m2._reserved2); + ISP_IOXPUT_16(isp, ctsrc->rsp.m2._reserved3, + &ctdst->rsp.m2._reserved3); + ISP_IOXPUT_32(isp, ctsrc->rsp.m2.ct_datalen, + &ctdst->rsp.m2.ct_datalen); + ISP_IOXPUT_32(isp, ctsrc->rsp.m2.ct_fcp_rsp_iudata.ds_base, + &ctdst->rsp.m2.ct_fcp_rsp_iudata.ds_base); + ISP_IOXPUT_32(isp, ctsrc->rsp.m2.ct_fcp_rsp_iudata.ds_count, + &ctdst->rsp.m2.ct_fcp_rsp_iudata.ds_count); + } +} + +void +isp_get_ctio2(ispsoftc_t *isp, ct2_entry_t *ctsrc, ct2_entry_t *ctdst) +{ + isp_copy_in_hdr(isp, &ctsrc->ct_header, &ctdst->ct_header); + ISP_IOXGET_16(isp, &ctsrc->ct_reserved, ctdst->ct_reserved); + ISP_IOXGET_16(isp, &ctsrc->ct_fwhandle, ctdst->ct_fwhandle); + ISP_IOXGET_8(isp, &ctsrc->ct_lun, ctdst->ct_lun); + ISP_IOXGET_8(isp, &ctsrc->ct_iid, ctdst->ct_iid); + ISP_IOXGET_16(isp, &ctsrc->ct_rxid, ctdst->ct_rxid); + ISP_IOXGET_16(isp, &ctsrc->ct_flags, ctdst->ct_flags); + ISP_IOXGET_16(isp, &ctsrc->ct_status, ctdst->ct_status); + ISP_IOXGET_16(isp, &ctsrc->ct_timeout, ctdst->ct_timeout); + ISP_IOXGET_16(isp, &ctsrc->ct_seg_count, ctdst->ct_seg_count); + ISP_IOXGET_32(isp, &ctsrc->ct_reloff, ctdst->ct_reloff); + ISP_IOXGET_32(isp, &ctsrc->ct_resid, ctdst->ct_resid); +} + +void +isp_get_ctio2e(ispsoftc_t *isp, ct2e_entry_t *ctsrc, ct2e_entry_t *ctdst) +{ + isp_copy_in_hdr(isp, &ctsrc->ct_header, &ctdst->ct_header); + ISP_IOXGET_16(isp, &ctsrc->ct_reserved, ctdst->ct_reserved); + ISP_IOXGET_16(isp, &ctsrc->ct_fwhandle, ctdst->ct_fwhandle); + ISP_IOXGET_16(isp, &ctsrc->ct_iid, ctdst->ct_iid); + ISP_IOXGET_16(isp, &ctsrc->ct_rxid, ctdst->ct_rxid); + ISP_IOXGET_16(isp, &ctsrc->ct_flags, ctdst->ct_flags); + ISP_IOXGET_16(isp, &ctsrc->ct_status, ctdst->ct_status); + ISP_IOXGET_16(isp, &ctsrc->ct_timeout, ctdst->ct_timeout); + ISP_IOXGET_16(isp, &ctsrc->ct_seg_count, ctdst->ct_seg_count); + ISP_IOXGET_32(isp, &ctsrc->ct_reloff, ctdst->ct_reloff); + ISP_IOXGET_32(isp, &ctsrc->ct_resid, ctdst->ct_resid); +} + +void +isp_put_enable_lun(ispsoftc_t *isp, lun_entry_t *lesrc, lun_entry_t *ledst) +{ + int i; + isp_copy_out_hdr(isp, &lesrc->le_header, &ledst->le_header); + ISP_IOXPUT_32(isp, lesrc->le_reserved, &ledst->le_reserved); + if (ISP_IS_SBUS(isp)) { + ISP_IOXPUT_8(isp, lesrc->le_lun, &ledst->le_rsvd); + ISP_IOXPUT_8(isp, lesrc->le_rsvd, &ledst->le_lun); + ISP_IOXPUT_8(isp, lesrc->le_ops, &ledst->le_tgt); + ISP_IOXPUT_8(isp, lesrc->le_tgt, &ledst->le_ops); + ISP_IOXPUT_8(isp, lesrc->le_status, &ledst->le_reserved2); + ISP_IOXPUT_8(isp, lesrc->le_reserved2, &ledst->le_status); + ISP_IOXPUT_8(isp, lesrc->le_cmd_count, &ledst->le_in_count); + ISP_IOXPUT_8(isp, lesrc->le_in_count, &ledst->le_cmd_count); + ISP_IOXPUT_8(isp, lesrc->le_cdb6len, &ledst->le_cdb7len); + ISP_IOXPUT_8(isp, lesrc->le_cdb7len, &ledst->le_cdb6len); + } else { + ISP_IOXPUT_8(isp, lesrc->le_lun, &ledst->le_lun); + ISP_IOXPUT_8(isp, lesrc->le_rsvd, &ledst->le_rsvd); + ISP_IOXPUT_8(isp, lesrc->le_ops, &ledst->le_ops); + ISP_IOXPUT_8(isp, lesrc->le_tgt, &ledst->le_tgt); + ISP_IOXPUT_8(isp, lesrc->le_status, &ledst->le_status); + ISP_IOXPUT_8(isp, lesrc->le_reserved2, &ledst->le_reserved2); + ISP_IOXPUT_8(isp, lesrc->le_cmd_count, &ledst->le_cmd_count); + ISP_IOXPUT_8(isp, lesrc->le_in_count, &ledst->le_in_count); + ISP_IOXPUT_8(isp, lesrc->le_cdb6len, &ledst->le_cdb6len); + ISP_IOXPUT_8(isp, lesrc->le_cdb7len, &ledst->le_cdb7len); + } + ISP_IOXPUT_32(isp, lesrc->le_flags, &ledst->le_flags); + ISP_IOXPUT_16(isp, lesrc->le_timeout, &ledst->le_timeout); + for (i = 0; i < 20; i++) { + ISP_IOXPUT_8(isp, lesrc->le_reserved3[i], + &ledst->le_reserved3[i]); + } +} + +void +isp_get_enable_lun(ispsoftc_t *isp, lun_entry_t *lesrc, lun_entry_t *ledst) +{ + int i; + isp_copy_in_hdr(isp, &lesrc->le_header, &ledst->le_header); + ISP_IOXGET_32(isp, &lesrc->le_reserved, ledst->le_reserved); + if (ISP_IS_SBUS(isp)) { + ISP_IOXGET_8(isp, &lesrc->le_lun, ledst->le_rsvd); + ISP_IOXGET_8(isp, &lesrc->le_rsvd, ledst->le_lun); + ISP_IOXGET_8(isp, &lesrc->le_ops, ledst->le_tgt); + ISP_IOXGET_8(isp, &lesrc->le_tgt, ledst->le_ops); + ISP_IOXGET_8(isp, &lesrc->le_status, ledst->le_reserved2); + ISP_IOXGET_8(isp, &lesrc->le_reserved2, ledst->le_status); + ISP_IOXGET_8(isp, &lesrc->le_cmd_count, ledst->le_in_count); + ISP_IOXGET_8(isp, &lesrc->le_in_count, ledst->le_cmd_count); + ISP_IOXGET_8(isp, &lesrc->le_cdb6len, ledst->le_cdb7len); + ISP_IOXGET_8(isp, &lesrc->le_cdb7len, ledst->le_cdb6len); + } else { + ISP_IOXGET_8(isp, &lesrc->le_lun, ledst->le_lun); + ISP_IOXGET_8(isp, &lesrc->le_rsvd, ledst->le_rsvd); + ISP_IOXGET_8(isp, &lesrc->le_ops, ledst->le_ops); + ISP_IOXGET_8(isp, &lesrc->le_tgt, ledst->le_tgt); + ISP_IOXGET_8(isp, &lesrc->le_status, ledst->le_status); + ISP_IOXGET_8(isp, &lesrc->le_reserved2, ledst->le_reserved2); + ISP_IOXGET_8(isp, &lesrc->le_cmd_count, ledst->le_cmd_count); + ISP_IOXGET_8(isp, &lesrc->le_in_count, ledst->le_in_count); + ISP_IOXGET_8(isp, &lesrc->le_cdb6len, ledst->le_cdb6len); + ISP_IOXGET_8(isp, &lesrc->le_cdb7len, ledst->le_cdb7len); + } + ISP_IOXGET_32(isp, &lesrc->le_flags, ledst->le_flags); + ISP_IOXGET_16(isp, &lesrc->le_timeout, ledst->le_timeout); + for (i = 0; i < 20; i++) { + ISP_IOXGET_8(isp, &lesrc->le_reserved3[i], + ledst->le_reserved3[i]); + } +} + +void +isp_put_notify(ispsoftc_t *isp, in_entry_t *insrc, in_entry_t *indst) +{ + int i; + isp_copy_out_hdr(isp, &insrc->in_header, &indst->in_header); + ISP_IOXPUT_32(isp, insrc->in_reserved, &indst->in_reserved); + if (ISP_IS_SBUS(isp)) { + ISP_IOXPUT_8(isp, insrc->in_lun, &indst->in_iid); + ISP_IOXPUT_8(isp, insrc->in_iid, &indst->in_lun); + ISP_IOXPUT_8(isp, insrc->in_reserved2, &indst->in_tgt); + ISP_IOXPUT_8(isp, insrc->in_tgt, &indst->in_reserved2); + ISP_IOXPUT_8(isp, insrc->in_status, &indst->in_rsvd2); + ISP_IOXPUT_8(isp, insrc->in_rsvd2, &indst->in_status); + ISP_IOXPUT_8(isp, insrc->in_tag_val, &indst->in_tag_type); + ISP_IOXPUT_8(isp, insrc->in_tag_type, &indst->in_tag_val); + } else { + ISP_IOXPUT_8(isp, insrc->in_lun, &indst->in_lun); + ISP_IOXPUT_8(isp, insrc->in_iid, &indst->in_iid); + ISP_IOXPUT_8(isp, insrc->in_reserved2, &indst->in_reserved2); + ISP_IOXPUT_8(isp, insrc->in_tgt, &indst->in_tgt); + ISP_IOXPUT_8(isp, insrc->in_status, &indst->in_status); + ISP_IOXPUT_8(isp, insrc->in_rsvd2, &indst->in_rsvd2); + ISP_IOXPUT_8(isp, insrc->in_tag_val, &indst->in_tag_val); + ISP_IOXPUT_8(isp, insrc->in_tag_type, &indst->in_tag_type); + } + ISP_IOXPUT_32(isp, insrc->in_flags, &indst->in_flags); + ISP_IOXPUT_16(isp, insrc->in_seqid, &indst->in_seqid); + for (i = 0; i < IN_MSGLEN; i++) { + ISP_IOXPUT_8(isp, insrc->in_msg[i], &indst->in_msg[i]); + } + for (i = 0; i < IN_RSVDLEN; i++) { + ISP_IOXPUT_8(isp, insrc->in_reserved3[i], + &indst->in_reserved3[i]); + } + for (i = 0; i < QLTM_SENSELEN; i++) { + ISP_IOXPUT_8(isp, insrc->in_sense[i], + &indst->in_sense[i]); + } +} + +void +isp_get_notify(ispsoftc_t *isp, in_entry_t *insrc, in_entry_t *indst) +{ + int i; + isp_copy_in_hdr(isp, &insrc->in_header, &indst->in_header); + ISP_IOXGET_32(isp, &insrc->in_reserved, indst->in_reserved); + if (ISP_IS_SBUS(isp)) { + ISP_IOXGET_8(isp, &insrc->in_lun, indst->in_iid); + ISP_IOXGET_8(isp, &insrc->in_iid, indst->in_lun); + ISP_IOXGET_8(isp, &insrc->in_reserved2, indst->in_tgt); + ISP_IOXGET_8(isp, &insrc->in_tgt, indst->in_reserved2); + ISP_IOXGET_8(isp, &insrc->in_status, indst->in_rsvd2); + ISP_IOXGET_8(isp, &insrc->in_rsvd2, indst->in_status); + ISP_IOXGET_8(isp, &insrc->in_tag_val, indst->in_tag_type); + ISP_IOXGET_8(isp, &insrc->in_tag_type, indst->in_tag_val); + } else { + ISP_IOXGET_8(isp, &insrc->in_lun, indst->in_lun); + ISP_IOXGET_8(isp, &insrc->in_iid, indst->in_iid); + ISP_IOXGET_8(isp, &insrc->in_reserved2, indst->in_reserved2); + ISP_IOXGET_8(isp, &insrc->in_tgt, indst->in_tgt); + ISP_IOXGET_8(isp, &insrc->in_status, indst->in_status); + ISP_IOXGET_8(isp, &insrc->in_rsvd2, indst->in_rsvd2); + ISP_IOXGET_8(isp, &insrc->in_tag_val, indst->in_tag_val); + ISP_IOXGET_8(isp, &insrc->in_tag_type, indst->in_tag_type); + } + ISP_IOXGET_32(isp, &insrc->in_flags, indst->in_flags); + ISP_IOXGET_16(isp, &insrc->in_seqid, indst->in_seqid); + for (i = 0; i < IN_MSGLEN; i++) { + ISP_IOXGET_8(isp, &insrc->in_msg[i], indst->in_msg[i]); + } + for (i = 0; i < IN_RSVDLEN; i++) { + ISP_IOXGET_8(isp, &insrc->in_reserved3[i], + indst->in_reserved3[i]); + } + for (i = 0; i < QLTM_SENSELEN; i++) { + ISP_IOXGET_8(isp, &insrc->in_sense[i], + indst->in_sense[i]); + } +} + +void +isp_put_notify_fc(ispsoftc_t *isp, in_fcentry_t *insrc, + in_fcentry_t *indst) +{ + isp_copy_out_hdr(isp, &insrc->in_header, &indst->in_header); + ISP_IOXPUT_32(isp, insrc->in_reserved, &indst->in_reserved); + ISP_IOXPUT_8(isp, insrc->in_lun, &indst->in_lun); + ISP_IOXPUT_8(isp, insrc->in_iid, &indst->in_iid); + ISP_IOXPUT_16(isp, insrc->in_scclun, &indst->in_scclun); + ISP_IOXPUT_32(isp, insrc->in_reserved2, &indst->in_reserved2); + ISP_IOXPUT_16(isp, insrc->in_status, &indst->in_status); + ISP_IOXPUT_16(isp, insrc->in_task_flags, &indst->in_task_flags); + ISP_IOXPUT_16(isp, insrc->in_seqid, &indst->in_seqid); +} + +void +isp_put_notify_fc_e(ispsoftc_t *isp, in_fcentry_e_t *insrc, + in_fcentry_e_t *indst) +{ + isp_copy_out_hdr(isp, &insrc->in_header, &indst->in_header); + ISP_IOXPUT_32(isp, insrc->in_reserved, &indst->in_reserved); + ISP_IOXPUT_16(isp, insrc->in_iid, &indst->in_iid); + ISP_IOXPUT_16(isp, insrc->in_scclun, &indst->in_scclun); + ISP_IOXPUT_32(isp, insrc->in_reserved2, &indst->in_reserved2); + ISP_IOXPUT_16(isp, insrc->in_status, &indst->in_status); + ISP_IOXPUT_16(isp, insrc->in_task_flags, &indst->in_task_flags); + ISP_IOXPUT_16(isp, insrc->in_seqid, &indst->in_seqid); +} + +void +isp_get_notify_fc(ispsoftc_t *isp, in_fcentry_t *insrc, + in_fcentry_t *indst) +{ + isp_copy_in_hdr(isp, &insrc->in_header, &indst->in_header); + ISP_IOXGET_32(isp, &insrc->in_reserved, indst->in_reserved); + ISP_IOXGET_8(isp, &insrc->in_lun, indst->in_lun); + ISP_IOXGET_8(isp, &insrc->in_iid, indst->in_iid); + ISP_IOXGET_16(isp, &insrc->in_scclun, indst->in_scclun); + ISP_IOXGET_32(isp, &insrc->in_reserved2, indst->in_reserved2); + ISP_IOXGET_16(isp, &insrc->in_status, indst->in_status); + ISP_IOXGET_16(isp, &insrc->in_task_flags, indst->in_task_flags); + ISP_IOXGET_16(isp, &insrc->in_seqid, indst->in_seqid); +} + +void +isp_get_notify_fc_e(ispsoftc_t *isp, in_fcentry_e_t *insrc, + in_fcentry_e_t *indst) +{ + isp_copy_in_hdr(isp, &insrc->in_header, &indst->in_header); + ISP_IOXGET_32(isp, &insrc->in_reserved, indst->in_reserved); + ISP_IOXGET_16(isp, &insrc->in_iid, indst->in_iid); + ISP_IOXGET_16(isp, &insrc->in_scclun, indst->in_scclun); + ISP_IOXGET_32(isp, &insrc->in_reserved2, indst->in_reserved2); + ISP_IOXGET_16(isp, &insrc->in_status, indst->in_status); + ISP_IOXGET_16(isp, &insrc->in_task_flags, indst->in_task_flags); + ISP_IOXGET_16(isp, &insrc->in_seqid, indst->in_seqid); +} + +void +isp_put_notify_ack(ispsoftc_t *isp, na_entry_t *nasrc, na_entry_t *nadst) +{ + int i; + isp_copy_out_hdr(isp, &nasrc->na_header, &nadst->na_header); + ISP_IOXPUT_32(isp, nasrc->na_reserved, &nadst->na_reserved); + if (ISP_IS_SBUS(isp)) { + ISP_IOXPUT_8(isp, nasrc->na_lun, &nadst->na_iid); + ISP_IOXPUT_8(isp, nasrc->na_iid, &nadst->na_lun); + ISP_IOXPUT_8(isp, nasrc->na_status, &nadst->na_event); + ISP_IOXPUT_8(isp, nasrc->na_event, &nadst->na_status); + } else { + ISP_IOXPUT_8(isp, nasrc->na_lun, &nadst->na_lun); + ISP_IOXPUT_8(isp, nasrc->na_iid, &nadst->na_iid); + ISP_IOXPUT_8(isp, nasrc->na_status, &nadst->na_status); + ISP_IOXPUT_8(isp, nasrc->na_event, &nadst->na_event); + } + ISP_IOXPUT_32(isp, nasrc->na_flags, &nadst->na_flags); + for (i = 0; i < NA_RSVDLEN; i++) { + ISP_IOXPUT_16(isp, nasrc->na_reserved3[i], + &nadst->na_reserved3[i]); + } +} + +void +isp_get_notify_ack(ispsoftc_t *isp, na_entry_t *nasrc, na_entry_t *nadst) +{ + int i; + isp_copy_in_hdr(isp, &nasrc->na_header, &nadst->na_header); + ISP_IOXGET_32(isp, &nasrc->na_reserved, nadst->na_reserved); + if (ISP_IS_SBUS(isp)) { + ISP_IOXGET_8(isp, &nasrc->na_lun, nadst->na_iid); + ISP_IOXGET_8(isp, &nasrc->na_iid, nadst->na_lun); + ISP_IOXGET_8(isp, &nasrc->na_status, nadst->na_event); + ISP_IOXGET_8(isp, &nasrc->na_event, nadst->na_status); + } else { + ISP_IOXGET_8(isp, &nasrc->na_lun, nadst->na_lun); + ISP_IOXGET_8(isp, &nasrc->na_iid, nadst->na_iid); + ISP_IOXGET_8(isp, &nasrc->na_status, nadst->na_status); + ISP_IOXGET_8(isp, &nasrc->na_event, nadst->na_event); + } + ISP_IOXGET_32(isp, &nasrc->na_flags, nadst->na_flags); + for (i = 0; i < NA_RSVDLEN; i++) { + ISP_IOXGET_16(isp, &nasrc->na_reserved3[i], + nadst->na_reserved3[i]); + } +} + +void +isp_put_notify_ack_fc(ispsoftc_t *isp, na_fcentry_t *nasrc, + na_fcentry_t *nadst) +{ + int i; + isp_copy_out_hdr(isp, &nasrc->na_header, &nadst->na_header); + ISP_IOXPUT_32(isp, nasrc->na_reserved, &nadst->na_reserved); + ISP_IOXPUT_8(isp, nasrc->na_reserved1, &nadst->na_reserved1); + ISP_IOXPUT_8(isp, nasrc->na_iid, &nadst->na_iid); + ISP_IOXPUT_16(isp, nasrc->na_response, &nadst->na_response); + ISP_IOXPUT_16(isp, nasrc->na_flags, &nadst->na_flags); + ISP_IOXPUT_16(isp, nasrc->na_reserved2, &nadst->na_reserved2); + ISP_IOXPUT_16(isp, nasrc->na_status, &nadst->na_status); + ISP_IOXPUT_16(isp, nasrc->na_task_flags, &nadst->na_task_flags); + ISP_IOXPUT_16(isp, nasrc->na_seqid, &nadst->na_seqid); + for (i = 0; i < NA2_RSVDLEN; i++) { + ISP_IOXPUT_16(isp, nasrc->na_reserved3[i], + &nadst->na_reserved3[i]); + } +} + +void +isp_put_notify_ack_fc_e(ispsoftc_t *isp, na_fcentry_e_t *nasrc, + na_fcentry_e_t *nadst) +{ + int i; + isp_copy_out_hdr(isp, &nasrc->na_header, &nadst->na_header); + ISP_IOXPUT_32(isp, nasrc->na_reserved, &nadst->na_reserved); + ISP_IOXPUT_16(isp, nasrc->na_iid, &nadst->na_iid); + ISP_IOXPUT_16(isp, nasrc->na_response, &nadst->na_response); + ISP_IOXPUT_16(isp, nasrc->na_flags, &nadst->na_flags); + ISP_IOXPUT_16(isp, nasrc->na_reserved2, &nadst->na_reserved2); + ISP_IOXPUT_16(isp, nasrc->na_status, &nadst->na_status); + ISP_IOXPUT_16(isp, nasrc->na_task_flags, &nadst->na_task_flags); + ISP_IOXPUT_16(isp, nasrc->na_seqid, &nadst->na_seqid); + for (i = 0; i < NA2_RSVDLEN; i++) { + ISP_IOXPUT_16(isp, nasrc->na_reserved3[i], + &nadst->na_reserved3[i]); + } +} + +void +isp_get_notify_ack_fc(ispsoftc_t *isp, na_fcentry_t *nasrc, + na_fcentry_t *nadst) +{ + int i; + isp_copy_in_hdr(isp, &nasrc->na_header, &nadst->na_header); + ISP_IOXGET_32(isp, &nasrc->na_reserved, nadst->na_reserved); + ISP_IOXGET_8(isp, &nasrc->na_reserved1, nadst->na_reserved1); + ISP_IOXGET_8(isp, &nasrc->na_iid, nadst->na_iid); + ISP_IOXGET_16(isp, &nasrc->na_response, nadst->na_response); + ISP_IOXGET_16(isp, &nasrc->na_flags, nadst->na_flags); + ISP_IOXGET_16(isp, &nasrc->na_reserved2, nadst->na_reserved2); + ISP_IOXGET_16(isp, &nasrc->na_status, nadst->na_status); + ISP_IOXGET_16(isp, &nasrc->na_task_flags, nadst->na_task_flags); + ISP_IOXGET_16(isp, &nasrc->na_seqid, nadst->na_seqid); + for (i = 0; i < NA2_RSVDLEN; i++) { + ISP_IOXGET_16(isp, &nasrc->na_reserved3[i], + nadst->na_reserved3[i]); + } +} + +void +isp_get_notify_ack_fc_e(ispsoftc_t *isp, na_fcentry_e_t *nasrc, + na_fcentry_e_t *nadst) +{ + int i; + isp_copy_in_hdr(isp, &nasrc->na_header, &nadst->na_header); + ISP_IOXGET_32(isp, &nasrc->na_reserved, nadst->na_reserved); + ISP_IOXGET_16(isp, &nasrc->na_iid, nadst->na_iid); + ISP_IOXGET_16(isp, &nasrc->na_response, nadst->na_response); + ISP_IOXGET_16(isp, &nasrc->na_flags, nadst->na_flags); + ISP_IOXGET_16(isp, &nasrc->na_reserved2, nadst->na_reserved2); + ISP_IOXGET_16(isp, &nasrc->na_status, nadst->na_status); + ISP_IOXGET_16(isp, &nasrc->na_task_flags, nadst->na_task_flags); + ISP_IOXGET_16(isp, &nasrc->na_seqid, nadst->na_seqid); + for (i = 0; i < NA2_RSVDLEN; i++) { + ISP_IOXGET_16(isp, &nasrc->na_reserved3[i], + nadst->na_reserved3[i]); + } +} +#endif /* ISP_TARGET_MODE */ diff --git a/sys/dev/isp/isp_library.h b/sys/dev/isp/isp_library.h new file mode 100644 index 000000000000..4faa320e3bd6 --- /dev/null +++ b/sys/dev/isp/isp_library.h @@ -0,0 +1,164 @@ +/* $FreeBSD$ */ +/*- + * Qlogic Host Adapter Library Functions + * + * Copyright (c) 1999-2006 by Matthew Jacob + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +#ifndef _ISP_LIBRARY_H +#define _ISP_LIBRARY_H + +extern int isp_save_xs(ispsoftc_t *, XS_T *, uint16_t *); +extern XS_T *isp_find_xs(ispsoftc_t *, uint16_t); +extern uint16_t isp_find_handle(ispsoftc_t *, XS_T *); +extern int isp_handle_index(uint16_t); +extern uint16_t isp_index_handle(int); +extern void isp_destroy_handle(ispsoftc_t *, uint16_t); +extern int isp_getrqentry(ispsoftc_t *, uint16_t *, uint16_t *, void **); +extern void isp_print_qentry (ispsoftc_t *, char *, int, void *); +extern void isp_print_bytes(ispsoftc_t *, char *, int, void *); +extern int isp_fc_runstate(ispsoftc_t *, int); +extern void isp_copy_out_hdr(ispsoftc_t *, isphdr_t *, isphdr_t *); +extern void isp_copy_in_hdr(ispsoftc_t *, isphdr_t *, isphdr_t *); +extern int isp_get_response_type(ispsoftc_t *, isphdr_t *); +extern void +isp_put_request(ispsoftc_t *, ispreq_t *, ispreq_t *); +extern void +isp_put_request_t2(ispsoftc_t *, ispreqt2_t *, ispreqt2_t *); +extern void +isp_put_request_t2e(ispsoftc_t *, ispreqt2e_t *, ispreqt2e_t *); +extern void +isp_put_request_t3(ispsoftc_t *, ispreqt3_t *, ispreqt3_t *); +extern void +isp_put_request_t3e(ispsoftc_t *, ispreqt3e_t *, ispreqt3e_t *); +extern void +isp_put_extended_request(ispsoftc_t *, ispextreq_t *, ispextreq_t *); +extern void +isp_put_cont_req(ispsoftc_t *, ispcontreq_t *, ispcontreq_t *); +extern void +isp_put_cont64_req(ispsoftc_t *, ispcontreq64_t *, ispcontreq64_t *); +extern void +isp_get_response(ispsoftc_t *, ispstatusreq_t *, ispstatusreq_t *); +extern void +isp_get_response_x(ispsoftc_t *, ispstatus_cont_t *, ispstatus_cont_t *); +extern void +isp_get_rio2(ispsoftc_t *, isp_rio2_t *, isp_rio2_t *); +extern void +isp_put_icb(ispsoftc_t *, isp_icb_t *, isp_icb_t *); +extern void +isp_get_pdb(ispsoftc_t *, isp_pdb_t *, isp_pdb_t *); +extern void +isp_get_ct_hdr(ispsoftc_t *isp, ct_hdr_t *, ct_hdr_t *); +extern void +isp_put_sns_request(ispsoftc_t *, sns_screq_t *, sns_screq_t *); +extern void +isp_put_gid_ft_request(ispsoftc_t *, sns_gid_ft_req_t *, + sns_gid_ft_req_t *); +extern void +isp_put_gxn_id_request(ispsoftc_t *, sns_gxn_id_req_t *, + sns_gxn_id_req_t *); +extern void +isp_get_sns_response(ispsoftc_t *, sns_scrsp_t *, sns_scrsp_t *, int); +extern void +isp_get_gid_ft_response(ispsoftc_t *, sns_gid_ft_rsp_t *, + sns_gid_ft_rsp_t *, int); +extern void +isp_get_gxn_id_response(ispsoftc_t *, sns_gxn_id_rsp_t *, + sns_gxn_id_rsp_t *); +extern void +isp_get_gff_id_response(ispsoftc_t *, sns_gff_id_rsp_t *, + sns_gff_id_rsp_t *); +extern void +isp_get_ga_nxt_response(ispsoftc_t *, sns_ga_nxt_rsp_t *, + sns_ga_nxt_rsp_t *); + +#ifdef ISP_TARGET_MODE +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include <dev/ic/isp_target.h> +#elif defined(__FreeBSD__) +#include <dev/isp/isp_target.h> +#else +#include "isp_target.h" +#endif + +extern int isp_save_xs_tgt(ispsoftc_t *, void *, uint16_t *); +extern void *isp_find_xs_tgt(ispsoftc_t *, uint16_t); +extern uint16_t isp_find_tgt_handle(ispsoftc_t *, void *); +extern void isp_destroy_tgt_handle(ispsoftc_t *, uint16_t); + +extern void +isp_put_atio(ispsoftc_t *, at_entry_t *, at_entry_t *); +extern void +isp_get_atio(ispsoftc_t *, at_entry_t *, at_entry_t *); +extern void +isp_put_atio2(ispsoftc_t *, at2_entry_t *, at2_entry_t *); +extern void +isp_put_atio2e(ispsoftc_t *, at2e_entry_t *, at2e_entry_t *); +extern void +isp_get_atio2(ispsoftc_t *, at2_entry_t *, at2_entry_t *); +extern void +isp_get_atio2e(ispsoftc_t *, at2e_entry_t *, at2e_entry_t *); +extern void +isp_put_ctio(ispsoftc_t *, ct_entry_t *, ct_entry_t *); +extern void +isp_get_ctio(ispsoftc_t *, ct_entry_t *, ct_entry_t *); +extern void +isp_put_ctio2(ispsoftc_t *, ct2_entry_t *, ct2_entry_t *); +extern void +isp_put_ctio2e(ispsoftc_t *, ct2e_entry_t *, ct2e_entry_t *); +extern void +isp_get_ctio2(ispsoftc_t *, ct2_entry_t *, ct2_entry_t *); +extern void +isp_get_ctio2e(ispsoftc_t *, ct2e_entry_t *, ct2e_entry_t *); +extern void +isp_put_enable_lun(ispsoftc_t *, lun_entry_t *, lun_entry_t *); +extern void +isp_get_enable_lun(ispsoftc_t *, lun_entry_t *, lun_entry_t *); +extern void +isp_put_notify(ispsoftc_t *, in_entry_t *, in_entry_t *); +extern void +isp_get_notify(ispsoftc_t *, in_entry_t *, in_entry_t *); +extern void +isp_put_notify_fc(ispsoftc_t *, in_fcentry_t *, in_fcentry_t *); +extern void +isp_put_notify_fc_e(ispsoftc_t *, in_fcentry_e_t *, in_fcentry_e_t *); +extern void +isp_get_notify_fc(ispsoftc_t *, in_fcentry_t *, in_fcentry_t *); +extern void +isp_get_notify_fc_e(ispsoftc_t *, in_fcentry_e_t *, in_fcentry_e_t *); +extern void +isp_put_notify_ack(ispsoftc_t *, na_entry_t *, na_entry_t *); +extern void +isp_get_notify_ack(ispsoftc_t *, na_entry_t *, na_entry_t *); +extern void +isp_put_notify_ack_fc(ispsoftc_t *, na_fcentry_t *, na_fcentry_t *); +extern void +isp_put_notify_ack_fc_e(ispsoftc_t *, na_fcentry_e_t *, na_fcentry_e_t *); +extern void +isp_get_notify_ack_fc(ispsoftc_t *, na_fcentry_t *, na_fcentry_t *); +extern void +isp_get_notify_ack_fc_e(ispsoftc_t *, na_fcentry_e_t *, na_fcentry_e_t *); +#endif /* ISP_TARGET_MODE */ +#endif /* _ISP_LIBRARY_H */ diff --git a/sys/dev/isp/isp_pci.c b/sys/dev/isp/isp_pci.c new file mode 100644 index 000000000000..7ea7dbf5bc7c --- /dev/null +++ b/sys/dev/isp/isp_pci.c @@ -0,0 +1,2506 @@ +/*- + * + * Copyright (c) 1997-2006 by Matthew Jacob + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +/* + * PCI specific probe and attach routines for Qlogic ISP SCSI adapters. + * FreeBSD Version. + */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/module.h> +#if __FreeBSD_version >= 700000 +#include <sys/linker.h> +#include <sys/firmware.h> +#endif +#include <sys/bus.h> +#if __FreeBSD_version < 500000 +#include <pci/pcireg.h> +#include <pci/pcivar.h> +#include <machine/bus_memio.h> +#include <machine/bus_pio.h> +#else +#include <sys/stdint.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#endif +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> +#include <sys/malloc.h> + +#include <dev/isp/isp_freebsd.h> + +#if __FreeBSD_version < 500000 +#define BUS_PROBE_DEFAULT 0 +#endif + +static uint16_t isp_pci_rd_reg(ispsoftc_t *, int); +static void isp_pci_wr_reg(ispsoftc_t *, int, uint16_t); +static uint16_t isp_pci_rd_reg_1080(ispsoftc_t *, int); +static void isp_pci_wr_reg_1080(ispsoftc_t *, int, uint16_t); +static int +isp_pci_rd_isr(ispsoftc_t *, uint16_t *, uint16_t *, uint16_t *); +static int +isp_pci_rd_isr_2300(ispsoftc_t *, uint16_t *, uint16_t *, uint16_t *); +static int isp_pci_mbxdma(ispsoftc_t *); +static int +isp_pci_dmasetup(ispsoftc_t *, XS_T *, ispreq_t *, uint16_t *, uint16_t); +static void +isp_pci_dmateardown(ispsoftc_t *, XS_T *, uint16_t); + + +static void isp_pci_reset1(ispsoftc_t *); +static void isp_pci_dumpregs(ispsoftc_t *, const char *); + +static struct ispmdvec mdvec = { + isp_pci_rd_isr, + isp_pci_rd_reg, + isp_pci_wr_reg, + isp_pci_mbxdma, + isp_pci_dmasetup, + isp_pci_dmateardown, + NULL, + isp_pci_reset1, + isp_pci_dumpregs, + NULL, + BIU_BURST_ENABLE|BIU_PCI_CONF1_FIFO_64 +}; + +static struct ispmdvec mdvec_1080 = { + isp_pci_rd_isr, + isp_pci_rd_reg_1080, + isp_pci_wr_reg_1080, + isp_pci_mbxdma, + isp_pci_dmasetup, + isp_pci_dmateardown, + NULL, + isp_pci_reset1, + isp_pci_dumpregs, + NULL, + BIU_BURST_ENABLE|BIU_PCI_CONF1_FIFO_64 +}; + +static struct ispmdvec mdvec_12160 = { + isp_pci_rd_isr, + isp_pci_rd_reg_1080, + isp_pci_wr_reg_1080, + isp_pci_mbxdma, + isp_pci_dmasetup, + isp_pci_dmateardown, + NULL, + isp_pci_reset1, + isp_pci_dumpregs, + NULL, + BIU_BURST_ENABLE|BIU_PCI_CONF1_FIFO_64 +}; + +static struct ispmdvec mdvec_2100 = { + isp_pci_rd_isr, + isp_pci_rd_reg, + isp_pci_wr_reg, + isp_pci_mbxdma, + isp_pci_dmasetup, + isp_pci_dmateardown, + NULL, + isp_pci_reset1, + isp_pci_dumpregs +}; + +static struct ispmdvec mdvec_2200 = { + isp_pci_rd_isr, + isp_pci_rd_reg, + isp_pci_wr_reg, + isp_pci_mbxdma, + isp_pci_dmasetup, + isp_pci_dmateardown, + NULL, + isp_pci_reset1, + isp_pci_dumpregs +}; + +static struct ispmdvec mdvec_2300 = { + isp_pci_rd_isr_2300, + isp_pci_rd_reg, + isp_pci_wr_reg, + isp_pci_mbxdma, + isp_pci_dmasetup, + isp_pci_dmateardown, + NULL, + isp_pci_reset1, + isp_pci_dumpregs +}; + +#ifndef PCIM_CMD_INVEN +#define PCIM_CMD_INVEN 0x10 +#endif +#ifndef PCIM_CMD_BUSMASTEREN +#define PCIM_CMD_BUSMASTEREN 0x0004 +#endif +#ifndef PCIM_CMD_PERRESPEN +#define PCIM_CMD_PERRESPEN 0x0040 +#endif +#ifndef PCIM_CMD_SEREN +#define PCIM_CMD_SEREN 0x0100 +#endif +#ifndef PCIM_CMD_INTX_DISABLE +#define PCIM_CMD_INTX_DISABLE 0x0400 +#endif + +#ifndef PCIR_COMMAND +#define PCIR_COMMAND 0x04 +#endif + +#ifndef PCIR_CACHELNSZ +#define PCIR_CACHELNSZ 0x0c +#endif + +#ifndef PCIR_LATTIMER +#define PCIR_LATTIMER 0x0d +#endif + +#ifndef PCIR_ROMADDR +#define PCIR_ROMADDR 0x30 +#endif + +#ifndef PCI_VENDOR_QLOGIC +#define PCI_VENDOR_QLOGIC 0x1077 +#endif + +#ifndef PCI_PRODUCT_QLOGIC_ISP1020 +#define PCI_PRODUCT_QLOGIC_ISP1020 0x1020 +#endif + +#ifndef PCI_PRODUCT_QLOGIC_ISP1080 +#define PCI_PRODUCT_QLOGIC_ISP1080 0x1080 +#endif + +#ifndef PCI_PRODUCT_QLOGIC_ISP10160 +#define PCI_PRODUCT_QLOGIC_ISP10160 0x1016 +#endif + +#ifndef PCI_PRODUCT_QLOGIC_ISP12160 +#define PCI_PRODUCT_QLOGIC_ISP12160 0x1216 +#endif + +#ifndef PCI_PRODUCT_QLOGIC_ISP1240 +#define PCI_PRODUCT_QLOGIC_ISP1240 0x1240 +#endif + +#ifndef PCI_PRODUCT_QLOGIC_ISP1280 +#define PCI_PRODUCT_QLOGIC_ISP1280 0x1280 +#endif + +#ifndef PCI_PRODUCT_QLOGIC_ISP2100 +#define PCI_PRODUCT_QLOGIC_ISP2100 0x2100 +#endif + +#ifndef PCI_PRODUCT_QLOGIC_ISP2200 +#define PCI_PRODUCT_QLOGIC_ISP2200 0x2200 +#endif + +#ifndef PCI_PRODUCT_QLOGIC_ISP2300 +#define PCI_PRODUCT_QLOGIC_ISP2300 0x2300 +#endif + +#ifndef PCI_PRODUCT_QLOGIC_ISP2312 +#define PCI_PRODUCT_QLOGIC_ISP2312 0x2312 +#endif + +#ifndef PCI_PRODUCT_QLOGIC_ISP2322 +#define PCI_PRODUCT_QLOGIC_ISP2322 0x2322 +#endif + +#ifndef PCI_PRODUCT_QLOGIC_ISP2422 +#define PCI_PRODUCT_QLOGIC_ISP2422 0x2422 +#endif + +#ifndef PCI_PRODUCT_QLOGIC_ISP6312 +#define PCI_PRODUCT_QLOGIC_ISP6312 0x6312 +#endif + +#ifndef PCI_PRODUCT_QLOGIC_ISP6322 +#define PCI_PRODUCT_QLOGIC_ISP6322 0x6322 +#endif + + +#define PCI_QLOGIC_ISP1020 \ + ((PCI_PRODUCT_QLOGIC_ISP1020 << 16) | PCI_VENDOR_QLOGIC) + +#define PCI_QLOGIC_ISP1080 \ + ((PCI_PRODUCT_QLOGIC_ISP1080 << 16) | PCI_VENDOR_QLOGIC) + +#define PCI_QLOGIC_ISP10160 \ + ((PCI_PRODUCT_QLOGIC_ISP10160 << 16) | PCI_VENDOR_QLOGIC) + +#define PCI_QLOGIC_ISP12160 \ + ((PCI_PRODUCT_QLOGIC_ISP12160 << 16) | PCI_VENDOR_QLOGIC) + +#define PCI_QLOGIC_ISP1240 \ + ((PCI_PRODUCT_QLOGIC_ISP1240 << 16) | PCI_VENDOR_QLOGIC) + +#define PCI_QLOGIC_ISP1280 \ + ((PCI_PRODUCT_QLOGIC_ISP1280 << 16) | PCI_VENDOR_QLOGIC) + +#define PCI_QLOGIC_ISP2100 \ + ((PCI_PRODUCT_QLOGIC_ISP2100 << 16) | PCI_VENDOR_QLOGIC) + +#define PCI_QLOGIC_ISP2200 \ + ((PCI_PRODUCT_QLOGIC_ISP2200 << 16) | PCI_VENDOR_QLOGIC) + +#define PCI_QLOGIC_ISP2300 \ + ((PCI_PRODUCT_QLOGIC_ISP2300 << 16) | PCI_VENDOR_QLOGIC) + +#define PCI_QLOGIC_ISP2312 \ + ((PCI_PRODUCT_QLOGIC_ISP2312 << 16) | PCI_VENDOR_QLOGIC) + +#define PCI_QLOGIC_ISP2322 \ + ((PCI_PRODUCT_QLOGIC_ISP2322 << 16) | PCI_VENDOR_QLOGIC) + +#define PCI_QLOGIC_ISP2422 \ + ((PCI_PRODUCT_QLOGIC_ISP2422 << 16) | PCI_VENDOR_QLOGIC) + +#define PCI_QLOGIC_ISP6312 \ + ((PCI_PRODUCT_QLOGIC_ISP6312 << 16) | PCI_VENDOR_QLOGIC) + +#define PCI_QLOGIC_ISP6322 \ + ((PCI_PRODUCT_QLOGIC_ISP6322 << 16) | PCI_VENDOR_QLOGIC) + +/* + * Odd case for some AMI raid cards... We need to *not* attach to this. + */ +#define AMI_RAID_SUBVENDOR_ID 0x101e + +#define IO_MAP_REG 0x10 +#define MEM_MAP_REG 0x14 + +#define PCI_DFLT_LTNCY 0x40 +#define PCI_DFLT_LNSZ 0x10 + +static int isp_pci_probe (device_t); +static int isp_pci_attach (device_t); + + +struct isp_pcisoftc { + ispsoftc_t pci_isp; + device_t pci_dev; + struct resource * pci_reg; + bus_space_tag_t pci_st; + bus_space_handle_t pci_sh; + void * ih; + int16_t pci_poff[_NREG_BLKS]; + bus_dma_tag_t dmat; + bus_dmamap_t *dmaps; +}; + +static device_method_t isp_pci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, isp_pci_probe), + DEVMETHOD(device_attach, isp_pci_attach), + { 0, 0 } +}; +static void isp_pci_intr(void *); + +static driver_t isp_pci_driver = { + "isp", isp_pci_methods, sizeof (struct isp_pcisoftc) +}; +static devclass_t isp_devclass; +DRIVER_MODULE(isp, pci, isp_pci_driver, isp_devclass, 0, 0); +#if __FreeBSD_version >= 700000 +MODULE_DEPEND(isp, ispfw, 1, 1, 1); +MODULE_DEPEND(isp, firmware, 1, 1, 1); +#else +extern ispfwfunc *isp_get_firmware_p; +#endif + +static int +isp_pci_probe(device_t dev) +{ + switch ((pci_get_device(dev) << 16) | (pci_get_vendor(dev))) { + case PCI_QLOGIC_ISP1020: + device_set_desc(dev, "Qlogic ISP 1020/1040 PCI SCSI Adapter"); + break; + case PCI_QLOGIC_ISP1080: + device_set_desc(dev, "Qlogic ISP 1080 PCI SCSI Adapter"); + break; + case PCI_QLOGIC_ISP1240: + device_set_desc(dev, "Qlogic ISP 1240 PCI SCSI Adapter"); + break; + case PCI_QLOGIC_ISP1280: + device_set_desc(dev, "Qlogic ISP 1280 PCI SCSI Adapter"); + break; + case PCI_QLOGIC_ISP10160: + device_set_desc(dev, "Qlogic ISP 10160 PCI SCSI Adapter"); + break; + case PCI_QLOGIC_ISP12160: + if (pci_get_subvendor(dev) == AMI_RAID_SUBVENDOR_ID) { + return (ENXIO); + } + device_set_desc(dev, "Qlogic ISP 12160 PCI SCSI Adapter"); + break; + case PCI_QLOGIC_ISP2100: + device_set_desc(dev, "Qlogic ISP 2100 PCI FC-AL Adapter"); + break; + case PCI_QLOGIC_ISP2200: + device_set_desc(dev, "Qlogic ISP 2200 PCI FC-AL Adapter"); + break; + case PCI_QLOGIC_ISP2300: + device_set_desc(dev, "Qlogic ISP 2300 PCI FC-AL Adapter"); + break; + case PCI_QLOGIC_ISP2312: + device_set_desc(dev, "Qlogic ISP 2312 PCI FC-AL Adapter"); + break; + case PCI_QLOGIC_ISP2322: + device_set_desc(dev, "Qlogic ISP 2322 PCI FC-AL Adapter"); + break; +#if 0 + case PCI_QLOGIC_ISP2422: + device_set_desc(dev, "Qlogic ISP 2422 PCI FC-AL Adapter"); + break; +#endif + case PCI_QLOGIC_ISP6312: + device_set_desc(dev, "Qlogic ISP 6312 PCI FC-AL Adapter"); + break; + case PCI_QLOGIC_ISP6322: + device_set_desc(dev, "Qlogic ISP 6322 PCI FC-AL Adapter"); + break; + default: + return (ENXIO); + } + if (isp_announced == 0 && bootverbose) { + printf("Qlogic ISP Driver, FreeBSD Version %d.%d, " + "Core Version %d.%d\n", + ISP_PLATFORM_VERSION_MAJOR, ISP_PLATFORM_VERSION_MINOR, + ISP_CORE_VERSION_MAJOR, ISP_CORE_VERSION_MINOR); + isp_announced++; + } + /* + * XXXX: Here is where we might load the f/w module + * XXXX: (or increase a reference count to it). + */ + return (BUS_PROBE_DEFAULT); +} + +#if __FreeBSD_version < 500000 +static void +isp_get_options(device_t dev, ispsoftc_t *isp) +{ + uint64_t wwn; + int bitmap, unit; + + unit = device_get_unit(dev); + if (getenv_int("isp_disable", &bitmap)) { + if (bitmap & (1 << unit)) { + isp->isp_osinfo.disabled = 1; + return; + } + } + + if (getenv_int("isp_no_fwload", &bitmap)) { + if (bitmap & (1 << unit)) + isp->isp_confopts |= ISP_CFG_NORELOAD; + } + if (getenv_int("isp_fwload", &bitmap)) { + if (bitmap & (1 << unit)) + isp->isp_confopts &= ~ISP_CFG_NORELOAD; + } + if (getenv_int("isp_no_nvram", &bitmap)) { + if (bitmap & (1 << unit)) + isp->isp_confopts |= ISP_CFG_NONVRAM; + } + if (getenv_int("isp_nvram", &bitmap)) { + if (bitmap & (1 << unit)) + isp->isp_confopts &= ~ISP_CFG_NONVRAM; + } + if (getenv_int("isp_fcduplex", &bitmap)) { + if (bitmap & (1 << unit)) + isp->isp_confopts |= ISP_CFG_FULL_DUPLEX; + } + if (getenv_int("isp_no_fcduplex", &bitmap)) { + if (bitmap & (1 << unit)) + isp->isp_confopts &= ~ISP_CFG_FULL_DUPLEX; + } + if (getenv_int("isp_nport", &bitmap)) { + if (bitmap & (1 << unit)) + isp->isp_confopts |= ISP_CFG_NPORT; + } + + /* + * Because the resource_*_value functions can neither return + * 64 bit integer values, nor can they be directly coerced + * to interpret the right hand side of the assignment as + * you want them to interpret it, we have to force WWN + * hint replacement to specify WWN strings with a leading + * 'w' (e..g w50000000aaaa0001). Sigh. + */ + if (getenv_quad("isp_portwwn", &wwn)) { + isp->isp_osinfo.default_port_wwn = wwn; + isp->isp_confopts |= ISP_CFG_OWNWWPN; + } + if (isp->isp_osinfo.default_port_wwn == 0) { + isp->isp_osinfo.default_port_wwn = 0x400000007F000009ull; + } + + if (getenv_quad("isp_nodewwn", &wwn)) { + isp->isp_osinfo.default_node_wwn = wwn; + isp->isp_confopts |= ISP_CFG_OWNWWNN; + } + if (isp->isp_osinfo.default_node_wwn == 0) { + isp->isp_osinfo.default_node_wwn = 0x400000007F000009ull; + } + + bitmap = 0; + (void) getenv_int("isp_debug", &bitmap); + if (bitmap) { + isp->isp_dblev = bitmap; + } else { + isp->isp_dblev = ISP_LOGWARN|ISP_LOGERR; + } + if (bootverbose) { + isp->isp_dblev |= ISP_LOGCONFIG|ISP_LOGINFO; + } + +#ifdef ISP_FW_CRASH_DUMP + bitmap = 0; + if (getenv_int("isp_fw_dump_enable", &bitmap)) { + if (bitmap & (1 << unit) { + size_t amt = 0; + if (IS_2200(isp)) { + amt = QLA2200_RISC_IMAGE_DUMP_SIZE; + } else if (IS_23XX(isp)) { + amt = QLA2300_RISC_IMAGE_DUMP_SIZE; + } + if (amt) { + FCPARAM(isp)->isp_dump_data = + malloc(amt, M_DEVBUF, M_WAITOK); + memset(FCPARAM(isp)->isp_dump_data, 0, amt); + } else { + device_printf(dev, + "f/w crash dumps not supported for card\n"); + } + } + } +#endif + bitmap = 0; + if (getenv_int("role", &bitmap)) { + isp->isp_role = bitmap; + } else { + isp->isp_role = ISP_DEFAULT_ROLES; + } +} + +static void +isp_get_pci_options(device_t dev, int *m1, int *m2) +{ + int bitmap; + int unit = device_get_unit(dev); + + *m1 = PCIM_CMD_MEMEN; + *m2 = PCIM_CMD_PORTEN; + if (getenv_int("isp_mem_map", &bitmap)) { + if (bitmap & (1 << unit)) { + *m1 = PCIM_CMD_MEMEN; + *m2 = PCIM_CMD_PORTEN; + } + } + bitmap = 0; + if (getenv_int("isp_io_map", &bitmap)) { + if (bitmap & (1 << unit)) { + *m1 = PCIM_CMD_PORTEN; + *m2 = PCIM_CMD_MEMEN; + } + } +} +#else +static void +isp_get_options(device_t dev, ispsoftc_t *isp) +{ + int tval; + const char *sptr; + /* + * Figure out if we're supposed to skip this one. + */ + + tval = 0; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "disable", &tval) == 0 && tval) { + device_printf(dev, "disabled at user request\n"); + isp->isp_osinfo.disabled = 1; + return; + } + + tval = -1; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "role", &tval) == 0 && tval != -1) { + tval &= (ISP_ROLE_INITIATOR|ISP_ROLE_TARGET); + isp->isp_role = tval; + device_printf(dev, "setting role to 0x%x\n", isp->isp_role); + } else { +#ifdef ISP_TARGET_MODE + isp->isp_role = ISP_ROLE_TARGET; +#else + isp->isp_role = ISP_DEFAULT_ROLES; +#endif + } + + tval = 0; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "fwload_disable", &tval) == 0 && tval != 0) { + isp->isp_confopts |= ISP_CFG_NORELOAD; + } + tval = 0; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "ignore_nvram", &tval) == 0 && tval != 0) { + isp->isp_confopts |= ISP_CFG_NONVRAM; + } + tval = 0; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "fullduplex", &tval) == 0 && tval != 0) { + isp->isp_confopts |= ISP_CFG_FULL_DUPLEX; + } +#ifdef ISP_FW_CRASH_DUMP + tval = 0; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "fw_dump_enable", &tval) == 0 && tval != 0) { + size_t amt = 0; + if (IS_2200(isp)) { + amt = QLA2200_RISC_IMAGE_DUMP_SIZE; + } else if (IS_23XX(isp)) { + amt = QLA2300_RISC_IMAGE_DUMP_SIZE; + } + if (amt) { + FCPARAM(isp)->isp_dump_data = + malloc(amt, M_DEVBUF, M_WAITOK | M_ZERO); + } else { + device_printf(dev, + "f/w crash dumps not supported for this model\n"); + } + } +#endif + + sptr = 0; + if (resource_string_value(device_get_name(dev), device_get_unit(dev), + "topology", (const char **) &sptr) == 0 && sptr != 0) { + if (strcmp(sptr, "lport") == 0) { + isp->isp_confopts |= ISP_CFG_LPORT; + } else if (strcmp(sptr, "nport") == 0) { + isp->isp_confopts |= ISP_CFG_NPORT; + } else if (strcmp(sptr, "lport-only") == 0) { + isp->isp_confopts |= ISP_CFG_LPORT_ONLY; + } else if (strcmp(sptr, "nport-only") == 0) { + isp->isp_confopts |= ISP_CFG_NPORT_ONLY; + } + } + + /* + * Because the resource_*_value functions can neither return + * 64 bit integer values, nor can they be directly coerced + * to interpret the right hand side of the assignment as + * you want them to interpret it, we have to force WWN + * hint replacement to specify WWN strings with a leading + * 'w' (e..g w50000000aaaa0001). Sigh. + */ + sptr = 0; + tval = resource_string_value(device_get_name(dev), device_get_unit(dev), + "portwwn", (const char **) &sptr); + if (tval == 0 && sptr != 0 && *sptr++ == 'w') { + char *eptr = 0; + isp->isp_osinfo.default_port_wwn = strtouq(sptr, &eptr, 16); + if (eptr < sptr + 16 || isp->isp_osinfo.default_port_wwn == 0) { + device_printf(dev, "mangled portwwn hint '%s'\n", sptr); + isp->isp_osinfo.default_port_wwn = 0; + } else { + isp->isp_confopts |= ISP_CFG_OWNWWPN; + } + } + if (isp->isp_osinfo.default_port_wwn == 0) { + isp->isp_osinfo.default_port_wwn = 0x400000007F000009ull; + } + + sptr = 0; + tval = resource_string_value(device_get_name(dev), device_get_unit(dev), + "nodewwn", (const char **) &sptr); + if (tval == 0 && sptr != 0 && *sptr++ == 'w') { + char *eptr = 0; + isp->isp_osinfo.default_node_wwn = strtouq(sptr, &eptr, 16); + if (eptr < sptr + 16 || isp->isp_osinfo.default_node_wwn == 0) { + device_printf(dev, "mangled nodewwn hint '%s'\n", sptr); + isp->isp_osinfo.default_node_wwn = 0; + } else { + isp->isp_confopts |= ISP_CFG_OWNWWNN; + } + } + if (isp->isp_osinfo.default_node_wwn == 0) { + isp->isp_osinfo.default_node_wwn = 0x400000007F000009ull; + } + + isp->isp_osinfo.default_id = -1; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "iid", &tval) == 0) { + isp->isp_osinfo.default_id = tval; + isp->isp_confopts |= ISP_CFG_OWNLOOPID; + } + if (isp->isp_osinfo.default_id == -1) { + if (IS_FC(isp)) { + isp->isp_osinfo.default_id = 109; + } else { + isp->isp_osinfo.default_id = 7; + } + } + + /* + * Set up logging levels. + */ + tval = 0; + (void) resource_int_value(device_get_name(dev), device_get_unit(dev), + "debug", &tval); + if (tval) { + isp->isp_dblev = tval; + } else { + isp->isp_dblev = ISP_LOGWARN|ISP_LOGERR; + } + if (bootverbose) { + isp->isp_dblev |= ISP_LOGCONFIG|ISP_LOGINFO; + } + +} + +static void +isp_get_pci_options(device_t dev, int *m1, int *m2) +{ + int tval; + /* + * Which we should try first - memory mapping or i/o mapping? + * + * We used to try memory first followed by i/o on alpha, otherwise + * the reverse, but we should just try memory first all the time now. + */ + *m1 = PCIM_CMD_MEMEN; + *m2 = PCIM_CMD_PORTEN; + + tval = 0; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "prefer_iomap", &tval) == 0 && tval != 0) { + *m1 = PCIM_CMD_PORTEN; + *m2 = PCIM_CMD_MEMEN; + } + tval = 0; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "prefer_memmap", &tval) == 0 && tval != 0) { + *m1 = PCIM_CMD_MEMEN; + *m2 = PCIM_CMD_PORTEN; + } +} +#endif + +static int +isp_pci_attach(device_t dev) +{ + struct resource *regs, *irq; + int rtp, rgd, iqd, m1, m2; + uint32_t data, cmd, linesz, psize, basetype; + struct isp_pcisoftc *pcs; + ispsoftc_t *isp = NULL; + struct ispmdvec *mdvp; +#if __FreeBSD_version >= 500000 + int locksetup = 0; +#endif + + pcs = device_get_softc(dev); + if (pcs == NULL) { + device_printf(dev, "cannot get softc\n"); + return (ENOMEM); + } + memset(pcs, 0, sizeof (*pcs)); + pcs->pci_dev = dev; + isp = &pcs->pci_isp; + + /* + * Get Generic Options + */ + isp_get_options(dev, isp); + + /* + * Check to see if options have us disabled + */ + if (isp->isp_osinfo.disabled) { + /* + * But return zero to preserve unit numbering + */ + return (0); + } + + /* + * Get PCI options- which in this case are just mapping preferences. + */ + isp_get_pci_options(dev, &m1, &m2); + + + linesz = PCI_DFLT_LNSZ; + irq = regs = NULL; + rgd = rtp = iqd = 0; + + cmd = pci_read_config(dev, PCIR_COMMAND, 2); + if (cmd & m1) { + rtp = (m1 == PCIM_CMD_MEMEN)? SYS_RES_MEMORY : SYS_RES_IOPORT; + rgd = (m1 == PCIM_CMD_MEMEN)? MEM_MAP_REG : IO_MAP_REG; + regs = bus_alloc_resource_any(dev, rtp, &rgd, RF_ACTIVE); + } + if (regs == NULL && (cmd & m2)) { + rtp = (m2 == PCIM_CMD_MEMEN)? SYS_RES_MEMORY : SYS_RES_IOPORT; + rgd = (m2 == PCIM_CMD_MEMEN)? MEM_MAP_REG : IO_MAP_REG; + regs = bus_alloc_resource_any(dev, rtp, &rgd, RF_ACTIVE); + } + if (regs == NULL) { + device_printf(dev, "unable to map any ports\n"); + goto bad; + } + if (bootverbose) { + device_printf(dev, "using %s space register mapping\n", + (rgd == IO_MAP_REG)? "I/O" : "Memory"); + } + pcs->pci_dev = dev; + pcs->pci_reg = regs; + pcs->pci_st = rman_get_bustag(regs); + pcs->pci_sh = rman_get_bushandle(regs); + + pcs->pci_poff[BIU_BLOCK >> _BLK_REG_SHFT] = BIU_REGS_OFF; + pcs->pci_poff[MBOX_BLOCK >> _BLK_REG_SHFT] = PCI_MBOX_REGS_OFF; + pcs->pci_poff[SXP_BLOCK >> _BLK_REG_SHFT] = PCI_SXP_REGS_OFF; + pcs->pci_poff[RISC_BLOCK >> _BLK_REG_SHFT] = PCI_RISC_REGS_OFF; + pcs->pci_poff[DMA_BLOCK >> _BLK_REG_SHFT] = DMA_REGS_OFF; + mdvp = &mdvec; + basetype = ISP_HA_SCSI_UNKNOWN; + psize = sizeof (sdparam); + if (pci_get_devid(dev) == PCI_QLOGIC_ISP1020) { + mdvp = &mdvec; + basetype = ISP_HA_SCSI_UNKNOWN; + psize = sizeof (sdparam); + } + if (pci_get_devid(dev) == PCI_QLOGIC_ISP1080) { + mdvp = &mdvec_1080; + basetype = ISP_HA_SCSI_1080; + psize = sizeof (sdparam); + pcs->pci_poff[DMA_BLOCK >> _BLK_REG_SHFT] = + ISP1080_DMA_REGS_OFF; + } + if (pci_get_devid(dev) == PCI_QLOGIC_ISP1240) { + mdvp = &mdvec_1080; + basetype = ISP_HA_SCSI_1240; + psize = 2 * sizeof (sdparam); + pcs->pci_poff[DMA_BLOCK >> _BLK_REG_SHFT] = + ISP1080_DMA_REGS_OFF; + } + if (pci_get_devid(dev) == PCI_QLOGIC_ISP1280) { + mdvp = &mdvec_1080; + basetype = ISP_HA_SCSI_1280; + psize = 2 * sizeof (sdparam); + pcs->pci_poff[DMA_BLOCK >> _BLK_REG_SHFT] = + ISP1080_DMA_REGS_OFF; + } + if (pci_get_devid(dev) == PCI_QLOGIC_ISP10160) { + mdvp = &mdvec_12160; + basetype = ISP_HA_SCSI_10160; + psize = sizeof (sdparam); + pcs->pci_poff[DMA_BLOCK >> _BLK_REG_SHFT] = + ISP1080_DMA_REGS_OFF; + } + if (pci_get_devid(dev) == PCI_QLOGIC_ISP12160) { + mdvp = &mdvec_12160; + basetype = ISP_HA_SCSI_12160; + psize = 2 * sizeof (sdparam); + pcs->pci_poff[DMA_BLOCK >> _BLK_REG_SHFT] = + ISP1080_DMA_REGS_OFF; + } + if (pci_get_devid(dev) == PCI_QLOGIC_ISP2100) { + mdvp = &mdvec_2100; + basetype = ISP_HA_FC_2100; + psize = sizeof (fcparam); + pcs->pci_poff[MBOX_BLOCK >> _BLK_REG_SHFT] = + PCI_MBOX_REGS2100_OFF; + if (pci_get_revid(dev) < 3) { + /* + * XXX: Need to get the actual revision + * XXX: number of the 2100 FB. At any rate, + * XXX: lower cache line size for early revision + * XXX; boards. + */ + linesz = 1; + } + } + if (pci_get_devid(dev) == PCI_QLOGIC_ISP2200) { + mdvp = &mdvec_2200; + basetype = ISP_HA_FC_2200; + psize = sizeof (fcparam); + pcs->pci_poff[MBOX_BLOCK >> _BLK_REG_SHFT] = + PCI_MBOX_REGS2100_OFF; + } + if (pci_get_devid(dev) == PCI_QLOGIC_ISP2300) { + mdvp = &mdvec_2300; + basetype = ISP_HA_FC_2300; + psize = sizeof (fcparam); + pcs->pci_poff[MBOX_BLOCK >> _BLK_REG_SHFT] = + PCI_MBOX_REGS2300_OFF; + } + if (pci_get_devid(dev) == PCI_QLOGIC_ISP2312 || + pci_get_devid(dev) == PCI_QLOGIC_ISP6312) { + mdvp = &mdvec_2300; + basetype = ISP_HA_FC_2312; + psize = sizeof (fcparam); + pcs->pci_poff[MBOX_BLOCK >> _BLK_REG_SHFT] = + PCI_MBOX_REGS2300_OFF; + } + if (pci_get_devid(dev) == PCI_QLOGIC_ISP2322 || + pci_get_devid(dev) == PCI_QLOGIC_ISP6322) { + mdvp = &mdvec_2300; + basetype = ISP_HA_FC_2322; + psize = sizeof (fcparam); + pcs->pci_poff[MBOX_BLOCK >> _BLK_REG_SHFT] = + PCI_MBOX_REGS2300_OFF; + } + if (pci_get_devid(dev) == PCI_QLOGIC_ISP2422) { + mdvp = &mdvec_2300; + basetype = ISP_HA_FC_2422; + psize = sizeof (fcparam); + pcs->pci_poff[MBOX_BLOCK >> _BLK_REG_SHFT] = + PCI_MBOX_REGS2300_OFF; + } + isp = &pcs->pci_isp; + isp->isp_param = malloc(psize, M_DEVBUF, M_NOWAIT | M_ZERO); + if (isp->isp_param == NULL) { + device_printf(dev, "cannot allocate parameter data\n"); + goto bad; + } + isp->isp_mdvec = mdvp; + isp->isp_type = basetype; + isp->isp_revision = pci_get_revid(dev); + isp->isp_dev = dev; + +#if __FreeBSD_version >= 700000 + /* + * Try and find firmware for this device. + */ + { + char fwname[32]; + unsigned int did = pci_get_device(dev); + + /* + * Map a few pci ids to fw names + */ + switch (did) { + case PCI_PRODUCT_QLOGIC_ISP1020: + did = 0x1040; + break; + case PCI_PRODUCT_QLOGIC_ISP1240: + did = 0x1080; + break; + case PCI_PRODUCT_QLOGIC_ISP10160: + case PCI_PRODUCT_QLOGIC_ISP12160: + did = 0x12160; + break; + case PCI_PRODUCT_QLOGIC_ISP6312: + case PCI_PRODUCT_QLOGIC_ISP2312: + did = 0x2300; + break; + case PCI_PRODUCT_QLOGIC_ISP6322: + did = 0x2322; + break; + default: + break; + } + + isp->isp_osinfo.fw = NULL; + if (isp->isp_role & ISP_ROLE_TARGET) { + snprintf(fwname, sizeof (fwname), "isp_%04x_it", did); + isp->isp_osinfo.fw = firmware_get(fwname); + } + if (isp->isp_osinfo.fw == NULL) { + snprintf(fwname, sizeof (fwname), "isp_%04x", did); + isp->isp_osinfo.fw = firmware_get(fwname); + } + if (isp->isp_osinfo.fw != NULL) { + union { + const void *fred; + uint16_t *bob; + } u; + u.fred = isp->isp_osinfo.fw->data; + isp->isp_mdvec->dv_ispfw = u.bob; + } + } +#else + if (isp_get_firmware_p) { + int device = (int) pci_get_device(dev); +#ifdef ISP_TARGET_MODE + (*isp_get_firmware_p)(0, 1, device, &mdvp->dv_ispfw); +#else + (*isp_get_firmware_p)(0, 0, device, &mdvp->dv_ispfw); +#endif + } +#endif + + /* + * Make sure that SERR, PERR, WRITE INVALIDATE and BUSMASTER + * are set. + */ + cmd |= PCIM_CMD_SEREN | PCIM_CMD_PERRESPEN | + PCIM_CMD_BUSMASTEREN | PCIM_CMD_INVEN; + + if (IS_2300(isp)) { /* per QLogic errata */ + cmd &= ~PCIM_CMD_INVEN; + } + + if (IS_23XX(isp)) { + /* + * Can't tell if ROM will hang on 'ABOUT FIRMWARE' command. + */ + isp->isp_touched = 1; + + } + + if (IS_2322(isp) || pci_get_devid(dev) == PCI_QLOGIC_ISP6312) { + cmd &= ~PCIM_CMD_INTX_DISABLE; + } + + pci_write_config(dev, PCIR_COMMAND, cmd, 2); + + /* + * Make sure the Cache Line Size register is set sensibly. + */ + data = pci_read_config(dev, PCIR_CACHELNSZ, 1); + if (data != linesz) { + data = PCI_DFLT_LNSZ; + isp_prt(isp, ISP_LOGCONFIG, "set PCI line size to %d", data); + pci_write_config(dev, PCIR_CACHELNSZ, data, 1); + } + + /* + * Make sure the Latency Timer is sane. + */ + data = pci_read_config(dev, PCIR_LATTIMER, 1); + if (data < PCI_DFLT_LTNCY) { + data = PCI_DFLT_LTNCY; + isp_prt(isp, ISP_LOGCONFIG, "set PCI latency to %d", data); + pci_write_config(dev, PCIR_LATTIMER, data, 1); + } + + /* + * Make sure we've disabled the ROM. + */ + data = pci_read_config(dev, PCIR_ROMADDR, 4); + data &= ~1; + pci_write_config(dev, PCIR_ROMADDR, data, 4); + + iqd = 0; + irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &iqd, + RF_ACTIVE | RF_SHAREABLE); + if (irq == NULL) { + device_printf(dev, "could not allocate interrupt\n"); + goto bad; + } + +#if __FreeBSD_version >= 500000 + /* Make sure the lock is set up. */ + mtx_init(&isp->isp_osinfo.lock, "isp", NULL, MTX_DEF); + locksetup++; +#endif + + if (bus_setup_intr(dev, irq, ISP_IFLAGS, isp_pci_intr, isp, &pcs->ih)) { + device_printf(dev, "could not setup interrupt\n"); + goto bad; + } + + /* + * Last minute checks... + */ + if (IS_23XX(isp)) { + isp->isp_port = pci_get_function(dev); + } + + /* + * Make sure we're in reset state. + */ + ISP_LOCK(isp); + isp_reset(isp); + if (isp->isp_state != ISP_RESETSTATE) { + ISP_UNLOCK(isp); + goto bad; + } + isp_init(isp); + if (isp->isp_role != ISP_ROLE_NONE && isp->isp_state != ISP_INITSTATE) { + isp_uninit(isp); + ISP_UNLOCK(isp); + goto bad; + } + isp_attach(isp); + if (isp->isp_role != ISP_ROLE_NONE && isp->isp_state != ISP_RUNSTATE) { + isp_uninit(isp); + ISP_UNLOCK(isp); + goto bad; + } + /* + * XXXX: Here is where we might unload the f/w module + * XXXX: (or decrease the reference count to it). + */ + ISP_UNLOCK(isp); + return (0); + +bad: + + if (pcs && pcs->ih) { + (void) bus_teardown_intr(dev, irq, pcs->ih); + } + +#if __FreeBSD_version >= 500000 + if (locksetup && isp) { + mtx_destroy(&isp->isp_osinfo.lock); + } +#endif + + if (irq) { + (void) bus_release_resource(dev, SYS_RES_IRQ, iqd, irq); + } + + + if (regs) { + (void) bus_release_resource(dev, rtp, rgd, regs); + } + + if (pcs) { + if (pcs->pci_isp.isp_param) { +#ifdef ISP_FW_CRASH_DUMP + if (IS_FC(isp) && FCPARAM(isp)->isp_dump_data) { + free(FCPARAM(isp)->isp_dump_data, M_DEVBUF); + } +#endif + free(pcs->pci_isp.isp_param, M_DEVBUF); + } + } + + /* + * XXXX: Here is where we might unload the f/w module + * XXXX: (or decrease the reference count to it). + */ + return (ENXIO); +} + +static void +isp_pci_intr(void *arg) +{ + ispsoftc_t *isp = arg; + uint16_t isr, sema, mbox; + + ISP_LOCK(isp); + isp->isp_intcnt++; + if (ISP_READ_ISR(isp, &isr, &sema, &mbox) == 0) { + isp->isp_intbogus++; + } else { + int iok = isp->isp_osinfo.intsok; + isp->isp_osinfo.intsok = 0; + isp_intr(isp, isr, sema, mbox); + isp->isp_osinfo.intsok = iok; + } + ISP_UNLOCK(isp); +} + + +#define IspVirt2Off(a, x) \ + (((struct isp_pcisoftc *)a)->pci_poff[((x) & _BLK_REG_MASK) >> \ + _BLK_REG_SHFT] + ((x) & 0xfff)) + +#define BXR2(pcs, off) \ + bus_space_read_2(pcs->pci_st, pcs->pci_sh, off) +#define BXW2(pcs, off, v) \ + bus_space_write_2(pcs->pci_st, pcs->pci_sh, off, v) + + +static __inline int +isp_pci_rd_debounced(ispsoftc_t *isp, int off, uint16_t *rp) +{ + struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp; + uint16_t val0, val1; + int i = 0; + + do { + val0 = BXR2(pcs, IspVirt2Off(isp, off)); + val1 = BXR2(pcs, IspVirt2Off(isp, off)); + } while (val0 != val1 && ++i < 1000); + if (val0 != val1) { + return (1); + } + *rp = val0; + return (0); +} + +static int +isp_pci_rd_isr(ispsoftc_t *isp, uint16_t *isrp, + uint16_t *semap, uint16_t *mbp) +{ + struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp; + uint16_t isr, sema; + + if (IS_2100(isp)) { + if (isp_pci_rd_debounced(isp, BIU_ISR, &isr)) { + return (0); + } + if (isp_pci_rd_debounced(isp, BIU_SEMA, &sema)) { + return (0); + } + } else { + isr = BXR2(pcs, IspVirt2Off(isp, BIU_ISR)); + sema = BXR2(pcs, IspVirt2Off(isp, BIU_SEMA)); + } + isp_prt(isp, ISP_LOGDEBUG3, "ISR 0x%x SEMA 0x%x", isr, sema); + isr &= INT_PENDING_MASK(isp); + sema &= BIU_SEMA_LOCK; + if (isr == 0 && sema == 0) { + return (0); + } + *isrp = isr; + if ((*semap = sema) != 0) { + if (IS_2100(isp)) { + if (isp_pci_rd_debounced(isp, OUTMAILBOX0, mbp)) { + return (0); + } + } else { + *mbp = BXR2(pcs, IspVirt2Off(isp, OUTMAILBOX0)); + } + } + return (1); +} + +static int +isp_pci_rd_isr_2300(ispsoftc_t *isp, uint16_t *isrp, + uint16_t *semap, uint16_t *mbox0p) +{ + struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp; + uint16_t hccr; + uint32_t r2hisr; + + if (!(BXR2(pcs, IspVirt2Off(isp, BIU_ISR) & BIU2100_ISR_RISC_INT))) { + *isrp = 0; + return (0); + } + r2hisr = bus_space_read_4(pcs->pci_st, pcs->pci_sh, + IspVirt2Off(pcs, BIU_R2HSTSLO)); + isp_prt(isp, ISP_LOGDEBUG3, "RISC2HOST ISR 0x%x", r2hisr); + if ((r2hisr & BIU_R2HST_INTR) == 0) { + *isrp = 0; + return (0); + } + switch (r2hisr & BIU_R2HST_ISTAT_MASK) { + case ISPR2HST_ROM_MBX_OK: + case ISPR2HST_ROM_MBX_FAIL: + case ISPR2HST_MBX_OK: + case ISPR2HST_MBX_FAIL: + case ISPR2HST_ASYNC_EVENT: + *isrp = r2hisr & 0xffff; + *mbox0p = (r2hisr >> 16); + *semap = 1; + return (1); + case ISPR2HST_RIO_16: + *isrp = r2hisr & 0xffff; + *mbox0p = ASYNC_RIO1; + *semap = 1; + return (1); + case ISPR2HST_FPOST: + *isrp = r2hisr & 0xffff; + *mbox0p = ASYNC_CMD_CMPLT; + *semap = 1; + return (1); + case ISPR2HST_FPOST_CTIO: + *isrp = r2hisr & 0xffff; + *mbox0p = ASYNC_CTIO_DONE; + *semap = 1; + return (1); + case ISPR2HST_RSPQ_UPDATE: + *isrp = r2hisr & 0xffff; + *mbox0p = 0; + *semap = 0; + return (1); + default: + hccr = ISP_READ(isp, HCCR); + if (hccr & HCCR_PAUSE) { + ISP_WRITE(isp, HCCR, HCCR_RESET); + isp_prt(isp, ISP_LOGERR, + "RISC paused at interrupt (%x->%x\n", hccr, + ISP_READ(isp, HCCR)); + } else { + isp_prt(isp, ISP_LOGERR, "unknown interrerupt 0x%x\n", + r2hisr); + } + return (0); + } +} + +static uint16_t +isp_pci_rd_reg(ispsoftc_t *isp, int regoff) +{ + uint16_t rv; + struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp; + int oldconf = 0; + + if ((regoff & _BLK_REG_MASK) == SXP_BLOCK) { + /* + * We will assume that someone has paused the RISC processor. + */ + oldconf = BXR2(pcs, IspVirt2Off(isp, BIU_CONF1)); + BXW2(pcs, IspVirt2Off(isp, BIU_CONF1), + oldconf | BIU_PCI_CONF1_SXP); + } + rv = BXR2(pcs, IspVirt2Off(isp, regoff)); + if ((regoff & _BLK_REG_MASK) == SXP_BLOCK) { + BXW2(pcs, IspVirt2Off(isp, BIU_CONF1), oldconf); + } + return (rv); +} + +static void +isp_pci_wr_reg(ispsoftc_t *isp, int regoff, uint16_t val) +{ + struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp; + int oldconf = 0; + + if ((regoff & _BLK_REG_MASK) == SXP_BLOCK) { + /* + * We will assume that someone has paused the RISC processor. + */ + oldconf = BXR2(pcs, IspVirt2Off(isp, BIU_CONF1)); + BXW2(pcs, IspVirt2Off(isp, BIU_CONF1), + oldconf | BIU_PCI_CONF1_SXP); + } + BXW2(pcs, IspVirt2Off(isp, regoff), val); + if ((regoff & _BLK_REG_MASK) == SXP_BLOCK) { + BXW2(pcs, IspVirt2Off(isp, BIU_CONF1), oldconf); + } +} + +static uint16_t +isp_pci_rd_reg_1080(ispsoftc_t *isp, int regoff) +{ + uint16_t rv, oc = 0; + struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp; + + if ((regoff & _BLK_REG_MASK) == SXP_BLOCK || + (regoff & _BLK_REG_MASK) == (SXP_BLOCK|SXP_BANK1_SELECT)) { + uint16_t tc; + /* + * We will assume that someone has paused the RISC processor. + */ + oc = BXR2(pcs, IspVirt2Off(isp, BIU_CONF1)); + tc = oc & ~BIU_PCI1080_CONF1_DMA; + if (regoff & SXP_BANK1_SELECT) + tc |= BIU_PCI1080_CONF1_SXP1; + else + tc |= BIU_PCI1080_CONF1_SXP0; + BXW2(pcs, IspVirt2Off(isp, BIU_CONF1), tc); + } else if ((regoff & _BLK_REG_MASK) == DMA_BLOCK) { + oc = BXR2(pcs, IspVirt2Off(isp, BIU_CONF1)); + BXW2(pcs, IspVirt2Off(isp, BIU_CONF1), + oc | BIU_PCI1080_CONF1_DMA); + } + rv = BXR2(pcs, IspVirt2Off(isp, regoff)); + if (oc) { + BXW2(pcs, IspVirt2Off(isp, BIU_CONF1), oc); + } + return (rv); +} + +static void +isp_pci_wr_reg_1080(ispsoftc_t *isp, int regoff, uint16_t val) +{ + struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp; + int oc = 0; + + if ((regoff & _BLK_REG_MASK) == SXP_BLOCK || + (regoff & _BLK_REG_MASK) == (SXP_BLOCK|SXP_BANK1_SELECT)) { + uint16_t tc; + /* + * We will assume that someone has paused the RISC processor. + */ + oc = BXR2(pcs, IspVirt2Off(isp, BIU_CONF1)); + tc = oc & ~BIU_PCI1080_CONF1_DMA; + if (regoff & SXP_BANK1_SELECT) + tc |= BIU_PCI1080_CONF1_SXP1; + else + tc |= BIU_PCI1080_CONF1_SXP0; + BXW2(pcs, IspVirt2Off(isp, BIU_CONF1), tc); + } else if ((regoff & _BLK_REG_MASK) == DMA_BLOCK) { + oc = BXR2(pcs, IspVirt2Off(isp, BIU_CONF1)); + BXW2(pcs, IspVirt2Off(isp, BIU_CONF1), + oc | BIU_PCI1080_CONF1_DMA); + } + BXW2(pcs, IspVirt2Off(isp, regoff), val); + if (oc) { + BXW2(pcs, IspVirt2Off(isp, BIU_CONF1), oc); + } +} + + +struct imush { + ispsoftc_t *isp; + int error; +}; + +static void imc(void *, bus_dma_segment_t *, int, int); + +static void +imc(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + struct imush *imushp = (struct imush *) arg; + if (error) { + imushp->error = error; + } else { + ispsoftc_t *isp =imushp->isp; + bus_addr_t addr = segs->ds_addr; + + isp->isp_rquest_dma = addr; + addr += ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp)); + isp->isp_result_dma = addr; + if (IS_FC(isp)) { + addr += ISP_QUEUE_SIZE(RESULT_QUEUE_LEN(isp)); + FCPARAM(isp)->isp_scdma = addr; + } + } +} + +/* + * Should be BUS_SPACE_MAXSIZE, but MAXPHYS is larger than BUS_SPACE_MAXSIZE + */ +#define ISP_NSEGS ((MAXPHYS / PAGE_SIZE) + 1) + +#if __FreeBSD_version < 500000 +#define isp_dma_tag_create bus_dma_tag_create +#else +#define isp_dma_tag_create(a, b, c, d, e, f, g, h, i, j, k, z) \ + bus_dma_tag_create(a, b, c, d, e, f, g, h, i, j, k, \ + busdma_lock_mutex, &Giant, z) +#endif + +static int +isp_pci_mbxdma(ispsoftc_t *isp) +{ + struct isp_pcisoftc *pcs = (struct isp_pcisoftc *)isp; + caddr_t base; + uint32_t len; + int i, error, ns; + bus_size_t slim; /* segment size */ + bus_addr_t llim; /* low limit of unavailable dma */ + bus_addr_t hlim; /* high limit of unavailable dma */ + struct imush im; + + /* + * Already been here? If so, leave... + */ + if (isp->isp_rquest) { + return (0); + } + + hlim = BUS_SPACE_MAXADDR; + if (IS_ULTRA2(isp) || IS_FC(isp) || IS_1240(isp)) { + slim = (bus_size_t) (1ULL << 32); + llim = BUS_SPACE_MAXADDR; + } else { + llim = BUS_SPACE_MAXADDR_32BIT; + slim = (1 << 24); + } + + /* + * XXX: We don't really support 64 bit target mode for parallel scsi yet + */ +#ifdef ISP_TARGET_MODE + if (IS_SCSI(isp) && sizeof (bus_addr_t) > 4) { + isp_prt(isp, ISP_LOGERR, "we cannot do DAC for SPI cards yet"); + return (1); + } +#endif + + ISP_UNLOCK(isp); + if (isp_dma_tag_create(NULL, 1, slim, llim, hlim, + NULL, NULL, BUS_SPACE_MAXSIZE, ISP_NSEGS, slim, 0, &pcs->dmat)) { + isp_prt(isp, ISP_LOGERR, "could not create master dma tag"); + ISP_LOCK(isp); + return (1); + } + + + len = sizeof (XS_T **) * isp->isp_maxcmds; + isp->isp_xflist = (XS_T **) malloc(len, M_DEVBUF, M_WAITOK | M_ZERO); + if (isp->isp_xflist == NULL) { + isp_prt(isp, ISP_LOGERR, "cannot alloc xflist array"); + ISP_LOCK(isp); + return (1); + } +#ifdef ISP_TARGET_MODE + len = sizeof (void **) * isp->isp_maxcmds; + isp->isp_tgtlist = (void **) malloc(len, M_DEVBUF, M_WAITOK | M_ZERO); + if (isp->isp_tgtlist == NULL) { + isp_prt(isp, ISP_LOGERR, "cannot alloc tgtlist array"); + ISP_LOCK(isp); + return (1); + } +#endif + len = sizeof (bus_dmamap_t) * isp->isp_maxcmds; + pcs->dmaps = (bus_dmamap_t *) malloc(len, M_DEVBUF, M_WAITOK); + if (pcs->dmaps == NULL) { + isp_prt(isp, ISP_LOGERR, "can't alloc dma map storage"); + free(isp->isp_xflist, M_DEVBUF); +#ifdef ISP_TARGET_MODE + free(isp->isp_tgtlist, M_DEVBUF); +#endif + ISP_LOCK(isp); + return (1); + } + + /* + * Allocate and map the request, result queues, plus FC scratch area. + */ + len = ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp)); + len += ISP_QUEUE_SIZE(RESULT_QUEUE_LEN(isp)); + if (IS_FC(isp)) { + len += ISP2100_SCRLEN; + } + + ns = (len / PAGE_SIZE) + 1; + /* + * Create a tag for the control spaces- force it to within 32 bits. + */ + if (isp_dma_tag_create(pcs->dmat, QENTRY_LEN, slim, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, + NULL, NULL, len, ns, slim, 0, &isp->isp_cdmat)) { + isp_prt(isp, ISP_LOGERR, + "cannot create a dma tag for control spaces"); + free(pcs->dmaps, M_DEVBUF); + free(isp->isp_xflist, M_DEVBUF); +#ifdef ISP_TARGET_MODE + free(isp->isp_tgtlist, M_DEVBUF); +#endif + ISP_LOCK(isp); + return (1); + } + + if (bus_dmamem_alloc(isp->isp_cdmat, (void **)&base, BUS_DMA_NOWAIT, + &isp->isp_cdmap) != 0) { + isp_prt(isp, ISP_LOGERR, + "cannot allocate %d bytes of CCB memory", len); + bus_dma_tag_destroy(isp->isp_cdmat); + free(isp->isp_xflist, M_DEVBUF); +#ifdef ISP_TARGET_MODE + free(isp->isp_tgtlist, M_DEVBUF); +#endif + free(pcs->dmaps, M_DEVBUF); + ISP_LOCK(isp); + return (1); + } + + for (i = 0; i < isp->isp_maxcmds; i++) { + error = bus_dmamap_create(pcs->dmat, 0, &pcs->dmaps[i]); + if (error) { + isp_prt(isp, ISP_LOGERR, + "error %d creating per-cmd DMA maps", error); + while (--i >= 0) { + bus_dmamap_destroy(pcs->dmat, pcs->dmaps[i]); + } + goto bad; + } + } + + im.isp = isp; + im.error = 0; + bus_dmamap_load(isp->isp_cdmat, isp->isp_cdmap, base, len, imc, &im, 0); + if (im.error) { + isp_prt(isp, ISP_LOGERR, + "error %d loading dma map for control areas", im.error); + goto bad; + } + + isp->isp_rquest = base; + base += ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp)); + isp->isp_result = base; + if (IS_FC(isp)) { + base += ISP_QUEUE_SIZE(RESULT_QUEUE_LEN(isp)); + FCPARAM(isp)->isp_scratch = base; + } + ISP_LOCK(isp); + return (0); + +bad: + bus_dmamem_free(isp->isp_cdmat, base, isp->isp_cdmap); + bus_dma_tag_destroy(isp->isp_cdmat); + free(isp->isp_xflist, M_DEVBUF); +#ifdef ISP_TARGET_MODE + free(isp->isp_tgtlist, M_DEVBUF); +#endif + free(pcs->dmaps, M_DEVBUF); + ISP_LOCK(isp); + isp->isp_rquest = NULL; + return (1); +} + +typedef struct { + ispsoftc_t *isp; + void *cmd_token; + void *rq; + uint16_t *nxtip; + uint16_t optr; + int error; +} mush_t; + +#define MUSHERR_NOQENTRIES -2 + +#ifdef ISP_TARGET_MODE +/* + * We need to handle DMA for target mode differently from initiator mode. + * + * DMA mapping and construction and submission of CTIO Request Entries + * and rendevous for completion are very tightly coupled because we start + * out by knowing (per platform) how much data we have to move, but we + * don't know, up front, how many DMA mapping segments will have to be used + * cover that data, so we don't know how many CTIO Request Entries we + * will end up using. Further, for performance reasons we may want to + * (on the last CTIO for Fibre Channel), send status too (if all went well). + * + * The standard vector still goes through isp_pci_dmasetup, but the callback + * for the DMA mapping routines comes here instead with the whole transfer + * mapped and a pointer to a partially filled in already allocated request + * queue entry. We finish the job. + */ +static void tdma_mk(void *, bus_dma_segment_t *, int, int); +static void tdma_mkfc(void *, bus_dma_segment_t *, int, int); + +#define STATUS_WITH_DATA 1 + +static void +tdma_mk(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error) +{ + mush_t *mp; + struct ccb_scsiio *csio; + ispsoftc_t *isp; + struct isp_pcisoftc *pcs; + bus_dmamap_t *dp; + ct_entry_t *cto, *qe; + uint8_t scsi_status; + uint16_t curi, nxti, handle; + uint32_t sflags; + int32_t resid; + int nth_ctio, nctios, send_status; + + mp = (mush_t *) arg; + if (error) { + mp->error = error; + return; + } + + isp = mp->isp; + csio = mp->cmd_token; + cto = mp->rq; + curi = isp->isp_reqidx; + qe = (ct_entry_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, curi); + + cto->ct_xfrlen = 0; + cto->ct_seg_count = 0; + cto->ct_header.rqs_entry_count = 1; + MEMZERO(cto->ct_dataseg, sizeof(cto->ct_dataseg)); + + if (nseg == 0) { + cto->ct_header.rqs_seqno = 1; + isp_prt(isp, ISP_LOGTDEBUG1, + "CTIO[%x] lun%d iid%d tag %x flgs %x sts %x ssts %x res %d", + cto->ct_fwhandle, csio->ccb_h.target_lun, cto->ct_iid, + cto->ct_tag_val, cto->ct_flags, cto->ct_status, + cto->ct_scsi_status, cto->ct_resid); + ISP_TDQE(isp, "tdma_mk[no data]", curi, cto); + isp_put_ctio(isp, cto, qe); + return; + } + + nctios = nseg / ISP_RQDSEG; + if (nseg % ISP_RQDSEG) { + nctios++; + } + + /* + * Save syshandle, and potentially any SCSI status, which we'll + * reinsert on the last CTIO we're going to send. + */ + + handle = cto->ct_syshandle; + cto->ct_syshandle = 0; + cto->ct_header.rqs_seqno = 0; + send_status = (cto->ct_flags & CT_SENDSTATUS) != 0; + + if (send_status) { + sflags = cto->ct_flags & (CT_SENDSTATUS | CT_CCINCR); + cto->ct_flags &= ~(CT_SENDSTATUS | CT_CCINCR); + /* + * Preserve residual. + */ + resid = cto->ct_resid; + + /* + * Save actual SCSI status. + */ + scsi_status = cto->ct_scsi_status; + +#ifndef STATUS_WITH_DATA + sflags |= CT_NO_DATA; + /* + * We can't do a status at the same time as a data CTIO, so + * we need to synthesize an extra CTIO at this level. + */ + nctios++; +#endif + } else { + sflags = scsi_status = resid = 0; + } + + cto->ct_resid = 0; + cto->ct_scsi_status = 0; + + pcs = (struct isp_pcisoftc *)isp; + dp = &pcs->dmaps[isp_handle_index(handle)]; + if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { + bus_dmamap_sync(pcs->dmat, *dp, BUS_DMASYNC_PREREAD); + } else { + bus_dmamap_sync(pcs->dmat, *dp, BUS_DMASYNC_PREWRITE); + } + + nxti = *mp->nxtip; + + for (nth_ctio = 0; nth_ctio < nctios; nth_ctio++) { + int seglim; + + seglim = nseg; + if (seglim) { + int seg; + + if (seglim > ISP_RQDSEG) + seglim = ISP_RQDSEG; + + for (seg = 0; seg < seglim; seg++, nseg--) { + /* + * Unlike normal initiator commands, we don't + * do any swizzling here. + */ + cto->ct_dataseg[seg].ds_count = dm_segs->ds_len; + cto->ct_dataseg[seg].ds_base = dm_segs->ds_addr; + cto->ct_xfrlen += dm_segs->ds_len; + dm_segs++; + } + cto->ct_seg_count = seg; + } else { + /* + * This case should only happen when we're sending an + * extra CTIO with final status. + */ + if (send_status == 0) { + isp_prt(isp, ISP_LOGWARN, + "tdma_mk ran out of segments"); + mp->error = EINVAL; + return; + } + } + + /* + * At this point, the fields ct_lun, ct_iid, ct_tagval, + * ct_tagtype, and ct_timeout have been carried over + * unchanged from what our caller had set. + * + * The dataseg fields and the seg_count fields we just got + * through setting. The data direction we've preserved all + * along and only clear it if we're now sending status. + */ + + if (nth_ctio == nctios - 1) { + /* + * We're the last in a sequence of CTIOs, so mark + * this CTIO and save the handle to the CCB such that + * when this CTIO completes we can free dma resources + * and do whatever else we need to do to finish the + * rest of the command. We *don't* give this to the + * firmware to work on- the caller will do that. + */ + + cto->ct_syshandle = handle; + cto->ct_header.rqs_seqno = 1; + + if (send_status) { + cto->ct_scsi_status = scsi_status; + cto->ct_flags |= sflags; + cto->ct_resid = resid; + } + if (send_status) { + isp_prt(isp, ISP_LOGTDEBUG1, + "CTIO[%x] lun%d iid %d tag %x ct_flags %x " + "scsi status %x resid %d", + cto->ct_fwhandle, csio->ccb_h.target_lun, + cto->ct_iid, cto->ct_tag_val, cto->ct_flags, + cto->ct_scsi_status, cto->ct_resid); + } else { + isp_prt(isp, ISP_LOGTDEBUG1, + "CTIO[%x] lun%d iid%d tag %x ct_flags 0x%x", + cto->ct_fwhandle, csio->ccb_h.target_lun, + cto->ct_iid, cto->ct_tag_val, + cto->ct_flags); + } + isp_put_ctio(isp, cto, qe); + ISP_TDQE(isp, "last tdma_mk", curi, cto); + if (nctios > 1) { + MEMORYBARRIER(isp, SYNC_REQUEST, + curi, QENTRY_LEN); + } + } else { + ct_entry_t *oqe = qe; + + /* + * Make sure syshandle fields are clean + */ + cto->ct_syshandle = 0; + cto->ct_header.rqs_seqno = 0; + + isp_prt(isp, ISP_LOGTDEBUG1, + "CTIO[%x] lun%d for ID%d ct_flags 0x%x", + cto->ct_fwhandle, csio->ccb_h.target_lun, + cto->ct_iid, cto->ct_flags); + + /* + * Get a new CTIO + */ + qe = (ct_entry_t *) + ISP_QUEUE_ENTRY(isp->isp_rquest, nxti); + nxti = ISP_NXT_QENTRY(nxti, RQUEST_QUEUE_LEN(isp)); + if (nxti == mp->optr) { + isp_prt(isp, ISP_LOGTDEBUG0, + "Queue Overflow in tdma_mk"); + mp->error = MUSHERR_NOQENTRIES; + return; + } + + /* + * Now that we're done with the old CTIO, + * flush it out to the request queue. + */ + ISP_TDQE(isp, "dma_tgt_fc", curi, cto); + isp_put_ctio(isp, cto, oqe); + if (nth_ctio != 0) { + MEMORYBARRIER(isp, SYNC_REQUEST, curi, + QENTRY_LEN); + } + curi = ISP_NXT_QENTRY(curi, RQUEST_QUEUE_LEN(isp)); + + /* + * Reset some fields in the CTIO so we can reuse + * for the next one we'll flush to the request + * queue. + */ + cto->ct_header.rqs_entry_type = RQSTYPE_CTIO; + cto->ct_header.rqs_entry_count = 1; + cto->ct_header.rqs_flags = 0; + cto->ct_status = 0; + cto->ct_scsi_status = 0; + cto->ct_xfrlen = 0; + cto->ct_resid = 0; + cto->ct_seg_count = 0; + MEMZERO(cto->ct_dataseg, sizeof(cto->ct_dataseg)); + } + } + *mp->nxtip = nxti; +} + +/* + * We don't have to do multiple CTIOs here. Instead, we can just do + * continuation segments as needed. This greatly simplifies the code + * improves performance. + */ + +static void +tdma_mkfc(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error) +{ + mush_t *mp; + struct ccb_scsiio *csio; + ispsoftc_t *isp; + ct2_entry_t *cto, *qe; + uint16_t curi, nxti; + ispds_t *ds; + ispds64_t *ds64; + int segcnt, seglim; + + mp = (mush_t *) arg; + if (error) { + mp->error = error; + return; + } + + isp = mp->isp; + csio = mp->cmd_token; + cto = mp->rq; + + curi = isp->isp_reqidx; + qe = (ct2_entry_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, curi); + + if (nseg == 0) { + if ((cto->ct_flags & CT2_FLAG_MMASK) != CT2_FLAG_MODE1) { + isp_prt(isp, ISP_LOGWARN, + "dma2_tgt_fc, a status CTIO2 without MODE1 " + "set (0x%x)", cto->ct_flags); + mp->error = EINVAL; + return; + } + /* + * We preserve ct_lun, ct_iid, ct_rxid. We set the data + * flags to NO DATA and clear relative offset flags. + * We preserve the ct_resid and the response area. + */ + cto->ct_header.rqs_seqno = 1; + cto->ct_seg_count = 0; + cto->ct_reloff = 0; + isp_prt(isp, ISP_LOGTDEBUG1, + "CTIO2[%x] lun %d->iid%d flgs 0x%x sts 0x%x ssts " + "0x%x res %d", cto->ct_rxid, csio->ccb_h.target_lun, + cto->ct_iid, cto->ct_flags, cto->ct_status, + cto->rsp.m1.ct_scsi_status, cto->ct_resid); + if (IS_2KLOGIN(isp)) { + isp_put_ctio2e(isp, + (ct2e_entry_t *)cto, (ct2e_entry_t *)qe); + } else { + isp_put_ctio2(isp, cto, qe); + } + ISP_TDQE(isp, "dma2_tgt_fc[no data]", curi, qe); + return; + } + + if ((cto->ct_flags & CT2_FLAG_MMASK) != CT2_FLAG_MODE0) { + isp_prt(isp, ISP_LOGERR, + "dma2_tgt_fc, a data CTIO2 without MODE0 set " + "(0x%x)", cto->ct_flags); + mp->error = EINVAL; + return; + } + + + nxti = *mp->nxtip; + + /* + * Check to see if we need to DAC addressing or not. + * + * Any address that's over the 4GB boundary causes this + * to happen. + */ + segcnt = nseg; + if (sizeof (bus_addr_t) > 4) { + for (segcnt = 0; segcnt < nseg; segcnt++) { + uint64_t addr = dm_segs[segcnt].ds_addr; + if (addr >= 0x100000000LL) { + break; + } + } + } + if (segcnt != nseg) { + cto->ct_header.rqs_entry_type = RQSTYPE_CTIO3; + seglim = ISP_RQDSEG_T3; + ds64 = &cto->rsp.m0.ct_dataseg64[0]; + ds = NULL; + } else { + seglim = ISP_RQDSEG_T2; + ds64 = NULL; + ds = &cto->rsp.m0.ct_dataseg[0]; + } + cto->ct_seg_count = 0; + + /* + * Set up the CTIO2 data segments. + */ + for (segcnt = 0; cto->ct_seg_count < seglim && segcnt < nseg; + cto->ct_seg_count++, segcnt++) { + if (ds64) { + ds64->ds_basehi = + ((uint64_t) (dm_segs[segcnt].ds_addr) >> 32); + ds64->ds_base = dm_segs[segcnt].ds_addr; + ds64->ds_count = dm_segs[segcnt].ds_len; + ds64++; + } else { + ds->ds_base = dm_segs[segcnt].ds_addr; + ds->ds_count = dm_segs[segcnt].ds_len; + ds++; + } + cto->rsp.m0.ct_xfrlen += dm_segs[segcnt].ds_len; +#if __FreeBSD_version < 500000 + isp_prt(isp, ISP_LOGTDEBUG1, + "isp_send_ctio2: ent0[%d]0x%llx:%llu", + cto->ct_seg_count, (uint64_t)dm_segs[segcnt].ds_addr, + (uint64_t)dm_segs[segcnt].ds_len); +#else + isp_prt(isp, ISP_LOGTDEBUG1, + "isp_send_ctio2: ent0[%d]0x%jx:%ju", + cto->ct_seg_count, (uintmax_t)dm_segs[segcnt].ds_addr, + (uintmax_t)dm_segs[segcnt].ds_len); +#endif + } + + while (segcnt < nseg) { + uint16_t curip; + int seg; + ispcontreq_t local, *crq = &local, *qep; + + qep = (ispcontreq_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, nxti); + curip = nxti; + nxti = ISP_NXT_QENTRY(curip, RQUEST_QUEUE_LEN(isp)); + if (nxti == mp->optr) { + ISP_UNLOCK(isp); + isp_prt(isp, ISP_LOGTDEBUG0, + "tdma_mkfc: request queue overflow"); + mp->error = MUSHERR_NOQENTRIES; + return; + } + cto->ct_header.rqs_entry_count++; + MEMZERO((void *)crq, sizeof (*crq)); + crq->req_header.rqs_entry_count = 1; + if (cto->ct_header.rqs_entry_type == RQSTYPE_CTIO3) { + seglim = ISP_CDSEG64; + ds = NULL; + ds64 = &((ispcontreq64_t *)crq)->req_dataseg[0]; + crq->req_header.rqs_entry_type = RQSTYPE_A64_CONT; + } else { + seglim = ISP_CDSEG; + ds = &crq->req_dataseg[0]; + ds64 = NULL; + crq->req_header.rqs_entry_type = RQSTYPE_DATASEG; + } + for (seg = 0; segcnt < nseg && seg < seglim; + segcnt++, seg++) { + if (ds64) { + ds64->ds_basehi = + ((uint64_t) (dm_segs[segcnt].ds_addr) >> 32); + ds64->ds_base = dm_segs[segcnt].ds_addr; + ds64->ds_count = dm_segs[segcnt].ds_len; + ds64++; + } else { + ds->ds_base = dm_segs[segcnt].ds_addr; + ds->ds_count = dm_segs[segcnt].ds_len; + ds++; + } +#if __FreeBSD_version < 500000 + isp_prt(isp, ISP_LOGTDEBUG1, + "isp_send_ctio2: ent%d[%d]%llx:%llu", + cto->ct_header.rqs_entry_count-1, seg, + (uint64_t)dm_segs[segcnt].ds_addr, + (uint64_t)dm_segs[segcnt].ds_len); +#else + isp_prt(isp, ISP_LOGTDEBUG1, + "isp_send_ctio2: ent%d[%d]%jx:%ju", + cto->ct_header.rqs_entry_count-1, seg, + (uintmax_t)dm_segs[segcnt].ds_addr, + (uintmax_t)dm_segs[segcnt].ds_len); +#endif + cto->rsp.m0.ct_xfrlen += dm_segs[segcnt].ds_len; + cto->ct_seg_count++; + } + MEMORYBARRIER(isp, SYNC_REQUEST, curip, QENTRY_LEN); + isp_put_cont_req(isp, crq, qep); + ISP_TDQE(isp, "cont entry", curi, qep); + } + + /* + * No do final twiddling for the CTIO itself. + */ + cto->ct_header.rqs_seqno = 1; + isp_prt(isp, ISP_LOGTDEBUG1, + "CTIO2[%x] lun %d->iid%d flgs 0x%x sts 0x%x ssts 0x%x resid %d", + cto->ct_rxid, csio->ccb_h.target_lun, (int) cto->ct_iid, + cto->ct_flags, cto->ct_status, cto->rsp.m1.ct_scsi_status, + cto->ct_resid); + if (IS_2KLOGIN(isp)) + isp_put_ctio2e(isp, (ct2e_entry_t *)cto, (ct2e_entry_t *)qe); + else + isp_put_ctio2(isp, cto, qe); + ISP_TDQE(isp, "last dma2_tgt_fc", curi, qe); + *mp->nxtip = nxti; +} +#endif + +static void dma2_a64(void *, bus_dma_segment_t *, int, int); +static void dma2(void *, bus_dma_segment_t *, int, int); + +static void +dma2_a64(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error) +{ + mush_t *mp; + ispsoftc_t *isp; + struct ccb_scsiio *csio; + struct isp_pcisoftc *pcs; + bus_dmamap_t *dp; + bus_dma_segment_t *eseg; + ispreq64_t *rq; + int seglim, datalen; + uint16_t nxti; + + mp = (mush_t *) arg; + if (error) { + mp->error = error; + return; + } + + if (nseg < 1) { + isp_prt(mp->isp, ISP_LOGERR, "bad segment count (%d)", nseg); + mp->error = EFAULT; + return; + } + csio = mp->cmd_token; + isp = mp->isp; + rq = mp->rq; + pcs = (struct isp_pcisoftc *)mp->isp; + dp = &pcs->dmaps[isp_handle_index(rq->req_handle)]; + nxti = *mp->nxtip; + + if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { + bus_dmamap_sync(pcs->dmat, *dp, BUS_DMASYNC_PREREAD); + } else { + bus_dmamap_sync(pcs->dmat, *dp, BUS_DMASYNC_PREWRITE); + } + datalen = XS_XFRLEN(csio); + + /* + * We're passed an initial partially filled in entry that + * has most fields filled in except for data transfer + * related values. + * + * Our job is to fill in the initial request queue entry and + * then to start allocating and filling in continuation entries + * until we've covered the entire transfer. + */ + + if (IS_FC(isp)) { + rq->req_header.rqs_entry_type = RQSTYPE_T3RQS; + seglim = ISP_RQDSEG_T3; + ((ispreqt3_t *)rq)->req_totalcnt = datalen; + if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { + ((ispreqt3_t *)rq)->req_flags |= REQFLAG_DATA_IN; + } else { + ((ispreqt3_t *)rq)->req_flags |= REQFLAG_DATA_OUT; + } + } else { + rq->req_header.rqs_entry_type = RQSTYPE_A64; + if (csio->cdb_len > 12) { + seglim = 0; + } else { + seglim = ISP_RQDSEG_A64; + } + if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { + rq->req_flags |= REQFLAG_DATA_IN; + } else { + rq->req_flags |= REQFLAG_DATA_OUT; + } + } + + eseg = dm_segs + nseg; + + while (datalen != 0 && rq->req_seg_count < seglim && dm_segs != eseg) { + if (IS_FC(isp)) { + ispreqt3_t *rq3 = (ispreqt3_t *)rq; + rq3->req_dataseg[rq3->req_seg_count].ds_base = + DMA_LO32(dm_segs->ds_addr); + rq3->req_dataseg[rq3->req_seg_count].ds_basehi = + DMA_HI32(dm_segs->ds_addr); + rq3->req_dataseg[rq3->req_seg_count].ds_count = + dm_segs->ds_len; + } else { + rq->req_dataseg[rq->req_seg_count].ds_base = + DMA_LO32(dm_segs->ds_addr); + rq->req_dataseg[rq->req_seg_count].ds_basehi = + DMA_HI32(dm_segs->ds_addr); + rq->req_dataseg[rq->req_seg_count].ds_count = + dm_segs->ds_len; + } + datalen -= dm_segs->ds_len; + rq->req_seg_count++; + dm_segs++; + } + + while (datalen > 0 && dm_segs != eseg) { + uint16_t onxti; + ispcontreq64_t local, *crq = &local, *cqe; + + cqe = (ispcontreq64_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, nxti); + onxti = nxti; + nxti = ISP_NXT_QENTRY(onxti, RQUEST_QUEUE_LEN(isp)); + if (nxti == mp->optr) { + isp_prt(isp, ISP_LOGDEBUG0, "Request Queue Overflow++"); + mp->error = MUSHERR_NOQENTRIES; + return; + } + rq->req_header.rqs_entry_count++; + MEMZERO((void *)crq, sizeof (*crq)); + crq->req_header.rqs_entry_count = 1; + crq->req_header.rqs_entry_type = RQSTYPE_A64_CONT; + + seglim = 0; + while (datalen > 0 && seglim < ISP_CDSEG64 && dm_segs != eseg) { + crq->req_dataseg[seglim].ds_base = + DMA_LO32(dm_segs->ds_addr); + crq->req_dataseg[seglim].ds_basehi = + DMA_HI32(dm_segs->ds_addr); + crq->req_dataseg[seglim].ds_count = + dm_segs->ds_len; + rq->req_seg_count++; + dm_segs++; + seglim++; + datalen -= dm_segs->ds_len; + } + isp_put_cont64_req(isp, crq, cqe); + MEMORYBARRIER(isp, SYNC_REQUEST, onxti, QENTRY_LEN); + } + *mp->nxtip = nxti; +} + +static void +dma2(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error) +{ + mush_t *mp; + ispsoftc_t *isp; + struct ccb_scsiio *csio; + struct isp_pcisoftc *pcs; + bus_dmamap_t *dp; + bus_dma_segment_t *eseg; + ispreq_t *rq; + int seglim, datalen; + uint16_t nxti; + + mp = (mush_t *) arg; + if (error) { + mp->error = error; + return; + } + + if (nseg < 1) { + isp_prt(mp->isp, ISP_LOGERR, "bad segment count (%d)", nseg); + mp->error = EFAULT; + return; + } + csio = mp->cmd_token; + isp = mp->isp; + rq = mp->rq; + pcs = (struct isp_pcisoftc *)mp->isp; + dp = &pcs->dmaps[isp_handle_index(rq->req_handle)]; + nxti = *mp->nxtip; + + if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { + bus_dmamap_sync(pcs->dmat, *dp, BUS_DMASYNC_PREREAD); + } else { + bus_dmamap_sync(pcs->dmat, *dp, BUS_DMASYNC_PREWRITE); + } + + datalen = XS_XFRLEN(csio); + + /* + * We're passed an initial partially filled in entry that + * has most fields filled in except for data transfer + * related values. + * + * Our job is to fill in the initial request queue entry and + * then to start allocating and filling in continuation entries + * until we've covered the entire transfer. + */ + + if (IS_FC(isp)) { + seglim = ISP_RQDSEG_T2; + ((ispreqt2_t *)rq)->req_totalcnt = datalen; + if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { + ((ispreqt2_t *)rq)->req_flags |= REQFLAG_DATA_IN; + } else { + ((ispreqt2_t *)rq)->req_flags |= REQFLAG_DATA_OUT; + } + } else { + if (csio->cdb_len > 12) { + seglim = 0; + } else { + seglim = ISP_RQDSEG; + } + if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { + rq->req_flags |= REQFLAG_DATA_IN; + } else { + rq->req_flags |= REQFLAG_DATA_OUT; + } + } + + eseg = dm_segs + nseg; + + while (datalen != 0 && rq->req_seg_count < seglim && dm_segs != eseg) { + if (IS_FC(isp)) { + ispreqt2_t *rq2 = (ispreqt2_t *)rq; + rq2->req_dataseg[rq2->req_seg_count].ds_base = + DMA_LO32(dm_segs->ds_addr); + rq2->req_dataseg[rq2->req_seg_count].ds_count = + dm_segs->ds_len; + } else { + rq->req_dataseg[rq->req_seg_count].ds_base = + DMA_LO32(dm_segs->ds_addr); + rq->req_dataseg[rq->req_seg_count].ds_count = + dm_segs->ds_len; + } + datalen -= dm_segs->ds_len; + rq->req_seg_count++; + dm_segs++; + } + + while (datalen > 0 && dm_segs != eseg) { + uint16_t onxti; + ispcontreq_t local, *crq = &local, *cqe; + + cqe = (ispcontreq_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, nxti); + onxti = nxti; + nxti = ISP_NXT_QENTRY(onxti, RQUEST_QUEUE_LEN(isp)); + if (nxti == mp->optr) { + isp_prt(isp, ISP_LOGDEBUG0, "Request Queue Overflow++"); + mp->error = MUSHERR_NOQENTRIES; + return; + } + rq->req_header.rqs_entry_count++; + MEMZERO((void *)crq, sizeof (*crq)); + crq->req_header.rqs_entry_count = 1; + crq->req_header.rqs_entry_type = RQSTYPE_DATASEG; + + seglim = 0; + while (datalen > 0 && seglim < ISP_CDSEG && dm_segs != eseg) { + crq->req_dataseg[seglim].ds_base = + DMA_LO32(dm_segs->ds_addr); + crq->req_dataseg[seglim].ds_count = + dm_segs->ds_len; + rq->req_seg_count++; + dm_segs++; + seglim++; + datalen -= dm_segs->ds_len; + } + isp_put_cont_req(isp, crq, cqe); + MEMORYBARRIER(isp, SYNC_REQUEST, onxti, QENTRY_LEN); + } + *mp->nxtip = nxti; +} + +/* + * We enter with ISP_LOCK held + */ +static int +isp_pci_dmasetup(ispsoftc_t *isp, struct ccb_scsiio *csio, ispreq_t *rq, + uint16_t *nxtip, uint16_t optr) +{ + struct isp_pcisoftc *pcs = (struct isp_pcisoftc *)isp; + ispreq_t *qep; + bus_dmamap_t *dp = NULL; + mush_t mush, *mp; + void (*eptr)(void *, bus_dma_segment_t *, int, int); + + qep = (ispreq_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, isp->isp_reqidx); +#ifdef ISP_TARGET_MODE + if (csio->ccb_h.func_code == XPT_CONT_TARGET_IO) { + if (IS_FC(isp)) { + eptr = tdma_mkfc; + } else { + eptr = tdma_mk; + } + if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE || + (csio->dxfer_len == 0)) { + mp = &mush; + mp->isp = isp; + mp->cmd_token = csio; + mp->rq = rq; /* really a ct_entry_t or ct2_entry_t */ + mp->nxtip = nxtip; + mp->optr = optr; + mp->error = 0; + ISPLOCK_2_CAMLOCK(isp); + (*eptr)(mp, NULL, 0, 0); + CAMLOCK_2_ISPLOCK(isp); + goto mbxsync; + } + } else +#endif + if (sizeof (bus_addr_t) > 4) { + eptr = dma2_a64; + } else { + eptr = dma2; + } + + + if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE || + (csio->dxfer_len == 0)) { + rq->req_seg_count = 1; + goto mbxsync; + } + + /* + * Do a virtual grapevine step to collect info for + * the callback dma allocation that we have to use... + */ + mp = &mush; + mp->isp = isp; + mp->cmd_token = csio; + mp->rq = rq; + mp->nxtip = nxtip; + mp->optr = optr; + mp->error = 0; + + ISPLOCK_2_CAMLOCK(isp); + if ((csio->ccb_h.flags & CAM_SCATTER_VALID) == 0) { + if ((csio->ccb_h.flags & CAM_DATA_PHYS) == 0) { + int error, s; + dp = &pcs->dmaps[isp_handle_index(rq->req_handle)]; + s = splsoftvm(); + error = bus_dmamap_load(pcs->dmat, *dp, + csio->data_ptr, csio->dxfer_len, eptr, mp, 0); + if (error == EINPROGRESS) { + bus_dmamap_unload(pcs->dmat, *dp); + mp->error = EINVAL; + isp_prt(isp, ISP_LOGERR, + "deferred dma allocation not supported"); + } else if (error && mp->error == 0) { +#ifdef DIAGNOSTIC + isp_prt(isp, ISP_LOGERR, + "error %d in dma mapping code", error); +#endif + mp->error = error; + } + splx(s); + } else { + /* Pointer to physical buffer */ + struct bus_dma_segment seg; + seg.ds_addr = (bus_addr_t)(vm_offset_t)csio->data_ptr; + seg.ds_len = csio->dxfer_len; + (*eptr)(mp, &seg, 1, 0); + } + } else { + struct bus_dma_segment *segs; + + if ((csio->ccb_h.flags & CAM_DATA_PHYS) != 0) { + isp_prt(isp, ISP_LOGERR, + "Physical segment pointers unsupported"); + mp->error = EINVAL; + } else if ((csio->ccb_h.flags & CAM_SG_LIST_PHYS) == 0) { + isp_prt(isp, ISP_LOGERR, + "Virtual segment addresses unsupported"); + mp->error = EINVAL; + } else { + /* Just use the segments provided */ + segs = (struct bus_dma_segment *) csio->data_ptr; + (*eptr)(mp, segs, csio->sglist_cnt, 0); + } + } + CAMLOCK_2_ISPLOCK(isp); + if (mp->error) { + int retval = CMD_COMPLETE; + if (mp->error == MUSHERR_NOQENTRIES) { + retval = CMD_EAGAIN; + } else if (mp->error == EFBIG) { + XS_SETERR(csio, CAM_REQ_TOO_BIG); + } else if (mp->error == EINVAL) { + XS_SETERR(csio, CAM_REQ_INVALID); + } else { + XS_SETERR(csio, CAM_UNREC_HBA_ERROR); + } + return (retval); + } +mbxsync: + switch (rq->req_header.rqs_entry_type) { + case RQSTYPE_REQUEST: + isp_put_request(isp, rq, qep); + break; + case RQSTYPE_CMDONLY: + isp_put_extended_request(isp, (ispextreq_t *)rq, + (ispextreq_t *)qep); + break; + case RQSTYPE_T2RQS: + isp_put_request_t2(isp, (ispreqt2_t *) rq, (ispreqt2_t *) qep); + break; + case RQSTYPE_A64: + case RQSTYPE_T3RQS: + isp_put_request_t3(isp, (ispreqt3_t *) rq, (ispreqt3_t *) qep); + break; + } + return (CMD_QUEUED); +} + +static void +isp_pci_dmateardown(ispsoftc_t *isp, XS_T *xs, uint16_t handle) +{ + struct isp_pcisoftc *pcs = (struct isp_pcisoftc *)isp; + bus_dmamap_t *dp = &pcs->dmaps[isp_handle_index(handle)]; + if ((xs->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { + bus_dmamap_sync(pcs->dmat, *dp, BUS_DMASYNC_POSTREAD); + } else { + bus_dmamap_sync(pcs->dmat, *dp, BUS_DMASYNC_POSTWRITE); + } + bus_dmamap_unload(pcs->dmat, *dp); +} + + +static void +isp_pci_reset1(ispsoftc_t *isp) +{ + /* Make sure the BIOS is disabled */ + isp_pci_wr_reg(isp, HCCR, PCI_HCCR_CMD_BIOS); + /* and enable interrupts */ + ENABLE_INTS(isp); +} + +static void +isp_pci_dumpregs(ispsoftc_t *isp, const char *msg) +{ + struct isp_pcisoftc *pcs = (struct isp_pcisoftc *)isp; + if (msg) + printf("%s: %s\n", device_get_nameunit(isp->isp_dev), msg); + else + printf("%s:\n", device_get_nameunit(isp->isp_dev)); + if (IS_SCSI(isp)) + printf(" biu_conf1=%x", ISP_READ(isp, BIU_CONF1)); + else + printf(" biu_csr=%x", ISP_READ(isp, BIU2100_CSR)); + printf(" biu_icr=%x biu_isr=%x biu_sema=%x ", ISP_READ(isp, BIU_ICR), + ISP_READ(isp, BIU_ISR), ISP_READ(isp, BIU_SEMA)); + printf("risc_hccr=%x\n", ISP_READ(isp, HCCR)); + + + if (IS_SCSI(isp)) { + ISP_WRITE(isp, HCCR, HCCR_CMD_PAUSE); + printf(" cdma_conf=%x cdma_sts=%x cdma_fifostat=%x\n", + ISP_READ(isp, CDMA_CONF), ISP_READ(isp, CDMA_STATUS), + ISP_READ(isp, CDMA_FIFO_STS)); + printf(" ddma_conf=%x ddma_sts=%x ddma_fifostat=%x\n", + ISP_READ(isp, DDMA_CONF), ISP_READ(isp, DDMA_STATUS), + ISP_READ(isp, DDMA_FIFO_STS)); + printf(" sxp_int=%x sxp_gross=%x sxp(scsi_ctrl)=%x\n", + ISP_READ(isp, SXP_INTERRUPT), + ISP_READ(isp, SXP_GROSS_ERR), + ISP_READ(isp, SXP_PINS_CTRL)); + ISP_WRITE(isp, HCCR, HCCR_CMD_RELEASE); + } + printf(" mbox regs: %x %x %x %x %x\n", + ISP_READ(isp, OUTMAILBOX0), ISP_READ(isp, OUTMAILBOX1), + ISP_READ(isp, OUTMAILBOX2), ISP_READ(isp, OUTMAILBOX3), + ISP_READ(isp, OUTMAILBOX4)); + printf(" PCI Status Command/Status=%x\n", + pci_read_config(pcs->pci_dev, PCIR_COMMAND, 1)); +} diff --git a/sys/dev/isp/isp_sbus.c b/sys/dev/isp/isp_sbus.c new file mode 100644 index 000000000000..c8b88fc61b0a --- /dev/null +++ b/sys/dev/isp/isp_sbus.c @@ -0,0 +1,874 @@ +/*- + * Copyright (c) 1997-2006 by Matthew Jacob + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * SBus specific probe and attach routines for Qlogic ISP SCSI adapters. + * FreeBSD Version. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#if __FreeBSD_version >= 700000 +#include <sys/linker.h> +#include <sys/firmware.h> +#endif +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/resource.h> + +#include <dev/ofw/ofw_bus.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> +#include <sparc64/sbus/sbusvar.h> + +#include <dev/isp/isp_freebsd.h> + +static uint16_t isp_sbus_rd_reg(ispsoftc_t *, int); +static void isp_sbus_wr_reg(ispsoftc_t *, int, uint16_t); +static int +isp_sbus_rd_isr(ispsoftc_t *, uint16_t *, uint16_t *, uint16_t *); +static int isp_sbus_mbxdma(ispsoftc_t *); +static int +isp_sbus_dmasetup(ispsoftc_t *, XS_T *, ispreq_t *, uint16_t *, uint16_t); +static void +isp_sbus_dmateardown(ispsoftc_t *, XS_T *, uint16_t); + +static void isp_sbus_reset1(ispsoftc_t *); +static void isp_sbus_dumpregs(ispsoftc_t *, const char *); + +static struct ispmdvec mdvec = { + isp_sbus_rd_isr, + isp_sbus_rd_reg, + isp_sbus_wr_reg, + isp_sbus_mbxdma, + isp_sbus_dmasetup, + isp_sbus_dmateardown, + NULL, + isp_sbus_reset1, + isp_sbus_dumpregs, + NULL, + BIU_BURST_ENABLE|BIU_PCI_CONF1_FIFO_64 +}; + +static int isp_sbus_probe (device_t); +static int isp_sbus_attach (device_t); + + +struct isp_sbussoftc { + ispsoftc_t sbus_isp; + device_t sbus_dev; + struct resource * sbus_reg; + bus_space_tag_t sbus_st; + bus_space_handle_t sbus_sh; + void * ih; + int16_t sbus_poff[_NREG_BLKS]; + bus_dma_tag_t dmat; + bus_dmamap_t *dmaps; + sdparam sbus_param; + struct ispmdvec sbus_mdvec; + struct resource * sbus_ires; +}; + + +static device_method_t isp_sbus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, isp_sbus_probe), + DEVMETHOD(device_attach, isp_sbus_attach), + { 0, 0 } +}; +static void isp_sbus_intr(void *); + +static driver_t isp_sbus_driver = { + "isp", isp_sbus_methods, sizeof (struct isp_sbussoftc) +}; +static devclass_t isp_devclass; +DRIVER_MODULE(isp, sbus, isp_sbus_driver, isp_devclass, 0, 0); +#if __FreeBSD_version >= 700000 +MODULE_DEPEND(isp, firmware, 1, 1, 1); +#else +extern ispfwfunc *isp_get_firmware_p; +#endif + +static int +isp_sbus_probe(device_t dev) +{ + int found = 0; + const char *name = ofw_bus_get_name(dev); + if (strcmp(name, "SUNW,isp") == 0 || + strcmp(name, "QLGC,isp") == 0 || + strcmp(name, "ptisp") == 0 || + strcmp(name, "PTI,ptisp") == 0) { + found++; + } + if (!found) + return (ENXIO); + + if (isp_announced == 0 && bootverbose) { + printf("Qlogic ISP Driver, FreeBSD Version %d.%d, " + "Core Version %d.%d\n", + ISP_PLATFORM_VERSION_MAJOR, ISP_PLATFORM_VERSION_MINOR, + ISP_CORE_VERSION_MAJOR, ISP_CORE_VERSION_MINOR); + isp_announced++; + } + return (0); +} + +static int +isp_sbus_attach(device_t dev) +{ + struct resource *regs; + int tval, iqd, isp_debug, role, rid, ispburst; + struct isp_sbussoftc *sbs; + ispsoftc_t *isp = NULL; + int locksetup = 0; + + /* + * Figure out if we're supposed to skip this one. + * If we are, we actually go to ISP_ROLE_NONE. + */ + + tval = 0; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "disable", &tval) == 0 && tval) { + device_printf(dev, "device is disabled\n"); + /* but return 0 so the !$)$)*!$*) unit isn't reused */ + return (0); + } + + role = 0; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "role", &role) == 0 && + ((role & ~(ISP_ROLE_INITIATOR|ISP_ROLE_TARGET)) == 0)) { + device_printf(dev, "setting role to 0x%x\n", role); + } else { +#ifdef ISP_TARGET_MODE + role = ISP_ROLE_INITIATOR|ISP_ROLE_TARGET; +#else + role = ISP_DEFAULT_ROLES; +#endif + } + + sbs = malloc(sizeof (*sbs), M_DEVBUF, M_NOWAIT | M_ZERO); + if (sbs == NULL) { + device_printf(dev, "cannot allocate softc\n"); + return (ENOMEM); + } + + regs = NULL; + iqd = 0; + + rid = 0; + regs = + bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); + if (regs == 0) { + device_printf(dev, "unable to map registers\n"); + goto bad; + } + sbs->sbus_dev = dev; + sbs->sbus_reg = regs; + sbs->sbus_st = rman_get_bustag(regs); + sbs->sbus_sh = rman_get_bushandle(regs); + sbs->sbus_mdvec = mdvec; + + sbs->sbus_poff[BIU_BLOCK >> _BLK_REG_SHFT] = BIU_REGS_OFF; + sbs->sbus_poff[MBOX_BLOCK >> _BLK_REG_SHFT] = SBUS_MBOX_REGS_OFF; + sbs->sbus_poff[SXP_BLOCK >> _BLK_REG_SHFT] = SBUS_SXP_REGS_OFF; + sbs->sbus_poff[RISC_BLOCK >> _BLK_REG_SHFT] = SBUS_RISC_REGS_OFF; + sbs->sbus_poff[DMA_BLOCK >> _BLK_REG_SHFT] = DMA_REGS_OFF; + isp = &sbs->sbus_isp; + isp->isp_mdvec = &sbs->sbus_mdvec; + isp->isp_bustype = ISP_BT_SBUS; + isp->isp_type = ISP_HA_SCSI_UNKNOWN; + isp->isp_param = &sbs->sbus_param; + isp->isp_revision = 0; /* XXX */ + isp->isp_role = role; + isp->isp_dev = dev; + + /* + * Get the clock frequency and convert it from HZ to MHz, + * rounding up. This defaults to 25MHz if there isn't a + * device specific one in the OFW device tree. + */ + sbs->sbus_mdvec.dv_clock = (sbus_get_clockfreq(dev) + 500000)/1000000; + + /* + * Now figure out what the proper burst sizes, etc., to use. + * Unfortunately, there is no ddi_dma_burstsizes here which + * walks up the tree finding the limiting burst size node (if + * any). We just use what's here for isp. + */ + ispburst = sbus_get_burstsz(dev); + if (ispburst == 0) { + ispburst = SBUS_BURST_32 - 1; + } + sbs->sbus_mdvec.dv_conf1 = 0; + if (ispburst & (1 << 5)) { + sbs->sbus_mdvec.dv_conf1 = BIU_SBUS_CONF1_FIFO_32; + } else if (ispburst & (1 << 4)) { + sbs->sbus_mdvec.dv_conf1 = BIU_SBUS_CONF1_FIFO_16; + } else if (ispburst & (1 << 3)) { + sbs->sbus_mdvec.dv_conf1 = + BIU_SBUS_CONF1_BURST8 | BIU_SBUS_CONF1_FIFO_8; + } + if (sbs->sbus_mdvec.dv_conf1) { + sbs->sbus_mdvec.dv_conf1 |= BIU_BURST_ENABLE; + } + + /* + * Some early versions of the PTI SBus adapter + * would fail in trying to download (via poking) + * FW. We give up on them. + */ + if (strcmp("PTI,ptisp", ofw_bus_get_name(dev)) == 0 || + strcmp("ptisp", ofw_bus_get_name(dev)) == 0) { + isp->isp_confopts |= ISP_CFG_NORELOAD; + } + + /* + * We don't trust NVRAM on SBus cards + */ + isp->isp_confopts |= ISP_CFG_NONVRAM; + + +#if __FreeBSD_version >= 700000 + isp->isp_osinfo.fw = firmware_get("isp_1000"); + if (isp->isp_osinfo.fw) { + union { + const void *cp; + uint16_t *sp; + } stupid; + stupid.cp = isp->isp_osinfo.fw->data; + isp->isp_mdvec->dv_ispfw = stupid.sp; + } +#else + /* + * Try and find firmware for this device. + */ + if (isp_get_firmware_p) { + (*isp_get_firmware_p)(0, 0, 0x1000, &sbs->sbus_mdvec.dv_ispfw); + } +#endif + + iqd = 0; + sbs->sbus_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, &iqd, + RF_ACTIVE | RF_SHAREABLE); + if (sbs->sbus_ires == NULL) { + device_printf(dev, "could not allocate interrupt\n"); + goto bad; + } + + tval = 0; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "fwload_disable", &tval) == 0 && tval != 0) { + isp->isp_confopts |= ISP_CFG_NORELOAD; + } + + isp->isp_osinfo.default_id = -1; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "iid", &tval) == 0) { + isp->isp_osinfo.default_id = tval; + isp->isp_confopts |= ISP_CFG_OWNLOOPID; + } + if (isp->isp_osinfo.default_id == -1) { + /* + * XXX: should be a way to get properties w/o having + * XXX: to call OF_xxx functions + */ + isp->isp_osinfo.default_id = 7; + } + + isp_debug = 0; + (void) resource_int_value(device_get_name(dev), device_get_unit(dev), + "debug", &isp_debug); + + /* Make sure the lock is set up. */ + mtx_init(&isp->isp_osinfo.lock, "isp", NULL, MTX_DEF); + locksetup++; + + if (bus_setup_intr(dev, sbs->sbus_ires, ISP_IFLAGS, + isp_sbus_intr, isp, &sbs->ih)) { + device_printf(dev, "could not setup interrupt\n"); + goto bad; + } + + /* + * Set up logging levels. + */ + if (isp_debug) { + isp->isp_dblev = isp_debug; + } else { + isp->isp_dblev = ISP_LOGWARN|ISP_LOGERR; + } + if (bootverbose) + isp->isp_dblev |= ISP_LOGCONFIG|ISP_LOGINFO; + + /* + * Make sure we're in reset state. + */ + ISP_LOCK(isp); + isp_reset(isp); + if (isp->isp_state != ISP_RESETSTATE) { + ISP_UNLOCK(isp); + goto bad; + } + isp_init(isp); + if (isp->isp_role != ISP_ROLE_NONE && isp->isp_state != ISP_INITSTATE) { + isp_uninit(isp); + ISP_UNLOCK(isp); + goto bad; + } + isp_attach(isp); + if (isp->isp_role != ISP_ROLE_NONE && isp->isp_state != ISP_RUNSTATE) { + isp_uninit(isp); + ISP_UNLOCK(isp); + goto bad; + } + /* + * XXXX: Here is where we might unload the f/w module + * XXXX: (or decrease the reference count to it). + */ + ISP_UNLOCK(isp); + return (0); + +bad: + + if (sbs && sbs->ih) { + (void) bus_teardown_intr(dev, sbs->sbus_ires, sbs->ih); + } + + if (locksetup && isp) { + mtx_destroy(&isp->isp_osinfo.lock); + } + + if (sbs && sbs->sbus_ires) { + bus_release_resource(dev, SYS_RES_IRQ, iqd, sbs->sbus_ires); + } + + + if (regs) { + (void) bus_release_resource(dev, 0, 0, regs); + } + + if (sbs) { + if (sbs->sbus_isp.isp_param) + free(sbs->sbus_isp.isp_param, M_DEVBUF); + free(sbs, M_DEVBUF); + } + + /* + * XXXX: Here is where we might unload the f/w module + * XXXX: (or decrease the reference count to it). + */ + return (ENXIO); +} + +static void +isp_sbus_intr(void *arg) +{ + ispsoftc_t *isp = arg; + uint16_t isr, sema, mbox; + + ISP_LOCK(isp); + isp->isp_intcnt++; + if (ISP_READ_ISR(isp, &isr, &sema, &mbox) == 0) { + isp->isp_intbogus++; + } else { + int iok = isp->isp_osinfo.intsok; + isp->isp_osinfo.intsok = 0; + isp_intr(isp, isr, sema, mbox); + isp->isp_osinfo.intsok = iok; + } + ISP_UNLOCK(isp); +} + +#define IspVirt2Off(a, x) \ + (((struct isp_sbussoftc *)a)->sbus_poff[((x) & _BLK_REG_MASK) >> \ + _BLK_REG_SHFT] + ((x) & 0xff)) + +#define BXR2(sbc, off) \ + bus_space_read_2(sbc->sbus_st, sbc->sbus_sh, off) + +static int +isp_sbus_rd_isr(ispsoftc_t *isp, uint16_t *isrp, + uint16_t *semap, uint16_t *mbp) +{ + struct isp_sbussoftc *sbc = (struct isp_sbussoftc *) isp; + uint16_t isr, sema; + + isr = BXR2(sbc, IspVirt2Off(isp, BIU_ISR)); + sema = BXR2(sbc, IspVirt2Off(isp, BIU_SEMA)); + isp_prt(isp, ISP_LOGDEBUG3, "ISR 0x%x SEMA 0x%x", isr, sema); + isr &= INT_PENDING_MASK(isp); + sema &= BIU_SEMA_LOCK; + if (isr == 0 && sema == 0) { + return (0); + } + *isrp = isr; + if ((*semap = sema) != 0) { + *mbp = BXR2(sbc, IspVirt2Off(isp, OUTMAILBOX0)); + } + return (1); +} + +static uint16_t +isp_sbus_rd_reg(ispsoftc_t *isp, int regoff) +{ + uint16_t rval; + struct isp_sbussoftc *sbs = (struct isp_sbussoftc *) isp; + int offset = sbs->sbus_poff[(regoff & _BLK_REG_MASK) >> _BLK_REG_SHFT]; + offset += (regoff & 0xff); + rval = bus_space_read_2(sbs->sbus_st, sbs->sbus_sh, offset); + isp_prt(isp, ISP_LOGDEBUG3, + "isp_sbus_rd_reg(off %x) = %x", regoff, rval); + return (rval); +} + +static void +isp_sbus_wr_reg(ispsoftc_t *isp, int regoff, uint16_t val) +{ + struct isp_sbussoftc *sbs = (struct isp_sbussoftc *) isp; + int offset = sbs->sbus_poff[(regoff & _BLK_REG_MASK) >> _BLK_REG_SHFT]; + offset += (regoff & 0xff); + isp_prt(isp, ISP_LOGDEBUG3, + "isp_sbus_wr_reg(off %x) = %x", regoff, val); + bus_space_write_2(sbs->sbus_st, sbs->sbus_sh, offset, val); +} + +struct imush { + ispsoftc_t *isp; + int error; +}; + +static void imc(void *, bus_dma_segment_t *, int, int); + +static void +imc(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + struct imush *imushp = (struct imush *) arg; + if (error) { + imushp->error = error; + } else { + ispsoftc_t *isp =imushp->isp; + bus_addr_t addr = segs->ds_addr; + + isp->isp_rquest_dma = addr; + addr += ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp)); + isp->isp_result_dma = addr; + } +} + +/* + * Should be BUS_SPACE_MAXSIZE, but MAXPHYS is larger than BUS_SPACE_MAXSIZE + */ +#define ISP_NSEGS ((MAXPHYS / PAGE_SIZE) + 1) + +static int +isp_sbus_mbxdma(ispsoftc_t *isp) +{ + struct isp_sbussoftc *sbs = (struct isp_sbussoftc *)isp; + caddr_t base; + uint32_t len; + int i, error, ns; + struct imush im; + + /* + * Already been here? If so, leave... + */ + if (isp->isp_rquest) { + return (0); + } + + ISP_UNLOCK(isp); + + if (bus_dma_tag_create(NULL, 1, BUS_SPACE_MAXADDR_24BIT+1, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR_32BIT, + NULL, NULL, BUS_SPACE_MAXSIZE_32BIT, ISP_NSEGS, + BUS_SPACE_MAXADDR_24BIT, 0, busdma_lock_mutex, &Giant, + &sbs->dmat)) { + isp_prt(isp, ISP_LOGERR, "could not create master dma tag"); + ISP_LOCK(isp); + return(1); + } + + len = sizeof (XS_T **) * isp->isp_maxcmds; + isp->isp_xflist = (XS_T **) malloc(len, M_DEVBUF, M_WAITOK | M_ZERO); + if (isp->isp_xflist == NULL) { + isp_prt(isp, ISP_LOGERR, "cannot alloc xflist array"); + ISP_LOCK(isp); + return (1); + } + len = sizeof (bus_dmamap_t) * isp->isp_maxcmds; + sbs->dmaps = (bus_dmamap_t *) malloc(len, M_DEVBUF, M_WAITOK); + if (sbs->dmaps == NULL) { + isp_prt(isp, ISP_LOGERR, "can't alloc dma map storage"); + free(isp->isp_xflist, M_DEVBUF); + ISP_LOCK(isp); + return (1); + } + + /* + * Allocate and map the request, result queues, plus FC scratch area. + */ + len = ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp)); + len += ISP_QUEUE_SIZE(RESULT_QUEUE_LEN(isp)); + + ns = (len / PAGE_SIZE) + 1; + if (bus_dma_tag_create(sbs->dmat, QENTRY_LEN, BUS_SPACE_MAXADDR_24BIT+1, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR_32BIT, NULL, NULL, + len, ns, BUS_SPACE_MAXADDR_24BIT, 0, busdma_lock_mutex, &Giant, + &isp->isp_cdmat)) { + isp_prt(isp, ISP_LOGERR, + "cannot create a dma tag for control spaces"); + free(sbs->dmaps, M_DEVBUF); + free(isp->isp_xflist, M_DEVBUF); + ISP_LOCK(isp); + return (1); + } + + if (bus_dmamem_alloc(isp->isp_cdmat, (void **)&base, BUS_DMA_NOWAIT, + &isp->isp_cdmap) != 0) { + isp_prt(isp, ISP_LOGERR, + "cannot allocate %d bytes of CCB memory", len); + bus_dma_tag_destroy(isp->isp_cdmat); + free(isp->isp_xflist, M_DEVBUF); + free(sbs->dmaps, M_DEVBUF); + ISP_LOCK(isp); + return (1); + } + + for (i = 0; i < isp->isp_maxcmds; i++) { + error = bus_dmamap_create(sbs->dmat, 0, &sbs->dmaps[i]); + if (error) { + isp_prt(isp, ISP_LOGERR, + "error %d creating per-cmd DMA maps", error); + while (--i >= 0) { + bus_dmamap_destroy(sbs->dmat, sbs->dmaps[i]); + } + goto bad; + } + } + + im.isp = isp; + im.error = 0; + bus_dmamap_load(isp->isp_cdmat, isp->isp_cdmap, base, len, imc, &im, 0); + if (im.error) { + isp_prt(isp, ISP_LOGERR, + "error %d loading dma map for control areas", im.error); + goto bad; + } + + isp->isp_rquest = base; + base += ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp)); + ISP_LOCK(isp); + isp->isp_result = base; + return (0); + +bad: + bus_dmamem_free(isp->isp_cdmat, base, isp->isp_cdmap); + bus_dma_tag_destroy(isp->isp_cdmat); + free(isp->isp_xflist, M_DEVBUF); + free(sbs->dmaps, M_DEVBUF); + ISP_LOCK(isp); + isp->isp_rquest = NULL; + return (1); +} + +typedef struct { + ispsoftc_t *isp; + void *cmd_token; + void *rq; + uint16_t *nxtip; + uint16_t optr; + int error; +} mush_t; + +#define MUSHERR_NOQENTRIES -2 + + +static void dma2(void *, bus_dma_segment_t *, int, int); + +static void +dma2(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error) +{ + mush_t *mp; + ispsoftc_t *isp; + struct ccb_scsiio *csio; + struct isp_sbussoftc *sbs; + bus_dmamap_t *dp; + bus_dma_segment_t *eseg; + ispreq_t *rq; + int seglim, datalen; + uint16_t nxti; + + mp = (mush_t *) arg; + if (error) { + mp->error = error; + return; + } + + if (nseg < 1) { + isp_prt(mp->isp, ISP_LOGERR, "bad segment count (%d)", nseg); + mp->error = EFAULT; + return; + } + csio = mp->cmd_token; + isp = mp->isp; + rq = mp->rq; + sbs = (struct isp_sbussoftc *)mp->isp; + dp = &sbs->dmaps[isp_handle_index(rq->req_handle)]; + nxti = *mp->nxtip; + + if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { + bus_dmamap_sync(sbs->dmat, *dp, BUS_DMASYNC_PREREAD); + } else { + bus_dmamap_sync(sbs->dmat, *dp, BUS_DMASYNC_PREWRITE); + } + + datalen = XS_XFRLEN(csio); + + /* + * We're passed an initial partially filled in entry that + * has most fields filled in except for data transfer + * related values. + * + * Our job is to fill in the initial request queue entry and + * then to start allocating and filling in continuation entries + * until we've covered the entire transfer. + */ + + if (csio->cdb_len > 12) { + seglim = 0; + } else { + seglim = ISP_RQDSEG; + } + if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { + rq->req_flags |= REQFLAG_DATA_IN; + } else { + rq->req_flags |= REQFLAG_DATA_OUT; + } + + eseg = dm_segs + nseg; + + while (datalen != 0 && rq->req_seg_count < seglim && dm_segs != eseg) { + rq->req_dataseg[rq->req_seg_count].ds_base = dm_segs->ds_addr; + rq->req_dataseg[rq->req_seg_count].ds_count = dm_segs->ds_len; + datalen -= dm_segs->ds_len; + rq->req_seg_count++; + dm_segs++; + } + + while (datalen > 0 && dm_segs != eseg) { + uint16_t onxti; + ispcontreq_t local, *crq = &local, *cqe; + + cqe = (ispcontreq_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, nxti); + onxti = nxti; + nxti = ISP_NXT_QENTRY(onxti, RQUEST_QUEUE_LEN(isp)); + if (nxti == mp->optr) { + isp_prt(isp, ISP_LOGDEBUG0, "Request Queue Overflow++"); + mp->error = MUSHERR_NOQENTRIES; + return; + } + rq->req_header.rqs_entry_count++; + MEMZERO((void *)crq, sizeof (*crq)); + crq->req_header.rqs_entry_count = 1; + crq->req_header.rqs_entry_type = RQSTYPE_DATASEG; + + seglim = 0; + while (datalen > 0 && seglim < ISP_CDSEG && dm_segs != eseg) { + crq->req_dataseg[seglim].ds_base = + dm_segs->ds_addr; + crq->req_dataseg[seglim].ds_count = + dm_segs->ds_len; + rq->req_seg_count++; + dm_segs++; + seglim++; + datalen -= dm_segs->ds_len; + } + isp_put_cont_req(isp, crq, cqe); + MEMORYBARRIER(isp, SYNC_REQUEST, onxti, QENTRY_LEN); + } + *mp->nxtip = nxti; +} + +static int +isp_sbus_dmasetup(ispsoftc_t *isp, struct ccb_scsiio *csio, ispreq_t *rq, + uint16_t *nxtip, uint16_t optr) +{ + struct isp_sbussoftc *sbs = (struct isp_sbussoftc *)isp; + ispreq_t *qep; + bus_dmamap_t *dp = NULL; + mush_t mush, *mp; + void (*eptr)(void *, bus_dma_segment_t *, int, int); + + qep = (ispreq_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, isp->isp_reqidx); + eptr = dma2; + + + if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE || + (csio->dxfer_len == 0)) { + rq->req_seg_count = 1; + goto mbxsync; + } + + /* + * Do a virtual grapevine step to collect info for + * the callback dma allocation that we have to use... + */ + mp = &mush; + mp->isp = isp; + mp->cmd_token = csio; + mp->rq = rq; + mp->nxtip = nxtip; + mp->optr = optr; + mp->error = 0; + + if ((csio->ccb_h.flags & CAM_SCATTER_VALID) == 0) { + if ((csio->ccb_h.flags & CAM_DATA_PHYS) == 0) { + int error, s; + dp = &sbs->dmaps[isp_handle_index(rq->req_handle)]; + s = splsoftvm(); + error = bus_dmamap_load(sbs->dmat, *dp, + csio->data_ptr, csio->dxfer_len, eptr, mp, 0); + if (error == EINPROGRESS) { + bus_dmamap_unload(sbs->dmat, *dp); + mp->error = EINVAL; + isp_prt(isp, ISP_LOGERR, + "deferred dma allocation not supported"); + } else if (error && mp->error == 0) { +#ifdef DIAGNOSTIC + isp_prt(isp, ISP_LOGERR, + "error %d in dma mapping code", error); +#endif + mp->error = error; + } + splx(s); + } else { + /* Pointer to physical buffer */ + struct bus_dma_segment seg; + seg.ds_addr = (bus_addr_t)csio->data_ptr; + seg.ds_len = csio->dxfer_len; + (*eptr)(mp, &seg, 1, 0); + } + } else { + struct bus_dma_segment *segs; + + if ((csio->ccb_h.flags & CAM_DATA_PHYS) != 0) { + isp_prt(isp, ISP_LOGERR, + "Physical segment pointers unsupported"); + mp->error = EINVAL; + } else if ((csio->ccb_h.flags & CAM_SG_LIST_PHYS) == 0) { + isp_prt(isp, ISP_LOGERR, + "Virtual segment addresses unsupported"); + mp->error = EINVAL; + } else { + /* Just use the segments provided */ + segs = (struct bus_dma_segment *) csio->data_ptr; + (*eptr)(mp, segs, csio->sglist_cnt, 0); + } + } + if (mp->error) { + int retval = CMD_COMPLETE; + if (mp->error == MUSHERR_NOQENTRIES) { + retval = CMD_EAGAIN; + } else if (mp->error == EFBIG) { + XS_SETERR(csio, CAM_REQ_TOO_BIG); + } else if (mp->error == EINVAL) { + XS_SETERR(csio, CAM_REQ_INVALID); + } else { + XS_SETERR(csio, CAM_UNREC_HBA_ERROR); + } + return (retval); + } +mbxsync: + switch (rq->req_header.rqs_entry_type) { + case RQSTYPE_REQUEST: + isp_put_request(isp, rq, qep); + break; + case RQSTYPE_CMDONLY: + isp_put_extended_request(isp, (ispextreq_t *)rq, + (ispextreq_t *)qep); + break; + } + return (CMD_QUEUED); +} + +static void +isp_sbus_dmateardown(ispsoftc_t *isp, XS_T *xs, uint16_t handle) +{ + struct isp_sbussoftc *sbs = (struct isp_sbussoftc *)isp; + bus_dmamap_t *dp = &sbs->dmaps[isp_handle_index(handle)]; + if ((xs->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { + bus_dmamap_sync(sbs->dmat, *dp, BUS_DMASYNC_POSTREAD); + } else { + bus_dmamap_sync(sbs->dmat, *dp, BUS_DMASYNC_POSTWRITE); + } + bus_dmamap_unload(sbs->dmat, *dp); +} + + +static void +isp_sbus_reset1(ispsoftc_t *isp) +{ + /* enable interrupts */ + ENABLE_INTS(isp); +} + +static void +isp_sbus_dumpregs(ispsoftc_t *isp, const char *msg) +{ + if (msg) + printf("%s: %s\n", device_get_nameunit(isp->isp_dev), msg); + else + printf("%s:\n", device_get_nameunit(isp->isp_dev)); + printf(" biu_conf1=%x", ISP_READ(isp, BIU_CONF1)); + printf(" biu_icr=%x biu_isr=%x biu_sema=%x ", ISP_READ(isp, BIU_ICR), + ISP_READ(isp, BIU_ISR), ISP_READ(isp, BIU_SEMA)); + printf("risc_hccr=%x\n", ISP_READ(isp, HCCR)); + + + ISP_WRITE(isp, HCCR, HCCR_CMD_PAUSE); + printf(" cdma_conf=%x cdma_sts=%x cdma_fifostat=%x\n", + ISP_READ(isp, CDMA_CONF), ISP_READ(isp, CDMA_STATUS), + ISP_READ(isp, CDMA_FIFO_STS)); + printf(" ddma_conf=%x ddma_sts=%x ddma_fifostat=%x\n", + ISP_READ(isp, DDMA_CONF), ISP_READ(isp, DDMA_STATUS), + ISP_READ(isp, DDMA_FIFO_STS)); + printf(" sxp_int=%x sxp_gross=%x sxp(scsi_ctrl)=%x\n", + ISP_READ(isp, SXP_INTERRUPT), + ISP_READ(isp, SXP_GROSS_ERR), + ISP_READ(isp, SXP_PINS_CTRL)); + ISP_WRITE(isp, HCCR, HCCR_CMD_RELEASE); + printf(" mbox regs: %x %x %x %x %x\n", + ISP_READ(isp, OUTMAILBOX0), ISP_READ(isp, OUTMAILBOX1), + ISP_READ(isp, OUTMAILBOX2), ISP_READ(isp, OUTMAILBOX3), + ISP_READ(isp, OUTMAILBOX4)); +} diff --git a/sys/dev/isp/isp_target.c b/sys/dev/isp/isp_target.c new file mode 100644 index 000000000000..ebbd80b71114 --- /dev/null +++ b/sys/dev/isp/isp_target.c @@ -0,0 +1,1315 @@ +/*- + * Machine and OS Independent Target Mode Code for the Qlogic SCSI/FC adapters. + * + * Copyright (c) 1997-2006 by Matthew Jacob + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Bug fixes gratefully acknowledged from: + * Oded Kedem <oded@kashya.com> + */ +/* + * Include header file appropriate for platform we're building on. + */ + +#ifdef __NetBSD__ +#include <dev/ic/isp_netbsd.h> +#endif +#ifdef __FreeBSD__ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); +#include <dev/isp/isp_freebsd.h> +#endif +#ifdef __OpenBSD__ +#include <dev/ic/isp_openbsd.h> +#endif +#ifdef __linux__ +#include "isp_linux.h" +#endif + +#ifdef ISP_TARGET_MODE +static const char atiocope[] = + "ATIO returned for lun %d because it was in the middle of Bus Device Reset " + "on bus %d"; +static const char atior[] = + "ATIO returned on for lun %d on from IID %d because a Bus Reset occurred " + "on bus %d"; + +static void isp_got_msg(ispsoftc_t *, in_entry_t *); +static void isp_got_msg_fc(ispsoftc_t *, in_fcentry_t *); +static void isp_handle_atio(ispsoftc_t *, at_entry_t *); +static void isp_handle_atio2(ispsoftc_t *, at2_entry_t *); +static void isp_handle_ctio(ispsoftc_t *, ct_entry_t *); +static void isp_handle_ctio2(ispsoftc_t *, ct2_entry_t *); + +/* + * The Qlogic driver gets an interrupt to look at response queue entries. + * Some of these are status completions for initiatior mode commands, but + * if target mode is enabled, we get a whole wad of response queue entries + * to be handled here. + * + * Basically the split into 3 main groups: Lun Enable/Modification responses, + * SCSI Command processing, and Immediate Notification events. + * + * You start by writing a request queue entry to enable target mode (and + * establish some resource limitations which you can modify later). + * The f/w responds with a LUN ENABLE or LUN MODIFY response with + * the status of this action. If the enable was successful, you can expect... + * + * Response queue entries with SCSI commands encapsulate show up in an ATIO + * (Accept Target IO) type- sometimes with enough info to stop the command at + * this level. Ultimately the driver has to feed back to the f/w's request + * queue a sequence of CTIOs (continue target I/O) that describe data to + * be moved and/or status to be sent) and finally finishing with sending + * to the f/w's response queue an ATIO which then completes the handshake + * with the f/w for that command. There's a lot of variations on this theme, + * including flags you can set in the CTIO for the Qlogic 2X00 fibre channel + * cards that 'auto-replenish' the f/w's ATIO count, but this is the basic + * gist of it. + * + * The third group that can show up in the response queue are Immediate + * Notification events. These include things like notifications of SCSI bus + * resets, or Bus Device Reset messages or other messages received. This + * a classic oddbins area. It can get a little weird because you then turn + * around and acknowledge the Immediate Notify by writing an entry onto the + * request queue and then the f/w turns around and gives you an acknowledgement + * to *your* acknowledgement on the response queue (the idea being to let + * the f/w tell you when the event is *really* over I guess). + * + */ + + +/* + * A new response queue entry has arrived. The interrupt service code + * has already swizzled it into the platform dependent from canonical form. + * + * Because of the way this driver is designed, unfortunately most of the + * actual synchronization work has to be done in the platform specific + * code- we have no synchroniation primitives in the common code. + */ + +int +isp_target_notify(ispsoftc_t *isp, void *vptr, uint16_t *optrp) +{ + uint16_t status, seqid; + union { + at_entry_t *atiop; + at2_entry_t *at2iop; + at2e_entry_t *at2eiop; + ct_entry_t *ctiop; + ct2_entry_t *ct2iop; + ct2e_entry_t *ct2eiop; + lun_entry_t *lunenp; + in_entry_t *inotp; + in_fcentry_t *inot_fcp; + in_fcentry_e_t *inote_fcp; + na_entry_t *nackp; + na_fcentry_t *nack_fcp; + na_fcentry_e_t *nacke_fcp; + isphdr_t *hp; + void * *vp; +#define atiop unp.atiop +#define at2iop unp.at2iop +#define at2eiop unp.at2eiop +#define ctiop unp.ctiop +#define ct2iop unp.ct2iop +#define ct2eiop unp.ct2eiop +#define lunenp unp.lunenp +#define inotp unp.inotp +#define inot_fcp unp.inot_fcp +#define inote_fcp unp.inote_fcp +#define nackp unp.nackp +#define nack_fcp unp.nack_fcp +#define nacke_fcp unp.nacke_fcp +#define hdrp unp.hp + } unp; + uint8_t local[QENTRY_LEN]; + int bus, type, rval = 1; + + type = isp_get_response_type(isp, (isphdr_t *)vptr); + unp.vp = vptr; + + ISP_TDQE(isp, "isp_target_notify", (int) *optrp, vptr); + + switch(type) { + case RQSTYPE_ATIO: + isp_get_atio(isp, atiop, (at_entry_t *) local); + isp_handle_atio(isp, (at_entry_t *) local); + break; + case RQSTYPE_CTIO: + isp_get_ctio(isp, ctiop, (ct_entry_t *) local); + isp_handle_ctio(isp, (ct_entry_t *) local); + break; + case RQSTYPE_ATIO2: + if (IS_2KLOGIN(isp)) { + isp_get_atio2e(isp, at2eiop, (at2e_entry_t *) local); + } else { + isp_get_atio2(isp, at2iop, (at2_entry_t *) local); + } + isp_handle_atio2(isp, (at2_entry_t *) local); + break; + case RQSTYPE_CTIO3: + case RQSTYPE_CTIO2: + if (IS_2KLOGIN(isp)) { + isp_get_ctio2e(isp, ct2eiop, (ct2e_entry_t *) local); + } else { + isp_get_ctio2(isp, ct2iop, (ct2_entry_t *) local); + } + isp_handle_ctio2(isp, (ct2_entry_t *) local); + break; + case RQSTYPE_ENABLE_LUN: + case RQSTYPE_MODIFY_LUN: + isp_get_enable_lun(isp, lunenp, (lun_entry_t *) local); + (void) isp_async(isp, ISPASYNC_TARGET_ACTION, local); + break; + + case RQSTYPE_NOTIFY: + /* + * Either the ISP received a SCSI message it can't + * handle, or it's returning an Immed. Notify entry + * we sent. We can send Immed. Notify entries to + * increment the firmware's resource count for them + * (we set this initially in the Enable Lun entry). + */ + bus = 0; + if (IS_FC(isp)) { + if (IS_2KLOGIN(isp)) { + isp_get_notify_fc_e(isp, inote_fcp, + (in_fcentry_e_t *)local); + } else { + isp_get_notify_fc(isp, inot_fcp, + (in_fcentry_t *)local); + } + inot_fcp = (in_fcentry_t *) local; + status = inot_fcp->in_status; + seqid = inot_fcp->in_seqid; + } else { + isp_get_notify(isp, inotp, (in_entry_t *)local); + inotp = (in_entry_t *) local; + status = inotp->in_status & 0xff; + seqid = inotp->in_seqid; + if (IS_DUALBUS(isp)) { + bus = GET_BUS_VAL(inotp->in_iid); + SET_BUS_VAL(inotp->in_iid, 0); + } + } + + isp_prt(isp, ISP_LOGTDEBUG0, + "Immediate Notify On Bus %d, status=0x%x seqid=0x%x", + bus, status, seqid); + + switch (status) { + case IN_MSG_RECEIVED: + case IN_IDE_RECEIVED: + if (IS_FC(isp)) { + isp_got_msg_fc(isp, (in_fcentry_t *)local); + } else { + isp_got_msg(isp, (in_entry_t *)local); + } + break; + case IN_RSRC_UNAVAIL: + isp_prt(isp, ISP_LOGWARN, "Firmware out of ATIOs"); + isp_notify_ack(isp, local); + break; + case IN_RESET: + (void) isp_target_async(isp, 0, ASYNC_BUS_RESET); + break; + case IN_PORT_LOGOUT: + case IN_ABORT_TASK: + case IN_PORT_CHANGED: + case IN_GLOBAL_LOGO: + (void) isp_async(isp, ISPASYNC_TARGET_ACTION, &local); + break; + default: + isp_prt(isp, ISP_LOGERR, + "bad status (0x%x) in isp_target_notify", status); + isp_notify_ack(isp, local); + break; + } + break; + + case RQSTYPE_NOTIFY_ACK: + /* + * The ISP is acknowledging our acknowledgement of an + * Immediate Notify entry for some asynchronous event. + */ + if (IS_FC(isp)) { + if (IS_2KLOGIN(isp)) { + isp_get_notify_ack_fc_e(isp, nacke_fcp, + (na_fcentry_e_t *)local); + } else { + isp_get_notify_ack_fc(isp, nack_fcp, + (na_fcentry_t *)local); + } + nack_fcp = (na_fcentry_t *)local; + isp_prt(isp, ISP_LOGTDEBUG1, + "Notify Ack status=0x%x seqid 0x%x", + nack_fcp->na_status, nack_fcp->na_seqid); + } else { + isp_get_notify_ack(isp, nackp, (na_entry_t *)local); + nackp = (na_entry_t *)local; + isp_prt(isp, ISP_LOGTDEBUG1, + "Notify Ack event 0x%x status=0x%x seqid 0x%x", + nackp->na_event, nackp->na_status, nackp->na_seqid); + } + break; + default: + isp_prt(isp, ISP_LOGERR, + "Unknown entry type 0x%x in isp_target_notify", type); + rval = 0; + break; + } +#undef atiop +#undef at2iop +#undef at2eiop +#undef ctiop +#undef ct2iop +#undef ct2eiop +#undef lunenp +#undef inotp +#undef inot_fcp +#undef inote_fcp +#undef nackp +#undef nack_fcp +#undef nacke_fcp +#undef hdrp + return (rval); +} + + +/* + * Toggle (on/off) target mode for bus/target/lun + * + * The caller has checked for overlap and legality. + * + * Note that not all of bus, target or lun can be paid attention to. + * Note also that this action will not be complete until the f/w writes + * response entry. The caller is responsible for synchronizing this. + */ +int +isp_lun_cmd(ispsoftc_t *isp, int cmd, int bus, int tgt, int lun, + int cmd_cnt, int inot_cnt, uint32_t opaque) +{ + lun_entry_t el; + uint16_t nxti, optr; + void *outp; + + + MEMZERO(&el, sizeof (el)); + if (IS_DUALBUS(isp)) { + el.le_rsvd = (bus & 0x1) << 7; + } + el.le_cmd_count = cmd_cnt; + el.le_in_count = inot_cnt; + if (cmd == RQSTYPE_ENABLE_LUN) { + if (IS_SCSI(isp)) { + el.le_flags = LUN_TQAE|LUN_DISAD; + el.le_cdb6len = 12; + el.le_cdb7len = 12; + } + } else if (cmd == -RQSTYPE_ENABLE_LUN) { + cmd = RQSTYPE_ENABLE_LUN; + el.le_cmd_count = 0; + el.le_in_count = 0; + } else if (cmd == -RQSTYPE_MODIFY_LUN) { + cmd = RQSTYPE_MODIFY_LUN; + el.le_ops = LUN_CCDECR | LUN_INDECR; + } else { + el.le_ops = LUN_CCINCR | LUN_ININCR; + } + el.le_header.rqs_entry_type = cmd; + el.le_header.rqs_entry_count = 1; + el.le_reserved = opaque; + if (IS_SCSI(isp)) { + el.le_tgt = tgt; + el.le_lun = lun; + } else if ((FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) == 0) { + el.le_lun = lun; + } + + if (isp_getrqentry(isp, &nxti, &optr, &outp)) { + isp_prt(isp, ISP_LOGERR, + "Request Queue Overflow in isp_lun_cmd"); + return (-1); + } + ISP_TDQE(isp, "isp_lun_cmd", (int) optr, &el); + isp_put_enable_lun(isp, &el, outp); + ISP_ADD_REQUEST(isp, nxti); + return (0); +} + + +int +isp_target_put_entry(ispsoftc_t *isp, void *ap) +{ + void *outp; + uint16_t nxti, optr; + uint8_t etype = ((isphdr_t *) ap)->rqs_entry_type; + + if (isp_getrqentry(isp, &nxti, &optr, &outp)) { + isp_prt(isp, ISP_LOGWARN, + "Request Queue Overflow in isp_target_put_entry"); + return (-1); + } + switch (etype) { + case RQSTYPE_ATIO: + isp_put_atio(isp, (at_entry_t *) ap, (at_entry_t *) outp); + break; + case RQSTYPE_ATIO2: + if (IS_2KLOGIN(isp)) { + isp_put_atio2e(isp, (at2e_entry_t *) ap, (at2e_entry_t *) outp); + } else { + isp_put_atio2(isp, (at2_entry_t *) ap, (at2_entry_t *) outp); + } + break; + case RQSTYPE_CTIO: + isp_put_ctio(isp, (ct_entry_t *) ap, (ct_entry_t *) outp); + break; + case RQSTYPE_CTIO2: + if (IS_2KLOGIN(isp)) { + isp_put_ctio2e(isp, (ct2e_entry_t *) ap, (ct2e_entry_t *) outp); + } else { + isp_put_ctio2(isp, (ct2_entry_t *) ap, (ct2_entry_t *) outp); + } + break; + default: + isp_prt(isp, ISP_LOGERR, + "Unknown type 0x%x in isp_put_entry", etype); + return (-1); + } + + ISP_TDQE(isp, "isp_target_put_entry", (int) optr, ap); + ISP_ADD_REQUEST(isp, nxti); + return (0); +} + +int +isp_target_put_atio(ispsoftc_t *isp, void *arg) +{ + union { + at_entry_t _atio; + at2_entry_t _atio2; + at2e_entry_t _atio2e; + } atun; + + MEMZERO(&atun, sizeof atun); + if (IS_FC(isp)) { + at2_entry_t *aep = arg; + atun._atio2.at_header.rqs_entry_type = RQSTYPE_ATIO2; + atun._atio2.at_header.rqs_entry_count = 1; + if (FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) { + atun._atio2.at_scclun = (uint16_t) aep->at_scclun; + } else { + atun._atio2.at_lun = (uint8_t) aep->at_lun; + } + if (IS_2KLOGIN(isp)) { + atun._atio2e.at_iid = ((at2e_entry_t *)aep)->at_iid; + } else { + atun._atio2.at_iid = aep->at_iid; + } + atun._atio2.at_rxid = aep->at_rxid; + atun._atio2.at_status = CT_OK; + } else { + at_entry_t *aep = arg; + atun._atio.at_header.rqs_entry_type = RQSTYPE_ATIO; + atun._atio.at_header.rqs_entry_count = 1; + atun._atio.at_handle = aep->at_handle; + atun._atio.at_iid = aep->at_iid; + atun._atio.at_tgt = aep->at_tgt; + atun._atio.at_lun = aep->at_lun; + atun._atio.at_tag_type = aep->at_tag_type; + atun._atio.at_tag_val = aep->at_tag_val; + atun._atio.at_status = (aep->at_flags & AT_TQAE); + atun._atio.at_status |= CT_OK; + } + return (isp_target_put_entry(isp, &atun)); +} + +/* + * Command completion- both for handling cases of no resources or + * no blackhole driver, or other cases where we have to, inline, + * finish the command sanely, or for normal command completion. + * + * The 'completion' code value has the scsi status byte in the low 8 bits. + * If status is a CHECK CONDITION and bit 8 is nonzero, then bits 12..15 have + * the sense key and bits 16..23 have the ASCQ and bits 24..31 have the ASC + * values. + * + * NB: the key, asc, ascq, cannot be used for parallel SCSI as it doesn't + * NB: inline SCSI sense reporting. As such, we lose this information. XXX. + * + * For both parallel && fibre channel, we use the feature that does + * an automatic resource autoreplenish so we don't have then later do + * put of an atio to replenish the f/w's resource count. + */ + +int +isp_endcmd(ispsoftc_t *isp, void *arg, uint32_t code, uint16_t hdl) +{ + int sts; + union { + ct_entry_t _ctio; + ct2_entry_t _ctio2; + ct2e_entry_t _ctio2e; + } un; + + MEMZERO(&un, sizeof un); + sts = code & 0xff; + + if (IS_FC(isp)) { + at2_entry_t *aep = arg; + ct2_entry_t *cto = &un._ctio2; + + cto->ct_header.rqs_entry_type = RQSTYPE_CTIO2; + cto->ct_header.rqs_entry_count = 1; + if ((FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) == 0) { + cto->ct_lun = aep->at_lun; + } + if (IS_2KLOGIN(isp)) { + un._ctio2e.ct_iid = ((at2e_entry_t *)aep)->at_iid; + } else { + cto->ct_iid = aep->at_iid; + } + cto->ct_rxid = aep->at_rxid; + cto->rsp.m1.ct_scsi_status = sts; + cto->ct_flags = CT2_SENDSTATUS | CT2_NO_DATA | CT2_FLAG_MODE1; + if (hdl == 0) { + cto->ct_flags |= CT2_CCINCR; + } + if (aep->at_datalen) { + cto->ct_resid = aep->at_datalen; + cto->rsp.m1.ct_scsi_status |= CT2_DATA_UNDER; + } + if (sts == SCSI_CHECK && (code & ECMD_SVALID)) { + cto->rsp.m1.ct_resp[0] = 0xf0; + cto->rsp.m1.ct_resp[2] = (code >> 12) & 0xf; + cto->rsp.m1.ct_resp[7] = 8; + cto->rsp.m1.ct_resp[12] = (code >> 24) & 0xff; + cto->rsp.m1.ct_resp[13] = (code >> 16) & 0xff; + cto->rsp.m1.ct_senselen = 16; + cto->rsp.m1.ct_scsi_status |= CT2_SNSLEN_VALID; + } + cto->ct_syshandle = hdl; + } else { + at_entry_t *aep = arg; + ct_entry_t *cto = &un._ctio; + + cto->ct_header.rqs_entry_type = RQSTYPE_CTIO; + cto->ct_header.rqs_entry_count = 1; + cto->ct_fwhandle = aep->at_handle; + cto->ct_iid = aep->at_iid; + cto->ct_tgt = aep->at_tgt; + cto->ct_lun = aep->at_lun; + cto->ct_tag_type = aep->at_tag_type; + cto->ct_tag_val = aep->at_tag_val; + if (aep->at_flags & AT_TQAE) { + cto->ct_flags |= CT_TQAE; + } + cto->ct_flags = CT_SENDSTATUS | CT_NO_DATA; + if (hdl == 0) { + cto->ct_flags |= CT_CCINCR; + } + cto->ct_scsi_status = sts; + cto->ct_syshandle = hdl; + } + return (isp_target_put_entry(isp, &un)); +} + +/* + * These are either broadcast events or specifically CTIO fast completion + */ +int +isp_target_async(ispsoftc_t *isp, int bus, int event) +{ + tmd_notify_t notify; + + MEMZERO(¬ify, sizeof (tmd_notify_t)); + notify.nt_hba = isp; + notify.nt_iid = INI_ANY; + /* nt_tgt set in outer layers */ + notify.nt_lun = LUN_ANY; + notify.nt_tagval = TAG_ANY; + + if (IS_SCSI(isp)) { + TAG_INSERT_BUS(notify.nt_tagval, bus); + } + + switch (event) { + case ASYNC_LOOP_UP: + case ASYNC_PTPMODE: + notify.nt_ncode = NT_LINK_UP; + (void) isp_async(isp, ISPASYNC_TARGET_NOTIFY, ¬ify); + break; + case ASYNC_LOOP_DOWN: + notify.nt_ncode = NT_LINK_DOWN; + (void) isp_async(isp, ISPASYNC_TARGET_NOTIFY, ¬ify); + break; + case ASYNC_LIP_F8: + case ASYNC_LIP_OCCURRED: + case ASYNC_LOOP_RESET: + notify.nt_ncode = NT_LIP_RESET; + (void) isp_async(isp, ISPASYNC_TARGET_NOTIFY, ¬ify); + break; + case ASYNC_BUS_RESET: + case ASYNC_TIMEOUT_RESET: /* XXX: where does this come from ? */ + notify.nt_ncode = NT_BUS_RESET; + (void) isp_async(isp, ISPASYNC_TARGET_NOTIFY, ¬ify); + break; + case ASYNC_DEVICE_RESET: + notify.nt_ncode = NT_TARGET_RESET; + (void) isp_async(isp, ISPASYNC_TARGET_NOTIFY, ¬ify); + break; + case ASYNC_CTIO_DONE: + { + uint8_t storage[QENTRY_LEN]; + memset(storage, 0, QENTRY_LEN); + if (IS_FC(isp)) { + /* This should also suffice for 2K login code */ + ct2_entry_t *ct = (ct2_entry_t *) storage; + ct->ct_header.rqs_entry_type = RQSTYPE_CTIO2; + ct->ct_status = CT_OK; + ct->ct_syshandle = bus; + ct->ct_flags = CT2_SENDSTATUS|CT2_FASTPOST; + } else { + ct_entry_t *ct = (ct_entry_t *) storage; + ct->ct_header.rqs_entry_type = RQSTYPE_CTIO; + ct->ct_status = CT_OK; + ct->ct_fwhandle = bus; + ct->ct_flags = CT_SENDSTATUS; + } + (void) isp_async(isp, ISPASYNC_TARGET_ACTION, storage); + return (0); + } + default: + isp_prt(isp, ISP_LOGERR, + "isp_target_async: unknown event 0x%x", event); + if (isp->isp_state == ISP_RUNSTATE) { + isp_notify_ack(isp, NULL); + } + break; + } + return (0); +} + + +/* + * Process a received message. + * The ISP firmware can handle most messages, there are only + * a few that we need to deal with: + * - abort: clean up the current command + * - abort tag and clear queue + */ + +static void +isp_got_msg(ispsoftc_t *isp, in_entry_t *inp) +{ + tmd_notify_t nt; + uint8_t status = inp->in_status & ~QLTM_SVALID; + + MEMZERO(&nt, sizeof (nt)); + nt.nt_hba = isp; + nt.nt_iid = GET_IID_VAL(inp->in_iid); + nt.nt_tgt = inp->in_tgt; + nt.nt_lun = inp->in_lun; + IN_MAKE_TAGID(nt.nt_tagval, 0, inp); + nt.nt_lreserved = inp; + + if (status == IN_IDE_RECEIVED || status == IN_MSG_RECEIVED) { + switch (inp->in_msg[0]) { + case MSG_ABORT: + nt.nt_ncode = NT_ABORT_TASK_SET; + break; + case MSG_BUS_DEV_RESET: + nt.nt_ncode = NT_TARGET_RESET; + break; + case MSG_ABORT_TAG: + nt.nt_ncode = NT_ABORT_TASK; + break; + case MSG_CLEAR_QUEUE: + nt.nt_ncode = NT_CLEAR_TASK_SET; + break; + case MSG_REL_RECOVERY: + nt.nt_ncode = NT_CLEAR_ACA; + break; + case MSG_TERM_IO_PROC: + nt.nt_ncode = NT_ABORT_TASK; + break; + case MSG_LUN_RESET: + nt.nt_ncode = NT_LUN_RESET; + break; + default: + isp_prt(isp, ISP_LOGERR, + "unhandled message 0x%x", inp->in_msg[0]); + isp_notify_ack(isp, inp); + return; + } + (void) isp_async(isp, ISPASYNC_TARGET_NOTIFY, &nt); + } else { + isp_prt(isp, ISP_LOGERR, + "unknown immediate notify status 0x%x", inp->in_status); + isp_notify_ack(isp, inp); + } +} + +/* + * Synthesize a message from the task management flags in a FCP_CMND_IU. + */ +static void +isp_got_msg_fc(ispsoftc_t *isp, in_fcentry_t *inp) +{ + tmd_notify_t nt; + static const char f1[] = "%s from loop id %d lun %d seq 0x%x"; + static const char f2[] = + "unknown %s 0x%x lun %d loop id %d task flags 0x%x seq 0x%x\n"; + uint16_t seqid, loopid; + + MEMZERO(&nt, sizeof (tmd_notify_t)); + nt.nt_hba = isp; + if (IS_2KLOGIN(isp)) { + nt.nt_iid = ((in_fcentry_e_t *)inp)->in_iid; + loopid = ((in_fcentry_e_t *)inp)->in_iid; + seqid = ((in_fcentry_e_t *)inp)->in_seqid; + } else { + nt.nt_iid = inp->in_iid; + loopid = inp->in_iid; + seqid = inp->in_seqid; + } + /* nt_tgt set in outer layers */ + if (FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) { + nt.nt_lun = inp->in_scclun; + } else { + nt.nt_lun = inp->in_lun; + } + IN_FC_MAKE_TAGID(nt.nt_tagval, 0, seqid); + nt.nt_lreserved = inp; + + if (inp->in_status != IN_MSG_RECEIVED) { + isp_prt(isp, ISP_LOGINFO, f2, "immediate notify status", + inp->in_status, nt.nt_lun, loopid, inp->in_task_flags, + inp->in_seqid); + isp_notify_ack(isp, inp); + return; + } + + if (inp->in_task_flags & TASK_FLAGS_ABORT_TASK_SET) { + isp_prt(isp, ISP_LOGINFO, f1, "ABORT TASK SET", + loopid, nt.nt_lun, inp->in_seqid); + nt.nt_ncode = NT_ABORT_TASK_SET; + } else if (inp->in_task_flags & TASK_FLAGS_CLEAR_TASK_SET) { + isp_prt(isp, ISP_LOGINFO, f1, "CLEAR TASK SET", + loopid, nt.nt_lun, inp->in_seqid); + nt.nt_ncode = NT_CLEAR_TASK_SET; + } else if (inp->in_task_flags & TASK_FLAGS_LUN_RESET) { + isp_prt(isp, ISP_LOGINFO, f1, "LUN RESET", + loopid, nt.nt_lun, inp->in_seqid); + nt.nt_ncode = NT_LUN_RESET; + } else if (inp->in_task_flags & TASK_FLAGS_TARGET_RESET) { + isp_prt(isp, ISP_LOGINFO, f1, "TARGET RESET", + loopid, nt.nt_lun, inp->in_seqid); + nt.nt_ncode = NT_TARGET_RESET; + } else if (inp->in_task_flags & TASK_FLAGS_CLEAR_ACA) { + isp_prt(isp, ISP_LOGINFO, f1, "CLEAR ACA", + loopid, nt.nt_lun, inp->in_seqid); + nt.nt_ncode = NT_CLEAR_ACA; + } else { + isp_prt(isp, ISP_LOGWARN, f2, "task flag", inp->in_status, + nt.nt_lun, loopid, inp->in_task_flags, inp->in_seqid); + isp_notify_ack(isp, inp); + return; + } + (void) isp_async(isp, ISPASYNC_TARGET_NOTIFY, &nt); +} + +void +isp_notify_ack(ispsoftc_t *isp, void *arg) +{ + char storage[QENTRY_LEN]; + uint16_t nxti, optr; + void *outp; + + if (isp_getrqentry(isp, &nxti, &optr, &outp)) { + isp_prt(isp, ISP_LOGWARN, + "Request Queue Overflow For isp_notify_ack"); + return; + } + + MEMZERO(storage, QENTRY_LEN); + + if (IS_FC(isp)) { + na_fcentry_t *na = (na_fcentry_t *) storage; + int iid = 0; + + if (arg) { + in_fcentry_t *inp = arg; + MEMCPY(storage, arg, sizeof (isphdr_t)); + if (IS_2KLOGIN(isp)) { + ((na_fcentry_e_t *)na)->na_iid = + ((in_fcentry_e_t *)inp)->in_iid; + iid = ((na_fcentry_e_t *)na)->na_iid; + } else { + na->na_iid = inp->in_iid; + iid = na->na_iid; + } + na->na_task_flags = + inp->in_task_flags & TASK_FLAGS_RESERVED_MASK; + na->na_seqid = inp->in_seqid; + na->na_flags = NAFC_RCOUNT; + na->na_status = inp->in_status; + if (inp->in_status == IN_RESET) { + na->na_flags |= NAFC_RST_CLRD; + } + if (inp->in_status == IN_MSG_RECEIVED) { + na->na_flags |= NAFC_TVALID; + na->na_response = 0; /* XXX SUCCEEDED XXX */ + } + } else { + na->na_flags = NAFC_RST_CLRD; + } + na->na_header.rqs_entry_type = RQSTYPE_NOTIFY_ACK; + na->na_header.rqs_entry_count = 1; + if (IS_2KLOGIN(isp)) { + isp_put_notify_ack_fc_e(isp, (na_fcentry_e_t *) na, + (na_fcentry_e_t *)outp); + } else { + isp_put_notify_ack_fc(isp, na, (na_fcentry_t *)outp); + } + isp_prt(isp, ISP_LOGTDEBUG0, "notify ack iid %u seqid %x flags " + "%x tflags %x response %x", iid, na->na_seqid, + na->na_flags, na->na_task_flags, na->na_response); + } else { + na_entry_t *na = (na_entry_t *) storage; + if (arg) { + in_entry_t *inp = arg; + MEMCPY(storage, arg, sizeof (isphdr_t)); + na->na_iid = inp->in_iid; + na->na_lun = inp->in_lun; + na->na_tgt = inp->in_tgt; + na->na_seqid = inp->in_seqid; + if (inp->in_status == IN_RESET) { + na->na_event = NA_RST_CLRD; + } + } else { + na->na_event = NA_RST_CLRD; + } + na->na_header.rqs_entry_type = RQSTYPE_NOTIFY_ACK; + na->na_header.rqs_entry_count = 1; + isp_put_notify_ack(isp, na, (na_entry_t *)outp); + isp_prt(isp, ISP_LOGTDEBUG0, "notify ack iid %u lun %u tgt %u " + "seqid %x event %x", na->na_iid, na->na_lun, na->na_tgt, + na->na_seqid, na->na_event); + } + ISP_TDQE(isp, "isp_notify_ack", (int) optr, storage); + ISP_ADD_REQUEST(isp, nxti); +} + +static void +isp_handle_atio(ispsoftc_t *isp, at_entry_t *aep) +{ + int lun; + lun = aep->at_lun; + /* + * The firmware status (except for the QLTM_SVALID bit) indicates + * why this ATIO was sent to us. + * + * If QLTM_SVALID is set, the firware has recommended Sense Data. + * + * If the DISCONNECTS DISABLED bit is set in the flags field, + * we're still connected on the SCSI bus - i.e. the initiator + * did not set DiscPriv in the identify message. We don't care + * about this so it's ignored. + */ + + switch(aep->at_status & ~QLTM_SVALID) { + case AT_PATH_INVALID: + /* + * ATIO rejected by the firmware due to disabled lun. + */ + isp_prt(isp, ISP_LOGERR, + "rejected ATIO for disabled lun %d", lun); + break; + case AT_NOCAP: + /* + * Requested Capability not available + * We sent an ATIO that overflowed the firmware's + * command resource count. + */ + isp_prt(isp, ISP_LOGERR, + "rejected ATIO for lun %d because of command count" + " overflow", lun); + break; + + case AT_BDR_MSG: + /* + * If we send an ATIO to the firmware to increment + * its command resource count, and the firmware is + * recovering from a Bus Device Reset, it returns + * the ATIO with this status. We set the command + * resource count in the Enable Lun entry and do + * not increment it. Therefore we should never get + * this status here. + */ + isp_prt(isp, ISP_LOGERR, atiocope, lun, + GET_BUS_VAL(aep->at_iid)); + break; + + case AT_CDB: /* Got a CDB */ + case AT_PHASE_ERROR: /* Bus Phase Sequence Error */ + /* + * Punt to platform specific layer. + */ + (void) isp_async(isp, ISPASYNC_TARGET_ACTION, aep); + break; + + case AT_RESET: + /* + * A bus reset came along and blew away this command. Why + * they do this in addition the async event code stuff, + * I dunno. + * + * Ignore it because the async event will clear things + * up for us. + */ + isp_prt(isp, ISP_LOGWARN, atior, lun, + GET_IID_VAL(aep->at_iid), GET_BUS_VAL(aep->at_iid)); + break; + + + default: + isp_prt(isp, ISP_LOGERR, + "Unknown ATIO status 0x%x from initiator %d for lun %d", + aep->at_status, aep->at_iid, lun); + (void) isp_target_put_atio(isp, aep); + break; + } +} + +static void +isp_handle_atio2(ispsoftc_t *isp, at2_entry_t *aep) +{ + int lun, iid; + + if (FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) { + lun = aep->at_scclun; + } else { + lun = aep->at_lun; + } + + if (IS_2KLOGIN(isp)) { + iid = ((at2e_entry_t *)aep)->at_iid; + } else { + iid = aep->at_iid; + } + + /* + * The firmware status (except for the QLTM_SVALID bit) indicates + * why this ATIO was sent to us. + * + * If QLTM_SVALID is set, the firware has recommended Sense Data. + * + * If the DISCONNECTS DISABLED bit is set in the flags field, + * we're still connected on the SCSI bus - i.e. the initiator + * did not set DiscPriv in the identify message. We don't care + * about this so it's ignored. + */ + + switch(aep->at_status & ~QLTM_SVALID) { + case AT_PATH_INVALID: + /* + * ATIO rejected by the firmware due to disabled lun. + */ + isp_prt(isp, ISP_LOGERR, + "rejected ATIO2 for disabled lun %d", lun); + break; + case AT_NOCAP: + /* + * Requested Capability not available + * We sent an ATIO that overflowed the firmware's + * command resource count. + */ + isp_prt(isp, ISP_LOGERR, + "rejected ATIO2 for lun %d- command count overflow", lun); + break; + + case AT_BDR_MSG: + /* + * If we send an ATIO to the firmware to increment + * its command resource count, and the firmware is + * recovering from a Bus Device Reset, it returns + * the ATIO with this status. We set the command + * resource count in the Enable Lun entry and no + * not increment it. Therefore we should never get + * this status here. + */ + isp_prt(isp, ISP_LOGERR, atiocope, lun, 0); + break; + + case AT_CDB: /* Got a CDB */ + /* + * Punt to platform specific layer. + */ + (void) isp_async(isp, ISPASYNC_TARGET_ACTION, aep); + break; + + case AT_RESET: + /* + * A bus reset came along an blew away this command. Why + * they do this in addition the async event code stuff, + * I dunno. + * + * Ignore it because the async event will clear things + * up for us. + */ + isp_prt(isp, ISP_LOGERR, atior, lun, iid, 0); + break; + + + default: + isp_prt(isp, ISP_LOGERR, + "Unknown ATIO2 status 0x%x from initiator %d for lun %d", + aep->at_status, iid, lun); + (void) isp_target_put_atio(isp, aep); + break; + } +} + +static void +isp_handle_ctio(ispsoftc_t *isp, ct_entry_t *ct) +{ + void *xs; + int pl = ISP_LOGTDEBUG2; + char *fmsg = NULL; + + if (ct->ct_syshandle) { + xs = isp_find_xs_tgt(isp, ct->ct_syshandle); + if (xs == NULL) + pl = ISP_LOGALL; + } else { + xs = NULL; + } + + switch(ct->ct_status & ~QLTM_SVALID) { + case CT_OK: + /* + * There are generally 3 possibilities as to why we'd get + * this condition: + * We disconnected after receiving a CDB. + * We sent or received data. + * We sent status & command complete. + */ + + if (ct->ct_flags & CT_SENDSTATUS) { + break; + } else if ((ct->ct_flags & CT_DATAMASK) == CT_NO_DATA) { + /* + * Nothing to do in this case. + */ + isp_prt(isp, pl, "CTIO- iid %d disconnected OK", + ct->ct_iid); + return; + } + break; + + case CT_BDR_MSG: + /* + * Bus Device Reset message received or the SCSI Bus has + * been Reset; the firmware has gone to Bus Free. + * + * The firmware generates an async mailbox interupt to + * notify us of this and returns outstanding CTIOs with this + * status. These CTIOs are handled in that same way as + * CT_ABORTED ones, so just fall through here. + */ + fmsg = "Bus Device Reset"; + /*FALLTHROUGH*/ + case CT_RESET: + if (fmsg == NULL) + fmsg = "Bus Reset"; + /*FALLTHROUGH*/ + case CT_ABORTED: + /* + * When an Abort message is received the firmware goes to + * Bus Free and returns all outstanding CTIOs with the status + * set, then sends us an Immediate Notify entry. + */ + if (fmsg == NULL) + fmsg = "ABORT TAG message sent by Initiator"; + + isp_prt(isp, ISP_LOGWARN, "CTIO destroyed by %s", fmsg); + break; + + case CT_INVAL: + /* + * CTIO rejected by the firmware due to disabled lun. + * "Cannot Happen". + */ + isp_prt(isp, ISP_LOGERR, + "Firmware rejected CTIO for disabled lun %d", + ct->ct_lun); + break; + + case CT_NOPATH: + /* + * CTIO rejected by the firmware due "no path for the + * nondisconnecting nexus specified". This means that + * we tried to access the bus while a non-disconnecting + * command is in process. + */ + isp_prt(isp, ISP_LOGERR, + "Firmware rejected CTIO for bad nexus %d/%d/%d", + ct->ct_iid, ct->ct_tgt, ct->ct_lun); + break; + + case CT_RSELTMO: + fmsg = "Reselection"; + /*FALLTHROUGH*/ + case CT_TIMEOUT: + if (fmsg == NULL) + fmsg = "Command"; + isp_prt(isp, ISP_LOGERR, "Firmware timed out on %s", fmsg); + break; + + case CT_PANIC: + if (fmsg == NULL) + fmsg = "Unrecoverable Error"; + /*FALLTHROUGH*/ + case CT_ERR: + if (fmsg == NULL) + fmsg = "Completed with Error"; + /*FALLTHROUGH*/ + case CT_PHASE_ERROR: + if (fmsg == NULL) + fmsg = "Phase Sequence Error"; + /*FALLTHROUGH*/ + case CT_TERMINATED: + if (fmsg == NULL) + fmsg = "terminated by TERMINATE TRANSFER"; + /*FALLTHROUGH*/ + case CT_NOACK: + if (fmsg == NULL) + fmsg = "unacknowledged Immediate Notify pending"; + isp_prt(isp, ISP_LOGERR, "CTIO returned by f/w- %s", fmsg); + break; + default: + isp_prt(isp, ISP_LOGERR, "Unknown CTIO status 0x%x", + ct->ct_status & ~QLTM_SVALID); + break; + } + + if (xs == NULL) { + /* + * There may be more than one CTIO for a data transfer, + * or this may be a status CTIO we're not monitoring. + * + * The assumption is that they'll all be returned in the + * order we got them. + */ + if (ct->ct_syshandle == 0) { + if ((ct->ct_flags & CT_SENDSTATUS) == 0) { + isp_prt(isp, pl, + "intermediate CTIO completed ok"); + } else { + isp_prt(isp, pl, + "unmonitored CTIO completed ok"); + } + } else { + isp_prt(isp, pl, + "NO xs for CTIO (handle 0x%x) status 0x%x", + ct->ct_syshandle, ct->ct_status & ~QLTM_SVALID); + } + } else { + /* + * Final CTIO completed. Release DMA resources and + * notify platform dependent layers. + */ + if ((ct->ct_flags & CT_DATAMASK) != CT_NO_DATA) { + ISP_DMAFREE(isp, xs, ct->ct_syshandle); + } + isp_prt(isp, pl, "final CTIO complete"); + /* + * The platform layer will destroy the handle if appropriate. + */ + (void) isp_async(isp, ISPASYNC_TARGET_ACTION, ct); + } +} + +static void +isp_handle_ctio2(ispsoftc_t *isp, ct2_entry_t *ct) +{ + XS_T *xs; + int pl = ISP_LOGTDEBUG2; + char *fmsg = NULL; + + if (ct->ct_syshandle) { + xs = isp_find_xs_tgt(isp, ct->ct_syshandle); + if (xs == NULL) + pl = ISP_LOGALL; + } else { + xs = NULL; + } + + switch(ct->ct_status & ~QLTM_SVALID) { + case CT_BUS_ERROR: + isp_prt(isp, ISP_LOGERR, "PCI DMA Bus Error"); + /* FALL Through */ + case CT_DATA_OVER: + case CT_DATA_UNDER: + case CT_OK: + /* + * There are generally 2 possibilities as to why we'd get + * this condition: + * We sent or received data. + * We sent status & command complete. + */ + + break; + + case CT_BDR_MSG: + /* + * Target Reset function received. + * + * The firmware generates an async mailbox interupt to + * notify us of this and returns outstanding CTIOs with this + * status. These CTIOs are handled in that same way as + * CT_ABORTED ones, so just fall through here. + */ + fmsg = "TARGET RESET Task Management Function Received"; + /*FALLTHROUGH*/ + case CT_RESET: + if (fmsg == NULL) + fmsg = "LIP Reset"; + /*FALLTHROUGH*/ + case CT_ABORTED: + /* + * When an Abort message is received the firmware goes to + * Bus Free and returns all outstanding CTIOs with the status + * set, then sends us an Immediate Notify entry. + */ + if (fmsg == NULL) + fmsg = "ABORT Task Management Function Received"; + + isp_prt(isp, ISP_LOGERR, "CTIO2 destroyed by %s: RX_ID=0x%x", + fmsg, ct->ct_rxid); + break; + + case CT_INVAL: + /* + * CTIO rejected by the firmware - invalid data direction. + */ + isp_prt(isp, ISP_LOGERR, "CTIO2 had wrong data direction"); + break; + + case CT_RSELTMO: + fmsg = "failure to reconnect to initiator"; + /*FALLTHROUGH*/ + case CT_TIMEOUT: + if (fmsg == NULL) + fmsg = "command"; + isp_prt(isp, ISP_LOGERR, "Firmware timed out on %s", fmsg); + break; + + case CT_ERR: + fmsg = "Completed with Error"; + /*FALLTHROUGH*/ + case CT_LOGOUT: + if (fmsg == NULL) + fmsg = "Port Logout"; + /*FALLTHROUGH*/ + case CT_PORTNOTAVAIL: + if (fmsg == NULL) + fmsg = "Port not available"; + /*FALLTHROUGH*/ + case CT_PORTCHANGED: + if (fmsg == NULL) + fmsg = "Port Changed"; + /*FALLTHROUGH*/ + case CT_NOACK: + if (fmsg == NULL) + fmsg = "unacknowledged Immediate Notify pending"; + isp_prt(isp, ISP_LOGERR, "CTIO returned by f/w- %s", fmsg); + break; + + case CT_INVRXID: + /* + * CTIO rejected by the firmware because an invalid RX_ID. + * Just print a message. + */ + isp_prt(isp, ISP_LOGERR, + "CTIO2 completed with Invalid RX_ID 0x%x", ct->ct_rxid); + break; + + default: + isp_prt(isp, ISP_LOGERR, "Unknown CTIO2 status 0x%x", + ct->ct_status & ~QLTM_SVALID); + break; + } + + if (xs == NULL) { + /* + * There may be more than one CTIO for a data transfer, + * or this may be a status CTIO we're not monitoring. + * + * The assumption is that they'll all be returned in the + * order we got them. + */ + if (ct->ct_syshandle == 0) { + if ((ct->ct_flags & CT2_SENDSTATUS) == 0) { + isp_prt(isp, pl, + "intermediate CTIO completed ok"); + } else { + isp_prt(isp, pl, + "unmonitored CTIO completed ok"); + } + } else { + isp_prt(isp, pl, + "NO xs for CTIO (handle 0x%x) status 0x%x", + ct->ct_syshandle, ct->ct_status & ~QLTM_SVALID); + } + } else { + if ((ct->ct_flags & CT2_DATAMASK) != CT2_NO_DATA) { + ISP_DMAFREE(isp, xs, ct->ct_syshandle); + } + if (ct->ct_flags & CT2_SENDSTATUS) { + /* + * Sent status and command complete. + * + * We're now really done with this command, so we + * punt to the platform dependent layers because + * only there can we do the appropriate command + * complete thread synchronization. + */ + isp_prt(isp, pl, "status CTIO complete"); + } else { + /* + * Final CTIO completed. Release DMA resources and + * notify platform dependent layers. + */ + isp_prt(isp, pl, "data CTIO complete"); + } + (void) isp_async(isp, ISPASYNC_TARGET_ACTION, ct); + /* + * The platform layer will destroy the handle if appropriate. + */ + } +} +#endif diff --git a/sys/dev/isp/isp_target.h b/sys/dev/isp/isp_target.h new file mode 100644 index 000000000000..be0387760c6d --- /dev/null +++ b/sys/dev/isp/isp_target.h @@ -0,0 +1,703 @@ +/* $FreeBSD$ */ +/*- + * Qlogic Target Mode Structure and Flag Definitions + * + * Copyright (c) 1997, 1998 + * Patrick Stirling + * pms@psconsult.com + * All rights reserved. + * + * Additonal Copyright (c) 1997-2006 by Matthew Jacob + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +#ifndef _ISP_TARGET_H +#define _ISP_TARGET_H + +#define QLTM_SENSELEN 18 /* non-FC cards only */ +#define QLTM_SVALID 0x80 + +/* + * Structure for Enable Lun and Modify Lun queue entries + */ +typedef struct { + isphdr_t le_header; + uint32_t le_reserved; + uint8_t le_lun; + uint8_t le_rsvd; + uint8_t le_ops; /* Modify LUN only */ + uint8_t le_tgt; /* Not for FC */ + uint32_t le_flags; /* Not for FC */ + uint8_t le_status; + uint8_t le_reserved2; + uint8_t le_cmd_count; + uint8_t le_in_count; + uint8_t le_cdb6len; /* Not for FC */ + uint8_t le_cdb7len; /* Not for FC */ + uint16_t le_timeout; + uint16_t le_reserved3[20]; +} lun_entry_t; + +/* + * le_flags values + */ +#define LUN_TQAE 0x00000002 /* bit1 Tagged Queue Action Enable */ +#define LUN_DSSM 0x01000000 /* bit24 Disable Sending SDP Message */ +#define LUN_DISAD 0x02000000 /* bit25 Disable autodisconnect */ +#define LUN_DM 0x40000000 /* bit30 Disconnects Mandatory */ + +/* + * le_ops values + */ +#define LUN_CCINCR 0x01 /* increment command count */ +#define LUN_CCDECR 0x02 /* decrement command count */ +#define LUN_ININCR 0x40 /* increment immed. notify count */ +#define LUN_INDECR 0x80 /* decrement immed. notify count */ + +/* + * le_status values + */ +#define LUN_OK 0x01 /* we be rockin' */ +#define LUN_ERR 0x04 /* request completed with error */ +#define LUN_INVAL 0x06 /* invalid request */ +#define LUN_NOCAP 0x16 /* can't provide requested capability */ +#define LUN_ENABLED 0x3E /* LUN already enabled */ + +/* + * Immediate Notify Entry structure + */ +#define IN_MSGLEN 8 /* 8 bytes */ +#define IN_RSVDLEN 8 /* 8 words */ +typedef struct { + isphdr_t in_header; + uint32_t in_reserved; + uint8_t in_lun; /* lun */ + uint8_t in_iid; /* initiator */ + uint8_t in_reserved2; + uint8_t in_tgt; /* target */ + uint32_t in_flags; + uint8_t in_status; + uint8_t in_rsvd2; + uint8_t in_tag_val; /* tag value */ + uint8_t in_tag_type; /* tag type */ + uint16_t in_seqid; /* sequence id */ + uint8_t in_msg[IN_MSGLEN]; /* SCSI message bytes */ + uint16_t in_reserved3[IN_RSVDLEN]; + uint8_t in_sense[QLTM_SENSELEN];/* suggested sense data */ +} in_entry_t; + +typedef struct { + isphdr_t in_header; + uint32_t in_reserved; + uint8_t in_lun; /* lun */ + uint8_t in_iid; /* initiator */ + uint16_t in_scclun; + uint32_t in_reserved2; + uint16_t in_status; + uint16_t in_task_flags; + uint16_t in_seqid; /* sequence id */ +} in_fcentry_t; + +typedef struct { + isphdr_t in_header; + uint32_t in_reserved; + uint16_t in_iid; /* initiator */ + uint16_t in_scclun; + uint32_t in_reserved2; + uint16_t in_status; + uint16_t in_task_flags; + uint16_t in_seqid; /* sequence id */ +} in_fcentry_e_t; + +/* + * Values for the in_status field + */ +#define IN_REJECT 0x0D /* Message Reject message received */ +#define IN_RESET 0x0E /* Bus Reset occurred */ +#define IN_NO_RCAP 0x16 /* requested capability not available */ +#define IN_IDE_RECEIVED 0x33 /* Initiator Detected Error msg received */ +#define IN_RSRC_UNAVAIL 0x34 /* resource unavailable */ +#define IN_MSG_RECEIVED 0x36 /* SCSI message received */ +#define IN_ABORT_TASK 0x20 /* task named in RX_ID is being aborted (FC) */ +#define IN_PORT_LOGOUT 0x29 /* port has logged out (FC) */ +#define IN_PORT_CHANGED 0x2A /* port changed */ +#define IN_GLOBAL_LOGO 0x2E /* all ports logged out */ +#define IN_NO_NEXUS 0x3B /* Nexus not established */ + +/* + * Values for the in_task_flags field- should only get one at a time! + */ +#define TASK_FLAGS_RESERVED_MASK (0xe700) +#define TASK_FLAGS_CLEAR_ACA (1<<14) +#define TASK_FLAGS_TARGET_RESET (1<<13) +#define TASK_FLAGS_LUN_RESET (1<<12) +#define TASK_FLAGS_CLEAR_TASK_SET (1<<10) +#define TASK_FLAGS_ABORT_TASK_SET (1<<9) + +#ifndef MSG_ABORT +#define MSG_ABORT 0x06 +#endif +#ifndef MSG_BUS_DEV_RESET +#define MSG_BUS_DEV_RESET 0x0c +#endif +#ifndef MSG_ABORT_TAG +#define MSG_ABORT_TAG 0x0d +#endif +#ifndef MSG_CLEAR_QUEUE +#define MSG_CLEAR_QUEUE 0x0e +#endif +#ifndef MSG_REL_RECOVERY +#define MSG_REL_RECOVERY 0x10 +#endif +#ifndef MSG_TERM_IO_PROC +#define MSG_TERM_IO_PROC 0x11 +#endif +#ifndef MSG_LUN_RESET +#define MSG_LUN_RESET 0x17 +#endif + +/* + * Notify Acknowledge Entry structure + */ +#define NA_RSVDLEN 22 +typedef struct { + isphdr_t na_header; + uint32_t na_reserved; + uint8_t na_lun; /* lun */ + uint8_t na_iid; /* initiator */ + uint8_t na_reserved2; + uint8_t na_tgt; /* target */ + uint32_t na_flags; + uint8_t na_status; + uint8_t na_event; + uint16_t na_seqid; /* sequence id */ + uint16_t na_reserved3[NA_RSVDLEN]; +} na_entry_t; + +/* + * Value for the na_event field + */ +#define NA_RST_CLRD 0x80 /* Clear an async event notification */ +#define NA_OK 0x01 /* Notify Acknowledge Succeeded */ +#define NA_INVALID 0x06 /* Invalid Notify Acknowledge */ + +#define NA2_RSVDLEN 21 +typedef struct { + isphdr_t na_header; + uint32_t na_reserved; + uint8_t na_reserved1; + uint8_t na_iid; /* initiator loop id */ + uint16_t na_response; + uint16_t na_flags; + uint16_t na_reserved2; + uint16_t na_status; + uint16_t na_task_flags; + uint16_t na_seqid; /* sequence id */ + uint16_t na_reserved3[NA2_RSVDLEN]; +} na_fcentry_t; + +typedef struct { + isphdr_t na_header; + uint32_t na_reserved; + uint16_t na_iid; /* initiator loop id */ + uint16_t na_response; /* response code */ + uint16_t na_flags; + uint16_t na_reserved2; + uint16_t na_status; + uint16_t na_task_flags; + uint16_t na_seqid; /* sequence id */ + uint16_t na_reserved3[NA2_RSVDLEN]; +} na_fcentry_e_t; + +#define NAFC_RCOUNT 0x80 /* increment resource count */ +#define NAFC_RST_CLRD 0x20 /* Clear LIP Reset */ +#define NAFC_TVALID 0x10 /* task mangement response code is valid */ + +/* + * Accept Target I/O Entry structure + */ +#define ATIO_CDBLEN 26 + +typedef struct { + isphdr_t at_header; + uint16_t at_reserved; + uint16_t at_handle; + uint8_t at_lun; /* lun */ + uint8_t at_iid; /* initiator */ + uint8_t at_cdblen; /* cdb length */ + uint8_t at_tgt; /* target */ + uint32_t at_flags; + uint8_t at_status; /* firmware status */ + uint8_t at_scsi_status; /* scsi status */ + uint8_t at_tag_val; /* tag value */ + uint8_t at_tag_type; /* tag type */ + uint8_t at_cdb[ATIO_CDBLEN]; /* received CDB */ + uint8_t at_sense[QLTM_SENSELEN];/* suggested sense data */ +} at_entry_t; + +/* + * at_flags values + */ +#define AT_NODISC 0x00008000 /* disconnect disabled */ +#define AT_TQAE 0x00000002 /* Tagged Queue Action enabled */ + +/* + * at_status values + */ +#define AT_PATH_INVALID 0x07 /* ATIO sent to firmware for disabled lun */ +#define AT_RESET 0x0E /* SCSI Bus Reset Occurred */ +#define AT_PHASE_ERROR 0x14 /* Bus phase sequence error */ +#define AT_NOCAP 0x16 /* Requested capability not available */ +#define AT_BDR_MSG 0x17 /* Bus Device Reset msg received */ +#define AT_CDB 0x3D /* CDB received */ +/* + * Macros to create and fetch and test concatenated handle and tag value macros + */ + +#define AT_MAKE_TAGID(tid, inst, aep) \ + tid = aep->at_handle; \ + if (aep->at_flags & AT_TQAE) { \ + tid |= (aep->at_tag_val << 16); \ + tid |= (1 << 24); \ + } \ + tid |= (GET_BUS_VAL(aep->at_iid) << 25); \ + tid |= (inst << 26) + +#define CT_MAKE_TAGID(tid, bus, inst, ct) \ + tid = ct->ct_fwhandle; \ + if (ct->ct_flags & CT_TQAE) { \ + tid |= (ct->ct_tag_val << 16); \ + tid |= (1 << 24); \ + } \ + tid |= ((bus & 0x1) << 25); \ + tid |= (inst << 26) + +#define AT_HAS_TAG(val) ((val) & (1 << 24)) +#define AT_GET_TAG(val) (((val) >> 16) & 0xff) +#define AT_GET_INST(val) (((val) >> 26) & 0x3f) +#define AT_GET_BUS(val) (((val) >> 25) & 0x1) +#define AT_GET_HANDLE(val) ((val) & 0xffff) + +#define IN_MAKE_TAGID(tid, inst, inp) \ + tid = inp->in_seqid; \ + tid |= (inp->in_tag_val << 16); \ + tid |= (1 << 24); \ + tid |= (GET_BUS_VAL(inp->in_iid) << 25); \ + tid |= (inst << 26) + +#define TAG_INSERT_INST(tid, inst) \ + tid &= ~(0x3ffffff); \ + tid |= (inst << 26) + +#define TAG_INSERT_BUS(tid, bus) \ + tid &= ~(1 << 25); \ + tid |= (bus << 25) + +/* + * Accept Target I/O Entry structure, Type 2 + */ +#define ATIO2_CDBLEN 16 + +typedef struct { + isphdr_t at_header; + uint32_t at_reserved; + uint8_t at_lun; /* lun or reserved */ + uint8_t at_iid; /* initiator */ + uint16_t at_rxid; /* response ID */ + uint16_t at_flags; + uint16_t at_status; /* firmware status */ + uint8_t at_crn; /* command reference number */ + uint8_t at_taskcodes; + uint8_t at_taskflags; + uint8_t at_execodes; + uint8_t at_cdb[ATIO2_CDBLEN]; /* received CDB */ + uint32_t at_datalen; /* allocated data len */ + uint16_t at_scclun; /* SCC Lun or reserved */ + uint16_t at_wwpn[4]; /* WWPN of initiator */ + uint16_t at_reserved2[6]; + uint16_t at_oxid; +} at2_entry_t; + +typedef struct { + isphdr_t at_header; + uint32_t at_reserved; + uint16_t at_iid; /* initiator */ + uint16_t at_rxid; /* response ID */ + uint16_t at_flags; + uint16_t at_status; /* firmware status */ + uint8_t at_crn; /* command reference number */ + uint8_t at_taskcodes; + uint8_t at_taskflags; + uint8_t at_execodes; + uint8_t at_cdb[ATIO2_CDBLEN]; /* received CDB */ + uint32_t at_datalen; /* allocated data len */ + uint16_t at_scclun; /* SCC Lun or reserved */ + uint16_t at_wwpn[4]; /* WWPN of initiator */ + uint16_t at_reserved2[6]; + uint16_t at_oxid; +} at2e_entry_t; + +#define ATIO2_WWPN_OFFSET 0x2A +#define ATIO2_OXID_OFFSET 0x3E + +#define ATIO2_TC_ATTR_MASK 0x7 +#define ATIO2_TC_ATTR_SIMPLEQ 0 +#define ATIO2_TC_ATTR_HEADOFQ 1 +#define ATIO2_TC_ATTR_ORDERED 2 +#define ATIO2_TC_ATTR_ACAQ 4 +#define ATIO2_TC_ATTR_UNTAGGED 5 + +#define ATIO2_EX_WRITE 0x1 +#define ATIO2_EX_READ 0x2 +/* + * Macros to create and fetch and test concatenated handle and tag value macros + */ +#define AT2_MAKE_TAGID(tid, inst, aep) \ + tid = aep->at_rxid; \ + tid |= (inst << 16) + +#define CT2_MAKE_TAGID(tid, inst, ct) \ + tid = ct->ct_rxid; \ + tid |= (inst << 16) + +#define AT2_HAS_TAG(val) 1 +#define AT2_GET_TAG(val) ((val) & 0xffff) +#define AT2_GET_INST(val) ((val) >> 16) +#define AT2_GET_HANDLE AT2_GET_TAG + +#define FC_HAS_TAG AT2_HAS_TAG +#define FC_GET_TAG AT2_GET_TAG +#define FC_GET_INST AT2_GET_INST +#define FC_GET_HANDLE AT2_GET_HANDLE + +#define IN_FC_MAKE_TAGID(tid, inst, seqid) \ + tid = seqid; \ + tid |= (inst << 16) + +#define FC_TAG_INSERT_INST(tid, inst) \ + tid &= ~0xffff; \ + tid |= (inst << 16) + + +/* + * Continue Target I/O Entry structure + * Request from driver. The response from the + * ISP firmware is the same except that the last 18 + * bytes are overwritten by suggested sense data if + * the 'autosense valid' bit is set in the status byte. + */ +typedef struct { + isphdr_t ct_header; + uint16_t ct_reserved; +#define ct_syshandle ct_reserved /* we use this */ + uint16_t ct_fwhandle; /* required by f/w */ + uint8_t ct_lun; /* lun */ + uint8_t ct_iid; /* initiator id */ + uint8_t ct_reserved2; + uint8_t ct_tgt; /* our target id */ + uint32_t ct_flags; + uint8_t ct_status; /* isp status */ + uint8_t ct_scsi_status; /* scsi status */ + uint8_t ct_tag_val; /* tag value */ + uint8_t ct_tag_type; /* tag type */ + uint32_t ct_xfrlen; /* transfer length */ + uint32_t ct_resid; /* residual length */ + uint16_t ct_timeout; + uint16_t ct_seg_count; + /* + * This is so we can share tag name space with + * CTIO{2,3,4} with the minimum of pain. + */ + union { + ispds_t ct_a[ISP_RQDSEG]; + } _u; +#define ct_dataseg _u.ct_a +} ct_entry_t; + +/* + * For some of the dual port SCSI adapters, port (bus #) is reported + * in the MSbit of ct_iid. Bit fields are a bit too awkward here. + * + * Note that this does not apply to FC adapters at all which can and + * do report IIDs between 0x81 && 0xfe (or 0x7ff) which represent devices + * that have logged in across a SCSI fabric. + */ +#define GET_IID_VAL(x) (x & 0x3f) +#define GET_BUS_VAL(x) ((x >> 7) & 0x1) +#define SET_IID_VAL(y, x) y = ((y & ~0x3f) | (x & 0x3f)) +#define SET_BUS_VAL(y, x) y = ((y & 0x3f) | ((x & 0x1) << 7)) + +/* + * ct_flags values + */ +#define CT_TQAE 0x00000002 /* bit 1, Tagged Queue Action enable */ +#define CT_DATA_IN 0x00000040 /* bits 6&7, Data direction */ +#define CT_DATA_OUT 0x00000080 /* bits 6&7, Data direction */ +#define CT_NO_DATA 0x000000C0 /* bits 6&7, Data direction */ +#define CT_CCINCR 0x00000100 /* bit 8, autoincrement atio count */ +#define CT_DATAMASK 0x000000C0 /* bits 6&7, Data direction */ +#define CT_INISYNCWIDE 0x00004000 /* bit 14, Do Sync/Wide Negotiation */ +#define CT_NODISC 0x00008000 /* bit 15, Disconnects disabled */ +#define CT_DSDP 0x01000000 /* bit 24, Disable Save Data Pointers */ +#define CT_SENDRDP 0x04000000 /* bit 26, Send Restore Pointers msg */ +#define CT_SENDSTATUS 0x80000000 /* bit 31, Send SCSI status byte */ + +/* + * ct_status values + * - set by the firmware when it returns the CTIO + */ +#define CT_OK 0x01 /* completed without error */ +#define CT_ABORTED 0x02 /* aborted by host */ +#define CT_ERR 0x04 /* see sense data for error */ +#define CT_INVAL 0x06 /* request for disabled lun */ +#define CT_NOPATH 0x07 /* invalid ITL nexus */ +#define CT_INVRXID 0x08 /* (FC only) Invalid RX_ID */ +#define CT_DATA_OVER 0x09 /* (FC only) Data Overrun */ +#define CT_RSELTMO 0x0A /* reselection timeout after 2 tries */ +#define CT_TIMEOUT 0x0B /* timed out */ +#define CT_RESET 0x0E /* SCSI Bus Reset occurred */ +#define CT_PARITY 0x0F /* Uncorrectable Parity Error */ +#define CT_BUS_ERROR 0x10 /* (FC Only) DMA PCI Error */ +#define CT_PANIC 0x13 /* Unrecoverable Error */ +#define CT_PHASE_ERROR 0x14 /* Bus phase sequence error */ +#define CT_BDR_MSG 0x17 /* Bus Device Reset msg received */ +#define CT_DATA_UNDER 0x15 /* (FC only) Data Underrun */ +#define CT_TERMINATED 0x19 /* due to Terminate Transfer mbox cmd */ +#define CT_PORTNOTAVAIL 0x28 /* port not available */ +#define CT_LOGOUT 0x29 /* port logout */ +#define CT_PORTCHANGED 0x2A /* port changed */ +#define CT_IDE 0x33 /* Initiator Detected Error */ +#define CT_NOACK 0x35 /* Outstanding Immed. Notify. entry */ +#define CT_SRR 0x45 /* SRR Received */ +#define CT_LUN_RESET 0x48 /* Lun Reset Received */ + +/* + * When the firmware returns a CTIO entry, it may overwrite the last + * part of the structure with sense data. This starts at offset 0x2E + * into the entry, which is in the middle of ct_dataseg[1]. Rather + * than define a new struct for this, I'm just using the sense data + * offset. + */ +#define CTIO_SENSE_OFFSET 0x2E + +/* + * Entry length in u_longs. All entries are the same size so + * any one will do as the numerator. + */ +#define UINT32_ENTRY_SIZE (sizeof(at_entry_t)/sizeof(uint32_t)) + +/* + * QLA2100 CTIO (type 2) entry + */ +#define MAXRESPLEN 26 +typedef struct { + isphdr_t ct_header; + uint16_t ct_reserved; + uint16_t ct_fwhandle; /* just to match CTIO */ + uint8_t ct_lun; /* lun */ + uint8_t ct_iid; /* initiator id */ + uint16_t ct_rxid; /* response ID */ + uint16_t ct_flags; + uint16_t ct_status; /* isp status */ + uint16_t ct_timeout; + uint16_t ct_seg_count; + uint32_t ct_reloff; /* relative offset */ + int32_t ct_resid; /* residual length */ + union { + /* + * The three different modes that the target driver + * can set the CTIO{2,3,4} up as. + * + * The first is for sending FCP_DATA_IUs as well as + * (optionally) sending a terminal SCSI status FCP_RSP_IU. + * + * The second is for sending SCSI sense data in an FCP_RSP_IU. + * Note that no FCP_DATA_IUs will be sent. + * + * The third is for sending FCP_RSP_IUs as built specifically + * in system memory as located by the isp_dataseg. + */ + struct { + uint32_t _reserved; + uint16_t _reserved2; + uint16_t ct_scsi_status; + uint32_t ct_xfrlen; + union { + ispds_t ct_a[ISP_RQDSEG_T2]; /* CTIO2 */ + ispds64_t ct_b[ISP_RQDSEG_T3]; /* CTIO3 */ + ispdslist_t ct_c; /* CTIO4 */ + } _u; +#define ct_dataseg _u.ct_a +#define ct_dataseg64 _u.ct_b +#define ct_dslist _u.ct_c + } m0; + struct { + uint16_t _reserved; + uint16_t _reserved2; + uint16_t ct_senselen; + uint16_t ct_scsi_status; + uint16_t ct_resplen; + uint8_t ct_resp[MAXRESPLEN]; + } m1; + struct { + uint32_t _reserved; + uint16_t _reserved2; + uint16_t _reserved3; + uint32_t ct_datalen; + ispds_t ct_fcp_rsp_iudata; + } m2; + } rsp; +} ct2_entry_t; + +typedef struct { + isphdr_t ct_header; + uint16_t ct_reserved; + uint16_t ct_fwhandle; /* just to match CTIO */ + uint16_t ct_iid; /* initiator id */ + uint16_t ct_rxid; /* response ID */ + uint16_t ct_flags; + uint16_t ct_status; /* isp status */ + uint16_t ct_timeout; + uint16_t ct_seg_count; + uint32_t ct_reloff; /* relative offset */ + int32_t ct_resid; /* residual length */ + union { + struct { + uint32_t _reserved; + uint16_t _reserved2; + uint16_t ct_scsi_status; + uint32_t ct_xfrlen; + union { + ispds_t ct_a[ISP_RQDSEG_T2]; /* CTIO2 */ + ispds64_t ct_b[ISP_RQDSEG_T3]; /* CTIO3 */ + ispdslist_t ct_c; /* CTIO4 */ + } _u; + } m0; + struct { + uint16_t _reserved; + uint16_t _reserved2; + uint16_t ct_senselen; + uint16_t ct_scsi_status; + uint16_t ct_resplen; + uint8_t ct_resp[MAXRESPLEN]; + } m1; + struct { + uint32_t _reserved; + uint16_t _reserved2; + uint16_t _reserved3; + uint32_t ct_datalen; + ispds_t ct_fcp_rsp_iudata; + } m2; + } rsp; +} ct2e_entry_t; + +/* + * ct_flags values for CTIO2 + */ +#define CT2_FLAG_MMASK 0x0003 +#define CT2_FLAG_MODE0 0x0000 +#define CT2_FLAG_MODE1 0x0001 +#define CT2_FLAG_MODE2 0x0002 +#define CT2_DATA_IN CT_DATA_IN +#define CT2_DATA_OUT CT_DATA_OUT +#define CT2_NO_DATA CT_NO_DATA +#define CT2_DATAMASK CT_DATAMASK +#define CT2_CCINCR 0x0100 +#define CT2_FASTPOST 0x0200 +#define CT2_TERMINATE 0x4000 +#define CT2_SENDSTATUS 0x8000 + +/* + * ct_status values are (mostly) the same as that for ct_entry. + */ + +/* + * ct_scsi_status values- the low 8 bits are the normal SCSI status + * we know and love. The upper 8 bits are validity markers for FCP_RSP_IU + * fields. + */ +#define CT2_RSPLEN_VALID 0x0100 +#define CT2_SNSLEN_VALID 0x0200 +#define CT2_DATA_OVER 0x0400 +#define CT2_DATA_UNDER 0x0800 + +/* + * Debug macros + */ + +#define ISP_TDQE(isp, msg, idx, arg) \ + if (isp->isp_dblev & ISP_LOGTDEBUG2) isp_print_qentry(isp, msg, idx, arg) + +/* + * The functions below are for the publicly available + * target mode functions that are internal to the Qlogic driver. + */ + +/* + * This function handles new response queue entry appropriate for target mode. + */ +int isp_target_notify(ispsoftc_t *, void *, uint16_t *); + +/* + * This function externalizes the ability to acknowledge an Immediate Notify + * request. + */ +void isp_notify_ack(ispsoftc_t *, void *); + +/* + * Enable/Disable/Modify a logical unit. + * (softc, cmd, bus, tgt, lun, cmd_cnt, inotify_cnt, opaque) + */ +#define DFLT_CMND_CNT 0xfe /* unmonitored */ +#define DFLT_INOT_CNT 0xfe /* unmonitored */ +int isp_lun_cmd(ispsoftc_t *, int, int, int, int, int, int, uint32_t); + +/* + * General request queue 'put' routine for target mode entries. + */ +int isp_target_put_entry(ispsoftc_t *isp, void *); + +/* + * General routine to put back an ATIO entry- + * used for replenishing f/w resource counts. + * The argument is a pointer to a source ATIO + * or ATIO2. + */ +int isp_target_put_atio(ispsoftc_t *, void *); + +/* + * General routine to send a final CTIO for a command- used mostly for + * local responses. + */ +int isp_endcmd(ispsoftc_t *, void *, uint32_t, uint16_t); +#define ECMD_SVALID 0x100 + +/* + * Handle an asynchronous event + * + * Return nonzero if the interrupt that generated this event has been dismissed. + */ +int isp_target_async(ispsoftc_t *, int, int); + +#endif /* _ISP_TARGET_H */ diff --git a/sys/dev/isp/isp_tpublic.h b/sys/dev/isp/isp_tpublic.h new file mode 100644 index 000000000000..d1a0b3d2ca67 --- /dev/null +++ b/sys/dev/isp/isp_tpublic.h @@ -0,0 +1,372 @@ +/* $FreeBSD$ */ +/*- + * Qlogic ISP Host Adapter Public Target Interface Structures && Routines + * + * Copyright (c) 1997-2006 by Matthew Jacob + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Host Adapter Public Target Interface Structures && Routines + */ + +#ifndef _ISP_TPUBLIC_H +#define _ISP_TPUBLIC_H 1 + +/* + * Action codes set by the MD target driver for + * the external layer to figure out what to do with. + */ +typedef enum { + QOUT_HBA_REG=0, /* the argument is a pointer to a hba_register_t */ + QOUT_ENABLE, /* the argument is a pointer to a enadis_t */ + QOUT_DISABLE, /* the argument is a pointer to a enadis_t */ + QOUT_TMD_START, /* the argument is a pointer to a tmd_cmd_t */ + QOUT_TMD_DONE, /* the argument is a pointer to a tmd_cmd_t */ + QOUT_NOTIFY, /* the argument is a pointer to a tmd_notify_t */ + QOUT_HBA_UNREG /* the argument is a pointer to a hba_register_t */ +} tact_e; + +/* + * Action codes set by the external layer for the + * MD driver to figure out what to do with. + */ +typedef enum { + QIN_HBA_REG=99, /* the argument is a pointer to a hba_register_t */ + QIN_ENABLE, /* the argument is a pointer to a enadis_t */ + QIN_DISABLE, /* the argument is a pointer to a enadis_t */ + QIN_TMD_CONT, /* the argument is a pointer to a tmd_cmd_t */ + QIN_TMD_FIN, /* the argument is a pointer to a tmd_cmd_t */ + QIN_NOTIFY_ACK, /* the argument is a pointer to a tmd_notify_t */ + QIN_HBA_UNREG, /* the argument is a pointer to a hba_register_t */ +} qact_e; + +/* + * This structure is used to register to other software modules the + * binding of an HBA identifier, driver name and instance and the + * lun width capapbilities of this target driver. It's up to each + * platform to figure out how it wants to do this, but a typical + * sequence would be for the MD layer to find some external module's + * entry point and start by sending a QOUT_HBA_REG with info filled + * in, and the external module to call back with a QIN_HBA_REG that + * passes back the corresponding information. + */ +#define QR_VERSION 10 +typedef struct { + void * r_identity; + void (*r_action)(qact_e, void *); + char r_name[8]; + int r_inst; + int r_version; + enum { R_FC, R_SCSI } r_type; +} hba_register_t; + +/* + * Notify structure + */ +typedef enum { + NT_ABORT_TASK=0x1000, + NT_ABORT_TASK_SET, + NT_CLEAR_ACA, + NT_CLEAR_TASK_SET, + NT_LUN_RESET, + NT_TARGET_RESET, + NT_BUS_RESET, + NT_LIP_RESET, + NT_LINK_UP, + NT_LINK_DOWN, + NT_LOGOUT, + NT_HBA_RESET +} tmd_ncode_t; + +typedef struct tmd_notify { + void * nt_hba; /* HBA tag */ + uint64_t nt_iid; /* inititator id */ + uint64_t nt_tgt; /* target id */ + uint16_t nt_lun; /* logical unit */ + uint16_t nt_padding; /* padding */ + uint32_t nt_tagval; /* tag value */ + tmd_ncode_t nt_ncode; /* action */ + void * nt_lreserved; + void * nt_hreserved; +} tmd_notify_t; +#define LUN_ANY 0xffff +#define TGT_ANY ((uint64_t) -1) +#define INI_ANY ((uint64_t) -1) +#define TAG_ANY 0 +#define MATCH_TMD(tmd, iid, lun, tag) \ + ( \ + (tmd) && \ + (iid == INI_ANY || iid == tmd->cd_iid) && \ + (lun == LUN_ANY || lun == tmd->cd_lun) && \ + (tag == TAG_ANY || tag == tmd->cd_tagval) \ + ) + +/* + * A word about ENABLE/DISABLE: the argument is a pointer to a enadis_t + * with en_hba, en_iid, en_chan, en_tgt and en_lun filled out. + * + * If an error occurs in either enabling or disabling the described lun + * cd_error is set with an appropriate non-zero value. + */ +typedef struct { + void * en_private; /* for outer layer usage */ + void * en_hba; /* HBA tag */ + uint64_t en_iid; /* initiator ID */ + uint64_t en_tgt; /* target id */ + uint16_t en_lun; /* logical unit */ + uint16_t en_chan; /* channel on card */ + int en_error; +} enadis_t; + +/* + * Suggested Software Target Mode Command Handling structure. + * + * A note about terminology: + * + * MD stands for "Machine Dependent". + * + * This driver is structured in three layers: Outer MD, core, and inner MD. + * The latter also is bus dependent (i.e., is cognizant of PCI bus issues + * as well as platform issues). + * + * + * "Outer Layer" means "Other Module" + * + * Some additional module that actually implements SCSI target command + * policy is the recipient of incoming commands and the source of the + * disposition for them. + * + * The command structure below is one suggested possible MD command structure, + * but since the handling of thbis is entirely in the MD layer, there is + * no explicit or implicit requirement that it be used. + * + * The cd_private tag should be used by the MD layer to keep a free list + * of these structures. Code outside of this driver can then use this + * to identify it's own unit structures. That is, when not on the MD + * layer's freelist, the MD layer should shove into it the identifier + * that the outer layer has for it- passed in on an initial QIN_HBA_REG + * call (see below). + * + * The cd_hba tag is a tag that uniquely identifies the HBA this target + * mode command is coming from. The outer layer has to pass this back + * unchanged to avoid chaos. + * + * The cd_iid, cd_tgt, cd_lun and cd_port tags are used to identify the + * id of the initiator who sent us a command, the target claim to be, the + * lun on the target we claim to be, and the port instance (for multiple + * port host adapters) that this applies to (consider it an extra port + * parameter). The iid, tgt and lun values are deliberately chosen to be + * fat so that, for example, World Wide Names can be used instead of + * the units that the firmware uses (in the case where the MD + * layer maintains a port database, for example). + * + * The cd_tagtype field specifies what kind of command tag type, if + * any, has been sent with the command. Note that the Outer Layer + * still needs to pass the tag handle through unchanged even + * if the tag type is CD_UNTAGGED. + * + * The cd_cdb contains storage for the passed in command descriptor block. + * There is no need to define length as the callee should be able to + * figure this out. + * + * The tag cd_lflags are the flags set by the MD driver when it gets + * command incoming or when it needs to inform any outside entities + * that the last requested action failed. + * + * The tag cd_hflags should be set by any outside software to indicate + * the validity of sense and status fields (defined below) and to indicate + * the direction data is expected to move. It is an error to have both + * CDFH_DATA_IN and CDFH_DATA_OUT set. + * + * If the CDFH_STSVALID flag is set, the command should be completed (after + * sending any data and/or status). If CDFH_SNSVALID is set and the MD layer + * can also handle sending the associated sense data (either back with an + * FCP RESPONSE IU for Fibre Channel or otherwise automatically handling a + * REQUEST SENSE from the initator for this target/lun), the MD layer will + * set the CDFL_SENTSENSE flag on successful transmission of the sense data. + * It is an error for the CDFH_SNSVALID bit to be set and CDFH_STSVALID not + * to be set. It is an error for the CDFH_SNSVALID be set and the associated + * SCSI status (cd_scsi_status) not be set to CHECK CONDITON. + * + * The tag cd_data points to a data segment to either be filled or + * read from depending on the direction of data movement. The tag + * is undefined if no data direction is set. The MD layer and outer + * layers must agree on the meaning of cd_data and it is specifically + * not defined here. + * + * The tag cd_totlen is the total data amount expected to be moved + * over the life of the command. It may be set by the MD layer, possibly + * from the datalen field of an FCP CMND IU unit. If it shows up in the outer + * layers set to zero and the CDB indicates data should be moved, the outer + * layer should set it to the amount expected to be moved. + * + * The tag cd_resid should be the total residual of data not transferred. + * The outer layers need to set this at the begining of command processing + * to equal cd_totlen. As data is successfully moved, this value is decreased. + * At the end of a command, any nonzero residual indicates the number of bytes + * requested by the command but not moved. + * + * The tag cd_xfrlen is the length of the currently active data transfer. + * This allows several interations between any outside software and the + * MD layer to move data. + * + * The reason that total length and total residual have to be tracked + * is to keep track of relative offset. + * + * The tags cd_sense and cd_scsi_status are pretty obvious. + * + * The tag cd_error is to communicate between the MD layer and outer software + * the current error conditions. + * + * The tag cd_lreserved, cd_hreserved are scratch areas for use for the MD + * and outer layers respectively. + * + */ + +#ifndef TMD_CDBLEN +#define TMD_CDBLEN 16 +#endif +#ifndef TMD_SENSELEN +#define TMD_SENSELEN 18 +#endif +#ifndef QCDS +#define QCDS 8 +#endif + +typedef struct tmd_cmd { + void * cd_private; /* private data pointer */ + void * cd_hba; /* HBA tag */ + void * cd_data; /* 'pointer' to data */ + uint64_t cd_iid; /* initiator ID */ + uint64_t cd_tgt; /* target id */ + uint64_t cd_lun; /* logical unit */ + uint32_t cd_tagval; /* tag value */ + uint32_t cd_lflags; /* flags lower level sets */ + uint32_t cd_hflags; /* flags higher level sets */ + uint32_t cd_totlen; /* total data load */ + uint32_t cd_resid; /* total data residual */ + uint32_t cd_xfrlen; /* current data load */ + int32_t cd_error; /* current error */ + uint8_t cd_tagtype : 4, + cd_port : 4; /* port number on HBA */ + uint8_t cd_scsi_status; + uint8_t cd_sense[TMD_SENSELEN]; + uint8_t cd_cdb[TMD_CDBLEN]; + union { + void * ptrs[QCDS / sizeof (void *)]; + uint64_t llongs[QCDS / sizeof (uint64_t)]; + uint32_t longs[QCDS / sizeof (uint32_t)]; + uint16_t shorts[QCDS / sizeof (uint16_t)]; + uint8_t bytes[QCDS]; + } cd_lreserved[3], cd_hreserved[3]; +} tmd_cmd_t; + +/* defined tags */ +#define CD_UNTAGGED 0 +#define CD_SIMPLE_TAG 1 +#define CD_ORDERED_TAG 2 +#define CD_HEAD_TAG 3 +#define CD_ACA_TAG 4 + +#ifndef TMD_SIZE +#define TMD_SIZE (sizeof (tmd_cmd_t)) +#endif + +/* + * Note that NODISC (obviously) doesn't apply to non-SPI transport. + * + * Note that knowing the data direction and lengh at the time of receipt of + * a command from the initiator is a feature only of Fibre Channel. + * + * The CDFL_BIDIR is in anticipation of the adoption of some newer + * features required by OSD. + * + * The principle selector for MD layer to know whether data is to + * be transferred in any QOUT_TMD_CONT call is cd_xfrlen- the + * flags CDFH_DATA_IN and CDFH_DATA_OUT define which direction. + */ +#define CDFL_SNSVALID 0x01 /* sense data (from f/w) good */ +#define CDFL_SENTSTATUS 0x02 /* last action sent status */ +#define CDFL_DATA_IN 0x04 /* target (us) -> initiator (them) */ +#define CDFL_DATA_OUT 0x08 /* initiator (them) -> target (us) */ +#define CDFL_BIDIR 0x0C /* bidirectional data */ +#define CDFL_ERROR 0x10 /* last action ended in error */ +#define CDFL_NODISC 0x20 /* disconnects disabled */ +#define CDFL_SENTSENSE 0x40 /* last action sent sense data */ +#define CDFL_BUSY 0x80 /* this command is not on a free list */ +#define CDFL_PRIVATE 0xFF000000 /* private layer flags */ + +#define CDFH_SNSVALID 0x01 /* sense data (from outer layer) good */ +#define CDFH_STSVALID 0x02 /* status valid */ +#define CDFH_DATA_IN 0x04 /* target (us) -> initiator (them) */ +#define CDFH_DATA_OUT 0x08 /* initiator (them) -> target (us) */ +#define CDFH_DATA_MASK 0x0C /* mask to cover data direction */ +#define CDFH_PRIVATE 0xFF000000 /* private layer flags */ + + +/* + * A word about the START/CONT/DONE/FIN dance: + * + * When the HBA is enabled for receiving commands, one may show up + * without notice. When that happens, the MD target mode driver + * gets a tmd_cmd_t, fills it with the info that just arrived, and + * calls the outer layer with a QOUT_TMD_START code and pointer to + * the tmd_cmd_t. + * + * The outer layer decodes the command, fetches data, prepares stuff, + * whatever, and starts by passing back the pointer with a QIN_TMD_CONT + * code which causes the MD target mode driver to generate CTIOs to + * satisfy whatever action needs to be taken. When those CTIOs complete, + * the MD target driver sends the pointer to the cmd_tmd_t back with + * a QOUT_TMD_DONE code. This repeats for as long as necessary. These + * may not be done in parallel- they are sequential operations. + * + * The outer layer signals it wants to end the command by settings within + * the tmd_cmd_t itself. When the final QIN_TMD_CONT is reported completed, + * the outer layer frees the tmd_cmd_t by sending the pointer to it + * back with a QIN_TMD_FIN code. + * + * The graph looks like: + * + * QOUT_TMD_START -> [ QIN_TMD_CONT -> QOUT_TMD_DONE ] * -> QIN_TMD_FIN. + * + */ + +/* + * Target handler functions. + * + * The MD target handler function (the outer layer calls this) + * should be be prototyped like: + * + * void target_action(qact_e, void *arg) + * + * The outer layer target handler function (the MD layer calls this) + * should be be prototyped like: + * + * void scsi_target_handler(tact_e, void *arg) + */ +#endif /* _ISP_TPUBLIC_H */ +/* + * vim:ts=4:sw=4:expandtab + */ diff --git a/sys/dev/isp/ispmbox.h b/sys/dev/isp/ispmbox.h new file mode 100644 index 000000000000..2ef8d51e42d7 --- /dev/null +++ b/sys/dev/isp/ispmbox.h @@ -0,0 +1,986 @@ +/* $FreeBSD$ */ +/*- + * Mailbox and Queue Entry Definitions for for Qlogic ISP SCSI adapters. + * + * Copyright (c) 1997-2006 by Matthew Jacob + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +#ifndef _ISPMBOX_H +#define _ISPMBOX_H + +/* + * Mailbox Command Opcodes + */ +#define MBOX_NO_OP 0x0000 +#define MBOX_LOAD_RAM 0x0001 +#define MBOX_EXEC_FIRMWARE 0x0002 +#define MBOX_DUMP_RAM 0x0003 +#define MBOX_WRITE_RAM_WORD 0x0004 +#define MBOX_READ_RAM_WORD 0x0005 +#define MBOX_MAILBOX_REG_TEST 0x0006 +#define MBOX_VERIFY_CHECKSUM 0x0007 +#define MBOX_ABOUT_FIRMWARE 0x0008 + /* 9 */ + /* a */ + /* b */ + /* c */ +#define MBOX_WRITE_RAM_WORD_EXTENDED 0x000d +#define MBOX_CHECK_FIRMWARE 0x000e +#define MBOX_READ_RAM_WORD_EXTENDED 0x000f +#define MBOX_INIT_REQ_QUEUE 0x0010 +#define MBOX_INIT_RES_QUEUE 0x0011 +#define MBOX_EXECUTE_IOCB 0x0012 +#define MBOX_WAKE_UP 0x0013 +#define MBOX_STOP_FIRMWARE 0x0014 +#define MBOX_ABORT 0x0015 +#define MBOX_ABORT_DEVICE 0x0016 +#define MBOX_ABORT_TARGET 0x0017 +#define MBOX_BUS_RESET 0x0018 +#define MBOX_STOP_QUEUE 0x0019 +#define MBOX_START_QUEUE 0x001a +#define MBOX_SINGLE_STEP_QUEUE 0x001b +#define MBOX_ABORT_QUEUE 0x001c +#define MBOX_GET_DEV_QUEUE_STATUS 0x001d + /* 1e */ +#define MBOX_GET_FIRMWARE_STATUS 0x001f +#define MBOX_GET_INIT_SCSI_ID 0x0020 +#define MBOX_GET_SELECT_TIMEOUT 0x0021 +#define MBOX_GET_RETRY_COUNT 0x0022 +#define MBOX_GET_TAG_AGE_LIMIT 0x0023 +#define MBOX_GET_CLOCK_RATE 0x0024 +#define MBOX_GET_ACT_NEG_STATE 0x0025 +#define MBOX_GET_ASYNC_DATA_SETUP_TIME 0x0026 +#define MBOX_GET_SBUS_PARAMS 0x0027 +#define MBOX_GET_PCI_PARAMS MBOX_GET_SBUS_PARAMS +#define MBOX_GET_TARGET_PARAMS 0x0028 +#define MBOX_GET_DEV_QUEUE_PARAMS 0x0029 +#define MBOX_GET_RESET_DELAY_PARAMS 0x002a + /* 2b */ + /* 2c */ + /* 2d */ + /* 2e */ + /* 2f */ +#define MBOX_SET_INIT_SCSI_ID 0x0030 +#define MBOX_SET_SELECT_TIMEOUT 0x0031 +#define MBOX_SET_RETRY_COUNT 0x0032 +#define MBOX_SET_TAG_AGE_LIMIT 0x0033 +#define MBOX_SET_CLOCK_RATE 0x0034 +#define MBOX_SET_ACT_NEG_STATE 0x0035 +#define MBOX_SET_ASYNC_DATA_SETUP_TIME 0x0036 +#define MBOX_SET_SBUS_CONTROL_PARAMS 0x0037 +#define MBOX_SET_PCI_PARAMETERS 0x0037 +#define MBOX_SET_TARGET_PARAMS 0x0038 +#define MBOX_SET_DEV_QUEUE_PARAMS 0x0039 +#define MBOX_SET_RESET_DELAY_PARAMS 0x003a + /* 3b */ + /* 3c */ + /* 3d */ + /* 3e */ + /* 3f */ +#define MBOX_RETURN_BIOS_BLOCK_ADDR 0x0040 +#define MBOX_WRITE_FOUR_RAM_WORDS 0x0041 +#define MBOX_EXEC_BIOS_IOCB 0x0042 +#define MBOX_SET_FW_FEATURES 0x004a +#define MBOX_GET_FW_FEATURES 0x004b +#define FW_FEATURE_FAST_POST 0x1 +#define FW_FEATURE_LVD_NOTIFY 0x2 +#define FW_FEATURE_RIO_32BIT 0x4 +#define FW_FEATURE_RIO_16BIT 0x8 + +#define MBOX_INIT_REQ_QUEUE_A64 0x0052 +#define MBOX_INIT_RES_QUEUE_A64 0x0053 + +#define MBOX_ENABLE_TARGET_MODE 0x0055 +#define ENABLE_TARGET_FLAG 0x8000 +#define ENABLE_TQING_FLAG 0x0004 +#define ENABLE_MANDATORY_DISC 0x0002 +#define MBOX_GET_TARGET_STATUS 0x0056 + +/* These are for the ISP2X00 FC cards */ +#define MBOX_GET_LOOP_ID 0x0020 +#define MBOX_GET_FIRMWARE_OPTIONS 0x0028 +#define MBOX_SET_FIRMWARE_OPTIONS 0x0038 +#define MBOX_GET_RESOURCE_COUNT 0x0042 +#define MBOX_ENHANCED_GET_PDB 0x0047 +#define MBOX_EXEC_COMMAND_IOCB_A64 0x0054 +#define MBOX_INIT_FIRMWARE 0x0060 +#define MBOX_GET_INIT_CONTROL_BLOCK 0x0061 +#define MBOX_INIT_LIP 0x0062 +#define MBOX_GET_FC_AL_POSITION_MAP 0x0063 +#define MBOX_GET_PORT_DB 0x0064 +#define MBOX_CLEAR_ACA 0x0065 +#define MBOX_TARGET_RESET 0x0066 +#define MBOX_CLEAR_TASK_SET 0x0067 +#define MBOX_ABORT_TASK_SET 0x0068 +#define MBOX_GET_FW_STATE 0x0069 +#define MBOX_GET_PORT_NAME 0x006A +#define MBOX_GET_LINK_STATUS 0x006B +#define MBOX_INIT_LIP_RESET 0x006C +#define MBOX_SEND_SNS 0x006E +#define MBOX_FABRIC_LOGIN 0x006F +#define MBOX_SEND_CHANGE_REQUEST 0x0070 +#define MBOX_FABRIC_LOGOUT 0x0071 +#define MBOX_INIT_LIP_LOGIN 0x0072 +#define MBOX_LUN_RESET 0x007E + +#define MBOX_DRIVER_HEARTBEAT 0x005B +#define MBOX_FW_HEARTBEAT 0x005C + +#define MBOX_GET_SET_DATA_RATE 0x005D /* 23XX only */ +#define MBGSD_GET_RATE 0 +#define MBGSD_SET_RATE 1 +#define MBGSD_ONEGB 0 +#define MBGSD_TWOGB 1 +#define MBGSD_AUTO 2 + + +#define ISP2100_SET_PCI_PARAM 0x00ff + +#define MBOX_BUSY 0x04 + +/* + * Mailbox Command Complete Status Codes + */ +#define MBOX_COMMAND_COMPLETE 0x4000 +#define MBOX_INVALID_COMMAND 0x4001 +#define MBOX_HOST_INTERFACE_ERROR 0x4002 +#define MBOX_TEST_FAILED 0x4003 +#define MBOX_COMMAND_ERROR 0x4005 +#define MBOX_COMMAND_PARAM_ERROR 0x4006 +#define MBOX_PORT_ID_USED 0x4007 +#define MBOX_LOOP_ID_USED 0x4008 +#define MBOX_ALL_IDS_USED 0x4009 +#define MBOX_NOT_LOGGED_IN 0x400A +#define MBLOGALL 0x000f +#define MBLOGNONE 0x0000 +#define MBLOGMASK(x) ((x) & 0xf) + +/* + * Asynchronous event status codes + */ +#define ASYNC_BUS_RESET 0x8001 +#define ASYNC_SYSTEM_ERROR 0x8002 +#define ASYNC_RQS_XFER_ERR 0x8003 +#define ASYNC_RSP_XFER_ERR 0x8004 +#define ASYNC_QWAKEUP 0x8005 +#define ASYNC_TIMEOUT_RESET 0x8006 +#define ASYNC_DEVICE_RESET 0x8007 +#define ASYNC_EXTMSG_UNDERRUN 0x800A +#define ASYNC_SCAM_INT 0x800B +#define ASYNC_HUNG_SCSI 0x800C +#define ASYNC_KILLED_BUS 0x800D +#define ASYNC_BUS_TRANSIT 0x800E /* LVD -> HVD, eg. */ +#define ASYNC_LIP_OCCURRED 0x8010 +#define ASYNC_LOOP_UP 0x8011 +#define ASYNC_LOOP_DOWN 0x8012 +#define ASYNC_LOOP_RESET 0x8013 +#define ASYNC_PDB_CHANGED 0x8014 +#define ASYNC_CHANGE_NOTIFY 0x8015 +#define ASYNC_LIP_F8 0x8016 +#define ASYNC_CMD_CMPLT 0x8020 +#define ASYNC_CTIO_DONE 0x8021 +#define ASYNC_IP_XMIT_DONE 0x8022 +#define ASYNC_IP_RECV_DONE 0x8023 +#define ASYNC_IP_BROADCAST 0x8024 +#define ASYNC_IP_RCVQ_LOW 0x8025 +#define ASYNC_IP_RCVQ_EMPTY 0x8026 +#define ASYNC_IP_RECV_DONE_ALIGNED 0x8027 +#define ASYNC_PTPMODE 0x8030 +#define ASYNC_RIO1 0x8031 +#define ASYNC_RIO2 0x8032 +#define ASYNC_RIO3 0x8033 +#define ASYNC_RIO4 0x8034 +#define ASYNC_RIO5 0x8035 +#define ASYNC_CONNMODE 0x8036 +#define ISP_CONN_LOOP 1 +#define ISP_CONN_PTP 2 +#define ISP_CONN_BADLIP 3 +#define ISP_CONN_FATAL 4 +#define ISP_CONN_LOOPBACK 5 +#define ASYNC_RIO_RESP 0x8040 +#define ASYNC_RIO_COMP 0x8042 +/* + * 2.01.31 2200 Only. Need Bit 13 in Mailbox 1 for Set Firmware Options + * mailbox command to enable this. + */ +#define ASYNC_QFULL_SENT 0x8049 + +/* + * Mailbox Usages + */ + +#define WRITE_REQUEST_QUEUE_IN_POINTER(isp, value) \ + ISP_WRITE(isp, isp->isp_rqstinrp, value) + +#define READ_REQUEST_QUEUE_OUT_POINTER(isp) \ + ISP_READ(isp, isp->isp_rqstoutrp) + +#define READ_RESPONSE_QUEUE_IN_POINTER(isp) \ + ISP_READ(isp, isp->isp_respinrp) + +#define WRITE_RESPONSE_QUEUE_OUT_POINTER(isp, value) \ + ISP_WRITE(isp, isp->isp_respoutrp, value) + +/* + * Command Structure Definitions + */ + +typedef struct { + uint32_t ds_base; + uint32_t ds_count; +} ispds_t; + +typedef struct { + uint32_t ds_base; + uint32_t ds_basehi; + uint32_t ds_count; +} ispds64_t; + +#define DSTYPE_32BIT 0 +#define DSTYPE_64BIT 1 +typedef struct { + uint16_t ds_type; /* 0-> ispds_t, 1-> ispds64_t */ + uint32_t ds_segment; /* unused */ + uint32_t ds_base; /* 32 bit address of DSD list */ +} ispdslist_t; + + +/* + * These elements get swizzled around for SBus instances. + */ +#define ISP_SWAP8(a, b) { \ + uint8_t tmp; \ + tmp = a; \ + a = b; \ + b = tmp; \ +} +typedef struct { + uint8_t rqs_entry_type; + uint8_t rqs_entry_count; + uint8_t rqs_seqno; + uint8_t rqs_flags; +} isphdr_t; + +/* RQS Flag definitions */ +#define RQSFLAG_CONTINUATION 0x01 +#define RQSFLAG_FULL 0x02 +#define RQSFLAG_BADHEADER 0x04 +#define RQSFLAG_BADPACKET 0x08 + +/* RQS entry_type definitions */ +#define RQSTYPE_REQUEST 0x01 +#define RQSTYPE_DATASEG 0x02 +#define RQSTYPE_RESPONSE 0x03 +#define RQSTYPE_MARKER 0x04 +#define RQSTYPE_CMDONLY 0x05 +#define RQSTYPE_ATIO 0x06 /* Target Mode */ +#define RQSTYPE_CTIO 0x07 /* Target Mode */ +#define RQSTYPE_SCAM 0x08 +#define RQSTYPE_A64 0x09 +#define RQSTYPE_A64_CONT 0x0a +#define RQSTYPE_ENABLE_LUN 0x0b /* Target Mode */ +#define RQSTYPE_MODIFY_LUN 0x0c /* Target Mode */ +#define RQSTYPE_NOTIFY 0x0d /* Target Mode */ +#define RQSTYPE_NOTIFY_ACK 0x0e /* Target Mode */ +#define RQSTYPE_CTIO1 0x0f /* Target Mode */ +#define RQSTYPE_STATUS_CONT 0x10 +#define RQSTYPE_T2RQS 0x11 +#define RQSTYPE_IP_XMIT 0x13 +#define RQSTYPE_T4RQS 0x15 +#define RQSTYPE_ATIO2 0x16 /* Target Mode */ +#define RQSTYPE_CTIO2 0x17 /* Target Mode */ +#define RQSTYPE_CSET0 0x18 +#define RQSTYPE_T3RQS 0x19 +#define RQSTYPE_IP_XMIT_64 0x1b +#define RQSTYPE_CTIO4 0x1e /* Target Mode */ +#define RQSTYPE_CTIO3 0x1f /* Target Mode */ +#define RQSTYPE_RIO1 0x21 +#define RQSTYPE_RIO2 0x22 +#define RQSTYPE_IP_RECV 0x23 +#define RQSTYPE_IP_RECV_CONT 0x24 + + +#define ISP_RQDSEG 4 +typedef struct { + isphdr_t req_header; + uint32_t req_handle; + uint8_t req_lun_trn; + uint8_t req_target; + uint16_t req_cdblen; +#define req_modifier req_cdblen /* marker packet */ + uint16_t req_flags; + uint16_t req_reserved; + uint16_t req_time; + uint16_t req_seg_count; + uint8_t req_cdb[12]; + ispds_t req_dataseg[ISP_RQDSEG]; +} ispreq_t; + +#define ispreq64_t ispreqt3_t /* same as.... */ +#define ISP_RQDSEG_A64 2 + +/* + * A request packet can also be a marker packet. + */ +#define SYNC_DEVICE 0 +#define SYNC_TARGET 1 +#define SYNC_ALL 2 +#define SYNC_LIP 3 + +#define ISP_RQDSEG_T2 3 +typedef struct { + isphdr_t req_header; + uint32_t req_handle; + uint8_t req_lun_trn; + uint8_t req_target; + uint16_t req_scclun; + uint16_t req_flags; + uint16_t _res2; + uint16_t req_time; + uint16_t req_seg_count; + uint8_t req_cdb[16]; + uint32_t req_totalcnt; + ispds_t req_dataseg[ISP_RQDSEG_T2]; +} ispreqt2_t; + +typedef struct { + isphdr_t req_header; + uint32_t req_handle; + uint16_t req_target; + uint16_t req_scclun; + uint16_t req_flags; + uint16_t _res2; + uint16_t req_time; + uint16_t req_seg_count; + uint8_t req_cdb[16]; + uint32_t req_totalcnt; + ispds_t req_dataseg[ISP_RQDSEG_T2]; +} ispreqt2e_t; + +#define ISP_RQDSEG_T3 2 +typedef struct { + isphdr_t req_header; + uint32_t req_handle; + uint8_t req_lun_trn; + uint8_t req_target; + uint16_t req_scclun; + uint16_t req_flags; + uint16_t _res2; + uint16_t req_time; + uint16_t req_seg_count; + uint8_t req_cdb[16]; + uint32_t req_totalcnt; + ispds64_t req_dataseg[ISP_RQDSEG_T3]; +} ispreqt3_t; + +typedef struct { + isphdr_t req_header; + uint32_t req_handle; + uint16_t req_target; + uint16_t req_scclun; + uint16_t req_flags; + uint16_t _res2; + uint16_t req_time; + uint16_t req_seg_count; + uint8_t req_cdb[16]; + uint32_t req_totalcnt; + ispds64_t req_dataseg[ISP_RQDSEG_T3]; +} ispreqt3e_t; + +/* req_flag values */ +#define REQFLAG_NODISCON 0x0001 +#define REQFLAG_HTAG 0x0002 +#define REQFLAG_OTAG 0x0004 +#define REQFLAG_STAG 0x0008 +#define REQFLAG_TARGET_RTN 0x0010 + +#define REQFLAG_NODATA 0x0000 +#define REQFLAG_DATA_IN 0x0020 +#define REQFLAG_DATA_OUT 0x0040 +#define REQFLAG_DATA_UNKNOWN 0x0060 + +#define REQFLAG_DISARQ 0x0100 +#define REQFLAG_FRC_ASYNC 0x0200 +#define REQFLAG_FRC_SYNC 0x0400 +#define REQFLAG_FRC_WIDE 0x0800 +#define REQFLAG_NOPARITY 0x1000 +#define REQFLAG_STOPQ 0x2000 +#define REQFLAG_XTRASNS 0x4000 +#define REQFLAG_PRIORITY 0x8000 + +typedef struct { + isphdr_t req_header; + uint32_t req_handle; + uint8_t req_lun_trn; + uint8_t req_target; + uint16_t req_cdblen; + uint16_t req_flags; + uint16_t _res1; + uint16_t req_time; + uint16_t req_seg_count; + uint8_t req_cdb[44]; +} ispextreq_t; + +#define ISP_CDSEG 7 +typedef struct { + isphdr_t req_header; + uint32_t _res1; + ispds_t req_dataseg[ISP_CDSEG]; +} ispcontreq_t; + +#define ISP_CDSEG64 5 +typedef struct { + isphdr_t req_header; + ispds64_t req_dataseg[ISP_CDSEG64]; +} ispcontreq64_t; + +typedef struct { + isphdr_t req_header; + uint32_t req_handle; + uint16_t req_scsi_status; + uint16_t req_completion_status; + uint16_t req_state_flags; + uint16_t req_status_flags; + uint16_t req_time; +#define req_response_len req_time /* FC only */ + uint16_t req_sense_len; + uint32_t req_resid; + uint8_t req_response[8]; /* FC only */ + uint8_t req_sense_data[32]; +} ispstatusreq_t; + +typedef struct { + isphdr_t req_header; + uint8_t req_sense_data[60]; +} ispstatus_cont_t; + +/* + * For Qlogic 2X00, the high order byte of SCSI status has + * additional meaning. + */ +#define RQCS_RU 0x800 /* Residual Under */ +#define RQCS_RO 0x400 /* Residual Over */ +#define RQCS_RESID (RQCS_RU|RQCS_RO) +#define RQCS_SV 0x200 /* Sense Length Valid */ +#define RQCS_RV 0x100 /* FCP Response Length Valid */ + +/* + * Completion Status Codes. + */ +#define RQCS_COMPLETE 0x0000 +#define RQCS_DMA_ERROR 0x0002 +#define RQCS_RESET_OCCURRED 0x0004 +#define RQCS_ABORTED 0x0005 +#define RQCS_TIMEOUT 0x0006 +#define RQCS_DATA_OVERRUN 0x0007 +#define RQCS_DATA_UNDERRUN 0x0015 +#define RQCS_QUEUE_FULL 0x001C + +/* 1X00 Only Completion Codes */ +#define RQCS_INCOMPLETE 0x0001 +#define RQCS_TRANSPORT_ERROR 0x0003 +#define RQCS_COMMAND_OVERRUN 0x0008 +#define RQCS_STATUS_OVERRUN 0x0009 +#define RQCS_BAD_MESSAGE 0x000a +#define RQCS_NO_MESSAGE_OUT 0x000b +#define RQCS_EXT_ID_FAILED 0x000c +#define RQCS_IDE_MSG_FAILED 0x000d +#define RQCS_ABORT_MSG_FAILED 0x000e +#define RQCS_REJECT_MSG_FAILED 0x000f +#define RQCS_NOP_MSG_FAILED 0x0010 +#define RQCS_PARITY_ERROR_MSG_FAILED 0x0011 +#define RQCS_DEVICE_RESET_MSG_FAILED 0x0012 +#define RQCS_ID_MSG_FAILED 0x0013 +#define RQCS_UNEXP_BUS_FREE 0x0014 +#define RQCS_XACT_ERR1 0x0018 +#define RQCS_XACT_ERR2 0x0019 +#define RQCS_XACT_ERR3 0x001A +#define RQCS_BAD_ENTRY 0x001B +#define RQCS_PHASE_SKIPPED 0x001D +#define RQCS_ARQS_FAILED 0x001E +#define RQCS_WIDE_FAILED 0x001F +#define RQCS_SYNCXFER_FAILED 0x0020 +#define RQCS_LVD_BUSERR 0x0021 + +/* 2X00 Only Completion Codes */ +#define RQCS_PORT_UNAVAILABLE 0x0028 +#define RQCS_PORT_LOGGED_OUT 0x0029 +#define RQCS_PORT_CHANGED 0x002A +#define RQCS_PORT_BUSY 0x002B + +/* + * 1X00 specific State Flags + */ +#define RQSF_GOT_BUS 0x0100 +#define RQSF_GOT_TARGET 0x0200 +#define RQSF_SENT_CDB 0x0400 +#define RQSF_XFRD_DATA 0x0800 +#define RQSF_GOT_STATUS 0x1000 +#define RQSF_GOT_SENSE 0x2000 +#define RQSF_XFER_COMPLETE 0x4000 + +/* + * 2X00 specific State Flags + * (same as 1X00 except RQSF_GOT_BUS/RQSF_GOT_TARGET are not available) + */ +#define RQSF_DATA_IN 0x0020 +#define RQSF_DATA_OUT 0x0040 +#define RQSF_STAG 0x0008 +#define RQSF_OTAG 0x0004 +#define RQSF_HTAG 0x0002 +/* + * 1X00 Status Flags + */ +#define RQSTF_DISCONNECT 0x0001 +#define RQSTF_SYNCHRONOUS 0x0002 +#define RQSTF_PARITY_ERROR 0x0004 +#define RQSTF_BUS_RESET 0x0008 +#define RQSTF_DEVICE_RESET 0x0010 +#define RQSTF_ABORTED 0x0020 +#define RQSTF_TIMEOUT 0x0040 +#define RQSTF_NEGOTIATION 0x0080 + +/* + * 2X00 specific state flags + */ +/* RQSF_SENT_CDB */ +/* RQSF_XFRD_DATA */ +/* RQSF_GOT_STATUS */ +/* RQSF_XFER_COMPLETE */ + +/* + * 2X00 specific status flags + */ +/* RQSTF_ABORTED */ +/* RQSTF_TIMEOUT */ +#define RQSTF_DMA_ERROR 0x0080 +#define RQSTF_LOGOUT 0x2000 + +/* + * Miscellaneous + */ +#ifndef ISP_EXEC_THROTTLE +#define ISP_EXEC_THROTTLE 16 +#endif + +/* + * About Firmware returns an 'attribute' word in mailbox 6. + */ +#define ISP_FW_ATTR_TMODE 0x01 +#define ISP_FW_ATTR_SCCLUN 0x02 +#define ISP_FW_ATTR_FABRIC 0x04 +#define ISP_FW_ATTR_CLASS2 0x08 +#define ISP_FW_ATTR_FCTAPE 0x10 +#define ISP_FW_ATTR_IP 0x20 +#define ISP_FW_ATTR_VI 0x40 +#define ISP_FW_ATTR_VI_SOLARIS 0x80 +#define ISP_FW_ATTR_2KLOGINS 0x100 /* XXX: just a guess */ + +#define IS_2KLOGIN(isp) \ + (IS_FC(isp) && (FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_2KLOGINS)) + +/* + * Reduced Interrupt Operation Response Queue Entreis + */ + +typedef struct { + isphdr_t req_header; + uint32_t req_handles[15]; +} isp_rio1_t; + +typedef struct { + isphdr_t req_header; + uint16_t req_handles[30]; +} isp_rio2_t; + +/* + * FC (ISP2100) specific data structures + */ + +/* + * Initialization Control Block + * + * Version One (prime) format. + */ +typedef struct isp_icb { + uint8_t icb_version; + uint8_t _reserved0; + uint16_t icb_fwoptions; + uint16_t icb_maxfrmlen; + uint16_t icb_maxalloc; + uint16_t icb_execthrottle; + uint8_t icb_retry_count; + uint8_t icb_retry_delay; + uint8_t icb_portname[8]; + uint16_t icb_hardaddr; + uint8_t icb_iqdevtype; + uint8_t icb_logintime; + uint8_t icb_nodename[8]; + uint16_t icb_rqstout; + uint16_t icb_rspnsin; + uint16_t icb_rqstqlen; + uint16_t icb_rsltqlen; + uint16_t icb_rqstaddr[4]; + uint16_t icb_respaddr[4]; + uint16_t icb_lunenables; + uint8_t icb_ccnt; + uint8_t icb_icnt; + uint16_t icb_lunetimeout; + uint16_t _reserved1; + uint16_t icb_xfwoptions; + uint8_t icb_racctimer; + uint8_t icb_idelaytimer; + uint16_t icb_zfwoptions; + uint16_t _reserved2[13]; +} isp_icb_t; +#define ICB_VERSION1 1 + +#define ICBOPT_HARD_ADDRESS 0x0001 +#define ICBOPT_FAIRNESS 0x0002 +#define ICBOPT_FULL_DUPLEX 0x0004 +#define ICBOPT_FAST_POST 0x0008 +#define ICBOPT_TGT_ENABLE 0x0010 +#define ICBOPT_INI_DISABLE 0x0020 +#define ICBOPT_INI_ADISC 0x0040 +#define ICBOPT_INI_TGTTYPE 0x0080 +#define ICBOPT_PDBCHANGE_AE 0x0100 +#define ICBOPT_NOLIP 0x0200 +#define ICBOPT_SRCHDOWN 0x0400 +#define ICBOPT_PREVLOOP 0x0800 +#define ICBOPT_STOP_ON_QFULL 0x1000 +#define ICBOPT_FULL_LOGIN 0x2000 +#define ICBOPT_BOTH_WWNS 0x4000 +#define ICBOPT_EXTENDED 0x8000 + +#define ICBXOPT_CLASS2_ACK0 0x0200 +#define ICBXOPT_CLASS2 0x0100 +#define ICBXOPT_LOOP_ONLY (0 << 4) +#define ICBXOPT_PTP_ONLY (1 << 4) +#define ICBXOPT_LOOP_2_PTP (2 << 4) +#define ICBXOPT_PTP_2_LOOP (3 << 4) + +/* + * The lower 4 bits of the xfwoptions field are the OPERATION MODE bits. + * RIO is not defined for the 23XX cards + */ +#define ICBXOPT_RIO_OFF 0 +#define ICBXOPT_RIO_16BIT 1 +#define ICBXOPT_RIO_32BIT 2 +#define ICBXOPT_RIO_16BIT_IOCB 3 +#define ICBXOPT_RIO_32BIT_IOCB 4 +#define ICBXOPT_ZIO 5 +#define ICBXOPT_TIMER_MASK 0x7 + +#define ICBZOPT_ENA_RDXFR_RDY 0x01 +#define ICBZOPT_ENA_OOF (1 << 6) /* out of order frame handling */ +#define ICBZOPT_50_OHM 0x0200 +/* These 3 only apply to the 2300 */ +#define ICBZOPT_RATE_ONEGB (MBGSD_ONEGB << 14) +#define ICBZOPT_RATE_TWOGB (MBGSD_TWOGB << 14) +#define ICBZOPT_RATE_AUTO (MBGSD_AUTO << 14) + + +#define ICB_MIN_FRMLEN 256 +#define ICB_MAX_FRMLEN 2112 +#define ICB_DFLT_FRMLEN 1024 +#define ICB_DFLT_ALLOC 256 +#define ICB_DFLT_THROTTLE 16 +#define ICB_DFLT_RDELAY 5 +#define ICB_DFLT_RCOUNT 3 + +#define ICB_LOGIN_TOV 30 +#define ICB_LUN_ENABLE_TOV 180 + + + +#define RQRSP_ADDR0015 0 +#define RQRSP_ADDR1631 1 +#define RQRSP_ADDR3247 2 +#define RQRSP_ADDR4863 3 + + +#define ICB_NNM0 7 +#define ICB_NNM1 6 +#define ICB_NNM2 5 +#define ICB_NNM3 4 +#define ICB_NNM4 3 +#define ICB_NNM5 2 +#define ICB_NNM6 1 +#define ICB_NNM7 0 + +#define MAKE_NODE_NAME_FROM_WWN(array, wwn) \ + array[ICB_NNM0] = (uint8_t) ((wwn >> 0) & 0xff), \ + array[ICB_NNM1] = (uint8_t) ((wwn >> 8) & 0xff), \ + array[ICB_NNM2] = (uint8_t) ((wwn >> 16) & 0xff), \ + array[ICB_NNM3] = (uint8_t) ((wwn >> 24) & 0xff), \ + array[ICB_NNM4] = (uint8_t) ((wwn >> 32) & 0xff), \ + array[ICB_NNM5] = (uint8_t) ((wwn >> 40) & 0xff), \ + array[ICB_NNM6] = (uint8_t) ((wwn >> 48) & 0xff), \ + array[ICB_NNM7] = (uint8_t) ((wwn >> 56) & 0xff) + +#define MAKE_WWN_FROM_NODE_NAME(wwn, array) \ + wwn = ((uint64_t) array[ICB_NNM0]) | \ + ((uint64_t) array[ICB_NNM1] << 8) | \ + ((uint64_t) array[ICB_NNM2] << 16) | \ + ((uint64_t) array[ICB_NNM3] << 24) | \ + ((uint64_t) array[ICB_NNM4] << 32) | \ + ((uint64_t) array[ICB_NNM5] << 40) | \ + ((uint64_t) array[ICB_NNM6] << 48) | \ + ((uint64_t) array[ICB_NNM7] << 56) + +/* + * FC-AL Position Map + * + * This is an at most 128 byte map that returns either + * the LILP or Firmware generated list of ports. + * + * We deviate a bit from the returned qlogic format to + * use an extra bit to say whether this was a LILP or + * f/w generated map. + */ +typedef struct { + uint8_t fwmap : 1, + count : 7; + uint8_t map[127]; +} fcpos_map_t; + +/* + * Port Data Base Element + */ + +typedef struct { + uint16_t pdb_options; + uint8_t pdb_mstate; + uint8_t pdb_sstate; +#define BITS2WORD(x) ((x)[0] << 16 | (x)[3] << 8 | (x)[2]) + uint8_t pdb_hardaddr_bits[4]; + uint8_t pdb_portid_bits[4]; + uint8_t pdb_nodename[8]; + uint8_t pdb_portname[8]; + uint16_t pdb_execthrottle; + uint16_t pdb_exec_count; + uint8_t pdb_retry_count; + uint8_t pdb_retry_delay; + uint16_t pdb_resalloc; + uint16_t pdb_curalloc; + uint16_t pdb_qhead; + uint16_t pdb_qtail; + uint16_t pdb_tl_next; + uint16_t pdb_tl_last; + uint16_t pdb_features; /* PLOGI, Common Service */ + uint16_t pdb_pconcurrnt; /* PLOGI, Common Service */ + uint16_t pdb_roi; /* PLOGI, Common Service */ + uint8_t pdb_target; + uint8_t pdb_initiator; /* PLOGI, Class 3 Control Flags */ + uint16_t pdb_rdsiz; /* PLOGI, Class 3 */ + uint16_t pdb_ncseq; /* PLOGI, Class 3 */ + uint16_t pdb_noseq; /* PLOGI, Class 3 */ + uint16_t pdb_labrtflg; + uint16_t pdb_lstopflg; + uint16_t pdb_sqhead; + uint16_t pdb_sqtail; + uint16_t pdb_ptimer; + uint16_t pdb_nxt_seqid; + uint16_t pdb_fcount; + uint16_t pdb_prli_len; + uint16_t pdb_prli_svc0; + uint16_t pdb_prli_svc3; + uint16_t pdb_loopid; + uint16_t pdb_il_ptr; + uint16_t pdb_sl_ptr; +} isp_pdb_t; + +#define PDB_OPTIONS_XMITTING (1<<11) +#define PDB_OPTIONS_LNKXMIT (1<<10) +#define PDB_OPTIONS_ABORTED (1<<9) +#define PDB_OPTIONS_ADISC (1<<1) + +#define PDB_STATE_DISCOVERY 0 +#define PDB_STATE_WDISC_ACK 1 +#define PDB_STATE_PLOGI 2 +#define PDB_STATE_PLOGI_ACK 3 +#define PDB_STATE_PRLI 4 +#define PDB_STATE_PRLI_ACK 5 +#define PDB_STATE_LOGGED_IN 6 +#define PDB_STATE_PORT_UNAVAIL 7 +#define PDB_STATE_PRLO 8 +#define PDB_STATE_PRLO_ACK 9 +#define PDB_STATE_PLOGO 10 +#define PDB_STATE_PLOG_ACK 11 + +#define SVC3_TGT_ROLE 0x10 +#define SVC3_INI_ROLE 0x20 +#define SVC3_ROLE_MASK 0x30 +#define SVC3_ROLE_SHIFT 4 + +/* + * CT definition + * + * This is as the QLogic f/w documentations defines it- which is just opposite, + * bit wise, from what the specification defines it as. Additionally, the + * ct_response and ct_resid (really from FC-GS-2) need to be byte swapped. + */ + +typedef struct { + uint8_t ct_revision; + uint8_t ct_portid[3]; + uint8_t ct_fcs_type; + uint8_t ct_fcs_subtype; + uint8_t ct_options; + uint8_t ct_res0; + uint16_t ct_response; + uint16_t ct_resid; + uint8_t ct_res1; + uint8_t ct_reason; + uint8_t ct_explanation; + uint8_t ct_vunique; +} ct_hdr_t; +#define FS_ACC 0x8002 +#define FS_RJT 0x8001 + +#define FC4_IP 5 /* ISO/EEC 8802-2 LLC/SNAP "Out of Order Delivery" */ +#define FC4_SCSI 8 /* SCSI-3 via Fivre Channel Protocol (FCP) */ +#define FC4_FC_SVC 0x20 /* Fibre Channel Services */ + +#define SNS_GA_NXT 0x100 +#define SNS_GPN_ID 0x112 +#define SNS_GNN_ID 0x113 +#define SNS_GFF_ID 0x11F +#define SNS_GID_FT 0x171 +#define SNS_RFT_ID 0x217 +typedef struct { + uint16_t snscb_rblen; /* response buffer length (words) */ + uint16_t snscb_res0; + uint16_t snscb_addr[4]; /* response buffer address */ + uint16_t snscb_sblen; /* subcommand buffer length (words) */ + uint16_t snscb_res1; + uint16_t snscb_data[1]; /* variable data */ +} sns_screq_t; /* Subcommand Request Structure */ + +typedef struct { + uint16_t snscb_rblen; /* response buffer length (words) */ + uint16_t snscb_res0; + uint16_t snscb_addr[4]; /* response buffer address */ + uint16_t snscb_sblen; /* subcommand buffer length (words) */ + uint16_t snscb_res1; + uint16_t snscb_cmd; + uint16_t snscb_res2; + uint32_t snscb_res3; + uint32_t snscb_port; +} sns_ga_nxt_req_t; +#define SNS_GA_NXT_REQ_SIZE (sizeof (sns_ga_nxt_req_t)) + +typedef struct { + uint16_t snscb_rblen; /* response buffer length (words) */ + uint16_t snscb_res0; + uint16_t snscb_addr[4]; /* response buffer address */ + uint16_t snscb_sblen; /* subcommand buffer length (words) */ + uint16_t snscb_res1; + uint16_t snscb_cmd; + uint16_t snscb_res2; + uint32_t snscb_res3; + uint32_t snscb_portid; +} sns_gxn_id_req_t; +#define SNS_GXN_ID_REQ_SIZE (sizeof (sns_gxn_id_req_t)) + +typedef struct { + uint16_t snscb_rblen; /* response buffer length (words) */ + uint16_t snscb_res0; + uint16_t snscb_addr[4]; /* response buffer address */ + uint16_t snscb_sblen; /* subcommand buffer length (words) */ + uint16_t snscb_res1; + uint16_t snscb_cmd; + uint16_t snscb_mword_div_2; + uint32_t snscb_res3; + uint32_t snscb_fc4_type; +} sns_gid_ft_req_t; +#define SNS_GID_FT_REQ_SIZE (sizeof (sns_gid_ft_req_t)) + +typedef struct { + uint16_t snscb_rblen; /* response buffer length (words) */ + uint16_t snscb_res0; + uint16_t snscb_addr[4]; /* response buffer address */ + uint16_t snscb_sblen; /* subcommand buffer length (words) */ + uint16_t snscb_res1; + uint16_t snscb_cmd; + uint16_t snscb_res2; + uint32_t snscb_res3; + uint32_t snscb_port; + uint32_t snscb_fc4_types[8]; +} sns_rft_id_req_t; +#define SNS_RFT_ID_REQ_SIZE (sizeof (sns_rft_id_req_t)) + +typedef struct { + ct_hdr_t snscb_cthdr; + uint8_t snscb_port_type; + uint8_t snscb_port_id[3]; + uint8_t snscb_portname[8]; + uint16_t snscb_data[1]; /* variable data */ +} sns_scrsp_t; /* Subcommand Response Structure */ + +typedef struct { + ct_hdr_t snscb_cthdr; + uint8_t snscb_port_type; + uint8_t snscb_port_id[3]; + uint8_t snscb_portname[8]; + uint8_t snscb_pnlen; /* symbolic port name length */ + uint8_t snscb_pname[255]; /* symbolic port name */ + uint8_t snscb_nodename[8]; + uint8_t snscb_nnlen; /* symbolic node name length */ + uint8_t snscb_nname[255]; /* symbolic node name */ + uint8_t snscb_ipassoc[8]; + uint8_t snscb_ipaddr[16]; + uint8_t snscb_svc_class[4]; + uint8_t snscb_fc4_types[32]; + uint8_t snscb_fpname[8]; + uint8_t snscb_reserved; + uint8_t snscb_hardaddr[3]; +} sns_ga_nxt_rsp_t; /* Subcommand Response Structure */ +#define SNS_GA_NXT_RESP_SIZE (sizeof (sns_ga_nxt_rsp_t)) + +typedef struct { + ct_hdr_t snscb_cthdr; + uint8_t snscb_wwn[8]; +} sns_gxn_id_rsp_t; +#define SNS_GXN_ID_RESP_SIZE (sizeof (sns_gxn_id_rsp_t)) + +typedef struct { + ct_hdr_t snscb_cthdr; + uint32_t snscb_fc4_features[32]; +} sns_gff_id_rsp_t; +#define SNS_GFF_ID_RESP_SIZE (sizeof (sns_gff_id_rsp_t)) + +typedef struct { + ct_hdr_t snscb_cthdr; + struct { + uint8_t control; + uint8_t portid[3]; + } snscb_ports[1]; +} sns_gid_ft_rsp_t; +#define SNS_GID_FT_RESP_SIZE(x) ((sizeof (sns_gid_ft_rsp_t)) + ((x - 1) << 2)) + +#define SNS_RFT_ID_RESP_SIZE (sizeof (ct_hdr_t)) + +#endif /* _ISPMBOX_H */ diff --git a/sys/dev/isp/ispreg.h b/sys/dev/isp/ispreg.h new file mode 100644 index 000000000000..8c5e5d413ee2 --- /dev/null +++ b/sys/dev/isp/ispreg.h @@ -0,0 +1,1035 @@ +/* $FreeBSD$ */ +/*- + * Machine Independent (well, as best as possible) register + * definitions for Qlogic ISP SCSI adapters. + * + * Copyright (c) 1997-2006 by Matthew Jacob + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef _ISPREG_H +#define _ISPREG_H + +/* + * Hardware definitions for the Qlogic ISP registers. + */ + +/* + * This defines types of access to various registers. + * + * R: Read Only + * W: Write Only + * RW: Read/Write + * + * R*, W*, RW*: Read Only, Write Only, Read/Write, but only + * if RISC processor in ISP is paused. + */ + +/* + * Offsets for various register blocks. + * + * Sad but true, different architectures have different offsets. + * + * Don't be alarmed if none of this makes sense. The original register + * layout set some defines in a certain pattern. Everything else has been + * grafted on since. For example, the ISP1080 manual will state that DMA + * registers start at 0x80 from the base of the register address space. + * That's true, but for our purposes, we define DMA_REGS_OFF for the 1080 + * to start at offset 0x60 because the DMA registers are all defined to + * be DMA_BLOCK+0x20 and so on. Clear? + */ + +#define BIU_REGS_OFF 0x00 + +#define PCI_MBOX_REGS_OFF 0x70 +#define PCI_MBOX_REGS2100_OFF 0x10 +#define PCI_MBOX_REGS2300_OFF 0x40 +#define SBUS_MBOX_REGS_OFF 0x80 + +#define PCI_SXP_REGS_OFF 0x80 +#define SBUS_SXP_REGS_OFF 0x200 + +#define PCI_RISC_REGS_OFF 0x80 +#define SBUS_RISC_REGS_OFF 0x400 + +/* Bless me! Chip designers have putzed it again! */ +#define ISP1080_DMA_REGS_OFF 0x60 +#define DMA_REGS_OFF 0x00 /* same as BIU block */ + +#define SBUS_REGSIZE 0x450 +#define PCI_REGSIZE 0x100 + +/* + * NB: The *_BLOCK definitions have no specific hardware meaning. + * They serve simply to note to the MD layer which block of + * registers offsets are being accessed. + */ +#define _NREG_BLKS 5 +#define _BLK_REG_SHFT 13 +#define _BLK_REG_MASK (7 << _BLK_REG_SHFT) +#define BIU_BLOCK (0 << _BLK_REG_SHFT) +#define MBOX_BLOCK (1 << _BLK_REG_SHFT) +#define SXP_BLOCK (2 << _BLK_REG_SHFT) +#define RISC_BLOCK (3 << _BLK_REG_SHFT) +#define DMA_BLOCK (4 << _BLK_REG_SHFT) + +/* + * Bus Interface Block Register Offsets + */ + +#define BIU_ID_LO (BIU_BLOCK+0x0) /* R : Bus ID, Low */ +#define BIU2100_FLASH_ADDR (BIU_BLOCK+0x0) +#define BIU_ID_HI (BIU_BLOCK+0x2) /* R : Bus ID, High */ +#define BIU2100_FLASH_DATA (BIU_BLOCK+0x2) +#define BIU_CONF0 (BIU_BLOCK+0x4) /* R : Bus Configuration #0 */ +#define BIU_CONF1 (BIU_BLOCK+0x6) /* R : Bus Configuration #1 */ +#define BIU2100_CSR (BIU_BLOCK+0x6) +#define BIU_ICR (BIU_BLOCK+0x8) /* RW : Bus Interface Ctrl */ +#define BIU_ISR (BIU_BLOCK+0xA) /* R : Bus Interface Status */ +#define BIU_SEMA (BIU_BLOCK+0xC) /* RW : Bus Semaphore */ +#define BIU_NVRAM (BIU_BLOCK+0xE) /* RW : Bus NVRAM */ +/* + * These are specific to the 2300. + * + * They *claim* you can read BIU_R2HSTSLO with a full 32 bit access + * and get both registers, but I'm a bit dubious about that. But the + * point here is that the top 16 bits are firmware defined bits that + * the RISC processor uses to inform the host about something- usually + * something which was nominally in a mailbox register. + */ +#define BIU_REQINP (BIU_BLOCK+0x10) /* Request Queue In */ +#define BIU_REQOUTP (BIU_BLOCK+0x12) /* Request Queue Out */ +#define BIU_RSPINP (BIU_BLOCK+0x14) /* Response Queue In */ +#define BIU_RSPOUTP (BIU_BLOCK+0x16) /* Response Queue Out */ + +#define BIU_R2HSTSLO (BIU_BLOCK+0x18) +#define BIU_R2HSTSHI (BIU_BLOCK+0x1A) + +#define BIU_R2HST_INTR (1 << 15) /* RISC to Host Interrupt */ +#define BIU_R2HST_PAUSED (1 << 8) /* RISC paused */ +#define BIU_R2HST_ISTAT_MASK 0x3f /* intr information && status */ +#define ISPR2HST_ROM_MBX_OK 0x1 /* ROM mailbox cmd done ok */ +#define ISPR2HST_ROM_MBX_FAIL 0x2 /* ROM mailbox cmd done fail */ +#define ISPR2HST_MBX_OK 0x10 /* mailbox cmd done ok */ +#define ISPR2HST_MBX_FAIL 0x11 /* mailbox cmd done fail */ +#define ISPR2HST_ASYNC_EVENT 0x12 /* Async Event */ +#define ISPR2HST_RSPQ_UPDATE 0x13 /* Response Queue Update */ +#define ISPR2HST_RQST_UPDATE 0x14 /* Resquest Queue Update */ +#define ISPR2HST_RIO_16 0x15 /* RIO 1-16 */ +#define ISPR2HST_FPOST 0x16 /* Low 16 bits fast post */ +#define ISPR2HST_FPOST_CTIO 0x17 /* Low 16 bits fast post ctio */ + +#define DFIFO_COMMAND (BIU_BLOCK+0x60) /* RW : Command FIFO Port */ +#define RDMA2100_CONTROL DFIFO_COMMAND +#define DFIFO_DATA (BIU_BLOCK+0x62) /* RW : Data FIFO Port */ + +/* + * Putzed DMA register layouts. + */ +#define CDMA_CONF (DMA_BLOCK+0x20) /* RW*: DMA Configuration */ +#define CDMA2100_CONTROL CDMA_CONF +#define CDMA_CONTROL (DMA_BLOCK+0x22) /* RW*: DMA Control */ +#define CDMA_STATUS (DMA_BLOCK+0x24) /* R : DMA Status */ +#define CDMA_FIFO_STS (DMA_BLOCK+0x26) /* R : DMA FIFO Status */ +#define CDMA_COUNT (DMA_BLOCK+0x28) /* RW*: DMA Transfer Count */ +#define CDMA_ADDR0 (DMA_BLOCK+0x2C) /* RW*: DMA Address, Word 0 */ +#define CDMA_ADDR1 (DMA_BLOCK+0x2E) /* RW*: DMA Address, Word 1 */ +#define CDMA_ADDR2 (DMA_BLOCK+0x30) /* RW*: DMA Address, Word 2 */ +#define CDMA_ADDR3 (DMA_BLOCK+0x32) /* RW*: DMA Address, Word 3 */ + +#define DDMA_CONF (DMA_BLOCK+0x40) /* RW*: DMA Configuration */ +#define TDMA2100_CONTROL DDMA_CONF +#define DDMA_CONTROL (DMA_BLOCK+0x42) /* RW*: DMA Control */ +#define DDMA_STATUS (DMA_BLOCK+0x44) /* R : DMA Status */ +#define DDMA_FIFO_STS (DMA_BLOCK+0x46) /* R : DMA FIFO Status */ +#define DDMA_COUNT_LO (DMA_BLOCK+0x48) /* RW*: DMA Xfer Count, Low */ +#define DDMA_COUNT_HI (DMA_BLOCK+0x4A) /* RW*: DMA Xfer Count, High */ +#define DDMA_ADDR0 (DMA_BLOCK+0x4C) /* RW*: DMA Address, Word 0 */ +#define DDMA_ADDR1 (DMA_BLOCK+0x4E) /* RW*: DMA Address, Word 1 */ +/* these are for the 1040A cards */ +#define DDMA_ADDR2 (DMA_BLOCK+0x50) /* RW*: DMA Address, Word 2 */ +#define DDMA_ADDR3 (DMA_BLOCK+0x52) /* RW*: DMA Address, Word 3 */ + + +/* + * Bus Interface Block Register Definitions + */ +/* BUS CONFIGURATION REGISTER #0 */ +#define BIU_CONF0_HW_MASK 0x000F /* Hardware revision mask */ +/* BUS CONFIGURATION REGISTER #1 */ + +#define BIU_SBUS_CONF1_PARITY 0x0100 /* Enable parity checking */ +#define BIU_SBUS_CONF1_FCODE_MASK 0x00F0 /* Fcode cycle mask */ + +#define BIU_PCI_CONF1_FIFO_128 0x0040 /* 128 bytes FIFO threshold */ +#define BIU_PCI_CONF1_FIFO_64 0x0030 /* 64 bytes FIFO threshold */ +#define BIU_PCI_CONF1_FIFO_32 0x0020 /* 32 bytes FIFO threshold */ +#define BIU_PCI_CONF1_FIFO_16 0x0010 /* 16 bytes FIFO threshold */ +#define BIU_BURST_ENABLE 0x0004 /* Global enable Bus bursts */ +#define BIU_SBUS_CONF1_FIFO_64 0x0003 /* 64 bytes FIFO threshold */ +#define BIU_SBUS_CONF1_FIFO_32 0x0002 /* 32 bytes FIFO threshold */ +#define BIU_SBUS_CONF1_FIFO_16 0x0001 /* 16 bytes FIFO threshold */ +#define BIU_SBUS_CONF1_FIFO_8 0x0000 /* 8 bytes FIFO threshold */ +#define BIU_SBUS_CONF1_BURST8 0x0008 /* Enable 8-byte bursts */ +#define BIU_PCI_CONF1_SXP 0x0008 /* SXP register select */ + +#define BIU_PCI1080_CONF1_SXP0 0x0100 /* SXP bank #1 select */ +#define BIU_PCI1080_CONF1_SXP1 0x0200 /* SXP bank #2 select */ +#define BIU_PCI1080_CONF1_DMA 0x0300 /* DMA bank select */ + +/* ISP2100 Bus Control/Status Register */ + +#define BIU2100_ICSR_REGBSEL 0x30 /* RW: register bank select */ +#define BIU2100_RISC_REGS (0 << 4) /* RISC Regs */ +#define BIU2100_FB_REGS (1 << 4) /* FrameBuffer Regs */ +#define BIU2100_FPM0_REGS (2 << 4) /* FPM 0 Regs */ +#define BIU2100_FPM1_REGS (3 << 4) /* FPM 1 Regs */ +#define BIU2100_NVRAM_OFFSET (1 << 14) +#define BIU2100_FLASH_UPPER_64K 0x04 /* RW: Upper 64K Bank Select */ +#define BIU2100_FLASH_ENABLE 0x02 /* RW: Enable Flash RAM */ +#define BIU2100_SOFT_RESET 0x01 +/* SOFT RESET FOR ISP2100 is same bit, but in this register, not ICR */ + + +/* BUS CONTROL REGISTER */ +#define BIU_ICR_ENABLE_DMA_INT 0x0020 /* Enable DMA interrupts */ +#define BIU_ICR_ENABLE_CDMA_INT 0x0010 /* Enable CDMA interrupts */ +#define BIU_ICR_ENABLE_SXP_INT 0x0008 /* Enable SXP interrupts */ +#define BIU_ICR_ENABLE_RISC_INT 0x0004 /* Enable Risc interrupts */ +#define BIU_ICR_ENABLE_ALL_INTS 0x0002 /* Global enable all inter */ +#define BIU_ICR_SOFT_RESET 0x0001 /* Soft Reset of ISP */ + +#define BIU2100_ICR_ENABLE_ALL_INTS 0x8000 +#define BIU2100_ICR_ENA_FPM_INT 0x0020 +#define BIU2100_ICR_ENA_FB_INT 0x0010 +#define BIU2100_ICR_ENA_RISC_INT 0x0008 +#define BIU2100_ICR_ENA_CDMA_INT 0x0004 +#define BIU2100_ICR_ENABLE_RXDMA_INT 0x0002 +#define BIU2100_ICR_ENABLE_TXDMA_INT 0x0001 +#define BIU2100_ICR_DISABLE_ALL_INTS 0x0000 + +#define ENABLE_INTS(isp) (IS_SCSI(isp))? \ + ISP_WRITE(isp, BIU_ICR, BIU_ICR_ENABLE_RISC_INT | BIU_ICR_ENABLE_ALL_INTS) : \ + ISP_WRITE(isp, BIU_ICR, BIU2100_ICR_ENA_RISC_INT | BIU2100_ICR_ENABLE_ALL_INTS) + +#define INTS_ENABLED(isp) ((IS_SCSI(isp))? \ + (ISP_READ(isp, BIU_ICR) & (BIU_ICR_ENABLE_RISC_INT|BIU_ICR_ENABLE_ALL_INTS)) :\ + (ISP_READ(isp, BIU_ICR) & \ + (BIU2100_ICR_ENA_RISC_INT|BIU2100_ICR_ENABLE_ALL_INTS))) + +#define DISABLE_INTS(isp) ISP_WRITE(isp, BIU_ICR, 0) + +/* BUS STATUS REGISTER */ +#define BIU_ISR_DMA_INT 0x0020 /* DMA interrupt pending */ +#define BIU_ISR_CDMA_INT 0x0010 /* CDMA interrupt pending */ +#define BIU_ISR_SXP_INT 0x0008 /* SXP interrupt pending */ +#define BIU_ISR_RISC_INT 0x0004 /* Risc interrupt pending */ +#define BIU_ISR_IPEND 0x0002 /* Global interrupt pending */ + +#define BIU2100_ISR_INT_PENDING 0x8000 /* Global interrupt pending */ +#define BIU2100_ISR_FPM_INT 0x0020 /* FPM interrupt pending */ +#define BIU2100_ISR_FB_INT 0x0010 /* FB interrupt pending */ +#define BIU2100_ISR_RISC_INT 0x0008 /* Risc interrupt pending */ +#define BIU2100_ISR_CDMA_INT 0x0004 /* CDMA interrupt pending */ +#define BIU2100_ISR_RXDMA_INT_PENDING 0x0002 /* Global interrupt pending */ +#define BIU2100_ISR_TXDMA_INT_PENDING 0x0001 /* Global interrupt pending */ + +#define INT_PENDING(isp, isr) (IS_FC(isp)? \ + ((isr & BIU2100_ISR_RISC_INT) != 0) : ((isr & BIU_ISR_RISC_INT) != 0)) + +#define INT_PENDING_MASK(isp) \ + (IS_FC(isp)? BIU2100_ISR_RISC_INT: BIU_ISR_RISC_INT) + +/* BUS SEMAPHORE REGISTER */ +#define BIU_SEMA_STATUS 0x0002 /* Semaphore Status Bit */ +#define BIU_SEMA_LOCK 0x0001 /* Semaphore Lock Bit */ + +/* NVRAM SEMAPHORE REGISTER */ +#define BIU_NVRAM_CLOCK 0x0001 +#define BIU_NVRAM_SELECT 0x0002 +#define BIU_NVRAM_DATAOUT 0x0004 +#define BIU_NVRAM_DATAIN 0x0008 +#define BIU_NVRAM_BUSY 0x0080 /* 2322/24xx only */ +#define ISP_NVRAM_READ 6 + +/* COMNMAND && DATA DMA CONFIGURATION REGISTER */ +#define DMA_ENABLE_SXP_DMA 0x0008 /* Enable SXP to DMA Data */ +#define DMA_ENABLE_INTS 0x0004 /* Enable interrupts to RISC */ +#define DMA_ENABLE_BURST 0x0002 /* Enable Bus burst trans */ +#define DMA_DMA_DIRECTION 0x0001 /* + * Set DMA direction: + * 0 - DMA FIFO to host + * 1 - Host to DMA FIFO + */ + +/* COMMAND && DATA DMA CONTROL REGISTER */ +#define DMA_CNTRL_SUSPEND_CHAN 0x0010 /* Suspend DMA transfer */ +#define DMA_CNTRL_CLEAR_CHAN 0x0008 /* + * Clear FIFO and DMA Channel, + * reset DMA registers + */ +#define DMA_CNTRL_CLEAR_FIFO 0x0004 /* Clear DMA FIFO */ +#define DMA_CNTRL_RESET_INT 0x0002 /* Clear DMA interrupt */ +#define DMA_CNTRL_STROBE 0x0001 /* Start DMA transfer */ + +/* + * Variants of same for 2100 + */ +#define DMA_CNTRL2100_CLEAR_CHAN 0x0004 +#define DMA_CNTRL2100_RESET_INT 0x0002 + + + +/* DMA STATUS REGISTER */ +#define DMA_SBUS_STATUS_PIPE_MASK 0x00C0 /* DMA Pipeline status mask */ +#define DMA_SBUS_STATUS_CHAN_MASK 0x0030 /* Channel status mask */ +#define DMA_SBUS_STATUS_BUS_PARITY 0x0008 /* Parity Error on bus */ +#define DMA_SBUS_STATUS_BUS_ERR 0x0004 /* Error Detected on bus */ +#define DMA_SBUS_STATUS_TERM_COUNT 0x0002 /* DMA Transfer Completed */ +#define DMA_SBUS_STATUS_INTERRUPT 0x0001 /* Enable DMA channel inter */ + +#define DMA_PCI_STATUS_INTERRUPT 0x8000 /* Enable DMA channel inter */ +#define DMA_PCI_STATUS_RETRY_STAT 0x4000 /* Retry status */ +#define DMA_PCI_STATUS_CHAN_MASK 0x3000 /* Channel status mask */ +#define DMA_PCI_STATUS_FIFO_OVR 0x0100 /* DMA FIFO overrun cond */ +#define DMA_PCI_STATUS_FIFO_UDR 0x0080 /* DMA FIFO underrun cond */ +#define DMA_PCI_STATUS_BUS_ERR 0x0040 /* Error Detected on bus */ +#define DMA_PCI_STATUS_BUS_PARITY 0x0020 /* Parity Error on bus */ +#define DMA_PCI_STATUS_CLR_PEND 0x0010 /* DMA clear pending */ +#define DMA_PCI_STATUS_TERM_COUNT 0x0008 /* DMA Transfer Completed */ +#define DMA_PCI_STATUS_DMA_SUSP 0x0004 /* DMA suspended */ +#define DMA_PCI_STATUS_PIPE_MASK 0x0003 /* DMA Pipeline status mask */ + +/* DMA Status Register, pipeline status bits */ +#define DMA_SBUS_PIPE_FULL 0x00C0 /* Both pipeline stages full */ +#define DMA_SBUS_PIPE_OVERRUN 0x0080 /* Pipeline overrun */ +#define DMA_SBUS_PIPE_STAGE1 0x0040 /* + * Pipeline stage 1 Loaded, + * stage 2 empty + */ +#define DMA_PCI_PIPE_FULL 0x0003 /* Both pipeline stages full */ +#define DMA_PCI_PIPE_OVERRUN 0x0002 /* Pipeline overrun */ +#define DMA_PCI_PIPE_STAGE1 0x0001 /* + * Pipeline stage 1 Loaded, + * stage 2 empty + */ +#define DMA_PIPE_EMPTY 0x0000 /* All pipeline stages empty */ + +/* DMA Status Register, channel status bits */ +#define DMA_SBUS_CHAN_SUSPEND 0x0030 /* Channel error or suspended */ +#define DMA_SBUS_CHAN_TRANSFER 0x0020 /* Chan transfer in progress */ +#define DMA_SBUS_CHAN_ACTIVE 0x0010 /* Chan trans to host active */ +#define DMA_PCI_CHAN_TRANSFER 0x3000 /* Chan transfer in progress */ +#define DMA_PCI_CHAN_SUSPEND 0x2000 /* Channel error or suspended */ +#define DMA_PCI_CHAN_ACTIVE 0x1000 /* Chan trans to host active */ +#define ISP_DMA_CHAN_IDLE 0x0000 /* Chan idle (normal comp) */ + + +/* DMA FIFO STATUS REGISTER */ +#define DMA_FIFO_STATUS_OVERRUN 0x0200 /* FIFO Overrun Condition */ +#define DMA_FIFO_STATUS_UNDERRUN 0x0100 /* FIFO Underrun Condition */ +#define DMA_FIFO_SBUS_COUNT_MASK 0x007F /* FIFO Byte count mask */ +#define DMA_FIFO_PCI_COUNT_MASK 0x00FF /* FIFO Byte count mask */ + +/* + * Mailbox Block Register Offsets + */ + +#define INMAILBOX0 (MBOX_BLOCK+0x0) +#define INMAILBOX1 (MBOX_BLOCK+0x2) +#define INMAILBOX2 (MBOX_BLOCK+0x4) +#define INMAILBOX3 (MBOX_BLOCK+0x6) +#define INMAILBOX4 (MBOX_BLOCK+0x8) +#define INMAILBOX5 (MBOX_BLOCK+0xA) +#define INMAILBOX6 (MBOX_BLOCK+0xC) +#define INMAILBOX7 (MBOX_BLOCK+0xE) + +#define OUTMAILBOX0 (MBOX_BLOCK+0x0) +#define OUTMAILBOX1 (MBOX_BLOCK+0x2) +#define OUTMAILBOX2 (MBOX_BLOCK+0x4) +#define OUTMAILBOX3 (MBOX_BLOCK+0x6) +#define OUTMAILBOX4 (MBOX_BLOCK+0x8) +#define OUTMAILBOX5 (MBOX_BLOCK+0xA) +#define OUTMAILBOX6 (MBOX_BLOCK+0xC) +#define OUTMAILBOX7 (MBOX_BLOCK+0xE) + +/* + * Strictly speaking, it's + * SCSI && 2100 : 8 MBOX registers + * 2200: 24 MBOX registers + * 2300: 32 MBOX registers + */ +#define MBOX_OFF(n) (MBOX_BLOCK + ((n) << 1)) +#define NMBOX(isp) \ + (((((isp)->isp_type & ISP_HA_SCSI) >= ISP_HA_SCSI_1040A) || \ + ((isp)->isp_type & ISP_HA_FC))? 12 : 6) +#define NMBOX_BMASK(isp) \ + (((((isp)->isp_type & ISP_HA_SCSI) >= ISP_HA_SCSI_1040A) || \ + ((isp)->isp_type & ISP_HA_FC))? 0xfff : 0x3f) + +#define MAX_MAILBOX(isp) ((IS_FC(isp))? 12 : 8) +#define MAILBOX_STORAGE 12 +typedef struct { + uint16_t param[MAILBOX_STORAGE]; + uint16_t ibits, obits; +} mbreg_t; + +/* + * Fibre Protocol Module and Frame Buffer Register Offsets/Definitions (2X00). + * NB: The RISC processor must be paused and the appropriate register + * bank selected via BIU2100_CSR bits. + */ + +#define FPM_DIAG_CONFIG (BIU_BLOCK + 0x96) +#define FPM_SOFT_RESET 0x0100 + +#define FBM_CMD (BIU_BLOCK + 0xB8) +#define FBMCMD_FIFO_RESET_ALL 0xA000 + + +/* + * SXP Block Register Offsets + */ +#define SXP_PART_ID (SXP_BLOCK+0x0) /* R : Part ID Code */ +#define SXP_CONFIG1 (SXP_BLOCK+0x2) /* RW*: Configuration Reg #1 */ +#define SXP_CONFIG2 (SXP_BLOCK+0x4) /* RW*: Configuration Reg #2 */ +#define SXP_CONFIG3 (SXP_BLOCK+0x6) /* RW*: Configuration Reg #2 */ +#define SXP_INSTRUCTION (SXP_BLOCK+0xC) /* RW*: Instruction Pointer */ +#define SXP_RETURN_ADDR (SXP_BLOCK+0x10) /* RW*: Return Address */ +#define SXP_COMMAND (SXP_BLOCK+0x14) /* RW*: Command */ +#define SXP_INTERRUPT (SXP_BLOCK+0x18) /* R : Interrupt */ +#define SXP_SEQUENCE (SXP_BLOCK+0x1C) /* RW*: Sequence */ +#define SXP_GROSS_ERR (SXP_BLOCK+0x1E) /* R : Gross Error */ +#define SXP_EXCEPTION (SXP_BLOCK+0x20) /* RW*: Exception Enable */ +#define SXP_OVERRIDE (SXP_BLOCK+0x24) /* RW*: Override */ +#define SXP_LIT_BASE (SXP_BLOCK+0x28) /* RW*: Literal Base */ +#define SXP_USER_FLAGS (SXP_BLOCK+0x2C) /* RW*: User Flags */ +#define SXP_USER_EXCEPT (SXP_BLOCK+0x30) /* RW*: User Exception */ +#define SXP_BREAKPOINT (SXP_BLOCK+0x34) /* RW*: Breakpoint */ +#define SXP_SCSI_ID (SXP_BLOCK+0x40) /* RW*: SCSI ID */ +#define SXP_DEV_CONFIG1 (SXP_BLOCK+0x42) /* RW*: Device Config Reg #1 */ +#define SXP_DEV_CONFIG2 (SXP_BLOCK+0x44) /* RW*: Device Config Reg #2 */ +#define SXP_PHASE_PTR (SXP_BLOCK+0x48) /* RW*: SCSI Phase Pointer */ +#define SXP_BUF_PTR (SXP_BLOCK+0x4C) /* RW*: SCSI Buffer Pointer */ +#define SXP_BUF_CTR (SXP_BLOCK+0x50) /* RW*: SCSI Buffer Counter */ +#define SXP_BUFFER (SXP_BLOCK+0x52) /* RW*: SCSI Buffer */ +#define SXP_BUF_BYTE (SXP_BLOCK+0x54) /* RW*: SCSI Buffer Byte */ +#define SXP_BUF_WD (SXP_BLOCK+0x56) /* RW*: SCSI Buffer Word */ +#define SXP_BUF_WD_TRAN (SXP_BLOCK+0x58) /* RW*: SCSI Buffer Wd xlate */ +#define SXP_FIFO (SXP_BLOCK+0x5A) /* RW*: SCSI FIFO */ +#define SXP_FIFO_STATUS (SXP_BLOCK+0x5C) /* RW*: SCSI FIFO Status */ +#define SXP_FIFO_TOP (SXP_BLOCK+0x5E) /* RW*: SCSI FIFO Top Resid */ +#define SXP_FIFO_BOTTOM (SXP_BLOCK+0x60) /* RW*: SCSI FIFO Bot Resid */ +#define SXP_TRAN_REG (SXP_BLOCK+0x64) /* RW*: SCSI Transferr Reg */ +#define SXP_TRAN_CNT_LO (SXP_BLOCK+0x68) /* RW*: SCSI Trans Count */ +#define SXP_TRAN_CNT_HI (SXP_BLOCK+0x6A) /* RW*: SCSI Trans Count */ +#define SXP_TRAN_CTR_LO (SXP_BLOCK+0x6C) /* RW*: SCSI Trans Counter */ +#define SXP_TRAN_CTR_HI (SXP_BLOCK+0x6E) /* RW*: SCSI Trans Counter */ +#define SXP_ARB_DATA (SXP_BLOCK+0x70) /* R : SCSI Arb Data */ +#define SXP_PINS_CTRL (SXP_BLOCK+0x72) /* RW*: SCSI Control Pins */ +#define SXP_PINS_DATA (SXP_BLOCK+0x74) /* RW*: SCSI Data Pins */ +#define SXP_PINS_DIFF (SXP_BLOCK+0x76) /* RW*: SCSI Diff Pins */ + +/* for 1080/1280/1240 only */ +#define SXP_BANK1_SELECT 0x100 + + +/* SXP CONF1 REGISTER */ +#define SXP_CONF1_ASYNCH_SETUP 0xF000 /* Asynchronous setup time */ +#define SXP_CONF1_SELECTION_UNIT 0x0000 /* Selection time unit */ +#define SXP_CONF1_SELECTION_TIMEOUT 0x0600 /* Selection timeout */ +#define SXP_CONF1_CLOCK_FACTOR 0x00E0 /* Clock factor */ +#define SXP_CONF1_SCSI_ID 0x000F /* SCSI id */ + +/* SXP CONF2 REGISTER */ +#define SXP_CONF2_DISABLE_FILTER 0x0040 /* Disable SCSI rec filters */ +#define SXP_CONF2_REQ_ACK_PULLUPS 0x0020 /* Enable req/ack pullups */ +#define SXP_CONF2_DATA_PULLUPS 0x0010 /* Enable data pullups */ +#define SXP_CONF2_CONFIG_AUTOLOAD 0x0008 /* Enable dev conf auto-load */ +#define SXP_CONF2_RESELECT 0x0002 /* Enable reselection */ +#define SXP_CONF2_SELECT 0x0001 /* Enable selection */ + +/* SXP INTERRUPT REGISTER */ +#define SXP_INT_PARITY_ERR 0x8000 /* Parity error detected */ +#define SXP_INT_GROSS_ERR 0x4000 /* Gross error detected */ +#define SXP_INT_FUNCTION_ABORT 0x2000 /* Last cmd aborted */ +#define SXP_INT_CONDITION_FAILED 0x1000 /* Last cond failed test */ +#define SXP_INT_FIFO_EMPTY 0x0800 /* SCSI FIFO is empty */ +#define SXP_INT_BUF_COUNTER_ZERO 0x0400 /* SCSI buf count == zero */ +#define SXP_INT_XFER_ZERO 0x0200 /* SCSI trans count == zero */ +#define SXP_INT_INT_PENDING 0x0080 /* SXP interrupt pending */ +#define SXP_INT_CMD_RUNNING 0x0040 /* SXP is running a command */ +#define SXP_INT_INT_RETURN_CODE 0x000F /* Interrupt return code */ + + +/* SXP GROSS ERROR REGISTER */ +#define SXP_GROSS_OFFSET_RESID 0x0040 /* Req/Ack offset not zero */ +#define SXP_GROSS_OFFSET_UNDERFLOW 0x0020 /* Req/Ack offset underflow */ +#define SXP_GROSS_OFFSET_OVERFLOW 0x0010 /* Req/Ack offset overflow */ +#define SXP_GROSS_FIFO_UNDERFLOW 0x0008 /* SCSI FIFO underflow */ +#define SXP_GROSS_FIFO_OVERFLOW 0x0004 /* SCSI FIFO overflow */ +#define SXP_GROSS_WRITE_ERR 0x0002 /* SXP and RISC wrote to reg */ +#define SXP_GROSS_ILLEGAL_INST 0x0001 /* Bad inst loaded into SXP */ + +/* SXP EXCEPTION REGISTER */ +#define SXP_EXCEPT_USER_0 0x8000 /* Enable user exception #0 */ +#define SXP_EXCEPT_USER_1 0x4000 /* Enable user exception #1 */ +#define PCI_SXP_EXCEPT_SCAM 0x0400 /* SCAM Selection enable */ +#define SXP_EXCEPT_BUS_FREE 0x0200 /* Enable Bus Free det */ +#define SXP_EXCEPT_TARGET_ATN 0x0100 /* Enable TGT mode atten det */ +#define SXP_EXCEPT_RESELECTED 0x0080 /* Enable ReSEL exc handling */ +#define SXP_EXCEPT_SELECTED 0x0040 /* Enable SEL exc handling */ +#define SXP_EXCEPT_ARBITRATION 0x0020 /* Enable ARB exc handling */ +#define SXP_EXCEPT_GROSS_ERR 0x0010 /* Enable gross error except */ +#define SXP_EXCEPT_BUS_RESET 0x0008 /* Enable Bus Reset except */ + + /* SXP OVERRIDE REGISTER */ +#define SXP_ORIDE_EXT_TRIGGER 0x8000 /* Enable external trigger */ +#define SXP_ORIDE_STEP 0x4000 /* Enable single step mode */ +#define SXP_ORIDE_BREAKPOINT 0x2000 /* Enable breakpoint reg */ +#define SXP_ORIDE_PIN_WRITE 0x1000 /* Enable write to SCSI pins */ +#define SXP_ORIDE_FORCE_OUTPUTS 0x0800 /* Force SCSI outputs on */ +#define SXP_ORIDE_LOOPBACK 0x0400 /* Enable SCSI loopback mode */ +#define SXP_ORIDE_PARITY_TEST 0x0200 /* Enable parity test mode */ +#define SXP_ORIDE_TRISTATE_ENA_PINS 0x0100 /* Tristate SCSI enable pins */ +#define SXP_ORIDE_TRISTATE_PINS 0x0080 /* Tristate SCSI pins */ +#define SXP_ORIDE_FIFO_RESET 0x0008 /* Reset SCSI FIFO */ +#define SXP_ORIDE_CMD_TERMINATE 0x0004 /* Terminate cur SXP com */ +#define SXP_ORIDE_RESET_REG 0x0002 /* Reset SXP registers */ +#define SXP_ORIDE_RESET_MODULE 0x0001 /* Reset SXP module */ + +/* SXP COMMANDS */ +#define SXP_RESET_BUS_CMD 0x300b + +/* SXP SCSI ID REGISTER */ +#define SXP_SELECTING_ID 0x0F00 /* (Re)Selecting id */ +#define SXP_SELECT_ID 0x000F /* Select id */ + +/* SXP DEV CONFIG1 REGISTER */ +#define SXP_DCONF1_SYNC_HOLD 0x7000 /* Synchronous data hold */ +#define SXP_DCONF1_SYNC_SETUP 0x0F00 /* Synchronous data setup */ +#define SXP_DCONF1_SYNC_OFFSET 0x000F /* Synchronous data offset */ + + +/* SXP DEV CONFIG2 REGISTER */ +#define SXP_DCONF2_FLAGS_MASK 0xF000 /* Device flags */ +#define SXP_DCONF2_WIDE 0x0400 /* Enable wide SCSI */ +#define SXP_DCONF2_PARITY 0x0200 /* Enable parity checking */ +#define SXP_DCONF2_BLOCK_MODE 0x0100 /* Enable blk mode xfr count */ +#define SXP_DCONF2_ASSERTION_MASK 0x0007 /* Assersion period mask */ + + +/* SXP PHASE POINTER REGISTER */ +#define SXP_PHASE_STATUS_PTR 0x1000 /* Status buffer offset */ +#define SXP_PHASE_MSG_IN_PTR 0x0700 /* Msg in buffer offset */ +#define SXP_PHASE_COM_PTR 0x00F0 /* Command buffer offset */ +#define SXP_PHASE_MSG_OUT_PTR 0x0007 /* Msg out buffer offset */ + + +/* SXP FIFO STATUS REGISTER */ +#define SXP_FIFO_TOP_RESID 0x8000 /* Top residue reg full */ +#define SXP_FIFO_ACK_RESID 0x4000 /* Wide transfers odd resid */ +#define SXP_FIFO_COUNT_MASK 0x001C /* Words in SXP FIFO */ +#define SXP_FIFO_BOTTOM_RESID 0x0001 /* Bottom residue reg full */ + + +/* SXP CONTROL PINS REGISTER */ +#define SXP_PINS_CON_PHASE 0x8000 /* Scsi phase valid */ +#define SXP_PINS_CON_PARITY_HI 0x0400 /* Parity pin */ +#define SXP_PINS_CON_PARITY_LO 0x0200 /* Parity pin */ +#define SXP_PINS_CON_REQ 0x0100 /* SCSI bus REQUEST */ +#define SXP_PINS_CON_ACK 0x0080 /* SCSI bus ACKNOWLEDGE */ +#define SXP_PINS_CON_RST 0x0040 /* SCSI bus RESET */ +#define SXP_PINS_CON_BSY 0x0020 /* SCSI bus BUSY */ +#define SXP_PINS_CON_SEL 0x0010 /* SCSI bus SELECT */ +#define SXP_PINS_CON_ATN 0x0008 /* SCSI bus ATTENTION */ +#define SXP_PINS_CON_MSG 0x0004 /* SCSI bus MESSAGE */ +#define SXP_PINS_CON_CD 0x0002 /* SCSI bus COMMAND */ +#define SXP_PINS_CON_IO 0x0001 /* SCSI bus INPUT */ + +/* + * Set the hold time for the SCSI Bus Reset to be 250 ms + */ +#define SXP_SCSI_BUS_RESET_HOLD_TIME 250 + +/* SXP DIFF PINS REGISTER */ +#define SXP_PINS_DIFF_SENSE 0x0200 /* DIFFSENS sig on SCSI bus */ +#define SXP_PINS_DIFF_MODE 0x0100 /* DIFFM signal */ +#define SXP_PINS_DIFF_ENABLE_OUTPUT 0x0080 /* Enable SXP SCSI data drv */ +#define SXP_PINS_DIFF_PINS_MASK 0x007C /* Differential control pins */ +#define SXP_PINS_DIFF_TARGET 0x0002 /* Enable SXP target mode */ +#define SXP_PINS_DIFF_INITIATOR 0x0001 /* Enable SXP initiator mode */ + +/* Ultra2 only */ +#define SXP_PINS_LVD_MODE 0x1000 +#define SXP_PINS_HVD_MODE 0x0800 +#define SXP_PINS_SE_MODE 0x0400 + +/* The above have to be put together with the DIFFM pin to make sense */ +#define ISP1080_LVD_MODE (SXP_PINS_LVD_MODE) +#define ISP1080_HVD_MODE (SXP_PINS_HVD_MODE|SXP_PINS_DIFF_MODE) +#define ISP1080_SE_MODE (SXP_PINS_SE_MODE) +#define ISP1080_MODE_MASK \ + (SXP_PINS_LVD_MODE|SXP_PINS_HVD_MODE|SXP_PINS_SE_MODE|SXP_PINS_DIFF_MODE) + +/* + * RISC and Host Command and Control Block Register Offsets + */ + +#define RISC_ACC RISC_BLOCK+0x0 /* RW*: Accumulator */ +#define RISC_R1 RISC_BLOCK+0x2 /* RW*: GP Reg R1 */ +#define RISC_R2 RISC_BLOCK+0x4 /* RW*: GP Reg R2 */ +#define RISC_R3 RISC_BLOCK+0x6 /* RW*: GP Reg R3 */ +#define RISC_R4 RISC_BLOCK+0x8 /* RW*: GP Reg R4 */ +#define RISC_R5 RISC_BLOCK+0xA /* RW*: GP Reg R5 */ +#define RISC_R6 RISC_BLOCK+0xC /* RW*: GP Reg R6 */ +#define RISC_R7 RISC_BLOCK+0xE /* RW*: GP Reg R7 */ +#define RISC_R8 RISC_BLOCK+0x10 /* RW*: GP Reg R8 */ +#define RISC_R9 RISC_BLOCK+0x12 /* RW*: GP Reg R9 */ +#define RISC_R10 RISC_BLOCK+0x14 /* RW*: GP Reg R10 */ +#define RISC_R11 RISC_BLOCK+0x16 /* RW*: GP Reg R11 */ +#define RISC_R12 RISC_BLOCK+0x18 /* RW*: GP Reg R12 */ +#define RISC_R13 RISC_BLOCK+0x1a /* RW*: GP Reg R13 */ +#define RISC_R14 RISC_BLOCK+0x1c /* RW*: GP Reg R14 */ +#define RISC_R15 RISC_BLOCK+0x1e /* RW*: GP Reg R15 */ +#define RISC_PSR RISC_BLOCK+0x20 /* RW*: Processor Status */ +#define RISC_IVR RISC_BLOCK+0x22 /* RW*: Interrupt Vector */ +#define RISC_PCR RISC_BLOCK+0x24 /* RW*: Processor Ctrl */ +#define RISC_RAR0 RISC_BLOCK+0x26 /* RW*: Ram Address #0 */ +#define RISC_RAR1 RISC_BLOCK+0x28 /* RW*: Ram Address #1 */ +#define RISC_LCR RISC_BLOCK+0x2a /* RW*: Loop Counter */ +#define RISC_PC RISC_BLOCK+0x2c /* R : Program Counter */ +#define RISC_MTR RISC_BLOCK+0x2e /* RW*: Memory Timing */ +#define RISC_MTR2100 RISC_BLOCK+0x30 + +#define RISC_EMB RISC_BLOCK+0x30 /* RW*: Ext Mem Boundary */ +#define DUAL_BANK 8 +#define RISC_SP RISC_BLOCK+0x32 /* RW*: Stack Pointer */ +#define RISC_HRL RISC_BLOCK+0x3e /* R *: Hardware Rev Level */ +#define HCCR RISC_BLOCK+0x40 /* RW : Host Command & Ctrl */ +#define BP0 RISC_BLOCK+0x42 /* RW : Processor Brkpt #0 */ +#define BP1 RISC_BLOCK+0x44 /* RW : Processor Brkpt #1 */ +#define TCR RISC_BLOCK+0x46 /* W : Test Control */ +#define TMR RISC_BLOCK+0x48 /* W : Test Mode */ + + +/* PROCESSOR STATUS REGISTER */ +#define RISC_PSR_FORCE_TRUE 0x8000 +#define RISC_PSR_LOOP_COUNT_DONE 0x4000 +#define RISC_PSR_RISC_INT 0x2000 +#define RISC_PSR_TIMER_ROLLOVER 0x1000 +#define RISC_PSR_ALU_OVERFLOW 0x0800 +#define RISC_PSR_ALU_MSB 0x0400 +#define RISC_PSR_ALU_CARRY 0x0200 +#define RISC_PSR_ALU_ZERO 0x0100 + +#define RISC_PSR_PCI_ULTRA 0x0080 +#define RISC_PSR_SBUS_ULTRA 0x0020 + +#define RISC_PSR_DMA_INT 0x0010 +#define RISC_PSR_SXP_INT 0x0008 +#define RISC_PSR_HOST_INT 0x0004 +#define RISC_PSR_INT_PENDING 0x0002 +#define RISC_PSR_FORCE_FALSE 0x0001 + + +/* Host Command and Control */ +#define HCCR_CMD_NOP 0x0000 /* NOP */ +#define HCCR_CMD_RESET 0x1000 /* Reset RISC */ +#define HCCR_CMD_PAUSE 0x2000 /* Pause RISC */ +#define HCCR_CMD_RELEASE 0x3000 /* Release Paused RISC */ +#define HCCR_CMD_STEP 0x4000 /* Single Step RISC */ +#define HCCR_2X00_DISABLE_PARITY_PAUSE 0x4001 /* + * Disable RISC pause on FPM + * parity error. + */ +#define HCCR_CMD_SET_HOST_INT 0x5000 /* Set Host Interrupt */ +#define HCCR_CMD_CLEAR_HOST_INT 0x6000 /* Clear Host Interrupt */ +#define HCCR_CMD_CLEAR_RISC_INT 0x7000 /* Clear RISC interrupt */ +#define HCCR_CMD_BREAKPOINT 0x8000 /* Change breakpoint enables */ +#define PCI_HCCR_CMD_BIOS 0x9000 /* Write BIOS (disable) */ +#define PCI_HCCR_CMD_PARITY 0xA000 /* Write parity enable */ +#define PCI_HCCR_CMD_PARITY_ERR 0xE000 /* Generate parity error */ +#define HCCR_CMD_TEST_MODE 0xF000 /* Set Test Mode */ + +#define ISP2100_HCCR_PARITY_ENABLE_2 0x0400 +#define ISP2100_HCCR_PARITY_ENABLE_1 0x0200 +#define ISP2100_HCCR_PARITY_ENABLE_0 0x0100 +#define ISP2100_HCCR_PARITY 0x0001 + +#define PCI_HCCR_PARITY 0x0400 /* Parity error flag */ +#define PCI_HCCR_PARITY_ENABLE_1 0x0200 /* Parity enable bank 1 */ +#define PCI_HCCR_PARITY_ENABLE_0 0x0100 /* Parity enable bank 0 */ + +#define HCCR_HOST_INT 0x0080 /* R : Host interrupt set */ +#define HCCR_RESET 0x0040 /* R : reset in progress */ +#define HCCR_PAUSE 0x0020 /* R : RISC paused */ + +#define PCI_HCCR_BIOS 0x0001 /* W : BIOS enable */ + +/* + * NVRAM Definitions (PCI cards only) + */ + +#define ISPBSMX(c, byte, shift, mask) \ + (((c)[(byte)] >> (shift)) & (mask)) +/* + * Qlogic 1020/1040 NVRAM is an array of 128 bytes. + * + * Some portion of the front of this is for general host adapter properties + * This is followed by an array of per-target parameters, and is tailed off + * with a checksum xor byte at offset 127. For non-byte entities data is + * stored in Little Endian order. + */ + +#define ISP_NVRAM_SIZE 128 + +#define ISP_NVRAM_VERSION(c) (c)[4] +#define ISP_NVRAM_FIFO_THRESHOLD(c) ISPBSMX(c, 5, 0, 0x03) +#define ISP_NVRAM_BIOS_DISABLE(c) ISPBSMX(c, 5, 2, 0x01) +#define ISP_NVRAM_HBA_ENABLE(c) ISPBSMX(c, 5, 3, 0x01) +#define ISP_NVRAM_INITIATOR_ID(c) ISPBSMX(c, 5, 4, 0x0f) +#define ISP_NVRAM_BUS_RESET_DELAY(c) (c)[6] +#define ISP_NVRAM_BUS_RETRY_COUNT(c) (c)[7] +#define ISP_NVRAM_BUS_RETRY_DELAY(c) (c)[8] +#define ISP_NVRAM_ASYNC_DATA_SETUP_TIME(c) ISPBSMX(c, 9, 0, 0x0f) +#define ISP_NVRAM_REQ_ACK_ACTIVE_NEGATION(c) ISPBSMX(c, 9, 4, 0x01) +#define ISP_NVRAM_DATA_LINE_ACTIVE_NEGATION(c) ISPBSMX(c, 9, 5, 0x01) +#define ISP_NVRAM_DATA_DMA_BURST_ENABLE(c) ISPBSMX(c, 9, 6, 0x01) +#define ISP_NVRAM_CMD_DMA_BURST_ENABLE(c) ISPBSMX(c, 9, 7, 0x01) +#define ISP_NVRAM_TAG_AGE_LIMIT(c) (c)[10] +#define ISP_NVRAM_LOWTRM_ENABLE(c) ISPBSMX(c, 11, 0, 0x01) +#define ISP_NVRAM_HITRM_ENABLE(c) ISPBSMX(c, 11, 1, 0x01) +#define ISP_NVRAM_PCMC_BURST_ENABLE(c) ISPBSMX(c, 11, 2, 0x01) +#define ISP_NVRAM_ENABLE_60_MHZ(c) ISPBSMX(c, 11, 3, 0x01) +#define ISP_NVRAM_SCSI_RESET_DISABLE(c) ISPBSMX(c, 11, 4, 0x01) +#define ISP_NVRAM_ENABLE_AUTO_TERM(c) ISPBSMX(c, 11, 5, 0x01) +#define ISP_NVRAM_FIFO_THRESHOLD_128(c) ISPBSMX(c, 11, 6, 0x01) +#define ISP_NVRAM_AUTO_TERM_SUPPORT(c) ISPBSMX(c, 11, 7, 0x01) +#define ISP_NVRAM_SELECTION_TIMEOUT(c) (((c)[12]) | ((c)[13] << 8)) +#define ISP_NVRAM_MAX_QUEUE_DEPTH(c) (((c)[14]) | ((c)[15] << 8)) +#define ISP_NVRAM_SCSI_BUS_SIZE(c) ISPBSMX(c, 16, 0, 0x01) +#define ISP_NVRAM_SCSI_BUS_TYPE(c) ISPBSMX(c, 16, 1, 0x01) +#define ISP_NVRAM_ADAPTER_CLK_SPEED(c) ISPBSMX(c, 16, 2, 0x01) +#define ISP_NVRAM_SOFT_TERM_SUPPORT(c) ISPBSMX(c, 16, 3, 0x01) +#define ISP_NVRAM_FLASH_ONBOARD(c) ISPBSMX(c, 16, 4, 0x01) +#define ISP_NVRAM_FAST_MTTR_ENABLE(c) ISPBSMX(c, 22, 0, 0x01) + +#define ISP_NVRAM_TARGOFF 28 +#define ISP_NVARM_TARGSIZE 6 +#define _IxT(tgt, tidx) \ + (ISP_NVRAM_TARGOFF + (ISP_NVARM_TARGSIZE * (tgt)) + (tidx)) +#define ISP_NVRAM_TGT_RENEG(c, t) ISPBSMX(c, _IxT(t, 0), 0, 0x01) +#define ISP_NVRAM_TGT_QFRZ(c, t) ISPBSMX(c, _IxT(t, 0), 1, 0x01) +#define ISP_NVRAM_TGT_ARQ(c, t) ISPBSMX(c, _IxT(t, 0), 2, 0x01) +#define ISP_NVRAM_TGT_TQING(c, t) ISPBSMX(c, _IxT(t, 0), 3, 0x01) +#define ISP_NVRAM_TGT_SYNC(c, t) ISPBSMX(c, _IxT(t, 0), 4, 0x01) +#define ISP_NVRAM_TGT_WIDE(c, t) ISPBSMX(c, _IxT(t, 0), 5, 0x01) +#define ISP_NVRAM_TGT_PARITY(c, t) ISPBSMX(c, _IxT(t, 0), 6, 0x01) +#define ISP_NVRAM_TGT_DISC(c, t) ISPBSMX(c, _IxT(t, 0), 7, 0x01) +#define ISP_NVRAM_TGT_EXEC_THROTTLE(c, t) ISPBSMX(c, _IxT(t, 1), 0, 0xff) +#define ISP_NVRAM_TGT_SYNC_PERIOD(c, t) ISPBSMX(c, _IxT(t, 2), 0, 0xff) +#define ISP_NVRAM_TGT_SYNC_OFFSET(c, t) ISPBSMX(c, _IxT(t, 3), 0, 0x0f) +#define ISP_NVRAM_TGT_DEVICE_ENABLE(c, t) ISPBSMX(c, _IxT(t, 3), 4, 0x01) +#define ISP_NVRAM_TGT_LUN_DISABLE(c, t) ISPBSMX(c, _IxT(t, 3), 5, 0x01) + +/* + * Qlogic 1080/1240 NVRAM is an array of 256 bytes. + * + * Some portion of the front of this is for general host adapter properties + * This is followed by an array of per-target parameters, and is tailed off + * with a checksum xor byte at offset 256. For non-byte entities data is + * stored in Little Endian order. + */ + +#define ISP1080_NVRAM_SIZE 256 + +#define ISP1080_NVRAM_VERSION(c) ISP_NVRAM_VERSION(c) + +/* Offset 5 */ +/* + uint8_t bios_configuration_mode :2; + uint8_t bios_disable :1; + uint8_t selectable_scsi_boot_enable :1; + uint8_t cd_rom_boot_enable :1; + uint8_t disable_loading_risc_code :1; + uint8_t enable_64bit_addressing :1; + uint8_t unused_7 :1; + */ + +/* Offsets 6, 7 */ +/* + uint8_t boot_lun_number :5; + uint8_t scsi_bus_number :1; + uint8_t unused_6 :1; + uint8_t unused_7 :1; + uint8_t boot_target_number :4; + uint8_t unused_12 :1; + uint8_t unused_13 :1; + uint8_t unused_14 :1; + uint8_t unused_15 :1; + */ + +#define ISP1080_NVRAM_HBA_ENABLE(c) ISPBSMX(c, 16, 3, 0x01) + +#define ISP1080_NVRAM_BURST_ENABLE(c) ISPBSMX(c, 16, 1, 0x01) +#define ISP1080_NVRAM_FIFO_THRESHOLD(c) ISPBSMX(c, 16, 4, 0x0f) + +#define ISP1080_NVRAM_AUTO_TERM_SUPPORT(c) ISPBSMX(c, 17, 7, 0x01) +#define ISP1080_NVRAM_BUS0_TERM_MODE(c) ISPBSMX(c, 17, 0, 0x03) +#define ISP1080_NVRAM_BUS1_TERM_MODE(c) ISPBSMX(c, 17, 2, 0x03) + +#define ISP1080_ISP_PARAMETER(c) \ + (((c)[18]) | ((c)[19] << 8)) + +#define ISP1080_FAST_POST(c) ISPBSMX(c, 20, 0, 0x01) +#define ISP1080_REPORT_LVD_TRANSITION(c) ISPBSMX(c, 20, 1, 0x01) + +#define ISP1080_BUS1_OFF 112 + +#define ISP1080_NVRAM_INITIATOR_ID(c, b) \ + ISPBSMX(c, ((b == 0)? 0 : ISP1080_BUS1_OFF) + 24, 0, 0x0f) +#define ISP1080_NVRAM_BUS_RESET_DELAY(c, b) \ + (c)[((b == 0)? 0 : ISP1080_BUS1_OFF) + 25] +#define ISP1080_NVRAM_BUS_RETRY_COUNT(c, b) \ + (c)[((b == 0)? 0 : ISP1080_BUS1_OFF) + 26] +#define ISP1080_NVRAM_BUS_RETRY_DELAY(c, b) \ + (c)[((b == 0)? 0 : ISP1080_BUS1_OFF) + 27] + +#define ISP1080_NVRAM_ASYNC_DATA_SETUP_TIME(c, b) \ + ISPBSMX(c, ((b == 0)? 0 : ISP1080_BUS1_OFF) + 28, 0, 0x0f) +#define ISP1080_NVRAM_REQ_ACK_ACTIVE_NEGATION(c, b) \ + ISPBSMX(c, ((b == 0)? 0 : ISP1080_BUS1_OFF) + 28, 4, 0x01) +#define ISP1080_NVRAM_DATA_LINE_ACTIVE_NEGATION(c, b) \ + ISPBSMX(c, ((b == 0)? 0 : ISP1080_BUS1_OFF) + 28, 5, 0x01) +#define ISP1080_NVRAM_SELECTION_TIMEOUT(c, b) \ + (((c)[((b == 0)? 0 : ISP1080_BUS1_OFF) + 30]) | \ + ((c)[((b == 0)? 0 : ISP1080_BUS1_OFF) + 31] << 8)) +#define ISP1080_NVRAM_MAX_QUEUE_DEPTH(c, b) \ + (((c)[((b == 0)? 0 : ISP1080_BUS1_OFF) + 32]) | \ + ((c)[((b == 0)? 0 : ISP1080_BUS1_OFF) + 33] << 8)) + +#define ISP1080_NVRAM_TARGOFF(b) \ + ((b == 0)? 40: (40 + ISP1080_BUS1_OFF)) +#define ISP1080_NVRAM_TARGSIZE 6 +#define _IxT8(tgt, tidx, b) \ + (ISP1080_NVRAM_TARGOFF((b)) + (ISP1080_NVRAM_TARGSIZE * (tgt)) + (tidx)) + +#define ISP1080_NVRAM_TGT_RENEG(c, t, b) \ + ISPBSMX(c, _IxT8(t, 0, (b)), 0, 0x01) +#define ISP1080_NVRAM_TGT_QFRZ(c, t, b) \ + ISPBSMX(c, _IxT8(t, 0, (b)), 1, 0x01) +#define ISP1080_NVRAM_TGT_ARQ(c, t, b) \ + ISPBSMX(c, _IxT8(t, 0, (b)), 2, 0x01) +#define ISP1080_NVRAM_TGT_TQING(c, t, b) \ + ISPBSMX(c, _IxT8(t, 0, (b)), 3, 0x01) +#define ISP1080_NVRAM_TGT_SYNC(c, t, b) \ + ISPBSMX(c, _IxT8(t, 0, (b)), 4, 0x01) +#define ISP1080_NVRAM_TGT_WIDE(c, t, b) \ + ISPBSMX(c, _IxT8(t, 0, (b)), 5, 0x01) +#define ISP1080_NVRAM_TGT_PARITY(c, t, b) \ + ISPBSMX(c, _IxT8(t, 0, (b)), 6, 0x01) +#define ISP1080_NVRAM_TGT_DISC(c, t, b) \ + ISPBSMX(c, _IxT8(t, 0, (b)), 7, 0x01) +#define ISP1080_NVRAM_TGT_EXEC_THROTTLE(c, t, b) \ + ISPBSMX(c, _IxT8(t, 1, (b)), 0, 0xff) +#define ISP1080_NVRAM_TGT_SYNC_PERIOD(c, t, b) \ + ISPBSMX(c, _IxT8(t, 2, (b)), 0, 0xff) +#define ISP1080_NVRAM_TGT_SYNC_OFFSET(c, t, b) \ + ISPBSMX(c, _IxT8(t, 3, (b)), 0, 0x0f) +#define ISP1080_NVRAM_TGT_DEVICE_ENABLE(c, t, b) \ + ISPBSMX(c, _IxT8(t, 3, (b)), 4, 0x01) +#define ISP1080_NVRAM_TGT_LUN_DISABLE(c, t, b) \ + ISPBSMX(c, _IxT8(t, 3, (b)), 5, 0x01) + +#define ISP12160_NVRAM_HBA_ENABLE ISP1080_NVRAM_HBA_ENABLE +#define ISP12160_NVRAM_BURST_ENABLE ISP1080_NVRAM_BURST_ENABLE +#define ISP12160_NVRAM_FIFO_THRESHOLD ISP1080_NVRAM_FIFO_THRESHOLD +#define ISP12160_NVRAM_AUTO_TERM_SUPPORT ISP1080_NVRAM_AUTO_TERM_SUPPORT +#define ISP12160_NVRAM_BUS0_TERM_MODE ISP1080_NVRAM_BUS0_TERM_MODE +#define ISP12160_NVRAM_BUS1_TERM_MODE ISP1080_NVRAM_BUS1_TERM_MODE +#define ISP12160_ISP_PARAMETER ISP12160_ISP_PARAMETER +#define ISP12160_FAST_POST ISP1080_FAST_POST +#define ISP12160_REPORT_LVD_TRANSITION ISP1080_REPORT_LVD_TRANSTION + +#define ISP12160_NVRAM_INITIATOR_ID \ + ISP1080_NVRAM_INITIATOR_ID +#define ISP12160_NVRAM_BUS_RESET_DELAY \ + ISP1080_NVRAM_BUS_RESET_DELAY +#define ISP12160_NVRAM_BUS_RETRY_COUNT \ + ISP1080_NVRAM_BUS_RETRY_COUNT +#define ISP12160_NVRAM_BUS_RETRY_DELAY \ + ISP1080_NVRAM_BUS_RETRY_DELAY +#define ISP12160_NVRAM_ASYNC_DATA_SETUP_TIME \ + ISP1080_NVRAM_ASYNC_DATA_SETUP_TIME +#define ISP12160_NVRAM_REQ_ACK_ACTIVE_NEGATION \ + ISP1080_NVRAM_REQ_ACK_ACTIVE_NEGATION +#define ISP12160_NVRAM_DATA_LINE_ACTIVE_NEGATION \ + ISP1080_NVRAM_DATA_LINE_ACTIVE_NEGATION +#define ISP12160_NVRAM_SELECTION_TIMEOUT \ + ISP1080_NVRAM_SELECTION_TIMEOUT +#define ISP12160_NVRAM_MAX_QUEUE_DEPTH \ + ISP1080_NVRAM_MAX_QUEUE_DEPTH + + +#define ISP12160_BUS0_OFF 24 +#define ISP12160_BUS1_OFF 136 + +#define ISP12160_NVRAM_TARGOFF(b) \ + (((b == 0)? ISP12160_BUS0_OFF : ISP12160_BUS1_OFF) + 16) + +#define ISP12160_NVRAM_TARGSIZE 6 +#define _IxT16(tgt, tidx, b) \ + (ISP12160_NVRAM_TARGOFF((b))+(ISP12160_NVRAM_TARGSIZE * (tgt))+(tidx)) + +#define ISP12160_NVRAM_TGT_RENEG(c, t, b) \ + ISPBSMX(c, _IxT16(t, 0, (b)), 0, 0x01) +#define ISP12160_NVRAM_TGT_QFRZ(c, t, b) \ + ISPBSMX(c, _IxT16(t, 0, (b)), 1, 0x01) +#define ISP12160_NVRAM_TGT_ARQ(c, t, b) \ + ISPBSMX(c, _IxT16(t, 0, (b)), 2, 0x01) +#define ISP12160_NVRAM_TGT_TQING(c, t, b) \ + ISPBSMX(c, _IxT16(t, 0, (b)), 3, 0x01) +#define ISP12160_NVRAM_TGT_SYNC(c, t, b) \ + ISPBSMX(c, _IxT16(t, 0, (b)), 4, 0x01) +#define ISP12160_NVRAM_TGT_WIDE(c, t, b) \ + ISPBSMX(c, _IxT16(t, 0, (b)), 5, 0x01) +#define ISP12160_NVRAM_TGT_PARITY(c, t, b) \ + ISPBSMX(c, _IxT16(t, 0, (b)), 6, 0x01) +#define ISP12160_NVRAM_TGT_DISC(c, t, b) \ + ISPBSMX(c, _IxT16(t, 0, (b)), 7, 0x01) + +#define ISP12160_NVRAM_TGT_EXEC_THROTTLE(c, t, b) \ + ISPBSMX(c, _IxT16(t, 1, (b)), 0, 0xff) +#define ISP12160_NVRAM_TGT_SYNC_PERIOD(c, t, b) \ + ISPBSMX(c, _IxT16(t, 2, (b)), 0, 0xff) + +#define ISP12160_NVRAM_TGT_SYNC_OFFSET(c, t, b) \ + ISPBSMX(c, _IxT16(t, 3, (b)), 0, 0x1f) +#define ISP12160_NVRAM_TGT_DEVICE_ENABLE(c, t, b) \ + ISPBSMX(c, _IxT16(t, 3, (b)), 5, 0x01) + +#define ISP12160_NVRAM_PPR_OPTIONS(c, t, b) \ + ISPBSMX(c, _IxT16(t, 4, (b)), 0, 0x0f) +#define ISP12160_NVRAM_PPR_WIDTH(c, t, b) \ + ISPBSMX(c, _IxT16(t, 4, (b)), 4, 0x03) +#define ISP12160_NVRAM_PPR_ENABLE(c, t, b) \ + ISPBSMX(c, _IxT16(t, 4, (b)), 7, 0x01) + +/* + * Qlogic 2XXX NVRAM is an array of 256 bytes. + * + * Some portion of the front of this is for general RISC engine parameters, + * mostly reflecting the state of the last INITIALIZE FIRMWARE mailbox command. + * + * This is followed by some general host adapter parameters, and ends with + * a checksum xor byte at offset 255. For non-byte entities data is stored + * in Little Endian order. + */ +#define ISP2100_NVRAM_SIZE 256 +/* ISP_NVRAM_VERSION is in same overall place */ +#define ISP2100_NVRAM_RISCVER(c) (c)[6] +#define ISP2100_NVRAM_OPTIONS(c) ((c)[8] | ((c)[9] << 8)) +#define ISP2100_NVRAM_MAXFRAMELENGTH(c) (((c)[10]) | ((c)[11] << 8)) +#define ISP2100_NVRAM_MAXIOCBALLOCATION(c) (((c)[12]) | ((c)[13] << 8)) +#define ISP2100_NVRAM_EXECUTION_THROTTLE(c) (((c)[14]) | ((c)[15] << 8)) +#define ISP2100_NVRAM_RETRY_COUNT(c) (c)[16] +#define ISP2100_NVRAM_RETRY_DELAY(c) (c)[17] + +#define ISP2100_NVRAM_PORT_NAME(c) (\ + (((uint64_t)(c)[18]) << 56) | \ + (((uint64_t)(c)[19]) << 48) | \ + (((uint64_t)(c)[20]) << 40) | \ + (((uint64_t)(c)[21]) << 32) | \ + (((uint64_t)(c)[22]) << 24) | \ + (((uint64_t)(c)[23]) << 16) | \ + (((uint64_t)(c)[24]) << 8) | \ + (((uint64_t)(c)[25]) << 0)) + +#define ISP2100_NVRAM_HARDLOOPID(c) ((c)[26] | ((c)[27] << 8)) +#define ISP2100_NVRAM_TOV(c) ((c)[29]) + +#define ISP2100_NVRAM_NODE_NAME(c) (\ + (((uint64_t)(c)[30]) << 56) | \ + (((uint64_t)(c)[31]) << 48) | \ + (((uint64_t)(c)[32]) << 40) | \ + (((uint64_t)(c)[33]) << 32) | \ + (((uint64_t)(c)[34]) << 24) | \ + (((uint64_t)(c)[35]) << 16) | \ + (((uint64_t)(c)[36]) << 8) | \ + (((uint64_t)(c)[37]) << 0)) + +#define ISP2100_XFW_OPTIONS(c) ((c)[38] | ((c)[39] << 8)) + +#define ISP2100_RACC_TIMER(c) (c)[40] +#define ISP2100_IDELAY_TIMER(c) (c)[41] + +#define ISP2100_ZFW_OPTIONS(c) ((c)[42] | ((c)[43] << 8)) + +#define ISP2100_SERIAL_LINK(c) ((c)[68] | ((c)[69] << 8)) + +#define ISP2100_NVRAM_HBA_OPTIONS(c) ((c)[70] | ((c)[71] << 8)) +#define ISP2100_NVRAM_HBA_DISABLE(c) ISPBSMX(c, 70, 0, 0x01) +#define ISP2100_NVRAM_BIOS_DISABLE(c) ISPBSMX(c, 70, 1, 0x01) +#define ISP2100_NVRAM_LUN_DISABLE(c) ISPBSMX(c, 70, 2, 0x01) +#define ISP2100_NVRAM_ENABLE_SELECT_BOOT(c) ISPBSMX(c, 70, 3, 0x01) +#define ISP2100_NVRAM_DISABLE_CODELOAD(c) ISPBSMX(c, 70, 4, 0x01) +#define ISP2100_NVRAM_SET_CACHELINESZ(c) ISPBSMX(c, 70, 5, 0x01) + +#define ISP2100_NVRAM_BOOT_NODE_NAME(c) (\ + (((uint64_t)(c)[72]) << 56) | \ + (((uint64_t)(c)[73]) << 48) | \ + (((uint64_t)(c)[74]) << 40) | \ + (((uint64_t)(c)[75]) << 32) | \ + (((uint64_t)(c)[76]) << 24) | \ + (((uint64_t)(c)[77]) << 16) | \ + (((uint64_t)(c)[78]) << 8) | \ + (((uint64_t)(c)[79]) << 0)) + +#define ISP2100_NVRAM_BOOT_LUN(c) (c)[80] +#define ISP2100_RESET_DELAY(c) (c)[81] + +#define ISP2100_HBA_FEATURES(c) ((c)[232] | ((c)[233] << 8)) + +/* + * Firmware Crash Dump + * + * QLogic needs specific information format when they look at firmware crashes. + * + * This is incredibly kernel memory consumptive (to say the least), so this + * code is only compiled in when needed. + */ + +#define QLA2200_RISC_IMAGE_DUMP_SIZE \ + (1 * sizeof (uint16_t)) + /* 'used' flag (also HBA type) */ \ + (352 * sizeof (uint16_t)) + /* RISC registers */ \ + (61440 * sizeof (uint16_t)) /* RISC SRAM (offset 0x1000..0xffff) */ +#define QLA2300_RISC_IMAGE_DUMP_SIZE \ + (1 * sizeof (uint16_t)) + /* 'used' flag (also HBA type) */ \ + (464 * sizeof (uint16_t)) + /* RISC registers */ \ + (63488 * sizeof (uint16_t)) + /* RISC SRAM (0x0800..0xffff) */ \ + (4096 * sizeof (uint16_t)) + /* RISC SRAM (0x10000..0x10FFF) */ \ + (61440 * sizeof (uint16_t)) /* RISC SRAM (0x11000..0x1FFFF) */ +/* the larger of the two */ +#define ISP_CRASH_IMAGE_SIZE QLA2300_RISC_IMAGE_DUMP_SIZE +#endif /* _ISPREG_H */ diff --git a/sys/dev/isp/ispvar.h b/sys/dev/isp/ispvar.h new file mode 100644 index 000000000000..5ea8e8a7c415 --- /dev/null +++ b/sys/dev/isp/ispvar.h @@ -0,0 +1,892 @@ +/* $FreeBSD$ */ +/*- + * Soft Definitions for for Qlogic ISP SCSI adapters. + * + * Copyright (c) 1997-2006 by Matthew Jacob + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _ISPVAR_H +#define _ISPVAR_H + +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include <dev/ic/ispmbox.h> +#endif +#ifdef __FreeBSD__ +#include <dev/isp/ispmbox.h> +#endif +#ifdef __linux__ +#include "ispmbox.h" +#endif +#ifdef __svr4__ +#include "ispmbox.h" +#endif + +#define ISP_CORE_VERSION_MAJOR 2 +#define ISP_CORE_VERSION_MINOR 11 + +/* + * Vector for bus specific code to provide specific services. + */ +typedef struct ispsoftc ispsoftc_t; +struct ispmdvec { + int (*dv_rd_isr) + (ispsoftc_t *, uint16_t *, uint16_t *, uint16_t *); + uint16_t (*dv_rd_reg) (ispsoftc_t *, int); + void (*dv_wr_reg) (ispsoftc_t *, int, uint16_t); + int (*dv_mbxdma) (ispsoftc_t *); + int (*dv_dmaset) + (ispsoftc_t *, XS_T *, ispreq_t *, uint16_t *, uint16_t); + void (*dv_dmaclr) (ispsoftc_t *, XS_T *, uint16_t); + void (*dv_reset0) (ispsoftc_t *); + void (*dv_reset1) (ispsoftc_t *); + void (*dv_dregs) (ispsoftc_t *, const char *); + uint16_t *dv_ispfw; /* ptr to f/w */ + uint16_t dv_conf1; + uint16_t dv_clock; /* clock frequency */ +}; + +/* + * Overall parameters + */ +#define MAX_TARGETS 16 +#define MAX_FC_TARG 256 +#define ISP_MAX_TARGETS(isp) (IS_FC(isp)? MAX_FC_TARG : MAX_TARGETS) +#define ISP_MAX_LUNS(isp) (isp)->isp_maxluns + +/* + * Macros to access ISP registers through bus specific layers- + * mostly wrappers to vector through the mdvec structure. + */ +#define ISP_READ_ISR(isp, isrp, semap, mbox0p) \ + (*(isp)->isp_mdvec->dv_rd_isr)(isp, isrp, semap, mbox0p) + +#define ISP_READ(isp, reg) \ + (*(isp)->isp_mdvec->dv_rd_reg)((isp), (reg)) + +#define ISP_WRITE(isp, reg, val) \ + (*(isp)->isp_mdvec->dv_wr_reg)((isp), (reg), (val)) + +#define ISP_MBOXDMASETUP(isp) \ + (*(isp)->isp_mdvec->dv_mbxdma)((isp)) + +#define ISP_DMASETUP(isp, xs, req, iptrp, optr) \ + (*(isp)->isp_mdvec->dv_dmaset)((isp), (xs), (req), (iptrp), (optr)) + +#define ISP_DMAFREE(isp, xs, hndl) \ + if ((isp)->isp_mdvec->dv_dmaclr) \ + (*(isp)->isp_mdvec->dv_dmaclr)((isp), (xs), (hndl)) + +#define ISP_RESET0(isp) \ + if ((isp)->isp_mdvec->dv_reset0) (*(isp)->isp_mdvec->dv_reset0)((isp)) +#define ISP_RESET1(isp) \ + if ((isp)->isp_mdvec->dv_reset1) (*(isp)->isp_mdvec->dv_reset1)((isp)) +#define ISP_DUMPREGS(isp, m) \ + if ((isp)->isp_mdvec->dv_dregs) (*(isp)->isp_mdvec->dv_dregs)((isp),(m)) + +#define ISP_SETBITS(isp, reg, val) \ + (*(isp)->isp_mdvec->dv_wr_reg)((isp), (reg), ISP_READ((isp), (reg)) | (val)) + +#define ISP_CLRBITS(isp, reg, val) \ + (*(isp)->isp_mdvec->dv_wr_reg)((isp), (reg), ISP_READ((isp), (reg)) & ~(val)) + +/* + * The MEMORYBARRIER macro is defined per platform (to provide synchronization + * on Request and Response Queues, Scratch DMA areas, and Registers) + * + * Defined Memory Barrier Synchronization Types + */ +#define SYNC_REQUEST 0 /* request queue synchronization */ +#define SYNC_RESULT 1 /* result queue synchronization */ +#define SYNC_SFORDEV 2 /* scratch, sync for ISP */ +#define SYNC_SFORCPU 3 /* scratch, sync for CPU */ +#define SYNC_REG 4 /* for registers */ + +/* + * Request/Response Queue defines and macros. + * The maximum is defined per platform (and can be based on board type). + */ +/* This is the size of a queue entry (request and response) */ +#define QENTRY_LEN 64 +/* Both request and result queue length must be a power of two */ +#define RQUEST_QUEUE_LEN(x) MAXISPREQUEST(x) +#ifdef ISP_TARGET_MODE +#define RESULT_QUEUE_LEN(x) MAXISPREQUEST(x) +#else +#define RESULT_QUEUE_LEN(x) \ + (((MAXISPREQUEST(x) >> 2) < 64)? 64 : MAXISPREQUEST(x) >> 2) +#endif +#define ISP_QUEUE_ENTRY(q, idx) (((uint8_t *)q) + ((idx) * QENTRY_LEN)) +#define ISP_QUEUE_SIZE(n) ((n) * QENTRY_LEN) +#define ISP_NXT_QENTRY(idx, qlen) (((idx) + 1) & ((qlen)-1)) +#define ISP_QFREE(in, out, qlen) \ + ((in == out)? (qlen - 1) : ((in > out)? \ + ((qlen - 1) - (in - out)) : (out - in - 1))) +#define ISP_QAVAIL(isp) \ + ISP_QFREE(isp->isp_reqidx, isp->isp_reqodx, RQUEST_QUEUE_LEN(isp)) + +#define ISP_ADD_REQUEST(isp, nxti) \ + MEMORYBARRIER(isp, SYNC_REQUEST, isp->isp_reqidx, QENTRY_LEN); \ + WRITE_REQUEST_QUEUE_IN_POINTER(isp, nxti); \ + isp->isp_reqidx = nxti + +/* + * SCSI Specific Host Adapter Parameters- per bus, per target + */ +typedef struct { + uint32_t isp_gotdparms : 1, + isp_req_ack_active_neg : 1, + isp_data_line_active_neg: 1, + isp_cmd_dma_burst_enable: 1, + isp_data_dma_burst_enabl: 1, + isp_fifo_threshold : 3, + isp_ultramode : 1, + isp_diffmode : 1, + isp_lvdmode : 1, + isp_fast_mttr : 1, /* fast sram */ + isp_initiator_id : 4, + isp_async_data_setup : 4; + uint16_t isp_selection_timeout; + uint16_t isp_max_queue_depth; + uint8_t isp_tag_aging; + uint8_t isp_bus_reset_delay; + uint8_t isp_retry_count; + uint8_t isp_retry_delay; + struct { + uint32_t + exc_throttle : 8, + : 1, + dev_enable : 1, /* ignored */ + dev_update : 1, + dev_refresh : 1, + actv_offset : 4, + goal_offset : 4, + nvrm_offset : 4; + uint8_t actv_period; /* current sync period */ + uint8_t goal_period; /* goal sync period */ + uint8_t nvrm_period; /* nvram sync period */ + uint16_t actv_flags; /* current device flags */ + uint16_t goal_flags; /* goal device flags */ + uint16_t nvrm_flags; /* nvram device flags */ + } isp_devparam[MAX_TARGETS]; +} sdparam; + +/* + * Device Flags + */ +#define DPARM_DISC 0x8000 +#define DPARM_PARITY 0x4000 +#define DPARM_WIDE 0x2000 +#define DPARM_SYNC 0x1000 +#define DPARM_TQING 0x0800 +#define DPARM_ARQ 0x0400 +#define DPARM_QFRZ 0x0200 +#define DPARM_RENEG 0x0100 +#define DPARM_NARROW 0x0080 +#define DPARM_ASYNC 0x0040 +#define DPARM_PPR 0x0020 +#define DPARM_DEFAULT (0xFF00 & ~DPARM_QFRZ) +#define DPARM_SAFE_DFLT (DPARM_DEFAULT & ~(DPARM_WIDE|DPARM_SYNC|DPARM_TQING)) + +/* technically, not really correct, as they need to be rated based upon clock */ +#define ISP_80M_SYNCPARMS 0x0c09 +#define ISP_40M_SYNCPARMS 0x0c0a +#define ISP_20M_SYNCPARMS 0x0c0c +#define ISP_20M_SYNCPARMS_1040 0x080c +#define ISP_10M_SYNCPARMS 0x0c19 +#define ISP_08M_SYNCPARMS 0x0c25 +#define ISP_05M_SYNCPARMS 0x0c32 +#define ISP_04M_SYNCPARMS 0x0c41 + +/* + * Fibre Channel Specifics + */ +#define FL_PORT_ID 0x7e /* FL_Port Special ID */ +#define FC_PORT_ID 0x7f /* Fabric Controller Special ID */ +#define FC_SNS_ID 0x80 /* SNS Server Special ID */ + +/* #define ISP_USE_GA_NXT 1 */ /* Use GA_NXT with switches */ +#ifndef GA_NXT_MAX +#define GA_NXT_MAX 256 +#endif + +typedef struct { + uint32_t : 13, + isp_gbspeed : 3, + : 1, + isp_iid_set : 1, + loop_seen_once : 1, + isp_loopstate : 4, /* Current Loop State */ + isp_fwstate : 4, /* ISP F/W state */ + isp_gotdparms : 1, + isp_topo : 3, + isp_onfabric : 1; + uint32_t : 8, + isp_portid : 24; /* S_ID */ + uint16_t isp_fwoptions; + uint16_t isp_xfwoptions; + uint16_t isp_zfwoptions; + uint16_t isp_iid; /* 'initiator' id */ + uint16_t isp_loopid; /* hard loop id */ + uint16_t isp_fwattr; /* firmware attributes */ + uint16_t isp_execthrottle; + uint8_t isp_retry_delay; + uint8_t isp_retry_count; + uint8_t isp_reserved; + uint16_t isp_maxalloc; + uint16_t isp_maxfrmlen; + uint64_t isp_nodewwn; + uint64_t isp_portwwn; + /* + * Port Data Base. This is indexed by 'target', which is invariate. + * However, elements within can move around due to loop changes, + * so the actual loop ID passed to the F/W is in this structure. + * The first time the loop is seen up, loopid will match the index + * (except for fabric nodes which are above mapped above FC_SNS_ID + * and are completely virtual), but subsequent LIPs can cause things + * to move around. + */ + struct lportdb { + uint32_t loopid : 16, + : 2, + fc4_type : 4, + last_fabric_dev : 1, + relogin : 1, + force_logout : 1, + was_fabric_dev : 1, + fabric_dev : 1, + loggedin : 1, + roles : 2, + tvalid : 1, + valid : 1; + uint32_t port_type : 8, + portid : 24; + uint64_t node_wwn; + uint64_t port_wwn; + } portdb[MAX_FC_TARG], tport[FC_PORT_ID]; + + /* + * Scratch DMA mapped in area to fetch Port Database stuff, etc. + */ + void * isp_scratch; + XS_DMA_ADDR_T isp_scdma; +#ifdef ISP_FW_CRASH_DUMP + uint16_t * isp_dump_data; +#endif +} fcparam; + +#define FW_CONFIG_WAIT 0 +#define FW_WAIT_AL_PA 1 +#define FW_WAIT_LOGIN 2 +#define FW_READY 3 +#define FW_LOSS_OF_SYNC 4 +#define FW_ERROR 5 +#define FW_REINIT 6 +#define FW_NON_PART 7 + +#define LOOP_NIL 0 +#define LOOP_LIP_RCVD 1 +#define LOOP_PDB_RCVD 2 +#define LOOP_SCANNING_FABRIC 3 +#define LOOP_FSCAN_DONE 4 +#define LOOP_SCANNING_LOOP 5 +#define LOOP_LSCAN_DONE 6 +#define LOOP_SYNCING_PDB 7 +#define LOOP_READY 8 + +#define TOPO_NL_PORT 0 +#define TOPO_FL_PORT 1 +#define TOPO_N_PORT 2 +#define TOPO_F_PORT 3 +#define TOPO_PTP_STUB 4 + +/* + * Soft Structure per host adapter + */ +struct ispsoftc { + /* + * Platform (OS) specific data + */ + struct isposinfo isp_osinfo; + + /* + * Pointer to bus specific functions and data + */ + struct ispmdvec * isp_mdvec; + + /* + * (Mostly) nonvolatile state. Board specific parameters + * may contain some volatile state (e.g., current loop state). + */ + + void * isp_param; /* type specific */ + uint16_t isp_fwrev[3]; /* Loaded F/W revision */ + uint16_t isp_romfw_rev[3]; /* PROM F/W revision */ + uint16_t isp_maxcmds; /* max possible I/O cmds */ + uint8_t isp_type; /* HBA Chip Type */ + uint8_t isp_revision; /* HBA Chip H/W Revision */ + uint32_t isp_maxluns; /* maximum luns supported */ + + uint32_t isp_clock : 8, /* input clock */ + : 4, + isp_port : 1, /* 23XX only */ + isp_failed : 1, /* board failed */ + isp_open : 1, /* opened (ioctl) */ + isp_touched : 1, /* board ever seen? */ + isp_bustype : 1, /* SBus or PCI */ + isp_loaded_fw : 1, /* loaded firmware */ + isp_role : 2, /* roles supported */ + isp_dblev : 12; /* debug log mask */ + + uint32_t isp_confopts; /* config options */ + + uint16_t isp_rqstinrp; /* register for REQINP */ + uint16_t isp_rqstoutrp; /* register for REQOUTP */ + uint16_t isp_respinrp; /* register for RESINP */ + uint16_t isp_respoutrp; /* register for RESOUTP */ + + /* + * Instrumentation + */ + uint64_t isp_intcnt; /* total int count */ + uint64_t isp_intbogus; /* spurious int count */ + uint64_t isp_intmboxc; /* mbox completions */ + uint64_t isp_intoasync; /* other async */ + uint64_t isp_rsltccmplt; /* CMDs on result q */ + uint64_t isp_fphccmplt; /* CMDs via fastpost */ + uint16_t isp_rscchiwater; + uint16_t isp_fpcchiwater; + + /* + * Volatile state + */ + + volatile uint32_t : 8, + isp_mboxbsy : 1, /* mailbox command active */ + isp_state : 3, + isp_sendmarker : 2, /* send a marker entry */ + isp_update : 2, /* update parameters */ + isp_nactive : 16; /* how many commands active */ + volatile uint16_t isp_reqodx; /* index of last ISP pickup */ + volatile uint16_t isp_reqidx; /* index of next request */ + volatile uint16_t isp_residx; /* index of next result */ + volatile uint16_t isp_resodx; /* index of next result */ + volatile uint16_t isp_rspbsy; + volatile uint16_t isp_lasthdls; /* last handle seed */ + volatile uint16_t isp_obits; /* mailbox command output */ + volatile uint16_t isp_mboxtmp[MAILBOX_STORAGE]; + volatile uint16_t isp_lastmbxcmd; /* last mbox command sent */ + volatile uint16_t isp_mbxwrk0; + volatile uint16_t isp_mbxwrk1; + volatile uint16_t isp_mbxwrk2; + volatile uint16_t isp_mbxwrk8; + void * isp_mbxworkp; + + /* + * Active commands are stored here, indexed by handle functions. + */ + XS_T **isp_xflist; + +#ifdef ISP_TARGET_MODE + /* + * Active target commands are stored here, indexed by handle function. + */ + void **isp_tgtlist; +#endif + + /* + * request/result queue pointers and DMA handles for them. + */ + void * isp_rquest; + void * isp_result; + XS_DMA_ADDR_T isp_rquest_dma; + XS_DMA_ADDR_T isp_result_dma; +}; + +#define SDPARAM(isp) ((sdparam *) (isp)->isp_param) +#define FCPARAM(isp) ((fcparam *) (isp)->isp_param) + +/* + * ISP Driver Run States + */ +#define ISP_NILSTATE 0 +#define ISP_CRASHED 1 +#define ISP_RESETSTATE 2 +#define ISP_INITSTATE 3 +#define ISP_RUNSTATE 4 + +/* + * ISP Configuration Options + */ +#define ISP_CFG_NORELOAD 0x80 /* don't download f/w */ +#define ISP_CFG_NONVRAM 0x40 /* ignore NVRAM */ +#define ISP_CFG_TWOGB 0x20 /* force 2GB connection (23XX only) */ +#define ISP_CFG_ONEGB 0x10 /* force 1GB connection (23XX only) */ +#define ISP_CFG_FULL_DUPLEX 0x01 /* Full Duplex (Fibre Channel only) */ +#define ISP_CFG_PORT_PREF 0x0C /* Mask for Port Prefs (2200 only) */ +#define ISP_CFG_LPORT 0x00 /* prefer {N/F}L-Port connection */ +#define ISP_CFG_NPORT 0x04 /* prefer {N/F}-Port connection */ +#define ISP_CFG_NPORT_ONLY 0x08 /* insist on {N/F}-Port connection */ +#define ISP_CFG_LPORT_ONLY 0x0C /* insist on {N/F}L-Port connection */ +#define ISP_CFG_OWNWWPN 0x100 /* override NVRAM wwpn */ +#define ISP_CFG_OWNWWNN 0x200 /* override NVRAM wwnn */ +#define ISP_CFG_OWNFSZ 0x400 /* override NVRAM frame size */ +#define ISP_CFG_OWNLOOPID 0x800 /* override NVRAM loopid */ +#define ISP_CFG_OWNEXCTHROTTLE 0x1000 /* override NVRAM execution throttle */ + +/* + * Prior to calling isp_reset for the first time, the outer layer + * should set isp_role to one of NONE, INITIATOR, TARGET, BOTH. + * + * If you set ISP_ROLE_NONE, the cards will be reset, new firmware loaded, + * NVRAM read, and defaults set, but any further initialization (e.g. + * INITIALIZE CONTROL BLOCK commands for 2X00 cards) won't be done. + * + * If INITIATOR MODE isn't set, attempts to run commands will be stopped + * at isp_start and completed with the moral equivalent of SELECTION TIMEOUT. + * + * If TARGET MODE is set, it doesn't mean that the rest of target mode support + * needs to be enabled, or will even work. What happens with the 2X00 cards + * here is that if you have enabled it with TARGET MODE as part of the ICB + * options, but you haven't given the f/w any ram resources for ATIOs or + * Immediate Notifies, the f/w just handles what it can and you never see + * anything. Basically, it sends a single byte of data (the first byte, + * which you can set as part of the INITIALIZE CONTROL BLOCK command) for + * INQUIRY, and sends back QUEUE FULL status for any other command. + * + */ +#define ISP_ROLE_NONE 0x0 +#define ISP_ROLE_TARGET 0x1 +#define ISP_ROLE_INITIATOR 0x2 +#define ISP_ROLE_BOTH (ISP_ROLE_TARGET|ISP_ROLE_INITIATOR) +#define ISP_ROLE_EITHER ISP_ROLE_BOTH +#ifndef ISP_DEFAULT_ROLES +#define ISP_DEFAULT_ROLES ISP_ROLE_INITIATOR +#endif + + +/* + * Firmware related defines + */ +#define ISP_CODE_ORG 0x1000 /* default f/w code start */ +#define ISP_CODE_ORG_2300 0x0800 /* ..except for 2300s */ +#define ISP_FW_REV(maj, min, mic) ((maj << 24) | (min << 16) | mic) +#define ISP_FW_MAJOR(code) ((code >> 24) & 0xff) +#define ISP_FW_MINOR(code) ((code >> 16) & 0xff) +#define ISP_FW_MICRO(code) ((code >> 8) & 0xff) +#define ISP_FW_REVX(xp) ((xp[0]<<24) | (xp[1] << 16) | xp[2]) +#define ISP_FW_MAJORX(xp) (xp[0]) +#define ISP_FW_MINORX(xp) (xp[1]) +#define ISP_FW_MICROX(xp) (xp[2]) +#define ISP_FW_NEWER_THAN(i, major, minor, micro) \ + (ISP_FW_REVX((i)->isp_fwrev) > ISP_FW_REV(major, minor, micro)) + +/* + * Bus (implementation) types + */ +#define ISP_BT_PCI 0 /* PCI Implementations */ +#define ISP_BT_SBUS 1 /* SBus Implementations */ + +/* + * If we have not otherwise defined SBus support away make sure + * it is defined here such that the code is included as default + */ +#ifndef ISP_SBUS_SUPPORTED +#define ISP_SBUS_SUPPORTED 1 +#endif + +/* + * Chip Types + */ +#define ISP_HA_SCSI 0xf +#define ISP_HA_SCSI_UNKNOWN 0x1 +#define ISP_HA_SCSI_1020 0x2 +#define ISP_HA_SCSI_1020A 0x3 +#define ISP_HA_SCSI_1040 0x4 +#define ISP_HA_SCSI_1040A 0x5 +#define ISP_HA_SCSI_1040B 0x6 +#define ISP_HA_SCSI_1040C 0x7 +#define ISP_HA_SCSI_1240 0x8 +#define ISP_HA_SCSI_1080 0x9 +#define ISP_HA_SCSI_1280 0xa +#define ISP_HA_SCSI_10160 0xb +#define ISP_HA_SCSI_12160 0xc +#define ISP_HA_FC 0xf0 +#define ISP_HA_FC_2100 0x10 +#define ISP_HA_FC_2200 0x20 +#define ISP_HA_FC_2300 0x30 +#define ISP_HA_FC_2312 0x40 +#define ISP_HA_FC_2322 0x50 +#define ISP_HA_FC_2400 0x60 +#define ISP_HA_FC_2422 0x61 + +#define IS_SCSI(isp) (isp->isp_type & ISP_HA_SCSI) +#define IS_1240(isp) (isp->isp_type == ISP_HA_SCSI_1240) +#define IS_1080(isp) (isp->isp_type == ISP_HA_SCSI_1080) +#define IS_1280(isp) (isp->isp_type == ISP_HA_SCSI_1280) +#define IS_10160(isp) (isp->isp_type == ISP_HA_SCSI_10160) +#define IS_12160(isp) (isp->isp_type == ISP_HA_SCSI_12160) + +#define IS_12X0(isp) (IS_1240(isp) || IS_1280(isp)) +#define IS_1X160(isp) (IS_10160(isp) || IS_12160(isp)) +#define IS_DUALBUS(isp) (IS_12X0(isp) || IS_12160(isp)) +#define IS_ULTRA2(isp) (IS_1080(isp) || IS_1280(isp) || IS_1X160(isp)) +#define IS_ULTRA3(isp) (IS_1X160(isp)) + +#define IS_FC(isp) ((isp)->isp_type & ISP_HA_FC) +#define IS_2100(isp) ((isp)->isp_type == ISP_HA_FC_2100) +#define IS_2200(isp) ((isp)->isp_type == ISP_HA_FC_2200) +#define IS_23XX(isp) \ + ((isp)->isp_type >= ISP_HA_FC_2300 && (isp)->isp_type < ISP_HA_FC_2400) +#define IS_2300(isp) ((isp)->isp_type == ISP_HA_FC_2300) +#define IS_2312(isp) ((isp)->isp_type == ISP_HA_FC_2312) +#define IS_2322(isp) ((isp)->isp_type == ISP_HA_FC_2322) +#define IS_24XX(isp) ((isp)->isp_type >= ISP_HA_FC_2400) + +/* + * DMA related macros + */ +#define DMA_WD3(x) ((((uint64_t)x) >> 48) & 0xffff) +#define DMA_WD2(x) ((((uint64_t)x) >> 32) & 0xffff) +#define DMA_WD1(x) (((x) >> 16) & 0xffff) +#define DMA_WD0(x) (((x) & 0xffff)) + +#define DMA_LO32(x) ((uint32_t) (x)) +#define DMA_HI32(x) ((uint32_t)(((uint64_t)x) >> 32)) + +/* + * Core System Function Prototypes + */ + +/* + * Reset Hardware. Totally. Assumes that you'll follow this with + * a call to isp_init. + */ +void isp_reset(ispsoftc_t *); + +/* + * Initialize Hardware to known state + */ +void isp_init(ispsoftc_t *); + +/* + * Reset the ISP and call completion for any orphaned commands. + */ +void isp_reinit(ispsoftc_t *); + +#ifdef ISP_FW_CRASH_DUMP +/* + * Dump firmware entry point. + */ +void isp_fw_dump(ispsoftc_t *isp); +#endif + +/* + * Internal Interrupt Service Routine + * + * The outer layers do the spade work to get the appropriate status register, + * semaphore register and first mailbox register (if appropriate). This also + * means that most spurious/bogus interrupts not for us can be filtered first. + */ +void isp_intr(ispsoftc_t *, uint16_t, uint16_t, uint16_t); + + +/* + * Command Entry Point- Platform Dependent layers call into this + */ +int isp_start(XS_T *); + +/* these values are what isp_start returns */ +#define CMD_COMPLETE 101 /* command completed */ +#define CMD_EAGAIN 102 /* busy- maybe retry later */ +#define CMD_QUEUED 103 /* command has been queued for execution */ +#define CMD_RQLATER 104 /* requeue this command later */ + +/* + * Command Completion Point- Core layers call out from this with completed cmds + */ +void isp_done(XS_T *); + +/* + * Platform Dependent to External to Internal Control Function + * + * Assumes locks are held on entry. You should note that with many of + * these commands and locks may be released while this is occurring. + * + * A few notes about some of these functions: + * + * ISPCTL_FCLINK_TEST tests to make sure we have good fibre channel link. + * The argument is a pointer to an integer which is the time, in microseconds, + * we should wait to see whether we have good link. This test, if successful, + * lets us know our connection topology and our Loop ID/AL_PA and so on. + * You can't get anywhere without this. + * + * ISPCTL_SCAN_FABRIC queries the name server (if we're on a fabric) for + * all entities using the FC Generic Services subcommand GET ALL NEXT. + * For each found entity, an ISPASYNC_FABRICDEV event is generated (see + * below). + * + * ISPCTL_SCAN_LOOP does a local loop scan. This is only done if the connection + * topology is NL or FL port (private or public loop). Since the Qlogic f/w + * 'automatically' manages local loop connections, this function essentially + * notes the arrival, departure, and possible shuffling around of local loop + * entities. Thus for each arrival and departure this generates an isp_async + * event of ISPASYNC_PROMENADE (see below). + * + * ISPCTL_PDB_SYNC is somewhat misnamed. It actually is the final step, in + * order, of ISPCTL_FCLINK_TEST, ISPCTL_SCAN_FABRIC, and ISPCTL_SCAN_LOOP. + * The main purpose of ISPCTL_PDB_SYNC is to complete management of logging + * and logging out of fabric devices (if one is on a fabric) and then marking + * the 'loop state' as being ready to now be used for sending commands to + * devices. Originally fabric name server and local loop scanning were + * part of this function. It's now been separated to allow for finer control. + */ +typedef enum { + ISPCTL_RESET_BUS, /* Reset Bus */ + ISPCTL_RESET_DEV, /* Reset Device */ + ISPCTL_ABORT_CMD, /* Abort Command */ + ISPCTL_UPDATE_PARAMS, /* Update Operating Parameters (SCSI) */ + ISPCTL_FCLINK_TEST, /* Test FC Link Status */ + ISPCTL_SCAN_FABRIC, /* (Re)scan Fabric Name Server */ + ISPCTL_SCAN_LOOP, /* (Re)scan Local Loop */ + ISPCTL_PDB_SYNC, /* Synchronize Port Database */ + ISPCTL_SEND_LIP, /* Send a LIP */ + ISPCTL_GET_POSMAP, /* Get FC-AL position map */ + ISPCTL_RUN_MBOXCMD, /* run a mailbox command */ + ISPCTL_TOGGLE_TMODE, /* toggle target mode */ + ISPCTL_GET_PDB /* get a single port database entry */ +} ispctl_t; +int isp_control(ispsoftc_t *, ispctl_t, void *); + + +/* + * Platform Dependent to Internal to External Control Function + * (each platform must provide such a function) + * + * Assumes locks are held. + * + * A few notes about some of these functions: + * + * ISPASYNC_CHANGE_NOTIFY notifies the outer layer that a change has + * occurred that invalidates the list of fabric devices known and/or + * the list of known loop devices. The argument passed is a pointer + * whose values are defined below (local loop change, name server + * change, other). 'Other' may simply be a LIP, or a change in + * connection topology. + * + * ISPASYNC_FABRIC_DEV announces the next element in a list of + * fabric device names we're getting out of the name server. The + * argument points to a GET ALL NEXT response structure. The list + * is known to terminate with an entry that refers to ourselves. + * One of the main purposes of this function is to allow outer + * layers, which are OS dependent, to set policy as to which fabric + * devices might actually be logged into (and made visible) later + * at ISPCTL_PDB_SYNC time. Since there's a finite number of fabric + * devices that we can log into (256 less 3 'reserved' for F-port + * topologies), and fabrics can grow up to 8 million or so entries + * (24 bits of Port Address, less a wad of reserved spaces), clearly + * we had better let the OS determine login policy. + * + * ISPASYNC_PROMENADE has an argument that is a pointer to an integer which + * is an index into the portdb in the softc ('target'). Whether that entry's + * valid tag is set or not says whether something has arrived or departed. + * The name refers to a favorite pastime of many city dwellers- watching + * people come and go, talking of Michaelangelo, and so on.. + * + * ISPASYNC_UNHANDLED_RESPONSE gives outer layers a chance to parse a + * response queue entry not otherwise handled. The outer layer should + * return non-zero if it handled it. The 'arg' points to an unmassaged + * response queue entry. + */ + +typedef enum { + ISPASYNC_NEW_TGT_PARAMS, /* New Target Parameters Negotiated */ + ISPASYNC_BUS_RESET, /* Bus Was Reset */ + ISPASYNC_LOOP_DOWN, /* FC Loop Down */ + ISPASYNC_LOOP_UP, /* FC Loop Up */ + ISPASYNC_LIP, /* LIP Received */ + ISPASYNC_LOOP_RESET, /* Loop Reset Received */ + ISPASYNC_CHANGE_NOTIFY, /* FC Change Notification */ + ISPASYNC_FABRIC_DEV, /* FC Fabric Device Arrival */ + ISPASYNC_PROMENADE, /* FC Objects coming && going */ + ISPASYNC_TARGET_NOTIFY, /* target asynchronous notification event */ + ISPASYNC_TARGET_ACTION, /* target action requested */ + ISPASYNC_CONF_CHANGE, /* Platform Configuration Change */ + ISPASYNC_UNHANDLED_RESPONSE, /* Unhandled Response Entry */ + ISPASYNC_FW_CRASH, /* Firmware has crashed */ + ISPASYNC_FW_DUMPED, /* Firmware crashdump taken */ + ISPASYNC_FW_RESTARTED /* Firmware has been restarted */ +} ispasync_t; +int isp_async(ispsoftc_t *, ispasync_t, void *); + +#define ISPASYNC_CHANGE_PDB ((void *) 0) +#define ISPASYNC_CHANGE_SNS ((void *) 1) +#define ISPASYNC_CHANGE_OTHER ((void *) 2) + +/* + * Platform Dependent Error and Debug Printout + * + * Generally this is: + * + * void isp_prt(ispsoftc_t *, int level, const char *, ...) + * + * but due to compiler differences on different platforms this won't be + * formally done here. Instead, it goes in each platform definition file. + */ + +#define ISP_LOGALL 0x0 /* log always */ +#define ISP_LOGCONFIG 0x1 /* log configuration messages */ +#define ISP_LOGINFO 0x2 /* log informational messages */ +#define ISP_LOGWARN 0x4 /* log warning messages */ +#define ISP_LOGERR 0x8 /* log error messages */ +#define ISP_LOGDEBUG0 0x10 /* log simple debug messages */ +#define ISP_LOGDEBUG1 0x20 /* log intermediate debug messages */ +#define ISP_LOGDEBUG2 0x40 /* log most debug messages */ +#define ISP_LOGDEBUG3 0x80 /* log high frequency debug messages */ +#define ISP_LOGDEBUG4 0x100 /* log high frequency debug messages */ +#define ISP_LOGTDEBUG0 0x200 /* log simple debug messages (target mode) */ +#define ISP_LOGTDEBUG1 0x400 /* log intermediate debug messages (target) */ +#define ISP_LOGTDEBUG2 0x800 /* log all debug messages (target) */ + +/* + * Each Platform provides it's own isposinfo substructure of the ispsoftc + * defined above. + * + * Each platform must also provide the following macros/defines: + * + * + * ISP2100_SCRLEN - length for the Fibre Channel scratch DMA area + * + * MEMZERO(dst, src) platform zeroing function + * MEMCPY(dst, src, count) platform copying function + * SNPRINTF(buf, bufsize, fmt, ...) snprintf + * USEC_DELAY(usecs) microsecond spindelay function + * USEC_SLEEP(isp, usecs) microsecond sleep function + * + * NANOTIME_T nanosecond time type + * + * GET_NANOTIME(NANOTIME_T *) get current nanotime. + * + * GET_NANOSEC(NANOTIME_T *) get uint64_t from NANOTIME_T + * + * NANOTIME_SUB(NANOTIME_T *, NANOTIME_T *) + * subtract two NANOTIME_T values + * + * + * MAXISPREQUEST(ispsoftc_t *) maximum request queue size + * for this particular board type + * + * MEMORYBARRIER(ispsoftc_t *, barrier_type, offset, size) + * + * Function/Macro the provides memory synchronization on + * various objects so that the ISP's and the system's view + * of the same object is consistent. + * + * MBOX_ACQUIRE(ispsoftc_t *) acquire lock on mailbox regs + * MBOX_WAIT_COMPLETE(ispsoftc_t *) wait for mailbox cmd to be done + * MBOX_NOTIFY_COMPLETE(ispsoftc_t *) notification of mbox cmd donee + * MBOX_RELEASE(ispsoftc_t *) release lock on mailbox regs + * + * FC_SCRATCH_ACQUIRE(ispsoftc_t *) acquire lock on FC scratch area + * FC_SCRATCH_RELEASE(ispsoftc_t *) acquire lock on FC scratch area + * + * SCSI_GOOD SCSI 'Good' Status + * SCSI_CHECK SCSI 'Check Condition' Status + * SCSI_BUSY SCSI 'Busy' Status + * SCSI_QFULL SCSI 'Queue Full' Status + * + * XS_T Platform SCSI transaction type (i.e., command for HBA) + * XS_DMA_ADDR_T Platform PCI DMA Address Type + * XS_ISP(xs) gets an instance out of an XS_T + * XS_CHANNEL(xs) gets the channel (bus # for DUALBUS cards) "" + * XS_TGT(xs) gets the target "" + * XS_LUN(xs) gets the lun "" + * XS_CDBP(xs) gets a pointer to the scsi CDB "" + * XS_CDBLEN(xs) gets the CDB's length "" + * XS_XFRLEN(xs) gets the associated data transfer length "" + * XS_TIME(xs) gets the time (in milliseconds) for this command + * XS_RESID(xs) gets the current residual count + * XS_STSP(xs) gets a pointer to the SCSI status byte "" + * XS_SNSP(xs) gets a pointer to the associate sense data + * XS_SNSLEN(xs) gets the length of sense data storage + * XS_SNSKEY(xs) dereferences XS_SNSP to get the current stored Sense Key + * XS_TAG_P(xs) predicate of whether this command should be tagged + * XS_TAG_TYPE(xs) which type of tag to use + * XS_SETERR(xs) set error state + * + * HBA_NOERROR command has no erros + * HBA_BOTCH hba botched something + * HBA_CMDTIMEOUT command timed out + * HBA_SELTIMEOUT selection timed out (also port logouts for FC) + * HBA_TGTBSY target returned a BUSY status + * HBA_BUSRESET bus reset destroyed command + * HBA_ABORTED command was aborted (by request) + * HBA_DATAOVR a data overrun was detected + * HBA_ARQFAIL Automatic Request Sense failed + * + * XS_ERR(xs) return current error state + * XS_NOERR(xs) there is no error currently set + * XS_INITERR(xs) initialize error state + * + * XS_SAVE_SENSE(xs, sp) save sense data + * + * XS_SET_STATE_STAT(isp, sp, xs) platform dependent interpreter of + * response queue entry status bits + * + * + * DEFAULT_IID(ispsoftc_t *) Default SCSI initiator ID + * DEFAULT_LOOPID(ispsoftc_t *) Default FC Loop ID + * DEFAULT_NODEWWN(ispsoftc_t *) Default Node WWN + * DEFAULT_PORTWWN(ispsoftc_t *) Default Port WWN + * DEFAULT_FRAMESIZE(ispsoftc_t *) Default Frame Size + * DEFAULT_EXEC_THROTTLE(ispsoftc_t *) Default Execution Throttle + * These establish reasonable defaults for each platform. + * These must be available independent of card NVRAM and are + * to be used should NVRAM not be readable. + * + * ISP_NODEWWN(ispsoftc_t *) FC Node WWN to use + * ISP_PORTWWN(ispsoftc_t *) FC Port WWN to use + * + * These are to be used after NVRAM is read. The tags + * in fcparam.isp_{node,port}wwn reflect the values + * read from NVRAM (possibly corrected for card botches). + * Each platform can take that information and override + * it or ignore and return the Node and Port WWNs to be + * used when sending the Qlogic f/w the Initialization Control + * Block. + * + * (XXX these do endian specific transformations- in transition XXX) + * + * ISP_IOXPUT_8(ispsoftc_t *, uint8_t srcval, uint8_t *dstptr) + * ISP_IOXPUT_16(ispsoftc_t *, uint16_t srcval, uint16_t *dstptr) + * ISP_IOXPUT_32(ispsoftc_t *, uint32_t srcval, uint32_t *dstptr) + * + * ISP_IOXGET_8(ispsoftc_t *, uint8_t *srcptr, uint8_t dstrval) + * ISP_IOXGET_16(ispsoftc_t *, uint16_t *srcptr, uint16_t dstrval) + * ISP_IOXGET_32(ispsoftc_t *, uint32_t *srcptr, uint32_t dstrval) + * + * ISP_SWIZZLE_NVRAM_WORD(ispsoftc_t *, uint16_t *) + */ + +#endif /* _ISPVAR_H */ |
