diff options
author | Alexandre Belloni <alexandre.belloni@free-electrons.com> | 2017-07-06 05:42:00 -0400 |
---|---|---|
committer | Alexandre Belloni <alexandre.belloni@free-electrons.com> | 2017-07-07 07:14:10 -0400 |
commit | 3068a254d5519cd5116f61297462da6d1aa84c20 (patch) | |
tree | 996896001b788c362f3de5e63c5ed9141f661754 /drivers/rtc/class.c | |
parent | b91336df8ac2f5d15a2132074ba596580526db1d (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.c | 84 |
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 */ | ||
153 | static struct rtc_device *rtc_allocate_device(void) | 154 | static 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 | } |
373 | EXPORT_SYMBOL_GPL(devm_rtc_device_unregister); | 374 | EXPORT_SYMBOL_GPL(devm_rtc_device_unregister); |
374 | 375 | ||
376 | static 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 | |||
386 | struct 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 | |||
416 | exit_devres: | ||
417 | devres_free(ptr); | ||
418 | exit_ida: | ||
419 | ida_simple_remove(&rtc_ida, id); | ||
420 | return ERR_PTR(err); | ||
421 | } | ||
422 | EXPORT_SYMBOL_GPL(devm_rtc_allocate_device); | ||
423 | |||
424 | int __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 | } | ||
457 | EXPORT_SYMBOL_GPL(__rtc_register_device); | ||
458 | |||
375 | static int __init rtc_init(void) | 459 | static int __init rtc_init(void) |
376 | { | 460 | { |
377 | rtc_class = class_create(THIS_MODULE, "rtc"); | 461 | rtc_class = class_create(THIS_MODULE, "rtc"); |