]>
Commit | Line | Data |
---|---|---|
05ee37eb AZ |
1 | /* |
2 | * CFI parallel flash with Intel command set emulation | |
3 | * | |
4 | * Copyright (c) 2006 Thorsten Zitterell | |
5 | * Copyright (c) 2005 Jocelyn Mayer | |
6 | * | |
7 | * This library is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU Lesser General Public | |
9 | * License as published by the Free Software Foundation; either | |
3564a919 | 10 | * version 2.1 of the License, or (at your option) any later version. |
05ee37eb AZ |
11 | * |
12 | * This library is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * Lesser General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU Lesser General Public | |
8167ee88 | 18 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
05ee37eb AZ |
19 | */ |
20 | ||
21 | /* | |
22 | * For now, this code can emulate flashes of 1, 2 or 4 bytes width. | |
23 | * Supported commands/modes are: | |
24 | * - flash read | |
25 | * - flash write | |
26 | * - flash ID read | |
27 | * - sector erase | |
28 | * - CFI queries | |
29 | * | |
30 | * It does not support timings | |
31 | * It does not support flash interleaving | |
32 | * It does not implement software data protection as found in many real chips | |
33 | * It does not implement erase suspend/resume commands | |
34 | * It does not implement multiple sectors erase | |
35 | * | |
36 | * It does not implement much more ... | |
37 | */ | |
38 | ||
80c71a24 | 39 | #include "qemu/osdep.h" |
06f15217 | 40 | #include "hw/block/block.h" |
0d09e41a | 41 | #include "hw/block/flash.h" |
a27bd6c7 | 42 | #include "hw/qdev-properties.h" |
ce35e229 | 43 | #include "hw/qdev-properties-system.h" |
4be74634 | 44 | #include "sysemu/block-backend.h" |
da34e65c | 45 | #include "qapi/error.h" |
1857b9db | 46 | #include "qemu/error-report.h" |
1997b485 | 47 | #include "qemu/bitops.h" |
2d731dbd | 48 | #include "qemu/error-report.h" |
1de7afc9 | 49 | #include "qemu/host-utils.h" |
03dd024f | 50 | #include "qemu/log.h" |
0b8fa32f | 51 | #include "qemu/module.h" |
2d731dbd | 52 | #include "qemu/option.h" |
83c9f4ca | 53 | #include "hw/sysbus.h" |
d6454270 | 54 | #include "migration/vmstate.h" |
2d731dbd | 55 | #include "sysemu/blockdev.h" |
54d31236 | 56 | #include "sysemu/runstate.h" |
13019f1f | 57 | #include "trace.h" |
05ee37eb | 58 | |
e9809422 | 59 | #define PFLASH_BE 0 |
f71e42a5 | 60 | #define PFLASH_SECURE 1 |
e9809422 | 61 | |
16434065 | 62 | struct PFlashCFI01 { |
f1b44f0e HT |
63 | /*< private >*/ |
64 | SysBusDevice parent_obj; | |
65 | /*< public >*/ | |
66 | ||
4be74634 | 67 | BlockBackend *blk; |
368a354f PC |
68 | uint32_t nb_blocs; |
69 | uint64_t sector_len; | |
4b6fedca | 70 | uint8_t bank_width; |
1997b485 | 71 | uint8_t device_width; /* If 0, device width not specified. */ |
fa21a7b1 | 72 | uint8_t max_device_width; /* max device width in bytes */ |
e9809422 | 73 | uint32_t features; |
d8d24fb7 | 74 | uint8_t wcycle; /* if 0, the flash is read normally */ |
2231bee2 | 75 | bool ro; |
05ee37eb AZ |
76 | uint8_t cmd; |
77 | uint8_t status; | |
368a354f PC |
78 | uint16_t ident0; |
79 | uint16_t ident1; | |
80 | uint16_t ident2; | |
81 | uint16_t ident3; | |
05ee37eb | 82 | uint8_t cfi_table[0x52]; |
d8d24fb7 | 83 | uint64_t counter; |
b4bf0a9a | 84 | unsigned int writeblock_size; |
cfe5f011 | 85 | MemoryRegion mem; |
368a354f | 86 | char *name; |
05ee37eb | 87 | void *storage; |
90c647db | 88 | VMChangeStateEntry *vmstate; |
feb0b1aa | 89 | bool old_multiple_chip_handling; |
05ee37eb AZ |
90 | }; |
91 | ||
4c0cfc72 LE |
92 | static int pflash_post_load(void *opaque, int version_id); |
93 | ||
d8d24fb7 PM |
94 | static const VMStateDescription vmstate_pflash = { |
95 | .name = "pflash_cfi01", | |
96 | .version_id = 1, | |
97 | .minimum_version_id = 1, | |
4c0cfc72 | 98 | .post_load = pflash_post_load, |
d8d24fb7 | 99 | .fields = (VMStateField[]) { |
16434065 MA |
100 | VMSTATE_UINT8(wcycle, PFlashCFI01), |
101 | VMSTATE_UINT8(cmd, PFlashCFI01), | |
102 | VMSTATE_UINT8(status, PFlashCFI01), | |
103 | VMSTATE_UINT64(counter, PFlashCFI01), | |
d8d24fb7 PM |
104 | VMSTATE_END_OF_LIST() |
105 | } | |
106 | }; | |
107 | ||
ccd8014b PMD |
108 | /* |
109 | * Perform a CFI query based on the bank width of the flash. | |
4433e660 RF |
110 | * If this code is called we know we have a device_width set for |
111 | * this flash. | |
112 | */ | |
16434065 | 113 | static uint32_t pflash_cfi_query(PFlashCFI01 *pfl, hwaddr offset) |
4433e660 RF |
114 | { |
115 | int i; | |
116 | uint32_t resp = 0; | |
117 | hwaddr boff; | |
118 | ||
ccd8014b PMD |
119 | /* |
120 | * Adjust incoming offset to match expected device-width | |
4433e660 RF |
121 | * addressing. CFI query addresses are always specified in terms of |
122 | * the maximum supported width of the device. This means that x8 | |
123 | * devices and x8/x16 devices in x8 mode behave differently. For | |
124 | * devices that are not used at their max width, we will be | |
125 | * provided with addresses that use higher address bits than | |
126 | * expected (based on the max width), so we will shift them lower | |
127 | * so that they will match the addresses used when | |
128 | * device_width==max_device_width. | |
129 | */ | |
130 | boff = offset >> (ctz32(pfl->bank_width) + | |
131 | ctz32(pfl->max_device_width) - ctz32(pfl->device_width)); | |
132 | ||
07c13a71 | 133 | if (boff >= sizeof(pfl->cfi_table)) { |
4433e660 RF |
134 | return 0; |
135 | } | |
ccd8014b PMD |
136 | /* |
137 | * Now we will construct the CFI response generated by a single | |
4433e660 RF |
138 | * device, then replicate that for all devices that make up the |
139 | * bus. For wide parts used in x8 mode, CFI query responses | |
140 | * are different than native byte-wide parts. | |
141 | */ | |
142 | resp = pfl->cfi_table[boff]; | |
143 | if (pfl->device_width != pfl->max_device_width) { | |
144 | /* The only case currently supported is x8 mode for a | |
145 | * wider part. | |
146 | */ | |
147 | if (pfl->device_width != 1 || pfl->bank_width > 4) { | |
91316cbb DE |
148 | trace_pflash_unsupported_device_configuration(pfl->name, |
149 | pfl->device_width, pfl->max_device_width); | |
4433e660 RF |
150 | return 0; |
151 | } | |
152 | /* CFI query data is repeated, rather than zero padded for | |
153 | * wide devices used in x8 mode. | |
154 | */ | |
155 | for (i = 1; i < pfl->max_device_width; i++) { | |
156 | resp = deposit32(resp, 8 * i, 8, pfl->cfi_table[boff]); | |
157 | } | |
158 | } | |
159 | /* Replicate responses for each device in bank. */ | |
160 | if (pfl->device_width < pfl->bank_width) { | |
161 | for (i = pfl->device_width; | |
162 | i < pfl->bank_width; i += pfl->device_width) { | |
163 | resp = deposit32(resp, 8 * i, 8 * pfl->device_width, resp); | |
164 | } | |
165 | } | |
166 | ||
167 | return resp; | |
168 | } | |
169 | ||
0163a2dc RF |
170 | |
171 | ||
172 | /* Perform a device id query based on the bank width of the flash. */ | |
16434065 | 173 | static uint32_t pflash_devid_query(PFlashCFI01 *pfl, hwaddr offset) |
0163a2dc RF |
174 | { |
175 | int i; | |
176 | uint32_t resp; | |
177 | hwaddr boff; | |
178 | ||
ccd8014b PMD |
179 | /* |
180 | * Adjust incoming offset to match expected device-width | |
0163a2dc RF |
181 | * addressing. Device ID read addresses are always specified in |
182 | * terms of the maximum supported width of the device. This means | |
183 | * that x8 devices and x8/x16 devices in x8 mode behave | |
184 | * differently. For devices that are not used at their max width, | |
185 | * we will be provided with addresses that use higher address bits | |
186 | * than expected (based on the max width), so we will shift them | |
187 | * lower so that they will match the addresses used when | |
188 | * device_width==max_device_width. | |
189 | */ | |
190 | boff = offset >> (ctz32(pfl->bank_width) + | |
191 | ctz32(pfl->max_device_width) - ctz32(pfl->device_width)); | |
192 | ||
ccd8014b PMD |
193 | /* |
194 | * Mask off upper bits which may be used in to query block | |
0163a2dc RF |
195 | * or sector lock status at other addresses. |
196 | * Offsets 2/3 are block lock status, is not emulated. | |
197 | */ | |
198 | switch (boff & 0xFF) { | |
199 | case 0: | |
200 | resp = pfl->ident0; | |
91316cbb | 201 | trace_pflash_manufacturer_id(pfl->name, resp); |
0163a2dc RF |
202 | break; |
203 | case 1: | |
204 | resp = pfl->ident1; | |
91316cbb | 205 | trace_pflash_device_id(pfl->name, resp); |
0163a2dc RF |
206 | break; |
207 | default: | |
91316cbb | 208 | trace_pflash_device_info(pfl->name, offset); |
0163a2dc | 209 | return 0; |
0163a2dc RF |
210 | } |
211 | /* Replicate responses for each device in bank. */ | |
212 | if (pfl->device_width < pfl->bank_width) { | |
213 | for (i = pfl->device_width; | |
214 | i < pfl->bank_width; i += pfl->device_width) { | |
215 | resp = deposit32(resp, 8 * i, 8 * pfl->device_width, resp); | |
216 | } | |
217 | } | |
218 | ||
219 | return resp; | |
220 | } | |
221 | ||
16434065 | 222 | static uint32_t pflash_data_read(PFlashCFI01 *pfl, hwaddr offset, |
f71e42a5 PB |
223 | int width, int be) |
224 | { | |
225 | uint8_t *p; | |
226 | uint32_t ret; | |
227 | ||
228 | p = pfl->storage; | |
229 | switch (width) { | |
230 | case 1: | |
231 | ret = p[offset]; | |
f71e42a5 PB |
232 | break; |
233 | case 2: | |
234 | if (be) { | |
235 | ret = p[offset] << 8; | |
236 | ret |= p[offset + 1]; | |
237 | } else { | |
238 | ret = p[offset]; | |
239 | ret |= p[offset + 1] << 8; | |
240 | } | |
f71e42a5 PB |
241 | break; |
242 | case 4: | |
243 | if (be) { | |
244 | ret = p[offset] << 24; | |
245 | ret |= p[offset + 1] << 16; | |
246 | ret |= p[offset + 2] << 8; | |
247 | ret |= p[offset + 3]; | |
248 | } else { | |
249 | ret = p[offset]; | |
250 | ret |= p[offset + 1] << 8; | |
251 | ret |= p[offset + 2] << 16; | |
252 | ret |= p[offset + 3] << 24; | |
253 | } | |
f71e42a5 PB |
254 | break; |
255 | default: | |
f71e42a5 PB |
256 | abort(); |
257 | } | |
91316cbb | 258 | trace_pflash_data_read(pfl->name, offset, width, ret); |
f71e42a5 PB |
259 | return ret; |
260 | } | |
261 | ||
16434065 MA |
262 | static uint32_t pflash_read(PFlashCFI01 *pfl, hwaddr offset, |
263 | int width, int be) | |
05ee37eb | 264 | { |
a8170e5e | 265 | hwaddr boff; |
05ee37eb | 266 | uint32_t ret; |
05ee37eb AZ |
267 | |
268 | ret = -1; | |
05ee37eb | 269 | switch (pfl->cmd) { |
1be97bf2 PM |
270 | default: |
271 | /* This should never happen : reset state & treat it as a read */ | |
91316cbb | 272 | trace_pflash_read_unknown_state(pfl->name, pfl->cmd); |
1be97bf2 | 273 | pfl->wcycle = 0; |
aba53a12 PMD |
274 | /* |
275 | * The command 0x00 is not assigned by the CFI open standard, | |
276 | * but QEMU historically uses it for the READ_ARRAY command (0xff). | |
277 | */ | |
278 | pfl->cmd = 0x00; | |
1be97bf2 | 279 | /* fall through to read code */ |
aba53a12 | 280 | case 0x00: /* This model reset value for READ_ARRAY (not CFI compliant) */ |
05ee37eb | 281 | /* Flash area read */ |
f71e42a5 | 282 | ret = pflash_data_read(pfl, offset, width, be); |
05ee37eb | 283 | break; |
6e392787 | 284 | case 0x10: /* Single byte program */ |
05ee37eb | 285 | case 0x20: /* Block erase */ |
6e392787 PM |
286 | case 0x28: /* Block erase */ |
287 | case 0x40: /* single byte program */ | |
05ee37eb AZ |
288 | case 0x50: /* Clear status register */ |
289 | case 0x60: /* Block /un)lock */ | |
290 | case 0x70: /* Status Register */ | |
291 | case 0xe8: /* Write block */ | |
ccd8014b PMD |
292 | /* |
293 | * Status register read. Return status from each device in | |
2003889f RF |
294 | * bank. |
295 | */ | |
05ee37eb | 296 | ret = pfl->status; |
2003889f RF |
297 | if (pfl->device_width && width > pfl->device_width) { |
298 | int shift = pfl->device_width * 8; | |
299 | while (shift + pfl->device_width * 8 <= width * 8) { | |
300 | ret |= pfl->status << shift; | |
301 | shift += pfl->device_width * 8; | |
302 | } | |
303 | } else if (!pfl->device_width && width > 2) { | |
ccd8014b PMD |
304 | /* |
305 | * Handle 32 bit flash cases where device width is not | |
2003889f RF |
306 | * set. (Existing behavior before device width added.) |
307 | */ | |
ea0a4f34 PB |
308 | ret |= pfl->status << 16; |
309 | } | |
91316cbb | 310 | trace_pflash_read_status(pfl->name, ret); |
05ee37eb | 311 | break; |
0b2ec6fc | 312 | case 0x90: |
0163a2dc RF |
313 | if (!pfl->device_width) { |
314 | /* Preserve old behavior if device width not specified */ | |
315 | boff = offset & 0xFF; | |
316 | if (pfl->bank_width == 2) { | |
317 | boff = boff >> 1; | |
318 | } else if (pfl->bank_width == 4) { | |
319 | boff = boff >> 2; | |
320 | } | |
4433e660 | 321 | |
0163a2dc RF |
322 | switch (boff) { |
323 | case 0: | |
324 | ret = pfl->ident0 << 8 | pfl->ident1; | |
91316cbb | 325 | trace_pflash_manufacturer_id(pfl->name, ret); |
0163a2dc RF |
326 | break; |
327 | case 1: | |
328 | ret = pfl->ident2 << 8 | pfl->ident3; | |
91316cbb | 329 | trace_pflash_device_id(pfl->name, ret); |
0163a2dc RF |
330 | break; |
331 | default: | |
91316cbb | 332 | trace_pflash_device_info(pfl->name, boff); |
0163a2dc RF |
333 | ret = 0; |
334 | break; | |
335 | } | |
336 | } else { | |
ccd8014b PMD |
337 | /* |
338 | * If we have a read larger than the bank_width, combine multiple | |
0163a2dc RF |
339 | * manufacturer/device ID queries into a single response. |
340 | */ | |
341 | int i; | |
342 | for (i = 0; i < width; i += pfl->bank_width) { | |
343 | ret = deposit32(ret, i * 8, pfl->bank_width * 8, | |
344 | pflash_devid_query(pfl, | |
345 | offset + i * pfl->bank_width)); | |
346 | } | |
0b2ec6fc MW |
347 | } |
348 | break; | |
05ee37eb | 349 | case 0x98: /* Query mode */ |
4433e660 RF |
350 | if (!pfl->device_width) { |
351 | /* Preserve old behavior if device width not specified */ | |
352 | boff = offset & 0xFF; | |
353 | if (pfl->bank_width == 2) { | |
354 | boff = boff >> 1; | |
355 | } else if (pfl->bank_width == 4) { | |
356 | boff = boff >> 2; | |
357 | } | |
358 | ||
07c13a71 | 359 | if (boff < sizeof(pfl->cfi_table)) { |
4433e660 | 360 | ret = pfl->cfi_table[boff]; |
07c13a71 PMD |
361 | } else { |
362 | ret = 0; | |
4433e660 RF |
363 | } |
364 | } else { | |
ccd8014b PMD |
365 | /* |
366 | * If we have a read larger than the bank_width, combine multiple | |
4433e660 RF |
367 | * CFI queries into a single response. |
368 | */ | |
369 | int i; | |
370 | for (i = 0; i < width; i += pfl->bank_width) { | |
371 | ret = deposit32(ret, i * 8, pfl->bank_width * 8, | |
372 | pflash_cfi_query(pfl, | |
373 | offset + i * pfl->bank_width)); | |
374 | } | |
375 | } | |
376 | ||
05ee37eb | 377 | break; |
05ee37eb | 378 | } |
91316cbb | 379 | trace_pflash_io_read(pfl->name, offset, width, ret, pfl->cmd, pfl->wcycle); |
e8aa2d95 | 380 | |
05ee37eb AZ |
381 | return ret; |
382 | } | |
383 | ||
384 | /* update flash content on disk */ | |
16434065 | 385 | static void pflash_update(PFlashCFI01 *pfl, int offset, |
05ee37eb AZ |
386 | int size) |
387 | { | |
388 | int offset_end; | |
1857b9db | 389 | int ret; |
4be74634 | 390 | if (pfl->blk) { |
05ee37eb | 391 | offset_end = offset + size; |
098e732d EB |
392 | /* widen to sector boundaries */ |
393 | offset = QEMU_ALIGN_DOWN(offset, BDRV_SECTOR_SIZE); | |
394 | offset_end = QEMU_ALIGN_UP(offset_end, BDRV_SECTOR_SIZE); | |
a9262f55 AF |
395 | ret = blk_pwrite(pfl->blk, offset, offset_end - offset, |
396 | pfl->storage + offset, 0); | |
1857b9db MA |
397 | if (ret < 0) { |
398 | /* TODO set error bit in status */ | |
399 | error_report("Could not update PFLASH: %s", strerror(-ret)); | |
400 | } | |
05ee37eb AZ |
401 | } |
402 | } | |
403 | ||
16434065 | 404 | static inline void pflash_data_write(PFlashCFI01 *pfl, hwaddr offset, |
3d08ff69 | 405 | uint32_t value, int width, int be) |
d361be25 AZ |
406 | { |
407 | uint8_t *p = pfl->storage; | |
408 | ||
91316cbb | 409 | trace_pflash_data_write(pfl->name, offset, width, value, pfl->counter); |
d361be25 AZ |
410 | switch (width) { |
411 | case 1: | |
412 | p[offset] = value; | |
d361be25 AZ |
413 | break; |
414 | case 2: | |
3d08ff69 BS |
415 | if (be) { |
416 | p[offset] = value >> 8; | |
417 | p[offset + 1] = value; | |
418 | } else { | |
419 | p[offset] = value; | |
420 | p[offset + 1] = value >> 8; | |
421 | } | |
d361be25 AZ |
422 | break; |
423 | case 4: | |
3d08ff69 BS |
424 | if (be) { |
425 | p[offset] = value >> 24; | |
426 | p[offset + 1] = value >> 16; | |
427 | p[offset + 2] = value >> 8; | |
428 | p[offset + 3] = value; | |
429 | } else { | |
430 | p[offset] = value; | |
431 | p[offset + 1] = value >> 8; | |
432 | p[offset + 2] = value >> 16; | |
433 | p[offset + 3] = value >> 24; | |
434 | } | |
d361be25 AZ |
435 | break; |
436 | } | |
437 | ||
438 | } | |
439 | ||
16434065 | 440 | static void pflash_write(PFlashCFI01 *pfl, hwaddr offset, |
3d08ff69 | 441 | uint32_t value, int width, int be) |
05ee37eb | 442 | { |
05ee37eb AZ |
443 | uint8_t *p; |
444 | uint8_t cmd; | |
445 | ||
05ee37eb | 446 | cmd = value; |
05ee37eb | 447 | |
91316cbb | 448 | trace_pflash_io_write(pfl->name, offset, width, value, pfl->wcycle); |
e9cbbcac EI |
449 | if (!pfl->wcycle) { |
450 | /* Set the device in I/O access mode */ | |
5f9a5ea1 | 451 | memory_region_rom_device_set_romd(&pfl->mem, false); |
e9cbbcac | 452 | } |
05ee37eb AZ |
453 | |
454 | switch (pfl->wcycle) { | |
455 | case 0: | |
456 | /* read mode */ | |
457 | switch (cmd) { | |
aba53a12 | 458 | case 0x00: /* This model reset value for READ_ARRAY (not CFI) */ |
3072182d | 459 | goto mode_read_array; |
d361be25 AZ |
460 | case 0x10: /* Single Byte Program */ |
461 | case 0x40: /* Single Byte Program */ | |
91316cbb | 462 | trace_pflash_write(pfl->name, "single byte program (0)"); |
d361be25 | 463 | break; |
05ee37eb AZ |
464 | case 0x20: /* Block erase */ |
465 | p = pfl->storage; | |
466 | offset &= ~(pfl->sector_len - 1); | |
467 | ||
91316cbb | 468 | trace_pflash_write_block_erase(pfl->name, offset, pfl->sector_len); |
05ee37eb | 469 | |
de8efe8f JJ |
470 | if (!pfl->ro) { |
471 | memset(p + offset, 0xff, pfl->sector_len); | |
472 | pflash_update(pfl, offset, pfl->sector_len); | |
473 | } else { | |
474 | pfl->status |= 0x20; /* Block erase error */ | |
475 | } | |
05ee37eb AZ |
476 | pfl->status |= 0x80; /* Ready! */ |
477 | break; | |
478 | case 0x50: /* Clear status bits */ | |
91316cbb | 479 | trace_pflash_write(pfl->name, "clear status bits"); |
05ee37eb | 480 | pfl->status = 0x0; |
3072182d | 481 | goto mode_read_array; |
05ee37eb | 482 | case 0x60: /* Block (un)lock */ |
91316cbb | 483 | trace_pflash_write(pfl->name, "block unlock"); |
05ee37eb AZ |
484 | break; |
485 | case 0x70: /* Status Register */ | |
91316cbb | 486 | trace_pflash_write(pfl->name, "read status register"); |
05ee37eb AZ |
487 | pfl->cmd = cmd; |
488 | return; | |
0b2ec6fc | 489 | case 0x90: /* Read Device ID */ |
91316cbb | 490 | trace_pflash_write(pfl->name, "read device information"); |
0b2ec6fc MW |
491 | pfl->cmd = cmd; |
492 | return; | |
05ee37eb | 493 | case 0x98: /* CFI query */ |
91316cbb | 494 | trace_pflash_write(pfl->name, "CFI query"); |
05ee37eb AZ |
495 | break; |
496 | case 0xe8: /* Write to buffer */ | |
91316cbb | 497 | trace_pflash_write(pfl->name, "write to buffer"); |
4dbda935 MA |
498 | /* FIXME should save @offset, @width for case 1+ */ |
499 | qemu_log_mask(LOG_UNIMP, | |
500 | "%s: Write to buffer emulation is flawed\n", | |
501 | __func__); | |
05ee37eb AZ |
502 | pfl->status |= 0x80; /* Ready! */ |
503 | break; | |
5928023c | 504 | case 0xf0: /* Probe for AMD flash */ |
91316cbb | 505 | trace_pflash_write(pfl->name, "probe for AMD flash"); |
3072182d PMD |
506 | goto mode_read_array; |
507 | case 0xff: /* Read Array */ | |
91316cbb | 508 | trace_pflash_write(pfl->name, "read array mode"); |
3072182d | 509 | goto mode_read_array; |
05ee37eb AZ |
510 | default: |
511 | goto error_flash; | |
512 | } | |
513 | pfl->wcycle++; | |
514 | pfl->cmd = cmd; | |
12dabc79 | 515 | break; |
05ee37eb AZ |
516 | case 1: |
517 | switch (pfl->cmd) { | |
d361be25 AZ |
518 | case 0x10: /* Single Byte Program */ |
519 | case 0x40: /* Single Byte Program */ | |
91316cbb | 520 | trace_pflash_write(pfl->name, "single byte program (1)"); |
de8efe8f JJ |
521 | if (!pfl->ro) { |
522 | pflash_data_write(pfl, offset, value, width, be); | |
523 | pflash_update(pfl, offset, width); | |
524 | } else { | |
525 | pfl->status |= 0x10; /* Programming error */ | |
526 | } | |
d361be25 AZ |
527 | pfl->status |= 0x80; /* Ready! */ |
528 | pfl->wcycle = 0; | |
529 | break; | |
05ee37eb AZ |
530 | case 0x20: /* Block erase */ |
531 | case 0x28: | |
532 | if (cmd == 0xd0) { /* confirm */ | |
3656744c | 533 | pfl->wcycle = 0; |
05ee37eb | 534 | pfl->status |= 0x80; |
3072182d PMD |
535 | } else if (cmd == 0xff) { /* Read Array */ |
536 | goto mode_read_array; | |
05ee37eb AZ |
537 | } else |
538 | goto error_flash; | |
539 | ||
540 | break; | |
541 | case 0xe8: | |
ccd8014b PMD |
542 | /* |
543 | * Mask writeblock size based on device width, or bank width if | |
1997b485 RF |
544 | * device width not specified. |
545 | */ | |
4dbda935 | 546 | /* FIXME check @offset, @width */ |
1997b485 RF |
547 | if (pfl->device_width) { |
548 | value = extract32(value, 0, pfl->device_width * 8); | |
549 | } else { | |
550 | value = extract32(value, 0, pfl->bank_width * 8); | |
551 | } | |
91316cbb | 552 | trace_pflash_write_block(pfl->name, value); |
71fb2348 | 553 | pfl->counter = value; |
05ee37eb AZ |
554 | pfl->wcycle++; |
555 | break; | |
556 | case 0x60: | |
557 | if (cmd == 0xd0) { | |
558 | pfl->wcycle = 0; | |
559 | pfl->status |= 0x80; | |
560 | } else if (cmd == 0x01) { | |
561 | pfl->wcycle = 0; | |
562 | pfl->status |= 0x80; | |
3072182d PMD |
563 | } else if (cmd == 0xff) { /* Read Array */ |
564 | goto mode_read_array; | |
05ee37eb | 565 | } else { |
91316cbb | 566 | trace_pflash_write(pfl->name, "unknown (un)locking command"); |
3072182d | 567 | goto mode_read_array; |
05ee37eb AZ |
568 | } |
569 | break; | |
570 | case 0x98: | |
3072182d PMD |
571 | if (cmd == 0xff) { /* Read Array */ |
572 | goto mode_read_array; | |
05ee37eb | 573 | } else { |
91316cbb | 574 | trace_pflash_write(pfl->name, "leaving query mode"); |
05ee37eb AZ |
575 | } |
576 | break; | |
577 | default: | |
578 | goto error_flash; | |
579 | } | |
12dabc79 | 580 | break; |
05ee37eb AZ |
581 | case 2: |
582 | switch (pfl->cmd) { | |
583 | case 0xe8: /* Block write */ | |
4dbda935 | 584 | /* FIXME check @offset, @width */ |
de8efe8f | 585 | if (!pfl->ro) { |
4dbda935 MA |
586 | /* |
587 | * FIXME writing straight to memory is *wrong*. We | |
588 | * should write to a buffer, and flush it to memory | |
589 | * only on confirm command (see below). | |
590 | */ | |
de8efe8f JJ |
591 | pflash_data_write(pfl, offset, value, width, be); |
592 | } else { | |
593 | pfl->status |= 0x10; /* Programming error */ | |
594 | } | |
05ee37eb AZ |
595 | |
596 | pfl->status |= 0x80; | |
597 | ||
598 | if (!pfl->counter) { | |
a8170e5e | 599 | hwaddr mask = pfl->writeblock_size - 1; |
b4bf0a9a EI |
600 | mask = ~mask; |
601 | ||
91316cbb | 602 | trace_pflash_write(pfl->name, "block write finished"); |
05ee37eb | 603 | pfl->wcycle++; |
de8efe8f JJ |
604 | if (!pfl->ro) { |
605 | /* Flush the entire write buffer onto backing storage. */ | |
4dbda935 | 606 | /* FIXME premature! */ |
de8efe8f JJ |
607 | pflash_update(pfl, offset & mask, pfl->writeblock_size); |
608 | } else { | |
609 | pfl->status |= 0x10; /* Programming error */ | |
610 | } | |
05ee37eb AZ |
611 | } |
612 | ||
613 | pfl->counter--; | |
614 | break; | |
7317b8ca AZ |
615 | default: |
616 | goto error_flash; | |
05ee37eb | 617 | } |
12dabc79 | 618 | break; |
05ee37eb AZ |
619 | case 3: /* Confirm mode */ |
620 | switch (pfl->cmd) { | |
621 | case 0xe8: /* Block write */ | |
622 | if (cmd == 0xd0) { | |
4dbda935 | 623 | /* FIXME this is where we should write out the buffer */ |
05ee37eb AZ |
624 | pfl->wcycle = 0; |
625 | pfl->status |= 0x80; | |
05ee37eb | 626 | } else { |
2d93bebf MA |
627 | qemu_log_mask(LOG_UNIMP, |
628 | "%s: Aborting write to buffer not implemented," | |
629 | " the data is already written to storage!\n" | |
630 | "Flash device reset into READ mode.\n", | |
631 | __func__); | |
3072182d | 632 | goto mode_read_array; |
05ee37eb | 633 | } |
7317b8ca AZ |
634 | break; |
635 | default: | |
636 | goto error_flash; | |
05ee37eb | 637 | } |
12dabc79 | 638 | break; |
05ee37eb AZ |
639 | default: |
640 | /* Should never happen */ | |
91316cbb | 641 | trace_pflash_write(pfl->name, "invalid write state"); |
3072182d | 642 | goto mode_read_array; |
05ee37eb AZ |
643 | } |
644 | return; | |
645 | ||
646 | error_flash: | |
d96fc51c PC |
647 | qemu_log_mask(LOG_UNIMP, "%s: Unimplemented flash cmd sequence " |
648 | "(offset " TARGET_FMT_plx ", wcycle 0x%x cmd 0x%x value 0x%x)" | |
649 | "\n", __func__, offset, pfl->wcycle, pfl->cmd, value); | |
05ee37eb | 650 | |
3072182d | 651 | mode_read_array: |
91316cbb | 652 | trace_pflash_mode_read_array(pfl->name); |
5f9a5ea1 | 653 | memory_region_rom_device_set_romd(&pfl->mem, true); |
05ee37eb | 654 | pfl->wcycle = 0; |
aba53a12 | 655 | pfl->cmd = 0x00; /* This model reset value for READ_ARRAY (not CFI) */ |
05ee37eb AZ |
656 | } |
657 | ||
658 | ||
f71e42a5 PB |
659 | static MemTxResult pflash_mem_read_with_attrs(void *opaque, hwaddr addr, uint64_t *value, |
660 | unsigned len, MemTxAttrs attrs) | |
3d08ff69 | 661 | { |
16434065 | 662 | PFlashCFI01 *pfl = opaque; |
5aa113f0 | 663 | bool be = !!(pfl->features & (1 << PFLASH_BE)); |
3d08ff69 | 664 | |
f71e42a5 PB |
665 | if ((pfl->features & (1 << PFLASH_SECURE)) && !attrs.secure) { |
666 | *value = pflash_data_read(opaque, addr, len, be); | |
667 | } else { | |
668 | *value = pflash_read(opaque, addr, len, be); | |
669 | } | |
670 | return MEMTX_OK; | |
3d08ff69 BS |
671 | } |
672 | ||
f71e42a5 PB |
673 | static MemTxResult pflash_mem_write_with_attrs(void *opaque, hwaddr addr, uint64_t value, |
674 | unsigned len, MemTxAttrs attrs) | |
05ee37eb | 675 | { |
16434065 | 676 | PFlashCFI01 *pfl = opaque; |
5aa113f0 | 677 | bool be = !!(pfl->features & (1 << PFLASH_BE)); |
3d08ff69 | 678 | |
f71e42a5 PB |
679 | if ((pfl->features & (1 << PFLASH_SECURE)) && !attrs.secure) { |
680 | return MEMTX_ERROR; | |
681 | } else { | |
682 | pflash_write(opaque, addr, value, len, be); | |
683 | return MEMTX_OK; | |
684 | } | |
05ee37eb AZ |
685 | } |
686 | ||
5aa113f0 | 687 | static const MemoryRegionOps pflash_cfi01_ops = { |
f71e42a5 PB |
688 | .read_with_attrs = pflash_mem_read_with_attrs, |
689 | .write_with_attrs = pflash_mem_write_with_attrs, | |
cfe5f011 | 690 | .endianness = DEVICE_NATIVE_ENDIAN, |
05ee37eb AZ |
691 | }; |
692 | ||
a42cd11b | 693 | static void pflash_cfi01_fill_cfi_table(PFlashCFI01 *pfl) |
05ee37eb | 694 | { |
feb0b1aa | 695 | uint64_t blocks_per_device, sector_len_per_device, device_len; |
a0289b8a | 696 | int num_devices; |
05ee37eb | 697 | |
ccd8014b PMD |
698 | /* |
699 | * These are only used to expose the parameters of each device | |
a0289b8a PM |
700 | * in the cfi_table[]. |
701 | */ | |
702 | num_devices = pfl->device_width ? (pfl->bank_width / pfl->device_width) : 1; | |
feb0b1aa PM |
703 | if (pfl->old_multiple_chip_handling) { |
704 | blocks_per_device = pfl->nb_blocs / num_devices; | |
705 | sector_len_per_device = pfl->sector_len; | |
706 | } else { | |
707 | blocks_per_device = pfl->nb_blocs; | |
708 | sector_len_per_device = pfl->sector_len / num_devices; | |
709 | } | |
710 | device_len = sector_len_per_device * blocks_per_device; | |
a0289b8a | 711 | |
05ee37eb | 712 | /* Hardcoded CFI table */ |
05ee37eb AZ |
713 | /* Standard "QRY" string */ |
714 | pfl->cfi_table[0x10] = 'Q'; | |
715 | pfl->cfi_table[0x11] = 'R'; | |
716 | pfl->cfi_table[0x12] = 'Y'; | |
717 | /* Command set (Intel) */ | |
718 | pfl->cfi_table[0x13] = 0x01; | |
719 | pfl->cfi_table[0x14] = 0x00; | |
720 | /* Primary extended table address (none) */ | |
721 | pfl->cfi_table[0x15] = 0x31; | |
722 | pfl->cfi_table[0x16] = 0x00; | |
723 | /* Alternate command set (none) */ | |
724 | pfl->cfi_table[0x17] = 0x00; | |
725 | pfl->cfi_table[0x18] = 0x00; | |
726 | /* Alternate extended table (none) */ | |
727 | pfl->cfi_table[0x19] = 0x00; | |
728 | pfl->cfi_table[0x1A] = 0x00; | |
729 | /* Vcc min */ | |
730 | pfl->cfi_table[0x1B] = 0x45; | |
731 | /* Vcc max */ | |
732 | pfl->cfi_table[0x1C] = 0x55; | |
733 | /* Vpp min (no Vpp pin) */ | |
734 | pfl->cfi_table[0x1D] = 0x00; | |
735 | /* Vpp max (no Vpp pin) */ | |
736 | pfl->cfi_table[0x1E] = 0x00; | |
737 | /* Reserved */ | |
738 | pfl->cfi_table[0x1F] = 0x07; | |
739 | /* Timeout for min size buffer write */ | |
740 | pfl->cfi_table[0x20] = 0x07; | |
741 | /* Typical timeout for block erase */ | |
742 | pfl->cfi_table[0x21] = 0x0a; | |
743 | /* Typical timeout for full chip erase (4096 ms) */ | |
744 | pfl->cfi_table[0x22] = 0x00; | |
745 | /* Reserved */ | |
746 | pfl->cfi_table[0x23] = 0x04; | |
747 | /* Max timeout for buffer write */ | |
748 | pfl->cfi_table[0x24] = 0x04; | |
749 | /* Max timeout for block erase */ | |
750 | pfl->cfi_table[0x25] = 0x04; | |
751 | /* Max timeout for chip erase */ | |
752 | pfl->cfi_table[0x26] = 0x00; | |
753 | /* Device size */ | |
a0289b8a | 754 | pfl->cfi_table[0x27] = ctz32(device_len); /* + 1; */ |
05ee37eb AZ |
755 | /* Flash device interface (8 & 16 bits) */ |
756 | pfl->cfi_table[0x28] = 0x02; | |
757 | pfl->cfi_table[0x29] = 0x00; | |
758 | /* Max number of bytes in multi-bytes write */ | |
4b6fedca | 759 | if (pfl->bank_width == 1) { |
4737fa26 EI |
760 | pfl->cfi_table[0x2A] = 0x08; |
761 | } else { | |
762 | pfl->cfi_table[0x2A] = 0x0B; | |
763 | } | |
b4bf0a9a | 764 | pfl->writeblock_size = 1 << pfl->cfi_table[0x2A]; |
feb0b1aa PM |
765 | if (!pfl->old_multiple_chip_handling && num_devices > 1) { |
766 | pfl->writeblock_size *= num_devices; | |
767 | } | |
b4bf0a9a | 768 | |
05ee37eb AZ |
769 | pfl->cfi_table[0x2B] = 0x00; |
770 | /* Number of erase block regions (uniform) */ | |
771 | pfl->cfi_table[0x2C] = 0x01; | |
772 | /* Erase block region 1 */ | |
a0289b8a PM |
773 | pfl->cfi_table[0x2D] = blocks_per_device - 1; |
774 | pfl->cfi_table[0x2E] = (blocks_per_device - 1) >> 8; | |
feb0b1aa PM |
775 | pfl->cfi_table[0x2F] = sector_len_per_device >> 8; |
776 | pfl->cfi_table[0x30] = sector_len_per_device >> 16; | |
05ee37eb AZ |
777 | |
778 | /* Extended */ | |
779 | pfl->cfi_table[0x31] = 'P'; | |
780 | pfl->cfi_table[0x32] = 'R'; | |
781 | pfl->cfi_table[0x33] = 'I'; | |
782 | ||
783 | pfl->cfi_table[0x34] = '1'; | |
262e1eaa | 784 | pfl->cfi_table[0x35] = '0'; |
05ee37eb AZ |
785 | |
786 | pfl->cfi_table[0x36] = 0x00; | |
787 | pfl->cfi_table[0x37] = 0x00; | |
788 | pfl->cfi_table[0x38] = 0x00; | |
789 | pfl->cfi_table[0x39] = 0x00; | |
790 | ||
791 | pfl->cfi_table[0x3a] = 0x00; | |
792 | ||
793 | pfl->cfi_table[0x3b] = 0x00; | |
794 | pfl->cfi_table[0x3c] = 0x00; | |
795 | ||
262e1eaa | 796 | pfl->cfi_table[0x3f] = 0x01; /* Number of protection fields */ |
368a354f PC |
797 | } |
798 | ||
a42cd11b PMD |
799 | static void pflash_cfi01_realize(DeviceState *dev, Error **errp) |
800 | { | |
801 | ERRP_GUARD(); | |
802 | PFlashCFI01 *pfl = PFLASH_CFI01(dev); | |
803 | uint64_t total_len; | |
804 | int ret; | |
805 | ||
806 | if (pfl->sector_len == 0) { | |
807 | error_setg(errp, "attribute \"sector-length\" not specified or zero."); | |
808 | return; | |
809 | } | |
810 | if (pfl->nb_blocs == 0) { | |
811 | error_setg(errp, "attribute \"num-blocks\" not specified or zero."); | |
812 | return; | |
813 | } | |
814 | if (pfl->name == NULL) { | |
815 | error_setg(errp, "attribute \"name\" not specified."); | |
816 | return; | |
817 | } | |
818 | ||
819 | total_len = pfl->sector_len * pfl->nb_blocs; | |
820 | ||
821 | memory_region_init_rom_device( | |
822 | &pfl->mem, OBJECT(dev), | |
823 | &pflash_cfi01_ops, | |
824 | pfl, | |
825 | pfl->name, total_len, errp); | |
826 | if (*errp) { | |
827 | return; | |
828 | } | |
829 | ||
830 | pfl->storage = memory_region_get_ram_ptr(&pfl->mem); | |
831 | sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem); | |
832 | ||
833 | if (pfl->blk) { | |
834 | uint64_t perm; | |
835 | pfl->ro = !blk_supports_write_perm(pfl->blk); | |
836 | perm = BLK_PERM_CONSISTENT_READ | (pfl->ro ? 0 : BLK_PERM_WRITE); | |
837 | ret = blk_set_perm(pfl->blk, perm, BLK_PERM_ALL, errp); | |
838 | if (ret < 0) { | |
839 | return; | |
840 | } | |
841 | } else { | |
2231bee2 | 842 | pfl->ro = false; |
a42cd11b PMD |
843 | } |
844 | ||
845 | if (pfl->blk) { | |
846 | if (!blk_check_size_and_read_all(pfl->blk, pfl->storage, total_len, | |
847 | errp)) { | |
848 | vmstate_unregister_ram(&pfl->mem, DEVICE(pfl)); | |
849 | return; | |
850 | } | |
851 | } | |
852 | ||
853 | /* | |
854 | * Default to devices being used at their maximum device width. This was | |
855 | * assumed before the device_width support was added. | |
856 | */ | |
857 | if (!pfl->max_device_width) { | |
858 | pfl->max_device_width = pfl->device_width; | |
859 | } | |
860 | ||
861 | pfl->wcycle = 0; | |
862 | /* | |
863 | * The command 0x00 is not assigned by the CFI open standard, | |
864 | * but QEMU historically uses it for the READ_ARRAY command (0xff). | |
865 | */ | |
866 | pfl->cmd = 0x00; | |
867 | pfl->status = 0x80; /* WSM ready */ | |
868 | pflash_cfi01_fill_cfi_table(pfl); | |
869 | } | |
870 | ||
3a283507 PMD |
871 | static void pflash_cfi01_system_reset(DeviceState *dev) |
872 | { | |
873 | PFlashCFI01 *pfl = PFLASH_CFI01(dev); | |
874 | ||
91316cbb | 875 | trace_pflash_reset(pfl->name); |
3a283507 PMD |
876 | /* |
877 | * The command 0x00 is not assigned by the CFI open standard, | |
878 | * but QEMU historically uses it for the READ_ARRAY command (0xff). | |
879 | */ | |
880 | pfl->cmd = 0x00; | |
881 | pfl->wcycle = 0; | |
882 | memory_region_rom_device_set_romd(&pfl->mem, true); | |
883 | /* | |
884 | * The WSM ready timer occurs at most 150ns after system reset. | |
885 | * This model deliberately ignores this delay. | |
886 | */ | |
887 | pfl->status = 0x80; | |
888 | } | |
889 | ||
368a354f | 890 | static Property pflash_cfi01_properties[] = { |
16434065 | 891 | DEFINE_PROP_DRIVE("drive", PFlashCFI01, blk), |
a0289b8a PM |
892 | /* num-blocks is the number of blocks actually visible to the guest, |
893 | * ie the total size of the device divided by the sector length. | |
894 | * If we're emulating flash devices wired in parallel the actual | |
895 | * number of blocks per indvidual device will differ. | |
896 | */ | |
16434065 MA |
897 | DEFINE_PROP_UINT32("num-blocks", PFlashCFI01, nb_blocs, 0), |
898 | DEFINE_PROP_UINT64("sector-length", PFlashCFI01, sector_len, 0), | |
fa21a7b1 RF |
899 | /* width here is the overall width of this QEMU device in bytes. |
900 | * The QEMU device may be emulating a number of flash devices | |
901 | * wired up in parallel; the width of each individual flash | |
902 | * device should be specified via device-width. If the individual | |
903 | * devices have a maximum width which is greater than the width | |
904 | * they are being used for, this maximum width should be set via | |
905 | * max-device-width (which otherwise defaults to device-width). | |
906 | * So for instance a 32-bit wide QEMU flash device made from four | |
907 | * 16-bit flash devices used in 8-bit wide mode would be configured | |
908 | * with width = 4, device-width = 1, max-device-width = 2. | |
909 | * | |
910 | * If device-width is not specified we default to backwards | |
911 | * compatible behaviour which is a bad emulation of two | |
912 | * 16 bit devices making up a 32 bit wide QEMU device. This | |
913 | * is deprecated for new uses of this device. | |
914 | */ | |
16434065 MA |
915 | DEFINE_PROP_UINT8("width", PFlashCFI01, bank_width, 0), |
916 | DEFINE_PROP_UINT8("device-width", PFlashCFI01, device_width, 0), | |
917 | DEFINE_PROP_UINT8("max-device-width", PFlashCFI01, max_device_width, 0), | |
918 | DEFINE_PROP_BIT("big-endian", PFlashCFI01, features, PFLASH_BE, 0), | |
919 | DEFINE_PROP_BIT("secure", PFlashCFI01, features, PFLASH_SECURE, 0), | |
920 | DEFINE_PROP_UINT16("id0", PFlashCFI01, ident0, 0), | |
921 | DEFINE_PROP_UINT16("id1", PFlashCFI01, ident1, 0), | |
922 | DEFINE_PROP_UINT16("id2", PFlashCFI01, ident2, 0), | |
923 | DEFINE_PROP_UINT16("id3", PFlashCFI01, ident3, 0), | |
924 | DEFINE_PROP_STRING("name", PFlashCFI01, name), | |
925 | DEFINE_PROP_BOOL("old-multiple-chip-handling", PFlashCFI01, | |
feb0b1aa | 926 | old_multiple_chip_handling, false), |
368a354f PC |
927 | DEFINE_PROP_END_OF_LIST(), |
928 | }; | |
929 | ||
930 | static void pflash_cfi01_class_init(ObjectClass *klass, void *data) | |
931 | { | |
932 | DeviceClass *dc = DEVICE_CLASS(klass); | |
368a354f | 933 | |
3a283507 | 934 | dc->reset = pflash_cfi01_system_reset; |
e40b5f3e | 935 | dc->realize = pflash_cfi01_realize; |
4f67d30b | 936 | device_class_set_props(dc, pflash_cfi01_properties); |
d8d24fb7 | 937 | dc->vmsd = &vmstate_pflash; |
125ee0ed | 938 | set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); |
368a354f PC |
939 | } |
940 | ||
941 | ||
942 | static const TypeInfo pflash_cfi01_info = { | |
e7b62741 | 943 | .name = TYPE_PFLASH_CFI01, |
368a354f | 944 | .parent = TYPE_SYS_BUS_DEVICE, |
16434065 | 945 | .instance_size = sizeof(PFlashCFI01), |
368a354f PC |
946 | .class_init = pflash_cfi01_class_init, |
947 | }; | |
948 | ||
949 | static void pflash_cfi01_register_types(void) | |
950 | { | |
951 | type_register_static(&pflash_cfi01_info); | |
952 | } | |
953 | ||
954 | type_init(pflash_cfi01_register_types) | |
955 | ||
16434065 | 956 | PFlashCFI01 *pflash_cfi01_register(hwaddr base, |
940d5b13 | 957 | const char *name, |
16434065 MA |
958 | hwaddr size, |
959 | BlockBackend *blk, | |
ce14710f | 960 | uint32_t sector_len, |
16434065 MA |
961 | int bank_width, |
962 | uint16_t id0, uint16_t id1, | |
963 | uint16_t id2, uint16_t id3, | |
964 | int be) | |
368a354f | 965 | { |
3e80f690 | 966 | DeviceState *dev = qdev_new(TYPE_PFLASH_CFI01); |
368a354f | 967 | |
9b3d111a | 968 | if (blk) { |
934df912 | 969 | qdev_prop_set_drive(dev, "drive", blk); |
368a354f | 970 | } |
4cdd0a77 | 971 | assert(QEMU_IS_ALIGNED(size, sector_len)); |
ce14710f | 972 | qdev_prop_set_uint32(dev, "num-blocks", size / sector_len); |
368a354f | 973 | qdev_prop_set_uint64(dev, "sector-length", sector_len); |
4b6fedca | 974 | qdev_prop_set_uint8(dev, "width", bank_width); |
e9809422 | 975 | qdev_prop_set_bit(dev, "big-endian", !!be); |
368a354f PC |
976 | qdev_prop_set_uint16(dev, "id0", id0); |
977 | qdev_prop_set_uint16(dev, "id1", id1); | |
978 | qdev_prop_set_uint16(dev, "id2", id2); | |
979 | qdev_prop_set_uint16(dev, "id3", id3); | |
980 | qdev_prop_set_string(dev, "name", name); | |
3c6ef471 | 981 | sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); |
368a354f | 982 | |
f1b44f0e | 983 | sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); |
e7b62741 | 984 | return PFLASH_CFI01(dev); |
05ee37eb | 985 | } |
cfe5f011 | 986 | |
e60cf765 PMD |
987 | BlockBackend *pflash_cfi01_get_blk(PFlashCFI01 *fl) |
988 | { | |
989 | return fl->blk; | |
990 | } | |
991 | ||
16434065 | 992 | MemoryRegion *pflash_cfi01_get_memory(PFlashCFI01 *fl) |
cfe5f011 AK |
993 | { |
994 | return &fl->mem; | |
995 | } | |
4c0cfc72 | 996 | |
2d731dbd MA |
997 | /* |
998 | * Handle -drive if=pflash for machines that use properties. | |
999 | * If @dinfo is null, do nothing. | |
1000 | * Else if @fl's property "drive" is already set, fatal error. | |
1001 | * Else set it to the BlockBackend with @dinfo. | |
1002 | */ | |
1003 | void pflash_cfi01_legacy_drive(PFlashCFI01 *fl, DriveInfo *dinfo) | |
1004 | { | |
1005 | Location loc; | |
1006 | ||
1007 | if (!dinfo) { | |
1008 | return; | |
1009 | } | |
1010 | ||
1011 | loc_push_none(&loc); | |
1012 | qemu_opts_loc_restore(dinfo->opts); | |
1013 | if (fl->blk) { | |
1014 | error_report("clashes with -machine"); | |
1015 | exit(1); | |
1016 | } | |
934df912 MA |
1017 | qdev_prop_set_drive_err(DEVICE(fl), "drive", blk_by_legacy_dinfo(dinfo), |
1018 | &error_fatal); | |
2d731dbd MA |
1019 | loc_pop(&loc); |
1020 | } | |
1021 | ||
538f0497 | 1022 | static void postload_update_cb(void *opaque, bool running, RunState state) |
90c647db | 1023 | { |
16434065 | 1024 | PFlashCFI01 *pfl = opaque; |
90c647db | 1025 | |
3b717194 | 1026 | /* This is called after bdrv_activate_all. */ |
90c647db DDAG |
1027 | qemu_del_vm_change_state_handler(pfl->vmstate); |
1028 | pfl->vmstate = NULL; | |
1029 | ||
91316cbb | 1030 | trace_pflash_postload_cb(pfl->name); |
90c647db DDAG |
1031 | pflash_update(pfl, 0, pfl->sector_len * pfl->nb_blocs); |
1032 | } | |
1033 | ||
4c0cfc72 LE |
1034 | static int pflash_post_load(void *opaque, int version_id) |
1035 | { | |
16434065 | 1036 | PFlashCFI01 *pfl = opaque; |
4c0cfc72 LE |
1037 | |
1038 | if (!pfl->ro) { | |
90c647db DDAG |
1039 | pfl->vmstate = qemu_add_vm_change_state_handler(postload_update_cb, |
1040 | pfl); | |
4c0cfc72 LE |
1041 | } |
1042 | return 0; | |
1043 | } |