0945879532d5a9fae172a7eccf067ab8c5f452ad
[qemu] / qemu / hw / m48t08.c
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 0x1ff0
36 #define NVRAM_MAXADDR 0x1fff
37
38 struct m48t08_t {
39     /* RTC management */
40     time_t   time_offset;
41     time_t   stop_time;
42     /* NVRAM storage */
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 */
81 void m48t08_write (m48t08_t *NVRAM, uint32_t addr, uint8_t val)
82 {
83     struct tm tm;
84     int tmp;
85
86     addr &= NVRAM_MAXADDR;
87     switch (addr) {
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:
164         NVRAM->buffer[addr] = val & 0xFF;
165         break;
166     }
167 }
168
169 uint8_t m48t08_read (m48t08_t *NVRAM, uint32_t addr)
170 {
171     struct tm tm;
172     uint8_t retval = 0xFF;
173
174     addr &= NVRAM_MAXADDR;
175     switch (addr) {
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:
215     do_read:
216         retval = NVRAM->buffer[addr];
217         break;
218     }
219     return retval;
220 }
221
222 static void nvram_writeb (void *opaque, target_phys_addr_t addr, uint32_t value)
223 {
224     m48t08_t *NVRAM = opaque;
225     
226     m48t08_write(NVRAM, addr, value);
227 }
228
229 static void nvram_writew (void *opaque, target_phys_addr_t addr, uint32_t value)
230 {
231     m48t08_t *NVRAM = opaque;
232     
233     m48t08_write(NVRAM, addr, value);
234     m48t08_write(NVRAM, addr + 1, value >> 8);
235 }
236
237 static void nvram_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
238 {
239     m48t08_t *NVRAM = opaque;
240     
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);
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     
252     retval = m48t08_read(NVRAM, addr);
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     
261     retval = m48t08_read(NVRAM, addr) << 8;
262     retval |= m48t08_read(NVRAM, addr + 1);
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     
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);
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
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
321 /* Initialisation routine */
322 m48t08_t *m48t08_init(uint32_t mem_base, uint16_t size)
323 {
324     m48t08_t *s;
325     int mem_index;
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     }
335     if (mem_base != 0) {
336         mem_index = cpu_register_io_memory(0, nvram_read, nvram_write, s);
337         cpu_register_physical_memory(mem_base, 0x2000, mem_index);
338     }
339
340     register_savevm("nvram", mem_base, 1, nvram_save, nvram_load, s);
341     qemu_register_reset(m48t08_reset, s);
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