From patchwork Fri Jan 25 08:19:45 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: 215576 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 B644A2C008E for ; Fri, 25 Jan 2013 19:23:16 +1100 (EST) Received: from localhost ([::1]:55091 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TyeZ8-0000r1-OA for incoming@patchwork.ozlabs.org; Fri, 25 Jan 2013 03:23:14 -0500 Received: from eggs.gnu.org ([208.118.235.92]:44597) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TyeWl-0004iL-Km for qemu-devel@nongnu.org; Fri, 25 Jan 2013 03:20:52 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TyeWf-000858-Sz for qemu-devel@nongnu.org; Fri, 25 Jan 2013 03:20:47 -0500 Received: from mail-pa0-f47.google.com ([209.85.220.47]:44624) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TyeWf-00084v-GR for qemu-devel@nongnu.org; Fri, 25 Jan 2013 03:20:41 -0500 Received: by mail-pa0-f47.google.com with SMTP id fa10so124601pad.20 for ; Fri, 25 Jan 2013 00:20:40 -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=JJx2GcrBktcCy4ETcNZCUiI7NjR2fv3VNvdXjL/ZhdA=; b=VExLNzXgSGByK9i3fNR7CzdCa7tmHzQ+TEstcpfQ+5YlON/vrzWnbOzsUlo8cGVcmG uqjQtY0dF1UrYF0BiV80AjstubWjMTq0IB47nDcAHe/YKttA+3K9Q7okSV37I2JoSqPH f7DmBZTxnC4uY3IzIlE9e3/qJs/AdZBLlU98YnwsFWbpGUZyCyUDxg1aFEMGArG3cCrT 8v0ZSBZBvxhKdgZmeTSumGk4mFH6DqM43t/kcesLOw4zeBAaWnIQR1bvZMHT7D8cALYx 2aedxa2Rk9/IvMmIMflzUkAQRWtLwB6u99dhUsbgt4kgefu6v2aWtb6X1VvZ0VFhJf6Z e9bA== X-Received: by 10.68.223.230 with SMTP id qx6mr12308553pbc.159.1359102040902; Fri, 25 Jan 2013 00:20:40 -0800 (PST) Received: from localhost.localdomain ([220.132.37.35]) by mx.google.com with ESMTPS id uh9sm246180pbc.65.2013.01.25.00.20.37 (version=TLSv1 cipher=DES-CBC3-SHA bits=168/168); Fri, 25 Jan 2013 00:20:40 -0800 (PST) From: Kuo-Jung Su To: qemu-devel@nongnu.org Date: Fri, 25 Jan 2013 16:19:45 +0800 Message-Id: <1359101996-11667-10-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.220.47 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 09/20] arm: add Faraday FTNANDC021 nand 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 FTNANDC021 NAND flash host controller allows the users to access the NAND flash memory simply by reading or writing into the registers. It provides the hardware-based NAND host controller so that data can be transferred in the high speed mode. The ECC information will be automatically generated in the programming operation and will be checked in the read operation. Signed-off-by: Kuo-Jung Su --- hw/ftnandc021.c | 514 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/ftnandc021.h | 55 ++++++ 2 files changed, 569 insertions(+) create mode 100644 hw/ftnandc021.c create mode 100644 hw/ftnandc021.h diff --git a/hw/ftnandc021.c b/hw/ftnandc021.c new file mode 100644 index 0000000..cf28c6e --- /dev/null +++ b/hw/ftnandc021.c @@ -0,0 +1,514 @@ +/* + * QEMU model of the FTNANDC021 NAND Flash Controller + * + * Copyright (C) 2012 Faraday Technology + * Written by Dante Su + * + * This file is licensed under GNU GPL v2. + */ + +#include "sysbus.h" +#include "devices.h" +#include "sysemu/blockdev.h" +#include "flash.h" + +#include "ftnandc021.h" + +#define TYPE_FTNANDC021 "ftnandc021" + +typedef struct Ftnandc021State { + SysBusDevice busdev; + MemoryRegion mmio; + + qemu_irq irq; + DeviceState *flash; + + /* DMA hardware handshake */ + qemu_irq req; + + uint8_t manf_id, chip_id; + + int cmd; + int len; /* buffer length for page read/write */ + int pi; /* page index */ + int bw; /* bus width (8-bits, 16-bits) */ + + uint64_t size; /* flash size (maximum access range) */ + uint32_t pgsz; /* page size (Bytes) */ + uint32_t bksz; /* block size (Bytes) */ + uint32_t alen; /* address length (cycle) */ + + uint32_t id[2]; + uint8_t oob[8];/* 5 bytes for 512/2048 page; 7 bytes for 4096 page */ + + /* HW register caches */ + uint32_t sr; + uint32_t fcr; + uint32_t mcr; + uint32_t ier; + uint32_t bcr; +} ftnandc021_state; + +#define FTNANDC021(obj) \ + OBJECT_CHECK(ftnandc021_state, obj, TYPE_FTNANDC021) + +static void ftnandc021_update_irq(ftnandc021_state *s) +{ + if (s->ier & (1 << 7)) { + if ((s->ier & 0x0f) & (s->sr >> 2)) { + qemu_set_irq(s->irq, 1); + } else { + qemu_set_irq(s->irq, 0); + } + } +} + +static void ftnandc021_set_idle(ftnandc021_state *s) +{ + /* CLE=0, ALE=0, CS=1 */ + nand_setpins(s->flash, 0, 0, 1, 1, 0); + + /* Set command compelete */ + s->sr |= (1 << 2); + + /* Update IRQ signal */ + ftnandc021_update_irq(s); +} + +static void ftnandc021_set_cmd(ftnandc021_state *s, uint8_t cmd) +{ + /* CLE=1, ALE=0, CS=0 */ + nand_setpins(s->flash, 1, 0, 0, 1, 0); + + /* Write out command code */ + nand_setio(s->flash, cmd); +} + +static void ftnandc021_set_addr(ftnandc021_state *s, int col, int row) +{ + /* CLE=0, ALE=1, CS=0 */ + nand_setpins(s->flash, 0, 1, 0, 1, 0); + + if (col < 0 && row < 0) { + /* special case for READ_ID (0x90) */ + nand_setio(s->flash, 0); + } else { + /* column address */ + if (col >= 0) { + nand_setio(s->flash, (col & 0x00ff) >> 0); + nand_setio(s->flash, (col & 0xff00) >> 8); + } + /* row address */ + if (row >= 0) { + nand_setio(s->flash, (row & 0x0000ff) >> 0); + if (s->alen >= 4) { + nand_setio(s->flash, (row & 0x00ff00) >> 8); + } + if (s->alen >= 5) { + nand_setio(s->flash, (row & 0xff0000) >> 16); + } + } + } +} + +static void ftnandc021_handle_ack(void *opaque, int line, int level) +{ + ftnandc021_state *s = FTNANDC021(opaque); + + if (!s->bcr) { + return; + } + + if (level) { + qemu_set_irq(s->req, 0); + } else if (s->len > 0) { + qemu_set_irq(s->req, 1); + } +} + +static void ftnandc021_command(ftnandc021_state *s, uint32_t cmd) +{ + int i; + + s->sr &= ~(1 << 2); + s->cmd = cmd; + + switch (cmd) { + case FTNANDC021_CMD_RDID: /* read id */ + ftnandc021_set_cmd(s, 0x90); + ftnandc021_set_addr(s, -1, -1); + nand_setpins(s->flash, 0, 0, 0, 1, 0); + if (s->bw == 8) { + s->id[0] = (nand_getio(s->flash) << 0) + | (nand_getio(s->flash) << 8) + | (nand_getio(s->flash) << 16) + | (nand_getio(s->flash) << 24); + s->id[1] = (nand_getio(s->flash) << 0); + } else { + s->id[0] = (nand_getio(s->flash) << 0) + | (nand_getio(s->flash) << 16); + s->id[1] = (nand_getio(s->flash) << 0); + } + break; + case FTNANDC021_CMD_RESET: /* reset */ + ftnandc021_set_cmd(s, 0xff); + break; + case FTNANDC021_CMD_RDST: /* read status */ + ftnandc021_set_cmd(s, 0x70); + nand_setpins(s->flash, 0, 0, 0, 1, 0); + s->id[1] = (nand_getio(s->flash) << 0); + break; + case FTNANDC021_CMD_RDPG: /* read page */ + ftnandc021_set_cmd(s, 0x00); + ftnandc021_set_addr(s, 0, s->pi); + ftnandc021_set_cmd(s, 0x30); + nand_setpins(s->flash, 0, 0, 0, 1, 0); + s->len = s->pgsz; + break; + case FTNANDC021_CMD_RDOOB: /* read oob */ + ftnandc021_set_cmd(s, 0x00); + ftnandc021_set_addr(s, s->pgsz, s->pi); + ftnandc021_set_cmd(s, 0x30); + nand_setpins(s->flash, 0, 0, 0, 1, 0); + for (i = 0; i < 16 * (s->pgsz / 512); ) { + if (s->bw == 8) { + if (i < 7) { + s->oob[i] = (uint8_t)nand_getio(s->flash); + } else { + (void)nand_getio(s->flash); + } + i += 1; + } else { + if (i < 7) { + *(uint16_t *)(s->oob + i) = (uint16_t)nand_getio(s->flash); + } else { + (void)nand_getio(s->flash); + } + i += 2; + } + } + break; + case FTNANDC021_CMD_WRPG: /* write page + read status */ + ftnandc021_set_cmd(s, 0x80); + ftnandc021_set_addr(s, 0, s->pi); + /* data phase */ + nand_setpins(s->flash, 0, 0, 0, 1, 0); + s->len = s->pgsz; + break; + case FTNANDC021_CMD_ERBLK: /* erase block + read status */ + ftnandc021_set_cmd(s, 0x60); + ftnandc021_set_addr(s, -1, s->pi); + ftnandc021_set_cmd(s, 0xd0); + /* read status */ + ftnandc021_command(s, 0x04); + break; + case FTNANDC021_CMD_WROOB: /* write oob + read status */ + ftnandc021_set_cmd(s, 0x80); + ftnandc021_set_addr(s, s->pgsz, s->pi); + /* data phase */ + nand_setpins(s->flash, 0, 0, 0, 1, 0); + for (i = 0; i < 16 * (s->pgsz / 512); ) { + if (s->bw == 8) { + if (i <= 7) { + nand_setio(s->flash, s->oob[i]); + } else { + nand_setio(s->flash, 0xffffffff); + } + i += 1; + } else { + if (i <= 7) { + nand_setio(s->flash, s->oob[i] | (s->oob[i + 1] << 8)); + } else { + nand_setio(s->flash, 0xffffffff); + } + i += 2; + } + } + ftnandc021_set_cmd(s, 0x10); + /* read status */ + ftnandc021_command(s, 0x04); + break; + default: + printf("[qemu] ftnandc021: unknow command=0x%02x\n", cmd); + break; + } + + /* if cmd is not page read/write, then return to idle mode */ + if (s->cmd != FTNANDC021_CMD_RDPG && s->cmd != FTNANDC021_CMD_WRPG) { + ftnandc021_set_idle(s); + } else if (s->bcr && (s->len > 0)) { + qemu_set_irq(s->req, 1); + } +} + +static uint64_t ftnandc021_mem_read(void *opaque, + hwaddr addr, + unsigned size) +{ + ftnandc021_state *s = FTNANDC021(opaque); + + switch (addr) { + case REG_DR: + if (s->cmd == FTNANDC021_CMD_RDPG && s->len > 0) { + uint32_t val = 0; + if (s->bw == 8) { + val |= (nand_getio(s->flash) & 0xff) << 0; + val |= (nand_getio(s->flash) & 0xff) << 8; + val |= (nand_getio(s->flash) & 0xff) << 16; + val |= (nand_getio(s->flash) & 0xff) << 24; + } else { + val |= (nand_getio(s->flash) & 0xffff) << 0; + val |= (nand_getio(s->flash) & 0xffff) << 16; + } + s->len -= 4; + if (s->len <= 0) { + ftnandc021_set_idle(s); + } + return val; + } + break; + case REG_SR: + return s->sr; + case REG_ACR: + return s->cmd << 8; + case REG_RDBR: + return s->oob[0]; + case REG_RDLSN: + return s->oob[1] | (s->oob[2] << 8); + case REG_RDCRC: + if (s->pgsz > 2048) { + return s->oob[3] | (s->oob[4] << 8) + | (s->oob[5] << 16) | (s->oob[6] << 24); + } else { + return s->oob[3] | (s->oob[4] << 8); + } + case REG_FCR: + return s->fcr; + case REG_PIR: + return s->pi; + case REG_PCR: + return 1; + case REG_MCR: + return s->mcr; + case REG_IDRL: + return s->id[0]; + case REG_IDRH: + return s->id[1]; + case REG_IER: + return s->ier; + case REG_BCR: + return s->bcr; + case REG_ATR1: + return 0x02240264; + case REG_ATR2: + return 0x42054209; + case REG_PRR: + return 0x00000001; + case REG_REVR: + return 0x00010100; + case REG_CFGR: + return 0x00081602; + default: + break; + } + + return 0; +} + +static void ftnandc021_mem_write(void *opaque, + hwaddr addr, + uint64_t val, + unsigned size) +{ + ftnandc021_state *s = FTNANDC021(opaque); + + switch (addr) { + case REG_DR: + if (s->cmd == FTNANDC021_CMD_WRPG && s->len > 0) { + if (s->bw == 8) { + nand_setio(s->flash, ((uint32_t)val >> 0) & 0xff); + nand_setio(s->flash, ((uint32_t)val >> 8) & 0xff); + nand_setio(s->flash, ((uint32_t)val >> 16) & 0xff); + nand_setio(s->flash, ((uint32_t)val >> 24) & 0xff); + } else { + nand_setio(s->flash, ((uint32_t)val >> 0) & 0xffff); + nand_setio(s->flash, ((uint32_t)val >> 16) & 0xffff); + } + s->len -= 4; + if (s->len <= 0) { + ftnandc021_set_cmd(s, 0x10); + /* read status */ + ftnandc021_command(s, 0x04); + } + } + break; + case REG_ACR: + if (!((uint32_t)val & (1 << 7))) { + break; + } + ftnandc021_command(s, ((uint32_t)val >> 8) & 0x1f); + break; + case REG_WRBR: + s->oob[0] = (uint32_t)val & 0xff; + break; + case REG_WRLSN: + s->oob[1] = ((uint32_t)val >> 0) & 0xff; + s->oob[2] = ((uint32_t)val >> 8) & 0xff; + break; + case REG_WRCRC: + s->oob[3] = ((uint32_t)val >> 0) & 0xff; + s->oob[4] = ((uint32_t)val >> 8) & 0xff; + if (s->pgsz > 2048) { + s->oob[5] = ((uint32_t)val >> 16) & 0xff; + s->oob[6] = ((uint32_t)val >> 24) & 0xff; + } + break; + case REG_FCR: + s->fcr = (uint32_t)val; + if (s->fcr & (1 << 4)) { + s->bw = 16; + } else { + s->bw = 8; + } + break; + case REG_PIR: + s->pi = (uint32_t)val & 0x03ffffff; + break; + case REG_MCR: + s->mcr = (uint32_t)val; + /* page size */ + switch ((s->mcr >> 8) & 0x03) { + case 0: + s->pgsz = 512; + break; + case 2: + s->pgsz = 4096; + break; + default: + s->pgsz = 2048; + break; + } + /* block size */ + s->bksz = s->pgsz * (1 << (4 + ((s->mcr >> 16) & 0x03))); + /* address length (cycle) */ + s->alen = 3 + ((s->mcr >> 10) & 0x03); + /* flash size */ + s->size = 1ULL << (24 + ((s->mcr >> 4) & 0x0f)); + break; + case REG_IER: + s->ier = (uint32_t)val & 0x8f; + ftnandc021_update_irq(s); + break; + case REG_ISCR: + s->sr &= ~(((uint32_t)val & 0x0f) << 2); + ftnandc021_update_irq(s); + break; + case REG_BCR: + s->bcr = (uint32_t)val; + break; + default: + break; + } +} + +static const MemoryRegionOps ftnandc021_ops = { + .read = ftnandc021_mem_read, + .write = ftnandc021_mem_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } +}; + +static void ftnandc021_reset(DeviceState *ds) +{ + SysBusDevice *busdev = SYS_BUS_DEVICE(ds); + ftnandc021_state *s = FTNANDC021(FROM_SYSBUS(ftnandc021_state, busdev)); + + s->sr = 0; + s->fcr = 0; + s->mcr = 0; + s->ier = 0; + s->bcr = 0; + s->id[0] = 0; + s->id[1] = 0; + + /* We can assume our GPIO outputs have been wired up now */ + qemu_set_irq(s->req, 0); +} + +static int ftnandc021_init(SysBusDevice *dev) +{ + ftnandc021_state *s = FTNANDC021(FROM_SYSBUS(ftnandc021_state, dev)); + DriveInfo *nand; + + memory_region_init_io(&s->mmio, + &ftnandc021_ops, + s, + TYPE_FTNANDC021, + 0x1000); + sysbus_init_mmio(dev, &s->mmio); + sysbus_init_irq(dev, &s->irq); + + qdev_init_gpio_in(&s->busdev.qdev, ftnandc021_handle_ack, 1); + qdev_init_gpio_out(&s->busdev.qdev, &s->req, 1); + + nand = drive_get_next(IF_MTD); + s->flash = nand_init(nand ? nand->bdrv : NULL, s->manf_id, s->chip_id); + + return 0; +} + +static const VMStateDescription vmstate_ftnandc021 = { + .name = TYPE_FTNANDC021, + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(sr, ftnandc021_state), + VMSTATE_UINT32(fcr, ftnandc021_state), + VMSTATE_UINT32(mcr, ftnandc021_state), + VMSTATE_UINT32(ier, ftnandc021_state), + VMSTATE_UINT32(bcr, ftnandc021_state), + VMSTATE_END_OF_LIST() + } +}; + +static Property ftnandc021_properties[] = { + DEFINE_PROP_UINT8("manufacturer_id", + ftnandc021_state, + manf_id, + NAND_MFR_SAMSUNG), + DEFINE_PROP_UINT8("chip_id", + ftnandc021_state, + chip_id, + 0xda), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ftnandc021_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + k->init = ftnandc021_init; + dc->vmsd = &vmstate_ftnandc021; + dc->props = ftnandc021_properties; + dc->reset = ftnandc021_reset; + dc->no_user = 1; +} + +static const TypeInfo ftnandc021_info = { + .name = TYPE_FTNANDC021, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ftnandc021_state), + .class_init = ftnandc021_class_init, +}; + +static void ftnandc021_register_types(void) +{ + type_register_static(&ftnandc021_info); +} + +type_init(ftnandc021_register_types) diff --git a/hw/ftnandc021.h b/hw/ftnandc021.h new file mode 100644 index 0000000..7d6d779 --- /dev/null +++ b/hw/ftnandc021.h @@ -0,0 +1,55 @@ +/* + * QEMU model of the FTNANDC021 NAND Flash Controller + * + * Copyright (C) 2012 Faraday Technology + * Written by Dante Su + * + * This file is licensed under GNU GPL. + */ + +#ifndef FTNANDC021_H +#define FTNANDC021_H + +/* NANDC control registers */ +#define REG_SR 0x100 /* Status Register */ +#define REG_ACR 0x104 /* Access Control Register */ +#define REG_FCR 0x108 /* Flow Control Register */ +#define REG_PIR 0x10C /* Page Index Register */ +#define REG_MCR 0x110 /* Memory Configuration Register */ +#define REG_ATR1 0x114 /* AC Timing Register 1 */ +#define REG_ATR2 0x118 /* AC Timing Register 2 */ +#define REG_IDRL 0x120 /* ID Register LSB */ +#define REG_IDRH 0x124 /* ID Register MSB */ +#define REG_IER 0x128 /* Interrupt Enable Register */ +#define REG_ISCR 0x12C /* Interrupt Status Clear Register */ +#define REG_WRBR 0x140 /* Write Bad Block Register */ +#define REG_WRLSN 0x144 /* Write LSN Register */ +#define REG_WRCRC 0x148 /* Write LSN CRC Register */ +#define REG_RDBR 0x150 /* Read Bad Block Register */ +#define REG_RDLSN 0x154 /* Read LSN Register */ +#define REG_RDCRC 0x158 /* Read LSN CRC Register */ + +/* BMC control registers */ +#define REG_PRR 0x208 /* BMC PIO Ready Register */ +#define REG_BCR 0x20C /* BMC Burst Control Register */ + +/** MISC register **/ +#define REG_DR 0x300 /* Data Register */ +#define REG_PCR 0x308 /* Page Count Register */ +#define REG_RSTR 0x30C /* MLC Reset Register */ +#define REG_REVR 0x3F8 /* Revision Register */ +#define REG_CFGR 0x3FC /* Configuration Register */ + +/* + * FTNANDC021 integrated command set + */ +#define FTNANDC021_CMD_RDID 0x01 /* read id */ +#define FTNANDC021_CMD_RESET 0x02 +#define FTNANDC021_CMD_RDST 0x04 /* read status */ +#define FTNANDC021_CMD_RDPG 0x05 /* read page (data + oob) */ +#define FTNANDC021_CMD_RDOOB 0x06 /* read oob */ +#define FTNANDC021_CMD_WRPG 0x10 /* write page (data + oob) */ +#define FTNANDC021_CMD_ERBLK 0x11 /* erase block */ +#define FTNANDC021_CMD_WROOB 0x13 /* write oob */ + +#endif /* EOF */