NAND emulation improvements
authorJuha Riihimäki <juhriihi@esdhcp03982.research.nokia.com>
Fri, 6 Mar 2009 11:15:21 +0000 (13:15 +0200)
committerJuha Riihimäki <juhriihi@esdhcp03982.research.nokia.com>
Fri, 6 Mar 2009 11:15:21 +0000 (13:15 +0200)
1. Removed Beagleboard specific NAND emulation.
2. Enhanced existing NAND emulation to support other than 8-bit devices.
3. Enhanced existing NAND emulation to support over 1Gb devices.
4. Enhanced existing NAND emulation to support cache status bits and read cache commands.
5. Fixed existing NAND emulation to correctly handle multiple IO reads after READ STATUS command.
6. Enhanced OMAP2/3 GPMC emulation to support NAND memories.

Makefile.target
hw/beagle.c
hw/flash.h
hw/nand.c
hw/nand_bpage.c [deleted file]
hw/nseries.c
hw/omap.h
hw/omap2.c

index 73038b5..caeabe4 100644 (file)
@@ -661,7 +661,7 @@ OBJS+= arm-semi.o
 OBJS+= pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o
 OBJS+= pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o
 OBJS+= pflash_cfi01.o gumstix.o
-OBJS+= zaurus.o ide.o serial.o nand.o nand_bpage.o ecc.o spitz.o tosa.o tc6393xb.o
+OBJS+= zaurus.o ide.o serial.o nand.o ecc.o spitz.o tosa.o tc6393xb.o
 OBJS+= omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o
 OBJS+= omap2.o omap_dss.o soc_dma.o
 OBJS+= omap3.o omap3_mmc.o omap3_usb.o beagle.o twl4030.o
index 06a4102..b8a540b 100644 (file)
@@ -59,7 +59,7 @@
 struct beagle_s {
     struct omap_mpu_state_s *cpu;
     
-    struct nand_bflash_s *nand;
+    struct nand_flash_s *nand;
     struct omap3_lcd_panel_s *lcd_panel;
     i2c_bus *i2c;
     struct twl4030_s *twl4030;
@@ -72,99 +72,42 @@ static struct arm_boot_info beagle_binfo = {
 };
 
 
-static uint32_t beagle_nand_read16(void *opaque, target_phys_addr_t addr)
-{
-       struct beagle_s *s = (struct beagle_s *) opaque;
-    //BEAGLE_DEBUG("beagle_nand_read16 offset %x\n",addr);
-
-       switch (addr)
-       {
-               case 0x7C: /*NAND_COMMAND*/
-               case 0x80: /*NAND_ADDRESS*/
-                       OMAP_BAD_REG(addr);
-                       break;
-               case 0x84: /*NAND_DATA*/
-                       return nandb_read_data16(s->nand);
-                       break;
-               default:
-                       OMAP_BAD_REG(addr);
-                       break;
-       }
-    return 0;
-}
-
-static void beagle_nand_write16(void *opaque, target_phys_addr_t addr,
-                uint32_t value)
-{
-       struct beagle_s *s = (struct beagle_s *) opaque;
-    switch (addr)
-       {
-               case 0x7C: /*NAND_COMMAND*/
-                       nandb_write_command(s->nand,value);
-                       break;
-               case 0x80: /*NAND_ADDRESS*/
-                       nandb_write_address(s->nand,value);
-                       break;
-               case 0x84: /*NAND_DATA*/
-                       nandb_write_data16(s->nand,value);
-                       break;
-               default:
-                       OMAP_BAD_REG(addr);
-                       break;
-       }
-}
-
-
-static CPUReadMemoryFunc *beagle_nand_readfn[] = {
-        beagle_nand_read16,
-        beagle_nand_read16,
-        omap_badwidth_read32,
-};
-
-static CPUWriteMemoryFunc *beagle_nand_writefn[] = {
-        beagle_nand_write16,
-        beagle_nand_write16,
-        omap_badwidth_write32,
-};
-
 static void beagle_nand_setup(struct beagle_s *s)
 {
-       //int iomemtype;
-       
-       /*MT29F2G16ABC*/
-       s->nand = nandb_init(NAND_MFR_MICRON,0xba);
-       /*wp=1, no write protect!!! */
-       //nand_set_wp(s->nand, 1);
-
-    omap_gpmc_attach(s->cpu->gpmc, BEAGLE_NAND_CS, 0, NULL, NULL, s,
-                     beagle_nand_readfn, beagle_nand_writefn);
-
-    omap3_set_mem_type(s->cpu,GPMC_NAND);
+       s->nand = nand_init(NAND_MFR_MICRON, 0xba); /* MT29F2G16ABC */
+       nand_setpins(s->nand, 0, 0, 0, 1, 0); /* no write-protect */
+    omap_gpmc_attach(s->cpu->gpmc, BEAGLE_NAND_CS, 0, NULL, NULL, s, s->nand);
+    omap3_set_mem_type(s->cpu, GPMC_NAND);
 }
 
-static int beagle_nand_read_page(struct beagle_s *s,uint8_t *buf, uint16_t page_addr)
+static int beagle_nand_read_page(struct beagle_s *s,uint8_t *buf, uint32_t addr)
 {
-       uint16_t *p;
+       uint16_t *p = (uint16_t *)buf;
        int i;
 
-       p=(uint16_t *)buf;
-
-       /*send command 0x0*/
-       beagle_nand_write16(s,0x7C,0);
-       /*send page address */
-       beagle_nand_write16(s,0x80,page_addr&0xff);
-       beagle_nand_write16(s,0x80,(page_addr>>8)&0x7);
-       beagle_nand_write16(s,0x80,(page_addr>>11)&0xff);
-       beagle_nand_write16(s,0x80,(page_addr>>19)&0xff);
-       beagle_nand_write16(s,0x80,(page_addr>>27)&0xff);
-       /*send command 0x30*/
-       beagle_nand_write16(s,0x7C,0x30);
-
-       for (i=0;i<0x800/2;i++)
-       {
-               *p++ = beagle_nand_read16(s,0x84);
-       }
-       return 1;
+    /* send command: reset */
+    nand_setpins(s->nand, 1, 0, 0, 1, 0);
+    nand_setio(s->nand, 0xff);
+       /* send command: read page (cycle1) */
+    nand_setpins(s->nand, 1, 0, 0, 1, 0);
+    nand_setio(s->nand, 0);
+       /* send page address (x16 device):
+       bits  0-11 define cache address in words (bit11 set only for OOB access)
+       bits 16-33 define page and block address */
+    nand_setpins(s->nand, 0, 1, 0, 1, 0);
+    nand_setio(s->nand, (addr >> 1) & 0xff);
+    nand_setio(s->nand, (addr >> 9) & 0x3);
+    nand_setio(s->nand, (addr >> 11) & 0xff);
+    nand_setio(s->nand, (addr >> 19) & 0xff);
+    nand_setio(s->nand, (addr >> 27) & 0x1);
+       /* send command: read page (cycle2) */
+    nand_setpins(s->nand, 1, 0, 0, 1, 0);
+    nand_setio(s->nand, 0x30);
+    /* read page data */
+    nand_setpins(s->nand, 0, 0, 0, 1, 0);
+    for (i = 0; i < 0x800 / 2; i++)
+        *(p++) = nand_getio(s->nand);
+    return 1;
 }
 
 /*read the xloader from NAND Flash into internal RAM*/
@@ -180,15 +123,14 @@ static int beagle_boot_from_nand(struct beagle_s *s)
        beagle_nand_read_page(s,nand_page,0);
        len = *((uint32_t*)nand_page);
        loadaddr =  *((uint32_t*)(nand_page+4));
-       if ((len==0)||(loadaddr==0)||(len==0xffffffff)||(loadaddr==0xffffffff))
+    fprintf(stderr, "%s: len = 0x%08x, addr = 0x%08x\n", __FUNCTION__, len, loadaddr);
+    if ((len==0)||(loadaddr==0)||(len==0xffffffff)||(loadaddr==0xffffffff))
                return (-1);
 
        /*put the first page into internal ram*/
-       load_dest = phys_ram_base +beagle_binfo.ram_size;
+       load_dest = phys_ram_base + beagle_binfo.ram_size;
        load_dest += loadaddr-OMAP3_SRAM_BASE;
        
-       BEAGLE_DEBUG("load_dest %x phys_ram_base %x \n",(unsigned)load_dest,(unsigned)phys_ram_base);
-       
        memcpy(load_dest,nand_page+8,0x800-8);
        load_dest += 0x800-8;
 
index 2d1784f..8cbd9e0 100644 (file)
@@ -23,16 +23,9 @@ 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);
-
-/* nand_bpage.c */
-struct nand_bflash_s;
-struct nand_bflash_s *nandb_init(int manf_id, int chip_id);
-void nandb_write_data16(struct nand_bflash_s *s, uint16_t value);
-uint16_t nandb_read_data16(struct nand_bflash_s *s);
-void nandb_write_address(struct nand_bflash_s *s, uint16_t value);
-void nandb_write_command(struct nand_bflash_s *s, uint16_t value);
+void nand_setio(struct nand_flash_s *s, uint32_t value);
+uint32_t nand_getio(struct nand_flash_s *s);
+uint32_t nand_getbuswidth(struct nand_flash_s *s);
 
 #define NAND_MFR_TOSHIBA       0x98
 #define NAND_MFR_SAMSUNG       0xec
