xref: /qemu/hw/misc/allwinner-a10-dramc.c (revision edd3a59d5b98964ed72265346cb4dc7e9ffccd27)
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