From 0cc5129f4fae84e4f039ff02fae34af26b4d71de Mon Sep 17 00:00:00 2001 From: yajin Date: Sat, 16 May 2009 18:50:11 +0800 Subject: [PATCH] add st_m41t80 rtc emulation --- hw/i2c.h | 3 + hw/st_m41t80.c | 228 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 231 insertions(+) create mode 100755 hw/st_m41t80.c diff --git a/hw/i2c.h b/hw/i2c.h index 396c5627bc..3d451d7d22 100644 --- a/hw/i2c.h +++ b/hw/i2c.h @@ -87,4 +87,7 @@ void tmp105_set(i2c_slave *i2c, int temp); struct i2c_slave *lm8323_init(i2c_bus *bus, qemu_irq nirq); void lm832x_key_event(struct i2c_slave *i2c, int key, int state); +/* st_m41t80.c */ +i2c_slave *m41t80_init(i2c_bus *bus, qemu_irq irq,int i2c_address); + #endif diff --git a/hw/st_m41t80.c b/hw/st_m41t80.c new file mode 100755 index 0000000000..cd7b93151d --- /dev/null +++ b/hw/st_m41t80.c @@ -0,0 +1,228 @@ +/* + * QEMU st M41T80 rtc emulation + * + * Copyright (c) 2009 yajin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hw.h" +#include "qemu-timer.h" +#include "i2c.h" +#include "sysemu.h" +#include "console.h" + +struct m41t80_s +{ + i2c_slave i2c; + qemu_irq irq; + QEMUTimer *hz_tm; + + int64_t last_time; + + int64_t next; + + int firstbyte; + uint8_t reg; + + uint8_t cmos_data[14]; + uint8_t stop; +#if 0 + uint8_t sec_001; /*0x00 */ + uint8_t sec, st; /*0x01 */ + uint8_t min; /*0x03 */ + uint8_t hour, cb, ceb; /*0x02 */ + uint8_t dayofweek; /*0x04 */ + uint8_t date; /*0x05 */ + uint8_t month; /*0x06 */ + uint8_t year; /*0x07 */ + uint8_t out; /*0x08 */ + uint8_t e32k; /*0x09 */ + uint8_t amonth, sqwe, afe; /*0x0a */ + uint8_t ad, rpt5, rpt4; /*0x0b */ + uint8_t ah, rpt3; /*0x0c */ + uint8_t asecond, rpt2; /*0x0d */ + uint8_t as, rpt1; /*0x0e */ + uint8_t af; /*0x0f */ + uint8_t rs0, rs1, rs2, rs3; /*0x13 */ +#endif + +}; + +static inline uint8_t to_bcd(int val) +{ + return ((val / 10) << 4) | (val % 10); +} + +static inline int from_bcd(uint8_t val) +{ + return ((val >> 4) * 10) + (val & 0x0f); +} + +static void m41t80_set_date(struct m41t80_s *s, struct tm tm) +{ + /* set the CMOS date */ + s->cmos_data[1] = to_bcd(tm.tm_sec); + s->cmos_data[2] = to_bcd(tm.tm_min); + s->cmos_data[3] = to_bcd(tm.tm_hour); + s->cmos_data[4] = tm.tm_wday; + s->cmos_data[5] = to_bcd(tm.tm_mday); + s->cmos_data[6] = to_bcd(tm.tm_mon); + s->cmos_data[7] = to_bcd(tm.tm_year); + if (tm.tm_year > 99) + { + s->cmos_data[3] |= 0xc0; + } +} + +static inline void m41t80_rtc_start(struct m41t80_s *s) +{ + s->last_time = qemu_get_clock(rt_clock); +} + +static inline void m41t80_rtc_stop(struct m41t80_s *s) +{ + +} + +static inline void m41t80_rtc_sync(struct m41t80_s *s) +{ + struct tm tm; + time_t seconds; + + if (s->stop) + return; + + int64_t delta = qemu_get_clock(rt_clock) - s->last_time; + s->last_time = qemu_get_clock(rt_clock); + if (delta > 0) + s->cmos_data[0] += to_bcd((delta / 10000000) % 100); + else + s->cmos_data[0] -= to_bcd((delta / 10000000) % 100); + + /*00: turn tm -> seconds */ + tm.tm_sec = from_bcd(s->cmos_data[1] & 0x7f); + tm.tm_min = from_bcd(s->cmos_data[2] & 0x7f); + tm.tm_hour = from_bcd(s->cmos_data[3] & 0x3f); + tm.tm_mday = from_bcd(s->cmos_data[5] & 0x3f); + tm.tm_mon = from_bcd(s->cmos_data[6] & 0x1f); + tm.tm_year = from_bcd(s->cmos_data[7]); + if (s->cmos_data[3] & 0xc0) + tm.tm_year += 100; + tm.tm_wday = from_bcd(s->cmos_data[4] & 0x7); + seconds = mktime(&tm); + + /*01: add the diff seconds */ + if (delta > 0) + seconds += delta / ticks_per_sec; + else + seconds -= delta / ticks_per_sec; + + /*02: change to tm again */ + memcpy(&tm, gmtime(&seconds), sizeof(tm)); + + /*03: set to register */ + m41t80_set_date(s, tm); +} + +static void m41t80_rtc_hz(void *opaque) +{ + struct m41t80_s *s = (struct m41t80_s *) opaque; + /*update */ +} + +static void m41t80_event(i2c_slave * i2c, enum i2c_event event) +{ + struct m41t80_s *s = (struct m41t80_s *) i2c; + //printf("m41t80_event %d \n", event); + if (event == I2C_START_SEND) + s->firstbyte = 1; + + +} + +static int m41t80_tx(i2c_slave * i2c, uint8_t data) +{ + struct m41t80_s *s = (struct m41t80_s *) i2c; + //printf("m41t80_tx %d \n", data); + if (s->firstbyte) + { + s->reg = data; + s->firstbyte = 0; + } + else + { + //printf("m41t80_tx s->reg %d \n", s->reg); + if ((s->reg == 1) && (s->cmos_data[1] & 0x80)) + { + s->stop = 0; + m41t80_rtc_start(s); + } + else + { + s->stop = 1; + m41t80_rtc_stop(s); + } + + m41t80_rtc_sync(s); + s->cmos_data[s->reg++] = data; + } + + return 0; +} + +static int m41t80_rx(i2c_slave * i2c) +{ + struct m41t80_s *s = (struct m41t80_s *) i2c; + //printf("m41t80_rx s->reg %d \n", s->reg); + + m41t80_rtc_sync(s); + return s->cmos_data[s->reg++]; + +} + +static void m41t80_reset(struct m41t80_s *s) +{ + struct tm tm; + s->last_time = qemu_get_clock(rt_clock); + qemu_get_timedate(&tm, 0); + s->cmos_data[0] = 0; + m41t80_set_date(s, tm); +} + + + +i2c_slave *m41t80_init(i2c_bus * bus, qemu_irq irq, int i2c_address) +{ + struct m41t80_s *s = (struct m41t80_s *) + i2c_slave_init(bus, i2c_address, sizeof(struct m41t80_s)); + + s->i2c.event = m41t80_event; + s->i2c.recv = m41t80_rx; + s->i2c.send = m41t80_tx; + + s->irq = irq; + s->hz_tm = qemu_new_timer(rt_clock, m41t80_rtc_hz, s); + + m41t80_reset(&s->i2c); + + //register_savevm("menelaus", -1, 0, menelaus_save, menelaus_load, s); + + return &s->i2c; +} -- 2.11.4.GIT