Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* Copyright(c) 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 NVRAM I2C hardware on | |
22 | // non-intelligent Fibre Host Adapters. | |
23 | // The primary purpose is to read the HBA's NVRAM to get adapter's | |
24 | // manufactured WWN to copy into Tachyon chip registers | |
25 | // Orignal source author unknown | |
26 | ||
27 | #include <linux/types.h> | |
28 | enum boolean { FALSE, TRUE } ; | |
29 | ||
30 | ||
31 | #ifndef UCHAR | |
32 | typedef __u8 UCHAR; | |
33 | #endif | |
34 | #ifndef BOOLEAN | |
35 | typedef __u8 BOOLEAN; | |
36 | #endif | |
37 | #ifndef USHORT | |
38 | typedef __u16 USHORT; | |
39 | #endif | |
40 | #ifndef ULONG | |
41 | typedef __u32 ULONG; | |
42 | #endif | |
43 | ||
44 | ||
45 | #include <linux/string.h> | |
46 | #include <linux/pci.h> | |
47 | #include <linux/delay.h> | |
48 | #include <linux/sched.h> | |
49 | #include <asm/io.h> // struct pt_regs for IRQ handler & Port I/O | |
50 | ||
51 | #include "cpqfcTSchip.h" | |
52 | ||
53 | static void tl_i2c_tx_byte( void* GPIOout, UCHAR data ); | |
54 | /*static BOOLEAN tl_write_i2c_page_portion( void* GPIOin, void* GPIOout, | |
55 | USHORT startOffset, // e.g. 0x2f for WWN start | |
56 | USHORT count, | |
57 | UCHAR *buf ); | |
58 | */ | |
59 | ||
60 | // | |
61 | // Tachlite GPIO2, GPIO3 (I2C) DEFINES | |
62 | // The NVRAM chip NM24C03 defines SCL (serial clock) and SDA (serial data) | |
63 | // GPIO2 drives SDA, and GPIO3 drives SCL | |
64 | // | |
65 | // Since Tachlite inverts the state of the GPIO 0-3 outputs, SET writes 0 | |
66 | // and clear writes 1. The input lines (read in TL status) is NOT inverted | |
67 | // This really helps confuse the code and debugging. | |
68 | ||
69 | #define SET_DATA_HI 0x0 | |
70 | #define SET_DATA_LO 0x8 | |
71 | #define SET_CLOCK_HI 0x0 | |
72 | #define SET_CLOCK_LO 0x4 | |
73 | ||
74 | #define SENSE_DATA_HI 0x8 | |
75 | #define SENSE_DATA_LO 0x0 | |
76 | #define SENSE_CLOCK_HI 0x4 | |
77 | #define SENSE_CLOCK_LO 0x0 | |
78 | ||
79 | #define SLAVE_READ_ADDRESS 0xA1 | |
80 | #define SLAVE_WRITE_ADDRESS 0xA0 | |
81 | ||
82 | ||
83 | static void i2c_delay(ULONG mstime); | |
84 | static void tl_i2c_clock_pulse( UCHAR , void* GPIOout); | |
85 | static UCHAR tl_read_i2c_data( void* ); | |
86 | ||
87 | ||
88 | //----------------------------------------------------------------------------- | |
89 | // | |
90 | // Name: I2C_RX_ACK | |
91 | // | |
92 | // This routine receives an acknowledge over the I2C bus. | |
93 | // | |
94 | //----------------------------------------------------------------------------- | |
95 | static unsigned short tl_i2c_rx_ack( void* GPIOin, void* GPIOout ) | |
96 | { | |
97 | unsigned long value; | |
98 | ||
99 | // do clock pulse, let data line float high | |
100 | tl_i2c_clock_pulse( SET_DATA_HI, GPIOout ); | |
101 | ||
102 | // slave must drive data low for acknowledge | |
103 | value = tl_read_i2c_data( GPIOin); | |
104 | if (value & SENSE_DATA_HI ) | |
105 | return( FALSE ); | |
106 | ||
107 | return( TRUE ); | |
108 | } | |
109 | //----------------------------------------------------------------------------- | |
110 | // | |
111 | // Name: READ_I2C_REG | |
112 | // | |
113 | // This routine reads the I2C control register using the global | |
114 | // IO address stored in gpioreg. | |
115 | // | |
116 | //----------------------------------------------------------------------------- | |
117 | static UCHAR tl_read_i2c_data( void* gpioreg ) | |
118 | { | |
119 | return( (UCHAR)(readl( gpioreg ) & 0x08L) ); // GPIO3 | |
120 | } | |
121 | //----------------------------------------------------------------------------- | |
122 | // | |
123 | // Name: WRITE_I2C_REG | |
124 | // | |
125 | // This routine writes the I2C control register using the global | |
126 | // IO address stored in gpioreg. | |
127 | // In Tachlite, we don't want to modify other bits in TL Control reg. | |
128 | // | |
129 | //----------------------------------------------------------------------------- | |
130 | static void tl_write_i2c_reg( void* gpioregOUT, UCHAR value ) | |
131 | { | |
132 | ULONG temp; | |
133 | ||
134 | // First read the register and clear out the old bits | |
135 | temp = readl( gpioregOUT ) & 0xfffffff3L; | |
136 | ||
137 | // Now or in the new data and send it back out | |
138 | writel( temp | value, gpioregOUT); | |
139 | } | |
140 | //----------------------------------------------------------------------------- | |
141 | // | |
142 | // Name: I2C_TX_START | |
143 | // | |
144 | // This routine transmits a start condition over the I2C bus. | |
145 | // 1. Set SCL (clock, GPIO2) HIGH, set SDA (data, GPIO3) HIGH, | |
146 | // wait 5us to stabilize. | |
147 | // 2. With SCL still HIGH, drive SDA low. The low transition marks | |
148 | // the start condition to NM24Cxx (the chip) | |
149 | // NOTE! In TL control reg., output 1 means chip sees LOW | |
150 | // | |
151 | //----------------------------------------------------------------------------- | |
152 | static unsigned short tl_i2c_tx_start( void* GPIOin, void* GPIOout ) | |
153 | { | |
154 | unsigned short i; | |
155 | ULONG value; | |
156 | ||
157 | if ( !(tl_read_i2c_data(GPIOin) & SENSE_DATA_HI)) | |
158 | { | |
159 | // start with clock high, let data float high | |
160 | tl_write_i2c_reg( GPIOout, SET_DATA_HI | SET_CLOCK_HI ); | |
161 | ||
162 | // keep sending clock pulses if slave is driving data line | |
163 | for (i = 0; i < 10; i++) | |
164 | { | |
165 | tl_i2c_clock_pulse( SET_DATA_HI, GPIOout ); | |
166 | ||
167 | if ( tl_read_i2c_data(GPIOin) & SENSE_DATA_HI ) | |
168 | break; | |
169 | } | |
170 | ||
171 | // if he's still driving data low after 10 clocks, abort | |
172 | value = tl_read_i2c_data( GPIOin ); // read status | |
173 | if (!(value & 0x08) ) | |
174 | return( FALSE ); | |
175 | } | |
176 | ||
177 | ||
178 | // To START, bring data low while clock high | |
179 | tl_write_i2c_reg( GPIOout, SET_CLOCK_HI | SET_DATA_LO ); | |
180 | ||
181 | i2c_delay(0); | |
182 | ||
183 | return( TRUE ); // TX start successful | |
184 | } | |
185 | //----------------------------------------------------------------------------- | |
186 | // | |
187 | // Name: I2C_TX_STOP | |
188 | // | |
189 | // This routine transmits a stop condition over the I2C bus. | |
190 | // | |
191 | //----------------------------------------------------------------------------- | |
192 | ||
193 | static unsigned short tl_i2c_tx_stop( void* GPIOin, void* GPIOout ) | |
194 | { | |
195 | int i; | |
196 | ||
197 | for (i = 0; i < 10; i++) | |
198 | { | |
199 | // Send clock pulse, drive data line low | |
200 | tl_i2c_clock_pulse( SET_DATA_LO, GPIOout ); | |
201 | ||
202 | // To STOP, bring data high while clock high | |
203 | tl_write_i2c_reg( GPIOout, SET_DATA_HI | SET_CLOCK_HI ); | |
204 | ||
205 | // Give the data line time to float high | |
206 | i2c_delay(0); | |
207 | ||
208 | // If slave is driving data line low, there's a problem; retry | |
209 | if ( tl_read_i2c_data(GPIOin) & SENSE_DATA_HI ) | |
210 | return( TRUE ); // TX STOP successful! | |
211 | } | |
212 | ||
213 | return( FALSE ); // error | |
214 | } | |
215 | //----------------------------------------------------------------------------- | |
216 | // | |
217 | // Name: I2C_TX_uchar | |
218 | // | |
219 | // This routine transmits a byte across the I2C bus. | |
220 | // | |
221 | //----------------------------------------------------------------------------- | |
222 | static void tl_i2c_tx_byte( void* GPIOout, UCHAR data ) | |
223 | { | |
224 | UCHAR bit; | |
225 | ||
226 | for (bit = 0x80; bit; bit >>= 1) | |
227 | { | |
228 | if( data & bit ) | |
229 | tl_i2c_clock_pulse( (UCHAR)SET_DATA_HI, GPIOout); | |
230 | else | |
231 | tl_i2c_clock_pulse( (UCHAR)SET_DATA_LO, GPIOout); | |
232 | } | |
233 | } | |
234 | //----------------------------------------------------------------------------- | |
235 | // | |
236 | // Name: I2C_RX_uchar | |
237 | // | |
238 | // This routine receives a byte across the I2C bus. | |
239 | // | |
240 | //----------------------------------------------------------------------------- | |
241 | static UCHAR tl_i2c_rx_byte( void* GPIOin, void* GPIOout ) | |
242 | { | |
243 | UCHAR bit; | |
244 | UCHAR data = 0; | |
245 | ||
246 | ||
247 | for (bit = 0x80; bit; bit >>= 1) { | |
248 | // do clock pulse, let data line float high | |
249 | tl_i2c_clock_pulse( SET_DATA_HI, GPIOout ); | |
250 | ||
251 | // read data line | |
252 | if ( tl_read_i2c_data( GPIOin) & 0x08 ) | |
253 | data |= bit; | |
254 | } | |
255 | ||
256 | return (data); | |
257 | } | |
258 | //***************************************************************************** | |
259 | //***************************************************************************** | |
260 | // Function: read_i2c_nvram | |
261 | // Arguments: UCHAR count number of bytes to read | |
262 | // UCHAR *buf area to store the bytes read | |
263 | // Returns: 0 - failed | |
264 | // 1 - success | |
265 | //***************************************************************************** | |
266 | //***************************************************************************** | |
267 | unsigned long cpqfcTS_ReadNVRAM( void* GPIOin, void* GPIOout , USHORT count, | |
268 | UCHAR *buf ) | |
269 | { | |
270 | unsigned short i; | |
271 | ||
272 | if( !( tl_i2c_tx_start(GPIOin, GPIOout) )) | |
273 | return FALSE; | |
274 | ||
275 | // Select the NVRAM for "dummy" write, to set the address | |
276 | tl_i2c_tx_byte( GPIOout , SLAVE_WRITE_ADDRESS ); | |
277 | if ( !tl_i2c_rx_ack(GPIOin, GPIOout ) ) | |
278 | return( FALSE ); | |
279 | ||
280 | // Now send the address where we want to start reading | |
281 | tl_i2c_tx_byte( GPIOout , 0 ); | |
282 | if ( !tl_i2c_rx_ack(GPIOin, GPIOout ) ) | |
283 | return( FALSE ); | |
284 | ||
285 | // Send a repeated start condition and select the | |
286 | // slave for reading now. | |
287 | if( tl_i2c_tx_start(GPIOin, GPIOout) ) | |
288 | tl_i2c_tx_byte( GPIOout, SLAVE_READ_ADDRESS ); | |
289 | ||
290 | if ( !tl_i2c_rx_ack(GPIOin, GPIOout) ) | |
291 | return( FALSE ); | |
292 | ||
293 | // this loop will now read out the data and store it | |
294 | // in the buffer pointed to by buf | |
295 | for ( i=0; i<count; i++) | |
296 | { | |
297 | *buf++ = tl_i2c_rx_byte(GPIOin, GPIOout); | |
298 | ||
299 | // Send ACK by holding data line low for 1 clock | |
300 | if ( i < (count-1) ) | |
301 | tl_i2c_clock_pulse( 0x08, GPIOout ); | |
302 | else { | |
303 | // Don't send ack for final byte | |
304 | tl_i2c_clock_pulse( SET_DATA_HI, GPIOout ); | |
305 | } | |
306 | } | |
307 | ||
308 | tl_i2c_tx_stop(GPIOin, GPIOout); | |
309 | ||
310 | return( TRUE ); | |
311 | } | |
312 | ||
313 | //**************************************************************** | |
314 | // | |
315 | // | |
316 | // | |
317 | // routines to set and clear the data and clock bits | |
318 | // | |
319 | // | |
320 | // | |
321 | //**************************************************************** | |
322 | ||
323 | static void tl_set_clock(void* gpioreg) | |
324 | { | |
325 | ULONG ret_val; | |
326 | ||
327 | ret_val = readl( gpioreg ); | |
328 | ret_val &= 0xffffffFBL; // clear GPIO2 (SCL) | |
329 | writel( ret_val, gpioreg); | |
330 | } | |
331 | ||
332 | static void tl_clr_clock(void* gpioreg) | |
333 | { | |
334 | ULONG ret_val; | |
335 | ||
336 | ret_val = readl( gpioreg ); | |
337 | ret_val |= SET_CLOCK_LO; | |
338 | writel( ret_val, gpioreg); | |
339 | } | |
340 | ||
341 | //***************************************************************** | |
342 | // | |
343 | // | |
344 | // This routine will advance the clock by one period | |
345 | // | |
346 | // | |
347 | //***************************************************************** | |
348 | static void tl_i2c_clock_pulse( UCHAR value, void* GPIOout ) | |
349 | { | |
350 | ULONG ret_val; | |
351 | ||
352 | // clear the clock bit | |
353 | tl_clr_clock( GPIOout ); | |
354 | ||
355 | i2c_delay(0); | |
356 | ||
357 | ||
358 | // read the port to preserve non-I2C bits | |
359 | ret_val = readl( GPIOout ); | |
360 | ||
361 | // clear the data & clock bits | |
362 | ret_val &= 0xFFFFFFf3; | |
363 | ||
364 | // write the value passed in... | |
365 | // data can only change while clock is LOW! | |
366 | ret_val |= value; // the data | |
367 | ret_val |= SET_CLOCK_LO; // the clock | |
368 | writel( ret_val, GPIOout ); | |
369 | ||
370 | i2c_delay(0); | |
371 | ||
372 | ||
373 | //set clock bit | |
374 | tl_set_clock( GPIOout); | |
375 | } | |
376 | ||
377 | ||
378 | ||
379 | ||
380 | //***************************************************************** | |
381 | // | |
382 | // | |
383 | // This routine returns the 64-bit WWN | |
384 | // | |
385 | // | |
386 | //***************************************************************** | |
387 | int cpqfcTS_GetNVRAM_data( UCHAR *wwnbuf, UCHAR *buf ) | |
388 | { | |
389 | ULONG len; | |
390 | ULONG sub_len; | |
391 | ULONG ptr_inc; | |
392 | ULONG i; | |
393 | ULONG j; | |
394 | UCHAR *data_ptr; | |
395 | UCHAR z; | |
396 | UCHAR name; | |
397 | UCHAR sub_name; | |
398 | UCHAR done; | |
399 | int iReturn=0; // def. 0 offset is failure to find WWN field | |
400 | ||
401 | ||
402 | ||
403 | data_ptr = (UCHAR *)buf; | |
404 | ||
405 | done = FALSE; | |
406 | i = 0; | |
407 | ||
408 | while ( (i < 128) && (!done) ) | |
409 | { | |
410 | z = data_ptr[i];\ | |
411 | if ( !(z & 0x80) ) | |
412 | { | |
413 | len = 1 + (z & 0x07); | |
414 | ||
415 | name = (z & 0x78) >> 3; | |
416 | if (name == 0x0F) | |
417 | done = TRUE; | |
418 | } | |
419 | else | |
420 | { | |
421 | name = z & 0x7F; | |
422 | len = 3 + data_ptr[i+1] + (data_ptr[i+2] << 8); | |
423 | ||
424 | switch (name) | |
425 | { | |
426 | case 0x0D: | |
427 | // | |
428 | j = i + 3; | |
429 | // | |
430 | if ( data_ptr[j] == 0x3b ) { | |
431 | len = 6; | |
432 | break; | |
433 | } | |
434 | ||
435 | while ( j<(i+len) ) { | |
436 | sub_name = (data_ptr[j] & 0x3f); | |
437 | sub_len = data_ptr[j+1] + | |
438 | (data_ptr[j+2] << 8); | |
439 | ptr_inc = sub_len + 3; | |
440 | switch (sub_name) | |
441 | { | |
442 | case 0x3C: | |
443 | memcpy( wwnbuf, &data_ptr[j+3], 8); | |
444 | iReturn = j+3; | |
445 | break; | |
446 | default: | |
447 | break; | |
448 | } | |
449 | j += ptr_inc; | |
450 | } | |
451 | break; | |
452 | default: | |
453 | break; | |
454 | } | |
455 | } | |
456 | // | |
457 | i += len; | |
458 | } // end while | |
459 | return iReturn; | |
460 | } | |
461 | ||
462 | ||
463 | ||
464 | ||
465 | ||
466 | // define a short 5 micro sec delay, and longer (ms) delay | |
467 | ||
468 | static void i2c_delay(ULONG mstime) | |
469 | { | |
470 | ULONG i; | |
471 | ||
472 | // NOTE: we only expect to use these delays when reading | |
473 | // our adapter's NVRAM, which happens only during adapter reset. | |
474 | // Delay technique from "Linux Device Drivers", A. Rubini | |
475 | // (1st Ed.) pg 137. | |
476 | ||
477 | // printk(" delay %lx ", mstime); | |
478 | if( mstime ) // ms delay? | |
479 | { | |
480 | // delay technique | |
481 | for( i=0; i < mstime; i++) | |
482 | udelay(1000); // 1ms per loop | |
483 | ||
484 | } | |
485 | else // 5 micro sec delay | |
486 | ||
487 | udelay( 5 ); // micro secs | |
488 | ||
489 | // printk("done\n"); | |
490 | } | |
491 | ||
492 | ||
493 |