143ddc182SClement Deschamps /* 243ddc182SClement Deschamps * Raspberry Pi (BCM2835) SD Host Controller 343ddc182SClement Deschamps * 443ddc182SClement Deschamps * Copyright (c) 2017 Antfield SAS 543ddc182SClement Deschamps * 643ddc182SClement Deschamps * Authors: 743ddc182SClement Deschamps * Clement Deschamps <clement.deschamps@antfield.fr> 843ddc182SClement Deschamps * Luc Michel <luc.michel@antfield.fr> 943ddc182SClement Deschamps * 1043ddc182SClement Deschamps * This work is licensed under the terms of the GNU GPL, version 2 or later. 1143ddc182SClement Deschamps * See the COPYING file in the top-level directory. 1243ddc182SClement Deschamps */ 1343ddc182SClement Deschamps 1443ddc182SClement Deschamps #include "qemu/osdep.h" 1543ddc182SClement Deschamps #include "qemu/log.h" 16*0b8fa32fSMarkus Armbruster #include "qemu/module.h" 1743ddc182SClement Deschamps #include "sysemu/blockdev.h" 1843ddc182SClement Deschamps #include "hw/sd/bcm2835_sdhost.h" 19b318f326SPeter Maydell #include "trace.h" 2043ddc182SClement Deschamps 2143ddc182SClement Deschamps #define TYPE_BCM2835_SDHOST_BUS "bcm2835-sdhost-bus" 2243ddc182SClement Deschamps #define BCM2835_SDHOST_BUS(obj) \ 2343ddc182SClement Deschamps OBJECT_CHECK(SDBus, (obj), TYPE_BCM2835_SDHOST_BUS) 2443ddc182SClement Deschamps 2543ddc182SClement Deschamps #define SDCMD 0x00 /* Command to SD card - 16 R/W */ 2643ddc182SClement Deschamps #define SDARG 0x04 /* Argument to SD card - 32 R/W */ 2743ddc182SClement Deschamps #define SDTOUT 0x08 /* Start value for timeout counter - 32 R/W */ 2843ddc182SClement Deschamps #define SDCDIV 0x0c /* Start value for clock divider - 11 R/W */ 2943ddc182SClement Deschamps #define SDRSP0 0x10 /* SD card rsp (31:0) - 32 R */ 3043ddc182SClement Deschamps #define SDRSP1 0x14 /* SD card rsp (63:32) - 32 R */ 3143ddc182SClement Deschamps #define SDRSP2 0x18 /* SD card rsp (95:64) - 32 R */ 3243ddc182SClement Deschamps #define SDRSP3 0x1c /* SD card rsp (127:96) - 32 R */ 3343ddc182SClement Deschamps #define SDHSTS 0x20 /* SD host status - 11 R */ 3443ddc182SClement Deschamps #define SDVDD 0x30 /* SD card power control - 1 R/W */ 3543ddc182SClement Deschamps #define SDEDM 0x34 /* Emergency Debug Mode - 13 R/W */ 3643ddc182SClement Deschamps #define SDHCFG 0x38 /* Host configuration - 2 R/W */ 3743ddc182SClement Deschamps #define SDHBCT 0x3c /* Host byte count (debug) - 32 R/W */ 3843ddc182SClement Deschamps #define SDDATA 0x40 /* Data to/from SD card - 32 R/W */ 3943ddc182SClement Deschamps #define SDHBLC 0x50 /* Host block count (SDIO/SDHC) - 9 R/W */ 4043ddc182SClement Deschamps 4143ddc182SClement Deschamps #define SDCMD_NEW_FLAG 0x8000 4243ddc182SClement Deschamps #define SDCMD_FAIL_FLAG 0x4000 4343ddc182SClement Deschamps #define SDCMD_BUSYWAIT 0x800 4443ddc182SClement Deschamps #define SDCMD_NO_RESPONSE 0x400 4543ddc182SClement Deschamps #define SDCMD_LONG_RESPONSE 0x200 4643ddc182SClement Deschamps #define SDCMD_WRITE_CMD 0x80 4743ddc182SClement Deschamps #define SDCMD_READ_CMD 0x40 4843ddc182SClement Deschamps #define SDCMD_CMD_MASK 0x3f 4943ddc182SClement Deschamps 5043ddc182SClement Deschamps #define SDCDIV_MAX_CDIV 0x7ff 5143ddc182SClement Deschamps 5243ddc182SClement Deschamps #define SDHSTS_BUSY_IRPT 0x400 5343ddc182SClement Deschamps #define SDHSTS_BLOCK_IRPT 0x200 5443ddc182SClement Deschamps #define SDHSTS_SDIO_IRPT 0x100 5543ddc182SClement Deschamps #define SDHSTS_REW_TIME_OUT 0x80 5643ddc182SClement Deschamps #define SDHSTS_CMD_TIME_OUT 0x40 5743ddc182SClement Deschamps #define SDHSTS_CRC16_ERROR 0x20 5843ddc182SClement Deschamps #define SDHSTS_CRC7_ERROR 0x10 5943ddc182SClement Deschamps #define SDHSTS_FIFO_ERROR 0x08 6043ddc182SClement Deschamps /* Reserved */ 6143ddc182SClement Deschamps /* Reserved */ 6243ddc182SClement Deschamps #define SDHSTS_DATA_FLAG 0x01 6343ddc182SClement Deschamps 6443ddc182SClement Deschamps #define SDHCFG_BUSY_IRPT_EN (1 << 10) 6543ddc182SClement Deschamps #define SDHCFG_BLOCK_IRPT_EN (1 << 8) 6643ddc182SClement Deschamps #define SDHCFG_SDIO_IRPT_EN (1 << 5) 6743ddc182SClement Deschamps #define SDHCFG_DATA_IRPT_EN (1 << 4) 6843ddc182SClement Deschamps #define SDHCFG_SLOW_CARD (1 << 3) 6943ddc182SClement Deschamps #define SDHCFG_WIDE_EXT_BUS (1 << 2) 7043ddc182SClement Deschamps #define SDHCFG_WIDE_INT_BUS (1 << 1) 7143ddc182SClement Deschamps #define SDHCFG_REL_CMD_LINE (1 << 0) 7243ddc182SClement Deschamps 7343ddc182SClement Deschamps #define SDEDM_FORCE_DATA_MODE (1 << 19) 7443ddc182SClement Deschamps #define SDEDM_CLOCK_PULSE (1 << 20) 7543ddc182SClement Deschamps #define SDEDM_BYPASS (1 << 21) 7643ddc182SClement Deschamps 7743ddc182SClement Deschamps #define SDEDM_WRITE_THRESHOLD_SHIFT 9 7843ddc182SClement Deschamps #define SDEDM_READ_THRESHOLD_SHIFT 14 7943ddc182SClement Deschamps #define SDEDM_THRESHOLD_MASK 0x1f 8043ddc182SClement Deschamps 8143ddc182SClement Deschamps #define SDEDM_FSM_MASK 0xf 8243ddc182SClement Deschamps #define SDEDM_FSM_IDENTMODE 0x0 8343ddc182SClement Deschamps #define SDEDM_FSM_DATAMODE 0x1 8443ddc182SClement Deschamps #define SDEDM_FSM_READDATA 0x2 8543ddc182SClement Deschamps #define SDEDM_FSM_WRITEDATA 0x3 8643ddc182SClement Deschamps #define SDEDM_FSM_READWAIT 0x4 8743ddc182SClement Deschamps #define SDEDM_FSM_READCRC 0x5 8843ddc182SClement Deschamps #define SDEDM_FSM_WRITECRC 0x6 8943ddc182SClement Deschamps #define SDEDM_FSM_WRITEWAIT1 0x7 9043ddc182SClement Deschamps #define SDEDM_FSM_POWERDOWN 0x8 9143ddc182SClement Deschamps #define SDEDM_FSM_POWERUP 0x9 9243ddc182SClement Deschamps #define SDEDM_FSM_WRITESTART1 0xa 9343ddc182SClement Deschamps #define SDEDM_FSM_WRITESTART2 0xb 9443ddc182SClement Deschamps #define SDEDM_FSM_GENPULSES 0xc 9543ddc182SClement Deschamps #define SDEDM_FSM_WRITEWAIT2 0xd 9643ddc182SClement Deschamps #define SDEDM_FSM_STARTPOWDOWN 0xf 9743ddc182SClement Deschamps 9843ddc182SClement Deschamps #define SDDATA_FIFO_WORDS 16 9943ddc182SClement Deschamps 10043ddc182SClement Deschamps static void bcm2835_sdhost_update_irq(BCM2835SDHostState *s) 10143ddc182SClement Deschamps { 10243ddc182SClement Deschamps uint32_t irq = s->status & 10343ddc182SClement Deschamps (SDHSTS_BUSY_IRPT | SDHSTS_BLOCK_IRPT | SDHSTS_SDIO_IRPT); 104b318f326SPeter Maydell trace_bcm2835_sdhost_update_irq(irq); 10543ddc182SClement Deschamps qemu_set_irq(s->irq, !!irq); 10643ddc182SClement Deschamps } 10743ddc182SClement Deschamps 10843ddc182SClement Deschamps static void bcm2835_sdhost_send_command(BCM2835SDHostState *s) 10943ddc182SClement Deschamps { 11043ddc182SClement Deschamps SDRequest request; 11143ddc182SClement Deschamps uint8_t rsp[16]; 11243ddc182SClement Deschamps int rlen; 11343ddc182SClement Deschamps 11443ddc182SClement Deschamps request.cmd = s->cmd & SDCMD_CMD_MASK; 11543ddc182SClement Deschamps request.arg = s->cmdarg; 11643ddc182SClement Deschamps 11743ddc182SClement Deschamps rlen = sdbus_do_command(&s->sdbus, &request, rsp); 11843ddc182SClement Deschamps if (rlen < 0) { 11943ddc182SClement Deschamps goto error; 12043ddc182SClement Deschamps } 12143ddc182SClement Deschamps if (!(s->cmd & SDCMD_NO_RESPONSE)) { 12243ddc182SClement Deschamps if (rlen == 0 || (rlen == 4 && (s->cmd & SDCMD_LONG_RESPONSE))) { 12343ddc182SClement Deschamps goto error; 12443ddc182SClement Deschamps } 12543ddc182SClement Deschamps if (rlen != 4 && rlen != 16) { 12643ddc182SClement Deschamps goto error; 12743ddc182SClement Deschamps } 12843ddc182SClement Deschamps if (rlen == 4) { 129b3141c06SPhilippe Mathieu-Daudé s->rsp[0] = ldl_be_p(&rsp[0]); 13043ddc182SClement Deschamps s->rsp[1] = s->rsp[2] = s->rsp[3] = 0; 13143ddc182SClement Deschamps } else { 132b3141c06SPhilippe Mathieu-Daudé s->rsp[0] = ldl_be_p(&rsp[12]); 133b3141c06SPhilippe Mathieu-Daudé s->rsp[1] = ldl_be_p(&rsp[8]); 134b3141c06SPhilippe Mathieu-Daudé s->rsp[2] = ldl_be_p(&rsp[4]); 135b3141c06SPhilippe Mathieu-Daudé s->rsp[3] = ldl_be_p(&rsp[0]); 13643ddc182SClement Deschamps } 13743ddc182SClement Deschamps } 138f3d9fe8fSPeter Maydell /* We never really delay commands, so if this was a 'busywait' command 139f3d9fe8fSPeter Maydell * then we've completed it now and can raise the interrupt. 140f3d9fe8fSPeter Maydell */ 141f3d9fe8fSPeter Maydell if ((s->cmd & SDCMD_BUSYWAIT) && (s->config & SDHCFG_BUSY_IRPT_EN)) { 142f3d9fe8fSPeter Maydell s->status |= SDHSTS_BUSY_IRPT; 143f3d9fe8fSPeter Maydell } 14443ddc182SClement Deschamps return; 14543ddc182SClement Deschamps 14643ddc182SClement Deschamps error: 14743ddc182SClement Deschamps s->cmd |= SDCMD_FAIL_FLAG; 14843ddc182SClement Deschamps s->status |= SDHSTS_CMD_TIME_OUT; 14943ddc182SClement Deschamps } 15043ddc182SClement Deschamps 15143ddc182SClement Deschamps static void bcm2835_sdhost_fifo_push(BCM2835SDHostState *s, uint32_t value) 15243ddc182SClement Deschamps { 15343ddc182SClement Deschamps int n; 15443ddc182SClement Deschamps 15543ddc182SClement Deschamps if (s->fifo_len == BCM2835_SDHOST_FIFO_LEN) { 15643ddc182SClement Deschamps /* FIFO overflow */ 15743ddc182SClement Deschamps return; 15843ddc182SClement Deschamps } 15943ddc182SClement Deschamps n = (s->fifo_pos + s->fifo_len) & (BCM2835_SDHOST_FIFO_LEN - 1); 16043ddc182SClement Deschamps s->fifo_len++; 16143ddc182SClement Deschamps s->fifo[n] = value; 16243ddc182SClement Deschamps } 16343ddc182SClement Deschamps 16443ddc182SClement Deschamps static uint32_t bcm2835_sdhost_fifo_pop(BCM2835SDHostState *s) 16543ddc182SClement Deschamps { 16643ddc182SClement Deschamps uint32_t value; 16743ddc182SClement Deschamps 16843ddc182SClement Deschamps if (s->fifo_len == 0) { 16943ddc182SClement Deschamps /* FIFO underflow */ 17043ddc182SClement Deschamps return 0; 17143ddc182SClement Deschamps } 17243ddc182SClement Deschamps value = s->fifo[s->fifo_pos]; 17343ddc182SClement Deschamps s->fifo_len--; 17443ddc182SClement Deschamps s->fifo_pos = (s->fifo_pos + 1) & (BCM2835_SDHOST_FIFO_LEN - 1); 17543ddc182SClement Deschamps return value; 17643ddc182SClement Deschamps } 17743ddc182SClement Deschamps 17843ddc182SClement Deschamps static void bcm2835_sdhost_fifo_run(BCM2835SDHostState *s) 17943ddc182SClement Deschamps { 18043ddc182SClement Deschamps uint32_t value = 0; 18143ddc182SClement Deschamps int n; 18243ddc182SClement Deschamps int is_read; 18303a31776SGuenter Roeck int is_write; 18443ddc182SClement Deschamps 18543ddc182SClement Deschamps is_read = (s->cmd & SDCMD_READ_CMD) != 0; 18603a31776SGuenter Roeck is_write = (s->cmd & SDCMD_WRITE_CMD) != 0; 18703a31776SGuenter Roeck if (s->datacnt != 0 && (is_write || sdbus_data_ready(&s->sdbus))) { 18843ddc182SClement Deschamps if (is_read) { 18943ddc182SClement Deschamps n = 0; 19043ddc182SClement Deschamps while (s->datacnt && s->fifo_len < BCM2835_SDHOST_FIFO_LEN) { 19143ddc182SClement Deschamps value |= (uint32_t)sdbus_read_data(&s->sdbus) << (n * 8); 19243ddc182SClement Deschamps s->datacnt--; 19343ddc182SClement Deschamps n++; 19443ddc182SClement Deschamps if (n == 4) { 19543ddc182SClement Deschamps bcm2835_sdhost_fifo_push(s, value); 196f3d9fe8fSPeter Maydell s->status |= SDHSTS_DATA_FLAG; 197f3d9fe8fSPeter Maydell if (s->config & SDHCFG_DATA_IRPT_EN) { 198f3d9fe8fSPeter Maydell s->status |= SDHSTS_SDIO_IRPT; 199f3d9fe8fSPeter Maydell } 20043ddc182SClement Deschamps n = 0; 20143ddc182SClement Deschamps value = 0; 20243ddc182SClement Deschamps } 20343ddc182SClement Deschamps } 20443ddc182SClement Deschamps if (n != 0) { 20543ddc182SClement Deschamps bcm2835_sdhost_fifo_push(s, value); 206f3d9fe8fSPeter Maydell s->status |= SDHSTS_DATA_FLAG; 20703a31776SGuenter Roeck if (s->config & SDHCFG_DATA_IRPT_EN) { 20803a31776SGuenter Roeck s->status |= SDHSTS_SDIO_IRPT; 20943ddc182SClement Deschamps } 21003a31776SGuenter Roeck } 21103a31776SGuenter Roeck } else if (is_write) { /* write */ 21243ddc182SClement Deschamps n = 0; 21343ddc182SClement Deschamps while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) { 21443ddc182SClement Deschamps if (n == 0) { 21543ddc182SClement Deschamps value = bcm2835_sdhost_fifo_pop(s); 216f3d9fe8fSPeter Maydell s->status |= SDHSTS_DATA_FLAG; 217f3d9fe8fSPeter Maydell if (s->config & SDHCFG_DATA_IRPT_EN) { 218f3d9fe8fSPeter Maydell s->status |= SDHSTS_SDIO_IRPT; 219f3d9fe8fSPeter Maydell } 22043ddc182SClement Deschamps n = 4; 22143ddc182SClement Deschamps } 22243ddc182SClement Deschamps n--; 22343ddc182SClement Deschamps s->datacnt--; 22443ddc182SClement Deschamps sdbus_write_data(&s->sdbus, value & 0xff); 22543ddc182SClement Deschamps value >>= 8; 22643ddc182SClement Deschamps } 22743ddc182SClement Deschamps } 22843ddc182SClement Deschamps if (s->datacnt == 0) { 229f3d9fe8fSPeter Maydell s->edm &= ~SDEDM_FSM_MASK; 23043ddc182SClement Deschamps s->edm |= SDEDM_FSM_DATAMODE; 231b318f326SPeter Maydell trace_bcm2835_sdhost_edm_change("datacnt 0", s->edm); 23203a31776SGuenter Roeck } 23303a31776SGuenter Roeck if (is_write) { 23403a31776SGuenter Roeck /* set block interrupt at end of each block transfer */ 23503a31776SGuenter Roeck if (s->hbct && s->datacnt % s->hbct == 0 && 236f3d9fe8fSPeter Maydell (s->config & SDHCFG_BLOCK_IRPT_EN)) { 23743ddc182SClement Deschamps s->status |= SDHSTS_BLOCK_IRPT; 23843ddc182SClement Deschamps } 23903a31776SGuenter Roeck /* set data interrupt after each transfer */ 24003a31776SGuenter Roeck s->status |= SDHSTS_DATA_FLAG; 24103a31776SGuenter Roeck if (s->config & SDHCFG_DATA_IRPT_EN) { 24203a31776SGuenter Roeck s->status |= SDHSTS_SDIO_IRPT; 24303a31776SGuenter Roeck } 244f3d9fe8fSPeter Maydell } 245f3d9fe8fSPeter Maydell } 24643ddc182SClement Deschamps 24743ddc182SClement Deschamps bcm2835_sdhost_update_irq(s); 24843ddc182SClement Deschamps 24943ddc182SClement Deschamps s->edm &= ~(0x1f << 4); 25043ddc182SClement Deschamps s->edm |= ((s->fifo_len & 0x1f) << 4); 251b318f326SPeter Maydell trace_bcm2835_sdhost_edm_change("fifo run", s->edm); 25243ddc182SClement Deschamps } 25343ddc182SClement Deschamps 25443ddc182SClement Deschamps static uint64_t bcm2835_sdhost_read(void *opaque, hwaddr offset, 25543ddc182SClement Deschamps unsigned size) 25643ddc182SClement Deschamps { 25743ddc182SClement Deschamps BCM2835SDHostState *s = (BCM2835SDHostState *)opaque; 25843ddc182SClement Deschamps uint32_t res = 0; 25943ddc182SClement Deschamps 26043ddc182SClement Deschamps switch (offset) { 26143ddc182SClement Deschamps case SDCMD: 26243ddc182SClement Deschamps res = s->cmd; 26343ddc182SClement Deschamps break; 26443ddc182SClement Deschamps case SDHSTS: 26543ddc182SClement Deschamps res = s->status; 26643ddc182SClement Deschamps break; 26743ddc182SClement Deschamps case SDRSP0: 26843ddc182SClement Deschamps res = s->rsp[0]; 26943ddc182SClement Deschamps break; 27043ddc182SClement Deschamps case SDRSP1: 27143ddc182SClement Deschamps res = s->rsp[1]; 27243ddc182SClement Deschamps break; 27343ddc182SClement Deschamps case SDRSP2: 27443ddc182SClement Deschamps res = s->rsp[2]; 27543ddc182SClement Deschamps break; 27643ddc182SClement Deschamps case SDRSP3: 27743ddc182SClement Deschamps res = s->rsp[3]; 27843ddc182SClement Deschamps break; 27943ddc182SClement Deschamps case SDEDM: 28043ddc182SClement Deschamps res = s->edm; 28143ddc182SClement Deschamps break; 28243ddc182SClement Deschamps case SDVDD: 28343ddc182SClement Deschamps res = s->vdd; 28443ddc182SClement Deschamps break; 28543ddc182SClement Deschamps case SDDATA: 28643ddc182SClement Deschamps res = bcm2835_sdhost_fifo_pop(s); 28743ddc182SClement Deschamps bcm2835_sdhost_fifo_run(s); 28843ddc182SClement Deschamps break; 28943ddc182SClement Deschamps case SDHBCT: 29043ddc182SClement Deschamps res = s->hbct; 29143ddc182SClement Deschamps break; 29243ddc182SClement Deschamps case SDHBLC: 29343ddc182SClement Deschamps res = s->hblc; 29443ddc182SClement Deschamps break; 29543ddc182SClement Deschamps 29643ddc182SClement Deschamps default: 29743ddc182SClement Deschamps qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", 29843ddc182SClement Deschamps __func__, offset); 29943ddc182SClement Deschamps res = 0; 30043ddc182SClement Deschamps break; 30143ddc182SClement Deschamps } 30243ddc182SClement Deschamps 303b318f326SPeter Maydell trace_bcm2835_sdhost_read(offset, res, size); 304b318f326SPeter Maydell 30543ddc182SClement Deschamps return res; 30643ddc182SClement Deschamps } 30743ddc182SClement Deschamps 30843ddc182SClement Deschamps static void bcm2835_sdhost_write(void *opaque, hwaddr offset, 30943ddc182SClement Deschamps uint64_t value, unsigned size) 31043ddc182SClement Deschamps { 31143ddc182SClement Deschamps BCM2835SDHostState *s = (BCM2835SDHostState *)opaque; 31243ddc182SClement Deschamps 313b318f326SPeter Maydell trace_bcm2835_sdhost_write(offset, value, size); 314b318f326SPeter Maydell 31543ddc182SClement Deschamps switch (offset) { 31643ddc182SClement Deschamps case SDCMD: 31743ddc182SClement Deschamps s->cmd = value; 31843ddc182SClement Deschamps if (value & SDCMD_NEW_FLAG) { 31943ddc182SClement Deschamps bcm2835_sdhost_send_command(s); 32043ddc182SClement Deschamps bcm2835_sdhost_fifo_run(s); 32143ddc182SClement Deschamps s->cmd &= ~SDCMD_NEW_FLAG; 32243ddc182SClement Deschamps } 32343ddc182SClement Deschamps break; 32443ddc182SClement Deschamps case SDTOUT: 32543ddc182SClement Deschamps break; 32643ddc182SClement Deschamps case SDCDIV: 32743ddc182SClement Deschamps break; 32843ddc182SClement Deschamps case SDHSTS: 32943ddc182SClement Deschamps s->status &= ~value; 33043ddc182SClement Deschamps bcm2835_sdhost_update_irq(s); 33143ddc182SClement Deschamps break; 33243ddc182SClement Deschamps case SDARG: 33343ddc182SClement Deschamps s->cmdarg = value; 33443ddc182SClement Deschamps break; 33543ddc182SClement Deschamps case SDEDM: 33643ddc182SClement Deschamps if ((value & 0xf) == 0xf) { 33743ddc182SClement Deschamps /* power down */ 33843ddc182SClement Deschamps value &= ~0xf; 33943ddc182SClement Deschamps } 34043ddc182SClement Deschamps s->edm = value; 341b318f326SPeter Maydell trace_bcm2835_sdhost_edm_change("guest register write", s->edm); 34243ddc182SClement Deschamps break; 34343ddc182SClement Deschamps case SDHCFG: 34443ddc182SClement Deschamps s->config = value; 34543ddc182SClement Deschamps bcm2835_sdhost_fifo_run(s); 34643ddc182SClement Deschamps break; 34743ddc182SClement Deschamps case SDVDD: 34843ddc182SClement Deschamps s->vdd = value; 34943ddc182SClement Deschamps break; 35043ddc182SClement Deschamps case SDDATA: 35143ddc182SClement Deschamps bcm2835_sdhost_fifo_push(s, value); 35243ddc182SClement Deschamps bcm2835_sdhost_fifo_run(s); 35343ddc182SClement Deschamps break; 35443ddc182SClement Deschamps case SDHBCT: 35543ddc182SClement Deschamps s->hbct = value; 35643ddc182SClement Deschamps break; 35743ddc182SClement Deschamps case SDHBLC: 35843ddc182SClement Deschamps s->hblc = value; 35943ddc182SClement Deschamps s->datacnt = s->hblc * s->hbct; 36043ddc182SClement Deschamps bcm2835_sdhost_fifo_run(s); 36143ddc182SClement Deschamps break; 36243ddc182SClement Deschamps 36343ddc182SClement Deschamps default: 36443ddc182SClement Deschamps qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", 36543ddc182SClement Deschamps __func__, offset); 36643ddc182SClement Deschamps break; 36743ddc182SClement Deschamps } 36843ddc182SClement Deschamps } 36943ddc182SClement Deschamps 37043ddc182SClement Deschamps static const MemoryRegionOps bcm2835_sdhost_ops = { 37143ddc182SClement Deschamps .read = bcm2835_sdhost_read, 37243ddc182SClement Deschamps .write = bcm2835_sdhost_write, 37343ddc182SClement Deschamps .endianness = DEVICE_NATIVE_ENDIAN, 37443ddc182SClement Deschamps }; 37543ddc182SClement Deschamps 37643ddc182SClement Deschamps static const VMStateDescription vmstate_bcm2835_sdhost = { 37743ddc182SClement Deschamps .name = TYPE_BCM2835_SDHOST, 37843ddc182SClement Deschamps .version_id = 1, 37943ddc182SClement Deschamps .minimum_version_id = 1, 38043ddc182SClement Deschamps .fields = (VMStateField[]) { 38143ddc182SClement Deschamps VMSTATE_UINT32(cmd, BCM2835SDHostState), 38243ddc182SClement Deschamps VMSTATE_UINT32(cmdarg, BCM2835SDHostState), 38343ddc182SClement Deschamps VMSTATE_UINT32(status, BCM2835SDHostState), 38443ddc182SClement Deschamps VMSTATE_UINT32_ARRAY(rsp, BCM2835SDHostState, 4), 38543ddc182SClement Deschamps VMSTATE_UINT32(config, BCM2835SDHostState), 38643ddc182SClement Deschamps VMSTATE_UINT32(edm, BCM2835SDHostState), 38743ddc182SClement Deschamps VMSTATE_UINT32(vdd, BCM2835SDHostState), 38843ddc182SClement Deschamps VMSTATE_UINT32(hbct, BCM2835SDHostState), 38943ddc182SClement Deschamps VMSTATE_UINT32(hblc, BCM2835SDHostState), 39043ddc182SClement Deschamps VMSTATE_INT32(fifo_pos, BCM2835SDHostState), 39143ddc182SClement Deschamps VMSTATE_INT32(fifo_len, BCM2835SDHostState), 39243ddc182SClement Deschamps VMSTATE_UINT32_ARRAY(fifo, BCM2835SDHostState, BCM2835_SDHOST_FIFO_LEN), 39343ddc182SClement Deschamps VMSTATE_UINT32(datacnt, BCM2835SDHostState), 39443ddc182SClement Deschamps VMSTATE_END_OF_LIST() 39543ddc182SClement Deschamps } 39643ddc182SClement Deschamps }; 39743ddc182SClement Deschamps 39843ddc182SClement Deschamps static void bcm2835_sdhost_init(Object *obj) 39943ddc182SClement Deschamps { 40043ddc182SClement Deschamps BCM2835SDHostState *s = BCM2835_SDHOST(obj); 40143ddc182SClement Deschamps 40243ddc182SClement Deschamps qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), 40343ddc182SClement Deschamps TYPE_BCM2835_SDHOST_BUS, DEVICE(s), "sd-bus"); 40443ddc182SClement Deschamps 40543ddc182SClement Deschamps memory_region_init_io(&s->iomem, obj, &bcm2835_sdhost_ops, s, 40643ddc182SClement Deschamps TYPE_BCM2835_SDHOST, 0x1000); 40743ddc182SClement Deschamps sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); 40843ddc182SClement Deschamps sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq); 40943ddc182SClement Deschamps } 41043ddc182SClement Deschamps 41143ddc182SClement Deschamps static void bcm2835_sdhost_reset(DeviceState *dev) 41243ddc182SClement Deschamps { 41343ddc182SClement Deschamps BCM2835SDHostState *s = BCM2835_SDHOST(dev); 41443ddc182SClement Deschamps 41543ddc182SClement Deschamps s->cmd = 0; 41643ddc182SClement Deschamps s->cmdarg = 0; 41743ddc182SClement Deschamps s->edm = 0x0000c60f; 418b318f326SPeter Maydell trace_bcm2835_sdhost_edm_change("device reset", s->edm); 41943ddc182SClement Deschamps s->config = 0; 42043ddc182SClement Deschamps s->hbct = 0; 42143ddc182SClement Deschamps s->hblc = 0; 42243ddc182SClement Deschamps s->datacnt = 0; 42343ddc182SClement Deschamps s->fifo_pos = 0; 42443ddc182SClement Deschamps s->fifo_len = 0; 42543ddc182SClement Deschamps } 42643ddc182SClement Deschamps 42743ddc182SClement Deschamps static void bcm2835_sdhost_class_init(ObjectClass *klass, void *data) 42843ddc182SClement Deschamps { 42943ddc182SClement Deschamps DeviceClass *dc = DEVICE_CLASS(klass); 43043ddc182SClement Deschamps 43143ddc182SClement Deschamps dc->reset = bcm2835_sdhost_reset; 43243ddc182SClement Deschamps dc->vmsd = &vmstate_bcm2835_sdhost; 43343ddc182SClement Deschamps } 43443ddc182SClement Deschamps 43543ddc182SClement Deschamps static TypeInfo bcm2835_sdhost_info = { 43643ddc182SClement Deschamps .name = TYPE_BCM2835_SDHOST, 43743ddc182SClement Deschamps .parent = TYPE_SYS_BUS_DEVICE, 43843ddc182SClement Deschamps .instance_size = sizeof(BCM2835SDHostState), 43943ddc182SClement Deschamps .class_init = bcm2835_sdhost_class_init, 44043ddc182SClement Deschamps .instance_init = bcm2835_sdhost_init, 44143ddc182SClement Deschamps }; 44243ddc182SClement Deschamps 44343ddc182SClement Deschamps static const TypeInfo bcm2835_sdhost_bus_info = { 44443ddc182SClement Deschamps .name = TYPE_BCM2835_SDHOST_BUS, 44543ddc182SClement Deschamps .parent = TYPE_SD_BUS, 44643ddc182SClement Deschamps .instance_size = sizeof(SDBus), 44743ddc182SClement Deschamps }; 44843ddc182SClement Deschamps 44943ddc182SClement Deschamps static void bcm2835_sdhost_register_types(void) 45043ddc182SClement Deschamps { 45143ddc182SClement Deschamps type_register_static(&bcm2835_sdhost_info); 45243ddc182SClement Deschamps type_register_static(&bcm2835_sdhost_bus_info); 45343ddc182SClement Deschamps } 45443ddc182SClement Deschamps 45543ddc182SClement Deschamps type_init(bcm2835_sdhost_register_types) 456