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 | } |