182e48382SNiek Linnenbank /* 282e48382SNiek Linnenbank * Allwinner (sun4i and above) SD Host Controller emulation 382e48382SNiek Linnenbank * 482e48382SNiek Linnenbank * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com> 582e48382SNiek Linnenbank * 682e48382SNiek Linnenbank * This program is free software: you can redistribute it and/or modify 782e48382SNiek Linnenbank * it under the terms of the GNU General Public License as published by 882e48382SNiek Linnenbank * the Free Software Foundation, either version 2 of the License, or 982e48382SNiek Linnenbank * (at your option) any later version. 1082e48382SNiek Linnenbank * 1182e48382SNiek Linnenbank * This program is distributed in the hope that it will be useful, 1282e48382SNiek Linnenbank * but WITHOUT ANY WARRANTY; without even the implied warranty of 1382e48382SNiek Linnenbank * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1482e48382SNiek Linnenbank * GNU General Public License for more details. 1582e48382SNiek Linnenbank * 1682e48382SNiek Linnenbank * You should have received a copy of the GNU General Public License 1782e48382SNiek Linnenbank * along with this program. If not, see <http://www.gnu.org/licenses/>. 1882e48382SNiek Linnenbank */ 1982e48382SNiek Linnenbank 2082e48382SNiek Linnenbank #include "qemu/osdep.h" 2182e48382SNiek Linnenbank #include "qemu/log.h" 2282e48382SNiek Linnenbank #include "qemu/module.h" 2382e48382SNiek Linnenbank #include "qemu/units.h" 24b3aec952SPhilippe Mathieu-Daudé #include "qapi/error.h" 2582e48382SNiek Linnenbank #include "sysemu/blockdev.h" 26b3aec952SPhilippe Mathieu-Daudé #include "sysemu/dma.h" 27b3aec952SPhilippe Mathieu-Daudé #include "hw/qdev-properties.h" 2882e48382SNiek Linnenbank #include "hw/irq.h" 2982e48382SNiek Linnenbank #include "hw/sd/allwinner-sdhost.h" 3082e48382SNiek Linnenbank #include "migration/vmstate.h" 3182e48382SNiek Linnenbank #include "trace.h" 32*db1015e9SEduardo Habkost #include "qom/object.h" 3382e48382SNiek Linnenbank 3482e48382SNiek Linnenbank #define TYPE_AW_SDHOST_BUS "allwinner-sdhost-bus" 3582e48382SNiek Linnenbank #define AW_SDHOST_BUS(obj) \ 3682e48382SNiek Linnenbank OBJECT_CHECK(SDBus, (obj), TYPE_AW_SDHOST_BUS) 3782e48382SNiek Linnenbank 3882e48382SNiek Linnenbank /* SD Host register offsets */ 3982e48382SNiek Linnenbank enum { 4082e48382SNiek Linnenbank REG_SD_GCTL = 0x00, /* Global Control */ 4182e48382SNiek Linnenbank REG_SD_CKCR = 0x04, /* Clock Control */ 4282e48382SNiek Linnenbank REG_SD_TMOR = 0x08, /* Timeout */ 4382e48382SNiek Linnenbank REG_SD_BWDR = 0x0C, /* Bus Width */ 4482e48382SNiek Linnenbank REG_SD_BKSR = 0x10, /* Block Size */ 4582e48382SNiek Linnenbank REG_SD_BYCR = 0x14, /* Byte Count */ 4682e48382SNiek Linnenbank REG_SD_CMDR = 0x18, /* Command */ 4782e48382SNiek Linnenbank REG_SD_CAGR = 0x1C, /* Command Argument */ 4882e48382SNiek Linnenbank REG_SD_RESP0 = 0x20, /* Response Zero */ 4982e48382SNiek Linnenbank REG_SD_RESP1 = 0x24, /* Response One */ 5082e48382SNiek Linnenbank REG_SD_RESP2 = 0x28, /* Response Two */ 5182e48382SNiek Linnenbank REG_SD_RESP3 = 0x2C, /* Response Three */ 5282e48382SNiek Linnenbank REG_SD_IMKR = 0x30, /* Interrupt Mask */ 5382e48382SNiek Linnenbank REG_SD_MISR = 0x34, /* Masked Interrupt Status */ 5482e48382SNiek Linnenbank REG_SD_RISR = 0x38, /* Raw Interrupt Status */ 5582e48382SNiek Linnenbank REG_SD_STAR = 0x3C, /* Status */ 5682e48382SNiek Linnenbank REG_SD_FWLR = 0x40, /* FIFO Water Level */ 5782e48382SNiek Linnenbank REG_SD_FUNS = 0x44, /* FIFO Function Select */ 5882e48382SNiek Linnenbank REG_SD_DBGC = 0x50, /* Debug Enable */ 5982e48382SNiek Linnenbank REG_SD_A12A = 0x58, /* Auto command 12 argument */ 6082e48382SNiek Linnenbank REG_SD_NTSR = 0x5C, /* SD NewTiming Set */ 6182e48382SNiek Linnenbank REG_SD_SDBG = 0x60, /* SD newTiming Set Debug */ 6282e48382SNiek Linnenbank REG_SD_HWRST = 0x78, /* Hardware Reset Register */ 6382e48382SNiek Linnenbank REG_SD_DMAC = 0x80, /* Internal DMA Controller Control */ 6482e48382SNiek Linnenbank REG_SD_DLBA = 0x84, /* Descriptor List Base Address */ 6582e48382SNiek Linnenbank REG_SD_IDST = 0x88, /* Internal DMA Controller Status */ 6682e48382SNiek Linnenbank REG_SD_IDIE = 0x8C, /* Internal DMA Controller IRQ Enable */ 6782e48382SNiek Linnenbank REG_SD_THLDC = 0x100, /* Card Threshold Control */ 6882e48382SNiek Linnenbank REG_SD_DSBD = 0x10C, /* eMMC DDR Start Bit Detection Control */ 6982e48382SNiek Linnenbank REG_SD_RES_CRC = 0x110, /* Response CRC from card/eMMC */ 7082e48382SNiek Linnenbank REG_SD_DATA7_CRC = 0x114, /* CRC Data 7 from card/eMMC */ 7182e48382SNiek Linnenbank REG_SD_DATA6_CRC = 0x118, /* CRC Data 6 from card/eMMC */ 7282e48382SNiek Linnenbank REG_SD_DATA5_CRC = 0x11C, /* CRC Data 5 from card/eMMC */ 7382e48382SNiek Linnenbank REG_SD_DATA4_CRC = 0x120, /* CRC Data 4 from card/eMMC */ 7482e48382SNiek Linnenbank REG_SD_DATA3_CRC = 0x124, /* CRC Data 3 from card/eMMC */ 7582e48382SNiek Linnenbank REG_SD_DATA2_CRC = 0x128, /* CRC Data 2 from card/eMMC */ 7682e48382SNiek Linnenbank REG_SD_DATA1_CRC = 0x12C, /* CRC Data 1 from card/eMMC */ 7782e48382SNiek Linnenbank REG_SD_DATA0_CRC = 0x130, /* CRC Data 0 from card/eMMC */ 7882e48382SNiek Linnenbank REG_SD_CRC_STA = 0x134, /* CRC status from card/eMMC during write */ 7982e48382SNiek Linnenbank REG_SD_FIFO = 0x200, /* Read/Write FIFO */ 8082e48382SNiek Linnenbank }; 8182e48382SNiek Linnenbank 8282e48382SNiek Linnenbank /* SD Host register flags */ 8382e48382SNiek Linnenbank enum { 8482e48382SNiek Linnenbank SD_GCTL_FIFO_AC_MOD = (1 << 31), 8582e48382SNiek Linnenbank SD_GCTL_DDR_MOD_SEL = (1 << 10), 8682e48382SNiek Linnenbank SD_GCTL_CD_DBC_ENB = (1 << 8), 8782e48382SNiek Linnenbank SD_GCTL_DMA_ENB = (1 << 5), 8882e48382SNiek Linnenbank SD_GCTL_INT_ENB = (1 << 4), 8982e48382SNiek Linnenbank SD_GCTL_DMA_RST = (1 << 2), 9082e48382SNiek Linnenbank SD_GCTL_FIFO_RST = (1 << 1), 9182e48382SNiek Linnenbank SD_GCTL_SOFT_RST = (1 << 0), 9282e48382SNiek Linnenbank }; 9382e48382SNiek Linnenbank 9482e48382SNiek Linnenbank enum { 9582e48382SNiek Linnenbank SD_CMDR_LOAD = (1 << 31), 9682e48382SNiek Linnenbank SD_CMDR_CLKCHANGE = (1 << 21), 9782e48382SNiek Linnenbank SD_CMDR_WRITE = (1 << 10), 9882e48382SNiek Linnenbank SD_CMDR_AUTOSTOP = (1 << 12), 9982e48382SNiek Linnenbank SD_CMDR_DATA = (1 << 9), 10082e48382SNiek Linnenbank SD_CMDR_RESPONSE_LONG = (1 << 7), 10182e48382SNiek Linnenbank SD_CMDR_RESPONSE = (1 << 6), 10282e48382SNiek Linnenbank SD_CMDR_CMDID_MASK = (0x3f), 10382e48382SNiek Linnenbank }; 10482e48382SNiek Linnenbank 10582e48382SNiek Linnenbank enum { 10682e48382SNiek Linnenbank SD_RISR_CARD_REMOVE = (1 << 31), 10782e48382SNiek Linnenbank SD_RISR_CARD_INSERT = (1 << 30), 10882e48382SNiek Linnenbank SD_RISR_SDIO_INTR = (1 << 16), 10982e48382SNiek Linnenbank SD_RISR_AUTOCMD_DONE = (1 << 14), 11082e48382SNiek Linnenbank SD_RISR_DATA_COMPLETE = (1 << 3), 11182e48382SNiek Linnenbank SD_RISR_CMD_COMPLETE = (1 << 2), 11282e48382SNiek Linnenbank SD_RISR_NO_RESPONSE = (1 << 1), 11382e48382SNiek Linnenbank }; 11482e48382SNiek Linnenbank 11582e48382SNiek Linnenbank enum { 11682e48382SNiek Linnenbank SD_STAR_CARD_PRESENT = (1 << 8), 11782e48382SNiek Linnenbank }; 11882e48382SNiek Linnenbank 11982e48382SNiek Linnenbank enum { 12082e48382SNiek Linnenbank SD_IDST_INT_SUMMARY = (1 << 8), 12182e48382SNiek Linnenbank SD_IDST_RECEIVE_IRQ = (1 << 1), 12282e48382SNiek Linnenbank SD_IDST_TRANSMIT_IRQ = (1 << 0), 12382e48382SNiek Linnenbank SD_IDST_IRQ_MASK = (1 << 1) | (1 << 0) | (1 << 8), 12482e48382SNiek Linnenbank SD_IDST_WR_MASK = (0x3ff), 12582e48382SNiek Linnenbank }; 12682e48382SNiek Linnenbank 12782e48382SNiek Linnenbank /* SD Host register reset values */ 12882e48382SNiek Linnenbank enum { 12982e48382SNiek Linnenbank REG_SD_GCTL_RST = 0x00000300, 13082e48382SNiek Linnenbank REG_SD_CKCR_RST = 0x0, 13182e48382SNiek Linnenbank REG_SD_TMOR_RST = 0xFFFFFF40, 13282e48382SNiek Linnenbank REG_SD_BWDR_RST = 0x0, 13382e48382SNiek Linnenbank REG_SD_BKSR_RST = 0x00000200, 13482e48382SNiek Linnenbank REG_SD_BYCR_RST = 0x00000200, 13582e48382SNiek Linnenbank REG_SD_CMDR_RST = 0x0, 13682e48382SNiek Linnenbank REG_SD_CAGR_RST = 0x0, 13782e48382SNiek Linnenbank REG_SD_RESP_RST = 0x0, 13882e48382SNiek Linnenbank REG_SD_IMKR_RST = 0x0, 13982e48382SNiek Linnenbank REG_SD_MISR_RST = 0x0, 14082e48382SNiek Linnenbank REG_SD_RISR_RST = 0x0, 14182e48382SNiek Linnenbank REG_SD_STAR_RST = 0x00000100, 14282e48382SNiek Linnenbank REG_SD_FWLR_RST = 0x000F0000, 14382e48382SNiek Linnenbank REG_SD_FUNS_RST = 0x0, 14482e48382SNiek Linnenbank REG_SD_DBGC_RST = 0x0, 14582e48382SNiek Linnenbank REG_SD_A12A_RST = 0x0000FFFF, 14682e48382SNiek Linnenbank REG_SD_NTSR_RST = 0x00000001, 14782e48382SNiek Linnenbank REG_SD_SDBG_RST = 0x0, 14882e48382SNiek Linnenbank REG_SD_HWRST_RST = 0x00000001, 14982e48382SNiek Linnenbank REG_SD_DMAC_RST = 0x0, 15082e48382SNiek Linnenbank REG_SD_DLBA_RST = 0x0, 15182e48382SNiek Linnenbank REG_SD_IDST_RST = 0x0, 15282e48382SNiek Linnenbank REG_SD_IDIE_RST = 0x0, 15382e48382SNiek Linnenbank REG_SD_THLDC_RST = 0x0, 15482e48382SNiek Linnenbank REG_SD_DSBD_RST = 0x0, 15582e48382SNiek Linnenbank REG_SD_RES_CRC_RST = 0x0, 15682e48382SNiek Linnenbank REG_SD_DATA_CRC_RST = 0x0, 15782e48382SNiek Linnenbank REG_SD_CRC_STA_RST = 0x0, 15882e48382SNiek Linnenbank REG_SD_FIFO_RST = 0x0, 15982e48382SNiek Linnenbank }; 16082e48382SNiek Linnenbank 16182e48382SNiek Linnenbank /* Data transfer descriptor for DMA */ 16282e48382SNiek Linnenbank typedef struct TransferDescriptor { 16382e48382SNiek Linnenbank uint32_t status; /* Status flags */ 16482e48382SNiek Linnenbank uint32_t size; /* Data buffer size */ 16582e48382SNiek Linnenbank uint32_t addr; /* Data buffer address */ 16682e48382SNiek Linnenbank uint32_t next; /* Physical address of next descriptor */ 16782e48382SNiek Linnenbank } TransferDescriptor; 16882e48382SNiek Linnenbank 16982e48382SNiek Linnenbank /* Data transfer descriptor flags */ 17082e48382SNiek Linnenbank enum { 17182e48382SNiek Linnenbank DESC_STATUS_HOLD = (1 << 31), /* Set when descriptor is in use by DMA */ 17282e48382SNiek Linnenbank DESC_STATUS_ERROR = (1 << 30), /* Set when DMA transfer error occurred */ 17382e48382SNiek Linnenbank DESC_STATUS_CHAIN = (1 << 4), /* Indicates chained descriptor. */ 17482e48382SNiek Linnenbank DESC_STATUS_FIRST = (1 << 3), /* Set on the first descriptor */ 17582e48382SNiek Linnenbank DESC_STATUS_LAST = (1 << 2), /* Set on the last descriptor */ 17682e48382SNiek Linnenbank DESC_STATUS_NOIRQ = (1 << 1), /* Skip raising interrupt after transfer */ 17782e48382SNiek Linnenbank DESC_SIZE_MASK = (0xfffffffc) 17882e48382SNiek Linnenbank }; 17982e48382SNiek Linnenbank 18082e48382SNiek Linnenbank static void allwinner_sdhost_update_irq(AwSdHostState *s) 18182e48382SNiek Linnenbank { 18282e48382SNiek Linnenbank uint32_t irq; 18382e48382SNiek Linnenbank 18482e48382SNiek Linnenbank if (s->global_ctl & SD_GCTL_INT_ENB) { 18582e48382SNiek Linnenbank irq = s->irq_status & s->irq_mask; 18682e48382SNiek Linnenbank } else { 18782e48382SNiek Linnenbank irq = 0; 18882e48382SNiek Linnenbank } 18982e48382SNiek Linnenbank 19082e48382SNiek Linnenbank trace_allwinner_sdhost_update_irq(irq); 19182e48382SNiek Linnenbank qemu_set_irq(s->irq, irq); 19282e48382SNiek Linnenbank } 19382e48382SNiek Linnenbank 19482e48382SNiek Linnenbank static void allwinner_sdhost_update_transfer_cnt(AwSdHostState *s, 19582e48382SNiek Linnenbank uint32_t bytes) 19682e48382SNiek Linnenbank { 19782e48382SNiek Linnenbank if (s->transfer_cnt > bytes) { 19882e48382SNiek Linnenbank s->transfer_cnt -= bytes; 19982e48382SNiek Linnenbank } else { 20082e48382SNiek Linnenbank s->transfer_cnt = 0; 20182e48382SNiek Linnenbank } 20282e48382SNiek Linnenbank 20382e48382SNiek Linnenbank if (!s->transfer_cnt) { 20482e48382SNiek Linnenbank s->irq_status |= SD_RISR_DATA_COMPLETE; 20582e48382SNiek Linnenbank } 20682e48382SNiek Linnenbank } 20782e48382SNiek Linnenbank 20882e48382SNiek Linnenbank static void allwinner_sdhost_set_inserted(DeviceState *dev, bool inserted) 20982e48382SNiek Linnenbank { 21082e48382SNiek Linnenbank AwSdHostState *s = AW_SDHOST(dev); 21182e48382SNiek Linnenbank 21282e48382SNiek Linnenbank trace_allwinner_sdhost_set_inserted(inserted); 21382e48382SNiek Linnenbank 21482e48382SNiek Linnenbank if (inserted) { 21582e48382SNiek Linnenbank s->irq_status |= SD_RISR_CARD_INSERT; 21682e48382SNiek Linnenbank s->irq_status &= ~SD_RISR_CARD_REMOVE; 21782e48382SNiek Linnenbank s->status |= SD_STAR_CARD_PRESENT; 21882e48382SNiek Linnenbank } else { 21982e48382SNiek Linnenbank s->irq_status &= ~SD_RISR_CARD_INSERT; 22082e48382SNiek Linnenbank s->irq_status |= SD_RISR_CARD_REMOVE; 22182e48382SNiek Linnenbank s->status &= ~SD_STAR_CARD_PRESENT; 22282e48382SNiek Linnenbank } 22382e48382SNiek Linnenbank 22482e48382SNiek Linnenbank allwinner_sdhost_update_irq(s); 22582e48382SNiek Linnenbank } 22682e48382SNiek Linnenbank 22782e48382SNiek Linnenbank static void allwinner_sdhost_send_command(AwSdHostState *s) 22882e48382SNiek Linnenbank { 22982e48382SNiek Linnenbank SDRequest request; 23082e48382SNiek Linnenbank uint8_t resp[16]; 23182e48382SNiek Linnenbank int rlen; 23282e48382SNiek Linnenbank 23382e48382SNiek Linnenbank /* Auto clear load flag */ 23482e48382SNiek Linnenbank s->command &= ~SD_CMDR_LOAD; 23582e48382SNiek Linnenbank 23682e48382SNiek Linnenbank /* Clock change does not actually interact with the SD bus */ 23782e48382SNiek Linnenbank if (!(s->command & SD_CMDR_CLKCHANGE)) { 23882e48382SNiek Linnenbank 23982e48382SNiek Linnenbank /* Prepare request */ 24082e48382SNiek Linnenbank request.cmd = s->command & SD_CMDR_CMDID_MASK; 24182e48382SNiek Linnenbank request.arg = s->command_arg; 24282e48382SNiek Linnenbank 24382e48382SNiek Linnenbank /* Send request to SD bus */ 24482e48382SNiek Linnenbank rlen = sdbus_do_command(&s->sdbus, &request, resp); 24582e48382SNiek Linnenbank if (rlen < 0) { 24682e48382SNiek Linnenbank goto error; 24782e48382SNiek Linnenbank } 24882e48382SNiek Linnenbank 24982e48382SNiek Linnenbank /* If the command has a response, store it in the response registers */ 25082e48382SNiek Linnenbank if ((s->command & SD_CMDR_RESPONSE)) { 25182e48382SNiek Linnenbank if (rlen == 4 && !(s->command & SD_CMDR_RESPONSE_LONG)) { 25282e48382SNiek Linnenbank s->response[0] = ldl_be_p(&resp[0]); 25382e48382SNiek Linnenbank s->response[1] = s->response[2] = s->response[3] = 0; 25482e48382SNiek Linnenbank 25582e48382SNiek Linnenbank } else if (rlen == 16 && (s->command & SD_CMDR_RESPONSE_LONG)) { 25682e48382SNiek Linnenbank s->response[0] = ldl_be_p(&resp[12]); 25782e48382SNiek Linnenbank s->response[1] = ldl_be_p(&resp[8]); 25882e48382SNiek Linnenbank s->response[2] = ldl_be_p(&resp[4]); 25982e48382SNiek Linnenbank s->response[3] = ldl_be_p(&resp[0]); 26082e48382SNiek Linnenbank } else { 26182e48382SNiek Linnenbank goto error; 26282e48382SNiek Linnenbank } 26382e48382SNiek Linnenbank } 26482e48382SNiek Linnenbank } 26582e48382SNiek Linnenbank 26682e48382SNiek Linnenbank /* Set interrupt status bits */ 26782e48382SNiek Linnenbank s->irq_status |= SD_RISR_CMD_COMPLETE; 26882e48382SNiek Linnenbank return; 26982e48382SNiek Linnenbank 27082e48382SNiek Linnenbank error: 27182e48382SNiek Linnenbank s->irq_status |= SD_RISR_NO_RESPONSE; 27282e48382SNiek Linnenbank } 27382e48382SNiek Linnenbank 27482e48382SNiek Linnenbank static void allwinner_sdhost_auto_stop(AwSdHostState *s) 27582e48382SNiek Linnenbank { 27682e48382SNiek Linnenbank /* 27782e48382SNiek Linnenbank * The stop command (CMD12) ensures the SD bus 27882e48382SNiek Linnenbank * returns to the transfer state. 27982e48382SNiek Linnenbank */ 28082e48382SNiek Linnenbank if ((s->command & SD_CMDR_AUTOSTOP) && (s->transfer_cnt == 0)) { 28182e48382SNiek Linnenbank /* First save current command registers */ 28282e48382SNiek Linnenbank uint32_t saved_cmd = s->command; 28382e48382SNiek Linnenbank uint32_t saved_arg = s->command_arg; 28482e48382SNiek Linnenbank 28582e48382SNiek Linnenbank /* Prepare stop command (CMD12) */ 28682e48382SNiek Linnenbank s->command &= ~SD_CMDR_CMDID_MASK; 28782e48382SNiek Linnenbank s->command |= 12; /* CMD12 */ 28882e48382SNiek Linnenbank s->command_arg = 0; 28982e48382SNiek Linnenbank 29082e48382SNiek Linnenbank /* Put the command on SD bus */ 29182e48382SNiek Linnenbank allwinner_sdhost_send_command(s); 29282e48382SNiek Linnenbank 29382e48382SNiek Linnenbank /* Restore command values */ 29482e48382SNiek Linnenbank s->command = saved_cmd; 29582e48382SNiek Linnenbank s->command_arg = saved_arg; 29682e48382SNiek Linnenbank 29782e48382SNiek Linnenbank /* Set IRQ status bit for automatic stop done */ 29882e48382SNiek Linnenbank s->irq_status |= SD_RISR_AUTOCMD_DONE; 29982e48382SNiek Linnenbank } 30082e48382SNiek Linnenbank } 30182e48382SNiek Linnenbank 30282e48382SNiek Linnenbank static uint32_t allwinner_sdhost_process_desc(AwSdHostState *s, 30382e48382SNiek Linnenbank hwaddr desc_addr, 30482e48382SNiek Linnenbank TransferDescriptor *desc, 30582e48382SNiek Linnenbank bool is_write, uint32_t max_bytes) 30682e48382SNiek Linnenbank { 30782e48382SNiek Linnenbank AwSdHostClass *klass = AW_SDHOST_GET_CLASS(s); 30882e48382SNiek Linnenbank uint32_t num_done = 0; 30982e48382SNiek Linnenbank uint32_t num_bytes = max_bytes; 31082e48382SNiek Linnenbank uint8_t buf[1024]; 31182e48382SNiek Linnenbank 31282e48382SNiek Linnenbank /* Read descriptor */ 313b3aec952SPhilippe Mathieu-Daudé dma_memory_read(&s->dma_as, desc_addr, desc, sizeof(*desc)); 31482e48382SNiek Linnenbank if (desc->size == 0) { 31582e48382SNiek Linnenbank desc->size = klass->max_desc_size; 31682e48382SNiek Linnenbank } else if (desc->size > klass->max_desc_size) { 31782e48382SNiek Linnenbank qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA descriptor buffer size " 31882e48382SNiek Linnenbank " is out-of-bounds: %" PRIu32 " > %zu", 31982e48382SNiek Linnenbank __func__, desc->size, klass->max_desc_size); 32082e48382SNiek Linnenbank desc->size = klass->max_desc_size; 32182e48382SNiek Linnenbank } 32282e48382SNiek Linnenbank if (desc->size < num_bytes) { 32382e48382SNiek Linnenbank num_bytes = desc->size; 32482e48382SNiek Linnenbank } 32582e48382SNiek Linnenbank 32682e48382SNiek Linnenbank trace_allwinner_sdhost_process_desc(desc_addr, desc->size, 32782e48382SNiek Linnenbank is_write, max_bytes); 32882e48382SNiek Linnenbank 32982e48382SNiek Linnenbank while (num_done < num_bytes) { 33082e48382SNiek Linnenbank /* Try to completely fill the local buffer */ 33182e48382SNiek Linnenbank uint32_t buf_bytes = num_bytes - num_done; 33282e48382SNiek Linnenbank if (buf_bytes > sizeof(buf)) { 33382e48382SNiek Linnenbank buf_bytes = sizeof(buf); 33482e48382SNiek Linnenbank } 33582e48382SNiek Linnenbank 33682e48382SNiek Linnenbank /* Write to SD bus */ 33782e48382SNiek Linnenbank if (is_write) { 338b3aec952SPhilippe Mathieu-Daudé dma_memory_read(&s->dma_as, 339b3aec952SPhilippe Mathieu-Daudé (desc->addr & DESC_SIZE_MASK) + num_done, 34082e48382SNiek Linnenbank buf, buf_bytes); 34162a21be6SPhilippe Mathieu-Daudé sdbus_write_data(&s->sdbus, buf, buf_bytes); 34282e48382SNiek Linnenbank 34382e48382SNiek Linnenbank /* Read from SD bus */ 34482e48382SNiek Linnenbank } else { 345618e0be1SPhilippe Mathieu-Daudé sdbus_read_data(&s->sdbus, buf, buf_bytes); 346b3aec952SPhilippe Mathieu-Daudé dma_memory_write(&s->dma_as, 347b3aec952SPhilippe Mathieu-Daudé (desc->addr & DESC_SIZE_MASK) + num_done, 34882e48382SNiek Linnenbank buf, buf_bytes); 34982e48382SNiek Linnenbank } 35082e48382SNiek Linnenbank num_done += buf_bytes; 35182e48382SNiek Linnenbank } 35282e48382SNiek Linnenbank 35382e48382SNiek Linnenbank /* Clear hold flag and flush descriptor */ 35482e48382SNiek Linnenbank desc->status &= ~DESC_STATUS_HOLD; 355b3aec952SPhilippe Mathieu-Daudé dma_memory_write(&s->dma_as, desc_addr, desc, sizeof(*desc)); 35682e48382SNiek Linnenbank 35782e48382SNiek Linnenbank return num_done; 35882e48382SNiek Linnenbank } 35982e48382SNiek Linnenbank 36082e48382SNiek Linnenbank static void allwinner_sdhost_dma(AwSdHostState *s) 36182e48382SNiek Linnenbank { 36282e48382SNiek Linnenbank TransferDescriptor desc; 36382e48382SNiek Linnenbank hwaddr desc_addr = s->desc_base; 36482e48382SNiek Linnenbank bool is_write = (s->command & SD_CMDR_WRITE); 36582e48382SNiek Linnenbank uint32_t bytes_done = 0; 36682e48382SNiek Linnenbank 36782e48382SNiek Linnenbank /* Check if DMA can be performed */ 36882e48382SNiek Linnenbank if (s->byte_count == 0 || s->block_size == 0 || 36982e48382SNiek Linnenbank !(s->global_ctl & SD_GCTL_DMA_ENB)) { 37082e48382SNiek Linnenbank return; 37182e48382SNiek Linnenbank } 37282e48382SNiek Linnenbank 37382e48382SNiek Linnenbank /* 37482e48382SNiek Linnenbank * For read operations, data must be available on the SD bus 37582e48382SNiek Linnenbank * If not, it is an error and we should not act at all 37682e48382SNiek Linnenbank */ 37782e48382SNiek Linnenbank if (!is_write && !sdbus_data_ready(&s->sdbus)) { 37882e48382SNiek Linnenbank return; 37982e48382SNiek Linnenbank } 38082e48382SNiek Linnenbank 38182e48382SNiek Linnenbank /* Process the DMA descriptors until all data is copied */ 38282e48382SNiek Linnenbank while (s->byte_count > 0) { 38382e48382SNiek Linnenbank bytes_done = allwinner_sdhost_process_desc(s, desc_addr, &desc, 38482e48382SNiek Linnenbank is_write, s->byte_count); 38582e48382SNiek Linnenbank allwinner_sdhost_update_transfer_cnt(s, bytes_done); 38682e48382SNiek Linnenbank 38782e48382SNiek Linnenbank if (bytes_done <= s->byte_count) { 38882e48382SNiek Linnenbank s->byte_count -= bytes_done; 38982e48382SNiek Linnenbank } else { 39082e48382SNiek Linnenbank s->byte_count = 0; 39182e48382SNiek Linnenbank } 39282e48382SNiek Linnenbank 39382e48382SNiek Linnenbank if (desc.status & DESC_STATUS_LAST) { 39482e48382SNiek Linnenbank break; 39582e48382SNiek Linnenbank } else { 39682e48382SNiek Linnenbank desc_addr = desc.next; 39782e48382SNiek Linnenbank } 39882e48382SNiek Linnenbank } 39982e48382SNiek Linnenbank 40082e48382SNiek Linnenbank /* Raise IRQ to signal DMA is completed */ 40182e48382SNiek Linnenbank s->irq_status |= SD_RISR_DATA_COMPLETE | SD_RISR_SDIO_INTR; 40282e48382SNiek Linnenbank 40382e48382SNiek Linnenbank /* Update DMAC bits */ 40482e48382SNiek Linnenbank s->dmac_status |= SD_IDST_INT_SUMMARY; 40582e48382SNiek Linnenbank 40682e48382SNiek Linnenbank if (is_write) { 40782e48382SNiek Linnenbank s->dmac_status |= SD_IDST_TRANSMIT_IRQ; 40882e48382SNiek Linnenbank } else { 40982e48382SNiek Linnenbank s->dmac_status |= SD_IDST_RECEIVE_IRQ; 41082e48382SNiek Linnenbank } 41182e48382SNiek Linnenbank } 41282e48382SNiek Linnenbank 41382e48382SNiek Linnenbank static uint64_t allwinner_sdhost_read(void *opaque, hwaddr offset, 41482e48382SNiek Linnenbank unsigned size) 41582e48382SNiek Linnenbank { 41682e48382SNiek Linnenbank AwSdHostState *s = AW_SDHOST(opaque); 41782e48382SNiek Linnenbank uint32_t res = 0; 41882e48382SNiek Linnenbank 41982e48382SNiek Linnenbank switch (offset) { 42082e48382SNiek Linnenbank case REG_SD_GCTL: /* Global Control */ 42182e48382SNiek Linnenbank res = s->global_ctl; 42282e48382SNiek Linnenbank break; 42382e48382SNiek Linnenbank case REG_SD_CKCR: /* Clock Control */ 42482e48382SNiek Linnenbank res = s->clock_ctl; 42582e48382SNiek Linnenbank break; 42682e48382SNiek Linnenbank case REG_SD_TMOR: /* Timeout */ 42782e48382SNiek Linnenbank res = s->timeout; 42882e48382SNiek Linnenbank break; 42982e48382SNiek Linnenbank case REG_SD_BWDR: /* Bus Width */ 43082e48382SNiek Linnenbank res = s->bus_width; 43182e48382SNiek Linnenbank break; 43282e48382SNiek Linnenbank case REG_SD_BKSR: /* Block Size */ 43382e48382SNiek Linnenbank res = s->block_size; 43482e48382SNiek Linnenbank break; 43582e48382SNiek Linnenbank case REG_SD_BYCR: /* Byte Count */ 43682e48382SNiek Linnenbank res = s->byte_count; 43782e48382SNiek Linnenbank break; 43882e48382SNiek Linnenbank case REG_SD_CMDR: /* Command */ 43982e48382SNiek Linnenbank res = s->command; 44082e48382SNiek Linnenbank break; 44182e48382SNiek Linnenbank case REG_SD_CAGR: /* Command Argument */ 44282e48382SNiek Linnenbank res = s->command_arg; 44382e48382SNiek Linnenbank break; 44482e48382SNiek Linnenbank case REG_SD_RESP0: /* Response Zero */ 44582e48382SNiek Linnenbank res = s->response[0]; 44682e48382SNiek Linnenbank break; 44782e48382SNiek Linnenbank case REG_SD_RESP1: /* Response One */ 44882e48382SNiek Linnenbank res = s->response[1]; 44982e48382SNiek Linnenbank break; 45082e48382SNiek Linnenbank case REG_SD_RESP2: /* Response Two */ 45182e48382SNiek Linnenbank res = s->response[2]; 45282e48382SNiek Linnenbank break; 45382e48382SNiek Linnenbank case REG_SD_RESP3: /* Response Three */ 45482e48382SNiek Linnenbank res = s->response[3]; 45582e48382SNiek Linnenbank break; 45682e48382SNiek Linnenbank case REG_SD_IMKR: /* Interrupt Mask */ 45782e48382SNiek Linnenbank res = s->irq_mask; 45882e48382SNiek Linnenbank break; 45982e48382SNiek Linnenbank case REG_SD_MISR: /* Masked Interrupt Status */ 46082e48382SNiek Linnenbank res = s->irq_status & s->irq_mask; 46182e48382SNiek Linnenbank break; 46282e48382SNiek Linnenbank case REG_SD_RISR: /* Raw Interrupt Status */ 46382e48382SNiek Linnenbank res = s->irq_status; 46482e48382SNiek Linnenbank break; 46582e48382SNiek Linnenbank case REG_SD_STAR: /* Status */ 46682e48382SNiek Linnenbank res = s->status; 46782e48382SNiek Linnenbank break; 46882e48382SNiek Linnenbank case REG_SD_FWLR: /* FIFO Water Level */ 46982e48382SNiek Linnenbank res = s->fifo_wlevel; 47082e48382SNiek Linnenbank break; 47182e48382SNiek Linnenbank case REG_SD_FUNS: /* FIFO Function Select */ 47282e48382SNiek Linnenbank res = s->fifo_func_sel; 47382e48382SNiek Linnenbank break; 47482e48382SNiek Linnenbank case REG_SD_DBGC: /* Debug Enable */ 47582e48382SNiek Linnenbank res = s->debug_enable; 47682e48382SNiek Linnenbank break; 47782e48382SNiek Linnenbank case REG_SD_A12A: /* Auto command 12 argument */ 47882e48382SNiek Linnenbank res = s->auto12_arg; 47982e48382SNiek Linnenbank break; 48082e48382SNiek Linnenbank case REG_SD_NTSR: /* SD NewTiming Set */ 48182e48382SNiek Linnenbank res = s->newtiming_set; 48282e48382SNiek Linnenbank break; 48382e48382SNiek Linnenbank case REG_SD_SDBG: /* SD newTiming Set Debug */ 48482e48382SNiek Linnenbank res = s->newtiming_debug; 48582e48382SNiek Linnenbank break; 48682e48382SNiek Linnenbank case REG_SD_HWRST: /* Hardware Reset Register */ 48782e48382SNiek Linnenbank res = s->hardware_rst; 48882e48382SNiek Linnenbank break; 48982e48382SNiek Linnenbank case REG_SD_DMAC: /* Internal DMA Controller Control */ 49082e48382SNiek Linnenbank res = s->dmac; 49182e48382SNiek Linnenbank break; 49282e48382SNiek Linnenbank case REG_SD_DLBA: /* Descriptor List Base Address */ 49382e48382SNiek Linnenbank res = s->desc_base; 49482e48382SNiek Linnenbank break; 49582e48382SNiek Linnenbank case REG_SD_IDST: /* Internal DMA Controller Status */ 49682e48382SNiek Linnenbank res = s->dmac_status; 49782e48382SNiek Linnenbank break; 49882e48382SNiek Linnenbank case REG_SD_IDIE: /* Internal DMA Controller Interrupt Enable */ 49982e48382SNiek Linnenbank res = s->dmac_irq; 50082e48382SNiek Linnenbank break; 50182e48382SNiek Linnenbank case REG_SD_THLDC: /* Card Threshold Control */ 50282e48382SNiek Linnenbank res = s->card_threshold; 50382e48382SNiek Linnenbank break; 50482e48382SNiek Linnenbank case REG_SD_DSBD: /* eMMC DDR Start Bit Detection Control */ 50582e48382SNiek Linnenbank res = s->startbit_detect; 50682e48382SNiek Linnenbank break; 50782e48382SNiek Linnenbank case REG_SD_RES_CRC: /* Response CRC from card/eMMC */ 50882e48382SNiek Linnenbank res = s->response_crc; 50982e48382SNiek Linnenbank break; 51082e48382SNiek Linnenbank case REG_SD_DATA7_CRC: /* CRC Data 7 from card/eMMC */ 51182e48382SNiek Linnenbank case REG_SD_DATA6_CRC: /* CRC Data 6 from card/eMMC */ 51282e48382SNiek Linnenbank case REG_SD_DATA5_CRC: /* CRC Data 5 from card/eMMC */ 51382e48382SNiek Linnenbank case REG_SD_DATA4_CRC: /* CRC Data 4 from card/eMMC */ 51482e48382SNiek Linnenbank case REG_SD_DATA3_CRC: /* CRC Data 3 from card/eMMC */ 51582e48382SNiek Linnenbank case REG_SD_DATA2_CRC: /* CRC Data 2 from card/eMMC */ 51682e48382SNiek Linnenbank case REG_SD_DATA1_CRC: /* CRC Data 1 from card/eMMC */ 51782e48382SNiek Linnenbank case REG_SD_DATA0_CRC: /* CRC Data 0 from card/eMMC */ 51882e48382SNiek Linnenbank res = s->data_crc[((offset - REG_SD_DATA7_CRC) / sizeof(uint32_t))]; 51982e48382SNiek Linnenbank break; 52082e48382SNiek Linnenbank case REG_SD_CRC_STA: /* CRC status from card/eMMC in write operation */ 52182e48382SNiek Linnenbank res = s->status_crc; 52282e48382SNiek Linnenbank break; 52382e48382SNiek Linnenbank case REG_SD_FIFO: /* Read/Write FIFO */ 52482e48382SNiek Linnenbank if (sdbus_data_ready(&s->sdbus)) { 525618e0be1SPhilippe Mathieu-Daudé sdbus_read_data(&s->sdbus, &res, sizeof(uint32_t)); 526618e0be1SPhilippe Mathieu-Daudé le32_to_cpus(&res); 52782e48382SNiek Linnenbank allwinner_sdhost_update_transfer_cnt(s, sizeof(uint32_t)); 52882e48382SNiek Linnenbank allwinner_sdhost_auto_stop(s); 52982e48382SNiek Linnenbank allwinner_sdhost_update_irq(s); 53082e48382SNiek Linnenbank } else { 53182e48382SNiek Linnenbank qemu_log_mask(LOG_GUEST_ERROR, "%s: no data ready on SD bus\n", 53282e48382SNiek Linnenbank __func__); 53382e48382SNiek Linnenbank } 53482e48382SNiek Linnenbank break; 53582e48382SNiek Linnenbank default: 53682e48382SNiek Linnenbank qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset %" 53782e48382SNiek Linnenbank HWADDR_PRIx"\n", __func__, offset); 53882e48382SNiek Linnenbank res = 0; 53982e48382SNiek Linnenbank break; 54082e48382SNiek Linnenbank } 54182e48382SNiek Linnenbank 54282e48382SNiek Linnenbank trace_allwinner_sdhost_read(offset, res, size); 54382e48382SNiek Linnenbank return res; 54482e48382SNiek Linnenbank } 54582e48382SNiek Linnenbank 54682e48382SNiek Linnenbank static void allwinner_sdhost_write(void *opaque, hwaddr offset, 54782e48382SNiek Linnenbank uint64_t value, unsigned size) 54882e48382SNiek Linnenbank { 54982e48382SNiek Linnenbank AwSdHostState *s = AW_SDHOST(opaque); 55062a21be6SPhilippe Mathieu-Daudé uint32_t u32; 55182e48382SNiek Linnenbank 55282e48382SNiek Linnenbank trace_allwinner_sdhost_write(offset, value, size); 55382e48382SNiek Linnenbank 55482e48382SNiek Linnenbank switch (offset) { 55582e48382SNiek Linnenbank case REG_SD_GCTL: /* Global Control */ 55682e48382SNiek Linnenbank s->global_ctl = value; 55782e48382SNiek Linnenbank s->global_ctl &= ~(SD_GCTL_DMA_RST | SD_GCTL_FIFO_RST | 55882e48382SNiek Linnenbank SD_GCTL_SOFT_RST); 55982e48382SNiek Linnenbank allwinner_sdhost_update_irq(s); 56082e48382SNiek Linnenbank break; 56182e48382SNiek Linnenbank case REG_SD_CKCR: /* Clock Control */ 56282e48382SNiek Linnenbank s->clock_ctl = value; 56382e48382SNiek Linnenbank break; 56482e48382SNiek Linnenbank case REG_SD_TMOR: /* Timeout */ 56582e48382SNiek Linnenbank s->timeout = value; 56682e48382SNiek Linnenbank break; 56782e48382SNiek Linnenbank case REG_SD_BWDR: /* Bus Width */ 56882e48382SNiek Linnenbank s->bus_width = value; 56982e48382SNiek Linnenbank break; 57082e48382SNiek Linnenbank case REG_SD_BKSR: /* Block Size */ 57182e48382SNiek Linnenbank s->block_size = value; 57282e48382SNiek Linnenbank break; 57382e48382SNiek Linnenbank case REG_SD_BYCR: /* Byte Count */ 57482e48382SNiek Linnenbank s->byte_count = value; 57582e48382SNiek Linnenbank s->transfer_cnt = value; 57682e48382SNiek Linnenbank break; 57782e48382SNiek Linnenbank case REG_SD_CMDR: /* Command */ 57882e48382SNiek Linnenbank s->command = value; 57982e48382SNiek Linnenbank if (value & SD_CMDR_LOAD) { 58082e48382SNiek Linnenbank allwinner_sdhost_send_command(s); 58182e48382SNiek Linnenbank allwinner_sdhost_dma(s); 58282e48382SNiek Linnenbank allwinner_sdhost_auto_stop(s); 58382e48382SNiek Linnenbank } 58482e48382SNiek Linnenbank allwinner_sdhost_update_irq(s); 58582e48382SNiek Linnenbank break; 58682e48382SNiek Linnenbank case REG_SD_CAGR: /* Command Argument */ 58782e48382SNiek Linnenbank s->command_arg = value; 58882e48382SNiek Linnenbank break; 58982e48382SNiek Linnenbank case REG_SD_RESP0: /* Response Zero */ 59082e48382SNiek Linnenbank s->response[0] = value; 59182e48382SNiek Linnenbank break; 59282e48382SNiek Linnenbank case REG_SD_RESP1: /* Response One */ 59382e48382SNiek Linnenbank s->response[1] = value; 59482e48382SNiek Linnenbank break; 59582e48382SNiek Linnenbank case REG_SD_RESP2: /* Response Two */ 59682e48382SNiek Linnenbank s->response[2] = value; 59782e48382SNiek Linnenbank break; 59882e48382SNiek Linnenbank case REG_SD_RESP3: /* Response Three */ 59982e48382SNiek Linnenbank s->response[3] = value; 60082e48382SNiek Linnenbank break; 60182e48382SNiek Linnenbank case REG_SD_IMKR: /* Interrupt Mask */ 60282e48382SNiek Linnenbank s->irq_mask = value; 60382e48382SNiek Linnenbank allwinner_sdhost_update_irq(s); 60482e48382SNiek Linnenbank break; 60582e48382SNiek Linnenbank case REG_SD_MISR: /* Masked Interrupt Status */ 60682e48382SNiek Linnenbank case REG_SD_RISR: /* Raw Interrupt Status */ 60782e48382SNiek Linnenbank s->irq_status &= ~value; 60882e48382SNiek Linnenbank allwinner_sdhost_update_irq(s); 60982e48382SNiek Linnenbank break; 61082e48382SNiek Linnenbank case REG_SD_STAR: /* Status */ 61182e48382SNiek Linnenbank s->status &= ~value; 61282e48382SNiek Linnenbank allwinner_sdhost_update_irq(s); 61382e48382SNiek Linnenbank break; 61482e48382SNiek Linnenbank case REG_SD_FWLR: /* FIFO Water Level */ 61582e48382SNiek Linnenbank s->fifo_wlevel = value; 61682e48382SNiek Linnenbank break; 61782e48382SNiek Linnenbank case REG_SD_FUNS: /* FIFO Function Select */ 61882e48382SNiek Linnenbank s->fifo_func_sel = value; 61982e48382SNiek Linnenbank break; 62082e48382SNiek Linnenbank case REG_SD_DBGC: /* Debug Enable */ 62182e48382SNiek Linnenbank s->debug_enable = value; 62282e48382SNiek Linnenbank break; 62382e48382SNiek Linnenbank case REG_SD_A12A: /* Auto command 12 argument */ 62482e48382SNiek Linnenbank s->auto12_arg = value; 62582e48382SNiek Linnenbank break; 62682e48382SNiek Linnenbank case REG_SD_NTSR: /* SD NewTiming Set */ 62782e48382SNiek Linnenbank s->newtiming_set = value; 62882e48382SNiek Linnenbank break; 62982e48382SNiek Linnenbank case REG_SD_SDBG: /* SD newTiming Set Debug */ 63082e48382SNiek Linnenbank s->newtiming_debug = value; 63182e48382SNiek Linnenbank break; 63282e48382SNiek Linnenbank case REG_SD_HWRST: /* Hardware Reset Register */ 63382e48382SNiek Linnenbank s->hardware_rst = value; 63482e48382SNiek Linnenbank break; 63582e48382SNiek Linnenbank case REG_SD_DMAC: /* Internal DMA Controller Control */ 63682e48382SNiek Linnenbank s->dmac = value; 63782e48382SNiek Linnenbank allwinner_sdhost_update_irq(s); 63882e48382SNiek Linnenbank break; 63982e48382SNiek Linnenbank case REG_SD_DLBA: /* Descriptor List Base Address */ 64082e48382SNiek Linnenbank s->desc_base = value; 64182e48382SNiek Linnenbank break; 64282e48382SNiek Linnenbank case REG_SD_IDST: /* Internal DMA Controller Status */ 64382e48382SNiek Linnenbank s->dmac_status &= (~SD_IDST_WR_MASK) | (~value & SD_IDST_WR_MASK); 64482e48382SNiek Linnenbank allwinner_sdhost_update_irq(s); 64582e48382SNiek Linnenbank break; 64682e48382SNiek Linnenbank case REG_SD_IDIE: /* Internal DMA Controller Interrupt Enable */ 64782e48382SNiek Linnenbank s->dmac_irq = value; 64882e48382SNiek Linnenbank allwinner_sdhost_update_irq(s); 64982e48382SNiek Linnenbank break; 65082e48382SNiek Linnenbank case REG_SD_THLDC: /* Card Threshold Control */ 65182e48382SNiek Linnenbank s->card_threshold = value; 65282e48382SNiek Linnenbank break; 65382e48382SNiek Linnenbank case REG_SD_DSBD: /* eMMC DDR Start Bit Detection Control */ 65482e48382SNiek Linnenbank s->startbit_detect = value; 65582e48382SNiek Linnenbank break; 65682e48382SNiek Linnenbank case REG_SD_FIFO: /* Read/Write FIFO */ 65762a21be6SPhilippe Mathieu-Daudé u32 = cpu_to_le32(value); 65862a21be6SPhilippe Mathieu-Daudé sdbus_write_data(&s->sdbus, &u32, sizeof(u32)); 65962a21be6SPhilippe Mathieu-Daudé allwinner_sdhost_update_transfer_cnt(s, sizeof(u32)); 66082e48382SNiek Linnenbank allwinner_sdhost_auto_stop(s); 66182e48382SNiek Linnenbank allwinner_sdhost_update_irq(s); 66282e48382SNiek Linnenbank break; 66382e48382SNiek Linnenbank case REG_SD_RES_CRC: /* Response CRC from card/eMMC */ 66482e48382SNiek Linnenbank case REG_SD_DATA7_CRC: /* CRC Data 7 from card/eMMC */ 66582e48382SNiek Linnenbank case REG_SD_DATA6_CRC: /* CRC Data 6 from card/eMMC */ 66682e48382SNiek Linnenbank case REG_SD_DATA5_CRC: /* CRC Data 5 from card/eMMC */ 66782e48382SNiek Linnenbank case REG_SD_DATA4_CRC: /* CRC Data 4 from card/eMMC */ 66882e48382SNiek Linnenbank case REG_SD_DATA3_CRC: /* CRC Data 3 from card/eMMC */ 66982e48382SNiek Linnenbank case REG_SD_DATA2_CRC: /* CRC Data 2 from card/eMMC */ 67082e48382SNiek Linnenbank case REG_SD_DATA1_CRC: /* CRC Data 1 from card/eMMC */ 67182e48382SNiek Linnenbank case REG_SD_DATA0_CRC: /* CRC Data 0 from card/eMMC */ 67282e48382SNiek Linnenbank case REG_SD_CRC_STA: /* CRC status from card/eMMC in write operation */ 67382e48382SNiek Linnenbank break; 67482e48382SNiek Linnenbank default: 67582e48382SNiek Linnenbank qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset %" 67682e48382SNiek Linnenbank HWADDR_PRIx"\n", __func__, offset); 67782e48382SNiek Linnenbank break; 67882e48382SNiek Linnenbank } 67982e48382SNiek Linnenbank } 68082e48382SNiek Linnenbank 68182e48382SNiek Linnenbank static const MemoryRegionOps allwinner_sdhost_ops = { 68282e48382SNiek Linnenbank .read = allwinner_sdhost_read, 68382e48382SNiek Linnenbank .write = allwinner_sdhost_write, 68482e48382SNiek Linnenbank .endianness = DEVICE_NATIVE_ENDIAN, 68582e48382SNiek Linnenbank .valid = { 68682e48382SNiek Linnenbank .min_access_size = 4, 68782e48382SNiek Linnenbank .max_access_size = 4, 68882e48382SNiek Linnenbank }, 68982e48382SNiek Linnenbank .impl.min_access_size = 4, 69082e48382SNiek Linnenbank }; 69182e48382SNiek Linnenbank 69282e48382SNiek Linnenbank static const VMStateDescription vmstate_allwinner_sdhost = { 69382e48382SNiek Linnenbank .name = "allwinner-sdhost", 69482e48382SNiek Linnenbank .version_id = 1, 69582e48382SNiek Linnenbank .minimum_version_id = 1, 69682e48382SNiek Linnenbank .fields = (VMStateField[]) { 69782e48382SNiek Linnenbank VMSTATE_UINT32(global_ctl, AwSdHostState), 69882e48382SNiek Linnenbank VMSTATE_UINT32(clock_ctl, AwSdHostState), 69982e48382SNiek Linnenbank VMSTATE_UINT32(timeout, AwSdHostState), 70082e48382SNiek Linnenbank VMSTATE_UINT32(bus_width, AwSdHostState), 70182e48382SNiek Linnenbank VMSTATE_UINT32(block_size, AwSdHostState), 70282e48382SNiek Linnenbank VMSTATE_UINT32(byte_count, AwSdHostState), 70382e48382SNiek Linnenbank VMSTATE_UINT32(transfer_cnt, AwSdHostState), 70482e48382SNiek Linnenbank VMSTATE_UINT32(command, AwSdHostState), 70582e48382SNiek Linnenbank VMSTATE_UINT32(command_arg, AwSdHostState), 70682e48382SNiek Linnenbank VMSTATE_UINT32_ARRAY(response, AwSdHostState, 4), 70782e48382SNiek Linnenbank VMSTATE_UINT32(irq_mask, AwSdHostState), 70882e48382SNiek Linnenbank VMSTATE_UINT32(irq_status, AwSdHostState), 70982e48382SNiek Linnenbank VMSTATE_UINT32(status, AwSdHostState), 71082e48382SNiek Linnenbank VMSTATE_UINT32(fifo_wlevel, AwSdHostState), 71182e48382SNiek Linnenbank VMSTATE_UINT32(fifo_func_sel, AwSdHostState), 71282e48382SNiek Linnenbank VMSTATE_UINT32(debug_enable, AwSdHostState), 71382e48382SNiek Linnenbank VMSTATE_UINT32(auto12_arg, AwSdHostState), 71482e48382SNiek Linnenbank VMSTATE_UINT32(newtiming_set, AwSdHostState), 71582e48382SNiek Linnenbank VMSTATE_UINT32(newtiming_debug, AwSdHostState), 71682e48382SNiek Linnenbank VMSTATE_UINT32(hardware_rst, AwSdHostState), 71782e48382SNiek Linnenbank VMSTATE_UINT32(dmac, AwSdHostState), 71882e48382SNiek Linnenbank VMSTATE_UINT32(desc_base, AwSdHostState), 71982e48382SNiek Linnenbank VMSTATE_UINT32(dmac_status, AwSdHostState), 72082e48382SNiek Linnenbank VMSTATE_UINT32(dmac_irq, AwSdHostState), 72182e48382SNiek Linnenbank VMSTATE_UINT32(card_threshold, AwSdHostState), 72282e48382SNiek Linnenbank VMSTATE_UINT32(startbit_detect, AwSdHostState), 72382e48382SNiek Linnenbank VMSTATE_UINT32(response_crc, AwSdHostState), 72482e48382SNiek Linnenbank VMSTATE_UINT32_ARRAY(data_crc, AwSdHostState, 8), 72582e48382SNiek Linnenbank VMSTATE_UINT32(status_crc, AwSdHostState), 72682e48382SNiek Linnenbank VMSTATE_END_OF_LIST() 72782e48382SNiek Linnenbank } 72882e48382SNiek Linnenbank }; 72982e48382SNiek Linnenbank 730b3aec952SPhilippe Mathieu-Daudé static Property allwinner_sdhost_properties[] = { 731b3aec952SPhilippe Mathieu-Daudé DEFINE_PROP_LINK("dma-memory", AwSdHostState, dma_mr, 732b3aec952SPhilippe Mathieu-Daudé TYPE_MEMORY_REGION, MemoryRegion *), 733b3aec952SPhilippe Mathieu-Daudé DEFINE_PROP_END_OF_LIST(), 734b3aec952SPhilippe Mathieu-Daudé }; 735b3aec952SPhilippe Mathieu-Daudé 73682e48382SNiek Linnenbank static void allwinner_sdhost_init(Object *obj) 73782e48382SNiek Linnenbank { 73882e48382SNiek Linnenbank AwSdHostState *s = AW_SDHOST(obj); 73982e48382SNiek Linnenbank 74082e48382SNiek Linnenbank qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), 74182e48382SNiek Linnenbank TYPE_AW_SDHOST_BUS, DEVICE(s), "sd-bus"); 74282e48382SNiek Linnenbank 74382e48382SNiek Linnenbank memory_region_init_io(&s->iomem, obj, &allwinner_sdhost_ops, s, 74482e48382SNiek Linnenbank TYPE_AW_SDHOST, 4 * KiB); 74582e48382SNiek Linnenbank sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); 74682e48382SNiek Linnenbank sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq); 74782e48382SNiek Linnenbank } 74882e48382SNiek Linnenbank 749b3aec952SPhilippe Mathieu-Daudé static void allwinner_sdhost_realize(DeviceState *dev, Error **errp) 750b3aec952SPhilippe Mathieu-Daudé { 751b3aec952SPhilippe Mathieu-Daudé AwSdHostState *s = AW_SDHOST(dev); 752b3aec952SPhilippe Mathieu-Daudé 753b3aec952SPhilippe Mathieu-Daudé if (!s->dma_mr) { 754b3aec952SPhilippe Mathieu-Daudé error_setg(errp, TYPE_AW_SDHOST " 'dma-memory' link not set"); 755b3aec952SPhilippe Mathieu-Daudé return; 756b3aec952SPhilippe Mathieu-Daudé } 757b3aec952SPhilippe Mathieu-Daudé 758b3aec952SPhilippe Mathieu-Daudé address_space_init(&s->dma_as, s->dma_mr, "sdhost-dma"); 759b3aec952SPhilippe Mathieu-Daudé } 760b3aec952SPhilippe Mathieu-Daudé 76182e48382SNiek Linnenbank static void allwinner_sdhost_reset(DeviceState *dev) 76282e48382SNiek Linnenbank { 76382e48382SNiek Linnenbank AwSdHostState *s = AW_SDHOST(dev); 76482e48382SNiek Linnenbank 76582e48382SNiek Linnenbank s->global_ctl = REG_SD_GCTL_RST; 76682e48382SNiek Linnenbank s->clock_ctl = REG_SD_CKCR_RST; 76782e48382SNiek Linnenbank s->timeout = REG_SD_TMOR_RST; 76882e48382SNiek Linnenbank s->bus_width = REG_SD_BWDR_RST; 76982e48382SNiek Linnenbank s->block_size = REG_SD_BKSR_RST; 77082e48382SNiek Linnenbank s->byte_count = REG_SD_BYCR_RST; 77182e48382SNiek Linnenbank s->transfer_cnt = 0; 77282e48382SNiek Linnenbank 77382e48382SNiek Linnenbank s->command = REG_SD_CMDR_RST; 77482e48382SNiek Linnenbank s->command_arg = REG_SD_CAGR_RST; 77582e48382SNiek Linnenbank 77682e48382SNiek Linnenbank for (int i = 0; i < ARRAY_SIZE(s->response); i++) { 77782e48382SNiek Linnenbank s->response[i] = REG_SD_RESP_RST; 77882e48382SNiek Linnenbank } 77982e48382SNiek Linnenbank 78082e48382SNiek Linnenbank s->irq_mask = REG_SD_IMKR_RST; 78182e48382SNiek Linnenbank s->irq_status = REG_SD_RISR_RST; 78282e48382SNiek Linnenbank s->status = REG_SD_STAR_RST; 78382e48382SNiek Linnenbank 78482e48382SNiek Linnenbank s->fifo_wlevel = REG_SD_FWLR_RST; 78582e48382SNiek Linnenbank s->fifo_func_sel = REG_SD_FUNS_RST; 78682e48382SNiek Linnenbank s->debug_enable = REG_SD_DBGC_RST; 78782e48382SNiek Linnenbank s->auto12_arg = REG_SD_A12A_RST; 78882e48382SNiek Linnenbank s->newtiming_set = REG_SD_NTSR_RST; 78982e48382SNiek Linnenbank s->newtiming_debug = REG_SD_SDBG_RST; 79082e48382SNiek Linnenbank s->hardware_rst = REG_SD_HWRST_RST; 79182e48382SNiek Linnenbank s->dmac = REG_SD_DMAC_RST; 79282e48382SNiek Linnenbank s->desc_base = REG_SD_DLBA_RST; 79382e48382SNiek Linnenbank s->dmac_status = REG_SD_IDST_RST; 79482e48382SNiek Linnenbank s->dmac_irq = REG_SD_IDIE_RST; 79582e48382SNiek Linnenbank s->card_threshold = REG_SD_THLDC_RST; 79682e48382SNiek Linnenbank s->startbit_detect = REG_SD_DSBD_RST; 79782e48382SNiek Linnenbank s->response_crc = REG_SD_RES_CRC_RST; 79882e48382SNiek Linnenbank 79982e48382SNiek Linnenbank for (int i = 0; i < ARRAY_SIZE(s->data_crc); i++) { 80082e48382SNiek Linnenbank s->data_crc[i] = REG_SD_DATA_CRC_RST; 80182e48382SNiek Linnenbank } 80282e48382SNiek Linnenbank 80382e48382SNiek Linnenbank s->status_crc = REG_SD_CRC_STA_RST; 80482e48382SNiek Linnenbank } 80582e48382SNiek Linnenbank 80682e48382SNiek Linnenbank static void allwinner_sdhost_bus_class_init(ObjectClass *klass, void *data) 80782e48382SNiek Linnenbank { 80882e48382SNiek Linnenbank SDBusClass *sbc = SD_BUS_CLASS(klass); 80982e48382SNiek Linnenbank 81082e48382SNiek Linnenbank sbc->set_inserted = allwinner_sdhost_set_inserted; 81182e48382SNiek Linnenbank } 81282e48382SNiek Linnenbank 81382e48382SNiek Linnenbank static void allwinner_sdhost_class_init(ObjectClass *klass, void *data) 81482e48382SNiek Linnenbank { 81582e48382SNiek Linnenbank DeviceClass *dc = DEVICE_CLASS(klass); 81682e48382SNiek Linnenbank 81782e48382SNiek Linnenbank dc->reset = allwinner_sdhost_reset; 81882e48382SNiek Linnenbank dc->vmsd = &vmstate_allwinner_sdhost; 819b3aec952SPhilippe Mathieu-Daudé dc->realize = allwinner_sdhost_realize; 820b3aec952SPhilippe Mathieu-Daudé device_class_set_props(dc, allwinner_sdhost_properties); 82182e48382SNiek Linnenbank } 82282e48382SNiek Linnenbank 82382e48382SNiek Linnenbank static void allwinner_sdhost_sun4i_class_init(ObjectClass *klass, void *data) 82482e48382SNiek Linnenbank { 82582e48382SNiek Linnenbank AwSdHostClass *sc = AW_SDHOST_CLASS(klass); 82682e48382SNiek Linnenbank sc->max_desc_size = 8 * KiB; 82782e48382SNiek Linnenbank } 82882e48382SNiek Linnenbank 82982e48382SNiek Linnenbank static void allwinner_sdhost_sun5i_class_init(ObjectClass *klass, void *data) 83082e48382SNiek Linnenbank { 83182e48382SNiek Linnenbank AwSdHostClass *sc = AW_SDHOST_CLASS(klass); 83282e48382SNiek Linnenbank sc->max_desc_size = 64 * KiB; 83382e48382SNiek Linnenbank } 83482e48382SNiek Linnenbank 83582e48382SNiek Linnenbank static TypeInfo allwinner_sdhost_info = { 83682e48382SNiek Linnenbank .name = TYPE_AW_SDHOST, 83782e48382SNiek Linnenbank .parent = TYPE_SYS_BUS_DEVICE, 83882e48382SNiek Linnenbank .instance_init = allwinner_sdhost_init, 83982e48382SNiek Linnenbank .instance_size = sizeof(AwSdHostState), 84082e48382SNiek Linnenbank .class_init = allwinner_sdhost_class_init, 84182e48382SNiek Linnenbank .class_size = sizeof(AwSdHostClass), 84282e48382SNiek Linnenbank .abstract = true, 84382e48382SNiek Linnenbank }; 84482e48382SNiek Linnenbank 84582e48382SNiek Linnenbank static const TypeInfo allwinner_sdhost_sun4i_info = { 84682e48382SNiek Linnenbank .name = TYPE_AW_SDHOST_SUN4I, 84782e48382SNiek Linnenbank .parent = TYPE_AW_SDHOST, 84882e48382SNiek Linnenbank .class_init = allwinner_sdhost_sun4i_class_init, 84982e48382SNiek Linnenbank }; 85082e48382SNiek Linnenbank 85182e48382SNiek Linnenbank static const TypeInfo allwinner_sdhost_sun5i_info = { 85282e48382SNiek Linnenbank .name = TYPE_AW_SDHOST_SUN5I, 85382e48382SNiek Linnenbank .parent = TYPE_AW_SDHOST, 85482e48382SNiek Linnenbank .class_init = allwinner_sdhost_sun5i_class_init, 85582e48382SNiek Linnenbank }; 85682e48382SNiek Linnenbank 85782e48382SNiek Linnenbank static const TypeInfo allwinner_sdhost_bus_info = { 85882e48382SNiek Linnenbank .name = TYPE_AW_SDHOST_BUS, 85982e48382SNiek Linnenbank .parent = TYPE_SD_BUS, 86082e48382SNiek Linnenbank .instance_size = sizeof(SDBus), 86182e48382SNiek Linnenbank .class_init = allwinner_sdhost_bus_class_init, 86282e48382SNiek Linnenbank }; 86382e48382SNiek Linnenbank 86482e48382SNiek Linnenbank static void allwinner_sdhost_register_types(void) 86582e48382SNiek Linnenbank { 86682e48382SNiek Linnenbank type_register_static(&allwinner_sdhost_info); 86782e48382SNiek Linnenbank type_register_static(&allwinner_sdhost_sun4i_info); 86882e48382SNiek Linnenbank type_register_static(&allwinner_sdhost_sun5i_info); 86982e48382SNiek Linnenbank type_register_static(&allwinner_sdhost_bus_info); 87082e48382SNiek Linnenbank } 87182e48382SNiek Linnenbank 87282e48382SNiek Linnenbank type_init(allwinner_sdhost_register_types) 873