root / hw / block / pflash_cfi02.c @ 3509c396
History | View | Annotate | Download (23.9 kB)
1 | 29133e9a | bellard | /*
|
---|---|---|---|
2 | 29133e9a | bellard | * CFI parallel flash with AMD command set emulation
|
3 | 5fafdf24 | ths | *
|
4 | 29133e9a | bellard | * Copyright (c) 2005 Jocelyn Mayer
|
5 | 29133e9a | bellard | *
|
6 | 29133e9a | bellard | * This library is free software; you can redistribute it and/or
|
7 | 29133e9a | bellard | * modify it under the terms of the GNU Lesser General Public
|
8 | 29133e9a | bellard | * License as published by the Free Software Foundation; either
|
9 | 29133e9a | bellard | * version 2 of the License, or (at your option) any later version.
|
10 | 29133e9a | bellard | *
|
11 | 29133e9a | bellard | * This library is distributed in the hope that it will be useful,
|
12 | 29133e9a | bellard | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 | 29133e9a | bellard | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14 | 29133e9a | bellard | * Lesser General Public License for more details.
|
15 | 29133e9a | bellard | *
|
16 | 29133e9a | bellard | * You should have received a copy of the GNU Lesser General Public
|
17 | 8167ee88 | Blue Swirl | * License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
18 | 29133e9a | bellard | */
|
19 | 29133e9a | bellard | |
20 | 29133e9a | bellard | /*
|
21 | 29133e9a | bellard | * For now, this code can emulate flashes of 1, 2 or 4 bytes width.
|
22 | 29133e9a | bellard | * Supported commands/modes are:
|
23 | 29133e9a | bellard | * - flash read
|
24 | 29133e9a | bellard | * - flash write
|
25 | 29133e9a | bellard | * - flash ID read
|
26 | 29133e9a | bellard | * - sector erase
|
27 | 29133e9a | bellard | * - chip erase
|
28 | 29133e9a | bellard | * - unlock bypass command
|
29 | 29133e9a | bellard | * - CFI queries
|
30 | 29133e9a | bellard | *
|
31 | 29133e9a | bellard | * It does not support flash interleaving.
|
32 | 29133e9a | bellard | * It does not implement boot blocs with reduced size
|
33 | 29133e9a | bellard | * It does not implement software data protection as found in many real chips
|
34 | 29133e9a | bellard | * It does not implement erase suspend/resume commands
|
35 | 29133e9a | bellard | * It does not implement multiple sectors erase
|
36 | 29133e9a | bellard | */
|
37 | 29133e9a | bellard | |
38 | 83c9f4ca | Paolo Bonzini | #include "hw/hw.h" |
39 | 0d09e41a | Paolo Bonzini | #include "hw/block/flash.h" |
40 | 1de7afc9 | Paolo Bonzini | #include "qemu/timer.h" |
41 | 737e150e | Paolo Bonzini | #include "block/block.h" |
42 | 022c62cb | Paolo Bonzini | #include "exec/address-spaces.h" |
43 | 1de7afc9 | Paolo Bonzini | #include "qemu/host-utils.h" |
44 | 83c9f4ca | Paolo Bonzini | #include "hw/sysbus.h" |
45 | 29133e9a | bellard | |
46 | 29133e9a | bellard | //#define PFLASH_DEBUG
|
47 | 29133e9a | bellard | #ifdef PFLASH_DEBUG
|
48 | ec9ea489 | Peter Crosthwaite | #define DPRINTF(fmt, ...) \
|
49 | ec9ea489 | Peter Crosthwaite | do { \
|
50 | ec9ea489 | Peter Crosthwaite | fprintf(stderr "PFLASH: " fmt , ## __VA_ARGS__); \ |
51 | 29133e9a | bellard | } while (0) |
52 | 29133e9a | bellard | #else
|
53 | 001faf32 | Blue Swirl | #define DPRINTF(fmt, ...) do { } while (0) |
54 | 29133e9a | bellard | #endif
|
55 | 29133e9a | bellard | |
56 | 661bfc80 | Jan Kiszka | #define PFLASH_LAZY_ROMD_THRESHOLD 42 |
57 | 661bfc80 | Jan Kiszka | |
58 | 3509c396 | Hu Tao | #define TYPE_CFI_PFLASH02 "cfi.pflash02" |
59 | 3509c396 | Hu Tao | #define CFI_PFLASH02(obj) OBJECT_CHECK(pflash_t, (obj), TYPE_CFI_PFLASH02)
|
60 | 3509c396 | Hu Tao | |
61 | c227f099 | Anthony Liguori | struct pflash_t {
|
62 | 3509c396 | Hu Tao | /*< private >*/
|
63 | 3509c396 | Hu Tao | SysBusDevice parent_obj; |
64 | 3509c396 | Hu Tao | /*< public >*/
|
65 | 3509c396 | Hu Tao | |
66 | 29133e9a | bellard | BlockDriverState *bs; |
67 | 71db710f | blueswir1 | uint32_t sector_len; |
68 | 368a354f | Peter Crosthwaite | uint32_t nb_blocs; |
69 | 4fbd24ba | balrog | uint32_t chip_len; |
70 | 368a354f | Peter Crosthwaite | uint8_t mappings; |
71 | 368a354f | Peter Crosthwaite | uint8_t width; |
72 | 368a354f | Peter Crosthwaite | uint8_t be; |
73 | 29133e9a | bellard | int wcycle; /* if 0, the flash is read normally */ |
74 | 29133e9a | bellard | int bypass;
|
75 | 29133e9a | bellard | int ro;
|
76 | 29133e9a | bellard | uint8_t cmd; |
77 | 29133e9a | bellard | uint8_t status; |
78 | 368a354f | Peter Crosthwaite | /* FIXME: implement array device properties */
|
79 | 368a354f | Peter Crosthwaite | uint16_t ident0; |
80 | 368a354f | Peter Crosthwaite | uint16_t ident1; |
81 | 368a354f | Peter Crosthwaite | uint16_t ident2; |
82 | 368a354f | Peter Crosthwaite | uint16_t ident3; |
83 | 368a354f | Peter Crosthwaite | uint16_t unlock_addr0; |
84 | 368a354f | Peter Crosthwaite | uint16_t unlock_addr1; |
85 | 29133e9a | bellard | uint8_t cfi_len; |
86 | 29133e9a | bellard | uint8_t cfi_table[0x52];
|
87 | 29133e9a | bellard | QEMUTimer *timer; |
88 | cfe5f011 | Avi Kivity | /* The device replicates the flash memory across its memory space. Emulate
|
89 | cfe5f011 | Avi Kivity | * that by having a container (.mem) filled with an array of aliases
|
90 | cfe5f011 | Avi Kivity | * (.mem_mappings) pointing to the flash memory (.orig_mem).
|
91 | cfe5f011 | Avi Kivity | */
|
92 | cfe5f011 | Avi Kivity | MemoryRegion mem; |
93 | cfe5f011 | Avi Kivity | MemoryRegion *mem_mappings; /* array; one per mapping */
|
94 | cfe5f011 | Avi Kivity | MemoryRegion orig_mem; |
95 | 9c9bb6c8 | balrog | int rom_mode;
|
96 | 661bfc80 | Jan Kiszka | int read_counter; /* used for lazy switch-back to rom mode */ |
97 | 368a354f | Peter Crosthwaite | char *name;
|
98 | 29133e9a | bellard | void *storage;
|
99 | 29133e9a | bellard | }; |
100 | 29133e9a | bellard | |
101 | cfe5f011 | Avi Kivity | /*
|
102 | cfe5f011 | Avi Kivity | * Set up replicated mappings of the same region.
|
103 | cfe5f011 | Avi Kivity | */
|
104 | cfe5f011 | Avi Kivity | static void pflash_setup_mappings(pflash_t *pfl) |
105 | c8a50e59 | Avi Kivity | { |
106 | cfe5f011 | Avi Kivity | unsigned i;
|
107 | a8170e5e | Avi Kivity | hwaddr size = memory_region_size(&pfl->orig_mem); |
108 | cfe5f011 | Avi Kivity | |
109 | 2d256e6f | Paolo Bonzini | memory_region_init(&pfl->mem, OBJECT(pfl), "pflash", pfl->mappings * size);
|
110 | cfe5f011 | Avi Kivity | pfl->mem_mappings = g_new(MemoryRegion, pfl->mappings); |
111 | cfe5f011 | Avi Kivity | for (i = 0; i < pfl->mappings; ++i) { |
112 | 2d256e6f | Paolo Bonzini | memory_region_init_alias(&pfl->mem_mappings[i], OBJECT(pfl), |
113 | 2d256e6f | Paolo Bonzini | "pflash-alias", &pfl->orig_mem, 0, size); |
114 | cfe5f011 | Avi Kivity | memory_region_add_subregion(&pfl->mem, i * size, &pfl->mem_mappings[i]); |
115 | cfe5f011 | Avi Kivity | } |
116 | cfe5f011 | Avi Kivity | } |
117 | 01e0451a | Anthony Liguori | |
118 | cfe5f011 | Avi Kivity | static void pflash_register_memory(pflash_t *pfl, int rom_mode) |
119 | cfe5f011 | Avi Kivity | { |
120 | 5f9a5ea1 | Jan Kiszka | memory_region_rom_device_set_romd(&pfl->orig_mem, rom_mode); |
121 | bda254da | Jan Kiszka | pfl->rom_mode = rom_mode; |
122 | 4fbd24ba | balrog | } |
123 | 4fbd24ba | balrog | |
124 | 29133e9a | bellard | static void pflash_timer (void *opaque) |
125 | 29133e9a | bellard | { |
126 | c227f099 | Anthony Liguori | pflash_t *pfl = opaque; |
127 | 29133e9a | bellard | |
128 | 29133e9a | bellard | DPRINTF("%s: command %02x done\n", __func__, pfl->cmd);
|
129 | 29133e9a | bellard | /* Reset flash */
|
130 | 29133e9a | bellard | pfl->status ^= 0x80;
|
131 | 29133e9a | bellard | if (pfl->bypass) {
|
132 | 29133e9a | bellard | pfl->wcycle = 2;
|
133 | 29133e9a | bellard | } else {
|
134 | 4fbd24ba | balrog | pflash_register_memory(pfl, 1);
|
135 | 29133e9a | bellard | pfl->wcycle = 0;
|
136 | 29133e9a | bellard | } |
137 | 29133e9a | bellard | pfl->cmd = 0;
|
138 | 29133e9a | bellard | } |
139 | 29133e9a | bellard | |
140 | a8170e5e | Avi Kivity | static uint32_t pflash_read (pflash_t *pfl, hwaddr offset,
|
141 | 5f9fc5ad | Blue Swirl | int width, int be) |
142 | 29133e9a | bellard | { |
143 | a8170e5e | Avi Kivity | hwaddr boff; |
144 | 29133e9a | bellard | uint32_t ret; |
145 | 29133e9a | bellard | uint8_t *p; |
146 | 29133e9a | bellard | |
147 | f8be67ee | Blue Swirl | DPRINTF("%s: offset " TARGET_FMT_plx "\n", __func__, offset); |
148 | 29133e9a | bellard | ret = -1;
|
149 | 661bfc80 | Jan Kiszka | /* Lazy reset to ROMD mode after a certain amount of read accesses */
|
150 | 661bfc80 | Jan Kiszka | if (!pfl->rom_mode && pfl->wcycle == 0 && |
151 | 661bfc80 | Jan Kiszka | ++pfl->read_counter > PFLASH_LAZY_ROMD_THRESHOLD) { |
152 | 661bfc80 | Jan Kiszka | pflash_register_memory(pfl, 1);
|
153 | 0f459d16 | pbrook | } |
154 | 4fbd24ba | balrog | offset &= pfl->chip_len - 1;
|
155 | 29133e9a | bellard | boff = offset & 0xFF;
|
156 | 29133e9a | bellard | if (pfl->width == 2) |
157 | 29133e9a | bellard | boff = boff >> 1;
|
158 | 29133e9a | bellard | else if (pfl->width == 4) |
159 | 29133e9a | bellard | boff = boff >> 2;
|
160 | 29133e9a | bellard | switch (pfl->cmd) {
|
161 | 29133e9a | bellard | default:
|
162 | 29133e9a | bellard | /* This should never happen : reset state & treat it as a read*/
|
163 | 29133e9a | bellard | DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd);
|
164 | 29133e9a | bellard | pfl->wcycle = 0;
|
165 | 29133e9a | bellard | pfl->cmd = 0;
|
166 | 30954850 | Peter Maydell | /* fall through to the read code */
|
167 | 29133e9a | bellard | case 0x80: |
168 | 29133e9a | bellard | /* We accept reads during second unlock sequence... */
|
169 | 29133e9a | bellard | case 0x00: |
170 | 29133e9a | bellard | flash_read:
|
171 | 29133e9a | bellard | /* Flash area read */
|
172 | 29133e9a | bellard | p = pfl->storage; |
173 | 29133e9a | bellard | switch (width) {
|
174 | 29133e9a | bellard | case 1: |
175 | 29133e9a | bellard | ret = p[offset]; |
176 | 29133e9a | bellard | // DPRINTF("%s: data offset %08x %02x\n", __func__, offset, ret);
|
177 | 29133e9a | bellard | break;
|
178 | 29133e9a | bellard | case 2: |
179 | 5f9fc5ad | Blue Swirl | if (be) {
|
180 | 5f9fc5ad | Blue Swirl | ret = p[offset] << 8;
|
181 | 5f9fc5ad | Blue Swirl | ret |= p[offset + 1];
|
182 | 5f9fc5ad | Blue Swirl | } else {
|
183 | 5f9fc5ad | Blue Swirl | ret = p[offset]; |
184 | 5f9fc5ad | Blue Swirl | ret |= p[offset + 1] << 8; |
185 | 5f9fc5ad | Blue Swirl | } |
186 | 29133e9a | bellard | // DPRINTF("%s: data offset %08x %04x\n", __func__, offset, ret);
|
187 | 29133e9a | bellard | break;
|
188 | 29133e9a | bellard | case 4: |
189 | 5f9fc5ad | Blue Swirl | if (be) {
|
190 | 5f9fc5ad | Blue Swirl | ret = p[offset] << 24;
|
191 | 5f9fc5ad | Blue Swirl | ret |= p[offset + 1] << 16; |
192 | 5f9fc5ad | Blue Swirl | ret |= p[offset + 2] << 8; |
193 | 5f9fc5ad | Blue Swirl | ret |= p[offset + 3];
|
194 | 5f9fc5ad | Blue Swirl | } else {
|
195 | 5f9fc5ad | Blue Swirl | ret = p[offset]; |
196 | 5f9fc5ad | Blue Swirl | ret |= p[offset + 1] << 8; |
197 | 5f9fc5ad | Blue Swirl | ret |= p[offset + 2] << 16; |
198 | 5f9fc5ad | Blue Swirl | ret |= p[offset + 3] << 24; |
199 | 5f9fc5ad | Blue Swirl | } |
200 | 29133e9a | bellard | // DPRINTF("%s: data offset %08x %08x\n", __func__, offset, ret);
|
201 | 29133e9a | bellard | break;
|
202 | 29133e9a | bellard | } |
203 | 29133e9a | bellard | break;
|
204 | 29133e9a | bellard | case 0x90: |
205 | 29133e9a | bellard | /* flash ID read */
|
206 | 29133e9a | bellard | switch (boff) {
|
207 | 29133e9a | bellard | case 0x00: |
208 | 29133e9a | bellard | case 0x01: |
209 | 368a354f | Peter Crosthwaite | ret = boff & 0x01 ? pfl->ident1 : pfl->ident0;
|
210 | 29133e9a | bellard | break;
|
211 | 29133e9a | bellard | case 0x02: |
212 | 29133e9a | bellard | ret = 0x00; /* Pretend all sectors are unprotected */ |
213 | 29133e9a | bellard | break;
|
214 | 29133e9a | bellard | case 0x0E: |
215 | 29133e9a | bellard | case 0x0F: |
216 | 368a354f | Peter Crosthwaite | ret = boff & 0x01 ? pfl->ident3 : pfl->ident2;
|
217 | 368a354f | Peter Crosthwaite | if (ret == (uint8_t)-1) { |
218 | 29133e9a | bellard | goto flash_read;
|
219 | 368a354f | Peter Crosthwaite | } |
220 | 29133e9a | bellard | break;
|
221 | 29133e9a | bellard | default:
|
222 | 29133e9a | bellard | goto flash_read;
|
223 | 29133e9a | bellard | } |
224 | b9055c3c | Stefan Weil | DPRINTF("%s: ID " TARGET_FMT_plx " %x\n", __func__, boff, ret); |
225 | 29133e9a | bellard | break;
|
226 | 29133e9a | bellard | case 0xA0: |
227 | 29133e9a | bellard | case 0x10: |
228 | 29133e9a | bellard | case 0x30: |
229 | 29133e9a | bellard | /* Status register read */
|
230 | 29133e9a | bellard | ret = pfl->status; |
231 | 29133e9a | bellard | DPRINTF("%s: status %x\n", __func__, ret);
|
232 | 29133e9a | bellard | /* Toggle bit 6 */
|
233 | 29133e9a | bellard | pfl->status ^= 0x40;
|
234 | 29133e9a | bellard | break;
|
235 | 29133e9a | bellard | case 0x98: |
236 | 29133e9a | bellard | /* CFI query mode */
|
237 | 29133e9a | bellard | if (boff > pfl->cfi_len)
|
238 | 29133e9a | bellard | ret = 0;
|
239 | 29133e9a | bellard | else
|
240 | 29133e9a | bellard | ret = pfl->cfi_table[boff]; |
241 | 29133e9a | bellard | break;
|
242 | 29133e9a | bellard | } |
243 | 29133e9a | bellard | |
244 | 29133e9a | bellard | return ret;
|
245 | 29133e9a | bellard | } |
246 | 29133e9a | bellard | |
247 | 29133e9a | bellard | /* update flash content on disk */
|
248 | c227f099 | Anthony Liguori | static void pflash_update(pflash_t *pfl, int offset, |
249 | 29133e9a | bellard | int size)
|
250 | 29133e9a | bellard | { |
251 | 29133e9a | bellard | int offset_end;
|
252 | 29133e9a | bellard | if (pfl->bs) {
|
253 | 29133e9a | bellard | offset_end = offset + size; |
254 | 29133e9a | bellard | /* round to sectors */
|
255 | 29133e9a | bellard | offset = offset >> 9;
|
256 | 29133e9a | bellard | offset_end = (offset_end + 511) >> 9; |
257 | 5fafdf24 | ths | bdrv_write(pfl->bs, offset, pfl->storage + (offset << 9),
|
258 | 29133e9a | bellard | offset_end - offset); |
259 | 29133e9a | bellard | } |
260 | 29133e9a | bellard | } |
261 | 29133e9a | bellard | |
262 | a8170e5e | Avi Kivity | static void pflash_write (pflash_t *pfl, hwaddr offset, |
263 | 5f9fc5ad | Blue Swirl | uint32_t value, int width, int be) |
264 | 29133e9a | bellard | { |
265 | a8170e5e | Avi Kivity | hwaddr boff; |
266 | 29133e9a | bellard | uint8_t *p; |
267 | 29133e9a | bellard | uint8_t cmd; |
268 | 29133e9a | bellard | |
269 | 95d1f3ed | j_mayer | cmd = value; |
270 | 95d1f3ed | j_mayer | if (pfl->cmd != 0xA0 && cmd == 0xF0) { |
271 | 95d1f3ed | j_mayer | #if 0
|
272 | 95d1f3ed | j_mayer | DPRINTF("%s: flash reset asked (%02x %02x)\n",
|
273 | 95d1f3ed | j_mayer | __func__, pfl->cmd, cmd);
|
274 | 95d1f3ed | j_mayer | #endif
|
275 | 95d1f3ed | j_mayer | goto reset_flash;
|
276 | 95d1f3ed | j_mayer | } |
277 | f8be67ee | Blue Swirl | DPRINTF("%s: offset " TARGET_FMT_plx " %08x %d %d\n", __func__, |
278 | 95d1f3ed | j_mayer | offset, value, width, pfl->wcycle); |
279 | 4fbd24ba | balrog | offset &= pfl->chip_len - 1;
|
280 | 3b46e624 | ths | |
281 | f8be67ee | Blue Swirl | DPRINTF("%s: offset " TARGET_FMT_plx " %08x %d\n", __func__, |
282 | e96efcfc | j_mayer | offset, value, width); |
283 | 29133e9a | bellard | boff = offset & (pfl->sector_len - 1);
|
284 | 29133e9a | bellard | if (pfl->width == 2) |
285 | 29133e9a | bellard | boff = boff >> 1;
|
286 | 29133e9a | bellard | else if (pfl->width == 4) |
287 | 29133e9a | bellard | boff = boff >> 2;
|
288 | 29133e9a | bellard | switch (pfl->wcycle) {
|
289 | 29133e9a | bellard | case 0: |
290 | 9c9bb6c8 | balrog | /* Set the device in I/O access mode if required */
|
291 | 9c9bb6c8 | balrog | if (pfl->rom_mode)
|
292 | 9c9bb6c8 | balrog | pflash_register_memory(pfl, 0);
|
293 | 661bfc80 | Jan Kiszka | pfl->read_counter = 0;
|
294 | 29133e9a | bellard | /* We're in read mode */
|
295 | 29133e9a | bellard | check_unlock0:
|
296 | 29133e9a | bellard | if (boff == 0x55 && cmd == 0x98) { |
297 | 29133e9a | bellard | enter_CFI_mode:
|
298 | 29133e9a | bellard | /* Enter CFI query mode */
|
299 | 29133e9a | bellard | pfl->wcycle = 7;
|
300 | 29133e9a | bellard | pfl->cmd = 0x98;
|
301 | 29133e9a | bellard | return;
|
302 | 29133e9a | bellard | } |
303 | 368a354f | Peter Crosthwaite | if (boff != pfl->unlock_addr0 || cmd != 0xAA) { |
304 | f8be67ee | Blue Swirl | DPRINTF("%s: unlock0 failed " TARGET_FMT_plx " %02x %04x\n", |
305 | 368a354f | Peter Crosthwaite | __func__, boff, cmd, pfl->unlock_addr0); |
306 | 29133e9a | bellard | goto reset_flash;
|
307 | 29133e9a | bellard | } |
308 | 29133e9a | bellard | DPRINTF("%s: unlock sequence started\n", __func__);
|
309 | 29133e9a | bellard | break;
|
310 | 29133e9a | bellard | case 1: |
311 | 29133e9a | bellard | /* We started an unlock sequence */
|
312 | 29133e9a | bellard | check_unlock1:
|
313 | 368a354f | Peter Crosthwaite | if (boff != pfl->unlock_addr1 || cmd != 0x55) { |
314 | f8be67ee | Blue Swirl | DPRINTF("%s: unlock1 failed " TARGET_FMT_plx " %02x\n", __func__, |
315 | e96efcfc | j_mayer | boff, cmd); |
316 | 29133e9a | bellard | goto reset_flash;
|
317 | 29133e9a | bellard | } |
318 | 29133e9a | bellard | DPRINTF("%s: unlock sequence done\n", __func__);
|
319 | 29133e9a | bellard | break;
|
320 | 29133e9a | bellard | case 2: |
321 | 29133e9a | bellard | /* We finished an unlock sequence */
|
322 | 368a354f | Peter Crosthwaite | if (!pfl->bypass && boff != pfl->unlock_addr0) {
|
323 | f8be67ee | Blue Swirl | DPRINTF("%s: command failed " TARGET_FMT_plx " %02x\n", __func__, |
324 | e96efcfc | j_mayer | boff, cmd); |
325 | 29133e9a | bellard | goto reset_flash;
|
326 | 29133e9a | bellard | } |
327 | 29133e9a | bellard | switch (cmd) {
|
328 | 29133e9a | bellard | case 0x20: |
329 | 29133e9a | bellard | pfl->bypass = 1;
|
330 | 29133e9a | bellard | goto do_bypass;
|
331 | 29133e9a | bellard | case 0x80: |
332 | 29133e9a | bellard | case 0x90: |
333 | 29133e9a | bellard | case 0xA0: |
334 | 29133e9a | bellard | pfl->cmd = cmd; |
335 | 29133e9a | bellard | DPRINTF("%s: starting command %02x\n", __func__, cmd);
|
336 | 29133e9a | bellard | break;
|
337 | 29133e9a | bellard | default:
|
338 | 29133e9a | bellard | DPRINTF("%s: unknown command %02x\n", __func__, cmd);
|
339 | 29133e9a | bellard | goto reset_flash;
|
340 | 29133e9a | bellard | } |
341 | 29133e9a | bellard | break;
|
342 | 29133e9a | bellard | case 3: |
343 | 29133e9a | bellard | switch (pfl->cmd) {
|
344 | 29133e9a | bellard | case 0x80: |
345 | 29133e9a | bellard | /* We need another unlock sequence */
|
346 | 29133e9a | bellard | goto check_unlock0;
|
347 | 29133e9a | bellard | case 0xA0: |
348 | f8be67ee | Blue Swirl | DPRINTF("%s: write data offset " TARGET_FMT_plx " %08x %d\n", |
349 | 29133e9a | bellard | __func__, offset, value, width); |
350 | 29133e9a | bellard | p = pfl->storage; |
351 | de8efe8f | Jordan Justen | if (!pfl->ro) {
|
352 | de8efe8f | Jordan Justen | switch (width) {
|
353 | de8efe8f | Jordan Justen | case 1: |
354 | 5f9fc5ad | Blue Swirl | p[offset] &= value; |
355 | de8efe8f | Jordan Justen | pflash_update(pfl, offset, 1);
|
356 | de8efe8f | Jordan Justen | break;
|
357 | de8efe8f | Jordan Justen | case 2: |
358 | de8efe8f | Jordan Justen | if (be) {
|
359 | de8efe8f | Jordan Justen | p[offset] &= value >> 8;
|
360 | de8efe8f | Jordan Justen | p[offset + 1] &= value;
|
361 | de8efe8f | Jordan Justen | } else {
|
362 | de8efe8f | Jordan Justen | p[offset] &= value; |
363 | de8efe8f | Jordan Justen | p[offset + 1] &= value >> 8; |
364 | de8efe8f | Jordan Justen | } |
365 | de8efe8f | Jordan Justen | pflash_update(pfl, offset, 2);
|
366 | de8efe8f | Jordan Justen | break;
|
367 | de8efe8f | Jordan Justen | case 4: |
368 | de8efe8f | Jordan Justen | if (be) {
|
369 | de8efe8f | Jordan Justen | p[offset] &= value >> 24;
|
370 | de8efe8f | Jordan Justen | p[offset + 1] &= value >> 16; |
371 | de8efe8f | Jordan Justen | p[offset + 2] &= value >> 8; |
372 | de8efe8f | Jordan Justen | p[offset + 3] &= value;
|
373 | de8efe8f | Jordan Justen | } else {
|
374 | de8efe8f | Jordan Justen | p[offset] &= value; |
375 | de8efe8f | Jordan Justen | p[offset + 1] &= value >> 8; |
376 | de8efe8f | Jordan Justen | p[offset + 2] &= value >> 16; |
377 | de8efe8f | Jordan Justen | p[offset + 3] &= value >> 24; |
378 | de8efe8f | Jordan Justen | } |
379 | de8efe8f | Jordan Justen | pflash_update(pfl, offset, 4);
|
380 | de8efe8f | Jordan Justen | break;
|
381 | 5f9fc5ad | Blue Swirl | } |
382 | 29133e9a | bellard | } |
383 | 29133e9a | bellard | pfl->status = 0x00 | ~(value & 0x80); |
384 | 29133e9a | bellard | /* Let's pretend write is immediate */
|
385 | 29133e9a | bellard | if (pfl->bypass)
|
386 | 29133e9a | bellard | goto do_bypass;
|
387 | 29133e9a | bellard | goto reset_flash;
|
388 | 29133e9a | bellard | case 0x90: |
389 | 29133e9a | bellard | if (pfl->bypass && cmd == 0x00) { |
390 | 29133e9a | bellard | /* Unlock bypass reset */
|
391 | 29133e9a | bellard | goto reset_flash;
|
392 | 29133e9a | bellard | } |
393 | 29133e9a | bellard | /* We can enter CFI query mode from autoselect mode */
|
394 | 29133e9a | bellard | if (boff == 0x55 && cmd == 0x98) |
395 | 29133e9a | bellard | goto enter_CFI_mode;
|
396 | 29133e9a | bellard | /* No break here */
|
397 | 29133e9a | bellard | default:
|
398 | 29133e9a | bellard | DPRINTF("%s: invalid write for command %02x\n",
|
399 | 29133e9a | bellard | __func__, pfl->cmd); |
400 | 29133e9a | bellard | goto reset_flash;
|
401 | 29133e9a | bellard | } |
402 | 29133e9a | bellard | case 4: |
403 | 29133e9a | bellard | switch (pfl->cmd) {
|
404 | 29133e9a | bellard | case 0xA0: |
405 | a1c7273b | Stefan Weil | /* Ignore writes while flash data write is occurring */
|
406 | 29133e9a | bellard | /* As we suppose write is immediate, this should never happen */
|
407 | 29133e9a | bellard | return;
|
408 | 29133e9a | bellard | case 0x80: |
409 | 29133e9a | bellard | goto check_unlock1;
|
410 | 29133e9a | bellard | default:
|
411 | 29133e9a | bellard | /* Should never happen */
|
412 | 29133e9a | bellard | DPRINTF("%s: invalid command state %02x (wc 4)\n",
|
413 | 29133e9a | bellard | __func__, pfl->cmd); |
414 | 29133e9a | bellard | goto reset_flash;
|
415 | 29133e9a | bellard | } |
416 | 29133e9a | bellard | break;
|
417 | 29133e9a | bellard | case 5: |
418 | 29133e9a | bellard | switch (cmd) {
|
419 | 29133e9a | bellard | case 0x10: |
420 | 368a354f | Peter Crosthwaite | if (boff != pfl->unlock_addr0) {
|
421 | f8be67ee | Blue Swirl | DPRINTF("%s: chip erase: invalid address " TARGET_FMT_plx "\n", |
422 | 29133e9a | bellard | __func__, offset); |
423 | 29133e9a | bellard | goto reset_flash;
|
424 | 29133e9a | bellard | } |
425 | 29133e9a | bellard | /* Chip erase */
|
426 | 29133e9a | bellard | DPRINTF("%s: start chip erase\n", __func__);
|
427 | de8efe8f | Jordan Justen | if (!pfl->ro) {
|
428 | de8efe8f | Jordan Justen | memset(pfl->storage, 0xFF, pfl->chip_len);
|
429 | de8efe8f | Jordan Justen | pflash_update(pfl, 0, pfl->chip_len);
|
430 | de8efe8f | Jordan Justen | } |
431 | 29133e9a | bellard | pfl->status = 0x00;
|
432 | 29133e9a | bellard | /* Let's wait 5 seconds before chip erase is done */
|
433 | 5fafdf24 | ths | qemu_mod_timer(pfl->timer, |
434 | 74475455 | Paolo Bonzini | qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() * 5));
|
435 | 29133e9a | bellard | break;
|
436 | 29133e9a | bellard | case 0x30: |
437 | 29133e9a | bellard | /* Sector erase */
|
438 | 29133e9a | bellard | p = pfl->storage; |
439 | 29133e9a | bellard | offset &= ~(pfl->sector_len - 1);
|
440 | f8be67ee | Blue Swirl | DPRINTF("%s: start sector erase at " TARGET_FMT_plx "\n", __func__, |
441 | e96efcfc | j_mayer | offset); |
442 | de8efe8f | Jordan Justen | if (!pfl->ro) {
|
443 | de8efe8f | Jordan Justen | memset(p + offset, 0xFF, pfl->sector_len);
|
444 | de8efe8f | Jordan Justen | pflash_update(pfl, offset, pfl->sector_len); |
445 | de8efe8f | Jordan Justen | } |
446 | 29133e9a | bellard | pfl->status = 0x00;
|
447 | 29133e9a | bellard | /* Let's wait 1/2 second before sector erase is done */
|
448 | 5fafdf24 | ths | qemu_mod_timer(pfl->timer, |
449 | 74475455 | Paolo Bonzini | qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() / 2));
|
450 | 29133e9a | bellard | break;
|
451 | 29133e9a | bellard | default:
|
452 | 29133e9a | bellard | DPRINTF("%s: invalid command %02x (wc 5)\n", __func__, cmd);
|
453 | 29133e9a | bellard | goto reset_flash;
|
454 | 29133e9a | bellard | } |
455 | 29133e9a | bellard | pfl->cmd = cmd; |
456 | 29133e9a | bellard | break;
|
457 | 29133e9a | bellard | case 6: |
458 | 29133e9a | bellard | switch (pfl->cmd) {
|
459 | 29133e9a | bellard | case 0x10: |
460 | 29133e9a | bellard | /* Ignore writes during chip erase */
|
461 | 29133e9a | bellard | return;
|
462 | 29133e9a | bellard | case 0x30: |
463 | 29133e9a | bellard | /* Ignore writes during sector erase */
|
464 | 29133e9a | bellard | return;
|
465 | 29133e9a | bellard | default:
|
466 | 29133e9a | bellard | /* Should never happen */
|
467 | 29133e9a | bellard | DPRINTF("%s: invalid command state %02x (wc 6)\n",
|
468 | 29133e9a | bellard | __func__, pfl->cmd); |
469 | 29133e9a | bellard | goto reset_flash;
|
470 | 29133e9a | bellard | } |
471 | 29133e9a | bellard | break;
|
472 | 29133e9a | bellard | case 7: /* Special value for CFI queries */ |
473 | 29133e9a | bellard | DPRINTF("%s: invalid write in CFI query mode\n", __func__);
|
474 | 29133e9a | bellard | goto reset_flash;
|
475 | 29133e9a | bellard | default:
|
476 | 29133e9a | bellard | /* Should never happen */
|
477 | 29133e9a | bellard | DPRINTF("%s: invalid write state (wc 7)\n", __func__);
|
478 | 29133e9a | bellard | goto reset_flash;
|
479 | 29133e9a | bellard | } |
480 | 29133e9a | bellard | pfl->wcycle++; |
481 | 29133e9a | bellard | |
482 | 29133e9a | bellard | return;
|
483 | 29133e9a | bellard | |
484 | 29133e9a | bellard | /* Reset flash */
|
485 | 29133e9a | bellard | reset_flash:
|
486 | 29133e9a | bellard | pfl->bypass = 0;
|
487 | 29133e9a | bellard | pfl->wcycle = 0;
|
488 | 29133e9a | bellard | pfl->cmd = 0;
|
489 | 29133e9a | bellard | return;
|
490 | 29133e9a | bellard | |
491 | 29133e9a | bellard | do_bypass:
|
492 | 29133e9a | bellard | pfl->wcycle = 2;
|
493 | 29133e9a | bellard | pfl->cmd = 0;
|
494 | 29133e9a | bellard | } |
495 | 29133e9a | bellard | |
496 | 29133e9a | bellard | |
497 | a8170e5e | Avi Kivity | static uint32_t pflash_readb_be(void *opaque, hwaddr addr) |
498 | 5f9fc5ad | Blue Swirl | { |
499 | 5f9fc5ad | Blue Swirl | return pflash_read(opaque, addr, 1, 1); |
500 | 5f9fc5ad | Blue Swirl | } |
501 | 5f9fc5ad | Blue Swirl | |
502 | a8170e5e | Avi Kivity | static uint32_t pflash_readb_le(void *opaque, hwaddr addr) |
503 | 5f9fc5ad | Blue Swirl | { |
504 | 5f9fc5ad | Blue Swirl | return pflash_read(opaque, addr, 1, 0); |
505 | 5f9fc5ad | Blue Swirl | } |
506 | 5f9fc5ad | Blue Swirl | |
507 | a8170e5e | Avi Kivity | static uint32_t pflash_readw_be(void *opaque, hwaddr addr) |
508 | 5f9fc5ad | Blue Swirl | { |
509 | 5f9fc5ad | Blue Swirl | pflash_t *pfl = opaque; |
510 | 5f9fc5ad | Blue Swirl | |
511 | 5f9fc5ad | Blue Swirl | return pflash_read(pfl, addr, 2, 1); |
512 | 5f9fc5ad | Blue Swirl | } |
513 | 5f9fc5ad | Blue Swirl | |
514 | a8170e5e | Avi Kivity | static uint32_t pflash_readw_le(void *opaque, hwaddr addr) |
515 | 5f9fc5ad | Blue Swirl | { |
516 | 5f9fc5ad | Blue Swirl | pflash_t *pfl = opaque; |
517 | 5f9fc5ad | Blue Swirl | |
518 | 5f9fc5ad | Blue Swirl | return pflash_read(pfl, addr, 2, 0); |
519 | 5f9fc5ad | Blue Swirl | } |
520 | 5f9fc5ad | Blue Swirl | |
521 | a8170e5e | Avi Kivity | static uint32_t pflash_readl_be(void *opaque, hwaddr addr) |
522 | 29133e9a | bellard | { |
523 | 5f9fc5ad | Blue Swirl | pflash_t *pfl = opaque; |
524 | 5f9fc5ad | Blue Swirl | |
525 | 5f9fc5ad | Blue Swirl | return pflash_read(pfl, addr, 4, 1); |
526 | 29133e9a | bellard | } |
527 | 29133e9a | bellard | |
528 | a8170e5e | Avi Kivity | static uint32_t pflash_readl_le(void *opaque, hwaddr addr) |
529 | 29133e9a | bellard | { |
530 | c227f099 | Anthony Liguori | pflash_t *pfl = opaque; |
531 | 29133e9a | bellard | |
532 | 5f9fc5ad | Blue Swirl | return pflash_read(pfl, addr, 4, 0); |
533 | 5f9fc5ad | Blue Swirl | } |
534 | 5f9fc5ad | Blue Swirl | |
535 | a8170e5e | Avi Kivity | static void pflash_writeb_be(void *opaque, hwaddr addr, |
536 | 5f9fc5ad | Blue Swirl | uint32_t value) |
537 | 5f9fc5ad | Blue Swirl | { |
538 | 5f9fc5ad | Blue Swirl | pflash_write(opaque, addr, value, 1, 1); |
539 | 29133e9a | bellard | } |
540 | 29133e9a | bellard | |
541 | a8170e5e | Avi Kivity | static void pflash_writeb_le(void *opaque, hwaddr addr, |
542 | 5f9fc5ad | Blue Swirl | uint32_t value) |
543 | 5f9fc5ad | Blue Swirl | { |
544 | 5f9fc5ad | Blue Swirl | pflash_write(opaque, addr, value, 1, 0); |
545 | 5f9fc5ad | Blue Swirl | } |
546 | 5f9fc5ad | Blue Swirl | |
547 | a8170e5e | Avi Kivity | static void pflash_writew_be(void *opaque, hwaddr addr, |
548 | 5f9fc5ad | Blue Swirl | uint32_t value) |
549 | 29133e9a | bellard | { |
550 | c227f099 | Anthony Liguori | pflash_t *pfl = opaque; |
551 | 29133e9a | bellard | |
552 | 5f9fc5ad | Blue Swirl | pflash_write(pfl, addr, value, 2, 1); |
553 | 29133e9a | bellard | } |
554 | 29133e9a | bellard | |
555 | a8170e5e | Avi Kivity | static void pflash_writew_le(void *opaque, hwaddr addr, |
556 | 5f9fc5ad | Blue Swirl | uint32_t value) |
557 | 29133e9a | bellard | { |
558 | 5f9fc5ad | Blue Swirl | pflash_t *pfl = opaque; |
559 | 5f9fc5ad | Blue Swirl | |
560 | 5f9fc5ad | Blue Swirl | pflash_write(pfl, addr, value, 2, 0); |
561 | 29133e9a | bellard | } |
562 | 29133e9a | bellard | |
563 | a8170e5e | Avi Kivity | static void pflash_writel_be(void *opaque, hwaddr addr, |
564 | 5f9fc5ad | Blue Swirl | uint32_t value) |
565 | 29133e9a | bellard | { |
566 | c227f099 | Anthony Liguori | pflash_t *pfl = opaque; |
567 | 29133e9a | bellard | |
568 | 5f9fc5ad | Blue Swirl | pflash_write(pfl, addr, value, 4, 1); |
569 | 29133e9a | bellard | } |
570 | 29133e9a | bellard | |
571 | a8170e5e | Avi Kivity | static void pflash_writel_le(void *opaque, hwaddr addr, |
572 | 5f9fc5ad | Blue Swirl | uint32_t value) |
573 | 29133e9a | bellard | { |
574 | c227f099 | Anthony Liguori | pflash_t *pfl = opaque; |
575 | 29133e9a | bellard | |
576 | 5f9fc5ad | Blue Swirl | pflash_write(pfl, addr, value, 4, 0); |
577 | 29133e9a | bellard | } |
578 | 29133e9a | bellard | |
579 | cfe5f011 | Avi Kivity | static const MemoryRegionOps pflash_cfi02_ops_be = { |
580 | cfe5f011 | Avi Kivity | .old_mmio = { |
581 | cfe5f011 | Avi Kivity | .read = { pflash_readb_be, pflash_readw_be, pflash_readl_be, }, |
582 | cfe5f011 | Avi Kivity | .write = { pflash_writeb_be, pflash_writew_be, pflash_writel_be, }, |
583 | cfe5f011 | Avi Kivity | }, |
584 | cfe5f011 | Avi Kivity | .endianness = DEVICE_NATIVE_ENDIAN, |
585 | 5f9fc5ad | Blue Swirl | }; |
586 | 5f9fc5ad | Blue Swirl | |
587 | cfe5f011 | Avi Kivity | static const MemoryRegionOps pflash_cfi02_ops_le = { |
588 | cfe5f011 | Avi Kivity | .old_mmio = { |
589 | cfe5f011 | Avi Kivity | .read = { pflash_readb_le, pflash_readw_le, pflash_readl_le, }, |
590 | cfe5f011 | Avi Kivity | .write = { pflash_writeb_le, pflash_writew_le, pflash_writel_le, }, |
591 | cfe5f011 | Avi Kivity | }, |
592 | cfe5f011 | Avi Kivity | .endianness = DEVICE_NATIVE_ENDIAN, |
593 | 29133e9a | bellard | }; |
594 | 29133e9a | bellard | |
595 | 368a354f | Peter Crosthwaite | static int pflash_cfi02_init(SysBusDevice *dev) |
596 | 29133e9a | bellard | { |
597 | 3509c396 | Hu Tao | pflash_t *pfl = CFI_PFLASH02(dev); |
598 | 368a354f | Peter Crosthwaite | uint32_t chip_len; |
599 | d0e7605e | Vijay Kumar | int ret;
|
600 | 29133e9a | bellard | |
601 | 368a354f | Peter Crosthwaite | chip_len = pfl->sector_len * pfl->nb_blocs; |
602 | 29133e9a | bellard | /* XXX: to be fixed */
|
603 | 95d1f3ed | j_mayer | #if 0
|
604 | 29133e9a | bellard | if (total_len != (8 * 1024 * 1024) && total_len != (16 * 1024 * 1024) &&
|
605 | 29133e9a | bellard | total_len != (32 * 1024 * 1024) && total_len != (64 * 1024 * 1024))
|
606 | 29133e9a | bellard | return NULL;
|
607 | 95d1f3ed | j_mayer | #endif
|
608 | 368a354f | Peter Crosthwaite | |
609 | 2d256e6f | Paolo Bonzini | memory_region_init_rom_device(&pfl->orig_mem, OBJECT(pfl), pfl->be ? |
610 | 368a354f | Peter Crosthwaite | &pflash_cfi02_ops_be : &pflash_cfi02_ops_le, |
611 | 368a354f | Peter Crosthwaite | pfl, pfl->name, chip_len); |
612 | 368a354f | Peter Crosthwaite | vmstate_register_ram(&pfl->orig_mem, DEVICE(pfl)); |
613 | cfe5f011 | Avi Kivity | pfl->storage = memory_region_get_ram_ptr(&pfl->orig_mem); |
614 | 4fbd24ba | balrog | pfl->chip_len = chip_len; |
615 | 29133e9a | bellard | if (pfl->bs) {
|
616 | 29133e9a | bellard | /* read the initial flash content */
|
617 | d0e7605e | Vijay Kumar | ret = bdrv_read(pfl->bs, 0, pfl->storage, chip_len >> 9); |
618 | d0e7605e | Vijay Kumar | if (ret < 0) { |
619 | 7267c094 | Anthony Liguori | g_free(pfl); |
620 | 368a354f | Peter Crosthwaite | return 1; |
621 | d0e7605e | Vijay Kumar | } |
622 | 29133e9a | bellard | } |
623 | de8efe8f | Jordan Justen | |
624 | cfe5f011 | Avi Kivity | pflash_setup_mappings(pfl); |
625 | cfe5f011 | Avi Kivity | pfl->rom_mode = 1;
|
626 | 368a354f | Peter Crosthwaite | sysbus_init_mmio(dev, &pfl->mem); |
627 | de8efe8f | Jordan Justen | |
628 | de8efe8f | Jordan Justen | if (pfl->bs) {
|
629 | de8efe8f | Jordan Justen | pfl->ro = bdrv_is_read_only(pfl->bs); |
630 | de8efe8f | Jordan Justen | } else {
|
631 | de8efe8f | Jordan Justen | pfl->ro = 0;
|
632 | de8efe8f | Jordan Justen | } |
633 | de8efe8f | Jordan Justen | |
634 | 74475455 | Paolo Bonzini | pfl->timer = qemu_new_timer_ns(vm_clock, pflash_timer, pfl); |
635 | 29133e9a | bellard | pfl->wcycle = 0;
|
636 | 29133e9a | bellard | pfl->cmd = 0;
|
637 | 29133e9a | bellard | pfl->status = 0;
|
638 | 29133e9a | bellard | /* Hardcoded CFI table (mostly from SG29 Spansion flash) */
|
639 | 29133e9a | bellard | pfl->cfi_len = 0x52;
|
640 | 29133e9a | bellard | /* Standard "QRY" string */
|
641 | 29133e9a | bellard | pfl->cfi_table[0x10] = 'Q'; |
642 | 29133e9a | bellard | pfl->cfi_table[0x11] = 'R'; |
643 | 29133e9a | bellard | pfl->cfi_table[0x12] = 'Y'; |
644 | 29133e9a | bellard | /* Command set (AMD/Fujitsu) */
|
645 | 29133e9a | bellard | pfl->cfi_table[0x13] = 0x02; |
646 | 29133e9a | bellard | pfl->cfi_table[0x14] = 0x00; |
647 | 78556820 | edgar_igl | /* Primary extended table address */
|
648 | 78556820 | edgar_igl | pfl->cfi_table[0x15] = 0x31; |
649 | 29133e9a | bellard | pfl->cfi_table[0x16] = 0x00; |
650 | 29133e9a | bellard | /* Alternate command set (none) */
|
651 | 29133e9a | bellard | pfl->cfi_table[0x17] = 0x00; |
652 | 29133e9a | bellard | pfl->cfi_table[0x18] = 0x00; |
653 | 29133e9a | bellard | /* Alternate extended table (none) */
|
654 | 29133e9a | bellard | pfl->cfi_table[0x19] = 0x00; |
655 | 29133e9a | bellard | pfl->cfi_table[0x1A] = 0x00; |
656 | 29133e9a | bellard | /* Vcc min */
|
657 | 29133e9a | bellard | pfl->cfi_table[0x1B] = 0x27; |
658 | 29133e9a | bellard | /* Vcc max */
|
659 | 29133e9a | bellard | pfl->cfi_table[0x1C] = 0x36; |
660 | 29133e9a | bellard | /* Vpp min (no Vpp pin) */
|
661 | 29133e9a | bellard | pfl->cfi_table[0x1D] = 0x00; |
662 | 29133e9a | bellard | /* Vpp max (no Vpp pin) */
|
663 | 29133e9a | bellard | pfl->cfi_table[0x1E] = 0x00; |
664 | 29133e9a | bellard | /* Reserved */
|
665 | 29133e9a | bellard | pfl->cfi_table[0x1F] = 0x07; |
666 | 78556820 | edgar_igl | /* Timeout for min size buffer write (NA) */
|
667 | 78556820 | edgar_igl | pfl->cfi_table[0x20] = 0x00; |
668 | 29133e9a | bellard | /* Typical timeout for block erase (512 ms) */
|
669 | 29133e9a | bellard | pfl->cfi_table[0x21] = 0x09; |
670 | 29133e9a | bellard | /* Typical timeout for full chip erase (4096 ms) */
|
671 | 29133e9a | bellard | pfl->cfi_table[0x22] = 0x0C; |
672 | 29133e9a | bellard | /* Reserved */
|
673 | 29133e9a | bellard | pfl->cfi_table[0x23] = 0x01; |
674 | 78556820 | edgar_igl | /* Max timeout for buffer write (NA) */
|
675 | 78556820 | edgar_igl | pfl->cfi_table[0x24] = 0x00; |
676 | 29133e9a | bellard | /* Max timeout for block erase */
|
677 | 29133e9a | bellard | pfl->cfi_table[0x25] = 0x0A; |
678 | 29133e9a | bellard | /* Max timeout for chip erase */
|
679 | 29133e9a | bellard | pfl->cfi_table[0x26] = 0x0D; |
680 | 29133e9a | bellard | /* Device size */
|
681 | 78556820 | edgar_igl | pfl->cfi_table[0x27] = ctz32(chip_len);
|
682 | 29133e9a | bellard | /* Flash device interface (8 & 16 bits) */
|
683 | 29133e9a | bellard | pfl->cfi_table[0x28] = 0x02; |
684 | 29133e9a | bellard | pfl->cfi_table[0x29] = 0x00; |
685 | 29133e9a | bellard | /* Max number of bytes in multi-bytes write */
|
686 | 95d1f3ed | j_mayer | /* XXX: disable buffered write as it's not supported */
|
687 | 95d1f3ed | j_mayer | // pfl->cfi_table[0x2A] = 0x05;
|
688 | 95d1f3ed | j_mayer | pfl->cfi_table[0x2A] = 0x00; |
689 | 29133e9a | bellard | pfl->cfi_table[0x2B] = 0x00; |
690 | 29133e9a | bellard | /* Number of erase block regions (uniform) */
|
691 | 29133e9a | bellard | pfl->cfi_table[0x2C] = 0x01; |
692 | 29133e9a | bellard | /* Erase block region 1 */
|
693 | 368a354f | Peter Crosthwaite | pfl->cfi_table[0x2D] = pfl->nb_blocs - 1; |
694 | 368a354f | Peter Crosthwaite | pfl->cfi_table[0x2E] = (pfl->nb_blocs - 1) >> 8; |
695 | 368a354f | Peter Crosthwaite | pfl->cfi_table[0x2F] = pfl->sector_len >> 8; |
696 | 368a354f | Peter Crosthwaite | pfl->cfi_table[0x30] = pfl->sector_len >> 16; |
697 | 29133e9a | bellard | |
698 | 78556820 | edgar_igl | /* Extended */
|
699 | 78556820 | edgar_igl | pfl->cfi_table[0x31] = 'P'; |
700 | 78556820 | edgar_igl | pfl->cfi_table[0x32] = 'R'; |
701 | 78556820 | edgar_igl | pfl->cfi_table[0x33] = 'I'; |
702 | 78556820 | edgar_igl | |
703 | 78556820 | edgar_igl | pfl->cfi_table[0x34] = '1'; |
704 | 78556820 | edgar_igl | pfl->cfi_table[0x35] = '0'; |
705 | 78556820 | edgar_igl | |
706 | 78556820 | edgar_igl | pfl->cfi_table[0x36] = 0x00; |
707 | 78556820 | edgar_igl | pfl->cfi_table[0x37] = 0x00; |
708 | 78556820 | edgar_igl | pfl->cfi_table[0x38] = 0x00; |
709 | 78556820 | edgar_igl | pfl->cfi_table[0x39] = 0x00; |
710 | 78556820 | edgar_igl | |
711 | 78556820 | edgar_igl | pfl->cfi_table[0x3a] = 0x00; |
712 | 78556820 | edgar_igl | |
713 | 78556820 | edgar_igl | pfl->cfi_table[0x3b] = 0x00; |
714 | 78556820 | edgar_igl | pfl->cfi_table[0x3c] = 0x00; |
715 | 78556820 | edgar_igl | |
716 | 368a354f | Peter Crosthwaite | return 0; |
717 | 368a354f | Peter Crosthwaite | } |
718 | 368a354f | Peter Crosthwaite | |
719 | 368a354f | Peter Crosthwaite | static Property pflash_cfi02_properties[] = {
|
720 | 368a354f | Peter Crosthwaite | DEFINE_PROP_DRIVE("drive", struct pflash_t, bs), |
721 | 368a354f | Peter Crosthwaite | DEFINE_PROP_UINT32("num-blocks", struct pflash_t, nb_blocs, 0), |
722 | 368a354f | Peter Crosthwaite | DEFINE_PROP_UINT32("sector-length", struct pflash_t, sector_len, 0), |
723 | 368a354f | Peter Crosthwaite | DEFINE_PROP_UINT8("width", struct pflash_t, width, 0), |
724 | 368a354f | Peter Crosthwaite | DEFINE_PROP_UINT8("mappings", struct pflash_t, mappings, 0), |
725 | 368a354f | Peter Crosthwaite | DEFINE_PROP_UINT8("big-endian", struct pflash_t, be, 0), |
726 | 368a354f | Peter Crosthwaite | DEFINE_PROP_UINT16("id0", struct pflash_t, ident0, 0), |
727 | 368a354f | Peter Crosthwaite | DEFINE_PROP_UINT16("id1", struct pflash_t, ident1, 0), |
728 | 368a354f | Peter Crosthwaite | DEFINE_PROP_UINT16("id2", struct pflash_t, ident2, 0), |
729 | 368a354f | Peter Crosthwaite | DEFINE_PROP_UINT16("id3", struct pflash_t, ident3, 0), |
730 | 368a354f | Peter Crosthwaite | DEFINE_PROP_UINT16("unlock-addr0", struct pflash_t, unlock_addr0, 0), |
731 | 368a354f | Peter Crosthwaite | DEFINE_PROP_UINT16("unlock-addr1", struct pflash_t, unlock_addr1, 0), |
732 | 368a354f | Peter Crosthwaite | DEFINE_PROP_STRING("name", struct pflash_t, name), |
733 | 368a354f | Peter Crosthwaite | DEFINE_PROP_END_OF_LIST(), |
734 | 368a354f | Peter Crosthwaite | }; |
735 | 368a354f | Peter Crosthwaite | |
736 | 368a354f | Peter Crosthwaite | static void pflash_cfi02_class_init(ObjectClass *klass, void *data) |
737 | 368a354f | Peter Crosthwaite | { |
738 | 368a354f | Peter Crosthwaite | DeviceClass *dc = DEVICE_CLASS(klass); |
739 | 368a354f | Peter Crosthwaite | SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); |
740 | 368a354f | Peter Crosthwaite | |
741 | 368a354f | Peter Crosthwaite | k->init = pflash_cfi02_init; |
742 | 368a354f | Peter Crosthwaite | dc->props = pflash_cfi02_properties; |
743 | 368a354f | Peter Crosthwaite | } |
744 | 368a354f | Peter Crosthwaite | |
745 | 368a354f | Peter Crosthwaite | static const TypeInfo pflash_cfi02_info = { |
746 | 3509c396 | Hu Tao | .name = TYPE_CFI_PFLASH02, |
747 | 368a354f | Peter Crosthwaite | .parent = TYPE_SYS_BUS_DEVICE, |
748 | 368a354f | Peter Crosthwaite | .instance_size = sizeof(struct pflash_t), |
749 | 368a354f | Peter Crosthwaite | .class_init = pflash_cfi02_class_init, |
750 | 368a354f | Peter Crosthwaite | }; |
751 | 368a354f | Peter Crosthwaite | |
752 | 368a354f | Peter Crosthwaite | static void pflash_cfi02_register_types(void) |
753 | 368a354f | Peter Crosthwaite | { |
754 | 368a354f | Peter Crosthwaite | type_register_static(&pflash_cfi02_info); |
755 | 368a354f | Peter Crosthwaite | } |
756 | 368a354f | Peter Crosthwaite | |
757 | 368a354f | Peter Crosthwaite | type_init(pflash_cfi02_register_types) |
758 | 368a354f | Peter Crosthwaite | |
759 | 368a354f | Peter Crosthwaite | pflash_t *pflash_cfi02_register(hwaddr base, |
760 | 368a354f | Peter Crosthwaite | DeviceState *qdev, const char *name, |
761 | 368a354f | Peter Crosthwaite | hwaddr size, |
762 | 368a354f | Peter Crosthwaite | BlockDriverState *bs, uint32_t sector_len, |
763 | 368a354f | Peter Crosthwaite | int nb_blocs, int nb_mappings, int width, |
764 | 368a354f | Peter Crosthwaite | uint16_t id0, uint16_t id1, |
765 | 368a354f | Peter Crosthwaite | uint16_t id2, uint16_t id3, |
766 | 368a354f | Peter Crosthwaite | uint16_t unlock_addr0, uint16_t unlock_addr1, |
767 | 368a354f | Peter Crosthwaite | int be)
|
768 | 368a354f | Peter Crosthwaite | { |
769 | 3509c396 | Hu Tao | DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH02);
|
770 | 368a354f | Peter Crosthwaite | |
771 | 368a354f | Peter Crosthwaite | if (bs && qdev_prop_set_drive(dev, "drive", bs)) { |
772 | 368a354f | Peter Crosthwaite | abort(); |
773 | 368a354f | Peter Crosthwaite | } |
774 | 368a354f | Peter Crosthwaite | qdev_prop_set_uint32(dev, "num-blocks", nb_blocs);
|
775 | 368a354f | Peter Crosthwaite | qdev_prop_set_uint32(dev, "sector-length", sector_len);
|
776 | 368a354f | Peter Crosthwaite | qdev_prop_set_uint8(dev, "width", width);
|
777 | 368a354f | Peter Crosthwaite | qdev_prop_set_uint8(dev, "mappings", nb_mappings);
|
778 | 368a354f | Peter Crosthwaite | qdev_prop_set_uint8(dev, "big-endian", !!be);
|
779 | 368a354f | Peter Crosthwaite | qdev_prop_set_uint16(dev, "id0", id0);
|
780 | 368a354f | Peter Crosthwaite | qdev_prop_set_uint16(dev, "id1", id1);
|
781 | 368a354f | Peter Crosthwaite | qdev_prop_set_uint16(dev, "id2", id2);
|
782 | 368a354f | Peter Crosthwaite | qdev_prop_set_uint16(dev, "id3", id3);
|
783 | 368a354f | Peter Crosthwaite | qdev_prop_set_uint16(dev, "unlock-addr0", unlock_addr0);
|
784 | 368a354f | Peter Crosthwaite | qdev_prop_set_uint16(dev, "unlock-addr1", unlock_addr1);
|
785 | 368a354f | Peter Crosthwaite | qdev_prop_set_string(dev, "name", name);
|
786 | 368a354f | Peter Crosthwaite | qdev_init_nofail(dev); |
787 | 368a354f | Peter Crosthwaite | |
788 | 3509c396 | Hu Tao | sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
|
789 | 3509c396 | Hu Tao | return CFI_PFLASH02(dev);
|
790 | 29133e9a | bellard | } |