qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 09/18] hw: add QEMU model for Faraday NAND flash con


From: Dante
Subject: [Qemu-devel] [PATCH 09/18] hw: add QEMU model for Faraday NAND flash controller
Date: Fri, 18 Jan 2013 14:31:07 +0800

Signed-off-by: Kuo-Jung Su <address@hidden>
---
 hw/ftnandc021.c |  528 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/ftnandc021.h |   58 ++++++
 2 files changed, 586 insertions(+)
 create mode 100644 hw/ftnandc021.c
 create mode 100644 hw/ftnandc021.h

diff --git a/hw/ftnandc021.c b/hw/ftnandc021.c
new file mode 100644
index 0000000..0eb4e83
--- /dev/null
+++ b/hw/ftnandc021.c
@@ -0,0 +1,528 @@
+/*
+ * QEMU model of the FTNANDC021 NAND Flash Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Copyright (C) 2012 Dante Su <address@hidden>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to 
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "devices.h"
+#include "sysemu/blockdev.h"
+#include "flash.h"
+
+#include "ftnandc021.h"
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion mmio;
+
+    qemu_irq irq;
+    DeviceState *flash;
+
+    /* DMA hardware handshake */
+    qemu_irq req;
+
+    uint8_t  manf_id, chip_id;
+
+    int      cmd;
+    int      len;    /* buffer length for page read/write */
+    int      pi;    /* page index */
+    int      bw;    /* bus width (8-bits, 16-bits) */
+
+    uint64_t size;    /* flash size (maximum access range) */
+    uint32_t pgsz;    /* page size (Bytes) */
+    uint32_t bksz;    /* block size (Bytes) */
+    uint32_t alen;    /* address length (cycle) */
+
+    uint32_t id[2];
+    uint8_t  oob[8];/* 5 bytes for 512/2048 page; 7 bytes for 4096 page */
+
+    /* HW register caches */
+    uint32_t sr;
+    uint32_t fcr;
+    uint32_t mcr;
+    uint32_t ier;
+    uint32_t bcr;
+} ftnandc021_state;
+
+static inline void
+ftnandc021_irq_update(void *opaque)
+{
+    ftnandc021_state *s = opaque;
+    
+    if (s->ier & (1 << 7)) {
+        if ((s->ier & 0x0f) & (s->sr >> 2))
+            qemu_set_irq(s->irq, 1);
+        else
+            qemu_set_irq(s->irq, 0);
+    }
+}
+
+static inline void
+ftnandc021_set_idle(void *opaque)
+{
+    ftnandc021_state *s = opaque;
+    
+    /* CLE=0, ALE=0, CS=1 */
+    nand_setpins(s->flash, 0, 0, 1, 1, 0);
+    
+    /* Set command compelete */
+    s->sr |= (1 << 2);
+    
+    /* Update IRQ signal */
+    ftnandc021_irq_update(s);
+}
+
+static inline void
+ftnandc021_set_cmd(void *opaque, uint8_t cmd)
+{
+    ftnandc021_state *s = opaque;
+
+    /* CLE=1, ALE=0, CS=0 */
+    nand_setpins(s->flash, 1, 0, 0, 1, 0);
+
+    /* Write out command code */
+    nand_setio(s->flash, cmd);
+}
+
+static inline void
+ftnandc021_set_addr(void *opaque, int col, int row)
+{
+    ftnandc021_state *s = opaque;
+
+    /* CLE=0, ALE=1, CS=0 */
+    nand_setpins(s->flash, 0, 1, 0, 1, 0);
+
+    if (col < 0 && row < 0) {
+        /* special case for READ_ID (0x90) */
+        nand_setio(s->flash, 0);
+    } else {
+        /* column address */
+        if (col >= 0) {
+            nand_setio(s->flash, (col & 0x00ff) >> 0);
+            nand_setio(s->flash, (col & 0xff00) >> 8);
+        }
+        /* row address */
+        if (row >= 0) {
+            nand_setio(s->flash, (row & 0x0000ff) >> 0);
+            if (s->alen >= 4)
+                nand_setio(s->flash, (row & 0x00ff00) >> 8);
+            if (s->alen >= 5)
+                nand_setio(s->flash, (row & 0xff0000) >> 16);
+        }
+    }
+}
+
+static void 
+ftnandc021_handle_ack(void *opaque, int line, int level)
+{
+    ftnandc021_state *s = (ftnandc021_state *)opaque;
+
+    if (!s->bcr)
+        return;
+
+    if (level) {
+        qemu_set_irq(s->req, 0);
+    } else if (s->len > 0) {
+        qemu_set_irq(s->req, 1);
+    }
+}
+
+static void
+ftnandc021_command(void *opaque, uint32_t cmd)
+{
+    int i;
+    ftnandc021_state *s = opaque;
+
+    s->sr &= ~(1 << 2);
+    s->cmd = cmd;
+
+    switch(cmd) {
+    case 0x01:    /* read id */
+        ftnandc021_set_cmd(s, 0x90);
+        ftnandc021_set_addr(s, -1, -1);
+        nand_setpins(s->flash, 0, 0, 0, 1, 0);
+        if (s->bw == 8) {
+            s->id[0] = (nand_getio(s->flash) << 0)
+                     | (nand_getio(s->flash) << 8)
+                     | (nand_getio(s->flash) << 16)
+                     | (nand_getio(s->flash) << 24);
+            s->id[1] = (nand_getio(s->flash) << 0);
+        } else {
+            s->id[0] = (nand_getio(s->flash) << 0)
+                     | (nand_getio(s->flash) << 16);
+            s->id[1] = (nand_getio(s->flash) << 0);
+        }
+        break;
+    case 0x02:    /* reset */
+        ftnandc021_set_cmd(s, 0xff);
+        break;
+    case 0x04:    /* read status */
+        ftnandc021_set_cmd(s, 0x70);
+        nand_setpins(s->flash, 0, 0, 0, 1, 0);
+        s->id[1] = (nand_getio(s->flash) << 0);
+        break;
+    case 0x05:    /* read page */
+        ftnandc021_set_cmd(s, 0x00);
+        ftnandc021_set_addr(s, 0, s->pi);
+        ftnandc021_set_cmd(s, 0x30);
+        nand_setpins(s->flash, 0, 0, 0, 1, 0);
+#if 0
+        s->len = s->pgsz + 16 * (s->pgsz / 512);
+#else
+        s->len = s->pgsz;
+#endif
+        break;
+    case 0x06:    /* read oob */
+#if 0
+        ftnandc021_set_cmd(s, 0x50);
+        ftnandc021_set_addr(s, 0, s->pi);
+#else
+        ftnandc021_set_cmd(s, 0x00);
+        ftnandc021_set_addr(s, s->pgsz, s->pi);
+        ftnandc021_set_cmd(s, 0x30);
+#endif
+        nand_setpins(s->flash, 0, 0, 0, 1, 0);
+        for (i = 0; i < 16 * (s->pgsz / 512); ) {
+            if (s->bw == 8) {
+                if (i < 7)
+                    s->oob[i] = (uint8_t)nand_getio(s->flash);
+                else
+                    (void)nand_getio(s->flash);
+                i += 1;
+            } else {
+                if (i < 7)
+                    *(uint16_t *)(s->oob + i) = (uint16_t)nand_getio(s->flash);
+                else
+                    (void)nand_getio(s->flash);
+                i += 2;
+            }
+        }
+        break;
+    case 0x10:    /* write page + read status */
+        ftnandc021_set_cmd(s, 0x80);
+        ftnandc021_set_addr(s, 0, s->pi);
+        /* data phase */
+        nand_setpins(s->flash, 0, 0, 0, 1, 0);
+#if 0
+        s->len = s->pgsz + 16 * (s->pgsz / 512);
+#else
+        s->len = s->pgsz;
+#endif
+        break;
+    case 0x11:    /* erase block + read status */
+        ftnandc021_set_cmd(s, 0x60);
+        ftnandc021_set_addr(s, -1, s->pi);
+        ftnandc021_set_cmd(s, 0xd0);
+        /* read status */
+        ftnandc021_command(s, 0x04);
+        break;
+    case 0x13:    /* write oob + read status */
+        ftnandc021_set_cmd(s, 0x80);
+        ftnandc021_set_addr(s, s->pgsz, s->pi);
+        /* data phase */
+        nand_setpins(s->flash, 0, 0, 0, 1, 0);
+        for (i = 0; i < 16 * (s->pgsz / 512); ) {
+            if (s->bw == 8) {
+                if (i <= 7)
+                    nand_setio(s->flash, s->oob[i]);
+                else
+                    nand_setio(s->flash, 0xffffffff);
+                i += 1;
+            } else {
+                if (i <= 7)
+                    nand_setio(s->flash, s->oob[i] | (s->oob[i + 1] << 8));
+                else
+                    nand_setio(s->flash, 0xffffffff);
+                i += 2;
+            }
+        }
+        ftnandc021_set_cmd(s, 0x10);
+        /* read status */
+        ftnandc021_command(s, 0x04);
+        break;
+    default:
+        printf("[qemu] ftnandc021: unknow command=0x%02x\n", cmd);
+        break;
+    }
+
+    /* if cmd is not page read/write, then return to idle mode */
+    if (s->cmd != 0x05 && s->cmd != 0x10) {
+        ftnandc021_set_idle(s);
+    } else if (s->bcr && (s->len > 0)) {
+        qemu_set_irq(s->req, 1);
+    }
+}
+
+static uint64_t
+ftnandc021_mem_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    ftnandc021_state *s = opaque;
+
+    switch (addr) {
+    case REG_DR:
+        if (s->cmd == 0x05 && s->len > 0) {
+            uint32_t val = 0;
+            if (s->bw == 8) {
+                val |= (nand_getio(s->flash) & 0xff) << 0;
+                val |= (nand_getio(s->flash) & 0xff) << 8;
+                val |= (nand_getio(s->flash) & 0xff) << 16;
+                val |= (nand_getio(s->flash) & 0xff) << 24;
+            } else {
+                val |= (nand_getio(s->flash) & 0xffff) << 0;
+                val |= (nand_getio(s->flash) & 0xffff) << 16;
+            }
+            s->len -= 4;
+            if (s->len <= 0) {
+                ftnandc021_set_idle(s);
+            }
+            return val;
+        }
+        break;
+    case REG_SR:
+        return s->sr;
+    case REG_ACR:
+        return s->cmd << 8;
+    case REG_RDBR:
+        return s->oob[0];
+    case REG_RDLSN:
+        return s->oob[1] | (s->oob[2] << 8);
+    case REG_RDCRC:
+        if (s->pgsz > 2048)
+            return s->oob[3] | (s->oob[4] << 8) | (s->oob[5] << 16) | 
(s->oob[6] << 24);
+        else
+            return s->oob[3] | (s->oob[4] << 8);
+    case REG_FCR:
+        return s->fcr;
+    case REG_PIR:
+        return s->pi;
+    case REG_PCR:
+        return 1;
+    case REG_MCR:
+        return s->mcr;
+    case REG_IDRL:
+        return s->id[0];
+    case REG_IDRH:
+        return s->id[1];
+    case REG_IER:
+        return s->ier;
+    case REG_BCR:
+        return s->bcr;
+    case REG_ATR1:
+        return 0x02240264;
+    case REG_ATR2:
+        return 0x42054209;
+    case REG_PRR:
+        return 0x00000001;
+    case REG_REVR:
+        return 0x00010100;
+    case REG_CFGR:
+        return 0x00081602;
+    default:
+        break;
+    }
+
+    return 0;
+}
+
+static void
+ftnandc021_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned int 
size)
+{
+    ftnandc021_state *s = opaque;
+    
+    switch (addr) {
+    case REG_DR:
+        if (s->cmd == 0x10 && s->len > 0) {
+            if (s->bw == 8) {
+                nand_setio(s->flash, ((uint32_t)val >> 0) & 0xff);
+                nand_setio(s->flash, ((uint32_t)val >> 8) & 0xff);
+                nand_setio(s->flash, ((uint32_t)val >> 16) & 0xff);
+                nand_setio(s->flash, ((uint32_t)val >> 24) & 0xff);
+            } else {
+                nand_setio(s->flash, ((uint32_t)val >> 0)  & 0xffff);
+                nand_setio(s->flash, ((uint32_t)val >> 16) & 0xffff);
+            }
+            s->len -= 4;
+            if (s->len <= 0) {
+                ftnandc021_set_cmd(s, 0x10);
+                /* read status */
+                ftnandc021_command(s, 0x04);
+            }
+        }
+        break;
+    case REG_ACR:
+        if (!((uint32_t)val & (1 << 7)))
+            break;
+        ftnandc021_command(s, ((uint32_t)val >> 8) & 0x1f);
+        break;
+    case REG_WRBR:
+        s->oob[0] = (uint32_t)val & 0xff;
+        break;
+    case REG_WRLSN:
+        s->oob[1] = ((uint32_t)val >> 0) & 0xff;
+        s->oob[2] = ((uint32_t)val >> 8) & 0xff;
+        break;
+    case REG_WRCRC:
+        s->oob[3] = ((uint32_t)val >> 0) & 0xff;
+        s->oob[4] = ((uint32_t)val >> 8) & 0xff;
+        if (s->pgsz > 2048) {
+            s->oob[5] = ((uint32_t)val >> 16) & 0xff;
+            s->oob[6] = ((uint32_t)val >> 24) & 0xff;
+        }
+        break;
+    case REG_FCR:
+        s->fcr = (uint32_t)val;
+        if (s->fcr & (1 << 4))
+            s->bw = 16;
+        else
+            s->bw = 8;
+        break;
+    case REG_PIR:
+        s->pi = (uint32_t)val & 0x03ffffff;
+        break;
+    case REG_MCR:
+        s->mcr = (uint32_t)val;
+        /* page size */
+        switch((s->mcr >> 8) & 0x03) {
+        case 0:
+            s->pgsz = 512;
+            break;
+        case 2:
+            s->pgsz = 4096;
+            break;
+        default:
+            s->pgsz = 2048;
+            break;
+        }
+        /* block size */
+        s->bksz = s->pgsz * (1 << (4 + ((s->mcr >> 16) & 0x03)));
+        /* address length (cycle) */
+        s->alen = 3 + ((s->mcr >> 10) & 0x03);
+        /* flash size */
+        s->size = 1ULL << (24 + ((s->mcr >> 4) & 0x0f));
+        break;
+    case REG_IER:
+        s->ier = (uint32_t)val & 0x8f;
+        ftnandc021_irq_update(s);
+        break;
+    case REG_ISCR:
+        s->sr &= ~(((uint32_t)val & 0x0f) << 2);
+        ftnandc021_irq_update(s);
+        break;
+    case REG_BCR:
+        s->bcr = (uint32_t)val;
+        break;
+    default:
+        break;
+    }
+}
+
+static const MemoryRegionOps bus_ops = {
+    .read  = ftnandc021_mem_read,
+    .write = ftnandc021_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void ftnandc021_reset(DeviceState *d)
+{
+    ftnandc021_state *s = DO_UPCAST(ftnandc021_state, busdev.qdev, d);
+    
+    s->sr   = 0;
+    s->fcr  = 0;
+    s->mcr  = 0;
+    s->ier  = 0;
+    s->bcr  = 0;
+    s->id[0]= 0;
+    s->id[1]= 0;
+    
+    /* We can assume our GPIO outputs have been wired up now */
+    qemu_set_irq(s->req, 0);
+}
+
+static int ftnandc021_init(SysBusDevice *dev)
+{
+    ftnandc021_state *s = FROM_SYSBUS(typeof(*s), dev);
+    DriveInfo *nand;
+
+    memory_region_init_io(&s->mmio, &bus_ops, s, "ftnandc021", 0x1000);
+    sysbus_init_mmio(dev, &s->mmio);
+    sysbus_init_irq(dev, &s->irq);
+    
+    qdev_init_gpio_in (&s->busdev.qdev, ftnandc021_handle_ack, 1);
+    qdev_init_gpio_out(&s->busdev.qdev, &s->req, 1);
+
+    nand = drive_get_next(IF_MTD);
+    s->flash = nand_init(nand ? nand->bdrv : NULL, s->manf_id, s->chip_id);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_ftnandc021 = {
+    .name = "ftnandc021",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(sr, ftnandc021_state),
+        VMSTATE_UINT32(fcr, ftnandc021_state),
+        VMSTATE_UINT32(mcr, ftnandc021_state),
+        VMSTATE_UINT32(ier, ftnandc021_state),
+        VMSTATE_UINT32(bcr, ftnandc021_state),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property ftnandc021_properties[] = {
+    /* Samsung 1 Gigabit (256MB) */
+    DEFINE_PROP_UINT8("manufacturer_id", ftnandc021_state, manf_id, 
NAND_MFR_SAMSUNG),
+    DEFINE_PROP_UINT8("chip_id", ftnandc021_state, chip_id, 0xda),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void ftnandc021_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *k = DEVICE_CLASS(klass);
+
+    sdc->init = ftnandc021_init;
+    k->vmsd = &vmstate_ftnandc021;
+    k->props = ftnandc021_properties;
+    k->reset = ftnandc021_reset;
+    k->no_user = 1;
+}
+
+static TypeInfo ftnandc021_info = {
+    .name           = "ftnandc021",
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(ftnandc021_state),
+    .class_init     = ftnandc021_class_init,
+};
+
+static void ftnandc021_register_types(void)
+{
+    type_register_static(&ftnandc021_info);
+}
+
+type_init(ftnandc021_register_types)
diff --git a/hw/ftnandc021.h b/hw/ftnandc021.h
new file mode 100644
index 0000000..bfe218a
--- /dev/null
+++ b/hw/ftnandc021.h
@@ -0,0 +1,58 @@
+/*
+ * (C) Copyright 2010
+ * Faraday Technology Inc. <www.faraday-tech.com>
+ * Dante Su <address@hidden>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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 of
+ * the License, or (at your option) any later version.
+ *
+ * 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
+ */
+
+#ifndef FTNANDC021_H
+#define FTNANDC021_H
+
+/* NANDC control registers */
+#define REG_SR                    0x100    /* Status Register */
+#define REG_ACR                    0x104    /* Access Control Register */
+#define REG_FCR                    0x108    /* Flow Control Register */
+#define REG_PIR                    0x10C    /* Page Index Register */
+#define REG_MCR                    0x110    /* Memory Configuration Register */
+#define REG_ATR1                0x114    /* AC Timing Register 1 */
+#define REG_ATR2                0x118    /* AC Timing Register 2 */
+#define REG_IDRL                0x120    /* ID Register LSB */
+#define REG_IDRH                0x124    /* ID Register MSB */
+#define REG_IER                    0x128    /* Interrupt Enable Register */
+#define REG_ISCR                0x12C    /* Interrupt Status Clear Register */
+#define REG_WRBR                0x140    /* Write Bad Block Register */
+#define REG_WRLSN                0x144    /* Write LSN Register */
+#define REG_WRCRC                0x148    /* Write LSN CRC Register */
+#define REG_RDBR                0x150    /* Read Bad Block Register */
+#define REG_RDLSN                0x154    /* Read LSN Register */
+#define REG_RDCRC                0x158    /* Read LSN CRC Register */
+
+/* BMC control registers */
+#define REG_PRR                    0x208    /* BMC PIO Ready Register */
+#define REG_BCR                    0x20C    /* BMC Burst Control Register */
+
+/** MISC register **/
+#define REG_DR                    0x300    /* Data Register */
+#define REG_PCR                    0x308    /* Page Count Register */
+#define REG_RSTR                0x30C    /* MLC Reset Register */
+#define REG_REVR                0x3F8    /* Revision Register */
+#define REG_CFGR                0x3FC    /* Configuration Register */
+
+#endif    /* EOF */
-- 
1.7.9.5


********************* Confidentiality Notice ************************
This electronic message and any attachments may contain
confidential and legally privileged information or
information which is otherwise protected from disclosure.
If you are not the intended recipient,please do not disclose
the contents, either in whole or in part, to anyone,and
immediately delete the message and any attachments from
your computer system and destroy all hard copies.
Thank you for your cooperation.
***********************************************************************




reply via email to

[Prev in Thread] Current Thread [Next in Thread]