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