ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / scsi / sym53c8xx_2 / sym_nvram.c
1 /*
2  * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family 
3  * of PCI-SCSI IO processors.
4  *
5  * Copyright (C) 1999-2001  Gerard Roudier <groudier@free.fr>
6  *
7  * This driver is derived from the Linux sym53c8xx driver.
8  * Copyright (C) 1998-2000  Gerard Roudier
9  *
10  * The sym53c8xx driver is derived from the ncr53c8xx driver that had been 
11  * a port of the FreeBSD ncr driver to Linux-1.2.13.
12  *
13  * The original ncr driver has been written for 386bsd and FreeBSD by
14  *         Wolfgang Stanglmeier        <wolf@cologne.de>
15  *         Stefan Esser                <se@mi.Uni-Koeln.de>
16  * Copyright (C) 1994  Wolfgang Stanglmeier
17  *
18  * Other major contributions:
19  *
20  * NVRAM detection and reading.
21  * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
22  *
23  *-----------------------------------------------------------------------------
24  *
25  * Redistribution and use in source and binary forms, with or without
26  * modification, are permitted provided that the following conditions
27  * are met:
28  * 1. Redistributions of source code must retain the above copyright
29  *    notice, this list of conditions and the following disclaimer.
30  * 2. The name of the author may not be used to endorse or promote products
31  *    derived from this software without specific prior written permission.
32  *
33  * Where this Software is combined with software released under the terms of 
34  * the GNU Public License ("GPL") and the terms of the GPL would require the 
35  * combined work to also be released under the terms of the GPL, the terms
36  * and conditions of this License will apply in addition to those of the
37  * GPL with the exception of any terms or conditions of this License that
38  * conflict with, or are expressly prohibited by, the GPL.
39  *
40  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
41  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
43  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
44  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
45  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
46  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
48  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
49  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50  * SUCH DAMAGE.
51  */
52
53 #include "sym_glue.h"
54 #include "sym_nvram.h"
55
56 /*
57  *  Some poor and bogus sync table that refers to Tekram NVRAM layout.
58  */
59 static u_char Tekram_sync[16] =
60         {25,31,37,43, 50,62,75,125, 12,15,18,21, 6,7,9,10};
61 #ifdef  SYM_CONF_DEBUG_NVRAM
62 static u_char Tekram_boot_delay[7] = {3, 5, 10, 20, 30, 60, 120};
63 #endif
64
65 /*
66  *  Get host setup from NVRAM.
67  */
68 void sym_nvram_setup_host(struct sym_hcb *np, struct sym_nvram *nvram)
69 {
70         /*
71          *  Get parity checking, host ID, verbose mode 
72          *  and miscellaneous host flags from NVRAM.
73          */
74         switch (nvram->type) {
75         case SYM_SYMBIOS_NVRAM:
76                 if (!(nvram->data.Symbios.flags & SYMBIOS_PARITY_ENABLE))
77                         np->rv_scntl0  &= ~0x0a;
78                 np->myaddr = nvram->data.Symbios.host_id & 0x0f;
79                 if (nvram->data.Symbios.flags & SYMBIOS_VERBOSE_MSGS)
80                         np->verbose += 1;
81                 if (nvram->data.Symbios.flags1 & SYMBIOS_SCAN_HI_LO)
82                         np->usrflags |= SYM_SCAN_TARGETS_HILO;
83                 if (nvram->data.Symbios.flags2 & SYMBIOS_AVOID_BUS_RESET)
84                         np->usrflags |= SYM_AVOID_BUS_RESET;
85                 break;
86         case SYM_TEKRAM_NVRAM:
87                 np->myaddr = nvram->data.Tekram.host_id & 0x0f;
88                 break;
89         default:
90                 break;
91         }
92 }
93
94 /*
95  *  Get target set-up from Symbios format NVRAM.
96  */
97 static void
98 sym_Symbios_setup_target(struct sym_hcb *np, int target, Symbios_nvram *nvram)
99 {
100         struct sym_tcb *tp = &np->target[target];
101         Symbios_target *tn = &nvram->target[target];
102
103         tp->tinfo.user.period = tn->sync_period ? (tn->sync_period + 3) / 4 : 0;
104         tp->tinfo.user.width  = tn->bus_width == 0x10 ? BUS_16_BIT : BUS_8_BIT;
105         tp->usrtags =
106                 (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? SYM_SETUP_MAX_TAG : 0;
107
108         if (!(tn->flags & SYMBIOS_DISCONNECT_ENABLE))
109                 tp->usrflags &= ~SYM_DISC_ENABLED;
110         if (!(tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME))
111                 tp->usrflags |= SYM_SCAN_BOOT_DISABLED;
112         if (!(tn->flags & SYMBIOS_SCAN_LUNS))
113                 tp->usrflags |= SYM_SCAN_LUNS_DISABLED;
114 }
115
116 /*
117  *  Get target set-up from Tekram format NVRAM.
118  */
119 static void
120 sym_Tekram_setup_target(struct sym_hcb *np, int target, Tekram_nvram *nvram)
121 {
122         struct sym_tcb *tp = &np->target[target];
123         struct Tekram_target *tn = &nvram->target[target];
124         int i;
125
126         if (tn->flags & TEKRAM_SYNC_NEGO) {
127                 i = tn->sync_index & 0xf;
128                 tp->tinfo.user.period = Tekram_sync[i];
129         }
130
131         tp->tinfo.user.width = (tn->flags & TEKRAM_WIDE_NEGO) ?
132                 BUS_16_BIT : BUS_8_BIT;
133
134         if (tn->flags & TEKRAM_TAGGED_COMMANDS) {
135                 tp->usrtags = 2 << nvram->max_tags_index;
136         }
137
138         if (tn->flags & TEKRAM_DISCONNECT_ENABLE)
139                 tp->usrflags |= SYM_DISC_ENABLED;
140  
141         /* If any device does not support parity, we will not use this option */
142         if (!(tn->flags & TEKRAM_PARITY_CHECK))
143                 np->rv_scntl0  &= ~0x0a; /* SCSI parity checking disabled */
144 }
145
146 /*
147  *  Get target setup from NVRAM.
148  */
149 void sym_nvram_setup_target(struct sym_hcb *np, int target, struct sym_nvram *nvp)
150 {
151         switch (nvp->type) {
152         case SYM_SYMBIOS_NVRAM:
153                 sym_Symbios_setup_target(np, target, &nvp->data.Symbios);
154                 break;
155         case SYM_TEKRAM_NVRAM:
156                 sym_Tekram_setup_target(np, target, &nvp->data.Tekram);
157                 break;
158         default:
159                 break;
160         }
161 }
162
163 #ifdef  SYM_CONF_DEBUG_NVRAM
164 /*
165  *  Dump Symbios format NVRAM for debugging purpose.
166  */
167 static void sym_display_Symbios_nvram(struct sym_device *np, Symbios_nvram *nvram)
168 {
169         int i;
170
171         /* display Symbios nvram host data */
172         printf("%s: HOST ID=%d%s%s%s%s%s%s\n",
173                 sym_name(np), nvram->host_id & 0x0f,
174                 (nvram->flags  & SYMBIOS_SCAM_ENABLE)   ? " SCAM"       :"",
175                 (nvram->flags  & SYMBIOS_PARITY_ENABLE) ? " PARITY"     :"",
176                 (nvram->flags  & SYMBIOS_VERBOSE_MSGS)  ? " VERBOSE"    :"", 
177                 (nvram->flags  & SYMBIOS_CHS_MAPPING)   ? " CHS_ALT"    :"", 
178                 (nvram->flags2 & SYMBIOS_AVOID_BUS_RESET)?" NO_RESET"   :"",
179                 (nvram->flags1 & SYMBIOS_SCAN_HI_LO)    ? " HI_LO"      :"");
180
181         /* display Symbios nvram drive data */
182         for (i = 0 ; i < 15 ; i++) {
183                 struct Symbios_target *tn = &nvram->target[i];
184                 printf("%s-%d:%s%s%s%s WIDTH=%d SYNC=%d TMO=%d\n",
185                 sym_name(np), i,
186                 (tn->flags & SYMBIOS_DISCONNECT_ENABLE) ? " DISC"       : "",
187                 (tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME) ? " SCAN_BOOT"  : "",
188                 (tn->flags & SYMBIOS_SCAN_LUNS)         ? " SCAN_LUNS"  : "",
189                 (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? " TCQ"        : "",
190                 tn->bus_width,
191                 tn->sync_period / 4,
192                 tn->timeout);
193         }
194 }
195
196 /*
197  *  Dump TEKRAM format NVRAM for debugging purpose.
198  */
199 static void sym_display_Tekram_nvram(struct sym_device *np, Tekram_nvram *nvram)
200 {
201         int i, tags, boot_delay;
202         char *rem;
203
204         /* display Tekram nvram host data */
205         tags = 2 << nvram->max_tags_index;
206         boot_delay = 0;
207         if (nvram->boot_delay_index < 6)
208                 boot_delay = Tekram_boot_delay[nvram->boot_delay_index];
209         switch ((nvram->flags & TEKRAM_REMOVABLE_FLAGS) >> 6) {
210         default:
211         case 0: rem = "";                       break;
212         case 1: rem = " REMOVABLE=boot device"; break;
213         case 2: rem = " REMOVABLE=all";         break;
214         }
215
216         printf("%s: HOST ID=%d%s%s%s%s%s%s%s%s%s BOOT DELAY=%d tags=%d\n",
217                 sym_name(np), nvram->host_id & 0x0f,
218                 (nvram->flags1 & SYMBIOS_SCAM_ENABLE)   ? " SCAM"       :"",
219                 (nvram->flags & TEKRAM_MORE_THAN_2_DRIVES) ? " >2DRIVES":"",
220                 (nvram->flags & TEKRAM_DRIVES_SUP_1GB)  ? " >1GB"       :"",
221                 (nvram->flags & TEKRAM_RESET_ON_POWER_ON) ? " RESET"    :"",
222                 (nvram->flags & TEKRAM_ACTIVE_NEGATION) ? " ACT_NEG"    :"",
223                 (nvram->flags & TEKRAM_IMMEDIATE_SEEK)  ? " IMM_SEEK"   :"",
224                 (nvram->flags & TEKRAM_SCAN_LUNS)       ? " SCAN_LUNS"  :"",
225                 (nvram->flags1 & TEKRAM_F2_F6_ENABLED)  ? " F2_F6"      :"",
226                 rem, boot_delay, tags);
227
228         /* display Tekram nvram drive data */
229         for (i = 0; i <= 15; i++) {
230                 int sync, j;
231                 struct Tekram_target *tn = &nvram->target[i];
232                 j = tn->sync_index & 0xf;
233                 sync = Tekram_sync[j];
234                 printf("%s-%d:%s%s%s%s%s%s PERIOD=%d\n",
235                 sym_name(np), i,
236                 (tn->flags & TEKRAM_PARITY_CHECK)       ? " PARITY"     : "",
237                 (tn->flags & TEKRAM_SYNC_NEGO)          ? " SYNC"       : "",
238                 (tn->flags & TEKRAM_DISCONNECT_ENABLE)  ? " DISC"       : "",
239                 (tn->flags & TEKRAM_START_CMD)          ? " START"      : "",
240                 (tn->flags & TEKRAM_TAGGED_COMMANDS)    ? " TCQ"        : "",
241                 (tn->flags & TEKRAM_WIDE_NEGO)          ? " WIDE"       : "",
242                 sync);
243         }
244 }
245 #else
246 static void sym_display_Symbios_nvram(struct sym_device *np, Symbios_nvram *nvram) { (void)np; (void)nvram; }
247 static void sym_display_Tekram_nvram(struct sym_device *np, Tekram_nvram *nvram) { (void)np; (void)nvram; }
248 #endif  /* SYM_CONF_DEBUG_NVRAM */
249
250
251 /*
252  *  24C16 EEPROM reading.
253  *
254  *  GPOI0 - data in/data out
255  *  GPIO1 - clock
256  *  Symbios NVRAM wiring now also used by Tekram.
257  */
258
259 #define SET_BIT 0
260 #define CLR_BIT 1
261 #define SET_CLK 2
262 #define CLR_CLK 3
263
264 /*
265  *  Set/clear data/clock bit in GPIO0
266  */
267 static void S24C16_set_bit(struct sym_device *np, u_char write_bit, u_char *gpreg, 
268                           int bit_mode)
269 {
270         UDELAY (5);
271         switch (bit_mode) {
272         case SET_BIT:
273                 *gpreg |= write_bit;
274                 break;
275         case CLR_BIT:
276                 *gpreg &= 0xfe;
277                 break;
278         case SET_CLK:
279                 *gpreg |= 0x02;
280                 break;
281         case CLR_CLK:
282                 *gpreg &= 0xfd;
283                 break;
284
285         }
286         OUTB (nc_gpreg, *gpreg);
287         UDELAY (5);
288 }
289
290 /*
291  *  Send START condition to NVRAM to wake it up.
292  */
293 static void S24C16_start(struct sym_device *np, u_char *gpreg)
294 {
295         S24C16_set_bit(np, 1, gpreg, SET_BIT);
296         S24C16_set_bit(np, 0, gpreg, SET_CLK);
297         S24C16_set_bit(np, 0, gpreg, CLR_BIT);
298         S24C16_set_bit(np, 0, gpreg, CLR_CLK);
299 }
300
301 /*
302  *  Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!!
303  */
304 static void S24C16_stop(struct sym_device *np, u_char *gpreg)
305 {
306         S24C16_set_bit(np, 0, gpreg, SET_CLK);
307         S24C16_set_bit(np, 1, gpreg, SET_BIT);
308 }
309
310 /*
311  *  Read or write a bit to the NVRAM,
312  *  read if GPIO0 input else write if GPIO0 output
313  */
314 static void S24C16_do_bit(struct sym_device *np, u_char *read_bit, u_char write_bit, 
315                          u_char *gpreg)
316 {
317         S24C16_set_bit(np, write_bit, gpreg, SET_BIT);
318         S24C16_set_bit(np, 0, gpreg, SET_CLK);
319         if (read_bit)
320                 *read_bit = INB (nc_gpreg);
321         S24C16_set_bit(np, 0, gpreg, CLR_CLK);
322         S24C16_set_bit(np, 0, gpreg, CLR_BIT);
323 }
324
325 /*
326  *  Output an ACK to the NVRAM after reading,
327  *  change GPIO0 to output and when done back to an input
328  */
329 static void S24C16_write_ack(struct sym_device *np, u_char write_bit, u_char *gpreg, 
330                             u_char *gpcntl)
331 {
332         OUTB (nc_gpcntl, *gpcntl & 0xfe);
333         S24C16_do_bit(np, 0, write_bit, gpreg);
334         OUTB (nc_gpcntl, *gpcntl);
335 }
336
337 /*
338  *  Input an ACK from NVRAM after writing,
339  *  change GPIO0 to input and when done back to an output
340  */
341 static void S24C16_read_ack(struct sym_device *np, u_char *read_bit, u_char *gpreg, 
342                            u_char *gpcntl)
343 {
344         OUTB (nc_gpcntl, *gpcntl | 0x01);
345         S24C16_do_bit(np, read_bit, 1, gpreg);
346         OUTB (nc_gpcntl, *gpcntl);
347 }
348
349 /*
350  *  WRITE a byte to the NVRAM and then get an ACK to see it was accepted OK,
351  *  GPIO0 must already be set as an output
352  */
353 static void S24C16_write_byte(struct sym_device *np, u_char *ack_data, u_char write_data, 
354                              u_char *gpreg, u_char *gpcntl)
355 {
356         int x;
357         
358         for (x = 0; x < 8; x++)
359                 S24C16_do_bit(np, 0, (write_data >> (7 - x)) & 0x01, gpreg);
360                 
361         S24C16_read_ack(np, ack_data, gpreg, gpcntl);
362 }
363
364 /*
365  *  READ a byte from the NVRAM and then send an ACK to say we have got it,
366  *  GPIO0 must already be set as an input
367  */
368 static void S24C16_read_byte(struct sym_device *np, u_char *read_data, u_char ack_data, 
369                             u_char *gpreg, u_char *gpcntl)
370 {
371         int x;
372         u_char read_bit;
373
374         *read_data = 0;
375         for (x = 0; x < 8; x++) {
376                 S24C16_do_bit(np, &read_bit, 1, gpreg);
377                 *read_data |= ((read_bit & 0x01) << (7 - x));
378         }
379
380         S24C16_write_ack(np, ack_data, gpreg, gpcntl);
381 }
382
383 #if SYM_CONF_NVRAM_WRITE_SUPPORT
384 /*
385  *  Write 'len' bytes starting at 'offset'.
386  */
387 static int sym_write_S24C16_nvram(struct sym_device *np, int offset,
388                 u_char *data, int len)
389 {
390         u_char  gpcntl, gpreg;
391         u_char  old_gpcntl, old_gpreg;
392         u_char  ack_data;
393         int     x;
394
395         /* save current state of GPCNTL and GPREG */
396         old_gpreg       = INB (nc_gpreg);
397         old_gpcntl      = INB (nc_gpcntl);
398         gpcntl          = old_gpcntl & 0x1c;
399
400         /* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */
401         OUTB (nc_gpreg,  old_gpreg);
402         OUTB (nc_gpcntl, gpcntl);
403
404         /* this is to set NVRAM into a known state with GPIO0/1 both low */
405         gpreg = old_gpreg;
406         S24C16_set_bit(np, 0, &gpreg, CLR_CLK);
407         S24C16_set_bit(np, 0, &gpreg, CLR_BIT);
408                 
409         /* now set NVRAM inactive with GPIO0/1 both high */
410         S24C16_stop(np, &gpreg);
411
412         /* NVRAM has to be written in segments of 16 bytes */
413         for (x = 0; x < len ; x += 16) {
414                 do {
415                         S24C16_start(np, &gpreg);
416                         S24C16_write_byte(np, &ack_data,
417                                           0xa0 | (((offset+x) >> 7) & 0x0e),
418                                           &gpreg, &gpcntl);
419                 } while (ack_data & 0x01);
420
421                 S24C16_write_byte(np, &ack_data, (offset+x) & 0xff, 
422                                   &gpreg, &gpcntl);
423
424                 for (y = 0; y < 16; y++)
425                         S24C16_write_byte(np, &ack_data, data[x+y], 
426                                           &gpreg, &gpcntl);
427                 S24C16_stop(np, &gpreg);
428         }
429
430         /* return GPIO0/1 to original states after having accessed NVRAM */
431         OUTB (nc_gpcntl, old_gpcntl);
432         OUTB (nc_gpreg,  old_gpreg);
433
434         return 0;
435 }
436 #endif /* SYM_CONF_NVRAM_WRITE_SUPPORT */
437
438 /*
439  *  Read 'len' bytes starting at 'offset'.
440  */
441 static int sym_read_S24C16_nvram(struct sym_device *np, int offset, u_char *data, int len)
442 {
443         u_char  gpcntl, gpreg;
444         u_char  old_gpcntl, old_gpreg;
445         u_char  ack_data;
446         int     retv = 1;
447         int     x;
448
449         /* save current state of GPCNTL and GPREG */
450         old_gpreg       = INB (nc_gpreg);
451         old_gpcntl      = INB (nc_gpcntl);
452         gpcntl          = old_gpcntl & 0x1c;
453
454         /* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */
455         OUTB (nc_gpreg,  old_gpreg);
456         OUTB (nc_gpcntl, gpcntl);
457
458         /* this is to set NVRAM into a known state with GPIO0/1 both low */
459         gpreg = old_gpreg;
460         S24C16_set_bit(np, 0, &gpreg, CLR_CLK);
461         S24C16_set_bit(np, 0, &gpreg, CLR_BIT);
462                 
463         /* now set NVRAM inactive with GPIO0/1 both high */
464         S24C16_stop(np, &gpreg);
465         
466         /* activate NVRAM */
467         S24C16_start(np, &gpreg);
468
469         /* write device code and random address MSB */
470         S24C16_write_byte(np, &ack_data,
471                 0xa0 | ((offset >> 7) & 0x0e), &gpreg, &gpcntl);
472         if (ack_data & 0x01)
473                 goto out;
474
475         /* write random address LSB */
476         S24C16_write_byte(np, &ack_data,
477                 offset & 0xff, &gpreg, &gpcntl);
478         if (ack_data & 0x01)
479                 goto out;
480
481         /* regenerate START state to set up for reading */
482         S24C16_start(np, &gpreg);
483         
484         /* rewrite device code and address MSB with read bit set (lsb = 0x01) */
485         S24C16_write_byte(np, &ack_data,
486                 0xa1 | ((offset >> 7) & 0x0e), &gpreg, &gpcntl);
487         if (ack_data & 0x01)
488                 goto out;
489
490         /* now set up GPIO0 for inputting data */
491         gpcntl |= 0x01;
492         OUTB (nc_gpcntl, gpcntl);
493                 
494         /* input all requested data - only part of total NVRAM */
495         for (x = 0; x < len; x++) 
496                 S24C16_read_byte(np, &data[x], (x == (len-1)), &gpreg, &gpcntl);
497
498         /* finally put NVRAM back in inactive mode */
499         gpcntl &= 0xfe;
500         OUTB (nc_gpcntl, gpcntl);
501         S24C16_stop(np, &gpreg);
502         retv = 0;
503 out:
504         /* return GPIO0/1 to original states after having accessed NVRAM */
505         OUTB (nc_gpcntl, old_gpcntl);
506         OUTB (nc_gpreg,  old_gpreg);
507
508         return retv;
509 }
510
511 #undef SET_BIT
512 #undef CLR_BIT
513 #undef SET_CLK
514 #undef CLR_CLK
515
516 /*
517  *  Try reading Symbios NVRAM.
518  *  Return 0 if OK.
519  */
520 static int sym_read_Symbios_nvram(struct sym_device *np, Symbios_nvram *nvram)
521 {
522         static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0};
523         u_char *data = (u_char *) nvram;
524         int len  = sizeof(*nvram);
525         u_short csum;
526         int x;
527
528         /* probe the 24c16 and read the SYMBIOS 24c16 area */
529         if (sym_read_S24C16_nvram (np, SYMBIOS_NVRAM_ADDRESS, data, len))
530                 return 1;
531
532         /* check valid NVRAM signature, verify byte count and checksum */
533         if (nvram->type != 0 ||
534             memcmp(nvram->trailer, Symbios_trailer, 6) ||
535             nvram->byte_count != len - 12)
536                 return 1;
537
538         /* verify checksum */
539         for (x = 6, csum = 0; x < len - 6; x++)
540                 csum += data[x];
541         if (csum != nvram->checksum)
542                 return 1;
543
544         return 0;
545 }
546
547 /*
548  *  93C46 EEPROM reading.
549  *
550  *  GPOI0 - data in
551  *  GPIO1 - data out
552  *  GPIO2 - clock
553  *  GPIO4 - chip select
554  *
555  *  Used by Tekram.
556  */
557
558 /*
559  *  Pulse clock bit in GPIO0
560  */
561 static void T93C46_Clk(struct sym_device *np, u_char *gpreg)
562 {
563         OUTB (nc_gpreg, *gpreg | 0x04);
564         UDELAY (2);
565         OUTB (nc_gpreg, *gpreg);
566 }
567
568 /* 
569  *  Read bit from NVRAM
570  */
571 static void T93C46_Read_Bit(struct sym_device *np, u_char *read_bit, u_char *gpreg)
572 {
573         UDELAY (2);
574         T93C46_Clk(np, gpreg);
575         *read_bit = INB (nc_gpreg);
576 }
577
578 /*
579  *  Write bit to GPIO0
580  */
581 static void T93C46_Write_Bit(struct sym_device *np, u_char write_bit, u_char *gpreg)
582 {
583         if (write_bit & 0x01)
584                 *gpreg |= 0x02;
585         else
586                 *gpreg &= 0xfd;
587                 
588         *gpreg |= 0x10;
589                 
590         OUTB (nc_gpreg, *gpreg);
591         UDELAY (2);
592
593         T93C46_Clk(np, gpreg);
594 }
595
596 /*
597  *  Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!!
598  */
599 static void T93C46_Stop(struct sym_device *np, u_char *gpreg)
600 {
601         *gpreg &= 0xef;
602         OUTB (nc_gpreg, *gpreg);
603         UDELAY (2);
604
605         T93C46_Clk(np, gpreg);
606 }
607
608 /*
609  *  Send read command and address to NVRAM
610  */
611 static void T93C46_Send_Command(struct sym_device *np, u_short write_data, 
612                                 u_char *read_bit, u_char *gpreg)
613 {
614         int x;
615
616         /* send 9 bits, start bit (1), command (2), address (6)  */
617         for (x = 0; x < 9; x++)
618                 T93C46_Write_Bit(np, (u_char) (write_data >> (8 - x)), gpreg);
619
620         *read_bit = INB (nc_gpreg);
621 }
622
623 /*
624  *  READ 2 bytes from the NVRAM
625  */
626 static void T93C46_Read_Word(struct sym_device *np,
627                 unsigned short *nvram_data, unsigned char *gpreg)
628 {
629         int x;
630         u_char read_bit;
631
632         *nvram_data = 0;
633         for (x = 0; x < 16; x++) {
634                 T93C46_Read_Bit(np, &read_bit, gpreg);
635
636                 if (read_bit & 0x01)
637                         *nvram_data |=  (0x01 << (15 - x));
638                 else
639                         *nvram_data &= ~(0x01 << (15 - x));
640         }
641 }
642
643 /*
644  *  Read Tekram NvRAM data.
645  */
646 static int T93C46_Read_Data(struct sym_device *np, unsigned short *data,
647                 int len, unsigned char *gpreg)
648 {
649         int x;
650
651         for (x = 0; x < len; x++)  {
652                 unsigned char read_bit;
653                 /* output read command and address */
654                 T93C46_Send_Command(np, 0x180 | x, &read_bit, gpreg);
655                 if (read_bit & 0x01)
656                         return 1; /* Bad */
657                 T93C46_Read_Word(np, &data[x], gpreg);
658                 T93C46_Stop(np, gpreg);
659         }
660
661         return 0;
662 }
663
664 /*
665  *  Try reading 93C46 Tekram NVRAM.
666  */
667 static int sym_read_T93C46_nvram(struct sym_device *np, Tekram_nvram *nvram)
668 {
669         u_char gpcntl, gpreg;
670         u_char old_gpcntl, old_gpreg;
671         int retv = 1;
672
673         /* save current state of GPCNTL and GPREG */
674         old_gpreg       = INB (nc_gpreg);
675         old_gpcntl      = INB (nc_gpcntl);
676
677         /* set up GPREG & GPCNTL to set GPIO0/1/2/4 in to known state, 0 in,
678            1/2/4 out */
679         gpreg = old_gpreg & 0xe9;
680         OUTB (nc_gpreg, gpreg);
681         gpcntl = (old_gpcntl & 0xe9) | 0x09;
682         OUTB (nc_gpcntl, gpcntl);
683
684         /* input all of NVRAM, 64 words */
685         retv = T93C46_Read_Data(np, (u_short *) nvram,
686                                 sizeof(*nvram) / sizeof(short), &gpreg);
687         
688         /* return GPIO0/1/2/4 to original states after having accessed NVRAM */
689         OUTB (nc_gpcntl, old_gpcntl);
690         OUTB (nc_gpreg,  old_gpreg);
691
692         return retv;
693 }
694
695 /*
696  *  Try reading Tekram NVRAM.
697  *  Return 0 if OK.
698  */
699 static int sym_read_Tekram_nvram (struct sym_device *np, Tekram_nvram *nvram)
700 {
701         u_char *data = (u_char *) nvram;
702         int len = sizeof(*nvram);
703         u_short csum;
704         int x;
705
706         switch (np->device_id) {
707         case PCI_DEVICE_ID_NCR_53C885:
708         case PCI_DEVICE_ID_NCR_53C895:
709         case PCI_DEVICE_ID_NCR_53C896:
710                 x = sym_read_S24C16_nvram(np, TEKRAM_24C16_NVRAM_ADDRESS,
711                                           data, len);
712                 break;
713         case PCI_DEVICE_ID_NCR_53C875:
714                 x = sym_read_S24C16_nvram(np, TEKRAM_24C16_NVRAM_ADDRESS,
715                                           data, len);
716                 if (!x)
717                         break;
718         default:
719                 x = sym_read_T93C46_nvram(np, nvram);
720                 break;
721         }
722         if (x)
723                 return 1;
724
725         /* verify checksum */
726         for (x = 0, csum = 0; x < len - 1; x += 2)
727                 csum += data[x] + (data[x+1] << 8);
728         if (csum != 0x1234)
729                 return 1;
730
731         return 0;
732 }
733
734 /*
735  *  Try reading Symbios or Tekram NVRAM
736  */
737 int sym_read_nvram(struct sym_device *np, struct sym_nvram *nvp)
738 {
739         if (!sym_read_Symbios_nvram(np, &nvp->data.Symbios)) {
740                 nvp->type = SYM_SYMBIOS_NVRAM;
741                 sym_display_Symbios_nvram(np, &nvp->data.Symbios);
742         } else if (!sym_read_Tekram_nvram(np, &nvp->data.Tekram)) {
743                 nvp->type = SYM_TEKRAM_NVRAM;
744                 sym_display_Tekram_nvram(np, &nvp->data.Tekram);
745         } else {
746                 nvp->type = 0;
747         }
748         return nvp->type;
749 }