index e73a1b8..25cb976 100644 (file)
--- a/hw/nand.c
+++ b/hw/nand.c
@@ -6,6 +6,12 @@
  * Copyright (c) 2006 Openedhand Ltd.
  * Written by Andrzej Zaborowski <balrog@zabor.org>
  *
+ * Copyright (C) 2009 Nokia Corporation
+ * Support for variable bus width and read cache commands based
+ * on "MT29F2G16ABCWP 2Gx16" datasheet from Micron Technology and
+ * "NAND02G-B2C" datasheet from ST Microelectronics.
+ * Written by Juha Riihimäki
+ *
  * This code is licensed under the GNU GPL v2.
  */
 
@@ -21,6 +27,9 @@
 # define NAND_CMD_READ1                0x01
 # define NAND_CMD_READ2                0x50
 # define NAND_CMD_LPREAD2      0x30
+# define NAND_CMD_READCACHESTART 0x31
+# define NAND_CMD_READCACHEEXIT  0x34
+# define NAND_CMD_READCACHELAST  0x3f
 # define NAND_CMD_NOSERIALREAD2        0x35
 # define NAND_CMD_RANDOMREAD1  0x05
 # define NAND_CMD_RANDOMREAD2  0xe0
@@ -39,7 +48,7 @@
 # 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_READY   (3 << 5)
 # define NAND_IOSTATUS_UNPROTCT        (1 << 7)
 
 # define MAX_PAGE              0x800
@@ -47,6 +56,7 @@
 
 struct nand_flash_s {
     uint8_t manf_id, chip_id;
+    uint8_t buswidth; /* in BYTES */
     int size, pages;
     int page_shift, oob_shift, erase_shift, addr_shift;
     uint8_t *storage;
@@ -59,14 +69,15 @@ struct nand_flash_s {
     uint8_t *ioaddr;
     int iolen;
 
-    uint32_t cmd, addr;
+    uint32_t cmd;
+    uint64_t 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);
+    void (*blk_load)(struct nand_flash_s *s, uint64_t addr, int offset);
 };
 
 # define NAND_NO_AUTOINCR      0x00000001
@@ -208,25 +219,34 @@ static void nand_reset(struct nand_flash_s *s)
     s->iolen = 0;
     s->offset = 0;
     s->status &= NAND_IOSTATUS_UNPROTCT;
+    s->status |= NAND_IOSTATUS_READY;
+}
+
+static inline void nand_pushio_byte(struct nand_flash_s *s, uint8_t value)
+{
+    s->ioaddr[s->iolen++] = value;
+    for (value = s->buswidth; --value;)
+        s->ioaddr[s->iolen++] = 0;
 }
 
 static void nand_command(struct nand_flash_s *s)
 {
     switch (s->cmd) {
     case NAND_CMD_READ0:
+    case NAND_CMD_READCACHEEXIT:
         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) */
+        s->ioaddr = s->io;
+        s->iolen = 0;
+        nand_pushio_byte(s, s->manf_id);
+        nand_pushio_byte(s, s->chip_id);
+        nand_pushio_byte(s, 'Q'); /* Don't-case byte (often 0xa5) */
         if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP)
-            s->io[3] = 0x15;   /* Page Size, Block Size, Spare Size.. */
+            nand_pushio_byte(s, (s->buswidth == 2) ? 0x55 : 0x15);
         else
