Skip to content

Commit dc0684a

Browse files
ychuang3alexandrebelloni
authored andcommitted
rtc: Add driver for Nuvoton ma35d1 rtc controller
The ma35d1 rtc controller provides real-time and calendar messaging capabilities. It supports programmable time tick and alarm match interrupts. The time and calendar messages are expressed in BCD format. This driver supports the built-in rtc controller of the ma35d1. It enables setting and reading the rtc time and configuring and reading the rtc alarm. Signed-off-by: Jacky Huang <ychuang3@nuvoton.com> Link: https://lore.kernel.org/r/20230925070251.28-4-ychuang570808@gmail.com Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
1 parent 3767bba commit dc0684a

3 files changed

Lines changed: 336 additions & 0 deletions

File tree

drivers/rtc/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1930,6 +1930,17 @@ config RTC_DRV_TI_K3
19301930
This driver can also be built as a module, if so, the module
19311931
will be called "rtc-ti-k3".
19321932

1933+
config RTC_DRV_MA35D1
1934+
tristate "Nuvoton MA35D1 RTC"
1935+
depends on ARCH_MA35 || COMPILE_TEST
1936+
select REGMAP_MMIO
1937+
help
1938+
If you say yes here you get support for the Nuvoton MA35D1
1939+
On-Chip Real Time Clock.
1940+
1941+
This driver can also be built as a module, if so, the module
1942+
will be called "rtc-ma35d1".
1943+
19331944
comment "HID Sensor RTC drivers"
19341945

19351946
config RTC_DRV_HID_SENSOR_TIME

drivers/rtc/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o
8888
obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o
8989
obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o
9090
obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o
91+
obj-$(CONFIG_RTC_DRV_MA35D1) += rtc-ma35d1.o
9192
obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o
9293
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
9394
obj-$(CONFIG_RTC_DRV_MAX6916) += rtc-max6916.o

