Commit | Line | Data |
---|---|---|
50ee11fe BB |
1 | /* pmc93x6_eeprom.c - PMC's 93LC46 EEPROM Device |
2 | * | |
3 | * The 93LC46 is a low-power, serial Electrically Erasable and | |
4 | * Programmable Read Only Memory organized as 128 8-bit bytes. | |
5 | * | |
6 | * Accesses to the 93LC46 are done in a bit serial stream, organized | |
7 | * in a 3 wire format. Writes are internally timed by the device | |
8 | * (the In data bit is pulled low until the write is complete and | |
9 | * then is pulled high) and take about 6 milliseconds. | |
10 | * | |
11 | * Copyright (C) 2003-2005 SBE, Inc. | |
12 | * | |
13 | * This program is free software; you can redistribute it and/or modify | |
14 | * it under the terms of the GNU General Public License as published by | |
15 | * the Free Software Foundation; either version 2 of the License, or | |
16 | * (at your option) any later version. | |
17 | * | |
18 | * This program is distributed in the hope that it will be useful, | |
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | * GNU General Public License for more details. | |
22 | */ | |
23 | ||
694a9807 JP |
24 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
25 | ||
50ee11fe BB |
26 | #include <linux/types.h> |
27 | #include "pmcc4_sysdep.h" | |
28 | #include "sbecom_inline_linux.h" | |
29 | #include "pmcc4.h" | |
30 | #include "sbe_promformat.h" | |
31 | ||
32 | #ifndef TRUE | |
33 | #define TRUE 1 | |
34 | #define FALSE 0 | |
35 | #endif | |
36 | ||
37 | #ifdef SBE_INCLUDE_SYMBOLS | |
38 | #define STATIC | |
39 | #else | |
40 | #define STATIC static | |
41 | #endif | |
42 | ||
43 | ||
44 | /*------------------------------------------------------------------------ | |
45 | * EEPROM address definitions | |
46 | *------------------------------------------------------------------------ | |
47 | * | |
48 | * The offset in the definitions below allows the test to skip over | |
49 | * areas of the EEPROM that other programs (such a VxWorks) are | |
50 | * using. | |
51 | */ | |
52 | ||
53 | #define EE_MFG (long)0 /* Index to manufacturing record */ | |
54 | #define EE_FIRST 0x28 /* Index to start testing at */ | |
55 | #define EE_LIMIT 128 /* Index to end testing at */ | |
56 | ||
57 | ||
58 | /* Bit Ordering for Instructions | |
59 | ** | |
60 | ** A0, A1, A2, A3, A4, A5, A6, OP0, OP1, SB (lsb, or 1st bit out) | |
61 | ** | |
62 | */ | |
63 | ||
64 | #define EPROM_EWEN 0x0019 /* Erase/Write enable (reversed) */ | |
65 | #define EPROM_EWDS 0x0001 /* Erase/Write disable (reversed) */ | |
66 | #define EPROM_READ 0x0003 /* Read (reversed) */ | |
67 | #define EPROM_WRITE 0x0005 /* Write (reversed) */ | |
68 | #define EPROM_ERASE 0x0007 /* Erase (reversed) */ | |
69 | #define EPROM_ERAL 0x0009 /* Erase All (reversed) */ | |
70 | #define EPROM_WRAL 0x0011 /* Write All (reversed) */ | |
71 | ||
72 | #define EPROM_ADR_SZ 7 /* Number of bits in offset address */ | |
73 | #define EPROM_OP_SZ 3 /* Number of bits in command */ | |
74 | #define SIZE_ADDR_OP (EPROM_ADR_SZ + EPROM_OP_SZ) | |
75 | #define LC46A_MAX_OPS 10 /* Number of bits in Instruction */ | |
76 | #define NUM_OF_BITS 8 /* Number of bits in data */ | |
77 | ||
78 | ||
79 | /* EEPROM signal bits */ | |
80 | #define EPROM_ACTIVE_OUT_BIT 0x0001 /* Out data bit */ | |
81 | #define EPROM_ACTIVE_IN_BIT 0x0002 /* In data bit */ | |
82 | #define ACTIVE_IN_BIT_SHIFT 0x0001 /* Shift In data bit to LSB */ | |
83 | #define EPROM_ENCS 0x0004 /* Set EEPROM CS during operation */ | |
84 | ||
85 | ||
86 | /*------------------------------------------------------------------------ | |
87 | * The ByteReverse table is used to reverses the 8 bits within a byte | |
88 | *------------------------------------------------------------------------ | |
89 | */ | |
90 | ||
91 | static unsigned char ByteReverse[256]; | |
92 | static int ByteReverseBuilt = FALSE; | |
93 | ||
94 | ||
95 | /*------------------------------------------------------------------------ | |
96 | * mfg_template - initial serial EEPROM data structure | |
97 | *------------------------------------------------------------------------ | |
98 | */ | |
99 | ||
100 | short mfg_template[sizeof (FLD_TYPE2)] = | |
101 | { | |
102 | PROM_FORMAT_TYPE2, /* type; */ | |
103 | 0x00, 0x1A, /* length[2]; */ | |
104 | 0x00, 0x00, 0x00, 0x00, /* Crc32[4]; */ | |
105 | 0x11, 0x76, /* Id[2]; */ | |
106 | 0x07, 0x05, /* SubId[2] E1; */ | |
107 | 0x00, 0xA0, 0xD6, 0x00, 0x00, 0x00, /* Serial[6]; */ | |
108 | 0x00, 0x00, 0x00, 0x00, /* CreateTime[4]; */ | |
109 | 0x00, 0x00, 0x00, 0x00, /* HeatRunTime[4]; */ | |
110 | 0x00, 0x00, 0x00, 0x00, /* HeatRunIterations[4]; */ | |
111 | 0x00, 0x00, 0x00, 0x00, /* HeatRunErrors[4]; */ | |
112 | }; | |
113 | ||
114 | ||
115 | /*------------------------------------------------------------------------ | |
116 | * BuildByteReverse - build the 8-bit reverse table | |
117 | *------------------------------------------------------------------------ | |
118 | * | |
119 | * The 'ByteReverse' table reverses the 8 bits within a byte | |
120 | * (the MSB becomes the LSB etc.). | |
121 | */ | |
122 | ||
123 | STATIC void | |
124 | BuildByteReverse (void) | |
125 | { | |
126 | long half; /* Used to build by powers to 2 */ | |
127 | int i; | |
128 | ||
129 | ByteReverse[0] = 0; | |
130 | ||
131 | for (half = 1; half < sizeof (ByteReverse); half <<= 1) | |
132 | for (i = 0; i < half; i++) | |
133 | ByteReverse[half + i] = (char) (ByteReverse[i] | (0x80 / half)); | |
134 | ||
135 | ByteReverseBuilt = TRUE; | |
136 | } | |
137 | ||
138 | ||
139 | /*------------------------------------------------------------------------ | |
140 | * eeprom_delay - small delay for EEPROM timing | |
141 | *------------------------------------------------------------------------ | |
142 | */ | |
143 | ||
144 | STATIC void | |
145 | eeprom_delay (void) | |
146 | { | |
147 | int timeout; | |
148 | ||
149 | for (timeout = 20; timeout; --timeout) | |
150 | { | |
151 | OS_uwait_dummy (); | |
152 | } | |
153 | } | |
154 | ||
155 | ||
156 | /*------------------------------------------------------------------------ | |
157 | * eeprom_put_byte - Send a byte to the EEPROM serially | |
158 | *------------------------------------------------------------------------ | |
159 | * | |
160 | * Given the PCI address and the data, this routine serially sends | |
161 | * the data to the EEPROM. | |
162 | */ | |
163 | ||
164 | void | |
165 | eeprom_put_byte (long addr, long data, int count) | |
166 | { | |
167 | u_int32_t output; | |
168 | ||
169 | while (--count >= 0) | |
170 | { | |
171 | output = (data & EPROM_ACTIVE_OUT_BIT) ? 1 : 0; /* Get next data bit */ | |
172 | output |= EPROM_ENCS; /* Add Chip Select */ | |
173 | data >>= 1; | |
174 | ||
175 | eeprom_delay (); | |
176 | pci_write_32 ((u_int32_t *) addr, output); /* Output it */ | |
177 | } | |
178 | } | |
179 | ||
180 | ||
181 | /*------------------------------------------------------------------------ | |
182 | * eeprom_get_byte - Receive a byte from the EEPROM serially | |
183 | *------------------------------------------------------------------------ | |
184 | * | |
185 | * Given the PCI address, this routine serially fetches the data | |
186 | * from the EEPROM. | |
187 | */ | |
188 | ||
189 | u_int32_t | |
190 | eeprom_get_byte (long addr) | |
191 | { | |
192 | u_int32_t input; | |
193 | u_int32_t data; | |
194 | int count; | |
195 | ||
196 | /* Start the Reading of DATA | |
197 | ** | |
198 | ** The first read is a dummy as the data is latched in the | |
199 | ** EPLD and read on the next read access to the EEPROM. | |
200 | */ | |
201 | ||
202 | input = pci_read_32 ((u_int32_t *) addr); | |
203 | ||
204 | data = 0; | |
205 | count = NUM_OF_BITS; | |
206 | while (--count >= 0) | |
207 | { | |
208 | eeprom_delay (); | |
209 | input = pci_read_32 ((u_int32_t *) addr); | |
210 | ||
211 | data <<= 1; /* Shift data over */ | |
212 | data |= (input & EPROM_ACTIVE_IN_BIT) ? 1 : 0; | |
213 | ||
214 | } | |
215 | ||
216 | return data; | |
217 | } | |
218 | ||
219 | ||
220 | /*------------------------------------------------------------------------ | |
221 | * disable_pmc_eeprom - Disable writes to the EEPROM | |
222 | *------------------------------------------------------------------------ | |
223 | * | |
224 | * Issue the EEPROM command to disable writes. | |
225 | */ | |
226 | ||
227 | STATIC void | |
228 | disable_pmc_eeprom (long addr) | |
229 | { | |
230 | eeprom_put_byte (addr, EPROM_EWDS, SIZE_ADDR_OP); | |
231 | ||
232 | pci_write_32 ((u_int32_t *) addr, 0); /* this removes Chip Select | |
233 | * from EEPROM */ | |
234 | } | |
235 | ||
236 | ||
237 | /*------------------------------------------------------------------------ | |
238 | * enable_pmc_eeprom - Enable writes to the EEPROM | |
239 | *------------------------------------------------------------------------ | |
240 | * | |
241 | * Issue the EEPROM command to enable writes. | |
242 | */ | |
243 | ||
244 | STATIC void | |
245 | enable_pmc_eeprom (long addr) | |
246 | { | |
247 | eeprom_put_byte (addr, EPROM_EWEN, SIZE_ADDR_OP); | |
248 | ||
249 | pci_write_32 ((u_int32_t *) addr, 0); /* this removes Chip Select | |
250 | * from EEPROM */ | |
251 | } | |
252 | ||
253 | ||
254 | /*------------------------------------------------------------------------ | |
255 | * pmc_eeprom_read - EEPROM location read | |
256 | *------------------------------------------------------------------------ | |
257 | * | |
258 | * Given a EEPROM PCI address and location offset, this routine returns | |
259 | * the contents of the specified location to the calling routine. | |
260 | */ | |
261 | ||
262 | u_int32_t | |
263 | pmc_eeprom_read (long addr, long mem_offset) | |
264 | { | |
265 | u_int32_t data; /* Data from chip */ | |
266 | ||
267 | if (!ByteReverseBuilt) | |
268 | BuildByteReverse (); | |
269 | ||
270 | mem_offset = ByteReverse[0x7F & mem_offset]; /* Reverse address */ | |
271 | /* | |
272 | * NOTE: The max offset address is 128 or half the reversal table. So the | |
273 | * LSB is always zero and counts as a built in shift of one bit. So even | |
274 | * though we need to shift 3 bits to make room for the command, we only | |
275 | * need to shift twice more because of the built in shift. | |
276 | */ | |
277 | mem_offset <<= 2; /* Shift for command */ | |
278 | mem_offset |= EPROM_READ; /* Add command */ | |
279 | ||
280 | eeprom_put_byte (addr, mem_offset, SIZE_ADDR_OP); /* Output chip address */ | |
281 | ||
282 | data = eeprom_get_byte (addr); /* Read chip data */ | |
283 | ||
284 | pci_write_32 ((u_int32_t *) addr, 0); /* Remove Chip Select from | |
285 | * EEPROM */ | |
286 | ||
287 | return (data & 0x000000FF); | |
288 | } | |
289 | ||
290 | ||
291 | /*------------------------------------------------------------------------ | |
292 | * pmc_eeprom_write - EEPROM location write | |
293 | *------------------------------------------------------------------------ | |
294 | * | |
295 | * Given a EEPROM PCI address, location offset and value, this | |
296 | * routine writes the value to the specified location. | |
297 | * | |
298 | * Note: it is up to the caller to determine if the write | |
299 | * operation succeeded. | |
300 | */ | |
301 | ||
302 | int | |
303 | pmc_eeprom_write (long addr, long mem_offset, u_int32_t data) | |
304 | { | |
305 | volatile u_int32_t temp; | |
306 | int count; | |
307 | ||
308 | if (!ByteReverseBuilt) | |
309 | BuildByteReverse (); | |
310 | ||
311 | mem_offset = ByteReverse[0x7F & mem_offset]; /* Reverse address */ | |
312 | /* | |
313 | * NOTE: The max offset address is 128 or half the reversal table. So the | |
314 | * LSB is always zero and counts as a built in shift of one bit. So even | |
315 | * though we need to shift 3 bits to make room for the command, we only | |
316 | * need to shift twice more because of the built in shift. | |
317 | */ | |
318 | mem_offset <<= 2; /* Shift for command */ | |
319 | mem_offset |= EPROM_WRITE; /* Add command */ | |
320 | ||
321 | eeprom_put_byte (addr, mem_offset, SIZE_ADDR_OP); /* Output chip address */ | |
322 | ||
323 | data = ByteReverse[0xFF & data];/* Reverse data */ | |
324 | eeprom_put_byte (addr, data, NUM_OF_BITS); /* Output chip data */ | |
325 | ||
326 | pci_write_32 ((u_int32_t *) addr, 0); /* Remove Chip Select from | |
327 | * EEPROM */ | |
328 | ||
329 | /* | |
330 | ** Must see Data In at a low state before completing this transaction. | |
331 | ** | |
332 | ** Afterwards, the data bit will return to a high state, ~6 ms, terminating | |
333 | ** the operation. | |
334 | */ | |
335 | pci_write_32 ((u_int32_t *) addr, EPROM_ENCS); /* Re-enable Chip Select */ | |
336 | temp = pci_read_32 ((u_int32_t *) addr); /* discard first read */ | |
337 | temp = pci_read_32 ((u_int32_t *) addr); | |
338 | if (temp & EPROM_ACTIVE_IN_BIT) | |
339 | { | |
340 | temp = pci_read_32 ((u_int32_t *) addr); | |
341 | if (temp & EPROM_ACTIVE_IN_BIT) | |
342 | { | |
343 | pci_write_32 ((u_int32_t *) addr, 0); /* Remove Chip Select | |
344 | * from EEPROM */ | |
345 | return (1); | |
346 | } | |
347 | } | |
348 | count = 1000; | |
349 | while (count--) | |
350 | { | |
351 | for (temp = 0; temp < 0x10; temp++) | |
352 | OS_uwait_dummy (); | |
353 | ||
354 | if (pci_read_32 ((u_int32_t *) addr) & EPROM_ACTIVE_IN_BIT) | |
355 | break; | |
356 | } | |
357 | ||
358 | if (count == -1) | |
359 | return (2); | |
360 | ||
361 | return (0); | |
362 | } | |
363 | ||
364 | ||
365 | /*------------------------------------------------------------------------ | |
366 | * pmcGetBuffValue - read the specified value from buffer | |
367 | *------------------------------------------------------------------------ | |
368 | */ | |
369 | ||
370 | long | |
371 | pmcGetBuffValue (char *ptr, int size) | |
372 | { | |
373 | long value = 0; | |
374 | int index; | |
375 | ||
376 | for (index = 0; index < size; ++index) | |
377 | { | |
378 | value <<= 8; | |
379 | value |= ptr[index] & 0xFF; | |
380 | } | |
381 | ||
382 | return value; | |
383 | } | |
384 | ||
385 | ||
386 | /*------------------------------------------------------------------------ | |
387 | * pmcSetBuffValue - save the specified value to buffer | |
388 | *------------------------------------------------------------------------ | |
389 | */ | |
390 | ||
391 | void | |
392 | pmcSetBuffValue (char *ptr, long value, int size) | |
393 | { | |
394 | int index = size; | |
395 | ||
396 | while (--index >= 0) | |
397 | { | |
398 | ptr[index] = (char) (value & 0xFF); | |
399 | value >>= 8; | |
400 | } | |
401 | } | |
402 | ||
403 | ||
404 | /*------------------------------------------------------------------------ | |
405 | * pmc_eeprom_read_buffer - read EEPROM data into specified buffer | |
406 | *------------------------------------------------------------------------ | |
407 | */ | |
408 | ||
409 | void | |
410 | pmc_eeprom_read_buffer (long addr, long mem_offset, char *dest_ptr, int size) | |
411 | { | |
412 | while (--size >= 0) | |
413 | *dest_ptr++ = (char) pmc_eeprom_read (addr, mem_offset++); | |
414 | } | |
415 | ||
416 | ||
417 | /*------------------------------------------------------------------------ | |
418 | * pmc_eeprom_write_buffer - write EEPROM data from specified buffer | |
419 | *------------------------------------------------------------------------ | |
420 | */ | |
421 | ||
422 | void | |
423 | pmc_eeprom_write_buffer (long addr, long mem_offset, char *dest_ptr, int size) | |
424 | { | |
425 | enable_pmc_eeprom (addr); | |
426 | ||
427 | while (--size >= 0) | |
428 | pmc_eeprom_write (addr, mem_offset++, *dest_ptr++); | |
429 | ||
430 | disable_pmc_eeprom (addr); | |
431 | } | |
432 | ||
433 | ||
434 | /*------------------------------------------------------------------------ | |
435 | * pmcCalcCrc - calculate the CRC for the serial EEPROM structure | |
436 | *------------------------------------------------------------------------ | |
437 | */ | |
438 | ||
439 | u_int32_t | |
440 | pmcCalcCrc_T01 (void *bufp) | |
441 | { | |
442 | FLD_TYPE2 *buf = bufp; | |
443 | u_int32_t crc; /* CRC of the structure */ | |
444 | ||
445 | /* Calc CRC for type and length fields */ | |
446 | sbeCrc ( | |
447 | (u_int8_t *) &buf->type, | |
448 | (u_int32_t) STRUCT_OFFSET (FLD_TYPE1, Crc32), | |
449 | (u_int32_t) 0, | |
450 | (u_int32_t *) &crc); | |
451 | ||
452 | #ifdef EEPROM_TYPE_DEBUG | |
694a9807 | 453 | pr_info("sbeCrc: crc 1 calculated as %08x\n", crc); /* RLD DEBUG */ |
50ee11fe BB |
454 | #endif |
455 | return ~crc; | |
456 | } | |
457 | ||
458 | u_int32_t | |
459 | pmcCalcCrc_T02 (void *bufp) | |
460 | { | |
461 | FLD_TYPE2 *buf = bufp; | |
462 | u_int32_t crc; /* CRC of the structure */ | |
463 | ||
464 | /* Calc CRC for type and length fields */ | |
465 | sbeCrc ( | |
466 | (u_int8_t *) &buf->type, | |
467 | (u_int32_t) STRUCT_OFFSET (FLD_TYPE2, Crc32), | |
468 | (u_int32_t) 0, | |
469 | (u_int32_t *) &crc); | |
470 | ||
471 | /* Calc CRC for remaining fields */ | |
472 | sbeCrc ( | |
473 | (u_int8_t *) &buf->Id[0], | |
474 | (u_int32_t) (sizeof (FLD_TYPE2) - STRUCT_OFFSET (FLD_TYPE2, Id)), | |
475 | (u_int32_t) crc, | |
476 | (u_int32_t *) &crc); | |
477 | ||
478 | #ifdef EEPROM_TYPE_DEBUG | |
694a9807 | 479 | pr_info("sbeCrc: crc 2 calculated as %08x\n", crc); /* RLD DEBUG */ |
50ee11fe BB |
480 | #endif |
481 | return crc; | |
482 | } | |
483 | ||
484 | ||
485 | /*------------------------------------------------------------------------ | |
486 | * pmc_init_seeprom - initialize the serial EEPROM structure | |
487 | *------------------------------------------------------------------------ | |
488 | * | |
489 | * At the front of the serial EEPROM there is a record that contains | |
490 | * manufacturing information. If the info does not already exist, it | |
491 | * is created. The only field modifiable by the operator is the | |
492 | * serial number field. | |
493 | */ | |
494 | ||
495 | void | |
496 | pmc_init_seeprom (u_int32_t addr, u_int32_t serialNum) | |
497 | { | |
498 | PROMFORMAT buffer; /* Memory image of structure */ | |
499 | u_int32_t crc; /* CRC of structure */ | |
500 | time_t createTime; | |
501 | int i; | |
502 | ||
50ee11fe | 503 | createTime = get_seconds (); |
50ee11fe BB |
504 | |
505 | /* use template data */ | |
506 | for (i = 0; i < sizeof (FLD_TYPE2); ++i) | |
507 | buffer.bytes[i] = mfg_template[i]; | |
508 | ||
509 | /* Update serial number field in buffer */ | |
510 | pmcSetBuffValue (&buffer.fldType2.Serial[3], serialNum, 3); | |
511 | ||
512 | /* Update create time field in buffer */ | |
513 | pmcSetBuffValue (&buffer.fldType2.CreateTime[0], createTime, 4); | |
514 | ||
515 | /* Update CRC field in buffer */ | |
516 | crc = pmcCalcCrc_T02 (&buffer); | |
517 | pmcSetBuffValue (&buffer.fldType2.Crc32[0], crc, 4); | |
518 | ||
519 | #ifdef DEBUG | |
520 | for (i = 0; i < sizeof (FLD_TYPE2); ++i) | |
694a9807 | 521 | pr_info("[%02X] = %02X\n", i, buffer.bytes[i] & 0xFF); |
50ee11fe BB |
522 | #endif |
523 | ||
524 | /* Write structure to serial EEPROM */ | |
525 | pmc_eeprom_write_buffer (addr, EE_MFG, (char *) &buffer, sizeof (FLD_TYPE2)); | |
526 | } | |
527 | ||
528 | ||
529 | char | |
530 | pmc_verify_cksum (void *bufp) | |
531 | { | |
532 | FLD_TYPE1 *buf1 = bufp; | |
533 | FLD_TYPE2 *buf2 = bufp; | |
534 | u_int32_t crc1, crc2; /* CRC read from EEPROM */ | |
535 | ||
536 | /* Retrieve contents of CRC field */ | |
537 | crc1 = pmcGetBuffValue (&buf1->Crc32[0], sizeof (buf1->Crc32)); | |
538 | #ifdef EEPROM_TYPE_DEBUG | |
694a9807 | 539 | pr_info("EEPROM: chksum 1 reads as %08x\n", crc1); /* RLD DEBUG */ |
50ee11fe BB |
540 | #endif |
541 | if ((buf1->type == PROM_FORMAT_TYPE1) && | |
542 | (pmcCalcCrc_T01 ((void *) buf1) == crc1)) | |
543 | return PROM_FORMAT_TYPE1; /* checksum type 1 verified */ | |
544 | ||
545 | crc2 = pmcGetBuffValue (&buf2->Crc32[0], sizeof (buf2->Crc32)); | |
546 | #ifdef EEPROM_TYPE_DEBUG | |
694a9807 | 547 | pr_info("EEPROM: chksum 2 reads as %08x\n", crc2); /* RLD DEBUG */ |
50ee11fe BB |
548 | #endif |
549 | if ((buf2->type == PROM_FORMAT_TYPE2) && | |
550 | (pmcCalcCrc_T02 ((void *) buf2) == crc2)) | |
551 | return PROM_FORMAT_TYPE2; /* checksum type 2 verified */ | |
552 | ||
553 | return PROM_FORMAT_Unk; /* failed to validate */ | |
554 | } | |
555 | ||
556 | ||
557 | /*** End-of-File ***/ |