1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * RTC driver for NXP LPC178x/18xx/43xx Real-Time Clock (RTC)
5 * Copyright (C) 2011 NXP Semiconductors
6 * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
11 #include <linux/kernel.h>
12 #include <linux/module.h>
14 #include <linux/of_device.h>
15 #include <linux/platform_device.h>
16 #include <linux/rtc.h>
18 /* LPC24xx RTC register offsets and bits */
19 #define LPC24XX_ILR 0x00
20 #define LPC24XX_RTCCIF BIT(0)
21 #define LPC24XX_RTCALF BIT(1)
22 #define LPC24XX_CTC 0x04
23 #define LPC24XX_CCR 0x08
24 #define LPC24XX_CLKEN BIT(0)
25 #define LPC178X_CCALEN BIT(4)
26 #define LPC24XX_CIIR 0x0c
27 #define LPC24XX_AMR 0x10
28 #define LPC24XX_ALARM_DISABLE 0xff
29 #define LPC24XX_CTIME0 0x14
30 #define LPC24XX_CTIME1 0x18
31 #define LPC24XX_CTIME2 0x1c
32 #define LPC24XX_SEC 0x20
33 #define LPC24XX_MIN 0x24
34 #define LPC24XX_HOUR 0x28
35 #define LPC24XX_DOM 0x2c
36 #define LPC24XX_DOW 0x30
37 #define LPC24XX_DOY 0x34
38 #define LPC24XX_MONTH 0x38
39 #define LPC24XX_YEAR 0x3c
40 #define LPC24XX_ALSEC 0x60
41 #define LPC24XX_ALMIN 0x64
42 #define LPC24XX_ALHOUR 0x68
43 #define LPC24XX_ALDOM 0x6c
44 #define LPC24XX_ALDOW 0x70
45 #define LPC24XX_ALDOY 0x74
46 #define LPC24XX_ALMON 0x78
47 #define LPC24XX_ALYEAR 0x7c
49 /* Macros to read fields in consolidated time (CT) registers */
50 #define CT0_SECS(x) (((x) >> 0) & 0x3f)
51 #define CT0_MINS(x) (((x) >> 8) & 0x3f)
52 #define CT0_HOURS(x) (((x) >> 16) & 0x1f)
53 #define CT0_DOW(x) (((x) >> 24) & 0x07)
54 #define CT1_DOM(x) (((x) >> 0) & 0x1f)
55 #define CT1_MONTH(x) (((x) >> 8) & 0x0f)
56 #define CT1_YEAR(x) (((x) >> 16) & 0xfff)
57 #define CT2_DOY(x) (((x) >> 0) & 0xfff)
59 #define rtc_readl(dev, reg) readl((dev)->rtc_base + (reg))
60 #define rtc_writel(dev, reg, val) writel((val), (dev)->rtc_base + (reg))
63 void __iomem
*rtc_base
;
64 struct rtc_device
*rtc
;
69 static int lpc24xx_rtc_set_time(struct device
*dev
, struct rtc_time
*tm
)
71 struct lpc24xx_rtc
*rtc
= dev_get_drvdata(dev
);
73 /* Disable RTC during update */
74 rtc_writel(rtc
, LPC24XX_CCR
, LPC178X_CCALEN
);
76 rtc_writel(rtc
, LPC24XX_SEC
, tm
->tm_sec
);
77 rtc_writel(rtc
, LPC24XX_MIN
, tm
->tm_min
);
78 rtc_writel(rtc
, LPC24XX_HOUR
, tm
->tm_hour
);
79 rtc_writel(rtc
, LPC24XX_DOW
, tm
->tm_wday
);
80 rtc_writel(rtc
, LPC24XX_DOM
, tm
->tm_mday
);
81 rtc_writel(rtc
, LPC24XX_DOY
, tm
->tm_yday
);
82 rtc_writel(rtc
, LPC24XX_MONTH
, tm
->tm_mon
);
83 rtc_writel(rtc
, LPC24XX_YEAR
, tm
->tm_year
);
85 rtc_writel(rtc
, LPC24XX_CCR
, LPC24XX_CLKEN
| LPC178X_CCALEN
);
90 static int lpc24xx_rtc_read_time(struct device
*dev
, struct rtc_time
*tm
)
92 struct lpc24xx_rtc
*rtc
= dev_get_drvdata(dev
);
95 ct0
= rtc_readl(rtc
, LPC24XX_CTIME0
);
96 ct1
= rtc_readl(rtc
, LPC24XX_CTIME1
);
97 ct2
= rtc_readl(rtc
, LPC24XX_CTIME2
);
99 tm
->tm_sec
= CT0_SECS(ct0
);
100 tm
->tm_min
= CT0_MINS(ct0
);
101 tm
->tm_hour
= CT0_HOURS(ct0
);
102 tm
->tm_wday
= CT0_DOW(ct0
);
103 tm
->tm_mon
= CT1_MONTH(ct1
);
104 tm
->tm_mday
= CT1_DOM(ct1
);
105 tm
->tm_year
= CT1_YEAR(ct1
);
106 tm
->tm_yday
= CT2_DOY(ct2
);
111 static int lpc24xx_rtc_read_alarm(struct device
*dev
, struct rtc_wkalrm
*wkalrm
)
113 struct lpc24xx_rtc
*rtc
= dev_get_drvdata(dev
);
114 struct rtc_time
*tm
= &wkalrm
->time
;
116 tm
->tm_sec
= rtc_readl(rtc
, LPC24XX_ALSEC
);
117 tm
->tm_min
= rtc_readl(rtc
, LPC24XX_ALMIN
);
118 tm
->tm_hour
= rtc_readl(rtc
, LPC24XX_ALHOUR
);
119 tm
->tm_mday
= rtc_readl(rtc
, LPC24XX_ALDOM
);
120 tm
->tm_wday
= rtc_readl(rtc
, LPC24XX_ALDOW
);
121 tm
->tm_yday
= rtc_readl(rtc
, LPC24XX_ALDOY
);
122 tm
->tm_mon
= rtc_readl(rtc
, LPC24XX_ALMON
);
123 tm
->tm_year
= rtc_readl(rtc
, LPC24XX_ALYEAR
);
125 wkalrm
->enabled
= rtc_readl(rtc
, LPC24XX_AMR
) == 0;
126 wkalrm
->pending
= !!(rtc_readl(rtc
, LPC24XX_ILR
) & LPC24XX_RTCCIF
);
128 return rtc_valid_tm(&wkalrm
->time
);
131 static int lpc24xx_rtc_set_alarm(struct device
*dev
, struct rtc_wkalrm
*wkalrm
)
133 struct lpc24xx_rtc
*rtc
= dev_get_drvdata(dev
);
134 struct rtc_time
*tm
= &wkalrm
->time
;
136 /* Disable alarm irq during update */
137 rtc_writel(rtc
, LPC24XX_AMR
, LPC24XX_ALARM_DISABLE
);
139 rtc_writel(rtc
, LPC24XX_ALSEC
, tm
->tm_sec
);
140 rtc_writel(rtc
, LPC24XX_ALMIN
, tm
->tm_min
);
141 rtc_writel(rtc
, LPC24XX_ALHOUR
, tm
->tm_hour
);
142 rtc_writel(rtc
, LPC24XX_ALDOM
, tm
->tm_mday
);
143 rtc_writel(rtc
, LPC24XX_ALDOW
, tm
->tm_wday
);
144 rtc_writel(rtc
, LPC24XX_ALDOY
, tm
->tm_yday
);
145 rtc_writel(rtc
, LPC24XX_ALMON
, tm
->tm_mon
);
146 rtc_writel(rtc
, LPC24XX_ALYEAR
, tm
->tm_year
);
149 rtc_writel(rtc
, LPC24XX_AMR
, 0);
154 static int lpc24xx_rtc_alarm_irq_enable(struct device
*dev
, unsigned int enable
)
156 struct lpc24xx_rtc
*rtc
= dev_get_drvdata(dev
);
159 rtc_writel(rtc
, LPC24XX_AMR
, 0);
161 rtc_writel(rtc
, LPC24XX_AMR
, LPC24XX_ALARM_DISABLE
);
166 static irqreturn_t
lpc24xx_rtc_interrupt(int irq
, void *data
)
168 unsigned long events
= RTC_IRQF
;
169 struct lpc24xx_rtc
*rtc
= data
;
172 /* Check interrupt cause */
173 rtc_iir
= rtc_readl(rtc
, LPC24XX_ILR
);
174 if (rtc_iir
& LPC24XX_RTCALF
) {
176 rtc_writel(rtc
, LPC24XX_AMR
, LPC24XX_ALARM_DISABLE
);
179 /* Clear interrupt status and report event */
180 rtc_writel(rtc
, LPC24XX_ILR
, rtc_iir
);
181 rtc_update_irq(rtc
->rtc
, 1, events
);
186 static const struct rtc_class_ops lpc24xx_rtc_ops
= {
187 .read_time
= lpc24xx_rtc_read_time
,
188 .set_time
= lpc24xx_rtc_set_time
,
189 .read_alarm
= lpc24xx_rtc_read_alarm
,
190 .set_alarm
= lpc24xx_rtc_set_alarm
,
191 .alarm_irq_enable
= lpc24xx_rtc_alarm_irq_enable
,
194 static int lpc24xx_rtc_probe(struct platform_device
*pdev
)
196 struct lpc24xx_rtc
*rtc
;
199 rtc
= devm_kzalloc(&pdev
->dev
, sizeof(*rtc
), GFP_KERNEL
);
203 rtc
->rtc_base
= devm_platform_ioremap_resource(pdev
, 0);
204 if (IS_ERR(rtc
->rtc_base
))
205 return PTR_ERR(rtc
->rtc_base
);
207 irq
= platform_get_irq(pdev
, 0);
211 rtc
->clk_rtc
= devm_clk_get(&pdev
->dev
, "rtc");
212 if (IS_ERR(rtc
->clk_rtc
)) {
213 dev_err(&pdev
->dev
, "error getting rtc clock\n");
214 return PTR_ERR(rtc
->clk_rtc
);
217 rtc
->clk_reg
= devm_clk_get(&pdev
->dev
, "reg");
218 if (IS_ERR(rtc
->clk_reg
)) {
219 dev_err(&pdev
->dev
, "error getting reg clock\n");
220 return PTR_ERR(rtc
->clk_reg
);
223 ret
= clk_prepare_enable(rtc
->clk_rtc
);
225 dev_err(&pdev
->dev
, "unable to enable rtc clock\n");
229 ret
= clk_prepare_enable(rtc
->clk_reg
);
231 dev_err(&pdev
->dev
, "unable to enable reg clock\n");
232 goto disable_rtc_clk
;
235 platform_set_drvdata(pdev
, rtc
);
237 /* Clear any pending interrupts */
238 rtc_writel(rtc
, LPC24XX_ILR
, LPC24XX_RTCCIF
| LPC24XX_RTCALF
);
240 /* Enable RTC count */
241 rtc_writel(rtc
, LPC24XX_CCR
, LPC24XX_CLKEN
| LPC178X_CCALEN
);
243 ret
= devm_request_irq(&pdev
->dev
, irq
, lpc24xx_rtc_interrupt
, 0,
246 dev_warn(&pdev
->dev
, "can't request interrupt\n");
250 rtc
->rtc
= devm_rtc_device_register(&pdev
->dev
, "lpc24xx-rtc",
251 &lpc24xx_rtc_ops
, THIS_MODULE
);
252 if (IS_ERR(rtc
->rtc
)) {
253 dev_err(&pdev
->dev
, "can't register rtc device\n");
254 ret
= PTR_ERR(rtc
->rtc
);
261 clk_disable_unprepare(rtc
->clk_reg
);
263 clk_disable_unprepare(rtc
->clk_rtc
);
267 static int lpc24xx_rtc_remove(struct platform_device
*pdev
)
269 struct lpc24xx_rtc
*rtc
= platform_get_drvdata(pdev
);
271 /* Ensure all interrupt sources are masked */
272 rtc_writel(rtc
, LPC24XX_AMR
, LPC24XX_ALARM_DISABLE
);
273 rtc_writel(rtc
, LPC24XX_CIIR
, 0);
275 rtc_writel(rtc
, LPC24XX_CCR
, LPC178X_CCALEN
);
277 clk_disable_unprepare(rtc
->clk_rtc
);
278 clk_disable_unprepare(rtc
->clk_reg
);
283 static const struct of_device_id lpc24xx_rtc_match
[] = {
284 { .compatible
= "nxp,lpc1788-rtc" },
287 MODULE_DEVICE_TABLE(of
, lpc24xx_rtc_match
);
289 static struct platform_driver lpc24xx_rtc_driver
= {
290 .probe
= lpc24xx_rtc_probe
,
291 .remove
= lpc24xx_rtc_remove
,
293 .name
= "lpc24xx-rtc",
294 .of_match_table
= lpc24xx_rtc_match
,
297 module_platform_driver(lpc24xx_rtc_driver
);
299 MODULE_AUTHOR("Kevin Wells <wellsk40@gmail.com>");
300 MODULE_DESCRIPTION("RTC driver for the LPC178x/18xx/408x/43xx SoCs");
301 MODULE_LICENSE("GPL");