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 DECLARE_GLOBAL_DATA_PTR; 32 33 /** 34 * struct sandbox_i2c_rtc_plat_data - platform data for the RTC 35 * 36 * @base_time: Base system time when RTC device was bound 37 * @offset: RTC offset from current system time 38 * @use_system_time: true to use system time, false to use @base_time 39 * @reg: Register values 40 */ 41 struct sandbox_i2c_rtc_plat_data { 42 long base_time; 43 long offset; 44 bool use_system_time; 45 u8 reg[REG_COUNT]; 46 }; 47 48 struct sandbox_i2c_rtc { 49 unsigned int offset_secs; 50 }; 51 52 long sandbox_i2c_rtc_set_offset(struct udevice *dev, bool use_system_time, 53 int offset) 54 { 55 struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev); 56 long old_offset; 57 58 old_offset = plat->offset; 59 plat->use_system_time = use_system_time; 60 if (offset != -1) 61 plat->offset = offset; 62 63 return old_offset; 64 } 65 66 long sandbox_i2c_rtc_get_set_base_time(struct udevice *dev, long base_time) 67 { 68 struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev); 69 long old_base_time; 70 71 old_base_time = plat->base_time; 72 if (base_time != -1) 73 plat->base_time = base_time; 74 75 return old_base_time; 76 } 77 78 static void reset_time(struct udevice *dev) 79 { 80 struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev); 81 struct rtc_time now; 82 83 os_localtime(&now); 84 plat->base_time = rtc_mktime(&now); 85 plat->offset = 0; 86 plat->use_system_time = true; 87 } 88 89 static int sandbox_i2c_rtc_get(struct udevice *dev, struct rtc_time *time) 90 { 91 struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev); 92 struct rtc_time tm_now; 93 long now; 94 95 if (plat->use_system_time) { 96 os_localtime(&tm_now); 97 now = rtc_mktime(&tm_now); 98 } else { 99 now = plat->base_time; 100 } 101 102 return rtc_to_tm(now + plat->offset, time); 103 } 104 105 static int sandbox_i2c_rtc_set(struct udevice *dev, const struct rtc_time *time) 106 { 107 struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev); 108 struct rtc_time tm_now; 109 long now; 110 111 if (plat->use_system_time) { 112 os_localtime(&tm_now); 113 now = rtc_mktime(&tm_now); 114 } else { 115 now = plat->base_time; 116 } 117 plat->offset = rtc_mktime(time) - now; 118 119 return 0; 120 } 121 122 /* Update the current time in the registers */ 123 static int sandbox_i2c_rtc_prepare_read(struct udevice *emul) 124 { 125 struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(emul); 126 struct rtc_time time; 127 int ret; 128 129 ret = sandbox_i2c_rtc_get(emul, &time); 130 if (ret) 131 return ret; 132 133 plat->reg[REG_SEC] = time.tm_sec; 134 plat->reg[REG_MIN] = time.tm_min; 135 plat->reg[REG_HOUR] = time.tm_hour; 136 plat->reg[REG_MDAY] = time.tm_mday; 137 plat->reg[REG_MON] = time.tm_mon; 138 plat->reg[REG_YEAR] = time.tm_year - 1900; 139 plat->reg[REG_WDAY] = time.tm_wday; 140 141 return 0; 142 } 143 144 static int sandbox_i2c_rtc_complete_write(struct udevice *emul) 145 { 146 struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(emul); 147 struct rtc_time time; 148 int ret; 149 150 time.tm_sec = plat->reg[REG_SEC]; 151 time.tm_min = plat->reg[REG_MIN]; 152 time.tm_hour = plat->reg[REG_HOUR]; 153 time.tm_mday = plat->reg[REG_MDAY]; 154 time.tm_mon = plat->reg[REG_MON]; 155 time.tm_year = plat->reg[REG_YEAR] + 1900; 156 time.tm_wday = plat->reg[REG_WDAY]; 157 158 ret = sandbox_i2c_rtc_set(emul, &time); 159 if (ret) 160 return ret; 161 162 return 0; 163 } 164 165 static int sandbox_i2c_rtc_xfer(struct udevice *emul, struct i2c_msg *msg, 166 int nmsgs) 167 { 168 struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(emul); 169 uint offset = 0; 170 int ret; 171 172 debug("\n%s\n", __func__); 173 ret = sandbox_i2c_rtc_prepare_read(emul); 174 if (ret) 175 return ret; 176 for (; nmsgs > 0; nmsgs--, msg++) { 177 int len; 178 u8 *ptr; 179 180 len = msg->len; 181 debug(" %s: msg->len=%d", 182 msg->flags & I2C_M_RD ? "read" : "write", 183 msg->len); 184 if (msg->flags & I2C_M_RD) { 185 debug(", offset %x, len %x: ", offset, len); 186 187 /* Read the register */ 188 memcpy(msg->buf, plat->reg + offset, len); 189 memset(msg->buf + len, '\xff', msg->len - len); 190 debug_buffer(0, msg->buf, 1, msg->len, 0); 191 } else if (len >= 1) { 192 ptr = msg->buf; 193 offset = *ptr++ & (REG_COUNT - 1); 194 len--; 195 debug(", set offset %x: ", offset); 196 debug_buffer(0, msg->buf, 1, msg->len, 0); 197 198 /* Write the register */ 199 memcpy(plat->reg + offset, ptr, len); 200 if (offset == REG_RESET) 201 reset_time(emul); 202 } 203 } 204 ret = sandbox_i2c_rtc_complete_write(emul); 205 if (ret) 206 return ret; 207 208 return 0; 209 } 210 211 struct dm_i2c_ops sandbox_i2c_rtc_emul_ops = { 212 .xfer = sandbox_i2c_rtc_xfer, 213 }; 214 215 static int sandbox_i2c_rtc_bind(struct udevice *dev) 216 { 217 reset_time(dev); 218 219 return 0; 220 } 221 222 static const struct udevice_id sandbox_i2c_rtc_ids[] = { 223 { .compatible = "sandbox,i2c-rtc" }, 224 { } 225 }; 226 227 U_BOOT_DRIVER(sandbox_i2c_rtc_emul) = { 228 .name = "sandbox_i2c_rtc_emul", 229 .id = UCLASS_I2C_EMUL, 230 .of_match = sandbox_i2c_rtc_ids, 231 .bind = sandbox_i2c_rtc_bind, 232 .priv_auto_alloc_size = sizeof(struct sandbox_i2c_rtc), 233 .platdata_auto_alloc_size = sizeof(struct sandbox_i2c_rtc_plat_data), 234 .ops = &sandbox_i2c_rtc_emul_ops, 235 }; 236