[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v4 21/23] hw/arm: add Faraday FTSPI020 SPI flash con
From: |
Kuo-Jung Su |
Subject: |
[Qemu-devel] [PATCH v4 21/23] hw/arm: add Faraday FTSPI020 SPI flash controller support |
Date: |
Tue, 26 Feb 2013 17:14:12 +0800 |
From: Kuo-Jung Su <address@hidden>
The FTSPI020 is an integrated SPI Flash controller
which supports upto 4 flash chips.
Signed-off-by: Kuo-Jung Su <address@hidden>
---
hw/arm/Makefile.objs | 1 +
hw/arm/faraday_a369.c | 13 ++
hw/arm/faraday_a369_soc.c | 4 +
hw/arm/ftspi020.c | 333 +++++++++++++++++++++++++++++++++++++++++++++
hw/arm/ftspi020.h | 81 +++++++++++
5 files changed, 432 insertions(+)
create mode 100644 hw/arm/ftspi020.c
create mode 100644 hw/arm/ftspi020.h
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index c25eba2..2ed1c7c 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -52,3 +52,4 @@ obj-y += ftgmac100.o
obj-y += ftlcdc200.o
obj-y += fttsc010.o
obj-y += ftsdc010.o
+obj-y += ftspi020.o
diff --git a/hw/arm/faraday_a369.c b/hw/arm/faraday_a369.c
index 46fc570..ace0784 100644
--- a/hw/arm/faraday_a369.c
+++ b/hw/arm/faraday_a369.c
@@ -67,6 +67,19 @@ a369_board_init(QEMUMachineInitArgs *args)
wm8731_data_req_set(s->codec, ftssp010_i2s_data_req, s->i2s[0]);
}
+ /* Attach the spi flash to ftspi020.0 */
+ nr_flash = 1;
+ for (i = 0; i < nr_flash; i++) {
+ SSIBus *ssi = (SSIBus *)qdev_get_child_bus(s->spi[1], "spi");
+ DeviceState *fl = ssi_create_slave_no_init(ssi, "m25p80");
+ qemu_irq cs_line;
+
+ qdev_prop_set_string(fl, "partname", "w25q64");
+ qdev_init_nofail(fl);
+ cs_line = qdev_get_gpio_in(fl, 0);
+ sysbus_connect_irq(SYS_BUS_DEVICE(s->spi[1]), i + 1, cs_line);
+ }
+
if (args->kernel_filename) {
s->bi = g_new0(struct arm_boot_info, 1);
diff --git a/hw/arm/faraday_a369_soc.c b/hw/arm/faraday_a369_soc.c
index eb9dd2f..8b07689 100644
--- a/hw/arm/faraday_a369_soc.c
+++ b/hw/arm/faraday_a369_soc.c
@@ -272,6 +272,10 @@ a369soc_device_init(FaradaySoCState *s)
req = qdev_get_gpio_in(s->hdma[0], 13);
qdev_connect_gpio_out(s->hdma[0], 13, ack);
qdev_connect_gpio_out(ds, 0, req);
+
+ /* ftspi020: as an external AHB device */
+ ds = sysbus_create_simple("ftspi020", 0xC0000000, pic[4]);
+ s->spi[1] = ds;
}
static int a369soc_init(SysBusDevice *busdev)
diff --git a/hw/arm/ftspi020.c b/hw/arm/ftspi020.c
new file mode 100644
index 0000000..fb8a510
--- /dev/null
+++ b/hw/arm/ftspi020.c
@@ -0,0 +1,333 @@
+/*
+ * Faraday FTSPI020 Flash Controller
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <address@hidden>
+ *
+ * This code is licensed under GNU GPL v2+.
+ */
+
+#include "hw/hw.h"
+#include "sysemu/sysemu.h"
+#include "hw/sysbus.h"
+#include "hw/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;
+ qemu_irq *cs_lines;
+
+ int wip; /* SPI Flash Status: Write In Progress BIT shift */
+
+ /* HW register caches */
+ uint32_t cmd[4];
+ uint32_t ctrl;
+ uint32_t timing;
+ uint32_t icr;
+ uint32_t isr;
+ uint32_t rdsr;
+} Ftspi020State;
+
+#define FTSPI020(obj) \
+ OBJECT_CHECK(Ftspi020State, obj, TYPE_FTSPI020)
+
+static void ftspi020_update_irq(Ftspi020State *s)
+{
+ qemu_set_irq(s->irq, s->isr ? 1 : 0);
+}
+
+static void ftspi020_handle_ack(void *opaque, int line, int level)
+{
+ Ftspi020State *s = FTSPI020(opaque);
+
+ if (!(s->icr & ICR_DMA)) {
+ return;
+ }
+
+ if (level) {
+ qemu_set_irq(s->req, 0);
+ } else if (s->cmd[2]) {
+ qemu_set_irq(s->req, 1);
+ }
+}
+
+static int ftspi020_do_command(Ftspi020State *s)
+{
+ uint32_t cs = extract32(s->cmd[3], 8, 2);
+ uint32_t cmd = extract32(s->cmd[3], 24, 8);
+ uint32_t ilen = extract32(s->cmd[1], 24, 2);
+ uint32_t alen = extract32(s->cmd[1], 0, 3);
+ uint32_t dcyc = extract32(s->cmd[1], 16, 8);
+
+ if (dcyc % 8) {
+ hw_error("ftspi020: bad dummy clock (%u) to QEMU\n", dcyc);
+ exit(1);
+ }
+
+ /* 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] & (CMD3_RDSR | CMD3_WRITE)) == CMD3_RDSR) {
+ uint32_t rdsr;
+
+ ssi_transfer(s->spi, cmd);
+ do {
+ rdsr = ssi_transfer(s->spi, 0x00);
+ if (s->cmd[3] & CMD3_RDSR_SW) {
+ break;
+ }
+ } while (rdsr & (1 << s->wip));
+ s->rdsr = rdsr;
+ } 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, extract32(s->cmd[0], i * 8, 8));
+ }
+ /* dummy cycles */
+ for (i = 0; i < (dcyc >> 3); ++i) {
+ ssi_transfer(s->spi, 0x00);
+ }
+ }
+
+ if (!s->cmd[2]) {
+ qemu_set_irq(s->cs_lines[cs], 1);
+ } else if (s->icr & ICR_DMA) {
+ qemu_set_irq(s->req, 1);
+ }
+
+ if (s->cmd[3] & CMD3_INTR) {
+ s->isr |= ISR_CMDFIN;
+ }
+ ftspi020_update_irq(s);
+
+ return 0;
+}
+
+static void ftspi020_chip_reset(Ftspi020State *s)
+{
+ int i;
+
+ 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);
+
+ /* DO NOT reset cs lines here, or the QEMU would crash */
+}
+
+static uint64_t
+ftspi020_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+ Ftspi020State *s = FTSPI020(opaque);
+ uint64_t ret = 0;
+
+ switch (addr) {
+ case REG_CMD0 ... REG_CMD3:
+ return s->cmd[(addr - REG_CMD0) / 4];
+ case REG_CR:
+ return s->ctrl;
+ case REG_TR:
+ return s->timing;
+ case REG_SR:
+ /* In QEMU, the data fifo is always ready for read/write */
+ return SR_RX_READY | SR_TX_READY;
+ case REG_ISR:
+ return s->isr;
+ case REG_ICR:
+ return s->icr;
+ case REG_RDSR:
+ return s->rdsr;
+ case REG_REVR:
+ return 0x00010001; /* rev. 1.0.1 */
+ case REG_FEAR:
+ return FEAR_CLKM_SYNC
+ | FEAR_CMDQ(2) | FEAR_RXFIFO(32) | FEAR_TXFIFO(32);
+ case REG_DR:
+ if (!(s->cmd[3] & CMD3_WRITE)) {
+ int i;
+ uint32_t cs = extract32(s->cmd[3], 8, 2);
+ for (i = 0; i < 4 && s->cmd[2]; i++, s->cmd[2]--) {
+ ret = deposit32(ret, i * 8, 8,
+ ssi_transfer(s->spi, 0x00) & 0xff);
+ }
+ if (!s->cmd[2]) {
+ qemu_set_irq(s->cs_lines[cs], 1);
+ if (s->cmd[3] & CMD3_INTR) {
+ s->isr |= ISR_CMDFIN;
+ }
+ ftspi020_update_irq(s);
+ }
+ }
+ break;
+ /* we don't care */
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static void
+ftspi020_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+ Ftspi020State *s = FTSPI020(opaque);
+
+ switch (addr) {
+ case REG_CMD0 ... REG_CMD2:
+ s->cmd[(addr - REG_CMD0) / 4] = (uint32_t)val;
+ break;
+ case REG_CMD3:
+ s->cmd[3] = (uint32_t)val;
+ ftspi020_do_command(s);
+ break;
+ case REG_CR:
+ if (val & CR_ABORT) {
+ ftspi020_chip_reset(s);
+ val &= ~CR_ABORT;
+ }
+ s->ctrl = (uint32_t)val;
+ s->wip = extract32(val, 16, 3);
+ break;
+ case REG_TR:
+ s->timing = (uint32_t)val;
+ break;
+ case REG_DR:
+ if (s->cmd[3] & CMD3_WRITE) {
+ int i;
+ uint32_t cs = extract32(s->cmd[3], 8, 2);
+ for (i = 0; i < 4 && s->cmd[2]; i++, s->cmd[2]--) {
+ ssi_transfer(s->spi, extract32((uint32_t)val, i * 8, 8));
+ }
+ if (!s->cmd[2]) {
+ qemu_set_irq(s->cs_lines[cs], 1);
+ if (s->cmd[3] & CMD3_INTR) {
+ s->isr |= ISR_CMDFIN;
+ }
+ ftspi020_update_irq(s);
+ }
+ }
+ break;
+ case REG_ISR:
+ s->isr &= ~((uint32_t)val);
+ ftspi020_update_irq(s);
+ break;
+ case REG_ICR:
+ s->icr = (uint32_t)val;
+ break;
+ /* we don't care */
+ 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);
+ Ftspi020State *s = FTSPI020(FROM_SYSBUS(Ftspi020State, busdev));
+
+ ftspi020_chip_reset(s);
+}
+
+static int ftspi020_init(SysBusDevice *dev)
+{
+ Ftspi020State *s = FTSPI020(FROM_SYSBUS(Ftspi020State, 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->cs_lines = g_new(qemu_irq, CFG_NR_CSLINES);
+ ssi_auto_connect_slaves(DEVICE(s), s->cs_lines, s->spi);
+ for (i = 0; i < CFG_NR_CSLINES; ++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, Ftspi020State, 4),
+ VMSTATE_UINT32(ctrl, Ftspi020State),
+ VMSTATE_UINT32(timing, Ftspi020State),
+ VMSTATE_UINT32(icr, Ftspi020State),
+ VMSTATE_UINT32(isr, Ftspi020State),
+ VMSTATE_UINT32(rdsr, Ftspi020State),
+ 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(Ftspi020State),
+ .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/arm/ftspi020.h b/hw/arm/ftspi020.h
new file mode 100644
index 0000000..47b5d2e
--- /dev/null
+++ b/hw/arm/ftspi020.h
@@ -0,0 +1,81 @@
+/*
+ * Faraday FTSPI020 Flash Controller
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <address@hidden>
+ *
+ * This code is licensed under GNU GPL v2+.
+ */
+
+#ifndef HW_ARM_FTSPI020_H
+#define HW_ARM_FTSPI020_H
+
+#include "qemu/bitops.h"
+
+/* Number of CS lines */
+#define CFG_NR_CSLINES 4
+
+/******************************************************************************
+ * FTSPI020 registers
+ *****************************************************************************/
+#define REG_CMD0 0x00 /* Flash address */
+#define REG_CMD1 0x04
+#define REG_CMD2 0x08 /* Flash data counter */
+#define REG_CMD3 0x0c
+#define REG_CR 0x10 /* Control Register */
+#define REG_TR 0x14 /* AC Timing Register */
+#define REG_SR 0x18 /* Status Register */
+#define REG_ICR 0x20 /* Interrupt Control Register */
+#define REG_ISR 0x24 /* Interrupt Status Register */
+#define REG_RDSR 0x28 /* Read Status Register */
+#define REG_REVR 0x50 /* Revision Register */
+#define REG_FEAR 0x54 /* Feature Register */
+#define REG_DR 0x100 /* Data Register */
+
+#define CMD1_CTRD BIT(28) /* Enable 1 byte continuous read */
+#define CMD1_INST_LEN(x) (((x) & 0x03) << 24)/* instruction length */
+#define CMD1_DCLK_LEN(x) (((x) & 0xff) << 16)/* dummy clock length */
+#define CMD1_ADDR_LEN(x) (((x) & 0x07) << 0) /* address length */
+
+#define CMD3_INST_OPC(x) (((x) & 0xff) << 24)/* instruction op code */
+#define CMD3_CTRD_OPC(x) (((x) & 0xff) << 16)/* cont. read op code */
+#define CMD3_CS(x) (((x) & 0x0f) << 8) /* chip select */
+#define CMD3_OPM_STD (0) /* standard 1-bit serial mode */
+#define CMD3_OPM_DUAL (1 << 5) /* fast read dual */
+#define CMD3_OPM_QUAD (2 << 5) /* fast read quad */
+#define CMD3_OPM_DUALIO (3 << 5) /* fast read dual io */
+#define CMD3_OPM_QUADIO (4 << 5) /* fast read quad io */
+#define CMD3_DTR BIT(4) /* Enable double transfer rate */
+#define CMD3_RDSR_HW (0) /* Enable HW polling RDSR */
+#define CMD3_RDSR_SW BIT(3) /* Disable HW polling RDSR */
+#define CMD3_RDSR BIT(2) /* Indicate it's a RDSR command */
+#define CMD3_READ 0 /* Indicate it's a read command */
+#define CMD3_WRITE BIT(1) /* Indicate it's a write command */
+#define CMD3_INTR BIT(0) /* Enable interrupt and status update */
+
+#define CR_BUSYBIT(x) (((x) & 0x07) << 16) /* Busy bit in the RDSR */
+#define CR_ABORT BIT(8)
+#define CR_MODE0 0 /* SPI MODE0 */
+#define CR_MODE3 BIT(4) /* SPI MODE3 */
+#define CR_CLKDIV(n) ((n) & 0x03) /* Clock divider = 2 * (n + 1) */
+
+#define TR_TRACE(x) (((x) & 0x0f) << 4) /* trace delay */
+#define TR_CS(x) (((x) & 0x0f) << 0) /* cs delay */
+
+#define SR_RX_READY BIT(1) /* Rx Ready */
+#define SR_TX_READY BIT(0) /* Tx Ready */
+
+#define ICR_RX_THRES(x) (((x) & 0x03) << 12)/* rx interrupt threshold */
+#define ICR_TX_THRES(x) (((x) & 0x03) << 8) /* tx interrupt threshold */
+#define ICR_DMA BIT(0) /* Enable DMA HW handshake */
+
+#define ISR_CMDFIN BIT(0) /* Command finished interrupt */
+
+#define FEAR_CLKM_BYPORT 0 /* clock mode = byport */
+#define FEAR_CLKM_SYNC BIT(25) /* clock mode = sync */
+#define FEAR_SUPP_DTR BIT(24) /* support double transfer rate */
+#define FEAR_CMDQ(x) (((x) & 0x07) << 16) /* cmd queue depth */
+#define FEAR_RXFIFO(x) (((x) & 0xff) << 8) /* rx fifo depth */
+#define FEAR_TXFIFO(x) (((x) & 0xff) << 0) /* tx fifo depth */
+
+#endif /* HW_ARM_FTSPI020_H */
--
1.7.9.5
- [Qemu-devel] [PATCH v4 20/23] hw/arm: add Faraday FTSDC010 MMC/SD controller support, (continued)
- [Qemu-devel] [PATCH v4 20/23] hw/arm: add Faraday FTSDC010 MMC/SD controller support, Kuo-Jung Su, 2013/02/26
- [Qemu-devel] [PATCH v4 23/23] hw/arm: add Faraday FTTMR010 timer support, Kuo-Jung Su, 2013/02/26
- [Qemu-devel] [PATCH v4 16/23] hw/arm: add Faraday FTSSP010 multi-function controller support, Kuo-Jung Su, 2013/02/26
- [Qemu-devel] [PATCH v4 10/23] hw/arm: add Faraday FTAPBBRG020 APB DMA support, Kuo-Jung Su, 2013/02/26
- [Qemu-devel] [PATCH v4 17/23] hw/arm: add Faraday FTGMAC100 1Gbps ethernet support, Kuo-Jung Su, 2013/02/26
- [Qemu-devel] [PATCH v4 22/23] hw/arm: add Faraday FTMAC110 10/100Mbps ethernet support, Kuo-Jung Su, 2013/02/26
- [Qemu-devel] [PATCH v4 13/23] hw/arm: add Faraday FTNANDC021 nand flash controller support, Kuo-Jung Su, 2013/02/26
- [Qemu-devel] [PATCH v4 15/23] hw: add WM8731 codec support, Kuo-Jung Su, 2013/02/26
- [Qemu-devel] [PATCH v4 14/23] hw/arm: add Faraday FTI2C010 I2C controller support, Kuo-Jung Su, 2013/02/26
- [Qemu-devel] [PATCH v4 18/23] hw/arm: add Faraday FTLCDC200 LCD controller support, Kuo-Jung Su, 2013/02/26
- [Qemu-devel] [PATCH v4 21/23] hw/arm: add Faraday FTSPI020 SPI flash controller support,
Kuo-Jung Su <=
- Re: [Qemu-devel] [PATCH v4 21/23] hw/arm: add Faraday FTSPI020 SPI flash controller support, Peter Crosthwaite, 2013/02/26
- Re: [Qemu-devel] [PATCH v4 21/23] hw/arm: add Faraday FTSPI020 SPI flash controller support, Andreas Färber, 2013/02/26
- Re: [Qemu-devel] [PATCH v4 21/23] hw/arm: add Faraday FTSPI020 SPI flash controller support, Kuo-Jung Su, 2013/02/26
- Re: [Qemu-devel] [PATCH v4 21/23] hw/arm: add Faraday FTSPI020 SPI flash controller support, Kuo-Jung Su, 2013/02/26
- Re: [Qemu-devel] [PATCH v4 21/23] hw/arm: add Faraday FTSPI020 SPI flash controller support, Peter Crosthwaite, 2013/02/26
- Re: [Qemu-devel] [PATCH v4 21/23] hw/arm: add Faraday FTSPI020 SPI flash controller support, Kuo-Jung Su, 2013/02/27
- [Qemu-devel] [PATCH v4 12/23] hw/nand.c: bug fix to erase operation, Kuo-Jung Su, 2013/02/26
- [Qemu-devel] [PATCH v4 19/23] hw/arm: add Faraday FTTSC010 touchscreen controller support, Kuo-Jung Su, 2013/02/26