Merge branch 'upstream-fixes' of git://lost.foo-projects.org/~ahkok/git/netdev-2...
[pandora-kernel.git] / arch / arm / common / rtctime.c
1 /*
2  *  linux/arch/arm/common/rtctime.c
3  *
4  *  Copyright (C) 2003 Deep Blue Solutions Ltd.
5  *  Based on sa1100-rtc.c, Nils Faerber, CIH, Nicolas Pitre.
6  *  Based on rtc.c by Paul Gortmaker
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  */
12 #include <linux/module.h>
13 #include <linux/kernel.h>
14 #include <linux/time.h>
15 #include <linux/rtc.h>
16 #include <linux/poll.h>
17 #include <linux/proc_fs.h>
18 #include <linux/miscdevice.h>
19 #include <linux/spinlock.h>
20 #include <linux/capability.h>
21 #include <linux/device.h>
22 #include <linux/mutex.h>
23 #include <linux/rtc.h>
24
25 #include <asm/rtc.h>
26 #include <asm/semaphore.h>
27
28 static DECLARE_WAIT_QUEUE_HEAD(rtc_wait);
29 static struct fasync_struct *rtc_async_queue;
30
31 /*
32  * rtc_lock protects rtc_irq_data
33  */
34 static DEFINE_SPINLOCK(rtc_lock);
35 static unsigned long rtc_irq_data;
36
37 /*
38  * rtc_sem protects rtc_inuse and rtc_ops
39  */
40 static DEFINE_MUTEX(rtc_mutex);
41 static unsigned long rtc_inuse;
42 static struct rtc_ops *rtc_ops;
43
44 #define rtc_epoch 1900UL
45
46 /*
47  * Calculate the next alarm time given the requested alarm time mask
48  * and the current time.
49  */
50 void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now, struct rtc_time *alrm)
51 {
52         unsigned long next_time;
53         unsigned long now_time;
54
55         next->tm_year = now->tm_year;
56         next->tm_mon = now->tm_mon;
57         next->tm_mday = now->tm_mday;
58         next->tm_hour = alrm->tm_hour;
59         next->tm_min = alrm->tm_min;
60         next->tm_sec = alrm->tm_sec;
61
62         rtc_tm_to_time(now, &now_time);
63         rtc_tm_to_time(next, &next_time);
64
65         if (next_time < now_time) {
66                 /* Advance one day */
67                 next_time += 60 * 60 * 24;
68                 rtc_time_to_tm(next_time, next);
69         }
70 }
71 EXPORT_SYMBOL(rtc_next_alarm_time);
72
73 static inline int rtc_arm_read_time(struct rtc_ops *ops, struct rtc_time *tm)
74 {
75         memset(tm, 0, sizeof(struct rtc_time));
76         return ops->read_time(tm);
77 }
78
79 static inline int rtc_arm_set_time(struct rtc_ops *ops, struct rtc_time *tm)
80 {
81         int ret;
82
83         ret = rtc_valid_tm(tm);
84         if (ret == 0)
85                 ret = ops->set_time(tm);
86
87         return ret;
88 }
89
90 static inline int rtc_arm_read_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm)
91 {
92         int ret = -EINVAL;
93         if (ops->read_alarm) {
94                 memset(alrm, 0, sizeof(struct rtc_wkalrm));
95                 ret = ops->read_alarm(alrm);
96         }
97         return ret;
98 }
99
100 static inline int rtc_arm_set_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm)
101 {
102         int ret = -EINVAL;
103         if (ops->set_alarm)
104                 ret = ops->set_alarm(alrm);
105         return ret;
106 }
107
108 void rtc_update(unsigned long num, unsigned long events)
109 {
110         spin_lock(&rtc_lock);
111         rtc_irq_data = (rtc_irq_data + (num << 8)) | events;
112         spin_unlock(&rtc_lock);
113
114         wake_up_interruptible(&rtc_wait);
115         kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);
116 }
117 EXPORT_SYMBOL(rtc_update);
118
119
120 static ssize_t
121 rtc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
122 {
123         DECLARE_WAITQUEUE(wait, current);
124         unsigned long data;
125         ssize_t ret;
126
127         if (count < sizeof(unsigned long))
128                 return -EINVAL;
129
130         add_wait_queue(&rtc_wait, &wait);
131         do {
132                 __set_current_state(TASK_INTERRUPTIBLE);
133
134                 spin_lock_irq(&rtc_lock);
135                 data = rtc_irq_data;
136                 rtc_irq_data = 0;
137                 spin_unlock_irq(&rtc_lock);
138
139                 if (data != 0) {
140                         ret = 0;
141                         break;
142                 }
143                 if (file->f_flags & O_NONBLOCK) {
144                         ret = -EAGAIN;
145                         break;
146                 }
147                 if (signal_pending(current)) {
148                         ret = -ERESTARTSYS;
149                         break;
150                 }
151                 schedule();
152         } while (1);
153         set_current_state(TASK_RUNNING);
154         remove_wait_queue(&rtc_wait, &wait);
155
156         if (ret == 0) {
157                 ret = put_user(data, (unsigned long __user *)buf);
158                 if (ret == 0)
159                         ret = sizeof(unsigned long);
160         }
161         return ret;
162 }
163
164 static unsigned int rtc_poll(struct file *file, poll_table *wait)
165 {
166         unsigned long data;
167
168         poll_wait(file, &rtc_wait, wait);
169
170         spin_lock_irq(&rtc_lock);
171         data = rtc_irq_data;
172         spin_unlock_irq(&rtc_lock);
173
174         return data != 0 ? POLLIN | POLLRDNORM : 0;
175 }
176
177 static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
178                      unsigned long arg)
179 {
180         struct rtc_ops *ops = file->private_data;
181         struct rtc_time tm;
182         struct rtc_wkalrm alrm;
183         void __user *uarg = (void __user *)arg;
184         int ret = -EINVAL;
185
186         switch (cmd) {
187         case RTC_ALM_READ:
188                 ret = rtc_arm_read_alarm(ops, &alrm);
189                 if (ret)
190                         break;
191                 ret = copy_to_user(uarg, &alrm.time, sizeof(tm));
192                 if (ret)
193                         ret = -EFAULT;
194                 break;
195
196         case RTC_ALM_SET:
197                 ret = copy_from_user(&alrm.time, uarg, sizeof(tm));
198                 if (ret) {
199                         ret = -EFAULT;
200                         break;
201                 }
202                 alrm.enabled = 0;
203                 alrm.pending = 0;
204                 alrm.time.tm_mday = -1;
205                 alrm.time.tm_mon = -1;
206                 alrm.time.tm_year = -1;
207                 alrm.time.tm_wday = -1;
208                 alrm.time.tm_yday = -1;
209                 alrm.time.tm_isdst = -1;
210                 ret = rtc_arm_set_alarm(ops, &alrm);
211                 break;
212
213         case RTC_RD_TIME:
214                 ret = rtc_arm_read_time(ops, &tm);
215                 if (ret)
216                         break;
217                 ret = copy_to_user(uarg, &tm, sizeof(tm));
218                 if (ret)
219                         ret = -EFAULT;
220                 break;
221
222         case RTC_SET_TIME:
223                 if (!capable(CAP_SYS_TIME)) {
224                         ret = -EACCES;
225                         break;
226                 }
227                 ret = copy_from_user(&tm, uarg, sizeof(tm));
228                 if (ret) {
229                         ret = -EFAULT;
230                         break;
231                 }
232                 ret = rtc_arm_set_time(ops, &tm);
233                 break;
234
235         case RTC_EPOCH_SET:
236 #ifndef rtc_epoch
237                 /*
238                  * There were no RTC clocks before 1900.
239                  */
240                 if (arg < 1900) {
241                         ret = -EINVAL;
242                         break;
243                 }
244                 if (!capable(CAP_SYS_TIME)) {
245                         ret = -EACCES;
246                         break;
247                 }
248                 rtc_epoch = arg;
249                 ret = 0;
250 #endif
251                 break;
252
253         case RTC_EPOCH_READ:
254                 ret = put_user(rtc_epoch, (unsigned long __user *)uarg);
255                 break;
256
257         case RTC_WKALM_SET:
258                 ret = copy_from_user(&alrm, uarg, sizeof(alrm));
259                 if (ret) {
260                         ret = -EFAULT;
261                         break;
262                 }
263                 ret = rtc_arm_set_alarm(ops, &alrm);
264                 break;
265
266         case RTC_WKALM_RD:
267                 ret = rtc_arm_read_alarm(ops, &alrm);
268                 if (ret)
269                         break;
270                 ret = copy_to_user(uarg, &alrm, sizeof(alrm));
271                 if (ret)
272                         ret = -EFAULT;
273                 break;
274
275         default:
276                 if (ops->ioctl)
277                         ret = ops->ioctl(cmd, arg);
278                 break;
279         }
280         return ret;
281 }
282
283 static int rtc_open(struct inode *inode, struct file *file)
284 {
285         int ret;
286
287         mutex_lock(&rtc_mutex);
288
289         if (rtc_inuse) {
290                 ret = -EBUSY;
291         } else if (!rtc_ops || !try_module_get(rtc_ops->owner)) {
292                 ret = -ENODEV;
293         } else {
294                 file->private_data = rtc_ops;
295
296                 ret = rtc_ops->open ? rtc_ops->open() : 0;
297                 if (ret == 0) {
298                         spin_lock_irq(&rtc_lock);
299                         rtc_irq_data = 0;
300                         spin_unlock_irq(&rtc_lock);
301
302                         rtc_inuse = 1;
303                 }
304         }
305         mutex_unlock(&rtc_mutex);
306
307         return ret;
308 }
309
310 static int rtc_release(struct inode *inode, struct file *file)
311 {
312         struct rtc_ops *ops = file->private_data;
313
314         if (ops->release)
315                 ops->release();
316
317         spin_lock_irq(&rtc_lock);
318         rtc_irq_data = 0;
319         spin_unlock_irq(&rtc_lock);
320
321         module_put(rtc_ops->owner);
322         rtc_inuse = 0;
323
324         return 0;
325 }
326
327 static int rtc_fasync(int fd, struct file *file, int on)
328 {
329         return fasync_helper(fd, file, on, &rtc_async_queue);
330 }
331
332 static struct file_operations rtc_fops = {
333         .owner          = THIS_MODULE,
334         .llseek         = no_llseek,
335         .read           = rtc_read,
336         .poll           = rtc_poll,
337         .ioctl          = rtc_ioctl,
338         .open           = rtc_open,
339         .release        = rtc_release,
340         .fasync         = rtc_fasync,
341 };
342
343 static struct miscdevice rtc_miscdev = {
344         .minor          = RTC_MINOR,
345         .name           = "rtc",
346         .fops           = &rtc_fops,
347 };
348
349
350 static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
351 {
352         struct rtc_ops *ops = data;
353         struct rtc_wkalrm alrm;
354         struct rtc_time tm;
355         char *p = page;
356
357         if (rtc_arm_read_time(ops, &tm) == 0) {
358                 p += sprintf(p,
359                         "rtc_time\t: %02d:%02d:%02d\n"
360                         "rtc_date\t: %04d-%02d-%02d\n"
361                         "rtc_epoch\t: %04lu\n",
362                         tm.tm_hour, tm.tm_min, tm.tm_sec,
363                         tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
364                         rtc_epoch);
365         }
366
367         if (rtc_arm_read_alarm(ops, &alrm) == 0) {
368                 p += sprintf(p, "alrm_time\t: ");
369                 if ((unsigned int)alrm.time.tm_hour <= 24)
370                         p += sprintf(p, "%02d:", alrm.time.tm_hour);
371                 else
372                         p += sprintf(p, "**:");
373                 if ((unsigned int)alrm.time.tm_min <= 59)
374                         p += sprintf(p, "%02d:", alrm.time.tm_min);
375                 else
376                         p += sprintf(p, "**:");
377                 if ((unsigned int)alrm.time.tm_sec <= 59)
378                         p += sprintf(p, "%02d\n", alrm.time.tm_sec);
379                 else
380                         p += sprintf(p, "**\n");
381
382                 p += sprintf(p, "alrm_date\t: ");
383                 if ((unsigned int)alrm.time.tm_year <= 200)
384                         p += sprintf(p, "%04d-", alrm.time.tm_year + 1900);
385                 else
386                         p += sprintf(p, "****-");
387                 if ((unsigned int)alrm.time.tm_mon <= 11)
388                         p += sprintf(p, "%02d-", alrm.time.tm_mon + 1);
389                 else
390                         p += sprintf(p, "**-");
391                 if ((unsigned int)alrm.time.tm_mday <= 31)
392                         p += sprintf(p, "%02d\n", alrm.time.tm_mday);
393                 else
394                         p += sprintf(p, "**\n");
395                 p += sprintf(p, "alrm_wakeup\t: %s\n",
396                              alrm.enabled ? "yes" : "no");
397                 p += sprintf(p, "alrm_pending\t: %s\n",
398                              alrm.pending ? "yes" : "no");
399         }
400
401         if (ops->proc)
402                 p += ops->proc(p);
403
404         return p - page;
405 }
406
407 int register_rtc(struct rtc_ops *ops)
408 {
409         int ret = -EBUSY;
410
411         mutex_lock(&rtc_mutex);
412         if (rtc_ops == NULL) {
413                 rtc_ops = ops;
414
415                 ret = misc_register(&rtc_miscdev);
416                 if (ret == 0)
417                         create_proc_read_entry("driver/rtc", 0, NULL,
418                                                rtc_read_proc, ops);
419         }
420         mutex_unlock(&rtc_mutex);
421
422         return ret;
423 }
424 EXPORT_SYMBOL(register_rtc);
425
426 void unregister_rtc(struct rtc_ops *rtc)
427 {
428         mutex_lock(&rtc_mutex);
429         if (rtc == rtc_ops) {
430                 remove_proc_entry("driver/rtc", NULL);
431                 misc_deregister(&rtc_miscdev);
432                 rtc_ops = NULL;
433         }
434         mutex_unlock(&rtc_mutex);
435 }
436 EXPORT_SYMBOL(unregister_rtc);