18092b518SShengtan Mao /* 28092b518SShengtan Mao * NPCM7xx SD-3.0 / eMMC-4.51 Host Controller 38092b518SShengtan Mao * 48092b518SShengtan Mao * Copyright (c) 2021 Google LLC 58092b518SShengtan Mao * 68092b518SShengtan Mao * This program is free software; you can redistribute it and/or modify it 78092b518SShengtan Mao * under the terms of the GNU General Public License as published by the 88092b518SShengtan Mao * Free Software Foundation; either version 2 of the License, or 98092b518SShengtan Mao * (at your option) any later version. 108092b518SShengtan Mao * 118092b518SShengtan Mao * This program is distributed in the hope that it will be useful, but WITHOUT 128092b518SShengtan Mao * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 138092b518SShengtan Mao * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 148092b518SShengtan Mao * for more details. 158092b518SShengtan Mao */ 168092b518SShengtan Mao 178092b518SShengtan Mao #include "qemu/osdep.h" 188092b518SShengtan Mao 19f28cfc39SPhilippe Mathieu-Daudé #include "hw/sd/sdhci.h" 208092b518SShengtan Mao #include "hw/sd/npcm7xx_sdhci.h" 218092b518SShengtan Mao #include "migration/vmstate.h" 228092b518SShengtan Mao #include "sdhci-internal.h" 238092b518SShengtan Mao #include "qemu/log.h" 248092b518SShengtan Mao 258092b518SShengtan Mao static uint64_t npcm7xx_sdhci_read(void *opaque, hwaddr addr, unsigned int size) 268092b518SShengtan Mao { 278092b518SShengtan Mao NPCM7xxSDHCIState *s = opaque; 288092b518SShengtan Mao uint64_t val = 0; 298092b518SShengtan Mao 308092b518SShengtan Mao switch (addr) { 318092b518SShengtan Mao case NPCM7XX_PRSTVALS_0: 328092b518SShengtan Mao case NPCM7XX_PRSTVALS_1: 338092b518SShengtan Mao case NPCM7XX_PRSTVALS_2: 348092b518SShengtan Mao case NPCM7XX_PRSTVALS_3: 358092b518SShengtan Mao case NPCM7XX_PRSTVALS_4: 368092b518SShengtan Mao case NPCM7XX_PRSTVALS_5: 378092b518SShengtan Mao val = s->regs.prstvals[(addr - NPCM7XX_PRSTVALS_0) / 2]; 388092b518SShengtan Mao break; 398092b518SShengtan Mao case NPCM7XX_BOOTTOCTRL: 408092b518SShengtan Mao val = s->regs.boottoctrl; 418092b518SShengtan Mao break; 428092b518SShengtan Mao default: 438092b518SShengtan Mao qemu_log_mask(LOG_GUEST_ERROR, "SDHCI read of nonexistent reg: 0x%02" 448092b518SShengtan Mao HWADDR_PRIx, addr); 458092b518SShengtan Mao break; 468092b518SShengtan Mao } 478092b518SShengtan Mao 488092b518SShengtan Mao return val; 498092b518SShengtan Mao } 508092b518SShengtan Mao 518092b518SShengtan Mao static void npcm7xx_sdhci_write(void *opaque, hwaddr addr, uint64_t val, 528092b518SShengtan Mao unsigned int size) 538092b518SShengtan Mao { 548092b518SShengtan Mao NPCM7xxSDHCIState *s = opaque; 558092b518SShengtan Mao 568092b518SShengtan Mao switch (addr) { 578092b518SShengtan Mao case NPCM7XX_BOOTTOCTRL: 588092b518SShengtan Mao s->regs.boottoctrl = val; 598092b518SShengtan Mao break; 608092b518SShengtan Mao default: 618092b518SShengtan Mao qemu_log_mask(LOG_GUEST_ERROR, "SDHCI write of nonexistent reg: 0x%02" 628092b518SShengtan Mao HWADDR_PRIx, addr); 638092b518SShengtan Mao break; 648092b518SShengtan Mao } 658092b518SShengtan Mao } 668092b518SShengtan Mao 678092b518SShengtan Mao static bool npcm7xx_sdhci_check_mem_op(void *opaque, hwaddr addr, 688092b518SShengtan Mao unsigned size, bool is_write, 698092b518SShengtan Mao MemTxAttrs attrs) 708092b518SShengtan Mao { 718092b518SShengtan Mao switch (addr) { 728092b518SShengtan Mao case NPCM7XX_PRSTVALS_0: 738092b518SShengtan Mao case NPCM7XX_PRSTVALS_1: 748092b518SShengtan Mao case NPCM7XX_PRSTVALS_2: 758092b518SShengtan Mao case NPCM7XX_PRSTVALS_3: 768092b518SShengtan Mao case NPCM7XX_PRSTVALS_4: 778092b518SShengtan Mao case NPCM7XX_PRSTVALS_5: 788092b518SShengtan Mao /* RO Word */ 798092b518SShengtan Mao return !is_write && size == 2; 808092b518SShengtan Mao case NPCM7XX_BOOTTOCTRL: 818092b518SShengtan Mao /* R/W Dword */ 828092b518SShengtan Mao return size == 4; 838092b518SShengtan Mao default: 848092b518SShengtan Mao return false; 858092b518SShengtan Mao } 868092b518SShengtan Mao } 878092b518SShengtan Mao 888092b518SShengtan Mao static const MemoryRegionOps npcm7xx_sdhci_ops = { 898092b518SShengtan Mao .read = npcm7xx_sdhci_read, 908092b518SShengtan Mao .write = npcm7xx_sdhci_write, 918092b518SShengtan Mao .endianness = DEVICE_NATIVE_ENDIAN, 928092b518SShengtan Mao .valid = { 938092b518SShengtan Mao .min_access_size = 1, 948092b518SShengtan Mao .max_access_size = 4, 958092b518SShengtan Mao .unaligned = false, 968092b518SShengtan Mao .accepts = npcm7xx_sdhci_check_mem_op, 978092b518SShengtan Mao }, 988092b518SShengtan Mao }; 998092b518SShengtan Mao 1008092b518SShengtan Mao static void npcm7xx_sdhci_realize(DeviceState *dev, Error **errp) 1018092b518SShengtan Mao { 1028092b518SShengtan Mao NPCM7xxSDHCIState *s = NPCM7XX_SDHCI(dev); 1038092b518SShengtan Mao SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 1048092b518SShengtan Mao SysBusDevice *sbd_sdhci = SYS_BUS_DEVICE(&s->sdhci); 1058092b518SShengtan Mao 1068092b518SShengtan Mao memory_region_init(&s->container, OBJECT(s), 1078092b518SShengtan Mao "npcm7xx.sdhci-container", 0x1000); 1088092b518SShengtan Mao sysbus_init_mmio(sbd, &s->container); 1098092b518SShengtan Mao 1108092b518SShengtan Mao memory_region_init_io(&s->iomem, OBJECT(s), &npcm7xx_sdhci_ops, s, 1118092b518SShengtan Mao TYPE_NPCM7XX_SDHCI, NPCM7XX_SDHCI_REGSIZE); 1128092b518SShengtan Mao memory_region_add_subregion_overlap(&s->container, NPCM7XX_PRSTVALS, 1138092b518SShengtan Mao &s->iomem, 1); 1148092b518SShengtan Mao 1158092b518SShengtan Mao sysbus_realize(sbd_sdhci, errp); 1168092b518SShengtan Mao memory_region_add_subregion(&s->container, 0, 1178092b518SShengtan Mao sysbus_mmio_get_region(sbd_sdhci, 0)); 1188092b518SShengtan Mao 1198092b518SShengtan Mao /* propagate irq and "sd-bus" from generic-sdhci */ 1208092b518SShengtan Mao sysbus_pass_irq(sbd, sbd_sdhci); 1218092b518SShengtan Mao s->bus = qdev_get_child_bus(DEVICE(sbd_sdhci), "sd-bus"); 1228092b518SShengtan Mao 1238092b518SShengtan Mao /* Set the read only preset values. */ 1248092b518SShengtan Mao memset(s->regs.prstvals, 0, sizeof(s->regs.prstvals)); 1258092b518SShengtan Mao s->regs.prstvals[0] = NPCM7XX_PRSTVALS_0_RESET; 1268092b518SShengtan Mao s->regs.prstvals[1] = NPCM7XX_PRSTVALS_1_RESET; 1278092b518SShengtan Mao s->regs.prstvals[3] = NPCM7XX_PRSTVALS_3_RESET; 1288092b518SShengtan Mao } 1298092b518SShengtan Mao 1308092b518SShengtan Mao static void npcm7xx_sdhci_reset(DeviceState *dev) 1318092b518SShengtan Mao { 1328092b518SShengtan Mao NPCM7xxSDHCIState *s = NPCM7XX_SDHCI(dev); 1338092b518SShengtan Mao device_cold_reset(DEVICE(&s->sdhci)); 1348092b518SShengtan Mao s->regs.boottoctrl = 0; 1358092b518SShengtan Mao 1368092b518SShengtan Mao s->sdhci.prnsts = NPCM7XX_PRSNTS_RESET; 1378092b518SShengtan Mao s->sdhci.blkgap = NPCM7XX_BLKGAP_RESET; 1388092b518SShengtan Mao s->sdhci.capareg = NPCM7XX_CAPAB_RESET; 1398092b518SShengtan Mao s->sdhci.maxcurr = NPCM7XX_MAXCURR_RESET; 1408092b518SShengtan Mao s->sdhci.version = NPCM7XX_HCVER_RESET; 1418092b518SShengtan Mao } 1428092b518SShengtan Mao 1438092b518SShengtan Mao static const VMStateDescription vmstate_npcm7xx_sdhci = { 1448092b518SShengtan Mao .name = TYPE_NPCM7XX_SDHCI, 1458092b518SShengtan Mao .version_id = 0, 146307119baSRichard Henderson .fields = (const VMStateField[]) { 1478092b518SShengtan Mao VMSTATE_UINT32(regs.boottoctrl, NPCM7xxSDHCIState), 1488092b518SShengtan Mao VMSTATE_END_OF_LIST(), 1498092b518SShengtan Mao }, 1508092b518SShengtan Mao }; 1518092b518SShengtan Mao 1528092b518SShengtan Mao static void npcm7xx_sdhci_class_init(ObjectClass *classp, void *data) 1538092b518SShengtan Mao { 1548092b518SShengtan Mao DeviceClass *dc = DEVICE_CLASS(classp); 1558092b518SShengtan Mao 1568092b518SShengtan Mao dc->desc = "NPCM7xx SD/eMMC Host Controller"; 1578092b518SShengtan Mao dc->realize = npcm7xx_sdhci_realize; 158*e3d08143SPeter Maydell device_class_set_legacy_reset(dc, npcm7xx_sdhci_reset); 1598092b518SShengtan Mao dc->vmsd = &vmstate_npcm7xx_sdhci; 1608092b518SShengtan Mao } 1618092b518SShengtan Mao 1628092b518SShengtan Mao static void npcm7xx_sdhci_instance_init(Object *obj) 1638092b518SShengtan Mao { 1648092b518SShengtan Mao NPCM7xxSDHCIState *s = NPCM7XX_SDHCI(obj); 1658092b518SShengtan Mao 166f28cfc39SPhilippe Mathieu-Daudé object_initialize_child(OBJECT(s), TYPE_SYSBUS_SDHCI, &s->sdhci, 1678092b518SShengtan Mao TYPE_SYSBUS_SDHCI); 1688092b518SShengtan Mao } 1698092b518SShengtan Mao 17088d2198cSPhilippe Mathieu-Daudé static const TypeInfo npcm7xx_sdhci_types[] = { 17188d2198cSPhilippe Mathieu-Daudé { 1728092b518SShengtan Mao .name = TYPE_NPCM7XX_SDHCI, 1738092b518SShengtan Mao .parent = TYPE_SYS_BUS_DEVICE, 1748092b518SShengtan Mao .instance_size = sizeof(NPCM7xxSDHCIState), 1758092b518SShengtan Mao .instance_init = npcm7xx_sdhci_instance_init, 1768092b518SShengtan Mao .class_init = npcm7xx_sdhci_class_init, 17788d2198cSPhilippe Mathieu-Daudé }, 1788092b518SShengtan Mao }; 1798092b518SShengtan Mao 18088d2198cSPhilippe Mathieu-Daudé DEFINE_TYPES(npcm7xx_sdhci_types) 181