aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rtc/class.c
diff options
context:
space:
mode:
authorAlexandre Belloni <alexandre.belloni@free-electrons.com>2017-07-06 05:42:00 -0400
committerAlexandre Belloni <alexandre.belloni@free-electrons.com>2017-07-07 07:14:10 -0400
commit3068a254d5519cd5116f61297462da6d1aa84c20 (patch)
tree996896001b788c362f3de5e63c5ed9141f661754 /drivers/rtc/class.c
parentb91336df8ac2f5d15a2132074ba596580526db1d (diff)
rtc: introduce new registration method
Introduce rtc_register_device() to register an already allocated and initialized struct rtc_device. It automatically sets up the owner and the two steps allocation/registration will allow to remove race conditions in the IRQ handling of some driver. It also allows to properly extend the core without adding more arguments to rtc_device_register(). Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Diffstat (limited to 'drivers/rtc/class.c')
-rw-r--r--drivers/rtc/class.c84
1 files changed, 84 insertions, 0 deletions
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
index 93ce88e1b1cb..58e2a05765bb 100644
--- a/drivers/rtc/class.c
+++ b/drivers/rtc/class.c
@@ -150,6 +150,7 @@ static SIMPLE_DEV_PM_OPS(rtc_class_dev_pm_ops, rtc_suspend, rtc_resume);
150#define RTC_CLASS_DEV_PM_OPS NULL 150#define RTC_CLASS_DEV_PM_OPS NULL
151#endif 151#endif
152 152
153/* Ensure the caller will set the id before releasing the device */
153static struct rtc_device *rtc_allocate_device(void) 154static struct rtc_device *rtc_allocate_device(void)
154{ 155{
155 struct rtc_device *rtc; 156 struct rtc_device *rtc;
@@ -372,6 +373,89 @@ void devm_rtc_device_unregister(struct device *dev, struct rtc_device *rtc)
372} 373}
373EXPORT_SYMBOL_GPL(devm_rtc_device_unregister); 374EXPORT_SYMBOL_GPL(devm_rtc_device_unregister);
374 375
376static void devm_rtc_release_device(struct device *dev, void *res)
377{
378 struct rtc_device *rtc = *(struct rtc_device **)res;
379
380 if (rtc->registered)
381 rtc_device_unregister(rtc);
382 else
383 put_device(&rtc->dev);
384}
385
386struct rtc_device *devm_rtc_allocate_device(struct device *dev)
387{
388 struct rtc_device **ptr, *rtc;
389 int id, err;
390
391 id = rtc_device_get_id(dev);
392 if (id < 0)
393 return ERR_PTR(id);
394
395 ptr = devres_alloc(devm_rtc_release_device, sizeof(*ptr), GFP_KERNEL);
396 if (!ptr) {
397 err = -ENOMEM;
398 goto exit_ida;
399 }
400
401 rtc = rtc_allocate_device();
402 if (!rtc) {
403 err = -ENOMEM;
404 goto exit_devres;
405 }
406
407 *ptr = rtc;
408 devres_add(dev, ptr);
409
410 rtc->id = id;
411 rtc->dev.parent = dev;
412 dev_set_name(&rtc->dev, "rtc%d", id);
413
414 return rtc;
415
416exit_devres:
417 devres_free(ptr);
418exit_ida:
419 ida_simple_remove(&rtc_ida, id);
420 return ERR_PTR(err);
421}
422EXPORT_SYMBOL_GPL(devm_rtc_allocate_device);
423
424int __rtc_register_device(struct module *owner, struct rtc_device *rtc)
425{
426 struct rtc_wkalrm alrm;
427 int err;
428
429 if (!rtc->ops)
430 return -EINVAL;
431
432 rtc->owner = owner;
433
434 /* Check to see if there is an ALARM already set in hw */
435 err = __rtc_read_alarm(rtc, &alrm);
436 if (!err && !rtc_valid_tm(&alrm.time))
437 rtc_initialize_alarm(rtc, &alrm);
438
439 rtc_dev_prepare(rtc);
440
441 err = cdev_device_add(&rtc->char_dev, &rtc->dev);
442 if (err)
443 dev_warn(rtc->dev.parent, "failed to add char device %d:%d\n",
444 MAJOR(rtc->dev.devt), rtc->id);
445 else
446 dev_dbg(rtc->dev.parent, "char device (%d:%d)\n",
447 MAJOR(rtc->dev.devt), rtc->id);
448
449 rtc_proc_add_device(rtc);
450
451 rtc->registered = true;
452 dev_info(rtc->dev.parent, "registered as %s\n",
453 dev_name(&rtc->dev));
454
455 return 0;
456}
457EXPORT_SYMBOL_GPL(__rtc_register_device);
458
375static int __init rtc_init(void) 459static int __init rtc_init(void)
376{ 460{
377 rtc_class = class_create(THIS_MODULE, "rtc"); 461 rtc_class = class_create(THIS_MODULE, "rtc");