diff options
Diffstat (limited to 'drivers/rtc/rtc-dev.c')
-rw-r--r-- | drivers/rtc/rtc-dev.c | 184 |
1 files changed, 78 insertions, 106 deletions
diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c index 137330b8636b..f4e5f0040ff7 100644 --- a/drivers/rtc/rtc-dev.c +++ b/drivers/rtc/rtc-dev.c | |||
@@ -13,8 +13,8 @@ | |||
13 | 13 | ||
14 | #include <linux/module.h> | 14 | #include <linux/module.h> |
15 | #include <linux/rtc.h> | 15 | #include <linux/rtc.h> |
16 | #include "rtc-core.h" | ||
16 | 17 | ||
17 | static struct class *rtc_dev_class; | ||
18 | static dev_t rtc_devt; | 18 | static dev_t rtc_devt; |
19 | 19 | ||
20 | #define RTC_DEV_MAX 16 /* 16 RTCs should be enough for everyone... */ | 20 | #define RTC_DEV_MAX 16 /* 16 RTCs should be enough for everyone... */ |
@@ -32,9 +32,9 @@ static int rtc_dev_open(struct inode *inode, struct file *file) | |||
32 | if (!(mutex_trylock(&rtc->char_lock))) | 32 | if (!(mutex_trylock(&rtc->char_lock))) |
33 | return -EBUSY; | 33 | return -EBUSY; |
34 | 34 | ||
35 | file->private_data = &rtc->class_dev; | 35 | file->private_data = rtc; |
36 | 36 | ||
37 | err = ops->open ? ops->open(rtc->class_dev.dev) : 0; | 37 | err = ops->open ? ops->open(rtc->dev.parent) : 0; |
38 | if (err == 0) { | 38 | if (err == 0) { |
39 | spin_lock_irq(&rtc->irq_lock); | 39 | spin_lock_irq(&rtc->irq_lock); |
40 | rtc->irq_data = 0; | 40 | rtc->irq_data = 0; |
@@ -61,7 +61,7 @@ static void rtc_uie_task(struct work_struct *work) | |||
61 | int num = 0; | 61 | int num = 0; |
62 | int err; | 62 | int err; |
63 | 63 | ||
64 | err = rtc_read_time(&rtc->class_dev, &tm); | 64 | err = rtc_read_time(rtc, &tm); |
65 | 65 | ||
66 | local_irq_disable(); | 66 | local_irq_disable(); |
67 | spin_lock(&rtc->irq_lock); | 67 | spin_lock(&rtc->irq_lock); |
@@ -79,7 +79,7 @@ static void rtc_uie_task(struct work_struct *work) | |||
79 | } | 79 | } |
80 | spin_unlock(&rtc->irq_lock); | 80 | spin_unlock(&rtc->irq_lock); |
81 | if (num) | 81 | if (num) |
82 | rtc_update_irq(&rtc->class_dev, num, RTC_UF | RTC_IRQF); | 82 | rtc_update_irq(rtc, num, RTC_UF | RTC_IRQF); |
83 | local_irq_enable(); | 83 | local_irq_enable(); |
84 | } | 84 | } |
85 | static void rtc_uie_timer(unsigned long data) | 85 | static void rtc_uie_timer(unsigned long data) |
@@ -121,7 +121,7 @@ static int set_uie(struct rtc_device *rtc) | |||
121 | struct rtc_time tm; | 121 | struct rtc_time tm; |
122 | int err; | 122 | int err; |
123 | 123 | ||
124 | err = rtc_read_time(&rtc->class_dev, &tm); | 124 | err = rtc_read_time(rtc, &tm); |
125 | if (err) | 125 | if (err) |
126 | return err; | 126 | return err; |
127 | spin_lock_irq(&rtc->irq_lock); | 127 | spin_lock_irq(&rtc->irq_lock); |
@@ -180,7 +180,7 @@ rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | |||
180 | if (ret == 0) { | 180 | if (ret == 0) { |
181 | /* Check for any data updates */ | 181 | /* Check for any data updates */ |
182 | if (rtc->ops->read_callback) | 182 | if (rtc->ops->read_callback) |
183 | data = rtc->ops->read_callback(rtc->class_dev.dev, | 183 | data = rtc->ops->read_callback(rtc->dev.parent, |
184 | data); | 184 | data); |
185 | 185 | ||
186 | if (sizeof(int) != sizeof(long) && | 186 | if (sizeof(int) != sizeof(long) && |
@@ -210,8 +210,7 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, | |||
210 | unsigned int cmd, unsigned long arg) | 210 | unsigned int cmd, unsigned long arg) |
211 | { | 211 | { |
212 | int err = 0; | 212 | int err = 0; |
213 | struct class_device *class_dev = file->private_data; | 213 | struct rtc_device *rtc = file->private_data; |
214 | struct rtc_device *rtc = to_rtc_device(class_dev); | ||
215 | const struct rtc_class_ops *ops = rtc->ops; | 214 | const struct rtc_class_ops *ops = rtc->ops; |
216 | struct rtc_time tm; | 215 | struct rtc_time tm; |
217 | struct rtc_wkalrm alarm; | 216 | struct rtc_wkalrm alarm; |
@@ -252,7 +251,7 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, | |||
252 | 251 | ||
253 | /* try the driver's ioctl interface */ | 252 | /* try the driver's ioctl interface */ |
254 | if (ops->ioctl) { | 253 | if (ops->ioctl) { |
255 | err = ops->ioctl(class_dev->dev, cmd, arg); | 254 | err = ops->ioctl(rtc->dev.parent, cmd, arg); |
256 | if (err != -ENOIOCTLCMD) | 255 | if (err != -ENOIOCTLCMD) |
257 | return err; | 256 | return err; |
258 | } | 257 | } |
@@ -264,7 +263,7 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, | |||
264 | 263 | ||
265 | switch (cmd) { | 264 | switch (cmd) { |
266 | case RTC_ALM_READ: | 265 | case RTC_ALM_READ: |
267 | err = rtc_read_alarm(class_dev, &alarm); | 266 | err = rtc_read_alarm(rtc, &alarm); |
268 | if (err < 0) | 267 | if (err < 0) |
269 | return err; | 268 | return err; |
270 | 269 | ||
@@ -278,17 +277,53 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, | |||
278 | 277 | ||
279 | alarm.enabled = 0; | 278 | alarm.enabled = 0; |
280 | alarm.pending = 0; | 279 | alarm.pending = 0; |
281 | alarm.time.tm_mday = -1; | ||
282 | alarm.time.tm_mon = -1; | ||
283 | alarm.time.tm_year = -1; | ||
284 | alarm.time.tm_wday = -1; | 280 | alarm.time.tm_wday = -1; |
285 | alarm.time.tm_yday = -1; | 281 | alarm.time.tm_yday = -1; |
286 | alarm.time.tm_isdst = -1; | 282 | alarm.time.tm_isdst = -1; |
287 | err = rtc_set_alarm(class_dev, &alarm); | 283 | |
284 | /* RTC_ALM_SET alarms may be up to 24 hours in the future. | ||
285 | * Rather than expecting every RTC to implement "don't care" | ||
286 | * for day/month/year fields, just force the alarm to have | ||
287 | * the right values for those fields. | ||
288 | * | ||
289 | * RTC_WKALM_SET should be used instead. Not only does it | ||
290 | * eliminate the need for a separate RTC_AIE_ON call, it | ||
291 | * doesn't have the "alarm 23:59:59 in the future" race. | ||
292 | * | ||
293 | * NOTE: some legacy code may have used invalid fields as | ||
294 | * wildcards, exposing hardware "periodic alarm" capabilities. | ||
295 | * Not supported here. | ||
296 | */ | ||
297 | { | ||
298 | unsigned long now, then; | ||
299 | |||
300 | err = rtc_read_time(rtc, &tm); | ||
301 | if (err < 0) | ||
302 | return err; | ||
303 | rtc_tm_to_time(&tm, &now); | ||
304 | |||
305 | alarm.time.tm_mday = tm.tm_mday; | ||
306 | alarm.time.tm_mon = tm.tm_mon; | ||
307 | alarm.time.tm_year = tm.tm_year; | ||
308 | err = rtc_valid_tm(&alarm.time); | ||
309 | if (err < 0) | ||
310 | return err; | ||
311 | rtc_tm_to_time(&alarm.time, &then); | ||
312 | |||
313 | /* alarm may need to wrap into tomorrow */ | ||
314 | if (then < now) { | ||
315 | rtc_time_to_tm(now + 24 * 60 * 60, &tm); | ||
316 | alarm.time.tm_mday = tm.tm_mday; | ||
317 | alarm.time.tm_mon = tm.tm_mon; | ||
318 | alarm.time.tm_year = tm.tm_year; | ||
319 | } | ||
320 | } | ||
321 | |||
322 | err = rtc_set_alarm(rtc, &alarm); | ||
288 | break; | 323 | break; |
289 | 324 | ||
290 | case RTC_RD_TIME: | 325 | case RTC_RD_TIME: |
291 | err = rtc_read_time(class_dev, &tm); | 326 | err = rtc_read_time(rtc, &tm); |
292 | if (err < 0) | 327 | if (err < 0) |
293 | return err; | 328 | return err; |
294 | 329 | ||
@@ -300,7 +335,7 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, | |||
300 | if (copy_from_user(&tm, uarg, sizeof(tm))) | 335 | if (copy_from_user(&tm, uarg, sizeof(tm))) |
301 | return -EFAULT; | 336 | return -EFAULT; |
302 | 337 | ||
303 | err = rtc_set_time(class_dev, &tm); | 338 | err = rtc_set_time(rtc, &tm); |
304 | break; | 339 | break; |
305 | 340 | ||
306 | case RTC_IRQP_READ: | 341 | case RTC_IRQP_READ: |
@@ -310,7 +345,7 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, | |||
310 | 345 | ||
311 | case RTC_IRQP_SET: | 346 | case RTC_IRQP_SET: |
312 | if (ops->irq_set_freq) | 347 | if (ops->irq_set_freq) |
313 | err = rtc_irq_set_freq(class_dev, rtc->irq_task, arg); | 348 | err = rtc_irq_set_freq(rtc, rtc->irq_task, arg); |
314 | break; | 349 | break; |
315 | 350 | ||
316 | #if 0 | 351 | #if 0 |
@@ -336,11 +371,11 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, | |||
336 | if (copy_from_user(&alarm, uarg, sizeof(alarm))) | 371 | if (copy_from_user(&alarm, uarg, sizeof(alarm))) |
337 | return -EFAULT; | 372 | return -EFAULT; |
338 | 373 | ||
339 | err = rtc_set_alarm(class_dev, &alarm); | 374 | err = rtc_set_alarm(rtc, &alarm); |
340 | break; | 375 | break; |
341 | 376 | ||
342 | case RTC_WKALM_RD: | 377 | case RTC_WKALM_RD: |
343 | err = rtc_read_alarm(class_dev, &alarm); | 378 | err = rtc_read_alarm(rtc, &alarm); |
344 | if (err < 0) | 379 | if (err < 0) |
345 | return err; | 380 | return err; |
346 | 381 | ||
@@ -372,7 +407,7 @@ static int rtc_dev_release(struct inode *inode, struct file *file) | |||
372 | clear_uie(rtc); | 407 | clear_uie(rtc); |
373 | #endif | 408 | #endif |
374 | if (rtc->ops->release) | 409 | if (rtc->ops->release) |
375 | rtc->ops->release(rtc->class_dev.dev); | 410 | rtc->ops->release(rtc->dev.parent); |
376 | 411 | ||
377 | mutex_unlock(&rtc->char_lock); | 412 | mutex_unlock(&rtc->char_lock); |
378 | return 0; | 413 | return 0; |
@@ -397,17 +432,18 @@ static const struct file_operations rtc_dev_fops = { | |||
397 | 432 | ||
398 | /* insertion/removal hooks */ | 433 | /* insertion/removal hooks */ |
399 | 434 | ||
400 | static int rtc_dev_add_device(struct class_device *class_dev, | 435 | void rtc_dev_prepare(struct rtc_device *rtc) |
401 | struct class_interface *class_intf) | ||
402 | { | 436 | { |
403 | int err = 0; | 437 | if (!rtc_devt) |
404 | struct rtc_device *rtc = to_rtc_device(class_dev); | 438 | return; |
405 | 439 | ||
406 | if (rtc->id >= RTC_DEV_MAX) { | 440 | if (rtc->id >= RTC_DEV_MAX) { |
407 | dev_err(class_dev->dev, "too many RTCs\n"); | 441 | pr_debug("%s: too many RTC devices\n", rtc->name); |
408 | return -EINVAL; | 442 | return; |
409 | } | 443 | } |
410 | 444 | ||
445 | rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id); | ||
446 | |||
411 | mutex_init(&rtc->char_lock); | 447 | mutex_init(&rtc->char_lock); |
412 | spin_lock_init(&rtc->irq_lock); | 448 | spin_lock_init(&rtc->irq_lock); |
413 | init_waitqueue_head(&rtc->irq_queue); | 449 | init_waitqueue_head(&rtc->irq_queue); |
@@ -418,100 +454,36 @@ static int rtc_dev_add_device(struct class_device *class_dev, | |||
418 | 454 | ||
419 | cdev_init(&rtc->char_dev, &rtc_dev_fops); | 455 | cdev_init(&rtc->char_dev, &rtc_dev_fops); |
420 | rtc->char_dev.owner = rtc->owner; | 456 | rtc->char_dev.owner = rtc->owner; |
457 | } | ||
421 | 458 | ||
422 | if (cdev_add(&rtc->char_dev, MKDEV(MAJOR(rtc_devt), rtc->id), 1)) { | 459 | void rtc_dev_add_device(struct rtc_device *rtc) |
423 | dev_err(class_dev->dev, | 460 | { |
424 | "failed to add char device %d:%d\n", | 461 | if (cdev_add(&rtc->char_dev, rtc->dev.devt, 1)) |
462 | printk(KERN_WARNING "%s: failed to add char device %d:%d\n", | ||
463 | rtc->name, MAJOR(rtc_devt), rtc->id); | ||
464 | else | ||
465 | pr_debug("%s: dev (%d:%d)\n", rtc->name, | ||
425 | MAJOR(rtc_devt), rtc->id); | 466 | MAJOR(rtc_devt), rtc->id); |
426 | return -ENODEV; | ||
427 | } | ||
428 | |||
429 | rtc->rtc_dev = class_device_create(rtc_dev_class, NULL, | ||
430 | MKDEV(MAJOR(rtc_devt), rtc->id), | ||
431 | class_dev->dev, "rtc%d", rtc->id); | ||
432 | if (IS_ERR(rtc->rtc_dev)) { | ||
433 | dev_err(class_dev->dev, "cannot create rtc_dev device\n"); | ||
434 | err = PTR_ERR(rtc->rtc_dev); | ||
435 | goto err_cdev_del; | ||
436 | } | ||
437 | |||
438 | dev_dbg(class_dev->dev, "rtc intf: dev (%d:%d)\n", | ||
439 | MAJOR(rtc->rtc_dev->devt), | ||
440 | MINOR(rtc->rtc_dev->devt)); | ||
441 | |||
442 | return 0; | ||
443 | |||
444 | err_cdev_del: | ||
445 | |||
446 | cdev_del(&rtc->char_dev); | ||
447 | return err; | ||
448 | } | 467 | } |
449 | 468 | ||
450 | static void rtc_dev_remove_device(struct class_device *class_dev, | 469 | void rtc_dev_del_device(struct rtc_device *rtc) |
451 | struct class_interface *class_intf) | ||
452 | { | 470 | { |
453 | struct rtc_device *rtc = to_rtc_device(class_dev); | 471 | if (rtc->dev.devt) |
454 | |||
455 | if (rtc->rtc_dev) { | ||
456 | dev_dbg(class_dev->dev, "removing char %d:%d\n", | ||
457 | MAJOR(rtc->rtc_dev->devt), | ||
458 | MINOR(rtc->rtc_dev->devt)); | ||
459 | |||
460 | class_device_unregister(rtc->rtc_dev); | ||
461 | cdev_del(&rtc->char_dev); | 472 | cdev_del(&rtc->char_dev); |
462 | } | ||
463 | } | 473 | } |
464 | 474 | ||
465 | /* interface registration */ | 475 | void __init rtc_dev_init(void) |
466 | |||
467 | static struct class_interface rtc_dev_interface = { | ||
468 | .add = &rtc_dev_add_device, | ||
469 | .remove = &rtc_dev_remove_device, | ||
470 | }; | ||
471 | |||
472 | static int __init rtc_dev_init(void) | ||
473 | { | 476 | { |
474 | int err; | 477 | int err; |
475 | 478 | ||
476 | rtc_dev_class = class_create(THIS_MODULE, "rtc-dev"); | ||
477 | if (IS_ERR(rtc_dev_class)) | ||
478 | return PTR_ERR(rtc_dev_class); | ||
479 | |||
480 | err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc"); | 479 | err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc"); |
481 | if (err < 0) { | 480 | if (err < 0) |
482 | printk(KERN_ERR "%s: failed to allocate char dev region\n", | 481 | printk(KERN_ERR "%s: failed to allocate char dev region\n", |
483 | __FILE__); | 482 | __FILE__); |
484 | goto err_destroy_class; | ||
485 | } | ||
486 | |||
487 | err = rtc_interface_register(&rtc_dev_interface); | ||
488 | if (err < 0) { | ||
489 | printk(KERN_ERR "%s: failed to register the interface\n", | ||
490 | __FILE__); | ||
491 | goto err_unregister_chrdev; | ||
492 | } | ||
493 | |||
494 | return 0; | ||
495 | |||
496 | err_unregister_chrdev: | ||
497 | unregister_chrdev_region(rtc_devt, RTC_DEV_MAX); | ||
498 | |||
499 | err_destroy_class: | ||
500 | class_destroy(rtc_dev_class); | ||
501 | |||
502 | return err; | ||
503 | } | 483 | } |
504 | 484 | ||
505 | static void __exit rtc_dev_exit(void) | 485 | void __exit rtc_dev_exit(void) |
506 | { | 486 | { |
507 | class_interface_unregister(&rtc_dev_interface); | 487 | if (rtc_devt) |
508 | class_destroy(rtc_dev_class); | 488 | unregister_chrdev_region(rtc_devt, RTC_DEV_MAX); |
509 | unregister_chrdev_region(rtc_devt, RTC_DEV_MAX); | ||
510 | } | 489 | } |
511 | |||
512 | subsys_initcall(rtc_dev_init); | ||
513 | module_exit(rtc_dev_exit); | ||
514 | |||
515 | MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); | ||
516 | MODULE_DESCRIPTION("RTC class dev interface"); | ||
517 | MODULE_LICENSE("GPL"); | ||