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