> I think this patch is more or less in it's final shape, please review it. comments inline > --- > drivers/iio/adc/Kconfig | 10 ++ > drivers/iio/adc/Makefile | 1 + > drivers/iio/adc/mcp3422.c | 432 +++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 443 insertions(+) > create mode 100644 drivers/iio/adc/mcp3422.c > > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig > index f725b45..e9879e6 100644 > --- a/drivers/iio/adc/Kconfig > +++ b/drivers/iio/adc/Kconfig > @@ -145,6 +145,16 @@ config MCP320X > This driver can also be built as a module. If so, the module will be > called mcp320x. > > +config MCP3422 > + tristate "Microchip Technology MCP3422/3/4 driver" > + depends on I2C > + help > + Say yes here to build support for Microchip Technology's MCP3422, > + MCP3423 or MCP3424 analog to digital converters. > + > + This driver can also be built as a module. If so, the module will be > + called mcp3422. > + > config NAU7802 > tristate "Nuvoton NAU7802 ADC driver" > depends on I2C > diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile > index 2a4324e..9772e3b 100644 > --- a/drivers/iio/adc/Makefile > +++ b/drivers/iio/adc/Makefile > @@ -16,6 +16,7 @@ obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o > obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o > obj-$(CONFIG_MAX1363) += max1363.o > obj-$(CONFIG_MCP320X) += mcp320x.o > +obj-$(CONFIG_MCP3422) += mcp3422.o > obj-$(CONFIG_NAU7802) += nau7802.o > obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o > obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o > diff --git a/drivers/iio/adc/mcp3422.c b/drivers/iio/adc/mcp3422.c > new file mode 100644 > index 0000000..eda5d39 > --- /dev/null > +++ b/drivers/iio/adc/mcp3422.c > @@ -0,0 +1,432 @@ > +/* > + * mcp3422.c - driver for the Microchip mcp3422/3/4 chip family > + * > + * Copyright (C) 2013, Angelo Compagnucci > + * Author: Angelo Compagnucci <angelo.compagnucci@xxxxxxxxx> > + * > + * Datasheet: http://ww1.microchip.com/downloads/en/devicedoc/22088b.pdf > + * > + * This driver exports the value of analog input voltage to sysfs, the > + * voltage unit is nV. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + */ > + > +#include <linux/err.h> > +#include <linux/i2c.h> > +#include <linux/module.h> > +#include <linux/delay.h> > +#include <linux/sysfs.h> > +#include <linux/string.h> > + > +#include <linux/iio/iio.h> > +#include <linux/iio/sysfs.h> > + > +/* Masks */ > +#define MCP3422_CHANNEL_MASK 0x60 > +#define MCP3422_PGA_MASK 0x03 > +#define MCP3422_SRATE_MASK 0x0C > +#define MCP3422_SRATE_240 0x0 > +#define MCP3422_SRATE_60 0x1 > +#define MCP3422_SRATE_15 0x2 > +#define MCP3422_SRATE_3 0x3 > +#define MCP3422_PGA_1 0 > +#define MCP3422_PGA_2 1 > +#define MCP3422_PGA_4 2 > +#define MCP3422_PGA_8 3 > +#define MCP3422_CONT_SAMPLING 0x10 > + > +#define MCP3422_CHANNEL(config) ((((config) & MCP3422_CHANNEL_MASK) >> 5) + 1) > +#define MCP3422_PGA(config) ((config) & MCP3422_PGA_MASK) > +#define MCP3422_SAMPLE_RATE(config) (((config) & MCP3422_SRATE_MASK) >> 2) > + > +#define MCP3422_CHANNEL_VALUE(value) (((value - 1) << 5) & MCP3422_CHANNEL_MASK) > +#define MCP3422_PGA_VALUE(value) ((value) & MCP3422_PGA_MASK) > +#define MCP3422_SAMPLE_RATE_VALUE(value) ((value << 2) & MCP3422_SRATE_MASK) > + > +#define MCP3422_CHAN(index) \ > + { \ > + .type = IIO_VOLTAGE, \ > + .indexed = 1, \ > + .channel = index, \ > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ > + | BIT(IIO_CHAN_INFO_SCALE), \ > + } > + > +/* LSB is in nV to eliminate floating point */ > +static const u32 rates_to_lsb[] = {1000000, 250000, 62500, 15625}; > + > +/* Client data (each client gets its own) */ > +struct mcp3422 { > + struct i2c_client *i2c; > + u8 config; > + u8 pga[4]; > + struct mutex lock; > +}; > + > +static int mcp3422_update_config(struct mcp3422 *adc, u8 newconfig) > +{ > + int ret; > + > + mutex_lock(&adc->lock); > + > + ret = i2c_master_send(adc->i2c, &newconfig, 1); > + if (ret > 0) > + adc->config = newconfig; > + > + mutex_unlock(&adc->lock); > + > + return ret; > +} > + > +static int mcp3422_read(struct mcp3422 *adc, int *value, u8 *config) > +{ > + int ret = 0; > + u8 sample_rate = MCP3422_SAMPLE_RATE(adc->config); > + u8 buf[4] = {0, 0, 0, 0}; > + u32 temp; > + > + if (sample_rate == MCP3422_SRATE_3) { > + ret = i2c_master_recv(adc->i2c, buf, 4); > + temp = buf[0] << 16 | buf[1] << 8 | buf[2]; > + *config = buf[3]; > + } else { > + ret = i2c_master_recv(adc->i2c, buf, 3); > + temp = buf[0] << 8 | buf[1]; > + *config = buf[2]; > + } > + > + switch (sample_rate) { > + case MCP3422_SRATE_240: > + *value = sign_extend32(temp, 12); > + break; > + case MCP3422_SRATE_60: > + *value = sign_extend32(temp, 14); > + break; > + case MCP3422_SRATE_15: > + *value = sign_extend32(temp, 16); > + break; > + case MCP3422_SRATE_3: > + *value = sign_extend32(temp, 18); > + break; > + } > + > + return ret; > +} > + > +static int mcp3422_read_channel(struct mcp3422 *adc, > + struct iio_chan_spec const *channel, int *value) > +{ > + int mtime = 0; > + u8 config = 0; > + u8 req_channel = channel->channel; > + > + if (req_channel != MCP3422_CHANNEL(adc->config)) { > + config = adc->config; > + config &= ~MCP3422_CHANNEL_MASK; > + config |= MCP3422_CHANNEL_VALUE(req_channel); > + config &= ~MCP3422_PGA_MASK; > + config |= MCP3422_PGA_VALUE(adc->pga[req_channel-1]); > + mcp3422_update_config(adc, config); > + switch (MCP3422_SAMPLE_RATE(config)) { > + case MCP3422_SRATE_240: > + mtime = 1000 / 240; > + break; > + case MCP3422_SRATE_60: > + mtime = 1000 / 60; > + break; > + case MCP3422_SRATE_15: > + mtime = 1000 / 15; > + break; > + case MCP3422_SRATE_3: > + mtime = 1000 / 3; > + break; > + } > + msleep(mtime); > + } > + > + return mcp3422_read(adc, value, &config); > +} > + > +static int mcp3422_read_raw(struct iio_dev *iio, > + struct iio_chan_spec const *channel, int *val1, > + int *val2, long mask) > +{ > + struct mcp3422 *adc = iio_priv(iio); > + int err, temp = 0; > + > + u8 sample_rate = MCP3422_SAMPLE_RATE(adc->config); > + u8 pga = MCP3422_PGA(adc->config); > + > + err = mcp3422_read_channel(adc, channel, &temp); > + if (err < 0) > + return err; > + > + switch (mask) { > + case IIO_CHAN_INFO_RAW: > + *val1 = temp; > + > + return IIO_VAL_INT; > + > + case IIO_CHAN_INFO_SCALE: > + > + temp = (temp * rates_to_lsb[sample_rate]) > + / (pga ? 1 << pga : 1); > + > + *val1 = temp / 1000000000; > + *val2 = temp % 1000000000; > + > + return IIO_VAL_INT_PLUS_NANO; > + > + default: > + break; > + } > + > + return -EINVAL; > +} > + > +static int mcp3422_write_raw(struct iio_dev *iio, > + struct iio_chan_spec const *channel, int val1, > + int val2, long mask) > +{ > + struct mcp3422 *adc = iio_priv(iio); > + u8 pga, config; > + u8 req_channel = channel->channel; > + > + switch (mask) { > + case IIO_CHAN_INFO_SCALE: > + switch (val1) { > + case 1: > + pga = MCP3422_PGA_1; > + break; > + case 2: > + pga = MCP3422_PGA_2; > + break; > + case 4: > + pga = MCP3422_PGA_4; > + break; > + case 8: > + pga = MCP3422_PGA_8; > + break; > + default: > + return -EINVAL; > + } > + mutex_lock(&adc->lock); > + > + config = adc->config; > + adc->pga[req_channel-1] = pga; > + > + mutex_unlock(&adc->lock); I don't understand the locking logic? what should be protected? > + > + config &= ~MCP3422_CHANNEL_MASK; > + config |= MCP3422_CHANNEL_VALUE(req_channel); > + config &= ~MCP3422_PGA_MASK; > + config |= MCP3422_PGA_VALUE(adc->pga[req_channel-1]); > + mcp3422_update_config(adc, config); > + return 0; > + default: > + break; > + } > + > + return -EINVAL; > +} > + > +static ssize_t mcp3422_set_sample_rate(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct i2c_client *client = to_i2c_client(dev); > + struct iio_dev *iio = i2c_get_clientdata(client); > + struct mcp3422 *adc = iio_priv(iio); > + u8 value = 0; no need to init value > + u8 config = adc->config; > + > + if (kstrtou8(buf, 10, &value) < 0) > + return -EINVAL; > + > + switch (value) { > + case 240: > + value = MCP3422_SRATE_240; > + break; > + case 60: > + value = MCP3422_SRATE_60; > + break; > + case 15: > + value = MCP3422_SRATE_15; > + break; > + case 3: > + value = MCP3422_SRATE_3; > + break; > + default: > + return -EINVAL; > + } > + > + config &= ~MCP3422_SRATE_MASK; > + config |= MCP3422_SAMPLE_RATE_VALUE(value); > + > + mcp3422_update_config(adc, config); > + > + return count; > +} > + > +static ssize_t mcp3422_get_sample_rate(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct i2c_client *client = to_i2c_client(dev); > + struct iio_dev *iio = i2c_get_clientdata(client); > + struct mcp3422 *adc = iio_priv(iio); > + u8 value = 0; > + > + switch (MCP3422_SAMPLE_RATE(adc->config)) { > + case MCP3422_SRATE_240: > + value = 240; > + break; > + case MCP3422_SRATE_60: > + value = 60; > + break; > + case MCP3422_SRATE_15: > + value = 15; > + break; > + case MCP3422_SRATE_3: > + value = 3; > + break; > + } > + > + return sprintf(buf, "%d\n", value); > +} > + > +static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR, > + mcp3422_get_sample_rate, > + mcp3422_set_sample_rate); > +static IIO_CONST_ATTR(sampling_frequency_available, "240 60 15 3"); sample rate should be exposed IIO_CHAN_INFO_SAMP_FREQ > +static IIO_CONST_ATTR(scale_available, "1 2 4 8"); > + > +static struct attribute *mcp3422_attributes[] = { > + &iio_dev_attr_sampling_frequency.dev_attr.attr, > + &iio_const_attr_sampling_frequency_available.dev_attr.attr, > + &iio_const_attr_scale_available.dev_attr.attr, > + NULL, > +}; > + > +static const struct attribute_group mcp3422_attribute_group = { > + .attrs = mcp3422_attributes, > +}; > + > +static const struct iio_chan_spec mcp3422_channels[] = { > + MCP3422_CHAN(1), channels are indexed starting from 0 generally > + MCP3422_CHAN(2), > +}; > + > +static const struct iio_chan_spec mcp3424_channels[] = { > + MCP3422_CHAN(1), > + MCP3422_CHAN(2), > + MCP3422_CHAN(3), > + MCP3422_CHAN(4), > +}; > + > +static const struct iio_info mcp3422_info = { > + .read_raw = mcp3422_read_raw, > + .write_raw = mcp3422_write_raw, > + .attrs = &mcp3422_attribute_group, > + .driver_module = THIS_MODULE, > +}; > + > +static int mcp3422_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct iio_dev *indio_dev; > + struct mcp3422 *adc; > + int err; > + u8 config; > + > + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) > + return -ENODEV; > + > + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*adc)); > + if (!indio_dev) > + return -ENOMEM; > + > + adc = iio_priv(indio_dev); > + adc->i2c = client; > + memset(&adc->pga, 0, 4); memset() not needed > + > + mutex_init(&adc->lock); > + > + indio_dev->dev.parent = &client->dev; > + indio_dev->name = dev_name(&client->dev); > + indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->info = &mcp3422_info; > + > + switch ((unsigned int)(id->driver_data)) { > + case 2: > + case 3: > + indio_dev->channels = mcp3422_channels; > + indio_dev->num_channels = ARRAY_SIZE(mcp3422_channels); > + break; > + case 4: > + indio_dev->channels = mcp3424_channels; > + indio_dev->num_channels = ARRAY_SIZE(mcp3424_channels); > + break; > + } > + > + err = iio_device_register(indio_dev); > + if (err < 0) > + return err; > + > + i2c_set_clientdata(client, indio_dev); > + > + /* meaningful default configuration */ > + config = (MCP3422_CONT_SAMPLING > + | MCP3422_CHANNEL_VALUE(1) > + | MCP3422_PGA_VALUE(MCP3422_PGA_1) > + | MCP3422_SAMPLE_RATE_VALUE(MCP3422_SRATE_240)); > + mcp3422_update_config(adc, config); move this before register() to avoid a race window? > + > + return 0; > +} > + > +static int mcp3422_remove(struct i2c_client *client) > +{ > + struct iio_dev *iio = i2c_get_clientdata(client); > + > + iio_device_unregister(iio); > + iio_device_free(iio); > + > + return 0; > +} > + > +static const struct i2c_device_id mcp3422_id[] = { > + { "mcp3422", 2 }, > + { "mcp3423", 3 }, > + { "mcp3424", 4 }, > + { } > +}; > +MODULE_DEVICE_TABLE(i2c, mcp3422_id); > + > +#ifdef CONFIG_OF > +static const struct of_device_id mcp3422_of_match[] = { > + { .compatible = "mcp3422" }, > + { } > +}; > +MODULE_DEVICE_TABLE(of, mcp3422_of_match); > +#endif > + > +static struct i2c_driver mcp3422_driver = { > + .driver = { > + .name = "mcp3422", > + .owner = THIS_MODULE, > + .of_match_table = of_match_ptr(mcp3422_of_match), > + }, > + .probe = mcp3422_probe, > + .remove = mcp3422_remove, > + .id_table = mcp3422_id, > +}; > +module_i2c_driver(mcp3422_driver); > + > +MODULE_AUTHOR("Angelo Compagnucci <angelo.compagnucci@xxxxxxxxx>"); > +MODULE_DESCRIPTION("Microchip mcp3422/3/4 driver"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald +43-664-2444418 (mobile) -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html