xref: /openbmc/qemu/hw/timer/mss-timer.c (revision 96401bad)
1*96401badSSubbaraya Sundeep /*
2*96401badSSubbaraya Sundeep  * Block model of System timer present in
3*96401badSSubbaraya Sundeep  * Microsemi's SmartFusion2 and SmartFusion SoCs.
4*96401badSSubbaraya Sundeep  *
5*96401badSSubbaraya Sundeep  * Copyright (c) 2017 Subbaraya Sundeep <sundeep.lkml@gmail.com>.
6*96401badSSubbaraya Sundeep  *
7*96401badSSubbaraya Sundeep  * Permission is hereby granted, free of charge, to any person obtaining a copy
8*96401badSSubbaraya Sundeep  * of this software and associated documentation files (the "Software"), to deal
9*96401badSSubbaraya Sundeep  * in the Software without restriction, including without limitation the rights
10*96401badSSubbaraya Sundeep  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11*96401badSSubbaraya Sundeep  * copies of the Software, and to permit persons to whom the Software is
12*96401badSSubbaraya Sundeep  * furnished to do so, subject to the following conditions:
13*96401badSSubbaraya Sundeep  *
14*96401badSSubbaraya Sundeep  * The above copyright notice and this permission notice shall be included in
15*96401badSSubbaraya Sundeep  * all copies or substantial portions of the Software.
16*96401badSSubbaraya Sundeep  *
17*96401badSSubbaraya Sundeep  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18*96401badSSubbaraya Sundeep  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19*96401badSSubbaraya Sundeep  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20*96401badSSubbaraya Sundeep  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21*96401badSSubbaraya Sundeep  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22*96401badSSubbaraya Sundeep  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23*96401badSSubbaraya Sundeep  * THE SOFTWARE.
24*96401badSSubbaraya Sundeep  */
25*96401badSSubbaraya Sundeep 
26*96401badSSubbaraya Sundeep #include "qemu/osdep.h"
27*96401badSSubbaraya Sundeep #include "qemu/main-loop.h"
28*96401badSSubbaraya Sundeep #include "qemu/log.h"
29*96401badSSubbaraya Sundeep #include "hw/timer/mss-timer.h"
30*96401badSSubbaraya Sundeep 
31*96401badSSubbaraya Sundeep #ifndef MSS_TIMER_ERR_DEBUG
32*96401badSSubbaraya Sundeep #define MSS_TIMER_ERR_DEBUG  0
33*96401badSSubbaraya Sundeep #endif
34*96401badSSubbaraya Sundeep 
35*96401badSSubbaraya Sundeep #define DB_PRINT_L(lvl, fmt, args...) do { \
36*96401badSSubbaraya Sundeep     if (MSS_TIMER_ERR_DEBUG >= lvl) { \
37*96401badSSubbaraya Sundeep         qemu_log("%s: " fmt "\n", __func__, ## args); \
38*96401badSSubbaraya Sundeep     } \
39*96401badSSubbaraya Sundeep } while (0);
40*96401badSSubbaraya Sundeep 
41*96401badSSubbaraya Sundeep #define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args)
42*96401badSSubbaraya Sundeep 
43*96401badSSubbaraya Sundeep #define R_TIM_VAL         0
44*96401badSSubbaraya Sundeep #define R_TIM_LOADVAL     1
45*96401badSSubbaraya Sundeep #define R_TIM_BGLOADVAL   2
46*96401badSSubbaraya Sundeep #define R_TIM_CTRL        3
47*96401badSSubbaraya Sundeep #define R_TIM_RIS         4
48*96401badSSubbaraya Sundeep #define R_TIM_MIS         5
49*96401badSSubbaraya Sundeep 
50*96401badSSubbaraya Sundeep #define TIMER_CTRL_ENBL     (1 << 0)
51*96401badSSubbaraya Sundeep #define TIMER_CTRL_ONESHOT  (1 << 1)
52*96401badSSubbaraya Sundeep #define TIMER_CTRL_INTR     (1 << 2)
53*96401badSSubbaraya Sundeep #define TIMER_RIS_ACK       (1 << 0)
54*96401badSSubbaraya Sundeep #define TIMER_RST_CLR       (1 << 6)
55*96401badSSubbaraya Sundeep #define TIMER_MODE          (1 << 0)
56*96401badSSubbaraya Sundeep 
57*96401badSSubbaraya Sundeep static void timer_update_irq(struct Msf2Timer *st)
58*96401badSSubbaraya Sundeep {
59*96401badSSubbaraya Sundeep     bool isr, ier;
60*96401badSSubbaraya Sundeep 
61*96401badSSubbaraya Sundeep     isr = !!(st->regs[R_TIM_RIS] & TIMER_RIS_ACK);
62*96401badSSubbaraya Sundeep     ier = !!(st->regs[R_TIM_CTRL] & TIMER_CTRL_INTR);
63*96401badSSubbaraya Sundeep     qemu_set_irq(st->irq, (ier && isr));
64*96401badSSubbaraya Sundeep }
65*96401badSSubbaraya Sundeep 
66*96401badSSubbaraya Sundeep static void timer_update(struct Msf2Timer *st)
67*96401badSSubbaraya Sundeep {
68*96401badSSubbaraya Sundeep     uint64_t count;
69*96401badSSubbaraya Sundeep 
70*96401badSSubbaraya Sundeep     if (!(st->regs[R_TIM_CTRL] & TIMER_CTRL_ENBL)) {
71*96401badSSubbaraya Sundeep         ptimer_stop(st->ptimer);
72*96401badSSubbaraya Sundeep         return;
73*96401badSSubbaraya Sundeep     }
74*96401badSSubbaraya Sundeep 
75*96401badSSubbaraya Sundeep     count = st->regs[R_TIM_LOADVAL];
76*96401badSSubbaraya Sundeep     ptimer_set_limit(st->ptimer, count, 1);
77*96401badSSubbaraya Sundeep     ptimer_run(st->ptimer, 1);
78*96401badSSubbaraya Sundeep }
79*96401badSSubbaraya Sundeep 
80*96401badSSubbaraya Sundeep static uint64_t
81*96401badSSubbaraya Sundeep timer_read(void *opaque, hwaddr offset, unsigned int size)
82*96401badSSubbaraya Sundeep {
83*96401badSSubbaraya Sundeep     MSSTimerState *t = opaque;
84*96401badSSubbaraya Sundeep     hwaddr addr;
85*96401badSSubbaraya Sundeep     struct Msf2Timer *st;
86*96401badSSubbaraya Sundeep     uint32_t ret = 0;
87*96401badSSubbaraya Sundeep     int timer = 0;
88*96401badSSubbaraya Sundeep     int isr;
89*96401badSSubbaraya Sundeep     int ier;
90*96401badSSubbaraya Sundeep 
91*96401badSSubbaraya Sundeep     addr = offset >> 2;
92*96401badSSubbaraya Sundeep     /*
93*96401badSSubbaraya Sundeep      * Two independent timers has same base address.
94*96401badSSubbaraya Sundeep      * Based on address passed figure out which timer is being used.
95*96401badSSubbaraya Sundeep      */
96*96401badSSubbaraya Sundeep     if ((addr >= R_TIM1_MAX) && (addr < NUM_TIMERS * R_TIM1_MAX)) {
97*96401badSSubbaraya Sundeep         timer = 1;
98*96401badSSubbaraya Sundeep         addr -= R_TIM1_MAX;
99*96401badSSubbaraya Sundeep     }
100*96401badSSubbaraya Sundeep 
101*96401badSSubbaraya Sundeep     st = &t->timers[timer];
102*96401badSSubbaraya Sundeep 
103*96401badSSubbaraya Sundeep     switch (addr) {
104*96401badSSubbaraya Sundeep     case R_TIM_VAL:
105*96401badSSubbaraya Sundeep         ret = ptimer_get_count(st->ptimer);
106*96401badSSubbaraya Sundeep         break;
107*96401badSSubbaraya Sundeep 
108*96401badSSubbaraya Sundeep     case R_TIM_MIS:
109*96401badSSubbaraya Sundeep         isr = !!(st->regs[R_TIM_RIS] & TIMER_RIS_ACK);
110*96401badSSubbaraya Sundeep         ier = !!(st->regs[R_TIM_CTRL] & TIMER_CTRL_INTR);
111*96401badSSubbaraya Sundeep         ret = ier & isr;
112*96401badSSubbaraya Sundeep         break;
113*96401badSSubbaraya Sundeep 
114*96401badSSubbaraya Sundeep     default:
115*96401badSSubbaraya Sundeep         if (addr < R_TIM1_MAX) {
116*96401badSSubbaraya Sundeep             ret = st->regs[addr];
117*96401badSSubbaraya Sundeep         } else {
118*96401badSSubbaraya Sundeep             qemu_log_mask(LOG_GUEST_ERROR,
119*96401badSSubbaraya Sundeep                         TYPE_MSS_TIMER": 64-bit mode not supported\n");
120*96401badSSubbaraya Sundeep             return ret;
121*96401badSSubbaraya Sundeep         }
122*96401badSSubbaraya Sundeep         break;
123*96401badSSubbaraya Sundeep     }
124*96401badSSubbaraya Sundeep 
125*96401badSSubbaraya Sundeep     DB_PRINT("timer=%d 0x%" HWADDR_PRIx "=0x%" PRIx32, timer, offset,
126*96401badSSubbaraya Sundeep             ret);
127*96401badSSubbaraya Sundeep     return ret;
128*96401badSSubbaraya Sundeep }
129*96401badSSubbaraya Sundeep 
130*96401badSSubbaraya Sundeep static void
131*96401badSSubbaraya Sundeep timer_write(void *opaque, hwaddr offset,
132*96401badSSubbaraya Sundeep             uint64_t val64, unsigned int size)
133*96401badSSubbaraya Sundeep {
134*96401badSSubbaraya Sundeep     MSSTimerState *t = opaque;
135*96401badSSubbaraya Sundeep     hwaddr addr;
136*96401badSSubbaraya Sundeep     struct Msf2Timer *st;
137*96401badSSubbaraya Sundeep     int timer = 0;
138*96401badSSubbaraya Sundeep     uint32_t value = val64;
139*96401badSSubbaraya Sundeep 
140*96401badSSubbaraya Sundeep     addr = offset >> 2;
141*96401badSSubbaraya Sundeep     /*
142*96401badSSubbaraya Sundeep      * Two independent timers has same base address.
143*96401badSSubbaraya Sundeep      * Based on addr passed figure out which timer is being used.
144*96401badSSubbaraya Sundeep      */
145*96401badSSubbaraya Sundeep     if ((addr >= R_TIM1_MAX) && (addr < NUM_TIMERS * R_TIM1_MAX)) {
146*96401badSSubbaraya Sundeep         timer = 1;
147*96401badSSubbaraya Sundeep         addr -= R_TIM1_MAX;
148*96401badSSubbaraya Sundeep     }
149*96401badSSubbaraya Sundeep 
150*96401badSSubbaraya Sundeep     st = &t->timers[timer];
151*96401badSSubbaraya Sundeep 
152*96401badSSubbaraya Sundeep     DB_PRINT("addr=0x%" HWADDR_PRIx " val=0x%" PRIx32 " (timer=%d)", offset,
153*96401badSSubbaraya Sundeep             value, timer);
154*96401badSSubbaraya Sundeep 
155*96401badSSubbaraya Sundeep     switch (addr) {
156*96401badSSubbaraya Sundeep     case R_TIM_CTRL:
157*96401badSSubbaraya Sundeep         st->regs[R_TIM_CTRL] = value;
158*96401badSSubbaraya Sundeep         timer_update(st);
159*96401badSSubbaraya Sundeep         break;
160*96401badSSubbaraya Sundeep 
161*96401badSSubbaraya Sundeep     case R_TIM_RIS:
162*96401badSSubbaraya Sundeep         if (value & TIMER_RIS_ACK) {
163*96401badSSubbaraya Sundeep             st->regs[R_TIM_RIS] &= ~TIMER_RIS_ACK;
164*96401badSSubbaraya Sundeep         }
165*96401badSSubbaraya Sundeep         break;
166*96401badSSubbaraya Sundeep 
167*96401badSSubbaraya Sundeep     case R_TIM_LOADVAL:
168*96401badSSubbaraya Sundeep         st->regs[R_TIM_LOADVAL] = value;
169*96401badSSubbaraya Sundeep         if (st->regs[R_TIM_CTRL] & TIMER_CTRL_ENBL) {
170*96401badSSubbaraya Sundeep             timer_update(st);
171*96401badSSubbaraya Sundeep         }
172*96401badSSubbaraya Sundeep         break;
173*96401badSSubbaraya Sundeep 
174*96401badSSubbaraya Sundeep     case R_TIM_BGLOADVAL:
175*96401badSSubbaraya Sundeep         st->regs[R_TIM_BGLOADVAL] = value;
176*96401badSSubbaraya Sundeep         st->regs[R_TIM_LOADVAL] = value;
177*96401badSSubbaraya Sundeep         break;
178*96401badSSubbaraya Sundeep 
179*96401badSSubbaraya Sundeep     case R_TIM_VAL:
180*96401badSSubbaraya Sundeep     case R_TIM_MIS:
181*96401badSSubbaraya Sundeep         break;
182*96401badSSubbaraya Sundeep 
183*96401badSSubbaraya Sundeep     default:
184*96401badSSubbaraya Sundeep         if (addr < R_TIM1_MAX) {
185*96401badSSubbaraya Sundeep             st->regs[addr] = value;
186*96401badSSubbaraya Sundeep         } else {
187*96401badSSubbaraya Sundeep             qemu_log_mask(LOG_GUEST_ERROR,
188*96401badSSubbaraya Sundeep                         TYPE_MSS_TIMER": 64-bit mode not supported\n");
189*96401badSSubbaraya Sundeep             return;
190*96401badSSubbaraya Sundeep         }
191*96401badSSubbaraya Sundeep         break;
192*96401badSSubbaraya Sundeep     }
193*96401badSSubbaraya Sundeep     timer_update_irq(st);
194*96401badSSubbaraya Sundeep }
195*96401badSSubbaraya Sundeep 
196*96401badSSubbaraya Sundeep static const MemoryRegionOps timer_ops = {
197*96401badSSubbaraya Sundeep     .read = timer_read,
198*96401badSSubbaraya Sundeep     .write = timer_write,
199*96401badSSubbaraya Sundeep     .endianness = DEVICE_NATIVE_ENDIAN,
200*96401badSSubbaraya Sundeep     .valid = {
201*96401badSSubbaraya Sundeep         .min_access_size = 1,
202*96401badSSubbaraya Sundeep         .max_access_size = 4
203*96401badSSubbaraya Sundeep     }
204*96401badSSubbaraya Sundeep };
205*96401badSSubbaraya Sundeep 
206*96401badSSubbaraya Sundeep static void timer_hit(void *opaque)
207*96401badSSubbaraya Sundeep {
208*96401badSSubbaraya Sundeep     struct Msf2Timer *st = opaque;
209*96401badSSubbaraya Sundeep 
210*96401badSSubbaraya Sundeep     st->regs[R_TIM_RIS] |= TIMER_RIS_ACK;
211*96401badSSubbaraya Sundeep 
212*96401badSSubbaraya Sundeep     if (!(st->regs[R_TIM_CTRL] & TIMER_CTRL_ONESHOT)) {
213*96401badSSubbaraya Sundeep         timer_update(st);
214*96401badSSubbaraya Sundeep     }
215*96401badSSubbaraya Sundeep     timer_update_irq(st);
216*96401badSSubbaraya Sundeep }
217*96401badSSubbaraya Sundeep 
218*96401badSSubbaraya Sundeep static void mss_timer_init(Object *obj)
219*96401badSSubbaraya Sundeep {
220*96401badSSubbaraya Sundeep     MSSTimerState *t = MSS_TIMER(obj);
221*96401badSSubbaraya Sundeep     int i;
222*96401badSSubbaraya Sundeep 
223*96401badSSubbaraya Sundeep     /* Init all the ptimers.  */
224*96401badSSubbaraya Sundeep     for (i = 0; i < NUM_TIMERS; i++) {
225*96401badSSubbaraya Sundeep         struct Msf2Timer *st = &t->timers[i];
226*96401badSSubbaraya Sundeep 
227*96401badSSubbaraya Sundeep         st->bh = qemu_bh_new(timer_hit, st);
228*96401badSSubbaraya Sundeep         st->ptimer = ptimer_init(st->bh, PTIMER_POLICY_DEFAULT);
229*96401badSSubbaraya Sundeep         ptimer_set_freq(st->ptimer, t->freq_hz);
230*96401badSSubbaraya Sundeep         sysbus_init_irq(SYS_BUS_DEVICE(obj), &st->irq);
231*96401badSSubbaraya Sundeep     }
232*96401badSSubbaraya Sundeep 
233*96401badSSubbaraya Sundeep     memory_region_init_io(&t->mmio, OBJECT(t), &timer_ops, t, TYPE_MSS_TIMER,
234*96401badSSubbaraya Sundeep                           NUM_TIMERS * R_TIM1_MAX * 4);
235*96401badSSubbaraya Sundeep     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &t->mmio);
236*96401badSSubbaraya Sundeep }
237*96401badSSubbaraya Sundeep 
238*96401badSSubbaraya Sundeep static const VMStateDescription vmstate_timers = {
239*96401badSSubbaraya Sundeep     .name = "mss-timer-block",
240*96401badSSubbaraya Sundeep     .version_id = 1,
241*96401badSSubbaraya Sundeep     .minimum_version_id = 1,
242*96401badSSubbaraya Sundeep     .fields = (VMStateField[]) {
243*96401badSSubbaraya Sundeep         VMSTATE_PTIMER(ptimer, struct Msf2Timer),
244*96401badSSubbaraya Sundeep         VMSTATE_UINT32_ARRAY(regs, struct Msf2Timer, R_TIM1_MAX),
245*96401badSSubbaraya Sundeep         VMSTATE_END_OF_LIST()
246*96401badSSubbaraya Sundeep     }
247*96401badSSubbaraya Sundeep };
248*96401badSSubbaraya Sundeep 
249*96401badSSubbaraya Sundeep static const VMStateDescription vmstate_mss_timer = {
250*96401badSSubbaraya Sundeep     .name = TYPE_MSS_TIMER,
251*96401badSSubbaraya Sundeep     .version_id = 1,
252*96401badSSubbaraya Sundeep     .minimum_version_id = 1,
253*96401badSSubbaraya Sundeep     .fields = (VMStateField[]) {
254*96401badSSubbaraya Sundeep         VMSTATE_UINT32(freq_hz, MSSTimerState),
255*96401badSSubbaraya Sundeep         VMSTATE_STRUCT_ARRAY(timers, MSSTimerState, NUM_TIMERS, 0,
256*96401badSSubbaraya Sundeep                 vmstate_timers, struct Msf2Timer),
257*96401badSSubbaraya Sundeep         VMSTATE_END_OF_LIST()
258*96401badSSubbaraya Sundeep     }
259*96401badSSubbaraya Sundeep };
260*96401badSSubbaraya Sundeep 
261*96401badSSubbaraya Sundeep static Property mss_timer_properties[] = {
262*96401badSSubbaraya Sundeep     /* Libero GUI shows 100Mhz as default for clocks */
263*96401badSSubbaraya Sundeep     DEFINE_PROP_UINT32("clock-frequency", MSSTimerState, freq_hz,
264*96401badSSubbaraya Sundeep                       100 * 1000000),
265*96401badSSubbaraya Sundeep     DEFINE_PROP_END_OF_LIST(),
266*96401badSSubbaraya Sundeep };
267*96401badSSubbaraya Sundeep 
268*96401badSSubbaraya Sundeep static void mss_timer_class_init(ObjectClass *klass, void *data)
269*96401badSSubbaraya Sundeep {
270*96401badSSubbaraya Sundeep     DeviceClass *dc = DEVICE_CLASS(klass);
271*96401badSSubbaraya Sundeep 
272*96401badSSubbaraya Sundeep     dc->props = mss_timer_properties;
273*96401badSSubbaraya Sundeep     dc->vmsd = &vmstate_mss_timer;
274*96401badSSubbaraya Sundeep }
275*96401badSSubbaraya Sundeep 
276*96401badSSubbaraya Sundeep static const TypeInfo mss_timer_info = {
277*96401badSSubbaraya Sundeep     .name          = TYPE_MSS_TIMER,
278*96401badSSubbaraya Sundeep     .parent        = TYPE_SYS_BUS_DEVICE,
279*96401badSSubbaraya Sundeep     .instance_size = sizeof(MSSTimerState),
280*96401badSSubbaraya Sundeep     .instance_init = mss_timer_init,
281*96401badSSubbaraya Sundeep     .class_init    = mss_timer_class_init,
282*96401badSSubbaraya Sundeep };
283*96401badSSubbaraya Sundeep 
284*96401badSSubbaraya Sundeep static void mss_timer_register_types(void)
285*96401badSSubbaraya Sundeep {
286*96401badSSubbaraya Sundeep     type_register_static(&mss_timer_info);
287*96401badSSubbaraya Sundeep }
288*96401badSSubbaraya Sundeep 
289*96401badSSubbaraya Sundeep type_init(mss_timer_register_types)
290