[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH v1 1/2] SDHCI: inital version
From: |
Peter Maydell |
Subject: |
Re: [Qemu-devel] [PATCH v1 1/2] SDHCI: inital version |
Date: |
Mon, 2 Apr 2012 08:20:23 +0100 |
On 2 April 2012 07:24, Peter A. G. Crosthwaite
<address@hidden> wrote:
> device more for standard SD host controller interface (SDHCI).
>
> Signed-off-by: Peter A. G. Crosthwaite <address@hidden>
So how does this compare with Vincent Palatin's version?
(http://patchwork.ozlabs.org/patch/106767/) I'm guessing from
the copyright string that it's a modified version -- it would
be nice to say what the differences are.
-- PMM
> ---
> Makefile.target | 1 +
> hw/sdhci.c | 748
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 749 insertions(+), 0 deletions(-)
> create mode 100644 hw/sdhci.c
>
> diff --git a/Makefile.target b/Makefile.target
> index 9b0cf74..c118dc6 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -363,6 +363,7 @@ obj-arm-y += versatile_pci.o
> obj-arm-y += cadence_uart.o
> obj-arm-y += cadence_ttc.o
> obj-arm-y += cadence_gem.o
> +obj-arm-y += sdhci.o
> obj-arm-y += xilinx_zynq.o zynq_slcr.o
> obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
> obj-arm-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o
> diff --git a/hw/sdhci.c b/hw/sdhci.c
> new file mode 100644
> index 0000000..c8299d5
> --- /dev/null
> +++ b/hw/sdhci.c
> @@ -0,0 +1,748 @@
> +/*
> + * Copyright 2011 Google Inc.
> + * Copyright (C) 2012 Peter A.G. Crosthwaite <address@hidden>
> + * Copyright (C) 2012 PetaLogix Pty Ltd.
> + *
> + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
> + * USA.
> + *
> + *
> + * SDHCI (SD Host Controler Interface) emulation
> + */
> +
> +#include "blockdev.h"
> +#include "sysbus.h"
> +#include "sd.h"
> +
> +#ifdef SDHCI_ERR_DEBUG
> +#define DB_PRINT(...) do { \
> + fprintf(stderr, ": %s: ", __func__); \
> + fprintf(stderr, ## __VA_ARGS__); \
> + } while (0);
> +#else
> + #define DB_PRINT(...)
> +#endif
> +
> +
> +/* Controller registers */
> +
> +#define SDHCI_DMA_ADDRESS 0x00
> +
> +#define SDHCI_BLOCK_SIZE 0x04
> +
> +#define SDHCI_BLOCK_COUNT 0x06
> +
> +#define SDHCI_ARGUMENT 0x08
> +
> +#define SDHCI_TRANSFER_MODE 0x0C
> +#define SDHCI_TRNS_DMA 0x01
> +#define SDHCI_TRNS_BLK_CNT_EN 0x02
> +#define SDHCI_TRNS_ACMD12 0x04
> +#define SDHCI_TRNS_READ 0x10
> +#define SDHCI_TRNS_MULTI 0x20
> +
> +#define SDHCI_COMMAND 0x0E
> +#define SDHCI_CMD_RESP_MASK 0x03
> +#define SDHCI_CMD_CRC 0x08
> +#define SDHCI_CMD_INDEX 0x10
> +#define SDHCI_CMD_DATA 0x20
> +
> +#define SDHCI_CMD_RESP_NONE 0x00
> +#define SDHCI_CMD_RESP_LONG 0x01
> +#define SDHCI_CMD_RESP_SHORT 0x02
> +#define SDHCI_CMD_RESP_SHORT_BUSY 0x03
> +
> +#define SDHCI_RESPONSE 0x10
> +
> +#define SDHCI_BUFFER 0x20
> +
> +#define SDHCI_PRESENT_STATE 0x24
> +#define SDHCI_CMD_INHIBIT 0x00000001
> +#define SDHCI_DATA_INHIBIT 0x00000002
> +#define SDHCI_DOING_WRITE 0x00000100
> +#define SDHCI_DOING_READ 0x00000200
> +#define SDHCI_SPACE_AVAILABLE 0x00000400
> +#define SDHCI_DATA_AVAILABLE 0x00000800
> +#define SDHCI_CARD_PRESENT 0x00010000
> +#define SDHCI_WRITE_PROTECT 0x00080000
> +
> +#define SDHCI_HOST_CONTROL 0x28
> +#define SDHCI_CTRL_LED 0x01
> +#define SDHCI_CTRL_4BITBUS 0x02
> +#define SDHCI_CTRL_HISPD 0x04
> +#define SDHCI_CTRL_DMA_MASK 0x18
> +#define SDHCI_CTRL_SDMA 0x00
> +#define SDHCI_CTRL_ADMA1 0x08
> +#define SDHCI_CTRL_ADMA32 0x10
> +#define SDHCI_CTRL_ADMA64 0x18
> +#define SDHCI_CTRL_8BITBUS 0x20
> +
> +#define SDHCI_POWER_CONTROL 0x29
> +#define SDHCI_POWER_ON 0x01
> +#define SDHCI_POWER_180 0x0A
> +#define SDHCI_POWER_300 0x0C
> +#define SDHCI_POWER_330 0x0E
> +
> +#define SDHCI_BLOCK_GAP_CONTROL 0x2A
> +
> +#define SDHCI_WAKE_UP_CONTROL 0x2B
> +#define SDHCI_WAKE_ON_INT 0x01
> +#define SDHCI_WAKE_ON_INSERT 0x02
> +#define SDHCI_WAKE_ON_REMOVE 0x04
> +
> +#define SDHCI_CLOCK_CONTROL 0x2C
> +#define SDHCI_DIVIDER_SHIFT 8
> +#define SDHCI_DIVIDER_HI_SHIFT 6
> +#define SDHCI_DIV_MASK 0xFF
> +#define SDHCI_DIV_MASK_LEN 8
> +#define SDHCI_DIV_HI_MASK 0x300
> +#define SDHCI_CLOCK_CARD_EN 0x0004
> +#define SDHCI_CLOCK_INT_STABLE 0x0002
> +#define SDHCI_CLOCK_INT_EN 0x0001
> +
> +#define SDHCI_TIMEOUT_CONTROL 0x2E
> +
> +#define SDHCI_SOFTWARE_RESET 0x2F
> +#define SDHCI_RESET_ALL 0x01
> +#define SDHCI_RESET_CMD 0x02
> +#define SDHCI_RESET_DATA 0x04
> +
> +#define SDHCI_INT_STATUS 0x30
> +#define SDHCI_INT_ENABLE 0x34
> +#define SDHCI_SIGNAL_ENABLE 0x38
> +#define SDHCI_INT_RESPONSE 0x00000001
> +#define SDHCI_INT_DATA_END 0x00000002
> +#define SDHCI_INT_DMA_END 0x00000008
> +#define SDHCI_INT_SPACE_AVAIL 0x00000010
> +#define SDHCI_INT_DATA_AVAIL 0x00000020
> +#define SDHCI_INT_CARD_INSERT 0x00000040
> +#define SDHCI_INT_CARD_REMOVE 0x00000080
> +#define SDHCI_INT_CARD_INT 0x00000100
> +#define SDHCI_INT_ERROR 0x00008000
> +#define SDHCI_INT_TIMEOUT 0x00010000
> +#define SDHCI_INT_CRC 0x00020000
> +#define SDHCI_INT_END_BIT 0x00040000
> +#define SDHCI_INT_INDEX 0x00080000
> +#define SDHCI_INT_DATA_TIMEOUT 0x00100000
> +#define SDHCI_INT_DATA_CRC 0x00200000
> +#define SDHCI_INT_DATA_END_BIT 0x00400000
> +#define SDHCI_INT_BUS_POWER 0x00800000
> +#define SDHCI_INT_ACMD12ERR 0x01000000
> +#define SDHCI_INT_ADMA_ERROR 0x02000000
> +
> +#define SDHCI_INT_NORMAL_MASK 0x00007FFF
> +#define SDHCI_INT_ERROR_MASK 0xFFFF8000
> +
> +#define SDHCI_ACMD12_ERR 0x3C
> +
> +/* 3E-3F reserved */
> +
> +#define SDHCI_CAPABILITIES 0x40
> +#define SDHCI_TIMEOUT_CLK_MASK 0x0000003F
> +#define SDHCI_TIMEOUT_CLK_SHIFT 0
> +#define SDHCI_TIMEOUT_CLK_UNIT 0x00000080
> +#define SDHCI_CLOCK_BASE_MASK 0x00003F00
> +#define SDHCI_CLOCK_V3_BASE_MASK 0x0000FF00
> +#define SDHCI_CLOCK_BASE_SHIFT 8
> +#define SDHCI_MAX_BLOCK_MASK 0x00030000
> +#define SDHCI_MAX_BLOCK_SHIFT 16
> +#define SDHCI_CAN_DO_8BIT 0x00040000
> +#define SDHCI_CAN_DO_ADMA2 0x00080000
> +#define SDHCI_CAN_DO_ADMA1 0x00100000
> +#define SDHCI_CAN_DO_HISPD 0x00200000
> +#define SDHCI_CAN_DO_SDMA 0x00400000
> +#define SDHCI_CAN_VDD_330 0x01000000
> +#define SDHCI_CAN_VDD_300 0x02000000
> +#define SDHCI_CAN_VDD_180 0x04000000
> +#define SDHCI_CAN_64BIT 0x10000000
> +#define SDHCI_SLOT_TYPE_EMBED 0x40000000
> +
> +#define SDHCI_CAPABILITIES_1 0x44
> +
> +/* 44-47 reserved for more caps */
> +
> +#define SDHCI_MAX_CURRENT 0x48
> +
> +/* 4C-4F reserved for more max current */
> +
> +#define SDHCI_SET_ACMD12_ERROR 0x50
> +#define SDHCI_SET_INT_ERROR 0x52
> +
> +#define SDHCI_ADMA_ERROR 0x54
> +
> +/* 55-57 reserved */
> +
> +#define SDHCI_ADMA_ADDRESS 0x58
> +
> +/* 60-FB reserved */
> +
> +#define SDHCI_SLOT_INT_STATUS 0xFC
> +
> +#define SDHCI_HOST_VERSION 0xFE
> +#define SDHCI_VENDOR_VER_MASK 0xFF00
> +#define SDHCI_VENDOR_VER_SHIFT 8
> +#define SDHCI_SPEC_VER_MASK 0x00FF
> +#define SDHCI_SPEC_VER_SHIFT 0
> +#define SDHCI_SPEC_100 0
> +#define SDHCI_SPEC_200 1
> +#define SDHCI_SPEC_300 2
> +
> +/* ADMA descriptor flags */
> +#define ADMA_VALID (1<<0)
> +#define ADMA_END (1<<1)
> +#define ADMA_INT (1<<2)
> +
> +#define ADMA_OP_MASK (3<<4)
> +#define ADMA_OP_NOP (0<<4)
> +#define ADMA_OP_SET (1<<4)
> +#define ADMA_OP_TRAN (2<<4)
> +#define ADMA_OP_LINK (3<<4)
> +
> +/* re-write a part of a variable using a mask
> + * to emulate the "byte-enable" behaviour in hardware
> + */
> +#define MASKED_WRITE(var, val, mask) do {\
> + var = (var & ~(mask)) | ((val) & (mask));\
> + } while (0)
> +
> +typedef struct {
> + SysBusDevice busdev;
> + MemoryRegion iomem;
> + BlockDriverState *bs;
> + uint32_t sdma_address;
> + uint16_t block_size;
> + uint16_t block_count;
> + uint32_t arg;
> + uint16_t transfer_mode;
> + uint32_t response[4];
> + uint32_t clock;
> + uint32_t host_control;
> + uint32_t present_state;
> + uint32_t int_enable;
> + uint32_t int_status;
> + uint32_t adma_address;
> + uint8_t adma_error;
> + SDState *sd;
> + qemu_irq irq;
> + uint32_t pio_btt;
> +} SDHCI_State;
> +
> +static void sdhci_reset(DeviceState *d)
> +{
> + SDHCI_State *s = container_of(d, SDHCI_State, busdev.qdev);
> +
> + if (!s->bs) {
> + DriveInfo *inf = drive_get_next(IF_SD);
> + s->bs = inf ? inf->bdrv : NULL;
> + }
> + if (!s->sd && s->bs) {
> + s->sd = sd_init(s->bs, 0);
> + }
> +
> + s->sdma_address = 0;
> + s->block_size = 0;
> + s->block_count = 0;
> + s->arg = 0;
> + s->transfer_mode = 0;
> + s->clock = SDHCI_CLOCK_INT_STABLE;
> + s->int_enable = 0;
> + s->int_status = 0;
> + s->adma_address = 0;
> + s->adma_error = 0;
> +}
> +
> +static void sdhci_set_irq(SDHCI_State *s)
> +{
> + qemu_set_irq(s->irq, !!(s->int_status & s->int_enable));
> +}
> +
> +static void sdhci_end_of_transfer(SDHCI_State *s)
> +{
> + DB_PRINT("\n");
> + SDRequest request;
> + uint8_t r[16];
> + request.cmd = 12;
> + request.arg = 0;
> + if (s->transfer_mode & SDHCI_TRNS_ACMD12) {
> + sd_do_command(s->sd, &request, r);
> + }
> + s->present_state &= ~(SDHCI_DOING_READ | SDHCI_DOING_WRITE);
> + s->int_status |= SDHCI_INT_DATA_END;
> + sdhci_set_irq(s);
> +}
> +
> +static void pio_end_of_block(SDHCI_State *s)
> +{
> + DB_PRINT("\n");
> + if (!(s->transfer_mode & SDHCI_TRNS_MULTI)) {
> + sdhci_end_of_transfer(s);
> + return;
> + }
> +
> + if ((s->transfer_mode & SDHCI_TRNS_BLK_CNT_EN)) {
> + if (!s->block_count) {
> + sdhci_end_of_transfer(s);
> + return;
> + }
> + s->block_count--;
> + }
> + s->pio_btt = s->block_size;
> + s->int_status |=
> + (s->present_state & SDHCI_DOING_READ) ? SDHCI_INT_DATA_AVAIL : 0;
> + s->int_status |=
> + (s->present_state & SDHCI_DOING_WRITE) ? SDHCI_INT_SPACE_AVAIL :
> 0;
> +}
> +
> +static uint32_t do_pio_read(SDHCI_State *s)
> +{
> + int i;
> + uint32_t ret = 0;
> + if (!(s->present_state & SDHCI_DOING_READ)) {
> + DB_PRINT("reading pio data register in non-write state");
> + return 0;
> + }
> + for (i = 0; i < 4; ++i) {
> + ret = (ret >> 8) | ((uint32_t)sd_read_data(s->sd) << 24);
> + }
> + s->pio_btt -= 4;
> + if (!s->pio_btt) {
> + pio_end_of_block(s);
> + }
> + return ret;
> +}
> +
> +static void do_pio_write(SDHCI_State *s, uint32_t value)
> +{
> + int i;
> + DB_PRINT("btt:%x\n", s->pio_btt);
> + if (!(s->present_state & SDHCI_DOING_WRITE)) {
> + DB_PRINT("writing pio data register in non-write state");
> + return;
> + }
> + for (i = 0; i < 4; ++i) {
> + sd_write_data(s->sd, (value >> (i*8)) & 0xff);
> + }
> + s->pio_btt -= 4;
> + if (!s->pio_btt) {
> + pio_end_of_block(s);
> + }
> +}
> +
> +static void sdhci_pio_transfer(SDHCI_State *s)
> +{
> + DB_PRINT("\n");
> + s->present_state |= (s->transfer_mode & SDHCI_TRNS_READ) ?
> + SDHCI_DOING_READ : SDHCI_DOING_WRITE;
> + pio_end_of_block(s);
> +}
> +
> +
> +struct adma_desc {
> + uint16_t flags;
> + uint16_t size;
> + uint32_t addr;
> +};
> +
> +static int sdhci_get_adma_desc(SDHCI_State *s, struct adma_desc *ret,
> + uint32_t adma1_size)
> +{
> + if (s->host_control & SDHCI_CTRL_ADMA32) { /* ADMA2 */
> + cpu_physical_memory_read(s->adma_address, (void *)ret, sizeof(*ret));
> + return sizeof(*ret);
> + } else { /* ADMA1 */
> + uint32_t adma1;
> + cpu_physical_memory_read(s->adma_address, (void *)&adma1,
> + sizeof(adma1));
> + ret->addr = adma1 & 0xfffff000;
> + ret->size = adma1_size;
> + ret->flags = adma1 & 0x3f;
> + return sizeof(adma1);
> + }
> +}
> +
> +static void sdhci_dma_transfer(SDHCI_State *s)
> +{
> + int b;
> + uint16_t xfer_size;
> +
> + uint32_t total_size, remaining_size;
> +
> + struct adma_desc desc;
> + int adma_stride;
> + s->adma_error = 0;
> + uint32_t adma1_size = (4 << 10);
> +
> + DB_PRINT("");
> +
> + if (s->host_control & SDHCI_CTRL_DMA_MASK) {
> + adma_stride = sdhci_get_adma_desc(s, &desc, adma1_size);
> + } else { /* use SDMA */
> + /* Generate hardcoded descriptor to emulate ADMA */
> + desc.addr = s->sdma_address;
> + desc.size = s->block_count * (s->block_size & 0xfff);
> + desc.flags = ADMA_VALID | ADMA_END | ADMA_OP_TRAN;
> + }
> + /* 0 in the size field means 65536 bytes */
> + remaining_size = desc.size ? desc.size : 65536;
> +
> + s->int_status |= SDHCI_INT_DATA_END;
> +
> + total_size = s->block_count*(s->block_size & 0xfff);
> + while (total_size) {
> + /* if the descriptor is invalid OR the desciptor is done OR the
> + * descriptor is not a transfer operation ...
> + */
> + while (!(desc.flags & ADMA_VALID) || !remaining_size ||
> + ((desc.flags & ADMA_OP_MASK) != ADMA_OP_TRAN)) {
> + if ((desc.flags & ADMA_END) || !(desc.flags & ADMA_VALID)) {
> + /* Abort ADMA transfer */
> + s->int_status |= SDHCI_INT_ADMA_ERROR;
> + s->adma_error = 1 /* ST_FDS */;
> + s->block_count = total_size / s->block_size;
> + return;
> + }
> + /* ADMA operation */
> + if ((desc.flags & ADMA_OP_MASK) == ADMA_OP_SET) {
> + adma1_size = (desc.addr >> 12) & 0xFFFF;
> + }
> + if ((desc.flags & ADMA_OP_MASK) == ADMA_OP_LINK) {
> + s->adma_address = desc.addr;
> + } else {
> + s->adma_address += adma_stride;
> + }
> + adma_stride = sdhci_get_adma_desc(s, &desc, adma1_size);
> + /* 0 in the size field means 65536 bytes */
> + remaining_size = desc.size ? desc.size : 65536;
> + }
> +
> + xfer_size = MIN((s->block_size & 0xfff), remaining_size);
> + for (b = 0; b < xfer_size; b++, desc.addr++) {
> + if (s->transfer_mode & SDHCI_TRNS_READ) {
> + uint8_t data = sd_read_data(s->sd);
> + cpu_physical_memory_write(desc.addr, &data, 1);
> + } else {
> + uint8_t data;
> + cpu_physical_memory_read(desc.addr, &data, 1);
> + sd_write_data(s->sd, data);
> + }
> + }
> + remaining_size -= xfer_size;
> + total_size -= xfer_size;
> + }
> + sdhci_end_of_transfer(s);
> + s->block_count = 0;
> +}
> +
> +static void sdhci_command(SDHCI_State *s, uint32_t cmd)
> +{
> + SDRequest request;
> + int len;
> + uint8_t r[16];
> +
> + DB_PRINT("SDHCI command %08x\n", cmd);
> + if (!s->sd) { /* no SD device attached to the controller */
> + s->int_status |= SDHCI_INT_TIMEOUT;
> + sdhci_set_irq(s);
> + return;
> + }
> +
> + request.cmd = (cmd>>8) & 0xff;
> + request.arg = s->arg;
> + len = sd_do_command(s->sd, &request, r);
> + if (len == 0) {
> + if (cmd & SDHCI_CMD_RESP_MASK) {
> + /* no response expected */
> + s->int_status |= SDHCI_INT_INDEX;
> + DB_PRINT("no response expected %08x\n", cmd);
> + } else {
> + /* error */
> + s->int_status |= SDHCI_INT_RESPONSE;
> + DB_PRINT("error response %08x\n", cmd);
> + }
> + } else {
> + DB_PRINT("actual response %08x\n", cmd);
> + if (len == 4) {
> + s->response[0] = (r[0]<<24) | (r[1]<<16) | (r[2]<<8) | r[3];
> + } else if (len == 16) {
> + s->response[0] = (r[11]<<24) | (r[12]<<16) | (r[13]<<8) | r[14];
> + s->response[1] = (r[7]<<24) | (r[8]<<16) | (r[9]<<8) | r[10];
> + s->response[2] = (r[3]<<24) | (r[4]<<16) | (r[5]<<8) | r[6];
> + s->response[3] = (0xcc<<24) | (r[0]<<16) | (r[1]<<8) | r[2];
> + }
> + s->int_status |= SDHCI_INT_RESPONSE;
> + if ((cmd & 3) == SDHCI_CMD_RESP_SHORT_BUSY) {
> + /* the command will trigger the busy (DAT[0]) line ON then OFF
> + * this will raise the Data END interrupt when done.
> + */
> + DB_PRINT("raising completion interrupt %08x\n", cmd);
> + s->int_status |= SDHCI_INT_DATA_END;
> + }
> + if (cmd & SDHCI_CMD_DATA) {
> + if (s->transfer_mode & SDHCI_TRNS_DMA) {
> + sdhci_dma_transfer(s);
> + } else {
> + sdhci_pio_transfer(s);
> + }
> + }
> + }
> + sdhci_set_irq(s);
> +}
> +
> +static uint32_t sdhci_read32i(void *opaque, target_phys_addr_t offset)
> +{
> + SDHCI_State *s = opaque;
> +
> + if ((offset >= 0x100) && (offset < 0x200)) {
> + fprintf(stderr, "sdhci: unsupported vendor read @" TARGET_FMT_plx
> "\n",
> + offset);
> + return 0;
> + }
> +
> + switch (offset) {
> + case SDHCI_DMA_ADDRESS:
> + return s->sdma_address;
> + case SDHCI_BLOCK_SIZE /* +SDHCI_BLOCK_COUNT */:
> + return s->block_size | (s->block_count << 16);
> + case SDHCI_ARGUMENT:
> + return s->arg;
> + case SDHCI_TRANSFER_MODE /* +SDHCI_COMMAND */:
> + return s->transfer_mode;
> + case SDHCI_RESPONSE:
> + case SDHCI_RESPONSE+4:
> + case SDHCI_RESPONSE+8:
> + case SDHCI_RESPONSE+12:
> + return s->response[(offset-SDHCI_RESPONSE)/sizeof(uint32_t)];
> + case SDHCI_BUFFER:
> + return do_pio_read(s);
> + case SDHCI_PRESENT_STATE:
> + return s->present_state | (s->sd ? 0x00170000 : 0) |
> + (s->bs && bdrv_is_read_only(s->bs) ? 0 : 0x00080000);
> + case SDHCI_HOST_CONTROL:
> + /*+SDHCI_POWER_CONTROL +SDHCI_BLOCK_GAP_CONTROL
> +SDHCI_WAKE_UP_CONTROL*/
> + return s->host_control;
> + case SDHCI_CLOCK_CONTROL /* +SDHCI_TIMEOUT_CONTROL +SDHCI_SOFTWARE_RESET
> */:
> + return s->clock | (0<<24 /* reset done */);
> + case SDHCI_INT_STATUS:
> + return s->int_status;
> + case SDHCI_INT_ENABLE:
> + return s->int_enable;
> + case SDHCI_SIGNAL_ENABLE:
> + return 0;
> + case SDHCI_ACMD12_ERR:
> + return 0;
> + case SDHCI_CAPABILITIES:
> + return SDHCI_SLOT_TYPE_EMBED | SDHCI_CAN_VDD_330 |
> + SDHCI_CAN_DO_ADMA1 | SDHCI_CAN_DO_HISPD | SDHCI_CAN_DO_SDMA |
> + SDHCI_CAN_DO_8BIT | SDHCI_CAN_DO_ADMA2 |
> + (52 << SDHCI_CLOCK_BASE_SHIFT) |
> + SDHCI_TIMEOUT_CLK_UNIT /* MHz */ |
> (52<<SDHCI_TIMEOUT_CLK_SHIFT);
> + case SDHCI_CAPABILITIES_1:
> + return 0;
> + case SDHCI_MAX_CURRENT:
> + return 0;
> + case SDHCI_SET_ACMD12_ERROR /* +SDHCI_SET_INT_ERROR */:
> + hw_error("sdhci_read: read only register at " TARGET_FMT_plx "\n",
> + offset);
> + case SDHCI_ADMA_ERROR:
> + return s->adma_error;
> + case SDHCI_ADMA_ADDRESS:
> + return s->adma_address;
> + case SDHCI_SLOT_INT_STATUS /* +SDHCI_HOST_VERSION */:
> + return 0 | (SDHCI_SPEC_200 << 16);
> + default:
> + hw_error("sdhci_read: Bad offset " TARGET_FMT_plx "\n", offset);
> + }
> +
> + return 0;
> +}
> +
> +static uint64_t sdhci_read(void *opaque, target_phys_addr_t offset,
> + unsigned size)
> +{
> + uint32_t ret = sdhci_read32i(opaque, offset & ~0x3);
> + ret >>= (offset & 0x3) * 8;
> + ret &= ~(-1ULL << (size * 8));
> + DB_PRINT("addr=" TARGET_FMT_plx " = %x\n", offset, ret);
> + return ret;
> +}
> +
> +static void sdhci_write_masked(void *opaque, target_phys_addr_t offset,
> + uint32_t value, uint32_t mask)
> +{
> + DB_PRINT("addr=" TARGET_FMT_plx " = %x, mask = %08x\n", offset, value,
> + mask);
> + SDHCI_State *s = opaque;
> +
> + if ((offset >= 0x100) && (offset < 0x200)) {
> + DB_PRINT("unsupported vendor write");
> + return;
> + }
> +
> + switch (offset) {
> + case SDHCI_DMA_ADDRESS:
> + MASKED_WRITE(s->sdma_address, value, mask);
> + break;
> + case SDHCI_BLOCK_SIZE /* +SDHCI_BLOCK_COUNT */:
> + MASKED_WRITE(s->block_size, value & 0x7fff, mask);
> + MASKED_WRITE(s->block_count, value >> 16, mask >> 16);
> + break;
> + case SDHCI_ARGUMENT:
> + MASKED_WRITE(s->arg, value, mask);
> + break;
> + case SDHCI_TRANSFER_MODE /* +SDHCI_COMMAND */:
> + MASKED_WRITE(s->transfer_mode, value & 0x3f, mask);
> + if (mask & 0xffff0000) {
> + sdhci_command(s, value >> 16);
> + }
> + break;
> + case SDHCI_BUFFER:
> + do_pio_write(s, value);
> + break;
> + case SDHCI_HOST_CONTROL:
> + /*+SDHCI_POWER_CONTROL +SDHCI_BLOCK_GAP_CONTROL
> +SDHCI_WAKE_UP_CONTROL*/
> + MASKED_WRITE(s->host_control, value, mask);
> + if ((mask >> 8) & SDHCI_POWER_ON) {
> + if (s->sd) {
> + sd_enable(s->sd, ((value & mask) >> 8) & SDHCI_POWER_ON);
> + }
> + }
> + break;
> + case SDHCI_CLOCK_CONTROL /* +SDHCI_TIMEOUT_CONTROL +SDHCI_SOFTWARE_RESET
> */:
> + if (((value & mask) >> 24) & SDHCI_RESET_ALL) {
> + sdhci_reset(opaque);
> + }
> + if (((value & mask) >> 24) & SDHCI_RESET_CMD) {
> + s->int_status &= ~SDHCI_INT_RESPONSE;
> + sdhci_set_irq(s);
> + }
> + if (((value & mask) >> 24) & SDHCI_RESET_DATA) {
> + s->adma_error = 0;
> + s->int_status &= ~(SDHCI_INT_DATA_END | SDHCI_INT_DMA_END);
> + sdhci_set_irq(s);
> + }
> + MASKED_WRITE(s->clock, (value & 0xfffff) | SDHCI_CLOCK_INT_STABLE,
> + mask);
> + break;
> + case SDHCI_INT_STATUS:
> + s->int_status &= ~(value & mask);
> + sdhci_set_irq(s);
> + break;
> + case SDHCI_INT_ENABLE:
> + MASKED_WRITE(s->int_enable, value, mask);
> + sdhci_set_irq(s);
> + break;
> + case SDHCI_SIGNAL_ENABLE:
> + break;
> + case SDHCI_SET_ACMD12_ERROR /* +SDHCI_SET_INT_ERROR */:
> + break;
> + case SDHCI_ADMA_ADDRESS:
> + MASKED_WRITE(s->adma_address, value, mask);
> + break;
> + case SDHCI_SLOT_INT_STATUS /* +SDHCI_HOST_VERSION */:
> + break;
> + /* Read only registers */
> + case SDHCI_RESPONSE:
> + case SDHCI_RESPONSE+4:
> + case SDHCI_RESPONSE+8:
> + case SDHCI_RESPONSE+12:
> + case SDHCI_PRESENT_STATE:
> + case SDHCI_ACMD12_ERR:
> + case SDHCI_CAPABILITIES:
> + case SDHCI_CAPABILITIES_1:
> + case SDHCI_MAX_CURRENT:
> + case SDHCI_ADMA_ERROR:
> + DB_PRINT("Ignored write to read only register");
> + break;
> + default:
> + hw_error("sdhci_write: Bad offset " TARGET_FMT_plx "\n", offset);
> + }
> +}
> +
> +static void sdhci_write(void *opaque, target_phys_addr_t offset,
> + uint64_t value, unsigned size)
> +{
> + uint32_t mask = (1ULL << (size * 8)) - 1;
> + int shift = 8 * (offset & 0x3);
> + sdhci_write_masked(opaque, offset & ~0x3, value << shift, mask << shift);
> +}
> +
> +static const MemoryRegionOps sdhci_ops = {
> + .read = sdhci_read,
> + .write = sdhci_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static const VMStateDescription sdhci_vmstate = {
> + .name = "sdhci",
> + .version_id = 0,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT32(sdma_address, SDHCI_State),
> + VMSTATE_UINT16(block_size, SDHCI_State),
> + VMSTATE_UINT16(block_count, SDHCI_State),
> + VMSTATE_UINT32(arg, SDHCI_State),
> + VMSTATE_UINT16(transfer_mode, SDHCI_State),
> + VMSTATE_UINT32_ARRAY(response, SDHCI_State, 4),
> + VMSTATE_UINT32(clock, SDHCI_State),
> + VMSTATE_UINT32(int_enable, SDHCI_State),
> + VMSTATE_UINT32(int_status, SDHCI_State),
> + VMSTATE_UINT32(adma_address, SDHCI_State),
> + VMSTATE_UINT8(adma_error, SDHCI_State),
> + VMSTATE_END_OF_LIST()
> + }
> +};
> +
> +static int sdhci_sysbus_init(SysBusDevice *dev)
> +{
> + SDHCI_State *s = FROM_SYSBUS(SDHCI_State, dev);
> +
> + if (s->bs) {
> + bdrv_detach_dev(s->bs, s);
> + }
> + sysbus_init_irq(dev, &s->irq);
> + memory_region_init_io(&s->iomem, &sdhci_ops, s, "sdhci", 0x200);
> + sysbus_init_mmio(dev, &s->iomem);
> +
> + return 0;
> +}
> +
> +static Property sdhci_properties[] = {
> + DEFINE_PROP_DRIVE("block", SDHCI_State, bs),
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void sdhci_sysbus_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
> +
> + sdc->init = sdhci_sysbus_init;
> + dc->props = sdhci_properties;
> + dc->vmsd = &sdhci_vmstate;
> + dc->reset = sdhci_reset;
> +}
> +
> +static TypeInfo sdhci_sysbus_info = {
> + .name = "sysbus_sdhci",
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(SDHCI_State),
> + .class_init = sdhci_sysbus_class_init,
> +};
> +
> +static void sdhci_register_types(void)
> +{
> + type_register_static(&sdhci_sysbus_info);
> +}
> +
> +type_init(sdhci_register_types)
> --
> 1.7.3.2
>
--
12345678901234567890123456789012345678901234567890123456789012345678901234567890
1 2 3 4 5 6 7 8
- [Qemu-devel] [PATCH v1 0/2] SDHCI for Xilinx Zynq, Peter A. G. Crosthwaite, 2012/04/02
- [Qemu-devel] [PATCH v1 1/2] SDHCI: inital version, Peter A. G. Crosthwaite, 2012/04/02
- Re: [Qemu-devel] [PATCH v1 1/2] SDHCI: inital version,
Peter Maydell <=
- Re: [Qemu-devel] [PATCH v1 1/2] SDHCI: inital version, Andreas Färber, 2012/04/02
- Re: [Qemu-devel] [PATCH v1 1/2] SDHCI: inital version, Igor Mitsyanko, 2012/04/02
- Re: [Qemu-devel] [PATCH v1 1/2] SDHCI: inital version, Peter Crosthwaite, 2012/04/02
- Re: [Qemu-devel] [PATCH v1 1/2] SDHCI: inital version, Igor Mitsyanko, 2012/04/02
- Re: [Qemu-devel] [PATCH v1 1/2] SDHCI: inital version, Peter Crosthwaite, 2012/04/02
- Re: [Qemu-devel] [PATCH v1 1/2] SDHCI: inital version, Igor Mitsyanko, 2012/04/02
- Re: [Qemu-devel] [PATCH v1 1/2] SDHCI: inital version, Peter Maydell, 2012/04/02
- Re: [Qemu-devel] [PATCH v1 1/2] SDHCI: inital version, Igor Mitsyanko, 2012/04/02
- Re: [Qemu-devel] [PATCH v1 1/2] SDHCI: inital version, Peter Crosthwaite, 2012/04/02
[Qemu-devel] [PATCH v1 2/2] xilinx_zynq: added sdhci controller, Peter A. G. Crosthwaite, 2012/04/02