2 * Copyright (c) 2008-2009 Nuvoton technology corporation.
4 * Wan ZongShun <mcuos.com@gmail.com>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation;version 2 of the License.
12 #include <linux/module.h>
13 #include <linux/init.h>
14 #include <linux/platform_device.h>
15 #include <linux/slab.h>
16 #include <linux/rtc.h>
17 #include <linux/delay.h>
19 #include <linux/bcd.h>
21 /* RTC Control Registers */
22 #define REG_RTC_INIR 0x00
23 #define REG_RTC_AER 0x04
24 #define REG_RTC_FCR 0x08
25 #define REG_RTC_TLR 0x0C
26 #define REG_RTC_CLR 0x10
27 #define REG_RTC_TSSR 0x14
28 #define REG_RTC_DWR 0x18
29 #define REG_RTC_TAR 0x1C
30 #define REG_RTC_CAR 0x20
31 #define REG_RTC_LIR 0x24
32 #define REG_RTC_RIER 0x28
33 #define REG_RTC_RIIR 0x2C
34 #define REG_RTC_TTR 0x30
37 #define AERRWENB 0x10000
38 #define INIRRESET 0xa5eb1357
39 #define AERPOWERON 0xA965
40 #define AERPOWEROFF 0x0000
41 #define LEAPYEAR 0x0001
43 #define TICKINTENB 0x0002
44 #define ALARMINTENB 0x0001
49 void __iomem
*rtc_reg
;
50 struct rtc_device
*rtcdev
;
53 struct nuc900_bcd_time
{
62 static irqreturn_t
nuc900_rtc_interrupt(int irq
, void *_rtc
)
64 struct nuc900_rtc
*rtc
= _rtc
;
65 unsigned long events
= 0, rtc_irq
;
67 rtc_irq
= __raw_readl(rtc
->rtc_reg
+ REG_RTC_RIIR
);
69 if (rtc_irq
& ALARMINTENB
) {
70 rtc_irq
&= ~ALARMINTENB
;
71 __raw_writel(rtc_irq
, rtc
->rtc_reg
+ REG_RTC_RIIR
);
72 events
|= RTC_AF
| RTC_IRQF
;
75 if (rtc_irq
& TICKINTENB
) {
76 rtc_irq
&= ~TICKINTENB
;
77 __raw_writel(rtc_irq
, rtc
->rtc_reg
+ REG_RTC_RIIR
);
78 events
|= RTC_UF
| RTC_IRQF
;
81 rtc_update_irq(rtc
->rtcdev
, 1, events
);
86 static int *check_rtc_access_enable(struct nuc900_rtc
*nuc900_rtc
)
88 unsigned int timeout
= 0x1000;
89 __raw_writel(INIRRESET
, nuc900_rtc
->rtc_reg
+ REG_RTC_INIR
);
93 __raw_writel(AERPOWERON
, nuc900_rtc
->rtc_reg
+ REG_RTC_AER
);
95 while (!(__raw_readl(nuc900_rtc
->rtc_reg
+ REG_RTC_AER
) & AERRWENB
)
100 return ERR_PTR(-EPERM
);
105 static int nuc900_rtc_bcd2bin(unsigned int timereg
,
106 unsigned int calreg
, struct rtc_time
*tm
)
108 tm
->tm_mday
= bcd2bin(calreg
>> 0);
109 tm
->tm_mon
= bcd2bin(calreg
>> 8);
110 tm
->tm_year
= bcd2bin(calreg
>> 16) + 100;
112 tm
->tm_sec
= bcd2bin(timereg
>> 0);
113 tm
->tm_min
= bcd2bin(timereg
>> 8);
114 tm
->tm_hour
= bcd2bin(timereg
>> 16);
116 return rtc_valid_tm(tm
);
119 static void nuc900_rtc_bin2bcd(struct device
*dev
, struct rtc_time
*settm
,
120 struct nuc900_bcd_time
*gettm
)
122 gettm
->bcd_mday
= bin2bcd(settm
->tm_mday
) << 0;
123 gettm
->bcd_mon
= bin2bcd(settm
->tm_mon
) << 8;
125 if (settm
->tm_year
< 100) {
126 dev_warn(dev
, "The year will be between 1970-1999, right?\n");
127 gettm
->bcd_year
= bin2bcd(settm
->tm_year
) << 16;
129 gettm
->bcd_year
= bin2bcd(settm
->tm_year
- 100) << 16;
132 gettm
->bcd_sec
= bin2bcd(settm
->tm_sec
) << 0;
133 gettm
->bcd_min
= bin2bcd(settm
->tm_min
) << 8;
134 gettm
->bcd_hour
= bin2bcd(settm
->tm_hour
) << 16;
137 static int nuc900_alarm_irq_enable(struct device
*dev
, unsigned int enabled
)
139 struct nuc900_rtc
*rtc
= dev_get_drvdata(dev
);
142 __raw_writel(__raw_readl(rtc
->rtc_reg
+ REG_RTC_RIER
)|
143 (ALARMINTENB
), rtc
->rtc_reg
+ REG_RTC_RIER
);
145 __raw_writel(__raw_readl(rtc
->rtc_reg
+ REG_RTC_RIER
)&
146 (~ALARMINTENB
), rtc
->rtc_reg
+ REG_RTC_RIER
);
151 static int nuc900_rtc_read_time(struct device
*dev
, struct rtc_time
*tm
)
153 struct nuc900_rtc
*rtc
= dev_get_drvdata(dev
);
154 unsigned int timeval
, clrval
;
156 timeval
= __raw_readl(rtc
->rtc_reg
+ REG_RTC_TLR
);
157 clrval
= __raw_readl(rtc
->rtc_reg
+ REG_RTC_CLR
);
159 return nuc900_rtc_bcd2bin(timeval
, clrval
, tm
);
162 static int nuc900_rtc_set_time(struct device
*dev
, struct rtc_time
*tm
)
164 struct nuc900_rtc
*rtc
= dev_get_drvdata(dev
);
165 struct nuc900_bcd_time gettm
;
169 nuc900_rtc_bin2bcd(dev
, tm
, &gettm
);
171 err
= check_rtc_access_enable(rtc
);
175 val
= gettm
.bcd_mday
| gettm
.bcd_mon
| gettm
.bcd_year
;
176 __raw_writel(val
, rtc
->rtc_reg
+ REG_RTC_CLR
);
178 val
= gettm
.bcd_sec
| gettm
.bcd_min
| gettm
.bcd_hour
;
179 __raw_writel(val
, rtc
->rtc_reg
+ REG_RTC_TLR
);
184 static int nuc900_rtc_read_alarm(struct device
*dev
, struct rtc_wkalrm
*alrm
)
186 struct nuc900_rtc
*rtc
= dev_get_drvdata(dev
);
187 unsigned int timeval
, carval
;
189 timeval
= __raw_readl(rtc
->rtc_reg
+ REG_RTC_TAR
);
190 carval
= __raw_readl(rtc
->rtc_reg
+ REG_RTC_CAR
);
192 return nuc900_rtc_bcd2bin(timeval
, carval
, &alrm
->time
);
195 static int nuc900_rtc_set_alarm(struct device
*dev
, struct rtc_wkalrm
*alrm
)
197 struct nuc900_rtc
*rtc
= dev_get_drvdata(dev
);
198 struct nuc900_bcd_time tm
;
202 nuc900_rtc_bin2bcd(dev
, &alrm
->time
, &tm
);
204 err
= check_rtc_access_enable(rtc
);
208 val
= tm
.bcd_mday
| tm
.bcd_mon
| tm
.bcd_year
;
209 __raw_writel(val
, rtc
->rtc_reg
+ REG_RTC_CAR
);
211 val
= tm
.bcd_sec
| tm
.bcd_min
| tm
.bcd_hour
;
212 __raw_writel(val
, rtc
->rtc_reg
+ REG_RTC_TAR
);
217 static struct rtc_class_ops nuc900_rtc_ops
= {
218 .read_time
= nuc900_rtc_read_time
,
219 .set_time
= nuc900_rtc_set_time
,
220 .read_alarm
= nuc900_rtc_read_alarm
,
221 .set_alarm
= nuc900_rtc_set_alarm
,
222 .alarm_irq_enable
= nuc900_alarm_irq_enable
,
225 static int __devinit
nuc900_rtc_probe(struct platform_device
*pdev
)
227 struct resource
*res
;
228 struct nuc900_rtc
*nuc900_rtc
;
231 nuc900_rtc
= kzalloc(sizeof(struct nuc900_rtc
), GFP_KERNEL
);
233 dev_err(&pdev
->dev
, "kzalloc nuc900_rtc failed\n");
236 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
238 dev_err(&pdev
->dev
, "platform_get_resource failed\n");
243 if (!request_mem_region(res
->start
, resource_size(res
),
245 dev_err(&pdev
->dev
, "request_mem_region failed\n");
250 nuc900_rtc
->rtc_reg
= ioremap(res
->start
, resource_size(res
));
251 if (!nuc900_rtc
->rtc_reg
) {
252 dev_err(&pdev
->dev
, "ioremap rtc_reg failed\n");
257 platform_set_drvdata(pdev
, nuc900_rtc
);
259 nuc900_rtc
->rtcdev
= rtc_device_register(pdev
->name
, &pdev
->dev
,
260 &nuc900_rtc_ops
, THIS_MODULE
);
261 if (IS_ERR(nuc900_rtc
->rtcdev
)) {
262 dev_err(&pdev
->dev
, "rtc device register failed\n");
263 err
= PTR_ERR(nuc900_rtc
->rtcdev
);
267 __raw_writel(__raw_readl(nuc900_rtc
->rtc_reg
+ REG_RTC_TSSR
) | MODE24
,
268 nuc900_rtc
->rtc_reg
+ REG_RTC_TSSR
);
270 nuc900_rtc
->irq_num
= platform_get_irq(pdev
, 0);
271 if (request_irq(nuc900_rtc
->irq_num
, nuc900_rtc_interrupt
,
272 IRQF_DISABLED
, "nuc900rtc", nuc900_rtc
)) {
273 dev_err(&pdev
->dev
, "NUC900 RTC request irq failed\n");
280 fail4
: rtc_device_unregister(nuc900_rtc
->rtcdev
);
281 fail3
: iounmap(nuc900_rtc
->rtc_reg
);
282 fail2
: release_mem_region(res
->start
, resource_size(res
));
283 fail1
: kfree(nuc900_rtc
);
287 static int __devexit
nuc900_rtc_remove(struct platform_device
*pdev
)
289 struct nuc900_rtc
*nuc900_rtc
= platform_get_drvdata(pdev
);
290 struct resource
*res
;
292 free_irq(nuc900_rtc
->irq_num
, nuc900_rtc
);
293 rtc_device_unregister(nuc900_rtc
->rtcdev
);
294 iounmap(nuc900_rtc
->rtc_reg
);
296 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
297 release_mem_region(res
->start
, resource_size(res
));
301 platform_set_drvdata(pdev
, NULL
);
306 static struct platform_driver nuc900_rtc_driver
= {
307 .remove
= __devexit_p(nuc900_rtc_remove
),
309 .name
= "nuc900-rtc",
310 .owner
= THIS_MODULE
,
314 static int __init
nuc900_rtc_init(void)
316 return platform_driver_probe(&nuc900_rtc_driver
, nuc900_rtc_probe
);
319 static void __exit
nuc900_rtc_exit(void)
321 platform_driver_unregister(&nuc900_rtc_driver
);
324 module_init(nuc900_rtc_init
);
325 module_exit(nuc900_rtc_exit
);
327 MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
328 MODULE_DESCRIPTION("nuc910/nuc920 RTC driver");
329 MODULE_LICENSE("GPL");
330 MODULE_ALIAS("platform:nuc900-rtc");