1 /* 2 * NPCM7xx SD-3.0 / eMMC-4.51 Host Controller 3 * 4 * Copyright (c) 2021 Google LLC 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License as published by the 8 * Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * for more details. 15 */ 16 17 #include "qemu/osdep.h" 18 19 #include "hw/sd/npcm7xx_sdhci.h" 20 #include "migration/vmstate.h" 21 #include "sdhci-internal.h" 22 #include "qemu/log.h" 23 24 static uint64_t npcm7xx_sdhci_read(void *opaque, hwaddr addr, unsigned int size) 25 { 26 NPCM7xxSDHCIState *s = opaque; 27 uint64_t val = 0; 28 29 switch (addr) { 30 case NPCM7XX_PRSTVALS_0: 31 case NPCM7XX_PRSTVALS_1: 32 case NPCM7XX_PRSTVALS_2: 33 case NPCM7XX_PRSTVALS_3: 34 case NPCM7XX_PRSTVALS_4: 35 case NPCM7XX_PRSTVALS_5: 36 val = s->regs.prstvals[(addr - NPCM7XX_PRSTVALS_0) / 2]; 37 break; 38 case NPCM7XX_BOOTTOCTRL: 39 val = s->regs.boottoctrl; 40 break; 41 default: 42 qemu_log_mask(LOG_GUEST_ERROR, "SDHCI read of nonexistent reg: 0x%02" 43 HWADDR_PRIx, addr); 44 break; 45 } 46 47 return val; 48 } 49 50 static void npcm7xx_sdhci_write(void *opaque, hwaddr addr, uint64_t val, 51 unsigned int size) 52 { 53 NPCM7xxSDHCIState *s = opaque; 54 55 switch (addr) { 56 case NPCM7XX_BOOTTOCTRL: 57 s->regs.boottoctrl = val; 58 break; 59 default: 60 qemu_log_mask(LOG_GUEST_ERROR, "SDHCI write of nonexistent reg: 0x%02" 61 HWADDR_PRIx, addr); 62 break; 63 } 64 } 65 66 static bool npcm7xx_sdhci_check_mem_op(void *opaque, hwaddr addr, 67 unsigned size, bool is_write, 68 MemTxAttrs attrs) 69 { 70 switch (addr) { 71 case NPCM7XX_PRSTVALS_0: 72 case NPCM7XX_PRSTVALS_1: 73 case NPCM7XX_PRSTVALS_2: 74 case NPCM7XX_PRSTVALS_3: 75 case NPCM7XX_PRSTVALS_4: 76 case NPCM7XX_PRSTVALS_5: 77 /* RO Word */ 78 return !is_write && size == 2; 79 case NPCM7XX_BOOTTOCTRL: 80 /* R/W Dword */ 81 return size == 4; 82 default: 83 return false; 84 } 85 } 86 87 static const MemoryRegionOps npcm7xx_sdhci_ops = { 88 .read = npcm7xx_sdhci_read, 89 .write = npcm7xx_sdhci_write, 90 .endianness = DEVICE_NATIVE_ENDIAN, 91 .valid = { 92 .min_access_size = 1, 93 .max_access_size = 4, 94 .unaligned = false, 95 .accepts = npcm7xx_sdhci_check_mem_op, 96 }, 97 }; 98 99 static void npcm7xx_sdhci_realize(DeviceState *dev, Error **errp) 100 { 101 NPCM7xxSDHCIState *s = NPCM7XX_SDHCI(dev); 102 SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 103 SysBusDevice *sbd_sdhci = SYS_BUS_DEVICE(&s->sdhci); 104 105 memory_region_init(&s->container, OBJECT(s), 106 "npcm7xx.sdhci-container", 0x1000); 107 sysbus_init_mmio(sbd, &s->container); 108 109 memory_region_init_io(&s->iomem, OBJECT(s), &npcm7xx_sdhci_ops, s, 110 TYPE_NPCM7XX_SDHCI, NPCM7XX_SDHCI_REGSIZE); 111 memory_region_add_subregion_overlap(&s->container, NPCM7XX_PRSTVALS, 112 &s->iomem, 1); 113 114 sysbus_realize(sbd_sdhci, errp); 115 memory_region_add_subregion(&s->container, 0, 116 sysbus_mmio_get_region(sbd_sdhci, 0)); 117 118 /* propagate irq and "sd-bus" from generic-sdhci */ 119 sysbus_pass_irq(sbd, sbd_sdhci); 120 s->bus = qdev_get_child_bus(DEVICE(sbd_sdhci), "sd-bus"); 121 122 /* Set the read only preset values. */ 123 memset(s->regs.prstvals, 0, sizeof(s->regs.prstvals)); 124 s->regs.prstvals[0] = NPCM7XX_PRSTVALS_0_RESET; 125 s->regs.prstvals[1] = NPCM7XX_PRSTVALS_1_RESET; 126 s->regs.prstvals[3] = NPCM7XX_PRSTVALS_3_RESET; 127 } 128 129 static void npcm7xx_sdhci_reset(DeviceState *dev) 130 { 131 NPCM7xxSDHCIState *s = NPCM7XX_SDHCI(dev); 132 device_cold_reset(DEVICE(&s->sdhci)); 133 s->regs.boottoctrl = 0; 134 135 s->sdhci.prnsts = NPCM7XX_PRSNTS_RESET; 136 s->sdhci.blkgap = NPCM7XX_BLKGAP_RESET; 137 s->sdhci.capareg = NPCM7XX_CAPAB_RESET; 138 s->sdhci.maxcurr = NPCM7XX_MAXCURR_RESET; 139 s->sdhci.version = NPCM7XX_HCVER_RESET; 140 } 141 142 static const VMStateDescription vmstate_npcm7xx_sdhci = { 143 .name = TYPE_NPCM7XX_SDHCI, 144 .version_id = 0, 145 .fields = (VMStateField[]) { 146 VMSTATE_UINT32(regs.boottoctrl, NPCM7xxSDHCIState), 147 VMSTATE_END_OF_LIST(), 148 }, 149 }; 150 151 static void npcm7xx_sdhci_class_init(ObjectClass *classp, void *data) 152 { 153 DeviceClass *dc = DEVICE_CLASS(classp); 154 155 dc->desc = "NPCM7xx SD/eMMC Host Controller"; 156 dc->realize = npcm7xx_sdhci_realize; 157 dc->reset = npcm7xx_sdhci_reset; 158 dc->vmsd = &vmstate_npcm7xx_sdhci; 159 } 160 161 static void npcm7xx_sdhci_instance_init(Object *obj) 162 { 163 NPCM7xxSDHCIState *s = NPCM7XX_SDHCI(obj); 164 165 object_initialize_child(OBJECT(s), "generic-sdhci", &s->sdhci, 166 TYPE_SYSBUS_SDHCI); 167 } 168 169 static const TypeInfo npcm7xx_sdhci_types[] = { 170 { 171 .name = TYPE_NPCM7XX_SDHCI, 172 .parent = TYPE_SYS_BUS_DEVICE, 173 .instance_size = sizeof(NPCM7xxSDHCIState), 174 .instance_init = npcm7xx_sdhci_instance_init, 175 .class_init = npcm7xx_sdhci_class_init, 176 }, 177 }; 178 179 DEFINE_TYPES(npcm7xx_sdhci_types) 180