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