-            s->io[3] = 0xc0;   /* Multi-plane */
-        s->ioaddr = s->io;
-        s->iolen = 4;
+            nand_pushio_byte(s, 0xc0); /* Multi-plane */
         break;
 
     case NAND_CMD_RANDOMREAD2:
@@ -234,7 +254,7 @@ static void nand_command(struct nand_flash_s *s)
         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));
+        s->blk_load(s, s->addr, (int)(s->addr & ((1 << s->addr_shift) - 1)));
         break;
 
     case NAND_CMD_RESET:
@@ -267,9 +287,9 @@ static void nand_command(struct nand_flash_s *s)
         break;
 
     case NAND_CMD_READSTATUS:
-        s->io[0] = s->status;
         s->ioaddr = s->io;
-        s->iolen = 1;
+        s->iolen = 0;
+        nand_pushio_byte(s, s->status);
         break;
 
     default:
@@ -290,7 +310,7 @@ static void nand_save(QEMUFile *f, void *opaque)
     qemu_put_be32(f, s->iolen);
 
     qemu_put_be32s(f, &s->cmd);
-    qemu_put_be32s(f, &s->addr);
+    qemu_put_be64s(f, &s->addr);
     qemu_put_be32(f, s->addrlen);
     qemu_put_be32(f, s->status);
     qemu_put_be32(f, s->offset);
@@ -312,7 +332,7 @@ static int nand_load(QEMUFile *f, void *opaque, int version_id)
         return -EINVAL;
 
     qemu_get_be32s(f, &s->cmd);
-    qemu_get_be32s(f, &s->addr);
+    qemu_get_be64s(f, &s->addr);
     s->addrlen = qemu_get_be32(f);
     s->status = qemu_get_be32(f);
     s->offset = qemu_get_be32(f);
@@ -344,11 +364,16 @@ void nand_getpins(struct nand_flash_s *s, int *rb)
     *rb = 1;
 }
 
-void nand_setio(struct nand_flash_s *s, uint8_t value)
+void nand_setio(struct nand_flash_s *s, uint32_t value)
 {
+    int i;
+    
     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)
+            if (s->cmd == NAND_CMD_READ0 
+                && (value == NAND_CMD_LPREAD2
+                    || value == NAND_CMD_READCACHESTART
+                    || value == NAND_CMD_READCACHELAST))
                 return;
             if (value == NAND_CMD_RANDOMREAD1) {
                 s->addr &= ~((1 << s->addr_shift) - 1);
@@ -375,7 +400,8 @@ void nand_setio(struct nand_flash_s *s, uint8_t value)
                 s->cmd == NAND_CMD_BLOCKERASE2 ||
                 s->cmd == NAND_CMD_NOSERIALREAD2 ||
                 s->cmd == NAND_CMD_RANDOMREAD2 ||
-                s->cmd == NAND_CMD_RESET)
+                s->cmd == NAND_CMD_RESET ||
+            s->cmd == NAND_CMD_READCACHEEXIT)
             nand_command(s);
 
         if (s->cmd != NAND_CMD_RANDOMREAD2) {
@@ -388,40 +414,60 @@ void nand_setio(struct nand_flash_s *s, uint8_t value)
         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);
+        switch (s->addrlen) {
+            case 1:
+                if (s->cmd == NAND_CMD_READID)
+                    nand_command(s);
+                break;
+            case 2: /* fix cache address as a byte address */
+                s->addr <<= (s->buswidth - 1);
+                break;
+            case 3:
+                if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP)
+                    && (s->cmd == NAND_CMD_READ0
+                        || s->cmd == NAND_CMD_PAGEPROGRAM1))
+                    nand_command(s);
+                break;
+            case 4:
+                if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP)
+                    && nand_flash_ids[s->chip_id].size < 256 /* 1Gb or less */
+                    && (s->cmd == NAND_CMD_READ0 ||
+                        s->cmd == NAND_CMD_PAGEPROGRAM1))
+                    nand_command(s);
+                break;
+            case 5:
+                if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP)
+                    && nand_flash_ids[s->chip_id].size >= 256 /* 2Gb or more */
+                    && (s->cmd == NAND_CMD_READ0 ||
+                        s->cmd == NAND_CMD_PAGEPROGRAM1))
+                    nand_command(s);
+                break;
+            default:
+                break;
+        }
     }
 
     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;
+            for (i = s->buswidth; i--; value >>= 8)
+                s->io[s->iolen++] = (uint8_t)(value & 0xff);
     } 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 ++;
-        }
+                (1 << s->page_shift) + (1 << s->oob_shift))
+            for (i = s->buswidth; i--; s->addr++, value >>= 8)
+                s->io[s->iolen + (s->addr & ((1 << s->addr_shift) - 1))] =
+                    (uint8_t)(value & 0xff);
     }
 }
 
