blob: 68aa1a76360d303613a57b856b811ddc922e82cc [file] [log] [blame]
Peter Maydell5dd85b42017-07-17 13:36:08 +01001/*
2 * ARM CMSDK APB timer emulation
3 *
4 * Copyright (c) 2017 Linaro Limited
5 * Written by Peter Maydell
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 or
9 * (at your option) any later version.
10 */
11
12/* This is a model of the "APB timer" which is part of the Cortex-M
13 * System Design Kit (CMSDK) and documented in the Cortex-M System
14 * Design Kit Technical Reference Manual (ARM DDI0479C):
15 * https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit
16 *
17 * The hardware has an EXTIN input wire, which can be configured
18 * by the guest to act either as a 'timer enable' (timer does not run
19 * when EXTIN is low), or as a 'timer clock' (timer runs at frequency
20 * of EXTIN clock, not PCLK frequency). We don't model this.
21 *
22 * The documentation is not very clear about the exact behaviour;
23 * we choose to implement that the interrupt is triggered when
24 * the counter goes from 1 to 0, that the counter then holds at 0
25 * for one clock cycle before reloading from the RELOAD register,
26 * and that if the RELOAD register is 0 this does not cause an
27 * interrupt (as there is no further 1->0 transition).
28 */
29
30#include "qemu/osdep.h"
31#include "qemu/log.h"
Markus Armbruster0b8fa322019-05-23 16:35:07 +020032#include "qemu/module.h"
Peter Maydell5dd85b42017-07-17 13:36:08 +010033#include "qapi/error.h"
34#include "trace.h"
35#include "hw/sysbus.h"
Markus Armbruster64552b62019-08-12 07:23:42 +020036#include "hw/irq.h"
Peter Maydell5dd85b42017-07-17 13:36:08 +010037#include "hw/registerfields.h"
Peter Maydell7cc378e2021-01-28 11:41:27 +000038#include "hw/qdev-clock.h"
Peter Maydell5dd85b42017-07-17 13:36:08 +010039#include "hw/timer/cmsdk-apb-timer.h"
Markus Armbrusterd6454272019-08-12 07:23:45 +020040#include "migration/vmstate.h"
Peter Maydell5dd85b42017-07-17 13:36:08 +010041
42REG32(CTRL, 0)
43 FIELD(CTRL, EN, 0, 1)
44 FIELD(CTRL, SELEXTEN, 1, 1)
45 FIELD(CTRL, SELEXTCLK, 2, 1)
46 FIELD(CTRL, IRQEN, 3, 1)
47REG32(VALUE, 4)
48REG32(RELOAD, 8)
49REG32(INTSTATUS, 0xc)
50 FIELD(INTSTATUS, IRQ, 0, 1)
51REG32(PID4, 0xFD0)
52REG32(PID5, 0xFD4)
53REG32(PID6, 0xFD8)
54REG32(PID7, 0xFDC)
55REG32(PID0, 0xFE0)
56REG32(PID1, 0xFE4)
57REG32(PID2, 0xFE8)
58REG32(PID3, 0xFEC)
59REG32(CID0, 0xFF0)
60REG32(CID1, 0xFF4)
61REG32(CID2, 0xFF8)
62REG32(CID3, 0xFFC)
63
64/* PID/CID values */
65static const int timer_id[] = {
66 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
67 0x22, 0xb8, 0x1b, 0x00, /* PID0..PID3 */
68 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
69};
70
Peter Maydellb56d3512021-01-28 11:41:26 +000071static void cmsdk_apb_timer_update(CMSDKAPBTimer *s)
Peter Maydell5dd85b42017-07-17 13:36:08 +010072{
73 qemu_set_irq(s->timerint, !!(s->intstatus & R_INTSTATUS_IRQ_MASK));
74}
75
76static uint64_t cmsdk_apb_timer_read(void *opaque, hwaddr offset, unsigned size)
77{
Peter Maydellb56d3512021-01-28 11:41:26 +000078 CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
Peter Maydell5dd85b42017-07-17 13:36:08 +010079 uint64_t r;
80
81 switch (offset) {
82 case A_CTRL:
83 r = s->ctrl;
84 break;
85 case A_VALUE:
86 r = ptimer_get_count(s->timer);
87 break;
88 case A_RELOAD:
89 r = ptimer_get_limit(s->timer);
90 break;
91 case A_INTSTATUS:
92 r = s->intstatus;
93 break;
94 case A_PID4 ... A_CID3:
95 r = timer_id[(offset - A_PID4) / 4];
96 break;
97 default:
98 qemu_log_mask(LOG_GUEST_ERROR,
99 "CMSDK APB timer read: bad offset %x\n", (int) offset);
100 r = 0;
101 break;
102 }
103 trace_cmsdk_apb_timer_read(offset, r, size);
104 return r;
105}
106
107static void cmsdk_apb_timer_write(void *opaque, hwaddr offset, uint64_t value,
108 unsigned size)
109{
Peter Maydellb56d3512021-01-28 11:41:26 +0000110 CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
Peter Maydell5dd85b42017-07-17 13:36:08 +0100111
112 trace_cmsdk_apb_timer_write(offset, value, size);
113
114 switch (offset) {
115 case A_CTRL:
116 if (value & 6) {
117 /* Bits [1] and [2] enable using EXTIN as either clock or
118 * an enable line. We don't model this.
119 */
120 qemu_log_mask(LOG_UNIMP,
121 "CMSDK APB timer: EXTIN input not supported\n");
122 }
123 s->ctrl = value & 0xf;
Peter Maydell19c12fe2019-10-08 18:17:28 +0100124 ptimer_transaction_begin(s->timer);
Peter Maydell5dd85b42017-07-17 13:36:08 +0100125 if (s->ctrl & R_CTRL_EN_MASK) {
Guenter Roeck0e256832018-07-09 14:51:34 +0100126 ptimer_run(s->timer, ptimer_get_limit(s->timer) == 0);
Peter Maydell5dd85b42017-07-17 13:36:08 +0100127 } else {
128 ptimer_stop(s->timer);
129 }
Peter Maydell19c12fe2019-10-08 18:17:28 +0100130 ptimer_transaction_commit(s->timer);
Peter Maydell5dd85b42017-07-17 13:36:08 +0100131 break;
132 case A_RELOAD:
133 /* Writing to reload also sets the current timer value */
Peter Maydell19c12fe2019-10-08 18:17:28 +0100134 ptimer_transaction_begin(s->timer);
Peter Maydell1a9b3062018-07-09 14:51:34 +0100135 if (!value) {
136 ptimer_stop(s->timer);
137 }
Peter Maydell5dd85b42017-07-17 13:36:08 +0100138 ptimer_set_limit(s->timer, value, 1);
Peter Maydell1a9b3062018-07-09 14:51:34 +0100139 if (value && (s->ctrl & R_CTRL_EN_MASK)) {
140 /*
141 * Make sure timer is running (it might have stopped if this
142 * was an expired one-shot timer)
143 */
144 ptimer_run(s->timer, 0);
145 }
Peter Maydell19c12fe2019-10-08 18:17:28 +0100146 ptimer_transaction_commit(s->timer);
Peter Maydell5dd85b42017-07-17 13:36:08 +0100147 break;
148 case A_VALUE:
Peter Maydell19c12fe2019-10-08 18:17:28 +0100149 ptimer_transaction_begin(s->timer);
Peter Maydell1a9b3062018-07-09 14:51:34 +0100150 if (!value && !ptimer_get_limit(s->timer)) {
151 ptimer_stop(s->timer);
152 }
Peter Maydell5dd85b42017-07-17 13:36:08 +0100153 ptimer_set_count(s->timer, value);
Peter Maydell1a9b3062018-07-09 14:51:34 +0100154 if (value && (s->ctrl & R_CTRL_EN_MASK)) {
155 ptimer_run(s->timer, ptimer_get_limit(s->timer) == 0);
156 }
Peter Maydell19c12fe2019-10-08 18:17:28 +0100157 ptimer_transaction_commit(s->timer);
Peter Maydell5dd85b42017-07-17 13:36:08 +0100158 break;
159 case A_INTSTATUS:
160 /* Just one bit, which is W1C. */
161 value &= 1;
162 s->intstatus &= ~value;
163 cmsdk_apb_timer_update(s);
164 break;
165 case A_PID4 ... A_CID3:
166 qemu_log_mask(LOG_GUEST_ERROR,
167 "CMSDK APB timer write: write to RO offset 0x%x\n",
168 (int)offset);
169 break;
170 default:
171 qemu_log_mask(LOG_GUEST_ERROR,
172 "CMSDK APB timer write: bad offset 0x%x\n", (int) offset);
173 break;
174 }
175}
176
177static const MemoryRegionOps cmsdk_apb_timer_ops = {
178 .read = cmsdk_apb_timer_read,
179 .write = cmsdk_apb_timer_write,
180 .endianness = DEVICE_LITTLE_ENDIAN,
181};
182
183static void cmsdk_apb_timer_tick(void *opaque)
184{
Peter Maydellb56d3512021-01-28 11:41:26 +0000185 CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
Peter Maydell5dd85b42017-07-17 13:36:08 +0100186
187 if (s->ctrl & R_CTRL_IRQEN_MASK) {
188 s->intstatus |= R_INTSTATUS_IRQ_MASK;
189 cmsdk_apb_timer_update(s);
190 }
191}
192
193static void cmsdk_apb_timer_reset(DeviceState *dev)
194{
Peter Maydellb56d3512021-01-28 11:41:26 +0000195 CMSDKAPBTimer *s = CMSDK_APB_TIMER(dev);
Peter Maydell5dd85b42017-07-17 13:36:08 +0100196
197 trace_cmsdk_apb_timer_reset();
198 s->ctrl = 0;
199 s->intstatus = 0;
Peter Maydell19c12fe2019-10-08 18:17:28 +0100200 ptimer_transaction_begin(s->timer);
Peter Maydell5dd85b42017-07-17 13:36:08 +0100201 ptimer_stop(s->timer);
202 /* Set the limit and the count */
203 ptimer_set_limit(s->timer, 0, 1);
Peter Maydell19c12fe2019-10-08 18:17:28 +0100204 ptimer_transaction_commit(s->timer);
Peter Maydell5dd85b42017-07-17 13:36:08 +0100205}
206
Peter Maydell5ee0abe2021-02-19 14:45:34 +0000207static void cmsdk_apb_timer_clk_update(void *opaque, ClockEvent event)
Peter Maydell5e066562021-01-28 11:41:38 +0000208{
209 CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
210
211 ptimer_transaction_begin(s->timer);
212 ptimer_set_period_from_clock(s->timer, s->pclk, 1);
213 ptimer_transaction_commit(s->timer);
214}
215
Peter Maydell5dd85b42017-07-17 13:36:08 +0100216static void cmsdk_apb_timer_init(Object *obj)
217{
218 SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
Peter Maydellb56d3512021-01-28 11:41:26 +0000219 CMSDKAPBTimer *s = CMSDK_APB_TIMER(obj);
Peter Maydell5dd85b42017-07-17 13:36:08 +0100220
221 memory_region_init_io(&s->iomem, obj, &cmsdk_apb_timer_ops,
222 s, "cmsdk-apb-timer", 0x1000);
223 sysbus_init_mmio(sbd, &s->iomem);
224 sysbus_init_irq(sbd, &s->timerint);
Peter Maydell5e066562021-01-28 11:41:38 +0000225 s->pclk = qdev_init_clock_in(DEVICE(s), "pclk",
Peter Maydell5ee0abe2021-02-19 14:45:34 +0000226 cmsdk_apb_timer_clk_update, s, ClockUpdate);
Peter Maydell5dd85b42017-07-17 13:36:08 +0100227}
228
229static void cmsdk_apb_timer_realize(DeviceState *dev, Error **errp)
230{
Peter Maydellb56d3512021-01-28 11:41:26 +0000231 CMSDKAPBTimer *s = CMSDK_APB_TIMER(dev);
Peter Maydell5dd85b42017-07-17 13:36:08 +0100232
Peter Maydell5e066562021-01-28 11:41:38 +0000233 if (!clock_has_source(s->pclk)) {
234 error_setg(errp, "CMSDK APB timer: pclk clock must be connected");
Peter Maydell5dd85b42017-07-17 13:36:08 +0100235 return;
236 }
237
Peter Maydell19c12fe2019-10-08 18:17:28 +0100238 s->timer = ptimer_init(cmsdk_apb_timer_tick, s,
Peter Maydell5dd85b42017-07-17 13:36:08 +0100239 PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD |
Peter Maydell65830802018-07-09 14:51:34 +0100240 PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT |
Peter Maydell5dd85b42017-07-17 13:36:08 +0100241 PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
242 PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
243
Peter Maydell19c12fe2019-10-08 18:17:28 +0100244 ptimer_transaction_begin(s->timer);
Peter Maydell5e066562021-01-28 11:41:38 +0000245 ptimer_set_period_from_clock(s->timer, s->pclk, 1);
Peter Maydell19c12fe2019-10-08 18:17:28 +0100246 ptimer_transaction_commit(s->timer);
Peter Maydell5dd85b42017-07-17 13:36:08 +0100247}
248
249static const VMStateDescription cmsdk_apb_timer_vmstate = {
250 .name = "cmsdk-apb-timer",
Peter Maydell7cc378e2021-01-28 11:41:27 +0000251 .version_id = 2,
252 .minimum_version_id = 2,
Peter Maydell5dd85b42017-07-17 13:36:08 +0100253 .fields = (VMStateField[]) {
Peter Maydellb56d3512021-01-28 11:41:26 +0000254 VMSTATE_PTIMER(timer, CMSDKAPBTimer),
Peter Maydell7cc378e2021-01-28 11:41:27 +0000255 VMSTATE_CLOCK(pclk, CMSDKAPBTimer),
Peter Maydellb56d3512021-01-28 11:41:26 +0000256 VMSTATE_UINT32(ctrl, CMSDKAPBTimer),
257 VMSTATE_UINT32(value, CMSDKAPBTimer),
258 VMSTATE_UINT32(reload, CMSDKAPBTimer),
259 VMSTATE_UINT32(intstatus, CMSDKAPBTimer),
Peter Maydell5dd85b42017-07-17 13:36:08 +0100260 VMSTATE_END_OF_LIST()
261 }
262};
263
Peter Maydell5dd85b42017-07-17 13:36:08 +0100264static void cmsdk_apb_timer_class_init(ObjectClass *klass, void *data)
265{
266 DeviceClass *dc = DEVICE_CLASS(klass);
267
268 dc->realize = cmsdk_apb_timer_realize;
269 dc->vmsd = &cmsdk_apb_timer_vmstate;
270 dc->reset = cmsdk_apb_timer_reset;
Peter Maydell5dd85b42017-07-17 13:36:08 +0100271}
272
273static const TypeInfo cmsdk_apb_timer_info = {
274 .name = TYPE_CMSDK_APB_TIMER,
275 .parent = TYPE_SYS_BUS_DEVICE,
Peter Maydellb56d3512021-01-28 11:41:26 +0000276 .instance_size = sizeof(CMSDKAPBTimer),
Peter Maydell5dd85b42017-07-17 13:36:08 +0100277 .instance_init = cmsdk_apb_timer_init,
278 .class_init = cmsdk_apb_timer_class_init,
279};
280
281static void cmsdk_apb_timer_register_types(void)
282{
283 type_register_static(&cmsdk_apb_timer_info);
284}
285
286type_init(cmsdk_apb_timer_register_types);