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