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