diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/cpqfcTScontrol.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/scsi/cpqfcTScontrol.c')
-rw-r--r-- | drivers/scsi/cpqfcTScontrol.c | 2231 |
1 files changed, 2231 insertions, 0 deletions
diff --git a/drivers/scsi/cpqfcTScontrol.c b/drivers/scsi/cpqfcTScontrol.c new file mode 100644 index 000000000000..bd94c70f473d --- /dev/null +++ b/drivers/scsi/cpqfcTScontrol.c | |||
@@ -0,0 +1,2231 @@ | |||
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 | } | ||