0.6.2-alt1.1
[qemu] / qemu-snapshot-2004-11-28_23 / 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 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 */
344 m48t08_t *m48t08_init(uint32_t mem_base, uint16_t size, uint8_t *macaddr)
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 */
370     memcpy(&s->buffer[i], macaddr, 6);
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