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/rtc.h>
16 #include <linux/delay.h>
18 #include <linux/bcd.h>
20 /* RTC Control Registers */
21 #define REG_RTC_INIR 0x00
22 #define REG_RTC_AER 0x04
23 #define REG_RTC_FCR 0x08
24 #define REG_RTC_TLR 0x0C
25 #define REG_RTC_CLR 0x10
26 #define REG_RTC_TSSR 0x14
27 #define REG_RTC_DWR 0x18
28 #define REG_RTC_TAR 0x1C
29 #define REG_RTC_CAR 0x20
30 #define REG_RTC_LIR 0x24
31 #define REG_RTC_RIER 0x28
32 #define REG_RTC_RIIR 0x2C
33 #define REG_RTC_TTR 0x30
36 #define AERRWENB 0x10000
37 #define INIRRESET 0xa5eb1357
38 #define AERPOWERON 0xA965
39 #define AERPOWEROFF 0x0000
40 #define LEAPYEAR 0x0001
42 #define TICKINTENB 0x0002
43 #define ALARMINTENB 0x0001
48 void __iomem
*rtc_reg
;
49 struct rtc_device
*rtcdev
;
52 struct nuc900_bcd_time
{
61 static irqreturn_t
nuc900_rtc_interrupt(int irq
, void *_rtc
)
63 struct nuc900_rtc
*rtc
= _rtc
;
64 unsigned long events
= 0, rtc_irq
;
66 rtc_irq
= __raw_readl(rtc
->rtc_reg
+ REG_RTC_RIIR
);
68 if (rtc_irq
& ALARMINTENB
) {
69 rtc_irq
&= ~ALARMINTENB
;
70 __raw_writel(rtc_irq
, rtc
->rtc_reg
+ REG_RTC_RIIR
);
71 events
|= RTC_AF
| RTC_IRQF
;
74 if (rtc_irq
& TICKINTENB
) {
75 rtc_irq
&= ~TICKINTENB
;
76 __raw_writel(rtc_irq
, rtc
->rtc_reg
+ REG_RTC_RIIR
);
77 events
|= RTC_UF
| RTC_IRQF
;
80 rtc_update_irq(rtc
->rtcdev
, 1, events
);
85 static int *check_rtc_access_enable(struct nuc900_rtc
*nuc900_rtc
)
88 __raw_writel(INIRRESET
, nuc900_rtc
->rtc_reg
+ REG_RTC_INIR
);
92 __raw_writel(AERPOWERON
, nuc900_rtc
->rtc_reg
+ REG_RTC_AER
);
94 for (i
= 0; i
< 1000; i
++) {
95 if (__raw_readl(nuc900_rtc
->rtc_reg
+ REG_RTC_AER
) & AERRWENB
)
99 if ((__raw_readl(nuc900_rtc
->rtc_reg
+ REG_RTC_AER
) & AERRWENB
) == 0x0)
100 return ERR_PTR(-ENODEV
);
102 return ERR_PTR(-EPERM
);
105 static void 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);
119 static void nuc900_rtc_bin2bcd(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;
124 gettm
->bcd_year
= bin2bcd(settm
->tm_year
- 100) << 16;
126 gettm
->bcd_sec
= bin2bcd(settm
->tm_sec
) << 0;
127 gettm
->bcd_min
= bin2bcd(settm
->tm_min
) << 8;
128 gettm
->bcd_hour
= bin2bcd(settm
->tm_hour
) << 16;
131 static int nuc900_update_irq_enable(struct device
*dev
, unsigned int enabled
)
133 struct nuc900_rtc
*rtc
= dev_get_drvdata(dev
);
136 __raw_writel(__raw_readl(rtc
->rtc_reg
+ REG_RTC_RIER
)|
137 (TICKINTENB
), rtc
->rtc_reg
+ REG_RTC_RIER
);
139 __raw_writel(__raw_readl(rtc
->rtc_reg
+ REG_RTC_RIER
)&
140 (~TICKINTENB
), rtc
->rtc_reg
+ REG_RTC_RIER
);
145 static int nuc900_alarm_irq_enable(struct device
*dev
, unsigned int enabled
)
147 struct nuc900_rtc
*rtc
= dev_get_drvdata(dev
);
150 __raw_writel(__raw_readl(rtc
->rtc_reg
+ REG_RTC_RIER
)|
151 (ALARMINTENB
), rtc
->rtc_reg
+ REG_RTC_RIER
);
153 __raw_writel(__raw_readl(rtc
->rtc_reg
+ REG_RTC_RIER
)&
154 (~ALARMINTENB
), rtc
->rtc_reg
+ REG_RTC_RIER
);
159 static int nuc900_rtc_read_time(struct device
*dev
, struct rtc_time
*tm
)
161 struct nuc900_rtc
*rtc
= dev_get_drvdata(dev
);
162 unsigned int timeval
, clrval
;
164 timeval
= __raw_readl(rtc
->rtc_reg
+ REG_RTC_TLR
);
165 clrval
= __raw_readl(rtc
->rtc_reg
+ REG_RTC_CLR
);
167 nuc900_rtc_bcd2bin(timeval
, clrval
, tm
);
172 static int nuc900_rtc_set_time(struct device
*dev
, struct rtc_time
*tm
)
174 struct nuc900_rtc
*rtc
= dev_get_drvdata(dev
);
175 struct nuc900_bcd_time gettm
;
179 nuc900_rtc_bin2bcd(tm
, &gettm
);
181 err
= check_rtc_access_enable(rtc
);
185 val
= gettm
.bcd_mday
| gettm
.bcd_mon
| gettm
.bcd_year
;
186 __raw_writel(val
, rtc
->rtc_reg
+ REG_RTC_CLR
);
188 val
= gettm
.bcd_sec
| gettm
.bcd_min
| gettm
.bcd_hour
;
189 __raw_writel(val
, rtc
->rtc_reg
+ REG_RTC_TLR
);
194 static int nuc900_rtc_read_alarm(struct device
*dev
, struct rtc_wkalrm
*alrm
)
196 struct nuc900_rtc
*rtc
= dev_get_drvdata(dev
);
197 unsigned int timeval
, carval
;
199 timeval
= __raw_readl(rtc
->rtc_reg
+ REG_RTC_TAR
);
200 carval
= __raw_readl(rtc
->rtc_reg
+ REG_RTC_CAR
);
202 nuc900_rtc_bcd2bin(timeval
, carval
, &alrm
->time
);
207 static int nuc900_rtc_set_alarm(struct device
*dev
, struct rtc_wkalrm
*alrm
)
209 struct nuc900_rtc
*rtc
= dev_get_drvdata(dev
);
210 struct nuc900_bcd_time tm
;
214 nuc900_rtc_bin2bcd(&alrm
->time
, &tm
);
216 err
= check_rtc_access_enable(rtc
);
220 val
= tm
.bcd_mday
| tm
.bcd_mon
| tm
.bcd_year
;
221 __raw_writel(val
, rtc
->rtc_reg
+ REG_RTC_CAR
);
223 val
= tm
.bcd_sec
| tm
.bcd_min
| tm
.bcd_hour
;
224 __raw_writel(val
, rtc
->rtc_reg
+ REG_RTC_TAR
);
229 static struct rtc_class_ops nuc900_rtc_ops
= {
230 .read_time
= nuc900_rtc_read_time
,
231 .set_time
= nuc900_rtc_set_time
,
232 .read_alarm
= nuc900_rtc_read_alarm
,
233 .set_alarm
= nuc900_rtc_set_alarm
,
234 .alarm_irq_enable
= nuc900_alarm_irq_enable
,
235 .update_irq_enable
= nuc900_update_irq_enable
,
238 static int __devinit
nuc900_rtc_probe(struct platform_device
*pdev
)
240 struct resource
*res
;
241 struct nuc900_rtc
*nuc900_rtc
;
244 nuc900_rtc
= kzalloc(sizeof(struct nuc900_rtc
), GFP_KERNEL
);
246 dev_err(&pdev
->dev
, "kzalloc nuc900_rtc failed\n");
249 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
251 dev_err(&pdev
->dev
, "platform_get_resource failed\n");
256 if (!request_mem_region(res
->start
, resource_size(res
),
258 dev_err(&pdev
->dev
, "request_mem_region failed\n");
263 nuc900_rtc
->rtc_reg
= ioremap(res
->start
, resource_size(res
));
264 if (!nuc900_rtc
->rtc_reg
) {
265 dev_err(&pdev
->dev
, "ioremap rtc_reg failed\n");
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");
278 nuc900_rtc
->rtcdev
= rtc_device_register(pdev
->name
, &pdev
->dev
,
279 &nuc900_rtc_ops
, THIS_MODULE
);
280 if (IS_ERR(nuc900_rtc
->rtcdev
)) {
281 dev_err(&pdev
->dev
, "rtc device register faild\n");
282 err
= PTR_ERR(nuc900_rtc
->rtcdev
);
286 platform_set_drvdata(pdev
, nuc900_rtc
);
287 __raw_writel(__raw_readl(nuc900_rtc
->rtc_reg
+ REG_RTC_TSSR
) | MODE24
,
288 nuc900_rtc
->rtc_reg
+ REG_RTC_TSSR
);
292 fail4
: free_irq(nuc900_rtc
->irq_num
, nuc900_rtc
);
293 fail3
: iounmap(nuc900_rtc
->rtc_reg
);
294 fail2
: release_mem_region(res
->start
, resource_size(res
));
295 fail1
: kfree(nuc900_rtc
);
299 static int __devexit
nuc900_rtc_remove(struct platform_device
*pdev
)
301 struct nuc900_rtc
*nuc900_rtc
= platform_get_drvdata(pdev
);
302 struct resource
*res
;
304 rtc_device_unregister(nuc900_rtc
->rtcdev
);
305 free_irq(nuc900_rtc
->irq_num
, nuc900_rtc
);
306 iounmap(nuc900_rtc
->rtc_reg
);
308 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
309 release_mem_region(res
->start
, resource_size(res
));
313 platform_set_drvdata(pdev
, NULL
);
318 static struct platform_driver nuc900_rtc_driver
= {
319 .remove
= __devexit_p(nuc900_rtc_remove
),
321 .name
= "nuc900-rtc",
322 .owner
= THIS_MODULE
,
326 static int __init
nuc900_rtc_init(void)
328 return platform_driver_probe(&nuc900_rtc_driver
, nuc900_rtc_probe
);
331 static void __exit
nuc900_rtc_exit(void)
333 platform_driver_unregister(&nuc900_rtc_driver
);
336 module_init(nuc900_rtc_init
);
337 module_exit(nuc900_rtc_exit
);
339 MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
340 MODULE_DESCRIPTION("nuc910/nuc920 RTC driver");
341 MODULE_LICENSE("GPL");
342 MODULE_ALIAS("platform:nuc900-rtc");