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 | |