| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* Copyright 2000, Compaq Computer Corporation | 
|  | 2 | * Fibre Channel Host Bus Adapter | 
|  | 3 | * 64-bit, 66MHz PCI | 
|  | 4 | * Originally developed and tested on: | 
|  | 5 | * (front): [chip] Tachyon TS HPFC-5166A/1.2  L2C1090 ... | 
|  | 6 | *          SP# P225CXCBFIEL6T, Rev XC | 
|  | 7 | *          SP# 161290-001, Rev XD | 
|  | 8 | * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5 | 
|  | 9 | * | 
|  | 10 | * This program is free software; you can redistribute it and/or modify it | 
|  | 11 | * under the terms of the GNU General Public License as published by the | 
|  | 12 | * Free Software Foundation; either version 2, or (at your option) any | 
|  | 13 | * later version. | 
|  | 14 | * | 
|  | 15 | * This program is distributed in the hope that it will be useful, but | 
|  | 16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | 18 | * General Public License for more details. | 
|  | 19 | * Written by Don Zimmerman | 
|  | 20 | */ | 
|  | 21 | /* These functions control the host bus adapter (HBA) hardware.  The main chip | 
|  | 22 | control takes place in the interrupt handler where we process the IMQ | 
|  | 23 | (Inbound Message Queue).  The IMQ is Tachyon's way of communicating FC link | 
|  | 24 | events and state information to the driver.  The Single Frame Queue (SFQ) | 
|  | 25 | buffers incoming FC frames for processing by the driver.  References to | 
|  | 26 | "TL/TS UG" are for: | 
|  | 27 | "HP HPFC-5100/5166 Tachyon TL/TS ICs User Guide", August 16, 1999, 1st Ed. | 
|  | 28 | Hewlitt Packard Manual Part Number 5968-1083E. | 
|  | 29 | */ | 
|  | 30 |  | 
|  | 31 | #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) | 
|  | 32 |  | 
|  | 33 | #include <linux/blkdev.h> | 
|  | 34 | #include <linux/kernel.h> | 
|  | 35 | #include <linux/string.h> | 
|  | 36 | #include <linux/ioport.h>  // request_region() prototype | 
|  | 37 | #include <linux/sched.h> | 
|  | 38 | #include <linux/slab.h>  // need "kfree" for ext. S/G pages | 
|  | 39 | #include <linux/types.h> | 
|  | 40 | #include <linux/pci.h> | 
|  | 41 | #include <linux/delay.h> | 
|  | 42 | #include <linux/unistd.h> | 
|  | 43 | #include <asm/io.h>  // struct pt_regs for IRQ handler & Port I/O | 
|  | 44 | #include <asm/irq.h> | 
|  | 45 | #include <linux/spinlock.h> | 
|  | 46 |  | 
|  | 47 | #include "scsi.h" | 
|  | 48 | #include <scsi/scsi_host.h>   // Scsi_Host definition for INT handler | 
|  | 49 | #include "cpqfcTSchip.h" | 
|  | 50 | #include "cpqfcTSstructs.h" | 
|  | 51 |  | 
|  | 52 | //#define IMQ_DEBUG 1 | 
|  | 53 |  | 
|  | 54 | static void fcParseLinkStatusCounters(TACHYON * fcChip); | 
|  | 55 | static void CpqTsGetSFQEntry(TACHYON * fcChip, | 
|  | 56 | USHORT pi, ULONG * buffr, BOOLEAN UpdateChip); | 
|  | 57 |  | 
|  | 58 | static void | 
|  | 59 | cpqfc_free_dma_consistent(CPQFCHBA *cpqfcHBAdata) | 
|  | 60 | { | 
|  | 61 | // free up the primary EXCHANGES struct and Link Q | 
|  | 62 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | 
|  | 63 |  | 
|  | 64 | if (fcChip->Exchanges != NULL) | 
|  | 65 | pci_free_consistent(cpqfcHBAdata->PciDev, sizeof(FC_EXCHANGES), | 
|  | 66 | fcChip->Exchanges, fcChip->exch_dma_handle); | 
|  | 67 | fcChip->Exchanges = NULL; | 
|  | 68 | if (cpqfcHBAdata->fcLQ != NULL) | 
|  | 69 | pci_free_consistent(cpqfcHBAdata->PciDev, sizeof(FC_LINK_QUE), | 
|  | 70 | cpqfcHBAdata->fcLQ, cpqfcHBAdata->fcLQ_dma_handle); | 
|  | 71 | cpqfcHBAdata->fcLQ = NULL; | 
|  | 72 | } | 
|  | 73 |  | 
|  | 74 | // Note special requirements for Q alignment!  (TL/TS UG pg. 190) | 
|  | 75 | // We place critical index pointers at end of QUE elements to assist | 
|  | 76 | // in non-symbolic (i.e. memory dump) debugging | 
|  | 77 | // opcode defines placement of Queues (e.g. local/external RAM) | 
|  | 78 |  | 
|  | 79 | int CpqTsCreateTachLiteQues( void* pHBA, int opcode) | 
|  | 80 | { | 
|  | 81 | CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA; | 
|  | 82 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | 
|  | 83 |  | 
|  | 84 | int iStatus=0; | 
|  | 85 | unsigned long ulAddr; | 
|  | 86 | dma_addr_t ERQdma, IMQdma, SPQdma, SESTdma; | 
|  | 87 | int i; | 
|  | 88 |  | 
|  | 89 | // NOTE! fcMemManager() will return system virtual addresses. | 
|  | 90 | // System (kernel) virtual addresses, though non-paged, still | 
|  | 91 | // aren't physical addresses.  Convert to PHYSICAL_ADDRESS for Tachyon's | 
|  | 92 | // DMA use. | 
|  | 93 | ENTER("CreateTachLiteQues"); | 
|  | 94 |  | 
|  | 95 |  | 
|  | 96 | // Allocate primary EXCHANGES array... | 
|  | 97 | fcChip->Exchanges = NULL; | 
|  | 98 | cpqfcHBAdata->fcLQ = NULL; | 
|  | 99 |  | 
|  | 100 | /* printk("Allocating %u for %u Exchanges ", | 
|  | 101 | (ULONG)sizeof(FC_EXCHANGES), TACH_MAX_XID); */ | 
|  | 102 | fcChip->Exchanges = pci_alloc_consistent(cpqfcHBAdata->PciDev, | 
|  | 103 | sizeof(FC_EXCHANGES), &fcChip->exch_dma_handle); | 
|  | 104 | /* printk("@ %p\n", fcChip->Exchanges); */ | 
|  | 105 |  | 
|  | 106 | if( fcChip->Exchanges == NULL ) // fatal error!! | 
|  | 107 | { | 
|  | 108 | printk("pci_alloc_consistent failure on Exchanges: fatal error\n"); | 
|  | 109 | return -1; | 
|  | 110 | } | 
|  | 111 | // zero out the entire EXCHANGE space | 
|  | 112 | memset( fcChip->Exchanges, 0, sizeof( FC_EXCHANGES)); | 
|  | 113 |  | 
|  | 114 |  | 
|  | 115 | /* printk("Allocating %u for LinkQ ", (ULONG)sizeof(FC_LINK_QUE)); */ | 
|  | 116 | cpqfcHBAdata->fcLQ = pci_alloc_consistent(cpqfcHBAdata->PciDev, | 
|  | 117 | sizeof( FC_LINK_QUE), &cpqfcHBAdata->fcLQ_dma_handle); | 
|  | 118 | /* printk("@ %p (%u elements)\n", cpqfcHBAdata->fcLQ, FC_LINKQ_DEPTH); */ | 
|  | 119 |  | 
|  | 120 | if( cpqfcHBAdata->fcLQ == NULL ) // fatal error!! | 
|  | 121 | { | 
|  | 122 | cpqfc_free_dma_consistent(cpqfcHBAdata); | 
|  | 123 | printk("pci_alloc_consistent() failure on fc Link Que: fatal error\n"); | 
|  | 124 | return -1; | 
|  | 125 | } | 
|  | 126 | // zero out the entire EXCHANGE space | 
|  | 127 | memset( cpqfcHBAdata->fcLQ, 0, sizeof( FC_LINK_QUE)); | 
|  | 128 |  | 
|  | 129 | // Verify that basic Tach I/O registers are not NULL | 
|  | 130 | if( !fcChip->Registers.ReMapMemBase ) | 
|  | 131 | { | 
|  | 132 | cpqfc_free_dma_consistent(cpqfcHBAdata); | 
|  | 133 | printk("HBA base address NULL: fatal error\n"); | 
|  | 134 | return -1; | 
|  | 135 | } | 
|  | 136 |  | 
|  | 137 |  | 
|  | 138 | // Initialize the fcMemManager memory pairs (stores allocated/aligned | 
|  | 139 | // pairs for future freeing) | 
|  | 140 | memset( cpqfcHBAdata->dynamic_mem, 0, sizeof(cpqfcHBAdata->dynamic_mem)); | 
|  | 141 |  | 
|  | 142 |  | 
|  | 143 | // Allocate Tach's Exchange Request Queue (each ERQ entry 32 bytes) | 
|  | 144 |  | 
|  | 145 | fcChip->ERQ = fcMemManager( cpqfcHBAdata->PciDev, | 
|  | 146 | &cpqfcHBAdata->dynamic_mem[0], | 
|  | 147 | sizeof( TachLiteERQ ), 32*(ERQ_LEN), 0L, &ERQdma); | 
|  | 148 | if( !fcChip->ERQ ) | 
|  | 149 | { | 
|  | 150 | cpqfc_free_dma_consistent(cpqfcHBAdata); | 
|  | 151 | printk("pci_alloc_consistent/alignment failure on ERQ: fatal error\n"); | 
|  | 152 | return -1; | 
|  | 153 | } | 
|  | 154 | fcChip->ERQ->length = ERQ_LEN-1; | 
|  | 155 | ulAddr = (ULONG) ERQdma; | 
|  | 156 | #if BITS_PER_LONG > 32 | 
|  | 157 | if( (ulAddr >> 32) ) | 
|  | 158 | { | 
|  | 159 | cpqfc_free_dma_consistent(cpqfcHBAdata); | 
|  | 160 | printk(" FATAL! ERQ ptr %p exceeds Tachyon's 32-bit register size\n", | 
|  | 161 | (void*)ulAddr); | 
|  | 162 | return -1;  // failed | 
|  | 163 | } | 
|  | 164 | #endif | 
|  | 165 | fcChip->ERQ->base = (ULONG)ulAddr;  // copy for quick reference | 
|  | 166 |  | 
|  | 167 |  | 
|  | 168 | // Allocate Tach's Inbound Message Queue (32 bytes per entry) | 
|  | 169 |  | 
|  | 170 | fcChip->IMQ = fcMemManager( cpqfcHBAdata->PciDev, | 
|  | 171 | &cpqfcHBAdata->dynamic_mem[0], | 
|  | 172 | sizeof( TachyonIMQ ), 32*(IMQ_LEN), 0L, &IMQdma ); | 
|  | 173 | if( !fcChip->IMQ ) | 
|  | 174 | { | 
|  | 175 | cpqfc_free_dma_consistent(cpqfcHBAdata); | 
|  | 176 | printk("pci_alloc_consistent/alignment failure on IMQ: fatal error\n"); | 
|  | 177 | return -1; | 
|  | 178 | } | 
|  | 179 | fcChip->IMQ->length = IMQ_LEN-1; | 
|  | 180 |  | 
|  | 181 | ulAddr = IMQdma; | 
|  | 182 | #if BITS_PER_LONG > 32 | 
|  | 183 | if( (ulAddr >> 32) ) | 
|  | 184 | { | 
|  | 185 | cpqfc_free_dma_consistent(cpqfcHBAdata); | 
|  | 186 | printk(" FATAL! IMQ ptr %p exceeds Tachyon's 32-bit register size\n", | 
|  | 187 | (void*)ulAddr); | 
|  | 188 | return -1;  // failed | 
|  | 189 | } | 
|  | 190 | #endif | 
|  | 191 | fcChip->IMQ->base = (ULONG)ulAddr;  // copy for quick reference | 
|  | 192 |  | 
|  | 193 |  | 
|  | 194 | // Allocate Tach's  Single Frame Queue (64 bytes per entry) | 
|  | 195 | fcChip->SFQ = fcMemManager( cpqfcHBAdata->PciDev, | 
|  | 196 | &cpqfcHBAdata->dynamic_mem[0], | 
|  | 197 | sizeof( TachLiteSFQ ), 64*(SFQ_LEN),0L, &SPQdma ); | 
|  | 198 | if( !fcChip->SFQ ) | 
|  | 199 | { | 
|  | 200 | cpqfc_free_dma_consistent(cpqfcHBAdata); | 
|  | 201 | printk("pci_alloc_consistent/alignment failure on SFQ: fatal error\n"); | 
|  | 202 | return -1; | 
|  | 203 | } | 
|  | 204 | fcChip->SFQ->length = SFQ_LEN-1;      // i.e. Que length [# entries - | 
|  | 205 | // min. 32; max.  4096 (0xffff)] | 
|  | 206 |  | 
|  | 207 | ulAddr = SPQdma; | 
|  | 208 | #if BITS_PER_LONG > 32 | 
|  | 209 | if( (ulAddr >> 32) ) | 
|  | 210 | { | 
|  | 211 | cpqfc_free_dma_consistent(cpqfcHBAdata); | 
|  | 212 | printk(" FATAL! SFQ ptr %p exceeds Tachyon's 32-bit register size\n", | 
|  | 213 | (void*)ulAddr); | 
|  | 214 | return -1;  // failed | 
|  | 215 | } | 
|  | 216 | #endif | 
|  | 217 | fcChip->SFQ->base = (ULONG)ulAddr;  // copy for quick reference | 
|  | 218 |  | 
|  | 219 |  | 
|  | 220 | // Allocate SCSI Exchange State Table; aligned nearest @sizeof | 
|  | 221 | // power-of-2 boundary | 
|  | 222 | // LIVE DANGEROUSLY!  Assume the boundary for SEST mem will | 
|  | 223 | // be on physical page (e.g. 4k) boundary. | 
|  | 224 | /* printk("Allocating %u for TachSEST for %u Exchanges\n", | 
|  | 225 | (ULONG)sizeof(TachSEST), TACH_SEST_LEN); */ | 
|  | 226 | fcChip->SEST = fcMemManager( cpqfcHBAdata->PciDev, | 
|  | 227 | &cpqfcHBAdata->dynamic_mem[0], | 
|  | 228 | sizeof(TachSEST),  4, 0L, &SESTdma ); | 
|  | 229 | //		  sizeof(TachSEST),  64*TACH_SEST_LEN, 0L ); | 
|  | 230 | if( !fcChip->SEST ) | 
|  | 231 | { | 
|  | 232 | cpqfc_free_dma_consistent(cpqfcHBAdata); | 
|  | 233 | printk("pci_alloc_consistent/alignment failure on SEST: fatal error\n"); | 
|  | 234 | return -1; | 
|  | 235 | } | 
|  | 236 |  | 
|  | 237 | for( i=0; i < TACH_SEST_LEN; i++)  // for each exchange | 
|  | 238 | fcChip->SEST->sgPages[i] = NULL; | 
|  | 239 |  | 
|  | 240 | fcChip->SEST->length = TACH_SEST_LEN;  // e.g. DON'T subtract one | 
|  | 241 | // (TL/TS UG, pg 153) | 
|  | 242 |  | 
|  | 243 | ulAddr = SESTdma; | 
|  | 244 | #if BITS_PER_LONG > 32 | 
|  | 245 | if( (ulAddr >> 32) ) | 
|  | 246 | { | 
|  | 247 | cpqfc_free_dma_consistent(cpqfcHBAdata); | 
|  | 248 | printk(" FATAL! SFQ ptr %p exceeds Tachyon's 32-bit register size\n", | 
|  | 249 | (void*)ulAddr); | 
|  | 250 | return -1;  // failed | 
|  | 251 | } | 
|  | 252 | #endif | 
|  | 253 | fcChip->SEST->base = (ULONG)ulAddr;  // copy for quick reference | 
|  | 254 |  | 
|  | 255 |  | 
|  | 256 | // Now that structures are defined, | 
|  | 257 | // fill in Tachyon chip registers... | 
|  | 258 |  | 
|  | 259 | // EEEEEEEE  EXCHANGE REQUEST QUEUE | 
|  | 260 |  | 
|  | 261 | writel( fcChip->ERQ->base, | 
|  | 262 | (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_BASE)); | 
|  | 263 |  | 
|  | 264 | writel( fcChip->ERQ->length, | 
|  | 265 | (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_LENGTH)); | 
|  | 266 |  | 
|  | 267 |  | 
|  | 268 | fcChip->ERQ->producerIndex = 0L; | 
|  | 269 | writel( fcChip->ERQ->producerIndex, | 
|  | 270 | (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_PRODUCER_INDEX)); | 
|  | 271 |  | 
|  | 272 |  | 
|  | 273 | // NOTE! write consumer index last, since the write | 
|  | 274 | // causes Tachyon to process the other registers | 
|  | 275 |  | 
|  | 276 | ulAddr = ((unsigned long)&fcChip->ERQ->consumerIndex - | 
|  | 277 | (unsigned long)fcChip->ERQ) + (unsigned long) ERQdma; | 
|  | 278 |  | 
|  | 279 | // NOTE! Tachyon DMAs to the ERQ consumer Index host | 
|  | 280 | // address; must be correctly aligned | 
|  | 281 | writel( (ULONG)ulAddr, | 
|  | 282 | (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_CONSUMER_INDEX_ADR)); | 
|  | 283 |  | 
|  | 284 |  | 
|  | 285 |  | 
|  | 286 | // IIIIIIIIIIIII  INBOUND MESSAGE QUEUE | 
|  | 287 | // Tell Tachyon where the Que starts | 
|  | 288 |  | 
|  | 289 | // set the Host's pointer for Tachyon to access | 
|  | 290 |  | 
|  | 291 | /* printk("  cpqfcTS: writing IMQ BASE %Xh  ", fcChip->IMQ->base ); */ | 
|  | 292 | writel( fcChip->IMQ->base, | 
|  | 293 | (fcChip->Registers.ReMapMemBase + IMQ_BASE)); | 
|  | 294 |  | 
|  | 295 | writel( fcChip->IMQ->length, | 
|  | 296 | (fcChip->Registers.ReMapMemBase + IMQ_LENGTH)); | 
|  | 297 |  | 
|  | 298 | writel( fcChip->IMQ->consumerIndex, | 
|  | 299 | (fcChip->Registers.ReMapMemBase + IMQ_CONSUMER_INDEX)); | 
|  | 300 |  | 
|  | 301 |  | 
|  | 302 | // NOTE: TachLite DMAs to the producerIndex host address | 
|  | 303 | // must be correctly aligned with address bits 1-0 cleared | 
|  | 304 | // Writing the BASE register clears the PI register, so write it last | 
|  | 305 | ulAddr = ((unsigned long)&fcChip->IMQ->producerIndex - | 
|  | 306 | (unsigned long)fcChip->IMQ) + (unsigned long) IMQdma; | 
|  | 307 |  | 
|  | 308 | #if BITS_PER_LONG > 32 | 
|  | 309 | if( (ulAddr >> 32) ) | 
|  | 310 | { | 
|  | 311 | cpqfc_free_dma_consistent(cpqfcHBAdata); | 
|  | 312 | printk(" FATAL! IMQ ptr %p exceeds Tachyon's 32-bit register size\n", | 
|  | 313 | (void*)ulAddr); | 
|  | 314 | return -1;  // failed | 
|  | 315 | } | 
|  | 316 | #endif | 
|  | 317 | #if DBG | 
|  | 318 | printk("  PI %Xh\n", (ULONG)ulAddr ); | 
|  | 319 | #endif | 
|  | 320 | writel( (ULONG)ulAddr, | 
|  | 321 | (fcChip->Registers.ReMapMemBase + IMQ_PRODUCER_INDEX)); | 
|  | 322 |  | 
|  | 323 |  | 
|  | 324 |  | 
|  | 325 | // SSSSSSSSSSSSSSS SINGLE FRAME SEQUENCE | 
|  | 326 | // Tell TachLite where the Que starts | 
|  | 327 |  | 
|  | 328 | writel( fcChip->SFQ->base, | 
|  | 329 | (fcChip->Registers.ReMapMemBase + TL_MEM_SFQ_BASE)); | 
|  | 330 |  | 
|  | 331 | writel( fcChip->SFQ->length, | 
|  | 332 | (fcChip->Registers.ReMapMemBase + TL_MEM_SFQ_LENGTH)); | 
|  | 333 |  | 
|  | 334 |  | 
|  | 335 | // tell TachLite where SEST table is & how long | 
|  | 336 | writel( fcChip->SEST->base, | 
|  | 337 | (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_BASE)); | 
|  | 338 |  | 
|  | 339 | /* printk("  cpqfcTS: SEST %p(virt): Wrote base %Xh @ %p\n", | 
|  | 340 | fcChip->SEST, fcChip->SEST->base, | 
|  | 341 | fcChip->Registers.ReMapMemBase + TL_MEM_SEST_BASE); */ | 
|  | 342 |  | 
|  | 343 | writel( fcChip->SEST->length, | 
|  | 344 | (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_LENGTH)); | 
|  | 345 |  | 
|  | 346 | writel( (TL_EXT_SG_PAGE_COUNT-1), | 
|  | 347 | (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_SG_PAGE)); | 
|  | 348 |  | 
|  | 349 |  | 
|  | 350 | LEAVE("CreateTachLiteQues"); | 
|  | 351 |  | 
|  | 352 | return iStatus; | 
|  | 353 | } | 
|  | 354 |  | 
|  | 355 |  | 
|  | 356 |  | 
|  | 357 | // function to return TachLite to Power On state | 
|  | 358 | // 1st - reset tachyon ('SOFT' reset) | 
|  | 359 | // others - future | 
|  | 360 |  | 
|  | 361 | int CpqTsResetTachLite(void *pHBA, int type) | 
|  | 362 | { | 
|  | 363 | CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA; | 
|  | 364 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | 
|  | 365 | ULONG ulBuff, i; | 
|  | 366 | int ret_status=0; // def. success | 
|  | 367 |  | 
|  | 368 | ENTER("ResetTach"); | 
|  | 369 |  | 
|  | 370 | switch(type) | 
|  | 371 | { | 
|  | 372 |  | 
|  | 373 | case CLEAR_FCPORTS: | 
|  | 374 |  | 
|  | 375 | // in case he was running previously, mask Tach's interrupt | 
|  | 376 | writeb( 0, (fcChip->Registers.ReMapMemBase + IINTEN)); | 
|  | 377 |  | 
|  | 378 | // de-allocate mem for any Logged in ports | 
|  | 379 | // (e.g., our module is unloading) | 
|  | 380 | // search the forward linked list, de-allocating | 
|  | 381 | // the memory we allocated when the port was initially logged in | 
|  | 382 | { | 
|  | 383 | PFC_LOGGEDIN_PORT pLoggedInPort = fcChip->fcPorts.pNextPort; | 
|  | 384 | PFC_LOGGEDIN_PORT ptr; | 
|  | 385 | //        printk("checking for allocated LoggedInPorts...\n"); | 
|  | 386 |  | 
|  | 387 | while( pLoggedInPort ) | 
|  | 388 | { | 
|  | 389 | ptr = pLoggedInPort; | 
|  | 390 | pLoggedInPort = ptr->pNextPort; | 
|  | 391 | //	  printk("kfree(%p) on FC LoggedInPort port_id 0x%06lX\n", | 
|  | 392 | //			  ptr, ptr->port_id); | 
|  | 393 | kfree( ptr ); | 
|  | 394 | } | 
|  | 395 | } | 
|  | 396 | // (continue resetting hardware...) | 
|  | 397 |  | 
|  | 398 | case 1:                   // RESTART Tachyon (power-up state) | 
|  | 399 |  | 
|  | 400 | // in case he was running previously, mask Tach's interrupt | 
|  | 401 | writeb( 0, (fcChip->Registers.ReMapMemBase + IINTEN)); | 
|  | 402 | // turn OFF laser (NOTE: laser is turned | 
|  | 403 | // off during reset, because GPIO4 is cleared | 
|  | 404 | // to 0 by reset action - see TLUM, sec 7.22) | 
|  | 405 | // However, CPQ 64-bit HBAs have a "health | 
|  | 406 | // circuit" which keeps laser ON for a brief | 
|  | 407 | // period after it is turned off ( < 1s) | 
|  | 408 |  | 
|  | 409 | fcChip->LaserControl( fcChip->Registers.ReMapMemBase, 0); | 
|  | 410 |  | 
|  | 411 |  | 
|  | 412 |  | 
|  | 413 | // soft reset timing constraints require: | 
|  | 414 | //   1. set RST to 1 | 
|  | 415 | //   2. read SOFTRST register | 
|  | 416 | //      (128 times per R. Callison code) | 
|  | 417 | //   3. clear PCI ints | 
|  | 418 | //   4. clear RST to 0 | 
|  | 419 | writel( 0xff000001L, | 
|  | 420 | (fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST)); | 
|  | 421 |  | 
|  | 422 | for( i=0; i<128; i++) | 
|  | 423 | ulBuff = readl( fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST); | 
|  | 424 |  | 
|  | 425 | // clear the soft reset | 
|  | 426 | for( i=0; i<8; i++) | 
|  | 427 | writel( 0, (fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST)); | 
|  | 428 |  | 
|  | 429 |  | 
|  | 430 |  | 
|  | 431 | // clear out our copy of Tach regs, | 
|  | 432 | // because they must be invalid now, | 
|  | 433 | // since TachLite reset all his regs. | 
|  | 434 | CpqTsDestroyTachLiteQues(cpqfcHBAdata,0); // remove Host-based Que structs | 
|  | 435 | cpqfcTSClearLinkStatusCounters(fcChip);  // clear our s/w accumulators | 
|  | 436 | // lower bits give GBIC info | 
|  | 437 | fcChip->Registers.TYstatus.value = | 
|  | 438 | readl( fcChip->Registers.TYstatus.address ); | 
|  | 439 | break; | 
|  | 440 |  | 
|  | 441 | /* | 
|  | 442 | case 2:                   // freeze SCSI | 
|  | 443 | case 3:                   // reset Outbound command que (ERQ) | 
|  | 444 | case 4:                   // unfreeze OSM (Outbound Seq. Man.) 'er' | 
|  | 445 | case 5:                   // report status | 
|  | 446 |  | 
|  | 447 | break; | 
|  | 448 | */ | 
|  | 449 | default: | 
|  | 450 | ret_status = -1;  // invalid option passed to RESET function | 
|  | 451 | break; | 
|  | 452 | } | 
|  | 453 | LEAVE("ResetTach"); | 
|  | 454 | return ret_status; | 
|  | 455 | } | 
|  | 456 |  | 
|  | 457 |  | 
|  | 458 |  | 
|  | 459 |  | 
|  | 460 |  | 
|  | 461 |  | 
|  | 462 | // 'addrBase' is IOBaseU for both TachLite and (older) Tachyon | 
|  | 463 | int CpqTsLaserControl( void* addrBase, int opcode ) | 
|  | 464 | { | 
|  | 465 | ULONG dwBuff; | 
|  | 466 |  | 
|  | 467 | dwBuff = readl((addrBase + TL_MEM_TACH_CONTROL) ); // read TL Control reg | 
|  | 468 | // (change only bit 4) | 
|  | 469 | if( opcode == 1) | 
|  | 470 | dwBuff |= ~0xffffffefL; // set - ON | 
|  | 471 | else | 
|  | 472 | dwBuff &= 0xffffffefL;  // clear - OFF | 
|  | 473 | writel( dwBuff, (addrBase + TL_MEM_TACH_CONTROL)); // write TL Control reg | 
|  | 474 | return 0; | 
|  | 475 | } | 
|  | 476 |  | 
|  | 477 |  | 
|  | 478 |  | 
|  | 479 |  | 
|  | 480 |  | 
|  | 481 | // Use controller's "Options" field to determine loopback mode (if any) | 
|  | 482 | //   internal loopback (silicon - no GBIC) | 
|  | 483 | //   external loopback (GBIC - no FC loop) | 
|  | 484 | //   no loopback: L_PORT, external cable from GBIC required | 
|  | 485 |  | 
|  | 486 | int CpqTsInitializeFrameManager( void *pChip, int opcode) | 
|  | 487 | { | 
|  | 488 | PTACHYON fcChip; | 
|  | 489 | int iStatus; | 
|  | 490 | ULONG wwnLo, wwnHi; // for readback verification | 
|  | 491 |  | 
|  | 492 | ENTER("InitializeFrameManager"); | 
|  | 493 | fcChip = (PTACHYON)pChip; | 
|  | 494 | if( !fcChip->Registers.ReMapMemBase )   // undefined controller? | 
|  | 495 | return -1; | 
|  | 496 |  | 
|  | 497 | // TL/TS UG, pg. 184 | 
|  | 498 | // 0x0065 = 100ms for RT_TOV | 
|  | 499 | // 0x01f5 = 500ms for ED_TOV | 
|  | 500 | // 0x07D1 = 2000ms | 
|  | 501 | fcChip->Registers.ed_tov.value = 0x006507D1; | 
|  | 502 | writel( fcChip->Registers.ed_tov.value, | 
|  | 503 | (fcChip->Registers.ed_tov.address)); | 
|  | 504 |  | 
|  | 505 |  | 
|  | 506 | // Set LP_TOV to the FC-AL2 specified 2 secs. | 
|  | 507 | // TL/TS UG, pg. 185 | 
|  | 508 | writel( 0x07d00010, fcChip->Registers.ReMapMemBase +TL_MEM_FM_TIMEOUT2); | 
|  | 509 |  | 
|  | 510 |  | 
|  | 511 | // Now try to read the WWN from the adapter's NVRAM | 
|  | 512 | iStatus = CpqTsReadWriteWWN( fcChip, 1); // '1' for READ | 
|  | 513 |  | 
|  | 514 | if( iStatus )   // NVRAM read failed? | 
|  | 515 | { | 
|  | 516 | printk(" WARNING! HBA NVRAM WWN read failed - make alias\n"); | 
|  | 517 | // make up a WWN.  If NULL or duplicated on loop, FC loop may hang! | 
|  | 518 |  | 
|  | 519 |  | 
|  | 520 | fcChip->Registers.wwn_hi = (__u32)jiffies; | 
|  | 521 | fcChip->Registers.wwn_hi |= 0x50000000L; | 
|  | 522 | fcChip->Registers.wwn_lo = 0x44556677L; | 
|  | 523 | } | 
|  | 524 |  | 
|  | 525 |  | 
|  | 526 | writel( fcChip->Registers.wwn_hi, | 
|  | 527 | fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_HI); | 
|  | 528 |  | 
|  | 529 | writel( fcChip->Registers.wwn_lo, | 
|  | 530 | fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_LO); | 
|  | 531 |  | 
|  | 532 |  | 
|  | 533 | // readback for verification: | 
|  | 534 | wwnHi = readl( fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_HI ); | 
|  | 535 |  | 
|  | 536 | wwnLo = readl( fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_LO); | 
|  | 537 | // test for correct chip register WRITE/READ | 
|  | 538 | DEBUG_PCI( printk("  WWN %08X%08X\n", | 
|  | 539 | fcChip->Registers.wwn_hi, fcChip->Registers.wwn_lo ) ); | 
|  | 540 |  | 
|  | 541 | if( wwnHi != fcChip->Registers.wwn_hi || | 
|  | 542 | wwnLo != fcChip->Registers.wwn_lo ) | 
|  | 543 | { | 
|  | 544 | printk( "cpqfcTS: WorldWideName register load failed\n"); | 
|  | 545 | return -1; // FAILED! | 
|  | 546 | } | 
|  | 547 |  | 
|  | 548 |  | 
|  | 549 |  | 
|  | 550 | // set Frame Manager Initialize command | 
|  | 551 | fcChip->Registers.FMcontrol.value = 0x06; | 
|  | 552 |  | 
|  | 553 | // Note: for test/debug purposes, we may use "Hard" address, | 
|  | 554 | // but we completely support "soft" addressing, including | 
|  | 555 | // dynamically changing our address. | 
|  | 556 | if( fcChip->Options.intLoopback == 1 )            // internal loopback | 
|  | 557 | fcChip->Registers.FMconfig.value = 0x0f002080L; | 
|  | 558 | else if( fcChip->Options.extLoopback == 1 )            // internal loopback | 
|  | 559 | fcChip->Registers.FMconfig.value = 0x0f004080L; | 
|  | 560 | else                  // L_Port | 
|  | 561 | fcChip->Registers.FMconfig.value = 0x55000100L; // hard address (55h start) | 
|  | 562 | //    fcChip->Registers.FMconfig.value = 0x01000080L; // soft address (can't pick) | 
|  | 563 | //    fcChip->Registers.FMconfig.value = 0x55000100L; // hard address (55h start) | 
|  | 564 |  | 
|  | 565 | // write config to FM | 
|  | 566 |  | 
|  | 567 | if( !fcChip->Options.intLoopback && !fcChip->Options.extLoopback ) | 
|  | 568 | // (also need LASER for real LOOP) | 
|  | 569 | fcChip->LaserControl( fcChip->Registers.ReMapMemBase, 1); // turn on LASER | 
|  | 570 |  | 
|  | 571 | writel( fcChip->Registers.FMconfig.value, | 
|  | 572 | fcChip->Registers.FMconfig.address); | 
|  | 573 |  | 
|  | 574 |  | 
|  | 575 | // issue INITIALIZE command to FM - ACTION! | 
|  | 576 | writel( fcChip->Registers.FMcontrol.value, | 
|  | 577 | fcChip->Registers.FMcontrol.address); | 
|  | 578 |  | 
|  | 579 | LEAVE("InitializeFrameManager"); | 
|  | 580 |  | 
|  | 581 | return 0; | 
|  | 582 | } | 
|  | 583 |  | 
|  | 584 |  | 
|  | 585 |  | 
|  | 586 |  | 
|  | 587 |  | 
|  | 588 | // This "look ahead" function examines the IMQ for occurrence of | 
|  | 589 | // "type".  Returns 1 if found, 0 if not. | 
|  | 590 | static int PeekIMQEntry( PTACHYON fcChip, ULONG type) | 
|  | 591 | { | 
|  | 592 | ULONG CI = fcChip->IMQ->consumerIndex; | 
|  | 593 | ULONG PI = fcChip->IMQ->producerIndex; // snapshot of IMQ indexes | 
|  | 594 |  | 
|  | 595 | while( CI != PI ) | 
|  | 596 | {                             // proceed with search | 
|  | 597 | if( (++CI) >= IMQ_LEN ) CI = 0; // rollover check | 
|  | 598 |  | 
|  | 599 | switch( type ) | 
|  | 600 | { | 
|  | 601 | case ELS_LILP_FRAME: | 
|  | 602 | { | 
|  | 603 | // first, we need to find an Inbound Completion message, | 
|  | 604 | // If we find it, check the incoming frame payload (1st word) | 
|  | 605 | // for LILP frame | 
|  | 606 | if( (fcChip->IMQ->QEntry[CI].type & 0x1FF) == 0x104 ) | 
|  | 607 | { | 
|  | 608 | TachFCHDR_GCMND* fchs; | 
|  | 609 | #error This is too much stack | 
|  | 610 | ULONG ulFibreFrame[2048/4];  // max DWORDS in incoming FC Frame | 
|  | 611 | USHORT SFQpi = (USHORT)(fcChip->IMQ->QEntry[CI].word[0] & 0x0fffL); | 
|  | 612 |  | 
|  | 613 | CpqTsGetSFQEntry( fcChip, | 
|  | 614 | SFQpi,        // SFQ producer ndx | 
|  | 615 | ulFibreFrame, // contiguous dest. buffer | 
|  | 616 | FALSE);       // DON'T update chip--this is a "lookahead" | 
|  | 617 |  | 
|  | 618 | fchs = (TachFCHDR_GCMND*)&ulFibreFrame; | 
|  | 619 | if( fchs->pl[0] == ELS_LILP_FRAME) | 
|  | 620 | { | 
|  | 621 | return 1; // found the LILP frame! | 
|  | 622 | } | 
|  | 623 | else | 
|  | 624 | { | 
|  | 625 | // keep looking... | 
|  | 626 | } | 
|  | 627 | } | 
|  | 628 | } | 
|  | 629 | break; | 
|  | 630 |  | 
|  | 631 | case OUTBOUND_COMPLETION: | 
|  | 632 | if( (fcChip->IMQ->QEntry[CI].type & 0x1FF) == 0x00 ) | 
|  | 633 | { | 
|  | 634 |  | 
|  | 635 | // any OCM errors? | 
|  | 636 | if( fcChip->IMQ->QEntry[CI].word[2] & 0x7a000000L ) | 
|  | 637 | return 1;   	    // found OCM error | 
|  | 638 | } | 
|  | 639 | break; | 
|  | 640 |  | 
|  | 641 |  | 
|  | 642 |  | 
|  | 643 | default: | 
|  | 644 | break; | 
|  | 645 | } | 
|  | 646 | } | 
|  | 647 | return 0; // failed to find "type" | 
|  | 648 | } | 
|  | 649 |  | 
|  | 650 |  | 
|  | 651 | static void SetTachTOV( CPQFCHBA* cpqfcHBAdata) | 
|  | 652 | { | 
|  | 653 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | 
|  | 654 |  | 
|  | 655 | // TL/TS UG, pg. 184 | 
|  | 656 | // 0x0065 = 100ms for RT_TOV | 
|  | 657 | // 0x01f5 = 500ms for ED_TOV | 
|  | 658 | // 0x07d1 = 2000ms for ED_TOV | 
|  | 659 |  | 
|  | 660 | // SANMark Level 1 requires an "initialization backoff" | 
|  | 661 | // (See "SANMark Test Suite Level 1": | 
|  | 662 | // initialization_timeout.fcal.SANMark-1.fc) | 
|  | 663 | // We have to use 2sec, 24sec, then 128sec when login/ | 
|  | 664 | // port discovery processes fail to complete. | 
|  | 665 |  | 
|  | 666 | // when port discovery completes (logins done), we set | 
|  | 667 | // ED_TOV to 500ms -- this is the normal operational case | 
|  | 668 | // On the first Link Down, we'll move to 2 secs (7D1 ms) | 
|  | 669 | if( (fcChip->Registers.ed_tov.value &0xFFFF) <= 0x1f5) | 
|  | 670 | fcChip->Registers.ed_tov.value = 0x006507D1; | 
|  | 671 |  | 
|  | 672 | // If we get another LST after we moved TOV to 2 sec, | 
|  | 673 | // increase to 24 seconds (5DC1 ms) per SANMark! | 
|  | 674 | else if( (fcChip->Registers.ed_tov.value &0xFFFF) <= 0x7D1) | 
|  | 675 | fcChip->Registers.ed_tov.value = 0x00655DC1; | 
|  | 676 |  | 
|  | 677 | // If we get still another LST, set the max TOV (Tachyon | 
|  | 678 | // has only 16 bits for ms timer, so the max is 65.5 sec) | 
|  | 679 | else if( (fcChip->Registers.ed_tov.value &0xFFFF) <= 0x5DC1) | 
|  | 680 | fcChip->Registers.ed_tov.value = 0x0065FFFF; | 
|  | 681 |  | 
|  | 682 | writel( fcChip->Registers.ed_tov.value, | 
|  | 683 | (fcChip->Registers.ed_tov.address)); | 
|  | 684 | // keep the same 2sec LP_TOV | 
|  | 685 | writel( 0x07D00010, fcChip->Registers.ReMapMemBase +TL_MEM_FM_TIMEOUT2); | 
|  | 686 | } | 
|  | 687 |  | 
|  | 688 |  | 
|  | 689 | // The IMQ is an array with IMQ_LEN length, each element (QEntry) | 
|  | 690 | // with eight 32-bit words.  Tachyon PRODUCES a QEntry with each | 
|  | 691 | // message it wants to send to the host.  The host CONSUMES IMQ entries | 
|  | 692 |  | 
|  | 693 | // This function copies the current | 
|  | 694 | // (or oldest not-yet-processed) QEntry to | 
|  | 695 | // the caller, clears/ re-enables the interrupt, and updates the | 
|  | 696 | // (Host) Consumer Index. | 
|  | 697 | // Return value: | 
|  | 698 | //  0   message processed, none remain (producer and consumer | 
|  | 699 | //        indexes match) | 
|  | 700 | //  1   message processed, more messages remain | 
|  | 701 | // -1   no message processed - none were available to process | 
|  | 702 | // Remarks: | 
|  | 703 | //   TL/TS UG specifices that the following actions for | 
|  | 704 | //   INTA_L handling: | 
|  | 705 | //   1. read PCI Interrupt Status register (0xff) | 
|  | 706 | //   2. all IMQ messages should be processed before writing the | 
|  | 707 | //      IMQ consumer index. | 
|  | 708 |  | 
|  | 709 |  | 
|  | 710 | int CpqTsProcessIMQEntry(void *host) | 
|  | 711 | { | 
|  | 712 | struct Scsi_Host *HostAdapter = (struct Scsi_Host *)host; | 
|  | 713 | CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; | 
|  | 714 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | 
|  | 715 | FC_EXCHANGES *Exchanges = fcChip->Exchanges; | 
|  | 716 | int iStatus; | 
|  | 717 | USHORT i, RPCset, DPCset; | 
|  | 718 | ULONG x_ID; | 
|  | 719 | ULONG ulBuff, dwStatus; | 
|  | 720 | TachFCHDR_GCMND* fchs; | 
|  | 721 | #error This is too much stack | 
|  | 722 | ULONG ulFibreFrame[2048/4];  // max number of DWORDS in incoming Fibre Frame | 
|  | 723 | UCHAR ucInboundMessageType;  // Inbound CM, dword 3 "type" field | 
|  | 724 |  | 
|  | 725 | ENTER("ProcessIMQEntry"); | 
|  | 726 |  | 
|  | 727 |  | 
|  | 728 | // check TachLite's IMQ producer index - | 
|  | 729 | // is a new message waiting for us? | 
|  | 730 | // equal indexes means empty que | 
|  | 731 |  | 
|  | 732 | if( fcChip->IMQ->producerIndex != fcChip->IMQ->consumerIndex ) | 
|  | 733 | {                             // need to process message | 
|  | 734 |  | 
|  | 735 |  | 
|  | 736 | #ifdef IMQ_DEBUG | 
|  | 737 | printk("PI %X, CI %X  type: %X\n", | 
|  | 738 | fcChip->IMQ->producerIndex,fcChip->IMQ->consumerIndex, | 
|  | 739 | fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].type); | 
|  | 740 | #endif | 
|  | 741 | // Examine Completion Messages in IMQ | 
|  | 742 | // what CM_Type? | 
|  | 743 | switch( (UCHAR)(fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].type | 
|  | 744 | & 0xffL) ) | 
|  | 745 | { | 
|  | 746 | case OUTBOUND_COMPLETION: | 
|  | 747 |  | 
|  | 748 | // Remarks: | 
|  | 749 | // x_IDs (OX_ID, RX_ID) are partitioned by SEST entries | 
|  | 750 | // (starting at 0), and SFS entries (starting at | 
|  | 751 | // SEST_LEN -- outside the SEST space). | 
|  | 752 | // Psuedo code: | 
|  | 753 | // x_ID (OX_ID or RX_ID) from message is Trans_ID or SEST index | 
|  | 754 | // range check - x_ID | 
|  | 755 | //   if x_ID outside 'Transactions' length, error - exit | 
|  | 756 | // if any OCM error, copy error status to Exchange slot | 
|  | 757 | // if FCP ASSIST transaction (x_ID within SEST), | 
|  | 758 | //   call fcComplete (to App) | 
|  | 759 | // ... | 
|  | 760 |  | 
|  | 761 |  | 
|  | 762 | ulBuff = fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1]; | 
|  | 763 | x_ID = ulBuff & 0x7fffL;     // lower 14 bits SEST_Index/Trans_ID | 
|  | 764 | // Range check CM OX/RX_ID value... | 
|  | 765 | if( x_ID < TACH_MAX_XID )   // don't go beyond array space | 
|  | 766 | { | 
|  | 767 |  | 
|  | 768 |  | 
|  | 769 | if( ulBuff & 0x20000000L ) // RPC -Response Phase Complete? | 
|  | 770 | RPCset = 1;              // (SEST transactions only) | 
|  | 771 | else | 
|  | 772 | RPCset = 0; | 
|  | 773 |  | 
|  | 774 | if( ulBuff & 0x40000000L ) // DPC -Data Phase Complete? | 
|  | 775 | DPCset = 1;              // (SEST transactions only) | 
|  | 776 | else | 
|  | 777 | DPCset = 0; | 
|  | 778 | // set the status for this Outbound transaction's ID | 
|  | 779 | dwStatus = 0L; | 
|  | 780 | if( ulBuff & 0x10000000L ) // SPE? (SEST Programming Error) | 
|  | 781 | dwStatus |= SESTPROG_ERR; | 
|  | 782 |  | 
|  | 783 | ulBuff = fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2]; | 
|  | 784 | if( ulBuff & 0x7a000000L ) // any other errs? | 
|  | 785 | { | 
|  | 786 | if( ulBuff & 0x40000000L ) | 
|  | 787 | dwStatus |= INV_ENTRY; | 
|  | 788 | if( ulBuff & 0x20000000L ) | 
|  | 789 | dwStatus |= FRAME_TO;        // FTO | 
|  | 790 | if( ulBuff & 0x10000000L ) | 
|  | 791 | dwStatus |= HOSTPROG_ERR; | 
|  | 792 | if( ulBuff & 0x08000000L ) | 
|  | 793 | dwStatus |= LINKFAIL_TX; | 
|  | 794 | if( ulBuff & 0x02000000L ) | 
|  | 795 | dwStatus |= ABORTSEQ_NOTIFY;  // ASN | 
|  | 796 | } | 
|  | 797 |  | 
|  | 798 |  | 
|  | 799 | if( dwStatus )          // any errors? | 
|  | 800 | { | 
|  | 801 | // set the Outbound Completion status | 
|  | 802 | Exchanges->fcExchange[ x_ID ].status |= dwStatus; | 
|  | 803 |  | 
|  | 804 | // if this Outbound frame was for a SEST entry, automatically | 
|  | 805 | // reque it in the case of LINKFAIL (it will restart on PDISC) | 
|  | 806 | if( x_ID < TACH_SEST_LEN ) | 
|  | 807 | { | 
|  | 808 |  | 
|  | 809 | printk(" #OCM error %Xh x_ID %X# ", | 
|  | 810 | dwStatus, x_ID); | 
|  | 811 |  | 
|  | 812 | Exchanges->fcExchange[x_ID].timeOut = 30000; // seconds default | 
|  | 813 |  | 
|  | 814 |  | 
|  | 815 | // We Q ABTS for each exchange. | 
|  | 816 | // NOTE: We can get FRAME_TO on bad alpa (device gone).  Since | 
|  | 817 | // bad alpa is reported before FRAME_TO, examine the status | 
|  | 818 | // flags to see if the device is removed.  If so, DON'T | 
|  | 819 | // post an ABTS, since it will be terminated by the bad alpa | 
|  | 820 | // message. | 
|  | 821 | if( dwStatus & FRAME_TO ) // check for device removed... | 
|  | 822 | { | 
|  | 823 | if( !(Exchanges->fcExchange[x_ID].status & DEVICE_REMOVED) ) | 
|  | 824 | { | 
|  | 825 | // presumes device is still there: send ABTS. | 
|  | 826 |  | 
|  | 827 | cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID); | 
|  | 828 | } | 
|  | 829 | } | 
|  | 830 | else  // Abort all other errors | 
|  | 831 | { | 
|  | 832 | cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID); | 
|  | 833 | } | 
|  | 834 |  | 
|  | 835 | // if the HPE bit is set, we have to CLose the LOOP | 
|  | 836 | // (see TL/TS UG, pg. 239) | 
|  | 837 |  | 
|  | 838 | if( dwStatus &= HOSTPROG_ERR ) | 
|  | 839 | // set CL bit (see TL/TS UG, pg. 172) | 
|  | 840 | writel( 4, fcChip->Registers.FMcontrol.address); | 
|  | 841 | } | 
|  | 842 | } | 
|  | 843 | // NOTE: we don't necessarily care about ALL completion messages... | 
|  | 844 | // SCSI resp. complete OR | 
|  | 845 | if( ((x_ID < TACH_SEST_LEN) && RPCset)|| | 
|  | 846 | (x_ID >= TACH_SEST_LEN) )  // non-SCSI command | 
|  | 847 | { | 
|  | 848 | // exchange done; complete to upper levels with status | 
|  | 849 | // (if necessary) and free the exchange slot | 
|  | 850 |  | 
|  | 851 |  | 
|  | 852 | if( x_ID >= TACH_SEST_LEN ) // Link Service Outbound frame? | 
|  | 853 | // A Request or Reply has been sent | 
|  | 854 | {                         // signal waiting WorkerThread | 
|  | 855 |  | 
|  | 856 | up( cpqfcHBAdata->TYOBcomplete);   // frame is OUT of Tach | 
|  | 857 |  | 
|  | 858 | // WorkerThread will complete Xchng | 
|  | 859 | } | 
|  | 860 | else  // X_ID is for FCP assist (SEST) | 
|  | 861 | { | 
|  | 862 | // TBD (target mode) | 
|  | 863 | //            fcCompleteExchange( fcChip, x_ID); // TRE completed | 
|  | 864 | } | 
|  | 865 | } | 
|  | 866 | } | 
|  | 867 | else  // ERROR CONDITION!  bogus x_ID in completion message | 
|  | 868 | { | 
|  | 869 |  | 
|  | 870 | printk(" ProcessIMQ (OBCM) x_id out of range %Xh\n", x_ID); | 
|  | 871 |  | 
|  | 872 | } | 
|  | 873 |  | 
|  | 874 |  | 
|  | 875 |  | 
|  | 876 | // Load the Frame Manager's error counters.  We check them here | 
|  | 877 | // because presumably the link is up and healthy enough for the | 
|  | 878 | // counters to be meaningful (i.e., don't check them while loop | 
|  | 879 | // is initializing). | 
|  | 880 | fcChip->Registers.FMLinkStatus1.value =    // get TL's counter | 
|  | 881 | readl(fcChip->Registers.FMLinkStatus1.address); | 
|  | 882 |  | 
|  | 883 | fcChip->Registers.FMLinkStatus2.value =    // get TL's counter | 
|  | 884 | readl(fcChip->Registers.FMLinkStatus2.address); | 
|  | 885 |  | 
|  | 886 |  | 
|  | 887 | fcParseLinkStatusCounters( fcChip); // load into 6 s/w accumulators | 
|  | 888 | break; | 
|  | 889 |  | 
|  | 890 |  | 
|  | 891 |  | 
|  | 892 | case ERROR_IDLE_COMPLETION:  // TachLite Error Idle... | 
|  | 893 |  | 
|  | 894 | // We usually get this when the link goes down during heavy traffic. | 
|  | 895 | // For now, presume that if SEST Exchanges are open, we will | 
|  | 896 | // get this as our cue to INVALIDATE all SEST entries | 
|  | 897 | // (and we OWN all the SEST entries). | 
|  | 898 | // See TL/TS UG, pg. 53 | 
|  | 899 |  | 
|  | 900 | for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++) | 
|  | 901 | { | 
|  | 902 |  | 
|  | 903 | // Does this VALid SEST entry need to be invalidated for Abort? | 
|  | 904 | fcChip->SEST->u[ x_ID].IWE.Hdr_Len &= 0x7FFFFFFF; | 
|  | 905 | } | 
|  | 906 |  | 
|  | 907 | CpqTsUnFreezeTachlite( fcChip, 2); // unfreeze Tachyon, if Link OK | 
|  | 908 |  | 
|  | 909 | break; | 
|  | 910 |  | 
|  | 911 |  | 
|  | 912 | case INBOUND_SFS_COMPLETION:  //0x04 | 
|  | 913 | // NOTE! we must process this SFQ message to avoid SFQ filling | 
|  | 914 | // up and stopping TachLite.  Incoming commands are placed here, | 
|  | 915 | // as well as 'unknown' frames (e.g. LIP loop position data) | 
|  | 916 | // write this CM's producer index to global... | 
|  | 917 | // TL/TS UG, pg 234: | 
|  | 918 | // Type: 0 - reserved | 
|  | 919 | //       1 - Unassisted FCP | 
|  | 920 | //       2 - BAD FCP | 
|  | 921 | //       3 - Unkown Frame | 
|  | 922 | //       4-F reserved | 
|  | 923 |  | 
|  | 924 |  | 
|  | 925 | fcChip->SFQ->producerIndex = (USHORT) | 
|  | 926 | (fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[0] & 0x0fffL); | 
|  | 927 |  | 
|  | 928 |  | 
|  | 929 | ucInboundMessageType = 0;  // default to useless frame | 
|  | 930 |  | 
|  | 931 | // we can only process two Types: 1, Unassisted FCP, and 3, Unknown | 
|  | 932 | // Also, we aren't interested in processing frame fragments | 
|  | 933 | // so don't Que anything with 'LKF' bit set | 
|  | 934 | if( !(fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2] | 
|  | 935 | & 0x40000000) )  // 'LKF' link failure bit clear? | 
|  | 936 | { | 
|  | 937 | ucInboundMessageType = (UCHAR)  // ICM DWord3, "Type" | 
|  | 938 | (fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2] & 0x0fL); | 
|  | 939 | } | 
|  | 940 | else | 
|  | 941 | { | 
|  | 942 | fcChip->fcStats.linkFailRX++; | 
|  | 943 | //        printk("LKF (link failure) bit set on inbound message\n"); | 
|  | 944 | } | 
|  | 945 |  | 
|  | 946 | // clears SFQ entry from Tachyon buffer; copies to contiguous ulBuff | 
|  | 947 | CpqTsGetSFQEntry( | 
|  | 948 | fcChip,                  // i.e. this Device Object | 
|  | 949 | (USHORT)fcChip->SFQ->producerIndex,  // SFQ producer ndx | 
|  | 950 | ulFibreFrame, TRUE);    // contiguous destination buffer, update chip | 
|  | 951 |  | 
|  | 952 | // analyze the incoming frame outside the INT handler... | 
|  | 953 | // (i.e., Worker) | 
|  | 954 |  | 
|  | 955 | if( ucInboundMessageType == 1 ) | 
|  | 956 | { | 
|  | 957 | fchs = (TachFCHDR_GCMND*)ulFibreFrame; // cast to examine IB frame | 
|  | 958 | // don't fill up our Q with garbage - only accept FCP-CMND | 
|  | 959 | // or XRDY frames | 
|  | 960 | if( (fchs->d_id & 0xFF000000) == 0x06000000 ) // CMND | 
|  | 961 | { | 
|  | 962 | // someone sent us a SCSI command | 
|  | 963 |  | 
|  | 964 | //          fcPutScsiQue( cpqfcHBAdata, | 
|  | 965 | //                        SFQ_UNASSISTED_FCP, ulFibreFrame); | 
|  | 966 | } | 
|  | 967 | else if( ((fchs->d_id & 0xFF000000) == 0x07000000) || // RSP (status) | 
|  | 968 | (fchs->d_id & 0xFF000000) == 0x05000000 )  // XRDY | 
|  | 969 | { | 
|  | 970 | ULONG x_ID; | 
|  | 971 | // Unfortunately, ABTS requires a Freeze on the chip so | 
|  | 972 | // we can modify the shared memory SEST.  When frozen, | 
|  | 973 | // any received Exchange frames cannot be processed by | 
|  | 974 | // Tachyon, so they will be dumped in here.  It is too | 
|  | 975 | // complex to attempt the reconstruct these frames in | 
|  | 976 | // the correct Exchange context, so we simply seek to | 
|  | 977 | // find status or transfer ready frames, and cause the | 
|  | 978 | // exchange to complete with errors before the timeout | 
|  | 979 | // expires.  We use a Linux Scsi Cmnd result code that | 
|  | 980 | // causes immediate retry. | 
|  | 981 |  | 
|  | 982 |  | 
|  | 983 | // Do we have an open exchange that matches this s_id | 
|  | 984 | // and ox_id? | 
|  | 985 | for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++) | 
|  | 986 | { | 
|  | 987 | if( (fchs->s_id & 0xFFFFFF) == | 
|  | 988 | (Exchanges->fcExchange[x_ID].fchs.d_id & 0xFFFFFF) | 
|  | 989 | && | 
|  | 990 | (fchs->ox_rx_id & 0xFFFF0000) == | 
|  | 991 | (Exchanges->fcExchange[x_ID].fchs.ox_rx_id & 0xFFFF0000) ) | 
|  | 992 | { | 
|  | 993 | //          printk(" #R/X frame x_ID %08X# ", fchs->ox_rx_id ); | 
|  | 994 | // simulate the anticipated error - since the | 
|  | 995 | // SEST was frozen, frames were lost... | 
|  | 996 | Exchanges->fcExchange[ x_ID ].status |= SFQ_FRAME; | 
|  | 997 |  | 
|  | 998 | // presumes device is still there: send ABTS. | 
|  | 999 | cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID); | 
|  | 1000 | break;  // done | 
|  | 1001 | } | 
|  | 1002 | } | 
|  | 1003 | } | 
|  | 1004 |  | 
|  | 1005 | } | 
|  | 1006 |  | 
|  | 1007 | else if( ucInboundMessageType == 3) | 
|  | 1008 | { | 
|  | 1009 | // FC Link Service frames (e.g. PLOGI, ACC) come in here. | 
|  | 1010 | cpqfcTSPutLinkQue( cpqfcHBAdata, SFQ_UNKNOWN, ulFibreFrame); | 
|  | 1011 |  | 
|  | 1012 | } | 
|  | 1013 |  | 
|  | 1014 | else if( ucInboundMessageType == 2 ) // "bad FCP"? | 
|  | 1015 | { | 
|  | 1016 | #ifdef IMQ_DEBUG | 
|  | 1017 | printk("Bad FCP incoming frame discarded\n"); | 
|  | 1018 | #endif | 
|  | 1019 | } | 
|  | 1020 |  | 
|  | 1021 | else // don't know this type | 
|  | 1022 | { | 
|  | 1023 | #ifdef IMQ_DEBUG | 
|  | 1024 | printk("Incoming frame discarded, type: %Xh\n", ucInboundMessageType); | 
|  | 1025 | #endif | 
|  | 1026 | } | 
|  | 1027 |  | 
|  | 1028 | // Check the Frame Manager's error counters.  We check them here | 
|  | 1029 | // because presumably the link is up and healthy enough for the | 
|  | 1030 | // counters to be meaningful (i.e., don't check them while loop | 
|  | 1031 | // is initializing). | 
|  | 1032 | fcChip->Registers.FMLinkStatus1.value =    // get TL's counter | 
|  | 1033 | readl(fcChip->Registers.FMLinkStatus1.address); | 
|  | 1034 |  | 
|  | 1035 |  | 
|  | 1036 | fcChip->Registers.FMLinkStatus2.value =    // get TL's counter | 
|  | 1037 | readl(fcChip->Registers.FMLinkStatus2.address); | 
|  | 1038 |  | 
|  | 1039 |  | 
|  | 1040 | break; | 
|  | 1041 |  | 
|  | 1042 |  | 
|  | 1043 |  | 
|  | 1044 |  | 
|  | 1045 | // We get this CM because we issued a freeze | 
|  | 1046 | // command to stop outbound frames.  We issue the | 
|  | 1047 | // freeze command at Link Up time; when this message | 
|  | 1048 | // is received, the ERQ base can be switched and PDISC | 
|  | 1049 | // frames can be sent. | 
|  | 1050 |  | 
|  | 1051 |  | 
|  | 1052 | case ERQ_FROZEN_COMPLETION:  // note: expect ERQ followed immediately | 
|  | 1053 | // by FCP when freezing TL | 
|  | 1054 | fcChip->Registers.TYstatus.value =         // read what's frozen | 
|  | 1055 | readl(fcChip->Registers.TYstatus.address); | 
|  | 1056 | // (do nothing; wait for FCP frozen message) | 
|  | 1057 | break; | 
|  | 1058 | case FCP_FROZEN_COMPLETION: | 
|  | 1059 |  | 
|  | 1060 | fcChip->Registers.TYstatus.value =         // read what's frozen | 
|  | 1061 | readl(fcChip->Registers.TYstatus.address); | 
|  | 1062 |  | 
|  | 1063 | // Signal the kernel thread to proceed with SEST modification | 
|  | 1064 | up( cpqfcHBAdata->TachFrozen); | 
|  | 1065 |  | 
|  | 1066 | break; | 
|  | 1067 |  | 
|  | 1068 |  | 
|  | 1069 |  | 
|  | 1070 | case INBOUND_C1_TIMEOUT: | 
|  | 1071 | case MFS_BUF_WARN: | 
|  | 1072 | case IMQ_BUF_WARN: | 
|  | 1073 | break; | 
|  | 1074 |  | 
|  | 1075 |  | 
|  | 1076 |  | 
|  | 1077 |  | 
|  | 1078 |  | 
|  | 1079 | // In older Tachyons, we 'clear' the internal 'core' interrupt state | 
|  | 1080 | // by reading the FMstatus register.  In newer TachLite (Tachyon), | 
|  | 1081 | // we must WRITE the register | 
|  | 1082 | // to clear the condition (TL/TS UG, pg 179) | 
|  | 1083 | case FRAME_MGR_INTERRUPT: | 
|  | 1084 | { | 
|  | 1085 | PFC_LOGGEDIN_PORT pLoggedInPort; | 
|  | 1086 |  | 
|  | 1087 | fcChip->Registers.FMstatus.value = | 
|  | 1088 | readl( fcChip->Registers.FMstatus.address ); | 
|  | 1089 |  | 
|  | 1090 | // PROBLEM: It is possible, especially with "dumb" hubs that | 
|  | 1091 | // don't automatically LIP on by-pass of ports that are going | 
|  | 1092 | // away, for the hub by-pass process to destroy critical | 
|  | 1093 | // ordered sets of a frame.  The result of this is a hung LPSM | 
|  | 1094 | // (Loop Port State Machine), which on Tachyon results in a | 
|  | 1095 | // (default 2 sec) Loop State Timeout (LST) FM message.  We | 
|  | 1096 | // want to avoid this relatively huge timeout by detecting | 
|  | 1097 | // likely scenarios which will result in LST. | 
|  | 1098 | // To do this, we could examine FMstatus for Loss of Synchronization | 
|  | 1099 | // and/or Elastic Store (ES) errors.  Of these, Elastic Store is better | 
|  | 1100 | // because we get this indication more quickly than the LOS. | 
|  | 1101 | // Not all ES errors are harmfull, so we don't want to LIP on every | 
|  | 1102 | // ES.  Instead, on every ES, detect whether our LPSM in in one | 
|  | 1103 | // of the LST states: ARBITRATING, OPEN, OPENED, XMITTED CLOSE, | 
|  | 1104 | // or RECEIVED CLOSE.  (See TL/TS UG, pg. 181) | 
|  | 1105 | // If any of these LPSM states are detected | 
|  | 1106 | // in combination with the LIP while LDn is not set, | 
|  | 1107 | // send an FM init (LIP F7,F7 for loops)! | 
|  | 1108 | // It is critical to the physical link stability NOT to reset (LIP) | 
|  | 1109 | // more than absolutely necessary; this is a basic premise of the | 
|  | 1110 | // SANMark level 1 spec. | 
|  | 1111 | { | 
|  | 1112 | ULONG Lpsm = (fcChip->Registers.FMstatus.value & 0xF0) >>4; | 
|  | 1113 |  | 
|  | 1114 | if( (fcChip->Registers.FMstatus.value & 0x400)  // ElasticStore? | 
|  | 1115 | && | 
|  | 1116 | !(fcChip->Registers.FMstatus.value & 0x100) // NOT LDn | 
|  | 1117 | && | 
|  | 1118 | !(fcChip->Registers.FMstatus.value & 0x1000)) // NOT LF | 
|  | 1119 | { | 
|  | 1120 | if( (Lpsm != 0) || // not MONITORING? or | 
|  | 1121 | !(Lpsm & 0x8) )// not already offline? | 
|  | 1122 | { | 
|  | 1123 | // now check the particular LST states... | 
|  | 1124 | if( (Lpsm == ARBITRATING) || (Lpsm == OPEN) || | 
|  | 1125 | (Lpsm == OPENED)      || (Lpsm == XMITTD_CLOSE) || | 
|  | 1126 | (Lpsm == RCVD_CLOSE) ) | 
|  | 1127 | { | 
|  | 1128 | // re-init the loop before it hangs itself! | 
|  | 1129 | printk(" #req FMinit on E-S: LPSM %Xh# ",Lpsm); | 
|  | 1130 |  | 
|  | 1131 |  | 
|  | 1132 | fcChip->fcStats.FMinits++; | 
|  | 1133 | writel( 6, fcChip->Registers.FMcontrol.address); // LIP | 
|  | 1134 | } | 
|  | 1135 | } | 
|  | 1136 | } | 
|  | 1137 | else if( fcChip->Registers.FMstatus.value & 0x40000 ) // LST? | 
|  | 1138 | { | 
|  | 1139 | printk(" #req FMinit on LST, LPSM %Xh# ",Lpsm); | 
|  | 1140 |  | 
|  | 1141 | fcChip->fcStats.FMinits++; | 
|  | 1142 | writel( 6, fcChip->Registers.FMcontrol.address);  // LIP | 
|  | 1143 | } | 
|  | 1144 | } | 
|  | 1145 |  | 
|  | 1146 |  | 
|  | 1147 | // clear only the 'interrupting' type bits for this REG read | 
|  | 1148 | writel( (fcChip->Registers.FMstatus.value & 0xff3fff00L), | 
|  | 1149 | fcChip->Registers.FMstatus.address); | 
|  | 1150 |  | 
|  | 1151 |  | 
|  | 1152 | // copy frame manager status to unused ULONG slot | 
|  | 1153 | fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[0] = | 
|  | 1154 | fcChip->Registers.FMstatus.value; // (for debugging) | 
|  | 1155 |  | 
|  | 1156 |  | 
|  | 1157 | // Load the Frame Manager's error counters.  We check them here | 
|  | 1158 | // because presumably the link is up and healthy enough for the | 
|  | 1159 | // counters to be meaningful (i.e., don't check them while loop | 
|  | 1160 | // is initializing). | 
|  | 1161 | fcChip->Registers.FMLinkStatus1.value =   // get TL's counter | 
|  | 1162 | readl(fcChip->Registers.FMLinkStatus1.address); | 
|  | 1163 |  | 
|  | 1164 | fcChip->Registers.FMLinkStatus2.value =   // get TL's counter | 
|  | 1165 | readl(fcChip->Registers.FMLinkStatus2.address); | 
|  | 1166 |  | 
|  | 1167 | // Get FM BB_Credit Zero Reg - does not clear on READ | 
|  | 1168 | fcChip->Registers.FMBB_CreditZero.value =   // get TL's counter | 
|  | 1169 | readl(fcChip->Registers.FMBB_CreditZero.address); | 
|  | 1170 |  | 
|  | 1171 |  | 
|  | 1172 |  | 
|  | 1173 | fcParseLinkStatusCounters( fcChip); // load into 6 s/w accumulators | 
|  | 1174 |  | 
|  | 1175 |  | 
|  | 1176 | // LINK DOWN | 
|  | 1177 |  | 
|  | 1178 | if( fcChip->Registers.FMstatus.value & 0x100L ) // Link DOWN bit | 
|  | 1179 | { | 
|  | 1180 |  | 
|  | 1181 | #ifdef IMQ_DEBUG | 
|  | 1182 | printk("LinkDn\n"); | 
|  | 1183 | #endif | 
|  | 1184 | printk(" #LDn# "); | 
|  | 1185 |  | 
|  | 1186 | fcChip->fcStats.linkDown++; | 
|  | 1187 |  | 
|  | 1188 | SetTachTOV( cpqfcHBAdata);  // must set according to SANMark | 
|  | 1189 |  | 
|  | 1190 | // Check the ERQ - force it to be "empty" to prevent Tach | 
|  | 1191 | // from sending out frames before we do logins. | 
|  | 1192 |  | 
|  | 1193 |  | 
|  | 1194 | if( fcChip->ERQ->producerIndex != fcChip->ERQ->consumerIndex) | 
|  | 1195 | { | 
|  | 1196 | //	  printk("#ERQ PI != CI#"); | 
|  | 1197 | CpqTsFreezeTachlite( fcChip, 1); // freeze ERQ only | 
|  | 1198 | fcChip->ERQ->producerIndex = fcChip->ERQ->consumerIndex = 0; | 
|  | 1199 | writel( fcChip->ERQ->base, | 
|  | 1200 | (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_BASE)); | 
|  | 1201 | // re-writing base forces ERQ PI to equal CI | 
|  | 1202 |  | 
|  | 1203 | } | 
|  | 1204 |  | 
|  | 1205 | // link down transition occurred -- port_ids can change | 
|  | 1206 | // on next LinkUp, so we must invalidate current logins | 
|  | 1207 | // (and any I/O in progress) until PDISC or PLOGI/PRLI | 
|  | 1208 | // completes | 
|  | 1209 | { | 
|  | 1210 | pLoggedInPort = &fcChip->fcPorts; | 
|  | 1211 | while( pLoggedInPort ) // for all ports which are expecting | 
|  | 1212 | // PDISC after the next LIP, set the | 
|  | 1213 | // logoutTimer | 
|  | 1214 | { | 
|  | 1215 |  | 
|  | 1216 | if( pLoggedInPort->pdisc) // expecting PDISC within 2 sec? | 
|  | 1217 | { | 
|  | 1218 | pLoggedInPort->LOGO_timer = 3;  // we want 2 seconds | 
|  | 1219 | // but Timer granularity | 
|  | 1220 | // is 1 second | 
|  | 1221 | } | 
|  | 1222 | // suspend any I/O in progress until | 
|  | 1223 | // PDISC received... | 
|  | 1224 | pLoggedInPort->prli = FALSE;   // block FCP-SCSI commands | 
|  | 1225 |  | 
|  | 1226 | pLoggedInPort = pLoggedInPort->pNextPort; | 
|  | 1227 | }  // ... all Previously known ports checked | 
|  | 1228 | } | 
|  | 1229 |  | 
|  | 1230 | // since any hot plugging device may NOT support LILP frames | 
|  | 1231 | // (such as early Tachyon chips), clear this flag indicating | 
|  | 1232 | // we shouldn't use (our copy of) a LILP map. | 
|  | 1233 | // If we receive an LILP frame, we'll set it again. | 
|  | 1234 | fcChip->Options.LILPin = 0; // our LILPmap is invalid | 
|  | 1235 | cpqfcHBAdata->PortDiscDone = 0; // must re-validate FC ports! | 
|  | 1236 |  | 
|  | 1237 | // also, we want to invalidate (i.e. INITIATOR_ABORT) any | 
|  | 1238 | // open Login exchanges, in case the LinkDown happened in the | 
|  | 1239 | // middle of logins.  It's possible that some ports already | 
|  | 1240 | // ACCepted login commands which we have not processed before | 
|  | 1241 | // another LinkDown occurred.  Any accepted Login exhanges are | 
|  | 1242 | // invalidated by LinkDown, even before they are acknowledged. | 
|  | 1243 | // It's also possible for a port to have a Queued Reply or Request | 
|  | 1244 | // for login which was interrupted by LinkDown; it may come later, | 
|  | 1245 | // but it will be unacceptable to us. | 
|  | 1246 |  | 
|  | 1247 | // we must scan the entire exchange space, find every Login type | 
|  | 1248 | // originated by us, and abort it. This is NOT an abort due to | 
|  | 1249 | // timeout, so we don't actually send abort to the other port - | 
|  | 1250 | // we just complete it to free up the fcExchange slot. | 
|  | 1251 |  | 
|  | 1252 | for( i=TACH_SEST_LEN; i< TACH_MAX_XID; i++) | 
|  | 1253 | {                     // looking for Extended Link Serv.Exchanges | 
|  | 1254 | if( Exchanges->fcExchange[i].type == ELS_PDISC || | 
|  | 1255 | Exchanges->fcExchange[i].type == ELS_PLOGI || | 
|  | 1256 | Exchanges->fcExchange[i].type == ELS_PRLI ) | 
|  | 1257 | { | 
|  | 1258 | // ABORT the exchange! | 
|  | 1259 | #ifdef IMQ_DEBUG | 
|  | 1260 | printk("Originator ABORT x_id %Xh, type %Xh, port_id %Xh on LDn\n", | 
|  | 1261 | i, Exchanges->fcExchange[i].type, | 
|  | 1262 | Exchanges->fcExchange[i].fchs.d_id); | 
|  | 1263 | #endif | 
|  | 1264 |  | 
|  | 1265 | Exchanges->fcExchange[i].status |= INITIATOR_ABORT; | 
|  | 1266 | cpqfcTSCompleteExchange( cpqfcHBAdata->PciDev, fcChip, i); // abort on LDn | 
|  | 1267 | } | 
|  | 1268 | } | 
|  | 1269 |  | 
|  | 1270 | } | 
|  | 1271 |  | 
|  | 1272 | // ################   LINK UP   ################## | 
|  | 1273 | if( fcChip->Registers.FMstatus.value & 0x200L ) // Link Up bit | 
|  | 1274 | {                                 // AL_PA could have changed | 
|  | 1275 |  | 
|  | 1276 | // We need the following code, duplicated from LinkDn condition, | 
|  | 1277 | // because it's possible for the Tachyon to re-initialize (hard | 
|  | 1278 | // reset) without ever getting a LinkDn indication. | 
|  | 1279 | pLoggedInPort = &fcChip->fcPorts; | 
|  | 1280 | while( pLoggedInPort )   // for all ports which are expecting | 
|  | 1281 | // PDISC after the next LIP, set the | 
|  | 1282 | // logoutTimer | 
|  | 1283 | { | 
|  | 1284 | if( pLoggedInPort->pdisc) // expecting PDISC within 2 sec? | 
|  | 1285 | { | 
|  | 1286 | pLoggedInPort->LOGO_timer = 3;  // we want 2 seconds | 
|  | 1287 | // but Timer granularity | 
|  | 1288 | // is 1 second | 
|  | 1289 |  | 
|  | 1290 | // suspend any I/O in progress until | 
|  | 1291 | // PDISC received... | 
|  | 1292 |  | 
|  | 1293 | } | 
|  | 1294 | pLoggedInPort = pLoggedInPort->pNextPort; | 
|  | 1295 | }  // ... all Previously known ports checked | 
|  | 1296 |  | 
|  | 1297 | // CpqTs acquired AL_PA in register AL_PA (ACQ_ALPA) | 
|  | 1298 | fcChip->Registers.rcv_al_pa.value = | 
|  | 1299 | readl(fcChip->Registers.rcv_al_pa.address); | 
|  | 1300 |  | 
|  | 1301 | // Now, if our acquired address is DIFFERENT from our | 
|  | 1302 | // previous one, we are not allow to do PDISC - we | 
|  | 1303 | // must go back to PLOGI, which will terminate I/O in | 
|  | 1304 | // progress for ALL logged in FC devices... | 
|  | 1305 | // (This is highly unlikely). | 
|  | 1306 |  | 
|  | 1307 | if( (fcChip->Registers.my_al_pa & 0xFF) != | 
|  | 1308 | ((fcChip->Registers.rcv_al_pa.value >> 16) &0xFF) ) | 
|  | 1309 | { | 
|  | 1310 |  | 
|  | 1311 | //	  printk(" #our HBA port_id changed!# "); // FC port_id changed!! | 
|  | 1312 |  | 
|  | 1313 | pLoggedInPort = &fcChip->fcPorts; | 
|  | 1314 | while( pLoggedInPort ) // for all ports which are expecting | 
|  | 1315 | // PDISC after the next LIP, set the | 
|  | 1316 | // logoutTimer | 
|  | 1317 | { | 
|  | 1318 | pLoggedInPort->pdisc  = FALSE; | 
|  | 1319 | pLoggedInPort->prli = FALSE; | 
|  | 1320 | pLoggedInPort = pLoggedInPort->pNextPort; | 
|  | 1321 | }  // ... all Previously known ports checked | 
|  | 1322 |  | 
|  | 1323 | // when the port_id changes, we must terminate | 
|  | 1324 | // all open exchanges. | 
|  | 1325 | cpqfcTSTerminateExchange( cpqfcHBAdata, NULL, PORTID_CHANGED); | 
|  | 1326 |  | 
|  | 1327 | } | 
|  | 1328 |  | 
|  | 1329 | // Replace the entire 24-bit port_id.  We only know the | 
|  | 1330 | // lower 8 bits (alpa) from Tachyon; if a FLOGI is done, | 
|  | 1331 | // we'll get the upper 16-bits from the FLOGI ACC frame. | 
|  | 1332 | // If someone plugs into Fabric switch, we'll do FLOGI and | 
|  | 1333 | // get full 24-bit port_id; someone could then remove and | 
|  | 1334 | // hot-plug us into a dumb hub.  If we send a 24-bit PLOGI | 
|  | 1335 | // to a "private" loop device, it might blow up. | 
|  | 1336 | // Consequently, we force the upper 16-bits of port_id to | 
|  | 1337 | // be re-set on every LinkUp transition | 
|  | 1338 | fcChip->Registers.my_al_pa = | 
|  | 1339 | (fcChip->Registers.rcv_al_pa.value >> 16) & 0xFF; | 
|  | 1340 |  | 
|  | 1341 |  | 
|  | 1342 | // copy frame manager status to unused ULONG slot | 
|  | 1343 | fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1] = | 
|  | 1344 | fcChip->Registers.my_al_pa; // (for debugging) | 
|  | 1345 |  | 
|  | 1346 | // for TachLite, we need to write the acquired al_pa | 
|  | 1347 | // back into the FMconfig register, because after | 
|  | 1348 | // first initialization, the AQ (prev. acq.) bit gets | 
|  | 1349 | // set, causing TL FM to use the AL_PA field in FMconfig. | 
|  | 1350 | // (In Tachyon, FM writes the acquired AL_PA for us.) | 
|  | 1351 | ulBuff = readl( fcChip->Registers.FMconfig.address); | 
|  | 1352 | ulBuff &= 0x00ffffffL;  // mask out current al_pa | 
|  | 1353 | ulBuff |= ( fcChip->Registers.my_al_pa << 24 ); // or in acq. al_pa | 
|  | 1354 | fcChip->Registers.FMconfig.value = ulBuff; // copy it back | 
|  | 1355 | writel( fcChip->Registers.FMconfig.value,  // put in TachLite | 
|  | 1356 | fcChip->Registers.FMconfig.address); | 
|  | 1357 |  | 
|  | 1358 |  | 
|  | 1359 | #ifdef IMQ_DEBUG | 
|  | 1360 | printk("#LUp %Xh, FMstat 0x%08X#", | 
|  | 1361 | fcChip->Registers.my_al_pa, fcChip->Registers.FMstatus.value); | 
|  | 1362 | #endif | 
|  | 1363 |  | 
|  | 1364 | // also set the WRITE-ONLY My_ID Register (for Fabric | 
|  | 1365 | // initialization) | 
|  | 1366 | writel( fcChip->Registers.my_al_pa, | 
|  | 1367 | fcChip->Registers.ReMapMemBase +TL_MEM_TACH_My_ID); | 
|  | 1368 |  | 
|  | 1369 |  | 
|  | 1370 | fcChip->fcStats.linkUp++; | 
|  | 1371 |  | 
|  | 1372 | // reset TL statistics counters | 
|  | 1373 | // (we ignore these error counters | 
|  | 1374 | // while link is down) | 
|  | 1375 | ulBuff =                     // just reset TL's counter | 
|  | 1376 | readl( fcChip->Registers.FMLinkStatus1.address); | 
|  | 1377 |  | 
|  | 1378 | ulBuff =                     // just reset TL's counter | 
|  | 1379 | readl( fcChip->Registers.FMLinkStatus2.address); | 
|  | 1380 |  | 
|  | 1381 | // for initiator, need to start verifying ports (e.g. PDISC) | 
|  | 1382 |  | 
|  | 1383 |  | 
|  | 1384 |  | 
|  | 1385 |  | 
|  | 1386 |  | 
|  | 1387 |  | 
|  | 1388 | CpqTsUnFreezeTachlite( fcChip, 2); // unfreeze Tachlite, if Link OK | 
|  | 1389 |  | 
|  | 1390 | // Tachyon creates an interesting problem for us on LILP frames. | 
|  | 1391 | // Instead of writing the incoming LILP frame into the SFQ before | 
|  | 1392 | // indicating LINK UP (the actual order of events), Tachyon tells | 
|  | 1393 | // us LINK UP, and later us the LILP.  So we delay, then examine the | 
|  | 1394 | // IMQ for an Inbound CM (x04); if found, we can set | 
|  | 1395 | // LINKACTIVE after processing the LILP.  Otherwise, just proceed. | 
|  | 1396 | // Since Tachyon imposes this time delay (and doesn't tell us | 
|  | 1397 | // what it is), we have to impose a delay before "Peeking" the IMQ | 
|  | 1398 | // for Tach hardware (DMA) delivery. | 
|  | 1399 | // Processing LILP is required by SANMark | 
|  | 1400 | udelay( 1000);  // microsec delay waiting for LILP (if it comes) | 
|  | 1401 | if( PeekIMQEntry( fcChip, ELS_LILP_FRAME) ) | 
|  | 1402 | {  // found SFQ LILP, which will post LINKACTIVE | 
|  | 1403 | //	  printk("skipping LINKACTIVE post\n"); | 
|  | 1404 |  | 
|  | 1405 | } | 
|  | 1406 | else | 
|  | 1407 | cpqfcTSPutLinkQue( cpqfcHBAdata, LINKACTIVE, ulFibreFrame); | 
|  | 1408 | } | 
|  | 1409 |  | 
|  | 1410 |  | 
|  | 1411 |  | 
|  | 1412 | // ******* Set Fabric Login indication ******** | 
|  | 1413 | if( fcChip->Registers.FMstatus.value & 0x2000 ) | 
|  | 1414 | { | 
|  | 1415 | printk(" #Fabric# "); | 
|  | 1416 | fcChip->Options.fabric = 1; | 
|  | 1417 | } | 
|  | 1418 | else | 
|  | 1419 | fcChip->Options.fabric = 0; | 
|  | 1420 |  | 
|  | 1421 |  | 
|  | 1422 |  | 
|  | 1423 | // ******* LIP(F8,x) or BAD AL_PA? ******** | 
|  | 1424 | if( fcChip->Registers.FMstatus.value & 0x30000L ) | 
|  | 1425 | { | 
|  | 1426 | // copy the error AL_PAs | 
|  | 1427 | fcChip->Registers.rcv_al_pa.value = | 
|  | 1428 | readl(fcChip->Registers.rcv_al_pa.address); | 
|  | 1429 |  | 
|  | 1430 | // Bad AL_PA? | 
|  | 1431 | if( fcChip->Registers.FMstatus.value & 0x10000L ) | 
|  | 1432 | { | 
|  | 1433 | PFC_LOGGEDIN_PORT pLoggedInPort; | 
|  | 1434 |  | 
|  | 1435 | // copy "BAD" al_pa field | 
|  | 1436 | fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1] = | 
|  | 1437 | (fcChip->Registers.rcv_al_pa.value & 0xff00L) >> 8; | 
|  | 1438 |  | 
|  | 1439 | pLoggedInPort = fcFindLoggedInPort( fcChip, | 
|  | 1440 | NULL,     // DON'T search Scsi Nexus | 
|  | 1441 | fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1], // port id | 
|  | 1442 | NULL,     // DON'T search linked list for FC WWN | 
|  | 1443 | NULL);    // DON'T care about end of list | 
|  | 1444 |  | 
|  | 1445 | if( pLoggedInPort ) | 
|  | 1446 | { | 
|  | 1447 | // Just in case we got this BAD_ALPA because a device | 
|  | 1448 | // quietly disappeared (can happen on non-managed hubs such | 
|  | 1449 | // as the Vixel Rapport 1000), | 
|  | 1450 | // do an Implicit Logout.  We never expect this on a Logged | 
|  | 1451 | // in port (but do expect it on port discovery). | 
|  | 1452 | // (As a reasonable alternative, this could be changed to | 
|  | 1453 | // simply start the implicit logout timer, giving the device | 
|  | 1454 | // several seconds to "come back".) | 
|  | 1455 | // | 
|  | 1456 | printk(" #BAD alpa %Xh# ", | 
|  | 1457 | fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1]); | 
|  | 1458 | cpqfcTSImplicitLogout( cpqfcHBAdata, pLoggedInPort); | 
|  | 1459 | } | 
|  | 1460 | } | 
|  | 1461 | // LIP(f8,x)? | 
|  | 1462 | if( fcChip->Registers.FMstatus.value & 0x20000L ) | 
|  | 1463 | { | 
|  | 1464 | // for debugging, copy al_pa field | 
|  | 1465 | fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2] = | 
|  | 1466 | (fcChip->Registers.rcv_al_pa.value & 0xffL); | 
|  | 1467 | // get the other port's al_pa | 
|  | 1468 | // (one that sent LIP(F8,?) ) | 
|  | 1469 | } | 
|  | 1470 | } | 
|  | 1471 |  | 
|  | 1472 | // Elastic store err | 
|  | 1473 | if( fcChip->Registers.FMstatus.value & 0x400L ) | 
|  | 1474 | { | 
|  | 1475 | // don't count e-s if loop is down! | 
|  | 1476 | if( !(USHORT)(fcChip->Registers.FMstatus.value & 0x80) ) | 
|  | 1477 | fcChip->fcStats.e_stores++; | 
|  | 1478 |  | 
|  | 1479 | } | 
|  | 1480 | } | 
|  | 1481 | break; | 
|  | 1482 |  | 
|  | 1483 |  | 
|  | 1484 | case INBOUND_FCP_XCHG_COMPLETION:  // 0x0C | 
|  | 1485 |  | 
|  | 1486 | // Remarks: | 
|  | 1487 | // On Tachlite TL/TS, we get this message when the data phase | 
|  | 1488 | // of a SEST inbound transfer is complete.  For example, if a WRITE command | 
|  | 1489 | // was received with OX_ID 0, we might respond with XFER_RDY with | 
|  | 1490 | // RX_ID 8001.  This would start the SEST controlled data phases.  When | 
|  | 1491 | // all data frames are received, we get this inbound completion. This means | 
|  | 1492 | // we should send a status frame to complete the status phase of the | 
|  | 1493 | // FCP-SCSI exchange, using the same OX_ID,RX_ID that we used for data | 
|  | 1494 | // frames. | 
|  | 1495 | // See Outbound CM discussion of x_IDs | 
|  | 1496 | // Psuedo Code | 
|  | 1497 | //   Get SEST index (x_ID) | 
|  | 1498 | //     x_ID out of range, return (err condition) | 
|  | 1499 | //   set status bits from 2nd dword | 
|  | 1500 | //   free transactionID & SEST entry | 
|  | 1501 | //   call fcComplete with transactionID & status | 
|  | 1502 |  | 
|  | 1503 | ulBuff = fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[0]; | 
|  | 1504 | x_ID = ulBuff & 0x7fffL;  // lower 14 bits SEST_Index/Trans_ID | 
|  | 1505 | // (mask out MSB "direction" bit) | 
|  | 1506 | // Range check CM OX/RX_ID value... | 
|  | 1507 | if( x_ID < TACH_SEST_LEN )  // don't go beyond SEST array space | 
|  | 1508 | { | 
|  | 1509 |  | 
|  | 1510 | //#define FCP_COMPLETION_DBG 1 | 
|  | 1511 | #ifdef FCP_COMPLETION_DBG | 
|  | 1512 | printk(" FCP_CM x_ID %Xh, status %Xh, Cmnd %p\n", | 
|  | 1513 | x_ID, ulBuff, Exchanges->fcExchange[x_ID].Cmnd); | 
|  | 1514 | #endif | 
|  | 1515 | if( ulBuff & 0x08000000L ) // RPC -Response Phase Complete - or - | 
|  | 1516 | // time to send response frame? | 
|  | 1517 | RPCset = 1;             // (SEST transaction) | 
|  | 1518 | else | 
|  | 1519 | RPCset = 0; | 
|  | 1520 | // set the status for this Inbound SCSI transaction's ID | 
|  | 1521 | dwStatus = 0L; | 
|  | 1522 | if( ulBuff & 0x70000000L ) // any errs? | 
|  | 1523 | { | 
|  | 1524 |  | 
|  | 1525 | if( ulBuff & 0x40000000L ) | 
|  | 1526 | dwStatus |= LINKFAIL_RX; | 
|  | 1527 |  | 
|  | 1528 | if( ulBuff & 0x20000000L ) | 
|  | 1529 | dwStatus |= COUNT_ERROR; | 
|  | 1530 |  | 
|  | 1531 | if( ulBuff & 0x10000000L ) | 
|  | 1532 | dwStatus |= OVERFLOW; | 
|  | 1533 | } | 
|  | 1534 |  | 
|  | 1535 |  | 
|  | 1536 | // FCP transaction done - copy status | 
|  | 1537 | Exchanges->fcExchange[ x_ID ].status = dwStatus; | 
|  | 1538 |  | 
|  | 1539 |  | 
|  | 1540 | // Did the exchange get an FCP-RSP response frame? | 
|  | 1541 | // (Note the little endian/big endian FC payload difference) | 
|  | 1542 |  | 
|  | 1543 | if( RPCset )             // SEST transaction Response frame rec'd | 
|  | 1544 | { | 
|  | 1545 | // complete the command in our driver... | 
|  | 1546 | cpqfcTSCompleteExchange( cpqfcHBAdata->PciDev,fcChip, x_ID); | 
|  | 1547 |  | 
|  | 1548 | }  // end "RPCset" | 
|  | 1549 |  | 
|  | 1550 | else  // ("target" logic) | 
|  | 1551 | { | 
|  | 1552 | // Tachlite says all data frames have been received - now it's time | 
|  | 1553 | // to analyze data transfer (successful?), then send a response | 
|  | 1554 | // frame for this exchange | 
|  | 1555 |  | 
|  | 1556 | ulFibreFrame[0] = x_ID; // copy for later reference | 
|  | 1557 |  | 
|  | 1558 | // if this was a TWE, we have to send satus response | 
|  | 1559 | if( Exchanges->fcExchange[ x_ID].type == SCSI_TWE ) | 
|  | 1560 | { | 
|  | 1561 | //            fcPutScsiQue( cpqfcHBAdata, | 
|  | 1562 | //                NEED_FCP_RSP, ulFibreFrame);  // (ulFibreFrame not used here) | 
|  | 1563 | } | 
|  | 1564 | } | 
|  | 1565 | } | 
|  | 1566 | else  // ERROR CONDITION!  bogus x_ID in completion message | 
|  | 1567 | { | 
|  | 1568 | printk("IN FCP_XCHG: bad x_ID: %Xh\n", x_ID); | 
|  | 1569 | } | 
|  | 1570 |  | 
|  | 1571 | break; | 
|  | 1572 |  | 
|  | 1573 |  | 
|  | 1574 |  | 
|  | 1575 |  | 
|  | 1576 | case INBOUND_SCSI_DATA_COMMAND: | 
|  | 1577 | case BAD_SCSI_FRAME: | 
|  | 1578 | case INB_SCSI_STATUS_COMPLETION: | 
|  | 1579 | case BUFFER_PROCESSED_COMPLETION: | 
|  | 1580 | break; | 
|  | 1581 | } | 
|  | 1582 |  | 
|  | 1583 | // Tachyon is producing; | 
|  | 1584 | // we are consuming | 
|  | 1585 | fcChip->IMQ->consumerIndex++;             // increment OUR consumerIndex | 
|  | 1586 | if( fcChip->IMQ->consumerIndex >= IMQ_LEN)// check for rollover | 
|  | 1587 | fcChip->IMQ->consumerIndex = 0L;        // reset it | 
|  | 1588 |  | 
|  | 1589 |  | 
|  | 1590 | if( fcChip->IMQ->producerIndex == fcChip->IMQ->consumerIndex ) | 
|  | 1591 | {                           // all Messages are processed - | 
|  | 1592 | iStatus = 0;              // no more messages to process | 
|  | 1593 |  | 
|  | 1594 | } | 
|  | 1595 | else | 
|  | 1596 | iStatus = 1;              // more messages to process | 
|  | 1597 |  | 
|  | 1598 | // update TachLite's ConsumerIndex... (clears INTA_L) | 
|  | 1599 | // NOTE: according to TL/TS UG, the | 
|  | 1600 | // "host must return completion messages in sequential order". | 
|  | 1601 | // Does this mean one at a time, in the order received?  We | 
|  | 1602 | // presume so. | 
|  | 1603 |  | 
|  | 1604 | writel( fcChip->IMQ->consumerIndex, | 
|  | 1605 | (fcChip->Registers.ReMapMemBase + IMQ_CONSUMER_INDEX)); | 
|  | 1606 |  | 
|  | 1607 | #if IMQ_DEBUG | 
|  | 1608 | printk("Process IMQ: writing consumer ndx %d\n ", | 
|  | 1609 | fcChip->IMQ->consumerIndex); | 
|  | 1610 | printk("PI %X, CI %X\n", | 
|  | 1611 | fcChip->IMQ->producerIndex,fcChip->IMQ->consumerIndex ); | 
|  | 1612 | #endif | 
|  | 1613 |  | 
|  | 1614 |  | 
|  | 1615 |  | 
|  | 1616 | } | 
|  | 1617 | else | 
|  | 1618 | { | 
|  | 1619 | // hmmm... why did we get interrupted/called with no message? | 
|  | 1620 | iStatus = -1;               // nothing to process | 
|  | 1621 | #if IMQ_DEBUG | 
|  | 1622 | printk("Process IMQ: no message PI %Xh  CI %Xh", | 
|  | 1623 | fcChip->IMQ->producerIndex, | 
|  | 1624 | fcChip->IMQ->consumerIndex); | 
|  | 1625 | #endif | 
|  | 1626 | } | 
|  | 1627 |  | 
|  | 1628 | LEAVE("ProcessIMQEntry"); | 
|  | 1629 |  | 
|  | 1630 | return iStatus; | 
|  | 1631 | } | 
|  | 1632 |  | 
|  | 1633 |  | 
|  | 1634 |  | 
|  | 1635 |  | 
|  | 1636 |  | 
|  | 1637 | // This routine initializes Tachyon according to the following | 
|  | 1638 | // options (opcode1): | 
|  | 1639 | // 1 - RESTART Tachyon, simulate power on condition by shutting | 
|  | 1640 | //     down laser, resetting the hardware, de-allocating all buffers; | 
|  | 1641 | //     continue | 
|  | 1642 | // 2 - Config Tachyon / PCI registers; | 
|  | 1643 | //     continue | 
|  | 1644 | // 3 - Allocating memory and setting Tachyon queues (write Tachyon regs); | 
|  | 1645 | //     continue | 
|  | 1646 | // 4 - Config frame manager registers, initialize, turn on laser | 
|  | 1647 | // | 
|  | 1648 | // Returns: | 
|  | 1649 | //  -1 on fatal error | 
|  | 1650 | //   0 on success | 
|  | 1651 |  | 
|  | 1652 | int CpqTsInitializeTachLite( void *pHBA, int opcode1, int opcode2) | 
|  | 1653 | { | 
|  | 1654 | CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA; | 
|  | 1655 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | 
|  | 1656 | ULONG ulBuff; | 
|  | 1657 | UCHAR bBuff; | 
|  | 1658 | int iStatus=-1;  // assume failure | 
|  | 1659 |  | 
|  | 1660 | ENTER("InitializeTachLite"); | 
|  | 1661 |  | 
|  | 1662 | // verify board's base address (sanity check) | 
|  | 1663 |  | 
|  | 1664 | if( !fcChip->Registers.ReMapMemBase)                // NULL address for card? | 
|  | 1665 | return -1;                         // FATAL error! | 
|  | 1666 |  | 
|  | 1667 |  | 
|  | 1668 |  | 
|  | 1669 | switch( opcode1 ) | 
|  | 1670 | { | 
|  | 1671 | case 1:       // restore hardware to power-on (hard) restart | 
|  | 1672 |  | 
|  | 1673 |  | 
|  | 1674 | iStatus = fcChip->ResetTachyon( | 
|  | 1675 | cpqfcHBAdata, opcode2); // laser off, reset hardware | 
|  | 1676 | // de-allocate aligned buffers | 
|  | 1677 |  | 
|  | 1678 |  | 
|  | 1679 | /* TBD      // reset FC link Q (producer and consumer = 0) | 
|  | 1680 | fcLinkQReset(cpqfcHBAdata); | 
|  | 1681 |  | 
|  | 1682 | */ | 
|  | 1683 |  | 
|  | 1684 | if( iStatus ) | 
|  | 1685 | break; | 
|  | 1686 |  | 
|  | 1687 | case 2:       // Config PCI/Tachyon registers | 
|  | 1688 | // NOTE: For Tach TL/TS, bit 31 must be set to 1.  For TS chips, a read | 
|  | 1689 | // of bit 31 indicates state of M66EN signal; if 1, chip may run at | 
|  | 1690 | // 33-66MHz  (see TL/TS UG, pg 159) | 
|  | 1691 |  | 
|  | 1692 | ulBuff = 0x80000000;  // TachLite Configuration Register | 
|  | 1693 |  | 
|  | 1694 | writel( ulBuff, fcChip->Registers.TYconfig.address); | 
|  | 1695 | //      ulBuff = 0x0147L;  // CpqTs PCI CFGCMD register | 
|  | 1696 | //      WritePCIConfiguration( fcChip->Backplane.bus, | 
|  | 1697 | //                           fcChip->Backplane.slot, TLCFGCMD, ulBuff, 4); | 
|  | 1698 | //      ulBuff = 0x0L;  // test! | 
|  | 1699 | //      ReadPCIConfiguration( fcChip->Backplane.bus, | 
|  | 1700 | //                           fcChip->Backplane.slot, TLCFGCMD, &ulBuff, 4); | 
|  | 1701 |  | 
|  | 1702 | // read back for reference... | 
|  | 1703 | fcChip->Registers.TYconfig.value = | 
|  | 1704 | readl( fcChip->Registers.TYconfig.address ); | 
|  | 1705 |  | 
|  | 1706 | // what is the PCI bus width? | 
|  | 1707 | pci_read_config_byte( cpqfcHBAdata->PciDev, | 
|  | 1708 | 0x43, // PCIMCTR offset | 
|  | 1709 | &bBuff); | 
|  | 1710 |  | 
|  | 1711 | fcChip->Registers.PCIMCTR = bBuff; | 
|  | 1712 |  | 
|  | 1713 | // set string identifying the chip on the circuit board | 
|  | 1714 |  | 
|  | 1715 | fcChip->Registers.TYstatus.value = | 
|  | 1716 | readl( fcChip->Registers.TYstatus.address); | 
|  | 1717 |  | 
|  | 1718 | { | 
|  | 1719 | // Now that we are supporting multiple boards, we need to change | 
|  | 1720 | // this logic to check for PCI vendor/device IDs... | 
|  | 1721 | // for now, quick & dirty is simply checking Chip rev | 
|  | 1722 |  | 
|  | 1723 | ULONG RevId = (fcChip->Registers.TYstatus.value &0x3E0)>>5; | 
|  | 1724 | UCHAR Minor = (UCHAR)(RevId & 0x3); | 
|  | 1725 | UCHAR Major = (UCHAR)((RevId & 0x1C) >>2); | 
|  | 1726 |  | 
|  | 1727 | /* printk("  HBA Tachyon RevId %d.%d\n", Major, Minor); */ | 
|  | 1728 | if( (Major == 1) && (Minor == 2) ) | 
|  | 1729 | { | 
|  | 1730 | sprintf( cpqfcHBAdata->fcChip.Name, STACHLITE66_TS12); | 
|  | 1731 |  | 
|  | 1732 | } | 
|  | 1733 | else if( (Major == 1) && (Minor == 3) ) | 
|  | 1734 | { | 
|  | 1735 | sprintf( cpqfcHBAdata->fcChip.Name, STACHLITE66_TS13); | 
|  | 1736 | } | 
|  | 1737 | else if( (Major == 2) && (Minor == 1) ) | 
|  | 1738 | { | 
|  | 1739 | sprintf( cpqfcHBAdata->fcChip.Name, SAGILENT_XL2_21); | 
|  | 1740 | } | 
|  | 1741 | else | 
|  | 1742 | sprintf( cpqfcHBAdata->fcChip.Name, STACHLITE_UNKNOWN); | 
|  | 1743 | } | 
|  | 1744 |  | 
|  | 1745 |  | 
|  | 1746 |  | 
|  | 1747 | case 3:       // allocate mem, set Tachyon Que registers | 
|  | 1748 | iStatus = CpqTsCreateTachLiteQues( cpqfcHBAdata, opcode2); | 
|  | 1749 |  | 
|  | 1750 | if( iStatus ) | 
|  | 1751 | break; | 
|  | 1752 |  | 
|  | 1753 | // now that the Queues exist, Tach can DMA to them, so | 
|  | 1754 | // we can begin processing INTs | 
|  | 1755 | // INTEN register - enable INT (TachLite interrupt) | 
|  | 1756 | writeb( 0x1F, fcChip->Registers.ReMapMemBase + IINTEN); | 
|  | 1757 |  | 
|  | 1758 | // Fall through | 
|  | 1759 | case 4:       // Config Fame Manager, Init Loop Command, laser on | 
|  | 1760 |  | 
|  | 1761 | // L_PORT or loopback | 
|  | 1762 | // depending on Options | 
|  | 1763 | iStatus = CpqTsInitializeFrameManager( fcChip,0 ); | 
|  | 1764 | if( iStatus ) | 
|  | 1765 | { | 
|  | 1766 | // failed to initialize Frame Manager | 
|  | 1767 | break; | 
|  | 1768 | } | 
|  | 1769 |  | 
|  | 1770 | default: | 
|  | 1771 | break; | 
|  | 1772 | } | 
|  | 1773 | LEAVE("InitializeTachLite"); | 
|  | 1774 |  | 
|  | 1775 | return iStatus; | 
|  | 1776 | } | 
|  | 1777 |  | 
|  | 1778 |  | 
|  | 1779 |  | 
|  | 1780 |  | 
|  | 1781 | // Depending on the type of platform memory allocation (e.g. dynamic), | 
|  | 1782 | // it's probably best to free memory in opposite order as it was allocated. | 
|  | 1783 | // Order of allocation: see other function | 
|  | 1784 |  | 
|  | 1785 |  | 
|  | 1786 | int CpqTsDestroyTachLiteQues( void *pHBA, int opcode) | 
|  | 1787 | { | 
|  | 1788 | CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA; | 
|  | 1789 | PTACHYON fcChip = &cpqfcHBAdata->fcChip; | 
|  | 1790 | USHORT i, iStatus=0; | 
|  | 1791 | void* vPtr;  // mem Align manager sets this to the freed address on success | 
|  | 1792 | unsigned long ulPtr;  // for 64-bit pointer cast (e.g. Alpa machine) | 
|  | 1793 | FC_EXCHANGES *Exchanges = fcChip->Exchanges; | 
|  | 1794 | PSGPAGES j, next; | 
|  | 1795 |  | 
|  | 1796 | ENTER("DestroyTachLiteQues"); | 
|  | 1797 |  | 
|  | 1798 | if( fcChip->SEST ) | 
|  | 1799 | { | 
|  | 1800 | // search out and free Pool for Extended S/G list pages | 
|  | 1801 |  | 
|  | 1802 | for( i=0; i < TACH_SEST_LEN; i++)  // for each exchange | 
|  | 1803 | { | 
|  | 1804 | // It's possible that extended S/G pages were allocated, mapped, and | 
|  | 1805 | // not cleared due to error conditions or O/S driver termination. | 
|  | 1806 | // Make sure they're all gone. | 
|  | 1807 | if (Exchanges->fcExchange[i].Cmnd != NULL) | 
|  | 1808 | cpqfc_pci_unmap(cpqfcHBAdata->PciDev, Exchanges->fcExchange[i].Cmnd, | 
|  | 1809 | fcChip, i); // undo DMA mappings. | 
|  | 1810 |  | 
|  | 1811 | for (j=fcChip->SEST->sgPages[i] ; j != NULL ; j = next) { | 
|  | 1812 | next = j->next; | 
|  | 1813 | kfree(j); | 
|  | 1814 | } | 
|  | 1815 | fcChip->SEST->sgPages[i] = NULL; | 
|  | 1816 | } | 
|  | 1817 | ulPtr = (unsigned long)fcChip->SEST; | 
|  | 1818 | vPtr = fcMemManager( cpqfcHBAdata->PciDev, | 
|  | 1819 | &cpqfcHBAdata->dynamic_mem[0], | 
|  | 1820 | 0,0, (ULONG)ulPtr, NULL ); // 'free' mem | 
|  | 1821 | fcChip->SEST = 0L;  // null invalid ptr | 
|  | 1822 | if( !vPtr ) | 
|  | 1823 | { | 
|  | 1824 | printk("SEST mem not freed\n"); | 
|  | 1825 | iStatus = -1; | 
|  | 1826 | } | 
|  | 1827 | } | 
|  | 1828 |  | 
|  | 1829 | if( fcChip->SFQ ) | 
|  | 1830 | { | 
|  | 1831 |  | 
|  | 1832 | ulPtr = (unsigned long)fcChip->SFQ; | 
|  | 1833 | vPtr = fcMemManager( cpqfcHBAdata->PciDev, | 
|  | 1834 | &cpqfcHBAdata->dynamic_mem[0], | 
|  | 1835 | 0,0, (ULONG)ulPtr, NULL ); // 'free' mem | 
|  | 1836 | fcChip->SFQ = 0L;  // null invalid ptr | 
|  | 1837 | if( !vPtr ) | 
|  | 1838 | { | 
|  | 1839 | printk("SFQ mem not freed\n"); | 
|  | 1840 | iStatus = -2; | 
|  | 1841 | } | 
|  | 1842 | } | 
|  | 1843 |  | 
|  | 1844 |  | 
|  | 1845 | if( fcChip->IMQ ) | 
|  | 1846 | { | 
|  | 1847 | // clear Indexes to show empty Queue | 
|  | 1848 | fcChip->IMQ->producerIndex = 0; | 
|  | 1849 | fcChip->IMQ->consumerIndex = 0; | 
|  | 1850 |  | 
|  | 1851 | ulPtr = (unsigned long)fcChip->IMQ; | 
|  | 1852 | vPtr = fcMemManager( cpqfcHBAdata->PciDev, &cpqfcHBAdata->dynamic_mem[0], | 
|  | 1853 | 0,0, (ULONG)ulPtr, NULL ); // 'free' mem | 
|  | 1854 | fcChip->IMQ = 0L;  // null invalid ptr | 
|  | 1855 | if( !vPtr ) | 
|  | 1856 | { | 
|  | 1857 | printk("IMQ mem not freed\n"); | 
|  | 1858 | iStatus = -3; | 
|  | 1859 | } | 
|  | 1860 | } | 
|  | 1861 |  | 
|  | 1862 | if( fcChip->ERQ )         // release memory blocks used by the queues | 
|  | 1863 | { | 
|  | 1864 | ulPtr = (unsigned long)fcChip->ERQ; | 
|  | 1865 | vPtr = fcMemManager( cpqfcHBAdata->PciDev, &cpqfcHBAdata->dynamic_mem[0], | 
|  | 1866 | 0,0, (ULONG)ulPtr, NULL ); // 'free' mem | 
|  | 1867 | fcChip->ERQ = 0L;  // null invalid ptr | 
|  | 1868 | if( !vPtr ) | 
|  | 1869 | { | 
|  | 1870 | printk("ERQ mem not freed\n"); | 
|  | 1871 | iStatus = -4; | 
|  | 1872 | } | 
|  | 1873 | } | 
|  | 1874 |  | 
|  | 1875 | // free up the primary EXCHANGES struct and Link Q | 
|  | 1876 | cpqfc_free_dma_consistent(cpqfcHBAdata); | 
|  | 1877 |  | 
|  | 1878 | LEAVE("DestroyTachLiteQues"); | 
|  | 1879 |  | 
|  | 1880 | return iStatus;     // non-zero (failed) if any memory not freed | 
|  | 1881 | } | 
|  | 1882 |  | 
|  | 1883 |  | 
|  | 1884 |  | 
|  | 1885 |  | 
|  | 1886 |  | 
|  | 1887 | // The SFQ is an array with SFQ_LEN length, each element (QEntry) | 
|  | 1888 | // with eight 32-bit words.  TachLite places incoming FC frames (i.e. | 
|  | 1889 | // a valid FC frame with our AL_PA ) in contiguous SFQ entries | 
|  | 1890 | // and sends a completion message telling the host where the frame is | 
|  | 1891 | // in the que. | 
|  | 1892 | // This function copies the current (or oldest not-yet-processed) QEntry to | 
|  | 1893 | // a caller's contiguous buffer and updates the Tachyon chip's consumer index | 
|  | 1894 | // | 
|  | 1895 | // NOTE: | 
|  | 1896 | //   An FC frame may consume one or many SFQ entries.  We know the total | 
|  | 1897 | //   length from the completion message.  The caller passes a buffer large | 
|  | 1898 | //   enough for the complete message (max 2k). | 
|  | 1899 |  | 
|  | 1900 | static void CpqTsGetSFQEntry( | 
|  | 1901 | PTACHYON fcChip, | 
|  | 1902 | USHORT producerNdx, | 
|  | 1903 | ULONG *ulDestPtr,            // contiguous destination buffer | 
|  | 1904 | BOOLEAN UpdateChip) | 
|  | 1905 | { | 
|  | 1906 | ULONG total_bytes=0; | 
|  | 1907 | ULONG consumerIndex = fcChip->SFQ->consumerIndex; | 
|  | 1908 |  | 
|  | 1909 | // check passed copy of SFQ producer index - | 
|  | 1910 | // is a new message waiting for us? | 
|  | 1911 | // equal indexes means SFS is copied | 
|  | 1912 |  | 
|  | 1913 | while( producerNdx != consumerIndex ) | 
|  | 1914 | {                             // need to process message | 
|  | 1915 | total_bytes += 64;   // maintain count to prevent writing past buffer | 
|  | 1916 | // don't allow copies over Fibre Channel defined length! | 
|  | 1917 | if( total_bytes <= 2048 ) | 
|  | 1918 | { | 
|  | 1919 | memcpy( ulDestPtr, | 
|  | 1920 | &fcChip->SFQ->QEntry[consumerIndex], | 
|  | 1921 | 64 );  // each SFQ entry is 64 bytes | 
|  | 1922 | ulDestPtr += 16;   // advance pointer to next 64 byte block | 
|  | 1923 | } | 
|  | 1924 | // Tachyon is producing, | 
|  | 1925 | // and we are consuming | 
|  | 1926 |  | 
|  | 1927 | if( ++consumerIndex >= SFQ_LEN)// check for rollover | 
|  | 1928 | consumerIndex = 0L;        // reset it | 
|  | 1929 | } | 
|  | 1930 |  | 
|  | 1931 | // if specified, update the Tachlite chip ConsumerIndex... | 
|  | 1932 | if( UpdateChip ) | 
|  | 1933 | { | 
|  | 1934 | fcChip->SFQ->consumerIndex = consumerIndex; | 
|  | 1935 | writel( fcChip->SFQ->consumerIndex, | 
|  | 1936 | fcChip->Registers.SFQconsumerIndex.address); | 
|  | 1937 | } | 
|  | 1938 | } | 
|  | 1939 |  | 
|  | 1940 |  | 
|  | 1941 |  | 
|  | 1942 | // TachLite routinely freezes it's core ques - Outbound FIFO, Inbound FIFO, | 
|  | 1943 | // and Exchange Request Queue (ERQ) on error recover - | 
|  | 1944 | // (e.g. whenever a LIP occurs).  Here | 
|  | 1945 | // we routinely RESUME by clearing these bits, but only if the loop is up | 
|  | 1946 | // to avoid ERROR IDLE messages forever. | 
|  | 1947 |  | 
|  | 1948 | void CpqTsUnFreezeTachlite( void *pChip, int type ) | 
|  | 1949 | { | 
|  | 1950 | PTACHYON fcChip = (PTACHYON)pChip; | 
|  | 1951 | fcChip->Registers.TYcontrol.value = | 
|  | 1952 | readl(fcChip->Registers.TYcontrol.address); | 
|  | 1953 |  | 
|  | 1954 | // (bit 4 of value is GBIC LASER) | 
|  | 1955 | // if we 'unfreeze' the core machines before the loop is healthy | 
|  | 1956 | // (i.e. FLT, OS, LS failure bits set in FMstatus) | 
|  | 1957 | // we can get 'error idle' messages forever.  Verify that | 
|  | 1958 | // FMstatus (Link Status) is OK before unfreezing. | 
|  | 1959 |  | 
|  | 1960 | if( !(fcChip->Registers.FMstatus.value & 0x07000000L) && // bits clear? | 
|  | 1961 | !(fcChip->Registers.FMstatus.value & 0x80  ))  // Active LPSM? | 
|  | 1962 | { | 
|  | 1963 | fcChip->Registers.TYcontrol.value &=  ~0x300L; // clear FEQ, FFA | 
|  | 1964 | if( type == 1 )  // unfreeze ERQ only | 
|  | 1965 | { | 
|  | 1966 | //      printk("Unfreezing ERQ\n"); | 
|  | 1967 | fcChip->Registers.TYcontrol.value |= 0x10000L; // set REQ | 
|  | 1968 | } | 
|  | 1969 | else             // unfreeze both ERQ and FCP-ASSIST (SEST) | 
|  | 1970 | { | 
|  | 1971 | //      printk("Unfreezing ERQ & FCP-ASSIST\n"); | 
|  | 1972 |  | 
|  | 1973 | // set ROF, RIF, REQ - resume Outbound FCP, Inbnd FCP, ERQ | 
|  | 1974 | fcChip->Registers.TYcontrol.value |= 0x70000L; // set ROF, RIF, REQ | 
|  | 1975 | } | 
|  | 1976 |  | 
|  | 1977 | writel( fcChip->Registers.TYcontrol.value, | 
|  | 1978 | fcChip->Registers.TYcontrol.address); | 
|  | 1979 |  | 
|  | 1980 | } | 
|  | 1981 | // readback for verify (TachLite still frozen?) | 
|  | 1982 | fcChip->Registers.TYstatus.value = | 
|  | 1983 | readl(fcChip->Registers.TYstatus.address); | 
|  | 1984 | } | 
|  | 1985 |  | 
|  | 1986 |  | 
|  | 1987 | // Whenever an FC Exchange Abort is required, we must manipulate the | 
|  | 1988 | // Host/Tachyon shared memory SEST table.  Before doing this, we | 
|  | 1989 | // must freeze Tachyon, which flushes certain buffers and ensure we | 
|  | 1990 | // can manipulate the SEST without contention. | 
|  | 1991 | // This freeze function will result in FCP & ERQ FROZEN completion | 
|  | 1992 | // messages (per argument "type"). | 
|  | 1993 |  | 
|  | 1994 | void CpqTsFreezeTachlite( void *pChip, int type ) | 
|  | 1995 | { | 
|  | 1996 | PTACHYON fcChip = (PTACHYON)pChip; | 
|  | 1997 | fcChip->Registers.TYcontrol.value = | 
|  | 1998 | readl(fcChip->Registers.TYcontrol.address); | 
|  | 1999 |  | 
|  | 2000 | //set FFA, FEQ - freezes SCSI assist and ERQ | 
|  | 2001 | if( type == 1)    // freeze ERQ only | 
|  | 2002 | fcChip->Registers.TYcontrol.value |= 0x100L; // (bit 4 is laser) | 
|  | 2003 | else              // freeze both FCP assists (SEST) and ERQ | 
|  | 2004 | fcChip->Registers.TYcontrol.value |= 0x300L; // (bit 4 is laser) | 
|  | 2005 |  | 
|  | 2006 | writel( fcChip->Registers.TYcontrol.value, | 
|  | 2007 | fcChip->Registers.TYcontrol.address); | 
|  | 2008 |  | 
|  | 2009 | } | 
|  | 2010 |  | 
|  | 2011 |  | 
|  | 2012 |  | 
|  | 2013 |  | 
|  | 2014 | // TL has two Frame Manager Link Status Registers, with three 8-bit | 
|  | 2015 | // fields each. These eight bit counters are cleared after each read, | 
|  | 2016 | // so we define six 32-bit accumulators for these TL counters. This | 
|  | 2017 | // function breaks out each 8-bit field and adds the value to the existing | 
|  | 2018 | // sum.  (s/w counters cleared independently) | 
|  | 2019 |  | 
|  | 2020 | void fcParseLinkStatusCounters(PTACHYON fcChip) | 
|  | 2021 | { | 
|  | 2022 | UCHAR bBuff; | 
|  | 2023 | ULONG ulBuff; | 
|  | 2024 |  | 
|  | 2025 |  | 
|  | 2026 | // The BB0 timer usually increments when TL is initialized, resulting | 
|  | 2027 | // in an initially bogus count.  If our own counter is ZERO, it means we | 
|  | 2028 | // are reading this thing for the first time, so we ignore the first count. | 
|  | 2029 | // Also, reading the register does not clear it, so we have to keep an | 
|  | 2030 | // additional static counter to detect rollover (yuk). | 
|  | 2031 |  | 
|  | 2032 | if( fcChip->fcStats.lastBB0timer == 0L)  // TL was reset? (ignore 1st values) | 
|  | 2033 | { | 
|  | 2034 | // get TL's register counter - the "last" count | 
|  | 2035 | fcChip->fcStats.lastBB0timer = | 
|  | 2036 | fcChip->Registers.FMBB_CreditZero.value & 0x00ffffffL; | 
|  | 2037 | } | 
|  | 2038 | else  // subsequent pass - check for rollover | 
|  | 2039 | { | 
|  | 2040 | // "this" count | 
|  | 2041 | ulBuff = fcChip->Registers.FMBB_CreditZero.value & 0x00ffffffL; | 
|  | 2042 | if( fcChip->fcStats.lastBB0timer > ulBuff ) // rollover happened | 
|  | 2043 | { | 
|  | 2044 | // counter advanced to max... | 
|  | 2045 | fcChip->fcStats.BB0_Timer += (0x00FFFFFFL - fcChip->fcStats.lastBB0timer); | 
|  | 2046 | fcChip->fcStats.BB0_Timer += ulBuff;  // plus some more | 
|  | 2047 |  | 
|  | 2048 |  | 
|  | 2049 | } | 
|  | 2050 | else // no rollover -- more counts or no change | 
|  | 2051 | { | 
|  | 2052 | fcChip->fcStats.BB0_Timer +=  (ulBuff - fcChip->fcStats.lastBB0timer); | 
|  | 2053 |  | 
|  | 2054 | } | 
|  | 2055 |  | 
|  | 2056 | fcChip->fcStats.lastBB0timer = ulBuff; | 
|  | 2057 | } | 
|  | 2058 |  | 
|  | 2059 |  | 
|  | 2060 |  | 
|  | 2061 | bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus1.value >> 24); | 
|  | 2062 | fcChip->fcStats.LossofSignal += bBuff; | 
|  | 2063 |  | 
|  | 2064 | bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus1.value >> 16); | 
|  | 2065 | fcChip->fcStats.BadRXChar += bBuff; | 
|  | 2066 |  | 
|  | 2067 | bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus1.value >> 8); | 
|  | 2068 | fcChip->fcStats.LossofSync += bBuff; | 
|  | 2069 |  | 
|  | 2070 |  | 
|  | 2071 | bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus2.value >> 24); | 
|  | 2072 | fcChip->fcStats.Rx_EOFa += bBuff; | 
|  | 2073 |  | 
|  | 2074 | bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus2.value >> 16); | 
|  | 2075 | fcChip->fcStats.Dis_Frm += bBuff; | 
|  | 2076 |  | 
|  | 2077 | bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus2.value >> 8); | 
|  | 2078 | fcChip->fcStats.Bad_CRC += bBuff; | 
|  | 2079 | } | 
|  | 2080 |  | 
|  | 2081 |  | 
|  | 2082 | void cpqfcTSClearLinkStatusCounters(PTACHYON fcChip) | 
|  | 2083 | { | 
|  | 2084 | ENTER("ClearLinkStatusCounters"); | 
|  | 2085 | memset( &fcChip->fcStats, 0, sizeof( FCSTATS)); | 
|  | 2086 | LEAVE("ClearLinkStatusCounters"); | 
|  | 2087 |  | 
|  | 2088 | } | 
|  | 2089 |  | 
|  | 2090 |  | 
|  | 2091 |  | 
|  | 2092 |  | 
|  | 2093 | // The following function reads the I2C hardware to get the adapter's | 
|  | 2094 | // World Wide Name (WWN). | 
|  | 2095 | // If the WWN is "500805f1fadb43e8" (as printed on the card), the | 
|  | 2096 | // Tachyon WWN_hi (32-bit) register is 500805f1, and WWN_lo register | 
|  | 2097 | // is fadb43e8. | 
|  | 2098 | // In the NVRAM, the bytes appear as: | 
|  | 2099 | // [2d] .. | 
|  | 2100 | // [2e] .. | 
|  | 2101 | // [2f] 50 | 
|  | 2102 | // [30] 08 | 
|  | 2103 | // [31] 05 | 
|  | 2104 | // [32] f1 | 
|  | 2105 | // [33] fa | 
|  | 2106 | // [34] db | 
|  | 2107 | // [35] 43 | 
|  | 2108 | // [36] e8 | 
|  | 2109 | // | 
|  | 2110 | // In the Fibre Channel (Big Endian) format, the FC-AL LISM frame will | 
|  | 2111 | // be correctly loaded by Tachyon silicon.  In the login payload, bytes | 
|  | 2112 | // must be correctly swapped for Big Endian format. | 
|  | 2113 |  | 
|  | 2114 | int CpqTsReadWriteWWN( PVOID pChip, int Read) | 
|  | 2115 | { | 
|  | 2116 | PTACHYON fcChip = (PTACHYON)pChip; | 
|  | 2117 | #define NVRAM_SIZE 512 | 
|  | 2118 | unsigned short i, count = NVRAM_SIZE; | 
|  | 2119 | UCHAR nvRam[NVRAM_SIZE], WWNbuf[8]; | 
|  | 2120 | ULONG ulBuff; | 
|  | 2121 | int iStatus=-1;  // assume failure | 
|  | 2122 | int WWNoffset; | 
|  | 2123 |  | 
|  | 2124 | ENTER("ReadWriteWWN"); | 
|  | 2125 | // Now try to read the WWN from the adapter's NVRAM | 
|  | 2126 |  | 
|  | 2127 | if( Read )  // READing NVRAM WWN? | 
|  | 2128 | { | 
|  | 2129 | ulBuff = cpqfcTS_ReadNVRAM( fcChip->Registers.TYstatus.address, | 
|  | 2130 | fcChip->Registers.TYcontrol.address, | 
|  | 2131 | count, &nvRam[0] ); | 
|  | 2132 |  | 
|  | 2133 | if( ulBuff )   // NVRAM read successful? | 
|  | 2134 | { | 
|  | 2135 | iStatus = 0; // success! | 
|  | 2136 |  | 
|  | 2137 | // for engineering/ prototype boards, the data may be | 
|  | 2138 | // invalid (GIGO, usually all "FF"); this prevents the | 
|  | 2139 | // parse routine from working correctly, which means | 
|  | 2140 | // nothing will be written to our passed buffer. | 
|  | 2141 |  | 
|  | 2142 | WWNoffset = cpqfcTS_GetNVRAM_data( WWNbuf, nvRam ); | 
|  | 2143 |  | 
|  | 2144 | if( !WWNoffset ) // uninitialized NVRAM -- copy bytes directly | 
|  | 2145 | { | 
|  | 2146 | printk( "CAUTION: Copying NVRAM data on fcChip\n"); | 
|  | 2147 | for( i= 0; i < 8; i++) | 
|  | 2148 | WWNbuf[i] = nvRam[i +0x2f]; // dangerous! some formats won't work | 
|  | 2149 | } | 
|  | 2150 |  | 
|  | 2151 | fcChip->Registers.wwn_hi = 0L; | 
|  | 2152 | fcChip->Registers.wwn_lo = 0L; | 
|  | 2153 | for( i=0; i<4; i++)  // WWN bytes are big endian in NVRAM | 
|  | 2154 | { | 
|  | 2155 | ulBuff = 0L; | 
|  | 2156 | ulBuff = (ULONG)(WWNbuf[i]) << (8 * (3-i)); | 
|  | 2157 | fcChip->Registers.wwn_hi |= ulBuff; | 
|  | 2158 | } | 
|  | 2159 | for( i=0; i<4; i++)  // WWN bytes are big endian in NVRAM | 
|  | 2160 | { | 
|  | 2161 | ulBuff = 0L; | 
|  | 2162 | ulBuff = (ULONG)(WWNbuf[i+4]) << (8 * (3-i)); | 
|  | 2163 | fcChip->Registers.wwn_lo |= ulBuff; | 
|  | 2164 | } | 
|  | 2165 | }  // done reading | 
|  | 2166 | else | 
|  | 2167 | { | 
|  | 2168 |  | 
|  | 2169 | printk( "cpqfcTS: NVRAM read failed\n"); | 
|  | 2170 |  | 
|  | 2171 | } | 
|  | 2172 | } | 
|  | 2173 |  | 
|  | 2174 | else  // WRITE | 
|  | 2175 | { | 
|  | 2176 |  | 
|  | 2177 | // NOTE: WRITE not supported & not used in released driver. | 
|  | 2178 |  | 
|  | 2179 |  | 
|  | 2180 | printk("ReadWriteNRAM: can't write NVRAM; aborting write\n"); | 
|  | 2181 | } | 
|  | 2182 |  | 
|  | 2183 | LEAVE("ReadWriteWWN"); | 
|  | 2184 | return iStatus; | 
|  | 2185 | } | 
|  | 2186 |  | 
|  | 2187 |  | 
|  | 2188 |  | 
|  | 2189 |  | 
|  | 2190 |  | 
|  | 2191 | // The following function reads or writes the entire "NVRAM" contents of | 
|  | 2192 | // the I2C hardware (i.e. the NM24C03).  Note that HP's 5121A (TS 66Mhz) | 
|  | 2193 | // adapter does not use the NM24C03 chip, so this function only works on | 
|  | 2194 | // Compaq's adapters. | 
|  | 2195 |  | 
|  | 2196 | int CpqTsReadWriteNVRAM( PVOID pChip, PVOID buf, int Read) | 
|  | 2197 | { | 
|  | 2198 | PTACHYON fcChip = (PTACHYON)pChip; | 
|  | 2199 | #define NVRAM_SIZE 512 | 
|  | 2200 | ULONG ulBuff; | 
|  | 2201 | UCHAR *ucPtr = buf; // cast caller's void ptr to UCHAR array | 
|  | 2202 | int iStatus=-1;  // assume failure | 
|  | 2203 |  | 
|  | 2204 |  | 
|  | 2205 | if( Read )  // READing NVRAM? | 
|  | 2206 | { | 
|  | 2207 | ulBuff = cpqfcTS_ReadNVRAM(   // TRUE on success | 
|  | 2208 | fcChip->Registers.TYstatus.address, | 
|  | 2209 | fcChip->Registers.TYcontrol.address, | 
|  | 2210 | 256,            // bytes to write | 
|  | 2211 | ucPtr );        // source ptr | 
|  | 2212 |  | 
|  | 2213 |  | 
|  | 2214 | if( ulBuff ) | 
|  | 2215 | iStatus = 0; // success | 
|  | 2216 | else | 
|  | 2217 | { | 
|  | 2218 | #ifdef DBG | 
|  | 2219 | printk( "CAUTION: NVRAM read failed\n"); | 
|  | 2220 | #endif | 
|  | 2221 | } | 
|  | 2222 | }  // done reading | 
|  | 2223 |  | 
|  | 2224 | else  // WRITING NVRAM | 
|  | 2225 | { | 
|  | 2226 |  | 
|  | 2227 | printk("cpqfcTS: WRITE of FC Controller's NVRAM disabled\n"); | 
|  | 2228 | } | 
|  | 2229 |  | 
|  | 2230 | return iStatus; | 
|  | 2231 | } |