qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] [PATCH 12/18] hw: add QEMU model for Faraday high-speed SPI


From: Dante
Subject: [Qemu-devel] [PATCH 12/18] hw: add QEMU model for Faraday high-speed SPI-NOR flash controller
Date: Fri, 18 Jan 2013 14:31:50 +0800

Signed-off-by: Kuo-Jung Su <address@hidden>
---
 hw/ftspi020.c |  338 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/ftspi020.h |   71 ++++++++++++
 2 files changed, 409 insertions(+)
 create mode 100644 hw/ftspi020.c
 create mode 100644 hw/ftspi020.h

diff --git a/hw/ftspi020.c b/hw/ftspi020.c
new file mode 100644
index 0000000..eb68bc6
--- /dev/null
+++ b/hw/ftspi020.c
@@ -0,0 +1,338 @@
+/*
+ * Faraday FTSPI020 Flash Controller
+ *
+ * Copyright (c) 2012 Faraday Technology
+ *
+ * Written by Dante Su <address@hidden>
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "hw.h"
+#include "sysemu/sysemu.h"
+#include "sysbus.h"
+#include "ssi.h"
+
+#include "ftspi020.h"
+
+#ifndef min
+#define min(a, b)                ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef max
+#define max(a, b)                ((a) > (b) ? (a) : (b))
+#endif
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    qemu_irq irq;
+    
+    /* DMA hardware handshake */
+    qemu_irq req;
+    
+    SSIBus *spi;
+    uint8_t num_cs;
+    qemu_irq *cs_lines;
+
+    int wip;    /* SPI Flash Status: Write In Progress BIT shift */
+    uint32_t datacnt;
+    
+    /* HW register caches */
+    uint32_t cmd[4];
+    uint32_t ctrl;
+    uint32_t timing;
+    uint32_t icr;
+    uint32_t isr;
+    uint32_t rdsr;
+} ftspi020_state;
+
+static inline void ftspi020_update_irq(ftspi020_state *s)
+{
+    if (s->isr)
+        qemu_set_irq(s->irq, 1);
+    else
+        qemu_set_irq(s->irq, 0);
+}
+
+static void ftspi020_handle_ack(void *opaque, int line, int level)
+{
+    ftspi020_state *s = (ftspi020_state *)opaque;
+
+    if (!(s->icr & 0x01))
+        return;
+
+    if (level) {
+        qemu_set_irq(s->req, 0);
+    } else if (s->datacnt > 0) {
+        qemu_set_irq(s->req, 1);
+    }
+}
+
+static int
+ftspi020_do_command(ftspi020_state *s)
+{
+    int cs   = (s->cmd[3] >> 8)  & 0x03;
+    int cmd  = (s->cmd[3] >> 24) & 0xff;
+    int ilen = (s->cmd[1] >> 24) & 0x03;
+    int alen = (s->cmd[1] >> 0)  & 0x07;
+    int dcyc = (s->cmd[1] >> 16) & 0xff;
+    
+    /* make sure the spi flash is de-activated */
+    qemu_set_irq(s->cs_lines[cs], 1);
+    
+    /* activate the spi flash */
+    qemu_set_irq(s->cs_lines[cs], 0);
+
+    /* if it's a SPI flash READ_STATUS command */
+    if ((s->cmd[3] & 0x06) == 0x04) {
+        do {
+            ssi_transfer(s->spi, cmd);
+            s->rdsr = ssi_transfer(s->spi, 0x00);
+            if (s->cmd[3] & 0x08)
+                break;
+        } while(s->rdsr & (1 << s->wip));
+    } else {
+    /* otherwise */
+        int i;
+        
+        ilen = min(ilen, 2);
+        alen = min(alen, 4);
+        
+        /* command cycles */
+        for (i = 0; i < ilen; ++i)
+            ssi_transfer(s->spi, cmd);
+        /* address cycles */
+        for (i = alen - 1; i >= 0; --i)
+            ssi_transfer(s->spi, (s->cmd[0] >> (8 * i)) & 0xff);
+        /* dummy cycles */
+        for (i = 0; i < (dcyc >> 3); ++i)
+            ssi_transfer(s->spi, 0x00);
+    }
+
+    if (s->datacnt <= 0)
+        qemu_set_irq(s->cs_lines[cs], 1);
+    else if (s->icr & 0x01)
+        qemu_set_irq(s->req, 1);
+
+    if (s->cmd[3] & 0x01)
+        s->isr |= 0x01;
+    ftspi020_update_irq(s);
+
+    return 0;
+}
+
+static void 
+ftspi020_chip_reset(ftspi020_state *s)
+{
+    int i;
+    
+    s->datacnt = 0;
+    for (i = 0; i < 4; ++i)
+        s->cmd[i] = 0;    
+    s->wip = 0;
+    s->ctrl = 0;
+    s->timing = 0;
+    s->icr = 0;
+    s->isr = 0;
+    s->rdsr = 0;
+
+    qemu_set_irq(s->irq, 0);
+}
+
+static uint64_t 
+ftspi020_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    ftspi020_state *s = (ftspi020_state *) opaque;
+    uint64_t ret = 0;
+
+    switch (addr) {
+    case REG_DATA:
+        if (!(s->cmd[3] & 0x02)) {
+            if (s->datacnt > 0) {
+                ret |= (uint32_t)(ssi_transfer(s->spi, 0x00) & 0xff) << 0;
+                ret |= (uint32_t)(ssi_transfer(s->spi, 0x00) & 0xff) << 8;
+                ret |= (uint32_t)(ssi_transfer(s->spi, 0x00) & 0xff) << 16;
+                ret |= (uint32_t)(ssi_transfer(s->spi, 0x00) & 0xff) << 24;
+                if (s->datacnt < 4)
+                    s->datacnt = 0;
+                else
+                    s->datacnt -= 4;
+            }
+            if (s->datacnt == 0) {
+                uint8_t cs = (s->cmd[3] >> 8)  & 0x03;
+                qemu_set_irq(s->cs_lines[cs], 1);
+                if (s->cmd[3] & 0x01)
+                    s->isr |= 0x01;
+                ftspi020_update_irq(s);
+            }
+        }    
+        break;
+    case REG_RDST:
+        return s->rdsr;
+    case REG_CMD0:
+        return s->cmd[0];
+    case REG_CMD1:
+        return s->cmd[1];
+    case REG_CMD2:
+        return s->cmd[2];
+    case REG_CMD3:
+        return s->cmd[3];
+    case REG_STR:
+        /* In QEMU, the data fifo is always ready for read/write */
+        return 0x00000003;
+    case REG_ISR:
+        return s->isr;
+    case REG_ICR:
+        return s->icr;
+    case REG_CTRL:
+        return s->ctrl;
+    case REG_ACT:
+        return s->timing;
+    case REG_REV:
+        return 0x00010001;
+    case REG_FEA:
+        return 0x02022020;
+    default:
+        break;
+    }
+
+    return ret;
+}
+
+static void 
+ftspi020_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    ftspi020_state *s = (ftspi020_state *) opaque;
+
+    switch (addr) {
+    case REG_DATA:
+        if (s->cmd[3] & 0x02) {
+            if (s->datacnt > 0) {
+                ssi_transfer(s->spi, (uint8_t)((val >> 0) & 0xff));
+                ssi_transfer(s->spi, (uint8_t)((val >> 8) & 0xff));
+                ssi_transfer(s->spi, (uint8_t)((val >> 16) & 0xff));
+                ssi_transfer(s->spi, (uint8_t)((val >> 24) & 0xff));
+                if (s->datacnt < 4)
+                    s->datacnt = 0;
+                else
+                    s->datacnt -= 4;
+            }
+            if (s->datacnt == 0) {
+                uint8_t cs = (s->cmd[3] >> 8)  & 0x03;
+                qemu_set_irq(s->cs_lines[cs], 1);
+                if (s->cmd[3] & 0x01)
+                    s->isr |= 0x01;
+                ftspi020_update_irq(s);
+            }
+        }
+        break;
+    case REG_CMD0:
+        s->cmd[0] = (uint32_t)val;
+        break;
+    case REG_CMD1:
+        s->cmd[1] = (uint32_t)val;
+        break;
+    case REG_CMD2:
+        s->datacnt = s->cmd[2] = (uint32_t)val;
+        break;
+    case REG_CMD3:
+        s->cmd[3] = (uint32_t)val;
+        ftspi020_do_command(s);
+        break;
+    case REG_ISR:
+        s->isr &= ~((uint32_t)val);
+        ftspi020_update_irq(s);
+        break;
+    case REG_ICR:
+        s->icr = (uint32_t)val;
+        break;
+    case REG_CTRL:
+        if (val & 0x100)
+            ftspi020_chip_reset(s);
+        s->ctrl = (uint32_t)val & 0x70013;
+        s->wip = ((uint32_t)val >> 16) & 0x07;
+        break;
+    case REG_ACT:
+        s->timing = (uint32_t)val;
+        break;
+    default:
+        break;
+    }
+}
+
+static const MemoryRegionOps ftspi020_mem_ops = {
+    .read  = ftspi020_mem_read,
+    .write = ftspi020_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void ftspi020_reset(DeviceState *d)
+{
+    ftspi020_state *s = DO_UPCAST(ftspi020_state, busdev.qdev, d);
+    ftspi020_chip_reset(s);
+}
+
+static int ftspi020_init(SysBusDevice *dev)
+{
+    ftspi020_state *s = FROM_SYSBUS(ftspi020_state, dev);
+    int i;
+
+    memory_region_init_io(&s->iomem, &ftspi020_mem_ops, s, "ftspi020", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    sysbus_init_irq(dev, &s->irq);
+    
+    s->spi = ssi_create_bus(&dev->qdev, "spi");
+    s->num_cs = 4;
+    s->cs_lines = g_new(qemu_irq, s->num_cs);
+    ssi_auto_connect_slaves(DEVICE(s), s->cs_lines, s->spi);
+    for (i = 0; i < s->num_cs; ++i) {
+        sysbus_init_irq(dev, &s->cs_lines[i]);
+    }
+    
+    qdev_init_gpio_in (&s->busdev.qdev, ftspi020_handle_ack, 1);
+    qdev_init_gpio_out(&s->busdev.qdev, &s->req, 1);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_ftspi020_regs = {
+    .name = "ftspi020",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(cmd, ftspi020_state, 4),
+        VMSTATE_UINT32(ctrl, ftspi020_state),
+        VMSTATE_UINT32(timing, ftspi020_state),
+        VMSTATE_UINT32(icr, ftspi020_state),
+        VMSTATE_UINT32(isr, ftspi020_state),
+        VMSTATE_UINT32(rdsr, ftspi020_state),
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+static void ftspi020_dev_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *k = DEVICE_CLASS(klass);
+
+    sdc->init  = ftspi020_init;
+    k->vmsd    = &vmstate_ftspi020_regs;
+    k->reset   = ftspi020_reset;
+    k->no_user = 1;
+}
+
+static TypeInfo ftspi020_dev_info = {
+    .name          = "ftspi020",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(ftspi020_state),
+    .class_init    = ftspi020_dev_class_init,
+};
+
+static void ftspi020_register_types(void)
+{
+    type_register_static(&ftspi020_dev_info);
+}
+
+type_init(ftspi020_register_types)
diff --git a/hw/ftspi020.h b/hw/ftspi020.h
new file mode 100644
index 0000000..2c737cc
--- /dev/null
+++ b/hw/ftspi020.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2011, Dante Su (address@hidden)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    This product includes software developed by the <organization>.
+ * 4. Neither the name of the <organization> nor the
+ *    names of its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 
THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _FTSPI020_H
+#define _FTSPI020_H
+
+/******************************************************************************
+ * FTSPI020 registers
+ *****************************************************************************/
+#define    REG_CMD0            0x00    /* Flash address */
+#define    REG_CMD1            0x04
+#define    REG_CMD2            0x08
+#define    REG_CMD3            0x0c
+#define    REG_CTRL            0x10    /* Control Register */
+#define    REG_ACT                0x14    /* AC Timing Register */
+#define    REG_STR                0x18    /* Status Register */
+#define    REG_ICR                0x20    /* Interrupt Control Register */
+#define    REG_ISR                0x24    /* Interrupt Status Register */
+#define    REG_RDST            0x28    /* Read Status Register */
+#define REG_REV                0x50    /* Revision Register */
+#define REG_FEA                0x54    /* Feature Register */
+#define REG_DATA            0x100    /* Data Register */
+
+/******************************************************************************
+ * Common SPI flash opcodes (Not FTSPI020 specific)
+ *****************************************************************************/
+#define    OPCODE_WREN            0x06    /* Write enable */
+#define    OPCODE_WRSR            0x01    /* Write status register 1 byte */
+#define    OPCODE_RDSR            0x05    /* Read status register */
+#define    OPCODE_NORM_READ    0x03    /* Read data bytes (low frequency) */
+#define    OPCODE_NORM_READ4    0x13    /* Read data bytes (low frequency, 4 
bytes address) */
+#define    OPCODE_FAST_READ    0x0b    /* Read data bytes (high frequency) */
+#define    OPCODE_FAST_READ4    0x0c    /* Read data bytes (high frequency, 4 
bytes address) */
+#define    OPCODE_PP            0x02    /* Page program (up to 256 bytes) */
+#define    OPCODE_PP4            0x12    /* Page program (up to 256 bytes, 4 
bytes address) */
+#define    OPCODE_SE            0xd8    /* Sector erase (usually 64KiB) */
+#define    OPCODE_SE4            0xdc    /* Sector erase (usually 64KiB, 4 
bytes address) */
+#define    OPCODE_RDID            0x9f    /* Read JEDEC ID */
+
+/* Status Register bits. */
+#define    SR_WIP           1        /* Write in progress */
+#define    SR_WEL           2        /* Write enable latch */
+
+#endif
-- 
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]