1*edd3a59dSStrahinja Jankovic /* 2*edd3a59dSStrahinja Jankovic * Allwinner A10 DRAM Controller emulation 3*edd3a59dSStrahinja Jankovic * 4*edd3a59dSStrahinja Jankovic * Copyright (C) 2022 Strahinja Jankovic <strahinja.p.jankovic@gmail.com> 5*edd3a59dSStrahinja Jankovic * 6*edd3a59dSStrahinja Jankovic * This file is derived from Allwinner H3 DRAMC, 7*edd3a59dSStrahinja Jankovic * by Niek Linnenbank. 8*edd3a59dSStrahinja Jankovic * 9*edd3a59dSStrahinja Jankovic * This program is free software: you can redistribute it and/or modify 10*edd3a59dSStrahinja Jankovic * it under the terms of the GNU General Public License as published by 11*edd3a59dSStrahinja Jankovic * the Free Software Foundation, either version 2 of the License, or 12*edd3a59dSStrahinja Jankovic * (at your option) any later version. 13*edd3a59dSStrahinja Jankovic * 14*edd3a59dSStrahinja Jankovic * This program is distributed in the hope that it will be useful, 15*edd3a59dSStrahinja Jankovic * but WITHOUT ANY WARRANTY; without even the implied warranty of 16*edd3a59dSStrahinja Jankovic * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17*edd3a59dSStrahinja Jankovic * GNU General Public License for more details. 18*edd3a59dSStrahinja Jankovic * 19*edd3a59dSStrahinja Jankovic * You should have received a copy of the GNU General Public License 20*edd3a59dSStrahinja Jankovic * along with this program. If not, see <http://www.gnu.org/licenses/>. 21*edd3a59dSStrahinja Jankovic */ 22*edd3a59dSStrahinja Jankovic 23*edd3a59dSStrahinja Jankovic #include "qemu/osdep.h" 24*edd3a59dSStrahinja Jankovic #include "qemu/units.h" 25*edd3a59dSStrahinja Jankovic #include "hw/sysbus.h" 26*edd3a59dSStrahinja Jankovic #include "migration/vmstate.h" 27*edd3a59dSStrahinja Jankovic #include "qemu/log.h" 28*edd3a59dSStrahinja Jankovic #include "qemu/module.h" 29*edd3a59dSStrahinja Jankovic #include "hw/misc/allwinner-a10-dramc.h" 30*edd3a59dSStrahinja Jankovic 31*edd3a59dSStrahinja Jankovic /* DRAMC register offsets */ 32*edd3a59dSStrahinja Jankovic enum { 33*edd3a59dSStrahinja Jankovic REG_SDR_CCR = 0x0000, 34*edd3a59dSStrahinja Jankovic REG_SDR_ZQCR0 = 0x00a8, 35*edd3a59dSStrahinja Jankovic REG_SDR_ZQSR = 0x00b0 36*edd3a59dSStrahinja Jankovic }; 37*edd3a59dSStrahinja Jankovic 38*edd3a59dSStrahinja Jankovic #define REG_INDEX(offset) (offset / sizeof(uint32_t)) 39*edd3a59dSStrahinja Jankovic 40*edd3a59dSStrahinja Jankovic /* DRAMC register flags */ 41*edd3a59dSStrahinja Jankovic enum { 42*edd3a59dSStrahinja Jankovic REG_SDR_CCR_DATA_TRAINING = (1 << 30), 43*edd3a59dSStrahinja Jankovic REG_SDR_CCR_DRAM_INIT = (1 << 31), 44*edd3a59dSStrahinja Jankovic }; 45*edd3a59dSStrahinja Jankovic enum { 46*edd3a59dSStrahinja Jankovic REG_SDR_ZQSR_ZCAL = (1 << 31), 47*edd3a59dSStrahinja Jankovic }; 48*edd3a59dSStrahinja Jankovic 49*edd3a59dSStrahinja Jankovic /* DRAMC register reset values */ 50*edd3a59dSStrahinja Jankovic enum { 51*edd3a59dSStrahinja Jankovic REG_SDR_CCR_RESET = 0x80020000, 52*edd3a59dSStrahinja Jankovic REG_SDR_ZQCR0_RESET = 0x07b00000, 53*edd3a59dSStrahinja Jankovic REG_SDR_ZQSR_RESET = 0x80000000 54*edd3a59dSStrahinja Jankovic }; 55*edd3a59dSStrahinja Jankovic 56*edd3a59dSStrahinja Jankovic static uint64_t allwinner_a10_dramc_read(void *opaque, hwaddr offset, 57*edd3a59dSStrahinja Jankovic unsigned size) 58*edd3a59dSStrahinja Jankovic { 59*edd3a59dSStrahinja Jankovic const AwA10DramControllerState *s = AW_A10_DRAMC(opaque); 60*edd3a59dSStrahinja Jankovic const uint32_t idx = REG_INDEX(offset); 61*edd3a59dSStrahinja Jankovic 62*edd3a59dSStrahinja Jankovic switch (offset) { 63*edd3a59dSStrahinja Jankovic case REG_SDR_CCR: 64*edd3a59dSStrahinja Jankovic case REG_SDR_ZQCR0: 65*edd3a59dSStrahinja Jankovic case REG_SDR_ZQSR: 66*edd3a59dSStrahinja Jankovic break; 67*edd3a59dSStrahinja Jankovic case 0x2e4 ... AW_A10_DRAMC_IOSIZE: 68*edd3a59dSStrahinja Jankovic qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", 69*edd3a59dSStrahinja Jankovic __func__, (uint32_t)offset); 70*edd3a59dSStrahinja Jankovic return 0; 71*edd3a59dSStrahinja Jankovic default: 72*edd3a59dSStrahinja Jankovic qemu_log_mask(LOG_UNIMP, "%s: unimplemented read offset 0x%04x\n", 73*edd3a59dSStrahinja Jankovic __func__, (uint32_t)offset); 74*edd3a59dSStrahinja Jankovic return 0; 75*edd3a59dSStrahinja Jankovic } 76*edd3a59dSStrahinja Jankovic 77*edd3a59dSStrahinja Jankovic return s->regs[idx]; 78*edd3a59dSStrahinja Jankovic } 79*edd3a59dSStrahinja Jankovic 80*edd3a59dSStrahinja Jankovic static void allwinner_a10_dramc_write(void *opaque, hwaddr offset, 81*edd3a59dSStrahinja Jankovic uint64_t val, unsigned size) 82*edd3a59dSStrahinja Jankovic { 83*edd3a59dSStrahinja Jankovic AwA10DramControllerState *s = AW_A10_DRAMC(opaque); 84*edd3a59dSStrahinja Jankovic const uint32_t idx = REG_INDEX(offset); 85*edd3a59dSStrahinja Jankovic 86*edd3a59dSStrahinja Jankovic switch (offset) { 87*edd3a59dSStrahinja Jankovic case REG_SDR_CCR: 88*edd3a59dSStrahinja Jankovic if (val & REG_SDR_CCR_DRAM_INIT) { 89*edd3a59dSStrahinja Jankovic /* Clear DRAM_INIT to indicate process is done. */ 90*edd3a59dSStrahinja Jankovic val &= ~REG_SDR_CCR_DRAM_INIT; 91*edd3a59dSStrahinja Jankovic } 92*edd3a59dSStrahinja Jankovic if (val & REG_SDR_CCR_DATA_TRAINING) { 93*edd3a59dSStrahinja Jankovic /* Clear DATA_TRAINING to indicate process is done. */ 94*edd3a59dSStrahinja Jankovic val &= ~REG_SDR_CCR_DATA_TRAINING; 95*edd3a59dSStrahinja Jankovic } 96*edd3a59dSStrahinja Jankovic break; 97*edd3a59dSStrahinja Jankovic case REG_SDR_ZQCR0: 98*edd3a59dSStrahinja Jankovic /* Set ZCAL in ZQSR to indicate calibration is done. */ 99*edd3a59dSStrahinja Jankovic s->regs[REG_INDEX(REG_SDR_ZQSR)] |= REG_SDR_ZQSR_ZCAL; 100*edd3a59dSStrahinja Jankovic break; 101*edd3a59dSStrahinja Jankovic case 0x2e4 ... AW_A10_DRAMC_IOSIZE: 102*edd3a59dSStrahinja Jankovic qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", 103*edd3a59dSStrahinja Jankovic __func__, (uint32_t)offset); 104*edd3a59dSStrahinja Jankovic break; 105*edd3a59dSStrahinja Jankovic default: 106*edd3a59dSStrahinja Jankovic qemu_log_mask(LOG_UNIMP, "%s: unimplemented write offset 0x%04x\n", 107*edd3a59dSStrahinja Jankovic __func__, (uint32_t)offset); 108*edd3a59dSStrahinja Jankovic break; 109*edd3a59dSStrahinja Jankovic } 110*edd3a59dSStrahinja Jankovic 111*edd3a59dSStrahinja Jankovic s->regs[idx] = (uint32_t) val; 112*edd3a59dSStrahinja Jankovic } 113*edd3a59dSStrahinja Jankovic 114*edd3a59dSStrahinja Jankovic static const MemoryRegionOps allwinner_a10_dramc_ops = { 115*edd3a59dSStrahinja Jankovic .read = allwinner_a10_dramc_read, 116*edd3a59dSStrahinja Jankovic .write = allwinner_a10_dramc_write, 117*edd3a59dSStrahinja Jankovic .endianness = DEVICE_NATIVE_ENDIAN, 118*edd3a59dSStrahinja Jankovic .valid = { 119*edd3a59dSStrahinja Jankovic .min_access_size = 4, 120*edd3a59dSStrahinja Jankovic .max_access_size = 4, 121*edd3a59dSStrahinja Jankovic }, 122*edd3a59dSStrahinja Jankovic .impl.min_access_size = 4, 123*edd3a59dSStrahinja Jankovic }; 124*edd3a59dSStrahinja Jankovic 125*edd3a59dSStrahinja Jankovic static void allwinner_a10_dramc_reset_enter(Object *obj, ResetType type) 126*edd3a59dSStrahinja Jankovic { 127*edd3a59dSStrahinja Jankovic AwA10DramControllerState *s = AW_A10_DRAMC(obj); 128*edd3a59dSStrahinja Jankovic 129*edd3a59dSStrahinja Jankovic /* Set default values for registers */ 130*edd3a59dSStrahinja Jankovic s->regs[REG_INDEX(REG_SDR_CCR)] = REG_SDR_CCR_RESET; 131*edd3a59dSStrahinja Jankovic s->regs[REG_INDEX(REG_SDR_ZQCR0)] = REG_SDR_ZQCR0_RESET; 132*edd3a59dSStrahinja Jankovic s->regs[REG_INDEX(REG_SDR_ZQSR)] = REG_SDR_ZQSR_RESET; 133*edd3a59dSStrahinja Jankovic } 134*edd3a59dSStrahinja Jankovic 135*edd3a59dSStrahinja Jankovic static void allwinner_a10_dramc_init(Object *obj) 136*edd3a59dSStrahinja Jankovic { 137*edd3a59dSStrahinja Jankovic SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 138*edd3a59dSStrahinja Jankovic AwA10DramControllerState *s = AW_A10_DRAMC(obj); 139*edd3a59dSStrahinja Jankovic 140*edd3a59dSStrahinja Jankovic /* Memory mapping */ 141*edd3a59dSStrahinja Jankovic memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_a10_dramc_ops, s, 142*edd3a59dSStrahinja Jankovic TYPE_AW_A10_DRAMC, AW_A10_DRAMC_IOSIZE); 143*edd3a59dSStrahinja Jankovic sysbus_init_mmio(sbd, &s->iomem); 144*edd3a59dSStrahinja Jankovic } 145*edd3a59dSStrahinja Jankovic 146*edd3a59dSStrahinja Jankovic static const VMStateDescription allwinner_a10_dramc_vmstate = { 147*edd3a59dSStrahinja Jankovic .name = "allwinner-a10-dramc", 148*edd3a59dSStrahinja Jankovic .version_id = 1, 149*edd3a59dSStrahinja Jankovic .minimum_version_id = 1, 150*edd3a59dSStrahinja Jankovic .fields = (VMStateField[]) { 151*edd3a59dSStrahinja Jankovic VMSTATE_UINT32_ARRAY(regs, AwA10DramControllerState, 152*edd3a59dSStrahinja Jankovic AW_A10_DRAMC_REGS_NUM), 153*edd3a59dSStrahinja Jankovic VMSTATE_END_OF_LIST() 154*edd3a59dSStrahinja Jankovic } 155*edd3a59dSStrahinja Jankovic }; 156*edd3a59dSStrahinja Jankovic 157*edd3a59dSStrahinja Jankovic static void allwinner_a10_dramc_class_init(ObjectClass *klass, void *data) 158*edd3a59dSStrahinja Jankovic { 159*edd3a59dSStrahinja Jankovic DeviceClass *dc = DEVICE_CLASS(klass); 160*edd3a59dSStrahinja Jankovic ResettableClass *rc = RESETTABLE_CLASS(klass); 161*edd3a59dSStrahinja Jankovic 162*edd3a59dSStrahinja Jankovic rc->phases.enter = allwinner_a10_dramc_reset_enter; 163*edd3a59dSStrahinja Jankovic dc->vmsd = &allwinner_a10_dramc_vmstate; 164*edd3a59dSStrahinja Jankovic } 165*edd3a59dSStrahinja Jankovic 166*edd3a59dSStrahinja Jankovic static const TypeInfo allwinner_a10_dramc_info = { 167*edd3a59dSStrahinja Jankovic .name = TYPE_AW_A10_DRAMC, 168*edd3a59dSStrahinja Jankovic .parent = TYPE_SYS_BUS_DEVICE, 169*edd3a59dSStrahinja Jankovic .instance_init = allwinner_a10_dramc_init, 170*edd3a59dSStrahinja Jankovic .instance_size = sizeof(AwA10DramControllerState), 171*edd3a59dSStrahinja Jankovic .class_init = allwinner_a10_dramc_class_init, 172*edd3a59dSStrahinja Jankovic }; 173*edd3a59dSStrahinja Jankovic 174*edd3a59dSStrahinja Jankovic static void allwinner_a10_dramc_register(void) 175*edd3a59dSStrahinja Jankovic { 176*edd3a59dSStrahinja Jankovic type_register_static(&allwinner_a10_dramc_info); 177*edd3a59dSStrahinja Jankovic } 178*edd3a59dSStrahinja Jankovic 179*edd3a59dSStrahinja Jankovic type_init(allwinner_a10_dramc_register) 180