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" 2532cad1ffSPhilippe Mathieu-Daudé #include "system/blockdev.h" 2632cad1ffSPhilippe Mathieu-Daudé #include "system/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 */ 6893e2da36SStrahinja 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 */ 802c992b88Sqianfan Zhao REG_SD_SAMP_DL = 0x144, /* Sample Delay Control (sun50i-a64) */ 8182e48382SNiek Linnenbank REG_SD_FIFO = 0x200, /* Read/Write FIFO */ 8282e48382SNiek Linnenbank }; 8382e48382SNiek Linnenbank 8482e48382SNiek Linnenbank /* SD Host register flags */ 8582e48382SNiek Linnenbank enum { 8682e48382SNiek Linnenbank SD_GCTL_FIFO_AC_MOD = (1 << 31), 8782e48382SNiek Linnenbank SD_GCTL_DDR_MOD_SEL = (1 << 10), 8882e48382SNiek Linnenbank SD_GCTL_CD_DBC_ENB = (1 << 8), 8982e48382SNiek Linnenbank SD_GCTL_DMA_ENB = (1 << 5), 9082e48382SNiek Linnenbank SD_GCTL_INT_ENB = (1 << 4), 9182e48382SNiek Linnenbank SD_GCTL_DMA_RST = (1 << 2), 9282e48382SNiek Linnenbank SD_GCTL_FIFO_RST = (1 << 1), 9382e48382SNiek Linnenbank SD_GCTL_SOFT_RST = (1 << 0), 9482e48382SNiek Linnenbank }; 9582e48382SNiek Linnenbank 9682e48382SNiek Linnenbank enum { 9782e48382SNiek Linnenbank SD_CMDR_LOAD = (1 << 31), 9882e48382SNiek Linnenbank SD_CMDR_CLKCHANGE = (1 << 21), 9982e48382SNiek Linnenbank SD_CMDR_WRITE = (1 << 10), 10082e48382SNiek Linnenbank SD_CMDR_AUTOSTOP = (1 << 12), 10182e48382SNiek Linnenbank SD_CMDR_DATA = (1 << 9), 10282e48382SNiek Linnenbank SD_CMDR_RESPONSE_LONG = (1 << 7), 10382e48382SNiek Linnenbank SD_CMDR_RESPONSE = (1 << 6), 10482e48382SNiek Linnenbank SD_CMDR_CMDID_MASK = (0x3f), 10582e48382SNiek Linnenbank }; 10682e48382SNiek Linnenbank 10782e48382SNiek Linnenbank enum { 10882e48382SNiek Linnenbank SD_RISR_CARD_REMOVE = (1 << 31), 10982e48382SNiek Linnenbank SD_RISR_CARD_INSERT = (1 << 30), 11082e48382SNiek Linnenbank SD_RISR_SDIO_INTR = (1 << 16), 11182e48382SNiek Linnenbank SD_RISR_AUTOCMD_DONE = (1 << 14), 11282e48382SNiek Linnenbank SD_RISR_DATA_COMPLETE = (1 << 3), 11382e48382SNiek Linnenbank SD_RISR_CMD_COMPLETE = (1 << 2), 11482e48382SNiek Linnenbank SD_RISR_NO_RESPONSE = (1 << 1), 11582e48382SNiek Linnenbank }; 11682e48382SNiek Linnenbank 11782e48382SNiek Linnenbank enum { 118fd71f258SIcenowy Zheng SD_STAR_FIFO_EMPTY = (1 << 2), 11982e48382SNiek Linnenbank SD_STAR_CARD_PRESENT = (1 << 8), 120fd71f258SIcenowy Zheng SD_STAR_FIFO_LEVEL_1 = (1 << 17), 12182e48382SNiek Linnenbank }; 12282e48382SNiek Linnenbank 12382e48382SNiek Linnenbank enum { 12482e48382SNiek Linnenbank SD_IDST_INT_SUMMARY = (1 << 8), 12582e48382SNiek Linnenbank SD_IDST_RECEIVE_IRQ = (1 << 1), 12682e48382SNiek Linnenbank SD_IDST_TRANSMIT_IRQ = (1 << 0), 12782e48382SNiek Linnenbank SD_IDST_IRQ_MASK = (1 << 1) | (1 << 0) | (1 << 8), 12882e48382SNiek Linnenbank SD_IDST_WR_MASK = (0x3ff), 12982e48382SNiek Linnenbank }; 13082e48382SNiek Linnenbank 13182e48382SNiek Linnenbank /* SD Host register reset values */ 13282e48382SNiek Linnenbank enum { 13382e48382SNiek Linnenbank REG_SD_GCTL_RST = 0x00000300, 13482e48382SNiek Linnenbank REG_SD_CKCR_RST = 0x0, 13582e48382SNiek Linnenbank REG_SD_TMOR_RST = 0xFFFFFF40, 13682e48382SNiek Linnenbank REG_SD_BWDR_RST = 0x0, 13782e48382SNiek Linnenbank REG_SD_BKSR_RST = 0x00000200, 13882e48382SNiek Linnenbank REG_SD_BYCR_RST = 0x00000200, 13982e48382SNiek Linnenbank REG_SD_CMDR_RST = 0x0, 14082e48382SNiek Linnenbank REG_SD_CAGR_RST = 0x0, 14182e48382SNiek Linnenbank REG_SD_RESP_RST = 0x0, 14282e48382SNiek Linnenbank REG_SD_IMKR_RST = 0x0, 14382e48382SNiek Linnenbank REG_SD_MISR_RST = 0x0, 14482e48382SNiek Linnenbank REG_SD_RISR_RST = 0x0, 14582e48382SNiek Linnenbank REG_SD_STAR_RST = 0x00000100, 14682e48382SNiek Linnenbank REG_SD_FWLR_RST = 0x000F0000, 14782e48382SNiek Linnenbank REG_SD_FUNS_RST = 0x0, 14882e48382SNiek Linnenbank REG_SD_DBGC_RST = 0x0, 14982e48382SNiek Linnenbank REG_SD_A12A_RST = 0x0000FFFF, 15082e48382SNiek Linnenbank REG_SD_NTSR_RST = 0x00000001, 15182e48382SNiek Linnenbank REG_SD_SDBG_RST = 0x0, 15282e48382SNiek Linnenbank REG_SD_HWRST_RST = 0x00000001, 15382e48382SNiek Linnenbank REG_SD_DMAC_RST = 0x0, 15482e48382SNiek Linnenbank REG_SD_DLBA_RST = 0x0, 15582e48382SNiek Linnenbank REG_SD_IDST_RST = 0x0, 15682e48382SNiek Linnenbank REG_SD_IDIE_RST = 0x0, 15782e48382SNiek Linnenbank REG_SD_THLDC_RST = 0x0, 15882e48382SNiek Linnenbank REG_SD_DSBD_RST = 0x0, 15982e48382SNiek Linnenbank REG_SD_RES_CRC_RST = 0x0, 16082e48382SNiek Linnenbank REG_SD_DATA_CRC_RST = 0x0, 16182e48382SNiek Linnenbank REG_SD_CRC_STA_RST = 0x0, 1622c992b88Sqianfan Zhao REG_SD_SAMPLE_DL_RST = 0x00002000, 16382e48382SNiek Linnenbank REG_SD_FIFO_RST = 0x0, 16482e48382SNiek Linnenbank }; 16582e48382SNiek Linnenbank 16682e48382SNiek Linnenbank /* Data transfer descriptor for DMA */ 16782e48382SNiek Linnenbank typedef struct TransferDescriptor { 16882e48382SNiek Linnenbank uint32_t status; /* Status flags */ 16982e48382SNiek Linnenbank uint32_t size; /* Data buffer size */ 17082e48382SNiek Linnenbank uint32_t addr; /* Data buffer address */ 17182e48382SNiek Linnenbank uint32_t next; /* Physical address of next descriptor */ 17282e48382SNiek Linnenbank } TransferDescriptor; 17382e48382SNiek Linnenbank 17482e48382SNiek Linnenbank /* Data transfer descriptor flags */ 17582e48382SNiek Linnenbank enum { 17682e48382SNiek Linnenbank DESC_STATUS_HOLD = (1 << 31), /* Set when descriptor is in use by DMA */ 17782e48382SNiek Linnenbank DESC_STATUS_ERROR = (1 << 30), /* Set when DMA transfer error occurred */ 17882e48382SNiek Linnenbank DESC_STATUS_CHAIN = (1 << 4), /* Indicates chained descriptor. */ 17982e48382SNiek Linnenbank DESC_STATUS_FIRST = (1 << 3), /* Set on the first descriptor */ 18082e48382SNiek Linnenbank DESC_STATUS_LAST = (1 << 2), /* Set on the last descriptor */ 18182e48382SNiek Linnenbank DESC_STATUS_NOIRQ = (1 << 1), /* Skip raising interrupt after transfer */ 18282e48382SNiek Linnenbank DESC_SIZE_MASK = (0xfffffffc) 18382e48382SNiek Linnenbank }; 18482e48382SNiek Linnenbank 18582e48382SNiek Linnenbank static void allwinner_sdhost_update_irq(AwSdHostState *s) 18682e48382SNiek Linnenbank { 18782e48382SNiek Linnenbank uint32_t irq; 18882e48382SNiek Linnenbank 18982e48382SNiek Linnenbank if (s->global_ctl & SD_GCTL_INT_ENB) { 19082e48382SNiek Linnenbank irq = s->irq_status & s->irq_mask; 19182e48382SNiek Linnenbank } else { 19282e48382SNiek Linnenbank irq = 0; 19382e48382SNiek Linnenbank } 19482e48382SNiek Linnenbank 19582e48382SNiek Linnenbank trace_allwinner_sdhost_update_irq(irq); 19622c81783SPeter Maydell qemu_set_irq(s->irq, !!irq); 19782e48382SNiek Linnenbank } 19882e48382SNiek Linnenbank 19982e48382SNiek Linnenbank static void allwinner_sdhost_update_transfer_cnt(AwSdHostState *s, 20082e48382SNiek Linnenbank uint32_t bytes) 20182e48382SNiek Linnenbank { 20282e48382SNiek Linnenbank if (s->transfer_cnt > bytes) { 20382e48382SNiek Linnenbank s->transfer_cnt -= bytes; 20482e48382SNiek Linnenbank } else { 20582e48382SNiek Linnenbank s->transfer_cnt = 0; 20682e48382SNiek Linnenbank } 20782e48382SNiek Linnenbank 20882e48382SNiek Linnenbank if (!s->transfer_cnt) { 20982e48382SNiek Linnenbank s->irq_status |= SD_RISR_DATA_COMPLETE; 21082e48382SNiek Linnenbank } 21182e48382SNiek Linnenbank } 21282e48382SNiek Linnenbank 21382e48382SNiek Linnenbank static void allwinner_sdhost_set_inserted(DeviceState *dev, bool inserted) 21482e48382SNiek Linnenbank { 21582e48382SNiek Linnenbank AwSdHostState *s = AW_SDHOST(dev); 21682e48382SNiek Linnenbank 21782e48382SNiek Linnenbank trace_allwinner_sdhost_set_inserted(inserted); 21882e48382SNiek Linnenbank 21982e48382SNiek Linnenbank if (inserted) { 22082e48382SNiek Linnenbank s->irq_status |= SD_RISR_CARD_INSERT; 22182e48382SNiek Linnenbank s->irq_status &= ~SD_RISR_CARD_REMOVE; 22282e48382SNiek Linnenbank s->status |= SD_STAR_CARD_PRESENT; 22382e48382SNiek Linnenbank } else { 22482e48382SNiek Linnenbank s->irq_status &= ~SD_RISR_CARD_INSERT; 22582e48382SNiek Linnenbank s->irq_status |= SD_RISR_CARD_REMOVE; 22682e48382SNiek Linnenbank s->status &= ~SD_STAR_CARD_PRESENT; 22782e48382SNiek Linnenbank } 22882e48382SNiek Linnenbank 22982e48382SNiek Linnenbank allwinner_sdhost_update_irq(s); 23082e48382SNiek Linnenbank } 23182e48382SNiek Linnenbank 23282e48382SNiek Linnenbank static void allwinner_sdhost_send_command(AwSdHostState *s) 23382e48382SNiek Linnenbank { 23482e48382SNiek Linnenbank SDRequest request; 23582e48382SNiek Linnenbank uint8_t resp[16]; 23682e48382SNiek Linnenbank int rlen; 23782e48382SNiek Linnenbank 23882e48382SNiek Linnenbank /* Auto clear load flag */ 23982e48382SNiek Linnenbank s->command &= ~SD_CMDR_LOAD; 24082e48382SNiek Linnenbank 24182e48382SNiek Linnenbank /* Clock change does not actually interact with the SD bus */ 24282e48382SNiek Linnenbank if (!(s->command & SD_CMDR_CLKCHANGE)) { 24382e48382SNiek Linnenbank 24482e48382SNiek Linnenbank /* Prepare request */ 24582e48382SNiek Linnenbank request.cmd = s->command & SD_CMDR_CMDID_MASK; 24682e48382SNiek Linnenbank request.arg = s->command_arg; 24782e48382SNiek Linnenbank 24882e48382SNiek Linnenbank /* Send request to SD bus */ 24982e48382SNiek Linnenbank rlen = sdbus_do_command(&s->sdbus, &request, resp); 25082e48382SNiek Linnenbank if (rlen < 0) { 25182e48382SNiek Linnenbank goto error; 25282e48382SNiek Linnenbank } 25382e48382SNiek Linnenbank 25482e48382SNiek Linnenbank /* If the command has a response, store it in the response registers */ 25582e48382SNiek Linnenbank if ((s->command & SD_CMDR_RESPONSE)) { 25682e48382SNiek Linnenbank if (rlen == 4 && !(s->command & SD_CMDR_RESPONSE_LONG)) { 25782e48382SNiek Linnenbank s->response[0] = ldl_be_p(&resp[0]); 25882e48382SNiek Linnenbank s->response[1] = s->response[2] = s->response[3] = 0; 25982e48382SNiek Linnenbank 26082e48382SNiek Linnenbank } else if (rlen == 16 && (s->command & SD_CMDR_RESPONSE_LONG)) { 26182e48382SNiek Linnenbank s->response[0] = ldl_be_p(&resp[12]); 26282e48382SNiek Linnenbank s->response[1] = ldl_be_p(&resp[8]); 26382e48382SNiek Linnenbank s->response[2] = ldl_be_p(&resp[4]); 26482e48382SNiek Linnenbank s->response[3] = ldl_be_p(&resp[0]); 26582e48382SNiek Linnenbank } else { 26682e48382SNiek Linnenbank goto error; 26782e48382SNiek Linnenbank } 26882e48382SNiek Linnenbank } 26982e48382SNiek Linnenbank } 27082e48382SNiek Linnenbank 27182e48382SNiek Linnenbank /* Set interrupt status bits */ 27282e48382SNiek Linnenbank s->irq_status |= SD_RISR_CMD_COMPLETE; 27382e48382SNiek Linnenbank return; 27482e48382SNiek Linnenbank 27582e48382SNiek Linnenbank error: 27682e48382SNiek Linnenbank s->irq_status |= SD_RISR_NO_RESPONSE; 27782e48382SNiek Linnenbank } 27882e48382SNiek Linnenbank 27982e48382SNiek Linnenbank static void allwinner_sdhost_auto_stop(AwSdHostState *s) 28082e48382SNiek Linnenbank { 28182e48382SNiek Linnenbank /* 28282e48382SNiek Linnenbank * The stop command (CMD12) ensures the SD bus 28382e48382SNiek Linnenbank * returns to the transfer state. 28482e48382SNiek Linnenbank */ 28582e48382SNiek Linnenbank if ((s->command & SD_CMDR_AUTOSTOP) && (s->transfer_cnt == 0)) { 28682e48382SNiek Linnenbank /* First save current command registers */ 28782e48382SNiek Linnenbank uint32_t saved_cmd = s->command; 28882e48382SNiek Linnenbank uint32_t saved_arg = s->command_arg; 28982e48382SNiek Linnenbank 29082e48382SNiek Linnenbank /* Prepare stop command (CMD12) */ 29182e48382SNiek Linnenbank s->command &= ~SD_CMDR_CMDID_MASK; 29282e48382SNiek Linnenbank s->command |= 12; /* CMD12 */ 29382e48382SNiek Linnenbank s->command_arg = 0; 29482e48382SNiek Linnenbank 29582e48382SNiek Linnenbank /* Put the command on SD bus */ 29682e48382SNiek Linnenbank allwinner_sdhost_send_command(s); 29782e48382SNiek Linnenbank 29882e48382SNiek Linnenbank /* Restore command values */ 29982e48382SNiek Linnenbank s->command = saved_cmd; 30082e48382SNiek Linnenbank s->command_arg = saved_arg; 30182e48382SNiek Linnenbank 30282e48382SNiek Linnenbank /* Set IRQ status bit for automatic stop done */ 30382e48382SNiek Linnenbank s->irq_status |= SD_RISR_AUTOCMD_DONE; 30482e48382SNiek Linnenbank } 30582e48382SNiek Linnenbank } 30682e48382SNiek Linnenbank 3073e20d908SPeter Maydell static void read_descriptor(AwSdHostState *s, hwaddr desc_addr, 3083e20d908SPeter Maydell TransferDescriptor *desc) 3093e20d908SPeter Maydell { 3103e20d908SPeter Maydell uint32_t desc_words[4]; 3113e20d908SPeter Maydell dma_memory_read(&s->dma_as, desc_addr, &desc_words, sizeof(desc_words), 3123e20d908SPeter Maydell MEMTXATTRS_UNSPECIFIED); 3133e20d908SPeter Maydell desc->status = le32_to_cpu(desc_words[0]); 3143e20d908SPeter Maydell desc->size = le32_to_cpu(desc_words[1]); 3153e20d908SPeter Maydell desc->addr = le32_to_cpu(desc_words[2]); 3163e20d908SPeter Maydell desc->next = le32_to_cpu(desc_words[3]); 3173e20d908SPeter Maydell } 3183e20d908SPeter Maydell 3193e20d908SPeter Maydell static void write_descriptor(AwSdHostState *s, hwaddr desc_addr, 3203e20d908SPeter Maydell const TransferDescriptor *desc) 3213e20d908SPeter Maydell { 3223e20d908SPeter Maydell uint32_t desc_words[4]; 3233e20d908SPeter Maydell desc_words[0] = cpu_to_le32(desc->status); 3243e20d908SPeter Maydell desc_words[1] = cpu_to_le32(desc->size); 3253e20d908SPeter Maydell desc_words[2] = cpu_to_le32(desc->addr); 3263e20d908SPeter Maydell desc_words[3] = cpu_to_le32(desc->next); 3273e20d908SPeter Maydell dma_memory_write(&s->dma_as, desc_addr, &desc_words, sizeof(desc_words), 3283e20d908SPeter Maydell MEMTXATTRS_UNSPECIFIED); 3293e20d908SPeter Maydell } 3303e20d908SPeter Maydell 33182e48382SNiek Linnenbank static uint32_t allwinner_sdhost_process_desc(AwSdHostState *s, 33282e48382SNiek Linnenbank hwaddr desc_addr, 33382e48382SNiek Linnenbank TransferDescriptor *desc, 33482e48382SNiek Linnenbank bool is_write, uint32_t max_bytes) 33582e48382SNiek Linnenbank { 33682e48382SNiek Linnenbank AwSdHostClass *klass = AW_SDHOST_GET_CLASS(s); 33782e48382SNiek Linnenbank uint32_t num_done = 0; 33882e48382SNiek Linnenbank uint32_t num_bytes = max_bytes; 33982e48382SNiek Linnenbank uint8_t buf[1024]; 34082e48382SNiek Linnenbank 3413e20d908SPeter Maydell read_descriptor(s, desc_addr, desc); 34282e48382SNiek Linnenbank if (desc->size == 0) { 34382e48382SNiek Linnenbank desc->size = klass->max_desc_size; 34482e48382SNiek Linnenbank } else if (desc->size > klass->max_desc_size) { 34582e48382SNiek Linnenbank qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA descriptor buffer size " 34682e48382SNiek Linnenbank " is out-of-bounds: %" PRIu32 " > %zu", 34782e48382SNiek Linnenbank __func__, desc->size, klass->max_desc_size); 34882e48382SNiek Linnenbank desc->size = klass->max_desc_size; 34982e48382SNiek Linnenbank } 35082e48382SNiek Linnenbank if (desc->size < num_bytes) { 35182e48382SNiek Linnenbank num_bytes = desc->size; 35282e48382SNiek Linnenbank } 35382e48382SNiek Linnenbank 35482e48382SNiek Linnenbank trace_allwinner_sdhost_process_desc(desc_addr, desc->size, 35582e48382SNiek Linnenbank is_write, max_bytes); 35682e48382SNiek Linnenbank 35782e48382SNiek Linnenbank while (num_done < num_bytes) { 35882e48382SNiek Linnenbank /* Try to completely fill the local buffer */ 35982e48382SNiek Linnenbank uint32_t buf_bytes = num_bytes - num_done; 36082e48382SNiek Linnenbank if (buf_bytes > sizeof(buf)) { 36182e48382SNiek Linnenbank buf_bytes = sizeof(buf); 36282e48382SNiek Linnenbank } 36382e48382SNiek Linnenbank 36482e48382SNiek Linnenbank /* Write to SD bus */ 36582e48382SNiek Linnenbank if (is_write) { 366b3aec952SPhilippe Mathieu-Daudé dma_memory_read(&s->dma_as, 367ba06fe8aSPhilippe Mathieu-Daudé (desc->addr & DESC_SIZE_MASK) + num_done, buf, 368ba06fe8aSPhilippe Mathieu-Daudé buf_bytes, MEMTXATTRS_UNSPECIFIED); 36962a21be6SPhilippe Mathieu-Daudé sdbus_write_data(&s->sdbus, buf, buf_bytes); 37082e48382SNiek Linnenbank 37182e48382SNiek Linnenbank /* Read from SD bus */ 37282e48382SNiek Linnenbank } else { 373618e0be1SPhilippe Mathieu-Daudé sdbus_read_data(&s->sdbus, buf, buf_bytes); 374b3aec952SPhilippe Mathieu-Daudé dma_memory_write(&s->dma_as, 375ba06fe8aSPhilippe Mathieu-Daudé (desc->addr & DESC_SIZE_MASK) + num_done, buf, 376ba06fe8aSPhilippe Mathieu-Daudé buf_bytes, MEMTXATTRS_UNSPECIFIED); 37782e48382SNiek Linnenbank } 37882e48382SNiek Linnenbank num_done += buf_bytes; 37982e48382SNiek Linnenbank } 38082e48382SNiek Linnenbank 38182e48382SNiek Linnenbank /* Clear hold flag and flush descriptor */ 38282e48382SNiek Linnenbank desc->status &= ~DESC_STATUS_HOLD; 3833e20d908SPeter Maydell write_descriptor(s, desc_addr, desc); 38482e48382SNiek Linnenbank 38582e48382SNiek Linnenbank return num_done; 38682e48382SNiek Linnenbank } 38782e48382SNiek Linnenbank 38882e48382SNiek Linnenbank static void allwinner_sdhost_dma(AwSdHostState *s) 38982e48382SNiek Linnenbank { 39082e48382SNiek Linnenbank TransferDescriptor desc; 39182e48382SNiek Linnenbank hwaddr desc_addr = s->desc_base; 39282e48382SNiek Linnenbank bool is_write = (s->command & SD_CMDR_WRITE); 39382e48382SNiek Linnenbank uint32_t bytes_done = 0; 39482e48382SNiek Linnenbank 39582e48382SNiek Linnenbank /* Check if DMA can be performed */ 39682e48382SNiek Linnenbank if (s->byte_count == 0 || s->block_size == 0 || 39782e48382SNiek Linnenbank !(s->global_ctl & SD_GCTL_DMA_ENB)) { 39882e48382SNiek Linnenbank return; 39982e48382SNiek Linnenbank } 40082e48382SNiek Linnenbank 40182e48382SNiek Linnenbank /* 40282e48382SNiek Linnenbank * For read operations, data must be available on the SD bus 40382e48382SNiek Linnenbank * If not, it is an error and we should not act at all 40482e48382SNiek Linnenbank */ 40582e48382SNiek Linnenbank if (!is_write && !sdbus_data_ready(&s->sdbus)) { 40682e48382SNiek Linnenbank return; 40782e48382SNiek Linnenbank } 40882e48382SNiek Linnenbank 40982e48382SNiek Linnenbank /* Process the DMA descriptors until all data is copied */ 41082e48382SNiek Linnenbank while (s->byte_count > 0) { 41182e48382SNiek Linnenbank bytes_done = allwinner_sdhost_process_desc(s, desc_addr, &desc, 41282e48382SNiek Linnenbank is_write, s->byte_count); 41382e48382SNiek Linnenbank allwinner_sdhost_update_transfer_cnt(s, bytes_done); 41482e48382SNiek Linnenbank 41582e48382SNiek Linnenbank if (bytes_done <= s->byte_count) { 41682e48382SNiek Linnenbank s->byte_count -= bytes_done; 41782e48382SNiek Linnenbank } else { 41882e48382SNiek Linnenbank s->byte_count = 0; 41982e48382SNiek Linnenbank } 42082e48382SNiek Linnenbank 42182e48382SNiek Linnenbank if (desc.status & DESC_STATUS_LAST) { 42282e48382SNiek Linnenbank break; 42382e48382SNiek Linnenbank } else { 42482e48382SNiek Linnenbank desc_addr = desc.next; 42582e48382SNiek Linnenbank } 42682e48382SNiek Linnenbank } 42782e48382SNiek Linnenbank 42882e48382SNiek Linnenbank /* Raise IRQ to signal DMA is completed */ 42982e48382SNiek Linnenbank s->irq_status |= SD_RISR_DATA_COMPLETE | SD_RISR_SDIO_INTR; 43082e48382SNiek Linnenbank 43182e48382SNiek Linnenbank /* Update DMAC bits */ 43282e48382SNiek Linnenbank s->dmac_status |= SD_IDST_INT_SUMMARY; 43382e48382SNiek Linnenbank 43482e48382SNiek Linnenbank if (is_write) { 43582e48382SNiek Linnenbank s->dmac_status |= SD_IDST_TRANSMIT_IRQ; 43682e48382SNiek Linnenbank } else { 43782e48382SNiek Linnenbank s->dmac_status |= SD_IDST_RECEIVE_IRQ; 43882e48382SNiek Linnenbank } 43982e48382SNiek Linnenbank } 44082e48382SNiek Linnenbank 44193e2da36SStrahinja Jankovic static uint32_t allwinner_sdhost_fifo_read(AwSdHostState *s) 44293e2da36SStrahinja Jankovic { 44393e2da36SStrahinja Jankovic uint32_t res = 0; 44493e2da36SStrahinja Jankovic 44593e2da36SStrahinja Jankovic if (sdbus_data_ready(&s->sdbus)) { 44693e2da36SStrahinja Jankovic sdbus_read_data(&s->sdbus, &res, sizeof(uint32_t)); 44793e2da36SStrahinja Jankovic le32_to_cpus(&res); 44893e2da36SStrahinja Jankovic allwinner_sdhost_update_transfer_cnt(s, sizeof(uint32_t)); 44993e2da36SStrahinja Jankovic allwinner_sdhost_auto_stop(s); 45093e2da36SStrahinja Jankovic allwinner_sdhost_update_irq(s); 45193e2da36SStrahinja Jankovic } else { 45293e2da36SStrahinja Jankovic qemu_log_mask(LOG_GUEST_ERROR, "%s: no data ready on SD bus\n", 45393e2da36SStrahinja Jankovic __func__); 45493e2da36SStrahinja Jankovic } 45593e2da36SStrahinja Jankovic 45693e2da36SStrahinja Jankovic return res; 45793e2da36SStrahinja Jankovic } 45893e2da36SStrahinja Jankovic 45982e48382SNiek Linnenbank static uint64_t allwinner_sdhost_read(void *opaque, hwaddr offset, 46082e48382SNiek Linnenbank unsigned size) 46182e48382SNiek Linnenbank { 46282e48382SNiek Linnenbank AwSdHostState *s = AW_SDHOST(opaque); 46393e2da36SStrahinja Jankovic AwSdHostClass *sc = AW_SDHOST_GET_CLASS(s); 4642c992b88Sqianfan Zhao bool out_of_bounds = false; 46582e48382SNiek Linnenbank uint32_t res = 0; 46682e48382SNiek Linnenbank 46782e48382SNiek Linnenbank switch (offset) { 46882e48382SNiek Linnenbank case REG_SD_GCTL: /* Global Control */ 46982e48382SNiek Linnenbank res = s->global_ctl; 47082e48382SNiek Linnenbank break; 47182e48382SNiek Linnenbank case REG_SD_CKCR: /* Clock Control */ 47282e48382SNiek Linnenbank res = s->clock_ctl; 47382e48382SNiek Linnenbank break; 47482e48382SNiek Linnenbank case REG_SD_TMOR: /* Timeout */ 47582e48382SNiek Linnenbank res = s->timeout; 47682e48382SNiek Linnenbank break; 47782e48382SNiek Linnenbank case REG_SD_BWDR: /* Bus Width */ 47882e48382SNiek Linnenbank res = s->bus_width; 47982e48382SNiek Linnenbank break; 48082e48382SNiek Linnenbank case REG_SD_BKSR: /* Block Size */ 48182e48382SNiek Linnenbank res = s->block_size; 48282e48382SNiek Linnenbank break; 48382e48382SNiek Linnenbank case REG_SD_BYCR: /* Byte Count */ 48482e48382SNiek Linnenbank res = s->byte_count; 48582e48382SNiek Linnenbank break; 48682e48382SNiek Linnenbank case REG_SD_CMDR: /* Command */ 48782e48382SNiek Linnenbank res = s->command; 48882e48382SNiek Linnenbank break; 48982e48382SNiek Linnenbank case REG_SD_CAGR: /* Command Argument */ 49082e48382SNiek Linnenbank res = s->command_arg; 49182e48382SNiek Linnenbank break; 49282e48382SNiek Linnenbank case REG_SD_RESP0: /* Response Zero */ 49382e48382SNiek Linnenbank res = s->response[0]; 49482e48382SNiek Linnenbank break; 49582e48382SNiek Linnenbank case REG_SD_RESP1: /* Response One */ 49682e48382SNiek Linnenbank res = s->response[1]; 49782e48382SNiek Linnenbank break; 49882e48382SNiek Linnenbank case REG_SD_RESP2: /* Response Two */ 49982e48382SNiek Linnenbank res = s->response[2]; 50082e48382SNiek Linnenbank break; 50182e48382SNiek Linnenbank case REG_SD_RESP3: /* Response Three */ 50282e48382SNiek Linnenbank res = s->response[3]; 50382e48382SNiek Linnenbank break; 50482e48382SNiek Linnenbank case REG_SD_IMKR: /* Interrupt Mask */ 50582e48382SNiek Linnenbank res = s->irq_mask; 50682e48382SNiek Linnenbank break; 50782e48382SNiek Linnenbank case REG_SD_MISR: /* Masked Interrupt Status */ 50882e48382SNiek Linnenbank res = s->irq_status & s->irq_mask; 50982e48382SNiek Linnenbank break; 51082e48382SNiek Linnenbank case REG_SD_RISR: /* Raw Interrupt Status */ 51182e48382SNiek Linnenbank res = s->irq_status; 51282e48382SNiek Linnenbank break; 51382e48382SNiek Linnenbank case REG_SD_STAR: /* Status */ 51482e48382SNiek Linnenbank res = s->status; 515fd71f258SIcenowy Zheng if (sdbus_data_ready(&s->sdbus)) { 516fd71f258SIcenowy Zheng res |= SD_STAR_FIFO_LEVEL_1; 517fd71f258SIcenowy Zheng } else { 518fd71f258SIcenowy Zheng res |= SD_STAR_FIFO_EMPTY; 519fd71f258SIcenowy Zheng } 52082e48382SNiek Linnenbank break; 52182e48382SNiek Linnenbank case REG_SD_FWLR: /* FIFO Water Level */ 52282e48382SNiek Linnenbank res = s->fifo_wlevel; 52382e48382SNiek Linnenbank break; 52482e48382SNiek Linnenbank case REG_SD_FUNS: /* FIFO Function Select */ 52582e48382SNiek Linnenbank res = s->fifo_func_sel; 52682e48382SNiek Linnenbank break; 52782e48382SNiek Linnenbank case REG_SD_DBGC: /* Debug Enable */ 52882e48382SNiek Linnenbank res = s->debug_enable; 52982e48382SNiek Linnenbank break; 53082e48382SNiek Linnenbank case REG_SD_A12A: /* Auto command 12 argument */ 53182e48382SNiek Linnenbank res = s->auto12_arg; 53282e48382SNiek Linnenbank break; 53382e48382SNiek Linnenbank case REG_SD_NTSR: /* SD NewTiming Set */ 53482e48382SNiek Linnenbank res = s->newtiming_set; 53582e48382SNiek Linnenbank break; 53682e48382SNiek Linnenbank case REG_SD_SDBG: /* SD newTiming Set Debug */ 53782e48382SNiek Linnenbank res = s->newtiming_debug; 53882e48382SNiek Linnenbank break; 53982e48382SNiek Linnenbank case REG_SD_HWRST: /* Hardware Reset Register */ 54082e48382SNiek Linnenbank res = s->hardware_rst; 54182e48382SNiek Linnenbank break; 54282e48382SNiek Linnenbank case REG_SD_DMAC: /* Internal DMA Controller Control */ 54382e48382SNiek Linnenbank res = s->dmac; 54482e48382SNiek Linnenbank break; 54582e48382SNiek Linnenbank case REG_SD_DLBA: /* Descriptor List Base Address */ 54682e48382SNiek Linnenbank res = s->desc_base; 54782e48382SNiek Linnenbank break; 54882e48382SNiek Linnenbank case REG_SD_IDST: /* Internal DMA Controller Status */ 54982e48382SNiek Linnenbank res = s->dmac_status; 55082e48382SNiek Linnenbank break; 55182e48382SNiek Linnenbank case REG_SD_IDIE: /* Internal DMA Controller Interrupt Enable */ 55282e48382SNiek Linnenbank res = s->dmac_irq; 55382e48382SNiek Linnenbank break; 55493e2da36SStrahinja Jankovic case REG_SD_THLDC: /* Card Threshold Control or FIFO register (sun4i) */ 55593e2da36SStrahinja Jankovic if (sc->is_sun4i) { 55693e2da36SStrahinja Jankovic res = allwinner_sdhost_fifo_read(s); 55793e2da36SStrahinja Jankovic } else { 55882e48382SNiek Linnenbank res = s->card_threshold; 55993e2da36SStrahinja Jankovic } 56082e48382SNiek Linnenbank break; 56182e48382SNiek Linnenbank case REG_SD_DSBD: /* eMMC DDR Start Bit Detection Control */ 56282e48382SNiek Linnenbank res = s->startbit_detect; 56382e48382SNiek Linnenbank break; 56482e48382SNiek Linnenbank case REG_SD_RES_CRC: /* Response CRC from card/eMMC */ 56582e48382SNiek Linnenbank res = s->response_crc; 56682e48382SNiek Linnenbank break; 56782e48382SNiek Linnenbank case REG_SD_DATA7_CRC: /* CRC Data 7 from card/eMMC */ 56882e48382SNiek Linnenbank case REG_SD_DATA6_CRC: /* CRC Data 6 from card/eMMC */ 56982e48382SNiek Linnenbank case REG_SD_DATA5_CRC: /* CRC Data 5 from card/eMMC */ 57082e48382SNiek Linnenbank case REG_SD_DATA4_CRC: /* CRC Data 4 from card/eMMC */ 57182e48382SNiek Linnenbank case REG_SD_DATA3_CRC: /* CRC Data 3 from card/eMMC */ 57282e48382SNiek Linnenbank case REG_SD_DATA2_CRC: /* CRC Data 2 from card/eMMC */ 57382e48382SNiek Linnenbank case REG_SD_DATA1_CRC: /* CRC Data 1 from card/eMMC */ 57482e48382SNiek Linnenbank case REG_SD_DATA0_CRC: /* CRC Data 0 from card/eMMC */ 57582e48382SNiek Linnenbank res = s->data_crc[((offset - REG_SD_DATA7_CRC) / sizeof(uint32_t))]; 57682e48382SNiek Linnenbank break; 57782e48382SNiek Linnenbank case REG_SD_CRC_STA: /* CRC status from card/eMMC in write operation */ 57882e48382SNiek Linnenbank res = s->status_crc; 57982e48382SNiek Linnenbank break; 58082e48382SNiek Linnenbank case REG_SD_FIFO: /* Read/Write FIFO */ 58193e2da36SStrahinja Jankovic res = allwinner_sdhost_fifo_read(s); 58282e48382SNiek Linnenbank break; 5832c992b88Sqianfan Zhao case REG_SD_SAMP_DL: /* Sample Delay */ 5842c992b88Sqianfan Zhao if (sc->can_calibrate) { 5852c992b88Sqianfan Zhao res = s->sample_delay; 5862c992b88Sqianfan Zhao } else { 5872c992b88Sqianfan Zhao out_of_bounds = true; 5882c992b88Sqianfan Zhao } 5892c992b88Sqianfan Zhao break; 59082e48382SNiek Linnenbank default: 5912c992b88Sqianfan Zhao out_of_bounds = true; 59282e48382SNiek Linnenbank res = 0; 59382e48382SNiek Linnenbank break; 59482e48382SNiek Linnenbank } 59582e48382SNiek Linnenbank 5962c992b88Sqianfan Zhao if (out_of_bounds) { 5972c992b88Sqianfan Zhao qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset %" 5982c992b88Sqianfan Zhao HWADDR_PRIx"\n", __func__, offset); 5992c992b88Sqianfan Zhao } 6002c992b88Sqianfan Zhao 60182e48382SNiek Linnenbank trace_allwinner_sdhost_read(offset, res, size); 60282e48382SNiek Linnenbank return res; 60382e48382SNiek Linnenbank } 60482e48382SNiek Linnenbank 60593e2da36SStrahinja Jankovic static void allwinner_sdhost_fifo_write(AwSdHostState *s, uint64_t value) 60693e2da36SStrahinja Jankovic { 60793e2da36SStrahinja Jankovic uint32_t u32 = cpu_to_le32(value); 60893e2da36SStrahinja Jankovic sdbus_write_data(&s->sdbus, &u32, sizeof(u32)); 60993e2da36SStrahinja Jankovic allwinner_sdhost_update_transfer_cnt(s, sizeof(u32)); 61093e2da36SStrahinja Jankovic allwinner_sdhost_auto_stop(s); 61193e2da36SStrahinja Jankovic allwinner_sdhost_update_irq(s); 61293e2da36SStrahinja Jankovic } 61393e2da36SStrahinja Jankovic 61482e48382SNiek Linnenbank static void allwinner_sdhost_write(void *opaque, hwaddr offset, 61582e48382SNiek Linnenbank uint64_t value, unsigned size) 61682e48382SNiek Linnenbank { 61782e48382SNiek Linnenbank AwSdHostState *s = AW_SDHOST(opaque); 61893e2da36SStrahinja Jankovic AwSdHostClass *sc = AW_SDHOST_GET_CLASS(s); 6192c992b88Sqianfan Zhao bool out_of_bounds = false; 62082e48382SNiek Linnenbank 62182e48382SNiek Linnenbank trace_allwinner_sdhost_write(offset, value, size); 62282e48382SNiek Linnenbank 62382e48382SNiek Linnenbank switch (offset) { 62482e48382SNiek Linnenbank case REG_SD_GCTL: /* Global Control */ 62582e48382SNiek Linnenbank s->global_ctl = value; 62682e48382SNiek Linnenbank s->global_ctl &= ~(SD_GCTL_DMA_RST | SD_GCTL_FIFO_RST | 62782e48382SNiek Linnenbank SD_GCTL_SOFT_RST); 62882e48382SNiek Linnenbank allwinner_sdhost_update_irq(s); 62982e48382SNiek Linnenbank break; 63082e48382SNiek Linnenbank case REG_SD_CKCR: /* Clock Control */ 63182e48382SNiek Linnenbank s->clock_ctl = value; 63282e48382SNiek Linnenbank break; 63382e48382SNiek Linnenbank case REG_SD_TMOR: /* Timeout */ 63482e48382SNiek Linnenbank s->timeout = value; 63582e48382SNiek Linnenbank break; 63682e48382SNiek Linnenbank case REG_SD_BWDR: /* Bus Width */ 63782e48382SNiek Linnenbank s->bus_width = value; 63882e48382SNiek Linnenbank break; 63982e48382SNiek Linnenbank case REG_SD_BKSR: /* Block Size */ 64082e48382SNiek Linnenbank s->block_size = value; 64182e48382SNiek Linnenbank break; 64282e48382SNiek Linnenbank case REG_SD_BYCR: /* Byte Count */ 64382e48382SNiek Linnenbank s->byte_count = value; 64482e48382SNiek Linnenbank s->transfer_cnt = value; 64582e48382SNiek Linnenbank break; 64682e48382SNiek Linnenbank case REG_SD_CMDR: /* Command */ 64782e48382SNiek Linnenbank s->command = value; 64882e48382SNiek Linnenbank if (value & SD_CMDR_LOAD) { 64982e48382SNiek Linnenbank allwinner_sdhost_send_command(s); 65082e48382SNiek Linnenbank allwinner_sdhost_dma(s); 65182e48382SNiek Linnenbank allwinner_sdhost_auto_stop(s); 65282e48382SNiek Linnenbank } 65382e48382SNiek Linnenbank allwinner_sdhost_update_irq(s); 65482e48382SNiek Linnenbank break; 65582e48382SNiek Linnenbank case REG_SD_CAGR: /* Command Argument */ 65682e48382SNiek Linnenbank s->command_arg = value; 65782e48382SNiek Linnenbank break; 65882e48382SNiek Linnenbank case REG_SD_RESP0: /* Response Zero */ 65982e48382SNiek Linnenbank s->response[0] = value; 66082e48382SNiek Linnenbank break; 66182e48382SNiek Linnenbank case REG_SD_RESP1: /* Response One */ 66282e48382SNiek Linnenbank s->response[1] = value; 66382e48382SNiek Linnenbank break; 66482e48382SNiek Linnenbank case REG_SD_RESP2: /* Response Two */ 66582e48382SNiek Linnenbank s->response[2] = value; 66682e48382SNiek Linnenbank break; 66782e48382SNiek Linnenbank case REG_SD_RESP3: /* Response Three */ 66882e48382SNiek Linnenbank s->response[3] = value; 66982e48382SNiek Linnenbank break; 67082e48382SNiek Linnenbank case REG_SD_IMKR: /* Interrupt Mask */ 67182e48382SNiek Linnenbank s->irq_mask = value; 67282e48382SNiek Linnenbank allwinner_sdhost_update_irq(s); 67382e48382SNiek Linnenbank break; 67482e48382SNiek Linnenbank case REG_SD_MISR: /* Masked Interrupt Status */ 67582e48382SNiek Linnenbank case REG_SD_RISR: /* Raw Interrupt Status */ 67682e48382SNiek Linnenbank s->irq_status &= ~value; 67782e48382SNiek Linnenbank allwinner_sdhost_update_irq(s); 67882e48382SNiek Linnenbank break; 67982e48382SNiek Linnenbank case REG_SD_STAR: /* Status */ 68082e48382SNiek Linnenbank s->status &= ~value; 68182e48382SNiek Linnenbank allwinner_sdhost_update_irq(s); 68282e48382SNiek Linnenbank break; 68382e48382SNiek Linnenbank case REG_SD_FWLR: /* FIFO Water Level */ 68482e48382SNiek Linnenbank s->fifo_wlevel = value; 68582e48382SNiek Linnenbank break; 68682e48382SNiek Linnenbank case REG_SD_FUNS: /* FIFO Function Select */ 68782e48382SNiek Linnenbank s->fifo_func_sel = value; 68882e48382SNiek Linnenbank break; 68982e48382SNiek Linnenbank case REG_SD_DBGC: /* Debug Enable */ 69082e48382SNiek Linnenbank s->debug_enable = value; 69182e48382SNiek Linnenbank break; 69282e48382SNiek Linnenbank case REG_SD_A12A: /* Auto command 12 argument */ 69382e48382SNiek Linnenbank s->auto12_arg = value; 69482e48382SNiek Linnenbank break; 69582e48382SNiek Linnenbank case REG_SD_NTSR: /* SD NewTiming Set */ 69682e48382SNiek Linnenbank s->newtiming_set = value; 69782e48382SNiek Linnenbank break; 69882e48382SNiek Linnenbank case REG_SD_SDBG: /* SD newTiming Set Debug */ 69982e48382SNiek Linnenbank s->newtiming_debug = value; 70082e48382SNiek Linnenbank break; 70182e48382SNiek Linnenbank case REG_SD_HWRST: /* Hardware Reset Register */ 70282e48382SNiek Linnenbank s->hardware_rst = value; 70382e48382SNiek Linnenbank break; 70482e48382SNiek Linnenbank case REG_SD_DMAC: /* Internal DMA Controller Control */ 70582e48382SNiek Linnenbank s->dmac = value; 70682e48382SNiek Linnenbank allwinner_sdhost_update_irq(s); 70782e48382SNiek Linnenbank break; 70882e48382SNiek Linnenbank case REG_SD_DLBA: /* Descriptor List Base Address */ 70982e48382SNiek Linnenbank s->desc_base = value; 71082e48382SNiek Linnenbank break; 71182e48382SNiek Linnenbank case REG_SD_IDST: /* Internal DMA Controller Status */ 71282e48382SNiek Linnenbank s->dmac_status &= (~SD_IDST_WR_MASK) | (~value & SD_IDST_WR_MASK); 71382e48382SNiek Linnenbank allwinner_sdhost_update_irq(s); 71482e48382SNiek Linnenbank break; 71582e48382SNiek Linnenbank case REG_SD_IDIE: /* Internal DMA Controller Interrupt Enable */ 71682e48382SNiek Linnenbank s->dmac_irq = value; 71782e48382SNiek Linnenbank allwinner_sdhost_update_irq(s); 71882e48382SNiek Linnenbank break; 71993e2da36SStrahinja Jankovic case REG_SD_THLDC: /* Card Threshold Control or FIFO (sun4i) */ 72093e2da36SStrahinja Jankovic if (sc->is_sun4i) { 72193e2da36SStrahinja Jankovic allwinner_sdhost_fifo_write(s, value); 72293e2da36SStrahinja Jankovic } else { 72382e48382SNiek Linnenbank s->card_threshold = value; 72493e2da36SStrahinja Jankovic } 72582e48382SNiek Linnenbank break; 72682e48382SNiek Linnenbank case REG_SD_DSBD: /* eMMC DDR Start Bit Detection Control */ 72782e48382SNiek Linnenbank s->startbit_detect = value; 72882e48382SNiek Linnenbank break; 72982e48382SNiek Linnenbank case REG_SD_FIFO: /* Read/Write FIFO */ 73093e2da36SStrahinja Jankovic allwinner_sdhost_fifo_write(s, value); 73182e48382SNiek Linnenbank break; 73282e48382SNiek Linnenbank case REG_SD_RES_CRC: /* Response CRC from card/eMMC */ 73382e48382SNiek Linnenbank case REG_SD_DATA7_CRC: /* CRC Data 7 from card/eMMC */ 73482e48382SNiek Linnenbank case REG_SD_DATA6_CRC: /* CRC Data 6 from card/eMMC */ 73582e48382SNiek Linnenbank case REG_SD_DATA5_CRC: /* CRC Data 5 from card/eMMC */ 73682e48382SNiek Linnenbank case REG_SD_DATA4_CRC: /* CRC Data 4 from card/eMMC */ 73782e48382SNiek Linnenbank case REG_SD_DATA3_CRC: /* CRC Data 3 from card/eMMC */ 73882e48382SNiek Linnenbank case REG_SD_DATA2_CRC: /* CRC Data 2 from card/eMMC */ 73982e48382SNiek Linnenbank case REG_SD_DATA1_CRC: /* CRC Data 1 from card/eMMC */ 74082e48382SNiek Linnenbank case REG_SD_DATA0_CRC: /* CRC Data 0 from card/eMMC */ 74182e48382SNiek Linnenbank case REG_SD_CRC_STA: /* CRC status from card/eMMC in write operation */ 74282e48382SNiek Linnenbank break; 7432c992b88Sqianfan Zhao case REG_SD_SAMP_DL: /* Sample delay control */ 7442c992b88Sqianfan Zhao if (sc->can_calibrate) { 7452c992b88Sqianfan Zhao s->sample_delay = value; 7462c992b88Sqianfan Zhao } else { 7472c992b88Sqianfan Zhao out_of_bounds = true; 7482c992b88Sqianfan Zhao } 7492c992b88Sqianfan Zhao break; 75082e48382SNiek Linnenbank default: 7512c992b88Sqianfan Zhao out_of_bounds = true; 7522c992b88Sqianfan Zhao break; 7532c992b88Sqianfan Zhao } 7542c992b88Sqianfan Zhao 7552c992b88Sqianfan Zhao if (out_of_bounds) { 75682e48382SNiek Linnenbank qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset %" 75782e48382SNiek Linnenbank HWADDR_PRIx"\n", __func__, offset); 75882e48382SNiek Linnenbank } 75982e48382SNiek Linnenbank } 76082e48382SNiek Linnenbank 76182e48382SNiek Linnenbank static const MemoryRegionOps allwinner_sdhost_ops = { 76282e48382SNiek Linnenbank .read = allwinner_sdhost_read, 76382e48382SNiek Linnenbank .write = allwinner_sdhost_write, 764*ba26f147SPhilippe Mathieu-Daudé .endianness = DEVICE_LITTLE_ENDIAN, 76582e48382SNiek Linnenbank .valid = { 76682e48382SNiek Linnenbank .min_access_size = 4, 76782e48382SNiek Linnenbank .max_access_size = 4, 76882e48382SNiek Linnenbank }, 76982e48382SNiek Linnenbank .impl.min_access_size = 4, 77082e48382SNiek Linnenbank }; 77182e48382SNiek Linnenbank 77282e48382SNiek Linnenbank static const VMStateDescription vmstate_allwinner_sdhost = { 77382e48382SNiek Linnenbank .name = "allwinner-sdhost", 77482e48382SNiek Linnenbank .version_id = 1, 77582e48382SNiek Linnenbank .minimum_version_id = 1, 776307119baSRichard Henderson .fields = (const VMStateField[]) { 77782e48382SNiek Linnenbank VMSTATE_UINT32(global_ctl, AwSdHostState), 77882e48382SNiek Linnenbank VMSTATE_UINT32(clock_ctl, AwSdHostState), 77982e48382SNiek Linnenbank VMSTATE_UINT32(timeout, AwSdHostState), 78082e48382SNiek Linnenbank VMSTATE_UINT32(bus_width, AwSdHostState), 78182e48382SNiek Linnenbank VMSTATE_UINT32(block_size, AwSdHostState), 78282e48382SNiek Linnenbank VMSTATE_UINT32(byte_count, AwSdHostState), 78382e48382SNiek Linnenbank VMSTATE_UINT32(transfer_cnt, AwSdHostState), 78482e48382SNiek Linnenbank VMSTATE_UINT32(command, AwSdHostState), 78582e48382SNiek Linnenbank VMSTATE_UINT32(command_arg, AwSdHostState), 78682e48382SNiek Linnenbank VMSTATE_UINT32_ARRAY(response, AwSdHostState, 4), 78782e48382SNiek Linnenbank VMSTATE_UINT32(irq_mask, AwSdHostState), 78882e48382SNiek Linnenbank VMSTATE_UINT32(irq_status, AwSdHostState), 78982e48382SNiek Linnenbank VMSTATE_UINT32(status, AwSdHostState), 79082e48382SNiek Linnenbank VMSTATE_UINT32(fifo_wlevel, AwSdHostState), 79182e48382SNiek Linnenbank VMSTATE_UINT32(fifo_func_sel, AwSdHostState), 79282e48382SNiek Linnenbank VMSTATE_UINT32(debug_enable, AwSdHostState), 79382e48382SNiek Linnenbank VMSTATE_UINT32(auto12_arg, AwSdHostState), 79482e48382SNiek Linnenbank VMSTATE_UINT32(newtiming_set, AwSdHostState), 79582e48382SNiek Linnenbank VMSTATE_UINT32(newtiming_debug, AwSdHostState), 79682e48382SNiek Linnenbank VMSTATE_UINT32(hardware_rst, AwSdHostState), 79782e48382SNiek Linnenbank VMSTATE_UINT32(dmac, AwSdHostState), 79882e48382SNiek Linnenbank VMSTATE_UINT32(desc_base, AwSdHostState), 79982e48382SNiek Linnenbank VMSTATE_UINT32(dmac_status, AwSdHostState), 80082e48382SNiek Linnenbank VMSTATE_UINT32(dmac_irq, AwSdHostState), 80182e48382SNiek Linnenbank VMSTATE_UINT32(card_threshold, AwSdHostState), 80282e48382SNiek Linnenbank VMSTATE_UINT32(startbit_detect, AwSdHostState), 80382e48382SNiek Linnenbank VMSTATE_UINT32(response_crc, AwSdHostState), 80482e48382SNiek Linnenbank VMSTATE_UINT32_ARRAY(data_crc, AwSdHostState, 8), 80582e48382SNiek Linnenbank VMSTATE_UINT32(status_crc, AwSdHostState), 8062c992b88Sqianfan Zhao VMSTATE_UINT32(sample_delay, AwSdHostState), 80782e48382SNiek Linnenbank VMSTATE_END_OF_LIST() 80882e48382SNiek Linnenbank } 80982e48382SNiek Linnenbank }; 81082e48382SNiek Linnenbank 8112ba395a5SRichard Henderson static const Property allwinner_sdhost_properties[] = { 812b3aec952SPhilippe Mathieu-Daudé DEFINE_PROP_LINK("dma-memory", AwSdHostState, dma_mr, 813b3aec952SPhilippe Mathieu-Daudé TYPE_MEMORY_REGION, MemoryRegion *), 814b3aec952SPhilippe Mathieu-Daudé }; 815b3aec952SPhilippe Mathieu-Daudé 81682e48382SNiek Linnenbank static void allwinner_sdhost_init(Object *obj) 81782e48382SNiek Linnenbank { 81882e48382SNiek Linnenbank AwSdHostState *s = AW_SDHOST(obj); 81982e48382SNiek Linnenbank 820d637e1dcSPeter Maydell qbus_init(&s->sdbus, sizeof(s->sdbus), 82182e48382SNiek Linnenbank TYPE_AW_SDHOST_BUS, DEVICE(s), "sd-bus"); 82282e48382SNiek Linnenbank 82382e48382SNiek Linnenbank memory_region_init_io(&s->iomem, obj, &allwinner_sdhost_ops, s, 82482e48382SNiek Linnenbank TYPE_AW_SDHOST, 4 * KiB); 82582e48382SNiek Linnenbank sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); 82682e48382SNiek Linnenbank sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq); 82782e48382SNiek Linnenbank } 82882e48382SNiek Linnenbank 829b3aec952SPhilippe Mathieu-Daudé static void allwinner_sdhost_realize(DeviceState *dev, Error **errp) 830b3aec952SPhilippe Mathieu-Daudé { 831b3aec952SPhilippe Mathieu-Daudé AwSdHostState *s = AW_SDHOST(dev); 832b3aec952SPhilippe Mathieu-Daudé 833b3aec952SPhilippe Mathieu-Daudé if (!s->dma_mr) { 834b3aec952SPhilippe Mathieu-Daudé error_setg(errp, TYPE_AW_SDHOST " 'dma-memory' link not set"); 835b3aec952SPhilippe Mathieu-Daudé return; 836b3aec952SPhilippe Mathieu-Daudé } 837b3aec952SPhilippe Mathieu-Daudé 838b3aec952SPhilippe Mathieu-Daudé address_space_init(&s->dma_as, s->dma_mr, "sdhost-dma"); 839b3aec952SPhilippe Mathieu-Daudé } 840b3aec952SPhilippe Mathieu-Daudé 84182e48382SNiek Linnenbank static void allwinner_sdhost_reset(DeviceState *dev) 84282e48382SNiek Linnenbank { 84382e48382SNiek Linnenbank AwSdHostState *s = AW_SDHOST(dev); 8442c992b88Sqianfan Zhao AwSdHostClass *sc = AW_SDHOST_GET_CLASS(s); 84582e48382SNiek Linnenbank 84682e48382SNiek Linnenbank s->global_ctl = REG_SD_GCTL_RST; 84782e48382SNiek Linnenbank s->clock_ctl = REG_SD_CKCR_RST; 84882e48382SNiek Linnenbank s->timeout = REG_SD_TMOR_RST; 84982e48382SNiek Linnenbank s->bus_width = REG_SD_BWDR_RST; 85082e48382SNiek Linnenbank s->block_size = REG_SD_BKSR_RST; 85182e48382SNiek Linnenbank s->byte_count = REG_SD_BYCR_RST; 85282e48382SNiek Linnenbank s->transfer_cnt = 0; 85382e48382SNiek Linnenbank 85482e48382SNiek Linnenbank s->command = REG_SD_CMDR_RST; 85582e48382SNiek Linnenbank s->command_arg = REG_SD_CAGR_RST; 85682e48382SNiek Linnenbank 85782e48382SNiek Linnenbank for (int i = 0; i < ARRAY_SIZE(s->response); i++) { 85882e48382SNiek Linnenbank s->response[i] = REG_SD_RESP_RST; 85982e48382SNiek Linnenbank } 86082e48382SNiek Linnenbank 86182e48382SNiek Linnenbank s->irq_mask = REG_SD_IMKR_RST; 86282e48382SNiek Linnenbank s->irq_status = REG_SD_RISR_RST; 86382e48382SNiek Linnenbank s->status = REG_SD_STAR_RST; 86482e48382SNiek Linnenbank 86582e48382SNiek Linnenbank s->fifo_wlevel = REG_SD_FWLR_RST; 86682e48382SNiek Linnenbank s->fifo_func_sel = REG_SD_FUNS_RST; 86782e48382SNiek Linnenbank s->debug_enable = REG_SD_DBGC_RST; 86882e48382SNiek Linnenbank s->auto12_arg = REG_SD_A12A_RST; 86982e48382SNiek Linnenbank s->newtiming_set = REG_SD_NTSR_RST; 87082e48382SNiek Linnenbank s->newtiming_debug = REG_SD_SDBG_RST; 87182e48382SNiek Linnenbank s->hardware_rst = REG_SD_HWRST_RST; 87282e48382SNiek Linnenbank s->dmac = REG_SD_DMAC_RST; 87382e48382SNiek Linnenbank s->desc_base = REG_SD_DLBA_RST; 87482e48382SNiek Linnenbank s->dmac_status = REG_SD_IDST_RST; 87582e48382SNiek Linnenbank s->dmac_irq = REG_SD_IDIE_RST; 87682e48382SNiek Linnenbank s->card_threshold = REG_SD_THLDC_RST; 87782e48382SNiek Linnenbank s->startbit_detect = REG_SD_DSBD_RST; 87882e48382SNiek Linnenbank s->response_crc = REG_SD_RES_CRC_RST; 87982e48382SNiek Linnenbank 88082e48382SNiek Linnenbank for (int i = 0; i < ARRAY_SIZE(s->data_crc); i++) { 88182e48382SNiek Linnenbank s->data_crc[i] = REG_SD_DATA_CRC_RST; 88282e48382SNiek Linnenbank } 88382e48382SNiek Linnenbank 88482e48382SNiek Linnenbank s->status_crc = REG_SD_CRC_STA_RST; 8852c992b88Sqianfan Zhao 8862c992b88Sqianfan Zhao if (sc->can_calibrate) { 8872c992b88Sqianfan Zhao s->sample_delay = REG_SD_SAMPLE_DL_RST; 8882c992b88Sqianfan Zhao } 88982e48382SNiek Linnenbank } 89082e48382SNiek Linnenbank 89182e48382SNiek Linnenbank static void allwinner_sdhost_bus_class_init(ObjectClass *klass, void *data) 89282e48382SNiek Linnenbank { 89382e48382SNiek Linnenbank SDBusClass *sbc = SD_BUS_CLASS(klass); 89482e48382SNiek Linnenbank 89582e48382SNiek Linnenbank sbc->set_inserted = allwinner_sdhost_set_inserted; 89682e48382SNiek Linnenbank } 89782e48382SNiek Linnenbank 89882e48382SNiek Linnenbank static void allwinner_sdhost_class_init(ObjectClass *klass, void *data) 89982e48382SNiek Linnenbank { 90082e48382SNiek Linnenbank DeviceClass *dc = DEVICE_CLASS(klass); 90182e48382SNiek Linnenbank 902e3d08143SPeter Maydell device_class_set_legacy_reset(dc, allwinner_sdhost_reset); 90382e48382SNiek Linnenbank dc->vmsd = &vmstate_allwinner_sdhost; 904b3aec952SPhilippe Mathieu-Daudé dc->realize = allwinner_sdhost_realize; 905b3aec952SPhilippe Mathieu-Daudé device_class_set_props(dc, allwinner_sdhost_properties); 90682e48382SNiek Linnenbank } 90782e48382SNiek Linnenbank 90882e48382SNiek Linnenbank static void allwinner_sdhost_sun4i_class_init(ObjectClass *klass, void *data) 90982e48382SNiek Linnenbank { 91082e48382SNiek Linnenbank AwSdHostClass *sc = AW_SDHOST_CLASS(klass); 91182e48382SNiek Linnenbank sc->max_desc_size = 8 * KiB; 91293e2da36SStrahinja Jankovic sc->is_sun4i = true; 9132c992b88Sqianfan Zhao sc->can_calibrate = false; 91482e48382SNiek Linnenbank } 91582e48382SNiek Linnenbank 91682e48382SNiek Linnenbank static void allwinner_sdhost_sun5i_class_init(ObjectClass *klass, void *data) 91782e48382SNiek Linnenbank { 91882e48382SNiek Linnenbank AwSdHostClass *sc = AW_SDHOST_CLASS(klass); 91982e48382SNiek Linnenbank sc->max_desc_size = 64 * KiB; 92093e2da36SStrahinja Jankovic sc->is_sun4i = false; 9212c992b88Sqianfan Zhao sc->can_calibrate = false; 9222c992b88Sqianfan Zhao } 9232c992b88Sqianfan Zhao 9242c992b88Sqianfan Zhao static void allwinner_sdhost_sun50i_a64_class_init(ObjectClass *klass, 9252c992b88Sqianfan Zhao void *data) 9262c992b88Sqianfan Zhao { 9272c992b88Sqianfan Zhao AwSdHostClass *sc = AW_SDHOST_CLASS(klass); 9282c992b88Sqianfan Zhao sc->max_desc_size = 64 * KiB; 9292c992b88Sqianfan Zhao sc->is_sun4i = false; 9302c992b88Sqianfan Zhao sc->can_calibrate = true; 9312c992b88Sqianfan Zhao } 9322c992b88Sqianfan Zhao 9332c992b88Sqianfan Zhao static void allwinner_sdhost_sun50i_a64_emmc_class_init(ObjectClass *klass, 9342c992b88Sqianfan Zhao void *data) 9352c992b88Sqianfan Zhao { 9362c992b88Sqianfan Zhao AwSdHostClass *sc = AW_SDHOST_CLASS(klass); 9372c992b88Sqianfan Zhao sc->max_desc_size = 8 * KiB; 9382c992b88Sqianfan Zhao sc->is_sun4i = false; 9392c992b88Sqianfan Zhao sc->can_calibrate = true; 94082e48382SNiek Linnenbank } 94182e48382SNiek Linnenbank 9425e78c98bSBernhard Beschow static const TypeInfo allwinner_sdhost_info = { 94382e48382SNiek Linnenbank .name = TYPE_AW_SDHOST, 94482e48382SNiek Linnenbank .parent = TYPE_SYS_BUS_DEVICE, 94582e48382SNiek Linnenbank .instance_init = allwinner_sdhost_init, 94682e48382SNiek Linnenbank .instance_size = sizeof(AwSdHostState), 94782e48382SNiek Linnenbank .class_init = allwinner_sdhost_class_init, 94882e48382SNiek Linnenbank .class_size = sizeof(AwSdHostClass), 94982e48382SNiek Linnenbank .abstract = true, 95082e48382SNiek Linnenbank }; 95182e48382SNiek Linnenbank 95282e48382SNiek Linnenbank static const TypeInfo allwinner_sdhost_sun4i_info = { 95382e48382SNiek Linnenbank .name = TYPE_AW_SDHOST_SUN4I, 95482e48382SNiek Linnenbank .parent = TYPE_AW_SDHOST, 95582e48382SNiek Linnenbank .class_init = allwinner_sdhost_sun4i_class_init, 95682e48382SNiek Linnenbank }; 95782e48382SNiek Linnenbank 95882e48382SNiek Linnenbank static const TypeInfo allwinner_sdhost_sun5i_info = { 95982e48382SNiek Linnenbank .name = TYPE_AW_SDHOST_SUN5I, 96082e48382SNiek Linnenbank .parent = TYPE_AW_SDHOST, 96182e48382SNiek Linnenbank .class_init = allwinner_sdhost_sun5i_class_init, 96282e48382SNiek Linnenbank }; 96382e48382SNiek Linnenbank 9642c992b88Sqianfan Zhao static const TypeInfo allwinner_sdhost_sun50i_a64_info = { 9652c992b88Sqianfan Zhao .name = TYPE_AW_SDHOST_SUN50I_A64, 9662c992b88Sqianfan Zhao .parent = TYPE_AW_SDHOST, 9672c992b88Sqianfan Zhao .class_init = allwinner_sdhost_sun50i_a64_class_init, 9682c992b88Sqianfan Zhao }; 9692c992b88Sqianfan Zhao 9702c992b88Sqianfan Zhao static const TypeInfo allwinner_sdhost_sun50i_a64_emmc_info = { 9712c992b88Sqianfan Zhao .name = TYPE_AW_SDHOST_SUN50I_A64_EMMC, 9722c992b88Sqianfan Zhao .parent = TYPE_AW_SDHOST, 9732c992b88Sqianfan Zhao .class_init = allwinner_sdhost_sun50i_a64_emmc_class_init, 9742c992b88Sqianfan Zhao }; 9752c992b88Sqianfan Zhao 97682e48382SNiek Linnenbank static const TypeInfo allwinner_sdhost_bus_info = { 97782e48382SNiek Linnenbank .name = TYPE_AW_SDHOST_BUS, 97882e48382SNiek Linnenbank .parent = TYPE_SD_BUS, 97982e48382SNiek Linnenbank .instance_size = sizeof(SDBus), 98082e48382SNiek Linnenbank .class_init = allwinner_sdhost_bus_class_init, 98182e48382SNiek Linnenbank }; 98282e48382SNiek Linnenbank 98382e48382SNiek Linnenbank static void allwinner_sdhost_register_types(void) 98482e48382SNiek Linnenbank { 98582e48382SNiek Linnenbank type_register_static(&allwinner_sdhost_info); 98682e48382SNiek Linnenbank type_register_static(&allwinner_sdhost_sun4i_info); 98782e48382SNiek Linnenbank type_register_static(&allwinner_sdhost_sun5i_info); 9882c992b88Sqianfan Zhao type_register_static(&allwinner_sdhost_sun50i_a64_info); 9892c992b88Sqianfan Zhao type_register_static(&allwinner_sdhost_sun50i_a64_emmc_info); 99082e48382SNiek Linnenbank type_register_static(&allwinner_sdhost_bus_info); 99182e48382SNiek Linnenbank } 99282e48382SNiek Linnenbank 99382e48382SNiek Linnenbank type_init(allwinner_sdhost_register_types) 994