new file mode 100644
@@ -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)
new file mode 100644
@@ -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