1 /* 2 * Simulate an I2C real time clock 3 * 4 * Copyright (c) 2015 Google, Inc 5 * Written by Simon Glass <sjg@chromium.org> 6 * 7 * SPDX-License-Identifier: GPL-2.0+ 8 */ 9 10 /* 11 * This is a test driver. It starts off with the current time of the machine, 12 * but also supports setting the time, using an offset from the current 13 * clock. This driver is only intended for testing, not accurate 14 * time-keeping. It does not change the system time. 15 */ 16 17 #include <common.h> 18 #include <dm.h> 19 #include <i2c.h> 20 #include <os.h> 21 #include <rtc.h> 22 #include <asm/rtc.h> 23 #include <asm/test.h> 24 25 #ifdef DEBUG 26 #define debug_buffer print_buffer 27 #else 28 #define debug_buffer(x, ...) 29 #endif 30 31 /** 32 * struct sandbox_i2c_rtc_plat_data - platform data for the RTC 33 * 34 * @base_time: Base system time when RTC device was bound 35 * @offset: RTC offset from current system time 36 * @use_system_time: true to use system time, false to use @base_time 37 * @reg: Register values 38 */ 39 struct sandbox_i2c_rtc_plat_data { 40 long base_time; 41 long offset; 42 bool use_system_time; 43 u8 reg[REG_COUNT]; 44 }; 45 46 struct sandbox_i2c_rtc { 47 unsigned int offset_secs; 48 }; 49 50 long sandbox_i2c_rtc_set_offset(struct udevice *dev, bool use_system_time, 51 int offset) 52 { 53 struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev); 54 long old_offset; 55 56 old_offset = plat->offset; 57 plat->use_system_time = use_system_time; 58 if (offset != -1) 59 plat->offset = offset; 60 61 return old_offset; 62 } 63 64 long sandbox_i2c_rtc_get_set_base_time(struct udevice *dev, long base_time) 65 { 66 struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev); 67 long old_base_time; 68 69 old_base_time = plat->base_time; 70 if (base_time != -1) 71 plat->base_time = base_time; 72 73 return old_base_time; 74 } 75 76 static void reset_time(struct udevice *dev) 77 { 78 struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev); 79 struct rtc_time now; 80 81 os_localtime(&now); 82 plat->base_time = rtc_mktime(&now); 83 plat->offset = 0; 84 plat->use_system_time = true; 85 } 86 87 static int sandbox_i2c_rtc_get(struct udevice *dev, struct rtc_time *time) 88 { 89 struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev); 90 struct rtc_time tm_now; 91 long now; 92 93 if (plat->use_system_time) { 94 os_localtime(&tm_now); 95 now = rtc_mktime(&tm_now); 96 } else { 97 now = plat->base_time; 98 } 99 100 return rtc_to_tm(now + plat->offset, time); 101 } 102 103 static int sandbox_i2c_rtc_set(struct udevice *dev, const struct rtc_time *time) 104 { 105 struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev); 106 struct rtc_time tm_now; 107 long now; 108 109 if (plat->use_system_time) { 110 os_localtime(&tm_now); 111 now = rtc_mktime(&tm_now); 112 } else { 113 now = plat->base_time; 114 } 115 plat->offset = rtc_mktime(time) - now; 116 117 return 0; 118 } 119 120 /* Update the current time in the registers */ 121 static int sandbox_i2c_rtc_prepare_read(struct udevice *emul) 122 { 123 struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(emul); 124 struct rtc_time time; 125 int ret; 126 127 ret = sandbox_i2c_rtc_get(emul, &time); 128 if (ret) 129 return ret; 130 131 plat->reg[REG_SEC] = time.tm_sec; 132 plat->reg[REG_MIN] = time.tm_min; 133 plat->reg[REG_HOUR] = time.tm_hour; 134 plat->reg[REG_MDAY] = time.tm_mday; 135 plat->reg[REG_MON] = time.tm_mon; 136 plat->reg[REG_YEAR] = time.tm_year - 1900; 137 plat->reg[REG_WDAY] = time.tm_wday; 138 139 return 0; 140 } 141 142 static int sandbox_i2c_rtc_complete_write(struct udevice *emul) 143 { 144 struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(emul); 145 struct rtc_time time; 146 int ret; 147 148 time.tm_sec = plat->reg[REG_SEC]; 149 time.tm_min = plat->reg[REG_MIN]; 150 time.tm_hour = plat->reg[REG_HOUR]; 151 time.tm_mday = plat->reg[REG_MDAY]; 152 time.tm_mon = plat->reg[REG_MON]; 153 time.tm_year = plat->reg[REG_YEAR] + 1900; 154 time.tm_wday = plat->reg[REG_WDAY]; 155 156 ret = sandbox_i2c_rtc_set(emul, &time); 157 if (ret) 158 return ret; 159 160 return 0; 161 } 162 163 static int sandbox_i2c_rtc_xfer(struct udevice *emul, struct i2c_msg *msg, 164 int nmsgs) 165 { 166 struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(emul); 167 uint offset = 0; 168 int ret; 169 170 debug("\n%s\n", __func__); 171 ret = sandbox_i2c_rtc_prepare_read(emul); 172 if (ret) 173 return ret; 174 for (; nmsgs > 0; nmsgs--, msg++) { 175 int len; 176 u8 *ptr; 177 178 len = msg->len; 179 debug(" %s: msg->len=%d", 180 msg->flags & I2C_M_RD ? "read" : "write", 181 msg->len); 182 if (msg->flags & I2C_M_RD) { 183 debug(", offset %x, len %x: ", offset, len); 184 185 /* Read the register */ 186 memcpy(msg->buf, plat->reg + offset, len); 187 memset(msg->buf + len, '\xff', msg->len - len); 188 debug_buffer(0, msg->buf, 1, msg->len, 0); 189 } else if (len >= 1) { 190 ptr = msg->buf; 191 offset = *ptr++ & (REG_COUNT - 1); 192 len--; 193 debug(", set offset %x: ", offset); 194 debug_buffer(0, msg->buf, 1, msg->len, 0); 195 196 /* Write the register */ 197 memcpy(plat->reg + offset, ptr, len); 198 if (offset == REG_RESET) 199 reset_time(emul); 200 } 201 } 202 ret = sandbox_i2c_rtc_complete_write(emul); 203 if (ret) 204 return ret; 205 206 return 0; 207 } 208 209 struct dm_i2c_ops sandbox_i2c_rtc_emul_ops = { 210 .xfer = sandbox_i2c_rtc_xfer, 211 }; 212 213 static int sandbox_i2c_rtc_bind(struct udevice *dev) 214 { 215 reset_time(dev); 216 217 return 0; 218 } 219 220 static const struct udevice_id sandbox_i2c_rtc_ids[] = { 221 { .compatible = "sandbox,i2c-rtc" }, 222 { } 223 }; 224 225 U_BOOT_DRIVER(sandbox_i2c_rtc_emul) = { 226 .name = "sandbox_i2c_rtc_emul", 227 .id = UCLASS_I2C_EMUL, 228 .of_match = sandbox_i2c_rtc_ids, 229 .bind = sandbox_i2c_rtc_bind, 230 .priv_auto_alloc_size = sizeof(struct sandbox_i2c_rtc), 231 .platdata_auto_alloc_size = sizeof(struct sandbox_i2c_rtc_plat_data), 232 .ops = &sandbox_i2c_rtc_emul_ops, 233 }; 234