Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | } |