Skip to content

Commit deeb4b5

Browse files
Michel Polletalexandrebelloni
authored andcommitted
rtc: rzn1: Add new RTC driver
Add a basic RTC driver for the RZ/N1. Signed-off-by: Michel Pollet <[email protected]> Co-developed-by: Miquel Raynal <[email protected]> Signed-off-by: Miquel Raynal <[email protected]> Signed-off-by: Alexandre Belloni <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 4c4d145 commit deeb4b5

File tree

3 files changed

+251
-0
lines changed

3 files changed

+251
-0
lines changed

drivers/rtc/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1548,6 +1548,13 @@ config RTC_DRV_RS5C313
15481548
help
15491549
If you say yes here you get support for the Ricoh RS5C313 RTC chips.
15501550

1551+
config RTC_DRV_RZN1
1552+
tristate "Renesas RZ/N1 RTC"
1553+
depends on ARCH_RZN1 || COMPILE_TEST
1554+
depends on OF && HAS_IOMEM
1555+
help
1556+
If you say yes here you get support for the Renesas RZ/N1 RTC.
1557+
15511558
config RTC_DRV_GENERIC
15521559
tristate "Generic RTC support"
15531560
# Please consider writing a new RTC driver instead of using the generic

drivers/rtc/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ obj-$(CONFIG_RTC_DRV_RX6110) += rtc-rx6110.o
151151
obj-$(CONFIG_RTC_DRV_RX8010) += rtc-rx8010.o
152152
obj-$(CONFIG_RTC_DRV_RX8025) += rtc-rx8025.o
153153
obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o
154+
obj-$(CONFIG_RTC_DRV_RZN1) += rtc-rzn1.o
154155
obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o
155156
obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o
156157
obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o

