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