1 /*
2 * Allwinner R40 SRAM controller emulation
3 *
4 * Copyright (C) 2023 qianfan Zhao <qianfanguijin@163.com>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "qemu/osdep.h"
21 #include "qemu/units.h"
22 #include "hw/sysbus.h"
23 #include "migration/vmstate.h"
24 #include "qemu/log.h"
25 #include "qemu/module.h"
26 #include "qapi/error.h"
27 #include "hw/qdev-properties.h"
28 #include "hw/qdev-properties-system.h"
29 #include "hw/misc/allwinner-sramc.h"
30 #include "trace.h"
31
32 /*
33 * register offsets
34 * https://linux-sunxi.org/SRAM_Controller_Register_Guide
35 */
36 enum {
37 REG_SRAM_CTL1_CFG = 0x04, /* SRAM Control register 1 */
38 REG_SRAM_VER = 0x24, /* SRAM Version register */
39 REG_SRAM_R40_SOFT_ENTRY_REG0 = 0xbc,
40 };
41
42 /* REG_SRAMC_VERSION bit defines */
43 #define SRAM_VER_READ_ENABLE (1 << 15)
44 #define SRAM_VER_VERSION_SHIFT 16
45 #define SRAM_VERSION_SUN8I_R40 0x1701
46
allwinner_sramc_read(void * opaque,hwaddr offset,unsigned size)47 static uint64_t allwinner_sramc_read(void *opaque, hwaddr offset,
48 unsigned size)
49 {
50 AwSRAMCState *s = AW_SRAMC(opaque);
51 AwSRAMCClass *sc = AW_SRAMC_GET_CLASS(s);
52 uint64_t val = 0;
53
54 switch (offset) {
55 case REG_SRAM_CTL1_CFG:
56 val = s->sram_ctl1;
57 break;
58 case REG_SRAM_VER:
59 /* bit15: lock bit, set this bit before reading this register */
60 if (s->sram_ver & SRAM_VER_READ_ENABLE) {
61 val = SRAM_VER_READ_ENABLE |
62 (sc->sram_version_code << SRAM_VER_VERSION_SHIFT);
63 }
64 break;
65 case REG_SRAM_R40_SOFT_ENTRY_REG0:
66 val = s->sram_soft_entry_reg0;
67 break;
68 default:
69 qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
70 __func__, (uint32_t)offset);
71 return 0;
72 }
73
74 trace_allwinner_sramc_read(offset, val);
75
76 return val;
77 }
78
allwinner_sramc_write(void * opaque,hwaddr offset,uint64_t val,unsigned size)79 static void allwinner_sramc_write(void *opaque, hwaddr offset,
80 uint64_t val, unsigned size)
81 {
82 AwSRAMCState *s = AW_SRAMC(opaque);
83
84 trace_allwinner_sramc_write(offset, val);
85
86 switch (offset) {
87 case REG_SRAM_CTL1_CFG:
88 s->sram_ctl1 = val;
89 break;
90 case REG_SRAM_VER:
91 /* Only the READ_ENABLE bit is writeable */
92 s->sram_ver = val & SRAM_VER_READ_ENABLE;
93 break;
94 case REG_SRAM_R40_SOFT_ENTRY_REG0:
95 s->sram_soft_entry_reg0 = val;
96 break;
97 default:
98 qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n",
99 __func__, (uint32_t)offset);
100 break;
101 }
102 }
103
104 static const MemoryRegionOps allwinner_sramc_ops = {
105 .read = allwinner_sramc_read,
106 .write = allwinner_sramc_write,
107 .endianness = DEVICE_NATIVE_ENDIAN,
108 .valid = {
109 .min_access_size = 4,
110 .max_access_size = 4,
111 },
112 .impl.min_access_size = 4,
113 };
114
115 static const VMStateDescription allwinner_sramc_vmstate = {
116 .name = "allwinner-sramc",
117 .version_id = 1,
118 .minimum_version_id = 1,
119 .fields = (const VMStateField[]) {
120 VMSTATE_UINT32(sram_ver, AwSRAMCState),
121 VMSTATE_UINT32(sram_soft_entry_reg0, AwSRAMCState),
122 VMSTATE_END_OF_LIST()
123 }
124 };
125
allwinner_sramc_reset(DeviceState * dev)126 static void allwinner_sramc_reset(DeviceState *dev)
127 {
128 AwSRAMCState *s = AW_SRAMC(dev);
129 AwSRAMCClass *sc = AW_SRAMC_GET_CLASS(s);
130
131 switch (sc->sram_version_code) {
132 case SRAM_VERSION_SUN8I_R40:
133 s->sram_ctl1 = 0x1300;
134 break;
135 }
136 }
137
allwinner_sramc_class_init(ObjectClass * klass,void * data)138 static void allwinner_sramc_class_init(ObjectClass *klass, void *data)
139 {
140 DeviceClass *dc = DEVICE_CLASS(klass);
141
142 dc->reset = allwinner_sramc_reset;
143 dc->vmsd = &allwinner_sramc_vmstate;
144 }
145
allwinner_sramc_init(Object * obj)146 static void allwinner_sramc_init(Object *obj)
147 {
148 SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
149 AwSRAMCState *s = AW_SRAMC(obj);
150
151 /* Memory mapping */
152 memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_sramc_ops, s,
153 TYPE_AW_SRAMC, 1 * KiB);
154 sysbus_init_mmio(sbd, &s->iomem);
155 }
156
157 static const TypeInfo allwinner_sramc_info = {
158 .name = TYPE_AW_SRAMC,
159 .parent = TYPE_SYS_BUS_DEVICE,
160 .instance_init = allwinner_sramc_init,
161 .instance_size = sizeof(AwSRAMCState),
162 .class_size = sizeof(AwSRAMCClass),
163 .class_init = allwinner_sramc_class_init,
164 };
165
allwinner_r40_sramc_class_init(ObjectClass * klass,void * data)166 static void allwinner_r40_sramc_class_init(ObjectClass *klass, void *data)
167 {
168 AwSRAMCClass *sc = AW_SRAMC_CLASS(klass);
169
170 sc->sram_version_code = SRAM_VERSION_SUN8I_R40;
171 }
172
173 static const TypeInfo allwinner_r40_sramc_info = {
174 .name = TYPE_AW_SRAMC_SUN8I_R40,
175 .parent = TYPE_AW_SRAMC,
176 .class_init = allwinner_r40_sramc_class_init,
177 };
178
allwinner_sramc_register(void)179 static void allwinner_sramc_register(void)
180 {
181 type_register_static(&allwinner_sramc_info);
182 type_register_static(&allwinner_r40_sramc_info);
183 }
184
185 type_init(allwinner_sramc_register)
186