linux/drivers/iio/adc/mcp3911.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Driver for Microchip MCP3911, Two-channel Analog Front End
   4 *
   5 * Copyright (C) 2018 Marcus Folkesson <marcus.folkesson@gmail.com>
   6 * Copyright (C) 2018 Kent Gustavsson <kent@minoris.se>
   7 */
   8#include <linux/clk.h>
   9#include <linux/delay.h>
  10#include <linux/err.h>
  11#include <linux/iio/iio.h>
  12#include <linux/module.h>
  13#include <linux/regulator/consumer.h>
  14#include <linux/spi/spi.h>
  15
  16#define MCP3911_REG_CHANNEL0            0x00
  17#define MCP3911_REG_CHANNEL1            0x03
  18#define MCP3911_REG_MOD                 0x06
  19#define MCP3911_REG_PHASE               0x07
  20#define MCP3911_REG_GAIN                0x09
  21
  22#define MCP3911_REG_STATUSCOM           0x0a
  23#define MCP3911_STATUSCOM_CH1_24WIDTH   BIT(4)
  24#define MCP3911_STATUSCOM_CH0_24WIDTH   BIT(3)
  25#define MCP3911_STATUSCOM_EN_OFFCAL     BIT(2)
  26#define MCP3911_STATUSCOM_EN_GAINCAL    BIT(1)
  27
  28#define MCP3911_REG_CONFIG              0x0c
  29#define MCP3911_CONFIG_CLKEXT           BIT(1)
  30#define MCP3911_CONFIG_VREFEXT          BIT(2)
  31
  32#define MCP3911_REG_OFFCAL_CH0          0x0e
  33#define MCP3911_REG_GAINCAL_CH0         0x11
  34#define MCP3911_REG_OFFCAL_CH1          0x14
  35#define MCP3911_REG_GAINCAL_CH1         0x17
  36#define MCP3911_REG_VREFCAL             0x1a
  37
  38#define MCP3911_CHANNEL(x)              (MCP3911_REG_CHANNEL0 + x * 3)
  39#define MCP3911_OFFCAL(x)               (MCP3911_REG_OFFCAL_CH0 + x * 6)
  40
  41/* Internal voltage reference in uV */
  42#define MCP3911_INT_VREF_UV             1200000
  43
  44#define MCP3911_REG_READ(reg, id)       ((((reg) << 1) | ((id) << 5) | (1 << 0)) & 0xff)
  45#define MCP3911_REG_WRITE(reg, id)      ((((reg) << 1) | ((id) << 5) | (0 << 0)) & 0xff)
  46
  47#define MCP3911_NUM_CHANNELS            2
  48
  49struct mcp3911 {
  50        struct spi_device *spi;
  51        struct mutex lock;
  52        struct regulator *vref;
  53        struct clk *clki;
  54        u32 dev_addr;
  55};
  56
  57static int mcp3911_read(struct mcp3911 *adc, u8 reg, u32 *val, u8 len)
  58{
  59        int ret;
  60
  61        reg = MCP3911_REG_READ(reg, adc->dev_addr);
  62        ret = spi_write_then_read(adc->spi, &reg, 1, val, len);
  63        if (ret < 0)
  64                return ret;
  65
  66        be32_to_cpus(val);
  67        *val >>= ((4 - len) * 8);
  68        dev_dbg(&adc->spi->dev, "reading 0x%x from register 0x%x\n", *val,
  69                reg >> 1);
  70        return ret;
  71}
  72
  73static int mcp3911_write(struct mcp3911 *adc, u8 reg, u32 val, u8 len)
  74{
  75        dev_dbg(&adc->spi->dev, "writing 0x%x to register 0x%x\n", val, reg);
  76
  77        val <<= (3 - len) * 8;
  78        cpu_to_be32s(&val);
  79        val |= MCP3911_REG_WRITE(reg, adc->dev_addr);
  80
  81        return spi_write(adc->spi, &val, len + 1);
  82}
  83
  84static int mcp3911_update(struct mcp3911 *adc, u8 reg, u32 mask,
  85                u32 val, u8 len)
  86{
  87        u32 tmp;
  88        int ret;
  89
  90        ret = mcp3911_read(adc, reg, &tmp, len);
  91        if (ret)
  92                return ret;
  93
  94        val &= mask;
  95        val |= tmp & ~mask;
  96        return mcp3911_write(adc, reg, val, len);
  97}
  98
  99static int mcp3911_read_raw(struct iio_dev *indio_dev,
 100                            struct iio_chan_spec const *channel, int *val,
 101                            int *val2, long mask)
 102{
 103        struct mcp3911 *adc = iio_priv(indio_dev);
 104        int ret = -EINVAL;
 105
 106        mutex_lock(&adc->lock);
 107        switch (mask) {
 108        case IIO_CHAN_INFO_RAW:
 109                ret = mcp3911_read(adc,
 110                                   MCP3911_CHANNEL(channel->channel), val, 3);
 111                if (ret)
 112                        goto out;
 113
 114                ret = IIO_VAL_INT;
 115                break;
 116
 117        case IIO_CHAN_INFO_OFFSET:
 118                ret = mcp3911_read(adc,
 119                                   MCP3911_OFFCAL(channel->channel), val, 3);
 120                if (ret)
 121                        goto out;
 122
 123                ret = IIO_VAL_INT;
 124                break;
 125
 126        case IIO_CHAN_INFO_SCALE:
 127                if (adc->vref) {
 128                        ret = regulator_get_voltage(adc->vref);
 129                        if (ret < 0) {
 130                                dev_err(indio_dev->dev.parent,
 131                                        "failed to get vref voltage: %d\n",
 132                                       ret);
 133                                goto out;
 134                        }
 135
 136                        *val = ret / 1000;
 137                } else {
 138                        *val = MCP3911_INT_VREF_UV;
 139                }
 140
 141                *val2 = 24;
 142                ret = IIO_VAL_FRACTIONAL_LOG2;
 143                break;
 144        }
 145
 146out:
 147        mutex_unlock(&adc->lock);
 148        return ret;
 149}
 150
 151static int mcp3911_write_raw(struct iio_dev *indio_dev,
 152                            struct iio_chan_spec const *channel, int val,
 153                            int val2, long mask)
 154{
 155        struct mcp3911 *adc = iio_priv(indio_dev);
 156        int ret = -EINVAL;
 157
 158        mutex_lock(&adc->lock);
 159        switch (mask) {
 160        case IIO_CHAN_INFO_OFFSET:
 161                if (val2 != 0) {
 162                        ret = -EINVAL;
 163                        goto out;
 164                }
 165
 166                /* Write offset */
 167                ret = mcp3911_write(adc, MCP3911_OFFCAL(channel->channel), val,
 168                                    3);
 169                if (ret)
 170                        goto out;
 171
 172                /* Enable offset*/
 173                ret = mcp3911_update(adc, MCP3911_REG_STATUSCOM,
 174                                MCP3911_STATUSCOM_EN_OFFCAL,
 175                                MCP3911_STATUSCOM_EN_OFFCAL, 2);
 176                break;
 177        }
 178
 179out:
 180        mutex_unlock(&adc->lock);
 181        return ret;
 182}
 183
 184#define MCP3911_CHAN(idx) {                                     \
 185                .type = IIO_VOLTAGE,                            \
 186                .indexed = 1,                                   \
 187                .channel = idx,                                 \
 188                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
 189                        BIT(IIO_CHAN_INFO_OFFSET) |             \
 190                        BIT(IIO_CHAN_INFO_SCALE),               \
 191}
 192
 193static const struct iio_chan_spec mcp3911_channels[] = {
 194        MCP3911_CHAN(0),
 195        MCP3911_CHAN(1),
 196};
 197
 198static const struct iio_info mcp3911_info = {
 199        .read_raw = mcp3911_read_raw,
 200        .write_raw = mcp3911_write_raw,
 201};
 202
 203static int mcp3911_config(struct mcp3911 *adc, struct device_node *of_node)
 204{
 205        u32 configreg;
 206        int ret;
 207
 208        of_property_read_u32(of_node, "device-addr", &adc->dev_addr);
 209        if (adc->dev_addr > 3) {
 210                dev_err(&adc->spi->dev,
 211                        "invalid device address (%i). Must be in range 0-3.\n",
 212                        adc->dev_addr);
 213                return -EINVAL;
 214        }
 215        dev_dbg(&adc->spi->dev, "use device address %i\n", adc->dev_addr);
 216
 217        ret = mcp3911_read(adc, MCP3911_REG_CONFIG, &configreg, 2);
 218        if (ret)
 219                return ret;
 220
 221        if (adc->vref) {
 222                dev_dbg(&adc->spi->dev, "use external voltage reference\n");
 223                configreg |= MCP3911_CONFIG_VREFEXT;
 224        } else {
 225                dev_dbg(&adc->spi->dev,
 226                        "use internal voltage reference (1.2V)\n");
 227                configreg &= ~MCP3911_CONFIG_VREFEXT;
 228        }
 229
 230        if (adc->clki) {
 231                dev_dbg(&adc->spi->dev, "use external clock as clocksource\n");
 232                configreg |= MCP3911_CONFIG_CLKEXT;
 233        } else {
 234                dev_dbg(&adc->spi->dev,
 235                        "use crystal oscillator as clocksource\n");
 236                configreg &= ~MCP3911_CONFIG_CLKEXT;
 237        }
 238
 239        return  mcp3911_write(adc, MCP3911_REG_CONFIG, configreg, 2);
 240}
 241
 242static int mcp3911_probe(struct spi_device *spi)
 243{
 244        struct iio_dev *indio_dev;
 245        struct mcp3911 *adc;
 246        int ret;
 247
 248        indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
 249        if (!indio_dev)
 250                return -ENOMEM;
 251
 252        adc = iio_priv(indio_dev);
 253        adc->spi = spi;
 254
 255        adc->vref = devm_regulator_get_optional(&adc->spi->dev, "vref");
 256        if (IS_ERR(adc->vref)) {
 257                if (PTR_ERR(adc->vref) == -ENODEV) {
 258                        adc->vref = NULL;
 259                } else {
 260                        dev_err(&adc->spi->dev,
 261                                "failed to get regulator (%ld)\n",
 262                                PTR_ERR(adc->vref));
 263                        return PTR_ERR(adc->vref);
 264                }
 265
 266        } else {
 267                ret = regulator_enable(adc->vref);
 268                if (ret)
 269                        return ret;
 270        }
 271
 272        adc->clki = devm_clk_get(&adc->spi->dev, NULL);
 273        if (IS_ERR(adc->clki)) {
 274                if (PTR_ERR(adc->clki) == -ENOENT) {
 275                        adc->clki = NULL;
 276                } else {
 277                        dev_err(&adc->spi->dev,
 278                                "failed to get adc clk (%ld)\n",
 279                                PTR_ERR(adc->clki));
 280                        ret = PTR_ERR(adc->clki);
 281                        goto reg_disable;
 282                }
 283        } else {
 284                ret = clk_prepare_enable(adc->clki);
 285                if (ret < 0) {
 286                        dev_err(&adc->spi->dev,
 287                                "Failed to enable clki: %d\n", ret);
 288                        goto reg_disable;
 289                }
 290        }
 291
 292        ret = mcp3911_config(adc, spi->dev.of_node);
 293        if (ret)
 294                goto clk_disable;
 295
 296        indio_dev->name = spi_get_device_id(spi)->name;
 297        indio_dev->modes = INDIO_DIRECT_MODE;
 298        indio_dev->info = &mcp3911_info;
 299        spi_set_drvdata(spi, indio_dev);
 300
 301        indio_dev->channels = mcp3911_channels;
 302        indio_dev->num_channels = ARRAY_SIZE(mcp3911_channels);
 303
 304        mutex_init(&adc->lock);
 305
 306        ret = iio_device_register(indio_dev);
 307        if (ret)
 308                goto clk_disable;
 309
 310        return ret;
 311
 312clk_disable:
 313        clk_disable_unprepare(adc->clki);
 314reg_disable:
 315        if (adc->vref)
 316                regulator_disable(adc->vref);
 317
 318        return ret;
 319}
 320
 321static int mcp3911_remove(struct spi_device *spi)
 322{
 323        struct iio_dev *indio_dev = spi_get_drvdata(spi);
 324        struct mcp3911 *adc = iio_priv(indio_dev);
 325
 326        iio_device_unregister(indio_dev);
 327
 328        clk_disable_unprepare(adc->clki);
 329        if (adc->vref)
 330                regulator_disable(adc->vref);
 331
 332        return 0;
 333}
 334
 335static const struct of_device_id mcp3911_dt_ids[] = {
 336        { .compatible = "microchip,mcp3911" },
 337        { }
 338};
 339MODULE_DEVICE_TABLE(of, mcp3911_dt_ids);
 340
 341static const struct spi_device_id mcp3911_id[] = {
 342        { "mcp3911", 0 },
 343        { }
 344};
 345MODULE_DEVICE_TABLE(spi, mcp3911_id);
 346
 347static struct spi_driver mcp3911_driver = {
 348        .driver = {
 349                .name = "mcp3911",
 350                .of_match_table = mcp3911_dt_ids,
 351        },
 352        .probe = mcp3911_probe,
 353        .remove = mcp3911_remove,
 354        .id_table = mcp3911_id,
 355};
 356module_spi_driver(mcp3911_driver);
 357
 358MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
 359MODULE_AUTHOR("Kent Gustavsson <kent@minoris.se>");
 360MODULE_DESCRIPTION("Microchip Technology MCP3911");
 361MODULE_LICENSE("GPL v2");
 362