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