From patchwork Fri Jan 18 06:31:50 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dante X-Patchwork-Id: 213496 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 085192C0089 for ; Fri, 18 Jan 2013 17:44:45 +1100 (EST) Received: from localhost ([::1]:45823 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Tw5gx-0005QQ-61 for incoming@patchwork.ozlabs.org; Fri, 18 Jan 2013 01:44:43 -0500 Received: from eggs.gnu.org ([208.118.235.92]:42086) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Tw5gk-0005Pm-AK for qemu-devel@nongnu.org; Fri, 18 Jan 2013 01:44:34 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Tw5gg-0000O3-Ah for qemu-devel@nongnu.org; Fri, 18 Jan 2013 01:44:30 -0500 Received: from ftcsun4.faraday-tech.com ([60.248.181.187]:56186 helo=ftcsun4.faraday.com.tw) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Tw5Uh-0005u8-Mx for qemu-devel@nongnu.org; Fri, 18 Jan 2013 01:32:04 -0500 Received: from ftcpcs100.faraday.com.tw ([192.168.31.10]) by ftcsun4.faraday.com.tw (8.11.7p1+Sun/8.11.6) with ESMTP id r0I6Vxm07336 for ; Fri, 18 Jan 2013 14:31:59 +0800 (CST) Received: from FTCPCS63.faraday.com.tw (unverified [192.168.32.63]) by ftcpcs100.faraday.com.tw (Clearswift SMTPRS 5.2.9) with ESMTP id ; Fri, 18 Jan 2013 14:32:00 +0800 Received: from vmware.faraday.com.tw (192.168.65.96) by FTCPCS63.faraday.com.tw (192.168.32.65) with Microsoft SMTP Server (TLS) id 14.2.328.9; Fri, 18 Jan 2013 14:31:59 +0800 From: Dante To: Date: Fri, 18 Jan 2013 14:31:50 +0800 Message-ID: <1358490710-18587-1-git-send-email-dantesu@faraday-tech.com> X-Mailer: git-send-email 1.7.9.5 MIME-Version: 1.0 X-Originating-IP: [192.168.65.96] X-KSE-Antivirus-Interceptor-Info: scan successful X-KSE-Antivirus-Info: Clean X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 60.248.181.187 Cc: peter.maydell@linaro.org, paul@codesourcery.com, dantesu@faraday-tech.com Subject: [Qemu-devel] [PATCH 12/18] hw: add QEMU model for Faraday high-speed SPI-NOR flash controller X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Signed-off-by: Kuo-Jung Su --- 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 + * + * 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 (dantesu@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 . + * 4. Neither the name of the 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 ''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 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