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