]> Git Repo - qemu.git/blob - hw/timer/ds1338.c
Merge remote-tracking branch 'afaerber/tags/qom-devices-for-anthony' into staging
[qemu.git] / hw / timer / ds1338.c
1 /*
2  * MAXIM DS1338 I2C RTC+NVRAM
3  *
4  * Copyright (c) 2009 CodeSourcery.
5  * Written by Paul Brook
6  *
7  * This code is licensed under the GNU GPL v2.
8  *
9  * Contributions after 2012-01-13 are licensed under the terms of the
10  * GNU GPL, version 2 or (at your option) any later version.
11  */
12
13 #include "hw/i2c/i2c.h"
14
15 /* Size of NVRAM including both the user-accessible area and the
16  * secondary register area.
17  */
18 #define NVRAM_SIZE 64
19
20 /* Flags definitions */
21 #define SECONDS_CH 0x80
22 #define HOURS_12   0x40
23 #define HOURS_PM   0x20
24 #define CTRL_OSF   0x20
25
26 typedef struct {
27     I2CSlave i2c;
28     int64_t offset;
29     uint8_t wday_offset;
30     uint8_t nvram[NVRAM_SIZE];
31     int32_t ptr;
32     bool addr_byte;
33 } DS1338State;
34
35 static const VMStateDescription vmstate_ds1338 = {
36     .name = "ds1338",
37     .version_id = 2,
38     .minimum_version_id = 1,
39     .minimum_version_id_old = 1,
40     .fields = (VMStateField[]) {
41         VMSTATE_I2C_SLAVE(i2c, DS1338State),
42         VMSTATE_INT64(offset, DS1338State),
43         VMSTATE_UINT8_V(wday_offset, DS1338State, 2),
44         VMSTATE_UINT8_ARRAY(nvram, DS1338State, NVRAM_SIZE),
45         VMSTATE_INT32(ptr, DS1338State),
46         VMSTATE_BOOL(addr_byte, DS1338State),
47         VMSTATE_END_OF_LIST()
48     }
49 };
50
51 static void capture_current_time(DS1338State *s)
52 {
53     /* Capture the current time into the secondary registers
54      * which will be actually read by the data transfer operation.
55      */
56     struct tm now;
57     qemu_get_timedate(&now, s->offset);
58     s->nvram[0] = to_bcd(now.tm_sec);
59     s->nvram[1] = to_bcd(now.tm_min);
60     if (s->nvram[2] & HOURS_12) {
61         int tmp = now.tm_hour;
62         if (tmp % 12 == 0) {
63             tmp += 12;
64         }
65         if (tmp <= 12) {
66             s->nvram[2] = HOURS_12 | to_bcd(tmp);
67         } else {
68             s->nvram[2] = HOURS_12 | HOURS_PM | to_bcd(tmp - 12);
69         }
70     } else {
71         s->nvram[2] = to_bcd(now.tm_hour);
72     }
73     s->nvram[3] = (now.tm_wday + s->wday_offset) % 7 + 1;
74     s->nvram[4] = to_bcd(now.tm_mday);
75     s->nvram[5] = to_bcd(now.tm_mon + 1);
76     s->nvram[6] = to_bcd(now.tm_year - 100);
77 }
78
79 static void inc_regptr(DS1338State *s)
80 {
81     /* The register pointer wraps around after 0x3F; wraparound
82      * causes the current time/date to be retransferred into
83      * the secondary registers.
84      */
85     s->ptr = (s->ptr + 1) & (NVRAM_SIZE - 1);
86     if (!s->ptr) {
87         capture_current_time(s);
88     }
89 }
90
91 static void ds1338_event(I2CSlave *i2c, enum i2c_event event)
92 {
93     DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c);
94
95     switch (event) {
96     case I2C_START_RECV:
97         /* In h/w, capture happens on any START condition, not just a
98          * START_RECV, but there is no need to actually capture on
99          * START_SEND, because the guest can't get at that data
100          * without going through a START_RECV which would overwrite it.
101          */
102         capture_current_time(s);
103         break;
104     case I2C_START_SEND:
105         s->addr_byte = true;
106         break;
107     default:
108         break;
109     }
110 }
111
112 static int ds1338_recv(I2CSlave *i2c)
113 {
114     DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c);
115     uint8_t res;
116
117     res  = s->nvram[s->ptr];
118     inc_regptr(s);
119     return res;
120 }
121
122 static int ds1338_send(I2CSlave *i2c, uint8_t data)
123 {
124     DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c);
125     if (s->addr_byte) {
126         s->ptr = data & (NVRAM_SIZE - 1);
127         s->addr_byte = false;
128         return 0;
129     }
130     if (s->ptr < 7) {
131         /* Time register. */
132         struct tm now;
133         qemu_get_timedate(&now, s->offset);
134         switch(s->ptr) {
135         case 0:
136             /* TODO: Implement CH (stop) bit.  */
137             now.tm_sec = from_bcd(data & 0x7f);
138             break;
139         case 1:
140             now.tm_min = from_bcd(data & 0x7f);
141             break;
142         case 2:
143             if (data & HOURS_12) {
144                 int tmp = from_bcd(data & (HOURS_PM - 1));
145                 if (data & HOURS_PM) {
146                     tmp += 12;
147                 }
148                 if (tmp % 12 == 0) {
149                     tmp -= 12;
150                 }
151                 now.tm_hour = tmp;
152             } else {
153                 now.tm_hour = from_bcd(data & (HOURS_12 - 1));
154             }
155             break;
156         case 3:
157             {
158                 /* The day field is supposed to contain a value in
159                    the range 1-7. Otherwise behavior is undefined.
160                  */
161                 int user_wday = (data & 7) - 1;
162                 s->wday_offset = (user_wday - now.tm_wday + 7) % 7;
163             }
164             break;
165         case 4:
166             now.tm_mday = from_bcd(data & 0x3f);
167             break;
168         case 5:
169             now.tm_mon = from_bcd(data & 0x1f) - 1;
170             break;
171         case 6:
172             now.tm_year = from_bcd(data) + 100;
173             break;
174         }
175         s->offset = qemu_timedate_diff(&now);
176     } else if (s->ptr == 7) {
177         /* Control register. */
178
179         /* Ensure bits 2, 3 and 6 will read back as zero. */
180         data &= 0xB3;
181
182         /* Attempting to write the OSF flag to logic 1 leaves the
183            value unchanged. */
184         data = (data & ~CTRL_OSF) | (data & s->nvram[s->ptr] & CTRL_OSF);
185
186         s->nvram[s->ptr] = data;
187     } else {
188         s->nvram[s->ptr] = data;
189     }
190     inc_regptr(s);
191     return 0;
192 }
193
194 static int ds1338_init(I2CSlave *i2c)
195 {
196     return 0;
197 }
198
199 static void ds1338_reset(DeviceState *dev)
200 {
201     DS1338State *s = FROM_I2C_SLAVE(DS1338State, I2C_SLAVE(dev));
202
203     /* The clock is running and synchronized with the host */
204     s->offset = 0;
205     s->wday_offset = 0;
206     memset(s->nvram, 0, NVRAM_SIZE);
207     s->ptr = 0;
208     s->addr_byte = false;
209 }
210
211 static void ds1338_class_init(ObjectClass *klass, void *data)
212 {
213     DeviceClass *dc = DEVICE_CLASS(klass);
214     I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
215
216     k->init = ds1338_init;
217     k->event = ds1338_event;
218     k->recv = ds1338_recv;
219     k->send = ds1338_send;
220     dc->reset = ds1338_reset;
221     dc->vmsd = &vmstate_ds1338;
222 }
223
224 static const TypeInfo ds1338_info = {
225     .name          = "ds1338",
226     .parent        = TYPE_I2C_SLAVE,
227     .instance_size = sizeof(DS1338State),
228     .class_init    = ds1338_class_init,
229 };
230
231 static void ds1338_register_types(void)
232 {
233     type_register_static(&ds1338_info);
234 }
235
236 type_init(ds1338_register_types)
This page took 0.035964 seconds and 4 git commands to generate.