blob: 3231eb0daf8337a360a20b87973c95dd664912a2 [file] [log] [blame]
Heinrich Schuchardtd76beea2023-12-31 00:53:00 +01001// 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
Heinrich Schuchardt59d10d02024-03-21 21:16:13 +010075static int goldfish_rtc_probe(struct udevice *dev)
Heinrich Schuchardtd76beea2023-12-31 00:53:00 +010076{
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};