2 * Allwinner CPU Configuration Module emulation
4 * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com>
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.
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.
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/>.
20 #include "qemu/osdep.h"
21 #include "qemu/units.h"
22 #include "hw/sysbus.h"
23 #include "migration/vmstate.h"
25 #include "qemu/module.h"
26 #include "qemu/error-report.h"
27 #include "qemu/timer.h"
28 #include "hw/core/cpu.h"
29 #include "target/arm/arm-powerctl.h"
30 #include "target/arm/cpu.h"
31 #include "hw/misc/allwinner-cpucfg.h"
34 /* CPUCFG register offsets */
36 REG_CPUS_RST_CTRL
= 0x0000, /* CPUs Reset Control */
37 REG_CPU0_RST_CTRL
= 0x0040, /* CPU#0 Reset Control */
38 REG_CPU0_CTRL
= 0x0044, /* CPU#0 Control */
39 REG_CPU0_STATUS
= 0x0048, /* CPU#0 Status */
40 REG_CPU1_RST_CTRL
= 0x0080, /* CPU#1 Reset Control */
41 REG_CPU1_CTRL
= 0x0084, /* CPU#1 Control */
42 REG_CPU1_STATUS
= 0x0088, /* CPU#1 Status */
43 REG_CPU2_RST_CTRL
= 0x00C0, /* CPU#2 Reset Control */
44 REG_CPU2_CTRL
= 0x00C4, /* CPU#2 Control */
45 REG_CPU2_STATUS
= 0x00C8, /* CPU#2 Status */
46 REG_CPU3_RST_CTRL
= 0x0100, /* CPU#3 Reset Control */
47 REG_CPU3_CTRL
= 0x0104, /* CPU#3 Control */
48 REG_CPU3_STATUS
= 0x0108, /* CPU#3 Status */
49 REG_CPU_SYS_RST
= 0x0140, /* CPU System Reset */
50 REG_CLK_GATING
= 0x0144, /* CPU Clock Gating */
51 REG_GEN_CTRL
= 0x0184, /* General Control */
52 REG_SUPER_STANDBY
= 0x01A0, /* Super Standby Flag */
53 REG_ENTRY_ADDR
= 0x01A4, /* Reset Entry Address */
54 REG_DBG_EXTERN
= 0x01E4, /* Debug External */
55 REG_CNT64_CTRL
= 0x0280, /* 64-bit Counter Control */
56 REG_CNT64_LOW
= 0x0284, /* 64-bit Counter Low */
57 REG_CNT64_HIGH
= 0x0288, /* 64-bit Counter High */
60 /* CPUCFG register flags */
62 CPUX_RESET_RELEASED
= ((1 << 1) | (1 << 0)),
63 CPUX_STATUS_SMP
= (1 << 0),
64 CPU_SYS_RESET_RELEASED
= (1 << 0),
65 CLK_GATING_ENABLE
= ((1 << 8) | 0xF),
68 /* CPUCFG register reset values */
70 REG_CLK_GATING_RST
= 0x0000010F,
71 REG_GEN_CTRL_RST
= 0x00000020,
72 REG_SUPER_STANDBY_RST
= 0x0,
73 REG_CNT64_CTRL_RST
= 0x0,
76 /* CPUCFG constants */
78 CPU_EXCEPTION_LEVEL_ON_RESET
= 3, /* EL3 */
81 static void allwinner_cpucfg_cpu_reset(AwCpuCfgState
*s
, uint8_t cpu_id
)
85 trace_allwinner_cpucfg_cpu_reset(cpu_id
, s
->entry_addr
);
87 ARMCPU
*target_cpu
= ARM_CPU(arm_get_cpu_by_id(cpu_id
));
90 * Called with a bogus value for cpu_id. Guest error will
91 * already have been logged, we can simply return here.
95 bool target_aa64
= arm_feature(&target_cpu
->env
, ARM_FEATURE_AARCH64
);
97 ret
= arm_set_cpu_on(cpu_id
, s
->entry_addr
, 0,
98 CPU_EXCEPTION_LEVEL_ON_RESET
, target_aa64
);
99 if (ret
!= QEMU_ARM_POWERCTL_RET_SUCCESS
) {
100 error_report("%s: failed to bring up CPU %d: err %d",
101 __func__
, cpu_id
, ret
);
106 static uint64_t allwinner_cpucfg_read(void *opaque
, hwaddr offset
,
109 const AwCpuCfgState
*s
= AW_CPUCFG(opaque
);
113 case REG_CPUS_RST_CTRL
: /* CPUs Reset Control */
114 case REG_CPU_SYS_RST
: /* CPU System Reset */
115 val
= CPU_SYS_RESET_RELEASED
;
117 case REG_CPU0_RST_CTRL
: /* CPU#0 Reset Control */
118 case REG_CPU1_RST_CTRL
: /* CPU#1 Reset Control */
119 case REG_CPU2_RST_CTRL
: /* CPU#2 Reset Control */
120 case REG_CPU3_RST_CTRL
: /* CPU#3 Reset Control */
121 val
= CPUX_RESET_RELEASED
;
123 case REG_CPU0_CTRL
: /* CPU#0 Control */
124 case REG_CPU1_CTRL
: /* CPU#1 Control */
125 case REG_CPU2_CTRL
: /* CPU#2 Control */
126 case REG_CPU3_CTRL
: /* CPU#3 Control */
129 case REG_CPU0_STATUS
: /* CPU#0 Status */
130 case REG_CPU1_STATUS
: /* CPU#1 Status */
131 case REG_CPU2_STATUS
: /* CPU#2 Status */
132 case REG_CPU3_STATUS
: /* CPU#3 Status */
133 val
= CPUX_STATUS_SMP
;
135 case REG_CLK_GATING
: /* CPU Clock Gating */
136 val
= CLK_GATING_ENABLE
;
138 case REG_GEN_CTRL
: /* General Control */
141 case REG_SUPER_STANDBY
: /* Super Standby Flag */
142 val
= s
->super_standby
;
144 case REG_ENTRY_ADDR
: /* Reset Entry Address */
147 case REG_DBG_EXTERN
: /* Debug External */
148 case REG_CNT64_CTRL
: /* 64-bit Counter Control */
149 case REG_CNT64_LOW
: /* 64-bit Counter Low */
150 case REG_CNT64_HIGH
: /* 64-bit Counter High */
151 qemu_log_mask(LOG_UNIMP
, "%s: unimplemented register at 0x%04x\n",
152 __func__
, (uint32_t)offset
);
155 qemu_log_mask(LOG_GUEST_ERROR
, "%s: out-of-bounds offset 0x%04x\n",
156 __func__
, (uint32_t)offset
);
160 trace_allwinner_cpucfg_read(offset
, val
, size
);
165 static void allwinner_cpucfg_write(void *opaque
, hwaddr offset
,
166 uint64_t val
, unsigned size
)
168 AwCpuCfgState
*s
= AW_CPUCFG(opaque
);
170 trace_allwinner_cpucfg_write(offset
, val
, size
);
173 case REG_CPUS_RST_CTRL
: /* CPUs Reset Control */
174 case REG_CPU_SYS_RST
: /* CPU System Reset */
176 case REG_CPU0_RST_CTRL
: /* CPU#0 Reset Control */
177 case REG_CPU1_RST_CTRL
: /* CPU#1 Reset Control */
178 case REG_CPU2_RST_CTRL
: /* CPU#2 Reset Control */
179 case REG_CPU3_RST_CTRL
: /* CPU#3 Reset Control */
181 allwinner_cpucfg_cpu_reset(s
, (offset
- REG_CPU0_RST_CTRL
) >> 6);
184 case REG_CPU0_CTRL
: /* CPU#0 Control */
185 case REG_CPU1_CTRL
: /* CPU#1 Control */
186 case REG_CPU2_CTRL
: /* CPU#2 Control */
187 case REG_CPU3_CTRL
: /* CPU#3 Control */
188 case REG_CPU0_STATUS
: /* CPU#0 Status */
189 case REG_CPU1_STATUS
: /* CPU#1 Status */
190 case REG_CPU2_STATUS
: /* CPU#2 Status */
191 case REG_CPU3_STATUS
: /* CPU#3 Status */
192 case REG_CLK_GATING
: /* CPU Clock Gating */
194 case REG_GEN_CTRL
: /* General Control */
197 case REG_SUPER_STANDBY
: /* Super Standby Flag */
198 s
->super_standby
= val
;
200 case REG_ENTRY_ADDR
: /* Reset Entry Address */
203 case REG_DBG_EXTERN
: /* Debug External */
204 case REG_CNT64_CTRL
: /* 64-bit Counter Control */
205 case REG_CNT64_LOW
: /* 64-bit Counter Low */
206 case REG_CNT64_HIGH
: /* 64-bit Counter High */
207 qemu_log_mask(LOG_UNIMP
, "%s: unimplemented register at 0x%04x\n",
208 __func__
, (uint32_t)offset
);
211 qemu_log_mask(LOG_GUEST_ERROR
, "%s: out-of-bounds offset 0x%04x\n",
212 __func__
, (uint32_t)offset
);
217 static const MemoryRegionOps allwinner_cpucfg_ops
= {
218 .read
= allwinner_cpucfg_read
,
219 .write
= allwinner_cpucfg_write
,
220 .endianness
= DEVICE_NATIVE_ENDIAN
,
222 .min_access_size
= 4,
223 .max_access_size
= 4,
225 .impl
.min_access_size
= 4,
228 static void allwinner_cpucfg_reset(DeviceState
*dev
)
230 AwCpuCfgState
*s
= AW_CPUCFG(dev
);
232 /* Set default values for registers */
233 s
->gen_ctrl
= REG_GEN_CTRL_RST
;
234 s
->super_standby
= REG_SUPER_STANDBY_RST
;
238 static void allwinner_cpucfg_init(Object
*obj
)
240 SysBusDevice
*sbd
= SYS_BUS_DEVICE(obj
);
241 AwCpuCfgState
*s
= AW_CPUCFG(obj
);
244 memory_region_init_io(&s
->iomem
, OBJECT(s
), &allwinner_cpucfg_ops
, s
,
245 TYPE_AW_CPUCFG
, 1 * KiB
);
246 sysbus_init_mmio(sbd
, &s
->iomem
);
249 static const VMStateDescription allwinner_cpucfg_vmstate
= {
250 .name
= "allwinner-cpucfg",
252 .minimum_version_id
= 1,
253 .fields
= (VMStateField
[]) {
254 VMSTATE_UINT32(gen_ctrl
, AwCpuCfgState
),
255 VMSTATE_UINT32(super_standby
, AwCpuCfgState
),
256 VMSTATE_UINT32(entry_addr
, AwCpuCfgState
),
257 VMSTATE_END_OF_LIST()
261 static void allwinner_cpucfg_class_init(ObjectClass
*klass
, void *data
)
263 DeviceClass
*dc
= DEVICE_CLASS(klass
);
265 dc
->reset
= allwinner_cpucfg_reset
;
266 dc
->vmsd
= &allwinner_cpucfg_vmstate
;
269 static const TypeInfo allwinner_cpucfg_info
= {
270 .name
= TYPE_AW_CPUCFG
,
271 .parent
= TYPE_SYS_BUS_DEVICE
,
272 .instance_init
= allwinner_cpucfg_init
,
273 .instance_size
= sizeof(AwCpuCfgState
),
274 .class_init
= allwinner_cpucfg_class_init
,
277 static void allwinner_cpucfg_register(void)
279 type_register_static(&allwinner_cpucfg_info
);
282 type_init(allwinner_cpucfg_register
)