]>
Commit | Line | Data |
---|---|---|
1dfe3943 PB |
1 | /* |
2 | * MAXIM DS1338 I2C RTC+NVRAM | |
3 | * | |
4 | * Copyright (c) 2009 CodeSourcery. | |
5 | * Written by Paul Brook | |
6 | * | |
8e31bf38 | 7 | * This code is licensed under the GNU GPL v2. |
6b620ca3 PB |
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. | |
1dfe3943 PB |
11 | */ |
12 | ||
13 | #include "i2c.h" | |
14 | ||
15 | typedef struct { | |
9e07bdf8 | 16 | I2CSlave i2c; |
1dfe3943 PB |
17 | time_t offset; |
18 | struct tm now; | |
19 | uint8_t nvram[56]; | |
20 | int ptr; | |
21 | int addr_byte; | |
22 | } DS1338State; | |
23 | ||
9e07bdf8 | 24 | static void ds1338_event(I2CSlave *i2c, enum i2c_event event) |
1dfe3943 PB |
25 | { |
26 | DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c); | |
27 | ||
28 | switch (event) { | |
29 | case I2C_START_RECV: | |
30 | qemu_get_timedate(&s->now, s->offset); | |
31 | s->nvram[0] = to_bcd(s->now.tm_sec); | |
32 | s->nvram[1] = to_bcd(s->now.tm_min); | |
33 | if (s->nvram[2] & 0x40) { | |
34 | s->nvram[2] = (to_bcd((s->now.tm_hour % 12)) + 1) | 0x40; | |
35 | if (s->now.tm_hour >= 12) { | |
36 | s->nvram[2] |= 0x20; | |
37 | } | |
38 | } else { | |
39 | s->nvram[2] = to_bcd(s->now.tm_hour); | |
40 | } | |
41 | s->nvram[3] = to_bcd(s->now.tm_wday) + 1; | |
42 | s->nvram[4] = to_bcd(s->now.tm_mday); | |
43 | s->nvram[5] = to_bcd(s->now.tm_mon) + 1; | |
44 | s->nvram[6] = to_bcd(s->now.tm_year - 100); | |
45 | break; | |
46 | case I2C_START_SEND: | |
47 | s->addr_byte = 1; | |
48 | break; | |
49 | default: | |
50 | break; | |
51 | } | |
52 | } | |
53 | ||
9e07bdf8 | 54 | static int ds1338_recv(I2CSlave *i2c) |
1dfe3943 PB |
55 | { |
56 | DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c); | |
57 | uint8_t res; | |
58 | ||
59 | res = s->nvram[s->ptr]; | |
60 | s->ptr = (s->ptr + 1) & 0xff; | |
61 | return res; | |
62 | } | |
63 | ||
9e07bdf8 | 64 | static int ds1338_send(I2CSlave *i2c, uint8_t data) |
1dfe3943 PB |
65 | { |
66 | DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c); | |
67 | if (s->addr_byte) { | |
68 | s->ptr = data; | |
69 | s->addr_byte = 0; | |
70 | return 0; | |
71 | } | |
72 | s->nvram[s->ptr - 8] = data; | |
73 | if (data < 8) { | |
74 | qemu_get_timedate(&s->now, s->offset); | |
75 | switch(data) { | |
76 | case 0: | |
77 | /* TODO: Implement CH (stop) bit. */ | |
78 | s->now.tm_sec = from_bcd(data & 0x7f); | |
79 | break; | |
80 | case 1: | |
81 | s->now.tm_min = from_bcd(data & 0x7f); | |
82 | break; | |
83 | case 2: | |
84 | if (data & 0x40) { | |
85 | if (data & 0x20) { | |
86 | data = from_bcd(data & 0x4f) + 11; | |
87 | } else { | |
88 | data = from_bcd(data & 0x1f) - 1; | |
89 | } | |
90 | } else { | |
91 | data = from_bcd(data); | |
92 | } | |
93 | s->now.tm_hour = data; | |
94 | break; | |
95 | case 3: | |
96 | s->now.tm_wday = from_bcd(data & 7) - 1; | |
97 | break; | |
98 | case 4: | |
99 | s->now.tm_mday = from_bcd(data & 0x3f); | |
100 | break; | |
101 | case 5: | |
102 | s->now.tm_mon = from_bcd(data & 0x1f) - 1; | |
fbac6a7d | 103 | break; |
1dfe3943 PB |
104 | case 6: |
105 | s->now.tm_year = from_bcd(data) + 100; | |
106 | break; | |
107 | case 7: | |
108 | /* Control register. Currently ignored. */ | |
109 | break; | |
110 | } | |
111 | s->offset = qemu_timedate_diff(&s->now); | |
112 | } | |
113 | s->ptr = (s->ptr + 1) & 0xff; | |
114 | return 0; | |
115 | } | |
116 | ||
9e07bdf8 | 117 | static int ds1338_init(I2CSlave *i2c) |
1dfe3943 PB |
118 | { |
119 | return 0; | |
120 | } | |
121 | ||
b5ea9327 AL |
122 | static void ds1338_class_init(ObjectClass *klass, void *data) |
123 | { | |
124 | I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); | |
125 | ||
126 | k->init = ds1338_init; | |
127 | k->event = ds1338_event; | |
128 | k->recv = ds1338_recv; | |
129 | k->send = ds1338_send; | |
130 | } | |
131 | ||
39bffca2 AL |
132 | static TypeInfo ds1338_info = { |
133 | .name = "ds1338", | |
134 | .parent = TYPE_I2C_SLAVE, | |
135 | .instance_size = sizeof(DS1338State), | |
136 | .class_init = ds1338_class_init, | |
1dfe3943 PB |
137 | }; |
138 | ||
83f7d43a | 139 | static void ds1338_register_types(void) |
1dfe3943 | 140 | { |
39bffca2 | 141 | type_register_static(&ds1338_info); |
1dfe3943 PB |
142 | } |
143 | ||
83f7d43a | 144 | type_init(ds1338_register_types) |