164e85ffdSPhilippe Mathieu-Daudé /*
264e85ffdSPhilippe Mathieu-Daudé * MAXIM DS1338 I2C RTC+NVRAM
364e85ffdSPhilippe Mathieu-Daudé *
464e85ffdSPhilippe Mathieu-Daudé * Copyright (c) 2009 CodeSourcery.
564e85ffdSPhilippe Mathieu-Daudé * Written by Paul Brook
664e85ffdSPhilippe Mathieu-Daudé *
764e85ffdSPhilippe Mathieu-Daudé * This code is licensed under the GNU GPL v2.
864e85ffdSPhilippe Mathieu-Daudé *
964e85ffdSPhilippe Mathieu-Daudé * Contributions after 2012-01-13 are licensed under the terms of the
1064e85ffdSPhilippe Mathieu-Daudé * GNU GPL, version 2 or (at your option) any later version.
1164e85ffdSPhilippe Mathieu-Daudé */
1264e85ffdSPhilippe Mathieu-Daudé
1364e85ffdSPhilippe Mathieu-Daudé #include "qemu/osdep.h"
1464e85ffdSPhilippe Mathieu-Daudé #include "hw/i2c/i2c.h"
1564e85ffdSPhilippe Mathieu-Daudé #include "migration/vmstate.h"
1664e85ffdSPhilippe Mathieu-Daudé #include "qemu/bcd.h"
17db1015e9SEduardo Habkost #include "qom/object.h"
182f93d8b0SPeter Maydell #include "sysemu/rtc.h"
19e8217c57SBernhard Beschow #include "trace.h"
2064e85ffdSPhilippe Mathieu-Daudé
2164e85ffdSPhilippe Mathieu-Daudé /* Size of NVRAM including both the user-accessible area and the
2264e85ffdSPhilippe Mathieu-Daudé * secondary register area.
2364e85ffdSPhilippe Mathieu-Daudé */
2464e85ffdSPhilippe Mathieu-Daudé #define NVRAM_SIZE 64
2564e85ffdSPhilippe Mathieu-Daudé
2664e85ffdSPhilippe Mathieu-Daudé /* Flags definitions */
2764e85ffdSPhilippe Mathieu-Daudé #define SECONDS_CH 0x80
2864e85ffdSPhilippe Mathieu-Daudé #define HOURS_12 0x40
2964e85ffdSPhilippe Mathieu-Daudé #define HOURS_PM 0x20
3064e85ffdSPhilippe Mathieu-Daudé #define CTRL_OSF 0x20
3164e85ffdSPhilippe Mathieu-Daudé
3264e85ffdSPhilippe Mathieu-Daudé #define TYPE_DS1338 "ds1338"
338063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(DS1338State, DS1338)
3464e85ffdSPhilippe Mathieu-Daudé
35db1015e9SEduardo Habkost struct DS1338State {
3664e85ffdSPhilippe Mathieu-Daudé I2CSlave parent_obj;
3764e85ffdSPhilippe Mathieu-Daudé
3864e85ffdSPhilippe Mathieu-Daudé int64_t offset;
3964e85ffdSPhilippe Mathieu-Daudé uint8_t wday_offset;
4064e85ffdSPhilippe Mathieu-Daudé uint8_t nvram[NVRAM_SIZE];
4164e85ffdSPhilippe Mathieu-Daudé int32_t ptr;
4264e85ffdSPhilippe Mathieu-Daudé bool addr_byte;
43db1015e9SEduardo Habkost };
4464e85ffdSPhilippe Mathieu-Daudé
4564e85ffdSPhilippe Mathieu-Daudé static const VMStateDescription vmstate_ds1338 = {
4664e85ffdSPhilippe Mathieu-Daudé .name = "ds1338",
4764e85ffdSPhilippe Mathieu-Daudé .version_id = 2,
4864e85ffdSPhilippe Mathieu-Daudé .minimum_version_id = 1,
49a80cc662SRichard Henderson .fields = (const VMStateField[]) {
5064e85ffdSPhilippe Mathieu-Daudé VMSTATE_I2C_SLAVE(parent_obj, DS1338State),
5164e85ffdSPhilippe Mathieu-Daudé VMSTATE_INT64(offset, DS1338State),
5264e85ffdSPhilippe Mathieu-Daudé VMSTATE_UINT8_V(wday_offset, DS1338State, 2),
5364e85ffdSPhilippe Mathieu-Daudé VMSTATE_UINT8_ARRAY(nvram, DS1338State, NVRAM_SIZE),
5464e85ffdSPhilippe Mathieu-Daudé VMSTATE_INT32(ptr, DS1338State),
5564e85ffdSPhilippe Mathieu-Daudé VMSTATE_BOOL(addr_byte, DS1338State),
5664e85ffdSPhilippe Mathieu-Daudé VMSTATE_END_OF_LIST()
5764e85ffdSPhilippe Mathieu-Daudé }
5864e85ffdSPhilippe Mathieu-Daudé };
5964e85ffdSPhilippe Mathieu-Daudé
capture_current_time(DS1338State * s)6064e85ffdSPhilippe Mathieu-Daudé static void capture_current_time(DS1338State *s)
6164e85ffdSPhilippe Mathieu-Daudé {
6264e85ffdSPhilippe Mathieu-Daudé /* Capture the current time into the secondary registers
6364e85ffdSPhilippe Mathieu-Daudé * which will be actually read by the data transfer operation.
6464e85ffdSPhilippe Mathieu-Daudé */
6564e85ffdSPhilippe Mathieu-Daudé struct tm now;
6664e85ffdSPhilippe Mathieu-Daudé qemu_get_timedate(&now, s->offset);
6764e85ffdSPhilippe Mathieu-Daudé s->nvram[0] = to_bcd(now.tm_sec);
6864e85ffdSPhilippe Mathieu-Daudé s->nvram[1] = to_bcd(now.tm_min);
6964e85ffdSPhilippe Mathieu-Daudé if (s->nvram[2] & HOURS_12) {
7064e85ffdSPhilippe Mathieu-Daudé int tmp = now.tm_hour;
7164e85ffdSPhilippe Mathieu-Daudé if (tmp % 12 == 0) {
7264e85ffdSPhilippe Mathieu-Daudé tmp += 12;
7364e85ffdSPhilippe Mathieu-Daudé }
7464e85ffdSPhilippe Mathieu-Daudé if (tmp <= 12) {
7564e85ffdSPhilippe Mathieu-Daudé s->nvram[2] = HOURS_12 | to_bcd(tmp);
7664e85ffdSPhilippe Mathieu-Daudé } else {
7764e85ffdSPhilippe Mathieu-Daudé s->nvram[2] = HOURS_12 | HOURS_PM | to_bcd(tmp - 12);
7864e85ffdSPhilippe Mathieu-Daudé }
7964e85ffdSPhilippe Mathieu-Daudé } else {
8064e85ffdSPhilippe Mathieu-Daudé s->nvram[2] = to_bcd(now.tm_hour);
8164e85ffdSPhilippe Mathieu-Daudé }
8264e85ffdSPhilippe Mathieu-Daudé s->nvram[3] = (now.tm_wday + s->wday_offset) % 7 + 1;
8364e85ffdSPhilippe Mathieu-Daudé s->nvram[4] = to_bcd(now.tm_mday);
8464e85ffdSPhilippe Mathieu-Daudé s->nvram[5] = to_bcd(now.tm_mon + 1);
8564e85ffdSPhilippe Mathieu-Daudé s->nvram[6] = to_bcd(now.tm_year - 100);
8664e85ffdSPhilippe Mathieu-Daudé }
8764e85ffdSPhilippe Mathieu-Daudé
inc_regptr(DS1338State * s)8864e85ffdSPhilippe Mathieu-Daudé static void inc_regptr(DS1338State *s)
8964e85ffdSPhilippe Mathieu-Daudé {
9064e85ffdSPhilippe Mathieu-Daudé /* The register pointer wraps around after 0x3F; wraparound
9164e85ffdSPhilippe Mathieu-Daudé * causes the current time/date to be retransferred into
9264e85ffdSPhilippe Mathieu-Daudé * the secondary registers.
9364e85ffdSPhilippe Mathieu-Daudé */
9464e85ffdSPhilippe Mathieu-Daudé s->ptr = (s->ptr + 1) & (NVRAM_SIZE - 1);
9564e85ffdSPhilippe Mathieu-Daudé if (!s->ptr) {
9664e85ffdSPhilippe Mathieu-Daudé capture_current_time(s);
9764e85ffdSPhilippe Mathieu-Daudé }
9864e85ffdSPhilippe Mathieu-Daudé }
9964e85ffdSPhilippe Mathieu-Daudé
ds1338_event(I2CSlave * i2c,enum i2c_event event)10064e85ffdSPhilippe Mathieu-Daudé static int ds1338_event(I2CSlave *i2c, enum i2c_event event)
10164e85ffdSPhilippe Mathieu-Daudé {
10264e85ffdSPhilippe Mathieu-Daudé DS1338State *s = DS1338(i2c);
10364e85ffdSPhilippe Mathieu-Daudé
10464e85ffdSPhilippe Mathieu-Daudé switch (event) {
10564e85ffdSPhilippe Mathieu-Daudé case I2C_START_RECV:
10664e85ffdSPhilippe Mathieu-Daudé /* In h/w, capture happens on any START condition, not just a
10764e85ffdSPhilippe Mathieu-Daudé * START_RECV, but there is no need to actually capture on
10864e85ffdSPhilippe Mathieu-Daudé * START_SEND, because the guest can't get at that data
10964e85ffdSPhilippe Mathieu-Daudé * without going through a START_RECV which would overwrite it.
11064e85ffdSPhilippe Mathieu-Daudé */
11164e85ffdSPhilippe Mathieu-Daudé capture_current_time(s);
11264e85ffdSPhilippe Mathieu-Daudé break;
11364e85ffdSPhilippe Mathieu-Daudé case I2C_START_SEND:
11464e85ffdSPhilippe Mathieu-Daudé s->addr_byte = true;
11564e85ffdSPhilippe Mathieu-Daudé break;
11664e85ffdSPhilippe Mathieu-Daudé default:
11764e85ffdSPhilippe Mathieu-Daudé break;
11864e85ffdSPhilippe Mathieu-Daudé }
11964e85ffdSPhilippe Mathieu-Daudé
12064e85ffdSPhilippe Mathieu-Daudé return 0;
12164e85ffdSPhilippe Mathieu-Daudé }
12264e85ffdSPhilippe Mathieu-Daudé
ds1338_recv(I2CSlave * i2c)12364e85ffdSPhilippe Mathieu-Daudé static uint8_t ds1338_recv(I2CSlave *i2c)
12464e85ffdSPhilippe Mathieu-Daudé {
12564e85ffdSPhilippe Mathieu-Daudé DS1338State *s = DS1338(i2c);
12664e85ffdSPhilippe Mathieu-Daudé uint8_t res;
12764e85ffdSPhilippe Mathieu-Daudé
12864e85ffdSPhilippe Mathieu-Daudé res = s->nvram[s->ptr];
129e8217c57SBernhard Beschow
130e8217c57SBernhard Beschow trace_ds1338_recv(s->ptr, res);
131e8217c57SBernhard Beschow
13264e85ffdSPhilippe Mathieu-Daudé inc_regptr(s);
13364e85ffdSPhilippe Mathieu-Daudé return res;
13464e85ffdSPhilippe Mathieu-Daudé }
13564e85ffdSPhilippe Mathieu-Daudé
ds1338_send(I2CSlave * i2c,uint8_t data)13664e85ffdSPhilippe Mathieu-Daudé static int ds1338_send(I2CSlave *i2c, uint8_t data)
13764e85ffdSPhilippe Mathieu-Daudé {
13864e85ffdSPhilippe Mathieu-Daudé DS1338State *s = DS1338(i2c);
13964e85ffdSPhilippe Mathieu-Daudé
140e8217c57SBernhard Beschow trace_ds1338_send(s->ptr, data);
141e8217c57SBernhard Beschow
14264e85ffdSPhilippe Mathieu-Daudé if (s->addr_byte) {
14364e85ffdSPhilippe Mathieu-Daudé s->ptr = data & (NVRAM_SIZE - 1);
14464e85ffdSPhilippe Mathieu-Daudé s->addr_byte = false;
14564e85ffdSPhilippe Mathieu-Daudé return 0;
14664e85ffdSPhilippe Mathieu-Daudé }
14764e85ffdSPhilippe Mathieu-Daudé if (s->ptr < 7) {
14864e85ffdSPhilippe Mathieu-Daudé /* Time register. */
14964e85ffdSPhilippe Mathieu-Daudé struct tm now;
15064e85ffdSPhilippe Mathieu-Daudé qemu_get_timedate(&now, s->offset);
15164e85ffdSPhilippe Mathieu-Daudé switch(s->ptr) {
15264e85ffdSPhilippe Mathieu-Daudé case 0:
15364e85ffdSPhilippe Mathieu-Daudé /* TODO: Implement CH (stop) bit. */
15464e85ffdSPhilippe Mathieu-Daudé now.tm_sec = from_bcd(data & 0x7f);
15564e85ffdSPhilippe Mathieu-Daudé break;
15664e85ffdSPhilippe Mathieu-Daudé case 1:
15764e85ffdSPhilippe Mathieu-Daudé now.tm_min = from_bcd(data & 0x7f);
15864e85ffdSPhilippe Mathieu-Daudé break;
15964e85ffdSPhilippe Mathieu-Daudé case 2:
16064e85ffdSPhilippe Mathieu-Daudé if (data & HOURS_12) {
16164e85ffdSPhilippe Mathieu-Daudé int tmp = from_bcd(data & (HOURS_PM - 1));
16264e85ffdSPhilippe Mathieu-Daudé if (data & HOURS_PM) {
16364e85ffdSPhilippe Mathieu-Daudé tmp += 12;
16464e85ffdSPhilippe Mathieu-Daudé }
16564e85ffdSPhilippe Mathieu-Daudé if (tmp % 12 == 0) {
16664e85ffdSPhilippe Mathieu-Daudé tmp -= 12;
16764e85ffdSPhilippe Mathieu-Daudé }
16864e85ffdSPhilippe Mathieu-Daudé now.tm_hour = tmp;
16964e85ffdSPhilippe Mathieu-Daudé } else {
17064e85ffdSPhilippe Mathieu-Daudé now.tm_hour = from_bcd(data & (HOURS_12 - 1));
17164e85ffdSPhilippe Mathieu-Daudé }
17264e85ffdSPhilippe Mathieu-Daudé break;
17364e85ffdSPhilippe Mathieu-Daudé case 3:
17464e85ffdSPhilippe Mathieu-Daudé {
17564e85ffdSPhilippe Mathieu-Daudé /* The day field is supposed to contain a value in
17664e85ffdSPhilippe Mathieu-Daudé the range 1-7. Otherwise behavior is undefined.
17764e85ffdSPhilippe Mathieu-Daudé */
17864e85ffdSPhilippe Mathieu-Daudé int user_wday = (data & 7) - 1;
17964e85ffdSPhilippe Mathieu-Daudé s->wday_offset = (user_wday - now.tm_wday + 7) % 7;
18064e85ffdSPhilippe Mathieu-Daudé }
18164e85ffdSPhilippe Mathieu-Daudé break;
18264e85ffdSPhilippe Mathieu-Daudé case 4:
18364e85ffdSPhilippe Mathieu-Daudé now.tm_mday = from_bcd(data & 0x3f);
18464e85ffdSPhilippe Mathieu-Daudé break;
18564e85ffdSPhilippe Mathieu-Daudé case 5:
18664e85ffdSPhilippe Mathieu-Daudé now.tm_mon = from_bcd(data & 0x1f) - 1;
18764e85ffdSPhilippe Mathieu-Daudé break;
18864e85ffdSPhilippe Mathieu-Daudé case 6:
18964e85ffdSPhilippe Mathieu-Daudé now.tm_year = from_bcd(data) + 100;
19064e85ffdSPhilippe Mathieu-Daudé break;
19164e85ffdSPhilippe Mathieu-Daudé }
19264e85ffdSPhilippe Mathieu-Daudé s->offset = qemu_timedate_diff(&now);
19364e85ffdSPhilippe Mathieu-Daudé } else if (s->ptr == 7) {
19464e85ffdSPhilippe Mathieu-Daudé /* Control register. */
19564e85ffdSPhilippe Mathieu-Daudé
19664e85ffdSPhilippe Mathieu-Daudé /* Ensure bits 2, 3 and 6 will read back as zero. */
19764e85ffdSPhilippe Mathieu-Daudé data &= 0xB3;
19864e85ffdSPhilippe Mathieu-Daudé
19964e85ffdSPhilippe Mathieu-Daudé /* Attempting to write the OSF flag to logic 1 leaves the
20064e85ffdSPhilippe Mathieu-Daudé value unchanged. */
20164e85ffdSPhilippe Mathieu-Daudé data = (data & ~CTRL_OSF) | (data & s->nvram[s->ptr] & CTRL_OSF);
20264e85ffdSPhilippe Mathieu-Daudé
20364e85ffdSPhilippe Mathieu-Daudé s->nvram[s->ptr] = data;
20464e85ffdSPhilippe Mathieu-Daudé } else {
20564e85ffdSPhilippe Mathieu-Daudé s->nvram[s->ptr] = data;
20664e85ffdSPhilippe Mathieu-Daudé }
20764e85ffdSPhilippe Mathieu-Daudé inc_regptr(s);
20864e85ffdSPhilippe Mathieu-Daudé return 0;
20964e85ffdSPhilippe Mathieu-Daudé }
21064e85ffdSPhilippe Mathieu-Daudé
ds1338_reset(DeviceState * dev)21164e85ffdSPhilippe Mathieu-Daudé static void ds1338_reset(DeviceState *dev)
21264e85ffdSPhilippe Mathieu-Daudé {
21364e85ffdSPhilippe Mathieu-Daudé DS1338State *s = DS1338(dev);
21464e85ffdSPhilippe Mathieu-Daudé
21564e85ffdSPhilippe Mathieu-Daudé /* The clock is running and synchronized with the host */
21664e85ffdSPhilippe Mathieu-Daudé s->offset = 0;
21764e85ffdSPhilippe Mathieu-Daudé s->wday_offset = 0;
21864e85ffdSPhilippe Mathieu-Daudé memset(s->nvram, 0, NVRAM_SIZE);
21964e85ffdSPhilippe Mathieu-Daudé s->ptr = 0;
22064e85ffdSPhilippe Mathieu-Daudé s->addr_byte = false;
22164e85ffdSPhilippe Mathieu-Daudé }
22264e85ffdSPhilippe Mathieu-Daudé
ds1338_class_init(ObjectClass * klass,void * data)22364e85ffdSPhilippe Mathieu-Daudé static void ds1338_class_init(ObjectClass *klass, void *data)
22464e85ffdSPhilippe Mathieu-Daudé {
22564e85ffdSPhilippe Mathieu-Daudé DeviceClass *dc = DEVICE_CLASS(klass);
22664e85ffdSPhilippe Mathieu-Daudé I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
22764e85ffdSPhilippe Mathieu-Daudé
22864e85ffdSPhilippe Mathieu-Daudé k->event = ds1338_event;
22964e85ffdSPhilippe Mathieu-Daudé k->recv = ds1338_recv;
23064e85ffdSPhilippe Mathieu-Daudé k->send = ds1338_send;
231e3d08143SPeter Maydell device_class_set_legacy_reset(dc, ds1338_reset);
23264e85ffdSPhilippe Mathieu-Daudé dc->vmsd = &vmstate_ds1338;
23364e85ffdSPhilippe Mathieu-Daudé }
23464e85ffdSPhilippe Mathieu-Daudé
235*7a5f6badSBernhard Beschow static const TypeInfo ds1338_types[] = {
236*7a5f6badSBernhard Beschow {
23764e85ffdSPhilippe Mathieu-Daudé .name = TYPE_DS1338,
23864e85ffdSPhilippe Mathieu-Daudé .parent = TYPE_I2C_SLAVE,
23964e85ffdSPhilippe Mathieu-Daudé .instance_size = sizeof(DS1338State),
24064e85ffdSPhilippe Mathieu-Daudé .class_init = ds1338_class_init,
241*7a5f6badSBernhard Beschow },
24264e85ffdSPhilippe Mathieu-Daudé };
24364e85ffdSPhilippe Mathieu-Daudé
244*7a5f6badSBernhard Beschow DEFINE_TYPES(ds1338_types)
245