Signed-off-by: Kuo-Jung Su <dant...@faraday-tech.com> --- hw/ftsdc010.c | 360 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/ftsdc010.h | 103 +++++++++++++++++ 2 files changed, 463 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..006fcae --- /dev/null +++ b/hw/ftsdc010.c @@ -0,0 +1,360 @@ +/* + * QEMU model of the FTSDC010 MMC/SD Host Controller + * + * Copyright (C) 2012 Faraday Technology + * Copyright (C) 2012 Dante Su <dant...@faraday-tech.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysbus.h" +#include "sysemu/sysemu.h" +#include "sysemu/blockdev.h" +#include "sd.h" + +#include "ftsdc010.h" + +typedef struct { + 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; + +static inline 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_state *)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 int size) +{ + ftsdc010_state *s = 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 int size) +{ + ftsdc010_state *s = 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_mem_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 *d) +{ + ftsdc010_state *s = DO_UPCAST(ftsdc010_state, busdev.qdev, d); + + 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 = FROM_SYSBUS(typeof(*s), dev); + DriveInfo *dinfo; + + memory_region_init_io(&s->iomem, &ftsdc010_mem_ops, s, "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 = "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 *sdc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *k = DEVICE_CLASS(klass); + + sdc->init = ftsdc010_init; + k->vmsd = &vmstate_ftsdc010; + k->reset = ftsdc010_reset; + k->no_user = 1; +} + +static TypeInfo ftsdc010_info = { + .name = "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..482e6e2 --- /dev/null +++ b/hw/ftsdc010.h @@ -0,0 +1,103 @@ +/* + * (C) Copyright 2010 + * Faraday Technology Inc. <www.faraday-tech.com> + * Dante Su <dant...@gmail.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#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 -- 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. ***********************************************************************