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" 48 OBJECT_DECLARE_SIMPLE_TYPE(AlteraTimer, ALTERA_TIMER) 49 50 struct 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 59 static 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 66 static 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 89 static 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 149 static 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 159 static 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 178 static 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 198 static 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 206 static 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 217 static Property altera_timer_properties[] = { 218 DEFINE_PROP_UINT32("clock-frequency", AlteraTimer, freq_hz, 0), 219 DEFINE_PROP_END_OF_LIST(), 220 }; 221 222 static 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 231 static 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 239 static void altera_timer_register(void) 240 { 241 type_register_static(&altera_timer_info); 242 } 243 244 type_init(altera_timer_register) 245