drivers/rtc/rtc-rzn1.c

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
// SPDX-License-Identifier: GPL-2.0+
2+
/*
3+
* Renesas RZ/N1 Real Time Clock interface for Linux
4+
*
5+
* Copyright:
6+
* - 2014 Renesas Electronics Europe Limited
7+
* - 2022 Schneider Electric
8+
*
9+
* Authors:
10+
* - Michel Pollet <[email protected]>, <[email protected]>
11+
* - Miquel Raynal <[email protected]>
12+
*/
13+
14+
#include <linux/bcd.h>
15+
#include <linux/init.h>
16+
#include <linux/iopoll.h>
17+
#include <linux/module.h>
18+
#include <linux/of_device.h>
19+
#include <linux/platform_device.h>
20+
#include <linux/pm_runtime.h>
21+
#include <linux/rtc.h>
22+
23+
#define RZN1_RTC_CTL0 0x00
24+
#define RZN1_RTC_CTL0_SLSB_SUBU 0
25+
#define RZN1_RTC_CTL0_SLSB_SCMP BIT(4)
26+
#define RZN1_RTC_CTL0_AMPM BIT(5)
27+
#define RZN1_RTC_CTL0_CE BIT(7)
28+
29+
#define RZN1_RTC_CTL1 0x04
30+
#define RZN1_RTC_CTL1_ALME BIT(4)
31+
32+
#define RZN1_RTC_CTL2 0x08
33+
#define RZN1_RTC_CTL2_WAIT BIT(0)
34+
#define RZN1_RTC_CTL2_WST BIT(1)
35+
#define RZN1_RTC_CTL2_WUST BIT(5)
36+
#define RZN1_RTC_CTL2_STOPPED (RZN1_RTC_CTL2_WAIT | RZN1_RTC_CTL2_WST)
37+
38+
#define RZN1_RTC_SEC 0x14
39+
#define RZN1_RTC_MIN 0x18
40+
#define RZN1_RTC_HOUR 0x1c
41+
#define RZN1_RTC_WEEK 0x20
42+
#define RZN1_RTC_DAY 0x24
43+
#define RZN1_RTC_MONTH 0x28
44+
#define RZN1_RTC_YEAR 0x2c
45+
46+
#define RZN1_RTC_SUBU 0x38
47+
#define RZN1_RTC_SUBU_DEV BIT(7)
48+
#define RZN1_RTC_SUBU_DECR BIT(6)
49+
50+
#define RZN1_RTC_ALM 0x40
51+
#define RZN1_RTC_ALH 0x44
52+
#define RZN1_RTC_ALW 0x48
53+
54+
#define RZN1_RTC_SECC 0x4c
55+
#define RZN1_RTC_MINC 0x50
56+
#define RZN1_RTC_HOURC 0x54
57+
#define RZN1_RTC_WEEKC 0x58
58+
#define RZN1_RTC_DAYC 0x5c
59+
#define RZN1_RTC_MONTHC 0x60
60+
#define RZN1_RTC_YEARC 0x64
61+
62+
struct rzn1_rtc {
63+
struct rtc_device *rtcdev;
64+
void __iomem *base;
65+
};
66+
67+
static void rzn1_rtc_get_time_snapshot(struct rzn1_rtc *rtc, struct rtc_time *tm)
68+
{
69+
tm->tm_sec = readl(rtc->base + RZN1_RTC_SECC);
70+
tm->tm_min = readl(rtc->base + RZN1_RTC_MINC);
71+
tm->tm_hour = readl(rtc->base + RZN1_RTC_HOURC);
72+
tm->tm_wday = readl(rtc->base + RZN1_RTC_WEEKC);
73+
tm->tm_mday = readl(rtc->base + RZN1_RTC_DAYC);
74+
tm->tm_mon = readl(rtc->base + RZN1_RTC_MONTHC);
75+
tm->tm_year = readl(rtc->base + RZN1_RTC_YEARC);
76+
}
77+
78+
static unsigned int rzn1_rtc_tm_to_wday(struct rtc_time *tm)
79+
{
80+
time64_t time;
81+
unsigned int days;
82+
u32 secs;
83+
84+
time = rtc_tm_to_time64(tm);
85+
days = div_s64_rem(time, 86400, &secs);
86+
87+
/* day of the week, 1970-01-01 was a Thursday */
88+
return (days + 4) % 7;
89+
}
90+
91+
static int rzn1_rtc_read_time(struct device *dev, struct rtc_time *tm)
92+
{
93+
struct rzn1_rtc *rtc = dev_get_drvdata(dev);
94+
u32 val, secs;
95+
96+
/*
97+
* The RTC was not started or is stopped and thus does not carry the
98+
* proper time/date.
99+
*/
100+
val = readl(rtc->base + RZN1_RTC_CTL2);
101+
if (val & RZN1_RTC_CTL2_STOPPED)
102+
return -EINVAL;
103+
104+
rzn1_rtc_get_time_snapshot(rtc, tm);
105+
secs = readl(rtc->base + RZN1_RTC_SECC);
106+
if (tm->tm_sec != secs)
107+
rzn1_rtc_get_time_snapshot(rtc, tm);
108+
109+
tm->tm_sec = bcd2bin(tm->tm_sec);
110+
tm->tm_min = bcd2bin(tm->tm_min);
111+
tm->tm_hour = bcd2bin(tm->tm_hour);
112+
tm->tm_wday = bcd2bin(tm->tm_wday);
113+
tm->tm_mday = bcd2bin(tm->tm_mday);
114+
tm->tm_mon = bcd2bin(tm->tm_mon);
115+
tm->tm_year = bcd2bin(tm->tm_year);
116+
117+
return 0;
118+
}
119+
120+
static int rzn1_rtc_set_time(struct device *dev, struct rtc_time *tm)
121+
{
122+
struct rzn1_rtc *rtc = dev_get_drvdata(dev);
123+
u32 val;
124+
int ret;
125+
126+
tm->tm_sec = bin2bcd(tm->tm_sec);
127+
tm->tm_min = bin2bcd(tm->tm_min);
128+
tm->tm_hour = bin2bcd(tm->tm_hour);
129+
tm->tm_wday = bin2bcd(rzn1_rtc_tm_to_wday(tm));
130+
tm->tm_mday = bin2bcd(tm->tm_mday);
131+
tm->tm_mon = bin2bcd(tm->tm_mon);
132+
tm->tm_year = bin2bcd(tm->tm_year);
133+
134+
val = readl(rtc->base + RZN1_RTC_CTL2);
135+
if (!(val & RZN1_RTC_CTL2_STOPPED)) {
136+
/* Hold the counter if it was counting up */
137+
writel(RZN1_RTC_CTL2_WAIT, rtc->base + RZN1_RTC_CTL2);
138+
139+
/* Wait for the counter to stop: two 32k clock cycles */
140+
usleep_range(61, 100);
141+
ret = readl_poll_timeout(rtc->base + RZN1_RTC_CTL2, val,
142+
val & RZN1_RTC_CTL2_WST, 0, 100);
143+
if (ret)
144+
return ret;
145+
}
146+
147+
writel(tm->tm_sec, rtc->base + RZN1_RTC_SEC);
148+
writel(tm->tm_min, rtc->base + RZN1_RTC_MIN);
149+
writel(tm->tm_hour, rtc->base + RZN1_RTC_HOUR);
150+
writel(tm->tm_wday, rtc->base + RZN1_RTC_WEEK);
151+
writel(tm->tm_mday, rtc->base + RZN1_RTC_DAY);
152+
writel(tm->tm_mon, rtc->base + RZN1_RTC_MONTH);
153+
writel(tm->tm_year, rtc->base + RZN1_RTC_YEAR);
154+
writel(0, rtc->base + RZN1_RTC_CTL2);
155+
156+
return 0;
157+
}
158+
159+
static const struct rtc_class_ops rzn1_rtc_ops = {
160+
.read_time = rzn1_rtc_read_time,
161+
.set_time = rzn1_rtc_set_time,
162+
};
163+
164+
static int rzn1_rtc_probe(struct platform_device *pdev)
165+
{
166+
struct rzn1_rtc *rtc;
167+
int ret;
168+
169+
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
170+
if (!rtc)
171+
return -ENOMEM;
172+
173+
platform_set_drvdata(pdev, rtc);
174+
175+
rtc->base = devm_platform_ioremap_resource(pdev, 0);
176+
if (IS_ERR(rtc->base))
177+
return dev_err_probe(&pdev->dev, PTR_ERR(rtc->base), "Missing reg\n");
178+
179+
rtc->rtcdev = devm_rtc_allocate_device(&pdev->dev);
180+
if (IS_ERR(rtc->rtcdev))
181+
return PTR_ERR(rtc);
182+
183+
rtc->rtcdev->range_min = RTC_TIMESTAMP_BEGIN_2000;
184+
rtc->rtcdev->range_max = RTC_TIMESTAMP_END_2099;
185+
rtc->rtcdev->ops = &rzn1_rtc_ops;
186+
clear_bit(RTC_FEATURE_ALARM, rtc->rtcdev->features);
187+
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->rtcdev->features);
188+
189+
devm_pm_runtime_enable(&pdev->dev);
190+
ret = pm_runtime_resume_and_get(&pdev->dev);
191+
if (ret < 0)
192+
return ret;
193+
194+
/*
195+
* Ensure the clock counter is enabled.
196+
* Set 24-hour mode and possible oscillator offset compensation in SUBU mode.
197+
*/
198+
writel(RZN1_RTC_CTL0_CE | RZN1_RTC_CTL0_AMPM | RZN1_RTC_CTL0_SLSB_SUBU,
199+
rtc->base + RZN1_RTC_CTL0);
200+
201+
/* Disable all interrupts */
202+
writel(0, rtc->base + RZN1_RTC_CTL1);
203+
204+
ret = devm_rtc_register_device(rtc->rtcdev);
205+
if (ret)
206+
goto dis_runtime_pm;
207+
208+
return 0;
209+
210+
dis_runtime_pm:
211+
pm_runtime_put(&pdev->dev);
212+
213+
return ret;
214+
}
215+
216+
static int rzn1_rtc_remove(struct platform_device *pdev)
217+
{
218+
pm_runtime_put(&pdev->dev);
219+
220+
return 0;
221+
}
222+
223+
static const struct of_device_id rzn1_rtc_of_match[] = {
224+
{ .compatible = "renesas,rzn1-rtc" },
225+
{},
226+
};
227+
MODULE_DEVICE_TABLE(of, rzn1_rtc_of_match);
228+
229+
static struct platform_driver rzn1_rtc_driver = {
230+
.probe = rzn1_rtc_probe,
231+
.remove = rzn1_rtc_remove,
232+
.driver = {
233+
.name = "rzn1-rtc",
234+
.owner = THIS_MODULE,
235+
.of_match_table = rzn1_rtc_of_match,
236+
},
237+
};
238+
module_platform_driver(rzn1_rtc_driver);
239+
240+
MODULE_AUTHOR("Michel Pollet <[email protected]");
241+
MODULE_AUTHOR("Miquel Raynal <[email protected]");
242+
MODULE_DESCRIPTION("RZ/N1 RTC driver");
243+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)