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