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