1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright 2023, Heinrich Schuchardt <heinrich.schuchardt@canonical.com> 4 * 5 * This driver emulates a real time clock based on timer ticks. 6 */ 7 8#include <div64.h> 9#include <dm.h> 10#include <mapmem.h> 11#include <rtc.h> 12#include <linux/io.h> 13 14/** 15 * struct goldfish_rtc - private data for RTC driver 16 */ 17struct goldfish_rtc { 18 /** 19 * @base: base address for register file 20 */ 21 void __iomem *base; 22 /** 23 * @isdst: daylight saving time 24 */ 25 int isdst; 26}; 27 28/* Register offsets */ 29#define GOLDFISH_TIME_LOW 0x00 30#define GOLDFISH_TIME_HIGH 0x04 31 32static int goldfish_rtc_get(struct udevice *dev, struct rtc_time *time) 33{ 34 struct goldfish_rtc *priv = dev_get_priv(dev); 35 void __iomem *base = priv->base; 36 u64 time_high; 37 u64 time_low; 38 u64 now; 39 40 time_low = ioread32(base + GOLDFISH_TIME_LOW); 41 time_high = ioread32(base + GOLDFISH_TIME_HIGH); 42 now = (time_high << 32) | time_low; 43 44 do_div(now, 1000000000U); 45 46 rtc_to_tm(now, time); 47 time->tm_isdst = priv->isdst; 48 49 return 0; 50} 51 52static int goldfish_rtc_set(struct udevice *dev, const struct rtc_time *time) 53{ 54 struct goldfish_rtc *priv = dev_get_priv(dev); 55 void __iomem *base = priv->base; 56 u64 now; 57 58 if (time->tm_year < 1970) 59 return -EINVAL; 60 61 now = rtc_mktime(time) * 1000000000ULL; 62 iowrite32(now >> 32, base + GOLDFISH_TIME_HIGH); 63 iowrite32(now, base + GOLDFISH_TIME_LOW); 64 65 if (time->tm_isdst > 0) 66 priv->isdst = 1; 67 else if (time->tm_isdst < 0) 68 priv->isdst = -1; 69 else 70 priv->isdst = 0; 71 72 return 0; 73} 74 75static int goldfish_rtc_probe(struct udevice *dev) 76{ 77 struct goldfish_rtc *priv = dev_get_priv(dev); 78 fdt_addr_t addr; 79 80 addr = dev_read_addr(dev); 81 if (addr == FDT_ADDR_T_NONE) 82 return -EINVAL; 83 priv->base = map_sysmem(addr, 0x20); 84 85 return 0; 86} 87 88static const struct rtc_ops goldfish_rtc_ops = { 89 .get = goldfish_rtc_get, 90 .set = goldfish_rtc_set, 91}; 92 93static const struct udevice_id goldfish_rtc_of_match[] = { 94 { .compatible = "google,goldfish-rtc", }, 95 {}, 96}; 97 98U_BOOT_DRIVER(rtc_goldfish) = { 99 .name = "rtc_goldfish", 100 .id = UCLASS_RTC, 101 .ops = &goldfish_rtc_ops, 102 .probe = goldfish_rtc_probe, 103 .of_match = goldfish_rtc_of_match, 104 .priv_auto = sizeof(struct goldfish_rtc), 105}; 106