Break up vl.h.
[qemu] / hw / eeprom93xx.c
1 /*
2  * QEMU EEPROM 93xx emulation
3  *
4  * Copyright (c) 2006-2007 Stefan Weil
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20
21 /* Emulation for serial EEPROMs:
22  * NMC93C06 256-Bit (16 x 16)
23  * NMC93C46 1024-Bit (64 x 16)
24  * NMC93C56 2028 Bit (128 x 16)
25  * NMC93C66 4096 Bit (256 x 16)
26  * Compatible devices include FM93C46 and others.
27  *
28  * Other drivers use these interface functions:
29  * eeprom93xx_new   - add a new EEPROM (with 16, 64 or 256 words)
30  * eeprom93xx_free  - destroy EEPROM
31  * eeprom93xx_read  - read data from the EEPROM
32  * eeprom93xx_write - write data to the EEPROM
33  * eeprom93xx_data  - get EEPROM data array for external manipulation
34  *
35  * Todo list:
36  * - No emulation of EEPROM timings.
37  */
38
39 #include <assert.h>
40 #include "hw.h"
41 #include "eeprom93xx.h"
42
43 /* Debug EEPROM emulation. */
44 //~ #define DEBUG_EEPROM
45
46 #ifdef DEBUG_EEPROM
47 #define logout(fmt, args...) fprintf(stderr, "EEPROM\t%-24s" fmt, __func__, ##args)
48 #else
49 #define logout(fmt, args...) ((void)0)
50 #endif
51
52 static int eeprom_instance = 0;
53 static const int eeprom_version = 20061112;
54
55 #if 0
56 typedef enum {
57   eeprom_read  = 0x80,   /* read register xx */
58   eeprom_write = 0x40,   /* write register xx */
59   eeprom_erase = 0xc0,   /* erase register xx */
60   eeprom_ewen  = 0x30,   /* erase / write enable */
61   eeprom_ewds  = 0x00,   /* erase / write disable */
62   eeprom_eral  = 0x20,   /* erase all registers */
63   eeprom_wral  = 0x10,   /* write all registers */
64   eeprom_amask = 0x0f,
65   eeprom_imask = 0xf0
66 } eeprom_instruction_t;
67 #endif
68
69 #ifdef DEBUG_EEPROM
70 static const char *opstring[] = {
71   "extended", "write", "read", "erase"
72 };
73 #endif
74
75 struct _eeprom_t {
76     uint8_t  tick;
77     uint8_t  address;
78     uint8_t  command;
79     uint8_t  writeable;
80
81     uint8_t eecs;
82     uint8_t eesk;
83     uint8_t eedo;
84
85     uint8_t  addrbits;
86     uint8_t  size;
87     uint16_t data;
88     uint16_t contents[0];
89 };
90
91 /* Code for saving and restoring of EEPROM state. */
92
93 static void eeprom_save(QEMUFile *f, void *opaque)
94 {
95     /* Save EEPROM data. */
96     unsigned address;
97     eeprom_t *eeprom = (eeprom_t *)opaque;
98     qemu_put_buffer(f, (uint8_t *)eeprom, sizeof(*eeprom) - 2);
99     qemu_put_be16(f, eeprom->data);
100     for (address = 0; address < eeprom->size; address++) {
101         qemu_put_be16(f, eeprom->contents[address]);
102     }
103 }
104
105 static int eeprom_load(QEMUFile *f, void *opaque, int version_id)
106 {
107     /* Load EEPROM data from saved data if version and EEPROM size
108        of data and current EEPROM are identical. */
109     eeprom_t *eeprom = (eeprom_t *)opaque;
110     int result = -EINVAL;
111     if (version_id == eeprom_version) {
112         unsigned address;
113         uint8_t size = eeprom->size;
114         qemu_get_buffer(f, (uint8_t *)eeprom, sizeof(*eeprom) - 2);
115         if (eeprom->size == size) {
116             eeprom->data = qemu_get_be16(f);
117             for (address = 0; address < eeprom->size; address++) {
118                 eeprom->contents[address] = qemu_get_be16(f);
119             }
120             result = 0;
121         }
122     }
123     return result;
124 }
125
126 void eeprom93xx_write(eeprom_t *eeprom, int eecs, int eesk, int eedi)
127 {
128     uint8_t tick = eeprom->tick;
129     uint8_t eedo = eeprom->eedo;
130     uint16_t address = eeprom->address;
131     uint8_t command = eeprom->command;
132
133     logout("CS=%u SK=%u DI=%u DO=%u, tick = %u\n",
134            eecs, eesk, eedi, eedo, tick);
135
136     if (! eeprom->eecs && eecs) {
137         /* Start chip select cycle. */
138         logout("Cycle start, waiting for 1st start bit (0)\n");
139         tick = 0;
140         command = 0x0;
141         address = 0x0;
142     } else if (eeprom->eecs && ! eecs) {
143         /* End chip select cycle. This triggers write / erase. */
144         if (eeprom->writeable) {
145             uint8_t subcommand = address >> (eeprom->addrbits - 2);
146             if (command == 0 && subcommand == 2) {
147                 /* Erase all. */
148                 for (address = 0; address < eeprom->size; address++) {
149                     eeprom->contents[address] = 0xffff;
150                 }
151             } else if (command == 3) {
152                 /* Erase word. */
153                 eeprom->contents[address] = 0xffff;
154             } else if (tick >= 2 + 2 + eeprom->addrbits + 16) {
155                 if (command == 1) {
156                     /* Write word. */
157                     eeprom->contents[address] &= eeprom->data;
158                 } else if (command == 0 && subcommand == 1) {
159                     /* Write all. */
160                     for (address = 0; address < eeprom->size; address++) {
161                         eeprom->contents[address] &= eeprom->data;
162                     }
163                 }
164             }
165         }
166         /* Output DO is tristate, read results in 1. */
167         eedo = 1;
168     } else if (eecs && ! eeprom->eesk && eesk) {
169         /* Raising edge of clock shifts data in. */
170         if (tick == 0) {
171             /* Wait for 1st start bit. */
172             if (eedi == 0) {
173                 logout("Got correct 1st start bit, waiting for 2nd start bit (1)\n");
174                 tick++;
175             } else {
176                 logout("wrong 1st start bit (is 1, should be 0)\n");
177                 tick = 2;
178                 //~ assert(!"wrong start bit");
179             }
180         } else if (tick == 1) {
181             /* Wait for 2nd start bit. */
182             if (eedi != 0) {
183                 logout("Got correct 2nd start bit, getting command + address\n");
184                 tick++;
185             } else {
186                 logout("1st start bit is longer than needed\n");
187             }
188         } else if (tick < 2 + 2) {
189             /* Got 2 start bits, transfer 2 opcode bits. */
190             tick++;
191             command <<= 1;
192             if (eedi) {
193                 command += 1;
194             }
195         } else if (tick < 2 + 2 + eeprom->addrbits) {
196             /* Got 2 start bits and 2 opcode bits, transfer all address bits. */
197             tick++;
198             address = ((address << 1) | eedi);
199             if (tick == 2 + 2 + eeprom->addrbits) {
200                 logout("%s command, address = 0x%02x (value 0x%04x)\n",
201                        opstring[command], address, eeprom->contents[address]);
202                 if (command == 2) {
203                     eedo = 0;
204                 }
205                 address = address % eeprom->size;
206                 if (command == 0) {
207                     /* Command code in upper 2 bits of address. */
208                     switch (address >> (eeprom->addrbits - 2)) {
209                         case 0:
210                             logout("write disable command\n");
211                             eeprom->writeable = 0;
212                             break;
213                         case 1:
214                             logout("write all command\n");
215                             break;
216                         case 2:
217                             logout("erase all command\n");
218                             break;
219                         case 3:
220                             logout("write enable command\n");
221                             eeprom->writeable = 1;
222                             break;
223                     }
224                 } else {
225                     /* Read, write or erase word. */
226                     eeprom->data = eeprom->contents[address];
227                 }
228             }
229         } else if (tick < 2 + 2 + eeprom->addrbits + 16) {
230             /* Transfer 16 data bits. */
231             tick++;
232             if (command == 2) {
233                 /* Read word. */
234                 eedo = ((eeprom->data & 0x8000) != 0);
235             }
236             eeprom->data <<= 1;
237             eeprom->data += eedi;
238         } else {
239             logout("additional unneeded tick, not processed\n");
240         }
241     }
242     /* Save status of EEPROM. */
243     eeprom->tick = tick;
244     eeprom->eecs = eecs;
245     eeprom->eesk = eesk;
246     eeprom->eedo = eedo;
247     eeprom->address = address;
248     eeprom->command = command;
249 }
250
251 uint16_t eeprom93xx_read(eeprom_t *eeprom)
252 {
253     /* Return status of pin DO (0 or 1). */
254     logout("CS=%u DO=%u\n", eeprom->eecs, eeprom->eedo);
255     return (eeprom->eedo);
256 }
257
258 #if 0
259 void eeprom93xx_reset(eeprom_t *eeprom)
260 {
261     /* prepare eeprom */
262     logout("eeprom = 0x%p\n", eeprom);
263     eeprom->tick = 0;
264     eeprom->command = 0;
265 }
266 #endif
267
268 eeprom_t *eeprom93xx_new(uint16_t nwords)
269 {
270     /* Add a new EEPROM (with 16, 64 or 256 words). */
271     eeprom_t *eeprom;
272     uint8_t addrbits;
273
274     switch (nwords) {
275         case 16:
276         case 64:
277             addrbits = 6;
278             break;
279         case 128:
280         case 256:
281             addrbits = 8;
282             break;
283         default:
284             assert(!"Unsupported EEPROM size, fallback to 64 words!");
285             nwords = 64;
286             addrbits = 6;
287     }
288
289     eeprom = (eeprom_t *)qemu_mallocz(sizeof(*eeprom) + nwords * 2);
290     eeprom->size = nwords;
291     eeprom->addrbits = addrbits;
292     /* Output DO is tristate, read results in 1. */
293     eeprom->eedo = 1;
294     logout("eeprom = 0x%p, nwords = %u\n", eeprom, nwords);
295     register_savevm("eeprom", eeprom_instance, eeprom_version,
296                     eeprom_save, eeprom_load, eeprom);
297     return eeprom;
298 }
299
300 void eeprom93xx_free(eeprom_t *eeprom)
301 {
302     /* Destroy EEPROM. */
303     logout("eeprom = 0x%p\n", eeprom);
304     qemu_free(eeprom);
305 }
306
307 uint16_t *eeprom93xx_data(eeprom_t *eeprom)
308 {
309     /* Get EEPROM data array. */
310     return &eeprom->contents[0];
311 }
312
313 /* eof */