Signed-off-by: Kuo-Jung Su <dant...@faraday-tech.com> --- 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 <dant...@faraday-tech.com> + * + * 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 (dant...@faraday-tech.com) + * 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. ***********************************************************************