Statistics
| Branch: | Revision:

root / hw / ds1338.c @ bf3bc4c4

History | View | Annotate | Download (4.9 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 "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
typedef struct {
21
    I2CSlave i2c;
22
    int64_t offset;
23
    uint8_t nvram[NVRAM_SIZE];
24
    int32_t ptr;
25
    bool addr_byte;
26
} DS1338State;
27

    
28
static const VMStateDescription vmstate_ds1338 = {
29
    .name = "ds1338",
30
    .version_id = 1,
31
    .minimum_version_id = 1,
32
    .minimum_version_id_old = 1,
33
    .fields = (VMStateField[]) {
34
        VMSTATE_I2C_SLAVE(i2c, DS1338State),
35
        VMSTATE_INT64(offset, DS1338State),
36
        VMSTATE_UINT8_ARRAY(nvram, DS1338State, NVRAM_SIZE),
37
        VMSTATE_INT32(ptr, DS1338State),
38
        VMSTATE_BOOL(addr_byte, DS1338State),
39
        VMSTATE_END_OF_LIST()
40
    }
41
};
42

    
43
static void capture_current_time(DS1338State *s)
44
{
45
    /* Capture the current time into the secondary registers
46
     * which will be actually read by the data transfer operation.
47
     */
48
    struct tm now;
49
    qemu_get_timedate(&now, s->offset);
50
    s->nvram[0] = to_bcd(now.tm_sec);
51
    s->nvram[1] = to_bcd(now.tm_min);
52
    if (s->nvram[2] & 0x40) {
53
        s->nvram[2] = (to_bcd((now.tm_hour % 12)) + 1) | 0x40;
54
        if (now.tm_hour >= 12) {
55
            s->nvram[2] |= 0x20;
56
        }
57
    } else {
58
        s->nvram[2] = to_bcd(now.tm_hour);
59
    }
60
    s->nvram[3] = to_bcd(now.tm_wday) + 1;
61
    s->nvram[4] = to_bcd(now.tm_mday);
62
    s->nvram[5] = to_bcd(now.tm_mon) + 1;
63
    s->nvram[6] = to_bcd(now.tm_year - 100);
64
}
65

    
66
static void inc_regptr(DS1338State *s)
67
{
68
    /* The register pointer wraps around after 0x3F; wraparound
69
     * causes the current time/date to be retransferred into
70
     * the secondary registers.
71
     */
72
    s->ptr = (s->ptr + 1) & (NVRAM_SIZE - 1);
73
    if (!s->ptr) {
74
        capture_current_time(s);
75
    }
76
}
77

    
78
static void ds1338_event(I2CSlave *i2c, enum i2c_event event)
79
{
80
    DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c);
81

    
82
    switch (event) {
83
    case I2C_START_RECV:
84
        /* In h/w, capture happens on any START condition, not just a
85
         * START_RECV, but there is no need to actually capture on
86
         * START_SEND, because the guest can't get at that data
87
         * without going through a START_RECV which would overwrite it.
88
         */
89
        capture_current_time(s);
90
        break;
91
    case I2C_START_SEND:
92
        s->addr_byte = true;
93
        break;
94
    default:
95
        break;
96
    }
97
}
98

    
99
static int ds1338_recv(I2CSlave *i2c)
100
{
101
    DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c);
102
    uint8_t res;
103

    
104
    res  = s->nvram[s->ptr];
105
    inc_regptr(s);
106
    return res;
107
}
108

    
109
static int ds1338_send(I2CSlave *i2c, uint8_t data)
110
{
111
    DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c);
112
    if (s->addr_byte) {
113
        s->ptr = data & (NVRAM_SIZE - 1);
114
        s->addr_byte = false;
115
        return 0;
116
    }
117
    if (s->ptr < 8) {
118
        struct tm now;
119
        qemu_get_timedate(&now, s->offset);
120
        switch(s->ptr) {
121
        case 0:
122
            /* TODO: Implement CH (stop) bit.  */
123
            now.tm_sec = from_bcd(data & 0x7f);
124
            break;
125
        case 1:
126
            now.tm_min = from_bcd(data & 0x7f);
127
            break;
128
        case 2:
129
            if (data & 0x40) {
130
                if (data & 0x20) {
131
                    data = from_bcd(data & 0x4f) + 11;
132
                } else {
133
                    data = from_bcd(data & 0x1f) - 1;
134
                }
135
            } else {
136
                data = from_bcd(data);
137
            }
138
            now.tm_hour = data;
139
            break;
140
        case 3:
141
            now.tm_wday = from_bcd(data & 7) - 1;
142
            break;
143
        case 4:
144
            now.tm_mday = from_bcd(data & 0x3f);
145
            break;
146
        case 5:
147
            now.tm_mon = from_bcd(data & 0x1f) - 1;
148
            break;
149
        case 6:
150
            now.tm_year = from_bcd(data) + 100;
151
            break;
152
        case 7:
153
            /* Control register. Currently ignored.  */
154
            break;
155
        }
156
        s->offset = qemu_timedate_diff(&now);
157
    } else {
158
        s->nvram[s->ptr] = data;
159
    }
160
    inc_regptr(s);
161
    return 0;
162
}
163

    
164
static int ds1338_init(I2CSlave *i2c)
165
{
166
    return 0;
167
}
168

    
169
static void ds1338_class_init(ObjectClass *klass, void *data)
170
{
171
    DeviceClass *dc = DEVICE_CLASS(klass);
172
    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
173

    
174
    k->init = ds1338_init;
175
    k->event = ds1338_event;
176
    k->recv = ds1338_recv;
177
    k->send = ds1338_send;
178
    dc->vmsd = &vmstate_ds1338;
179
}
180

    
181
static TypeInfo ds1338_info = {
182
    .name          = "ds1338",
183
    .parent        = TYPE_I2C_SLAVE,
184
    .instance_size = sizeof(DS1338State),
185
    .class_init    = ds1338_class_init,
186
};
187

    
188
static void ds1338_register_types(void)
189
{
190
    type_register_static(&ds1338_info);
191
}
192

    
193
type_init(ds1338_register_types)