1 /* 2 * M41T80 serial rtc emulation 3 * 4 * Copyright (c) 2018 BALATON Zoltan 5 * 6 * This work is licensed under the GNU GPL license version 2 or later. 7 * 8 */ 9 10 #include "qemu/osdep.h" 11 #include "qemu/log.h" 12 #include "qemu/module.h" 13 #include "qemu/timer.h" 14 #include "qemu/bcd.h" 15 #include "hw/i2c/i2c.h" 16 #include "qom/object.h" 17 #include "sysemu/rtc.h" 18 19 #define TYPE_M41T80 "m41t80" 20 OBJECT_DECLARE_SIMPLE_TYPE(M41t80State, M41T80) 21 22 struct M41t80State { 23 I2CSlave parent_obj; 24 int8_t addr; 25 }; 26 27 static void m41t80_realize(DeviceState *dev, Error **errp) 28 { 29 M41t80State *s = M41T80(dev); 30 31 s->addr = -1; 32 } 33 34 static int m41t80_send(I2CSlave *i2c, uint8_t data) 35 { 36 M41t80State *s = M41T80(i2c); 37 38 if (s->addr < 0) { 39 s->addr = data; 40 } else { 41 s->addr++; 42 } 43 return 0; 44 } 45 46 static uint8_t m41t80_recv(I2CSlave *i2c) 47 { 48 M41t80State *s = M41T80(i2c); 49 struct tm now; 50 qemu_timeval tv; 51 52 if (s->addr < 0) { 53 s->addr = 0; 54 } 55 if (s->addr >= 1 && s->addr <= 7) { 56 qemu_get_timedate(&now, -1); 57 } 58 switch (s->addr++) { 59 case 0: 60 qemu_gettimeofday(&tv); 61 return to_bcd(tv.tv_usec / 10000); 62 case 1: 63 return to_bcd(now.tm_sec); 64 case 2: 65 return to_bcd(now.tm_min); 66 case 3: 67 return to_bcd(now.tm_hour); 68 case 4: 69 return to_bcd(now.tm_wday); 70 case 5: 71 return to_bcd(now.tm_mday); 72 case 6: 73 return to_bcd(now.tm_mon + 1); 74 case 7: 75 return to_bcd(now.tm_year % 100); 76 case 8 ... 19: 77 qemu_log_mask(LOG_UNIMP, "%s: unimplemented register: %d\n", 78 __func__, s->addr - 1); 79 return 0; 80 default: 81 qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid register: %d\n", 82 __func__, s->addr - 1); 83 return 0; 84 } 85 } 86 87 static int m41t80_event(I2CSlave *i2c, enum i2c_event event) 88 { 89 M41t80State *s = M41T80(i2c); 90 91 if (event == I2C_START_SEND) { 92 s->addr = -1; 93 } 94 return 0; 95 } 96 97 static void m41t80_class_init(ObjectClass *klass, void *data) 98 { 99 DeviceClass *dc = DEVICE_CLASS(klass); 100 I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); 101 102 dc->realize = m41t80_realize; 103 sc->send = m41t80_send; 104 sc->recv = m41t80_recv; 105 sc->event = m41t80_event; 106 } 107 108 static const TypeInfo m41t80_info = { 109 .name = TYPE_M41T80, 110 .parent = TYPE_I2C_SLAVE, 111 .instance_size = sizeof(M41t80State), 112 .class_init = m41t80_class_init, 113 }; 114 115 static void m41t80_register_types(void) 116 { 117 type_register_static(&m41t80_info); 118 } 119 120 type_init(m41t80_register_types) 121