Commit f13b3ca3e75d9bb5c19ba05f7531f2d15c5ddc66

Authored by Evgeniy Dushistov
1 parent 29020076

add emulation of atmel pflash memory

Makefile.target
... ... @@ -411,6 +411,7 @@ obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o
411 411 endif
412 412  
413 413 obj-arm-y = integratorcp.o versatilepb.o smc91c111.o arm_pic.o arm_timer.o
  414 +obj-arm-y += pflash_atmel.o
414 415 obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
415 416 obj-arm-y += versatile_pci.o
416 417 obj-arm-y += realview_gic.o realview.o arm_sysctl.o mpcore.o
... ...
hw/flash.h
... ... @@ -16,6 +16,17 @@ pflash_t *pflash_cfi02_register(target_phys_addr_t base, ram_addr_t off,
16 16 uint16_t id2, uint16_t id3,
17 17 uint16_t unlock_addr0, uint16_t unlock_addr1);
18 18  
  19 +/* pflash_cfi_atmel.c */
  20 +pflash_t *pflash_cfi_atmel_register(target_phys_addr_t base, ram_addr_t off,
  21 + BlockDriverState *bs,
  22 + uint32_t boot_sect_len,
  23 + int nb_boot_blocks,
  24 + uint32_t sector_len,
  25 + int nb_blocs, int width,
  26 + uint16_t id0, uint16_t id1,
  27 + uint16_t id2, uint16_t id3);
  28 +
  29 +
