diff options
Diffstat (limited to 'drivers/iio/industrialio-trigger.c')
| -rw-r--r-- | drivers/iio/industrialio-trigger.c | 157 |
1 files changed, 122 insertions, 35 deletions
diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index ea8a4146620..3383b025f62 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -55,25 +55,16 @@ static struct attribute *iio_trig_dev_attrs[] = { &dev_attr_name.attr, NULL, }; - -static struct attribute_group iio_trig_attr_group = { - .attrs = iio_trig_dev_attrs, -}; - -static const struct attribute_group *iio_trig_attr_groups[] = { - &iio_trig_attr_group, - NULL -}; +ATTRIBUTE_GROUPS(iio_trig_dev); int iio_trigger_register(struct iio_trigger *trig_info) { int ret; trig_info->id = ida_simple_get(&iio_trigger_ida, 0, 0, GFP_KERNEL); - if (trig_info->id < 0) { - ret = trig_info->id; - goto error_ret; - } + if (trig_info->id < 0) + return trig_info->id; + /* Set the name used for the sysfs directory etc */ dev_set_name(&trig_info->dev, "trigger%ld", (unsigned long) trig_info->id); @@ -91,7 +82,6 @@ int iio_trigger_register(struct iio_trigger *trig_info) error_unregister_id: ida_simple_remove(&iio_trigger_ida, trig_info->id); -error_ret: return ret; } EXPORT_SYMBOL(iio_trigger_register); @@ -127,12 +117,17 @@ static struct iio_trigger *iio_trigger_find_by_name(const char *name, void iio_trigger_poll(struct iio_trigger *trig, s64 time) { int i; - if (!trig->use_count) - for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) - if (trig->subirqs[i].enabled) { - trig->use_count++; + + if (!atomic_read(&trig->use_count)) { + atomic_set(&trig->use_count, CONFIG_IIO_CONSUMERS_PER_TRIGGER); + + for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) { + if (trig->subirqs[i].enabled) generic_handle_irq(trig->subirq_base + i); - } + else + iio_trigger_notify_done(trig); + } + } } EXPORT_SYMBOL(iio_trigger_poll); @@ -146,19 +141,24 @@ EXPORT_SYMBOL(iio_trigger_generic_data_rdy_poll); void iio_trigger_poll_chained(struct iio_trigger *trig, s64 time) { int i; - if (!trig->use_count) - for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) - if (trig->subirqs[i].enabled) { - trig->use_count++; + + if (!atomic_read(&trig->use_count)) { + atomic_set(&trig->use_count, CONFIG_IIO_CONSUMERS_PER_TRIGGER); + + for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) { + if (trig->subirqs[i].enabled) handle_nested_irq(trig->subirq_base + i); - } + else + iio_trigger_notify_done(trig); + } + } } EXPORT_SYMBOL(iio_trigger_poll_chained); void iio_trigger_notify_done(struct iio_trigger *trig) { - trig->use_count--; - if (trig->use_count == 0 && trig->ops && trig->ops->try_reenable) + if (atomic_dec_and_test(&trig->use_count) && trig->ops && + trig->ops->try_reenable) if (trig->ops->try_reenable(trig)) /* Missed an interrupt so launch new poll now */ iio_trigger_poll(trig, 0); @@ -232,13 +232,12 @@ static int iio_trigger_detach_poll_func(struct iio_trigger *trig, if (trig->ops && trig->ops->set_trigger_state && no_other_users) { ret = trig->ops->set_trigger_state(trig, false); if (ret) - goto error_ret; + return ret; } iio_trigger_put_irq(trig, pf->irq); free_irq(pf->irq, pf); module_put(pf->indio_dev->info->driver_module); -error_ret: return ret; } @@ -308,7 +307,7 @@ static ssize_t iio_trigger_read_current(struct device *dev, * iio_trigger_write_current() - trigger consumer sysfs set current trigger * * For trigger consumers the current_trigger interface allows the trigger - * used for this device to be specified at run time based on the triggers + * used for this device to be specified at run time based on the trigger's * name. **/ static ssize_t iio_trigger_write_current(struct device *dev, @@ -346,7 +345,7 @@ static ssize_t iio_trigger_write_current(struct device *dev, indio_dev->trig = trig; - if (oldtrig && indio_dev->trig != oldtrig) + if (oldtrig) iio_trigger_put(oldtrig); if (indio_dev->trig) iio_trigger_get(indio_dev->trig); @@ -393,7 +392,7 @@ static void iio_trig_release(struct device *device) static struct device_type iio_trig_type = { .release = iio_trig_release, - .groups = iio_trig_attr_groups, + .groups = iio_trig_dev_groups, }; static void iio_trig_subirqmask(struct irq_data *d) @@ -414,9 +413,8 @@ static void iio_trig_subirqunmask(struct irq_data *d) trig->subirqs[d->irq - trig->subirq_base].enabled = true; } -struct iio_trigger *iio_trigger_alloc(const char *fmt, ...) +static struct iio_trigger *viio_trigger_alloc(const char *fmt, va_list vargs) { - va_list vargs; struct iio_trigger *trig; trig = kzalloc(sizeof *trig, GFP_KERNEL); if (trig) { @@ -434,9 +432,8 @@ struct iio_trigger *iio_trigger_alloc(const char *fmt, ...) kfree(trig); return NULL; } - va_start(vargs, fmt); + trig->name = kvasprintf(GFP_KERNEL, fmt, vargs); - va_end(vargs); if (trig->name == NULL) { irq_free_descs(trig->subirq_base, CONFIG_IIO_CONSUMERS_PER_TRIGGER); @@ -457,6 +454,19 @@ struct iio_trigger *iio_trigger_alloc(const char *fmt, ...) } get_device(&trig->dev); } + + return trig; +} + +struct iio_trigger *iio_trigger_alloc(const char *fmt, ...) +{ + struct iio_trigger *trig; + va_list vargs; + + va_start(vargs, fmt); + trig = viio_trigger_alloc(fmt, vargs); + va_end(vargs); + return trig; } EXPORT_SYMBOL(iio_trigger_alloc); @@ -468,6 +478,83 @@ void iio_trigger_free(struct iio_trigger *trig) } EXPORT_SYMBOL(iio_trigger_free); +static void devm_iio_trigger_release(struct device *dev, void *res) +{ + iio_trigger_free(*(struct iio_trigger **)res); +} + +static int devm_iio_trigger_match(struct device *dev, void *res, void *data) +{ + struct iio_trigger **r = res; + + if (!r || !*r) { + WARN_ON(!r || !*r); + return 0; + } + + return *r == data; +} + +/** + * devm_iio_trigger_alloc - Resource-managed iio_trigger_alloc() + * @dev: Device to allocate iio_trigger for + * @fmt: trigger name format. If it includes format + * specifiers, the additional arguments following + * format are formatted and inserted in the resulting + * string replacing their respective specifiers. + * + * Managed iio_trigger_alloc. iio_trigger allocated with this function is + * automatically freed on driver detach. + * + * If an iio_trigger allocated with this function needs to be freed separately, + * devm_iio_trigger_free() must be used. + * + * RETURNS: + * Pointer to allocated iio_trigger on success, NULL on failure. + */ +struct iio_trigger *devm_iio_trigger_alloc(struct device *dev, + const char *fmt, ...) +{ + struct iio_trigger **ptr, *trig; + va_list vargs; + + ptr = devres_alloc(devm_iio_trigger_release, sizeof(*ptr), + GFP_KERNEL); + if (!ptr) + return NULL; + + /* use raw alloc_dr for kmalloc caller tracing */ + va_start(vargs, fmt); + trig = viio_trigger_alloc(fmt, vargs); + va_end(vargs); + if (trig) { + *ptr = trig; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return trig; +} +EXPORT_SYMBOL_GPL(devm_iio_trigger_alloc); + +/** + * devm_iio_trigger_free - Resource-managed iio_trigger_free() + * @dev: Device this iio_dev belongs to + * @iio_trig: the iio_trigger associated with the device + * + * Free iio_trigger allocated with devm_iio_trigger_alloc(). + */ +void devm_iio_trigger_free(struct device *dev, struct iio_trigger *iio_trig) +{ + int rc; + + rc = devres_release(dev, devm_iio_trigger_release, + devm_iio_trigger_match, iio_trig); + WARN_ON(rc); +} +EXPORT_SYMBOL_GPL(devm_iio_trigger_free); + void iio_device_register_trigger_consumer(struct iio_dev *indio_dev) { indio_dev->groups[indio_dev->groupcounter++] = |
