1 // SPDX-License-Identifier: GPL-2.0+
3 * Simulate an I2C real time clock
5 * Copyright (c) 2015 Google, Inc
6 * Written by Simon Glass <sjg@chromium.org>
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.
25 #define debug_buffer print_buffer
27 #define debug_buffer(x, ...)
31 * struct sandbox_i2c_rtc_plat_data - platform data for the RTC
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
38 struct sandbox_i2c_rtc_plat_data
{
45 struct sandbox_i2c_rtc
{
46 unsigned int offset_secs
;
49 long sandbox_i2c_rtc_set_offset(struct udevice
*dev
, bool use_system_time
,
52 struct sandbox_i2c_rtc_plat_data
*plat
= dev_get_platdata(dev
);
55 old_offset
= plat
->offset
;
56 plat
->use_system_time
= use_system_time
;
58 plat
->offset
= offset
;
63 long sandbox_i2c_rtc_get_set_base_time(struct udevice
*dev
, long base_time
)
65 struct sandbox_i2c_rtc_plat_data
*plat
= dev_get_platdata(dev
);
68 old_base_time
= plat
->base_time
;
70 plat
->base_time
= base_time
;
75 static void reset_time(struct udevice
*dev
)
77 struct sandbox_i2c_rtc_plat_data
*plat
= dev_get_platdata(dev
);
81 plat
->base_time
= rtc_mktime(&now
);
83 plat
->use_system_time
= true;
86 static int sandbox_i2c_rtc_get(struct udevice
*dev
, struct rtc_time
*time
)
88 struct sandbox_i2c_rtc_plat_data
*plat
= dev_get_platdata(dev
);
89 struct rtc_time tm_now
;
92 if (plat
->use_system_time
) {
93 os_localtime(&tm_now
);
94 now
= rtc_mktime(&tm_now
);
96 now
= plat
->base_time
;
99 return rtc_to_tm(now
+ plat
->offset
, time
);
102 static int sandbox_i2c_rtc_set(struct udevice
*dev
, const struct rtc_time
*time
)
104 struct sandbox_i2c_rtc_plat_data
*plat
= dev_get_platdata(dev
);
105 struct rtc_time tm_now
;
108 if (plat
->use_system_time
) {
109 os_localtime(&tm_now
);
110 now
= rtc_mktime(&tm_now
);
112 now
= plat
->base_time
;
114 plat
->offset
= rtc_mktime(time
) - now
;
119 /* Update the current time in the registers */
120 static int sandbox_i2c_rtc_prepare_read(struct udevice
*emul
)
122 struct sandbox_i2c_rtc_plat_data
*plat
= dev_get_platdata(emul
);
123 struct rtc_time time
;
126 ret
= sandbox_i2c_rtc_get(emul
, &time
);
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
;
141 static int sandbox_i2c_rtc_complete_write(struct udevice
*emul
)
143 struct sandbox_i2c_rtc_plat_data
*plat
= dev_get_platdata(emul
);
144 struct rtc_time time
;
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
];
155 ret
= sandbox_i2c_rtc_set(emul
, &time
);
162 static int sandbox_i2c_rtc_xfer(struct udevice
*emul
, struct i2c_msg
*msg
,
165 struct sandbox_i2c_rtc_plat_data
*plat
= dev_get_platdata(emul
);
169 debug("\n%s\n", __func__
);
170 ret
= sandbox_i2c_rtc_prepare_read(emul
);
173 for (; nmsgs
> 0; nmsgs
--, msg
++) {
178 debug(" %s: msg->len=%d",
179 msg
->flags
& I2C_M_RD
? "read" : "write",
181 if (msg
->flags
& I2C_M_RD
) {
182 debug(", offset %x, len %x: ", offset
, len
);
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) {
190 offset
= *ptr
++ & (REG_COUNT
- 1);
192 debug(", set offset %x: ", offset
);
193 debug_buffer(0, msg
->buf
, 1, msg
->len
, 0);
195 /* Write the register */
196 memcpy(plat
->reg
+ offset
, ptr
, len
);
197 if (offset
== REG_RESET
)
201 ret
= sandbox_i2c_rtc_complete_write(emul
);
208 struct dm_i2c_ops sandbox_i2c_rtc_emul_ops
= {
209 .xfer
= sandbox_i2c_rtc_xfer
,
212 static int sandbox_i2c_rtc_bind(struct udevice
*dev
)
219 static const struct udevice_id sandbox_i2c_rtc_ids
[] = {
220 { .compatible
= "sandbox,i2c-rtc" },
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
,