qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH v1 1/2] SDHCI: inital version


From: Peter Crosthwaite
Subject: Re: [Qemu-devel] [PATCH v1 1/2] SDHCI: inital version
Date: Mon, 2 Apr 2012 20:40:13 +1000

On Mon, Apr 2, 2012 at 5:20 PM, Peter Maydell <address@hidden> wrote:
> 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.
>

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 <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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]