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