-uint8_t nand_getio(struct nand_flash_s *s)
+uint32_t nand_getio(struct nand_flash_s *s)
 {
     int offset;
+    uint16_t x = 0;
 
     /* Allow sequential reading */
     if (!s->iolen && s->cmd == NAND_CMD_READ0) {
-        offset = (s->addr & ((1 << s->addr_shift) - 1)) + s->offset;
+        offset = (int)((s->addr & ((1 << s->addr_shift) - 1))) + s->offset;
         s->offset = 0;
 
         s->blk_load(s, s->addr, offset);
@@ -434,8 +480,22 @@ uint8_t nand_getio(struct nand_flash_s *s)
     if (s->ce || s->iolen <= 0)
         return 0;
 
-    s->iolen --;
-    return *(s->ioaddr ++);
+    for (offset = s->buswidth; offset--;)
+        x |= s->ioaddr[offset] << (offset << 3);
+    /* after receiving READ STATUS command all subsequent reads will
+       return the status register value until another command is issued */
+    if (s->cmd != NAND_CMD_READSTATUS) {
+        s->ioaddr += s->buswidth;
+        s->iolen  -= s->buswidth;
+    }
+    return x;
+}
+
+uint32_t nand_getbuswidth(struct nand_flash_s *s)
+{
+    if (!s)
+        return 0;
+    return (s->buswidth << 3);
 }
 
 struct nand_flash_s *nand_init(int manf_id, int chip_id)
@@ -455,6 +515,7 @@ struct nand_flash_s *nand_init(int manf_id, int chip_id)
         s->bdrv = drives_table[index].bdrv;
     s->manf_id = manf_id;
     s->chip_id = chip_id;
+    s->buswidth = (uint8_t)(nand_flash_ids[s->chip_id].width >> 3);
     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;
@@ -609,7 +670,7 @@ static void glue(nand_blk_erase_, PAGE_SIZE)(struct nand_flash_s *s)
 }
 
 static void glue(nand_blk_load_, PAGE_SIZE)(struct nand_flash_s *s,
-                uint32_t addr, int offset)
+                uint64_t addr, int offset)
 {
     if (PAGE(addr) >= s->pages)
         return;
@@ -617,7 +678,7 @@ static void glue(nand_blk_load_, PAGE_SIZE)(struct nand_flash_s *s,
     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",
+                printf("%s: read error in sector %lli\n",
                                 __FUNCTION__, SECTOR(addr));
             memcpy(s->io + SECTOR_OFFSET(s->addr) + PAGE_SIZE,
                             s->storage + (PAGE(s->addr) << OOB_SHIFT),
@@ -626,7 +687,7 @@ static void glue(nand_blk_load_, PAGE_SIZE)(struct nand_flash_s *s,
         } else {
             if (bdrv_read(s->bdrv, PAGE_START(addr) >> 9,
                                     s->io, (PAGE_SECTORS + 2)) == -1)
-                printf("%s: read error in sector %i\n",
+                printf("%s: read error in sector %lli\n",
                                 __FUNCTION__, PAGE_START(addr) >> 9);
             s->ioaddr = s->io + (PAGE_START(addr) & 0x1ff) + offset;
         }
diff --git a/hw/nand_bpage.c b/hw/nand_bpage.c
deleted file mode 100644 (file)
index 586b914..0000000
+++ /dev/null
@@ -1,445 +0,0 @@
-/*
- * Big page NAND flash memory emulation.  based on 256M/16 bit flash datasheet from micro(MT29F2G16ABC)
- *
- * Copyright (C) 2008 yajin(yajin@vm-kernel.org)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 or
- * (at your option) version 3 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-#include "hw.h"
-#include "flash.h"
-#include "block.h"
-#include "sysemu.h"
-
-
-#define MAX_PAGE               0x800
-#define MAX_OOB                0x40
-#define PAGE_MASK              (0xffff)
-#define BUS_WIDTH_16  2
-#define BUS_WIDTH_8 1
-
-//#define DEBUG
-
-struct nand_flash_info_s
-{
-    uint8_t  manf_id,chip_id;
-    uint32_t size;;
-    int bus_width;
-    int page_shift;
-    int oob_shift;
-    int block_shift;
-};
-struct nand_flash_info_s nand_flash_info[1] =
-{
-    {0x2c, 0xba, 256,2, 11, 6, 6}
-};
-
-
-struct nand_bflash_s
-{
-       BlockDriverState *bdrv;
-    uint8_t manf_id, chip_id;
-    uint32_t size, pages;
-    uint32_t page_size, page_shift;
-    uint32_t oob_size, oob_shift;
-    uint32_t page_oob_size;
-    uint32_t page_sectors;      /*sector = 512 bytes */
-    uint32_t block_shift, block_pages;  /*how many pages in a block */
-    uint32_t bus_width;         /*bytes */
-
-    //uint8_t *internal_buf;
-    uint8_t io[MAX_PAGE + MAX_OOB + 0x400];
-    uint8_t *ioaddr;
-    int iolen;
-
-
-    uint32 addr_low, addr_high;
-    uint32 addr_cycle;
-
-    uint32 cmd, status;
-  #ifdef DEBUG  
-    FILE *fp;
-  #endif
-};
-
-
-#ifdef DEBUG
-static void debug_init(struct nand_bflash_s *s)
-{
-       s->fp=fopen("nandflash_debug.txt","w+");
-       if (s->fp==NULL)
-       {
-               fprintf(stderr,"can not open nandflash_debug.txt \n");
-               exit(-1);
-       }
-               
-}
-static void debug_out(struct nand_bflash_s *s,const char* format, ...)
-{
-       va_list ap;
-       if (s->fp)
-       {
-                va_start(ap, format);
-        vfprintf(s->fp, format, ap);
-        fflush(s->fp);
-       va_end(ap);
-       }
-}
-
-#else
-static void debug_init(struct nand_bflash_s *s)
-{
-       
-}
-static void debug_out(struct nand_bflash_s *s,const char* format, ...)
-{
-       
-}
-
-#endif
-
-static inline uint32_t get_page_number(struct nand_bflash_s *s,
-                                       uint32_t addr_low, uint32 addr_high)
-{
-    return (addr_high << 16) + ((addr_low >> 16) & PAGE_MASK);
-}
-
-
-
-/* Program a single page */
-static void nand_blk_write(struct nand_bflash_s *s)
-{
-    uint32_t page_number, off,  sector, soff;
-    uint8_t *iobuf=NULL;
-
-       if (!iobuf)
-       iobuf = qemu_mallocz((s->page_sectors + 2) * 0x200);
-    if (!iobuf)
-    {
-        fprintf(stderr, "can not alloc io buffer size 0x%x \n",
-                (s->page_sectors + 2) * 0x200);
-        cpu_abort(cpu_single_env, "%s: can not alloc io buffer size 0x%x \n",
-                  __FUNCTION__, (s->page_sectors + 2) * 0x200);
-    }
-
-    page_number = get_page_number(s, s->addr_low, s->addr_high);
-
-    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);
-
-    if (page_number >= s->pages)
-        return;
-
-    off = page_number * s->page_oob_size + (s->addr_low & PAGE_MASK);
-    sector = off >> 9;
-    soff = off & 0x1ff;
-    if (bdrv_read(s->bdrv, sector, iobuf, s->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, s->page_sectors + 2) == -1)
-        printf("%s: write error in sector %i\n", __FUNCTION__, sector);
-
-    //qemu_free(iobuf);
-}
-
-
-static void nandb_blk_load(struct nand_bflash_s *s)
-{
-    uint32_t page_number, offset;
-    offset = s->addr_low & PAGE_MASK;
-
-    page_number = get_page_number(s, s->addr_low, s->addr_high);
-       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);
-    if (page_number >= s->pages)
-        return;
-       
-    if (bdrv_read(s->bdrv, (page_number * s->page_oob_size + offset) >> 9,
-                  s->io, (s->page_sectors + 2)) == -1)
-        printf("%s: read error in sector %i\n",
-               __FUNCTION__, page_number * s->page_oob_size);
-    s->ioaddr = s->io + ((page_number * s->page_oob_size + offset) & 0x1ff);
-}
-
-
-/* Erase a single block */
-static void nandb_blk_erase(struct nand_bflash_s *s)
-{
-    uint32_t page_number,  sector, addr, i;
-
-    uint8_t iobuf[0x200];
-
-        memset(iobuf,0xff,sizeof(iobuf));
-        s->addr_low = s->addr_low & ~((1 << (16 + s->block_shift)) - 1);
-    page_number = get_page_number(s, s->addr_low, s->addr_high);
-    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);
-    if (page_number >= s->pages)
-        return;
-
-    addr = page_number * s->page_oob_size;
-    
-    sector = addr >> 9;
-    if (bdrv_read(s->bdrv, sector, iobuf, 1) == -1)
-        printf("%s: read error in sector %i\n", __FUNCTION__, sector);
-    memset(iobuf + (addr & 0x1ff), 0xff, (~addr & 0x1ff) + 1);
-    if (bdrv_write(s->bdrv, sector, iobuf, 1) == -1)
-        printf("%s: write error in sector %i\n", __FUNCTION__, sector);
-
-    memset(iobuf, 0xff, 0x200);
-    i = (addr & ~0x1ff) + 0x200;
-    for (addr += (s->page_oob_size*s->block_pages - 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);
-
-    sector = i >> 9;
-    if (bdrv_read(s->bdrv, sector, iobuf, 1) == -1)
-        printf("%s: read error in sector %i\n", __FUNCTION__, sector);
-    memset(iobuf, 0xff, ((addr - 1) & 0x1ff) + 1);
-    if (bdrv_write(s->bdrv, sector, iobuf, 1) == -1)
-        printf("%s: write error in sector %i\n", __FUNCTION__, sector);
-}
-
-static void nandb_next_page(struct nand_bflash_s *s)
-{
-    if ((s->addr_low + 0x10000) < s->addr_low)
-        s->addr_high++;
-    s->addr_low += 0x10000;
-}
-
-void nandb_write_command(struct nand_bflash_s *s, uint16_t value)
-{
-    int id_index[4] = { 0, 1, 2, 3 };
-
-    debug_out(s,"nandb_write_command %x\n",value);
-
-    switch (value)
-    {
-    case 0x00: /* PAGE READ (cycle 1) */
-    case 0x05: /* RANDOM DATA READ (cycle 1) */
-       s->iolen = 0;
-        s->addr_cycle = 0;
-        break;
-    case 0x10: /* PROGRAM xxx / OTP PROTECT (cycle 2) */
-        nand_blk_write(s);
-        break;
-    case 0x30: /* PAGE READ / OTP DATA READ (cycle 2) */
-    case 0xe0: /* RANDOM DATA READ (cycle 2) */
-        s->iolen = s->page_oob_size - (s->addr_low & PAGE_MASK);
-        nandb_blk_load(s);
-        s->addr_cycle = 0;
-        break;
-    case 0x31: /* PAGE READ CACHE MODE START */
-    case 0x3f: /* PAGE READ CACHE MODE START LAST */
-        nandb_next_page(s);
-        s->iolen = s->page_oob_size - (s->addr_low & PAGE_MASK);
-        nandb_blk_load(s);
-        break;
-    case 0x60: /* BLOCK ERASE (cycle 1) */
-        /*erase only needs 3 address cycles. Its address is block address*/
-        s->addr_low &= ~PAGE_MASK;
-        s->addr_high =0;
-        s->addr_cycle = 2;
-        break;
-    case 0x70: /* READ STATUS */
-        if ((s->manf_id == NAND_MFR_MICRON) && (s->chip_id == 0xba))
-        {
-            s->status |= 0x60;  /*flash is ready */
-            s->status |= 0x80;  /*not protect */
-        }
-        s->io[0] = s->status;
-        s->ioaddr = s->io;
-        s->iolen = 1;
-        break;
-    case 0x80: /* PROGRAM PAGE / PROGRAM PAGE CACHE MODE (cycle 1) */
-    case 0x85: /* PROGRAM for INTERNAL DATA MOVE (cycle 1) / RANDOM DATA INPUT */
-        s->addr_cycle = 0;
-        s->ioaddr = s->io;
-        s->iolen = 0;
-        break;
-    case 0x90: /* READ ID */
-        s->iolen = 4 * s->bus_width;
-        memset(s->io, 0x0, s->iolen);
-        if (s->bus_width == BUS_WIDTH_16)
-        {
-            id_index[0] = 0;
-            id_index[1] = 2;
-            id_index[2] = 4;
-            id_index[3] = 6;
-        }
-        s->io[id_index[0]] = s->manf_id;
-        s->io[id_index[1]] = s->chip_id;
-        s->io[id_index[2]] = 'Q';       /* Don't-care byte (often 0xa5) */
-        if ((s->manf_id == NAND_MFR_MICRON) && (s->chip_id == 0xba))
-            s->io[id_index[3]] = 0x55;
-        s->ioaddr = s->io;
-        break;
-    case 0xa0: /* OTP DATA PROGRAM (cycle 1) */
-    case 0xa5: /* OTP DATA PROTECT (cycle 1) */
-    case 0xaf: /* OTP DATA READ (cycle 1) */
-        fprintf(stderr, "%s: OTP command 0x%x not implemented\n", __FUNCTION__, value);
-        break;
-    case 0xd0: /* BLOCK ERASE (cycle 2) */
-        nandb_blk_erase(s);
-        break;
-    case 0xff: /* RESET */
-       s->addr_cycle =0;
-       s->iolen=0;
-       s->addr_low =0;
-       s->addr_high =0;
-       s->ioaddr = NULL;
-       break;
-    default:
-        fprintf(stderr, "%s: unknown nand command 0x%x \n", __FUNCTION__, value);
-        exit(-1);
-    }
-    s->cmd = value;
-}
-
-void nandb_write_address(struct nand_bflash_s *s, uint16_t value)
-{
-    uint32_t mask;
-    uint32_t colum_addr;
-    
-    /*fprintf(stderr, "%s: addr_cycle=%d, value=0x%04x, addr_low=0x%08x, addr_high=0x%08x\n",
-            __FUNCTION__, s->addr_cycle, value, s->addr_low, s->addr_high );*/
-    
-    if (s->cmd==0x60)
-        debug_out(s,"value %x addr_cycle %x \n",value,s->addr_cycle);
-  
-    if (s->addr_cycle < 5) {
-        if (s->addr_cycle < 4) {
-            mask = ~(0xff << (s->addr_cycle * 8));
-            s->addr_low &= mask;
-            s->addr_low |= value << (s->addr_cycle * 8);
-        } else {
-            mask = ~(0xff << ((s->addr_cycle-4) * 8));
-            s->addr_high &= mask;
-            s->addr_high |= value << ((s->addr_cycle-4) * 8);
-        }
-    }
-    else {
-        fprintf(stderr,"%s wrong addr cycle\n",__FUNCTION__);
-        exit(-1);
-    }
-    if ((s->addr_cycle==1)&&(s->bus_width!=1)) {
-       colum_addr = s->addr_low & PAGE_MASK;
-       colum_addr *= s->bus_width;
-       s->addr_low &= ~PAGE_MASK;
-       s->addr_low += colum_addr;
-    }
-    s->addr_cycle++;
-}
-
-
-uint16_t nandb_read_data16(struct nand_bflash_s *s)
-{
-       uint16_t ret;
-       if ((s->iolen==0)&&(s->cmd==0x31))
-       {
-               nandb_next_page(s);
-        s->iolen = s->page_oob_size - (s->addr_low & PAGE_MASK);
-        nandb_blk_load(s);
-       }
-       if (s->iolen <= 0)
-       {
-               fprintf(stderr,"iolen <0 \n");
-               exit(-1);
-       }
-       if (s->cmd!=0x70)       
-       s->iolen -=2 ;
-    ret = *((uint16_t *)s->ioaddr);
-    if (s->cmd!=0x70)          
-       s->ioaddr += 2;         
-    return ret;
-}
-
-void nandb_write_data16(struct nand_bflash_s *s, uint16_t value)
-{
-        if ((s->cmd == 0x80) )
-        {
-        if (s->iolen < s->page_oob_size)
-        {
-               s->io[s->iolen ++] = value&0xff;
-               s->io[s->iolen ++] = (value>>8)&0xff;
-        }
-    }
-}
-
-struct nand_bflash_s *nandb_init(int manf_id, int chip_id)
-{
-    //int pagesize;
-    struct nand_bflash_s *s;
-    int index;
-    int i;
-
-    s = (struct nand_bflash_s *) qemu_mallocz(sizeof(struct nand_bflash_s));
-    for (i = 0; i < sizeof(nand_flash_info); i++)
-    {
-        if ((nand_flash_info[i].manf_id == manf_id)
-            && (nand_flash_info[i].chip_id == chip_id))
-        {
-            s->manf_id = manf_id;
-            s->chip_id = chip_id;
-            s->page_shift = nand_flash_info[i].page_shift;
-            s->oob_shift = nand_flash_info[i].oob_shift;
-            s->bus_width = nand_flash_info[i].bus_width;
-            s->page_size = 1 << s->page_shift;
-            s->oob_size = 1 << s->oob_shift;
-            s->block_shift = nand_flash_info[i].block_shift;
-            s->block_pages = 1 << s->block_shift;
-            s->page_oob_size = s->page_size + s->oob_size;
-            s->page_sectors = 1 << (s->page_shift - 9);
-            /*TODO: size overflow */
-            s->size = nand_flash_info[i].size << 20;
-            s->pages = (s->size / s->page_size);
-
-            break;
-        }
-
-    }
-    if (i >= sizeof(nand_flash_info))
-    {
-        fprintf(stderr, "%s: Unsupported NAND chip ID.\n",
-                  __FUNCTION__);
-        exit(-1);
-    }
-
-    
-    index = drive_get_index(IF_MTD, 0, 0);
-    if (index != -1)
-        s->bdrv = drives_table[index].bdrv;
-    else
-    {
-       fprintf(stderr, "%s: Please use -mtdblock to specify flash image.\n",
-                  __FUNCTION__);
-        exit(-1);
-    }
-
-    if (bdrv_getlength(s->bdrv) != (s->pages*s->page_oob_size))
-    {
-       fprintf(stderr,  "%s: Invalid flash image size.\n",
-                  __FUNCTION__);
-        exit(-1);
-
-    }
-
-       debug_init(s);
-    return s;
-
-}
index 65f1169..671de6e 100644 (file)
@@ -170,7 +170,7 @@ static void n8x0_nand_setup(struct n800_s *s)
                     (s->nand = onenand_init(0xec4800, 1,
                                             omap2_gpio_in_get(s->cpu->gpif,
                                                     N8X0_ONENAND_GPIO)[0])),
-                     NULL, NULL);
+                     NULL);
     otp_region = onenand_raw_otp(s->nand);
 
     memcpy(otp_region + 0x000, n8x0_cal_wlan_mac, sizeof(n8x0_cal_wlan_mac));
