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