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