Add Microchip MCP3422/3/4 high resolution ADC
PATCH v5 1/1 Add Microchip MCP3422/3/4 high resolution ADC
This is the fifth interation of the patch.
It removes PGA attribute and maps it to the write_raw function for each channel. PGAs are stored in a mutex protected array and restored in configuration if the channel changes.
I think this patch is more or less in it's final shape, please review it.
Thank You.
Angelo.
--- 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
drivers/iio/adc/Kconfig +10 -0
@@ -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
drivers/iio/adc/Makefile +1 -0
@@ -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
drivers/iio/adc/mcp3422.c new file +432 -0
432 lines not on this page · raw message
Signature · 1 line
1.7.9.5
Quoted 2 lines
I think this patch is more or less in it's final shape, please review it.
comments inline
Quoted 258 lines
--- 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.cdrivers/iio/adc/Kconfig +10
-0@@ -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 I2Cdrivers/iio/adc/Makefile +1
-0@@ -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.odrivers/iio/adc/mcp3422.c new file +212
-0@@ -0,0 +1,432 @@ +/* +* mcp3422.c - driver for the Microchip mcp3422/3/4 chip family +* +* Copyright (C) 2013, Angelo Compagnucci +* Author: Angelo Compagnucci <[email protected]> +* +* 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 ;
drivers/iio/adc/mcp3422.c line 222
Quoted 10 lines
drivers/iio/adc/mcp3422.c new file +10
-0+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?
Quoted 12 lines
drivers/iio/adc/mcp3422.c new file +12
-0+ + 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 ;
drivers/iio/adc/mcp3422.c line 244
Quoted 10 lines
drivers/iio/adc/mcp3422.c new file +10
-0+} + +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
Quoted 51 lines
drivers/iio/adc/mcp3422.c new file +51
-0+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 ;
drivers/iio/adc/mcp3422.c line 305
Quoted 10 lines
drivers/iio/adc/mcp3422.c new file +10
-0+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
Quoted 5 lines
drivers/iio/adc/mcp3422.c new file +5
-0+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,
drivers/iio/adc/mcp3422.c line 320
Quoted 10 lines
drivers/iio/adc/mcp3422.c new file +10
-0+ &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
Quoted 25 lines
drivers/iio/adc/mcp3422.c new file +25
-0+ 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; +
drivers/iio/adc/mcp3422.c line 355
Quoted 10 lines
drivers/iio/adc/mcp3422.c new file +10
-0+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
Quoted 22 lines
drivers/iio/adc/mcp3422.c new file +22
-0+ +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 )
drivers/iio/adc/mcp3422.c line 387
Quoted 10 lines
drivers/iio/adc/mcp3422.c new file +10
-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?
Quoted 46 lines
drivers/iio/adc/mcp3422.c new file +45
-0+ +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 <[email protected]>" ); +MODULE_DESCRIPTION ("Microchip mcp3422/3/4 driver" ); +MODULE_LICENSE ("GPL v2" );
Signature · 2 lines
Peter Meerwald +43-664-2444418 (mobile)