Statistics
| Branch: | Revision:

root / hw / timer / ds1338.c @ 49ab747f

History | View | Annotate | Download (6.2 kB)

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)