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
)
89 __raw_writel(INIRRESET
, nuc900_rtc
->rtc_reg
+ REG_RTC_INIR
);
93 __raw_writel(AERPOWERON
, nuc900_rtc
->rtc_reg
+ REG_RTC_AER
);
95 for (i
= 0; i
< 1000; i
++) {
96 if (__raw_readl(nuc900_rtc
->rtc_reg
+ REG_RTC_AER
) & AERRWENB
)
100 if ((__raw_readl(nuc900_rtc
->rtc_reg
+ REG_RTC_AER
) & AERRWENB
) == 0x0)
101 return ERR_PTR(-ENODEV
);
103 return ERR_PTR(-EPERM
);
106 static void nuc900_rtc_bcd2bin(unsigned int timereg
,
107 unsigned int calreg
, struct rtc_time
*tm
)
109 tm
->tm_mday
= bcd2bin(calreg
>> 0);
110 tm
->tm_mon
= bcd2bin(calreg
>> 8);
111 tm
->tm_year
= bcd2bin(calreg
>> 16) + 100;
113 tm
->tm_sec
= bcd2bin(timereg
>> 0);
114 tm
->tm_min
= bcd2bin(timereg
>> 8);
115 tm
->tm_hour
= bcd2bin(timereg
>> 16);
120 static void nuc900_rtc_bin2bcd(struct rtc_time
*settm
,
121 struct nuc900_bcd_time
*gettm
)
123 gettm
->bcd_mday
= bin2bcd(settm
->tm_mday
) << 0;
124 gettm
->bcd_mon
= bin2bcd(settm
->tm_mon
) << 8;
125 gettm
->bcd_year
= bin2bcd(settm
->tm_year
- 100) << 16;
127 gettm
->bcd_sec
= bin2bcd(settm
->tm_sec
) << 0;
128 gettm
->bcd_min
= bin2bcd(settm
->tm_min
) << 8;
129 gettm
->bcd_hour
= bin2bcd(settm
->tm_hour
) << 16;
132 static int nuc900_update_irq_enable(struct device
*dev
, unsigned int enabled
)
134 struct nuc900_rtc
*rtc
= dev_get_drvdata(dev
);
137 __raw_writel(__raw_readl(rtc
->rtc_reg
+ REG_RTC_RIER
)|
138 (TICKINTENB
), rtc
->rtc_reg
+ REG_RTC_RIER
);
140 __raw_writel(__raw_readl(rtc
->rtc_reg
+ REG_RTC_RIER
)&
141 (~TICKINTENB
), rtc
->rtc_reg
+ REG_RTC_RIER
);
146 static int nuc900_alarm_irq_enable(struct device
*dev
, unsigned int enabled
)
148 struct nuc900_rtc
*rtc
= dev_get_drvdata(dev
);
151 __raw_writel(__raw_readl(rtc
->rtc_reg
+ REG_RTC_RIER
)|
152 (ALARMINTENB
), rtc
->rtc_reg
+ REG_RTC_RIER
);
154 __raw_writel(__raw_readl(rtc
->rtc_reg
+ REG_RTC_RIER
)&
155 (~ALARMINTENB
), rtc
->rtc_reg
+ REG_RTC_RIER
);
160 static int nuc900_rtc_read_time(struct device
*dev
, struct rtc_time
*tm
)
162 struct nuc900_rtc
*rtc
= dev_get_drvdata(dev
);
163 unsigned int timeval
, clrval
;
165 timeval
= __raw_readl(rtc
->rtc_reg
+ REG_RTC_TLR
);
166 clrval
= __raw_readl(rtc
->rtc_reg
+ REG_RTC_CLR
);
168 nuc900_rtc_bcd2bin(timeval
, clrval
, tm
);
173 static int nuc900_rtc_set_time(struct device
*dev
, struct rtc_time
*tm
)
175 struct nuc900_rtc
*rtc
= dev_get_drvdata(dev
);
176 struct nuc900_bcd_time gettm
;
180 nuc900_rtc_bin2bcd(tm
, &gettm
);
182 err
= check_rtc_access_enable(rtc
);
186 val
= gettm
.bcd_mday
| gettm
.bcd_mon
| gettm
.bcd_year
;
187 __raw_writel(val
, rtc
->rtc_reg
+ REG_RTC_CLR
);
189 val
= gettm
.bcd_sec
| gettm
.bcd_min
| gettm
.bcd_hour
;
190 __raw_writel(val
, rtc
->rtc_reg
+ REG_RTC_TLR
);
195 static int nuc900_rtc_read_alarm(struct device
*dev
, struct rtc_wkalrm
*alrm
)
197 struct nuc900_rtc
*rtc
= dev_get_drvdata(dev
);
198 unsigned int timeval
, carval
;
200 timeval
= __raw_readl(rtc
->rtc_reg
+ REG_RTC_TAR
);
201 carval
= __raw_readl(rtc
->rtc_reg
+ REG_RTC_CAR
);
203 nuc900_rtc_bcd2bin(timeval
, carval
, &alrm
->time
);
208 static int nuc900_rtc_set_alarm(struct device
*dev
, struct rtc_wkalrm
*alrm
)
210 struct nuc900_rtc
*rtc
= dev_get_drvdata(dev
);
211 struct nuc900_bcd_time tm
;
215 nuc900_rtc_bin2bcd(&alrm
->time
, &tm
);
217 err
= check_rtc_access_enable(rtc
);
221 val
= tm
.bcd_mday
| tm
.bcd_mon
| tm
.bcd_year
;
222 __raw_writel(val
, rtc
->rtc_reg
+ REG_RTC_CAR
);
224 val
= tm
.bcd_sec
| tm
.bcd_min
| tm
.bcd_hour
;
225 __raw_writel(val
, rtc
->rtc_reg
+ REG_RTC_TAR
);
230 static struct rtc_class_ops nuc900_rtc_ops
= {
231 .read_time
= nuc900_rtc_read_time
,
232 .set_time
= nuc900_rtc_set_time
,
233 .read_alarm
= nuc900_rtc_read_alarm
,
234 .set_alarm
= nuc900_rtc_set_alarm
,
235 .alarm_irq_enable
= nuc900_alarm_irq_enable
,
236 .update_irq_enable
= nuc900_update_irq_enable
,
239 static int __devinit
nuc900_rtc_probe(struct platform_device
*pdev
)
241 struct resource
*res
;
242 struct nuc900_rtc
*nuc900_rtc
;
245 nuc900_rtc
= kzalloc(sizeof(struct nuc900_rtc
), GFP_KERNEL
);
247 dev_err(&pdev
->dev
, "kzalloc nuc900_rtc failed\n");
250 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
252 dev_err(&pdev
->dev
, "platform_get_resource failed\n");
257 if (!request_mem_region(res
->start
, resource_size(res
),
259 dev_err(&pdev
->dev
, "request_mem_region failed\n");
264 nuc900_rtc
->rtc_reg
= ioremap(res
->start
, resource_size(res
));
265 if (!nuc900_rtc
->rtc_reg
) {
266 dev_err(&pdev
->dev
, "ioremap rtc_reg failed\n");
271 nuc900_rtc
->irq_num
= platform_get_irq(pdev
, 0);
272 if (request_irq(nuc900_rtc
->irq_num
, nuc900_rtc_interrupt
,
273 IRQF_DISABLED
, "nuc900rtc", nuc900_rtc
)) {
274 dev_err(&pdev
->dev
, "NUC900 RTC request irq failed\n");
279 nuc900_rtc
->rtcdev
= rtc_device_register(pdev
->name
, &pdev
->dev
,
280 &nuc900_rtc_ops
, THIS_MODULE
);
281 if (IS_ERR(nuc900_rtc
->rtcdev
)) {
282 dev_err(&pdev
->dev
, "rtc device register faild\n");
283 err
= PTR_ERR(nuc900_rtc
->rtcdev
);
287 platform_set_drvdata(pdev
, nuc900_rtc
);
288 __raw_writel(__raw_readl(nuc900_rtc
->rtc_reg
+ REG_RTC_TSSR
) | MODE24
,
289 nuc900_rtc
->rtc_reg
+ REG_RTC_TSSR
);
293 fail4
: free_irq(nuc900_rtc
->irq_num
, nuc900_rtc
);
294 fail3
: iounmap(nuc900_rtc
->rtc_reg
);
295 fail2
: release_mem_region(res
->start
, resource_size(res
));
296 fail1
: kfree(nuc900_rtc
);
300 static int __devexit
nuc900_rtc_remove(struct platform_device
*pdev
)
302 struct nuc900_rtc
*nuc900_rtc
= platform_get_drvdata(pdev
);
303 struct resource
*res
;
305 rtc_device_unregister(nuc900_rtc
->rtcdev
);
306 free_irq(nuc900_rtc
->irq_num
, nuc900_rtc
);
307 iounmap(nuc900_rtc
->rtc_reg
);
309 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
310 release_mem_region(res
->start
, resource_size(res
));
314 platform_set_drvdata(pdev
, NULL
);
319 static struct platform_driver nuc900_rtc_driver
= {
320 .remove
= __devexit_p(nuc900_rtc_remove
),
322 .name
= "nuc900-rtc",
323 .owner
= THIS_MODULE
,
327 static int __init
nuc900_rtc_init(void)
329 return platform_driver_probe(&nuc900_rtc_driver
, nuc900_rtc_probe
);
332 static void __exit
nuc900_rtc_exit(void)
334 platform_driver_unregister(&nuc900_rtc_driver
);
337 module_init(nuc900_rtc_init
);
338 module_exit(nuc900_rtc_exit
);
340 MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
341 MODULE_DESCRIPTION("nuc910/nuc920 RTC driver");
342 MODULE_LICENSE("GPL");
343 MODULE_ALIAS("platform:nuc900-rtc");