]>
Commit | Line | Data |
---|---|---|
a541f297 | 1 | /* |
819385c5 | 2 | * QEMU M48T59 and M48T08 NVRAM emulation for PPC PREP and Sparc platforms |
5fafdf24 | 3 | * |
cf83f140 | 4 | * Copyright (c) 2003-2005, 2007, 2017 Jocelyn Mayer |
051ddccd | 5 | * Copyright (c) 2013 Hervé Poussineau |
5fafdf24 | 6 | * |
a541f297 FB |
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 | */ | |
0b8fa32f | 25 | |
282bc81e | 26 | #include "qemu/osdep.h" |
a8d25326 | 27 | #include "qemu-common.h" |
64552b6b | 28 | #include "hw/irq.h" |
0d09e41a | 29 | #include "hw/timer/m48t59.h" |
1de7afc9 | 30 | #include "qemu/timer.h" |
9c17d615 | 31 | #include "sysemu/sysemu.h" |
83c9f4ca | 32 | #include "hw/sysbus.h" |
022c62cb | 33 | #include "exec/address-spaces.h" |
f348b6d1 | 34 | #include "qemu/bcd.h" |
0b8fa32f | 35 | #include "qemu/module.h" |
a541f297 | 36 | |
c124c4d1 | 37 | #include "m48t59-internal.h" |
d6454270 | 38 | #include "migration/vmstate.h" |
a541f297 | 39 | |
051ddccd HP |
40 | #define TYPE_M48TXX_SYS_BUS "sysbus-m48txx" |
41 | #define M48TXX_SYS_BUS_GET_CLASS(obj) \ | |
42 | OBJECT_GET_CLASS(M48txxSysBusDeviceClass, (obj), TYPE_M48TXX_SYS_BUS) | |
43 | #define M48TXX_SYS_BUS_CLASS(klass) \ | |
44 | OBJECT_CLASS_CHECK(M48txxSysBusDeviceClass, (klass), TYPE_M48TXX_SYS_BUS) | |
45 | #define M48TXX_SYS_BUS(obj) \ | |
46 | OBJECT_CHECK(M48txxSysBusState, (obj), TYPE_M48TXX_SYS_BUS) | |
47 | ||
930f3fe1 BS |
48 | /* |
49 | * Chipset docs: | |
50 | * http://www.st.com/stonline/products/literature/ds/2410/m48t02.pdf | |
51 | * http://www.st.com/stonline/products/literature/ds/2411/m48t08.pdf | |
52 | * http://www.st.com/stonline/products/literature/od/7001/m48t59y.pdf | |
53 | */ | |
54 | ||
051ddccd | 55 | typedef struct M48txxSysBusState { |
29d1ffc3 | 56 | SysBusDevice parent_obj; |
43a34704 | 57 | M48t59State state; |
087bd055 | 58 | MemoryRegion io; |
051ddccd HP |
59 | } M48txxSysBusState; |
60 | ||
61 | typedef struct M48txxSysBusDeviceClass { | |
62 | SysBusDeviceClass parent_class; | |
63 | M48txxInfo info; | |
64 | } M48txxSysBusDeviceClass; | |
65 | ||
c124c4d1 | 66 | static M48txxInfo m48txx_sysbus_info[] = { |
051ddccd | 67 | { |
c124c4d1 | 68 | .bus_name = "sysbus-m48t02", |
051ddccd HP |
69 | .model = 2, |
70 | .size = 0x800, | |
71 | },{ | |
c124c4d1 | 72 | .bus_name = "sysbus-m48t08", |
051ddccd HP |
73 | .model = 8, |
74 | .size = 0x2000, | |
0278377d | 75 | },{ |
c124c4d1 | 76 | .bus_name = "sysbus-m48t59", |
051ddccd HP |
77 | .model = 59, |
78 | .size = 0x2000, | |
79 | } | |
80 | }; | |
81 | ||
f80237d4 | 82 | |
a541f297 | 83 | /* Fake timer functions */ |
a541f297 | 84 | |
a541f297 FB |
85 | /* Alarm management */ |
86 | static void alarm_cb (void *opaque) | |
87 | { | |
f6503059 | 88 | struct tm tm; |
a541f297 | 89 | uint64_t next_time; |
43a34704 | 90 | M48t59State *NVRAM = opaque; |
a541f297 | 91 | |
d537cf6c | 92 | qemu_set_irq(NVRAM->IRQ, 1); |
5fafdf24 | 93 | if ((NVRAM->buffer[0x1FF5] & 0x80) == 0 && |
a541f297 FB |
94 | (NVRAM->buffer[0x1FF4] & 0x80) == 0 && |
95 | (NVRAM->buffer[0x1FF3] & 0x80) == 0 && | |
96 | (NVRAM->buffer[0x1FF2] & 0x80) == 0) { | |
f6503059 AZ |
97 | /* Repeat once a month */ |
98 | qemu_get_timedate(&tm, NVRAM->time_offset); | |
99 | tm.tm_mon++; | |
100 | if (tm.tm_mon == 13) { | |
101 | tm.tm_mon = 1; | |
102 | tm.tm_year++; | |
103 | } | |
104 | next_time = qemu_timedate_diff(&tm) - NVRAM->time_offset; | |
a541f297 FB |
105 | } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 && |
106 | (NVRAM->buffer[0x1FF4] & 0x80) == 0 && | |
107 | (NVRAM->buffer[0x1FF3] & 0x80) == 0 && | |
108 | (NVRAM->buffer[0x1FF2] & 0x80) == 0) { | |
f6503059 AZ |
109 | /* Repeat once a day */ |
110 | next_time = 24 * 60 * 60; | |
a541f297 FB |
111 | } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 && |
112 | (NVRAM->buffer[0x1FF4] & 0x80) != 0 && | |
113 | (NVRAM->buffer[0x1FF3] & 0x80) == 0 && | |
114 | (NVRAM->buffer[0x1FF2] & 0x80) == 0) { | |
f6503059 AZ |
115 | /* Repeat once an hour */ |
116 | next_time = 60 * 60; | |
a541f297 FB |
117 | } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 && |
118 | (NVRAM->buffer[0x1FF4] & 0x80) != 0 && | |
119 | (NVRAM->buffer[0x1FF3] & 0x80) != 0 && | |
120 | (NVRAM->buffer[0x1FF2] & 0x80) == 0) { | |
f6503059 AZ |
121 | /* Repeat once a minute */ |
122 | next_time = 60; | |
a541f297 | 123 | } else { |
f6503059 AZ |
124 | /* Repeat once a second */ |
125 | next_time = 1; | |
a541f297 | 126 | } |
bc72ad67 | 127 | timer_mod(NVRAM->alrm_timer, qemu_clock_get_ns(rtc_clock) + |
f6503059 | 128 | next_time * 1000); |
d537cf6c | 129 | qemu_set_irq(NVRAM->IRQ, 0); |
a541f297 FB |
130 | } |
131 | ||
43a34704 | 132 | static void set_alarm(M48t59State *NVRAM) |
f6503059 AZ |
133 | { |
134 | int diff; | |
135 | if (NVRAM->alrm_timer != NULL) { | |
bc72ad67 | 136 | timer_del(NVRAM->alrm_timer); |
f6503059 AZ |
137 | diff = qemu_timedate_diff(&NVRAM->alarm) - NVRAM->time_offset; |
138 | if (diff > 0) | |
bc72ad67 | 139 | timer_mod(NVRAM->alrm_timer, diff * 1000); |
f6503059 AZ |
140 | } |
141 | } | |
a541f297 | 142 | |
f6503059 | 143 | /* RTC management helpers */ |
43a34704 | 144 | static inline void get_time(M48t59State *NVRAM, struct tm *tm) |
a541f297 | 145 | { |
f6503059 | 146 | qemu_get_timedate(tm, NVRAM->time_offset); |
a541f297 FB |
147 | } |
148 | ||
43a34704 | 149 | static void set_time(M48t59State *NVRAM, struct tm *tm) |
a541f297 | 150 | { |
f6503059 AZ |
151 | NVRAM->time_offset = qemu_timedate_diff(tm); |
152 | set_alarm(NVRAM); | |
a541f297 FB |
153 | } |
154 | ||
155 | /* Watchdog management */ | |
156 | static void watchdog_cb (void *opaque) | |
157 | { | |
43a34704 | 158 | M48t59State *NVRAM = opaque; |
a541f297 FB |
159 | |
160 | NVRAM->buffer[0x1FF0] |= 0x80; | |
161 | if (NVRAM->buffer[0x1FF7] & 0x80) { | |
162 | NVRAM->buffer[0x1FF7] = 0x00; | |
163 | NVRAM->buffer[0x1FFC] &= ~0x40; | |
13ab5daa | 164 | /* May it be a hw CPU Reset instead ? */ |
cf83f140 | 165 | qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); |
a541f297 | 166 | } else { |
d537cf6c PB |
167 | qemu_set_irq(NVRAM->IRQ, 1); |
168 | qemu_set_irq(NVRAM->IRQ, 0); | |
a541f297 FB |
169 | } |
170 | } | |
171 | ||
43a34704 | 172 | static void set_up_watchdog(M48t59State *NVRAM, uint8_t value) |
a541f297 FB |
173 | { |
174 | uint64_t interval; /* in 1/16 seconds */ | |
175 | ||
868d585a | 176 | NVRAM->buffer[0x1FF0] &= ~0x80; |
a541f297 | 177 | if (NVRAM->wd_timer != NULL) { |
bc72ad67 | 178 | timer_del(NVRAM->wd_timer); |
868d585a JM |
179 | if (value != 0) { |
180 | interval = (1 << (2 * (value & 0x03))) * ((value >> 2) & 0x1F); | |
bc72ad67 | 181 | timer_mod(NVRAM->wd_timer, ((uint64_t)time(NULL) * 1000) + |
868d585a JM |
182 | ((interval * 1000) >> 4)); |
183 | } | |
a541f297 FB |
184 | } |
185 | } | |
186 | ||
187 | /* Direct access to NVRAM */ | |
c124c4d1 | 188 | void m48t59_write(M48t59State *NVRAM, uint32_t addr, uint32_t val) |
a541f297 | 189 | { |
a541f297 FB |
190 | struct tm tm; |
191 | int tmp; | |
192 | ||
819385c5 FB |
193 | if (addr > 0x1FF8 && addr < 0x2000) |
194 | NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, addr, val); | |
4aed2c33 BS |
195 | |
196 | /* check for NVRAM access */ | |
7bc3018b PB |
197 | if ((NVRAM->model == 2 && addr < 0x7f8) || |
198 | (NVRAM->model == 8 && addr < 0x1ff8) || | |
199 | (NVRAM->model == 59 && addr < 0x1ff0)) { | |
819385c5 | 200 | goto do_write; |
7bc3018b | 201 | } |
4aed2c33 BS |
202 | |
203 | /* TOD access */ | |
819385c5 | 204 | switch (addr) { |
a541f297 FB |
205 | case 0x1FF0: |
206 | /* flags register : read-only */ | |
207 | break; | |
208 | case 0x1FF1: | |
209 | /* unused */ | |
210 | break; | |
211 | case 0x1FF2: | |
212 | /* alarm seconds */ | |
abd0c6bd | 213 | tmp = from_bcd(val & 0x7F); |
819385c5 | 214 | if (tmp >= 0 && tmp <= 59) { |
f6503059 | 215 | NVRAM->alarm.tm_sec = tmp; |
819385c5 | 216 | NVRAM->buffer[0x1FF2] = val; |
f6503059 | 217 | set_alarm(NVRAM); |
819385c5 | 218 | } |
a541f297 FB |
219 | break; |
220 | case 0x1FF3: | |
221 | /* alarm minutes */ | |
abd0c6bd | 222 | tmp = from_bcd(val & 0x7F); |
819385c5 | 223 | if (tmp >= 0 && tmp <= 59) { |
f6503059 | 224 | NVRAM->alarm.tm_min = tmp; |
819385c5 | 225 | NVRAM->buffer[0x1FF3] = val; |
f6503059 | 226 | set_alarm(NVRAM); |
819385c5 | 227 | } |
a541f297 FB |
228 | break; |
229 | case 0x1FF4: | |
230 | /* alarm hours */ | |
abd0c6bd | 231 | tmp = from_bcd(val & 0x3F); |
819385c5 | 232 | if (tmp >= 0 && tmp <= 23) { |
f6503059 | 233 | NVRAM->alarm.tm_hour = tmp; |
819385c5 | 234 | NVRAM->buffer[0x1FF4] = val; |
f6503059 | 235 | set_alarm(NVRAM); |
819385c5 | 236 | } |
a541f297 FB |
237 | break; |
238 | case 0x1FF5: | |
239 | /* alarm date */ | |
02f5da11 | 240 | tmp = from_bcd(val & 0x3F); |
819385c5 | 241 | if (tmp != 0) { |
f6503059 | 242 | NVRAM->alarm.tm_mday = tmp; |
819385c5 | 243 | NVRAM->buffer[0x1FF5] = val; |
f6503059 | 244 | set_alarm(NVRAM); |
819385c5 | 245 | } |
a541f297 FB |
246 | break; |
247 | case 0x1FF6: | |
248 | /* interrupts */ | |
819385c5 | 249 | NVRAM->buffer[0x1FF6] = val; |
a541f297 FB |
250 | break; |
251 | case 0x1FF7: | |
252 | /* watchdog */ | |
819385c5 FB |
253 | NVRAM->buffer[0x1FF7] = val; |
254 | set_up_watchdog(NVRAM, val); | |
a541f297 FB |
255 | break; |
256 | case 0x1FF8: | |
4aed2c33 | 257 | case 0x07F8: |
a541f297 | 258 | /* control */ |
4aed2c33 | 259 | NVRAM->buffer[addr] = (val & ~0xA0) | 0x90; |
a541f297 FB |
260 | break; |
261 | case 0x1FF9: | |
4aed2c33 | 262 | case 0x07F9: |
a541f297 | 263 | /* seconds (BCD) */ |
abd0c6bd | 264 | tmp = from_bcd(val & 0x7F); |
a541f297 FB |
265 | if (tmp >= 0 && tmp <= 59) { |
266 | get_time(NVRAM, &tm); | |
267 | tm.tm_sec = tmp; | |
268 | set_time(NVRAM, &tm); | |
269 | } | |
f6503059 | 270 | if ((val & 0x80) ^ (NVRAM->buffer[addr] & 0x80)) { |
a541f297 FB |
271 | if (val & 0x80) { |
272 | NVRAM->stop_time = time(NULL); | |
273 | } else { | |
274 | NVRAM->time_offset += NVRAM->stop_time - time(NULL); | |
275 | NVRAM->stop_time = 0; | |
276 | } | |
277 | } | |
f6503059 | 278 | NVRAM->buffer[addr] = val & 0x80; |
a541f297 FB |
279 | break; |
280 | case 0x1FFA: | |
4aed2c33 | 281 | case 0x07FA: |
a541f297 | 282 | /* minutes (BCD) */ |
abd0c6bd | 283 | tmp = from_bcd(val & 0x7F); |
a541f297 FB |
284 | if (tmp >= 0 && tmp <= 59) { |
285 | get_time(NVRAM, &tm); | |
286 | tm.tm_min = tmp; | |
287 | set_time(NVRAM, &tm); | |
288 | } | |
289 | break; | |
290 | case 0x1FFB: | |
4aed2c33 | 291 | case 0x07FB: |
a541f297 | 292 | /* hours (BCD) */ |
abd0c6bd | 293 | tmp = from_bcd(val & 0x3F); |
a541f297 FB |
294 | if (tmp >= 0 && tmp <= 23) { |
295 | get_time(NVRAM, &tm); | |
296 | tm.tm_hour = tmp; | |
297 | set_time(NVRAM, &tm); | |
298 | } | |
299 | break; | |
300 | case 0x1FFC: | |
4aed2c33 | 301 | case 0x07FC: |
a541f297 | 302 | /* day of the week / century */ |
abd0c6bd | 303 | tmp = from_bcd(val & 0x07); |
a541f297 FB |
304 | get_time(NVRAM, &tm); |
305 | tm.tm_wday = tmp; | |
306 | set_time(NVRAM, &tm); | |
4aed2c33 | 307 | NVRAM->buffer[addr] = val & 0x40; |
a541f297 FB |
308 | break; |
309 | case 0x1FFD: | |
4aed2c33 | 310 | case 0x07FD: |
02f5da11 AT |
311 | /* date (BCD) */ |
312 | tmp = from_bcd(val & 0x3F); | |
a541f297 FB |
313 | if (tmp != 0) { |
314 | get_time(NVRAM, &tm); | |
315 | tm.tm_mday = tmp; | |
316 | set_time(NVRAM, &tm); | |
317 | } | |
318 | break; | |
319 | case 0x1FFE: | |
4aed2c33 | 320 | case 0x07FE: |
a541f297 | 321 | /* month */ |
abd0c6bd | 322 | tmp = from_bcd(val & 0x1F); |
a541f297 FB |
323 | if (tmp >= 1 && tmp <= 12) { |
324 | get_time(NVRAM, &tm); | |
325 | tm.tm_mon = tmp - 1; | |
326 | set_time(NVRAM, &tm); | |
327 | } | |
328 | break; | |
329 | case 0x1FFF: | |
4aed2c33 | 330 | case 0x07FF: |
a541f297 | 331 | /* year */ |
abd0c6bd | 332 | tmp = from_bcd(val); |
a541f297 FB |
333 | if (tmp >= 0 && tmp <= 99) { |
334 | get_time(NVRAM, &tm); | |
6de04973 | 335 | tm.tm_year = from_bcd(val) + NVRAM->base_year - 1900; |
a541f297 FB |
336 | set_time(NVRAM, &tm); |
337 | } | |
338 | break; | |
339 | default: | |
13ab5daa | 340 | /* Check lock registers state */ |
819385c5 | 341 | if (addr >= 0x20 && addr <= 0x2F && (NVRAM->lock & 1)) |
13ab5daa | 342 | break; |
819385c5 | 343 | if (addr >= 0x30 && addr <= 0x3F && (NVRAM->lock & 2)) |
13ab5daa | 344 | break; |
819385c5 FB |
345 | do_write: |
346 | if (addr < NVRAM->size) { | |
347 | NVRAM->buffer[addr] = val & 0xFF; | |
a541f297 FB |
348 | } |
349 | break; | |
350 | } | |
351 | } | |
352 | ||
c124c4d1 | 353 | uint32_t m48t59_read(M48t59State *NVRAM, uint32_t addr) |
a541f297 | 354 | { |
a541f297 FB |
355 | struct tm tm; |
356 | uint32_t retval = 0xFF; | |
357 | ||
4aed2c33 | 358 | /* check for NVRAM access */ |
7bc3018b PB |
359 | if ((NVRAM->model == 2 && addr < 0x078f) || |
360 | (NVRAM->model == 8 && addr < 0x1ff8) || | |
361 | (NVRAM->model == 59 && addr < 0x1ff0)) { | |
819385c5 | 362 | goto do_read; |
7bc3018b | 363 | } |
4aed2c33 BS |
364 | |
365 | /* TOD access */ | |
819385c5 | 366 | switch (addr) { |
a541f297 FB |
367 | case 0x1FF0: |
368 | /* flags register */ | |
369 | goto do_read; | |
370 | case 0x1FF1: | |
371 | /* unused */ | |
372 | retval = 0; | |
373 | break; | |
374 | case 0x1FF2: | |
375 | /* alarm seconds */ | |
376 | goto do_read; | |
377 | case 0x1FF3: | |
378 | /* alarm minutes */ | |
379 | goto do_read; | |
380 | case 0x1FF4: | |
381 | /* alarm hours */ | |
382 | goto do_read; | |
383 | case 0x1FF5: | |
384 | /* alarm date */ | |
385 | goto do_read; | |
386 | case 0x1FF6: | |
387 | /* interrupts */ | |
388 | goto do_read; | |
389 | case 0x1FF7: | |
390 | /* A read resets the watchdog */ | |
391 | set_up_watchdog(NVRAM, NVRAM->buffer[0x1FF7]); | |
392 | goto do_read; | |
393 | case 0x1FF8: | |
4aed2c33 | 394 | case 0x07F8: |
a541f297 FB |
395 | /* control */ |
396 | goto do_read; | |
397 | case 0x1FF9: | |
4aed2c33 | 398 | case 0x07F9: |
a541f297 FB |
399 | /* seconds (BCD) */ |
400 | get_time(NVRAM, &tm); | |
abd0c6bd | 401 | retval = (NVRAM->buffer[addr] & 0x80) | to_bcd(tm.tm_sec); |
a541f297 FB |
402 | break; |
403 | case 0x1FFA: | |
4aed2c33 | 404 | case 0x07FA: |
a541f297 FB |
405 | /* minutes (BCD) */ |
406 | get_time(NVRAM, &tm); | |
abd0c6bd | 407 | retval = to_bcd(tm.tm_min); |
a541f297 FB |
408 | break; |
409 | case 0x1FFB: | |
4aed2c33 | 410 | case 0x07FB: |
a541f297 FB |
411 | /* hours (BCD) */ |
412 | get_time(NVRAM, &tm); | |
abd0c6bd | 413 | retval = to_bcd(tm.tm_hour); |
a541f297 FB |
414 | break; |
415 | case 0x1FFC: | |
4aed2c33 | 416 | case 0x07FC: |
a541f297 FB |
417 | /* day of the week / century */ |
418 | get_time(NVRAM, &tm); | |
4aed2c33 | 419 | retval = NVRAM->buffer[addr] | tm.tm_wday; |
a541f297 FB |
420 | break; |
421 | case 0x1FFD: | |
4aed2c33 | 422 | case 0x07FD: |
a541f297 FB |
423 | /* date */ |
424 | get_time(NVRAM, &tm); | |
abd0c6bd | 425 | retval = to_bcd(tm.tm_mday); |
a541f297 FB |
426 | break; |
427 | case 0x1FFE: | |
4aed2c33 | 428 | case 0x07FE: |
a541f297 FB |
429 | /* month */ |
430 | get_time(NVRAM, &tm); | |
abd0c6bd | 431 | retval = to_bcd(tm.tm_mon + 1); |
a541f297 FB |
432 | break; |
433 | case 0x1FFF: | |
4aed2c33 | 434 | case 0x07FF: |
a541f297 FB |
435 | /* year */ |
436 | get_time(NVRAM, &tm); | |
6de04973 | 437 | retval = to_bcd((tm.tm_year + 1900 - NVRAM->base_year) % 100); |
a541f297 FB |
438 | break; |
439 | default: | |
13ab5daa | 440 | /* Check lock registers state */ |
819385c5 | 441 | if (addr >= 0x20 && addr <= 0x2F && (NVRAM->lock & 1)) |
13ab5daa | 442 | break; |
819385c5 | 443 | if (addr >= 0x30 && addr <= 0x3F && (NVRAM->lock & 2)) |
13ab5daa | 444 | break; |
819385c5 FB |
445 | do_read: |
446 | if (addr < NVRAM->size) { | |
447 | retval = NVRAM->buffer[addr]; | |
a541f297 FB |
448 | } |
449 | break; | |
450 | } | |
819385c5 | 451 | if (addr > 0x1FF9 && addr < 0x2000) |
9ed1e667 | 452 | NVRAM_PRINTF("%s: 0x%08x <= 0x%08x\n", __func__, addr, retval); |
a541f297 FB |
453 | |
454 | return retval; | |
455 | } | |
456 | ||
a541f297 | 457 | /* IO access to NVRAM */ |
087bd055 AG |
458 | static void NVRAM_writeb(void *opaque, hwaddr addr, uint64_t val, |
459 | unsigned size) | |
a541f297 | 460 | { |
43a34704 | 461 | M48t59State *NVRAM = opaque; |
a541f297 | 462 | |
54be4c42 | 463 | NVRAM_PRINTF("%s: 0x%"HWADDR_PRIx" => 0x%"PRIx64"\n", __func__, addr, val); |
a541f297 FB |
464 | switch (addr) { |
465 | case 0: | |
466 | NVRAM->addr &= ~0x00FF; | |
467 | NVRAM->addr |= val; | |
468 | break; | |
469 | case 1: | |
470 | NVRAM->addr &= ~0xFF00; | |
471 | NVRAM->addr |= val << 8; | |
472 | break; | |
473 | case 3: | |
b1f88301 | 474 | m48t59_write(NVRAM, NVRAM->addr, val); |
a541f297 FB |
475 | NVRAM->addr = 0x0000; |
476 | break; | |
477 | default: | |
478 | break; | |
479 | } | |
480 | } | |
481 | ||
087bd055 | 482 | static uint64_t NVRAM_readb(void *opaque, hwaddr addr, unsigned size) |
a541f297 | 483 | { |
43a34704 | 484 | M48t59State *NVRAM = opaque; |
13ab5daa | 485 | uint32_t retval; |
a541f297 | 486 | |
13ab5daa FB |
487 | switch (addr) { |
488 | case 3: | |
819385c5 | 489 | retval = m48t59_read(NVRAM, NVRAM->addr); |
13ab5daa FB |
490 | break; |
491 | default: | |
492 | retval = -1; | |
493 | break; | |
494 | } | |
54be4c42 | 495 | NVRAM_PRINTF("%s: 0x%"HWADDR_PRIx" <= 0x%08x\n", __func__, addr, retval); |
a541f297 | 496 | |
13ab5daa | 497 | return retval; |
a541f297 FB |
498 | } |
499 | ||
62b9cf0a | 500 | static uint64_t nvram_read(void *opaque, hwaddr addr, unsigned size) |
e1bb04f7 | 501 | { |
43a34704 | 502 | M48t59State *NVRAM = opaque; |
3b46e624 | 503 | |
bf5f78ef | 504 | return m48t59_read(NVRAM, addr); |
e1bb04f7 FB |
505 | } |
506 | ||
62b9cf0a PM |
507 | static void nvram_write(void *opaque, hwaddr addr, uint64_t value, |
508 | unsigned size) | |
e1bb04f7 | 509 | { |
43a34704 | 510 | M48t59State *NVRAM = opaque; |
e1bb04f7 | 511 | |
62b9cf0a | 512 | return m48t59_write(NVRAM, addr, value); |
e1bb04f7 FB |
513 | } |
514 | ||
5a31cd68 | 515 | static const MemoryRegionOps nvram_ops = { |
62b9cf0a PM |
516 | .read = nvram_read, |
517 | .write = nvram_write, | |
518 | .impl.min_access_size = 1, | |
519 | .impl.max_access_size = 1, | |
520 | .valid.min_access_size = 1, | |
521 | .valid.max_access_size = 4, | |
522 | .endianness = DEVICE_BIG_ENDIAN, | |
e1bb04f7 | 523 | }; |
819385c5 | 524 | |
fd484ae4 JQ |
525 | static const VMStateDescription vmstate_m48t59 = { |
526 | .name = "m48t59", | |
527 | .version_id = 1, | |
528 | .minimum_version_id = 1, | |
3aff6c2f | 529 | .fields = (VMStateField[]) { |
fd484ae4 JQ |
530 | VMSTATE_UINT8(lock, M48t59State), |
531 | VMSTATE_UINT16(addr, M48t59State), | |
59046ec2 | 532 | VMSTATE_VBUFFER_UINT32(buffer, M48t59State, 0, NULL, size), |
fd484ae4 JQ |
533 | VMSTATE_END_OF_LIST() |
534 | } | |
535 | }; | |
3ccacc4a | 536 | |
c124c4d1 | 537 | void m48t59_reset_common(M48t59State *NVRAM) |
3ccacc4a | 538 | { |
6e6b7363 BS |
539 | NVRAM->addr = 0; |
540 | NVRAM->lock = 0; | |
3ccacc4a | 541 | if (NVRAM->alrm_timer != NULL) |
bc72ad67 | 542 | timer_del(NVRAM->alrm_timer); |
3ccacc4a BS |
543 | |
544 | if (NVRAM->wd_timer != NULL) | |
bc72ad67 | 545 | timer_del(NVRAM->wd_timer); |
3ccacc4a BS |
546 | } |
547 | ||
285e468d BS |
548 | static void m48t59_reset_sysbus(DeviceState *d) |
549 | { | |
051ddccd | 550 | M48txxSysBusState *sys = M48TXX_SYS_BUS(d); |
43a34704 | 551 | M48t59State *NVRAM = &sys->state; |
285e468d BS |
552 | |
553 | m48t59_reset_common(NVRAM); | |
554 | } | |
555 | ||
c124c4d1 | 556 | const MemoryRegionOps m48t59_io_ops = { |
087bd055 AG |
557 | .read = NVRAM_readb, |
558 | .write = NVRAM_writeb, | |
559 | .impl = { | |
560 | .min_access_size = 1, | |
561 | .max_access_size = 1, | |
562 | }, | |
563 | .endianness = DEVICE_LITTLE_ENDIAN, | |
9936d6e4 RH |
564 | }; |
565 | ||
a541f297 | 566 | /* Initialisation routine */ |
31688246 | 567 | Nvram *m48t59_init(qemu_irq IRQ, hwaddr mem_base, |
6de04973 MCA |
568 | uint32_t io_base, uint16_t size, int base_year, |
569 | int model) | |
a541f297 | 570 | { |
d27cf0ae BS |
571 | DeviceState *dev; |
572 | SysBusDevice *s; | |
051ddccd | 573 | int i; |
d27cf0ae | 574 | |
c124c4d1 DG |
575 | for (i = 0; i < ARRAY_SIZE(m48txx_sysbus_info); i++) { |
576 | if (m48txx_sysbus_info[i].size != size || | |
577 | m48txx_sysbus_info[i].model != model) { | |
051ddccd HP |
578 | continue; |
579 | } | |
580 | ||
c124c4d1 | 581 | dev = qdev_create(NULL, m48txx_sysbus_info[i].bus_name); |
6de04973 | 582 | qdev_prop_set_int32(dev, "base-year", base_year); |
051ddccd HP |
583 | qdev_init_nofail(dev); |
584 | s = SYS_BUS_DEVICE(dev); | |
051ddccd HP |
585 | sysbus_connect_irq(s, 0, IRQ); |
586 | if (io_base != 0) { | |
587 | memory_region_add_subregion(get_system_io(), io_base, | |
588 | sysbus_mmio_get_region(s, 1)); | |
589 | } | |
590 | if (mem_base != 0) { | |
591 | sysbus_mmio_map(s, 0, mem_base); | |
592 | } | |
593 | ||
31688246 | 594 | return NVRAM(s); |
e1bb04f7 | 595 | } |
d27cf0ae | 596 | |
051ddccd HP |
597 | assert(false); |
598 | return NULL; | |
d27cf0ae BS |
599 | } |
600 | ||
c124c4d1 | 601 | void m48t59_realize_common(M48t59State *s, Error **errp) |
f80237d4 | 602 | { |
7267c094 | 603 | s->buffer = g_malloc0(s->size); |
7bc3018b | 604 | if (s->model == 59) { |
884f17c2 | 605 | s->alrm_timer = timer_new_ns(rtc_clock, &alarm_cb, s); |
bc72ad67 | 606 | s->wd_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &watchdog_cb, s); |
819385c5 | 607 | } |
f6503059 | 608 | qemu_get_timedate(&s->alarm, 0); |
f80237d4 BS |
609 | } |
610 | ||
c04e34a9 | 611 | static void m48t59_init1(Object *obj) |
f80237d4 | 612 | { |
c04e34a9 XZ |
613 | M48txxSysBusDeviceClass *u = M48TXX_SYS_BUS_GET_CLASS(obj); |
614 | M48txxSysBusState *d = M48TXX_SYS_BUS(obj); | |
615 | SysBusDevice *dev = SYS_BUS_DEVICE(obj); | |
43a34704 | 616 | M48t59State *s = &d->state; |
f80237d4 | 617 | |
051ddccd HP |
618 | s->model = u->info.model; |
619 | s->size = u->info.size; | |
f80237d4 BS |
620 | sysbus_init_irq(dev, &s->IRQ); |
621 | ||
c04e34a9 | 622 | memory_region_init_io(&s->iomem, obj, &nvram_ops, s, "m48t59.nvram", |
72cd63f8 | 623 | s->size); |
c04e34a9 XZ |
624 | memory_region_init_io(&d->io, obj, &m48t59_io_ops, s, "m48t59", 4); |
625 | } | |
626 | ||
627 | static void m48t59_realize(DeviceState *dev, Error **errp) | |
628 | { | |
629 | M48txxSysBusState *d = M48TXX_SYS_BUS(dev); | |
630 | M48t59State *s = &d->state; | |
631 | SysBusDevice *sbd = SYS_BUS_DEVICE(dev); | |
f80237d4 | 632 | |
c04e34a9 XZ |
633 | sysbus_init_mmio(sbd, &s->iomem); |
634 | sysbus_init_mmio(sbd, &d->io); | |
635 | m48t59_realize_common(s, errp); | |
f80237d4 BS |
636 | } |
637 | ||
43745328 HP |
638 | static uint32_t m48txx_sysbus_read(Nvram *obj, uint32_t addr) |
639 | { | |
640 | M48txxSysBusState *d = M48TXX_SYS_BUS(obj); | |
641 | return m48t59_read(&d->state, addr); | |
642 | } | |
643 | ||
644 | static void m48txx_sysbus_write(Nvram *obj, uint32_t addr, uint32_t val) | |
645 | { | |
646 | M48txxSysBusState *d = M48TXX_SYS_BUS(obj); | |
647 | m48t59_write(&d->state, addr, val); | |
648 | } | |
649 | ||
650 | static void m48txx_sysbus_toggle_lock(Nvram *obj, int lock) | |
651 | { | |
652 | M48txxSysBusState *d = M48TXX_SYS_BUS(obj); | |
653 | m48t59_toggle_lock(&d->state, lock); | |
654 | } | |
655 | ||
6de04973 MCA |
656 | static Property m48t59_sysbus_properties[] = { |
657 | DEFINE_PROP_INT32("base-year", M48txxSysBusState, state.base_year, 0), | |
658 | DEFINE_PROP_END_OF_LIST(), | |
659 | }; | |
660 | ||
051ddccd | 661 | static void m48txx_sysbus_class_init(ObjectClass *klass, void *data) |
999e12bb | 662 | { |
39bffca2 | 663 | DeviceClass *dc = DEVICE_CLASS(klass); |
43745328 | 664 | NvramClass *nc = NVRAM_CLASS(klass); |
999e12bb | 665 | |
c04e34a9 | 666 | dc->realize = m48t59_realize; |
39bffca2 | 667 | dc->reset = m48t59_reset_sysbus; |
6de04973 | 668 | dc->props = m48t59_sysbus_properties; |
c04e34a9 | 669 | dc->vmsd = &vmstate_m48t59; |
43745328 HP |
670 | nc->read = m48txx_sysbus_read; |
671 | nc->write = m48txx_sysbus_write; | |
672 | nc->toggle_lock = m48txx_sysbus_toggle_lock; | |
999e12bb AL |
673 | } |
674 | ||
051ddccd HP |
675 | static void m48txx_sysbus_concrete_class_init(ObjectClass *klass, void *data) |
676 | { | |
677 | M48txxSysBusDeviceClass *u = M48TXX_SYS_BUS_CLASS(klass); | |
678 | M48txxInfo *info = data; | |
679 | ||
680 | u->info = *info; | |
681 | } | |
682 | ||
43745328 HP |
683 | static const TypeInfo nvram_info = { |
684 | .name = TYPE_NVRAM, | |
685 | .parent = TYPE_INTERFACE, | |
686 | .class_size = sizeof(NvramClass), | |
687 | }; | |
688 | ||
051ddccd HP |
689 | static const TypeInfo m48txx_sysbus_type_info = { |
690 | .name = TYPE_M48TXX_SYS_BUS, | |
691 | .parent = TYPE_SYS_BUS_DEVICE, | |
692 | .instance_size = sizeof(M48txxSysBusState), | |
c04e34a9 | 693 | .instance_init = m48t59_init1, |
051ddccd HP |
694 | .abstract = true, |
695 | .class_init = m48txx_sysbus_class_init, | |
43745328 HP |
696 | .interfaces = (InterfaceInfo[]) { |
697 | { TYPE_NVRAM }, | |
698 | { } | |
699 | } | |
051ddccd HP |
700 | }; |
701 | ||
83f7d43a | 702 | static void m48t59_register_types(void) |
d27cf0ae | 703 | { |
051ddccd HP |
704 | TypeInfo sysbus_type_info = { |
705 | .parent = TYPE_M48TXX_SYS_BUS, | |
706 | .class_size = sizeof(M48txxSysBusDeviceClass), | |
707 | .class_init = m48txx_sysbus_concrete_class_init, | |
708 | }; | |
051ddccd HP |
709 | int i; |
710 | ||
43745328 | 711 | type_register_static(&nvram_info); |
051ddccd | 712 | type_register_static(&m48txx_sysbus_type_info); |
051ddccd | 713 | |
c124c4d1 DG |
714 | for (i = 0; i < ARRAY_SIZE(m48txx_sysbus_info); i++) { |
715 | sysbus_type_info.name = m48txx_sysbus_info[i].bus_name; | |
716 | sysbus_type_info.class_data = &m48txx_sysbus_info[i]; | |
717 | type_register(&sysbus_type_info); | |
051ddccd | 718 | } |
a541f297 | 719 | } |
d27cf0ae | 720 | |
83f7d43a | 721 | type_init(m48t59_register_types) |