aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rtc
diff options
context:
space:
mode:
authorThomas Hommel <thomas.hommel@gefanuc.com>2007-07-21 07:37:58 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-21 20:49:17 -0400
commit029641151bfede0930a79ecabb2572dc27a3c86f (patch)
treeb986836a6beb0de893baa05e5dfa8fb8ed85e5bd /drivers/rtc
parent8a2601f6aa837903bfb385b138b50b1e305f3e04 (diff)
rtc: add support for STK17TA8 chip
This patch adds support for the Simtek STK17TA8 timekeeping chip. The STK17TA8 is quite similar to the DS1553, but differs in register layout and in various control bits in the registers. I chose to make this a new driver to avoid confusion in the code and to not get lost in #ifdefs. Signed-off-by: Thomas Hommel <thomas.hommel@gefanuc.com> Cc: Alessandro Zummo <a.zummo@towertech.it> Cc: David Brownell <david-b@pacbell.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/Kconfig10
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-stk17ta8.c420
3 files changed, 431 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 505f512ac254..9d8d40d5c8f7 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -308,6 +308,16 @@ config RTC_DRV_DS1553
308 This driver can also be built as a module. If so, the module 308 This driver can also be built as a module. If so, the module
309 will be called rtc-ds1553. 309 will be called rtc-ds1553.
310 310
311config RTC_DRV_STK17TA8
312 tristate "Simtek STK17TA8"
313 depends on RTC_CLASS
314 help
315 If you say yes here you get support for the
316 Simtek STK17TA8 timekeeping chip.
317
318 This driver can also be built as a module. If so, the module
319 will be called rtc-stk17ta8.
320
311config RTC_DRV_DS1742 321config RTC_DRV_DS1742
312 tristate "Dallas DS1742/1743" 322 tristate "Dallas DS1742/1743"
313 depends on RTC_CLASS 323 depends on RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 3109af9a1651..7ede9e725360 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o
32obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o 32obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o
33obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o 33obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o
34obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o 34obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
35obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
35obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o 36obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o
36obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o 37obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
37obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o 38obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o
diff --git a/drivers/rtc/rtc-stk17ta8.c b/drivers/rtc/rtc-stk17ta8.c
new file mode 100644
index 000000000000..f10d3facecbe
--- /dev/null
+++ b/drivers/rtc/rtc-stk17ta8.c
@@ -0,0 +1,420 @@
1/*
2 * A RTC driver for the Simtek STK17TA8
3 *
4 * By Thomas Hommel <thomas.hommel@gefanuc.com>
5 *
6 * Based on the DS1553 driver from
7 * Atsushi Nemoto <anemo@mba.ocn.ne.jp>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#include <linux/bcd.h>
15#include <linux/init.h>
16#include <linux/kernel.h>
17#include <linux/delay.h>
18#include <linux/jiffies.h>
19#include <linux/interrupt.h>
20#include <linux/rtc.h>
21#include <linux/platform_device.h>
22#include <linux/io.h>
23
24#define DRV_VERSION "0.1"
25
26#define RTC_REG_SIZE 0x20000
27#define RTC_OFFSET 0x1fff0
28
29#define RTC_FLAGS (RTC_OFFSET + 0)
30#define RTC_CENTURY (RTC_OFFSET + 1)
31#define RTC_SECONDS_ALARM (RTC_OFFSET + 2)
32#define RTC_MINUTES_ALARM (RTC_OFFSET + 3)
33#define RTC_HOURS_ALARM (RTC_OFFSET + 4)
34#define RTC_DATE_ALARM (RTC_OFFSET + 5)
35#define RTC_INTERRUPTS (RTC_OFFSET + 6)
36#define RTC_WATCHDOG (RTC_OFFSET + 7)
37#define RTC_CALIBRATION (RTC_OFFSET + 8)
38#define RTC_SECONDS (RTC_OFFSET + 9)
39#define RTC_MINUTES (RTC_OFFSET + 10)
40#define RTC_HOURS (RTC_OFFSET + 11)
41#define RTC_DAY (RTC_OFFSET + 12)
42#define RTC_DATE (RTC_OFFSET + 13)
43#define RTC_MONTH (RTC_OFFSET + 14)
44#define RTC_YEAR (RTC_OFFSET + 15)
45
46#define RTC_SECONDS_MASK 0x7f
47#define RTC_DAY_MASK 0x07
48#define RTC_CAL_MASK 0x3f
49
50/* Bits in the Calibration register */
51#define RTC_STOP 0x80
52
53/* Bits in the Flags register */
54#define RTC_FLAGS_AF 0x40
55#define RTC_FLAGS_PF 0x20
56#define RTC_WRITE 0x02
57#define RTC_READ 0x01
58
59/* Bits in the Interrupts register */
60#define RTC_INTS_AIE 0x40
61
62struct rtc_plat_data {
63 struct rtc_device *rtc;
64 void __iomem *ioaddr;
65 unsigned long baseaddr;
66 unsigned long last_jiffies;
67 int irq;
68 unsigned int irqen;
69 int alrm_sec;
70 int alrm_min;
71 int alrm_hour;
72 int alrm_mday;
73};
74
75static int stk17ta8_rtc_set_time(struct device *dev, struct rtc_time *tm)
76{
77 struct platform_device *pdev = to_platform_device(dev);
78 struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
79 void __iomem *ioaddr = pdata->ioaddr;
80 u8 flags;
81
82 flags = readb(pdata->ioaddr + RTC_FLAGS);
83 writeb(flags | RTC_WRITE, pdata->ioaddr + RTC_FLAGS);
84
85 writeb(BIN2BCD(tm->tm_year % 100), ioaddr + RTC_YEAR);
86 writeb(BIN2BCD(tm->tm_mon + 1), ioaddr + RTC_MONTH);
87 writeb(BIN2BCD(tm->tm_wday) & RTC_DAY_MASK, ioaddr + RTC_DAY);
88 writeb(BIN2BCD(tm->tm_mday), ioaddr + RTC_DATE);
89 writeb(BIN2BCD(tm->tm_hour), ioaddr + RTC_HOURS);
90 writeb(BIN2BCD(tm->tm_min), ioaddr + RTC_MINUTES);
91 writeb(BIN2BCD(tm->tm_sec) & RTC_SECONDS_MASK, ioaddr + RTC_SECONDS);
92 writeb(BIN2BCD((tm->tm_year + 1900) / 100), ioaddr + RTC_CENTURY);
93
94 writeb(flags & ~RTC_WRITE, pdata->ioaddr + RTC_FLAGS);
95 return 0;
96}
97
98static int stk17ta8_rtc_read_time(struct device *dev, struct rtc_time *tm)
99{
100 struct platform_device *pdev = to_platform_device(dev);
101 struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
102 void __iomem *ioaddr = pdata->ioaddr;
103 unsigned int year, month, day, hour, minute, second, week;
104 unsigned int century;
105 u8 flags;
106
107 /* give enough time to update RTC in case of continuous read */
108 if (pdata->last_jiffies == jiffies)
109 msleep(1);
110 pdata->last_jiffies = jiffies;
111
112 flags = readb(pdata->ioaddr + RTC_FLAGS);
113 writeb(flags | RTC_READ, ioaddr + RTC_FLAGS);
114 second = readb(ioaddr + RTC_SECONDS) & RTC_SECONDS_MASK;
115 minute = readb(ioaddr + RTC_MINUTES);
116 hour = readb(ioaddr + RTC_HOURS);
117 day = readb(ioaddr + RTC_DATE);
118 week = readb(ioaddr + RTC_DAY) & RTC_DAY_MASK;
119 month = readb(ioaddr + RTC_MONTH);
120 year = readb(ioaddr + RTC_YEAR);
121 century = readb(ioaddr + RTC_CENTURY);
122 writeb(flags & ~RTC_READ, ioaddr + RTC_FLAGS);
123 tm->tm_sec = BCD2BIN(second);
124 tm->tm_min = BCD2BIN(minute);
125 tm->tm_hour = BCD2BIN(hour);
126 tm->tm_mday = BCD2BIN(day);
127 tm->tm_wday = BCD2BIN(week);
128 tm->tm_mon = BCD2BIN(month) - 1;
129 /* year is 1900 + tm->tm_year */
130 tm->tm_year = BCD2BIN(year) + BCD2BIN(century) * 100 - 1900;
131
132 if (rtc_valid_tm(tm) < 0) {
133 dev_err(dev, "retrieved date/time is not valid.\n");
134 rtc_time_to_tm(0, tm);
135 }
136 return 0;
137}
138
139static void stk17ta8_rtc_update_alarm(struct rtc_plat_data *pdata)
140{
141 void __iomem *ioaddr = pdata->ioaddr;
142 unsigned long irqflags;
143 u8 flags;
144
145 spin_lock_irqsave(&pdata->rtc->irq_lock, irqflags);
146
147 flags = readb(ioaddr + RTC_FLAGS);
148 writeb(flags | RTC_WRITE, ioaddr + RTC_FLAGS);
149
150 writeb(pdata->alrm_mday < 0 || (pdata->irqen & RTC_UF) ?
151 0x80 : BIN2BCD(pdata->alrm_mday),
152 ioaddr + RTC_DATE_ALARM);
153 writeb(pdata->alrm_hour < 0 || (pdata->irqen & RTC_UF) ?
154 0x80 : BIN2BCD(pdata->alrm_hour),
155 ioaddr + RTC_HOURS_ALARM);
156 writeb(pdata->alrm_min < 0 || (pdata->irqen & RTC_UF) ?
157 0x80 : BIN2BCD(pdata->alrm_min),
158 ioaddr + RTC_MINUTES_ALARM);
159 writeb(pdata->alrm_sec < 0 || (pdata->irqen & RTC_UF) ?
160 0x80 : BIN2BCD(pdata->alrm_sec),
161 ioaddr + RTC_SECONDS_ALARM);
162 writeb(pdata->irqen ? RTC_INTS_AIE : 0, ioaddr + RTC_INTERRUPTS);
163 readb(ioaddr + RTC_FLAGS); /* clear interrupts */
164 writeb(flags & ~RTC_WRITE, ioaddr + RTC_FLAGS);
165 spin_unlock_irqrestore(&pdata->rtc->irq_lock, irqflags);
166}
167
168static int stk17ta8_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
169{
170 struct platform_device *pdev = to_platform_device(dev);
171 struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
172
173 if (pdata->irq < 0)
174 return -EINVAL;
175 pdata->alrm_mday = alrm->time.tm_mday;
176 pdata->alrm_hour = alrm->time.tm_hour;
177 pdata->alrm_min = alrm->time.tm_min;
178 pdata->alrm_sec = alrm->time.tm_sec;
179 if (alrm->enabled)
180 pdata->irqen |= RTC_AF;
181 stk17ta8_rtc_update_alarm(pdata);
182 return 0;
183}
184
185static int stk17ta8_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
186{
187 struct platform_device *pdev = to_platform_device(dev);
188 struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
189
190 if (pdata->irq < 0)
191 return -EINVAL;
192 alrm->time.tm_mday = pdata->alrm_mday < 0 ? 0 : pdata->alrm_mday;
193 alrm->time.tm_hour = pdata->alrm_hour < 0 ? 0 : pdata->alrm_hour;
194 alrm->time.tm_min = pdata->alrm_min < 0 ? 0 : pdata->alrm_min;
195 alrm->time.tm_sec = pdata->alrm_sec < 0 ? 0 : pdata->alrm_sec;
196 alrm->enabled = (pdata->irqen & RTC_AF) ? 1 : 0;
197 return 0;
198}
199
200static irqreturn_t stk17ta8_rtc_interrupt(int irq, void *dev_id)
201{
202 struct platform_device *pdev = dev_id;
203 struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
204 void __iomem *ioaddr = pdata->ioaddr;
205 unsigned long events = RTC_IRQF;
206
207 /* read and clear interrupt */
208 if (!(readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_AF))
209 return IRQ_NONE;
210 if (readb(ioaddr + RTC_SECONDS_ALARM) & 0x80)
211 events |= RTC_UF;
212 else
213 events |= RTC_AF;
214 rtc_update_irq(pdata->rtc, 1, events);
215 return IRQ_HANDLED;
216}
217
218static void stk17ta8_rtc_release(struct device *dev)
219{
220 struct platform_device *pdev = to_platform_device(dev);
221 struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
222
223 if (pdata->irq >= 0) {
224 pdata->irqen = 0;
225 stk17ta8_rtc_update_alarm(pdata);
226 }
227}
228
229static int stk17ta8_rtc_ioctl(struct device *dev, unsigned int cmd,
230 unsigned long arg)
231{
232 struct platform_device *pdev = to_platform_device(dev);
233 struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
234
235 if (pdata->irq < 0)
236 return -ENOIOCTLCMD; /* fall back into rtc-dev's emulation */
237 switch (cmd) {
238 case RTC_AIE_OFF:
239 pdata->irqen &= ~RTC_AF;
240 stk17ta8_rtc_update_alarm(pdata);
241 break;
242 case RTC_AIE_ON:
243 pdata->irqen |= RTC_AF;
244 stk17ta8_rtc_update_alarm(pdata);
245 break;
246 default:
247 return -ENOIOCTLCMD;
248 }
249 return 0;
250}
251
252static const struct rtc_class_ops stk17ta8_rtc_ops = {
253 .read_time = stk17ta8_rtc_read_time,
254 .set_time = stk17ta8_rtc_set_time,
255 .read_alarm = stk17ta8_rtc_read_alarm,
256 .set_alarm = stk17ta8_rtc_set_alarm,
257 .release = stk17ta8_rtc_release,
258 .ioctl = stk17ta8_rtc_ioctl,
259};
260
261static ssize_t stk17ta8_nvram_read(struct kobject *kobj, char *buf,
262 loff_t pos, size_t size)
263{
264 struct platform_device *pdev =
265 to_platform_device(container_of(kobj, struct device, kobj));
266 struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
267 void __iomem *ioaddr = pdata->ioaddr;
268 ssize_t count;
269
270 for (count = 0; size > 0 && pos < RTC_OFFSET; count++, size--)
271 *buf++ = readb(ioaddr + pos++);
272 return count;
273}
274
275static ssize_t stk17ta8_nvram_write(struct kobject *kobj, char *buf,
276 loff_t pos, size_t size)
277{
278 struct platform_device *pdev =
279 to_platform_device(container_of(kobj, struct device, kobj));
280 struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
281 void __iomem *ioaddr = pdata->ioaddr;
282 ssize_t count;
283
284 for (count = 0; size > 0 && pos < RTC_OFFSET; count++, size--)
285 writeb(*buf++, ioaddr + pos++);
286 return count;
287}
288
289static struct bin_attribute stk17ta8_nvram_attr = {
290 .attr = {
291 .name = "nvram",
292 .mode = S_IRUGO | S_IWUGO,
293 .owner = THIS_MODULE,
294 },
295 .size = RTC_OFFSET,
296 .read = stk17ta8_nvram_read,
297 .write = stk17ta8_nvram_write,
298};
299
300static int __init stk17ta8_rtc_probe(struct platform_device *pdev)
301{
302 struct rtc_device *rtc;
303 struct resource *res;
304 unsigned int cal;
305 unsigned int flags;
306 struct rtc_plat_data *pdata;
307 void __iomem *ioaddr = NULL;
308 int ret = 0;
309
310 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
311 if (!res)
312 return -ENODEV;
313
314 pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
315 if (!pdata)
316 return -ENOMEM;
317 pdata->irq = -1;
318 if (!request_mem_region(res->start, RTC_REG_SIZE, pdev->name)) {
319 ret = -EBUSY;
320 goto out;
321 }
322 pdata->baseaddr = res->start;
323 ioaddr = ioremap(pdata->baseaddr, RTC_REG_SIZE);
324 if (!ioaddr) {
325 ret = -ENOMEM;
326 goto out;
327 }
328 pdata->ioaddr = ioaddr;
329 pdata->irq = platform_get_irq(pdev, 0);
330
331 /* turn RTC on if it was not on */
332 cal = readb(ioaddr + RTC_CALIBRATION);
333 if (cal & RTC_STOP) {
334 cal &= RTC_CAL_MASK;
335 flags = readb(ioaddr + RTC_FLAGS);
336 writeb(flags | RTC_WRITE, ioaddr + RTC_FLAGS);
337 writeb(cal, ioaddr + RTC_CALIBRATION);
338 writeb(flags & ~RTC_WRITE, ioaddr + RTC_FLAGS);
339 }
340 if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_PF)
341 dev_warn(&pdev->dev, "voltage-low detected.\n");
342
343 if (pdata->irq >= 0) {
344 writeb(0, ioaddr + RTC_INTERRUPTS);
345 if (request_irq(pdata->irq, stk17ta8_rtc_interrupt,
346 IRQF_DISABLED | IRQF_SHARED,
347 pdev->name, pdev) < 0) {
348 dev_warn(&pdev->dev, "interrupt not available.\n");
349 pdata->irq = -1;
350 }
351 }
352
353 rtc = rtc_device_register(pdev->name, &pdev->dev,
354 &stk17ta8_rtc_ops, THIS_MODULE);
355 if (IS_ERR(rtc)) {
356 ret = PTR_ERR(rtc);
357 goto out;
358 }
359 pdata->rtc = rtc;
360 pdata->last_jiffies = jiffies;
361 platform_set_drvdata(pdev, pdata);
362 ret = sysfs_create_bin_file(&pdev->dev.kobj, &stk17ta8_nvram_attr);
363 if (ret)
364 goto out;
365 return 0;
366 out:
367 if (pdata->rtc)
368 rtc_device_unregister(pdata->rtc);
369 if (pdata->irq >= 0)
370 free_irq(pdata->irq, pdev);
371 if (ioaddr)
372 iounmap(ioaddr);
373 if (pdata->baseaddr)
374 release_mem_region(pdata->baseaddr, RTC_REG_SIZE);
375 kfree(pdata);
376 return ret;
377}
378
379static int __devexit stk17ta8_rtc_remove(struct platform_device *pdev)
380{
381 struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
382
383 sysfs_remove_bin_file(&pdev->dev.kobj, &stk17ta8_nvram_attr);
384 rtc_device_unregister(pdata->rtc);
385 if (pdata->irq >= 0) {
386 writeb(0, pdata->ioaddr + RTC_INTERRUPTS);
387 free_irq(pdata->irq, pdev);
388 }
389 iounmap(pdata->ioaddr);
390 release_mem_region(pdata->baseaddr, RTC_REG_SIZE);
391 kfree(pdata);
392 return 0;
393}
394
395static struct platform_driver stk17ta8_rtc_driver = {
396 .probe = stk17ta8_rtc_probe,
397 .remove = __devexit_p(stk17ta8_rtc_remove),
398 .driver = {
399 .name = "stk17ta8",
400 .owner = THIS_MODULE,
401 },
402};
403
404static __init int stk17ta8_init(void)
405{
406 return platform_driver_register(&stk17ta8_rtc_driver);
407}
408
409static __exit void stk17ta8_exit(void)
410{
411 return platform_driver_unregister(&stk17ta8_rtc_driver);
412}
413
414module_init(stk17ta8_init);
415module_exit(stk17ta8_exit);
416
417MODULE_AUTHOR("Thomas Hommel <thomas.hommel@gefanuc.com>");
418MODULE_DESCRIPTION("Simtek STK17TA8 RTC driver");
419MODULE_LICENSE("GPL");
420MODULE_VERSION(DRV_VERSION);