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