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 198092b518SShengtan Mao #include "hw/sd/npcm7xx_sdhci.h" 208092b518SShengtan Mao #include "migration/vmstate.h" 218092b518SShengtan Mao #include "sdhci-internal.h" 228092b518SShengtan Mao #include "qemu/log.h" 238092b518SShengtan Mao 248092b518SShengtan Mao static uint64_t npcm7xx_sdhci_read(void *opaque, hwaddr addr, unsigned int size) 258092b518SShengtan Mao { 268092b518SShengtan Mao NPCM7xxSDHCIState *s = opaque; 278092b518SShengtan Mao uint64_t val = 0; 288092b518SShengtan Mao 298092b518SShengtan Mao switch (addr) { 308092b518SShengtan Mao case NPCM7XX_PRSTVALS_0: 318092b518SShengtan Mao case NPCM7XX_PRSTVALS_1: 328092b518SShengtan Mao case NPCM7XX_PRSTVALS_2: 338092b518SShengtan Mao case NPCM7XX_PRSTVALS_3: 348092b518SShengtan Mao case NPCM7XX_PRSTVALS_4: 358092b518SShengtan Mao case NPCM7XX_PRSTVALS_5: 368092b518SShengtan Mao val = s->regs.prstvals[(addr - NPCM7XX_PRSTVALS_0) / 2]; 378092b518SShengtan Mao break; 388092b518SShengtan Mao case NPCM7XX_BOOTTOCTRL: 398092b518SShengtan Mao val = s->regs.boottoctrl; 408092b518SShengtan Mao break; 418092b518SShengtan Mao default: 428092b518SShengtan Mao qemu_log_mask(LOG_GUEST_ERROR, "SDHCI read of nonexistent reg: 0x%02" 438092b518SShengtan Mao HWADDR_PRIx, addr); 448092b518SShengtan Mao break; 458092b518SShengtan Mao } 468092b518SShengtan Mao 478092b518SShengtan Mao return val; 488092b518SShengtan Mao } 498092b518SShengtan Mao 508092b518SShengtan Mao static void npcm7xx_sdhci_write(void *opaque, hwaddr addr, uint64_t val, 518092b518SShengtan Mao unsigned int size) 528092b518SShengtan Mao { 538092b518SShengtan Mao NPCM7xxSDHCIState *s = opaque; 548092b518SShengtan Mao 558092b518SShengtan Mao switch (addr) { 568092b518SShengtan Mao case NPCM7XX_BOOTTOCTRL: 578092b518SShengtan Mao s->regs.boottoctrl = val; 588092b518SShengtan Mao break; 598092b518SShengtan Mao default: 608092b518SShengtan Mao qemu_log_mask(LOG_GUEST_ERROR, "SDHCI write of nonexistent reg: 0x%02" 618092b518SShengtan Mao HWADDR_PRIx, addr); 628092b518SShengtan Mao break; 638092b518SShengtan Mao } 648092b518SShengtan Mao } 658092b518SShengtan Mao 668092b518SShengtan Mao static bool npcm7xx_sdhci_check_mem_op(void *opaque, hwaddr addr, 678092b518SShengtan Mao unsigned size, bool is_write, 688092b518SShengtan Mao MemTxAttrs attrs) 698092b518SShengtan Mao { 708092b518SShengtan Mao switch (addr) { 718092b518SShengtan Mao case NPCM7XX_PRSTVALS_0: 728092b518SShengtan Mao case NPCM7XX_PRSTVALS_1: 738092b518SShengtan Mao case NPCM7XX_PRSTVALS_2: 748092b518SShengtan Mao case NPCM7XX_PRSTVALS_3: 758092b518SShengtan Mao case NPCM7XX_PRSTVALS_4: 768092b518SShengtan Mao case NPCM7XX_PRSTVALS_5: 778092b518SShengtan Mao /* RO Word */ 788092b518SShengtan Mao return !is_write && size == 2; 798092b518SShengtan Mao case NPCM7XX_BOOTTOCTRL: 808092b518SShengtan Mao /* R/W Dword */ 818092b518SShengtan Mao return size == 4; 828092b518SShengtan Mao default: 838092b518SShengtan Mao return false; 848092b518SShengtan Mao } 858092b518SShengtan Mao } 868092b518SShengtan Mao 878092b518SShengtan Mao static const MemoryRegionOps npcm7xx_sdhci_ops = { 888092b518SShengtan Mao .read = npcm7xx_sdhci_read, 898092b518SShengtan Mao .write = npcm7xx_sdhci_write, 908092b518SShengtan Mao .endianness = DEVICE_NATIVE_ENDIAN, 918092b518SShengtan Mao .valid = { 928092b518SShengtan Mao .min_access_size = 1, 938092b518SShengtan Mao .max_access_size = 4, 948092b518SShengtan Mao .unaligned = false, 958092b518SShengtan Mao .accepts = npcm7xx_sdhci_check_mem_op, 968092b518SShengtan Mao }, 978092b518SShengtan Mao }; 988092b518SShengtan Mao 998092b518SShengtan Mao static void npcm7xx_sdhci_realize(DeviceState *dev, Error **errp) 1008092b518SShengtan Mao { 1018092b518SShengtan Mao NPCM7xxSDHCIState *s = NPCM7XX_SDHCI(dev); 1028092b518SShengtan Mao SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 1038092b518SShengtan Mao SysBusDevice *sbd_sdhci = SYS_BUS_DEVICE(&s->sdhci); 1048092b518SShengtan Mao 1058092b518SShengtan Mao memory_region_init(&s->container, OBJECT(s), 1068092b518SShengtan Mao "npcm7xx.sdhci-container", 0x1000); 1078092b518SShengtan Mao sysbus_init_mmio(sbd, &s->container); 1088092b518SShengtan Mao 1098092b518SShengtan Mao memory_region_init_io(&s->iomem, OBJECT(s), &npcm7xx_sdhci_ops, s, 1108092b518SShengtan Mao TYPE_NPCM7XX_SDHCI, NPCM7XX_SDHCI_REGSIZE); 1118092b518SShengtan Mao memory_region_add_subregion_overlap(&s->container, NPCM7XX_PRSTVALS, 1128092b518SShengtan Mao &s->iomem, 1); 1138092b518SShengtan Mao 1148092b518SShengtan Mao sysbus_realize(sbd_sdhci, errp); 1158092b518SShengtan Mao memory_region_add_subregion(&s->container, 0, 1168092b518SShengtan Mao sysbus_mmio_get_region(sbd_sdhci, 0)); 1178092b518SShengtan Mao 1188092b518SShengtan Mao /* propagate irq and "sd-bus" from generic-sdhci */ 1198092b518SShengtan Mao sysbus_pass_irq(sbd, sbd_sdhci); 1208092b518SShengtan Mao s->bus = qdev_get_child_bus(DEVICE(sbd_sdhci), "sd-bus"); 1218092b518SShengtan Mao 1228092b518SShengtan Mao /* Set the read only preset values. */ 1238092b518SShengtan Mao memset(s->regs.prstvals, 0, sizeof(s->regs.prstvals)); 1248092b518SShengtan Mao s->regs.prstvals[0] = NPCM7XX_PRSTVALS_0_RESET; 1258092b518SShengtan Mao s->regs.prstvals[1] = NPCM7XX_PRSTVALS_1_RESET; 1268092b518SShengtan Mao s->regs.prstvals[3] = NPCM7XX_PRSTVALS_3_RESET; 1278092b518SShengtan Mao } 1288092b518SShengtan Mao 1298092b518SShengtan Mao static void npcm7xx_sdhci_reset(DeviceState *dev) 1308092b518SShengtan Mao { 1318092b518SShengtan Mao NPCM7xxSDHCIState *s = NPCM7XX_SDHCI(dev); 1328092b518SShengtan Mao device_cold_reset(DEVICE(&s->sdhci)); 1338092b518SShengtan Mao s->regs.boottoctrl = 0; 1348092b518SShengtan Mao 1358092b518SShengtan Mao s->sdhci.prnsts = NPCM7XX_PRSNTS_RESET; 1368092b518SShengtan Mao s->sdhci.blkgap = NPCM7XX_BLKGAP_RESET; 1378092b518SShengtan Mao s->sdhci.capareg = NPCM7XX_CAPAB_RESET; 1388092b518SShengtan Mao s->sdhci.maxcurr = NPCM7XX_MAXCURR_RESET; 1398092b518SShengtan Mao s->sdhci.version = NPCM7XX_HCVER_RESET; 1408092b518SShengtan Mao } 1418092b518SShengtan Mao 1428092b518SShengtan Mao static const VMStateDescription vmstate_npcm7xx_sdhci = { 1438092b518SShengtan Mao .name = TYPE_NPCM7XX_SDHCI, 1448092b518SShengtan Mao .version_id = 0, 145*307119baSRichard Henderson .fields = (const VMStateField[]) { 1468092b518SShengtan Mao VMSTATE_UINT32(regs.boottoctrl, NPCM7xxSDHCIState), 1478092b518SShengtan Mao VMSTATE_END_OF_LIST(), 1488092b518SShengtan Mao }, 1498092b518SShengtan Mao }; 1508092b518SShengtan Mao 1518092b518SShengtan Mao static void npcm7xx_sdhci_class_init(ObjectClass *classp, void *data) 1528092b518SShengtan Mao { 1538092b518SShengtan Mao DeviceClass *dc = DEVICE_CLASS(classp); 1548092b518SShengtan Mao 1558092b518SShengtan Mao dc->desc = "NPCM7xx SD/eMMC Host Controller"; 1568092b518SShengtan Mao dc->realize = npcm7xx_sdhci_realize; 1578092b518SShengtan Mao dc->reset = npcm7xx_sdhci_reset; 1588092b518SShengtan Mao dc->vmsd = &vmstate_npcm7xx_sdhci; 1598092b518SShengtan Mao } 1608092b518SShengtan Mao 1618092b518SShengtan Mao static void npcm7xx_sdhci_instance_init(Object *obj) 1628092b518SShengtan Mao { 1638092b518SShengtan Mao NPCM7xxSDHCIState *s = NPCM7XX_SDHCI(obj); 1648092b518SShengtan Mao 1658092b518SShengtan Mao object_initialize_child(OBJECT(s), "generic-sdhci", &s->sdhci, 1668092b518SShengtan Mao TYPE_SYSBUS_SDHCI); 1678092b518SShengtan Mao } 1688092b518SShengtan Mao 16988d2198cSPhilippe Mathieu-Daudé static const TypeInfo npcm7xx_sdhci_types[] = { 17088d2198cSPhilippe Mathieu-Daudé { 1718092b518SShengtan Mao .name = TYPE_NPCM7XX_SDHCI, 1728092b518SShengtan Mao .parent = TYPE_SYS_BUS_DEVICE, 1738092b518SShengtan Mao .instance_size = sizeof(NPCM7xxSDHCIState), 1748092b518SShengtan Mao .instance_init = npcm7xx_sdhci_instance_init, 1758092b518SShengtan Mao .class_init = npcm7xx_sdhci_class_init, 17688d2198cSPhilippe Mathieu-Daudé }, 1778092b518SShengtan Mao }; 1788092b518SShengtan Mao 17988d2198cSPhilippe Mathieu-Daudé DEFINE_TYPES(npcm7xx_sdhci_types) 180