1 /* 2 * MAXIM DS1338 I2C RTC+NVRAM 3 * 4 * Copyright (c) 2009 CodeSourcery. 5 * Written by Paul Brook 6 * 7 * This code is licensed under the GNU GPL v2. 8 * 9 * Contributions after 2012-01-13 are licensed under the terms of the 10 * GNU GPL, version 2 or (at your option) any later version. 11 */ 12 13 #include "qemu/osdep.h" 14 #include "qemu-common.h" 15 #include "hw/i2c/i2c.h" 16 #include "migration/vmstate.h" 17 #include "qemu/bcd.h" 18 #include "qemu/module.h" 19 #include "qom/object.h" 20 21 /* Size of NVRAM including both the user-accessible area and the 22 * secondary register area. 23 */ 24 #define NVRAM_SIZE 64 25 26 /* Flags definitions */ 27 #define SECONDS_CH 0x80 28 #define HOURS_12 0x40 29 #define HOURS_PM 0x20 30 #define CTRL_OSF 0x20 31 32 #define TYPE_DS1338 "ds1338" 33 typedef struct DS1338State DS1338State; 34 DECLARE_INSTANCE_CHECKER(DS1338State, DS1338, 35 TYPE_DS1338) 36 37 struct DS1338State { 38 I2CSlave parent_obj; 39 40 int64_t offset; 41 uint8_t wday_offset; 42 uint8_t nvram[NVRAM_SIZE]; 43 int32_t ptr; 44 bool addr_byte; 45 }; 46 47 static const VMStateDescription vmstate_ds1338 = { 48 .name = "ds1338", 49 .version_id = 2, 50 .minimum_version_id = 1, 51 .fields = (VMStateField[]) { 52 VMSTATE_I2C_SLAVE(parent_obj, DS1338State), 53 VMSTATE_INT64(offset, DS1338State), 54 VMSTATE_UINT8_V(wday_offset, DS1338State, 2), 55 VMSTATE_UINT8_ARRAY(nvram, DS1338State, NVRAM_SIZE), 56 VMSTATE_INT32(ptr, DS1338State), 57 VMSTATE_BOOL(addr_byte, DS1338State), 58 VMSTATE_END_OF_LIST() 59 } 60 }; 61 62 static void capture_current_time(DS1338State *s) 63 { 64 /* Capture the current time into the secondary registers 65 * which will be actually read by the data transfer operation. 66 */ 67 struct tm now; 68 qemu_get_timedate(&now, s->offset); 69 s->nvram[0] = to_bcd(now.tm_sec); 70 s->nvram[1] = to_bcd(now.tm_min); 71 if (s->nvram[2] & HOURS_12) { 72 int tmp = now.tm_hour; 73 if (tmp % 12 == 0) { 74 tmp += 12; 75 } 76 if (tmp <= 12) { 77 s->nvram[2] = HOURS_12 | to_bcd(tmp); 78 } else { 79 s->nvram[2] = HOURS_12 | HOURS_PM | to_bcd(tmp - 12); 80 } 81 } else { 82 s->nvram[2] = to_bcd(now.tm_hour); 83 } 84 s->nvram[3] = (now.tm_wday + s->wday_offset) % 7 + 1; 85 s->nvram[4] = to_bcd(now.tm_mday); 86 s->nvram[5] = to_bcd(now.tm_mon + 1); 87 s->nvram[6] = to_bcd(now.tm_year - 100); 88 } 89 90 static void inc_regptr(DS1338State *s) 91 { 92 /* The register pointer wraps around after 0x3F; wraparound 93 * causes the current time/date to be retransferred into 94 * the secondary registers. 95 */ 96 s->ptr = (s->ptr + 1) & (NVRAM_SIZE - 1); 97 if (!s->ptr) { 98 capture_current_time(s); 99 } 100 } 101 102 static int ds1338_event(I2CSlave *i2c, enum i2c_event event) 103 { 104 DS1338State *s = DS1338(i2c); 105 106 switch (event) { 107 case I2C_START_RECV: 108 /* In h/w, capture happens on any START condition, not just a 109 * START_RECV, but there is no need to actually capture on 110 * START_SEND, because the guest can't get at that data 111 * without going through a START_RECV which would overwrite it. 112 */ 113 capture_current_time(s); 114 break; 115 case I2C_START_SEND: 116 s->addr_byte = true; 117 break; 118 default: 119 break; 120 } 121 122 return 0; 123 } 124 125 static uint8_t ds1338_recv(I2CSlave *i2c) 126 { 127 DS1338State *s = DS1338(i2c); 128 uint8_t res; 129 130 res = s->nvram[s->ptr]; 131 inc_regptr(s); 132 return res; 133 } 134 135 static int ds1338_send(I2CSlave *i2c, uint8_t data) 136 { 137 DS1338State *s = DS1338(i2c); 138 139 if (s->addr_byte) { 140 s->ptr = data & (NVRAM_SIZE - 1); 141 s->addr_byte = false; 142 return 0; 143 } 144 if (s->ptr < 7) { 145 /* Time register. */ 146 struct tm now; 147 qemu_get_timedate(&now, s->offset); 148 switch(s->ptr) { 149 case 0: 150 /* TODO: Implement CH (stop) bit. */ 151 now.tm_sec = from_bcd(data & 0x7f); 152 break; 153 case 1: 154 now.tm_min = from_bcd(data & 0x7f); 155 break; 156 case 2: 157 if (data & HOURS_12) { 158 int tmp = from_bcd(data & (HOURS_PM - 1)); 159 if (data & HOURS_PM) { 160 tmp += 12; 161 } 162 if (tmp % 12 == 0) { 163 tmp -= 12; 164 } 165 now.tm_hour = tmp; 166 } else { 167 now.tm_hour = from_bcd(data & (HOURS_12 - 1)); 168 } 169 break; 170 case 3: 171 { 172 /* The day field is supposed to contain a value in 173 the range 1-7. Otherwise behavior is undefined. 174 */ 175 int user_wday = (data & 7) - 1; 176 s->wday_offset = (user_wday - now.tm_wday + 7) % 7; 177 } 178 break; 179 case 4: 180 now.tm_mday = from_bcd(data & 0x3f); 181 break; 182 case 5: 183 now.tm_mon = from_bcd(data & 0x1f) - 1; 184 break; 185 case 6: 186 now.tm_year = from_bcd(data) + 100; 187 break; 188 } 189 s->offset = qemu_timedate_diff(&now); 190 } else if (s->ptr == 7) { 191 /* Control register. */ 192 193 /* Ensure bits 2, 3 and 6 will read back as zero. */ 194 data &= 0xB3; 195 196 /* Attempting to write the OSF flag to logic 1 leaves the 197 value unchanged. */ 198 data = (data & ~CTRL_OSF) | (data & s->nvram[s->ptr] & CTRL_OSF); 199 200 s->nvram[s->ptr] = data; 201 } else { 202 s->nvram[s->ptr] = data; 203 } 204 inc_regptr(s); 205 return 0; 206 } 207 208 static void ds1338_reset(DeviceState *dev) 209 { 210 DS1338State *s = DS1338(dev); 211 212 /* The clock is running and synchronized with the host */ 213 s->offset = 0; 214 s->wday_offset = 0; 215 memset(s->nvram, 0, NVRAM_SIZE); 216 s->ptr = 0; 217 s->addr_byte = false; 218 } 219 220 static void ds1338_class_init(ObjectClass *klass, void *data) 221 { 222 DeviceClass *dc = DEVICE_CLASS(klass); 223 I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); 224 225 k->event = ds1338_event; 226 k->recv = ds1338_recv; 227 k->send = ds1338_send; 228 dc->reset = ds1338_reset; 229 dc->vmsd = &vmstate_ds1338; 230 } 231 232 static const TypeInfo ds1338_info = { 233 .name = TYPE_DS1338, 234 .parent = TYPE_I2C_SLAVE, 235 .instance_size = sizeof(DS1338State), 236 .class_init = ds1338_class_init, 237 }; 238 239 static void ds1338_register_types(void) 240 { 241 type_register_static(&ds1338_info); 242 } 243 244 type_init(ds1338_register_types) 245