@@ -771,10 +771,10 @@ static void n8x0_usb_setup(struct n800_s *s)
     /* Using the NOR interface */
     omap_gpmc_attach(s->cpu->gpmc, N8X0_USB_ASYNC_CS,
                     tusb6010_async_io(tusb), 0, 0, tusb,
-                    NULL, NULL);
+                    NULL);
     omap_gpmc_attach(s->cpu->gpmc, N8X0_USB_SYNC_CS,
                     tusb6010_sync_io(tusb), 0, 0, tusb,
-                    NULL, NULL);
+                    NULL);
 
     s->usb = tusb;
     omap2_gpio_out_set(s->cpu->gpif, N8X0_TUSB_ENABLE_GPIO, tusb_pwr);
index 949b853..e0fb14d 100644 (file)
--- a/hw/omap.h
+++ b/hw/omap.h
@@ -115,13 +115,13 @@ struct omap_sdrc_s *omap_sdrc_init(target_phys_addr_t base);
 void omap_sdrc_write_mcfg(struct omap_sdrc_s *s, uint32_t value, uint32_t cs);
 
 struct omap_gpmc_s;
+struct nand_flash_s;
 struct omap_gpmc_s *omap_gpmc_init(struct omap_mpu_state_s *mpu,
                                    target_phys_addr_t base, qemu_irq irq);
 void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, int iomemtype,
                 void (*base_upd)(void *opaque, target_phys_addr_t new),
                 void (*unmap)(void *opaque), void *opaque,
