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