/* The industrial I/O periodic RTC trigger driver * * Copyright (c) 2008 Jonathan Cameron * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * * This is a heavily rewritten version of the periodic timer system in * earlier version of industrialio. It supplies the same functionality * but via a trigger rather than a specific periodic timer system. */ #include #include #include #include #include #include "../iio.h" #include "../trigger.h" LIST_HEAD(iio_prtc_trigger_list); DEFINE_MUTEX(iio_prtc_trigger_list_lock); struct iio_prtc_trigger_info { struct rtc_device *rtc; int frequency; char *name; struct rtc_task task; }; static int iio_trig_periodic_rtc_set_state(struct iio_trigger *trig, bool state) { struct iio_prtc_trigger_info *trig_info = trig->private_data; if (trig_info->frequency == 0) return -EINVAL; printk(KERN_INFO "trigger frequency is %d\n", trig_info->frequency); return rtc_irq_set_state(trig_info->rtc, &trig_info->task, state); } static ssize_t iio_trig_periodic_read_freq(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_trigger *trig = dev_get_drvdata(dev); struct iio_prtc_trigger_info *trig_info = trig->private_data; return sprintf(buf, "%u\n", trig_info->frequency); } static ssize_t iio_trig_periodic_write_freq(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { struct iio_trigger *trig = dev_get_drvdata(dev); struct iio_prtc_trigger_info *trig_info = trig->private_data; unsigned long val; int ret; ret = strict_strtoul(buf, 10, &val); if (ret) goto error_ret; ret = rtc_irq_set_freq(trig_info->rtc, &trig_info->task, val); if (ret) goto error_ret; trig_info->frequency = val; return len; error_ret: return ret; } static ssize_t iio_trig_periodic_read_name(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_trigger *trig = dev_get_drvdata(dev); struct iio_prtc_trigger_info *trig_info = trig->private_data; return sprintf(buf, "%s\n", trig_info->name); } static DEVICE_ATTR(name, S_IRUGO, iio_trig_periodic_read_name, NULL); static DEVICE_ATTR(frequency, S_IRUGO | S_IWUSR, iio_trig_periodic_read_freq, iio_trig_periodic_write_freq); static struct attribute *iio_trig_prtc_attrs[] = { &dev_attr_frequency.attr, &dev_attr_name.attr, NULL, }; static const struct attribute_group iio_trig_prtc_attr_group = { .attrs = iio_trig_prtc_attrs, }; static void iio_prtc_trigger_poll(void *private_data) { iio_trigger_poll(private_data); } static int iio_trig_periodic_rtc_probe(struct platform_device *dev) { char **pdata = dev->dev.platform_data; struct iio_prtc_trigger_info *trig_info; struct iio_trigger *trig, *trig2; int i, ret; for (i = 0;; i++) { if (pdata[i] == NULL) break; trig = iio_allocate_trigger(); if (!trig) { ret = -ENOMEM; goto error_free_completed_registrations; } list_add(&trig->alloc_list, &iio_prtc_trigger_list); trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL); if (!trig_info) { ret = -ENOMEM; goto error_put_trigger_and_remove_from_list; } trig->private_data = trig_info; trig->owner = THIS_MODULE; trig->set_trigger_state = &iio_trig_periodic_rtc_set_state; trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, GFP_KERNEL); if (trig->name == NULL) { ret = -ENOMEM; goto error_free_trig_info; } snprintf((char *)trig->name, IIO_TRIGGER_NAME_LENGTH, "periodic%s", pdata[i]); trig_info->name = (char *)trig->name; /* RTC access */ trig_info->rtc = rtc_class_open(pdata[i]); if (trig_info->rtc == NULL) { ret = -EINVAL; goto error_free_name; } trig_info->task.func = iio_prtc_trigger_poll; trig_info->task.private_data = trig; ret = rtc_irq_register(trig_info->rtc, &trig_info->task); if (ret) goto error_close_rtc; trig->control_attrs = &iio_trig_prtc_attr_group; ret = iio_trigger_register(trig); if (ret) goto error_unregister_rtc_irq; } return 0; error_unregister_rtc_irq: rtc_irq_unregister(trig_info->rtc, &trig_info->task); error_close_rtc: rtc_class_close(trig_info->rtc); error_free_name: kfree(trig->name); error_free_trig_info: kfree(trig_info); error_put_trigger_and_remove_from_list: list_del(&trig->alloc_list); iio_put_trigger(trig); error_free_completed_registrations: list_for_each_entry_safe(trig, trig2, &iio_prtc_trigger_list, alloc_list) { trig_info = trig->private_data; rtc_irq_unregister(trig_info->rtc, &trig_info->task); rtc_class_close(trig_info->rtc); kfree(trig->name); kfree(trig_info); iio_trigger_unregister(trig); } return ret; } static int iio_trig_periodic_rtc_remove(struct platform_device *dev) { struct iio_trigger *trig, *trig2; struct iio_prtc_trigger_info *trig_info; mutex_lock(&iio_prtc_trigger_list_lock); list_for_each_entry_safe(trig, trig2, &iio_prtc_trigger_list, alloc_list) { trig_info = trig->private_data; rtc_irq_unregister(trig_info->rtc, &trig_info->task); rtc_class_close(trig_info->rtc); kfree(trig->name); kfree(trig_info); iio_trigger_unregister(trig); } mutex_unlock(&iio_prtc_trigger_list_lock); return 0; } static struct platform_driver iio_trig_periodic_rtc_driver = { .probe = iio_trig_periodic_rtc_probe, .remove = iio_trig_periodic_rtc_remove, .driver = { .name = "iio_prtc_trigger", .owner = THIS_MODULE, }, }; static int __init iio_trig_periodic_rtc_init(void) { return platform_driver_register(&iio_trig_periodic_rtc_driver); } static void __exit iio_trig_periodic_rtc_exit(void) { return platform_driver_unregister(&iio_trig_periodic_rtc_driver); } module_init(iio_trig_periodic_rtc_init); module_exit(iio_trig_periodic_rtc_exit); MODULE_AUTHOR("Jonathan Cameron "); MODULE_DESCRIPTION("Periodic realtime clock trigger for the iio subsystem"); MODULE_LICENSE("GPL v2");