drivers/rtc/rtc-ma35d1.c

Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* RTC driver for Nuvoton MA35D1
4+
*
5+
* Copyright (C) 2023 Nuvoton Technology Corp.
6+
*/
7+
8+
#include <linux/bcd.h>
9+
#include <linux/clk.h>
10+
#include <linux/delay.h>
11+
#include <linux/init.h>
12+
#include <linux/io.h>
13+
#include <linux/module.h>
14+
#include <linux/of.h>
15+
#include <linux/platform_device.h>
16+
#include <linux/rtc.h>
17+
18+
/* MA35D1 RTC Control Registers */
19+
#define MA35_REG_RTC_INIT 0x00
20+
#define MA35_REG_RTC_SINFASTS 0x04
21+
#define MA35_REG_RTC_FREQADJ 0x08
22+
#define MA35_REG_RTC_TIME 0x0c
23+
#define MA35_REG_RTC_CAL 0x10
24+
#define MA35_REG_RTC_CLKFMT 0x14
25+
#define MA35_REG_RTC_WEEKDAY 0x18
26+
#define MA35_REG_RTC_TALM 0x1c
27+
#define MA35_REG_RTC_CALM 0x20
28+
#define MA35_REG_RTC_LEAPYEAR 0x24
29+
#define MA35_REG_RTC_INTEN 0x28
30+
#define MA35_REG_RTC_INTSTS 0x2c
31+
32+
/* register MA35_REG_RTC_INIT */
33+
#define RTC_INIT_ACTIVE BIT(0)
34+
#define RTC_INIT_MAGIC_CODE 0xa5eb1357
35+
36+
/* register MA35_REG_RTC_CLKFMT */
37+
#define RTC_CLKFMT_24HEN BIT(0)
38+
#define RTC_CLKFMT_DCOMPEN BIT(16)
39+
40+
/* register MA35_REG_RTC_INTEN */
41+
#define RTC_INTEN_ALMIEN BIT(0)
42+
#define RTC_INTEN_UIEN BIT(1)
43+
#define RTC_INTEN_CLKFIEN BIT(24)
44+
#define RTC_INTEN_CLKSTIEN BIT(25)
45+
46+
/* register MA35_REG_RTC_INTSTS */
47+
#define RTC_INTSTS_ALMIF BIT(0)
48+
#define RTC_INTSTS_UIF BIT(1)
49+
#define RTC_INTSTS_CLKFIF BIT(24)
50+
#define RTC_INTSTS_CLKSTIF BIT(25)
51+
52+
#define RTC_INIT_TIMEOUT 250
53+
54+
struct ma35_rtc {
55+
int irq_num;
56+
void __iomem *rtc_reg;
57+
struct rtc_device *rtcdev;
58+
};
59+
60+
static u32 rtc_reg_read(struct ma35_rtc *p, u32 offset)
61+
{
62+
return __raw_readl(p->rtc_reg + offset);
63+
}
64+
65+
static inline void rtc_reg_write(struct ma35_rtc *p, u32 offset, u32 value)
66+
{
67+
__raw_writel(value, p->rtc_reg + offset);
68+
}
69+
70+
static irqreturn_t ma35d1_rtc_interrupt(int irq, void *data)
71+
{
72+
struct ma35_rtc *rtc = (struct ma35_rtc *)data;
73+
unsigned long events = 0, rtc_irq;
74+
75+
rtc_irq = rtc_reg_read(rtc, MA35_REG_RTC_INTSTS);
76+
77+
if (rtc_irq & RTC_INTSTS_ALMIF) {
78+
rtc_reg_write(rtc, MA35_REG_RTC_INTSTS, RTC_INTSTS_ALMIF);
79+
events |= RTC_AF | RTC_IRQF;
80+
}
81+
82+
if (rtc_irq & RTC_INTSTS_UIF) {
83+
rtc_reg_write(rtc, MA35_REG_RTC_INTSTS, RTC_INTSTS_UIF);
84+
events |= RTC_UF | RTC_IRQF;
85+
}
86+
87+
rtc_update_irq(rtc->rtcdev, 1, events);
88+
89+
return IRQ_HANDLED;
90+
}
91+
92+
static int ma35d1_rtc_init(struct ma35_rtc *rtc, u32 ms_timeout)
93+
{
94+
const unsigned long timeout = jiffies + msecs_to_jiffies(ms_timeout);
95+
96+
do {
97+
if (rtc_reg_read(rtc, MA35_REG_RTC_INIT) & RTC_INIT_ACTIVE)
98+
return 0;
99+
100+
rtc_reg_write(rtc, MA35_REG_RTC_INIT, RTC_INIT_MAGIC_CODE);
101+
102+
mdelay(1);
103+
104+
} while (time_before(jiffies, timeout));
105+
106+
return -ETIMEDOUT;
107+
}
108+
109+
static int ma35d1_alarm_irq_enable(struct device *dev, u32 enabled)
110+
{
111+
struct ma35_rtc *rtc = dev_get_drvdata(dev);
112+
u32 reg_ien;
113+
114+
reg_ien = rtc_reg_read(rtc, MA35_REG_RTC_INTEN);
115+
116+
if (enabled)
117+
rtc_reg_write(rtc, MA35_REG_RTC_INTEN, reg_ien | RTC_INTEN_ALMIEN);
118+
else
119+
rtc_reg_write(rtc, MA35_REG_RTC_INTEN, reg_ien & ~RTC_INTEN_ALMIEN);
120+
121+
return 0;
122+
}
123+
124+
static int ma35d1_rtc_read_time(struct device *dev, struct rtc_time *tm)
125+
{
126+
struct ma35_rtc *rtc = dev_get_drvdata(dev);
127+
u32 time, cal, wday;
128+
129+
do {
130+
time = rtc_reg_read(rtc, MA35_REG_RTC_TIME);
131+
cal = rtc_reg_read(rtc, MA35_REG_RTC_CAL);
132+
wday = rtc_reg_read(rtc, MA35_REG_RTC_WEEKDAY);
133+
} while (time != rtc_reg_read(rtc, MA35_REG_RTC_TIME) ||
134+
cal != rtc_reg_read(rtc, MA35_REG_RTC_CAL));
135+
136+
tm->tm_mday = bcd2bin(cal >> 0);
137+
tm->tm_wday = wday;
138+
tm->tm_mon = bcd2bin(cal >> 8);
139+
tm->tm_mon = tm->tm_mon - 1;
140+
tm->tm_year = bcd2bin(cal >> 16) + 100;
141+
142+
tm->tm_sec = bcd2bin(time >> 0);
143+
tm->tm_min = bcd2bin(time >> 8);
144+
tm->tm_hour = bcd2bin(time >> 16);
145+
146+
return rtc_valid_tm(tm);
147+
}
148+
149+
static int ma35d1_rtc_set_time(struct device *dev, struct rtc_time *tm)
150+
{
151+
struct ma35_rtc *rtc = dev_get_drvdata(dev);
152+
u32 val;
153+
154+
val = bin2bcd(tm->tm_mday) << 0 | bin2bcd(tm->tm_mon + 1) << 8 |
155+
bin2bcd(tm->tm_year - 100) << 16;
156+
rtc_reg_write(rtc, MA35_REG_RTC_CAL, val);
157+
158+
val = bin2bcd(tm->tm_sec) << 0 | bin2bcd(tm->tm_min) << 8 |
159+
bin2bcd(tm->tm_hour) << 16;
160+
rtc_reg_write(rtc, MA35_REG_RTC_TIME, val);
161+
162+
val = tm->tm_wday;
163+
rtc_reg_write(rtc, MA35_REG_RTC_WEEKDAY, val);
164+
165+
return 0;
166+
}
167+
168+
static int ma35d1_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
169+
{
170+
struct ma35_rtc *rtc = dev_get_drvdata(dev);
171+
u32 talm, calm;
172+
173+
talm = rtc_reg_read(rtc, MA35_REG_RTC_TALM);
174+
calm = rtc_reg_read(rtc, MA35_REG_RTC_CALM);
175+
176+
alrm->time.tm_mday = bcd2bin(calm >> 0);
177+
alrm->time.tm_mon = bcd2bin(calm >> 8);
178+
alrm->time.tm_mon = alrm->time.tm_mon - 1;
179+
180+
alrm->time.tm_year = bcd2bin(calm >> 16) + 100;
181+
182+
alrm->time.tm_sec = bcd2bin(talm >> 0);
183+
alrm->time.tm_min = bcd2bin(talm >> 8);
184+
alrm->time.tm_hour = bcd2bin(talm >> 16);
185+
186+
return rtc_valid_tm(&alrm->time);
187+
}
188+
189+
static int ma35d1_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
190+
{
191+
struct ma35_rtc *rtc = dev_get_drvdata(dev);
192+
unsigned long val;
193+
194+
val = bin2bcd(alrm->time.tm_mday) << 0 | bin2bcd(alrm->time.tm_mon + 1) << 8 |
195+
bin2bcd(alrm->time.tm_year - 100) << 16;
196+
rtc_reg_write(rtc, MA35_REG_RTC_CALM, val);
197+
198+
val = bin2bcd(alrm->time.tm_sec) << 0 | bin2bcd(alrm->time.tm_min) << 8 |
199+
bin2bcd(alrm->time.tm_hour) << 16;
200+
rtc_reg_write(rtc, MA35_REG_RTC_TALM, val);
201+
202+
ma35d1_alarm_irq_enable(dev, alrm->enabled);
203+
204+
return 0;
205+
}
206+
207+
static const struct rtc_class_ops ma35d1_rtc_ops = {
208+
.read_time = ma35d1_rtc_read_time,
209+
.set_time = ma35d1_rtc_set_time,
210+
.read_alarm = ma35d1_rtc_read_alarm,
211+
.set_alarm = ma35d1_rtc_set_alarm,
212+
.alarm_irq_enable = ma35d1_alarm_irq_enable,
213+
};
214+
215+
static int ma35d1_rtc_probe(struct platform_device *pdev)
216+
{
217+
struct ma35_rtc *rtc;
218+
struct clk *clk;
219+
u32 regval;
220+
int ret;
221+
222+
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
223+
if (!rtc)
224+
return -ENOMEM;
225+
226+
rtc->rtc_reg = devm_platform_ioremap_resource(pdev, 0);
227+
if (IS_ERR(rtc->rtc_reg))
228+
return PTR_ERR(rtc->rtc_reg);
229+
230+
clk = of_clk_get(pdev->dev.of_node, 0);
231+
if (IS_ERR(clk))
232+
return dev_err_probe(&pdev->dev, PTR_ERR(clk), "failed to find rtc clock\n");
233+
234+
ret = clk_prepare_enable(clk);
235+
if (ret)
236+
return ret;
237+
238+
if (!(rtc_reg_read(rtc, MA35_REG_RTC_INIT) & RTC_INIT_ACTIVE)) {
239+
ret = ma35d1_rtc_init(rtc, RTC_INIT_TIMEOUT);
240+
if (ret)
241+
return dev_err_probe(&pdev->dev, ret, "rtc init failed\n");
242+
}
243+
244+
rtc->irq_num = platform_get_irq(pdev, 0);
245+
246+
ret = devm_request_irq(&pdev->dev, rtc->irq_num, ma35d1_rtc_interrupt,
247+
IRQF_NO_SUSPEND, "ma35d1rtc", rtc);
248+
if (ret)
249+
return dev_err_probe(&pdev->dev, ret, "Failed to request rtc irq\n");
250+
251+
platform_set_drvdata(pdev, rtc);
252+
253+
device_init_wakeup(&pdev->dev, true);
254+
255+
rtc->rtcdev = devm_rtc_allocate_device(&pdev->dev);
256+
if (IS_ERR(rtc->rtcdev))
257+
return PTR_ERR(rtc->rtcdev);
258+
259+
rtc->rtcdev->ops = &ma35d1_rtc_ops;
260+
rtc->rtcdev->range_min = RTC_TIMESTAMP_BEGIN_2000;
261+
rtc->rtcdev->range_max = RTC_TIMESTAMP_END_2099;
262+
263+
ret = devm_rtc_register_device(rtc->rtcdev);
264+
if (ret)
265+
return dev_err_probe(&pdev->dev, ret, "Failed to register rtc device\n");
266+
267+
regval = rtc_reg_read(rtc, MA35_REG_RTC_INTEN);
268+
regval |= RTC_INTEN_UIEN;
269+
rtc_reg_write(rtc, MA35_REG_RTC_INTEN, regval);
270+
271+
return 0;
272+
}
273+
274+
static int ma35d1_rtc_suspend(struct platform_device *pdev, pm_message_t state)
275+
{
276+
struct ma35_rtc *rtc = platform_get_drvdata(pdev);
277+
u32 regval;
278+
279+
if (device_may_wakeup(&pdev->dev))
280+
enable_irq_wake(rtc->irq_num);
281+
282+
regval = rtc_reg_read(rtc, MA35_REG_RTC_INTEN);
283+
regval &= ~RTC_INTEN_UIEN;
284+
rtc_reg_write(rtc, MA35_REG_RTC_INTEN, regval);
285+
286+
return 0;
287+
}
288+
289+
static int ma35d1_rtc_resume(struct platform_device *pdev)
290+
{
291+
struct ma35_rtc *rtc = platform_get_drvdata(pdev);
292+
u32 regval;
293+
294+
if (device_may_wakeup(&pdev->dev))
295+
disable_irq_wake(rtc->irq_num);
296+
297+
regval = rtc_reg_read(rtc, MA35_REG_RTC_INTEN);
298+
regval |= RTC_INTEN_UIEN;
299+
rtc_reg_write(rtc, MA35_REG_RTC_INTEN, regval);
300+
301+
return 0;
302+
}
303+
304+
static const struct of_device_id ma35d1_rtc_of_match[] = {
305+
{ .compatible = "nuvoton,ma35d1-rtc", },
306+
{},
307+
};
308+
MODULE_DEVICE_TABLE(of, ma35d1_rtc_of_match);
309+
310+
static struct platform_driver ma35d1_rtc_driver = {
311+
.suspend = ma35d1_rtc_suspend,
312+
.resume = ma35d1_rtc_resume,
313+
.probe = ma35d1_rtc_probe,
314+
.driver = {
315+
.name = "rtc-ma35d1",
316+
.of_match_table = ma35d1_rtc_of_match,
317+
},
318+
};
319+
320+
module_platform_driver(ma35d1_rtc_driver);
321+
322+
MODULE_AUTHOR("Ming-Jen Chen <mjchen@nuvoton.com>");
323+
MODULE_DESCRIPTION("MA35D1 RTC driver");
324+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)