]>
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 | ||
35 | #define NVRAM_MAX_MEM 0xfff0 | |
36 | ||
37 | struct m48t08_t { | |
38 | /* Hardware parameters */ | |
39 | int mem_index; | |
40 | uint32_t mem_base; | |
41 | uint16_t size; | |
42 | /* RTC management */ | |
43 | time_t time_offset; | |
44 | time_t stop_time; | |
45 | /* NVRAM storage */ | |
46 | uint8_t lock; | |
47 | uint16_t addr; | |
48 | uint8_t *buffer; | |
49 | }; | |
50 | ||
51 | /* Fake timer functions */ | |
52 | /* Generic helpers for BCD */ | |
53 | static inline uint8_t toBCD (uint8_t value) | |
54 | { | |
55 | return (((value / 10) % 10) << 4) | (value % 10); | |
56 | } | |
57 | ||
58 | static inline uint8_t fromBCD (uint8_t BCD) | |
59 | { | |
60 | return ((BCD >> 4) * 10) + (BCD & 0x0F); | |
61 | } | |
62 | ||
63 | /* RTC management helpers */ | |
64 | static void get_time (m48t08_t *NVRAM, struct tm *tm) | |
65 | { | |
66 | time_t t; | |
67 | ||
68 | t = time(NULL) + NVRAM->time_offset; | |
69 | #ifdef _WIN32 | |
70 | memcpy(tm,localtime(&t),sizeof(*tm)); | |
71 | #else | |
72 | localtime_r (&t, tm) ; | |
73 | #endif | |
74 | } | |
75 | ||
76 | static void set_time (m48t08_t *NVRAM, struct tm *tm) | |
77 | { | |
78 | time_t now, new_time; | |
79 | ||
80 | new_time = mktime(tm); | |
81 | now = time(NULL); | |
82 | NVRAM->time_offset = new_time - now; | |
83 | } | |
84 | ||
85 | /* Direct access to NVRAM */ | |
86 | void m48t08_write (m48t08_t *NVRAM, uint32_t val) | |
87 | { | |
88 | struct tm tm; | |
89 | int tmp; | |
90 | ||
91 | if (NVRAM->addr > NVRAM_MAX_MEM && NVRAM->addr < 0x2000) | |
92 | NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, NVRAM->addr, val); | |
93 | switch (NVRAM->addr) { | |
94 | case 0x1FF8: | |
95 | /* control */ | |
96 | NVRAM->buffer[0x1FF8] = (val & ~0xA0) | 0x90; | |
97 | break; | |
98 | case 0x1FF9: | |
99 | /* seconds (BCD) */ | |
100 | tmp = fromBCD(val & 0x7F); | |
101 | if (tmp >= 0 && tmp <= 59) { | |
102 | get_time(NVRAM, &tm); | |
103 | tm.tm_sec = tmp; | |
104 | set_time(NVRAM, &tm); | |
105 | } | |
106 | if ((val & 0x80) ^ (NVRAM->buffer[0x1FF9] & 0x80)) { | |
107 | if (val & 0x80) { | |
108 | NVRAM->stop_time = time(NULL); | |
109 | } else { | |
110 | NVRAM->time_offset += NVRAM->stop_time - time(NULL); | |
111 | NVRAM->stop_time = 0; | |
112 | } | |
113 | } | |
114 | NVRAM->buffer[0x1FF9] = val & 0x80; | |
115 | break; | |
116 | case 0x1FFA: | |
117 | /* minutes (BCD) */ | |
118 | tmp = fromBCD(val & 0x7F); | |
119 | if (tmp >= 0 && tmp <= 59) { | |
120 | get_time(NVRAM, &tm); | |
121 | tm.tm_min = tmp; | |
122 | set_time(NVRAM, &tm); | |
123 | } | |
124 | break; | |
125 | case 0x1FFB: | |
126 | /* hours (BCD) */ | |
127 | tmp = fromBCD(val & 0x3F); | |
128 | if (tmp >= 0 && tmp <= 23) { | |
129 | get_time(NVRAM, &tm); | |
130 | tm.tm_hour = tmp; | |
131 | set_time(NVRAM, &tm); | |
132 | } | |
133 | break; | |
134 | case 0x1FFC: | |
135 | /* day of the week / century */ | |
136 | tmp = fromBCD(val & 0x07); | |
137 | get_time(NVRAM, &tm); | |
138 | tm.tm_wday = tmp; | |
139 | set_time(NVRAM, &tm); | |
140 | NVRAM->buffer[0x1FFC] = val & 0x40; | |
141 | break; | |
142 | case 0x1FFD: | |
143 | /* date */ | |
144 | tmp = fromBCD(val & 0x1F); | |
145 | if (tmp != 0) { | |
146 | get_time(NVRAM, &tm); | |
147 | tm.tm_mday = tmp; | |
148 | set_time(NVRAM, &tm); | |
149 | } | |
150 | break; | |
151 | case 0x1FFE: | |
152 | /* month */ | |
153 | tmp = fromBCD(val & 0x1F); | |
154 | if (tmp >= 1 && tmp <= 12) { | |
155 | get_time(NVRAM, &tm); | |
156 | tm.tm_mon = tmp - 1; | |
157 | set_time(NVRAM, &tm); | |
158 | } | |
159 | break; | |
160 | case 0x1FFF: | |
161 | /* year */ | |
162 | tmp = fromBCD(val); | |
163 | if (tmp >= 0 && tmp <= 99) { | |
164 | get_time(NVRAM, &tm); | |
165 | tm.tm_year = fromBCD(val); | |
166 | set_time(NVRAM, &tm); | |
167 | } | |
168 | break; | |
169 | default: | |
170 | /* Check lock registers state */ | |
171 | if (NVRAM->addr >= 0x20 && NVRAM->addr <= 0x2F && (NVRAM->lock & 1)) | |
172 | break; | |
173 | if (NVRAM->addr >= 0x30 && NVRAM->addr <= 0x3F && (NVRAM->lock & 2)) | |
174 | break; | |
175 | if (NVRAM->addr < NVRAM_MAX_MEM || | |
176 | (NVRAM->addr > 0x1FFF && NVRAM->addr < NVRAM->size)) { | |
177 | NVRAM->buffer[NVRAM->addr] = val & 0xFF; | |
178 | } | |
179 | break; | |
180 | } | |
181 | } | |
182 | ||
183 | uint32_t m48t08_read (m48t08_t *NVRAM) | |
184 | { | |
185 | struct tm tm; | |
186 | uint32_t retval = 0xFF; | |
187 | ||
188 | switch (NVRAM->addr) { | |
189 | case 0x1FF8: | |
190 | /* control */ | |
191 | goto do_read; | |
192 | case 0x1FF9: | |
193 | /* seconds (BCD) */ | |
194 | get_time(NVRAM, &tm); | |
195 | retval = (NVRAM->buffer[0x1FF9] & 0x80) | toBCD(tm.tm_sec); | |
196 | break; | |
197 | case 0x1FFA: | |
198 | /* minutes (BCD) */ | |
199 | get_time(NVRAM, &tm); | |
200 | retval = toBCD(tm.tm_min); | |
201 | break; | |
202 | case 0x1FFB: | |
203 | /* hours (BCD) */ | |
204 | get_time(NVRAM, &tm); | |
205 | retval = toBCD(tm.tm_hour); | |
206 | break; | |
207 | case 0x1FFC: | |
208 | /* day of the week / century */ | |
209 | get_time(NVRAM, &tm); | |
210 | retval = NVRAM->buffer[0x1FFC] | tm.tm_wday; | |
211 | break; | |
212 | case 0x1FFD: | |
213 | /* date */ | |
214 | get_time(NVRAM, &tm); | |
215 | retval = toBCD(tm.tm_mday); | |
216 | break; | |
217 | case 0x1FFE: | |
218 | /* month */ | |
219 | get_time(NVRAM, &tm); | |
220 | retval = toBCD(tm.tm_mon + 1); | |
221 | break; | |
222 | case 0x1FFF: | |
223 | /* year */ | |
224 | get_time(NVRAM, &tm); | |
225 | retval = toBCD(tm.tm_year); | |
226 | break; | |
227 | default: | |
228 | /* Check lock registers state */ | |
229 | if (NVRAM->addr >= 0x20 && NVRAM->addr <= 0x2F && (NVRAM->lock & 1)) | |
230 | break; | |
231 | if (NVRAM->addr >= 0x30 && NVRAM->addr <= 0x3F && (NVRAM->lock & 2)) | |
232 | break; | |
233 | if (NVRAM->addr < NVRAM_MAX_MEM || | |
234 | (NVRAM->addr > 0x1FFF && NVRAM->addr < NVRAM->size)) { | |
235 | do_read: | |
236 | retval = NVRAM->buffer[NVRAM->addr]; | |
237 | } | |
238 | break; | |
239 | } | |
240 | if (NVRAM->addr > NVRAM_MAX_MEM + 1 && NVRAM->addr < 0x2000) | |
241 | NVRAM_PRINTF("0x%08x <= 0x%08x\n", NVRAM->addr, retval); | |
242 | ||
243 | return retval; | |
244 | } | |
245 | ||
246 | void m48t08_set_addr (m48t08_t *NVRAM, uint32_t addr) | |
247 | { | |
248 | NVRAM->addr = addr; | |
249 | } | |
250 | ||
251 | void m48t08_toggle_lock (m48t08_t *NVRAM, int lock) | |
252 | { | |
253 | NVRAM->lock ^= 1 << lock; | |
254 | } | |
255 | ||
256 | static void nvram_writeb (void *opaque, target_phys_addr_t addr, uint32_t value) | |
257 | { | |
258 | m48t08_t *NVRAM = opaque; | |
259 | ||
260 | addr -= NVRAM->mem_base; | |
261 | if (addr < NVRAM_MAX_MEM) | |
262 | NVRAM->buffer[addr] = value; | |
263 | } | |
264 | ||
265 | static void nvram_writew (void *opaque, target_phys_addr_t addr, uint32_t value) | |
266 | { | |
267 | m48t08_t *NVRAM = opaque; | |
268 | ||
269 | addr -= NVRAM->mem_base; | |
270 | if (addr < NVRAM_MAX_MEM) { | |
271 | NVRAM->buffer[addr] = value >> 8; | |
272 | NVRAM->buffer[addr + 1] = value; | |
273 | } | |
274 | } | |
275 | ||
276 | static void nvram_writel (void *opaque, target_phys_addr_t addr, uint32_t value) | |
277 | { | |
278 | m48t08_t *NVRAM = opaque; | |
279 | ||
280 | addr -= NVRAM->mem_base; | |
281 | if (addr < NVRAM_MAX_MEM) { | |
282 | NVRAM->buffer[addr] = value >> 24; | |
283 | NVRAM->buffer[addr + 1] = value >> 16; | |
284 | NVRAM->buffer[addr + 2] = value >> 8; | |
285 | NVRAM->buffer[addr + 3] = value; | |
286 | } | |
287 | } | |
288 | ||
289 | static uint32_t nvram_readb (void *opaque, target_phys_addr_t addr) | |
290 | { | |
291 | m48t08_t *NVRAM = opaque; | |
292 | uint32_t retval = 0; | |
293 | ||
294 | addr -= NVRAM->mem_base; | |
295 | if (addr < NVRAM_MAX_MEM) | |
296 | retval = NVRAM->buffer[addr]; | |
297 | ||
298 | return retval; | |
299 | } | |
300 | ||
301 | static uint32_t nvram_readw (void *opaque, target_phys_addr_t addr) | |
302 | { | |
303 | m48t08_t *NVRAM = opaque; | |
304 | uint32_t retval = 0; | |
305 | ||
306 | addr -= NVRAM->mem_base; | |
307 | if (addr < NVRAM_MAX_MEM) { | |
308 | retval = NVRAM->buffer[addr] << 8; | |
309 | retval |= NVRAM->buffer[addr + 1]; | |
310 | } | |
311 | ||
312 | return retval; | |
313 | } | |
314 | ||
315 | static uint32_t nvram_readl (void *opaque, target_phys_addr_t addr) | |
316 | { | |
317 | m48t08_t *NVRAM = opaque; | |
318 | uint32_t retval = 0; | |
319 | ||
320 | addr -= NVRAM->mem_base; | |
321 | if (addr < NVRAM_MAX_MEM) { | |
322 | retval = NVRAM->buffer[addr] << 24; | |
323 | retval |= NVRAM->buffer[addr + 1] << 16; | |
324 | retval |= NVRAM->buffer[addr + 2] << 8; | |
325 | retval |= NVRAM->buffer[addr + 3]; | |
326 | } | |
327 | ||
328 | return retval; | |
329 | } | |
330 | ||
331 | static CPUWriteMemoryFunc *nvram_write[] = { | |
332 | &nvram_writeb, | |
333 | &nvram_writew, | |
334 | &nvram_writel, | |
335 | }; | |
336 | ||
337 | static CPUReadMemoryFunc *nvram_read[] = { | |
338 | &nvram_readb, | |
339 | &nvram_readw, | |
340 | &nvram_readl, | |
341 | }; | |
342 | ||
343 | /* Initialisation routine */ | |
8d5f07fa | 344 | m48t08_t *m48t08_init(uint32_t mem_base, uint16_t size, uint8_t *macaddr) |
420557e8 FB |
345 | { |
346 | m48t08_t *s; | |
347 | int i; | |
348 | unsigned char tmp = 0; | |
349 | ||
350 | s = qemu_mallocz(sizeof(m48t08_t)); | |
351 | if (!s) | |
352 | return NULL; | |
353 | s->buffer = qemu_mallocz(size); | |
354 | if (!s->buffer) { | |
355 | qemu_free(s); | |
356 | return NULL; | |
357 | } | |
358 | s->size = size; | |
359 | s->mem_base = mem_base; | |
360 | s->addr = 0; | |
361 | if (mem_base != 0) { | |
362 | s->mem_index = cpu_register_io_memory(0, nvram_read, nvram_write, s); | |
363 | cpu_register_physical_memory(mem_base, 0x4000, s->mem_index); | |
364 | } | |
365 | s->lock = 0; | |
366 | ||
367 | i = 0x1fd8; | |
368 | s->buffer[i++] = 0x01; | |
369 | s->buffer[i++] = 0x80; /* Sun4m OBP */ | |
8d5f07fa | 370 | memcpy(&s->buffer[i], macaddr, 6); |
420557e8 FB |
371 | |
372 | /* Calculate checksum */ | |
373 | for (i = 0x1fd8; i < 0x1fe7; i++) { | |
374 | tmp ^= s->buffer[i]; | |
375 | } | |
376 | s->buffer[0x1fe7] = tmp; | |
377 | return s; | |
378 | } | |
379 | ||
380 | #if 0 | |
381 | struct idprom | |
382 | { | |
383 | unsigned char id_format; /* Format identifier (always 0x01) */ | |
384 | unsigned char id_machtype; /* Machine type */ | |
385 | unsigned char id_ethaddr[6]; /* Hardware ethernet address */ | |
386 | long id_date; /* Date of manufacture */ | |
387 | unsigned int id_sernum:24; /* Unique serial number */ | |
388 | unsigned char id_cksum; /* Checksum - xor of the data bytes */ | |
389 | unsigned char reserved[16]; | |
390 | }; | |
391 | #endif |