From patchwork Fri Jan 25 08:19:49 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kuo-Jung Su X-Patchwork-Id: 215602 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 284D22C007B for ; Fri, 25 Jan 2013 20:38:50 +1100 (EST) Received: from localhost ([::1]:51561 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TyeYC-0007Po-Qc for incoming@patchwork.ozlabs.org; Fri, 25 Jan 2013 03:22:16 -0500 Received: from eggs.gnu.org ([208.118.235.92]:44741) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TyeX8-0005jq-5G for qemu-devel@nongnu.org; Fri, 25 Jan 2013 03:21:18 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TyeWu-0008Dn-TD for qemu-devel@nongnu.org; Fri, 25 Jan 2013 03:21:09 -0500 Received: from mail-da0-f44.google.com ([209.85.210.44]:64832) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TyeWu-0008Dj-I6 for qemu-devel@nongnu.org; Fri, 25 Jan 2013 03:20:56 -0500 Received: by mail-da0-f44.google.com with SMTP id z20so61673dae.17 for ; Fri, 25 Jan 2013 00:20:55 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=x-received:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references:in-reply-to:references; bh=ZwGB80EN4U66L+zYX6FOYtmfZ1PB0he4Y5hOcIpqwcM=; b=Q8Q2VLZKJFnCwG1UBlBuEnYTEPCXFGm2uO/d6EXIocxqB4Pg/02YpIqj/a/+dJ71bV XTmmNp3+//FaQ//zrvi4K/XB1vypYffGQkBDz/PEGWe9Bgd0d98De3Qvq9n20zHE3vQ+ QIPzdNqz3pasK9zmmrmFdOleXHu55yKFmVuI6MyR9zqI6GddpO7eIQAYRrdYcAg8eqwj kzfQpi9fW7wH/18TxlaGuUzBKJe28Zwqv5WpRWdVL7W25M6aKT7cyxCn4zdGRUjylJxC YucAymWdCM/o8HV6OsLfae7upTi/IJKT2JHSe0+XUCAJzB15FGpIEdnu7HWFvoTIUKRC jtXw== X-Received: by 10.68.230.101 with SMTP id sx5mr12410659pbc.50.1359102055842; Fri, 25 Jan 2013 00:20:55 -0800 (PST) Received: from localhost.localdomain ([220.132.37.35]) by mx.google.com with ESMTPS id uh9sm246180pbc.65.2013.01.25.00.20.52 (version=TLSv1 cipher=DES-CBC3-SHA bits=168/168); Fri, 25 Jan 2013 00:20:55 -0800 (PST) From: Kuo-Jung Su To: qemu-devel@nongnu.org Date: Fri, 25 Jan 2013 16:19:49 +0800 Message-Id: <1359101996-11667-14-git-send-email-dantesu@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1359101996-11667-1-git-send-email-dantesu@gmail.com> References: <1359101996-11667-1-git-send-email-dantesu@gmail.com> In-Reply-To: <1358490506-18281-1-git-send-email-dantesu@faraday-tech.com> References: <1358490506-18281-1-git-send-email-dantesu@faraday-tech.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [fuzzy] X-Received-From: 209.85.210.44 Cc: peter.maydell@linaro.org, blauwirbel@gmail.com, paul@codesourcery.com, Kuo-Jung Su , afaerber@suse.de, fred.konrad@greensocs.com Subject: [Qemu-devel] [PATCH v2 13/20] arm: add Faraday FTSPI020 spi flash controller support 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 From: Kuo-Jung Su 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 --- 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 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 + * + * 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 + * + * 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