On 11/29/2017 11:13 PM, David Gibson wrote: > On Sun, Nov 26, 2017 at 03:59:03PM -0600, Michael Davidsaver wrote: >> Support for: ds1307, ds1337, ds1338, ds1339, >> ds1340, ds1375, ds1388, and ds3231. >> >> Tested with ds1338 and ds1375. >> >> Signed-off-by: Michael Davidsaver > > I certainly like the idea of consolidating this code, but reviewing to > see that the new code really is a generalization of the old is > something I won't have time for for a while. > > Also, hw/timer is not within my purview so it'll probably need to go > another path to merge. Could you be a bit more explicit about what, if anything, I need to do to move this forward? >> --- >> default-configs/arm-softmmu.mak | 2 +- >> hw/timer/Makefile.objs | 2 +- >> hw/timer/ds-rtc-i2c.c | 461 ++++++++++++++++++++++++++++++++++++++++ >> hw/timer/ds1338.c | 239 --------------------- >> 4 files changed, 463 insertions(+), 241 deletions(-) >> create mode 100644 hw/timer/ds-rtc-i2c.c >> delete mode 100644 hw/timer/ds1338.c >> >> diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak >> index d37edc4312..b857823681 100644 >> --- a/default-configs/arm-softmmu.mak >> +++ b/default-configs/arm-softmmu.mak >> @@ -31,7 +31,7 @@ CONFIG_SMC91C111=y >> CONFIG_ALLWINNER_EMAC=y >> CONFIG_IMX_FEC=y >> CONFIG_FTGMAC100=y >> -CONFIG_DS1338=y >> +CONFIG_DSRTCI2C=y >> CONFIG_PFLASH_CFI01=y >> CONFIG_PFLASH_CFI02=y >> CONFIG_MICRODRIVE=y >> diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs >> index 8c19eac3b6..290015ebec 100644 >> --- a/hw/timer/Makefile.objs >> +++ b/hw/timer/Makefile.objs >> @@ -3,7 +3,7 @@ common-obj-$(CONFIG_ARM_MPTIMER) += arm_mptimer.o >> common-obj-$(CONFIG_ARM_V7M) += armv7m_systick.o >> common-obj-$(CONFIG_A9_GTIMER) += a9gtimer.o >> common-obj-$(CONFIG_CADENCE) += cadence_ttc.o >> -common-obj-$(CONFIG_DS1338) += ds1338.o >> +common-obj-$(CONFIG_DSRTCI2C) += ds-rtc-i2c.o >> common-obj-$(CONFIG_HPET) += hpet.o >> common-obj-$(CONFIG_I8254) += i8254_common.o i8254.o >> common-obj-$(CONFIG_M48T59) += m48t59.o >> diff --git a/hw/timer/ds-rtc-i2c.c b/hw/timer/ds-rtc-i2c.c >> new file mode 100644 >> index 0000000000..ad2f8f2a68 >> --- /dev/null >> +++ b/hw/timer/ds-rtc-i2c.c >> @@ -0,0 +1,461 @@ >> +/* Emulation of various Dallas/Maxim RTCs accessed via I2C bus >> + * >> + * Copyright (c) 2017 Michael Davidsaver >> + * Copyright (c) 2009 CodeSourcery >> + * >> + * Authors: Michael Davidsaver >> + * Paul Brook >> + * >> + * This work is licensed under the terms of the GNU GPL, version 2. See >> + * the LICENSE file in the top-level directory. >> + * >> + * Models real time read/set and NVRAM. >> + * Does not model alarms, or control/status registers. >> + * >> + * Generalized register map is: >> + * [Current time] >> + * [Alarm settings] (optional) >> + * [Control/Status] (optional) >> + * [Non-volatile memory] (optional) >> + * >> + * The current time registers are almost always the same, >> + * with the exception being that some have a CENTURY bit >> + * in the month register. >> + */ >> +#include "qemu/osdep.h" >> +#include "qemu/log.h" >> +#include "qemu/timer.h" >> +#include "qemu/bcd.h" >> +#include "hw/hw.h" >> +#include "hw/registerfields.h" >> +#include "hw/i2c/i2c.h" >> +#include "sysemu/qtest.h" >> +#include "qemu/error-report.h" >> + >> +/* #define DEBUG_DSRTC */ >> + >> +#ifdef DEBUG_DSRTC >> +#define DPRINTK(FMT, ...) info_report(TYPE_DSRTC " : " FMT, ## __VA_ARGS__) >> +#else >> +#define DPRINTK(FMT, ...) do {} while (0) >> +#endif >> + >> +#define LOG(MSK, FMT, ...) qemu_log_mask(MSK, TYPE_DSRTC " : " FMT "\n", \ >> + ## __VA_ARGS__) >> + >> +#define DSRTC_REGSIZE (0x40) >> + >> +/* values stored in BCD */ >> +/* 00-59 */ >> +#define R_SEC (0x0) >> +/* 00-59 */ >> +#define R_MIN (0x1) >> +#define R_HOUR (0x2) >> +/* 1-7 */ >> +#define R_WDAY (0x3) >> +/* 0-31 */ >> +#define R_DATE (0x4) >> +#define R_MONTH (0x5) >> +/* 0-99 */ >> +#define R_YEAR (0x6) >> + >> +/* use 12 hour mode when set */ >> +FIELD(HOUR, SET12, 6, 1) >> +/* 00-23 */ >> +FIELD(HOUR, HOUR24, 0, 6) >> +FIELD(HOUR, AMPM, 5, 1) >> +/* 1-12 (not 0-11!) */ >> +FIELD(HOUR, HOUR12, 0, 5) >> + >> +/* 1-12 */ >> +FIELD(MONTH, MONTH, 0, 5) >> +FIELD(MONTH, CENTURY, 7, 1) >> + >> +typedef struct DSRTCInfo { >> + /* if bit 7 of the Month register is set after Y2K */ >> + bool has_century; >> + /* address of first non-volatile memory cell. >> + * nv_start >= reg_end means no NV memory. >> + */ >> + uint8_t nv_start; >> + /* total size of register range. When address counter rolls over. */ >> + uint8_t reg_size; >> +} DSRTCInfo; >> + >> +typedef struct DSRTCState { >> + I2CSlave parent_obj; >> + >> + const DSRTCInfo *info; >> + >> + qemu_irq alarm_irq; >> + >> + /* register address counter */ >> + uint8_t addr; >> + /* when writing, whether the address has been sent */ >> + bool addrd; >> + >> + int64_t time_offset; >> + int8_t wday_offset; >> + >> + uint8_t regs[DSRTC_REGSIZE]; >> +} DSRTCState; >> + >> +typedef struct DSRTCClass { >> + I2CSlaveClass parent_class; >> + >> + const DSRTCInfo *info; >> +} DSRTCClass; >> + >> +#define TYPE_DSRTC "ds-rtc-i2c" >> +#define DSRTC(obj) OBJECT_CHECK(DSRTCState, (obj), TYPE_DSRTC) >> +#define DSRTC_GET_CLASS(obj) \ >> + OBJECT_GET_CLASS(DSRTCClass, obj, TYPE_DSRTC) >> +#define DSRTC_CLASS(klass) \ >> + OBJECT_CLASS_CHECK(DSRTCClass, klass, TYPE_DSRTC) >> + >> +static const VMStateDescription vmstate_dsrtc = { >> + .name = TYPE_DSRTC, >> + .version_id = 1, >> + .minimum_version_id = 1, >> + .fields = (VMStateField[]) { >> + VMSTATE_I2C_SLAVE(parent_obj, DSRTCState), >> + VMSTATE_INT64(time_offset, DSRTCState), >> + VMSTATE_INT8_V(wday_offset, DSRTCState, 2), >> + VMSTATE_UINT8_ARRAY(regs, DSRTCState, DSRTC_REGSIZE), >> + VMSTATE_UINT8(addr, DSRTCState), >> + VMSTATE_BOOL(addrd, DSRTCState), >> + VMSTATE_END_OF_LIST() >> + } >> +}; >> + >> +static void dsrtc_reset(DeviceState *device); >> + >> +/* update current time registers */ >> +static >> +void dsrtc_latch(DSRTCState *ds) >> +{ >> + struct tm now; >> + bool use12; >> + >> + qemu_get_timedate(&now, ds->time_offset); >> + >> + DPRINTK("Current Time %3u/%2u/%u %2u:%2u:%2u (wday %u)", >> + now.tm_year, now.tm_mon, now.tm_mday, >> + now.tm_hour, now.tm_min, now.tm_sec, >> + now.tm_wday); >> + >> + use12 = ARRAY_FIELD_EX32(ds->regs, HOUR, SET12); >> + >> + /* ensure unused bits are zero */ >> + memset(ds->regs, 0, R_YEAR + 1); >> + >> + ds->regs[R_SEC] = to_bcd(now.tm_sec); >> + ds->regs[R_MIN] = to_bcd(now.tm_min); >> + >> + if (!use12) { >> + /* 24 hour (0-23) */ >> + ARRAY_FIELD_DP32(ds->regs, HOUR, HOUR24, to_bcd(now.tm_hour)); >> + } else { >> + /* 12 hour am/pm (1-12) */ >> + ARRAY_FIELD_DP32(ds->regs, HOUR, SET12, 1); >> + ARRAY_FIELD_DP32(ds->regs, HOUR, AMPM, now.tm_hour >= 12u); >> + ARRAY_FIELD_DP32(ds->regs, HOUR, HOUR12, >> + to_bcd(1u + (now.tm_hour % 12u))); >> + } >> + >> + ds->regs[R_WDAY] = (now.tm_wday + ds->wday_offset) % 7u + 1u; >> + ds->regs[R_DATE] = to_bcd(now.tm_mday); >> + >> + ARRAY_FIELD_DP32(ds->regs, MONTH, MONTH, to_bcd(now.tm_mon + 1)); >> + if (ds->info->has_century) { >> + ARRAY_FIELD_DP32(ds->regs, MONTH, CENTURY, now.tm_year >= 100u); >> + } >> + >> + ds->regs[R_YEAR] = to_bcd(now.tm_year % 100u); >> + >> + DPRINTK("Latched time"); >> +} >> + >> +/* call after guest writes to current time registers >> + * to re-compute our offset from host time. >> + */ >> +static >> +void dsrtc_update(DSRTCState *ds) >> +{ >> + int user_wday; >> + struct tm now; >> + >> + now.tm_sec = from_bcd(ds->regs[R_SEC]); >> + now.tm_min = from_bcd(ds->regs[R_MIN]); >> + >> + if (ARRAY_FIELD_EX32(ds->regs, HOUR, SET12)) { >> + /* 12 hour (1-12) */ >> + now.tm_hour = from_bcd(ARRAY_FIELD_EX32(ds->regs, HOUR, HOUR12)) - 1u; >> + if (ARRAY_FIELD_EX32(ds->regs, HOUR, AMPM)) { >> + now.tm_hour += 12; >> + } >> + >> + } else { >> + /* 23 hour (0-23) */ >> + now.tm_hour = from_bcd(ARRAY_FIELD_EX32(ds->regs, HOUR, HOUR24)); >> + } >> + >> + now.tm_wday = from_bcd(ds->regs[R_WDAY]) - 1u; >> + now.tm_mday = from_bcd(ds->regs[R_DATE]); >> + now.tm_mon = from_bcd(ARRAY_FIELD_EX32(ds->regs, MONTH, MONTH)) - 1; >> + >> + now.tm_year = from_bcd(ds->regs[R_YEAR]); >> + if (ARRAY_FIELD_EX32(ds->regs, MONTH, CENTURY) || !ds->info->has_century) { >> + now.tm_year += 100; >> + } >> + >> + DPRINTK("New Time %3u/%2u/%u %2u:%2u:%2u (wday %u)", >> + now.tm_year, now.tm_mon, now.tm_mday, >> + now.tm_hour, now.tm_min, now.tm_sec, >> + now.tm_wday); >> + >> + /* round trip to get real wday_offset based on time delta */ >> + user_wday = now.tm_wday; >> + ds->time_offset = qemu_timedate_diff(&now); >> + /* race possible if we run at midnight >> + * TODO: make qemu_timedate_diff() calculate wday offset as well? >> + */ >> + qemu_get_timedate(&now, ds->time_offset); >> + /* calculate wday_offset to achieve guest requested wday */ >> + ds->wday_offset = user_wday - now.tm_wday; >> + >> + DPRINTK("Update offset = %" PRId64 ", wday_offset = %d", >> + ds->time_offset, ds->wday_offset); >> +} >> + >> +static >> +void dsrtc_advance(DSRTCState *ds) >> +{ >> + ds->addr = (ds->addr + 1) % ds->info->reg_size; >> + if (ds->addr == 0) { >> + /* latch time on roll over */ >> + dsrtc_latch(ds); >> + } >> +} >> + >> +static >> +int dsrtc_event(I2CSlave *s, enum i2c_event event) >> +{ >> + DSRTCState *ds = DSRTC(s); >> + >> + switch (event) { >> + case I2C_START_SEND: >> + ds->addrd = false; >> + /* fall through */ >> + case I2C_START_RECV: >> + dsrtc_latch(ds); >> + /* fall through */ >> + case I2C_FINISH: >> + DPRINTK("Event %d", (int)event); >> + /* fall through */ >> + case I2C_NACK: >> + break; >> + } >> + return 0; >> +} >> + >> +static >> +int dsrtc_recv(I2CSlave *s) >> +{ >> + DSRTCState *ds = DSRTC(s); >> + int ret = 0; >> + >> + ret = ds->regs[ds->addr]; >> + >> + if (ds->addr > R_YEAR && ds->addr < ds->info->nv_start) { >> + LOG(LOG_UNIMP, "Read from unimplemented (%02x) %02x", ds->addr, ret); >> + } >> + >> + DPRINTK("Recv (%02x) %02x", ds->addr, ret); >> + >> + dsrtc_advance(ds); >> + >> + return ret; >> +} >> + >> +static >> +int dsrtc_send(I2CSlave *s, uint8_t data) >> +{ >> + DSRTCState *ds = DSRTC(s); >> + >> + if (!ds->addrd) { >> + if (data == 0xff && qtest_enabled()) { >> + /* allow test runner to zero offsets */ >> + DPRINTK("Testing reset"); >> + dsrtc_reset(DEVICE(s)); >> + return 0; >> + } >> + ds->addr = data % DSRTC_REGSIZE; >> + ds->addrd = true; >> + DPRINTK("Set address pointer %02x", data); >> + return 0; >> + } >> + >> + DPRINTK("Send (%02x) %02x", ds->addr, data); >> + >> + if (ds->addr <= R_YEAR) { >> + ds->regs[ds->addr] = data; >> + dsrtc_update(ds); >> + >> + } else if (ds->addr >= ds->info->nv_start) { >> + ds->regs[ds->addr] = data; >> + >> + } else { >> + LOG(LOG_UNIMP, "Register not modeled"); >> + } >> + >> + dsrtc_advance(ds); >> + >> + return 0; >> +} >> + >> +static >> +void dsrtc_reset(DeviceState *device) >> +{ >> + DSRTCState *ds = DSRTC(device); >> + >> + memset(ds->regs, 0, sizeof(ds->regs)); >> + >> + ds->addr = 0; >> + ds->addrd = false; >> + ds->time_offset = 0; >> + ds->wday_offset = 0; >> + >> + DPRINTK("Reset"); >> +} >> + >> +static >> +void dsrtc_realize(DeviceState *device, Error **errp) >> +{ >> + DSRTCState *ds = DSRTC(device); >> + DSRTCClass *r = DSRTC_GET_CLASS(device); >> + >> + ds->info = r->info; >> + >> + /* Alarms not yet implemented, but allow >> + * board code to wire up the alarm interrupt >> + * output anyway. >> + */ >> + qdev_init_gpio_out(device, &ds->alarm_irq, 1); >> +} >> + >> +static >> +void dsrtc_class_init(ObjectClass *klass, void *data) >> +{ >> + DeviceClass *dc = DEVICE_CLASS(klass); >> + I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); >> + DSRTCClass *r = DSRTC_CLASS(klass); >> + >> + r->info = data; >> + >> + k->event = &dsrtc_event; >> + k->recv = &dsrtc_recv; >> + k->send = &dsrtc_send; >> + >> + dc->vmsd = &vmstate_dsrtc; >> + dc->realize = dsrtc_realize; >> + dc->reset = dsrtc_reset; >> + dc->user_creatable = true; >> +} >> + >> +static >> +const TypeInfo ds_rtc_base_type = { >> + .abstract = true, >> + .name = TYPE_DSRTC, >> + .parent = TYPE_I2C_SLAVE, >> + .instance_size = sizeof(DSRTCState), >> +}; >> + >> +#define DSRTC_CONFIG(NAME) \ >> +static const TypeInfo NAME##_type = { \ >> + .name = #NAME, \ >> + .parent = TYPE_DSRTC, \ >> + .class_size = sizeof(I2CSlaveClass), \ >> + .class_init = dsrtc_class_init, \ >> + .class_data = (void *)&NAME##_info, \ >> +}; >> + >> +/* ds3231 - alarms, no eeprom */ >> +static const DSRTCInfo ds3231_info = { >> + .has_century = true, >> + .nv_start = 0x13, /* no nv memory */ >> + .reg_size = 0x13, >> +}; >> +DSRTC_CONFIG(ds3231) >> + >> +/* only model block 0 (RTC), blocks 1,2 (eeprom) not modeled. >> + * blocks have different i2c addresses >> + */ >> +static const DSRTCInfo ds1388_info = { >> + .has_century = false, >> + .nv_start = 0x0d, >> + .reg_size = 0x0d, >> +}; >> +DSRTC_CONFIG(ds1388) >> + >> +/* alarms, eeprom */ >> +static const DSRTCInfo ds1375_info = { >> + .has_century = true, >> + .nv_start = 0x10, >> + .reg_size = 0x20, >> +}; >> +DSRTC_CONFIG(ds1375) >> + >> +/* no alarms, no eeprom */ >> +static const DSRTCInfo ds1340_info = { >> + .has_century = false, >> + .nv_start = 0x10, >> + .reg_size = 0x10, >> +}; >> +DSRTC_CONFIG(ds1340) >> + >> +/* alarms, no eeprom */ >> +static const DSRTCInfo ds1339_info = { >> + .has_century = false, >> + .nv_start = 0x11, >> + .reg_size = 0x11, >> +}; >> +DSRTC_CONFIG(ds1339) >> + >> +/* no alarms, eeprom */ >> +static const DSRTCInfo ds1338_info = { >> + .has_century = false, >> + .nv_start = 0x08, >> + .reg_size = 0x40, >> +}; >> +DSRTC_CONFIG(ds1338) >> + >> +/* alarms, no eeprom */ >> +static const DSRTCInfo ds1337_info = { >> + .has_century = true, >> + .nv_start = 0x10, >> + .reg_size = 0x10, >> +}; >> +DSRTC_CONFIG(ds1337) >> + >> +/* ds1307 registers are identical to ds1338 */ >> +static >> +const TypeInfo ds1307_type = { >> + .name = "ds1307", >> + .parent = "ds1338", >> +}; >> + >> +static void ds_rtc_i2c_register(void) >> +{ >> + type_register_static(&ds_rtc_base_type); >> + type_register_static(&ds3231_type); >> + type_register_static(&ds1388_type); >> + type_register_static(&ds1375_type); >> + type_register_static(&ds1340_type); >> + type_register_static(&ds1339_type); >> + type_register_static(&ds1338_type); >> + type_register_static(&ds1337_type); >> + type_register_static(&ds1307_type); >> +} >> + >> +type_init(ds_rtc_i2c_register) >> diff --git a/hw/timer/ds1338.c b/hw/timer/ds1338.c >> deleted file mode 100644 >> index 3849b74a68..0000000000 >> --- a/hw/timer/ds1338.c >> +++ /dev/null >> @@ -1,239 +0,0 @@ >> -/* >> - * MAXIM DS1338 I2C RTC+NVRAM >> - * >> - * Copyright (c) 2009 CodeSourcery. >> - * Written by Paul Brook >> - * >> - * This code is licensed under the GNU GPL v2. >> - * >> - * Contributions after 2012-01-13 are licensed under the terms of the >> - * GNU GPL, version 2 or (at your option) any later version. >> - */ >> - >> -#include "qemu/osdep.h" >> -#include "qemu-common.h" >> -#include "hw/i2c/i2c.h" >> -#include "qemu/bcd.h" >> - >> -/* Size of NVRAM including both the user-accessible area and the >> - * secondary register area. >> - */ >> -#define NVRAM_SIZE 64 >> - >> -/* Flags definitions */ >> -#define SECONDS_CH 0x80 >> -#define HOURS_12 0x40 >> -#define HOURS_PM 0x20 >> -#define CTRL_OSF 0x20 >> - >> -#define TYPE_DS1338 "ds1338" >> -#define DS1338(obj) OBJECT_CHECK(DS1338State, (obj), TYPE_DS1338) >> - >> -typedef struct DS1338State { >> - I2CSlave parent_obj; >> - >> - int64_t offset; >> - uint8_t wday_offset; >> - uint8_t nvram[NVRAM_SIZE]; >> - int32_t ptr; >> - bool addr_byte; >> -} DS1338State; >> - >> -static const VMStateDescription vmstate_ds1338 = { >> - .name = "ds1338", >> - .version_id = 2, >> - .minimum_version_id = 1, >> - .fields = (VMStateField[]) { >> - VMSTATE_I2C_SLAVE(parent_obj, DS1338State), >> - VMSTATE_INT64(offset, DS1338State), >> - VMSTATE_UINT8_V(wday_offset, DS1338State, 2), >> - VMSTATE_UINT8_ARRAY(nvram, DS1338State, NVRAM_SIZE), >> - VMSTATE_INT32(ptr, DS1338State), >> - VMSTATE_BOOL(addr_byte, DS1338State), >> - VMSTATE_END_OF_LIST() >> - } >> -}; >> - >> -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. >> - */ >> - 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); >> - if (s->nvram[2] & HOURS_12) { >> - int tmp = now.tm_hour; >> - if (tmp % 12 == 0) { >> - tmp += 12; >> - } >> - if (tmp <= 12) { >> - s->nvram[2] = HOURS_12 | to_bcd(tmp); >> - } else { >> - s->nvram[2] = HOURS_12 | HOURS_PM | to_bcd(tmp - 12); >> - } >> - } else { >> - s->nvram[2] = to_bcd(now.tm_hour); >> - } >> - s->nvram[3] = (now.tm_wday + s->wday_offset) % 7 + 1; >> - s->nvram[4] = to_bcd(now.tm_mday); >> - s->nvram[5] = to_bcd(now.tm_mon + 1); >> - s->nvram[6] = to_bcd(now.tm_year - 100); >> -} >> - >> -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); >> - } >> -} >> - >> -static int ds1338_event(I2CSlave *i2c, enum i2c_event event) >> -{ >> - DS1338State *s = DS1338(i2c); >> - >> - switch (event) { >> - case I2C_START_RECV: >> - /* 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); >> - break; >> - case I2C_START_SEND: >> - s->addr_byte = true; >> - break; >> - default: >> - break; >> - } >> - >> - return 0; >> -} >> - >> -static int ds1338_recv(I2CSlave *i2c) >> -{ >> - DS1338State *s = DS1338(i2c); >> - uint8_t res; >> - >> - res = s->nvram[s->ptr]; >> - inc_regptr(s); >> - return res; >> -} >> - >> -static int ds1338_send(I2CSlave *i2c, uint8_t data) >> -{ >> - DS1338State *s = DS1338(i2c); >> - >> - if (s->addr_byte) { >> - s->ptr = data & (NVRAM_SIZE - 1); >> - s->addr_byte = false; >> - return 0; >> - } >> - if (s->ptr < 7) { >> - /* Time register. */ >> - struct tm now; >> - qemu_get_timedate(&now, s->offset); >> - switch(s->ptr) { >> - case 0: >> - /* TODO: Implement CH (stop) bit. */ >> - now.tm_sec = from_bcd(data & 0x7f); >> - break; >> - case 1: >> - now.tm_min = from_bcd(data & 0x7f); >> - break; >> - case 2: >> - if (data & HOURS_12) { >> - int tmp = from_bcd(data & (HOURS_PM - 1)); >> - if (data & HOURS_PM) { >> - tmp += 12; >> - } >> - if (tmp % 12 == 0) { >> - tmp -= 12; >> - } >> - now.tm_hour = tmp; >> - } else { >> - now.tm_hour = from_bcd(data & (HOURS_12 - 1)); >> - } >> - break; >> - case 3: >> - { >> - /* The day field is supposed to contain a value in >> - the range 1-7. Otherwise behavior is undefined. >> - */ >> - int user_wday = (data & 7) - 1; >> - s->wday_offset = (user_wday - now.tm_wday + 7) % 7; >> - } >> - break; >> - case 4: >> - now.tm_mday = from_bcd(data & 0x3f); >> - break; >> - case 5: >> - now.tm_mon = from_bcd(data & 0x1f) - 1; >> - break; >> - case 6: >> - now.tm_year = from_bcd(data) + 100; >> - break; >> - } >> - s->offset = qemu_timedate_diff(&now); >> - } else if (s->ptr == 7) { >> - /* Control register. */ >> - >> - /* Ensure bits 2, 3 and 6 will read back as zero. */ >> - data &= 0xB3; >> - >> - /* Attempting to write the OSF flag to logic 1 leaves the >> - value unchanged. */ >> - data = (data & ~CTRL_OSF) | (data & s->nvram[s->ptr] & CTRL_OSF); >> - >> - s->nvram[s->ptr] = data; >> - } else { >> - s->nvram[s->ptr] = data; >> - } >> - inc_regptr(s); >> - return 0; >> -} >> - >> -static void ds1338_reset(DeviceState *dev) >> -{ >> - DS1338State *s = DS1338(dev); >> - >> - /* The clock is running and synchronized with the host */ >> - s->offset = 0; >> - s->wday_offset = 0; >> - memset(s->nvram, 0, NVRAM_SIZE); >> - s->ptr = 0; >> - s->addr_byte = false; >> -} >> - >> -static void ds1338_class_init(ObjectClass *klass, void *data) >> -{ >> - DeviceClass *dc = DEVICE_CLASS(klass); >> - I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); >> - >> - k->event = ds1338_event; >> - k->recv = ds1338_recv; >> - k->send = ds1338_send; >> - dc->reset = ds1338_reset; >> - dc->vmsd = &vmstate_ds1338; >> -} >> - >> -static const TypeInfo ds1338_info = { >> - .name = TYPE_DS1338, >> - .parent = TYPE_I2C_SLAVE, >> - .instance_size = sizeof(DS1338State), >> - .class_init = ds1338_class_init, >> -}; >> - >> -static void ds1338_register_types(void) >> -{ >> - type_register_static(&ds1338_info); >> -} >> - >> -type_init(ds1338_register_types) >