diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-10-05 01:18:40 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-10-05 01:18:40 +0000 |
commit | 012bb3e61e5eced6c610f9e036372bf0c8def2d1 (patch) | |
tree | 87efc733f9b164e8c85c0336f92c8fb7eff6d183 /drivers/scsi/cpqfcTScontrol.c | |
parent | 625a1589d3d6464b5d90b8a0918789e3afffd220 (diff) |
Merge with Linux 2.4.0-test9. Please check DECstation, I had a number
of rejects to fixup while integrating Linus patches. I also found
that this kernel will only boot SMP on Origin; the UP kernel freeze
soon after bootup with SCSI timeout messages. I commit this anyway
since I found that the last CVS versions had the same problem.
Diffstat (limited to 'drivers/scsi/cpqfcTScontrol.c')
-rw-r--r-- | drivers/scsi/cpqfcTScontrol.c | 2200 |
1 files changed, 2200 insertions, 0 deletions
diff --git a/drivers/scsi/cpqfcTScontrol.c b/drivers/scsi/cpqfcTScontrol.c new file mode 100644 index 000000000..bc90b51c3 --- /dev/null +++ b/drivers/scsi/cpqfcTScontrol.c @@ -0,0 +1,2200 @@ +/* Copyright 2000, Compaq Computer Corporation + * Fibre Channel Host Bus Adapter + * 64-bit, 66MHz PCI + * Originally developed and tested on: + * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ... + * SP# P225CXCBFIEL6T, Rev XC + * SP# 161290-001, Rev XD + * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * Written by Don Zimmerman +*/ +/* These functions control the host bus adapter (HBA) hardware. The main chip + control takes place in the interrupt handler where we process the IMQ + (Inbound Message Queue). The IMQ is Tachyon's way of communicating FC link + events and state information to the driver. The Single Frame Queue (SFQ) + buffers incoming FC frames for processing by the driver. References to + "TL/TS UG" are for: + "HP HPFC-5100/5166 Tachyon TL/TS ICs User Guide", August 16, 1999, 1st Ed. + Hewlitt Packard Manual Part Number 5968-1083E. +*/ + +#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) + +#include <linux/blk.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/ioport.h> // request_region() prototype +#include <linux/sched.h> +#include <linux/malloc.h> // need "kfree" for ext. S/G pages +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/unistd.h> +#include <asm/io.h> // struct pt_regs for IRQ handler & Port I/O +#include <asm/irq.h> +#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18) +#include <asm/spinlock.h> +#else +#include <linux/spinlock.h> +#endif + +#include "sd.h" +#include "hosts.h" // Scsi_Host definition for INT handler +#include "cpqfcTSchip.h" +#include "cpqfcTSstructs.h" + +//#define IMQ_DEBUG 1 + +static void fcParseLinkStatusCounters(TACHYON * fcChip); +static void CpqTsGetSFQEntry(TACHYON * fcChip, + USHORT pi, ULONG * buffr, BOOLEAN UpdateChip); + + +// Note special requirements for Q alignment! (TL/TS UG pg. 190) +// We place critical index pointers at end of QUE elements to assist +// in non-symbolic (i.e. memory dump) debugging +// opcode defines placement of Queues (e.g. local/external RAM) + +int CpqTsCreateTachLiteQues( void* pHBA, int opcode) +{ + CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA; + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + + int iStatus=0; + unsigned long ulAddr; + + + // NOTE! fcMemManager() will return system virtual addresses. + // System (kernel) virtual addresses, though non-paged, still + // aren't physical addresses. Convert to PHYSICAL_ADDRESS for Tachyon's + // DMA use. + ENTER("CreateTachLiteQues"); + + + // Allocate primary EXCHANGES array... + + printk("Allocating %u for %u Exchanges ", + (ULONG)sizeof(FC_EXCHANGES), TACH_MAX_XID); + fcChip->Exchanges = kmalloc( sizeof( FC_EXCHANGES), GFP_KERNEL ); + printk("@ %p\n", fcChip->Exchanges); + + if( fcChip->Exchanges == NULL ) // fatal error!! + { + printk("kmalloc failure on Exchanges: fatal error\n"); + return -1; + } + // zero out the entire EXCHANGE space + memset( fcChip->Exchanges, 0, sizeof( FC_EXCHANGES)); + + + printk("Allocating %u for LinkQ ", (ULONG)sizeof(FC_LINK_QUE)); + cpqfcHBAdata->fcLQ = kmalloc( sizeof( FC_LINK_QUE), GFP_KERNEL ); + printk("@ %p (%u elements)\n", cpqfcHBAdata->fcLQ, FC_LINKQ_DEPTH); + memset( cpqfcHBAdata->fcLQ, 0, sizeof( FC_LINK_QUE)); + + if( cpqfcHBAdata->fcLQ == NULL ) // fatal error!! + { + printk("kmalloc failure on fc Link Que: fatal error\n"); + return -1; + } + // zero out the entire EXCHANGE space + memset( cpqfcHBAdata->fcLQ, 0, sizeof( FC_LINK_QUE)); + + + + + // Verify that basic Tach I/O registers are not NULL + + if( !fcChip->Registers.ReMapMemBase ) + { + printk("HBA base address NULL: fatal error\n"); + return -1; + } + + + // Initialize the fcMemManager memory pairs (stores allocated/aligned + // pairs for future freeing) + memset( cpqfcHBAdata->dynamic_mem, 0, sizeof(cpqfcHBAdata->dynamic_mem)); + + + // Allocate Tach's Exchange Request Queue (each ERQ entry 32 bytes) + + fcChip->ERQ = fcMemManager( &cpqfcHBAdata->dynamic_mem[0], + sizeof( TachLiteERQ ), 32*(ERQ_LEN), 0L ); + if( !fcChip->ERQ ) + { + printk("kmalloc/alignment failure on ERQ: fatal error\n"); + return -1; + } + fcChip->ERQ->length = ERQ_LEN-1; + ulAddr = virt_to_bus( fcChip->ERQ); +#if BITS_PER_LONG > 32 + if( (ulAddr >> 32) ) + { + printk(" FATAL! ERQ ptr %p exceeds Tachyon's 32-bit register size\n", + (void*)ulAddr); + return -1; // failed + } +#endif + fcChip->ERQ->base = (ULONG)ulAddr; // copy for quick reference + + + // Allocate Tach's Inbound Message Queue (32 bytes per entry) + + fcChip->IMQ = fcMemManager( &cpqfcHBAdata->dynamic_mem[0], + sizeof( TachyonIMQ ), 32*(IMQ_LEN), 0L ); + if( !fcChip->IMQ ) + { + printk("kmalloc/alignment failure on IMQ: fatal error\n"); + return -1; + } + fcChip->IMQ->length = IMQ_LEN-1; + + ulAddr = virt_to_bus( fcChip->IMQ); +#if BITS_PER_LONG > 32 + if( (ulAddr >> 32) ) + { + printk(" FATAL! IMQ ptr %p exceeds Tachyon's 32-bit register size\n", + (void*)ulAddr); + return -1; // failed + } +#endif + fcChip->IMQ->base = (ULONG)ulAddr; // copy for quick reference + + + // Allocate Tach's Single Frame Queue (64 bytes per entry) + fcChip->SFQ = fcMemManager( &cpqfcHBAdata->dynamic_mem[0], + sizeof( TachLiteSFQ ), 64*(SFQ_LEN),0L ); + if( !fcChip->SFQ ) + { + printk("kmalloc/alignment failure on SFQ: fatal error\n"); + return -1; + } + fcChip->SFQ->length = SFQ_LEN-1; // i.e. Que length [# entries - + // min. 32; max. 4096 (0xffff)] + + ulAddr = virt_to_bus( fcChip->SFQ); +#if BITS_PER_LONG > 32 + if( (ulAddr >> 32) ) + { + printk(" FATAL! SFQ ptr %p exceeds Tachyon's 32-bit register size\n", + (void*)ulAddr); + return -1; // failed + } +#endif + fcChip->SFQ->base = (ULONG)ulAddr; // copy for quick reference + + + // Allocate SCSI Exchange State Table; aligned nearest @sizeof + // power-of-2 boundary + // LIVE DANGEROUSLY! Assume the boundary for SEST mem will + // be on physical page (e.g. 4k) boundary. + printk("Allocating %u for TachSEST for %u Exchanges\n", + (ULONG)sizeof(TachSEST), TACH_SEST_LEN); + fcChip->SEST = fcMemManager( &cpqfcHBAdata->dynamic_mem[0], + sizeof(TachSEST), 4, 0L ); +// sizeof(TachSEST), 64*TACH_SEST_LEN, 0L ); + if( !fcChip->SEST ) + { + printk("kmalloc/alignment failure on SEST: fatal error\n"); + return -1; + } + + fcChip->SEST->length = TACH_SEST_LEN; // e.g. DON'T subtract one + // (TL/TS UG, pg 153) + + ulAddr = virt_to_bus( fcChip->SEST); +#if BITS_PER_LONG > 32 + if( (ulAddr >> 32) ) + { + printk(" FATAL! SFQ ptr %p exceeds Tachyon's 32-bit register size\n", + (void*)ulAddr); + return -1; // failed + } +#endif + fcChip->SEST->base = (ULONG)ulAddr; // copy for quick reference + + + // Now that structures are defined, + // fill in Tachyon chip registers... + + // EEEEEEEE EXCHANGE REQUEST QUEUE + + writel( fcChip->ERQ->base, + (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_BASE)); + + writel( fcChip->ERQ->length, + (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_LENGTH)); + + + fcChip->ERQ->producerIndex = 0L; + writel( fcChip->ERQ->producerIndex, + (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_PRODUCER_INDEX)); + + + // NOTE! write consumer index last, since the write + // causes Tachyon to process the other registers + + ulAddr = virt_to_bus( &fcChip->ERQ->consumerIndex); + + // NOTE! Tachyon DMAs to the ERQ consumer Index host + // address; must be correctly aligned + writel( (ULONG)ulAddr, + (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_CONSUMER_INDEX_ADR)); + + + + // IIIIIIIIIIIII INBOUND MESSAGE QUEUE + // Tell Tachyon where the Que starts + + // set the Host's pointer for Tachyon to access + + printk(" cpqfcTS: writing IMQ BASE %Xh ", fcChip->IMQ->base ); + writel( fcChip->IMQ->base, + (fcChip->Registers.ReMapMemBase + IMQ_BASE)); + + writel( fcChip->IMQ->length, + (fcChip->Registers.ReMapMemBase + IMQ_LENGTH)); + + writel( fcChip->IMQ->consumerIndex, + (fcChip->Registers.ReMapMemBase + IMQ_CONSUMER_INDEX)); + + + // NOTE: TachLite DMAs to the producerIndex host address + // must be correctly aligned with address bits 1-0 cleared + // Writing the BASE register clears the PI register, so write it last + ulAddr = virt_to_bus( &fcChip->IMQ->producerIndex); +#if BITS_PER_LONG > 32 + if( (ulAddr >> 32) ) + { + printk(" FATAL! IMQ ptr %p exceeds Tachyon's 32-bit register size\n", + (void*)ulAddr); + return -1; // failed + } +#endif +//#if DBG + printk(" PI %Xh\n", (ULONG)ulAddr ); +//#endif + writel( (ULONG)ulAddr, + (fcChip->Registers.ReMapMemBase + IMQ_PRODUCER_INDEX)); + + + + // SSSSSSSSSSSSSSS SINGLE FRAME SEQUENCE + // Tell TachLite where the Que starts + + writel( fcChip->SFQ->base, + (fcChip->Registers.ReMapMemBase + TL_MEM_SFQ_BASE)); + + writel( fcChip->SFQ->length, + (fcChip->Registers.ReMapMemBase + TL_MEM_SFQ_LENGTH)); + + + // tell TachLite where SEST table is & how long + writel( fcChip->SEST->base, + (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_BASE)); + + printk(" cpqfcTS: SEST %p(virt): Wrote base %Xh @ %p\n", + fcChip->SEST, fcChip->SEST->base, + fcChip->Registers.ReMapMemBase + TL_MEM_SEST_BASE); + + writel( fcChip->SEST->length, + (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_LENGTH)); + + writel( (TL_EXT_SG_PAGE_COUNT-1), + (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_SG_PAGE)); + + + LEAVE("CreateTachLiteQues"); + + return iStatus; +} + + + +// function to return TachLite to Power On state +// 1st - reset tachyon ('SOFT' reset) +// others - future + +int CpqTsResetTachLite(void *pHBA, int type) +{ + CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA; + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + ULONG ulBuff, i; + int ret_status=0; // def. success + + ENTER("ResetTach"); + + switch(type) + { + + case CLEAR_FCPORTS: + + // in case he was running previously, mask Tach's interrupt + writeb( 0, (fcChip->Registers.ReMapMemBase + IINTEN)); + + // de-allocate mem for any Logged in ports + // (e.g., our module is unloading) + // search the forward linked list, de-allocating + // the memory we allocated when the port was initially logged in + { + PFC_LOGGEDIN_PORT pLoggedInPort = fcChip->fcPorts.pNextPort; + PFC_LOGGEDIN_PORT ptr; +// printk("checking for allocated LoggedInPorts...\n"); + + while( pLoggedInPort ) + { + ptr = pLoggedInPort; + pLoggedInPort = ptr->pNextPort; +// printk("kfree(%p) on FC LoggedInPort port_id 0x%06lX\n", +// ptr, ptr->port_id); + kfree( ptr ); + } + } + // (continue resetting hardware...) + + case 1: // RESTART Tachyon (power-up state) + + // in case he was running previously, mask Tach's interrupt + writeb( 0, (fcChip->Registers.ReMapMemBase + IINTEN)); + // turn OFF laser (NOTE: laser is turned + // off during reset, because GPIO4 is cleared + // to 0 by reset action - see TLUM, sec 7.22) + // However, CPQ 64-bit HBAs have a "health + // circuit" which keeps laser ON for a brief + // period after it is turned off ( < 1s) + + fcChip->LaserControl( fcChip->Registers.ReMapMemBase, 0); + + + + // soft reset timing constraints require: + // 1. set RST to 1 + // 2. read SOFTRST register + // (128 times per R. Callison code) + // 3. clear PCI ints + // 4. clear RST to 0 + writel( 0xff000001L, + (fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST)); + + for( i=0; i<128; i++) + ulBuff = readl( fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST); + + // clear the soft reset + for( i=0; i<8; i++) + writel( 0, (fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST)); + + + + // clear out our copy of Tach regs, + // because they must be invalid now, + // since TachLite reset all his regs. + CpqTsDestroyTachLiteQues(cpqfcHBAdata,0); // remove Host-based Que structs + cpqfcTSClearLinkStatusCounters(fcChip); // clear our s/w accumulators + // lower bits give GBIC info + fcChip->Registers.TYstatus.value = + readl( fcChip->Registers.TYstatus.address ); + break; + +/* + case 2: // freeze SCSI + case 3: // reset Outbound command que (ERQ) + case 4: // unfreeze OSM (Outbound Seq. Man.) 'er' + case 5: // report status + + break; +*/ + default: + ret_status = -1; // invalid option passed to RESET function + break; + } + LEAVE("ResetTach"); + return ret_status; +} + + + + + + +// 'addrBase' is IOBaseU for both TachLite and (older) Tachyon +int CpqTsLaserControl( void* addrBase, int opcode ) +{ + ULONG dwBuff; + + dwBuff = readl((addrBase + TL_MEM_TACH_CONTROL) ); // read TL Control reg + // (change only bit 4) + if( opcode == 1) + dwBuff |= ~0xffffffefL; // set - ON + else + dwBuff &= 0xffffffefL; // clear - OFF + writel( dwBuff, (addrBase + TL_MEM_TACH_CONTROL)); // write TL Control reg + return 0; +} + + + + + +// Use controller's "Options" field to determine loopback mode (if any) +// internal loopback (silicon - no GBIC) +// external loopback (GBIC - no FC loop) +// no loopback: L_PORT, external cable from GBIC required + +int CpqTsInitializeFrameManager( void *pChip, int opcode) +{ + PTACHYON fcChip; + int iStatus; + ULONG wwnLo, wwnHi; // for readback verification + + ENTER("InitializeFrameManager"); + fcChip = (PTACHYON)pChip; + if( !fcChip->Registers.ReMapMemBase ) // undefined controller? + return -1; + + // TL/TS UG, pg. 184 + // 0x0065 = 100ms for RT_TOV + // 0x01f5 = 500ms for ED_TOV + // 0x07D1 = 2000ms + fcChip->Registers.ed_tov.value = 0x006507D1; + writel( fcChip->Registers.ed_tov.value, + (fcChip->Registers.ed_tov.address)); + + + // Set LP_TOV to the FC-AL2 specified 2 secs. + // TL/TS UG, pg. 185 + writel( 0x07d00010, fcChip->Registers.ReMapMemBase +TL_MEM_FM_TIMEOUT2); + + + // Now try to read the WWN from the adapter's NVRAM + iStatus = CpqTsReadWriteWWN( fcChip, 1); // '1' for READ + + if( iStatus ) // NVRAM read failed? + { + printk(" WARNING! HBA NVRAM WWN read failed - make alias\n"); + // make up a WWN. If NULL or duplicated on loop, FC loop may hang! + + + fcChip->Registers.wwn_hi = (__u32)jiffies; + fcChip->Registers.wwn_hi |= 0x50000000L; + fcChip->Registers.wwn_lo = 0x44556677L; + } + + + writel( fcChip->Registers.wwn_hi, + fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_HI); + + writel( fcChip->Registers.wwn_lo, + fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_LO); + + + // readback for verification: + wwnHi = readl( fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_HI ); + + wwnLo = readl( fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_LO); + // test for correct chip register WRITE/READ + DEBUG_PCI( printk(" WWN %08X%08X\n", + fcChip->Registers.wwn_hi, fcChip->Registers.wwn_lo ) ); + + if( wwnHi != fcChip->Registers.wwn_hi || + wwnLo != fcChip->Registers.wwn_lo ) + { + printk( "cpqfcTS: WorldWideName register load failed\n"); + return -1; // FAILED! + } + + + + // set Frame Manager Initialize command + fcChip->Registers.FMcontrol.value = 0x06; + + // Note: for test/debug purposes, we may use "Hard" address, + // but we completely support "soft" addressing, including + // dynamically changing our address. + if( fcChip->Options.intLoopback == 1 ) // internal loopback + fcChip->Registers.FMconfig.value = 0x0f002080L; + else if( fcChip->Options.extLoopback == 1 ) // internal loopback + fcChip->Registers.FMconfig.value = 0x0f004080L; + else // L_Port + fcChip->Registers.FMconfig.value = 0x55000100L; // hard address (55h start) +// fcChip->Registers.FMconfig.value = 0x01000080L; // soft address (can't pick) +// fcChip->Registers.FMconfig.value = 0x55000100L; // hard address (55h start) + + // write config to FM + + if( !fcChip->Options.intLoopback && !fcChip->Options.extLoopback ) + // (also need LASER for real LOOP) + fcChip->LaserControl( fcChip->Registers.ReMapMemBase, 1); // turn on LASER + + writel( fcChip->Registers.FMconfig.value, + fcChip->Registers.FMconfig.address); + + + // issue INITIALIZE command to FM - ACTION! + writel( fcChip->Registers.FMcontrol.value, + fcChip->Registers.FMcontrol.address); + + LEAVE("InitializeFrameManager"); + + return 0; +} + + + + + +// This "look ahead" function examines the IMQ for occurence of +// "type". Returns 1 if found, 0 if not. +static int PeekIMQEntry( PTACHYON fcChip, ULONG type) +{ + ULONG CI = fcChip->IMQ->consumerIndex; + ULONG PI = fcChip->IMQ->producerIndex; // snapshot of IMQ indexes + + while( CI != PI ) + { // proceed with search + if( (++CI) >= IMQ_LEN ) CI = 0; // rollover check + + switch( type ) + { + case ELS_LILP_FRAME: + { + // first, we need to find an Inbound Completion message, + // If we find it, check the incoming frame payload (1st word) + // for LILP frame + if( (fcChip->IMQ->QEntry[CI].type & 0x1FF) == 0x104 ) + { + TachFCHDR_GCMND* fchs; + ULONG ulFibreFrame[2048/4]; // max DWORDS in incoming FC Frame + USHORT SFQpi = (USHORT)(fcChip->IMQ->QEntry[CI].word[0] & 0x0fffL); + + CpqTsGetSFQEntry( fcChip, + SFQpi, // SFQ producer ndx + ulFibreFrame, // contiguous dest. buffer + FALSE); // DON'T update chip--this is a "lookahead" + + fchs = (TachFCHDR_GCMND*)&ulFibreFrame; + if( fchs->pl[0] == ELS_LILP_FRAME) + { + return 1; // found the LILP frame! + } + else + { + // keep looking... + } + } + } + break; + + case OUTBOUND_COMPLETION: + if( (fcChip->IMQ->QEntry[CI].type & 0x1FF) == 0x00 ) + { + + // any OCM errors? + if( fcChip->IMQ->QEntry[CI].word[2] & 0x7a000000L ) + return 1; // found OCM error + } + break; + + + + default: + break; + } + } + return 0; // failed to find "type" +} + + +static void SetTachTOV( CPQFCHBA* cpqfcHBAdata) +{ + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + + // TL/TS UG, pg. 184 + // 0x0065 = 100ms for RT_TOV + // 0x01f5 = 500ms for ED_TOV + // 0x07d1 = 2000ms for ED_TOV + + // SANMark Level 1 requires an "initialization backoff" + // (See "SANMark Test Suite Level 1": + // initialization_timeout.fcal.SANMark-1.fc) + // We have to use 2sec, 24sec, then 128sec when login/ + // port discovery processes fail to complete. + + // when port discovery completes (logins done), we set + // ED_TOV to 500ms -- this is the normal operational case + // On the first Link Down, we'll move to 2 secs (7D1 ms) + if( (fcChip->Registers.ed_tov.value &0xFFFF) <= 0x1f5) + fcChip->Registers.ed_tov.value = 0x006507D1; + + // If we get another LST after we moved TOV to 2 sec, + // increase to 24 seconds (5DC1 ms) per SANMark! + else if( (fcChip->Registers.ed_tov.value &0xFFFF) <= 0x7D1) + fcChip->Registers.ed_tov.value = 0x00655DC1; + + // If we get still another LST, set the max TOV (Tachyon + // has only 16 bits for ms timer, so the max is 65.5 sec) + else if( (fcChip->Registers.ed_tov.value &0xFFFF) <= 0x5DC1) + fcChip->Registers.ed_tov.value = 0x0065FFFF; + + writel( fcChip->Registers.ed_tov.value, + (fcChip->Registers.ed_tov.address)); + // keep the same 2sec LP_TOV + writel( 0x07D00010, fcChip->Registers.ReMapMemBase +TL_MEM_FM_TIMEOUT2); +} + + +// The IMQ is an array with IMQ_LEN length, each element (QEntry) +// with eight 32-bit words. Tachyon PRODUCES a QEntry with each +// message it wants to send to the host. The host CONSUMES IMQ entries + +// This function copies the current +// (or oldest not-yet-processed) QEntry to +// the caller, clears/ re-enables the interrupt, and updates the +// (Host) Consumer Index. +// Return value: +// 0 message processed, none remain (producer and consumer +// indexes match) +// 1 message processed, more messages remain +// -1 no message processed - none were available to process +// Remarks: +// TL/TS UG specifices that the following actions for +// INTA_L handling: +// 1. read PCI Interrupt Status register (0xff) +// 2. all IMQ messages should be processed before writing the +// IMQ consumer index. + + +int CpqTsProcessIMQEntry(void *host) +{ + struct Scsi_Host *HostAdapter = (struct Scsi_Host *)host; + CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + FC_EXCHANGES *Exchanges = fcChip->Exchanges; + int iStatus; + USHORT i, RPCset, DPCset; + ULONG x_ID; + ULONG ulBuff, dwStatus; + TachFCHDR_GCMND* fchs; + ULONG ulFibreFrame[2048/4]; // max number of DWORDS in incoming Fibre Frame + UCHAR ucInboundMessageType; // Inbound CM, dword 3 "type" field + + ENTER("ProcessIMQEntry"); + + + // check TachLite's IMQ producer index - + // is a new message waiting for us? + // equal indexes means empty que + + if( fcChip->IMQ->producerIndex != fcChip->IMQ->consumerIndex ) + { // need to process message + + +#ifdef IMQ_DEBUG + printk("PI %X, CI %X type: %X\n", + fcChip->IMQ->producerIndex,fcChip->IMQ->consumerIndex, + fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].type); +#endif + // Examine Completion Messages in IMQ + // what CM_Type? + switch( (UCHAR)(fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].type + & 0xffL) ) + { + case OUTBOUND_COMPLETION: + + // Remarks: + // x_IDs (OX_ID, RX_ID) are partitioned by SEST entries + // (starting at 0), and SFS entries (starting at + // SEST_LEN -- outside the SEST space). + // Psuedo code: + // x_ID (OX_ID or RX_ID) from message is Trans_ID or SEST index + // range check - x_ID + // if x_ID outside 'Transactions' length, error - exit + // if any OCM error, copy error status to Exchange slot + // if FCP ASSIST transaction (x_ID within SEST), + // call fcComplete (to App) + // ... + + + ulBuff = fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1]; + x_ID = ulBuff & 0x7fffL; // lower 14 bits SEST_Index/Trans_ID + // Range check CM OX/RX_ID value... + if( x_ID < TACH_MAX_XID ) // don't go beyond array space + { + + + if( ulBuff & 0x20000000L ) // RPC -Response Phase Complete? + RPCset = 1; // (SEST transactions only) + else + RPCset = 0; + + if( ulBuff & 0x40000000L ) // DPC -Data Phase Complete? + DPCset = 1; // (SEST transactions only) + else + DPCset = 0; + // set the status for this Outbound transaction's ID + dwStatus = 0L; + if( ulBuff & 0x10000000L ) // SPE? (SEST Programming Error) + dwStatus |= SESTPROG_ERR; + + ulBuff = fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2]; + if( ulBuff & 0x7a000000L ) // any other errs? + { + if( ulBuff & 0x40000000L ) + dwStatus |= INV_ENTRY; + if( ulBuff & 0x20000000L ) + dwStatus |= FRAME_TO; // FTO + if( ulBuff & 0x10000000L ) + dwStatus |= HOSTPROG_ERR; + if( ulBuff & 0x08000000L ) + dwStatus |= LINKFAIL_TX; + if( ulBuff & 0x02000000L ) + dwStatus |= ABORTSEQ_NOTIFY; // ASN + } + + + if( dwStatus ) // any errors? + { + // set the Outbound Completion status + Exchanges->fcExchange[ x_ID ].status |= dwStatus; + + // if this Outbound frame was for a SEST entry, automatically + // reque it in the case of LINKFAIL (it will restart on PDISC) + if( x_ID < TACH_SEST_LEN ) + { + + printk(" #OCM error %Xh x_ID %X# ", + dwStatus, x_ID); + + Exchanges->fcExchange[x_ID].timeOut = 30000; // seconds default + + + // We Q ABTS for each exchange. + // NOTE: We can get FRAME_TO on bad alpa (device gone). Since + // bad alpa is reported before FRAME_TO, examine the status + // flags to see if the device is removed. If so, DON'T + // post an ABTS, since it will be terminated by the bad alpa + // message. + if( dwStatus & FRAME_TO ) // check for device removed... + { + if( !(Exchanges->fcExchange[x_ID].status & DEVICE_REMOVED) ) + { + // presumes device is still there: send ABTS. + + cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID); + } + } + else // Abort all other errors + { + cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID); + } + + // if the HPE bit is set, we have to CLose the LOOP + // (see TL/TS UG, pg. 239) + + if( dwStatus &= HOSTPROG_ERR ) + // set CL bit (see TL/TS UG, pg. 172) + writel( 4, fcChip->Registers.FMcontrol.address); + } + } + // NOTE: we don't necessarily care about ALL completion messages... + // SCSI resp. complete OR + if( ((x_ID < TACH_SEST_LEN) && RPCset)|| + (x_ID >= TACH_SEST_LEN) ) // non-SCSI command + { + // exchange done; complete to upper levels with status + // (if necessary) and free the exchange slot + + + if( x_ID >= TACH_SEST_LEN ) // Link Service Outbound frame? + // A Request or Reply has been sent + { // signal waiting WorkerThread + + up( cpqfcHBAdata->TYOBcomplete); // frame is OUT of Tach + + // WorkerThread will complete Xchng + } + else // X_ID is for FCP assist (SEST) + { + // TBD (target mode) +// fcCompleteExchange( fcChip, x_ID); // TRE completed + } + } + } + else // ERROR CONDITION! bogus x_ID in completion message + { + + printk(" ProcessIMQ (OBCM) x_id out of range %Xh\n", x_ID); + + } + + + + // Load the Frame Manager's error counters. We check them here + // because presumably the link is up and healthy enough for the + // counters to be meaningful (i.e., don't check them while loop + // is initializing). + fcChip->Registers.FMLinkStatus1.value = // get TL's counter + readl(fcChip->Registers.FMLinkStatus1.address); + + fcChip->Registers.FMLinkStatus2.value = // get TL's counter + readl(fcChip->Registers.FMLinkStatus2.address); + + + fcParseLinkStatusCounters( fcChip); // load into 6 s/w accumulators + break; + + + + case ERROR_IDLE_COMPLETION: // TachLite Error Idle... + + // We usually get this when the link goes down during heavy traffic. + // For now, presume that if SEST Exchanges are open, we will + // get this as our cue to INVALIDATE all SEST entries + // (and we OWN all the SEST entries). + // See TL/TS UG, pg. 53 + + for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++) + { + + // Does this VALid SEST entry need to be invalidated for Abort? + fcChip->SEST->u[ x_ID].IWE.Hdr_Len &= 0x7FFFFFFF; + } + + CpqTsUnFreezeTachlite( fcChip, 2); // unfreeze Tachyon, if Link OK + + break; + + + case INBOUND_SFS_COMPLETION: //0x04 + // NOTE! we must process this SFQ message to avoid SFQ filling + // up and stopping TachLite. Incoming commands are placed here, + // as well as 'unknown' frames (e.g. LIP loop position data) + // write this CM's producer index to global... + // TL/TS UG, pg 234: + // Type: 0 - reserved + // 1 - Unassisted FCP + // 2 - BAD FCP + // 3 - Unkown Frame + // 4-F reserved + + + fcChip->SFQ->producerIndex = (USHORT) + (fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[0] & 0x0fffL); + + + ucInboundMessageType = 0; // default to useless frame + + // we can only process two Types: 1, Unassisted FCP, and 3, Unknown + // Also, we aren't interested in processing frame fragments + // so don't Que anything with 'LKF' bit set + if( !(fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2] + & 0x40000000) ) // 'LKF' link failure bit clear? + { + ucInboundMessageType = (UCHAR) // ICM DWord3, "Type" + (fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2] & 0x0fL); + } + else + { + fcChip->fcStats.linkFailRX++; +// printk("LKF (link failure) bit set on inbound message\n"); + } + + // clears SFQ entry from Tachyon buffer; copies to contiguous ulBuff + CpqTsGetSFQEntry( + fcChip, // i.e. this Device Object + (USHORT)fcChip->SFQ->producerIndex, // SFQ producer ndx + ulFibreFrame, TRUE); // contiguous destination buffer, update chip + + // analyze the incoming frame outside the INT handler... + // (i.e., Worker) + + if( ucInboundMessageType == 1 ) + { + fchs = (TachFCHDR_GCMND*)ulFibreFrame; // cast to examine IB frame + // don't fill up our Q with garbage - only accept FCP-CMND + // or XRDY frames + if( (fchs->d_id & 0xFF000000) == 0x06000000 ) // CMND + { + // someone sent us a SCSI command + +// fcPutScsiQue( cpqfcHBAdata, +// SFQ_UNASSISTED_FCP, ulFibreFrame); + } + else if( ((fchs->d_id & 0xFF000000) == 0x07000000) || // RSP (status) + (fchs->d_id & 0xFF000000) == 0x05000000 ) // XRDY + { + ULONG x_ID; + // Unfortunately, ABTS requires a Freeze on the chip so + // we can modify the shared memory SEST. When frozen, + // any received Exchange frames cannot be processed by + // Tachyon, so they will be dumped in here. It is too + // complex to attempt the reconstruct these frames in + // the correct Exchange context, so we simply seek to + // find status or transfer ready frames, and cause the + // exchange to complete with errors before the timeout + // expires. We use a Linux Scsi Cmnd result code that + // causes immediate retry. + + + // Do we have an open exchange that matches this s_id + // and ox_id? + for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++) + { + if( (fchs->s_id & 0xFFFFFF) == + (Exchanges->fcExchange[x_ID].fchs.d_id & 0xFFFFFF) + && + (fchs->ox_rx_id & 0xFFFF0000) == + (Exchanges->fcExchange[x_ID].fchs.ox_rx_id & 0xFFFF0000) ) + { + // printk(" #R/X frame x_ID %08X# ", fchs->ox_rx_id ); + // simulate the anticipated error - since the + // SEST was frozen, frames were lost... + Exchanges->fcExchange[ x_ID ].status |= SFQ_FRAME; + + // presumes device is still there: send ABTS. + cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID); + break; // done + } + } + } + + } + + else if( ucInboundMessageType == 3) + { + // FC Link Service frames (e.g. PLOGI, ACC) come in here. + cpqfcTSPutLinkQue( cpqfcHBAdata, SFQ_UNKNOWN, ulFibreFrame); + + } + + else if( ucInboundMessageType == 2 ) // "bad FCP"? + { +#ifdef IMQ_DEBUG + printk("Bad FCP incoming frame discarded\n"); +#endif + } + + else // don't know this type + { +#ifdef IMQ_DEBUG + printk("Incoming frame discarded, type: %Xh\n", ucInboundMessageType); +#endif + } + + // Check the Frame Manager's error counters. We check them here + // because presumably the link is up and healthy enough for the + // counters to be meaningful (i.e., don't check them while loop + // is initializing). + fcChip->Registers.FMLinkStatus1.value = // get TL's counter + readl(fcChip->Registers.FMLinkStatus1.address); + + + fcChip->Registers.FMLinkStatus2.value = // get TL's counter + readl(fcChip->Registers.FMLinkStatus2.address); + + + break; + + + + + // We get this CM because we issued a freeze + // command to stop outbound frames. We issue the + // freeze command at Link Up time; when this message + // is received, the ERQ base can be switched and PDISC + // frames can be sent. + + + case ERQ_FROZEN_COMPLETION: // note: expect ERQ followed immediately + // by FCP when freezing TL + fcChip->Registers.TYstatus.value = // read what's frozen + readl(fcChip->Registers.TYstatus.address); + // (do nothing; wait for FCP frozen message) + break; + case FCP_FROZEN_COMPLETION: + + fcChip->Registers.TYstatus.value = // read what's frozen + readl(fcChip->Registers.TYstatus.address); + + // Signal the kernel thread to proceed with SEST modification + up( cpqfcHBAdata->TachFrozen); + + break; + + + + case INBOUND_C1_TIMEOUT: + case MFS_BUF_WARN: + case IMQ_BUF_WARN: + break; + + + + + + // In older Tachyons, we 'clear' the internal 'core' interrupt state + // by reading the FMstatus register. In newer TachLite (Tachyon), + // we must WRITE the register + // to clear the condition (TL/TS UG, pg 179) + case FRAME_MGR_INTERRUPT: + { + PFC_LOGGEDIN_PORT pLoggedInPort; + + fcChip->Registers.FMstatus.value = + readl( fcChip->Registers.FMstatus.address ); + + // PROBLEM: It is possible, especially with "dumb" hubs that + // don't automatically LIP on by-pass of ports that are going + // away, for the hub by-pass process to destroy critical + // ordered sets of a frame. The result of this is a hung LPSM + // (Loop Port State Machine), which on Tachyon results in a + // (default 2 sec) Loop State Timeout (LST) FM message. We + // want to avoid this relatively huge timeout by detecting + // likely scenarios which will result in LST. + // To do this, we could examine FMstatus for Loss of Synchronization + // and/or Elastic Store (ES) errors. Of these, Elastic Store is better + // because we get this indication more quickly than the LOS. + // Not all ES errors are harmfull, so we don't want to LIP on every + // ES. Instead, on every ES, detect whether our LPSM in in one + // of the LST states: ARBITRATING, OPEN, OPENED, XMITTED CLOSE, + // or RECEIVED CLOSE. (See TL/TS UG, pg. 181) + // If any of these LPSM states are detected + // in combination with the LIP while LDn is not set, + // send an FM init (LIP F7,F7 for loops)! + // It is critical to the physical link stability NOT to reset (LIP) + // more than absolutely necessary; this is a basic premise of the + // SANMark level 1 spec. + { + ULONG Lpsm = (fcChip->Registers.FMstatus.value & 0xF0) >>4; + + if( (fcChip->Registers.FMstatus.value & 0x400) // ElasticStore? + && + !(fcChip->Registers.FMstatus.value & 0x100) // NOT LDn + && + !(fcChip->Registers.FMstatus.value & 0x1000)) // NOT LF + { + if( (Lpsm != 0) || // not MONITORING? or + !(Lpsm & 0x8) )// not already offline? + { + // now check the particular LST states... + if( (Lpsm == ARBITRATING) || (Lpsm == OPEN) || + (Lpsm == OPENED) || (Lpsm == XMITTD_CLOSE) || + (Lpsm == RCVD_CLOSE) ) + { + // re-init the loop before it hangs itself! + printk(" #req FMinit on E-S: LPSM %Xh# ",Lpsm); + + + fcChip->fcStats.FMinits++; + writel( 6, fcChip->Registers.FMcontrol.address); // LIP + } + } + } + else if( fcChip->Registers.FMstatus.value & 0x40000 ) // LST? + { + printk(" #req FMinit on LST, LPSM %Xh# ",Lpsm); + + fcChip->fcStats.FMinits++; + writel( 6, fcChip->Registers.FMcontrol.address); // LIP + } + } + + + // clear only the 'interrupting' type bits for this REG read + writel( (fcChip->Registers.FMstatus.value & 0xff3fff00L), + fcChip->Registers.FMstatus.address); + + + // copy frame manager status to unused ULONG slot + fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[0] = + fcChip->Registers.FMstatus.value; // (for debugging) + + + // Load the Frame Manager's error counters. We check them here + // because presumably the link is up and healthy enough for the + // counters to be meaningful (i.e., don't check them while loop + // is initializing). + fcChip->Registers.FMLinkStatus1.value = // get TL's counter + readl(fcChip->Registers.FMLinkStatus1.address); + + fcChip->Registers.FMLinkStatus2.value = // get TL's counter + readl(fcChip->Registers.FMLinkStatus2.address); + + // Get FM BB_Credit Zero Reg - does not clear on READ + fcChip->Registers.FMBB_CreditZero.value = // get TL's counter + readl(fcChip->Registers.FMBB_CreditZero.address); + + + + fcParseLinkStatusCounters( fcChip); // load into 6 s/w accumulators + + + // LINK DOWN + + if( fcChip->Registers.FMstatus.value & 0x100L ) // Link DOWN bit + { + +#ifdef IMQ_DEBUG + printk("LinkDn\n"); +#endif + printk(" #LDn# "); + + fcChip->fcStats.linkDown++; + + SetTachTOV( cpqfcHBAdata); // must set according to SANMark + + // Check the ERQ - force it to be "empty" to prevent Tach + // from sending out frames before we do logins. + + + if( fcChip->ERQ->producerIndex != fcChip->ERQ->consumerIndex) + { +// printk("#ERQ PI != CI#"); + CpqTsFreezeTachlite( fcChip, 1); // freeze ERQ only + fcChip->ERQ->producerIndex = fcChip->ERQ->consumerIndex = 0; + writel( fcChip->ERQ->base, + (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_BASE)); + // re-writing base forces ERQ PI to equal CI + + } + + // link down transition occurred -- port_ids can change + // on next LinkUp, so we must invalidate current logins + // (and any I/O in progress) until PDISC or PLOGI/PRLI + // completes + { + pLoggedInPort = &fcChip->fcPorts; + while( pLoggedInPort ) // for all ports which are expecting + // PDISC after the next LIP, set the + // logoutTimer + { + + if( pLoggedInPort->pdisc) // expecting PDISC within 2 sec? + { + pLoggedInPort->LOGO_timer = 3; // we want 2 seconds + // but Timer granularity + // is 1 second + } + // suspend any I/O in progress until + // PDISC received... + pLoggedInPort->prli = FALSE; // block FCP-SCSI commands + + pLoggedInPort = pLoggedInPort->pNextPort; + } // ... all Previously known ports checked + } + + // since any hot plugging device may NOT support LILP frames + // (such as early Tachyon chips), clear this flag indicating + // we shouldn't use (our copy of) a LILP map. + // If we receive an LILP frame, we'll set it again. + fcChip->Options.LILPin = 0; // our LILPmap is invalid + cpqfcHBAdata->PortDiscDone = 0; // must re-validate FC ports! + + // also, we want to invalidate (i.e. INITIATOR_ABORT) any + // open Login exchanges, in case the LinkDown happened in the + // middle of logins. It's possible that some ports already + // ACCepted login commands which we have not processed before + // another LinkDown occured. Any accepted Login exhanges are + // invalidated by LinkDown, even before they are acknowledged. + // It's also possible for a port to have a Queued Reply or Request + // for login which was interrupted by LinkDown; it may come later, + // but it will be unacceptable to us. + + // we must scan the entire exchange space, find every Login type + // originated by us, and abort it. This is NOT an abort due to + // timeout, so we don't actually send abort to the other port - + // we just complete it to free up the fcExchange slot. + + for( i=TACH_SEST_LEN; i< TACH_MAX_XID; i++) + { // looking for Extended Link Serv.Exchanges + if( Exchanges->fcExchange[i].type == ELS_PDISC || + Exchanges->fcExchange[i].type == ELS_PLOGI || + Exchanges->fcExchange[i].type == ELS_PRLI ) + { + // ABORT the exchange! +#ifdef IMQ_DEBUG + printk("Originator ABORT x_id %Xh, type %Xh, port_id %Xh on LDn\n", + i, Exchanges->fcExchange[i].type, + Exchanges->fcExchange[i].fchs.d_id); +#endif + + Exchanges->fcExchange[i].status |= INITIATOR_ABORT; + cpqfcTSCompleteExchange( fcChip, i); // abort on LDn + } + } + + } + + // ################ LINK UP ################## + if( fcChip->Registers.FMstatus.value & 0x200L ) // Link Up bit + { // AL_PA could have changed + + // We need the following code, duplicated from LinkDn condition, + // because it's possible for the Tachyon to re-initialize (hard + // reset) without ever getting a LinkDn indication. + pLoggedInPort = &fcChip->fcPorts; + while( pLoggedInPort ) // for all ports which are expecting + // PDISC after the next LIP, set the + // logoutTimer + { + if( pLoggedInPort->pdisc) // expecting PDISC within 2 sec? + { + pLoggedInPort->LOGO_timer = 3; // we want 2 seconds + // but Timer granularity + // is 1 second + + // suspend any I/O in progress until + // PDISC received... + + } + pLoggedInPort = pLoggedInPort->pNextPort; + } // ... all Previously known ports checked + + // CpqTs acquired AL_PA in register AL_PA (ACQ_ALPA) + fcChip->Registers.rcv_al_pa.value = + readl(fcChip->Registers.rcv_al_pa.address); + + // Now, if our acquired address is DIFFERENT from our + // previous one, we are not allow to do PDISC - we + // must go back to PLOGI, which will terminate I/O in + // progress for ALL logged in FC devices... + // (This is highly unlikely). + + if( (fcChip->Registers.my_al_pa & 0xFF) != + ((fcChip->Registers.rcv_al_pa.value >> 16) &0xFF) ) + { + +// printk(" #our HBA port_id changed!# "); // FC port_id changed!! + + pLoggedInPort = &fcChip->fcPorts; + while( pLoggedInPort ) // for all ports which are expecting + // PDISC after the next LIP, set the + // logoutTimer + { + pLoggedInPort->pdisc = FALSE; + pLoggedInPort->prli = FALSE; + pLoggedInPort = pLoggedInPort->pNextPort; + } // ... all Previously known ports checked + + // when the port_id changes, we must terminate + // all open exchanges. + cpqfcTSTerminateExchange( cpqfcHBAdata, NULL, PORTID_CHANGED); + + } + + // Replace the entire 24-bit port_id. We only know the + // lower 8 bits (alpa) from Tachyon; if a FLOGI is done, + // we'll get the upper 16-bits from the FLOGI ACC frame. + // If someone plugs into Fabric switch, we'll do FLOGI and + // get full 24-bit port_id; someone could then remove and + // hot-plug us into a dumb hub. If we send a 24-bit PLOGI + // to a "private" loop device, it might blow up. + // Consequently, we force the upper 16-bits of port_id to + // be re-set on every LinkUp transition + fcChip->Registers.my_al_pa = + (fcChip->Registers.rcv_al_pa.value >> 16) & 0xFF; + + + // copy frame manager status to unused ULONG slot + fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1] = + fcChip->Registers.my_al_pa; // (for debugging) + + // for TachLite, we need to write the acquired al_pa + // back into the FMconfig register, because after + // first initialization, the AQ (prev. acq.) bit gets + // set, causing TL FM to use the AL_PA field in FMconfig. + // (In Tachyon, FM writes the acquired AL_PA for us.) + ulBuff = readl( fcChip->Registers.FMconfig.address); + ulBuff &= 0x00ffffffL; // mask out current al_pa + ulBuff |= ( fcChip->Registers.my_al_pa << 24 ); // or in acq. al_pa + fcChip->Registers.FMconfig.value = ulBuff; // copy it back + writel( fcChip->Registers.FMconfig.value, // put in TachLite + fcChip->Registers.FMconfig.address); + + +#ifdef IMQ_DEBUG + printk("#LUp %Xh, FMstat 0x%08X#", + fcChip->Registers.my_al_pa, fcChip->Registers.FMstatus.value); +#endif + + // also set the WRITE-ONLY My_ID Register (for Fabric + // initialization) + writel( fcChip->Registers.my_al_pa, + fcChip->Registers.ReMapMemBase +TL_MEM_TACH_My_ID); + + + fcChip->fcStats.linkUp++; + + // reset TL statistics counters + // (we ignore these error counters + // while link is down) + ulBuff = // just reset TL's counter + readl( fcChip->Registers.FMLinkStatus1.address); + + ulBuff = // just reset TL's counter + readl( fcChip->Registers.FMLinkStatus2.address); + + // for initiator, need to start verifying ports (e.g. PDISC) + + + + + + + CpqTsUnFreezeTachlite( fcChip, 2); // unfreeze Tachlite, if Link OK + + // Tachyon creates an interesting problem for us on LILP frames. + // Instead of writing the incoming LILP frame into the SFQ before + // indicating LINK UP (the actual order of events), Tachyon tells + // us LINK UP, and later us the LILP. So we delay, then examine the + // IMQ for an Inbound CM (x04); if found, we can set + // LINKACTIVE after processing the LILP. Otherwise, just proceed. + // Since Tachyon imposes this time delay (and doesn't tell us + // what it is), we have to impose a delay before "Peeking" the IMQ + // for Tach hardware (DMA) delivery. + // Processing LILP is required by SANMark + udelay( 1000); // microsec delay waiting for LILP (if it comes) + if( PeekIMQEntry( fcChip, ELS_LILP_FRAME) ) + { // found SFQ LILP, which will post LINKACTIVE +// printk("skipping LINKACTIVE post\n"); + + } + else + cpqfcTSPutLinkQue( cpqfcHBAdata, LINKACTIVE, ulFibreFrame); + } + + + + // ******* Set Fabric Login indication ******** + if( fcChip->Registers.FMstatus.value & 0x2000 ) + { + printk(" #Fabric# "); + fcChip->Options.fabric = 1; + } + else + fcChip->Options.fabric = 0; + + + + // ******* LIP(F8,x) or BAD AL_PA? ******** + if( fcChip->Registers.FMstatus.value & 0x30000L ) + { + // copy the error AL_PAs + fcChip->Registers.rcv_al_pa.value = + readl(fcChip->Registers.rcv_al_pa.address); + + // Bad AL_PA? + if( fcChip->Registers.FMstatus.value & 0x10000L ) + { + PFC_LOGGEDIN_PORT pLoggedInPort; + + // copy "BAD" al_pa field + fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1] = + (fcChip->Registers.rcv_al_pa.value & 0xff00L) >> 8; + + pLoggedInPort = fcFindLoggedInPort( fcChip, + NULL, // DON'T search Scsi Nexus + fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1], // port id + NULL, // DON'T search linked list for FC WWN + NULL); // DON'T care about end of list + + if( pLoggedInPort ) + { + // Just in case we got this BAD_ALPA because a device + // quietly disappeared (can happen on non-managed hubs such + // as the Vixel Rapport 1000), + // do an Implicit Logout. We never expect this on a Logged + // in port (but do expect it on port discovery). + // (As a reasonable alternative, this could be changed to + // simply start the implicit logout timer, giving the device + // several seconds to "come back".) + // + printk(" #BAD alpa %Xh# ", + fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1]); + cpqfcTSImplicitLogout( cpqfcHBAdata, pLoggedInPort); + } + } + // LIP(f8,x)? + if( fcChip->Registers.FMstatus.value & 0x20000L ) + { + // for debugging, copy al_pa field + fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2] = + (fcChip->Registers.rcv_al_pa.value & 0xffL); + // get the other port's al_pa + // (one that sent LIP(F8,?) ) + } + } + + // Elastic store err + if( fcChip->Registers.FMstatus.value & 0x400L ) + { + // don't count e-s if loop is down! + if( !(USHORT)(fcChip->Registers.FMstatus.value & 0x80) ) + fcChip->fcStats.e_stores++; + + } + } + break; + + + case INBOUND_FCP_XCHG_COMPLETION: // 0x0C + + // Remarks: + // On Tachlite TL/TS, we get this message when the data phase + // of a SEST inbound transfer is complete. For example, if a WRITE command + // was received with OX_ID 0, we might respond with XFER_RDY with + // RX_ID 8001. This would start the SEST controlled data phases. When + // all data frames are received, we get this inbound completion. This means + // we should send a status frame to complete the status phase of the + // FCP-SCSI exchange, using the same OX_ID,RX_ID that we used for data + // frames. + // See Outbound CM discussion of x_IDs + // Psuedo Code + // Get SEST index (x_ID) + // x_ID out of range, return (err condition) + // set status bits from 2nd dword + // free transactionID & SEST entry + // call fcComplete with transactionID & status + + ulBuff = fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[0]; + x_ID = ulBuff & 0x7fffL; // lower 14 bits SEST_Index/Trans_ID + // (mask out MSB "direction" bit) + // Range check CM OX/RX_ID value... + if( x_ID < TACH_SEST_LEN ) // don't go beyond SEST array space + { + +//#define FCP_COMPLETION_DBG 1 +#ifdef FCP_COMPLETION_DBG + printk(" FCP_CM x_ID %Xh, status %Xh, Cmnd %p\n", + x_ID, ulBuff, Exchanges->fcExchange[x_ID].Cmnd); +#endif + if( ulBuff & 0x08000000L ) // RPC -Response Phase Complete - or - + // time to send response frame? + RPCset = 1; // (SEST transaction) + else + RPCset = 0; + // set the status for this Inbound SCSI transaction's ID + dwStatus = 0L; + if( ulBuff & 0x70000000L ) // any errs? + { + + if( ulBuff & 0x40000000L ) + dwStatus |= LINKFAIL_RX; + + if( ulBuff & 0x20000000L ) + dwStatus |= COUNT_ERROR; + + if( ulBuff & 0x10000000L ) + dwStatus |= OVERFLOW; + } + + + // FCP transaction done - copy status + Exchanges->fcExchange[ x_ID ].status = dwStatus; + + + // Did the exchange get an FCP-RSP response frame? + // (Note the little endian/big endian FC payload difference) + + if( RPCset ) // SEST transaction Response frame rec'd + { + // complete the command in our driver... + cpqfcTSCompleteExchange( fcChip, x_ID); + + } // end "RPCset" + + else // ("target" logic) + { + // Tachlite says all data frames have been received - now it's time + // to analyze data transfer (successful?), then send a response + // frame for this exchange + + ulFibreFrame[0] = x_ID; // copy for later reference + + // if this was a TWE, we have to send satus response + if( Exchanges->fcExchange[ x_ID].type == SCSI_TWE ) + { +// fcPutScsiQue( cpqfcHBAdata, +// NEED_FCP_RSP, ulFibreFrame); // (ulFibreFrame not used here) + } + } + } + else // ERROR CONDITION! bogus x_ID in completion message + { + printk("IN FCP_XCHG: bad x_ID: %Xh\n", x_ID); + } + + break; + + + + + case INBOUND_SCSI_DATA_COMMAND: + case BAD_SCSI_FRAME: + case INB_SCSI_STATUS_COMPLETION: + case BUFFER_PROCESSED_COMPLETION: + break; + } + + // Tachyon is producing; + // we are consuming + fcChip->IMQ->consumerIndex++; // increment OUR consumerIndex + if( fcChip->IMQ->consumerIndex >= IMQ_LEN)// check for rollover + fcChip->IMQ->consumerIndex = 0L; // reset it + + + if( fcChip->IMQ->producerIndex == fcChip->IMQ->consumerIndex ) + { // all Messages are processed - + iStatus = 0; // no more messages to process + + } + else + iStatus = 1; // more messages to process + + // update TachLite's ConsumerIndex... (clears INTA_L) + // NOTE: according to TL/TS UG, the + // "host must return completion messages in sequential order". + // Does this mean one at a time, in the order received? We + // presume so. + + writel( fcChip->IMQ->consumerIndex, + (fcChip->Registers.ReMapMemBase + IMQ_CONSUMER_INDEX)); + +#if IMQ_DEBUG + printk("Process IMQ: writing consumer ndx %d\n ", + fcChip->IMQ->consumerIndex); + printk("PI %X, CI %X\n", + fcChip->IMQ->producerIndex,fcChip->IMQ->consumerIndex ); +#endif + + + + } + else + { + // hmmm... why did we get interrupted/called with no message? + iStatus = -1; // nothing to process +#if IMQ_DEBUG + printk("Process IMQ: no message PI %Xh CI %Xh", + fcChip->IMQ->producerIndex, + fcChip->IMQ->consumerIndex); +#endif + } + + LEAVE("ProcessIMQEntry"); + + return iStatus; +} + + + + + +// This routine initializes Tachyon according to the following +// options (opcode1): +// 1 - RESTART Tachyon, simulate power on condition by shutting +// down laser, resetting the hardware, de-allocating all buffers; +// continue +// 2 - Config Tachyon / PCI registers; +// continue +// 3 - Allocating memory and setting Tachyon queues (write Tachyon regs); +// continue +// 4 - Config frame manager registers, initialize, turn on laser +// +// Returns: +// -1 on fatal error +// 0 on success + +int CpqTsInitializeTachLite( void *pHBA, int opcode1, int opcode2) +{ + CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA; + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + ULONG ulBuff; + UCHAR bBuff; + int iStatus=-1; // assume failure + + ENTER("InitializeTachLite"); + + // verify board's base address (sanity check) + + if( !fcChip->Registers.ReMapMemBase) // NULL address for card? + return -1; // FATAL error! + + + + switch( opcode1 ) + { + case 1: // restore hardware to power-on (hard) restart + + + iStatus = fcChip->ResetTachyon( + cpqfcHBAdata, opcode2); // laser off, reset hardware + // de-allocate aligned buffers + + +/* TBD // reset FC link Q (producer and consumer = 0) + fcLinkQReset(cpqfcHBAdata); + +*/ + + if( iStatus ) + break; + + case 2: // Config PCI/Tachyon registers + // NOTE: For Tach TL/TS, bit 31 must be set to 1. For TS chips, a read + // of bit 31 indicates state of M66EN signal; if 1, chip may run at + // 33-66MHz (see TL/TS UG, pg 159) + + ulBuff = 0x80000000; // TachLite Configuration Register + + writel( ulBuff, fcChip->Registers.TYconfig.address); +// ulBuff = 0x0147L; // CpqTs PCI CFGCMD register +// WritePCIConfiguration( fcChip->Backplane.bus, +// fcChip->Backplane.slot, TLCFGCMD, ulBuff, 4); +// ulBuff = 0x0L; // test! +// ReadPCIConfiguration( fcChip->Backplane.bus, +// fcChip->Backplane.slot, TLCFGCMD, &ulBuff, 4); + + // read back for reference... + fcChip->Registers.TYconfig.value = + readl( fcChip->Registers.TYconfig.address ); + + // what is the PCI bus width? + pci_read_config_byte( cpqfcHBAdata->PciDev, + 0x43, // PCIMCTR offset + &bBuff); + + fcChip->Registers.PCIMCTR = bBuff; + + // set string identifying the chip on the circuit board + + fcChip->Registers.TYstatus.value = + readl( fcChip->Registers.TYstatus.address); + + { +// Now that we are supporting multiple boards, we need to change +// this logic to check for PCI vendor/device IDs... +// for now, quick & dirty is simply checking Chip rev + + ULONG RevId = (fcChip->Registers.TYstatus.value &0x3E0)>>5; + UCHAR Minor = (UCHAR)(RevId & 0x3); + UCHAR Major = (UCHAR)((RevId & 0x1C) >>2); + + printk(" HBA Tachyon RevId %d.%d\n", Major, Minor); + if( (Major == 1) && (Minor == 2) ) + { + sprintf( cpqfcHBAdata->fcChip.Name, STACHLITE66_TS12); + + } + else if( (Major == 1) && (Minor == 3) ) + { + sprintf( cpqfcHBAdata->fcChip.Name, STACHLITE66_TS13); + } + else if( (Major == 2) && (Minor == 1) ) + { + sprintf( cpqfcHBAdata->fcChip.Name, SAGILENT_XL2_21); + } + else + sprintf( cpqfcHBAdata->fcChip.Name, STACHLITE_UNKNOWN); + } + + + + case 3: // allocate mem, set Tachyon Que registers + iStatus = CpqTsCreateTachLiteQues( cpqfcHBAdata, opcode2); + + // now that the Queues exist, Tach can DMA to them, so + // we can begin processing INTs + // INTEN register - enable INT (TachLite interrupt) + writeb( 0x1F, fcChip->Registers.ReMapMemBase + IINTEN); + + + if( iStatus ) + break; + + + case 4: // Config Fame Manager, Init Loop Command, laser on + + // L_PORT or loopback + // depending on Options + iStatus = CpqTsInitializeFrameManager( fcChip,0 ); + if( iStatus ) + { + // failed to initialize Frame Manager + break; + } + + default: + break; + } + LEAVE("InitializeTachLite"); + + return iStatus; +} + + + + +// Depending on the type of platform memory allocation (e.g. dynamic), +// it's probably best to free memory in opposite order as it was allocated. +// Order of allocation: see other function + + +int CpqTsDestroyTachLiteQues( void *pHBA, int opcode) +{ + CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA; + PTACHYON fcChip = &cpqfcHBAdata->fcChip; + USHORT i, j, iStatus=0; + void* vPtr; // mem Align manager sets this to the freed address on success + unsigned long ulPtr; // for 64-bit pointer cast (e.g. Alpa machine) + + ENTER("DestroyTachLiteQues"); + + if( fcChip->SEST ) + { + // search out and free Pool for Extended S/G list pages + + for( i=0, j=0; i < TACH_SEST_LEN; i++, j=0) // for each exchange + { + // It's possible that extended S/G pages were allocated and + // not cleared due to error conditions or O/S driver termination. + // Make sure they're all gone. + while( fcChip->SEST->sgPages[i].PoolPage[j] && + (j < TL_MAX_SGPAGES)) + kfree( fcChip->SEST->sgPages[i].PoolPage[j++]); + + } + ulPtr = (unsigned long)fcChip->SEST; + vPtr = fcMemManager( &cpqfcHBAdata->dynamic_mem[0], + 0,0, (ULONG)ulPtr ); // 'free' mem + fcChip->SEST = 0L; // null invalid ptr + if( !vPtr ) + { + printk("SEST mem not freed\n"); + iStatus = -1; + } + } + + if( fcChip->SFQ ) + { + + ulPtr = (unsigned long)fcChip->SFQ; + vPtr = fcMemManager( &cpqfcHBAdata->dynamic_mem[0], + 0,0, (ULONG)ulPtr ); // 'free' mem + fcChip->SFQ = 0L; // null invalid ptr + if( !vPtr ) + { + printk("SFQ mem not freed\n"); + iStatus = -2; + } + } + + + if( fcChip->IMQ ) + { + // clear Indexes to show empty Queue + fcChip->IMQ->producerIndex = 0; + fcChip->IMQ->consumerIndex = 0; + + ulPtr = (unsigned long)fcChip->IMQ; + vPtr = fcMemManager( &cpqfcHBAdata->dynamic_mem[0], + 0,0, (ULONG)ulPtr ); // 'free' mem + fcChip->IMQ = 0L; // null invalid ptr + if( !vPtr ) + { + printk("IMQ mem not freed\n"); + iStatus = -3; + } + } + + if( fcChip->ERQ ) // release memory blocks used by the queues + { + ulPtr = (unsigned long)fcChip->ERQ; + vPtr = fcMemManager( &cpqfcHBAdata->dynamic_mem[0], + 0,0, (ULONG)ulPtr ); // 'free' mem + fcChip->ERQ = 0L; // null invalid ptr + if( !vPtr ) + { + printk("ERQ mem not freed\n"); + iStatus = -4; + } + } + + // free up the primary EXCHANGES struct + if( fcChip->Exchanges != NULL) + { +// printk("kfree() on Exchanges @%p\n", fcChip->Exchanges); + kfree( fcChip->Exchanges); + } + + // free up Link Q + if( cpqfcHBAdata->fcLQ != NULL ) + { +// printk("kfree() on LinkQ @%p\n", fcChip->fcLQ); + kfree( cpqfcHBAdata->fcLQ); + } + + LEAVE("DestroyTachLiteQues"); + + return iStatus; // non-zero (failed) if any memory not freed +} + + + + + +// The SFQ is an array with SFQ_LEN length, each element (QEntry) +// with eight 32-bit words. TachLite places incoming FC frames (i.e. +// a valid FC frame with our AL_PA ) in contiguous SFQ entries +// and sends a completion message telling the host where the frame is +// in the que. +// This function copies the current (or oldest not-yet-processed) QEntry to +// a caller's contiguous buffer and updates the Tachyon chip's consumer index +// +// NOTE: +// An FC frame may consume one or many SFQ entries. We know the total +// length from the completion message. The caller passes a buffer large +// enough for the complete message (max 2k). + +static void CpqTsGetSFQEntry( + PTACHYON fcChip, + USHORT producerNdx, + ULONG *ulDestPtr, // contiguous destination buffer + BOOLEAN UpdateChip) +{ + ULONG total_bytes=0; + ULONG consumerIndex = fcChip->SFQ->consumerIndex; + + // check passed copy of SFQ producer index - + // is a new message waiting for us? + // equal indexes means SFS is copied + + while( producerNdx != consumerIndex ) + { // need to process message + total_bytes += 64; // maintain count to prevent writing past buffer + // don't allow copies over Fibre Channel defined length! + if( total_bytes <= 2048 ) + { + memcpy( ulDestPtr, + &fcChip->SFQ->QEntry[consumerIndex], + 64 ); // each SFQ entry is 64 bytes + ulDestPtr += 16; // advance pointer to next 64 byte block + } + // Tachyon is producing, + // and we are consuming + + if( ++consumerIndex >= SFQ_LEN)// check for rollover + consumerIndex = 0L; // reset it + } + + // if specified, update the Tachlite chip ConsumerIndex... + if( UpdateChip ) + { + fcChip->SFQ->consumerIndex = consumerIndex; + writel( fcChip->SFQ->consumerIndex, + fcChip->Registers.SFQconsumerIndex.address); + } +} + + + +// TachLite routinely freezes it's core ques - Outbound FIFO, Inbound FIFO, +// and Exchange Request Queue (ERQ) on error recover - +// (e.g. whenever a LIP occurs). Here +// we routinely RESUME by clearing these bits, but only if the loop is up +// to avoid ERROR IDLE messages forever. + +void CpqTsUnFreezeTachlite( void *pChip, int type ) +{ + PTACHYON fcChip = (PTACHYON)pChip; + fcChip->Registers.TYcontrol.value = + readl(fcChip->Registers.TYcontrol.address); + + // (bit 4 of value is GBIC LASER) + // if we 'unfreeze' the core machines before the loop is healthy + // (i.e. FLT, OS, LS failure bits set in FMstatus) + // we can get 'error idle' messages forever. Verify that + // FMstatus (Link Status) is OK before unfreezing. + + if( !(fcChip->Registers.FMstatus.value & 0x07000000L) && // bits clear? + !(fcChip->Registers.FMstatus.value & 0x80 )) // Active LPSM? + { + fcChip->Registers.TYcontrol.value &= ~0x300L; // clear FEQ, FFA + if( type == 1 ) // unfreeze ERQ only + { +// printk("Unfreezing ERQ\n"); + fcChip->Registers.TYcontrol.value |= 0x10000L; // set REQ + } + else // unfreeze both ERQ and FCP-ASSIST (SEST) + { +// printk("Unfreezing ERQ & FCP-ASSIST\n"); + + // set ROF, RIF, REQ - resume Outbound FCP, Inbnd FCP, ERQ + fcChip->Registers.TYcontrol.value |= 0x70000L; // set ROF, RIF, REQ + } + + writel( fcChip->Registers.TYcontrol.value, + fcChip->Registers.TYcontrol.address); + + } + // readback for verify (TachLite still frozen?) + fcChip->Registers.TYstatus.value = + readl(fcChip->Registers.TYstatus.address); +} + + +// Whenever an FC Exchange Abort is required, we must manipulate the +// Host/Tachyon shared memory SEST table. Before doing this, we +// must freeze Tachyon, which flushes certain buffers and ensure we +// can manipulate the SEST without contention. +// This freeze function will result in FCP & ERQ FROZEN completion +// messages (per argument "type"). + +void CpqTsFreezeTachlite( void *pChip, int type ) +{ + PTACHYON fcChip = (PTACHYON)pChip; + fcChip->Registers.TYcontrol.value = + readl(fcChip->Registers.TYcontrol.address); + + //set FFA, FEQ - freezes SCSI assist and ERQ + if( type == 1) // freeze ERQ only + fcChip->Registers.TYcontrol.value |= 0x100L; // (bit 4 is laser) + else // freeze both FCP assists (SEST) and ERQ + fcChip->Registers.TYcontrol.value |= 0x300L; // (bit 4 is laser) + + writel( fcChip->Registers.TYcontrol.value, + fcChip->Registers.TYcontrol.address); + +} + + + + +// TL has two Frame Manager Link Status Registers, with three 8-bit +// fields each. These eight bit counters are cleared after each read, +// so we define six 32-bit accumulators for these TL counters. This +// function breaks out each 8-bit field and adds the value to the existing +// sum. (s/w counters cleared independently) + +void fcParseLinkStatusCounters(PTACHYON fcChip) +{ + UCHAR bBuff; + ULONG ulBuff; + + +// The BB0 timer usually increments when TL is initialized, resulting +// in an initially bogus count. If our own counter is ZERO, it means we +// are reading this thing for the first time, so we ignore the first count. +// Also, reading the register does not clear it, so we have to keep an +// additional static counter to detect rollover (yuk). + + if( fcChip->fcStats.lastBB0timer == 0L) // TL was reset? (ignore 1st values) + { + // get TL's register counter - the "last" count + fcChip->fcStats.lastBB0timer = + fcChip->Registers.FMBB_CreditZero.value & 0x00ffffffL; + } + else // subsequent pass - check for rollover + { + // "this" count + ulBuff = fcChip->Registers.FMBB_CreditZero.value & 0x00ffffffL; + if( fcChip->fcStats.lastBB0timer > ulBuff ) // rollover happened + { + // counter advanced to max... + fcChip->fcStats.BB0_Timer += (0x00FFFFFFL - fcChip->fcStats.lastBB0timer); + fcChip->fcStats.BB0_Timer += ulBuff; // plus some more + + + } + else // no rollover -- more counts or no change + { + fcChip->fcStats.BB0_Timer += (ulBuff - fcChip->fcStats.lastBB0timer); + + } + + fcChip->fcStats.lastBB0timer = ulBuff; + } + + + + bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus1.value >> 24); + fcChip->fcStats.LossofSignal += bBuff; + + bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus1.value >> 16); + fcChip->fcStats.BadRXChar += bBuff; + + bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus1.value >> 8); + fcChip->fcStats.LossofSync += bBuff; + + + bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus2.value >> 24); + fcChip->fcStats.Rx_EOFa += bBuff; + + bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus2.value >> 16); + fcChip->fcStats.Dis_Frm += bBuff; + + bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus2.value >> 8); + fcChip->fcStats.Bad_CRC += bBuff; +} + + +void cpqfcTSClearLinkStatusCounters(PTACHYON fcChip) +{ + ENTER("ClearLinkStatusCounters"); + memset( &fcChip->fcStats, 0, sizeof( FCSTATS)); + LEAVE("ClearLinkStatusCounters"); + +} + + + + +// The following function reads the I2C hardware to get the adapter's +// World Wide Name (WWN). +// If the WWN is "500805f1fadb43e8" (as printed on the card), the +// Tachyon WWN_hi (32-bit) register is 500805f1, and WWN_lo register +// is fadb43e8. +// In the NVRAM, the bytes appear as: +// [2d] .. +// [2e] .. +// [2f] 50 +// [30] 08 +// [31] 05 +// [32] f1 +// [33] fa +// [34] db +// [35] 43 +// [36] e8 +// +// In the Fibre Channel (Big Endian) format, the FC-AL LISM frame will +// be correctly loaded by Tachyon silicon. In the login payload, bytes +// must be correctly swapped for Big Endian format. + +int CpqTsReadWriteWWN( PVOID pChip, int Read) +{ + PTACHYON fcChip = (PTACHYON)pChip; +#define NVRAM_SIZE 512 + unsigned short i, count = NVRAM_SIZE; + UCHAR nvRam[NVRAM_SIZE], WWNbuf[8]; + ULONG ulBuff; + int iStatus=-1; // assume failure + int WWNoffset; + + ENTER("ReadWriteWWN"); + // Now try to read the WWN from the adapter's NVRAM + + if( Read ) // READing NVRAM WWN? + { + ulBuff = cpqfcTS_ReadNVRAM( fcChip->Registers.TYstatus.address, + fcChip->Registers.TYcontrol.address, + count, &nvRam[0] ); + + if( ulBuff ) // NVRAM read successful? + { + iStatus = 0; // success! + + // for engineering/ prototype boards, the data may be + // invalid (GIGO, usually all "FF"); this prevents the + // parse routine from working correctly, which means + // nothing will be written to our passed buffer. + + WWNoffset = cpqfcTS_GetNVRAM_data( WWNbuf, nvRam ); + + if( !WWNoffset ) // uninitialized NVRAM -- copy bytes directly + { + printk( "CAUTION: Copying NVRAM data on fcChip\n"); + for( i= 0; i < 8; i++) + WWNbuf[i] = nvRam[i +0x2f]; // dangerous! some formats won't work + } + + fcChip->Registers.wwn_hi = 0L; + fcChip->Registers.wwn_lo = 0L; + for( i=0; i<4; i++) // WWN bytes are big endian in NVRAM + { + ulBuff = 0L; + ulBuff = (ULONG)(WWNbuf[i]) << (8 * (3-i)); + fcChip->Registers.wwn_hi |= ulBuff; + } + for( i=0; i<4; i++) // WWN bytes are big endian in NVRAM + { + ulBuff = 0L; + ulBuff = (ULONG)(WWNbuf[i+4]) << (8 * (3-i)); + fcChip->Registers.wwn_lo |= ulBuff; + } + } // done reading + else + { + + printk( "cpqfcTS: NVRAM read failed\n"); + + } + } + + else // WRITE + { + + // NOTE: WRITE not supported & not used in released driver. + + + printk("ReadWriteNRAM: can't write NVRAM; aborting write\n"); + } + + LEAVE("ReadWriteWWN"); + return iStatus; +} + + + + + +// The following function reads or writes the entire "NVRAM" contents of +// the I2C hardware (i.e. the NM24C03). Note that HP's 5121A (TS 66Mhz) +// adapter does not use the NM24C03 chip, so this function only works on +// Compaq's adapters. + +int CpqTsReadWriteNVRAM( PVOID pChip, PVOID buf, int Read) +{ + PTACHYON fcChip = (PTACHYON)pChip; +#define NVRAM_SIZE 512 + ULONG ulBuff; + UCHAR *ucPtr = buf; // cast caller's void ptr to UCHAR array + int iStatus=-1; // assume failure + + + if( Read ) // READing NVRAM? + { + ulBuff = cpqfcTS_ReadNVRAM( // TRUE on success + fcChip->Registers.TYstatus.address, + fcChip->Registers.TYcontrol.address, + 256, // bytes to write + ucPtr ); // source ptr + + + if( ulBuff ) + iStatus = 0; // success + else + { +#ifdef DBG + printk( "CAUTION: NVRAM read failed\n"); +#endif + } + } // done reading + + else // WRITING NVRAM + { + + printk("cpqfcTS: WRITE of FC Controller's NVRAM disabled\n"); + } + + return iStatus; +} |