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