Commit | Line | Data |
---|---|---|
c906108c SS |
1 | /* This file is part of the program psim. |
2 | ||
3 | Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au> | |
4 | ||
5 | This program is free software; you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation; either version 2 of the License, or | |
8 | (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
16 | along with this program; if not, write to the Free Software | |
17 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
18 | ||
19 | */ | |
20 | ||
21 | ||
22 | #ifndef _HW_EEPROM_C_ | |
23 | #define _HW_EEPROM_C_ | |
24 | ||
25 | #include "device_table.h" | |
26 | ||
27 | #ifdef HAVE_STRING_H | |
28 | #include <string.h> | |
29 | #else | |
30 | #ifdef HAVE_STRINGS_H | |
31 | #include <strings.h> | |
32 | #endif | |
33 | #endif | |
34 | ||
35 | ||
36 | /* DEVICE | |
37 | ||
38 | ||
39 | eeprom - JEDEC? compatible electricaly erasable programable device | |
40 | ||
41 | ||
42 | DESCRIPTION | |
43 | ||
44 | ||
45 | This device implements a small byte addressable EEPROM. | |
46 | Programming is performed using the same write sequences as used by | |
47 | standard modern EEPROM components. Writes occure in real time, the | |
48 | device returning a progress value until the programing has been | |
49 | completed. | |
50 | ||
51 | It is based on the AMD 29F040 component. | |
52 | ||
53 | ||
54 | PROPERTIES | |
55 | ||
56 | ||
57 | reg = <address> <size> (required) | |
58 | ||
59 | Determine where the device lives in the parents address space. | |
60 | ||
61 | ||
62 | nr-sectors = <integer> (required) | |
63 | ||
64 | When erasing an entire sector is cleared at a time. This specifies | |
65 | the number of sectors in the EEPROM component. | |
66 | ||
67 | ||
68 | sector-size = <integer> (required) | |
69 | ||
70 | The number of bytes in a sector. When erasing, memory chunks of | |
71 | this size are cleared. | |
72 | ||
73 | NOTE: The product nr-sectors * sector-size does not need to map the | |
74 | size specified in the reg property. If the specified size is | |
75 | smaller part of the eeprom will not be accessible while if it is | |
76 | larger the addresses will wrap. | |
77 | ||
78 | ||
79 | byte-write-delay = <integer> (required) | |
80 | ||
81 | Number of clock ticks before the programming of a single byte | |
82 | completes. | |
83 | ||
84 | ||
85 | sector-start-delay = <integer> (required) | |
86 | ||
87 | When erasing sectors, the number of clock ticks after the sector | |
88 | has been specified that the actual erase process commences. | |
89 | ||
90 | ||
91 | erase-delay = <intger> (required) | |
92 | ||
93 | Number of clock ticks before an erase program completes | |
94 | ||
95 | ||
96 | manufacture-code = <integer> (required) | |
97 | ||
98 | The one byte value returned when the auto-select manufacturer code | |
99 | is read. | |
100 | ||
101 | ||
102 | device-code = <integer> (required) | |
103 | ||
104 | The one byte value returned when the auto-select device code is | |
105 | read. | |
106 | ||
107 | ||
108 | input-file = <file-name> (optional) | |
109 | ||
110 | Initialize the eeprom using the specified binary file. | |
111 | ||
112 | ||
113 | output-file = <file-name> (optional) | |
114 | ||
115 | When ever the eeprom is updated, save the modified image into the | |
116 | specified file. | |
117 | ||
118 | ||
119 | EXAMPLES | |
120 | ||
121 | ||
122 | Enable tracing of the eeprom: | |
123 | ||
124 | | bash$ psim -t eeprom-device \ | |
125 | ||
126 | ||
127 | Configure something very like the Amd Am29F040 - 512byte EEPROM | |
128 | (but a bit faster): | |
129 | ||
130 | | -o '/eeprom@0xfff00000/reg 0xfff00000 0x80000' \ | |
131 | | -o '/eeprom@0xfff00000/nr-sectors 8' \ | |
132 | | -o '/eeprom@0xfff00000/sector-size 0x10000' \ | |
133 | | -o '/eeprom@0xfff00000/byte-write-delay 1000' \ | |
134 | | -o '/eeprom@0xfff00000/sector-start-delay 100' \ | |
135 | | -o '/eeprom@0xfff00000/erase-delay 1000' \ | |
136 | | -o '/eeprom@0xfff00000/manufacture-code 0x01' \ | |
137 | | -o '/eeprom@0xfff00000/device-code 0xa4' \ | |
138 | ||
139 | ||
140 | Initialize the eeprom from the file <</dev/zero>>: | |
141 | ||
142 | | -o '/eeprom@0xfff00000/input-file /dev/zero' | |
143 | ||
144 | ||
145 | BUGS | |
146 | ||
147 | ||
148 | */ | |
149 | ||
150 | typedef enum { | |
151 | read_reset, | |
152 | write_nr_2, | |
153 | write_nr_3, | |
154 | write_nr_4, | |
155 | write_nr_5, | |
156 | write_nr_6, | |
157 | byte_program, | |
158 | byte_programming, | |
159 | chip_erase, | |
160 | sector_erase, | |
161 | sector_erase_suspend, | |
162 | autoselect, | |
163 | } hw_eeprom_states; | |
164 | ||
165 | static const char * | |
166 | state2a(hw_eeprom_states state) | |
167 | { | |
168 | switch (state) { | |
169 | case read_reset: return "read_reset"; | |
170 | case write_nr_2: return "write_nr_2"; | |
171 | case write_nr_3: return "write_nr_3"; | |
172 | case write_nr_4: return "write_nr_4"; | |
173 | case write_nr_5: return "write_nr_5"; | |
174 | case write_nr_6: return "write_nr_6"; | |
175 | case byte_program: return "byte_program"; | |
176 | case byte_programming: return "byte_programming"; | |
177 | case chip_erase: return "chip_erase"; | |
178 | case sector_erase: return "sector_erase"; | |
179 | case sector_erase_suspend: return "sector_erase_suspend"; | |
180 | case autoselect: return "autoselect"; | |
181 | } | |
182 | return NULL; | |
183 | } | |
184 | ||
185 | typedef struct _hw_eeprom_device { | |
186 | /* general */ | |
187 | hw_eeprom_states state; | |
188 | unsigned8 *memory; | |
189 | unsigned sizeof_memory; | |
190 | unsigned erase_delay; | |
191 | signed64 program_start_time; | |
192 | signed64 program_finish_time; | |
193 | unsigned8 manufacture_code; | |
194 | unsigned8 device_code; | |
195 | unsigned8 toggle_bit; | |
196 | /* initialization */ | |
197 | const char *input_file_name; | |
198 | const char *output_file_name; | |
199 | /* for sector and sector programming */ | |
200 | hw_eeprom_states sector_state; | |
201 | unsigned8 *sectors; | |
202 | unsigned nr_sectors; | |
203 | unsigned sizeof_sector; | |
204 | unsigned sector_start_delay; | |
205 | unsigned sector_start_time; | |
206 | /* byte and byte programming */ | |
207 | unsigned byte_write_delay; | |
208 | unsigned_word byte_program_address; | |
209 | unsigned8 byte_program_byte; | |
210 | } hw_eeprom_device; | |
211 | ||
212 | typedef struct _hw_eeprom_reg_spec { | |
213 | unsigned32 base; | |
214 | unsigned32 size; | |
215 | } hw_eeprom_reg_spec; | |
216 | ||
217 | static void | |
218 | hw_eeprom_init_data(device *me) | |
219 | { | |
220 | hw_eeprom_device *eeprom = (hw_eeprom_device*)device_data(me); | |
221 | ||
222 | /* have we any input or output files */ | |
223 | if (device_find_property(me, "input-file") != NULL) | |
224 | eeprom->input_file_name = device_find_string_property(me, "input-file"); | |
225 | if (device_find_property(me, "output-file") != NULL) | |
226 | eeprom->input_file_name = device_find_string_property(me, "output-file"); | |
227 | ||
228 | /* figure out the sectors in the eeprom */ | |
229 | if (eeprom->sectors == NULL) { | |
230 | eeprom->nr_sectors = device_find_integer_property(me, "nr-sectors"); | |
231 | eeprom->sizeof_sector = device_find_integer_property(me, "sector-size"); | |
232 | eeprom->sectors = zalloc(eeprom->nr_sectors); | |
233 | } | |
234 | else | |
235 | memset(eeprom->sectors, 0, eeprom->nr_sectors); | |
236 | ||
237 | /* initialize the eeprom */ | |
238 | if (eeprom->memory == NULL) { | |
239 | eeprom->sizeof_memory = eeprom->sizeof_sector * eeprom->nr_sectors; | |
240 | eeprom->memory = zalloc(eeprom->sizeof_memory); | |
241 | } | |
242 | else | |
243 | memset(eeprom->memory, 0, eeprom->sizeof_memory); | |
244 | if (eeprom->input_file_name != NULL) { | |
245 | int i; | |
246 | FILE *input_file = fopen(eeprom->input_file_name, "r"); | |
247 | if (input_file == NULL) { | |
248 | perror("eeprom"); | |
249 | device_error(me, "Failed to open input file %s\n", eeprom->input_file_name); | |
250 | } | |
251 | for (i = 0; i < eeprom->sizeof_memory; i++) { | |
252 | if (fread(&eeprom->memory[i], 1, 1, input_file) != 1) | |
253 | break; | |
254 | } | |
255 | fclose(input_file); | |
256 | } | |
257 | ||
258 | /* timing */ | |
259 | eeprom->byte_write_delay = device_find_integer_property(me, "byte-write-delay"); | |
260 | eeprom->sector_start_delay = device_find_integer_property(me, "sector-start-delay"); | |
261 | eeprom->erase_delay = device_find_integer_property(me, "erase-delay"); | |
262 | ||
263 | /* misc */ | |
264 | eeprom->manufacture_code = device_find_integer_property(me, "manufacture-code"); | |
265 | eeprom->device_code = device_find_integer_property(me, "device-code"); | |
266 | } | |
267 | ||
268 | ||
269 | static void | |
270 | invalid_read(device *me, | |
271 | hw_eeprom_states state, | |
272 | unsigned_word address, | |
273 | const char *reason) | |
274 | { | |
275 | DTRACE(eeprom, ("Invalid read to 0x%lx while in state %s (%s)\n", | |
276 | (unsigned long)address, | |
277 | state2a(state), | |
278 | reason)); | |
279 | } | |
280 | ||
281 | static void | |
282 | invalid_write(device *me, | |
283 | hw_eeprom_states state, | |
284 | unsigned_word address, | |
285 | unsigned8 data, | |
286 | const char *reason) | |
287 | { | |
288 | DTRACE(eeprom, ("Invalid write of 0x%lx to 0x%lx while in state %s (%s)\n", | |
289 | (unsigned long)data, | |
290 | (unsigned long)address, | |
291 | state2a(state), | |
292 | reason)); | |
293 | } | |
294 | ||
295 | static void | |
296 | dump_eeprom(device *me, | |
297 | hw_eeprom_device *eeprom) | |
298 | { | |
299 | if (eeprom->output_file_name != NULL) { | |
300 | int i; | |
301 | FILE *output_file = fopen(eeprom->output_file_name, "w"); | |
302 | if (output_file == NULL) { | |
303 | perror("eeprom"); | |
304 | device_error(me, "Failed to open output file %s\n", | |
305 | eeprom->output_file_name); | |
306 | } | |
307 | for (i = 0; i < eeprom->sizeof_memory; i++) { | |
308 | if (fwrite(&eeprom->memory[i], 1, 1, output_file) != 1) | |
309 | break; | |
310 | } | |
311 | fclose(output_file); | |
312 | } | |
313 | } | |
314 | ||
315 | ||
316 | /* program a single byte of eeprom */ | |
317 | ||
318 | static void | |
319 | start_programming_byte(device *me, | |
320 | hw_eeprom_device *eeprom, | |
321 | unsigned_word address, | |
322 | unsigned8 new_byte) | |
323 | { | |
324 | unsigned8 old_byte = eeprom->memory[address]; | |
325 | DTRACE(eeprom, ("start-programing-byte - address 0x%lx, new 0x%lx, old 0x%lx\n", | |
326 | (unsigned long)address, | |
327 | (unsigned long)new_byte, | |
328 | (unsigned long)old_byte)); | |
329 | eeprom->byte_program_address = address; | |
330 | /* : old new : ~old : new&~old | |
331 | : 0 0 : 1 : 0 | |
332 | : 0 1 : 1 : 1 -- can not set a bit | |
333 | : 1 0 : 0 : 0 | |
334 | : 1 1 : 0 : 0 */ | |
335 | if (~old_byte & new_byte) | |
336 | invalid_write(me, eeprom->state, address, new_byte, "setting cleared bit"); | |
337 | /* : old new : old&new | |
338 | : 0 0 : 0 | |
339 | : 0 1 : 0 | |
340 | : 1 0 : 0 | |
341 | : 1 1 : 1 */ | |
342 | eeprom->byte_program_byte = new_byte & old_byte; | |
343 | eeprom->memory[address] = ~new_byte & ~0x24; /* LE-bits 5:3 zero */ | |
344 | eeprom->program_start_time = device_event_queue_time(me); | |
345 | eeprom->program_finish_time = (eeprom->program_start_time | |
346 | + eeprom->byte_write_delay); | |
347 | } | |
348 | ||
349 | static void | |
350 | finish_programming_byte(device *me, | |
351 | hw_eeprom_device *eeprom) | |
352 | { | |
353 | DTRACE(eeprom, ("finish-programming-byte - address 0x%lx, byte 0x%lx\n", | |
354 | (unsigned long)eeprom->byte_program_address, | |
355 | (unsigned long)eeprom->byte_program_byte)); | |
356 | eeprom->memory[eeprom->byte_program_address] = eeprom->byte_program_byte; | |
357 | dump_eeprom(me, eeprom); | |
358 | } | |
359 | ||
360 | ||
361 | /* erase the eeprom completly */ | |
362 | ||
363 | static void | |
364 | start_erasing_chip(device *me, | |
365 | hw_eeprom_device *eeprom) | |
366 | { | |
367 | DTRACE(eeprom, ("start-erasing-chip\n")); | |
368 | memset(eeprom->memory, 0, eeprom->sizeof_memory); | |
369 | eeprom->program_start_time = device_event_queue_time(me); | |
370 | eeprom->program_finish_time = (eeprom->program_start_time | |
371 | + eeprom->erase_delay); | |
372 | } | |
373 | ||
374 | static void | |
375 | finish_erasing_chip(device *me, | |
376 | hw_eeprom_device *eeprom) | |
377 | { | |
378 | DTRACE(eeprom, ("finish-erasing-chip\n")); | |
379 | memset(eeprom->memory, 0xff, eeprom->sizeof_memory); | |
380 | dump_eeprom(me, eeprom); | |
381 | } | |
382 | ||
383 | ||
384 | /* erase a single sector of the eeprom */ | |
385 | ||
386 | static void | |
387 | start_erasing_sector(device *me, | |
388 | hw_eeprom_device *eeprom, | |
389 | unsigned_word address) | |
390 | { | |
391 | int sector = address / eeprom->sizeof_sector; | |
392 | DTRACE(eeprom, ("start-erasing-sector - address 0x%lx, sector %d\n", | |
393 | (unsigned long)address, sector)); | |
394 | ASSERT(sector < eeprom->nr_sectors); | |
395 | eeprom->sectors[sector] = 1; | |
396 | memset(eeprom->memory + sector * eeprom->sizeof_sector, | |
397 | 0x4, eeprom->sizeof_sector); | |
398 | eeprom->program_start_time = device_event_queue_time(me); | |
399 | eeprom->sector_start_time = (eeprom->program_start_time | |
400 | + eeprom->sector_start_delay); | |
401 | eeprom->program_finish_time = (eeprom->sector_start_time | |
402 | + eeprom->erase_delay); | |
403 | ||
404 | } | |
405 | ||
406 | static void | |
407 | finish_erasing_sector(device *me, | |
408 | hw_eeprom_device *eeprom) | |
409 | { | |
410 | int sector; | |
411 | DTRACE(eeprom, ("finish-erasing-sector\n")); | |
412 | for (sector = 0; sector < eeprom->nr_sectors; sector++) { | |
413 | if (eeprom->sectors[sector]) { | |
414 | eeprom->sectors[sector] = 0; | |
415 | memset(eeprom->memory + sector * eeprom->sizeof_sector, | |
416 | 0xff, eeprom->sizeof_sector); | |
417 | } | |
418 | } | |
419 | dump_eeprom(me, eeprom); | |
420 | } | |
421 | ||
422 | ||
423 | /* eeprom reads */ | |
424 | ||
425 | static unsigned8 | |
426 | toggle(hw_eeprom_device *eeprom, | |
427 | unsigned8 byte) | |
428 | { | |
429 | eeprom->toggle_bit = eeprom->toggle_bit ^ 0x40; /* le-bit 6 */ | |
430 | return eeprom->toggle_bit ^ byte; | |
431 | } | |
432 | ||
433 | static unsigned8 | |
434 | read_byte(device *me, | |
435 | hw_eeprom_device *eeprom, | |
436 | unsigned_word address) | |
437 | { | |
438 | /* may need multiple iterations of this */ | |
439 | while (1) { | |
440 | switch (eeprom->state) { | |
441 | ||
442 | case read_reset: | |
443 | return eeprom->memory[address]; | |
444 | ||
445 | case autoselect: | |
446 | if ((address & 0xff) == 0x00) | |
447 | return eeprom->manufacture_code; | |
448 | else if ((address & 0xff) == 0x01) | |
449 | return eeprom->device_code; | |
450 | else | |
451 | return 0; /* not certain about this */ | |
452 | ||
453 | case byte_programming: | |
454 | if (device_event_queue_time(me) > eeprom->program_finish_time) { | |
455 | finish_programming_byte(me, eeprom); | |
456 | eeprom->state = read_reset; | |
457 | continue; | |
458 | } | |
459 | else if (address == eeprom->byte_program_address) { | |
460 | return toggle(eeprom, eeprom->memory[address]); | |
461 | } | |
462 | else { | |
463 | /* trash that memory location */ | |
464 | invalid_read(me, eeprom->state, address, "not byte program address"); | |
465 | eeprom->memory[address] = (eeprom->memory[address] | |
466 | & eeprom->byte_program_byte); | |
467 | return toggle(eeprom, eeprom->memory[eeprom->byte_program_address]); | |
468 | } | |
469 | ||
470 | case chip_erase: | |
471 | if (device_event_queue_time(me) > eeprom->program_finish_time) { | |
472 | finish_erasing_chip(me, eeprom); | |
473 | eeprom->state = read_reset; | |
474 | continue; | |
475 | } | |
476 | else { | |
477 | return toggle(eeprom, eeprom->memory[address]); | |
478 | } | |
479 | ||
480 | case sector_erase: | |
481 | if (device_event_queue_time(me) > eeprom->program_finish_time) { | |
482 | finish_erasing_sector(me, eeprom); | |
483 | eeprom->state = read_reset; | |
484 | continue; | |
485 | } | |
486 | else if (!eeprom->sectors[address / eeprom->sizeof_sector]) { | |
487 | /* read to wrong sector */ | |
488 | invalid_read(me, eeprom->state, address, "sector not being erased"); | |
489 | return toggle(eeprom, eeprom->memory[address]) & ~0x8; | |
490 | } | |
491 | else if (device_event_queue_time(me) > eeprom->sector_start_time) { | |
492 | return toggle(eeprom, eeprom->memory[address]) | 0x8; | |
493 | } | |
494 | else { | |
495 | return toggle(eeprom, eeprom->memory[address]) & ~0x8; | |
496 | } | |
497 | ||
498 | case sector_erase_suspend: | |
499 | if (!eeprom->sectors[address / eeprom->sizeof_sector]) { | |
500 | return eeprom->memory[address]; | |
501 | } | |
502 | else { | |
503 | invalid_read(me, eeprom->state, address, "sector being erased"); | |
504 | return eeprom->memory[address]; | |
505 | } | |
506 | ||
507 | default: | |
508 | invalid_read(me, eeprom->state, address, "invalid state"); | |
509 | return eeprom->memory[address]; | |
510 | ||
511 | } | |
512 | } | |
513 | return 0; | |
514 | } | |
515 | ||
516 | static unsigned | |
517 | hw_eeprom_io_read_buffer(device *me, | |
518 | void *dest, | |
519 | int space, | |
520 | unsigned_word addr, | |
521 | unsigned nr_bytes, | |
522 | cpu *processor, | |
523 | unsigned_word cia) | |
524 | { | |
525 | hw_eeprom_device *eeprom = (hw_eeprom_device*)device_data(me); | |
526 | int i; | |
527 | for (i = 0; i < nr_bytes; i++) { | |
528 | unsigned_word address = (addr + i) % eeprom->sizeof_memory; | |
529 | unsigned8 byte = read_byte(me, eeprom, address); | |
530 | ((unsigned8*)dest)[i] = byte; | |
531 | } | |
532 | return nr_bytes; | |
533 | } | |
534 | ||
535 | ||
536 | /* eeprom writes */ | |
537 | ||
538 | static void | |
539 | write_byte(device *me, | |
540 | hw_eeprom_device *eeprom, | |
541 | unsigned_word address, | |
542 | unsigned8 data) | |
543 | { | |
544 | /* may need multiple transitions to process a write */ | |
545 | while (1) { | |
546 | switch (eeprom->state) { | |
547 | ||
548 | case read_reset: | |
549 | if (address == 0x5555 && data == 0xaa) | |
550 | eeprom->state = write_nr_2; | |
551 | else if (data == 0xf0) | |
552 | eeprom->state = read_reset; | |
553 | else { | |
554 | invalid_write(me, eeprom->state, address, data, "unexpected"); | |
555 | eeprom->state = read_reset; | |
556 | } | |
557 | return; | |
558 | ||
559 | case write_nr_2: | |
560 | if (address == 0x2aaa && data == 0x55) | |
561 | eeprom->state = write_nr_3; | |
562 | else { | |
563 | invalid_write(me, eeprom->state, address, data, "unexpected"); | |
564 | eeprom->state = read_reset; | |
565 | } | |
566 | return; | |
567 | ||
568 | case write_nr_3: | |
569 | if (address == 0x5555 && data == 0xf0) | |
570 | eeprom->state = read_reset; | |
571 | else if (address == 0x5555 && data == 0x90) | |
572 | eeprom->state = autoselect; | |
573 | else if (address == 0x5555 && data == 0xa0) { | |
574 | eeprom->state = byte_program; | |
575 | } | |
576 | else if (address == 0x5555 && data == 0x80) | |
577 | eeprom->state = write_nr_4; | |
578 | else { | |
579 | invalid_write(me, eeprom->state, address, data, "unexpected"); | |
580 | eeprom->state = read_reset; | |
581 | } | |
582 | return; | |
583 | ||
584 | case write_nr_4: | |
585 | if (address == 0x5555 && data == 0xaa) | |
586 | eeprom->state = write_nr_5; | |
587 | else { | |
588 | invalid_write(me, eeprom->state, address, data, "unexpected"); | |
589 | eeprom->state = read_reset; | |
590 | } | |
591 | return; | |
592 | ||
593 | case write_nr_5: | |
594 | if (address == 0x2aaa && data == 0x55) | |
595 | eeprom->state = write_nr_6; | |
596 | else { | |
597 | invalid_write(me, eeprom->state, address, data, "unexpected"); | |
598 | eeprom->state = read_reset; | |
599 | } | |
600 | return; | |
601 | ||
602 | case write_nr_6: | |
603 | if (address == 0x5555 && data == 0x10) { | |
604 | start_erasing_chip(me, eeprom); | |
605 | eeprom->state = chip_erase; | |
606 | } | |
607 | else { | |
608 | start_erasing_sector(me, eeprom, address); | |
609 | eeprom->sector_state = read_reset; | |
610 | eeprom->state = sector_erase; | |
611 | } | |
612 | return; | |
613 | ||
614 | case autoselect: | |
615 | if (data == 0xf0) | |
616 | eeprom->state = read_reset; | |
617 | else if (address == 0x5555 && data == 0xaa) | |
618 | eeprom->state = write_nr_2; | |
619 | else { | |
620 | invalid_write(me, eeprom->state, address, data, "unsupported address"); | |
621 | eeprom->state = read_reset; | |
622 | } | |
623 | return; | |
624 | ||
625 | case byte_program: | |
626 | start_programming_byte(me, eeprom, address, data); | |
627 | eeprom->state = byte_programming; | |
628 | return; | |
629 | ||
630 | case byte_programming: | |
631 | if (device_event_queue_time(me) > eeprom->program_finish_time) { | |
632 | finish_programming_byte(me, eeprom); | |
633 | eeprom->state = read_reset; | |
634 | continue; | |
635 | } | |
636 | /* ignore it */ | |
637 | return; | |
638 | ||
639 | case chip_erase: | |
640 | if (device_event_queue_time(me) > eeprom->program_finish_time) { | |
641 | finish_erasing_chip(me, eeprom); | |
642 | eeprom->state = read_reset; | |
643 | continue; | |
644 | } | |
645 | /* ignore it */ | |
646 | return; | |
647 | ||
648 | case sector_erase: | |
649 | if (device_event_queue_time(me) > eeprom->program_finish_time) { | |
650 | finish_erasing_sector(me, eeprom); | |
651 | eeprom->state = eeprom->sector_state; | |
652 | continue; | |
653 | } | |
654 | else if (device_event_queue_time(me) > eeprom->sector_start_time | |
655 | && data == 0xb0) { | |
656 | eeprom->sector_state = read_reset; | |
657 | eeprom->state = sector_erase_suspend; | |
658 | } | |
659 | else { | |
660 | if (eeprom->sector_state == read_reset | |
661 | && address == 0x5555 && data == 0xaa) | |
662 | eeprom->sector_state = write_nr_2; | |
663 | else if (eeprom->sector_state == write_nr_2 | |
664 | && address == 0x2aaa && data == 0x55) | |
665 | eeprom->sector_state = write_nr_3; | |
666 | else if (eeprom->sector_state == write_nr_3 | |
667 | && address == 0x5555 && data == 0x80) | |
668 | eeprom->sector_state = write_nr_4; | |
669 | else if (eeprom->sector_state == write_nr_4 | |
670 | && address == 0x5555 && data == 0xaa) | |
671 | eeprom->sector_state = write_nr_5; | |
672 | else if (eeprom->sector_state == write_nr_5 | |
673 | && address == 0x2aaa && data == 0x55) | |
674 | eeprom->sector_state = write_nr_6; | |
675 | else if (eeprom->sector_state == write_nr_6 | |
676 | && address != 0x5555 && data == 0x30) { | |
677 | if (device_event_queue_time(me) > eeprom->sector_start_time) { | |
678 | DTRACE(eeprom, ("sector erase command after window closed\n")); | |
679 | eeprom->sector_state = read_reset; | |
680 | } | |
681 | else { | |
682 | start_erasing_sector(me, eeprom, address); | |
683 | eeprom->sector_state = read_reset; | |
684 | } | |
685 | } | |
686 | else { | |
687 | invalid_write(me, eeprom->state, address, data, state2a(eeprom->sector_state)); | |
688 | eeprom->state = read_reset; | |
689 | } | |
690 | } | |
691 | return; | |
692 | ||
693 | case sector_erase_suspend: | |
694 | if (data == 0x30) | |
695 | eeprom->state = sector_erase; | |
696 | else { | |
697 | invalid_write(me, eeprom->state, address, data, "not resume command"); | |
698 | eeprom->state = read_reset; | |
699 | } | |
700 | return; | |
701 | ||
702 | } | |
703 | } | |
704 | } | |
705 | ||
706 | static unsigned | |
707 | hw_eeprom_io_write_buffer(device *me, | |
708 | const void *source, | |
709 | int space, | |
710 | unsigned_word addr, | |
711 | unsigned nr_bytes, | |
712 | cpu *processor, | |
713 | unsigned_word cia) | |
714 | { | |
715 | hw_eeprom_device *eeprom = (hw_eeprom_device*)device_data(me); | |
716 | int i; | |
717 | for (i = 0; i < nr_bytes; i++) { | |
718 | unsigned_word address = (addr + i) % eeprom->sizeof_memory; | |
719 | unsigned8 byte = ((unsigned8*)source)[i]; | |
720 | write_byte(me, eeprom, address, byte); | |
721 | } | |
722 | return nr_bytes; | |
723 | } | |
724 | ||
725 | ||
726 | /* An instance of the eeprom */ | |
727 | ||
728 | typedef struct _hw_eeprom_instance { | |
729 | unsigned_word pos; | |
730 | hw_eeprom_device *eeprom; | |
731 | device *me; | |
732 | } hw_eeprom_instance; | |
733 | ||
734 | static void | |
735 | hw_eeprom_instance_delete(device_instance *instance) | |
736 | { | |
737 | hw_eeprom_instance *data = device_instance_data(instance); | |
738 | zfree(data); | |
739 | } | |
740 | ||
741 | static int | |
742 | hw_eeprom_instance_read(device_instance *instance, | |
743 | void *buf, | |
744 | unsigned_word len) | |
745 | { | |
746 | hw_eeprom_instance *data = device_instance_data(instance); | |
747 | int i; | |
748 | if (data->eeprom->state != read_reset) | |
749 | DITRACE(eeprom, ("eeprom not idle during instance read\n")); | |
750 | for (i = 0; i < len; i++) { | |
751 | ((unsigned8*)buf)[i] = data->eeprom->memory[data->pos]; | |
752 | data->pos = (data->pos + 1) % data->eeprom->sizeof_memory; | |
753 | } | |
754 | return len; | |
755 | } | |
756 | ||
757 | static int | |
758 | hw_eeprom_instance_write(device_instance *instance, | |
759 | const void *buf, | |
760 | unsigned_word len) | |
761 | { | |
762 | hw_eeprom_instance *data = device_instance_data(instance); | |
763 | int i; | |
764 | if (data->eeprom->state != read_reset) | |
765 | DITRACE(eeprom, ("eeprom not idle during instance write\n")); | |
766 | for (i = 0; i < len; i++) { | |
767 | data->eeprom->memory[data->pos] = ((unsigned8*)buf)[i]; | |
768 | data->pos = (data->pos + 1) % data->eeprom->sizeof_memory; | |
769 | } | |
770 | dump_eeprom(data->me, data->eeprom); | |
771 | return len; | |
772 | } | |
773 | ||
774 | static int | |
775 | hw_eeprom_instance_seek(device_instance *instance, | |
776 | unsigned_word pos_hi, | |
777 | unsigned_word pos_lo) | |
778 | { | |
779 | hw_eeprom_instance *data = device_instance_data(instance); | |
780 | if (pos_lo >= data->eeprom->sizeof_memory) | |
781 | device_error(data->me, "seek value 0x%lx out of range\n", | |
782 | (unsigned long)pos_lo); | |
783 | data->pos = pos_lo; | |
784 | return 0; | |
785 | } | |
786 | ||
787 | static const device_instance_callbacks hw_eeprom_instance_callbacks = { | |
788 | hw_eeprom_instance_delete, | |
789 | hw_eeprom_instance_read, | |
790 | hw_eeprom_instance_write, | |
791 | hw_eeprom_instance_seek, | |
792 | }; | |
793 | ||
794 | static device_instance * | |
795 | hw_eeprom_create_instance(device *me, | |
796 | const char *path, | |
797 | const char *args) | |
798 | { | |
799 | hw_eeprom_device *eeprom = device_data(me); | |
800 | hw_eeprom_instance *data = ZALLOC(hw_eeprom_instance); | |
801 | data->eeprom = eeprom; | |
802 | data->me = me; | |
803 | return device_create_instance_from(me, NULL, | |
804 | data, | |
805 | path, args, | |
806 | &hw_eeprom_instance_callbacks); | |
807 | } | |
808 | ||
809 | ||
810 | ||
811 | static device_callbacks const hw_eeprom_callbacks = { | |
812 | { generic_device_init_address, | |
813 | hw_eeprom_init_data }, | |
814 | { NULL, }, /* address */ | |
815 | { hw_eeprom_io_read_buffer, | |
816 | hw_eeprom_io_write_buffer }, /* IO */ | |
817 | { NULL, }, /* DMA */ | |
818 | { NULL, }, /* interrupt */ | |
819 | { NULL, }, /* unit */ | |
820 | hw_eeprom_create_instance, | |
821 | }; | |
822 | ||
823 | static void * | |
824 | hw_eeprom_create(const char *name, | |
825 | const device_unit *unit_address, | |
826 | const char *args) | |
827 | { | |
828 | hw_eeprom_device *eeprom = ZALLOC(hw_eeprom_device); | |
829 | return eeprom; | |
830 | } | |
831 | ||
832 | ||
833 | ||
834 | const device_descriptor hw_eeprom_device_descriptor[] = { | |
835 | { "eeprom", hw_eeprom_create, &hw_eeprom_callbacks }, | |
836 | { NULL }, | |
837 | }; | |
838 | ||
839 | #endif /* _HW_EEPROM_C_ */ |