-                CPUReadMemoryFunc **nand_readfn,
-                CPUWriteMemoryFunc **nand_writefn);
+                struct nand_flash_s *nand_s);
 
 /*
  * Common IRQ numbers for level 1 interrupt handler
index 2503eec..38cd6de 100644 (file)
@@ -4254,8 +4254,7 @@ struct omap_gpmc_s {
         void (*base_update)(void *opaque, target_phys_addr_t new);
         void (*unmap)(void *opaque);
         void *opaque;
-        CPUReadMemoryFunc **nand_readfn;
-        CPUWriteMemoryFunc **nand_writefn;
+        struct nand_flash_s *nand;
     } cs_file[8];
     int ecc_cs;
     int ecc_ptr;
@@ -4350,6 +4349,7 @@ static uint32_t omap_gpmc_read(void *opaque, target_phys_addr_t addr)
     struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque;
     int cs;
     struct omap_gpmc_cs_file_s *f;
+    uint32_t x1, x2, x3, x4;
 
     switch (addr) {
     case 0x000:        /* GPMC_REVISION */
@@ -4400,9 +4400,27 @@ static uint32_t omap_gpmc_read(void *opaque, target_phys_addr_t addr)
             case 0x78: /* GPMC_CONFIG7 */
                 return f->config[6];
             case 0x84: /* GPMC_NAND_DATA */
