xref: /openbmc/qemu/hw/timer/stm32f2xx_timer.c (revision be284705)
1*be284705SAlistair Francis /*
2*be284705SAlistair Francis  * STM32F2XX Timer
3*be284705SAlistair Francis  *
4*be284705SAlistair Francis  * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me>
5*be284705SAlistair Francis  *
6*be284705SAlistair Francis  * Permission is hereby granted, free of charge, to any person obtaining a copy
7*be284705SAlistair Francis  * of this software and associated documentation files (the "Software"), to deal
8*be284705SAlistair Francis  * in the Software without restriction, including without limitation the rights
9*be284705SAlistair Francis  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10*be284705SAlistair Francis  * copies of the Software, and to permit persons to whom the Software is
11*be284705SAlistair Francis  * furnished to do so, subject to the following conditions:
12*be284705SAlistair Francis  *
13*be284705SAlistair Francis  * The above copyright notice and this permission notice shall be included in
14*be284705SAlistair Francis  * all copies or substantial portions of the Software.
15*be284705SAlistair Francis  *
16*be284705SAlistair Francis  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17*be284705SAlistair Francis  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18*be284705SAlistair Francis  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19*be284705SAlistair Francis  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20*be284705SAlistair Francis  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21*be284705SAlistair Francis  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22*be284705SAlistair Francis  * THE SOFTWARE.
23*be284705SAlistair Francis  */
24*be284705SAlistair Francis 
25*be284705SAlistair Francis #include "hw/timer/stm32f2xx_timer.h"
26*be284705SAlistair Francis 
27*be284705SAlistair Francis #ifndef STM_TIMER_ERR_DEBUG
28*be284705SAlistair Francis #define STM_TIMER_ERR_DEBUG 0
29*be284705SAlistair Francis #endif
30*be284705SAlistair Francis 
31*be284705SAlistair Francis #define DB_PRINT_L(lvl, fmt, args...) do { \
32*be284705SAlistair Francis     if (STM_TIMER_ERR_DEBUG >= lvl) { \
33*be284705SAlistair Francis         qemu_log("%s: " fmt, __func__, ## args); \
34*be284705SAlistair Francis     } \
35*be284705SAlistair Francis } while (0);
36*be284705SAlistair Francis 
37*be284705SAlistair Francis #define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args)
38*be284705SAlistair Francis 
39*be284705SAlistair Francis static void stm32f2xx_timer_set_alarm(STM32F2XXTimerState *s, int64_t now);
40*be284705SAlistair Francis 
41*be284705SAlistair Francis static void stm32f2xx_timer_interrupt(void *opaque)
42*be284705SAlistair Francis {
43*be284705SAlistair Francis     STM32F2XXTimerState *s = opaque;
44*be284705SAlistair Francis 
45*be284705SAlistair Francis     DB_PRINT("Interrupt\n");
46*be284705SAlistair Francis 
47*be284705SAlistair Francis     if (s->tim_dier & TIM_DIER_UIE && s->tim_cr1 & TIM_CR1_CEN) {
48*be284705SAlistair Francis         s->tim_sr |= 1;
49*be284705SAlistair Francis         qemu_irq_pulse(s->irq);
50*be284705SAlistair Francis         stm32f2xx_timer_set_alarm(s, s->hit_time);
51*be284705SAlistair Francis     }
52*be284705SAlistair Francis }
53*be284705SAlistair Francis 
54*be284705SAlistair Francis static inline int64_t stm32f2xx_ns_to_ticks(STM32F2XXTimerState *s, int64_t t)
55*be284705SAlistair Francis {
56*be284705SAlistair Francis     return muldiv64(t, s->freq_hz, 1000000000ULL) / (s->tim_psc + 1);
57*be284705SAlistair Francis }
58*be284705SAlistair Francis 
59*be284705SAlistair Francis static void stm32f2xx_timer_set_alarm(STM32F2XXTimerState *s, int64_t now)
60*be284705SAlistair Francis {
61*be284705SAlistair Francis     uint64_t ticks;
62*be284705SAlistair Francis     int64_t now_ticks;
63*be284705SAlistair Francis 
64*be284705SAlistair Francis     if (s->tim_arr == 0) {
65*be284705SAlistair Francis         return;
66*be284705SAlistair Francis     }
67*be284705SAlistair Francis 
68*be284705SAlistair Francis     DB_PRINT("Alarm set at: 0x%x\n", s->tim_cr1);
69*be284705SAlistair Francis 
70*be284705SAlistair Francis     now_ticks = stm32f2xx_ns_to_ticks(s, now);
71*be284705SAlistair Francis     ticks = s->tim_arr - (now_ticks - s->tick_offset);
72*be284705SAlistair Francis 
73*be284705SAlistair Francis     DB_PRINT("Alarm set in %d ticks\n", (int) ticks);
74*be284705SAlistair Francis 
75*be284705SAlistair Francis     s->hit_time = muldiv64((ticks + (uint64_t) now_ticks) * (s->tim_psc + 1),
76*be284705SAlistair Francis                                1000000000ULL, s->freq_hz);
77*be284705SAlistair Francis 
78*be284705SAlistair Francis     timer_mod(s->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->hit_time);
79*be284705SAlistair Francis     DB_PRINT("Wait Time: %" PRId64 " ticks\n", s->hit_time);
80*be284705SAlistair Francis }
81*be284705SAlistair Francis 
82*be284705SAlistair Francis static void stm32f2xx_timer_reset(DeviceState *dev)
83*be284705SAlistair Francis {
84*be284705SAlistair Francis     STM32F2XXTimerState *s = STM32F2XXTIMER(dev);
85*be284705SAlistair Francis     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
86*be284705SAlistair Francis 
87*be284705SAlistair Francis     s->tim_cr1 = 0;
88*be284705SAlistair Francis     s->tim_cr2 = 0;
89*be284705SAlistair Francis     s->tim_smcr = 0;
90*be284705SAlistair Francis     s->tim_dier = 0;
91*be284705SAlistair Francis     s->tim_sr = 0;
92*be284705SAlistair Francis     s->tim_egr = 0;
93*be284705SAlistair Francis     s->tim_ccmr1 = 0;
94*be284705SAlistair Francis     s->tim_ccmr2 = 0;
95*be284705SAlistair Francis     s->tim_ccer = 0;
96*be284705SAlistair Francis     s->tim_psc = 0;
97*be284705SAlistair Francis     s->tim_arr = 0;
98*be284705SAlistair Francis     s->tim_ccr1 = 0;
99*be284705SAlistair Francis     s->tim_ccr2 = 0;
100*be284705SAlistair Francis     s->tim_ccr3 = 0;
101*be284705SAlistair Francis     s->tim_ccr4 = 0;
102*be284705SAlistair Francis     s->tim_dcr = 0;
103*be284705SAlistair Francis     s->tim_dmar = 0;
104*be284705SAlistair Francis     s->tim_or = 0;
105*be284705SAlistair Francis 
106*be284705SAlistair Francis     s->tick_offset = stm32f2xx_ns_to_ticks(s, now);
107*be284705SAlistair Francis }
108*be284705SAlistair Francis 
109*be284705SAlistair Francis static uint64_t stm32f2xx_timer_read(void *opaque, hwaddr offset,
110*be284705SAlistair Francis                            unsigned size)
111*be284705SAlistair Francis {
112*be284705SAlistair Francis     STM32F2XXTimerState *s = opaque;
113*be284705SAlistair Francis 
114*be284705SAlistair Francis     DB_PRINT("Read 0x%"HWADDR_PRIx"\n", offset);
115*be284705SAlistair Francis 
116*be284705SAlistair Francis     switch (offset) {
117*be284705SAlistair Francis     case TIM_CR1:
118*be284705SAlistair Francis         return s->tim_cr1;
119*be284705SAlistair Francis     case TIM_CR2:
120*be284705SAlistair Francis         return s->tim_cr2;
121*be284705SAlistair Francis     case TIM_SMCR:
122*be284705SAlistair Francis         return s->tim_smcr;
123*be284705SAlistair Francis     case TIM_DIER:
124*be284705SAlistair Francis         return s->tim_dier;
125*be284705SAlistair Francis     case TIM_SR:
126*be284705SAlistair Francis         return s->tim_sr;
127*be284705SAlistair Francis     case TIM_EGR:
128*be284705SAlistair Francis         return s->tim_egr;
129*be284705SAlistair Francis     case TIM_CCMR1:
130*be284705SAlistair Francis         return s->tim_ccmr1;
131*be284705SAlistair Francis     case TIM_CCMR2:
132*be284705SAlistair Francis         return s->tim_ccmr2;
133*be284705SAlistair Francis     case TIM_CCER:
134*be284705SAlistair Francis         return s->tim_ccer;
135*be284705SAlistair Francis     case TIM_CNT:
136*be284705SAlistair Francis         return stm32f2xx_ns_to_ticks(s, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) -
137*be284705SAlistair Francis                s->tick_offset;
138*be284705SAlistair Francis     case TIM_PSC:
139*be284705SAlistair Francis         return s->tim_psc;
140*be284705SAlistair Francis     case TIM_ARR:
141*be284705SAlistair Francis         return s->tim_arr;
142*be284705SAlistair Francis     case TIM_CCR1:
143*be284705SAlistair Francis         return s->tim_ccr1;
144*be284705SAlistair Francis     case TIM_CCR2:
145*be284705SAlistair Francis         return s->tim_ccr2;
146*be284705SAlistair Francis     case TIM_CCR3:
147*be284705SAlistair Francis         return s->tim_ccr3;
148*be284705SAlistair Francis     case TIM_CCR4:
149*be284705SAlistair Francis         return s->tim_ccr4;
150*be284705SAlistair Francis     case TIM_DCR:
151*be284705SAlistair Francis         return s->tim_dcr;
152*be284705SAlistair Francis     case TIM_DMAR:
153*be284705SAlistair Francis         return s->tim_dmar;
154*be284705SAlistair Francis     case TIM_OR:
155*be284705SAlistair Francis         return s->tim_or;
156*be284705SAlistair Francis     default:
157*be284705SAlistair Francis         qemu_log_mask(LOG_GUEST_ERROR,
158*be284705SAlistair Francis                       "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, offset);
159*be284705SAlistair Francis     }
160*be284705SAlistair Francis 
161*be284705SAlistair Francis     return 0;
162*be284705SAlistair Francis }
163*be284705SAlistair Francis 
164*be284705SAlistair Francis static void stm32f2xx_timer_write(void *opaque, hwaddr offset,
165*be284705SAlistair Francis                         uint64_t val64, unsigned size)
166*be284705SAlistair Francis {
167*be284705SAlistair Francis     STM32F2XXTimerState *s = opaque;
168*be284705SAlistair Francis     uint32_t value = val64;
169*be284705SAlistair Francis     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
170*be284705SAlistair Francis     uint32_t timer_val = 0;
171*be284705SAlistair Francis 
172*be284705SAlistair Francis     DB_PRINT("Write 0x%x, 0x%"HWADDR_PRIx"\n", value, offset);
173*be284705SAlistair Francis 
174*be284705SAlistair Francis     switch (offset) {
175*be284705SAlistair Francis     case TIM_CR1:
176*be284705SAlistair Francis         s->tim_cr1 = value;
177*be284705SAlistair Francis         return;
178*be284705SAlistair Francis     case TIM_CR2:
179*be284705SAlistair Francis         s->tim_cr2 = value;
180*be284705SAlistair Francis         return;
181*be284705SAlistair Francis     case TIM_SMCR:
182*be284705SAlistair Francis         s->tim_smcr = value;
183*be284705SAlistair Francis         return;
184*be284705SAlistair Francis     case TIM_DIER:
185*be284705SAlistair Francis         s->tim_dier = value;
186*be284705SAlistair Francis         return;
187*be284705SAlistair Francis     case TIM_SR:
188*be284705SAlistair Francis         /* This is set by hardware and cleared by software */
189*be284705SAlistair Francis         s->tim_sr &= value;
190*be284705SAlistair Francis         return;
191*be284705SAlistair Francis     case TIM_EGR:
192*be284705SAlistair Francis         s->tim_egr = value;
193*be284705SAlistair Francis         if (s->tim_egr & TIM_EGR_UG) {
194*be284705SAlistair Francis             timer_val = 0;
195*be284705SAlistair Francis             break;
196*be284705SAlistair Francis         }
197*be284705SAlistair Francis         return;
198*be284705SAlistair Francis     case TIM_CCMR1:
199*be284705SAlistair Francis         s->tim_ccmr1 = value;
200*be284705SAlistair Francis         return;
201*be284705SAlistair Francis     case TIM_CCMR2:
202*be284705SAlistair Francis         s->tim_ccmr2 = value;
203*be284705SAlistair Francis         return;
204*be284705SAlistair Francis     case TIM_CCER:
205*be284705SAlistair Francis         s->tim_ccer = value;
206*be284705SAlistair Francis         return;
207*be284705SAlistair Francis     case TIM_PSC:
208*be284705SAlistair Francis         timer_val = stm32f2xx_ns_to_ticks(s, now) - s->tick_offset;
209*be284705SAlistair Francis         s->tim_psc = value;
210*be284705SAlistair Francis         value = timer_val;
211*be284705SAlistair Francis         break;
212*be284705SAlistair Francis     case TIM_CNT:
213*be284705SAlistair Francis         timer_val = value;
214*be284705SAlistair Francis         break;
215*be284705SAlistair Francis     case TIM_ARR:
216*be284705SAlistair Francis         s->tim_arr = value;
217*be284705SAlistair Francis         stm32f2xx_timer_set_alarm(s, now);
218*be284705SAlistair Francis         return;
219*be284705SAlistair Francis     case TIM_CCR1:
220*be284705SAlistair Francis         s->tim_ccr1 = value;
221*be284705SAlistair Francis         return;
222*be284705SAlistair Francis     case TIM_CCR2:
223*be284705SAlistair Francis         s->tim_ccr2 = value;
224*be284705SAlistair Francis         return;
225*be284705SAlistair Francis     case TIM_CCR3:
226*be284705SAlistair Francis         s->tim_ccr3 = value;
227*be284705SAlistair Francis         return;
228*be284705SAlistair Francis     case TIM_CCR4:
229*be284705SAlistair Francis         s->tim_ccr4 = value;
230*be284705SAlistair Francis         return;
231*be284705SAlistair Francis     case TIM_DCR:
232*be284705SAlistair Francis         s->tim_dcr = value;
233*be284705SAlistair Francis         return;
234*be284705SAlistair Francis     case TIM_DMAR:
235*be284705SAlistair Francis         s->tim_dmar = value;
236*be284705SAlistair Francis         return;
237*be284705SAlistair Francis     case TIM_OR:
238*be284705SAlistair Francis         s->tim_or = value;
239*be284705SAlistair Francis         return;
240*be284705SAlistair Francis     default:
241*be284705SAlistair Francis         qemu_log_mask(LOG_GUEST_ERROR,
242*be284705SAlistair Francis                       "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, offset);
243*be284705SAlistair Francis         return;
244*be284705SAlistair Francis     }
245*be284705SAlistair Francis 
246*be284705SAlistair Francis     /* This means that a register write has affected the timer in a way that
247*be284705SAlistair Francis      * requires a refresh of both tick_offset and the alarm.
248*be284705SAlistair Francis      */
249*be284705SAlistair Francis     s->tick_offset = stm32f2xx_ns_to_ticks(s, now) - timer_val;
250*be284705SAlistair Francis     stm32f2xx_timer_set_alarm(s, now);
251*be284705SAlistair Francis }
252*be284705SAlistair Francis 
253*be284705SAlistair Francis static const MemoryRegionOps stm32f2xx_timer_ops = {
254*be284705SAlistair Francis     .read = stm32f2xx_timer_read,
255*be284705SAlistair Francis     .write = stm32f2xx_timer_write,
256*be284705SAlistair Francis     .endianness = DEVICE_NATIVE_ENDIAN,
257*be284705SAlistair Francis };
258*be284705SAlistair Francis 
259*be284705SAlistair Francis static const VMStateDescription vmstate_stm32f2xx_timer = {
260*be284705SAlistair Francis     .name = TYPE_STM32F2XX_TIMER,
261*be284705SAlistair Francis     .version_id = 1,
262*be284705SAlistair Francis     .minimum_version_id = 1,
263*be284705SAlistair Francis     .fields = (VMStateField[]) {
264*be284705SAlistair Francis         VMSTATE_INT64(tick_offset, STM32F2XXTimerState),
265*be284705SAlistair Francis         VMSTATE_UINT32(tim_cr1, STM32F2XXTimerState),
266*be284705SAlistair Francis         VMSTATE_UINT32(tim_cr2, STM32F2XXTimerState),
267*be284705SAlistair Francis         VMSTATE_UINT32(tim_smcr, STM32F2XXTimerState),
268*be284705SAlistair Francis         VMSTATE_UINT32(tim_dier, STM32F2XXTimerState),
269*be284705SAlistair Francis         VMSTATE_UINT32(tim_sr, STM32F2XXTimerState),
270*be284705SAlistair Francis         VMSTATE_UINT32(tim_egr, STM32F2XXTimerState),
271*be284705SAlistair Francis         VMSTATE_UINT32(tim_ccmr1, STM32F2XXTimerState),
272*be284705SAlistair Francis         VMSTATE_UINT32(tim_ccmr2, STM32F2XXTimerState),
273*be284705SAlistair Francis         VMSTATE_UINT32(tim_ccer, STM32F2XXTimerState),
274*be284705SAlistair Francis         VMSTATE_UINT32(tim_psc, STM32F2XXTimerState),
275*be284705SAlistair Francis         VMSTATE_UINT32(tim_arr, STM32F2XXTimerState),
276*be284705SAlistair Francis         VMSTATE_UINT32(tim_ccr1, STM32F2XXTimerState),
277*be284705SAlistair Francis         VMSTATE_UINT32(tim_ccr2, STM32F2XXTimerState),
278*be284705SAlistair Francis         VMSTATE_UINT32(tim_ccr3, STM32F2XXTimerState),
279*be284705SAlistair Francis         VMSTATE_UINT32(tim_ccr4, STM32F2XXTimerState),
280*be284705SAlistair Francis         VMSTATE_UINT32(tim_dcr, STM32F2XXTimerState),
281*be284705SAlistair Francis         VMSTATE_UINT32(tim_dmar, STM32F2XXTimerState),
282*be284705SAlistair Francis         VMSTATE_UINT32(tim_or, STM32F2XXTimerState),
283*be284705SAlistair Francis         VMSTATE_END_OF_LIST()
284*be284705SAlistair Francis     }
285*be284705SAlistair Francis };
286*be284705SAlistair Francis 
287*be284705SAlistair Francis static Property stm32f2xx_timer_properties[] = {
288*be284705SAlistair Francis     DEFINE_PROP_UINT64("clock-frequency", struct STM32F2XXTimerState,
289*be284705SAlistair Francis                        freq_hz, 1000000000),
290*be284705SAlistair Francis     DEFINE_PROP_END_OF_LIST(),
291*be284705SAlistair Francis };
292*be284705SAlistair Francis 
293*be284705SAlistair Francis static void stm32f2xx_timer_init(Object *obj)
294*be284705SAlistair Francis {
295*be284705SAlistair Francis     STM32F2XXTimerState *s = STM32F2XXTIMER(obj);
296*be284705SAlistair Francis 
297*be284705SAlistair Francis     sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
298*be284705SAlistair Francis 
299*be284705SAlistair Francis     memory_region_init_io(&s->iomem, obj, &stm32f2xx_timer_ops, s,
300*be284705SAlistair Francis                           "stm32f2xx_timer", 0x4000);
301*be284705SAlistair Francis     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
302*be284705SAlistair Francis 
303*be284705SAlistair Francis     s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, stm32f2xx_timer_interrupt, s);
304*be284705SAlistair Francis }
305*be284705SAlistair Francis 
306*be284705SAlistair Francis static void stm32f2xx_timer_class_init(ObjectClass *klass, void *data)
307*be284705SAlistair Francis {
308*be284705SAlistair Francis     DeviceClass *dc = DEVICE_CLASS(klass);
309*be284705SAlistair Francis 
310*be284705SAlistair Francis     dc->reset = stm32f2xx_timer_reset;
311*be284705SAlistair Francis     dc->props = stm32f2xx_timer_properties;
312*be284705SAlistair Francis     dc->vmsd = &vmstate_stm32f2xx_timer;
313*be284705SAlistair Francis }
314*be284705SAlistair Francis 
315*be284705SAlistair Francis static const TypeInfo stm32f2xx_timer_info = {
316*be284705SAlistair Francis     .name          = TYPE_STM32F2XX_TIMER,
317*be284705SAlistair Francis     .parent        = TYPE_SYS_BUS_DEVICE,
318*be284705SAlistair Francis     .instance_size = sizeof(STM32F2XXTimerState),
319*be284705SAlistair Francis     .instance_init = stm32f2xx_timer_init,
320*be284705SAlistair Francis     .class_init    = stm32f2xx_timer_class_init,
321*be284705SAlistair Francis };
322*be284705SAlistair Francis 
323*be284705SAlistair Francis static void stm32f2xx_timer_register_types(void)
324*be284705SAlistair Francis {
325*be284705SAlistair Francis     type_register_static(&stm32f2xx_timer_info);
326*be284705SAlistair Francis }
327*be284705SAlistair Francis 
328*be284705SAlistair Francis type_init(stm32f2xx_timer_register_types)
329