NAND chip that understands the basic NAND command set (most chips have
some own custom commands but operating systems don't use them). It can
emulate mostly all of the chips supported by Linux given the
manufacturer id and chip id, including large page devices. The table
of IDs is taken from Linux. I added a "-mtdblock filename" switch for
pointing qemu to the image to use. If no image is given nand.c will
store data only in memory and discard it on quit.

The patch also has a routine that calculates the ECC checksums that
most NAND controllers use (but not the chip itself).

Cheers,
Andrew
From 99122604f6cd1ffdb71b9d89faf1c0df99db2938 Mon Sep 17 00:00:00 2001
From: Andrzej Zaborowski <[EMAIL PROTECTED]>
Date: Fri, 16 Mar 2007 16:41:18 +0100
Subject: [PATCH] NAND Flash memory emulation. ECC calculation helpers.

---
 ecc.h     |   77 ++++++++
 hw/nand.c |  613 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vl.c      |    7 +
 vl.h      |   22 +++
 4 files changed, 719 insertions(+), 0 deletions(-)

diff --git a/ecc.h b/ecc.h
new file mode 100644
index 0000000..11e6d01
--- /dev/null
+++ b/ecc.h
@@ -0,0 +1,77 @@
+/*
+ * Calculate Error-correcting Codes. Used by NAND Flash controllers
+ * (not by NAND chips).
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <[EMAIL PROTECTED]>
+ *
+ * This code is licensed under the GNU GPL v2.
+ */
+
+struct ecc_state_s {
+    uint8_t cp;                /* Column parity */
+    uint16_t lp[2];    /* Line parity */
+    uint16_t count;
+};
+
+/*
+ * Pre-calculated 256-way 1 byte column parity.  Table borrowed from Linux.
+ */
+static const uint8_t nand_ecc_precalc_table[] = {
+    0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a,
+    0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
+    0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f,
+    0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
+    0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c,
+    0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
+    0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59,
+    0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
+    0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33,
+    0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
+    0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56,
+    0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
+    0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55,
+    0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
+    0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30,
+    0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
+    0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30,
+    0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
+    0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55,
+    0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
+    0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56,
+    0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
+    0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33,
+    0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
+    0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59,
+    0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
+    0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c,
+    0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
+    0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f,
+    0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
+    0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a,
+    0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
+};
+
+/* Update ECC parity count.  */
+static inline uint8_t ecc_digest(struct ecc_state_s *s, uint8_t sample)
+{
+    uint8_t idx = nand_ecc_precalc_table[sample];
+
+    s->cp ^= idx & 0x3f;
+    if (idx & 0x40) {
+        s->lp[0] ^= ~s->count;
+        s->lp[1] ^= s->count;
+    }
+    s->count ++;
+
+    return sample;
+}
+
+/* Reinitialise the counters.  */
+static inline void ecc_reset(struct ecc_state_s *s)
+{
+    s->lp[0] = 0x0000;
+    s->lp[1] = 0x0000;
+    s->cp = 0x00;
+    s->count = 0;
+}
diff --git a/hw/nand.c b/hw/nand.c
new file mode 100644
index 0000000..029a29f
--- /dev/null
+++ b/hw/nand.c
@@ -0,0 +1,613 @@
+/*
+ * Flash NAND memory emulation.  Based on "16M x 8 Bit NAND Flash
+ * Memory" datasheet for the KM29U128AT / K9F2808U0A chips from
+ * Samsung Electronic.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <[EMAIL PROTECTED]>
+ *
+ * This code is licensed under the GNU GPL v2.
+ */
+
+#ifndef NAND_IO
+
+# include "vl.h"
+
+# define NAND_CMD_READ0                0x00
+# define NAND_CMD_READ1                0x01
+# define NAND_CMD_READ2                0x50
+# define NAND_CMD_LPREAD2      0x30
+# define NAND_CMD_NOSERIALREAD2        0x35
+# define NAND_CMD_RANDOMREAD1  0x05
+# define NAND_CMD_RANDOMREAD2  0xe0
+# define NAND_CMD_READID       0x90
+# define NAND_CMD_RESET                0xff
+# define NAND_CMD_PAGEPROGRAM1 0x80
+# define NAND_CMD_PAGEPROGRAM2 0x10
+# define NAND_CMD_CACHEPROGRAM2        0x15
+# define NAND_CMD_BLOCKERASE1  0x60
+# define NAND_CMD_BLOCKERASE2  0xd0
+# define NAND_CMD_READSTATUS   0x70
+# define NAND_CMD_COPYBACKPRG1 0x85
+
+# define NAND_IOSTATUS_ERROR   (1 << 0)
+# define NAND_IOSTATUS_PLANE0  (1 << 1)
+# define NAND_IOSTATUS_PLANE1  (1 << 2)
+# define NAND_IOSTATUS_PLANE2  (1 << 3)
+# define NAND_IOSTATUS_PLANE3  (1 << 4)
+# define NAND_IOSTATUS_BUSY    (1 << 6)
+# define NAND_IOSTATUS_UNPROTCT        (1 << 7)
+
+# define MAX_PAGE              0x800
+# define MAX_OOB               0x40
+
+struct nand_flash_s {
+    uint8_t manf_id, chip_id;
+    int size, pages;
+    int page_shift, oob_shift, erase_shift, addr_shift;
+    uint8_t *storage;
+    BlockDriverState *bdrv;
+    int mem_oob;
+
+    int cle, ale, ce, wp, gnd;
+
+    uint8_t io[MAX_PAGE + MAX_OOB + 0x400];
+    uint8_t *ioaddr;
+    int iolen;
+
+    uint32_t cmd, addr;
+    int addrlen;
+    int status;
+    int offset;
+
+    void (*blk_write)(struct nand_flash_s *s);
+    void (*blk_erase)(struct nand_flash_s *s);
+    void (*blk_load)(struct nand_flash_s *s, uint32_t addr, int offset);
+};
+
+# define NAND_NO_AUTOINCR      0x00000001
+# define NAND_BUSWIDTH_16      0x00000002
+# define NAND_NO_PADDING       0x00000004
+# define NAND_CACHEPRG         0x00000008
+# define NAND_COPYBACK         0x00000010
+# define NAND_IS_AND           0x00000020
+# define NAND_4PAGE_ARRAY      0x00000040
+# define NAND_NO_READRDY       0x00000100
+# define NAND_SAMSUNG_LP       (NAND_NO_PADDING | NAND_COPYBACK)
+
+# define NAND_IO
+
+# define PAGE(addr)            ((addr) >> ADDR_SHIFT)
+# define PAGE_START(page)      (PAGE(page) * (PAGE_SIZE + OOB_SIZE))
+# define PAGE_MASK             ((1 << ADDR_SHIFT) - 1)
+# define OOB_SHIFT             (PAGE_SHIFT - 5)
+# define OOB_SIZE              (1 << OOB_SHIFT)
+# define SECTOR(addr)          ((addr) >> (9 + ADDR_SHIFT - PAGE_SHIFT))
+# define SECTOR_OFFSET(addr)   ((addr) & ((511 >> PAGE_SHIFT) << 8))
+
+# define PAGE_SIZE             256
+# define PAGE_SHIFT            8
+# define PAGE_SECTORS          1
+# define ADDR_SHIFT            8
+# include "nand.c"
+# define PAGE_SIZE             512
+# define PAGE_SHIFT            9
+# define PAGE_SECTORS          1
+# define ADDR_SHIFT            8
+# include "nand.c"
+# define PAGE_SIZE             2048
+# define PAGE_SHIFT            11
+# define PAGE_SECTORS          4
+# define ADDR_SHIFT            16
+# include "nand.c"
+
+/* Information based on Linux drivers/mtd/nand/nand_ids.c */
+struct nand_info_s {
+    int size;
+    int width;
+    int page_shift;
+    int erase_shift;
+    uint32_t options;
+} nand_flash_ids[0x100] = {
+    [0 ... 0xff] = { 0 },
+
+    [0x6e] = { 1,      8,      8, 4, 0 },
+    [0x64] = { 2,      8,      8, 4, 0 },
+    [0x6b] = { 4,      8,      9, 4, 0 },
+    [0xe8] = { 1,      8,      8, 4, 0 },
+    [0xec] = { 1,      8,      8, 4, 0 },
+    [0xea] = { 2,      8,      8, 4, 0 },
+    [0xd5] = { 4,      8,      9, 4, 0 },
+    [0xe3] = { 4,      8,      9, 4, 0 },
+    [0xe5] = { 4,      8,      9, 4, 0 },
+    [0xd6] = { 8,      8,      9, 4, 0 },
+
+    [0x39] = { 8,      8,      9, 4, 0 },
+    [0xe6] = { 8,      8,      9, 4, 0 },
+    [0x49] = { 8,      16,     9, 4, NAND_BUSWIDTH_16 },
+    [0x59] = { 8,      16,     9, 4, NAND_BUSWIDTH_16 },
+
+    [0x33] = { 16,     8,      9, 5, 0 },
+    [0x73] = { 16,     8,      9, 5, 0 },
+    [0x43] = { 16,     16,     9, 5, NAND_BUSWIDTH_16 },
+    [0x53] = { 16,     16,     9, 5, NAND_BUSWIDTH_16 },
+
+    [0x35] = { 32,     8,      9, 5, 0 },
+    [0x75] = { 32,     8,      9, 5, 0 },
+    [0x45] = { 32,     16,     9, 5, NAND_BUSWIDTH_16 },
+    [0x55] = { 32,     16,     9, 5, NAND_BUSWIDTH_16 },
+
+    [0x36] = { 64,     8,      9, 5, 0 },
+    [0x76] = { 64,     8,      9, 5, 0 },
+    [0x46] = { 64,     16,     9, 5, NAND_BUSWIDTH_16 },
+    [0x56] = { 64,     16,     9, 5, NAND_BUSWIDTH_16 },
+
+    [0x78] = { 128,    8,      9, 5, 0 },
+    [0x39] = { 128,    8,      9, 5, 0 },
+    [0x79] = { 128,    8,      9, 5, 0 },
+    [0x72] = { 128,    16,     9, 5, NAND_BUSWIDTH_16 },
+    [0x49] = { 128,    16,     9, 5, NAND_BUSWIDTH_16 },
+    [0x74] = { 128,    16,     9, 5, NAND_BUSWIDTH_16 },
+    [0x59] = { 128,    16,     9, 5, NAND_BUSWIDTH_16 },
+
+    [0x71] = { 256,    8,      9, 5, 0 },
+
+    /*
+     * These are the new chips with large page size. The pagesize and the
+     * erasesize is determined from the extended id bytes
+     */
+# define LP_OPTIONS    (NAND_SAMSUNG_LP | NAND_NO_READRDY | NAND_NO_AUTOINCR)
+# define LP_OPTIONS16  (LP_OPTIONS | NAND_BUSWIDTH_16)
+
+    /* 512 Megabit */
+    [0xa2] = { 64,     8,      0, 0, LP_OPTIONS },
+    [0xf2] = { 64,     8,      0, 0, LP_OPTIONS },
+    [0xb2] = { 64,     16,     0, 0, LP_OPTIONS16 },
+    [0xc2] = { 64,     16,     0, 0, LP_OPTIONS16 },
+
+    /* 1 Gigabit */
+    [0xa1] = { 128,    8,      0, 0, LP_OPTIONS },
+    [0xf1] = { 128,    8,      0, 0, LP_OPTIONS },
+    [0xb1] = { 128,    16,     0, 0, LP_OPTIONS16 },
+    [0xc1] = { 128,    16,     0, 0, LP_OPTIONS16 },
+
+    /* 2 Gigabit */
+    [0xaa] = { 256,    8,      0, 0, LP_OPTIONS },
+    [0xda] = { 256,    8,      0, 0, LP_OPTIONS },
+    [0xba] = { 256,    16,     0, 0, LP_OPTIONS16 },
+    [0xca] = { 256,    16,     0, 0, LP_OPTIONS16 },
+
+    /* 4 Gigabit */
+    [0xac] = { 512,    8,      0, 0, LP_OPTIONS },
+    [0xdc] = { 512,    8,      0, 0, LP_OPTIONS },
+    [0xbc] = { 512,    16,     0, 0, LP_OPTIONS16 },
+    [0xcc] = { 512,    16,     0, 0, LP_OPTIONS16 },
+
+    /* 8 Gigabit */
+    [0xa3] = { 1024,   8,      0, 0, LP_OPTIONS },
+    [0xd3] = { 1024,   8,      0, 0, LP_OPTIONS },
+    [0xb3] = { 1024,   16,     0, 0, LP_OPTIONS16 },
+    [0xc3] = { 1024,   16,     0, 0, LP_OPTIONS16 },
+
+    /* 16 Gigabit */
+    [0xa5] = { 2048,   8,      0, 0, LP_OPTIONS },
+    [0xd5] = { 2048,   8,      0, 0, LP_OPTIONS },
+    [0xb5] = { 2048,   16,     0, 0, LP_OPTIONS16 },
+    [0xc5] = { 2048,   16,     0, 0, LP_OPTIONS16 },
+};
+
+static void nand_reset(struct nand_flash_s *s)
+{
+    s->cmd = NAND_CMD_READ0;
+    s->addr = 0;
+    s->addrlen = 0;
+    s->iolen = 0;
+    s->offset = 0;
+    s->status &= NAND_IOSTATUS_UNPROTCT;
+}
+
+static void nand_command(struct nand_flash_s *s)
+{
+    switch (s->cmd) {
+    case NAND_CMD_READ0:
+        s->iolen = 0;
+        break;
+
+    case NAND_CMD_READID:
+        s->io[0] = s->manf_id;
+        s->io[1] = s->chip_id;
+        s->io[2] = 'Q';                /* Don't-care byte (often 0xa5) */
+        if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP)
+            s->io[3] = 0x15;   /* Page Size, Block Size, Spare Size.. */
+        else
+            s->io[3] = 0xc0;   /* Multi-plane */
+        s->ioaddr = s->io;
+        s->iolen = 4;
+        break;
+
+    case NAND_CMD_RANDOMREAD2:
+    case NAND_CMD_NOSERIALREAD2:
+        if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP))
+            break;
+
+        s->blk_load(s, s->addr, s->addr & ((1 << s->addr_shift) - 1));
+        break;
+
+    case NAND_CMD_RESET:
+        nand_reset(s);
+        break;
+
+    case NAND_CMD_PAGEPROGRAM1:
+        s->ioaddr = s->io;
+        s->iolen = 0;
+        break;
+
+    case NAND_CMD_PAGEPROGRAM2:
+        if (s->wp) {
+            s->blk_write(s);
+        }
+        break;
+
+    case NAND_CMD_BLOCKERASE1:
+        break;
+
+    case NAND_CMD_BLOCKERASE2:
+        if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP)
+            s->addr <<= 16;
+        else
+            s->addr <<= 8;
+
+        if (s->wp) {
+            s->blk_erase(s);
+        }
+        break;
+
+    case NAND_CMD_READSTATUS:
+        s->io[0] = s->status;
+        s->ioaddr = s->io;
+        s->iolen = 1;
+        break;
+
+    default:
+        printf("%s: Unknown NAND command 0x%02x\n", __FUNCTION__, s->cmd);
+    }
+}
+
+/*
+ * Chip inputs are CLE, ALE, CE, WP, GND and eight I/O pins.  Chip
+ * outputs are R/B and eight I/O pins.
+ *
+ * CE, WP and R/B are active low.
+ */
+void nand_setpins(struct nand_flash_s *s, 
+                int cle, int ale, int ce, int wp, int gnd)
+{
+    s->cle = cle;
+    s->ale = ale;
+    s->ce = ce;
+    s->wp = wp;
+    s->gnd = gnd;
+    if (wp)
+        s->status |= NAND_IOSTATUS_UNPROTCT;
+    else
+        s->status &= ~NAND_IOSTATUS_UNPROTCT;
+}
+
+void nand_getpins(struct nand_flash_s *s, int *rb)
+{
+    *rb = 1;
+}
+
+void nand_setio(struct nand_flash_s *s, uint8_t value)
+{
+    if (!s->ce && s->cle) {
+        if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) {
+            if (s->cmd == NAND_CMD_READ0 && value == NAND_CMD_LPREAD2)
+                return;
+            if (value == NAND_CMD_RANDOMREAD1) {
+                s->addr &= ~((1 << s->addr_shift) - 1);
+                s->addrlen = 0;
+                return;
+            }
+        }
+        if (value == NAND_CMD_READ0)
+            s->offset = 0;
+       else if (value == NAND_CMD_READ1) {
+            s->offset = 0x100;
+            value = NAND_CMD_READ0;
+        }
+       else if (value == NAND_CMD_READ2) {
+            s->offset = 1 << s->page_shift;
+            value = NAND_CMD_READ0;
+        }
+
+        s->cmd = value;
+
+        if (s->cmd == NAND_CMD_READSTATUS ||
+                s->cmd == NAND_CMD_PAGEPROGRAM2 ||
+                s->cmd == NAND_CMD_BLOCKERASE1 ||
+                s->cmd == NAND_CMD_BLOCKERASE2 ||
+                s->cmd == NAND_CMD_NOSERIALREAD2 ||
+                s->cmd == NAND_CMD_RANDOMREAD2 ||
+                s->cmd == NAND_CMD_RESET)
+            nand_command(s);
+
+        if (s->cmd != NAND_CMD_RANDOMREAD2) {
+            s->addrlen = 0;
+            s->addr = 0;
+        }
+    }
+
+    if (s->ale) {
+        s->addr |= value << (s->addrlen * 8);
+        s->addrlen ++;
+
+        if (s->addrlen == 1 && s->cmd == NAND_CMD_READID)
+            nand_command(s);
+
+        if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) &&
+                s->addrlen == 3 && (
+                    s->cmd == NAND_CMD_READ0 ||
+                    s->cmd == NAND_CMD_PAGEPROGRAM1))
+            nand_command(s);
+        if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) &&
+               s->addrlen == 4 && (
+                    s->cmd == NAND_CMD_READ0 ||
+                    s->cmd == NAND_CMD_PAGEPROGRAM1))
+            nand_command(s);
+    }
+
+    if (!s->cle && !s->ale && s->cmd == NAND_CMD_PAGEPROGRAM1) {
+        if (s->iolen < (1 << s->page_shift) + (1 << s->oob_shift))
+            s->io[s->iolen ++] = value;
+    } else if (!s->cle && !s->ale && s->cmd == NAND_CMD_COPYBACKPRG1) {
+        if ((s->addr & ((1 << s->addr_shift) - 1)) <
+                (1 << s->page_shift) + (1 << s->oob_shift)) {
+            s->io[s->iolen + (s->addr & ((1 << s->addr_shift) - 1))] = value;
+            s->addr ++;
+        }
+    }
+}
+
+uint8_t nand_getio(struct nand_flash_s *s)
+{
+    int offset;
+ 
+    /* Allow sequential reading */
+    if (!s->iolen && s->cmd == NAND_CMD_READ0) {
+        offset = (s->addr & ((1 << s->addr_shift) - 1)) + s->offset;
+        s->offset = 0;
+
+        s->blk_load(s, s->addr, offset);
+        if (s->gnd)
+            s->iolen = (1 << s->page_shift) - offset;
+        else
+            s->iolen = (1 << s->page_shift) + (1 << s->oob_shift) - offset;
+    }
+
+    if (s->ce || s->iolen <= 0)
+        return 0;
+
+    s->iolen --;
+    return *(s->ioaddr ++);
+}
+
+struct nand_flash_s *nand_init(int manf_id, int chip_id)
+{
+    int pagesize;
+    struct nand_flash_s *s;
+    BlockDriverState *bs = 0;
+
+    if (nand_flash_ids[chip_id].size == 0) {
+        cpu_abort(cpu_single_env, "%s: Unsupported NAND chip ID.\n",
+                        __FUNCTION__);
+    }
+
+    if (mtd_filename) {
+        bs = bdrv_new("mtd");
+        if (bdrv_open(bs, mtd_filename, snapshot ? BDRV_O_SNAPSHOT : 0) < 0 ||
+                        qemu_key_check(bs, mtd_filename)) {
+            bdrv_delete(bs);
+            bs = 0;
+        }
+    }
+ 
+    s = (struct nand_flash_s *) qemu_mallocz(sizeof(struct nand_flash_s));
+    s->bdrv = bs;
+    s->manf_id = manf_id;
+    s->chip_id = chip_id;
+    s->size = nand_flash_ids[s->chip_id].size << 20;
+    if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) {
+        s->page_shift = 11;
+        s->erase_shift = 6;
+    } else {
+        s->page_shift = nand_flash_ids[s->chip_id].page_shift;
+        s->erase_shift = nand_flash_ids[s->chip_id].erase_shift;
+    }
+
+    switch (1 << s->page_shift) {
+    case 256:
+        nand_init_256(s);
+        break;
+    case 512:
+        nand_init_512(s);
+        break;
+    case 2048:
+        nand_init_2048(s);
+        break;
+    default:
+        cpu_abort(cpu_single_env, "%s: Unsupported NAND block size.\n",
+                        __FUNCTION__);
+    }
+
+    pagesize = 1 << s->oob_shift;
+    s->mem_oob = 1;
+    if (bs && bdrv_getlength(bs) >=
+                    (s->pages << s->page_shift) + (s->pages << s->oob_shift)) {
+        pagesize = 0;
+        s->mem_oob = 0;
+    }
+
+    if (!bs)
+        pagesize += 1 << s->page_shift;
+    if (pagesize)
+        s->storage = (uint8_t *) memset(qemu_malloc(s->pages * pagesize),
+                        0xff, s->pages * pagesize);
+    return s;
+}
+
+void nand_done(struct nand_flash_s *s)
+{
+    if (s->bdrv) {
+        bdrv_close(s->bdrv);
+        bdrv_delete(s->bdrv);
+    }
+
+    if (!s->bdrv || s->mem_oob)
+        free(s->storage);
+
+    free(s);
+}
+
+#else
+
+/* Program a single page */
+static void glue(nand_blk_write_, PAGE_SIZE)(struct nand_flash_s *s)
+{
+    uint32_t off, page, sector, soff;
+    uint8_t iobuf[(PAGE_SECTORS + 2) * 0x200];
+    if (PAGE(s->addr) >= s->pages)
+        return;
+
+    if (!s->bdrv) {
+        memcpy(s->storage + PAGE_START(s->addr) + (s->addr & PAGE_MASK) +
+                        s->offset, s->io, s->iolen);
+    } else if (s->mem_oob) {
+        sector = SECTOR(s->addr);
+        off = (s->addr & PAGE_MASK) + s->offset;
+        soff = SECTOR_OFFSET(s->addr);
+        if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS) == -1) {
+            printf("%s: read error in sector %i\n", __FUNCTION__, sector);
+            return;
+        }
+
+        memcpy(iobuf + (soff | off), s->io, MIN(s->iolen, PAGE_SIZE - off));
+        if (off + s->iolen > PAGE_SIZE) {
+            page = PAGE(s->addr);
+            memcpy(s->storage + (page << OOB_SHIFT), s->io + PAGE_SIZE - off,
+                            MIN(OOB_SIZE, off + s->iolen - PAGE_SIZE));
+        }
+
+        if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS) == -1)
+            printf("%s: write error in sector %i\n", __FUNCTION__, sector);
+    } else {
+        off = PAGE_START(s->addr) + (s->addr & PAGE_MASK) + s->offset;
+        sector = off >> 9;
+        soff = off & 0x1ff;
+        if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1) {
+            printf("%s: read error in sector %i\n", __FUNCTION__, sector);
+            return;
+        }
+
+        memcpy(iobuf + soff, s->io, s->iolen);
+
+        if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1)
+            printf("%s: write error in sector %i\n", __FUNCTION__, sector);
+    }
+    s->offset = 0;
+}
+
+/* Erase a single block */
+static void glue(nand_blk_erase_, PAGE_SIZE)(struct nand_flash_s *s)
+{
+    uint32_t i, page, addr;
+    uint8_t iobuf[0x200] = { [0 ... 0x1ff] = 0xff, };
+    addr = s->addr & ~((1 << (ADDR_SHIFT + s->erase_shift)) - 1);
+
+    if (PAGE(addr) >= s->pages)
+        return;
+
+    if (!s->bdrv) {
+        memset(s->storage + PAGE_START(addr),
+                        0xff, (PAGE_SIZE + OOB_SIZE) << s->erase_shift);
+    } else if (s->mem_oob) {
+        memset(s->storage + (PAGE(addr) << OOB_SHIFT),
+                        0xff, OOB_SIZE << s->erase_shift);
+        i = SECTOR(addr);
+        page = SECTOR(addr + (ADDR_SHIFT + s->erase_shift));
+        for (; i < page; i ++)
+            if (bdrv_write(s->bdrv, i, iobuf, 1) == -1)
+                printf("%s: write error in sector %i\n", __FUNCTION__, i);
+    } else {
+        addr = PAGE_START(addr);
+        page = addr >> 9;
+        if (bdrv_read(s->bdrv, page, iobuf, 1) == -1)
+            printf("%s: read error in sector %i\n", __FUNCTION__, page);
+        memset(iobuf + (addr & 0x1ff), 0xff, (~addr & 0x1ff) + 1);
+        if (bdrv_write(s->bdrv, page, iobuf, 1) == -1)
+            printf("%s: write error in sector %i\n", __FUNCTION__, page);
+
+        memset(iobuf, 0xff, 0x200);
+        i = (addr & ~0x1ff) + 0x200;
+        for (addr += ((PAGE_SIZE + OOB_SIZE) << s->erase_shift) - 0x200;
+                        i < addr; i += 0x200)
+            if (bdrv_write(s->bdrv, i >> 9, iobuf, 1) == -1)
+                printf("%s: write error in sector %i\n", __FUNCTION__, i >> 9);
+
+        page = i >> 9;
+        if (bdrv_read(s->bdrv, page, iobuf, 1) == -1)
+            printf("%s: read error in sector %i\n", __FUNCTION__, page);
+        memset(iobuf, 0xff, addr & 0x1ff);
+        if (bdrv_write(s->bdrv, page, iobuf, 1) == -1)
+            printf("%s: write error in sector %i\n", __FUNCTION__, page);
+    }
+}
+
+static void glue(nand_blk_load_, PAGE_SIZE)(struct nand_flash_s *s,
+                uint32_t addr, int offset)
+{
+    if (PAGE(addr) >= s->pages)
+        return;
+
+    if (s->bdrv) {
+        if (s->mem_oob) {
+            if (bdrv_read(s->bdrv, SECTOR(addr), s->io, PAGE_SECTORS) == -1)
+                printf("%s: read error in sector %i\n",
+                                __FUNCTION__, SECTOR(addr));
+            memcpy(s->io + SECTOR_OFFSET(s->addr) + PAGE_SIZE,
+                            s->storage + (PAGE(s->addr) << OOB_SHIFT),
+                            OOB_SIZE);
+            s->ioaddr = s->io + SECTOR_OFFSET(s->addr) + offset;
+        } else {
+            if (bdrv_read(s->bdrv, PAGE_START(addr) >> 9,
+                                    s->io, (PAGE_SECTORS + 2)) == -1)
+                printf("%s: read error in sector %i\n",
+                                __FUNCTION__, PAGE_START(addr) >> 9);
+            s->ioaddr = s->io + (PAGE_START(addr) & 0x1ff) + offset;
+        }
+    } else {
+        memcpy(s->io, s->storage + PAGE_START(s->addr) +
+                        offset, PAGE_SIZE + OOB_SIZE - offset);
+        s->ioaddr = s->io;
+    }
+
+    s->addr &= PAGE_SIZE - 1;
+    s->addr += PAGE_SIZE;
+}
+
+static void glue(nand_init_, PAGE_SIZE)(struct nand_flash_s *s)
+{
+    s->oob_shift = PAGE_SHIFT - 5;
+    s->pages = s->size >> PAGE_SHIFT;
+    s->addr_shift = ADDR_SHIFT;
+
+    s->blk_erase = glue(nand_blk_erase_, PAGE_SIZE);
+    s->blk_write = glue(nand_blk_write_, PAGE_SIZE);
+    s->blk_load = glue(nand_blk_load_, PAGE_SIZE);
+}
+
+# undef PAGE_SIZE
+# undef PAGE_SHIFT
+# undef PAGE_SECTORS
+# undef ADDR_SHIFT
+#endif /* NAND_IO */
diff --git a/vl.c b/vl.c
index 281e541..a523d1a 100644
--- a/vl.c
+++ b/vl.c
@@ -186,6 +186,7 @@ int fd_bootchk = 1;
 int no_reboot = 0;
 int snapshot = 0;
 const char *sd_filename = 0;