-                if (f->nand_readfn)
-                    return f->nand_readfn[2](f->opaque, addr);
+                if (((f->config[0] >> 10) & 3) == 2 /* NAND device type ? */
+                    && f->nand) {
+                    nand_setpins(f->nand, 0, 0, 0, 1, 0);
+                    switch (((f->config[0] >> 12) & 3)) {
+                        case 0: /* 8bit */
+                            x1 = nand_getio(f->nand);
+                            x2 = nand_getio(f->nand);
+                            x3 = nand_getio(f->nand);
+                            x4 = nand_getio(f->nand);
+                            return (x4 << 24) | (x3 << 16) | (x2 << 8) | x1;
+                        case 1: /* 16bit */
+                            x1 = nand_getio(f->nand);
+                            x2 = nand_getio(f->nand);
+                            return (x2 << 16) | x1;
+                        default:
+                            return 0;
+                    }
+                }
                 return 0;
+            default:
+                break;
         }
         break;
 
@@ -4456,14 +4474,27 @@ static uint32_t omap_gpmc_read8(void *opaque, target_phys_addr_t addr)
             addr -= cs * 0x30;
             f = s->cs_file + cs;
             switch (addr) {
-                case 0x84:     /* GPMC_NAND_DATA */
-                    if (f->nand_readfn)
-                        return f->nand_readfn[0](f->opaque, addr);
+                case 0x84 ... 0x87:    /* GPMC_NAND_DATA */
+                    if (((f->config[0] >> 10) & 3) == 2 /* NAND device type ? */
+                        && f->nand) {
+                        nand_setpins(f->nand, 0, 0, 0, 1, 0);
+                        switch (((f->config[0] >> 12) & 3)) {
+                            case 0: /* 8bit */
+                                return nand_getio(f->nand);
+                            case 1: /* 16bit */
+                                /* reading 8bits from a 16bit device?! */
+                                return nand_getio(f->nand);
+                            default:
+                                return 0;
+                        }
+                    }
                     return 0;
+                default:
+                    break;
             }
             break;
-    default:
-        break;
+        default:
+            break;
     }
     OMAP_BAD_REG(addr);
     return 0;
@@ -4474,6 +4505,7 @@ static uint32_t omap_gpmc_read16(void *opaque, target_phys_addr_t addr)
     struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque;
     int cs;
     struct omap_gpmc_cs_file_s *f;
