From 57f0f512b273f60d52568b8c6b77e17f5636edc0 Mon Sep 17 00:00:00 2001 From: André Fabian Silva Delgado Date: Wed, 5 Aug 2015 17:04:01 -0300 Subject: Initial import --- drivers/staging/iio/cdc/ad7152.c | 543 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 543 insertions(+) create mode 100644 drivers/staging/iio/cdc/ad7152.c (limited to 'drivers/staging/iio/cdc/ad7152.c') diff --git a/drivers/staging/iio/cdc/ad7152.c b/drivers/staging/iio/cdc/ad7152.c new file mode 100644 index 000000000..87110d940 --- /dev/null +++ b/drivers/staging/iio/cdc/ad7152.c @@ -0,0 +1,543 @@ +/* + * AD7152 capacitive sensor driver supporting AD7152/3 + * + * Copyright 2010-2011a Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * TODO: Check compliance of calibbias with abi (units) + */ +/* + * AD7152 registers definition + */ + +#define AD7152_REG_STATUS 0 +#define AD7152_REG_CH1_DATA_HIGH 1 +#define AD7152_REG_CH2_DATA_HIGH 3 +#define AD7152_REG_CH1_OFFS_HIGH 5 +#define AD7152_REG_CH2_OFFS_HIGH 7 +#define AD7152_REG_CH1_GAIN_HIGH 9 +#define AD7152_REG_CH1_SETUP 11 +#define AD7152_REG_CH2_GAIN_HIGH 12 +#define AD7152_REG_CH2_SETUP 14 +#define AD7152_REG_CFG 15 +#define AD7152_REG_RESEVERD 16 +#define AD7152_REG_CAPDAC_POS 17 +#define AD7152_REG_CAPDAC_NEG 18 +#define AD7152_REG_CFG2 26 + +/* Status Register Bit Designations (AD7152_REG_STATUS) */ +#define AD7152_STATUS_RDY1 (1 << 0) +#define AD7152_STATUS_RDY2 (1 << 1) +#define AD7152_STATUS_C1C2 (1 << 2) +#define AD7152_STATUS_PWDN (1 << 7) + +/* Setup Register Bit Designations (AD7152_REG_CHx_SETUP) */ +#define AD7152_SETUP_CAPDIFF (1 << 5) +#define AD7152_SETUP_RANGE_2pF (0 << 6) +#define AD7152_SETUP_RANGE_0_5pF (1 << 6) +#define AD7152_SETUP_RANGE_1pF (2 << 6) +#define AD7152_SETUP_RANGE_4pF (3 << 6) +#define AD7152_SETUP_RANGE(x) ((x) << 6) + +/* Config Register Bit Designations (AD7152_REG_CFG) */ +#define AD7152_CONF_CH2EN (1 << 3) +#define AD7152_CONF_CH1EN (1 << 4) +#define AD7152_CONF_MODE_IDLE (0 << 0) +#define AD7152_CONF_MODE_CONT_CONV (1 << 0) +#define AD7152_CONF_MODE_SINGLE_CONV (2 << 0) +#define AD7152_CONF_MODE_OFFS_CAL (5 << 0) +#define AD7152_CONF_MODE_GAIN_CAL (6 << 0) + +/* Capdac Register Bit Designations (AD7152_REG_CAPDAC_XXX) */ +#define AD7152_CAPDAC_DACEN (1 << 7) +#define AD7152_CAPDAC_DACP(x) ((x) & 0x1F) + +/* CFG2 Register Bit Designations (AD7152_REG_CFG2) */ +#define AD7152_CFG2_OSR(x) (((x) & 0x3) << 4) + +enum { + AD7152_DATA, + AD7152_OFFS, + AD7152_GAIN, + AD7152_SETUP +}; + +/* + * struct ad7152_chip_info - chip specific information + */ + +struct ad7152_chip_info { + struct i2c_client *client; + /* + * Capacitive channel digital filter setup; + * conversion time/update rate setup per channel + */ + u8 filter_rate_setup; + u8 setup[2]; +}; + +static inline ssize_t ad7152_start_calib(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len, + u8 regval) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad7152_chip_info *chip = iio_priv(indio_dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + bool doit; + int ret, timeout = 10; + + ret = strtobool(buf, &doit); + if (ret < 0) + return ret; + + if (!doit) + return 0; + + if (this_attr->address == 0) + regval |= AD7152_CONF_CH1EN; + else + regval |= AD7152_CONF_CH2EN; + + mutex_lock(&indio_dev->mlock); + ret = i2c_smbus_write_byte_data(chip->client, AD7152_REG_CFG, regval); + if (ret < 0) { + mutex_unlock(&indio_dev->mlock); + return ret; + } + + do { + mdelay(20); + ret = i2c_smbus_read_byte_data(chip->client, AD7152_REG_CFG); + if (ret < 0) { + mutex_unlock(&indio_dev->mlock); + return ret; + } + } while ((ret == regval) && timeout--); + + mutex_unlock(&indio_dev->mlock); + return len; +} +static ssize_t ad7152_start_offset_calib(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + return ad7152_start_calib(dev, attr, buf, len, + AD7152_CONF_MODE_OFFS_CAL); +} +static ssize_t ad7152_start_gain_calib(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + return ad7152_start_calib(dev, attr, buf, len, + AD7152_CONF_MODE_GAIN_CAL); +} + +static IIO_DEVICE_ATTR(in_capacitance0_calibbias_calibration, + S_IWUSR, NULL, ad7152_start_offset_calib, 0); +static IIO_DEVICE_ATTR(in_capacitance1_calibbias_calibration, + S_IWUSR, NULL, ad7152_start_offset_calib, 1); +static IIO_DEVICE_ATTR(in_capacitance0_calibscale_calibration, + S_IWUSR, NULL, ad7152_start_gain_calib, 0); +static IIO_DEVICE_ATTR(in_capacitance1_calibscale_calibration, + S_IWUSR, NULL, ad7152_start_gain_calib, 1); + +/* Values are Update Rate (Hz), Conversion Time (ms) + 1*/ +static const unsigned char ad7152_filter_rate_table[][2] = { + {200, 5 + 1}, {50, 20 + 1}, {20, 50 + 1}, {17, 60 + 1}, +}; + +static ssize_t ad7152_show_filter_rate_setup(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad7152_chip_info *chip = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", + ad7152_filter_rate_table[chip->filter_rate_setup][0]); +} + +static ssize_t ad7152_store_filter_rate_setup(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad7152_chip_info *chip = iio_priv(indio_dev); + u8 data; + int ret, i; + + ret = kstrtou8(buf, 10, &data); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(ad7152_filter_rate_table); i++) + if (data >= ad7152_filter_rate_table[i][0]) + break; + + if (i >= ARRAY_SIZE(ad7152_filter_rate_table)) + i = ARRAY_SIZE(ad7152_filter_rate_table) - 1; + + mutex_lock(&indio_dev->mlock); + ret = i2c_smbus_write_byte_data(chip->client, + AD7152_REG_CFG2, AD7152_CFG2_OSR(i)); + if (ret < 0) { + mutex_unlock(&indio_dev->mlock); + return ret; + } + + chip->filter_rate_setup = i; + mutex_unlock(&indio_dev->mlock); + + return len; +} + +static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR, + ad7152_show_filter_rate_setup, + ad7152_store_filter_rate_setup); + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("200 50 20 17"); + +static IIO_CONST_ATTR(in_capacitance_scale_available, + "0.000061050 0.000030525 0.000015263 0.000007631"); + +static struct attribute *ad7152_attributes[] = { + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_dev_attr_in_capacitance0_calibbias_calibration.dev_attr.attr, + &iio_dev_attr_in_capacitance1_calibbias_calibration.dev_attr.attr, + &iio_dev_attr_in_capacitance0_calibscale_calibration.dev_attr.attr, + &iio_dev_attr_in_capacitance1_calibscale_calibration.dev_attr.attr, + &iio_const_attr_in_capacitance_scale_available.dev_attr.attr, + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ad7152_attribute_group = { + .attrs = ad7152_attributes, +}; + +static const u8 ad7152_addresses[][4] = { + { AD7152_REG_CH1_DATA_HIGH, AD7152_REG_CH1_OFFS_HIGH, + AD7152_REG_CH1_GAIN_HIGH, AD7152_REG_CH1_SETUP }, + { AD7152_REG_CH2_DATA_HIGH, AD7152_REG_CH2_OFFS_HIGH, + AD7152_REG_CH2_GAIN_HIGH, AD7152_REG_CH2_SETUP }, +}; + +/* Values are nano relative to pf base. */ +static const int ad7152_scale_table[] = { + 30525, 7631, 15263, 61050 +}; + +static int ad7152_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad7152_chip_info *chip = iio_priv(indio_dev); + int ret, i; + + mutex_lock(&indio_dev->mlock); + + switch (mask) { + case IIO_CHAN_INFO_CALIBSCALE: + if (val != 1) { + ret = -EINVAL; + goto out; + } + + val = (val2 * 1024) / 15625; + + ret = i2c_smbus_write_word_data(chip->client, + ad7152_addresses[chan->channel][AD7152_GAIN], + swab16(val)); + if (ret < 0) + goto out; + + ret = 0; + break; + + case IIO_CHAN_INFO_CALIBBIAS: + if ((val < 0) | (val > 0xFFFF)) { + ret = -EINVAL; + goto out; + } + ret = i2c_smbus_write_word_data(chip->client, + ad7152_addresses[chan->channel][AD7152_OFFS], + swab16(val)); + if (ret < 0) + goto out; + + ret = 0; + break; + case IIO_CHAN_INFO_SCALE: + if (val != 0) { + ret = -EINVAL; + goto out; + } + for (i = 0; i < ARRAY_SIZE(ad7152_scale_table); i++) + if (val2 == ad7152_scale_table[i]) + break; + + chip->setup[chan->channel] &= ~AD7152_SETUP_RANGE_4pF; + chip->setup[chan->channel] |= AD7152_SETUP_RANGE(i); + + ret = i2c_smbus_write_byte_data(chip->client, + ad7152_addresses[chan->channel][AD7152_SETUP], + chip->setup[chan->channel]); + if (ret < 0) + goto out; + + ret = 0; + break; + default: + ret = -EINVAL; + } + +out: + mutex_unlock(&indio_dev->mlock); + return ret; +} +static int ad7152_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, + long mask) +{ + struct ad7152_chip_info *chip = iio_priv(indio_dev); + int ret; + u8 regval = 0; + + mutex_lock(&indio_dev->mlock); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + /* First set whether in differential mode */ + + regval = chip->setup[chan->channel]; + + if (chan->differential) + chip->setup[chan->channel] |= AD7152_SETUP_CAPDIFF; + else + chip->setup[chan->channel] &= ~AD7152_SETUP_CAPDIFF; + + if (regval != chip->setup[chan->channel]) { + ret = i2c_smbus_write_byte_data(chip->client, + ad7152_addresses[chan->channel][AD7152_SETUP], + chip->setup[chan->channel]); + if (ret < 0) + goto out; + } + /* Make sure the channel is enabled */ + if (chan->channel == 0) + regval = AD7152_CONF_CH1EN; + else + regval = AD7152_CONF_CH2EN; + + /* Trigger a single read */ + regval |= AD7152_CONF_MODE_SINGLE_CONV; + ret = i2c_smbus_write_byte_data(chip->client, AD7152_REG_CFG, + regval); + if (ret < 0) + goto out; + + msleep(ad7152_filter_rate_table[chip->filter_rate_setup][1]); + /* Now read the actual register */ + ret = i2c_smbus_read_word_data(chip->client, + ad7152_addresses[chan->channel][AD7152_DATA]); + if (ret < 0) + goto out; + *val = swab16(ret); + + if (chan->differential) + *val -= 0x8000; + + ret = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_CALIBSCALE: + + ret = i2c_smbus_read_word_data(chip->client, + ad7152_addresses[chan->channel][AD7152_GAIN]); + if (ret < 0) + goto out; + /* 1 + gain_val / 2^16 */ + *val = 1; + *val2 = (15625 * swab16(ret)) / 1024; + + ret = IIO_VAL_INT_PLUS_MICRO; + break; + case IIO_CHAN_INFO_CALIBBIAS: + ret = i2c_smbus_read_word_data(chip->client, + ad7152_addresses[chan->channel][AD7152_OFFS]); + if (ret < 0) + goto out; + *val = swab16(ret); + + ret = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SCALE: + ret = i2c_smbus_read_byte_data(chip->client, + ad7152_addresses[chan->channel][AD7152_SETUP]); + if (ret < 0) + goto out; + *val = 0; + *val2 = ad7152_scale_table[ret >> 6]; + + ret = IIO_VAL_INT_PLUS_NANO; + break; + default: + ret = -EINVAL; + } +out: + mutex_unlock(&indio_dev->mlock); + return ret; +} + +static int ad7152_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + default: + return IIO_VAL_INT_PLUS_MICRO; + } +} + +static const struct iio_info ad7152_info = { + .attrs = &ad7152_attribute_group, + .read_raw = &ad7152_read_raw, + .write_raw = &ad7152_write_raw, + .write_raw_get_fmt = &ad7152_write_raw_get_fmt, + .driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec ad7152_channels[] = { + { + .type = IIO_CAPACITANCE, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_SCALE), + }, { + .type = IIO_CAPACITANCE, + .differential = 1, + .indexed = 1, + .channel = 0, + .channel2 = 2, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_SCALE), + }, { + .type = IIO_CAPACITANCE, + .indexed = 1, + .channel = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_SCALE), + }, { + .type = IIO_CAPACITANCE, + .differential = 1, + .indexed = 1, + .channel = 1, + .channel2 = 3, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_SCALE), + } +}; +/* + * device probe and remove + */ + +static int ad7152_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct ad7152_chip_info *chip; + struct iio_dev *indio_dev; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + chip = iio_priv(indio_dev); + /* this is only used for device removal purposes */ + i2c_set_clientdata(client, indio_dev); + + chip->client = client; + + /* Establish that the iio_dev is a child of the i2c device */ + indio_dev->name = id->name; + indio_dev->dev.parent = &client->dev; + indio_dev->info = &ad7152_info; + indio_dev->channels = ad7152_channels; + if (id->driver_data == 0) + indio_dev->num_channels = ARRAY_SIZE(ad7152_channels); + else + indio_dev->num_channels = 2; + indio_dev->num_channels = ARRAY_SIZE(ad7152_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = iio_device_register(indio_dev); + if (ret) + return ret; + + dev_err(&client->dev, "%s capacitive sensor registered\n", id->name); + + return 0; +} + +static int ad7152_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + iio_device_unregister(indio_dev); + + return 0; +} + +static const struct i2c_device_id ad7152_id[] = { + { "ad7152", 0 }, + { "ad7153", 1 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, ad7152_id); + +static struct i2c_driver ad7152_driver = { + .driver = { + .name = KBUILD_MODNAME, + }, + .probe = ad7152_probe, + .remove = ad7152_remove, + .id_table = ad7152_id, +}; +module_i2c_driver(ad7152_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices AD7152/3 capacitive sensor driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-54-g00ecf