xref: /qemu/hw/timer/npcm7xx_timer.c (revision 85fdd74ff074bf59644131cea9e2ae1f2a8d5fd1)
1*85fdd74fSHavard Skinnemoen /*
2*85fdd74fSHavard Skinnemoen  * Nuvoton NPCM7xx Timer Controller
3*85fdd74fSHavard Skinnemoen  *
4*85fdd74fSHavard Skinnemoen  * Copyright 2020 Google LLC
5*85fdd74fSHavard Skinnemoen  *
6*85fdd74fSHavard Skinnemoen  * This program is free software; you can redistribute it and/or modify it
7*85fdd74fSHavard Skinnemoen  * under the terms of the GNU General Public License as published by the
8*85fdd74fSHavard Skinnemoen  * Free Software Foundation; either version 2 of the License, or
9*85fdd74fSHavard Skinnemoen  * (at your option) any later version.
10*85fdd74fSHavard Skinnemoen  *
11*85fdd74fSHavard Skinnemoen  * This program is distributed in the hope that it will be useful, but WITHOUT
12*85fdd74fSHavard Skinnemoen  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13*85fdd74fSHavard Skinnemoen  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14*85fdd74fSHavard Skinnemoen  * for more details.
15*85fdd74fSHavard Skinnemoen  */
16*85fdd74fSHavard Skinnemoen 
17*85fdd74fSHavard Skinnemoen #include "qemu/osdep.h"
18*85fdd74fSHavard Skinnemoen 
19*85fdd74fSHavard Skinnemoen #include "hw/irq.h"
20*85fdd74fSHavard Skinnemoen #include "hw/misc/npcm7xx_clk.h"
21*85fdd74fSHavard Skinnemoen #include "hw/timer/npcm7xx_timer.h"
22*85fdd74fSHavard Skinnemoen #include "migration/vmstate.h"
23*85fdd74fSHavard Skinnemoen #include "qemu/bitops.h"
24*85fdd74fSHavard Skinnemoen #include "qemu/error-report.h"
25*85fdd74fSHavard Skinnemoen #include "qemu/log.h"
26*85fdd74fSHavard Skinnemoen #include "qemu/module.h"
27*85fdd74fSHavard Skinnemoen #include "qemu/timer.h"
28*85fdd74fSHavard Skinnemoen #include "qemu/units.h"
29*85fdd74fSHavard Skinnemoen #include "trace.h"
30*85fdd74fSHavard Skinnemoen 
31*85fdd74fSHavard Skinnemoen /* 32-bit register indices. */
32*85fdd74fSHavard Skinnemoen enum NPCM7xxTimerRegisters {
33*85fdd74fSHavard Skinnemoen     NPCM7XX_TIMER_TCSR0,
34*85fdd74fSHavard Skinnemoen     NPCM7XX_TIMER_TCSR1,
35*85fdd74fSHavard Skinnemoen     NPCM7XX_TIMER_TICR0,
36*85fdd74fSHavard Skinnemoen     NPCM7XX_TIMER_TICR1,
37*85fdd74fSHavard Skinnemoen     NPCM7XX_TIMER_TDR0,
38*85fdd74fSHavard Skinnemoen     NPCM7XX_TIMER_TDR1,
39*85fdd74fSHavard Skinnemoen     NPCM7XX_TIMER_TISR,
40*85fdd74fSHavard Skinnemoen     NPCM7XX_TIMER_WTCR,
41*85fdd74fSHavard Skinnemoen     NPCM7XX_TIMER_TCSR2,
42*85fdd74fSHavard Skinnemoen     NPCM7XX_TIMER_TCSR3,
43*85fdd74fSHavard Skinnemoen     NPCM7XX_TIMER_TICR2,
44*85fdd74fSHavard Skinnemoen     NPCM7XX_TIMER_TICR3,
45*85fdd74fSHavard Skinnemoen     NPCM7XX_TIMER_TDR2,
46*85fdd74fSHavard Skinnemoen     NPCM7XX_TIMER_TDR3,
47*85fdd74fSHavard Skinnemoen     NPCM7XX_TIMER_TCSR4         = 0x0040 / sizeof(uint32_t),
48*85fdd74fSHavard Skinnemoen     NPCM7XX_TIMER_TICR4         = 0x0048 / sizeof(uint32_t),
49*85fdd74fSHavard Skinnemoen     NPCM7XX_TIMER_TDR4          = 0x0050 / sizeof(uint32_t),
50*85fdd74fSHavard Skinnemoen     NPCM7XX_TIMER_REGS_END,
51*85fdd74fSHavard Skinnemoen };
52*85fdd74fSHavard Skinnemoen 
53*85fdd74fSHavard Skinnemoen /* Register field definitions. */
54*85fdd74fSHavard Skinnemoen #define NPCM7XX_TCSR_CEN                BIT(30)
55*85fdd74fSHavard Skinnemoen #define NPCM7XX_TCSR_IE                 BIT(29)
56*85fdd74fSHavard Skinnemoen #define NPCM7XX_TCSR_PERIODIC           BIT(27)
57*85fdd74fSHavard Skinnemoen #define NPCM7XX_TCSR_CRST               BIT(26)
58*85fdd74fSHavard Skinnemoen #define NPCM7XX_TCSR_CACT               BIT(25)
59*85fdd74fSHavard Skinnemoen #define NPCM7XX_TCSR_RSVD               0x01ffff00
60*85fdd74fSHavard Skinnemoen #define NPCM7XX_TCSR_PRESCALE_START     0
61*85fdd74fSHavard Skinnemoen #define NPCM7XX_TCSR_PRESCALE_LEN       8
62*85fdd74fSHavard Skinnemoen 
63*85fdd74fSHavard Skinnemoen /*
64*85fdd74fSHavard Skinnemoen  * Returns the index of timer in the tc->timer array. This can be used to
65*85fdd74fSHavard Skinnemoen  * locate the registers that belong to this timer.
66*85fdd74fSHavard Skinnemoen  */
67*85fdd74fSHavard Skinnemoen static int npcm7xx_timer_index(NPCM7xxTimerCtrlState *tc, NPCM7xxTimer *timer)
68*85fdd74fSHavard Skinnemoen {
69*85fdd74fSHavard Skinnemoen     int index = timer - tc->timer;
70*85fdd74fSHavard Skinnemoen 
71*85fdd74fSHavard Skinnemoen     g_assert(index >= 0 && index < NPCM7XX_TIMERS_PER_CTRL);
72*85fdd74fSHavard Skinnemoen 
73*85fdd74fSHavard Skinnemoen     return index;
74*85fdd74fSHavard Skinnemoen }
75*85fdd74fSHavard Skinnemoen 
76*85fdd74fSHavard Skinnemoen /* Return the value by which to divide the reference clock rate. */
77*85fdd74fSHavard Skinnemoen static uint32_t npcm7xx_tcsr_prescaler(uint32_t tcsr)
78*85fdd74fSHavard Skinnemoen {
79*85fdd74fSHavard Skinnemoen     return extract32(tcsr, NPCM7XX_TCSR_PRESCALE_START,
80*85fdd74fSHavard Skinnemoen                      NPCM7XX_TCSR_PRESCALE_LEN) + 1;
81*85fdd74fSHavard Skinnemoen }
82*85fdd74fSHavard Skinnemoen 
83*85fdd74fSHavard Skinnemoen /* Convert a timer cycle count to a time interval in nanoseconds. */
84*85fdd74fSHavard Skinnemoen static int64_t npcm7xx_timer_count_to_ns(NPCM7xxTimer *t, uint32_t count)
85*85fdd74fSHavard Skinnemoen {
86*85fdd74fSHavard Skinnemoen     int64_t ns = count;
87*85fdd74fSHavard Skinnemoen 
88*85fdd74fSHavard Skinnemoen     ns *= NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ;
89*85fdd74fSHavard Skinnemoen     ns *= npcm7xx_tcsr_prescaler(t->tcsr);
90*85fdd74fSHavard Skinnemoen 
91*85fdd74fSHavard Skinnemoen     return ns;
92*85fdd74fSHavard Skinnemoen }
93*85fdd74fSHavard Skinnemoen 
94*85fdd74fSHavard Skinnemoen /* Convert a time interval in nanoseconds to a timer cycle count. */
95*85fdd74fSHavard Skinnemoen static uint32_t npcm7xx_timer_ns_to_count(NPCM7xxTimer *t, int64_t ns)
96*85fdd74fSHavard Skinnemoen {
97*85fdd74fSHavard Skinnemoen     int64_t count;
98*85fdd74fSHavard Skinnemoen 
99*85fdd74fSHavard Skinnemoen     count = ns / (NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ);
100*85fdd74fSHavard Skinnemoen     count /= npcm7xx_tcsr_prescaler(t->tcsr);
101*85fdd74fSHavard Skinnemoen 
102*85fdd74fSHavard Skinnemoen     return count;
103*85fdd74fSHavard Skinnemoen }
104*85fdd74fSHavard Skinnemoen 
105*85fdd74fSHavard Skinnemoen /*
106*85fdd74fSHavard Skinnemoen  * Raise the interrupt line if there's a pending interrupt and interrupts are
107*85fdd74fSHavard Skinnemoen  * enabled for this timer. If not, lower it.
108*85fdd74fSHavard Skinnemoen  */
109*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_check_interrupt(NPCM7xxTimer *t)
110*85fdd74fSHavard Skinnemoen {
111*85fdd74fSHavard Skinnemoen     NPCM7xxTimerCtrlState *tc = t->ctrl;
112*85fdd74fSHavard Skinnemoen     int index = npcm7xx_timer_index(tc, t);
113*85fdd74fSHavard Skinnemoen     bool pending = (t->tcsr & NPCM7XX_TCSR_IE) && (tc->tisr & BIT(index));
114*85fdd74fSHavard Skinnemoen 
115*85fdd74fSHavard Skinnemoen     qemu_set_irq(t->irq, pending);
116*85fdd74fSHavard Skinnemoen     trace_npcm7xx_timer_irq(DEVICE(tc)->canonical_path, index, pending);
117*85fdd74fSHavard Skinnemoen }
118*85fdd74fSHavard Skinnemoen 
119*85fdd74fSHavard Skinnemoen /* Start or resume the timer. */
120*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_start(NPCM7xxTimer *t)
121*85fdd74fSHavard Skinnemoen {
122*85fdd74fSHavard Skinnemoen     int64_t now;
123*85fdd74fSHavard Skinnemoen 
124*85fdd74fSHavard Skinnemoen     now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
125*85fdd74fSHavard Skinnemoen     t->expires_ns = now + t->remaining_ns;
126*85fdd74fSHavard Skinnemoen     timer_mod(&t->qtimer, t->expires_ns);
127*85fdd74fSHavard Skinnemoen }
128*85fdd74fSHavard Skinnemoen 
129*85fdd74fSHavard Skinnemoen /*
130*85fdd74fSHavard Skinnemoen  * Called when the counter reaches zero. Sets the interrupt flag, and either
131*85fdd74fSHavard Skinnemoen  * restarts or disables the timer.
132*85fdd74fSHavard Skinnemoen  */
133*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_reached_zero(NPCM7xxTimer *t)
134*85fdd74fSHavard Skinnemoen {
135*85fdd74fSHavard Skinnemoen     NPCM7xxTimerCtrlState *tc = t->ctrl;
136*85fdd74fSHavard Skinnemoen     int index = npcm7xx_timer_index(tc, t);
137*85fdd74fSHavard Skinnemoen 
138*85fdd74fSHavard Skinnemoen     tc->tisr |= BIT(index);
139*85fdd74fSHavard Skinnemoen 
140*85fdd74fSHavard Skinnemoen     if (t->tcsr & NPCM7XX_TCSR_PERIODIC) {
141*85fdd74fSHavard Skinnemoen         t->remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr);
142*85fdd74fSHavard Skinnemoen         if (t->tcsr & NPCM7XX_TCSR_CEN) {
143*85fdd74fSHavard Skinnemoen             npcm7xx_timer_start(t);
144*85fdd74fSHavard Skinnemoen         }
145*85fdd74fSHavard Skinnemoen     } else {
146*85fdd74fSHavard Skinnemoen         t->tcsr &= ~(NPCM7XX_TCSR_CEN | NPCM7XX_TCSR_CACT);
147*85fdd74fSHavard Skinnemoen     }
148*85fdd74fSHavard Skinnemoen 
149*85fdd74fSHavard Skinnemoen     npcm7xx_timer_check_interrupt(t);
150*85fdd74fSHavard Skinnemoen }
151*85fdd74fSHavard Skinnemoen 
152*85fdd74fSHavard Skinnemoen /* Stop counting. Record the time remaining so we can continue later. */
153*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_pause(NPCM7xxTimer *t)
154*85fdd74fSHavard Skinnemoen {
155*85fdd74fSHavard Skinnemoen     int64_t now;
156*85fdd74fSHavard Skinnemoen 
157*85fdd74fSHavard Skinnemoen     timer_del(&t->qtimer);
158*85fdd74fSHavard Skinnemoen     now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
159*85fdd74fSHavard Skinnemoen     t->remaining_ns = t->expires_ns - now;
160*85fdd74fSHavard Skinnemoen     if (t->remaining_ns <= 0) {
161*85fdd74fSHavard Skinnemoen         npcm7xx_timer_reached_zero(t);
162*85fdd74fSHavard Skinnemoen     }
163*85fdd74fSHavard Skinnemoen }
164*85fdd74fSHavard Skinnemoen 
165*85fdd74fSHavard Skinnemoen /*
166*85fdd74fSHavard Skinnemoen  * Restart the timer from its initial value. If the timer was enabled and stays
167*85fdd74fSHavard Skinnemoen  * enabled, adjust the QEMU timer according to the new count. If the timer is
168*85fdd74fSHavard Skinnemoen  * transitioning from disabled to enabled, the caller is expected to start the
169*85fdd74fSHavard Skinnemoen  * timer later.
170*85fdd74fSHavard Skinnemoen  */
171*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_restart(NPCM7xxTimer *t, uint32_t old_tcsr)
172*85fdd74fSHavard Skinnemoen {
173*85fdd74fSHavard Skinnemoen     t->remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr);
174*85fdd74fSHavard Skinnemoen 
175*85fdd74fSHavard Skinnemoen     if (old_tcsr & t->tcsr & NPCM7XX_TCSR_CEN) {
176*85fdd74fSHavard Skinnemoen         npcm7xx_timer_start(t);
177*85fdd74fSHavard Skinnemoen     }
178*85fdd74fSHavard Skinnemoen }
179*85fdd74fSHavard Skinnemoen 
180*85fdd74fSHavard Skinnemoen /* Register read and write handlers */
181*85fdd74fSHavard Skinnemoen 
182*85fdd74fSHavard Skinnemoen static uint32_t npcm7xx_timer_read_tdr(NPCM7xxTimer *t)
183*85fdd74fSHavard Skinnemoen {
184*85fdd74fSHavard Skinnemoen     if (t->tcsr & NPCM7XX_TCSR_CEN) {
185*85fdd74fSHavard Skinnemoen         int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
186*85fdd74fSHavard Skinnemoen 
187*85fdd74fSHavard Skinnemoen         return npcm7xx_timer_ns_to_count(t, t->expires_ns - now);
188*85fdd74fSHavard Skinnemoen     }
189*85fdd74fSHavard Skinnemoen 
190*85fdd74fSHavard Skinnemoen     return npcm7xx_timer_ns_to_count(t, t->remaining_ns);
191*85fdd74fSHavard Skinnemoen }
192*85fdd74fSHavard Skinnemoen 
193*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_write_tcsr(NPCM7xxTimer *t, uint32_t new_tcsr)
194*85fdd74fSHavard Skinnemoen {
195*85fdd74fSHavard Skinnemoen     uint32_t old_tcsr = t->tcsr;
196*85fdd74fSHavard Skinnemoen     uint32_t tdr;
197*85fdd74fSHavard Skinnemoen 
198*85fdd74fSHavard Skinnemoen     if (new_tcsr & NPCM7XX_TCSR_RSVD) {
199*85fdd74fSHavard Skinnemoen         qemu_log_mask(LOG_GUEST_ERROR, "%s: reserved bits in 0x%08x ignored\n",
200*85fdd74fSHavard Skinnemoen                       __func__, new_tcsr);
201*85fdd74fSHavard Skinnemoen         new_tcsr &= ~NPCM7XX_TCSR_RSVD;
202*85fdd74fSHavard Skinnemoen     }
203*85fdd74fSHavard Skinnemoen     if (new_tcsr & NPCM7XX_TCSR_CACT) {
204*85fdd74fSHavard Skinnemoen         qemu_log_mask(LOG_GUEST_ERROR, "%s: read-only bits in 0x%08x ignored\n",
205*85fdd74fSHavard Skinnemoen                       __func__, new_tcsr);
206*85fdd74fSHavard Skinnemoen         new_tcsr &= ~NPCM7XX_TCSR_CACT;
207*85fdd74fSHavard Skinnemoen     }
208*85fdd74fSHavard Skinnemoen     if ((new_tcsr & NPCM7XX_TCSR_CRST) && (new_tcsr & NPCM7XX_TCSR_CEN)) {
209*85fdd74fSHavard Skinnemoen         qemu_log_mask(LOG_GUEST_ERROR,
210*85fdd74fSHavard Skinnemoen                       "%s: both CRST and CEN set; ignoring CEN.\n",
211*85fdd74fSHavard Skinnemoen                       __func__);
212*85fdd74fSHavard Skinnemoen         new_tcsr &= ~NPCM7XX_TCSR_CEN;
213*85fdd74fSHavard Skinnemoen     }
214*85fdd74fSHavard Skinnemoen 
215*85fdd74fSHavard Skinnemoen     /* Calculate the value of TDR before potentially changing the prescaler. */
216*85fdd74fSHavard Skinnemoen     tdr = npcm7xx_timer_read_tdr(t);
217*85fdd74fSHavard Skinnemoen 
218*85fdd74fSHavard Skinnemoen     t->tcsr = (t->tcsr & NPCM7XX_TCSR_CACT) | new_tcsr;
219*85fdd74fSHavard Skinnemoen 
220*85fdd74fSHavard Skinnemoen     if (npcm7xx_tcsr_prescaler(old_tcsr) != npcm7xx_tcsr_prescaler(new_tcsr)) {
221*85fdd74fSHavard Skinnemoen         /* Recalculate time remaining based on the current TDR value. */
222*85fdd74fSHavard Skinnemoen         t->remaining_ns = npcm7xx_timer_count_to_ns(t, tdr);
223*85fdd74fSHavard Skinnemoen         if (old_tcsr & t->tcsr & NPCM7XX_TCSR_CEN) {
224*85fdd74fSHavard Skinnemoen             npcm7xx_timer_start(t);
225*85fdd74fSHavard Skinnemoen         }
226*85fdd74fSHavard Skinnemoen     }
227*85fdd74fSHavard Skinnemoen 
228*85fdd74fSHavard Skinnemoen     if ((old_tcsr ^ new_tcsr) & NPCM7XX_TCSR_IE) {
229*85fdd74fSHavard Skinnemoen         npcm7xx_timer_check_interrupt(t);
230*85fdd74fSHavard Skinnemoen     }
231*85fdd74fSHavard Skinnemoen     if (new_tcsr & NPCM7XX_TCSR_CRST) {
232*85fdd74fSHavard Skinnemoen         npcm7xx_timer_restart(t, old_tcsr);
233*85fdd74fSHavard Skinnemoen         t->tcsr &= ~NPCM7XX_TCSR_CRST;
234*85fdd74fSHavard Skinnemoen     }
235*85fdd74fSHavard Skinnemoen     if ((old_tcsr ^ new_tcsr) & NPCM7XX_TCSR_CEN) {
236*85fdd74fSHavard Skinnemoen         if (new_tcsr & NPCM7XX_TCSR_CEN) {
237*85fdd74fSHavard Skinnemoen             t->tcsr |= NPCM7XX_TCSR_CACT;
238*85fdd74fSHavard Skinnemoen             npcm7xx_timer_start(t);
239*85fdd74fSHavard Skinnemoen         } else {
240*85fdd74fSHavard Skinnemoen             t->tcsr &= ~NPCM7XX_TCSR_CACT;
241*85fdd74fSHavard Skinnemoen             npcm7xx_timer_pause(t);
242*85fdd74fSHavard Skinnemoen         }
243*85fdd74fSHavard Skinnemoen     }
244*85fdd74fSHavard Skinnemoen }
245*85fdd74fSHavard Skinnemoen 
246*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_write_ticr(NPCM7xxTimer *t, uint32_t new_ticr)
247*85fdd74fSHavard Skinnemoen {
248*85fdd74fSHavard Skinnemoen     t->ticr = new_ticr;
249*85fdd74fSHavard Skinnemoen 
250*85fdd74fSHavard Skinnemoen     npcm7xx_timer_restart(t, t->tcsr);
251*85fdd74fSHavard Skinnemoen }
252*85fdd74fSHavard Skinnemoen 
253*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_write_tisr(NPCM7xxTimerCtrlState *s, uint32_t value)
254*85fdd74fSHavard Skinnemoen {
255*85fdd74fSHavard Skinnemoen     int i;
256*85fdd74fSHavard Skinnemoen 
257*85fdd74fSHavard Skinnemoen     s->tisr &= ~value;
258*85fdd74fSHavard Skinnemoen     for (i = 0; i < ARRAY_SIZE(s->timer); i++) {
259*85fdd74fSHavard Skinnemoen         if (value & (1U << i)) {
260*85fdd74fSHavard Skinnemoen             npcm7xx_timer_check_interrupt(&s->timer[i]);
261*85fdd74fSHavard Skinnemoen         }
262*85fdd74fSHavard Skinnemoen     }
263*85fdd74fSHavard Skinnemoen }
264*85fdd74fSHavard Skinnemoen 
265*85fdd74fSHavard Skinnemoen static hwaddr npcm7xx_tcsr_index(hwaddr reg)
266*85fdd74fSHavard Skinnemoen {
267*85fdd74fSHavard Skinnemoen     switch (reg) {
268*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TCSR0:
269*85fdd74fSHavard Skinnemoen         return 0;
270*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TCSR1:
271*85fdd74fSHavard Skinnemoen         return 1;
272*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TCSR2:
273*85fdd74fSHavard Skinnemoen         return 2;
274*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TCSR3:
275*85fdd74fSHavard Skinnemoen         return 3;
276*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TCSR4:
277*85fdd74fSHavard Skinnemoen         return 4;
278*85fdd74fSHavard Skinnemoen     default:
279*85fdd74fSHavard Skinnemoen         g_assert_not_reached();
280*85fdd74fSHavard Skinnemoen     }
281*85fdd74fSHavard Skinnemoen }
282*85fdd74fSHavard Skinnemoen 
283*85fdd74fSHavard Skinnemoen static hwaddr npcm7xx_ticr_index(hwaddr reg)
284*85fdd74fSHavard Skinnemoen {
285*85fdd74fSHavard Skinnemoen     switch (reg) {
286*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TICR0:
287*85fdd74fSHavard Skinnemoen         return 0;
288*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TICR1:
289*85fdd74fSHavard Skinnemoen         return 1;
290*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TICR2:
291*85fdd74fSHavard Skinnemoen         return 2;
292*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TICR3:
293*85fdd74fSHavard Skinnemoen         return 3;
294*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TICR4:
295*85fdd74fSHavard Skinnemoen         return 4;
296*85fdd74fSHavard Skinnemoen     default:
297*85fdd74fSHavard Skinnemoen         g_assert_not_reached();
298*85fdd74fSHavard Skinnemoen     }
299*85fdd74fSHavard Skinnemoen }
300*85fdd74fSHavard Skinnemoen 
301*85fdd74fSHavard Skinnemoen static hwaddr npcm7xx_tdr_index(hwaddr reg)
302*85fdd74fSHavard Skinnemoen {
303*85fdd74fSHavard Skinnemoen     switch (reg) {
304*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TDR0:
305*85fdd74fSHavard Skinnemoen         return 0;
306*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TDR1:
307*85fdd74fSHavard Skinnemoen         return 1;
308*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TDR2:
309*85fdd74fSHavard Skinnemoen         return 2;
310*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TDR3:
311*85fdd74fSHavard Skinnemoen         return 3;
312*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TDR4:
313*85fdd74fSHavard Skinnemoen         return 4;
314*85fdd74fSHavard Skinnemoen     default:
315*85fdd74fSHavard Skinnemoen         g_assert_not_reached();
316*85fdd74fSHavard Skinnemoen     }
317*85fdd74fSHavard Skinnemoen }
318*85fdd74fSHavard Skinnemoen 
319*85fdd74fSHavard Skinnemoen static uint64_t npcm7xx_timer_read(void *opaque, hwaddr offset, unsigned size)
320*85fdd74fSHavard Skinnemoen {
321*85fdd74fSHavard Skinnemoen     NPCM7xxTimerCtrlState *s = opaque;
322*85fdd74fSHavard Skinnemoen     uint64_t value = 0;
323*85fdd74fSHavard Skinnemoen     hwaddr reg;
324*85fdd74fSHavard Skinnemoen 
325*85fdd74fSHavard Skinnemoen     reg = offset / sizeof(uint32_t);
326*85fdd74fSHavard Skinnemoen     switch (reg) {
327*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TCSR0:
328*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TCSR1:
329*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TCSR2:
330*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TCSR3:
331*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TCSR4:
332*85fdd74fSHavard Skinnemoen         value = s->timer[npcm7xx_tcsr_index(reg)].tcsr;
333*85fdd74fSHavard Skinnemoen         break;
334*85fdd74fSHavard Skinnemoen 
335*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TICR0:
336*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TICR1:
337*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TICR2:
338*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TICR3:
339*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TICR4:
340*85fdd74fSHavard Skinnemoen         value = s->timer[npcm7xx_ticr_index(reg)].ticr;
341*85fdd74fSHavard Skinnemoen         break;
342*85fdd74fSHavard Skinnemoen 
343*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TDR0:
344*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TDR1:
345*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TDR2:
346*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TDR3:
347*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TDR4:
348*85fdd74fSHavard Skinnemoen         value = npcm7xx_timer_read_tdr(&s->timer[npcm7xx_tdr_index(reg)]);
349*85fdd74fSHavard Skinnemoen         break;
350*85fdd74fSHavard Skinnemoen 
351*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TISR:
352*85fdd74fSHavard Skinnemoen         value = s->tisr;
353*85fdd74fSHavard Skinnemoen         break;
354*85fdd74fSHavard Skinnemoen 
355*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_WTCR:
356*85fdd74fSHavard Skinnemoen         value = s->wtcr;
357*85fdd74fSHavard Skinnemoen         break;
358*85fdd74fSHavard Skinnemoen 
359*85fdd74fSHavard Skinnemoen     default:
360*85fdd74fSHavard Skinnemoen         qemu_log_mask(LOG_GUEST_ERROR,
361*85fdd74fSHavard Skinnemoen                       "%s: invalid offset 0x%04" HWADDR_PRIx "\n",
362*85fdd74fSHavard Skinnemoen                       __func__, offset);
363*85fdd74fSHavard Skinnemoen         break;
364*85fdd74fSHavard Skinnemoen     }
365*85fdd74fSHavard Skinnemoen 
366*85fdd74fSHavard Skinnemoen     trace_npcm7xx_timer_read(DEVICE(s)->canonical_path, offset, value);
367*85fdd74fSHavard Skinnemoen 
368*85fdd74fSHavard Skinnemoen     return value;
369*85fdd74fSHavard Skinnemoen }
370*85fdd74fSHavard Skinnemoen 
371*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_write(void *opaque, hwaddr offset,
372*85fdd74fSHavard Skinnemoen                                 uint64_t v, unsigned size)
373*85fdd74fSHavard Skinnemoen {
374*85fdd74fSHavard Skinnemoen     uint32_t reg = offset / sizeof(uint32_t);
375*85fdd74fSHavard Skinnemoen     NPCM7xxTimerCtrlState *s = opaque;
376*85fdd74fSHavard Skinnemoen     uint32_t value = v;
377*85fdd74fSHavard Skinnemoen 
378*85fdd74fSHavard Skinnemoen     trace_npcm7xx_timer_write(DEVICE(s)->canonical_path, offset, value);
379*85fdd74fSHavard Skinnemoen 
380*85fdd74fSHavard Skinnemoen     switch (reg) {
381*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TCSR0:
382*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TCSR1:
383*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TCSR2:
384*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TCSR3:
385*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TCSR4:
386*85fdd74fSHavard Skinnemoen         npcm7xx_timer_write_tcsr(&s->timer[npcm7xx_tcsr_index(reg)], value);
387*85fdd74fSHavard Skinnemoen         return;
388*85fdd74fSHavard Skinnemoen 
389*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TICR0:
390*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TICR1:
391*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TICR2:
392*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TICR3:
393*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TICR4:
394*85fdd74fSHavard Skinnemoen         npcm7xx_timer_write_ticr(&s->timer[npcm7xx_ticr_index(reg)], value);
395*85fdd74fSHavard Skinnemoen         return;
396*85fdd74fSHavard Skinnemoen 
397*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TDR0:
398*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TDR1:
399*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TDR2:
400*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TDR3:
401*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TDR4:
402*85fdd74fSHavard Skinnemoen         qemu_log_mask(LOG_GUEST_ERROR,
403*85fdd74fSHavard Skinnemoen                       "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n",
404*85fdd74fSHavard Skinnemoen                       __func__, offset);
405*85fdd74fSHavard Skinnemoen         return;
406*85fdd74fSHavard Skinnemoen 
407*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_TISR:
408*85fdd74fSHavard Skinnemoen         npcm7xx_timer_write_tisr(s, value);
409*85fdd74fSHavard Skinnemoen         return;
410*85fdd74fSHavard Skinnemoen 
411*85fdd74fSHavard Skinnemoen     case NPCM7XX_TIMER_WTCR:
412*85fdd74fSHavard Skinnemoen         qemu_log_mask(LOG_UNIMP, "%s: WTCR write not implemented: 0x%08x\n",
413*85fdd74fSHavard Skinnemoen                       __func__, value);
414*85fdd74fSHavard Skinnemoen         return;
415*85fdd74fSHavard Skinnemoen     }
416*85fdd74fSHavard Skinnemoen 
417*85fdd74fSHavard Skinnemoen     qemu_log_mask(LOG_GUEST_ERROR,
418*85fdd74fSHavard Skinnemoen                   "%s: invalid offset 0x%04" HWADDR_PRIx "\n",
419*85fdd74fSHavard Skinnemoen                   __func__, offset);
420*85fdd74fSHavard Skinnemoen }
421*85fdd74fSHavard Skinnemoen 
422*85fdd74fSHavard Skinnemoen static const struct MemoryRegionOps npcm7xx_timer_ops = {
423*85fdd74fSHavard Skinnemoen     .read       = npcm7xx_timer_read,
424*85fdd74fSHavard Skinnemoen     .write      = npcm7xx_timer_write,
425*85fdd74fSHavard Skinnemoen     .endianness = DEVICE_LITTLE_ENDIAN,
426*85fdd74fSHavard Skinnemoen     .valid      = {
427*85fdd74fSHavard Skinnemoen         .min_access_size        = 4,
428*85fdd74fSHavard Skinnemoen         .max_access_size        = 4,
429*85fdd74fSHavard Skinnemoen         .unaligned              = false,
430*85fdd74fSHavard Skinnemoen     },
431*85fdd74fSHavard Skinnemoen };
432*85fdd74fSHavard Skinnemoen 
433*85fdd74fSHavard Skinnemoen /* Called when the QEMU timer expires. */
434*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_expired(void *opaque)
435*85fdd74fSHavard Skinnemoen {
436*85fdd74fSHavard Skinnemoen     NPCM7xxTimer *t = opaque;
437*85fdd74fSHavard Skinnemoen 
438*85fdd74fSHavard Skinnemoen     if (t->tcsr & NPCM7XX_TCSR_CEN) {
439*85fdd74fSHavard Skinnemoen         npcm7xx_timer_reached_zero(t);
440*85fdd74fSHavard Skinnemoen     }
441*85fdd74fSHavard Skinnemoen }
442*85fdd74fSHavard Skinnemoen 
443*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_enter_reset(Object *obj, ResetType type)
444*85fdd74fSHavard Skinnemoen {
445*85fdd74fSHavard Skinnemoen     NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(obj);
446*85fdd74fSHavard Skinnemoen     int i;
447*85fdd74fSHavard Skinnemoen 
448*85fdd74fSHavard Skinnemoen     for (i = 0; i < NPCM7XX_TIMERS_PER_CTRL; i++) {
449*85fdd74fSHavard Skinnemoen         NPCM7xxTimer *t = &s->timer[i];
450*85fdd74fSHavard Skinnemoen 
451*85fdd74fSHavard Skinnemoen         timer_del(&t->qtimer);
452*85fdd74fSHavard Skinnemoen         t->expires_ns = 0;
453*85fdd74fSHavard Skinnemoen         t->remaining_ns = 0;
454*85fdd74fSHavard Skinnemoen         t->tcsr = 0x00000005;
455*85fdd74fSHavard Skinnemoen         t->ticr = 0x00000000;
456*85fdd74fSHavard Skinnemoen     }
457*85fdd74fSHavard Skinnemoen 
458*85fdd74fSHavard Skinnemoen     s->tisr = 0x00000000;
459*85fdd74fSHavard Skinnemoen     s->wtcr = 0x00000400;
460*85fdd74fSHavard Skinnemoen }
461*85fdd74fSHavard Skinnemoen 
462*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_hold_reset(Object *obj)
463*85fdd74fSHavard Skinnemoen {
464*85fdd74fSHavard Skinnemoen     NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(obj);
465*85fdd74fSHavard Skinnemoen     int i;
466*85fdd74fSHavard Skinnemoen 
467*85fdd74fSHavard Skinnemoen     for (i = 0; i < NPCM7XX_TIMERS_PER_CTRL; i++) {
468*85fdd74fSHavard Skinnemoen         qemu_irq_lower(s->timer[i].irq);
469*85fdd74fSHavard Skinnemoen     }
470*85fdd74fSHavard Skinnemoen }
471*85fdd74fSHavard Skinnemoen 
472*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_realize(DeviceState *dev, Error **errp)
473*85fdd74fSHavard Skinnemoen {
474*85fdd74fSHavard Skinnemoen     NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(dev);
475*85fdd74fSHavard Skinnemoen     SysBusDevice *sbd = &s->parent;
476*85fdd74fSHavard Skinnemoen     int i;
477*85fdd74fSHavard Skinnemoen 
478*85fdd74fSHavard Skinnemoen     for (i = 0; i < NPCM7XX_TIMERS_PER_CTRL; i++) {
479*85fdd74fSHavard Skinnemoen         NPCM7xxTimer *t = &s->timer[i];
480*85fdd74fSHavard Skinnemoen         t->ctrl = s;
481*85fdd74fSHavard Skinnemoen         timer_init_ns(&t->qtimer, QEMU_CLOCK_VIRTUAL, npcm7xx_timer_expired, t);
482*85fdd74fSHavard Skinnemoen         sysbus_init_irq(sbd, &t->irq);
483*85fdd74fSHavard Skinnemoen     }
484*85fdd74fSHavard Skinnemoen 
485*85fdd74fSHavard Skinnemoen     memory_region_init_io(&s->iomem, OBJECT(s), &npcm7xx_timer_ops, s,
486*85fdd74fSHavard Skinnemoen                           TYPE_NPCM7XX_TIMER, 4 * KiB);
487*85fdd74fSHavard Skinnemoen     sysbus_init_mmio(sbd, &s->iomem);
488*85fdd74fSHavard Skinnemoen }
489*85fdd74fSHavard Skinnemoen 
490*85fdd74fSHavard Skinnemoen static const VMStateDescription vmstate_npcm7xx_timer = {
491*85fdd74fSHavard Skinnemoen     .name = "npcm7xx-timer",
492*85fdd74fSHavard Skinnemoen     .version_id = 0,
493*85fdd74fSHavard Skinnemoen     .minimum_version_id = 0,
494*85fdd74fSHavard Skinnemoen     .fields = (VMStateField[]) {
495*85fdd74fSHavard Skinnemoen         VMSTATE_TIMER(qtimer, NPCM7xxTimer),
496*85fdd74fSHavard Skinnemoen         VMSTATE_INT64(expires_ns, NPCM7xxTimer),
497*85fdd74fSHavard Skinnemoen         VMSTATE_INT64(remaining_ns, NPCM7xxTimer),
498*85fdd74fSHavard Skinnemoen         VMSTATE_UINT32(tcsr, NPCM7xxTimer),
499*85fdd74fSHavard Skinnemoen         VMSTATE_UINT32(ticr, NPCM7xxTimer),
500*85fdd74fSHavard Skinnemoen         VMSTATE_END_OF_LIST(),
501*85fdd74fSHavard Skinnemoen     },
502*85fdd74fSHavard Skinnemoen };
503*85fdd74fSHavard Skinnemoen 
504*85fdd74fSHavard Skinnemoen static const VMStateDescription vmstate_npcm7xx_timer_ctrl = {
505*85fdd74fSHavard Skinnemoen     .name = "npcm7xx-timer-ctrl",
506*85fdd74fSHavard Skinnemoen     .version_id = 0,
507*85fdd74fSHavard Skinnemoen     .minimum_version_id = 0,
508*85fdd74fSHavard Skinnemoen     .fields = (VMStateField[]) {
509*85fdd74fSHavard Skinnemoen         VMSTATE_UINT32(tisr, NPCM7xxTimerCtrlState),
510*85fdd74fSHavard Skinnemoen         VMSTATE_UINT32(wtcr, NPCM7xxTimerCtrlState),
511*85fdd74fSHavard Skinnemoen         VMSTATE_STRUCT_ARRAY(timer, NPCM7xxTimerCtrlState,
512*85fdd74fSHavard Skinnemoen                              NPCM7XX_TIMERS_PER_CTRL, 0, vmstate_npcm7xx_timer,
513*85fdd74fSHavard Skinnemoen                              NPCM7xxTimer),
514*85fdd74fSHavard Skinnemoen         VMSTATE_END_OF_LIST(),
515*85fdd74fSHavard Skinnemoen     },
516*85fdd74fSHavard Skinnemoen };
517*85fdd74fSHavard Skinnemoen 
518*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_class_init(ObjectClass *klass, void *data)
519*85fdd74fSHavard Skinnemoen {
520*85fdd74fSHavard Skinnemoen     ResettableClass *rc = RESETTABLE_CLASS(klass);
521*85fdd74fSHavard Skinnemoen     DeviceClass *dc = DEVICE_CLASS(klass);
522*85fdd74fSHavard Skinnemoen 
523*85fdd74fSHavard Skinnemoen     QEMU_BUILD_BUG_ON(NPCM7XX_TIMER_REGS_END > NPCM7XX_TIMER_NR_REGS);
524*85fdd74fSHavard Skinnemoen 
525*85fdd74fSHavard Skinnemoen     dc->desc = "NPCM7xx Timer Controller";
526*85fdd74fSHavard Skinnemoen     dc->realize = npcm7xx_timer_realize;
527*85fdd74fSHavard Skinnemoen     dc->vmsd = &vmstate_npcm7xx_timer_ctrl;
528*85fdd74fSHavard Skinnemoen     rc->phases.enter = npcm7xx_timer_enter_reset;
529*85fdd74fSHavard Skinnemoen     rc->phases.hold = npcm7xx_timer_hold_reset;
530*85fdd74fSHavard Skinnemoen }
531*85fdd74fSHavard Skinnemoen 
532*85fdd74fSHavard Skinnemoen static const TypeInfo npcm7xx_timer_info = {
533*85fdd74fSHavard Skinnemoen     .name               = TYPE_NPCM7XX_TIMER,
534*85fdd74fSHavard Skinnemoen     .parent             = TYPE_SYS_BUS_DEVICE,
535*85fdd74fSHavard Skinnemoen     .instance_size      = sizeof(NPCM7xxTimerCtrlState),
536*85fdd74fSHavard Skinnemoen     .class_init         = npcm7xx_timer_class_init,
537*85fdd74fSHavard Skinnemoen };
538*85fdd74fSHavard Skinnemoen 
539*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_register_type(void)
540*85fdd74fSHavard Skinnemoen {
541*85fdd74fSHavard Skinnemoen     type_register_static(&npcm7xx_timer_info);
542*85fdd74fSHavard Skinnemoen }
543*85fdd74fSHavard Skinnemoen type_init(npcm7xx_timer_register_type);
544