ds1338.c 5.5 KB
Newer Older
P
Paul Brook 已提交
1 2 3 4 5 6
/*
 * MAXIM DS1338 I2C RTC+NVRAM
 *
 * Copyright (c) 2009 CodeSourcery.
 * Written by Paul Brook
 *
M
Matthew Fernandez 已提交
7
 * This code is licensed under the GNU GPL v2.
8 9 10
 *
 * Contributions after 2012-01-13 are licensed under the terms of the
 * GNU GPL, version 2 or (at your option) any later version.
P
Paul Brook 已提交
11 12 13 14
 */

#include "i2c.h"

15 16 17 18 19
/* Size of NVRAM including both the user-accessible area and the
 * secondary register area.
 */
#define NVRAM_SIZE 64

20 21 22 23 24 25
/* Flags definitions */
#define SECONDS_CH 0x80
#define HOURS_12   0x40
#define HOURS_PM   0x20
#define CTRL_OSF   0x20

P
Paul Brook 已提交
26
typedef struct {
27
    I2CSlave i2c;
28
    int64_t offset;
29
    uint8_t nvram[NVRAM_SIZE];
30 31
    int32_t ptr;
    bool addr_byte;
P
Paul Brook 已提交
32 33
} DS1338State;

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
static const VMStateDescription vmstate_ds1338 = {
    .name = "ds1338",
    .version_id = 1,
    .minimum_version_id = 1,
    .minimum_version_id_old = 1,
    .fields = (VMStateField[]) {
        VMSTATE_I2C_SLAVE(i2c, DS1338State),
        VMSTATE_INT64(offset, DS1338State),
        VMSTATE_UINT8_ARRAY(nvram, DS1338State, NVRAM_SIZE),
        VMSTATE_INT32(ptr, DS1338State),
        VMSTATE_BOOL(addr_byte, DS1338State),
        VMSTATE_END_OF_LIST()
    }
};

49 50 51 52 53
static void capture_current_time(DS1338State *s)
{
    /* Capture the current time into the secondary registers
     * which will be actually read by the data transfer operation.
     */
54 55 56 57
    struct tm now;
    qemu_get_timedate(&now, s->offset);
    s->nvram[0] = to_bcd(now.tm_sec);
    s->nvram[1] = to_bcd(now.tm_min);
58 59 60 61 62 63 64 65 66
    if (s->nvram[2] & HOURS_12) {
        int tmp = now.tm_hour;
        if (tmp == 0) {
            tmp = 24;
        }
        if (tmp <= 12) {
            s->nvram[2] = HOURS_12 | to_bcd(tmp);
        } else {
            s->nvram[2] = HOURS_12 | HOURS_PM | to_bcd(tmp - 12);
67 68
        }
    } else {
69
        s->nvram[2] = to_bcd(now.tm_hour);
70
    }
71
    s->nvram[3] = to_bcd(now.tm_wday + 1);
72
    s->nvram[4] = to_bcd(now.tm_mday);
73
    s->nvram[5] = to_bcd(now.tm_mon + 1);
74
    s->nvram[6] = to_bcd(now.tm_year - 100);
75 76 77 78 79 80 81 82 83 84 85 86 87 88
}

static void inc_regptr(DS1338State *s)
{
    /* The register pointer wraps around after 0x3F; wraparound
     * causes the current time/date to be retransferred into
     * the secondary registers.
     */
    s->ptr = (s->ptr + 1) & (NVRAM_SIZE - 1);
    if (!s->ptr) {
        capture_current_time(s);
    }
}

89
static void ds1338_event(I2CSlave *i2c, enum i2c_event event)
P
Paul Brook 已提交
90 91 92 93 94
{
    DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c);

    switch (event) {
    case I2C_START_RECV:
95 96 97 98 99 100
        /* In h/w, capture happens on any START condition, not just a
         * START_RECV, but there is no need to actually capture on
         * START_SEND, because the guest can't get at that data
         * without going through a START_RECV which would overwrite it.
         */
        capture_current_time(s);
P
Paul Brook 已提交
101 102
        break;
    case I2C_START_SEND:
103
        s->addr_byte = true;
P
Paul Brook 已提交
104 105 106 107 108 109
        break;
    default:
        break;
    }
}