19 30 /* nand.c */
20 31 typedef struct NANDFlashState NANDFlashState;
21 32 NANDFlashState *nand_init(int manf_id, int chip_id);
... ...
hw/pflash_atmel.c 0 → 100644
  1 +/*
  2 + * CFI parallel flash for emulating Atmel NOR flash (AT49BV642D)
  3 + *
  4 + * Copyright (c) 2009 Evgeniy Dushistov
  5 + * Copyright (c) 2006 Thorsten Zitterell
  6 + * Copyright (c) 2005 Jocelyn Mayer
  7 + *
  8 + * This library is free software; you can redistribute it and/or
  9 + * modify it under the terms of the GNU Lesser General Public
  10 + * License as published by the Free Software Foundation; either
  11 + * version 2 of the License, or (at your option) any later version.
  12 + *
  13 + * This library is distributed in the hope that it will be useful,
  14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16 + * Lesser General Public License for more details.
  17 + *
  18 + * You should have received a copy of the GNU Lesser General Public
  19 + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  20 + */
  21 +
  22 +/*
  23 + * For now, this code can emulate flashes of 1, 2 or 4 bytes width.
  24 + * Supported commands/modes are:
  25 + * - flash read
  26 + * - flash write
  27 + * - flash ID read
  28 + * - sector erase
  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 +
  39 +#include "hw.h"
  40 +#include "flash.h"
  41 +#include "block.h"
  42 +#include "qemu-timer.h"
  43 +
  44 +#define PFLASH_BUG(fmt, ...) \
  45 + do { \
  46 + printf("PFLASH: Possible BUG - " fmt, ## __VA_ARGS__); \
  47 + exit(1); \
  48 + } while(0)
  49 +
  50 +//#define PFLASH_DEBUG
  51 +#ifdef PFLASH_DEBUG
  52 +#define DPRINTF(fmt, ...) \
  53 + do { \
  54 + printf("PFLASH: " fmt , ## __VA_ARGS__); \
  55 + } while (0)
  56 +#else
  57 +#define DPRINTF(fmt, ...) do { } while (0)
  58 +#endif
  59 +
  60 +struct pflash_t {
  61 + BlockDriverState *bs;
  62 + target_phys_addr_t base;
  63 + target_phys_addr_t sector_len;
  64 + target_phys_addr_t boot_sect_len;
  65 + target_phys_addr_t nb_blocks;
  66 + target_phys_addr_t nb_boot_blocks;
  67 + target_phys_addr_t total_len;
  68 + int width;
  69 + int wcycle; /* if 0, the flash is read normally */
  70 + int bypass;
  71 + int ro;
  72 + uint8_t cmd;
  73 + uint8_t status;
  74 + uint16_t ident[4];
  75 + target_phys_addr_t counter;
  76 + QEMUTimer *timer;
  77 + ram_addr_t off;
  78 + int fl_mem;
  79 + void *storage;
  80 +};
  81 +
  82 +#define AT49_FLASH_CODE1 0xAA
  83 +#define AT49_FLASH_CODE2 0x55
  84 +#define AT49_ID_IN_CODE 0x90
  85 +#define AT49_ID_OUT_CODE 0xF0
  86 +#define AT49_FLASH_Setup_Erase 0x80
  87 +#define AT49_FLASH_Program 0xA0
  88 +#define AT49_FLASH_Sector_Erase 0x30
  89 +#define AT49_CMD_READ_ARRAY 0x00F0
  90 +#define AT49_MEM_FLASH_ADDR1 (0x5555<<1)
  91 +#define AT49_MEM_FLASH_ADDR2 (0x00002AAA<<1)
  92 +
  93 +static void pflash_timer (void *opaque)
  94 +{
  95 + pflash_t *pfl = opaque;
  96 +
  97 + DPRINTF("%s: command %02x done\n", __func__, pfl->cmd);
  98 + /* Reset flash */
  99 + pfl->status ^= 0x80;
  100 + if (pfl->bypass) {
  101 + pfl->wcycle = 2;
  102 + } else {
  103 + cpu_register_physical_memory(pfl->base, pfl->total_len,
  104 + pfl->off | IO_MEM_ROMD | pfl->fl_mem);
  105 + pfl->wcycle = 0;
  106 + }
  107 + pfl->cmd = 0;
  108 +}
  109 +
  110 +static uint32_t pflash_read (pflash_t *pfl, target_phys_addr_t offset,
  111 + int width)
  112 +{
  113 + target_phys_addr_t boff;
  114 + uint32_t ret;
  115 + uint8_t *p;
  116 +
  117 + ret = -1;
  118 + boff = offset & 0xFF; /* why this here ?? */
  119 +
  120 + if (pfl->width == 2)
  121 + boff = boff >> 1;
  122 + else if (pfl->width == 4)
  123 + boff = boff >> 2;
  124 +
  125 + DPRINTF("%s: reading offset " TARGET_FMT_plx " under cmd %02x width %d\n",
  126 + __func__, offset, pfl->cmd, width);
  127 +
  128 + switch (pfl->cmd) {
  129 + case 0x00:
  130 + /* Flash area read */
  131 + p = pfl->storage;
  132 + switch (width) {
  133 + case 1:
  134 + ret = p[offset];
  135 + DPRINTF("%s: data offset " TARGET_FMT_plx " %02x\n",
  136 + __func__, offset, ret);
  137 + break;
  138 + case 2:
  139 +#if defined(TARGET_WORDS_BIGENDIAN)
  140 + ret = p[offset] << 8;
  141 + ret |= p[offset + 1];
  142 +#else
  143 + ret = p[offset];
  144 + ret |= p[offset + 1] << 8;
  145 +#endif
  146 + DPRINTF("%s: data offset " TARGET_FMT_plx " %04x\n",
  147 + __func__, offset, ret);
  148 + break;
  149 + case 4:
  150 +#if defined(TARGET_WORDS_BIGENDIAN)
  151 + ret = p[offset] << 24;
  152 + ret |= p[offset + 1] << 16;
  153 + ret |= p[offset + 2] << 8;
  154 + ret |= p[offset + 3];
  155 +#else
  156 + ret = p[offset];
  157 + ret |= p[offset + 1] << 8;
  158 + ret |= p[offset + 1] << 8;
  159 + ret |= p[offset + 2] << 16;
  160 + ret |= p[offset + 3] << 24;
  161 +#endif
  162 + DPRINTF("%s: data offset " TARGET_FMT_plx " %08x\n",
  163 + __func__, offset, ret);
  164 + break;
  165 + default:
  166 + DPRINTF("BUG in %s\n", __func__);
  167 + }
  168 +
  169 + break;
  170 + case AT49_FLASH_Setup_Erase: /* Block erase */
  171 + case AT49_FLASH_Sector_Erase:
  172 + case AT49_FLASH_Program: /* Write block */
  173 + /* Status register read */
  174 + ret = pfl->status;
  175 + DPRINTF("%s: status %x\n", __func__, ret);
  176 + break;
  177 + case AT49_ID_IN_CODE:
  178 + DPRINTF("we read ID, boff %u, %X %X\n", boff, pfl->ident[0], pfl->ident[1]);
  179 + /* flash ID read */
  180 + switch (boff) {
  181 + case 0x00:
  182 + case 0x01:
  183 + ret = pfl->ident[boff & 0x01];
  184 + break;
  185 + case 0x02:
  186 + ret = 0x00; /* Pretend all sectors are unprotected */
  187 + break;
  188 + default:
  189 + break;
  190 + }
  191 + break;
  192 + default:
  193 + /* This should never happen : reset state & treat it as a read */
  194 + DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd);
  195 + pfl->wcycle = 0;
  196 + pfl->cmd = 0;
  197 + }
  198 + DPRINTF("%s: ret %X\n", __func__, ret);
  199 + return ret;
  200 +}
  201 +
  202 +/* update flash content on disk */
  203 +static void pflash_update(pflash_t *pfl, int offset,
  204 + int size)
  205 +{
  206 + int offset_end;
  207 + if (pfl->bs) {
  208 + offset_end = offset + size;
  209 + /* round to sectors */
  210 + offset = offset >> 9;
  211 + offset_end = (offset_end + 511) >> 9;
  212 + bdrv_write(pfl->bs, offset, pfl->storage + (offset << 9),
  213 + offset_end - offset);
  214 + }
  215 +}
  216 +
  217 +static inline void pflash_data_write(pflash_t *pfl, target_phys_addr_t offset,
  218 + uint32_t value, int width)
  219 +{
  220 + uint8_t *p = pfl->storage;
  221 +
  222 + DPRINTF("%s: block write offset " TARGET_FMT_plx
  223 + " value %x counter " TARGET_FMT_plx "\n",
  224 + __func__, offset, value, pfl->counter);
  225 + switch (width) {
  226 + case 1:
  227 + p[offset] = value;
  228 + pflash_update(pfl, offset, 1);
  229 + break;
  230 + case 2:
  231 +#if defined(TARGET_WORDS_BIGENDIAN)
  232 + p[offset] = value >> 8;
  233 + p[offset + 1] = value;
  234 +#else
  235 + p[offset] = value;
  236 + p[offset + 1] = value >> 8;
  237 +#endif
  238 + pflash_update(pfl, offset, 2);
  239 + break;
  240 + case 4:
  241 +#if defined(TARGET_WORDS_BIGENDIAN)
  242 + p[offset] = value >> 24;
  243 + p[offset + 1] = value >> 16;
  244 + p[offset + 2] = value >> 8;
  245 + p[offset + 3] = value;
  246 +#else
  247 + p[offset] = value;
  248 + p[offset + 1] = value >> 8;
  249 + p[offset + 2] = value >> 16;
  250 + p[offset + 3] = value >> 24;
  251 +#endif
  252 + pflash_update(pfl, offset, 4);
  253 + break;
  254 + }
  255 +
  256 +}
  257 +
  258 +static void pflash_write(pflash_t *pfl, target_phys_addr_t offset,
  259 + uint32_t value, int width)
  260 +{
  261 + target_phys_addr_t boff;
  262 + uint8_t *p;
  263 + uint8_t cmd;
  264 +
  265 + cmd = value;
  266 +
  267 + DPRINTF("%s: writing offset " TARGET_FMT_plx " value %08x width %d wcycle 0x%x\n",
  268 + __func__, offset, value, width, pfl->wcycle);
  269 +
  270 + /* Set the device in I/O access mode */
  271 + cpu_register_physical_memory(pfl->base, pfl->total_len, pfl->fl_mem);
  272 + boff = offset & (pfl->sector_len - 1);
  273 +
  274 + if (pfl->width == 2)
  275 + boff = boff >> 1;
  276 + else if (pfl->width == 4)
  277 + boff = boff >> 2;
  278 +
  279 + switch (pfl->wcycle) {
  280 + case 0:
  281 + /* read mode */
  282 + switch (cmd) {
  283 + case AT49_CMD_READ_ARRAY:
  284 + if (pfl->cmd == AT49_FLASH_Program) {
  285 + DPRINTF("%s: finish program\n", __func__);
  286 + goto reset_flash;
  287 + }
  288 + goto error_flash;
  289 + case AT49_FLASH_CODE1:
  290 + if (offset == AT49_MEM_FLASH_ADDR1) {
  291 + DPRINTF("flash code 1\n");
  292 + break;
  293 + }
  294 + default:
  295 + goto error_flash;
  296 + }
  297 + pfl->wcycle++;
  298 + pfl->cmd = cmd;
  299 + return;
  300 + case 1:
  301 + DPRINTF("offset %X, cmd %X wcyle 1\n", offset, pfl->cmd);
  302 + switch (pfl->cmd) {
  303 + case AT49_FLASH_Program:
  304 + DPRINTF("%s: Single Byte Program\n", __func__);
  305 + pflash_data_write(pfl, offset, value, width);
  306 + /* pfl->status = (value & 0x80); /\* Ready! *\/ */
  307 + pfl->wcycle = 0;
  308 + goto reset_flash;
  309 + case AT49_FLASH_CODE1:
  310 + if (offset == AT49_MEM_FLASH_ADDR2 && value == AT49_FLASH_CODE2) {
  311 + DPRINTF("flash code 2\n");
  312 + pfl->cmd = value;
  313 + pfl->wcycle++;
  314 + break;
  315 + }
  316 + default:
  317 + goto error_flash;
  318 + }
  319 + return;
  320 + case 2:
  321 + DPRINTF("%s: cmd %X\n", __func__, pfl->cmd);
  322 + switch (pfl->cmd) {
  323 + case AT49_FLASH_Sector_Erase:
  324 + if (value == AT49_CMD_READ_ARRAY) {
  325 + DPRINTF("%s: sector erase finished\n", __func__);
  326 + goto reset_flash;
  327 + }
  328 + break;
  329 + case AT49_FLASH_CODE2:
  330 + if (offset == AT49_MEM_FLASH_ADDR1) {
  331 + switch (value) {
  332 + case AT49_ID_IN_CODE:
  333 + DPRINTF("%s: ident command\n", __func__);
  334 + pfl->wcycle = 0;
  335 + break;
  336 + case AT49_FLASH_Setup_Erase:
  337 + DPRINTF("%s: erase command\n", __func__);
  338 + pfl->wcycle = 0;
  339 + break;
  340 + case AT49_FLASH_Program:
  341 + DPRINTF("%s: program command\n", __func__);
  342 + pfl->wcycle = 1;
  343 + break;
  344 + case AT49_ID_OUT_CODE:
  345 + DPRINTF("%s: ident command finished\n", __func__);
  346 + goto reset_flash;
  347 + default:
  348 + goto error_flash;
  349 + }
  350 + pfl->cmd = value;
  351 + break;
  352 + } else if (value == AT49_FLASH_Sector_Erase) {
  353 + target_phys_addr_t sect_len = offset < pfl->boot_sect_len * pfl->nb_boot_blocks ?
  354 + pfl->boot_sect_len : pfl->sector_len;
  355 + pfl->cmd = AT49_FLASH_Sector_Erase;
  356 + p = pfl->storage;
  357 + DPRINTF("%s: offset " TARGET_FMT_plx "\n", __func__, offset);
  358 + offset &= ~(sect_len - 1);
  359 + DPRINTF("%s: block erase at " TARGET_FMT_plx " bytes "
  360 + TARGET_FMT_plx "\n",
  361 + __func__, offset, sect_len);
  362 +
  363 + memset(p + offset, 0xff, sect_len);
  364 + pflash_update(pfl, offset, sect_len);
  365 + pfl->wcycle = 0;
  366 + pfl->status |= 0x80; /* Ready! */
  367 + break;
  368 + }
  369 + default:
  370 + goto error_flash;
  371 + }
  372 + return;
  373 + default:
  374 + /* Should never happen */
  375 + DPRINTF("%s: invalid write state\n", __func__);
  376 + goto reset_flash;
  377 + }
  378 + return;
  379 +
  380 +error_flash:
  381 + printf("%s: Unimplemented flash cmd sequence "
  382 + "(offset " TARGET_FMT_plx ", wcycle 0x%x cmd 0x%x value 0x%x)\n",
  383 + __func__, offset, pfl->wcycle, pfl->cmd, value);
  384 +
  385 +reset_flash:
  386 + cpu_register_physical_memory(pfl->base, pfl->total_len,
  387 + pfl->off | IO_MEM_ROMD | pfl->fl_mem);
  388 +
  389 + pfl->bypass = 0;
  390 + pfl->wcycle = 0;
  391 + pfl->cmd = 0;
  392 + return;
  393 +}
  394 +
  395 +
  396 +static uint32_t pflash_readb (void *opaque, target_phys_addr_t addr)
  397 +{
  398 + return pflash_read(opaque, addr, 1);
  399 +}
  400 +
  401 +static uint32_t pflash_readw (void *opaque, target_phys_addr_t addr)
  402 +{
  403 + pflash_t *pfl = opaque;
  404 +
  405 + return pflash_read(pfl, addr, 2);
  406 +}
  407 +
  408 +static uint32_t pflash_readl (void *opaque, target_phys_addr_t addr)
  409 +{
  410 + pflash_t *pfl = opaque;
  411 +
  412 + return pflash_read(pfl, addr, 4);
  413 +}
  414 +
  415 +static void pflash_writeb (void *opaque, target_phys_addr_t addr,
  416 + uint32_t value)
  417 +{
  418 + pflash_write(opaque, addr, value, 1);
  419 +}
  420 +
  421 +static void pflash_writew (void *opaque, target_phys_addr_t addr,
  422 + uint32_t value)
  423 +{
  424 + pflash_t *pfl = opaque;
  425 +
  426 + pflash_write(pfl, addr, value, 2);
  427 +}
  428 +
  429 +static void pflash_writel (void *opaque, target_phys_addr_t addr,
  430 + uint32_t value)
  431 +{
  432 + pflash_t *pfl = opaque;
  433 +
  434 + pflash_write(pfl, addr, value, 4);
  435 +}
  436 +
  437 +static CPUWriteMemoryFunc * const pflash_write_ops[] = {
  438 + &pflash_writeb,
  439 + &pflash_writew,
  440 + &pflash_writel,
  441 +};
  442 +
  443 +static CPUReadMemoryFunc * const pflash_read_ops[] = {
  444 + &pflash_readb,
  445 + &pflash_readw,
  446 + &pflash_readl,
  447 +};
  448 +
  449 +pflash_t *pflash_cfi_atmel_register(target_phys_addr_t base, ram_addr_t off,
  450 + BlockDriverState *bs,
  451 + uint32_t boot_sect_len,
  452 + int nb_boot_blocks,
  453 + uint32_t sector_len,
  454 + int nb_blocs, int width,
  455 + uint16_t id0, uint16_t id1,
  456 + uint16_t id2, uint16_t id3)
  457 +{
  458 + pflash_t *pfl;
  459 + target_phys_addr_t total_len;
  460 + int ret;
  461 +
  462 + total_len = boot_sect_len * nb_boot_blocks + sector_len * nb_blocs;
  463 +
  464 + pfl = qemu_mallocz(sizeof(pflash_t));
  465 +
  466 + pfl->storage = qemu_get_ram_ptr(off);
  467 + pfl->fl_mem = cpu_register_io_memory(
  468 + pflash_read_ops, pflash_write_ops, pfl);
  469 + pfl->off = off;
  470 + cpu_register_physical_memory(base, total_len,
  471 + off | pfl->fl_mem | IO_MEM_ROMD);
  472 +
  473 + pfl->bs = bs;
  474 + if (pfl->bs) {
  475 + /* read the initial flash content */
  476 + ret = bdrv_read(pfl->bs, 0, pfl->storage, total_len >> 9);
  477 + if (ret < 0) {
  478 + cpu_unregister_io_memory(pfl->fl_mem);
  479 + qemu_free(pfl);
  480 + return NULL;
  481 + }
  482 + }
  483 + pfl->ro = 0;
  484 + pfl->timer = qemu_new_timer(vm_clock, pflash_timer, pfl);
  485 + pfl->base = base;
  486 + pfl->sector_len = sector_len;
  487 + pfl->nb_blocks = nb_blocs;
  488 + pfl->boot_sect_len = boot_sect_len;
  489 + pfl->nb_boot_blocks = nb_boot_blocks;
  490 + pfl->total_len = total_len;
  491 + pfl->width = width;
  492 + pfl->wcycle = 0;
  493 + pfl->cmd = 0;
  494 + pfl->status = 0;
  495 + pfl->ident[0] = id0;
  496 + pfl->ident[1] = id1;
  497 + pfl->ident[2] = id2;
  498 + pfl->ident[3] = id3;
  499 +
  500 +
  501 + return pfl;
  502 +}
... ...