+    uint32_t x1, x2;
     
     switch (addr) {
         case 0x060 ... 0x1d4:
@@ -4482,9 +4514,24 @@ static uint32_t omap_gpmc_read16(void *opaque, target_phys_addr_t addr)
             f = s->cs_file + cs;
             switch (addr) {
                 case 0x84:     /* GPMC_NAND_DATA */
-                    if (f->nand_readfn)
-                        return f->nand_readfn[1](f->opaque, addr);
+                case 0x86:
+                    if (((f->config[0] >> 10) & 3) == 2 /* NAND device type ? */
+                        && f->nand) {
+                        nand_setpins(f->nand, 0, 0, 0, 1, 0);
+                        switch (((f->config[0] >> 12) & 3)) {
+                            case 0: /* 8bit */
+                                x1 = nand_getio(f->nand);
+                                x2 = nand_getio(f->nand);
+                                return (x2 << 8) | x1;
+                            case 1: /* 16bit */
+                                return nand_getio(f->nand);
+                            default:
+                                return 0;
+                        }
+                    }
                     return 0;
+                default:
+                    break;
             }
             break;
         default:
@@ -4579,10 +4626,30 @@ static void omap_gpmc_write(void *opaque, target_phys_addr_t addr,
             case 0x7c: /* GPMC_NAND_COMMAND */
             case 0x80: /* GPMC_NAND_ADDRESS */
             case 0x84: /* GPMC_NAND_DATA */
-                if (f->nand_writefn)
-                    f->nand_writefn[2](f->opaque, addr, value);
+                if (((f->config[0] >> 10) & 3) == 2 /* NAND device type ? */
+                    && f->nand) {
+                    switch (addr) {
+                        case 0x7c: nand_setpins(f->nand, 1, 0, 0, 1, 0); break; /* CLE */
+                        case 0x80: nand_setpins(f->nand, 0, 1, 0, 1, 0); break; /* ALE */
+                        case 0x84: nand_setpins(f->nand, 0, 0, 0, 1, 0); break;
+                        default: break;
+                    }
+                    switch (((f->config[0] >> 12) & 3)) {
+                        case 0: /* 8bit */
+                            nand_setio(f->nand, value & 0xff);
+                            nand_setio(f->nand, (value >> 8) & 0xff);
+                            nand_setio(f->nand, (value >> 16) & 0xff);
+                            nand_setio(f->nand, (value >> 24) & 0xff);
+                            break;
+                        case 1: /* 16bit */
+                            nand_setio(f->nand, value & 0xffff);
+                            nand_setio(f->nand, (value >> 16) & 0xffff);
+                            break;
+                        default:
+                            break;
+                    }
+                }
                 break;
-
             default:
                 goto bad_reg;
         }
@@ -4643,27 +4710,51 @@ static void omap_gpmc_write8(void *opaque, target_phys_addr_t addr,
     int cs;
     struct omap_gpmc_cs_file_s *f;
 
-    switch (addr)
-    {
-    case 0x060 ... 0x1d4:
-        cs = (addr - 0x060) / 0x30;
-        addr -= cs * 0x30;
-        f = s->cs_file + cs;
-        switch (addr) {
-        case 0x7c:     /* GPMC_NAND_COMMAND */
-        case 0x80:     /* GPMC_NAND_ADDRESS */
-        case 0x84:     /* GPMC_NAND_DATA */
-            if (f->nand_writefn)
-                f->nand_writefn[0](f->opaque, addr, value);
+    switch (addr) {
+        case 0x060 ... 0x1d4:
+            cs = (addr - 0x060) / 0x30;
+            addr -= cs * 0x30;
+            f = s->cs_file + cs;
+            switch (addr) {
+                case 0x7c ... 0x7f:    /* GPMC_NAND_COMMAND */
+                case 0x80 ... 0x83:    /* GPMC_NAND_ADDRESS */
+                case 0x84 ... 0x87:    /* GPMC_NAND_DATA */
+                    if (((f->config[0] >> 10) & 3) == 2 /* NAND device type ? */
+                        && f->nand) {
+                        switch (addr) {
+                            case 0x7c ... 0x7f:
+                                nand_setpins(f->nand, 1, 0, 0, 1, 0); /* CLE */
+                                break;
+                            case 0x80 ... 0x83:
+                                nand_setpins(f->nand, 0, 1, 0, 1, 0); /* ALE */
+                                break;
+                            case 0x84 ... 0x87:
+                                nand_setpins(f->nand, 0, 0, 0, 1, 0);
+                                break;
+                            default:
+                                break;
+                        }
+                        switch (((f->config[0] >> 12) & 3)) {
+                            case 0: /* 8bit */
+                                nand_setio(f->nand, value & 0xff);
+                                break;
+                            case 1: /* 16bit */
+                                /* writing to a 16bit device with 8bit access!? */
+                                nand_setio(f->nand, value & 0xffff);
+                                break;
+                            default:
+                                break;
+                        }
+                    }
+                    break;
+                default:
+                    goto bad_reg;
+            }
             break;
         default:
-            goto bad_reg;
-        }
-        break;
-    default:
-    bad_reg:
-        OMAP_BAD_REGV(addr, value);
-        return;
+        bad_reg:
+            OMAP_BAD_REGV(addr, value);
+            return;
     }
 }
 
@@ -4674,27 +4765,57 @@ static void omap_gpmc_write16(void *opaque, target_phys_addr_t addr,
     int cs;
     struct omap_gpmc_cs_file_s *f;
     
-    switch (addr)
-    {
-    case 0x060 ... 0x1d4:
-        cs = (addr - 0x060) / 0x30;
-        addr -= cs * 0x30;
-        f = s->cs_file + cs;
-        switch (addr) {
-        case 0x7c:     /* GPMC_NAND_COMMAND */
-        case 0x80:     /* GPMC_NAND_ADDRESS */
-        case 0x84:     /* GPMC_NAND_DATA */
-            if (f->nand_writefn)
-                f->nand_writefn[1](f->opaque, addr, value);
+    switch (addr) {
+        case 0x060 ... 0x1d4:
+            cs = (addr - 0x060) / 0x30;
+            addr -= cs * 0x30;
+            f = s->cs_file + cs;
+            switch (addr) {
+                case 0x7c:     /* GPMC_NAND_COMMAND */
+                case 0x7e:
+                case 0x80:     /* GPMC_NAND_ADDRESS */
+                case 0x82:
+                case 0x84:     /* GPMC_NAND_DATA */
+                case 0x86:
+                    if (((f->config[0] >> 10) & 3) == 2 /* NAND device type ? */
+                        && f->nand) {
+                        switch (addr) {
+                            case 0x7c:
+                            case 0x7e: 
+                                nand_setpins(f->nand, 1, 0, 0, 1, 0); /* CLE */
+                                break;
+                            case 0x80:
+                            case 0x82:
+                                nand_setpins(f->nand, 0, 1, 0, 1, 0); /* ALE */
+                                break;
+                            case 0x84:
+                            case 0x86:
+                                nand_setpins(f->nand, 0, 0, 0, 1, 0);
+                                break;
+                            default:
+                                break;
+                        }
+                        switch (((f->config[0] >> 12) & 3)) {
+                            case 0: /* 8bit */
+                                nand_setio(f->nand, value & 0xff);
+                                nand_setio(f->nand, (value >> 8) & 0xff);
+                                break;
+                            case 1: /* 16bit */
+                                nand_setio(f->nand, value & 0xffff);
+                                break;
+                            default:
+                                break;
+                        }
+                    }
+                    break;
+                default:
+                    goto bad_reg;
+            }
             break;
         default:
-        goto bad_reg;
-        }
-        break;
-    default:
-    bad_reg:
-        OMAP_BAD_REGV(addr, value);
-        return;
+        bad_reg:
+            OMAP_BAD_REGV(addr, value);
+            return;
     }
 }
 
@@ -4730,8 +4851,7 @@ struct omap_gpmc_s *omap_gpmc_init(struct omap_mpu_state_s *mpu,
 void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, int iomemtype,
                 void (*base_upd)(void *opaque, target_phys_addr_t new),
                 void (*unmap)(void *opaque), void *opaque,
-                CPUReadMemoryFunc **nand_readfn,
-                CPUWriteMemoryFunc **nand_writefn)
+                struct nand_flash_s *nand_s)
 {
     struct omap_gpmc_cs_file_s *f;
 
@@ -4745,9 +4865,15 @@ void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, int iomemtype,
     f->base_update = base_upd;
     f->unmap = unmap;
     f->opaque = opaque;
-    f->nand_readfn = nand_readfn;
-    f->nand_writefn = nand_writefn;
+    f->nand = nand_s;
 
+    if (f->nand) {
+        f->config[0] &= ~(0xf << 10);
+        f->config[0] |= 2 << 10;     /* DEVICETYPE = NAND */
+        if (nand_getbuswidth(f->nand) == 16)
+            f->config[0] |= 1 << 12; /* 16-bit device */
+    }
+    
     if (f->config[6] & (1 << 6))                               /* CSVALID */
         omap_gpmc_cs_map(f, f->config[6] & 0x3f,               /* MASKADDR */
                         (f->config[6] >> 8 & 0xf));            /* BASEADDR */