cregit-Linux how code gets into the kernel

Release 4.11 drivers/staging/iio/adc/ad7606.c

/*
 * AD7606 SPI ADC driver
 *
 * Copyright 2011 Analog Devices Inc.
 *
 * Licensed under the GPL-2.
 */

#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/regulator/consumer.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/module.h>

#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>

#include "ad7606.h"

/* Scales are computed as 2.5/2**16 and 5/2**16 respectively */

static const unsigned int scale_avail[2][2] = {
	{0, 38147}, {0, 76294}
};


static int ad7606_reset(struct ad7606_state *st) { if (st->gpio_reset) { gpiod_set_value(st->gpio_reset, 1); ndelay(100); /* t_reset >= 100ns */ gpiod_set_value(st->gpio_reset, 0); return 0; } return -ENODEV; }

Contributors

PersonTokensPropCommitsCommitProp
Michael Hennerich4692.00%125.00%
Lars-Peter Clausen36.00%250.00%
Jonathan Cameron12.00%125.00%
Total50100.00%4100.00%


static int ad7606_read_samples(struct ad7606_state *st) { unsigned int num = st->chip_info->num_channels; u16 *data = st->data; int ret; /* * The frstdata signal is set to high while and after reading the sample * of the first channel and low for all other channels. This can be used * to check that the incoming data is correctly aligned. During normal * operation the data should never become unaligned, but some glitch or * electrostatic discharge might cause an extra read or clock cycle. * Monitoring the frstdata signal allows to recover from such failure * situations. */ if (st->gpio_frstdata) { ret = st->bops->read_block(st->dev, 1, data); if (ret) return ret; if (!gpiod_get_value(st->gpio_frstdata)) { ad7606_reset(st); return -EIO; } data++; num--; } return st->bops->read_block(st->dev, num, data); }

Contributors

PersonTokensPropCommitsCommitProp
Michael Hennerich7367.59%233.33%
Lars-Peter Clausen3431.48%350.00%
Jonathan Cameron10.93%116.67%
Total108100.00%6100.00%


static irqreturn_t ad7606_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; struct ad7606_state *st = iio_priv(pf->indio_dev); gpiod_set_value(st->gpio_convst, 1); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
Lars-Peter Clausen44100.00%1100.00%
Total44100.00%1100.00%

/** * ad7606_poll_bh_to_ring() bh of trigger launched polling to ring buffer * @work_s: the work struct through which this was scheduled * * Currently there is no option in this driver to disable the saving of * timestamps within the ring. * I think the one copy of this at a time was to avoid problems if the * trigger was set far too high and the reads then locked up the computer. **/
static void ad7606_poll_bh_to_ring(struct work_struct *work_s) { struct ad7606_state *st = container_of(work_s, struct ad7606_state, poll_work); struct iio_dev *indio_dev = iio_priv_to_dev(st); int ret; ret = ad7606_read_samples(st); if (ret == 0) iio_push_to_buffers_with_timestamp(indio_dev, st->data, iio_get_time_ns(indio_dev)); gpiod_set_value(st->gpio_convst, 0); iio_trigger_notify_done(indio_dev->trig); }

Contributors

PersonTokensPropCommitsCommitProp
Lars-Peter Clausen82100.00%1100.00%
Total82100.00%1100.00%


