1*2bea128cSEddie James /* 2*2bea128cSEddie James * Aspeed SD Host Controller 3*2bea128cSEddie James * Eddie James <eajames@linux.ibm.com> 4*2bea128cSEddie James * 5*2bea128cSEddie James * Copyright (C) 2019 IBM Corp 6*2bea128cSEddie James * SPDX-License-Identifer: GPL-2.0-or-later 7*2bea128cSEddie James */ 8*2bea128cSEddie James 9*2bea128cSEddie James #include "qemu/osdep.h" 10*2bea128cSEddie James #include "qemu/log.h" 11*2bea128cSEddie James #include "qemu/error-report.h" 12*2bea128cSEddie James #include "hw/sd/aspeed_sdhci.h" 13*2bea128cSEddie James #include "qapi/error.h" 14*2bea128cSEddie James #include "hw/irq.h" 15*2bea128cSEddie James #include "migration/vmstate.h" 16*2bea128cSEddie James 17*2bea128cSEddie James #define ASPEED_SDHCI_INFO 0x00 18*2bea128cSEddie James #define ASPEED_SDHCI_INFO_RESET 0x00030000 19*2bea128cSEddie James #define ASPEED_SDHCI_DEBOUNCE 0x04 20*2bea128cSEddie James #define ASPEED_SDHCI_DEBOUNCE_RESET 0x00000005 21*2bea128cSEddie James #define ASPEED_SDHCI_BUS 0x08 22*2bea128cSEddie James #define ASPEED_SDHCI_SDIO_140 0x10 23*2bea128cSEddie James #define ASPEED_SDHCI_SDIO_148 0x18 24*2bea128cSEddie James #define ASPEED_SDHCI_SDIO_240 0x20 25*2bea128cSEddie James #define ASPEED_SDHCI_SDIO_248 0x28 26*2bea128cSEddie James #define ASPEED_SDHCI_WP_POL 0xec 27*2bea128cSEddie James #define ASPEED_SDHCI_CARD_DET 0xf0 28*2bea128cSEddie James #define ASPEED_SDHCI_IRQ_STAT 0xfc 29*2bea128cSEddie James 30*2bea128cSEddie James #define TO_REG(addr) ((addr) / sizeof(uint32_t)) 31*2bea128cSEddie James 32*2bea128cSEddie James static uint64_t aspeed_sdhci_read(void *opaque, hwaddr addr, unsigned int size) 33*2bea128cSEddie James { 34*2bea128cSEddie James uint32_t val = 0; 35*2bea128cSEddie James AspeedSDHCIState *sdhci = opaque; 36*2bea128cSEddie James 37*2bea128cSEddie James switch (addr) { 38*2bea128cSEddie James case ASPEED_SDHCI_SDIO_140: 39*2bea128cSEddie James val = (uint32_t)sdhci->slots[0].capareg; 40*2bea128cSEddie James break; 41*2bea128cSEddie James case ASPEED_SDHCI_SDIO_148: 42*2bea128cSEddie James val = (uint32_t)sdhci->slots[0].maxcurr; 43*2bea128cSEddie James break; 44*2bea128cSEddie James case ASPEED_SDHCI_SDIO_240: 45*2bea128cSEddie James val = (uint32_t)sdhci->slots[1].capareg; 46*2bea128cSEddie James break; 47*2bea128cSEddie James case ASPEED_SDHCI_SDIO_248: 48*2bea128cSEddie James val = (uint32_t)sdhci->slots[1].maxcurr; 49*2bea128cSEddie James break; 50*2bea128cSEddie James default: 51*2bea128cSEddie James if (addr < ASPEED_SDHCI_REG_SIZE) { 52*2bea128cSEddie James val = sdhci->regs[TO_REG(addr)]; 53*2bea128cSEddie James } else { 54*2bea128cSEddie James qemu_log_mask(LOG_GUEST_ERROR, 55*2bea128cSEddie James "%s: Out-of-bounds read at 0x%" HWADDR_PRIx "\n", 56*2bea128cSEddie James __func__, addr); 57*2bea128cSEddie James } 58*2bea128cSEddie James } 59*2bea128cSEddie James 60*2bea128cSEddie James return (uint64_t)val; 61*2bea128cSEddie James } 62*2bea128cSEddie James 63*2bea128cSEddie James static void aspeed_sdhci_write(void *opaque, hwaddr addr, uint64_t val, 64*2bea128cSEddie James unsigned int size) 65*2bea128cSEddie James { 66*2bea128cSEddie James AspeedSDHCIState *sdhci = opaque; 67*2bea128cSEddie James 68*2bea128cSEddie James switch (addr) { 69*2bea128cSEddie James case ASPEED_SDHCI_SDIO_140: 70*2bea128cSEddie James sdhci->slots[0].capareg = (uint64_t)(uint32_t)val; 71*2bea128cSEddie James break; 72*2bea128cSEddie James case ASPEED_SDHCI_SDIO_148: 73*2bea128cSEddie James sdhci->slots[0].maxcurr = (uint64_t)(uint32_t)val; 74*2bea128cSEddie James break; 75*2bea128cSEddie James case ASPEED_SDHCI_SDIO_240: 76*2bea128cSEddie James sdhci->slots[1].capareg = (uint64_t)(uint32_t)val; 77*2bea128cSEddie James break; 78*2bea128cSEddie James case ASPEED_SDHCI_SDIO_248: 79*2bea128cSEddie James sdhci->slots[1].maxcurr = (uint64_t)(uint32_t)val; 80*2bea128cSEddie James break; 81*2bea128cSEddie James default: 82*2bea128cSEddie James if (addr < ASPEED_SDHCI_REG_SIZE) { 83*2bea128cSEddie James sdhci->regs[TO_REG(addr)] = (uint32_t)val; 84*2bea128cSEddie James } else { 85*2bea128cSEddie James qemu_log_mask(LOG_GUEST_ERROR, 86*2bea128cSEddie James "%s: Out-of-bounds write at 0x%" HWADDR_PRIx "\n", 87*2bea128cSEddie James __func__, addr); 88*2bea128cSEddie James } 89*2bea128cSEddie James } 90*2bea128cSEddie James } 91*2bea128cSEddie James 92*2bea128cSEddie James static const MemoryRegionOps aspeed_sdhci_ops = { 93*2bea128cSEddie James .read = aspeed_sdhci_read, 94*2bea128cSEddie James .write = aspeed_sdhci_write, 95*2bea128cSEddie James .endianness = DEVICE_NATIVE_ENDIAN, 96*2bea128cSEddie James .valid.min_access_size = 4, 97*2bea128cSEddie James .valid.max_access_size = 4, 98*2bea128cSEddie James }; 99*2bea128cSEddie James 100*2bea128cSEddie James static void aspeed_sdhci_set_irq(void *opaque, int n, int level) 101*2bea128cSEddie James { 102*2bea128cSEddie James AspeedSDHCIState *sdhci = opaque; 103*2bea128cSEddie James 104*2bea128cSEddie James if (level) { 105*2bea128cSEddie James sdhci->regs[TO_REG(ASPEED_SDHCI_IRQ_STAT)] |= BIT(n); 106*2bea128cSEddie James 107*2bea128cSEddie James qemu_irq_raise(sdhci->irq); 108*2bea128cSEddie James } else { 109*2bea128cSEddie James sdhci->regs[TO_REG(ASPEED_SDHCI_IRQ_STAT)] &= ~BIT(n); 110*2bea128cSEddie James 111*2bea128cSEddie James qemu_irq_lower(sdhci->irq); 112*2bea128cSEddie James } 113*2bea128cSEddie James } 114*2bea128cSEddie James 115*2bea128cSEddie James static void aspeed_sdhci_realize(DeviceState *dev, Error **errp) 116*2bea128cSEddie James { 117*2bea128cSEddie James Error *err = NULL; 118*2bea128cSEddie James SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 119*2bea128cSEddie James AspeedSDHCIState *sdhci = ASPEED_SDHCI(dev); 120*2bea128cSEddie James 121*2bea128cSEddie James /* Create input irqs for the slots */ 122*2bea128cSEddie James qdev_init_gpio_in_named_with_opaque(DEVICE(sbd), aspeed_sdhci_set_irq, 123*2bea128cSEddie James sdhci, NULL, ASPEED_SDHCI_NUM_SLOTS); 124*2bea128cSEddie James 125*2bea128cSEddie James sysbus_init_irq(sbd, &sdhci->irq); 126*2bea128cSEddie James memory_region_init_io(&sdhci->iomem, OBJECT(sdhci), &aspeed_sdhci_ops, 127*2bea128cSEddie James sdhci, TYPE_ASPEED_SDHCI, 0x1000); 128*2bea128cSEddie James sysbus_init_mmio(sbd, &sdhci->iomem); 129*2bea128cSEddie James 130*2bea128cSEddie James for (int i = 0; i < ASPEED_SDHCI_NUM_SLOTS; ++i) { 131*2bea128cSEddie James Object *sdhci_slot = OBJECT(&sdhci->slots[i]); 132*2bea128cSEddie James SysBusDevice *sbd_slot = SYS_BUS_DEVICE(&sdhci->slots[i]); 133*2bea128cSEddie James 134*2bea128cSEddie James object_property_set_int(sdhci_slot, 2, "sd-spec-version", &err); 135*2bea128cSEddie James if (err) { 136*2bea128cSEddie James error_propagate(errp, err); 137*2bea128cSEddie James return; 138*2bea128cSEddie James } 139*2bea128cSEddie James 140*2bea128cSEddie James object_property_set_uint(sdhci_slot, ASPEED_SDHCI_CAPABILITIES, 141*2bea128cSEddie James "capareg", &err); 142*2bea128cSEddie James if (err) { 143*2bea128cSEddie James error_propagate(errp, err); 144*2bea128cSEddie James return; 145*2bea128cSEddie James } 146*2bea128cSEddie James 147*2bea128cSEddie James object_property_set_bool(sdhci_slot, true, "realized", &err); 148*2bea128cSEddie James if (err) { 149*2bea128cSEddie James error_propagate(errp, err); 150*2bea128cSEddie James return; 151*2bea128cSEddie James } 152*2bea128cSEddie James 153*2bea128cSEddie James sysbus_connect_irq(sbd_slot, 0, qdev_get_gpio_in(DEVICE(sbd), i)); 154*2bea128cSEddie James memory_region_add_subregion(&sdhci->iomem, (i + 1) * 0x100, 155*2bea128cSEddie James &sdhci->slots[i].iomem); 156*2bea128cSEddie James } 157*2bea128cSEddie James } 158*2bea128cSEddie James 159*2bea128cSEddie James static void aspeed_sdhci_reset(DeviceState *dev) 160*2bea128cSEddie James { 161*2bea128cSEddie James AspeedSDHCIState *sdhci = ASPEED_SDHCI(dev); 162*2bea128cSEddie James 163*2bea128cSEddie James memset(sdhci->regs, 0, ASPEED_SDHCI_REG_SIZE); 164*2bea128cSEddie James sdhci->regs[TO_REG(ASPEED_SDHCI_INFO)] = ASPEED_SDHCI_INFO_RESET; 165*2bea128cSEddie James sdhci->regs[TO_REG(ASPEED_SDHCI_DEBOUNCE)] = ASPEED_SDHCI_DEBOUNCE_RESET; 166*2bea128cSEddie James } 167*2bea128cSEddie James 168*2bea128cSEddie James static const VMStateDescription vmstate_aspeed_sdhci = { 169*2bea128cSEddie James .name = TYPE_ASPEED_SDHCI, 170*2bea128cSEddie James .version_id = 1, 171*2bea128cSEddie James .fields = (VMStateField[]) { 172*2bea128cSEddie James VMSTATE_UINT32_ARRAY(regs, AspeedSDHCIState, ASPEED_SDHCI_NUM_REGS), 173*2bea128cSEddie James VMSTATE_END_OF_LIST(), 174*2bea128cSEddie James }, 175*2bea128cSEddie James }; 176*2bea128cSEddie James 177*2bea128cSEddie James static void aspeed_sdhci_class_init(ObjectClass *classp, void *data) 178*2bea128cSEddie James { 179*2bea128cSEddie James DeviceClass *dc = DEVICE_CLASS(classp); 180*2bea128cSEddie James 181*2bea128cSEddie James dc->realize = aspeed_sdhci_realize; 182*2bea128cSEddie James dc->reset = aspeed_sdhci_reset; 183*2bea128cSEddie James dc->vmsd = &vmstate_aspeed_sdhci; 184*2bea128cSEddie James } 185*2bea128cSEddie James 186*2bea128cSEddie James static TypeInfo aspeed_sdhci_info = { 187*2bea128cSEddie James .name = TYPE_ASPEED_SDHCI, 188*2bea128cSEddie James .parent = TYPE_SYS_BUS_DEVICE, 189*2bea128cSEddie James .instance_size = sizeof(AspeedSDHCIState), 190*2bea128cSEddie James .class_init = aspeed_sdhci_class_init, 191*2bea128cSEddie James }; 192*2bea128cSEddie James 193*2bea128cSEddie James static void aspeed_sdhci_register_types(void) 194*2bea128cSEddie James { 195*2bea128cSEddie James type_register_static(&aspeed_sdhci_info); 196*2bea128cSEddie James } 197*2bea128cSEddie James 198*2bea128cSEddie James type_init(aspeed_sdhci_register_types) 199