On Mon, Apr 2, 2012 at 5:20 PM, Peter Maydell <peter.mayd...@linaro.org> wrote: > On 2 April 2012 07:24, Peter A. G. Crosthwaite > <peter.crosthwa...@petalogix.com> wrote: >> device more for standard SD host controller interface (SDHCI). >> >> Signed-off-by: Peter A. G. Crosthwaite <peter.crosthwa...@petalogix.com> > > 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. >
I implemented programmed io (PIO) support, Vincents original code was DMA datapath only. I also upgraded the ADMA support such that it can handle both ADMA1 and ADMA2. The original only supported ADMA2. > -- 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 >> <peter.crosthwa...@petalogix.com> >> + * 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