2 * Allwinner A10 DRAM Controller emulation
4 * Copyright (C) 2022 Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
6 * This file is derived from Allwinner H3 DRAMC,
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.
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.
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/>.
23 #include "qemu/osdep.h"
24 #include "qemu/units.h"
25 #include "hw/sysbus.h"
26 #include "migration/vmstate.h"
28 #include "qemu/module.h"
29 #include "hw/misc/allwinner-a10-dramc.h"
31 /* DRAMC register offsets */
34 REG_SDR_ZQCR0
= 0x00a8,
38 #define REG_INDEX(offset) (offset / sizeof(uint32_t))
40 /* DRAMC register flags */
42 REG_SDR_CCR_DATA_TRAINING
= (1 << 30),
43 REG_SDR_CCR_DRAM_INIT
= (1 << 31),
46 REG_SDR_ZQSR_ZCAL
= (1 << 31),
49 /* DRAMC register reset values */
51 REG_SDR_CCR_RESET
= 0x80020000,
52 REG_SDR_ZQCR0_RESET
= 0x07b00000,
53 REG_SDR_ZQSR_RESET
= 0x80000000
56 static uint64_t allwinner_a10_dramc_read(void *opaque
, hwaddr offset
,
59 const AwA10DramControllerState
*s
= AW_A10_DRAMC(opaque
);
60 const uint32_t idx
= REG_INDEX(offset
);
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
);
72 qemu_log_mask(LOG_UNIMP
, "%s: unimplemented read offset 0x%04x\n",
73 __func__
, (uint32_t)offset
);
80 static void allwinner_a10_dramc_write(void *opaque
, hwaddr offset
,
81 uint64_t val
, unsigned size
)
83 AwA10DramControllerState
*s
= AW_A10_DRAMC(opaque
);
84 const uint32_t idx
= REG_INDEX(offset
);
88 if (val
& REG_SDR_CCR_DRAM_INIT
) {
89 /* Clear DRAM_INIT to indicate process is done. */
90 val
&= ~REG_SDR_CCR_DRAM_INIT
;
92 if (val
& REG_SDR_CCR_DATA_TRAINING
) {
93 /* Clear DATA_TRAINING to indicate process is done. */
94 val
&= ~REG_SDR_CCR_DATA_TRAINING
;
98 /* Set ZCAL in ZQSR to indicate calibration is done. */
99 s
->regs
[REG_INDEX(REG_SDR_ZQSR
)] |= REG_SDR_ZQSR_ZCAL
;
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
);
106 qemu_log_mask(LOG_UNIMP
, "%s: unimplemented write offset 0x%04x\n",
107 __func__
, (uint32_t)offset
);
111 s
->regs
[idx
] = (uint32_t) val
;
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
,
119 .min_access_size
= 4,
120 .max_access_size
= 4,
122 .impl
.min_access_size
= 4,
125 static void allwinner_a10_dramc_reset_enter(Object
*obj
, ResetType type
)
127 AwA10DramControllerState
*s
= AW_A10_DRAMC(obj
);
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
;
135 static void allwinner_a10_dramc_init(Object
*obj
)
137 SysBusDevice
*sbd
= SYS_BUS_DEVICE(obj
);
138 AwA10DramControllerState
*s
= AW_A10_DRAMC(obj
);
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
);
146 static const VMStateDescription allwinner_a10_dramc_vmstate
= {
147 .name
= "allwinner-a10-dramc",
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()
157 static void allwinner_a10_dramc_class_init(ObjectClass
*klass
, void *data
)
159 DeviceClass
*dc
= DEVICE_CLASS(klass
);
160 ResettableClass
*rc
= RESETTABLE_CLASS(klass
);
162 rc
->phases
.enter
= allwinner_a10_dramc_reset_enter
;
163 dc
->vmsd
= &allwinner_a10_dramc_vmstate
;
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
,
174 static void allwinner_a10_dramc_register(void)
176 type_register_static(&allwinner_a10_dramc_info
);
179 type_init(allwinner_a10_dramc_register
)