110
static int ds1338_recv(I2CSlave *i2c)
P
Paul Brook 已提交
111 112 113 114 115
{
    DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c);
    uint8_t res;

    res  = s->nvram[s->ptr];
116
    inc_regptr(s);
P
Paul Brook 已提交
117 118 119
    return res;
}

120
static int ds1338_send(I2CSlave *i2c, uint8_t data)
P
Paul Brook 已提交
121 122 123
{
    DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c);
    if (s->addr_byte) {
124
        s->ptr = data & (NVRAM_SIZE - 1);
125
        s->addr_byte = false;
P
Paul Brook 已提交
126 127
        return 0;
    }
128
    if (s->ptr < 8) {
129 130
        struct tm now;
        qemu_get_timedate(&now, s->offset);
131
        switch(s->ptr) {
P
Paul Brook 已提交
132 133
        case 0:
            /* TODO: Implement CH (stop) bit.  */
134
            now.tm_sec = from_bcd(data & 0x7f);
P
Paul Brook 已提交
135 136
            break;
        case 1:
137
            now.tm_min = from_bcd(data & 0x7f);
P
Paul Brook 已提交
138 139
            break;
        case 2:
140 141 142 143 144 145 146
            if (data & HOURS_12) {
                int tmp = from_bcd(data & (HOURS_PM - 1));
                if (data & HOURS_PM) {
                    tmp += 12;
                }
                if (tmp == 24) {
                    tmp = 0;
P
Paul Brook 已提交
147
                }
148
                now.tm_hour = tmp;
P
Paul Brook 已提交
149
            } else {
150
                now.tm_hour = from_bcd(data & (HOURS_12 - 1));
P
Paul Brook 已提交
151 152 153
            }
            break;
        case 3:
154
            now.tm_wday = from_bcd(data & 7) - 1;
P
Paul Brook 已提交
155 156
            break;
        case 4:
157
            now.tm_mday = from_bcd(data & 0x3f);
P
Paul Brook 已提交
158 159
            break;
        case 5:
160
            now.tm_mon = from_bcd(data & 0x1f) - 1;
161
            break;
P
Paul Brook 已提交
162
        case 6:
163
            now.tm_year = from_bcd(data) + 100;
P
Paul Brook 已提交
164 165 166 167 168
            break;
        case 7:
            /* Control register. Currently ignored.  */
            break;
        }
169
        s->offset = qemu_timedate_diff(&now);
170 171
    } else {
        s->nvram[s->ptr] = data;
P
Paul Brook 已提交
172
    }
173
    inc_regptr(s);
P
Paul Brook 已提交
174 175 176
    return 0;
}

177
static int ds1338_init(I2CSlave *i2c)
P
Paul Brook 已提交
178 179 180 181
{
    return 0;
}

182 183 184 185 186 187 188 189 190 191 192
static void ds1338_reset(DeviceState *dev)
{
    DS1338State *s = FROM_I2C_SLAVE(DS1338State, I2C_SLAVE_FROM_QDEV(dev));

    /* The clock is running and synchronized with the host */
    s->offset = 0;
    memset(s->nvram, 0, NVRAM_SIZE);
    s->ptr = 0;
    s->addr_byte = false;
}

193 194
static void ds1338_class_init(ObjectClass *klass, void *data)
{
195
    DeviceClass *dc = DEVICE_CLASS(klass);
196 197 198 199 200 201
    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);

    k->init = ds1338_init;
    k->event = ds1338_event;
    k->recv = ds1338_recv;
    k->send = ds1338_send;
202
    dc->reset = ds1338_reset;
203
    dc->vmsd = &vmstate_ds1338;
204 205
}

206 207 208 209 210
static TypeInfo ds1338_info = {
    .name          = "ds1338",
    .parent        = TYPE_I2C_SLAVE,
    .instance_size = sizeof(DS1338State),
    .class_init    = ds1338_class_init,
P
Paul Brook 已提交
211 212
};

A
Andreas Färber 已提交
213
static void ds1338_register_types(void)
P
Paul Brook 已提交
214
{
215
    type_register_static(&ds1338_info);
P
Paul Brook 已提交
216 217
}

A
Andreas Färber 已提交
218
type_init(ds1338_register_types)