1 From 222dedcdc09247126d39364a614ff2019789f52a Mon Sep 17 00:00:00 2001
2 From: Dom Cobley <popcornmix@gmail.com>
3 Date: Fri, 7 Jul 2023 20:00:45 +0100
4 Subject: [PATCH] rtc: rtc-rpi: Add simple RTC driver for Raspberry Pi
6 This supports setting and reading the real time clock
7 and supports wakeup alarms.
9 To support wake up alarms you want this bootloader config:
14 echo +600 | sudo tee /sys/class/rtc/rtc0/wakealarm
17 That will halt (in an almost no power state),
18 then wake and restart after 10 minutes.
20 Signed-off-by: Dom Cobley <popcornmix@gmail.com>
22 drivers/rtc/Kconfig | 11 +++
23 drivers/rtc/Makefile | 1 +
24 drivers/rtc/rtc-rpi.c | 177 ++++++++++++++++++++++++++++++++++++++++++
25 3 files changed, 189 insertions(+)
26 create mode 100644 drivers/rtc/rtc-rpi.c
28 --- a/drivers/rtc/Kconfig
29 +++ b/drivers/rtc/Kconfig
30 @@ -223,6 +223,17 @@ config RTC_DRV_AC100
31 This driver can also be built as a module. If so, the module
32 will be called rtc-ac100.
35 + tristate "Raspberry Pi RTC"
36 + depends on ARCH_BRCMSTB || COMPILE_TEST
37 + default ARCH_BRCMSTB
39 + If you say yes here you get support for the RTC found on
40 + Raspberry Pi devices.
42 + This driver can also be built as a module. If so, the module
43 + will be called rtc-rpi.
45 config RTC_DRV_BRCMSTB
46 tristate "Broadcom STB wake-timer"
47 depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST
48 --- a/drivers/rtc/Makefile
49 +++ b/drivers/rtc/Makefile
50 @@ -140,6 +140,7 @@ obj-$(CONFIG_RTC_DRV_RC5T583) += rtc-rc5
51 obj-$(CONFIG_RTC_DRV_RC5T619) += rtc-rc5t619.o
52 obj-$(CONFIG_RTC_DRV_RK808) += rtc-rk808.o
53 obj-$(CONFIG_RTC_DRV_RP5C01) += rtc-rp5c01.o
54 +obj-$(CONFIG_RTC_DRV_RPI) += rtc-rpi.o
55 obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o
56 obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o
57 obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o
59 +++ b/drivers/rtc/rtc-rpi.c
61 +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
65 + * RTC driver using firmware mailbox
66 + * Supports battery backed RTC and wake alarms
68 + * Based on rtc-meson-vrtc by Neil Armstrong
70 + * Copyright (c) 2023, Raspberry Pi Ltd.
73 +#include <linux/module.h>
74 +#include <linux/platform_device.h>
75 +#include <linux/rtc.h>
76 +#include <linux/of.h>
77 +#include <soc/bcm2835/raspberrypi-firmware.h>
79 +struct rpi_rtc_data {
80 + struct rtc_device *rtc;
81 + struct rpi_firmware *fw;
84 +#define RPI_FIRMWARE_GET_RTC_REG 0x00030087
85 +#define RPI_FIRMWARE_SET_RTC_REG 0x00038087
86 +enum {RTC_TIME, RTC_ALARM, RTC_ALARM_PENDING, RTC_ALARM_ENABLE};
88 +static int rpi_rtc_read_time(struct device *dev, struct rtc_time *tm)
90 + struct rpi_rtc_data *vrtc = dev_get_drvdata(dev);
91 + u32 data[2] = {RTC_TIME};
94 + err = rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_GET_RTC_REG,
95 + &data, sizeof(data));
96 + rtc_time64_to_tm(data[1], tm);
100 +static int rpi_rtc_set_time(struct device *dev, struct rtc_time *tm)
102 + struct rpi_rtc_data *vrtc = dev_get_drvdata(dev);
103 + u32 data[2] = {RTC_TIME, rtc_tm_to_time64(tm)};
105 + return rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_SET_RTC_REG,
106 + &data, sizeof(data));
109 +static int rpi_rtc_alarm_irq_is_enabled(struct device *dev, unsigned char *enabled)
111 + struct rpi_rtc_data *vrtc = dev_get_drvdata(dev);
112 + u32 data[2] = {RTC_ALARM_ENABLE};
115 + err = rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_GET_RTC_REG,
116 + &data, sizeof(data));
117 + *enabled = data[1] & 0x1;
121 +static int rpi_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
123 + struct rpi_rtc_data *vrtc = dev_get_drvdata(dev);
124 + u32 data[2] = {RTC_ALARM_ENABLE, enabled};
126 + return rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_SET_RTC_REG,
127 + &data, sizeof(data));
130 +static int rpi_rtc_alarm_clear_pending(struct device *dev)
132 + struct rpi_rtc_data *vrtc = dev_get_drvdata(dev);
133 + u32 data[2] = {RTC_ALARM_PENDING, 1};
135 + return rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_SET_RTC_REG,
136 + &data, sizeof(data));
139 +static int rpi_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
141 + struct rpi_rtc_data *vrtc = dev_get_drvdata(dev);
142 + u32 data[2] = {RTC_ALARM};
145 + err = rpi_rtc_alarm_irq_is_enabled(dev, &alarm->enabled);
147 + err = rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_GET_RTC_REG,
148 + &data, sizeof(data));
149 + rtc_time64_to_tm(data[1], &alarm->time);
154 +static int rpi_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
156 + struct rpi_rtc_data *vrtc = dev_get_drvdata(dev);
157 + u32 data[2] = {RTC_ALARM, rtc_tm_to_time64(&alarm->time)};
160 + err = rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_SET_RTC_REG,
161 + &data, sizeof(data));
164 + err = rpi_rtc_alarm_irq_enable(dev, alarm->enabled);
169 +static const struct rtc_class_ops rpi_rtc_ops = {
170 + .read_time = rpi_rtc_read_time,
171 + .set_time = rpi_rtc_set_time,
172 + .read_alarm = rpi_rtc_read_alarm,
173 + .set_alarm = rpi_rtc_set_alarm,
174 + .alarm_irq_enable = rpi_rtc_alarm_irq_enable,
177 +static int rpi_rtc_probe(struct platform_device *pdev)
179 + struct rpi_rtc_data *vrtc;
180 + struct device *dev = &pdev->dev;
181 + struct device_node *np = dev->of_node;
182 + struct device_node *fw_node;
183 + struct rpi_firmware *fw;
186 + fw_node = of_parse_phandle(np, "firmware", 0);
188 + dev_err(dev, "Missing firmware node\n");
192 + fw = rpi_firmware_get(fw_node);
194 + return -EPROBE_DEFER;
196 + vrtc = devm_kzalloc(&pdev->dev, sizeof(*vrtc), GFP_KERNEL);
202 + device_init_wakeup(&pdev->dev, 1);
204 + platform_set_drvdata(pdev, vrtc);
206 + vrtc->rtc = devm_rtc_allocate_device(&pdev->dev);
207 + if (IS_ERR(vrtc->rtc))
208 + return PTR_ERR(vrtc->rtc);
210 + set_bit(RTC_FEATURE_ALARM_WAKEUP_ONLY, vrtc->rtc->features);
211 + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, vrtc->rtc->features);
213 + vrtc->rtc->ops = &rpi_rtc_ops;
214 + ret = devm_rtc_register_device(vrtc->rtc);
216 + rpi_rtc_alarm_clear_pending(dev);
220 +static const struct of_device_id rpi_rtc_dt_match[] = {
221 + { .compatible = "raspberrypi,rpi-rtc"},
224 +MODULE_DEVICE_TABLE(of, rpi_rtc_dt_match);
226 +static struct platform_driver rpi_rtc_driver = {
227 + .probe = rpi_rtc_probe,
230 + .of_match_table = rpi_rtc_dt_match,
234 +module_platform_driver(rpi_rtc_driver);
236 +MODULE_DESCRIPTION("Raspberry Pi RTC driver");
237 +MODULE_LICENSE("GPL");