linux/drivers/iio/adc/mcp320x.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2013 Oskar Andero <oskar.andero@gmail.com>
   3 *
   4 * Driver for Microchip Technology's MCP3204 and MCP3208 ADC chips.
   5 * Datasheet can be found here:
   6 * http://ww1.microchip.com/downloads/en/devicedoc/21298c.pdf
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 */
  12
  13#include <linux/err.h>
  14#include <linux/spi/spi.h>
  15#include <linux/module.h>
  16#include <linux/iio/iio.h>
  17#include <linux/regulator/consumer.h>
  18
  19#define MCP_SINGLE_ENDED        (1 << 3)
  20#define MCP_START_BIT           (1 << 4)
  21
  22enum {
  23        mcp3204,
  24        mcp3208,
  25};
  26
  27struct mcp320x {
  28        struct spi_device *spi;
  29        struct spi_message msg;
  30        struct spi_transfer transfer[2];
  31
  32        u8 tx_buf;
  33        u8 rx_buf[2];
  34
  35        struct regulator *reg;
  36        struct mutex lock;
  37};
  38
  39static int mcp320x_adc_conversion(struct mcp320x *adc, u8 msg)
  40{
  41        int ret;
  42
  43        adc->tx_buf = msg;
  44        ret = spi_sync(adc->spi, &adc->msg);
  45        if (ret < 0)
  46                return ret;
  47
  48        return ((adc->rx_buf[0] & 0x3f) << 6)  |
  49                (adc->rx_buf[1] >> 2);
  50}
  51
  52static int mcp320x_read_raw(struct iio_dev *indio_dev,
  53                            struct iio_chan_spec const *channel, int *val,
  54                            int *val2, long mask)
  55{
  56        struct mcp320x *adc = iio_priv(indio_dev);
  57        int ret = -EINVAL;
  58
  59        mutex_lock(&adc->lock);
  60
  61        switch (mask) {
  62        case IIO_CHAN_INFO_RAW:
  63                if (channel->differential)
  64                        ret = mcp320x_adc_conversion(adc,
  65                                MCP_START_BIT | channel->address);
  66                else
  67                        ret = mcp320x_adc_conversion(adc,
  68                                MCP_START_BIT | MCP_SINGLE_ENDED |
  69                                channel->address);
  70                if (ret < 0)
  71                        goto out;
  72
  73                *val = ret;
  74                ret = IIO_VAL_INT;
  75                break;
  76
  77        case IIO_CHAN_INFO_SCALE:
  78                /* Digital output code = (4096 * Vin) / Vref */
  79                ret = regulator_get_voltage(adc->reg);
  80                if (ret < 0)
  81                        goto out;
  82
  83                *val = ret / 1000;
  84                *val2 = 12;
  85                ret = IIO_VAL_FRACTIONAL_LOG2;
  86                break;
  87
  88        default:
  89                break;
  90        }
  91
  92out:
  93        mutex_unlock(&adc->lock);
  94
  95        return ret;
  96}
  97
  98#define MCP320X_VOLTAGE_CHANNEL(num)                            \
  99        {                                                       \
 100                .type = IIO_VOLTAGE,                            \
 101                .indexed = 1,                                   \
 102                .channel = (num),                               \
 103                .address = (num),                               \
 104                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),   \
 105                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
 106        }
 107
 108#define MCP320X_VOLTAGE_CHANNEL_DIFF(num)                       \
 109        {                                                       \
 110                .type = IIO_VOLTAGE,                            \
 111                .indexed = 1,                                   \
 112                .channel = (num * 2),                           \
 113                .channel2 = (num * 2 + 1),                      \
 114                .address = (num * 2),                           \
 115                .differential = 1,                              \
 116                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),   \
 117                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
 118        }
 119
 120static const struct iio_chan_spec mcp3204_channels[] = {
 121        MCP320X_VOLTAGE_CHANNEL(0),
 122        MCP320X_VOLTAGE_CHANNEL(1),
 123        MCP320X_VOLTAGE_CHANNEL(2),
 124        MCP320X_VOLTAGE_CHANNEL(3),
 125        MCP320X_VOLTAGE_CHANNEL_DIFF(0),
 126        MCP320X_VOLTAGE_CHANNEL_DIFF(1),
 127};
 128
 129static const struct iio_chan_spec mcp3208_channels[] = {
 130        MCP320X_VOLTAGE_CHANNEL(0),
 131        MCP320X_VOLTAGE_CHANNEL(1),
 132        MCP320X_VOLTAGE_CHANNEL(2),
 133        MCP320X_VOLTAGE_CHANNEL(3),
 134        MCP320X_VOLTAGE_CHANNEL(4),
 135        MCP320X_VOLTAGE_CHANNEL(5),
 136        MCP320X_VOLTAGE_CHANNEL(6),
 137        MCP320X_VOLTAGE_CHANNEL(7),
 138        MCP320X_VOLTAGE_CHANNEL_DIFF(0),
 139        MCP320X_VOLTAGE_CHANNEL_DIFF(1),
 140        MCP320X_VOLTAGE_CHANNEL_DIFF(2),
 141        MCP320X_VOLTAGE_CHANNEL_DIFF(3),
 142};
 143
 144static const struct iio_info mcp320x_info = {
 145        .read_raw = mcp320x_read_raw,
 146        .driver_module = THIS_MODULE,
 147};
 148
 149struct mcp3208_chip_info {
 150        const struct iio_chan_spec *channels;
 151        unsigned int num_channels;
 152};
 153
 154static const struct mcp3208_chip_info mcp3208_chip_infos[] = {
 155        [mcp3204] = {
 156                .channels = mcp3204_channels,
 157                .num_channels = ARRAY_SIZE(mcp3204_channels)
 158        },
 159        [mcp3208] = {
 160                .channels = mcp3208_channels,
 161                .num_channels = ARRAY_SIZE(mcp3208_channels)
 162        },
 163};
 164
 165static int mcp320x_probe(struct spi_device *spi)
 166{
 167        struct iio_dev *indio_dev;
 168        struct mcp320x *adc;
 169        const struct mcp3208_chip_info *chip_info;
 170        int ret;
 171
 172        indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
 173        if (!indio_dev)
 174                return -ENOMEM;
 175
 176        adc = iio_priv(indio_dev);
 177        adc->spi = spi;
 178
 179        indio_dev->dev.parent = &spi->dev;
 180        indio_dev->name = spi_get_device_id(spi)->name;
 181        indio_dev->modes = INDIO_DIRECT_MODE;
 182        indio_dev->info = &mcp320x_info;
 183
 184        chip_info = &mcp3208_chip_infos[spi_get_device_id(spi)->driver_data];
 185        indio_dev->channels = chip_info->channels;
 186        indio_dev->num_channels = chip_info->num_channels;
 187
 188        adc->transfer[0].tx_buf = &adc->tx_buf;
 189        adc->transfer[0].len = sizeof(adc->tx_buf);
 190        adc->transfer[1].rx_buf = adc->rx_buf;
 191        adc->transfer[1].len = sizeof(adc->rx_buf);
 192
 193        spi_message_init_with_transfers(&adc->msg, adc->transfer,
 194                                        ARRAY_SIZE(adc->transfer));
 195
 196        adc->reg = devm_regulator_get(&spi->dev, "vref");
 197        if (IS_ERR(adc->reg))
 198                return PTR_ERR(adc->reg);
 199
 200        ret = regulator_enable(adc->reg);
 201        if (ret < 0)
 202                return ret;
 203
 204        mutex_init(&adc->lock);
 205
 206        ret = iio_device_register(indio_dev);
 207        if (ret < 0)
 208                goto reg_disable;
 209
 210        return 0;
 211
 212reg_disable:
 213        regulator_disable(adc->reg);
 214
 215        return ret;
 216}
 217
 218static int mcp320x_remove(struct spi_device *spi)
 219{
 220        struct iio_dev *indio_dev = spi_get_drvdata(spi);
 221        struct mcp320x *adc = iio_priv(indio_dev);
 222
 223        iio_device_unregister(indio_dev);
 224        regulator_disable(adc->reg);
 225
 226        return 0;
 227}
 228
 229static const struct spi_device_id mcp320x_id[] = {
 230        { "mcp3204", mcp3204 },
 231        { "mcp3208", mcp3208 },
 232        { }
 233};
 234MODULE_DEVICE_TABLE(spi, mcp320x_id);
 235
 236static struct spi_driver mcp320x_driver = {
 237        .driver = {
 238                .name = "mcp320x",
 239                .owner = THIS_MODULE,
 240        },
 241        .probe = mcp320x_probe,
 242        .remove = mcp320x_remove,
 243        .id_table = mcp320x_id,
 244};
 245module_spi_driver(mcp320x_driver);
 246
 247MODULE_AUTHOR("Oskar Andero <oskar.andero@gmail.com>");
 248MODULE_DESCRIPTION("Microchip Technology MCP3204/08");
 249MODULE_LICENSE("GPL v2");
 250