add big page nand flash emulation
authoryajin <yajin@vm-kernel.org>
Wed, 14 Jan 2009 19:59:31 +0000 (15 03:59 +0800)
committeryajin <yajin@vm-kernel.org>
Wed, 14 Jan 2009 19:59:31 +0000 (15 03:59 +0800)
hw/nand_bpage.c [new file with mode: 0755]

diff --git a/hw/nand_bpage.c b/hw/nand_bpage.c
new file mode 100755 (executable)
index 0000000..fe1c09f
--- /dev/null
@@ -0,0 +1,490 @@
+/*\r
+ * Big page NAND flash memory emulation.  based on 256M/16 bit flash datasheet from micro(MT29F2G16ABC)\r
+ *\r
+ * Copyright (C) 2008 yajin(yajin@vm-kernel.org)\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License as\r
+ * published by the Free Software Foundation; either version 2 or\r
+ * (at your option) version 3 of the License.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,\r
+ * MA 02111-1307 USA\r
+ */\r
+\r
+#include "hw.h"\r
+#include "flash.h"\r
+#include "block.h"\r
+#include "sysemu.h"\r
+\r
+\r
+#define MAX_PAGE               0x800\r
+#define MAX_OOB                0x40\r
+#define PAGE_MASK              (0xffff)\r
+#define BUS_WIDTH_16  2\r
+#define BUS_WIDTH_8 1\r
+\r
+//#define DEBUG\r
+\r
+struct nand_flash_info_s\r
+{\r
+    uint8_t  manf_id,chip_id;\r
+    uint32_t size;;\r
+    int bus_width;\r
+    int page_shift;\r
+    int oob_shift;\r
+    int block_shift;\r
+};\r
+\r
+struct nand_flash_info_s nand_flash_info[2] =\r
+{\r
+    {0x2c, 0xba, 256,2, 11, 6, 6},\r
+    {0Xec, 0xd3, 128,1, 11, 6, 6}\r
+};\r
+\r
+\r
+struct nand_bflash_s\r
+{\r
+       BlockDriverState *bdrv;\r
+    uint8_t manf_id, chip_id;\r
+    uint32_t size, pages;\r
+    uint32_t page_size, page_shift;\r
+    uint32_t oob_size, oob_shift;\r
+    uint32_t page_oob_size;\r
+    uint32_t page_sectors;      /*sector = 512 bytes */\r
+    uint32_t block_shift, block_pages;  /*how many pages in a block */\r
+    uint32_t bus_width;         /*bytes */\r
+\r
+    //uint8_t *internal_buf;\r
+    uint8_t io[MAX_PAGE + MAX_OOB + 0x400];\r
+    uint8_t *ioaddr;\r
+    int iolen;\r
+\r
+\r
+    uint32 addr_low, addr_high;\r
+    uint32 addr_cycle;\r
+\r
+    uint32 cmd, status;\r
+  #ifdef DEBUG  \r
+    FILE *fp;\r
+  #endif\r
+};\r
+\r
+\r
+#ifdef DEBUG\r
+static void debug_init(struct nand_bflash_s *s)\r
+{\r
+       s->fp=fopen("nandflash_debug.txt","w+");\r
+       if (s->fp==NULL)\r
+       {\r
+               fprintf(stderr,"can not open nandflash_debug.txt \n");\r
+               exit(-1);\r
+       }\r
+               \r
+}\r
+static void debug_out(struct nand_bflash_s *s,const char* format, ...)\r
+{\r
+       va_list ap;\r
+       if (s->fp)\r
+       {\r
+                va_start(ap, format);\r
+        vfprintf(s->fp, format, ap);\r
+        fflush(s->fp);\r
+       va_end(ap);\r
+       }\r
+}\r
+\r
+#else\r
+static void debug_init(struct nand_bflash_s *s)\r
+{\r
+       \r
+}\r
+static void debug_out(struct nand_bflash_s *s,const char* format, ...)\r
+{\r
+       \r
+}\r
+\r
+#endif\r
+\r
+static inline uint32_t get_page_number(struct nand_bflash_s *s,\r
+                                       uint32_t addr_low, uint32 addr_high)\r
+{\r
+    return (addr_high << 16) + ((addr_low >> 16) & PAGE_MASK);\r
+}\r
+\r
+\r
+\r
+/* Program a single page */\r
+static void nand_blk_write(struct nand_bflash_s *s)\r
+{\r
+    uint32_t page_number, off,  sector, soff;\r
+    uint8_t *iobuf=NULL;\r
+\r
+       if (!iobuf)\r
+       iobuf = qemu_mallocz((s->page_sectors + 2) * 0x200);\r
+    if (!iobuf)\r
+    {\r
+        fprintf(stderr, "can not alloc io buffer size 0x%x \n",\r
+                (s->page_sectors + 2) * 0x200);\r
+        cpu_abort(cpu_single_env, "%s: can not alloc io buffer size 0x%x \n",\r
+                  __FUNCTION__, (s->page_sectors + 2) * 0x200);\r
+    }\r
+\r
+    page_number = get_page_number(s, s->addr_low, s->addr_high);\r
+\r
+    debug_out(s,"nand_blk_write page number %x s->addr_low %x s->addr_high %x\n",page_number,s->addr_low,s->addr_high);\r
+\r
+    if (page_number >= s->pages)\r
+        return;\r
+\r
+    off = page_number * s->page_oob_size + (s->addr_low & PAGE_MASK);\r
+    sector = off >> 9;\r
+    soff = off & 0x1ff;\r
+    if (bdrv_read(s->bdrv, sector, iobuf, s->page_sectors + 2) == -1)\r
+    {\r
+        printf("%s: read error in sector %i\n", __FUNCTION__, sector);\r
+        return;\r
+    }\r
+\r
+    memcpy(iobuf + soff, s->io, s->iolen);\r
+\r
+    if (bdrv_write(s->bdrv, sector, iobuf, s->page_sectors + 2) == -1)\r
+        printf("%s: write error in sector %i\n", __FUNCTION__, sector);\r
+\r
+    //qemu_free(iobuf);\r
+}\r
+\r
+\r
+static void nandb_blk_load(struct nand_bflash_s *s)\r
+{\r
+    uint32_t page_number, offset;\r
+    offset = s->addr_low & PAGE_MASK;\r
+\r
+    page_number = get_page_number(s, s->addr_low, s->addr_high);\r
+       debug_out(s,"nandb_blk_load page number %x s->addr_low %x s->addr_high %x\n",page_number,s->addr_low,s->addr_high);\r
+    if (page_number >= s->pages)\r
+        return;\r
+       \r
+    if (bdrv_read(s->bdrv, (page_number * s->page_oob_size + offset) >> 9,\r
+                  s->io, (s->page_sectors + 2)) == -1)\r
+        printf("%s: read error in sector %i\n",\r
+               __FUNCTION__, page_number * s->page_oob_size);\r
+    s->ioaddr = s->io + ((page_number * s->page_oob_size + offset) & 0x1ff);\r
+}\r
+\r
+\r
+/* Erase a single block */\r
+static void nandb_blk_erase(struct nand_bflash_s *s)\r
+{\r
+    uint32_t page_number,  sector, addr, i;\r
+\r
+    uint8_t iobuf[0x200];\r
+\r
+        memset(iobuf,0xff,sizeof(iobuf));\r
+        s->addr_low = s->addr_low & ~((1 << (16 + s->block_shift)) - 1);\r
+    page_number = get_page_number(s, s->addr_low, s->addr_high);\r
+    debug_out(s,"nandb_blk_erase page number %x s->addr_low %x s->addr_high %x\n",page_number,s->addr_low,s->addr_high);\r
+    if (page_number >= s->pages)\r
+        return;\r
+\r
+    addr = page_number * s->page_oob_size;\r
+    \r
+    sector = addr >> 9;\r
+    if (bdrv_read(s->bdrv, sector, iobuf, 1) == -1)\r
+        printf("%s: read error in sector %i\n", __FUNCTION__, sector);\r
+    memset(iobuf + (addr & 0x1ff), 0xff, (~addr & 0x1ff) + 1);\r
+    if (bdrv_write(s->bdrv, sector, iobuf, 1) == -1)\r
+        printf("%s: write error in sector %i\n", __FUNCTION__, sector);\r
+\r
+    memset(iobuf, 0xff, 0x200);\r
+    i = (addr & ~0x1ff) + 0x200;\r
+    for (addr += (s->page_oob_size*s->block_pages - 0x200); i < addr; i += 0x200)\r
+        if (bdrv_write(s->bdrv, i >> 9, iobuf, 1) == -1)\r
+            printf("%s: write error in sector %i\n", __FUNCTION__, i >> 9);\r
+\r
+    sector = i >> 9;\r
+    if (bdrv_read(s->bdrv, sector, iobuf, 1) == -1)\r
+        printf("%s: read error in sector %i\n", __FUNCTION__, sector);\r
+    memset(iobuf, 0xff, ((addr - 1) & 0x1ff) + 1);\r
+    if (bdrv_write(s->bdrv, sector, iobuf, 1) == -1)\r
+        printf("%s: write error in sector %i\n", __FUNCTION__, sector);\r
+}\r
+\r
+static void nandb_next_page(struct nand_bflash_s *s)\r
+{\r
+    if ((s->addr_low + 0x10000) < s->addr_low)\r
+        s->addr_high++;\r
+    s->addr_low += 0x10000;\r
+}\r
+\r
+void nandb_write_command(struct nand_bflash_s *s, uint16_t value)\r
+{\r
+    int id_index[5] = { 0, 1, 2, 3,4};\r
+\r
+    debug_out(s,"nandb_write_command %x\n",value);\r
+\r
+    switch (value)\r
+    {\r
+    case 0x00:\r
+    case 0x05:\r
+        s->iolen = 0;\r
+        s->addr_cycle = 0;\r
+        break;\r
+    case 0x60:\r
+       /*earse only need 3 addrss cycle.Its address is block address*/\r
+       s->addr_low &= ~PAGE_MASK;\r
+       s->addr_high =0;\r
+       s->addr_cycle = 2;\r
+       break;\r
+    case 0x30:\r
+    case 0xe0:\r
+        s->iolen = s->page_oob_size - (s->addr_low & PAGE_MASK);\r
+        nandb_blk_load(s);\r
+        break;\r
+    case 0x31:\r
+    case 0x3f:\r
+        nandb_next_page(s);\r
+        s->iolen = s->page_oob_size - (s->addr_low & PAGE_MASK);\r
+        nandb_blk_load(s);\r
+        break;\r
+    case 0x90:\r
+        s->iolen = 5 * s->bus_width;\r
+        memset(s->io, 0x0, s->iolen);\r
+        if (s->bus_width == BUS_WIDTH_16)\r
+        {\r
+            id_index[0] = 0;\r
+            id_index[1] = 2;\r
+            id_index[2] = 4;\r
+            id_index[3] = 6;\r
+            id_index[4] = 6;\r
+        }\r
+        s->io[id_index[0]] = s->manf_id;\r
+        s->io[id_index[1]] = s->chip_id;\r
+        s->io[id_index[2]] = 'Q';       /* Don't-care byte (often 0xa5) */\r
+        if ((s->manf_id == NAND_MFR_MICRON) && (s->chip_id == 0xba))\r
+            s->io[id_index[3]] = 0x55;\r
+       if ((s->manf_id == NAND_MFR_SAMSUNG) && (s->chip_id == 0xd3))\r
+       {\r
+               s->io[id_index[3]] = 0x95;\r
+               s->io[id_index[4]] = 0x48;\r
+       }\r
+        s->ioaddr = s->io;\r
+        s->addr_cycle = 0;\r
+        break;\r
+    case 0x70:\r
+        if ((s->manf_id == NAND_MFR_MICRON) && (s->chip_id == 0xba))\r
+        {\r
+            s->status |= 0x60;  /*flash is ready */\r
+            s->status |= 0x80;  /*not protect */\r
+        }\r
+         if ((s->manf_id == NAND_MFR_SAMSUNG) && (s->chip_id == 0xd3))\r
+        {\r
+            s->status |= 0x40;  /*flash is ready */\r
+            s->status |= 0x80;  /*not protect */\r
+        }\r
+        s->io[0] = s->status;\r
+        s->ioaddr = s->io;\r
+        s->iolen = 1;\r
+        break;\r
+    case 0xd0:\r
+        nandb_blk_erase(s);\r
+        break;\r
+    case 0x80:\r
+    case 0x85:\r
+       s->addr_cycle = 0;\r
+       s->ioaddr = s->io;\r
+       s->iolen = 0;\r
+        break;\r
+    case 0x10:\r
+        nand_blk_write(s);\r
+        break;\r
+    case 0xff:\r
+       s->addr_cycle =0;\r
+       s->iolen=0;\r
+       s->addr_low =0;\r
+       s->addr_high =0;\r
+       s->ioaddr = NULL;\r
+       break;\r
+    default:\r
+        fprintf(stderr, "unknown nand command 0x%x \n", value);\r
+        exit(-1);\r
+    }\r
+    s->cmd = value;\r
+}\r
+\r
+void nandb_write_address(struct nand_bflash_s *s, uint16_t value)\r
+{\r
+    uint32_t mask;\r
+    uint32_t colum_addr;\r
+    //if (s->cmd==0x60)\r
+    debug_out(s,"value %x addr_cycle %x \n",value,s->addr_cycle);\r
+    if (s->addr_cycle < 5)\r
+    {\r
+        if (s->addr_cycle < 4)\r
+        {\r
+            mask = ~(0xff << (s->addr_cycle * 8));\r
+            s->addr_low &= mask;\r
+            s->addr_low |= value << (s->addr_cycle * 8);\r
+        }\r
+        else\r
+        {\r
+                         mask = ~(0xff << ((s->addr_cycle-4) * 8));\r
+            s->addr_high &= mask;\r
+            s->addr_high |= value << ((s->addr_cycle-4) * 8);\r
+        }\r
+    }\r
+    else\r
+    {\r
+       fprintf(stderr,"%s wrong addr cycle\n",__FUNCTION__);\r
+       exit(-1);\r
+    }\r
+    if ((s->addr_cycle==1)&&(s->bus_width!=1))\r
+    {\r
+       colum_addr = s->addr_low & PAGE_MASK;\r
+       colum_addr *= s->bus_width;\r
+       s->addr_low &= ~PAGE_MASK;\r
+       s->addr_low += colum_addr;\r
+    }\r
+    s->addr_cycle++;\r
+    \r
+}\r
+\r
+uint8_t nandb_read_data8(struct nand_bflash_s *s)\r
+{\r
+       uint8_t ret;\r
+       if ((s->iolen==0)&&(s->cmd==0x31))\r
+       {\r
+               nandb_next_page(s);\r
+        s->iolen = s->page_oob_size - (s->addr_low & PAGE_MASK);\r
+        nandb_blk_load(s);\r
+       }\r
+       if (s->iolen <= 0)\r
+       {\r
+               fprintf(stderr,"iolen <0 \n");\r
+               exit(-1);\r
+       }\r
+       if (s->cmd!=0x70)       \r
+       s->iolen -=1 ;\r
+    ret = *((uint8_t *)s->ioaddr);\r
+    if (s->cmd!=0x70)          \r
+       s->ioaddr += 1;   \r
+\r
+    //debug_out(s," %x ",ret);\r
+    return ret;\r
+}\r
+\r
+void nandb_write_data8(struct nand_bflash_s *s, uint8_t value)\r
+{\r
+        if ((s->cmd == 0x80) )\r
+        {\r
+        if (s->iolen < s->page_oob_size)\r
+        {\r
+               s->io[s->iolen ++] = value&0xff;\r
+        }\r
+    }\r
+}\r
+\r
+uint16_t nandb_read_data16(struct nand_bflash_s *s)\r
+{\r
+       uint16_t ret;\r
+       if ((s->iolen==0)&&(s->cmd==0x31))\r
+       {\r
+               nandb_next_page(s);\r
+        s->iolen = s->page_oob_size - (s->addr_low & PAGE_MASK);\r
+        nandb_blk_load(s);\r
+       }\r
+       if (s->iolen <= 0)\r
+       {\r
+               fprintf(stderr,"iolen <0 \n");\r
+               exit(-1);\r
+       }\r
+       if (s->cmd!=0x70)       \r
+       s->iolen -=2 ;\r
+    ret = *((uint16_t *)s->ioaddr);\r
+    if (s->cmd!=0x70)          \r
+       s->ioaddr += 2;         \r
+    return ret;\r
+}\r
+\r
+void nandb_write_data16(struct nand_bflash_s *s, uint16_t value)\r
+{\r
+        if ((s->cmd == 0x80) )\r
+        {\r
+        if (s->iolen < s->page_oob_size)\r
+        {\r
+               s->io[s->iolen ++] = value&0xff;\r
+               s->io[s->iolen ++] = (value>>8)&0xff;\r
+        }\r
+    }\r
+}\r
+\r
+struct nand_bflash_s *nandb_init(int manf_id, int chip_id)\r
+{\r
+    //int pagesize;\r
+    struct nand_bflash_s *s;\r
+    int index;\r
+    int i;\r
+\r
+    s = (struct nand_bflash_s *) qemu_mallocz(sizeof(struct nand_bflash_s));\r
+    for (i = 0; i < sizeof(nand_flash_info); i++)\r
+    {\r
+        if ((nand_flash_info[i].manf_id == manf_id)\r
+            && (nand_flash_info[i].chip_id == chip_id))\r
+        {\r
+            s->manf_id = manf_id;\r
+            s->chip_id = chip_id;\r
+            s->page_shift = nand_flash_info[i].page_shift;\r
+            s->oob_shift = nand_flash_info[i].oob_shift;\r
+            s->bus_width = nand_flash_info[i].bus_width;\r
+            s->page_size = 1 << s->page_shift;\r
+            s->oob_size = 1 << s->oob_shift;\r
+            s->block_shift = nand_flash_info[i].block_shift;\r
+            s->block_pages = 1 << s->block_shift;\r
+            s->page_oob_size = s->page_size + s->oob_size;\r
+            s->page_sectors = 1 << (s->page_shift - 9);\r
+            /*TODO: size overflow */\r
+            s->size = nand_flash_info[i].size << 20;\r
+            s->pages = (s->size / s->page_size);\r
+\r
+            break;\r
+        }\r
+\r
+    }\r
+    if (i >= sizeof(nand_flash_info))\r
+    {\r
+        fprintf(stderr, "%s: Unsupported NAND chip ID.\n",\r
+                  __FUNCTION__);\r
+        exit(-1);\r
+    }\r
+\r
+    \r
+    index = drive_get_index(IF_MTD, 0, 0);\r
+    if (index != -1)\r
+        s->bdrv = drives_table[index].bdrv;\r
+    else\r
+    {\r
+       fprintf(stderr, "%s: Please use -mtdblock to specify flash image.\n",\r
+                  __FUNCTION__);\r
+        exit(-1);\r
+    }\r
+\r
+    if (bdrv_getlength(s->bdrv) != (s->pages*s->page_oob_size))\r
+    {\r
+       fprintf(stderr,  "%s: Invalid flash image size.\n",\r
+                  __FUNCTION__);\r
+        exit(-1);\r
+\r
+    }\r
+\r
+       debug_init(s);\r
+    return s;\r
+\r
+}\r
+\r