2 * Allwinner Real Time Clock 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-common.h"
27 #include "hw/qdev-properties.h"
28 #include "hw/rtc/allwinner-rtc.h"
33 REG_LOSC
= 1, /* Low Oscillator Control */
34 REG_YYMMDD
, /* RTC Year-Month-Day */
35 REG_HHMMSS
, /* RTC Hour-Minute-Second */
36 REG_ALARM1_WKHHMMSS
, /* Alarm1 Week Hour-Minute-Second */
37 REG_ALARM1_EN
, /* Alarm1 Enable */
38 REG_ALARM1_IRQ_EN
, /* Alarm1 IRQ Enable */
39 REG_ALARM1_IRQ_STA
, /* Alarm1 IRQ Status */
40 REG_GP0
, /* General Purpose Register 0 */
41 REG_GP1
, /* General Purpose Register 1 */
42 REG_GP2
, /* General Purpose Register 2 */
43 REG_GP3
, /* General Purpose Register 3 */
46 REG_ALARM1_DDHHMMSS
, /* Alarm1 Day Hour-Minute-Second */
47 REG_CPUCFG
, /* CPU Configuration Register */
50 REG_LOSC_AUTOSTA
, /* LOSC Auto Switch Status */
51 REG_INT_OSC_PRE
, /* Internal OSC Clock Prescaler */
52 REG_ALARM0_COUNTER
, /* Alarm0 Counter */
53 REG_ALARM0_CUR_VLU
, /* Alarm0 Counter Current Value */
54 REG_ALARM0_ENABLE
, /* Alarm0 Enable */
55 REG_ALARM0_IRQ_EN
, /* Alarm0 IRQ Enable */
56 REG_ALARM0_IRQ_STA
, /* Alarm0 IRQ Status */
57 REG_ALARM_CONFIG
, /* Alarm Config */
58 REG_LOSC_OUT_GATING
, /* LOSC Output Gating Register */
59 REG_GP4
, /* General Purpose Register 4 */
60 REG_GP5
, /* General Purpose Register 5 */
61 REG_GP6
, /* General Purpose Register 6 */
62 REG_GP7
, /* General Purpose Register 7 */
63 REG_RTC_DBG
, /* RTC Debug Register */
64 REG_GPL_HOLD_OUT
, /* GPL Hold Output Register */
65 REG_VDD_RTC
, /* VDD RTC Regulate Register */
66 REG_IC_CHARA
, /* IC Characteristics Register */
69 /* RTC register flags */
71 REG_LOSC_YMD
= (1 << 7),
72 REG_LOSC_HMS
= (1 << 8),
75 /* RTC sun4i register map (offset to name) */
76 const uint8_t allwinner_rtc_sun4i_regmap
[] = {
78 [0x0004] = REG_YYMMDD
,
79 [0x0008] = REG_HHMMSS
,
80 [0x000C] = REG_ALARM1_DDHHMMSS
,
81 [0x0010] = REG_ALARM1_WKHHMMSS
,
82 [0x0014] = REG_ALARM1_EN
,
83 [0x0018] = REG_ALARM1_IRQ_EN
,
84 [0x001C] = REG_ALARM1_IRQ_STA
,
89 [0x003C] = REG_CPUCFG
,
92 /* RTC sun6i register map (offset to name) */
93 const uint8_t allwinner_rtc_sun6i_regmap
[] = {
95 [0x0004] = REG_LOSC_AUTOSTA
,
96 [0x0008] = REG_INT_OSC_PRE
,
97 [0x0010] = REG_YYMMDD
,
98 [0x0014] = REG_HHMMSS
,
99 [0x0020] = REG_ALARM0_COUNTER
,
100 [0x0024] = REG_ALARM0_CUR_VLU
,
101 [0x0028] = REG_ALARM0_ENABLE
,
102 [0x002C] = REG_ALARM0_IRQ_EN
,
103 [0x0030] = REG_ALARM0_IRQ_STA
,
104 [0x0040] = REG_ALARM1_WKHHMMSS
,
105 [0x0044] = REG_ALARM1_EN
,
106 [0x0048] = REG_ALARM1_IRQ_EN
,
107 [0x004C] = REG_ALARM1_IRQ_STA
,
108 [0x0050] = REG_ALARM_CONFIG
,
109 [0x0060] = REG_LOSC_OUT_GATING
,
118 [0x0170] = REG_RTC_DBG
,
119 [0x0180] = REG_GPL_HOLD_OUT
,
120 [0x0190] = REG_VDD_RTC
,
121 [0x01F0] = REG_IC_CHARA
,
124 static bool allwinner_rtc_sun4i_read(AwRtcState
*s
, uint32_t offset
)
126 /* no sun4i specific registers currently implemented */
130 static bool allwinner_rtc_sun4i_write(AwRtcState
*s
, uint32_t offset
,
133 /* no sun4i specific registers currently implemented */
137 static bool allwinner_rtc_sun6i_read(AwRtcState
*s
, uint32_t offset
)
139 const AwRtcClass
*c
= AW_RTC_GET_CLASS(s
);
141 switch (c
->regmap
[offset
]) {
142 case REG_GP4
: /* General Purpose Register 4 */
143 case REG_GP5
: /* General Purpose Register 5 */
144 case REG_GP6
: /* General Purpose Register 6 */
145 case REG_GP7
: /* General Purpose Register 7 */
153 static bool allwinner_rtc_sun6i_write(AwRtcState
*s
, uint32_t offset
,
156 const AwRtcClass
*c
= AW_RTC_GET_CLASS(s
);
158 switch (c
->regmap
[offset
]) {
159 case REG_GP4
: /* General Purpose Register 4 */
160 case REG_GP5
: /* General Purpose Register 5 */
161 case REG_GP6
: /* General Purpose Register 6 */
162 case REG_GP7
: /* General Purpose Register 7 */
170 static uint64_t allwinner_rtc_read(void *opaque
, hwaddr offset
,
173 AwRtcState
*s
= AW_RTC(opaque
);
174 const AwRtcClass
*c
= AW_RTC_GET_CLASS(s
);
177 if (offset
>= c
->regmap_size
) {
178 qemu_log_mask(LOG_GUEST_ERROR
, "%s: out-of-bounds offset 0x%04x\n",
179 __func__
, (uint32_t)offset
);
183 if (!c
->regmap
[offset
]) {
184 qemu_log_mask(LOG_GUEST_ERROR
, "%s: invalid register 0x%04x\n",
185 __func__
, (uint32_t)offset
);
189 switch (c
->regmap
[offset
]) {
190 case REG_LOSC
: /* Low Oscillator Control */
191 val
= s
->regs
[REG_LOSC
];
192 s
->regs
[REG_LOSC
] &= ~(REG_LOSC_YMD
| REG_LOSC_HMS
);
194 case REG_YYMMDD
: /* RTC Year-Month-Day */
195 case REG_HHMMSS
: /* RTC Hour-Minute-Second */
196 case REG_GP0
: /* General Purpose Register 0 */
197 case REG_GP1
: /* General Purpose Register 1 */
198 case REG_GP2
: /* General Purpose Register 2 */
199 case REG_GP3
: /* General Purpose Register 3 */
200 val
= s
->regs
[c
->regmap
[offset
]];
203 if (!c
->read(s
, offset
)) {
204 qemu_log_mask(LOG_UNIMP
, "%s: unimplemented register 0x%04x\n",
205 __func__
, (uint32_t)offset
);
207 val
= s
->regs
[c
->regmap
[offset
]];
211 trace_allwinner_rtc_read(offset
, val
);
215 static void allwinner_rtc_write(void *opaque
, hwaddr offset
,
216 uint64_t val
, unsigned size
)
218 AwRtcState
*s
= AW_RTC(opaque
);
219 const AwRtcClass
*c
= AW_RTC_GET_CLASS(s
);
221 if (offset
>= c
->regmap_size
) {
222 qemu_log_mask(LOG_GUEST_ERROR
, "%s: out-of-bounds offset 0x%04x\n",
223 __func__
, (uint32_t)offset
);
227 if (!c
->regmap
[offset
]) {
228 qemu_log_mask(LOG_GUEST_ERROR
, "%s: invalid register 0x%04x\n",
229 __func__
, (uint32_t)offset
);
233 trace_allwinner_rtc_write(offset
, val
);
235 switch (c
->regmap
[offset
]) {
236 case REG_YYMMDD
: /* RTC Year-Month-Day */
237 s
->regs
[REG_YYMMDD
] = val
;
238 s
->regs
[REG_LOSC
] |= REG_LOSC_YMD
;
240 case REG_HHMMSS
: /* RTC Hour-Minute-Second */
241 s
->regs
[REG_HHMMSS
] = val
;
242 s
->regs
[REG_LOSC
] |= REG_LOSC_HMS
;
244 case REG_GP0
: /* General Purpose Register 0 */
245 case REG_GP1
: /* General Purpose Register 1 */
246 case REG_GP2
: /* General Purpose Register 2 */
247 case REG_GP3
: /* General Purpose Register 3 */
248 s
->regs
[c
->regmap
[offset
]] = val
;
251 if (!c
->write(s
, offset
, val
)) {
252 qemu_log_mask(LOG_UNIMP
, "%s: unimplemented register 0x%04x\n",
253 __func__
, (uint32_t)offset
);
259 static const MemoryRegionOps allwinner_rtc_ops
= {
260 .read
= allwinner_rtc_read
,
261 .write
= allwinner_rtc_write
,
262 .endianness
= DEVICE_NATIVE_ENDIAN
,
264 .min_access_size
= 4,
265 .max_access_size
= 4,
267 .impl
.min_access_size
= 4,
270 static void allwinner_rtc_reset(DeviceState
*dev
)
272 AwRtcState
*s
= AW_RTC(dev
);
275 /* Clear registers */
276 memset(s
->regs
, 0, sizeof(s
->regs
));
278 /* Get current datetime */
279 qemu_get_timedate(&now
, 0);
281 /* Set RTC with current datetime */
282 if (s
->base_year
> 1900) {
283 s
->regs
[REG_YYMMDD
] = ((now
.tm_year
+ 1900 - s
->base_year
) << 16) |
284 ((now
.tm_mon
+ 1) << 8) |
286 s
->regs
[REG_HHMMSS
] = (((now
.tm_wday
+ 6) % 7) << 29) |
287 (now
.tm_hour
<< 16) |
293 static void allwinner_rtc_init(Object
*obj
)
295 SysBusDevice
*sbd
= SYS_BUS_DEVICE(obj
);
296 AwRtcState
*s
= AW_RTC(obj
);
299 memory_region_init_io(&s
->iomem
, OBJECT(s
), &allwinner_rtc_ops
, s
,
300 TYPE_AW_RTC
, 1 * KiB
);
301 sysbus_init_mmio(sbd
, &s
->iomem
);
304 static const VMStateDescription allwinner_rtc_vmstate
= {
305 .name
= "allwinner-rtc",
307 .minimum_version_id
= 1,
308 .fields
= (VMStateField
[]) {
309 VMSTATE_UINT32_ARRAY(regs
, AwRtcState
, AW_RTC_REGS_NUM
),
310 VMSTATE_END_OF_LIST()
314 static Property allwinner_rtc_properties
[] = {
315 DEFINE_PROP_INT32("base-year", AwRtcState
, base_year
, 0),
316 DEFINE_PROP_END_OF_LIST(),
319 static void allwinner_rtc_class_init(ObjectClass
*klass
, void *data
)
321 DeviceClass
*dc
= DEVICE_CLASS(klass
);
323 dc
->reset
= allwinner_rtc_reset
;
324 dc
->vmsd
= &allwinner_rtc_vmstate
;
325 device_class_set_props(dc
, allwinner_rtc_properties
);
328 static void allwinner_rtc_sun4i_init(Object
*obj
)
330 AwRtcState
*s
= AW_RTC(obj
);
334 static void allwinner_rtc_sun4i_class_init(ObjectClass
*klass
, void *data
)
336 AwRtcClass
*arc
= AW_RTC_CLASS(klass
);
338 arc
->regmap
= allwinner_rtc_sun4i_regmap
;
339 arc
->regmap_size
= sizeof(allwinner_rtc_sun4i_regmap
);
340 arc
->read
= allwinner_rtc_sun4i_read
;
341 arc
->write
= allwinner_rtc_sun4i_write
;
344 static void allwinner_rtc_sun6i_init(Object
*obj
)
346 AwRtcState
*s
= AW_RTC(obj
);
350 static void allwinner_rtc_sun6i_class_init(ObjectClass
*klass
, void *data
)
352 AwRtcClass
*arc
= AW_RTC_CLASS(klass
);
354 arc
->regmap
= allwinner_rtc_sun6i_regmap
;
355 arc
->regmap_size
= sizeof(allwinner_rtc_sun6i_regmap
);
356 arc
->read
= allwinner_rtc_sun6i_read
;
357 arc
->write
= allwinner_rtc_sun6i_write
;
360 static void allwinner_rtc_sun7i_init(Object
*obj
)
362 AwRtcState
*s
= AW_RTC(obj
);
366 static void allwinner_rtc_sun7i_class_init(ObjectClass
*klass
, void *data
)
368 AwRtcClass
*arc
= AW_RTC_CLASS(klass
);
369 allwinner_rtc_sun4i_class_init(klass
, arc
);
372 static const TypeInfo allwinner_rtc_info
= {
374 .parent
= TYPE_SYS_BUS_DEVICE
,
375 .instance_init
= allwinner_rtc_init
,
376 .instance_size
= sizeof(AwRtcState
),
377 .class_init
= allwinner_rtc_class_init
,
378 .class_size
= sizeof(AwRtcClass
),
382 static const TypeInfo allwinner_rtc_sun4i_info
= {
383 .name
= TYPE_AW_RTC_SUN4I
,
384 .parent
= TYPE_AW_RTC
,
385 .class_init
= allwinner_rtc_sun4i_class_init
,
386 .instance_init
= allwinner_rtc_sun4i_init
,
389 static const TypeInfo allwinner_rtc_sun6i_info
= {
390 .name
= TYPE_AW_RTC_SUN6I
,
391 .parent
= TYPE_AW_RTC
,
392 .class_init
= allwinner_rtc_sun6i_class_init
,
393 .instance_init
= allwinner_rtc_sun6i_init
,
396 static const TypeInfo allwinner_rtc_sun7i_info
= {
397 .name
= TYPE_AW_RTC_SUN7I
,
398 .parent
= TYPE_AW_RTC
,
399 .class_init
= allwinner_rtc_sun7i_class_init
,
400 .instance_init
= allwinner_rtc_sun7i_init
,
403 static void allwinner_rtc_register(void)
405 type_register_static(&allwinner_rtc_info
);
406 type_register_static(&allwinner_rtc_sun4i_info
);
407 type_register_static(&allwinner_rtc_sun6i_info
);
408 type_register_static(&allwinner_rtc_sun7i_info
);
411 type_init(allwinner_rtc_register
)