altera_timer.c (6482B)
1/* 2 * QEMU model of the Altera timer. 3 * 4 * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com> 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library 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 GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, see 18 * <http://www.gnu.org/licenses/lgpl-2.1.html> 19 */ 20 21#include "qemu/osdep.h" 22#include "qemu/module.h" 23#include "qapi/error.h" 24 25#include "hw/sysbus.h" 26#include "hw/irq.h" 27#include "hw/ptimer.h" 28#include "hw/qdev-properties.h" 29#include "qom/object.h" 30 31#define R_STATUS 0 32#define R_CONTROL 1 33#define R_PERIODL 2 34#define R_PERIODH 3 35#define R_SNAPL 4 36#define R_SNAPH 5 37#define R_MAX 6 38 39#define STATUS_TO 0x0001 40#define STATUS_RUN 0x0002 41 42#define CONTROL_ITO 0x0001 43#define CONTROL_CONT 0x0002 44#define CONTROL_START 0x0004 45#define CONTROL_STOP 0x0008 46 47#define TYPE_ALTERA_TIMER "ALTR.timer" 48OBJECT_DECLARE_SIMPLE_TYPE(AlteraTimer, ALTERA_TIMER) 49 50struct AlteraTimer { 51 SysBusDevice busdev; 52 MemoryRegion mmio; 53 qemu_irq irq; 54 uint32_t freq_hz; 55 ptimer_state *ptimer; 56 uint32_t regs[R_MAX]; 57}; 58 59static int timer_irq_state(AlteraTimer *t) 60{ 61 bool irq = (t->regs[R_STATUS] & STATUS_TO) && 62 (t->regs[R_CONTROL] & CONTROL_ITO); 63 return irq; 64} 65 66static uint64_t timer_read(void *opaque, hwaddr addr, 67 unsigned int size) 68{ 69 AlteraTimer *t = opaque; 70 uint64_t r = 0; 71 72 addr >>= 2; 73 74 switch (addr) { 75 case R_CONTROL: 76 r = t->regs[R_CONTROL] & (CONTROL_ITO | CONTROL_CONT); 77 break; 78 79 default: 80 if (addr < ARRAY_SIZE(t->regs)) { 81 r = t->regs[addr]; 82 } 83 break; 84 } 85 86 return r; 87} 88 89static void timer_write(void *opaque, hwaddr addr, 90 uint64_t value, unsigned int size) 91{ 92 AlteraTimer *t = opaque; 93 uint64_t tvalue; 94 uint32_t count = 0; 95 int irqState = timer_irq_state(t); 96 97 addr >>= 2; 98 99 switch (addr) { 100 case R_STATUS: 101 /* The timeout bit is cleared by writing the status register. */ 102 t->regs[R_STATUS] &= ~STATUS_TO; 103 break; 104 105 case R_CONTROL: 106 ptimer_transaction_begin(t->ptimer); 107 t->regs[R_CONTROL] = value & (CONTROL_ITO | CONTROL_CONT); 108 if ((value & CONTROL_START) && 109 !(t->regs[R_STATUS] & STATUS_RUN)) { 110 ptimer_run(t->ptimer, 1); 111 t->regs[R_STATUS] |= STATUS_RUN; 112 } 113 if ((value & CONTROL_STOP) && (t->regs[R_STATUS] & STATUS_RUN)) { 114 ptimer_stop(t->ptimer); 115 t->regs[R_STATUS] &= ~STATUS_RUN; 116 } 117 ptimer_transaction_commit(t->ptimer); 118 break; 119 120 case R_PERIODL: 121 case R_PERIODH: 122 ptimer_transaction_begin(t->ptimer); 123 t->regs[addr] = value & 0xFFFF; 124 if (t->regs[R_STATUS] & STATUS_RUN) { 125 ptimer_stop(t->ptimer); 126 t->regs[R_STATUS] &= ~STATUS_RUN; 127 } 128 tvalue = (t->regs[R_PERIODH] << 16) | t->regs[R_PERIODL]; 129 ptimer_set_limit(t->ptimer, tvalue + 1, 1); 130 ptimer_transaction_commit(t->ptimer); 131 break; 132 133 case R_SNAPL: 134 case R_SNAPH: 135 count = ptimer_get_count(t->ptimer); 136 t->regs[R_SNAPL] = count & 0xFFFF; 137 t->regs[R_SNAPH] = count >> 16; 138 break; 139 140 default: 141 break; 142 } 143 144 if (irqState != timer_irq_state(t)) { 145 qemu_set_irq(t->irq, timer_irq_state(t)); 146 } 147} 148 149static const MemoryRegionOps timer_ops = { 150 .read = timer_read, 151 .write = timer_write, 152 .endianness = DEVICE_NATIVE_ENDIAN, 153 .valid = { 154 .min_access_size = 1, 155 .max_access_size = 4 156 } 157}; 158 159static void timer_hit(void *opaque) 160{ 161 AlteraTimer *t = opaque; 162 const uint64_t tvalue = (t->regs[R_PERIODH] << 16) | t->regs[R_PERIODL]; 163 164 t->regs[R_STATUS] |= STATUS_TO; 165 166 ptimer_set_limit(t->ptimer, tvalue + 1, 1); 167 168 if (!(t->regs[R_CONTROL] & CONTROL_CONT)) { 169 t->regs[R_STATUS] &= ~STATUS_RUN; 170 ptimer_set_count(t->ptimer, tvalue); 171 } else { 172 ptimer_run(t->ptimer, 1); 173 } 174 175 qemu_set_irq(t->irq, timer_irq_state(t)); 176} 177 178static void altera_timer_realize(DeviceState *dev, Error **errp) 179{ 180 AlteraTimer *t = ALTERA_TIMER(dev); 181 SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 182 183 if (t->freq_hz == 0) { 184 error_setg(errp, "\"clock-frequency\" property must be provided."); 185 return; 186 } 187 188 t->ptimer = ptimer_init(timer_hit, t, PTIMER_POLICY_DEFAULT); 189 ptimer_transaction_begin(t->ptimer); 190 ptimer_set_freq(t->ptimer, t->freq_hz); 191 ptimer_transaction_commit(t->ptimer); 192 193 memory_region_init_io(&t->mmio, OBJECT(t), &timer_ops, t, 194 TYPE_ALTERA_TIMER, R_MAX * sizeof(uint32_t)); 195 sysbus_init_mmio(sbd, &t->mmio); 196} 197 198static void altera_timer_init(Object *obj) 199{ 200 AlteraTimer *t = ALTERA_TIMER(obj); 201 SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 202 203 sysbus_init_irq(sbd, &t->irq); 204} 205 206static void altera_timer_reset(DeviceState *dev) 207{ 208 AlteraTimer *t = ALTERA_TIMER(dev); 209 210 ptimer_transaction_begin(t->ptimer); 211 ptimer_stop(t->ptimer); 212 ptimer_set_limit(t->ptimer, 0xffffffff, 1); 213 ptimer_transaction_commit(t->ptimer); 214 memset(t->regs, 0, sizeof(t->regs)); 215} 216 217static Property altera_timer_properties[] = { 218 DEFINE_PROP_UINT32("clock-frequency", AlteraTimer, freq_hz, 0), 219 DEFINE_PROP_END_OF_LIST(), 220}; 221 222static void altera_timer_class_init(ObjectClass *klass, void *data) 223{ 224 DeviceClass *dc = DEVICE_CLASS(klass); 225 226 dc->realize = altera_timer_realize; 227 device_class_set_props(dc, altera_timer_properties); 228 dc->reset = altera_timer_reset; 229} 230 231static const TypeInfo altera_timer_info = { 232 .name = TYPE_ALTERA_TIMER, 233 .parent = TYPE_SYS_BUS_DEVICE, 234 .instance_size = sizeof(AlteraTimer), 235 .instance_init = altera_timer_init, 236 .class_init = altera_timer_class_init, 237}; 238 239static void altera_timer_register(void) 240{ 241 type_register_static(&altera_timer_info); 242} 243 244type_init(altera_timer_register)