allwinner-a10-pit.c (9640B)
1/* 2 * Allwinner A10 timer device emulation 3 * 4 * Copyright (C) 2013 Li Guang 5 * Written by Li Guang <lig.fnst@cn.fujitsu.com> 6 * 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. 11 * 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 15 * for more details. 16 */ 17 18#include "qemu/osdep.h" 19#include "hw/irq.h" 20#include "hw/qdev-properties.h" 21#include "hw/sysbus.h" 22#include "hw/timer/allwinner-a10-pit.h" 23#include "migration/vmstate.h" 24#include "qemu/log.h" 25#include "qemu/module.h" 26 27static void a10_pit_update_irq(AwA10PITState *s) 28{ 29 int i; 30 31 for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) { 32 qemu_set_irq(s->irq[i], !!(s->irq_status & s->irq_enable & (1 << i))); 33 } 34} 35 36static uint64_t a10_pit_read(void *opaque, hwaddr offset, unsigned size) 37{ 38 AwA10PITState *s = AW_A10_PIT(opaque); 39 uint8_t index; 40 41 switch (offset) { 42 case AW_A10_PIT_TIMER_IRQ_EN: 43 return s->irq_enable; 44 case AW_A10_PIT_TIMER_IRQ_ST: 45 return s->irq_status; 46 case AW_A10_PIT_TIMER_BASE ... AW_A10_PIT_TIMER_BASE_END: 47 index = offset & 0xf0; 48 index >>= 4; 49 index -= 1; 50 switch (offset & 0x0f) { 51 case AW_A10_PIT_TIMER_CONTROL: 52 return s->control[index]; 53 case AW_A10_PIT_TIMER_INTERVAL: 54 return s->interval[index]; 55 case AW_A10_PIT_TIMER_COUNT: 56 s->count[index] = ptimer_get_count(s->timer[index]); 57 return s->count[index]; 58 default: 59 qemu_log_mask(LOG_GUEST_ERROR, 60 "%s: Bad offset 0x%x\n", __func__, (int)offset); 61 break; 62 } 63 case AW_A10_PIT_WDOG_CONTROL: 64 break; 65 case AW_A10_PIT_WDOG_MODE: 66 break; 67 case AW_A10_PIT_COUNT_LO: 68 return s->count_lo; 69 case AW_A10_PIT_COUNT_HI: 70 return s->count_hi; 71 case AW_A10_PIT_COUNT_CTL: 72 return s->count_ctl; 73 default: 74 qemu_log_mask(LOG_GUEST_ERROR, 75 "%s: Bad offset 0x%x\n", __func__, (int)offset); 76 break; 77 } 78 79 return 0; 80} 81 82/* Must be called inside a ptimer transaction block for s->timer[index] */ 83static void a10_pit_set_freq(AwA10PITState *s, int index) 84{ 85 uint32_t prescaler, source, source_freq; 86 87 prescaler = 1 << extract32(s->control[index], 4, 3); 88 source = extract32(s->control[index], 2, 2); 89 source_freq = s->clk_freq[source]; 90 91 if (source_freq) { 92 ptimer_set_freq(s->timer[index], source_freq / prescaler); 93 } else { 94 qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid clock source %u\n", 95 __func__, source); 96 } 97} 98 99static void a10_pit_write(void *opaque, hwaddr offset, uint64_t value, 100 unsigned size) 101{ 102 AwA10PITState *s = AW_A10_PIT(opaque); 103 uint8_t index; 104 105 switch (offset) { 106 case AW_A10_PIT_TIMER_IRQ_EN: 107 s->irq_enable = value; 108 a10_pit_update_irq(s); 109 break; 110 case AW_A10_PIT_TIMER_IRQ_ST: 111 s->irq_status &= ~value; 112 a10_pit_update_irq(s); 113 break; 114 case AW_A10_PIT_TIMER_BASE ... AW_A10_PIT_TIMER_BASE_END: 115 index = offset & 0xf0; 116 index >>= 4; 117 index -= 1; 118 switch (offset & 0x0f) { 119 case AW_A10_PIT_TIMER_CONTROL: 120 s->control[index] = value; 121 ptimer_transaction_begin(s->timer[index]); 122 a10_pit_set_freq(s, index); 123 if (s->control[index] & AW_A10_PIT_TIMER_RELOAD) { 124 ptimer_set_count(s->timer[index], s->interval[index]); 125 } 126 if (s->control[index] & AW_A10_PIT_TIMER_EN) { 127 int oneshot = 0; 128 if (s->control[index] & AW_A10_PIT_TIMER_MODE) { 129 oneshot = 1; 130 } 131 ptimer_run(s->timer[index], oneshot); 132 } else { 133 ptimer_stop(s->timer[index]); 134 } 135 ptimer_transaction_commit(s->timer[index]); 136 break; 137 case AW_A10_PIT_TIMER_INTERVAL: 138 s->interval[index] = value; 139 ptimer_transaction_begin(s->timer[index]); 140 ptimer_set_limit(s->timer[index], s->interval[index], 1); 141 ptimer_transaction_commit(s->timer[index]); 142 break; 143 case AW_A10_PIT_TIMER_COUNT: 144 s->count[index] = value; 145 break; 146 default: 147 qemu_log_mask(LOG_GUEST_ERROR, 148 "%s: Bad offset 0x%x\n", __func__, (int)offset); 149 } 150 break; 151 case AW_A10_PIT_WDOG_CONTROL: 152 s->watch_dog_control = value; 153 break; 154 case AW_A10_PIT_WDOG_MODE: 155 s->watch_dog_mode = value; 156 break; 157 case AW_A10_PIT_COUNT_LO: 158 s->count_lo = value; 159 break; 160 case AW_A10_PIT_COUNT_HI: 161 s->count_hi = value; 162 break; 163 case AW_A10_PIT_COUNT_CTL: 164 s->count_ctl = value; 165 if (s->count_ctl & AW_A10_PIT_COUNT_RL_EN) { 166 uint64_t tmp_count = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 167 168 s->count_lo = tmp_count; 169 s->count_hi = tmp_count >> 32; 170 s->count_ctl &= ~AW_A10_PIT_COUNT_RL_EN; 171 } 172 if (s->count_ctl & AW_A10_PIT_COUNT_CLR_EN) { 173 s->count_lo = 0; 174 s->count_hi = 0; 175 s->count_ctl &= ~AW_A10_PIT_COUNT_CLR_EN; 176 } 177 break; 178 default: 179 qemu_log_mask(LOG_GUEST_ERROR, 180 "%s: Bad offset 0x%x\n", __func__, (int)offset); 181 break; 182 } 183} 184 185static const MemoryRegionOps a10_pit_ops = { 186 .read = a10_pit_read, 187 .write = a10_pit_write, 188 .endianness = DEVICE_NATIVE_ENDIAN, 189}; 190 191static Property a10_pit_properties[] = { 192 DEFINE_PROP_UINT32("clk0-freq", AwA10PITState, clk_freq[0], 0), 193 DEFINE_PROP_UINT32("clk1-freq", AwA10PITState, clk_freq[1], 0), 194 DEFINE_PROP_UINT32("clk2-freq", AwA10PITState, clk_freq[2], 0), 195 DEFINE_PROP_UINT32("clk3-freq", AwA10PITState, clk_freq[3], 0), 196 DEFINE_PROP_END_OF_LIST(), 197}; 198 199static const VMStateDescription vmstate_a10_pit = { 200 .name = "a10.pit", 201 .version_id = 1, 202 .minimum_version_id = 1, 203 .fields = (VMStateField[]) { 204 VMSTATE_UINT32(irq_enable, AwA10PITState), 205 VMSTATE_UINT32(irq_status, AwA10PITState), 206 VMSTATE_UINT32_ARRAY(control, AwA10PITState, AW_A10_PIT_TIMER_NR), 207 VMSTATE_UINT32_ARRAY(interval, AwA10PITState, AW_A10_PIT_TIMER_NR), 208 VMSTATE_UINT32_ARRAY(count, AwA10PITState, AW_A10_PIT_TIMER_NR), 209 VMSTATE_UINT32(watch_dog_mode, AwA10PITState), 210 VMSTATE_UINT32(watch_dog_control, AwA10PITState), 211 VMSTATE_UINT32(count_lo, AwA10PITState), 212 VMSTATE_UINT32(count_hi, AwA10PITState), 213 VMSTATE_UINT32(count_ctl, AwA10PITState), 214 VMSTATE_PTIMER_ARRAY(timer, AwA10PITState, AW_A10_PIT_TIMER_NR), 215 VMSTATE_END_OF_LIST() 216 } 217}; 218 219static void a10_pit_reset(DeviceState *dev) 220{ 221 AwA10PITState *s = AW_A10_PIT(dev); 222 uint8_t i; 223 224 s->irq_enable = 0; 225 s->irq_status = 0; 226 a10_pit_update_irq(s); 227 228 for (i = 0; i < 6; i++) { 229 s->control[i] = AW_A10_PIT_DEFAULT_CLOCK; 230 s->interval[i] = 0; 231 s->count[i] = 0; 232 ptimer_transaction_begin(s->timer[i]); 233 ptimer_stop(s->timer[i]); 234 a10_pit_set_freq(s, i); 235 ptimer_transaction_commit(s->timer[i]); 236 } 237 s->watch_dog_mode = 0; 238 s->watch_dog_control = 0; 239 s->count_lo = 0; 240 s->count_hi = 0; 241 s->count_ctl = 0; 242} 243 244static void a10_pit_timer_cb(void *opaque) 245{ 246 AwA10TimerContext *tc = opaque; 247 AwA10PITState *s = tc->container; 248 uint8_t i = tc->index; 249 250 if (s->control[i] & AW_A10_PIT_TIMER_EN) { 251 s->irq_status |= 1 << i; 252 if (s->control[i] & AW_A10_PIT_TIMER_MODE) { 253 ptimer_stop(s->timer[i]); 254 s->control[i] &= ~AW_A10_PIT_TIMER_EN; 255 } 256 a10_pit_update_irq(s); 257 } 258} 259 260static void a10_pit_init(Object *obj) 261{ 262 AwA10PITState *s = AW_A10_PIT(obj); 263 SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 264 uint8_t i; 265 266 for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) { 267 sysbus_init_irq(sbd, &s->irq[i]); 268 } 269 memory_region_init_io(&s->iomem, OBJECT(s), &a10_pit_ops, s, 270 TYPE_AW_A10_PIT, 0x400); 271 sysbus_init_mmio(sbd, &s->iomem); 272 273 for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) { 274 AwA10TimerContext *tc = &s->timer_context[i]; 275 276 tc->container = s; 277 tc->index = i; 278 s->timer[i] = ptimer_init(a10_pit_timer_cb, tc, PTIMER_POLICY_DEFAULT); 279 } 280} 281 282static void a10_pit_finalize(Object *obj) 283{ 284 AwA10PITState *s = AW_A10_PIT(obj); 285 int i; 286 287 for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) { 288 ptimer_free(s->timer[i]); 289 } 290} 291 292static void a10_pit_class_init(ObjectClass *klass, void *data) 293{ 294 DeviceClass *dc = DEVICE_CLASS(klass); 295 296 dc->reset = a10_pit_reset; 297 device_class_set_props(dc, a10_pit_properties); 298 dc->desc = "allwinner a10 timer"; 299 dc->vmsd = &vmstate_a10_pit; 300} 301 302static const TypeInfo a10_pit_info = { 303 .name = TYPE_AW_A10_PIT, 304 .parent = TYPE_SYS_BUS_DEVICE, 305 .instance_size = sizeof(AwA10PITState), 306 .instance_init = a10_pit_init, 307 .instance_finalize = a10_pit_finalize, 308 .class_init = a10_pit_class_init, 309}; 310 311static void a10_register_types(void) 312{ 313 type_register_static(&a10_pit_info); 314} 315 316type_init(a10_register_types);