2 * Allwinner A10 timer device emulation
4 * Copyright (C) 2013 Li Guang
5 * Written by Li Guang <lig.fnst@cn.fujitsu.com>
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 #include "hw/sysbus.h"
19 #include "sysemu/sysemu.h"
20 #include "hw/timer/allwinner-a10-pit.h"
22 static uint64_t a10_pit_read(void *opaque
, hwaddr offset
, unsigned size
)
24 AwA10PITState
*s
= AW_A10_PIT(opaque
);
28 case AW_A10_PIT_TIMER_IRQ_EN
:
30 case AW_A10_PIT_TIMER_IRQ_ST
:
32 case AW_A10_PIT_TIMER_BASE
... AW_A10_PIT_TIMER_BASE_END
:
33 index
= offset
& 0xf0;
36 switch (offset
& 0x0f) {
37 case AW_A10_PIT_TIMER_CONTROL
:
38 return s
->control
[index
];
39 case AW_A10_PIT_TIMER_INTERVAL
:
40 return s
->interval
[index
];
41 case AW_A10_PIT_TIMER_COUNT
:
42 s
->count
[index
] = ptimer_get_count(s
->timer
[index
]);
43 return s
->count
[index
];
45 qemu_log_mask(LOG_GUEST_ERROR
,
46 "%s: Bad offset 0x%x\n", __func__
, (int)offset
);
49 case AW_A10_PIT_WDOG_CONTROL
:
51 case AW_A10_PIT_WDOG_MODE
:
53 case AW_A10_PIT_COUNT_LO
:
55 case AW_A10_PIT_COUNT_HI
:
57 case AW_A10_PIT_COUNT_CTL
:
60 qemu_log_mask(LOG_GUEST_ERROR
,
61 "%s: Bad offset 0x%x\n", __func__
, (int)offset
);
68 static void a10_pit_write(void *opaque
, hwaddr offset
, uint64_t value
,
71 AwA10PITState
*s
= AW_A10_PIT(opaque
);
75 case AW_A10_PIT_TIMER_IRQ_EN
:
76 s
->irq_enable
= value
;
78 case AW_A10_PIT_TIMER_IRQ_ST
:
79 s
->irq_status
&= ~value
;
81 case AW_A10_PIT_TIMER_BASE
... AW_A10_PIT_TIMER_BASE_END
:
82 index
= offset
& 0xf0;
85 switch (offset
& 0x0f) {
86 case AW_A10_PIT_TIMER_CONTROL
:
87 s
->control
[index
] = value
;
88 if (s
->control
[index
] & AW_A10_PIT_TIMER_RELOAD
) {
89 ptimer_set_count(s
->timer
[index
], s
->interval
[index
]);
91 if (s
->control
[index
] & AW_A10_PIT_TIMER_EN
) {
93 if (s
->control
[index
] & AW_A10_PIT_TIMER_MODE
) {
96 ptimer_run(s
->timer
[index
], oneshot
);
98 ptimer_stop(s
->timer
[index
]);
101 case AW_A10_PIT_TIMER_INTERVAL
:
102 s
->interval
[index
] = value
;
103 ptimer_set_limit(s
->timer
[index
], s
->interval
[index
], 1);
105 case AW_A10_PIT_TIMER_COUNT
:
106 s
->count
[index
] = value
;
109 qemu_log_mask(LOG_GUEST_ERROR
,
110 "%s: Bad offset 0x%x\n", __func__
, (int)offset
);
113 case AW_A10_PIT_WDOG_CONTROL
:
114 s
->watch_dog_control
= value
;
116 case AW_A10_PIT_WDOG_MODE
:
117 s
->watch_dog_mode
= value
;
119 case AW_A10_PIT_COUNT_LO
:
122 case AW_A10_PIT_COUNT_HI
:
125 case AW_A10_PIT_COUNT_CTL
:
126 s
->count_ctl
= value
;
127 if (s
->count_ctl
& AW_A10_PIT_COUNT_RL_EN
) {
128 uint64_t tmp_count
= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
);
130 s
->count_lo
= tmp_count
;
131 s
->count_hi
= tmp_count
>> 32;
132 s
->count_ctl
&= ~AW_A10_PIT_COUNT_RL_EN
;
134 if (s
->count_ctl
& AW_A10_PIT_COUNT_CLR_EN
) {
137 s
->count_ctl
&= ~AW_A10_PIT_COUNT_CLR_EN
;
141 qemu_log_mask(LOG_GUEST_ERROR
,
142 "%s: Bad offset 0x%x\n", __func__
, (int)offset
);
147 static const MemoryRegionOps a10_pit_ops
= {
148 .read
= a10_pit_read
,
149 .write
= a10_pit_write
,
150 .endianness
= DEVICE_NATIVE_ENDIAN
,
153 static const VMStateDescription vmstate_a10_pit
= {
156 .minimum_version_id
= 1,
157 .minimum_version_id_old
= 1,
158 .fields
= (VMStateField
[]) {
159 VMSTATE_UINT32(irq_enable
, AwA10PITState
),
160 VMSTATE_UINT32(irq_status
, AwA10PITState
),
161 VMSTATE_UINT32_ARRAY(control
, AwA10PITState
, AW_A10_PIT_TIMER_NR
),
162 VMSTATE_UINT32_ARRAY(interval
, AwA10PITState
, AW_A10_PIT_TIMER_NR
),
163 VMSTATE_UINT32_ARRAY(count
, AwA10PITState
, AW_A10_PIT_TIMER_NR
),
164 VMSTATE_UINT32(watch_dog_mode
, AwA10PITState
),
165 VMSTATE_UINT32(watch_dog_control
, AwA10PITState
),
166 VMSTATE_UINT32(count_lo
, AwA10PITState
),
167 VMSTATE_UINT32(count_hi
, AwA10PITState
),
168 VMSTATE_UINT32(count_ctl
, AwA10PITState
),
169 VMSTATE_PTIMER_ARRAY(timer
, AwA10PITState
, AW_A10_PIT_TIMER_NR
),
170 VMSTATE_END_OF_LIST()
174 static void a10_pit_reset(DeviceState
*dev
)
176 AwA10PITState
*s
= AW_A10_PIT(dev
);
181 for (i
= 0; i
< 6; i
++) {
182 s
->control
[i
] = AW_A10_PIT_DEFAULT_CLOCK
;
185 ptimer_stop(s
->timer
[i
]);
187 s
->watch_dog_mode
= 0;
188 s
->watch_dog_control
= 0;
194 static void a10_pit_timer_cb(void *opaque
)
196 AwA10PITState
*s
= AW_A10_PIT(opaque
);
199 for (i
= 0; i
< AW_A10_PIT_TIMER_NR
; i
++) {
200 if (s
->control
[i
] & AW_A10_PIT_TIMER_EN
) {
201 s
->irq_status
|= 1 << i
;
202 if (s
->control
[i
] & AW_A10_PIT_TIMER_MODE
) {
203 ptimer_stop(s
->timer
[i
]);
204 s
->control
[i
] &= ~AW_A10_PIT_TIMER_EN
;
206 qemu_irq_pulse(s
->irq
[i
]);
211 static void a10_pit_init(Object
*obj
)
213 AwA10PITState
*s
= AW_A10_PIT(obj
);
214 SysBusDevice
*sbd
= SYS_BUS_DEVICE(obj
);
215 QEMUBH
* bh
[AW_A10_PIT_TIMER_NR
];
218 for (i
= 0; i
< AW_A10_PIT_TIMER_NR
; i
++) {
219 sysbus_init_irq(sbd
, &s
->irq
[i
]);
221 memory_region_init_io(&s
->iomem
, OBJECT(s
), &a10_pit_ops
, s
,
222 TYPE_AW_A10_PIT
, 0x400);
223 sysbus_init_mmio(sbd
, &s
->iomem
);
225 for (i
= 0; i
< AW_A10_PIT_TIMER_NR
; i
++) {
226 bh
[i
] = qemu_bh_new(a10_pit_timer_cb
, s
);
227 s
->timer
[i
] = ptimer_init(bh
[i
]);
228 ptimer_set_freq(s
->timer
[i
], 240000);
232 static void a10_pit_class_init(ObjectClass
*klass
, void *data
)
234 DeviceClass
*dc
= DEVICE_CLASS(klass
);
236 dc
->reset
= a10_pit_reset
;
237 dc
->desc
= "allwinner a10 timer";
238 dc
->vmsd
= &vmstate_a10_pit
;
241 static const TypeInfo a10_pit_info
= {
242 .name
= TYPE_AW_A10_PIT
,
243 .parent
= TYPE_SYS_BUS_DEVICE
,
244 .instance_size
= sizeof(AwA10PITState
),
245 .instance_init
= a10_pit_init
,
246 .class_init
= a10_pit_class_init
,
249 static void a10_register_types(void)
251 type_register_static(&a10_pit_info
);
254 type_init(a10_register_types
);