Commit b12206e9 authored by Søren Andersen's avatar Søren Andersen Committed by Jonathan Cameron

iio: adc: mcp320x. Add support for more ADCs

Signed-off-by: Soeren Andersen <san at rosetechnology.dk>
Signed-off-by: default avatarJonathan Cameron <jic23@kernel.org>
parent 064a7463
/* /*
* Copyright (C) 2013 Oskar Andero <oskar.andero@gmail.com> * Copyright (C) 2013 Oskar Andero <oskar.andero@gmail.com>
* Copyright (C) 2014 Rose Technology
* Allan Bendorff Jensen <abj@rosetechnology.dk>
* Soren Andersen <san@rosetechnology.dk>
*
* Driver for following ADC chips from Microchip Technology's:
* 10 Bit converter
* MCP3001
* MCP3002
* MCP3004
* MCP3008
* ------------
* 12 bit converter
* MCP3201
* MCP3202
* MCP3204
* MCP3208
* ------------
* *
* Driver for Microchip Technology's MCP3204 and MCP3208 ADC chips.
* Datasheet can be found here: * Datasheet can be found here:
* http://ww1.microchip.com/downloads/en/devicedoc/21298c.pdf * http://ww1.microchip.com/downloads/en/DeviceDoc/21293C.pdf mcp3001
* http://ww1.microchip.com/downloads/en/DeviceDoc/21294E.pdf mcp3002
* http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf mcp3004/08
* http://ww1.microchip.com/downloads/en/DeviceDoc/21290D.pdf mcp3201
* http://ww1.microchip.com/downloads/en/DeviceDoc/21034D.pdf mcp3202
* http://ww1.microchip.com/downloads/en/DeviceDoc/21298c.pdf mcp3204/08
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
...@@ -11,19 +32,29 @@ ...@@ -11,19 +32,29 @@
*/ */
#include <linux/err.h> #include <linux/err.h>
#include <linux/delay.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/iio/iio.h> #include <linux/iio/iio.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#define MCP_SINGLE_ENDED (1 << 3)
#define MCP_START_BIT (1 << 4)
enum { enum {
mcp3001,
mcp3002,
mcp3004,
mcp3008,
mcp3201,
mcp3202,
mcp3204, mcp3204,
mcp3208, mcp3208,
}; };
struct mcp320x_chip_info {
const struct iio_chan_spec *channels;
unsigned int num_channels;
unsigned int resolution;
};
struct mcp320x { struct mcp320x {
struct spi_device *spi; struct spi_device *spi;
struct spi_message msg; struct spi_message msg;
...@@ -34,19 +65,69 @@ struct mcp320x { ...@@ -34,19 +65,69 @@ struct mcp320x {
struct regulator *reg; struct regulator *reg;
struct mutex lock; struct mutex lock;
const struct mcp320x_chip_info *chip_info;
}; };
static int mcp320x_adc_conversion(struct mcp320x *adc, u8 msg) static int mcp320x_channel_to_tx_data(int device_index,
const unsigned int channel, bool differential)
{
int start_bit = 1;
switch (device_index) {
case mcp3001:
case mcp3201:
return 0;
case mcp3002:
case mcp3202:
return ((start_bit << 4) | (!differential << 3) |
(channel << 2));
case mcp3004:
case mcp3204:
case mcp3008:
case mcp3208:
return ((start_bit << 6) | (!differential << 5) |
(channel << 2));
default:
return -EINVAL;
}
}
static int mcp320x_adc_conversion(struct mcp320x *adc, u8 channel,
bool differential, int device_index)
{ {
int ret; int ret;
adc->tx_buf = msg; adc->rx_buf[0] = 0;
ret = spi_sync(adc->spi, &adc->msg); adc->rx_buf[1] = 0;
if (ret < 0) adc->tx_buf = mcp320x_channel_to_tx_data(device_index,
return ret; channel, differential);
if (device_index != mcp3001 && device_index != mcp3201) {
ret = spi_sync(adc->spi, &adc->msg);
if (ret < 0)
return ret;
} else {
ret = spi_read(adc->spi, &adc->rx_buf, sizeof(adc->rx_buf));
if (ret < 0)
return ret;
}
return ((adc->rx_buf[0] & 0x3f) << 6) | switch (device_index) {
(adc->rx_buf[1] >> 2); case mcp3001:
return (adc->rx_buf[0] << 5 | adc->rx_buf[1] >> 3);
case mcp3002:
case mcp3004:
case mcp3008:
return (adc->rx_buf[0] << 2 | adc->rx_buf[1] >> 6);
case mcp3201:
return (adc->rx_buf[0] << 7 | adc->rx_buf[1] >> 1);
case mcp3202:
case mcp3204:
case mcp3208:
return (adc->rx_buf[0] << 4 | adc->rx_buf[1] >> 4);
default:
return -EINVAL;
}
} }
static int mcp320x_read_raw(struct iio_dev *indio_dev, static int mcp320x_read_raw(struct iio_dev *indio_dev,
...@@ -55,18 +136,17 @@ static int mcp320x_read_raw(struct iio_dev *indio_dev, ...@@ -55,18 +136,17 @@ static int mcp320x_read_raw(struct iio_dev *indio_dev,
{ {
struct mcp320x *adc = iio_priv(indio_dev); struct mcp320x *adc = iio_priv(indio_dev);
int ret = -EINVAL; int ret = -EINVAL;
int device_index = 0;
mutex_lock(&adc->lock); mutex_lock(&adc->lock);
device_index = spi_get_device_id(adc->spi)->driver_data;
switch (mask) { switch (mask) {
case IIO_CHAN_INFO_RAW: case IIO_CHAN_INFO_RAW:
if (channel->differential) ret = mcp320x_adc_conversion(adc, channel->address,
ret = mcp320x_adc_conversion(adc, channel->differential, device_index);
MCP_START_BIT | channel->address);
else
ret = mcp320x_adc_conversion(adc,
MCP_START_BIT | MCP_SINGLE_ENDED |
channel->address);
if (ret < 0) if (ret < 0)
goto out; goto out;
...@@ -75,18 +155,15 @@ static int mcp320x_read_raw(struct iio_dev *indio_dev, ...@@ -75,18 +155,15 @@ static int mcp320x_read_raw(struct iio_dev *indio_dev,
break; break;
case IIO_CHAN_INFO_SCALE: case IIO_CHAN_INFO_SCALE:
/* Digital output code = (4096 * Vin) / Vref */
ret = regulator_get_voltage(adc->reg); ret = regulator_get_voltage(adc->reg);
if (ret < 0) if (ret < 0)
goto out; goto out;
/* convert regulator output voltage to mV */
*val = ret / 1000; *val = ret / 1000;
*val2 = 12; *val2 = adc->chip_info->resolution;
ret = IIO_VAL_FRACTIONAL_LOG2; ret = IIO_VAL_FRACTIONAL_LOG2;
break; break;
default:
break;
} }
out: out:
...@@ -117,6 +194,16 @@ static int mcp320x_read_raw(struct iio_dev *indio_dev, ...@@ -117,6 +194,16 @@ static int mcp320x_read_raw(struct iio_dev *indio_dev,
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
} }
static const struct iio_chan_spec mcp3201_channels[] = {
MCP320X_VOLTAGE_CHANNEL_DIFF(0),
};
static const struct iio_chan_spec mcp3202_channels[] = {
MCP320X_VOLTAGE_CHANNEL(0),
MCP320X_VOLTAGE_CHANNEL(1),
MCP320X_VOLTAGE_CHANNEL_DIFF(0),
};
static const struct iio_chan_spec mcp3204_channels[] = { static const struct iio_chan_spec mcp3204_channels[] = {
MCP320X_VOLTAGE_CHANNEL(0), MCP320X_VOLTAGE_CHANNEL(0),
MCP320X_VOLTAGE_CHANNEL(1), MCP320X_VOLTAGE_CHANNEL(1),
...@@ -146,19 +233,46 @@ static const struct iio_info mcp320x_info = { ...@@ -146,19 +233,46 @@ static const struct iio_info mcp320x_info = {
.driver_module = THIS_MODULE, .driver_module = THIS_MODULE,
}; };
struct mcp3208_chip_info { static const struct mcp320x_chip_info mcp320x_chip_infos[] = {
const struct iio_chan_spec *channels; [mcp3001] = {
unsigned int num_channels; .channels = mcp3201_channels,
}; .num_channels = ARRAY_SIZE(mcp3201_channels),
.resolution = 10
static const struct mcp3208_chip_info mcp3208_chip_infos[] = { },
[mcp3002] = {
.channels = mcp3202_channels,
.num_channels = ARRAY_SIZE(mcp3202_channels),
.resolution = 10
},
[mcp3004] = {
.channels = mcp3204_channels,
.num_channels = ARRAY_SIZE(mcp3204_channels),
.resolution = 10
},
[mcp3008] = {
.channels = mcp3208_channels,
.num_channels = ARRAY_SIZE(mcp3208_channels),
.resolution = 10
},
[mcp3201] = {
.channels = mcp3201_channels,
.num_channels = ARRAY_SIZE(mcp3201_channels),
.resolution = 12
},
[mcp3202] = {
.channels = mcp3202_channels,
.num_channels = ARRAY_SIZE(mcp3202_channels),
.resolution = 12
},
[mcp3204] = { [mcp3204] = {
.channels = mcp3204_channels, .channels = mcp3204_channels,
.num_channels = ARRAY_SIZE(mcp3204_channels) .num_channels = ARRAY_SIZE(mcp3204_channels),
.resolution = 12
}, },
[mcp3208] = { [mcp3208] = {
.channels = mcp3208_channels, .channels = mcp3208_channels,
.num_channels = ARRAY_SIZE(mcp3208_channels) .num_channels = ARRAY_SIZE(mcp3208_channels),
.resolution = 12
}, },
}; };
...@@ -166,7 +280,7 @@ static int mcp320x_probe(struct spi_device *spi) ...@@ -166,7 +280,7 @@ static int mcp320x_probe(struct spi_device *spi)
{ {
struct iio_dev *indio_dev; struct iio_dev *indio_dev;
struct mcp320x *adc; struct mcp320x *adc;
const struct mcp3208_chip_info *chip_info; const struct mcp320x_chip_info *chip_info;
int ret; int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
...@@ -181,7 +295,7 @@ static int mcp320x_probe(struct spi_device *spi) ...@@ -181,7 +295,7 @@ static int mcp320x_probe(struct spi_device *spi)
indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &mcp320x_info; indio_dev->info = &mcp320x_info;
chip_info = &mcp3208_chip_infos[spi_get_device_id(spi)->driver_data]; chip_info = &mcp320x_chip_infos[spi_get_device_id(spi)->driver_data];
indio_dev->channels = chip_info->channels; indio_dev->channels = chip_info->channels;
indio_dev->num_channels = chip_info->num_channels; indio_dev->num_channels = chip_info->num_channels;
...@@ -226,7 +340,45 @@ static int mcp320x_remove(struct spi_device *spi) ...@@ -226,7 +340,45 @@ static int mcp320x_remove(struct spi_device *spi)
return 0; return 0;
} }
#if defined(CONFIG_OF)
static const struct of_device_id mcp320x_dt_ids[] = {
{
.compatible = "mcp3001",
.data = &mcp320x_chip_infos[mcp3001],
}, {
.compatible = "mcp3002",
.data = &mcp320x_chip_infos[mcp3002],
}, {
.compatible = "mcp3004",
.data = &mcp320x_chip_infos[mcp3004],
}, {
.compatible = "mcp3008",
.data = &mcp320x_chip_infos[mcp3008],
}, {
.compatible = "mcp3201",
.data = &mcp320x_chip_infos[mcp3201],
}, {
.compatible = "mcp3202",
.data = &mcp320x_chip_infos[mcp3202],
}, {
.compatible = "mcp3204",
.data = &mcp320x_chip_infos[mcp3204],
}, {
.compatible = "mcp3208",
.data = &mcp320x_chip_infos[mcp3208],
}, {
}
};
MODULE_DEVICE_TABLE(of, mcp320x_dt_ids);
#endif
static const struct spi_device_id mcp320x_id[] = { static const struct spi_device_id mcp320x_id[] = {
{ "mcp3001", mcp3001 },
{ "mcp3002", mcp3002 },
{ "mcp3004", mcp3004 },
{ "mcp3008", mcp3008 },
{ "mcp3201", mcp3201 },
{ "mcp3202", mcp3202 },
{ "mcp3204", mcp3204 }, { "mcp3204", mcp3204 },
{ "mcp3208", mcp3208 }, { "mcp3208", mcp3208 },
{ } { }
...@@ -245,5 +397,5 @@ static struct spi_driver mcp320x_driver = { ...@@ -245,5 +397,5 @@ static struct spi_driver mcp320x_driver = {
module_spi_driver(mcp320x_driver); module_spi_driver(mcp320x_driver);
MODULE_AUTHOR("Oskar Andero <oskar.andero@gmail.com>"); MODULE_AUTHOR("Oskar Andero <oskar.andero@gmail.com>");
MODULE_DESCRIPTION("Microchip Technology MCP3204/08"); MODULE_DESCRIPTION("Microchip Technology MCP3x01/02/04/08");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment