From patchwork Fri Jan 25 08:19:46 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: 215595 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 9B6D52C0097 for ; Fri, 25 Jan 2013 20:23:55 +1100 (EST) Received: from localhost ([::1]:49972 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TyeXp-0006Zu-Q9 for incoming@patchwork.ozlabs.org; Fri, 25 Jan 2013 03:21:53 -0500 Received: from eggs.gnu.org ([208.118.235.92]:44628) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TyeWr-0004z7-Go for qemu-devel@nongnu.org; Fri, 25 Jan 2013 03:20:59 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TyeWk-0008BE-C4 for qemu-devel@nongnu.org; Fri, 25 Jan 2013 03:20:53 -0500 Received: from mail-pb0-f42.google.com ([209.85.160.42]:62862) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TyeWj-000886-J8 for qemu-devel@nongnu.org; Fri, 25 Jan 2013 03:20:46 -0500 Received: by mail-pb0-f42.google.com with SMTP id rp2so79666pbb.1 for ; Fri, 25 Jan 2013 00:20:44 -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=FTpoR58z3XnI8quuYZMJxYQTsvDBkLjH52zMeOk6pzo=; b=MolfJ0rImTnZ5I6vphC/TG0DrVMOA7jbJEgOgY5cst5g+ixgT8GXfbz9J348N4mvx2 sksqiyACRxGI9xgJqzL+wxZHEUktB0KUyf74ft/I2mBOtpTy09x6yEqHyvddwoMac9Qs tRfR1Ch5Swnb6EPdnkO/VEf+vgRDspLxaISnd8dx1B0FNtdQr/ZYT7XI4tZTUeMULg8Q CGCCEQKZMOrKhcuDLm8qjg5vL+j+zZkB8m5yyQek4lCHwIBXKniQ/eVDPLko4W1Lf39c doCAeCfoi7d8M1LRNchp85KzDYM2RTvsuhih1uqK9AMbAusyZwCEuDJPSr0pRQ+kh9iG +XxA== X-Received: by 10.66.83.134 with SMTP id q6mr11656530pay.34.1359102044782; Fri, 25 Jan 2013 00:20:44 -0800 (PST) Received: from localhost.localdomain ([220.132.37.35]) by mx.google.com with ESMTPS id uh9sm246180pbc.65.2013.01.25.00.20.41 (version=TLSv1 cipher=DES-CBC3-SHA bits=168/168); Fri, 25 Jan 2013 00:20:43 -0800 (PST) From: Kuo-Jung Su To: qemu-devel@nongnu.org Date: Fri, 25 Jan 2013 16:19:46 +0800 Message-Id: <1359101996-11667-11-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.160.42 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 10/20] arm: add Faraday FTSDC010 MMC/SD 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 FTSDC010 functions as the master in an SD memory card interface. It controls the communication between the AHB/APB bus and the SD card. Its core supports the SD bus of the SD/SDIO operations and the MMC bus of the MMC operation as well. Signed-off-by: Kuo-Jung Su --- hw/ftsdc010.c | 363 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/ftsdc010.h | 88 ++++++++++++++ 2 files changed, 451 insertions(+) create mode 100644 hw/ftsdc010.c create mode 100644 hw/ftsdc010.h diff --git a/hw/ftsdc010.c b/hw/ftsdc010.c new file mode 100644 index 0000000..36a6ed3 --- /dev/null +++ b/hw/ftsdc010.c @@ -0,0 +1,363 @@ +/* + * QEMU model of the FTSDC010 MMC/SD Host Controller + * + * Copyright (C) 2012 Faraday Technology + * Written by Dante Su + * + * This file is licensed under GNU GPL v2. + */ + +#include "sysbus.h" +#include "sysemu/sysemu.h" +#include "sysemu/blockdev.h" +#include "sd.h" + +#include "ftsdc010.h" + +#define TYPE_FTSDC010 "ftsdc010" + +typedef struct Ftsdc010State { + SysBusDevice busdev; + MemoryRegion iomem; + SDState *card; + qemu_irq irq; + + /* DMA hardware handshake */ + qemu_irq req; + + uint32_t datacnt; + + /* HW register cache */ + uint32_t cmd; + uint32_t arg; + uint32_t rsp[4]; + uint32_t rspcmd; + uint32_t dcr; + uint32_t dtr; + uint32_t dlr; + uint32_t status; + uint32_t ier; + uint32_t pwr; + uint32_t clk; +} ftsdc010_state; + +#define FTSDC010(obj) \ + OBJECT_CHECK(ftsdc010_state, obj, TYPE_FTSDC010) + +static void ftsdc010_update_irq(ftsdc010_state *s) +{ + if (s->ier & s->status) { + qemu_set_irq(s->irq, 1); + } else { + qemu_set_irq(s->irq, 0); + } +} + +static void ftsdc010_handle_ack(void *opaque, int line, int level) +{ + ftsdc010_state *s = FTSDC010(opaque); + + if (!(s->dcr & DCR_DMA)) { + return; + } + + if (level) { + qemu_set_irq(s->req, 0); + } else if (s->datacnt > 0) { + qemu_set_irq(s->req, 1); + } +} + +static void ftsdc010_send_command(ftsdc010_state *s) +{ + SDRequest request; + uint8_t response[16]; + int rlen; + + request.cmd = s->cmd & CMD_IDX; + request.arg = s->arg; + + rlen = sd_do_command(s->card, &request, response); + if (rlen < 0) { + goto error; + } + if (s->cmd & CMD_WAIT_RSP) { +#define RWORD(n) ((response[n] << 24) | (response[n + 1] << 16) \ + | (response[n + 2] << 8) | response[n + 3]) + if (rlen == 0 || (rlen == 4 && (s->cmd & CMD_LONG_RSP))) { + goto error; + } + if (rlen != 4 && rlen != 16) { + goto error; + } + if (rlen == 4) { + s->rsp[0] = RWORD(0); + s->rsp[1] = s->rsp[2] = s->rsp[3] = 0; + } else { + s->rsp[3] = RWORD(0); + s->rsp[2] = RWORD(4); + s->rsp[1] = RWORD(8); + s->rsp[0] = RWORD(12) & ~1; + } + s->rspcmd = (s->cmd & CMD_IDX); + s->rspcmd |= (s->cmd & 0x100) ? (1 << 6) : 0; + s->status |= STR_RSP; +#undef RWORD + } else { + s->status |= STR_CMD; + } + + if ((s->dcr & DCR_DMA) && (s->datacnt > 0)) { + qemu_set_irq(s->req, 1); + } + + return; + +error: + s->status |= STR_RSP_TIMEOUT; +} + +static void ftsdc010_chip_reset(ftsdc010_state *s) +{ + s->cmd = 0; + s->arg = 0; + s->rsp[0] = 0; + s->rsp[1] = 0; + s->rsp[2] = 0; + s->rsp[3] = 0; + s->rspcmd = 0; + s->dcr = 0; + s->dtr = 0; + s->dlr = 0; + s->datacnt = 0; + s->status &= ~(STR_CARD_REMOVED | STR_WPROT); + s->status |= STR_TXRDY | STR_RXRDY; + s->ier = 0; + s->pwr = 0; + s->clk = 0; +} + +static uint64_t ftsdc010_mem_read(void *opaque, hwaddr addr, unsigned size) +{ + ftsdc010_state *s = FTSDC010(opaque); + uint32_t ret = 0; + + switch (addr) { + case REG_STR: + return s->status; + case REG_DWR: + if (!(s->dcr & DCR_WR) && (s->datacnt > 0)) { + ret = sd_read_data(s->card) + | sd_read_data(s->card) << 8 + | sd_read_data(s->card) << 16 + | sd_read_data(s->card) << 24; + s->datacnt -= 4; + if (s->datacnt <= 0) { + s->status |= STR_DAT_END; + } + s->status |= STR_DAT; + } + break; + case REG_DCR: + return s->dcr; + case REG_DTR: + return s->dtr; + case REG_DLR: + return s->dlr; + case REG_CMD: + return s->cmd; + case REG_ARG: + return s->arg; + case REG_RSP0: + return s->rsp[0]; + case REG_RSP1: + return s->rsp[1]; + case REG_RSP2: + return s->rsp[2]; + case REG_RSP3: + return s->rsp[3]; + case REG_RSPCMD: + return s->rspcmd; + case REG_IER: + return s->ier; + case REG_PWR: + return s->pwr; + case REG_CLK: + return s->clk; + case REG_BUS: + return 0x00000009; + case REG_FEA: + return 0x00000010; + case REG_REV: + return 0x00030300; + default: + break; + } + + return ret; +} + +static void ftsdc010_mem_write(void *opaque, + hwaddr addr, + uint64_t val, + unsigned size) +{ + ftsdc010_state *s = FTSDC010(opaque); + + switch (addr) { + case REG_DWR: + if ((s->dcr & DCR_WR) && (s->datacnt > 0)) { + sd_write_data(s->card, ((uint32_t)val >> 0) & 0xff); + sd_write_data(s->card, ((uint32_t)val >> 8) & 0xff); + sd_write_data(s->card, ((uint32_t)val >> 16) & 0xff); + sd_write_data(s->card, ((uint32_t)val >> 24) & 0xff); + s->datacnt -= 4; + if (s->datacnt <= 0) { + s->status |= STR_DAT_END; + } + s->status |= STR_DAT; + } + break; + case REG_CMD: + s->cmd = (uint32_t)val; + if (s->cmd & (1 << 10)) { + ftsdc010_chip_reset(s); + } else if (s->cmd & (1 << 9)) { + s->cmd &= ~(1 << 9); + s->status &= ~(STR_CMD | STR_RSP | STR_RSP_ERR); + ftsdc010_send_command(s); + } + break; + case REG_ARG: + s->arg = (uint32_t)val; + break; + case REG_DCR: + s->dcr = (uint32_t)val; + if (s->dcr & DCR_EN) { + s->dcr &= ~(DCR_EN); + s->status &= ~(STR_DAT | STR_DAT_END | STR_DAT_ERR); + s->datacnt = s->dlr; + } + break; + case REG_DTR: + s->dtr = (uint32_t)val; + break; + case REG_DLR: + s->dlr = (uint32_t)val; + break; + case REG_SCR: + s->status &= ~((uint32_t)val & 0x14ff); + ftsdc010_update_irq(s); + break; + case REG_IER: + s->ier = (uint32_t)val; + ftsdc010_update_irq(s); + break; + case REG_PWR: + s->pwr = (uint32_t)val; + break; + case REG_CLK: + s->clk = (uint32_t)val; + break; + default: + break; + } +} + +static const MemoryRegionOps ftsdc010_ops = { + .read = ftsdc010_mem_read, + .write = ftsdc010_mem_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } +}; + +static void ftsdc010_reset(DeviceState *ds) +{ + SysBusDevice *busdev = SYS_BUS_DEVICE(ds); + ftsdc010_state *s = FTSDC010(FROM_SYSBUS(ftsdc010_state, busdev)); + + ftsdc010_chip_reset(s); + + sd_set_cb(s->card, NULL, NULL); + + /* We can assume our GPIO outputs have been wired up now */ + qemu_set_irq(s->req, 0); +} + +static int ftsdc010_init(SysBusDevice *dev) +{ + ftsdc010_state *s = FTSDC010(FROM_SYSBUS(ftsdc010_state, dev)); + DriveInfo *dinfo; + + memory_region_init_io(&s->iomem, &ftsdc010_ops, s, TYPE_FTSDC010, 0x1000); + sysbus_init_mmio(dev, &s->iomem); + sysbus_init_irq(dev, &s->irq); + + qdev_init_gpio_in(&s->busdev.qdev, ftsdc010_handle_ack, 1); + qdev_init_gpio_out(&s->busdev.qdev, &s->req, 1); + + dinfo = drive_get_next(IF_SD); + s->card = sd_init(dinfo ? dinfo->bdrv : NULL, 0); + + s->status = STR_CARD_REMOVED; + if (dinfo) { + if (bdrv_is_read_only(dinfo->bdrv)) { + s->status |= STR_WPROT; + } + if (bdrv_is_inserted(dinfo->bdrv)) { + s->status &= ~STR_CARD_REMOVED; + } + } + + return 0; +} + +static const VMStateDescription vmstate_ftsdc010 = { + .name = TYPE_FTSDC010, + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cmd, ftsdc010_state), + VMSTATE_UINT32(arg, ftsdc010_state), + VMSTATE_UINT32_ARRAY(rsp, ftsdc010_state, 4), + VMSTATE_UINT32(rspcmd, ftsdc010_state), + VMSTATE_UINT32(dcr, ftsdc010_state), + VMSTATE_UINT32(dtr, ftsdc010_state), + VMSTATE_UINT32(dlr, ftsdc010_state), + VMSTATE_UINT32(datacnt, ftsdc010_state), + VMSTATE_UINT32(status, ftsdc010_state), + VMSTATE_UINT32(ier, ftsdc010_state), + VMSTATE_UINT32(pwr, ftsdc010_state), + VMSTATE_UINT32(clk, ftsdc010_state), + VMSTATE_END_OF_LIST() + } +}; + +static void ftsdc010_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + k->init = ftsdc010_init; + dc->vmsd = &vmstate_ftsdc010; + dc->reset = ftsdc010_reset; + dc->no_user = 1; +} + +static const TypeInfo ftsdc010_info = { + .name = TYPE_FTSDC010, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ftsdc010_state), + .class_init = ftsdc010_class_init, +}; + +static void ftsdc010_register_types(void) +{ + type_register_static(&ftsdc010_info); +} + +type_init(ftsdc010_register_types) diff --git a/hw/ftsdc010.h b/hw/ftsdc010.h new file mode 100644 index 0000000..26aa5a9 --- /dev/null +++ b/hw/ftsdc010.h @@ -0,0 +1,88 @@ +/* + * QEMU model of the FTSDC010 MMC/SD Host Controller + * + * Copyright (C) 2012 Faraday Technology + * Written by Dante Su + * + * This file is licensed under GNU GPL. + */ + +#ifndef _FTSDC010_H +#define _FTSDC010_H + +/* sd controller register */ +#define REG_CMD 0x0000 +#define REG_ARG 0x0004 +#define REG_RSP0 0x0008 /* response */ +#define REG_RSP1 0x000C +#define REG_RSP2 0x0010 +#define REG_RSP3 0x0014 +#define REG_RSPCMD 0x0018 /* responsed command */ +#define REG_DCR 0x001C /* data control */ +#define REG_DTR 0x0020 /* data timeout */ +#define REG_DLR 0x0024 /* data length */ +#define REG_STR 0x0028 /* status register */ +#define REG_SCR 0x002C /* status clear register */ +#define REG_IER 0x0030 /* interrupt mask/enable register */ +#define REG_PWR 0x0034 /* power control */ +#define REG_CLK 0x0038 /* clock control */ +#define REG_BUS 0x003C /* bus width */ +#define REG_DWR 0x0040 /* data window */ +#define REG_GPOR 0x0048 +#define REG_FEA 0x009C +#define REG_REV 0x00A0 + +/* bit mapping of command register */ +#define CMD_IDX 0x0000003F +#define CMD_WAIT_RSP 0x00000040 +#define CMD_LONG_RSP 0x00000080 +#define CMD_APP 0x00000100 +#define CMD_EN 0x00000200 +#define CMD_RST 0x00000400 + +/* bit mapping of response command register */ +#define RSP_CMDIDX 0x0000003F +#define RSP_CMDAPP 0x00000040 + +/* bit mapping of data control register */ +#define DCR_BKSZ 0x0000000F +#define DCR_WR 0x00000010 +#define DCR_RD 0x00000000 +#define DCR_DMA 0x00000020 +#define DCR_EN 0x00000040 +#define DCR_THRES 0x00000080 +#define DCR_BURST1 0x00000000 +#define DCR_BURST4 0x00000100 +#define DCR_BURST8 0x00000200 +#define DCR_FIFO_RESET 0x00000400 + +/* bit mapping of status register */ +#define STR_RSP_CRC 0x00000001 +#define STR_DAT_CRC 0x00000002 +#define STR_RSP_TIMEOUT 0x00000004 +#define STR_DAT_TIMEOUT 0x00000008 +#define STR_RSP_ERR (STR_RSP_CRC | STR_RSP_TIMEOUT) +#define STR_DAT_ERR (STR_DAT_CRC | STR_DAT_TIMEOUT) +#define STR_RSP 0x00000010 +#define STR_DAT 0x00000020 +#define STR_CMD 0x00000040 +#define STR_DAT_END 0x00000080 +#define STR_TXRDY 0x00000100 +#define STR_RXRDY 0x00000200 +#define STR_CARD_CHANGE 0x00000400 +#define STR_CARD_REMOVED 0x00000800 +#define STR_WPROT 0x00001000 +#define STR_SDIO 0x00010000 +#define STR_DAT0 0x00020000 + +/* bit mapping of clock register */ +#define CLK_HISPD 0x00000200 +#define CLK_OFF 0x00000100 +#define CLK_SD 0x00000080 + +/* bit mapping of bus register */ +#define BUS_CARD_DATA3 0x00000020 +#define BUS_4BITS_SUPP 0x00000008 +#define BUS_8BITS_SUPP 0x00000010 + +#endif