]>
Commit | Line | Data |
---|---|---|
420557e8 FB |
1 | /* |
2 | * QEMU M48T08 NVRAM emulation for Sparc platform | |
3 | * | |
4 | * Copyright (c) 2003-2004 Jocelyn Mayer | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
24 | #include "vl.h" | |
25 | #include "m48t08.h" | |
26 | ||
27 | //#define DEBUG_NVRAM | |
28 | ||
29 | #if defined(DEBUG_NVRAM) | |
30 | #define NVRAM_PRINTF(fmt, args...) do { printf(fmt , ##args); } while (0) | |
31 | #else | |
32 | #define NVRAM_PRINTF(fmt, args...) do { } while (0) | |
33 | #endif | |
34 | ||
e80cfcfc FB |
35 | #define NVRAM_MAX_MEM 0x1ff0 |
36 | #define NVRAM_MAXADDR 0x1fff | |
420557e8 FB |
37 | |
38 | struct m48t08_t { | |
420557e8 FB |
39 | /* RTC management */ |
40 | time_t time_offset; | |
41 | time_t stop_time; | |
42 | /* NVRAM storage */ | |
420557e8 FB |
43 | uint8_t *buffer; |
44 | }; | |
45 | ||
46 | /* Fake timer functions */ | |
47 | /* Generic helpers for BCD */ | |
48 | static inline uint8_t toBCD (uint8_t value) | |
49 | { | |
50 | return (((value / 10) % 10) << 4) | (value % 10); | |
51 | } | |
52 | ||
53 | static inline uint8_t fromBCD (uint8_t BCD) | |
54 | { | |
55 | return ((BCD >> 4) * 10) + (BCD & 0x0F); | |
56 | } | |
57 | ||
58 | /* RTC management helpers */ | |
59 | static void get_time (m48t08_t *NVRAM, struct tm *tm) | |
60 | { | |
61 | time_t t; | |
62 | ||
63 | t = time(NULL) + NVRAM->time_offset; | |
64 | #ifdef _WIN32 | |
65 | memcpy(tm,localtime(&t),sizeof(*tm)); | |
66 | #else | |
67 | localtime_r (&t, tm) ; | |
68 | #endif | |
69 | } | |
70 | ||
71 | static void set_time (m48t08_t *NVRAM, struct tm *tm) | |
72 | { | |
73 | time_t now, new_time; | |
74 | ||
75 | new_time = mktime(tm); | |
76 | now = time(NULL); | |
77 | NVRAM->time_offset = new_time - now; | |
78 | } | |
79 | ||
80 | /* Direct access to NVRAM */ | |
e80cfcfc | 81 | void m48t08_write (m48t08_t *NVRAM, uint32_t addr, uint8_t val) |
420557e8 FB |
82 | { |
83 | struct tm tm; | |
84 | int tmp; | |
85 | ||
e80cfcfc FB |
86 | addr &= NVRAM_MAXADDR; |
87 | switch (addr) { | |
420557e8 FB |
88 | case 0x1FF8: |
89 | /* control */ | |
90 | NVRAM->buffer[0x1FF8] = (val & ~0xA0) | 0x90; | |
91 | break; | |
92 | case 0x1FF9: | |
93 | /* seconds (BCD) */ | |
94 | tmp = fromBCD(val & 0x7F); | |
95 | if (tmp >= 0 && tmp <= 59) { | |
96 | get_time(NVRAM, &tm); | |
97 | tm.tm_sec = tmp; | |
98 | set_time(NVRAM, &tm); | |
99 | } | |
100 | if ((val & 0x80) ^ (NVRAM->buffer[0x1FF9] & 0x80)) { | |
101 | if (val & 0x80) { | |
102 | NVRAM->stop_time = time(NULL); | |
103 | } else { | |
104 | NVRAM->time_offset += NVRAM->stop_time - time(NULL); | |
105 | NVRAM->stop_time = 0; | |
106 | } | |
107 | } | |
108 | NVRAM->buffer[0x1FF9] = val & 0x80; | |
109 | break; | |
110 | case 0x1FFA: | |
111 | /* minutes (BCD) */ | |
112 | tmp = fromBCD(val & 0x7F); | |
113 | if (tmp >= 0 && tmp <= 59) { | |
114 | get_time(NVRAM, &tm); | |
115 | tm.tm_min = tmp; | |
116 | set_time(NVRAM, &tm); | |
117 | } | |
118 | break; | |
119 | case 0x1FFB: | |
120 | /* hours (BCD) */ | |
121 | tmp = fromBCD(val & 0x3F); | |
122 | if (tmp >= 0 && tmp <= 23) { | |
123 | get_time(NVRAM, &tm); | |
124 | tm.tm_hour = tmp; | |
125 | set_time(NVRAM, &tm); | |
126 | } | |
127 | break; | |
128 | case 0x1FFC: | |
129 | /* day of the week / century */ | |
130 | tmp = fromBCD(val & 0x07); | |
131 | get_time(NVRAM, &tm); | |
132 | tm.tm_wday = tmp; | |
133 | set_time(NVRAM, &tm); | |
134 | NVRAM->buffer[0x1FFC] = val & 0x40; | |
135 | break; | |
136 | case 0x1FFD: | |
137 | /* date */ | |
138 | tmp = fromBCD(val & 0x1F); | |
139 | if (tmp != 0) { | |
140 | get_time(NVRAM, &tm); | |
141 | tm.tm_mday = tmp; | |
142 | set_time(NVRAM, &tm); | |
143 | } | |
144 | break; | |
145 | case 0x1FFE: | |
146 | /* month */ | |
147 | tmp = fromBCD(val & 0x1F); | |
148 | if (tmp >= 1 && tmp <= 12) { | |
149 | get_time(NVRAM, &tm); | |
150 | tm.tm_mon = tmp - 1; | |
151 | set_time(NVRAM, &tm); | |
152 | } | |
153 | break; | |
154 | case 0x1FFF: | |
155 | /* year */ | |
156 | tmp = fromBCD(val); | |
157 | if (tmp >= 0 && tmp <= 99) { | |
158 | get_time(NVRAM, &tm); | |
159 | tm.tm_year = fromBCD(val); | |
160 | set_time(NVRAM, &tm); | |
161 | } | |
162 | break; | |
163 | default: | |
e80cfcfc | 164 | NVRAM->buffer[addr] = val & 0xFF; |
420557e8 FB |
165 | break; |
166 | } | |
167 | } | |
168 | ||
e80cfcfc | 169 | uint8_t m48t08_read (m48t08_t *NVRAM, uint32_t addr) |
420557e8 FB |
170 | { |
171 | struct tm tm; | |
e80cfcfc | 172 | uint8_t retval = 0xFF; |
420557e8 | 173 | |
e80cfcfc FB |
174 | addr &= NVRAM_MAXADDR; |
175 | switch (addr) { | |
420557e8 FB |
176 | case 0x1FF8: |
177 | /* control */ | |
178 | goto do_read; | |
179 | case 0x1FF9: | |
180 | /* seconds (BCD) */ | |
181 | get_time(NVRAM, &tm); | |
182 | retval = (NVRAM->buffer[0x1FF9] & 0x80) | toBCD(tm.tm_sec); | |
183 | break; | |
184 | case 0x1FFA: | |
185 | /* minutes (BCD) */ | |
186 | get_time(NVRAM, &tm); | |
187 | retval = toBCD(tm.tm_min); | |
188 | break; | |
189 | case 0x1FFB: | |
190 | /* hours (BCD) */ | |
191 | get_time(NVRAM, &tm); | |
192 | retval = toBCD(tm.tm_hour); | |
193 | break; | |
194 | case 0x1FFC: | |
195 | /* day of the week / century */ | |
196 | get_time(NVRAM, &tm); | |
197 | retval = NVRAM->buffer[0x1FFC] | tm.tm_wday; | |
198 | break; | |
199 | case 0x1FFD: | |
200 | /* date */ | |
201 | get_time(NVRAM, &tm); | |
202 | retval = toBCD(tm.tm_mday); | |
203 | break; | |
204 | case 0x1FFE: | |
205 | /* month */ | |
206 | get_time(NVRAM, &tm); | |
207 | retval = toBCD(tm.tm_mon + 1); | |
208 | break; | |
209 | case 0x1FFF: | |
210 | /* year */ | |
211 | get_time(NVRAM, &tm); | |
212 | retval = toBCD(tm.tm_year); | |
213 | break; | |
214 | default: | |
e80cfcfc FB |
215 | do_read: |
216 | retval = NVRAM->buffer[addr]; | |
420557e8 FB |
217 | break; |
218 | } | |
420557e8 FB |
219 | return retval; |
220 | } | |
221 | ||
420557e8 FB |
222 | static void nvram_writeb (void *opaque, target_phys_addr_t addr, uint32_t value) |
223 | { | |
224 | m48t08_t *NVRAM = opaque; | |
225 | ||
e80cfcfc | 226 | m48t08_write(NVRAM, addr, value); |
420557e8 FB |
227 | } |
228 | ||
229 | static void nvram_writew (void *opaque, target_phys_addr_t addr, uint32_t value) | |
230 | { | |
231 | m48t08_t *NVRAM = opaque; | |
232 | ||
e80cfcfc FB |
233 | m48t08_write(NVRAM, addr, value); |
234 | m48t08_write(NVRAM, addr + 1, value >> 8); | |
420557e8 FB |
235 | } |
236 | ||
237 | static void nvram_writel (void *opaque, target_phys_addr_t addr, uint32_t value) | |
238 | { | |
239 | m48t08_t *NVRAM = opaque; | |
240 | ||
e80cfcfc FB |
241 | m48t08_write(NVRAM, addr, value); |
242 | m48t08_write(NVRAM, addr + 1, value >> 8); | |
243 | m48t08_write(NVRAM, addr + 2, value >> 16); | |
244 | m48t08_write(NVRAM, addr + 3, value >> 24); | |
420557e8 FB |
245 | } |
246 | ||
247 | static uint32_t nvram_readb (void *opaque, target_phys_addr_t addr) | |
248 | { | |
249 | m48t08_t *NVRAM = opaque; | |
250 | uint32_t retval = 0; | |
251 | ||
e80cfcfc | 252 | retval = m48t08_read(NVRAM, addr); |
420557e8 FB |
253 | return retval; |
254 | } | |
255 | ||
256 | static uint32_t nvram_readw (void *opaque, target_phys_addr_t addr) | |
257 | { | |
258 | m48t08_t *NVRAM = opaque; | |
259 | uint32_t retval = 0; | |
260 | ||
e80cfcfc FB |
261 | retval = m48t08_read(NVRAM, addr) << 8; |
262 | retval |= m48t08_read(NVRAM, addr + 1); | |
420557e8 FB |
263 | return retval; |
264 | } | |
265 | ||
266 | static uint32_t nvram_readl (void *opaque, target_phys_addr_t addr) | |
267 | { | |
268 | m48t08_t *NVRAM = opaque; | |
269 | uint32_t retval = 0; | |
270 | ||
e80cfcfc FB |
271 | retval = m48t08_read(NVRAM, addr) << 24; |
272 | retval |= m48t08_read(NVRAM, addr + 1) << 16; | |
273 | retval |= m48t08_read(NVRAM, addr + 2) << 8; | |
274 | retval |= m48t08_read(NVRAM, addr + 3); | |
420557e8 FB |
275 | return retval; |
276 | } | |
277 | ||
278 | static CPUWriteMemoryFunc *nvram_write[] = { | |
279 | &nvram_writeb, | |
280 | &nvram_writew, | |
281 | &nvram_writel, | |
282 | }; | |
283 | ||
284 | static CPUReadMemoryFunc *nvram_read[] = { | |
285 | &nvram_readb, | |
286 | &nvram_readw, | |
287 | &nvram_readl, | |
288 | }; | |
289 | ||
e80cfcfc FB |
290 | static void nvram_save(QEMUFile *f, void *opaque) |
291 | { | |
292 | m48t08_t *s = opaque; | |
293 | ||
294 | qemu_put_be32s(f, (uint32_t *)&s->time_offset); | |
295 | qemu_put_be32s(f, (uint32_t *)&s->stop_time); | |
296 | qemu_put_buffer(f, s->buffer, 0x2000); | |
297 | } | |
298 | ||
299 | static int nvram_load(QEMUFile *f, void *opaque, int version_id) | |
300 | { | |
301 | m48t08_t *s = opaque; | |
302 | ||
303 | if (version_id != 1) | |
304 | return -EINVAL; | |
305 | ||
306 | qemu_get_be32s(f, (uint32_t *)&s->time_offset); | |
307 | qemu_get_be32s(f, (uint32_t *)&s->stop_time); | |
308 | qemu_get_buffer(f, s->buffer, 0x2000); | |
309 | return 0; | |
310 | } | |
311 | ||
312 | static void m48t08_reset(void *opaque) | |
313 | { | |
314 | m48t08_t *s = opaque; | |
315 | ||
316 | s->time_offset = 0; | |
317 | s->stop_time = 0; | |
318 | } | |
319 | ||
320 | ||
420557e8 | 321 | /* Initialisation routine */ |
e80cfcfc | 322 | m48t08_t *m48t08_init(uint32_t mem_base, uint16_t size) |
420557e8 FB |
323 | { |
324 | m48t08_t *s; | |
e80cfcfc | 325 | int mem_index; |
420557e8 FB |
326 | |
327 | s = qemu_mallocz(sizeof(m48t08_t)); | |
328 | if (!s) | |
329 | return NULL; | |
330 | s->buffer = qemu_mallocz(size); | |
331 | if (!s->buffer) { | |
332 | qemu_free(s); | |
333 | return NULL; | |
334 | } | |
420557e8 | 335 | if (mem_base != 0) { |
e80cfcfc FB |
336 | mem_index = cpu_register_io_memory(0, nvram_read, nvram_write, s); |
337 | cpu_register_physical_memory(mem_base, 0x2000, mem_index); | |
420557e8 | 338 | } |
420557e8 | 339 | |
e80cfcfc FB |
340 | register_savevm("nvram", mem_base, 1, nvram_save, nvram_load, s); |
341 | qemu_register_reset(m48t08_reset, s); | |
420557e8 FB |
342 | return s; |
343 | } | |
344 | ||
345 | #if 0 | |
346 | struct idprom | |
347 | { | |
348 | unsigned char id_format; /* Format identifier (always 0x01) */ | |
349 | unsigned char id_machtype; /* Machine type */ | |
350 | unsigned char id_ethaddr[6]; /* Hardware ethernet address */ | |
351 | long id_date; /* Date of manufacture */ | |
352 | unsigned int id_sernum:24; /* Unique serial number */ | |
353 | unsigned char id_cksum; /* Checksum - xor of the data bytes */ | |
354 | unsigned char reserved[16]; | |
355 | }; | |
356 | #endif |