1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright 2020, Heinrich Schuchardt <xypron.glpk@gmx.de> 4 * 5 * This driver emulates a real time clock based on timer ticks. 6 */ 7 8#include <common.h> 9#include <div64.h> 10#include <dm.h> 11#include <env.h> 12#include <rtc.h> 13#include <timestamp.h> 14 15/** 16 * struct emul_rtc - private data for emulated RTC driver 17 */ 18struct emul_rtc { 19 /** 20 * @offset_us: microseconds from 1970-01-01 to timer_get_us() base 21 */ 22 u64 offset_us; 23 /** 24 * @isdst: daylight saving time 25 */ 26 int isdst; 27}; 28 29static int emul_rtc_get(struct udevice *dev, struct rtc_time *time) 30{ 31 struct emul_rtc *priv = dev_get_priv(dev); 32 u64 now; 33 34 now = timer_get_us() + priv->offset_us; 35 do_div(now, 1000000); 36 rtc_to_tm(now, time); 37 time->tm_isdst = priv->isdst; 38 39 return 0; 40} 41 42static int emul_rtc_set(struct udevice *dev, const struct rtc_time *time) 43{ 44 struct emul_rtc *priv = dev_get_priv(dev); 45 46 if (time->tm_year < 1970) 47 return -EINVAL; 48 49 priv->offset_us = rtc_mktime(time) * 1000000ULL - timer_get_us(); 50 51 if (time->tm_isdst > 0) 52 priv->isdst = 1; 53 else if (time->tm_isdst < 0) 54 priv->isdst = -1; 55 else 56 priv->isdst = 0; 57 58 return 0; 59} 60 61int emul_rtc_probe(struct udevice *dev) 62{ 63 struct emul_rtc *priv = dev_get_priv(dev); 64 const char *epoch_str; 65 u64 epoch; 66 67 epoch_str = env_get("rtc_emul_epoch"); 68 69 if (epoch_str) { 70 epoch = simple_strtoull(epoch_str, NULL, 10); 71 } else { 72 /* Use the build date as initial time */ 73 epoch = U_BOOT_EPOCH; 74 } 75 priv->offset_us = epoch * 1000000ULL - timer_get_us(); 76 priv->isdst = -1; 77 78 return 0; 79} 80 81static const struct rtc_ops emul_rtc_ops = { 82 .get = emul_rtc_get, 83 .set = emul_rtc_set, 84}; 85 86U_BOOT_DRIVER(rtc_emul) = { 87 .name = "rtc_emul", 88 .id = UCLASS_RTC, 89 .ops = &emul_rtc_ops, 90 .probe = emul_rtc_probe, 91 .priv_auto = sizeof(struct emul_rtc), 92}; 93 94U_BOOT_DRVINFO(rtc_emul) = { 95 .name = "rtc_emul", 96}; 97