12bea128cSEddie James /* 22bea128cSEddie James * Aspeed SD Host Controller 32bea128cSEddie James * Eddie James <eajames@linux.ibm.com> 42bea128cSEddie James * 52bea128cSEddie James * Copyright (C) 2019 IBM Corp 62bea128cSEddie James * SPDX-License-Identifer: GPL-2.0-or-later 72bea128cSEddie James */ 82bea128cSEddie James 92bea128cSEddie James #include "qemu/osdep.h" 102bea128cSEddie James #include "qemu/log.h" 112bea128cSEddie James #include "qemu/error-report.h" 122bea128cSEddie James #include "hw/sd/aspeed_sdhci.h" 132bea128cSEddie James #include "qapi/error.h" 142bea128cSEddie James #include "hw/irq.h" 152bea128cSEddie James #include "migration/vmstate.h" 16*0e2c24c6SAndrew Jeffery #include "hw/qdev-properties.h" 172bea128cSEddie James 182bea128cSEddie James #define ASPEED_SDHCI_INFO 0x00 192bea128cSEddie James #define ASPEED_SDHCI_INFO_RESET 0x00030000 202bea128cSEddie James #define ASPEED_SDHCI_DEBOUNCE 0x04 212bea128cSEddie James #define ASPEED_SDHCI_DEBOUNCE_RESET 0x00000005 222bea128cSEddie James #define ASPEED_SDHCI_BUS 0x08 232bea128cSEddie James #define ASPEED_SDHCI_SDIO_140 0x10 242bea128cSEddie James #define ASPEED_SDHCI_SDIO_148 0x18 252bea128cSEddie James #define ASPEED_SDHCI_SDIO_240 0x20 262bea128cSEddie James #define ASPEED_SDHCI_SDIO_248 0x28 272bea128cSEddie James #define ASPEED_SDHCI_WP_POL 0xec 282bea128cSEddie James #define ASPEED_SDHCI_CARD_DET 0xf0 292bea128cSEddie James #define ASPEED_SDHCI_IRQ_STAT 0xfc 302bea128cSEddie James 312bea128cSEddie James #define TO_REG(addr) ((addr) / sizeof(uint32_t)) 322bea128cSEddie James 332bea128cSEddie James static uint64_t aspeed_sdhci_read(void *opaque, hwaddr addr, unsigned int size) 342bea128cSEddie James { 352bea128cSEddie James uint32_t val = 0; 362bea128cSEddie James AspeedSDHCIState *sdhci = opaque; 372bea128cSEddie James 382bea128cSEddie James switch (addr) { 392bea128cSEddie James case ASPEED_SDHCI_SDIO_140: 402bea128cSEddie James val = (uint32_t)sdhci->slots[0].capareg; 412bea128cSEddie James break; 422bea128cSEddie James case ASPEED_SDHCI_SDIO_148: 432bea128cSEddie James val = (uint32_t)sdhci->slots[0].maxcurr; 442bea128cSEddie James break; 452bea128cSEddie James case ASPEED_SDHCI_SDIO_240: 462bea128cSEddie James val = (uint32_t)sdhci->slots[1].capareg; 472bea128cSEddie James break; 482bea128cSEddie James case ASPEED_SDHCI_SDIO_248: 492bea128cSEddie James val = (uint32_t)sdhci->slots[1].maxcurr; 502bea128cSEddie James break; 512bea128cSEddie James default: 522bea128cSEddie James if (addr < ASPEED_SDHCI_REG_SIZE) { 532bea128cSEddie James val = sdhci->regs[TO_REG(addr)]; 542bea128cSEddie James } else { 552bea128cSEddie James qemu_log_mask(LOG_GUEST_ERROR, 562bea128cSEddie James "%s: Out-of-bounds read at 0x%" HWADDR_PRIx "\n", 572bea128cSEddie James __func__, addr); 582bea128cSEddie James } 592bea128cSEddie James } 602bea128cSEddie James 612bea128cSEddie James return (uint64_t)val; 622bea128cSEddie James } 632bea128cSEddie James 642bea128cSEddie James static void aspeed_sdhci_write(void *opaque, hwaddr addr, uint64_t val, 652bea128cSEddie James unsigned int size) 662bea128cSEddie James { 672bea128cSEddie James AspeedSDHCIState *sdhci = opaque; 682bea128cSEddie James 692bea128cSEddie James switch (addr) { 702bea128cSEddie James case ASPEED_SDHCI_SDIO_140: 712bea128cSEddie James sdhci->slots[0].capareg = (uint64_t)(uint32_t)val; 722bea128cSEddie James break; 732bea128cSEddie James case ASPEED_SDHCI_SDIO_148: 742bea128cSEddie James sdhci->slots[0].maxcurr = (uint64_t)(uint32_t)val; 752bea128cSEddie James break; 762bea128cSEddie James case ASPEED_SDHCI_SDIO_240: 772bea128cSEddie James sdhci->slots[1].capareg = (uint64_t)(uint32_t)val; 782bea128cSEddie James break; 792bea128cSEddie James case ASPEED_SDHCI_SDIO_248: 802bea128cSEddie James sdhci->slots[1].maxcurr = (uint64_t)(uint32_t)val; 812bea128cSEddie James break; 822bea128cSEddie James default: 832bea128cSEddie James if (addr < ASPEED_SDHCI_REG_SIZE) { 842bea128cSEddie James sdhci->regs[TO_REG(addr)] = (uint32_t)val; 852bea128cSEddie James } else { 862bea128cSEddie James qemu_log_mask(LOG_GUEST_ERROR, 872bea128cSEddie James "%s: Out-of-bounds write at 0x%" HWADDR_PRIx "\n", 882bea128cSEddie James __func__, addr); 892bea128cSEddie James } 902bea128cSEddie James } 912bea128cSEddie James } 922bea128cSEddie James 932bea128cSEddie James static const MemoryRegionOps aspeed_sdhci_ops = { 942bea128cSEddie James .read = aspeed_sdhci_read, 952bea128cSEddie James .write = aspeed_sdhci_write, 962bea128cSEddie James .endianness = DEVICE_NATIVE_ENDIAN, 972bea128cSEddie James .valid.min_access_size = 4, 982bea128cSEddie James .valid.max_access_size = 4, 992bea128cSEddie James }; 1002bea128cSEddie James 1012bea128cSEddie James static void aspeed_sdhci_set_irq(void *opaque, int n, int level) 1022bea128cSEddie James { 1032bea128cSEddie James AspeedSDHCIState *sdhci = opaque; 1042bea128cSEddie James 1052bea128cSEddie James if (level) { 1062bea128cSEddie James sdhci->regs[TO_REG(ASPEED_SDHCI_IRQ_STAT)] |= BIT(n); 1072bea128cSEddie James 1082bea128cSEddie James qemu_irq_raise(sdhci->irq); 1092bea128cSEddie James } else { 1102bea128cSEddie James sdhci->regs[TO_REG(ASPEED_SDHCI_IRQ_STAT)] &= ~BIT(n); 1112bea128cSEddie James 1122bea128cSEddie James qemu_irq_lower(sdhci->irq); 1132bea128cSEddie James } 1142bea128cSEddie James } 1152bea128cSEddie James 1162bea128cSEddie James static void aspeed_sdhci_realize(DeviceState *dev, Error **errp) 1172bea128cSEddie James { 1182bea128cSEddie James Error *err = NULL; 1192bea128cSEddie James SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 1202bea128cSEddie James AspeedSDHCIState *sdhci = ASPEED_SDHCI(dev); 1212bea128cSEddie James 1222bea128cSEddie James /* Create input irqs for the slots */ 1232bea128cSEddie James qdev_init_gpio_in_named_with_opaque(DEVICE(sbd), aspeed_sdhci_set_irq, 124*0e2c24c6SAndrew Jeffery sdhci, NULL, sdhci->num_slots); 1252bea128cSEddie James 1262bea128cSEddie James sysbus_init_irq(sbd, &sdhci->irq); 1272bea128cSEddie James memory_region_init_io(&sdhci->iomem, OBJECT(sdhci), &aspeed_sdhci_ops, 1282bea128cSEddie James sdhci, TYPE_ASPEED_SDHCI, 0x1000); 1292bea128cSEddie James sysbus_init_mmio(sbd, &sdhci->iomem); 1302bea128cSEddie James 131*0e2c24c6SAndrew Jeffery for (int i = 0; i < sdhci->num_slots; ++i) { 1322bea128cSEddie James Object *sdhci_slot = OBJECT(&sdhci->slots[i]); 1332bea128cSEddie James SysBusDevice *sbd_slot = SYS_BUS_DEVICE(&sdhci->slots[i]); 1342bea128cSEddie James 1352bea128cSEddie James object_property_set_int(sdhci_slot, 2, "sd-spec-version", &err); 1362bea128cSEddie James if (err) { 1372bea128cSEddie James error_propagate(errp, err); 1382bea128cSEddie James return; 1392bea128cSEddie James } 1402bea128cSEddie James 1412bea128cSEddie James object_property_set_uint(sdhci_slot, ASPEED_SDHCI_CAPABILITIES, 1422bea128cSEddie James "capareg", &err); 1432bea128cSEddie James if (err) { 1442bea128cSEddie James error_propagate(errp, err); 1452bea128cSEddie James return; 1462bea128cSEddie James } 1472bea128cSEddie James 1482bea128cSEddie James object_property_set_bool(sdhci_slot, true, "realized", &err); 1492bea128cSEddie James if (err) { 1502bea128cSEddie James error_propagate(errp, err); 1512bea128cSEddie James return; 1522bea128cSEddie James } 1532bea128cSEddie James 1542bea128cSEddie James sysbus_connect_irq(sbd_slot, 0, qdev_get_gpio_in(DEVICE(sbd), i)); 1552bea128cSEddie James memory_region_add_subregion(&sdhci->iomem, (i + 1) * 0x100, 1562bea128cSEddie James &sdhci->slots[i].iomem); 1572bea128cSEddie James } 1582bea128cSEddie James } 1592bea128cSEddie James 1602bea128cSEddie James static void aspeed_sdhci_reset(DeviceState *dev) 1612bea128cSEddie James { 1622bea128cSEddie James AspeedSDHCIState *sdhci = ASPEED_SDHCI(dev); 1632bea128cSEddie James 1642bea128cSEddie James memset(sdhci->regs, 0, ASPEED_SDHCI_REG_SIZE); 1652bea128cSEddie James sdhci->regs[TO_REG(ASPEED_SDHCI_INFO)] = ASPEED_SDHCI_INFO_RESET; 1662bea128cSEddie James sdhci->regs[TO_REG(ASPEED_SDHCI_DEBOUNCE)] = ASPEED_SDHCI_DEBOUNCE_RESET; 1672bea128cSEddie James } 1682bea128cSEddie James 1692bea128cSEddie James static const VMStateDescription vmstate_aspeed_sdhci = { 1702bea128cSEddie James .name = TYPE_ASPEED_SDHCI, 1712bea128cSEddie James .version_id = 1, 1722bea128cSEddie James .fields = (VMStateField[]) { 1732bea128cSEddie James VMSTATE_UINT32_ARRAY(regs, AspeedSDHCIState, ASPEED_SDHCI_NUM_REGS), 1742bea128cSEddie James VMSTATE_END_OF_LIST(), 1752bea128cSEddie James }, 1762bea128cSEddie James }; 1772bea128cSEddie James 178*0e2c24c6SAndrew Jeffery static Property aspeed_sdhci_properties[] = { 179*0e2c24c6SAndrew Jeffery DEFINE_PROP_UINT8("num-slots", AspeedSDHCIState, num_slots, 0), 180*0e2c24c6SAndrew Jeffery DEFINE_PROP_END_OF_LIST(), 181*0e2c24c6SAndrew Jeffery }; 182*0e2c24c6SAndrew Jeffery 1832bea128cSEddie James static void aspeed_sdhci_class_init(ObjectClass *classp, void *data) 1842bea128cSEddie James { 1852bea128cSEddie James DeviceClass *dc = DEVICE_CLASS(classp); 1862bea128cSEddie James 1872bea128cSEddie James dc->realize = aspeed_sdhci_realize; 1882bea128cSEddie James dc->reset = aspeed_sdhci_reset; 1892bea128cSEddie James dc->vmsd = &vmstate_aspeed_sdhci; 190*0e2c24c6SAndrew Jeffery device_class_set_props(dc, aspeed_sdhci_properties); 1912bea128cSEddie James } 1922bea128cSEddie James 1932bea128cSEddie James static TypeInfo aspeed_sdhci_info = { 1942bea128cSEddie James .name = TYPE_ASPEED_SDHCI, 1952bea128cSEddie James .parent = TYPE_SYS_BUS_DEVICE, 1962bea128cSEddie James .instance_size = sizeof(AspeedSDHCIState), 1972bea128cSEddie James .class_init = aspeed_sdhci_class_init, 1982bea128cSEddie James }; 1992bea128cSEddie James 2002bea128cSEddie James static void aspeed_sdhci_register_types(void) 2012bea128cSEddie James { 2022bea128cSEddie James type_register_static(&aspeed_sdhci_info); 2032bea128cSEddie James } 2042bea128cSEddie James 2052bea128cSEddie James type_init(aspeed_sdhci_register_types) 206