+const char *mtd_filename = 0;
 int graphic_rotate = 0;
 int daemonize = 0;
 const char *option_rom[MAX_OPTION_ROMS];
@@ -6418,6 +6419,7 @@ void help(void)
            "-hda/-hdb file  use 'file' as IDE hard disk 0/1 image\n"
            "-hdc/-hdd file  use 'file' as IDE hard disk 2/3 image\n"
            "-cdrom file     use 'file' as IDE cdrom image (cdrom is ide1 
master)\n"
+           "-mtdblock file  use 'file' as on-board Flash memory image\n"
            "-sd file        use 'file' as SecureDigital card image\n"
            "-boot [a|c|d|n] boot on floppy (a), hard disk (c), CD-ROM (d), or 
network (n)\n"
            "-snapshot       write to temporary files instead of disk image 
files\n"
@@ -6556,6 +6558,7 @@ enum {
     QEMU_OPTION_hdc,
     QEMU_OPTION_hdd,
     QEMU_OPTION_cdrom,
+    QEMU_OPTION_mtdblock,
     QEMU_OPTION_sd,
     QEMU_OPTION_boot,
     QEMU_OPTION_snapshot,
@@ -6634,6 +6637,7 @@ const QEMUOption qemu_options[] = {
     { "hdc", HAS_ARG, QEMU_OPTION_hdc },
     { "hdd", HAS_ARG, QEMU_OPTION_hdd },
     { "cdrom", HAS_ARG, QEMU_OPTION_cdrom },
+    { "mtdblock", HAS_ARG, QEMU_OPTION_mtdblock },
     { "sd", HAS_ARG, QEMU_OPTION_sd },
     { "boot", HAS_ARG, QEMU_OPTION_boot },
     { "snapshot", 0, QEMU_OPTION_snapshot },
@@ -7100,6 +7104,9 @@ int main(int argc, char **argv)
                         cdrom_index = -1;
                 }
                 break;
+            case QEMU_OPTION_mtdblock:
+                mtd_filename = optarg;
+                break;
             case QEMU_OPTION_sd:
                 sd_filename = optarg;
                 break;
diff --git a/vl.h b/vl.h
index c4d66d8..7c88c72 100644
--- a/vl.h
+++ b/vl.h
@@ -158,6 +158,7 @@ extern int usb_enabled;
 extern int smp_cpus;
 extern int snapshot;
 extern const char *sd_filename;
+extern const char *mtd_filename;
 extern int graphic_rotate;
 extern int no_quit;
 extern int semihosting_enabled;
@@ -1392,6 +1393,27 @@ pflash_t *pflash_register (target_ulong base, ram_addr_t 
off,
                            uint16_t id0, uint16_t id1, 
                            uint16_t id2, uint16_t id3);
 
+/* nand.c */
+struct nand_flash_s;
+struct nand_flash_s *nand_init(int manf_id, int chip_id);
+void nand_done(struct nand_flash_s *s);
+void nand_setpins(struct nand_flash_s *s, 
+                int cle, int ale, int ce, int wp, int gnd);
+void nand_getpins(struct nand_flash_s *s, int *rb);
+void nand_setio(struct nand_flash_s *s, uint8_t value);
+uint8_t nand_getio(struct nand_flash_s *s);
+
+#define NAND_MFR_TOSHIBA       0x98
+#define NAND_MFR_SAMSUNG       0xec
+#define NAND_MFR_FUJITSU       0x04
+#define NAND_MFR_NATIONAL      0x8f
+#define NAND_MFR_RENESAS       0x07
+#define NAND_MFR_STMICRO       0x20
+#define NAND_MFR_HYNIX         0xad
+#define NAND_MFR_MICRON                0x2c
+
+#include "ecc.h"
+
 /* PCMCIA/Cardbus */
 
 struct pcmcia_socket_s {
-- 
1.4.4.3

_______________________________________________
Qemu-devel mailing list
Qemu-devel@nongnu.org
http://lists.nongnu.org/mailman/listinfo/qemu-devel

Reply via email to