Patchwork [v2,13/20] arm: add Faraday FTSPI020 spi flash controller support

login
register
mail settings
Submitter Kuo-Jung Su
Date Jan. 25, 2013, 8:19 a.m.
Message ID <1359101996-11667-14-git-send-email-dantesu@gmail.com>
Download mbox | patch
Permalink /patch/215602/
State New
Headers show

Comments

Kuo-Jung Su - Jan. 25, 2013, 8:19 a.m.
From: Kuo-Jung Su <dantesu@faraday-tech.com>

The FTSPI020 is primariy designed for high-speed spi flash support.
It supports double data rate and fast-read dual/quad for spi flash.
It use one AHB slave port and one SPI interface controller to execute
the SPI Flash command. Moreover, it also provides the PIO mode or DMA
mode to access the data from the AHB data port.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/ftspi020.c |  345 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/ftspi020.h |   50 +++++++++
 2 files changed, 395 insertions(+)
 create mode 100644 hw/ftspi020.c
 create mode 100644 hw/ftspi020.h

Patch

diff --git a/hw/ftspi020.c b/hw/ftspi020.c
new file mode 100644
index 0000000..f7633e0
--- /dev/null
+++ b/hw/ftspi020.c
@@ -0,0 +1,345 @@ 
+/*
+ * Faraday FTSPI020 Flash Controller
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under GNU GPL v2.
+ */
+
+#include "hw.h"
+#include "sysemu/sysemu.h"
+#include "sysbus.h"
+#include "ssi.h"
+
+#include "ftspi020.h"
+
+#define TYPE_FTSPI020   "ftspi020"
+
+typedef struct Ftspi020State {
+    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;
+
+#define FTSPI020(obj) \
+    OBJECT_CHECK(ftspi020_state, obj, TYPE_FTSPI020)
+
+static 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(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(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;
+                s->datacnt = (s->datacnt < 4) ? 0 : (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(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));
+                s->datacnt = (s->datacnt < 4) ? 0 : (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_ops = {
+    .read  = ftspi020_mem_read,
+    .write = ftspi020_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void ftspi020_reset(DeviceState *ds)
+{
+    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
+    ftspi020_state *s = FTSPI020(FROM_SYSBUS(ftspi020_state, busdev));
+
+    ftspi020_chip_reset(s);
+}
+
+static int ftspi020_init(SysBusDevice *dev)
+{
+    ftspi020_state *s = FTSPI020(FROM_SYSBUS(ftspi020_state, dev));
+    int i;
+
+    memory_region_init_io(&s->iomem,
+                          &ftspi020_ops,
+                          s,
+                          TYPE_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 = {
+    .name = TYPE_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_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    k->init     = ftspi020_init;
+    dc->vmsd    = &vmstate_ftspi020;
+    dc->reset   = ftspi020_reset;
+    dc->no_user = 1;
+}
+
+static const TypeInfo ftspi020_info = {
+    .name          = TYPE_FTSPI020,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(ftspi020_state),
+    .class_init    = ftspi020_class_init,
+};
+
+static void ftspi020_register_types(void)
+{
+    type_register_static(&ftspi020_info);
+}
+
+type_init(ftspi020_register_types)
diff --git a/hw/ftspi020.h b/hw/ftspi020.h
new file mode 100644
index 0000000..2f320c6
--- /dev/null
+++ b/hw/ftspi020.h
@@ -0,0 +1,50 @@ 
+/*
+ * Faraday FTSPI020 Flash Controller
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under the GNU GPL.
+ */
+
+#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 (4 bytes address) */
+#define OPCODE_FAST_READ    0x0b    /* Read data bytes (high frequency) */
+#define OPCODE_FAST_READ4   0x0c    /* Read data bytes (4 bytes address) */
+#define OPCODE_PP           0x02    /* Page program (up to 256 bytes) */
+#define OPCODE_PP4          0x12    /* Page program (4 bytes address) */
+#define OPCODE_SE           0xd8    /* Sector erase (usually 64KiB) */
+#define OPCODE_SE4          0xdc    /* Sector erase (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