static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned int ch) { struct ad7606_state *st = iio_priv(indio_dev); int ret; st->done = false; gpiod_set_value(st->gpio_convst, 1); ret = wait_event_interruptible(st->wq_data_avail, st->done); if (ret) goto error_ret; ret = ad7606_read_samples(st); if (ret == 0) ret = st->data[ch]; error_ret: gpiod_set_value(st->gpio_convst, 0); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Lars-Peter Clausen5454.55%266.67%
Michael Hennerich4545.45%133.33%
Total99100.00%3100.00%


static int ad7606_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long m) { int ret; struct ad7606_state *st = iio_priv(indio_dev); switch (m) { case IIO_CHAN_INFO_RAW: ret = iio_device_claim_direct_mode(indio_dev); if (ret) return ret; ret = ad7606_scan_direct(indio_dev, chan->address); iio_device_release_direct_mode(indio_dev); if (ret < 0) return ret; *val = (short)ret; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: *val = scale_avail[st->range][0]; *val2 = scale_avail[st->range][1]; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: *val = st->oversampling; return IIO_VAL_INT; } return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
Michael Hennerich10973.15%440.00%
Eva Rachel Retuya2516.78%220.00%
Alison Schofield74.70%110.00%
Lars-Peter Clausen64.03%110.00%
Jonathan Cameron21.34%220.00%
Total149100.00%10100.00%


static ssize_t in_voltage_scale_available_show(struct device *dev, struct device_attribute *attr, char *buf) { int i, len = 0; for (i = 0; i < ARRAY_SIZE(scale_avail); i++) len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06u ", scale_avail[i][0], scale_avail[i][1]); buf[len - 1] = '\n'; return len; }

Contributors

PersonTokensPropCommitsCommitProp
Eva Rachel Retuya5058.14%133.33%
Michael Hennerich3540.70%133.33%
Aida Mynzhasova11.16%133.33%
Total86100.00%3100.00%

static IIO_DEVICE_ATTR_RO(in_voltage_scale_available, 0);
static int ad7606_oversampling_get_index(unsigned int val) { unsigned char supported[] = {1, 2, 4, 8, 16, 32, 64}; int i; for (i = 0; i < ARRAY_SIZE(supported); i++) if (val == supported[i]) return i; return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
Michael Hennerich6496.97%133.33%
Alison Schofield11.52%133.33%
Lars-Peter Clausen11.52%133.33%
Total66100.00%3100.00%


static int ad7606_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { struct ad7606_state *st = iio_priv(indio_dev); int values[3]; int ret, i; switch (mask) { case IIO_CHAN_INFO_SCALE: ret = -EINVAL; mutex_lock(&indio_dev->mlock); for (i = 0; i < ARRAY_SIZE(scale_avail); i++) if (val2 == scale_avail[i][1]) { gpiod_set_value(st->gpio_range, i); st->range = i; ret = 0; break; } mutex_unlock(&indio_dev->mlock); return ret; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: if (val2) return -EINVAL; ret = ad7606_oversampling_get_index(val); if (ret < 0) return ret; values[0] = (ret >> 0) & 1; values[1] = (ret >> 1) & 1; values[2] = (ret >> 2) & 1; mutex_lock(&indio_dev->mlock); gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc, values); st->oversampling = val; mutex_unlock(&indio_dev->mlock); return 0; default: return -EINVAL; } }

Contributors

PersonTokensPropCommitsCommitProp
Eva Rachel Retuya11346.69%228.57%
Michael Hennerich8334.30%342.86%
Lars-Peter Clausen4418.18%114.29%
Aida Mynzhasova20.83%114.29%
Total242100.00%7100.00%

static IIO_CONST_ATTR(oversampling_ratio_available, "1 2 4 8 16 32 64"); static struct attribute *ad7606_attributes_os_and_range[] = { &iio_dev_attr_in_voltage_scale_available.dev_attr.attr, &iio_const_attr_oversampling_ratio_available.dev_attr.attr, NULL, }; static const struct attribute_group ad7606_attribute_group_os_and_range = { .attrs = ad7606_attributes_os_and_range, }; static struct attribute *ad7606_attributes_os[] = { &iio_const_attr_oversampling_ratio_available.dev_attr.attr, NULL, }; static const struct attribute_group ad7606_attribute_group_os = { .attrs = ad7606_attributes_os, }; static struct attribute *ad7606_attributes_range[] = { &iio_dev_attr_in_voltage_scale_available.dev_attr.attr, NULL, }; static const struct attribute_group ad7606_attribute_group_range = { .attrs = ad7606_attributes_range, }; #define AD7606_CHANNEL(num) \ { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ .channel = num, \ .address = num, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\ .info_mask_shared_by_all = \ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .scan_index = num, \ .scan_type = { \ .sign = 's', \ .realbits = 16, \ .storagebits = 16, \ .endianness = IIO_CPU, \ }, \ } static const struct iio_chan_spec ad7606_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(8), AD7606_CHANNEL(0), AD7606_CHANNEL(1), AD7606_CHANNEL(2), AD7606_CHANNEL(3), AD7606_CHANNEL(4), AD7606_CHANNEL(5), AD7606_CHANNEL(6), AD7606_CHANNEL(7), }; static const struct ad7606_chip_info ad7606_chip_info_tbl[] = { /* * More devices added in future */ [ID_AD7606_8] = { .channels = ad7606_channels, .num_channels = 9, }, [ID_AD7606_6] = { .channels = ad7606_channels, .num_channels = 7, }, [ID_AD7606_4] = { .channels = ad7606_channels, .num_channels = 5, }, };
static int ad7606_request_gpios(struct ad7606_state *st) { struct device *dev = st->dev; st->gpio_convst = devm_gpiod_get(dev, "conversion-start", GPIOD_OUT_LOW); if (IS_ERR(st->gpio_convst)) return PTR_ERR(st->gpio_convst); st->gpio_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(st->gpio_reset)) return PTR_ERR(st->gpio_reset); st->gpio_range = devm_gpiod_get_optional(dev, "range", GPIOD_OUT_LOW); if (IS_ERR(st->gpio_range)) return PTR_ERR(st->gpio_range); st->gpio_standby = devm_gpiod_get_optional(dev, "standby", GPIOD_OUT_HIGH); if (IS_ERR(st->gpio_standby)) return PTR_ERR(st->gpio_standby); st->gpio_frstdata = devm_gpiod_get_optional(dev, "first-data", GPIOD_IN); if (IS_ERR(st->gpio_frstdata)) return PTR_ERR(st->gpio_frstdata); st->gpio_os = devm_gpiod_get_array_optional(dev, "oversampling-ratio", GPIOD_OUT_LOW); return PTR_ERR_OR_ZERO(st->gpio_os); }

Contributors

PersonTokensPropCommitsCommitProp
Lars-Peter Clausen8745.55%125.00%
Michael Hennerich7036.65%125.00%
Jonathan Cameron3317.28%125.00%
Fengguang Wu10.52%125.00%
Total191100.00%4100.00%

/** * Interrupt handler */
static irqreturn_t ad7606_interrupt(int irq, void *dev_id) { struct iio_dev *indio_dev = dev_id; struct ad7606_state *st = iio_priv(indio_dev); if (iio_buffer_enabled(indio_dev)) { schedule_work(&st->poll_work); } else { st->done = true; wake_up_interruptible(&st->wq_data_avail); } return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
Michael Hennerich6698.51%266.67%
Jonathan Cameron11.49%133.33%
Total67100.00%3100.00%

; static const struct iio_info ad7606_info_no_os_or_range = { .driver_module = THIS_MODULE, .read_raw = &ad7606_read_raw, }; static const struct iio_info ad7606_info_os_and_range = { .driver_module = THIS_MODULE, .read_raw = &ad7606_read_raw, .write_raw = &ad7606_write_raw, .attrs = &ad7606_attribute_group_os_and_range, }; static const struct iio_info ad7606_info_os = { .driver_module = THIS_MODULE, .read_raw = &ad7606_read_raw, .write_raw = &ad7606_write_raw, .attrs = &ad7606_attribute_group_os, }; static const struct iio_info ad7606_info_range = { .driver_module = THIS_MODULE, .read_raw = &ad7606_read_raw, .write_raw = &ad7606_write_raw, .attrs = &ad7606_attribute_group_range, };
int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, const char *name, unsigned int id, const struct ad7606_bus_ops *bops) { struct ad7606_state *st; int ret; struct iio_dev *indio_dev; indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; st = iio_priv(indio_dev); st->dev = dev; st->bops = bops; st->base_address = base_address; /* tied to logic low, analog input range is +/- 5V */ st->range = 0; st->oversampling = 1; INIT_WORK(&st->poll_work, &ad7606_poll_bh_to_ring); st->reg = devm_regulator_get(dev, "avcc"); if (IS_ERR(st->reg)) return PTR_ERR(st->reg); ret = regulator_enable(st->reg); if (ret) { dev_err(dev, "Failed to enable specified AVcc supply\n"); return ret; } ret = ad7606_request_gpios(st); if (ret) goto error_disable_reg; st->chip_info = &ad7606_chip_info_tbl[id]; indio_dev->dev.parent = dev; if (st->gpio_os) { if (st->gpio_range) indio_dev->info = &ad7606_info_os_and_range; else indio_dev->info = &ad7606_info_os; } else { if (st->gpio_range) indio_dev->info = &ad7606_info_range; else indio_dev->info = &ad7606_info_no_os_or_range; } indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->name = name; indio_dev->channels = st->chip_info->channels; indio_dev->num_channels = st->chip_info->num_channels; init_waitqueue_head(&st->wq_data_avail); ret = ad7606_reset(st); if (ret) dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n"); ret = request_irq(irq, ad7606_interrupt, IRQF_TRIGGER_FALLING, name, indio_dev); if (ret) goto error_disable_reg; ret = iio_triggered_buffer_setup(indio_dev, &ad7606_trigger_handler, NULL, NULL); if (ret) goto error_free_irq; ret = iio_device_register(indio_dev); if (ret) goto error_unregister_ring; dev_set_drvdata(dev, indio_dev); return 0; error_unregister_ring: iio_triggered_buffer_cleanup(indio_dev); error_free_irq: free_irq(irq, indio_dev); error_disable_reg: regulator_disable(st->reg); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Michael Hennerich27065.85%316.67%
Jonathan Cameron5713.90%422.22%
Lars-Peter Clausen4911.95%633.33%
Eva Rachel Retuya194.63%316.67%
Sachin Kamat143.41%15.56%
Alison Schofield10.24%15.56%
Total410100.00%18100.00%

EXPORT_SYMBOL_GPL(ad7606_probe);
int ad7606_remove(struct device *dev, int irq) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct ad7606_state *st = iio_priv(indio_dev); iio_device_unregister(indio_dev); iio_triggered_buffer_cleanup(indio_dev); free_irq(irq, indio_dev); regulator_disable(st->reg); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Michael Hennerich4066.67%233.33%
Lars-Peter Clausen1321.67%233.33%
Jonathan Cameron711.67%233.33%
Total60100.00%6100.00%

EXPORT_SYMBOL_GPL(ad7606_remove); #ifdef CONFIG_PM_SLEEP
static int ad7606_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct ad7606_state *st = iio_priv(indio_dev); if (st->gpio_standby) { gpiod_set_value(st->gpio_range, 1); gpiod_set_value(st->gpio_standby, 0); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Michael Hennerich3863.33%240.00%
Lars-Peter Clausen2135.00%240.00%
Jonathan Cameron11.67%120.00%
Total60100.00%5100.00%


static int ad7606_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct ad7606_state *st = iio_priv(indio_dev); if (st->gpio_standby) { gpiod_set_value(st->gpio_range, st->range); gpiod_set_value(st->gpio_standby, 1); ad7606_reset(st); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Michael Hennerich4567.16%240.00%
Lars-Peter Clausen2131.34%240.00%
Jonathan Cameron11.49%120.00%
Total67100.00%5100.00%

SIMPLE_DEV_PM_OPS(ad7606_pm_ops, ad7606_suspend, ad7606_resume); EXPORT_SYMBOL_GPL(ad7606_pm_ops); #endif MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("Analog Devices AD7606 ADC"); MODULE_LICENSE("GPL v2");

Overall Contributors

PersonTokensPropCommitsCommitProp
Michael Hennerich117852.24%512.50%
Lars-Peter Clausen49421.91%1230.00%
Jonathan Cameron28212.51%1230.00%
Eva Rachel Retuya25711.40%410.00%
Michal Marek140.62%12.50%
Sachin Kamat140.62%12.50%
Alison Schofield90.40%25.00%
Paul Gortmaker30.13%12.50%
Aida Mynzhasova30.13%12.50%
Fengguang Wu10.04%12.50%
Total2255100.00%40100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.