Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Industrial I/O driver for Microchip digital potentiometers
0004  *
0005  * Copyright (c) 2016 Slawomir Stepien
0006  * Based on: Peter Rosin's code from mcp4531.c
0007  *
0008  * Datasheet: https://ww1.microchip.com/downloads/en/DeviceDoc/22060b.pdf
0009  *
0010  * DEVID    #Wipers #Positions  Resistor Opts (kOhm)
0011  * mcp4131  1   129     5, 10, 50, 100
0012  * mcp4132  1   129     5, 10, 50, 100
0013  * mcp4141  1   129     5, 10, 50, 100
0014  * mcp4142  1   129     5, 10, 50, 100
0015  * mcp4151  1   257     5, 10, 50, 100
0016  * mcp4152  1   257     5, 10, 50, 100
0017  * mcp4161  1   257     5, 10, 50, 100
0018  * mcp4162  1   257     5, 10, 50, 100
0019  * mcp4231  2   129     5, 10, 50, 100
0020  * mcp4232  2   129     5, 10, 50, 100
0021  * mcp4241  2   129     5, 10, 50, 100
0022  * mcp4242  2   129     5, 10, 50, 100
0023  * mcp4251  2   257     5, 10, 50, 100
0024  * mcp4252  2   257     5, 10, 50, 100
0025  * mcp4261  2   257     5, 10, 50, 100
0026  * mcp4262  2   257     5, 10, 50, 100
0027  */
0028 
0029 /*
0030  * TODO:
0031  * 1. Write wiper setting to EEPROM for EEPROM capable models.
0032  */
0033 
0034 #include <linux/cache.h>
0035 #include <linux/err.h>
0036 #include <linux/export.h>
0037 #include <linux/iio/iio.h>
0038 #include <linux/iio/types.h>
0039 #include <linux/module.h>
0040 #include <linux/mod_devicetable.h>
0041 #include <linux/mutex.h>
0042 #include <linux/property.h>
0043 #include <linux/spi/spi.h>
0044 
0045 #define MCP4131_WRITE       (0x00 << 2)
0046 #define MCP4131_READ        (0x03 << 2)
0047 
0048 #define MCP4131_WIPER_SHIFT 4
0049 #define MCP4131_CMDERR(r)   ((r[0]) & 0x02)
0050 #define MCP4131_RAW(r)      ((r[0]) == 0xff ? 0x100 : (r[1]))
0051 
0052 struct mcp4131_cfg {
0053     int wipers;
0054     int max_pos;
0055     int kohms;
0056 };
0057 
0058 enum mcp4131_type {
0059     MCP413x_502 = 0,
0060     MCP413x_103,
0061     MCP413x_503,
0062     MCP413x_104,
0063     MCP414x_502,
0064     MCP414x_103,
0065     MCP414x_503,
0066     MCP414x_104,
0067     MCP415x_502,
0068     MCP415x_103,
0069     MCP415x_503,
0070     MCP415x_104,
0071     MCP416x_502,
0072     MCP416x_103,
0073     MCP416x_503,
0074     MCP416x_104,
0075     MCP423x_502,
0076     MCP423x_103,
0077     MCP423x_503,
0078     MCP423x_104,
0079     MCP424x_502,
0080     MCP424x_103,
0081     MCP424x_503,
0082     MCP424x_104,
0083     MCP425x_502,
0084     MCP425x_103,
0085     MCP425x_503,
0086     MCP425x_104,
0087     MCP426x_502,
0088     MCP426x_103,
0089     MCP426x_503,
0090     MCP426x_104,
0091 };
0092 
0093 static const struct mcp4131_cfg mcp4131_cfg[] = {
0094     [MCP413x_502] = { .wipers = 1, .max_pos = 128, .kohms =   5, },
0095     [MCP413x_103] = { .wipers = 1, .max_pos = 128, .kohms =  10, },
0096     [MCP413x_503] = { .wipers = 1, .max_pos = 128, .kohms =  50, },
0097     [MCP413x_104] = { .wipers = 1, .max_pos = 128, .kohms = 100, },
0098     [MCP414x_502] = { .wipers = 1, .max_pos = 128, .kohms =   5, },
0099     [MCP414x_103] = { .wipers = 1, .max_pos = 128, .kohms =  10, },
0100     [MCP414x_503] = { .wipers = 1, .max_pos = 128, .kohms =  50, },
0101     [MCP414x_104] = { .wipers = 1, .max_pos = 128, .kohms = 100, },
0102     [MCP415x_502] = { .wipers = 1, .max_pos = 256, .kohms =   5, },
0103     [MCP415x_103] = { .wipers = 1, .max_pos = 256, .kohms =  10, },
0104     [MCP415x_503] = { .wipers = 1, .max_pos = 256, .kohms =  50, },
0105     [MCP415x_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, },
0106     [MCP416x_502] = { .wipers = 1, .max_pos = 256, .kohms =   5, },
0107     [MCP416x_103] = { .wipers = 1, .max_pos = 256, .kohms =  10, },
0108     [MCP416x_503] = { .wipers = 1, .max_pos = 256, .kohms =  50, },
0109     [MCP416x_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, },
0110     [MCP423x_502] = { .wipers = 2, .max_pos = 128, .kohms =   5, },
0111     [MCP423x_103] = { .wipers = 2, .max_pos = 128, .kohms =  10, },
0112     [MCP423x_503] = { .wipers = 2, .max_pos = 128, .kohms =  50, },
0113     [MCP423x_104] = { .wipers = 2, .max_pos = 128, .kohms = 100, },
0114     [MCP424x_502] = { .wipers = 2, .max_pos = 128, .kohms =   5, },
0115     [MCP424x_103] = { .wipers = 2, .max_pos = 128, .kohms =  10, },
0116     [MCP424x_503] = { .wipers = 2, .max_pos = 128, .kohms =  50, },
0117     [MCP424x_104] = { .wipers = 2, .max_pos = 128, .kohms = 100, },
0118     [MCP425x_502] = { .wipers = 2, .max_pos = 256, .kohms =   5, },
0119     [MCP425x_103] = { .wipers = 2, .max_pos = 256, .kohms =  10, },
0120     [MCP425x_503] = { .wipers = 2, .max_pos = 256, .kohms =  50, },
0121     [MCP425x_104] = { .wipers = 2, .max_pos = 256, .kohms = 100, },
0122     [MCP426x_502] = { .wipers = 2, .max_pos = 256, .kohms =   5, },
0123     [MCP426x_103] = { .wipers = 2, .max_pos = 256, .kohms =  10, },
0124     [MCP426x_503] = { .wipers = 2, .max_pos = 256, .kohms =  50, },
0125     [MCP426x_104] = { .wipers = 2, .max_pos = 256, .kohms = 100, },
0126 };
0127 
0128 struct mcp4131_data {
0129     struct spi_device *spi;
0130     const struct mcp4131_cfg *cfg;
0131     struct mutex lock;
0132     u8 buf[2] __aligned(IIO_DMA_MINALIGN);
0133 };
0134 
0135 #define MCP4131_CHANNEL(ch) {                   \
0136     .type = IIO_RESISTANCE,                 \
0137     .indexed = 1,                       \
0138     .output = 1,                        \
0139     .channel = (ch),                    \
0140     .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),       \
0141     .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),   \
0142 }
0143 
0144 static const struct iio_chan_spec mcp4131_channels[] = {
0145     MCP4131_CHANNEL(0),
0146     MCP4131_CHANNEL(1),
0147 };
0148 
0149 static int mcp4131_read(struct spi_device *spi, void *buf, size_t len)
0150 {
0151     struct spi_transfer t = {
0152         .tx_buf = buf, /* We need to send addr, cmd and 12 bits */
0153         .rx_buf = buf,
0154         .len = len,
0155     };
0156     struct spi_message m;
0157 
0158     spi_message_init(&m);
0159     spi_message_add_tail(&t, &m);
0160 
0161     return spi_sync(spi, &m);
0162 }
0163 
0164 static int mcp4131_read_raw(struct iio_dev *indio_dev,
0165                 struct iio_chan_spec const *chan,
0166                 int *val, int *val2, long mask)
0167 {
0168     int err;
0169     struct mcp4131_data *data = iio_priv(indio_dev);
0170     int address = chan->channel;
0171 
0172     switch (mask) {
0173     case IIO_CHAN_INFO_RAW:
0174         mutex_lock(&data->lock);
0175 
0176         data->buf[0] = (address << MCP4131_WIPER_SHIFT) | MCP4131_READ;
0177         data->buf[1] = 0;
0178 
0179         err = mcp4131_read(data->spi, data->buf, 2);
0180         if (err) {
0181             mutex_unlock(&data->lock);
0182             return err;
0183         }
0184 
0185         /* Error, bad address/command combination */
0186         if (!MCP4131_CMDERR(data->buf)) {
0187             mutex_unlock(&data->lock);
0188             return -EIO;
0189         }
0190 
0191         *val = MCP4131_RAW(data->buf);
0192         mutex_unlock(&data->lock);
0193 
0194         return IIO_VAL_INT;
0195 
0196     case IIO_CHAN_INFO_SCALE:
0197         *val = 1000 * data->cfg->kohms;
0198         *val2 = data->cfg->max_pos;
0199         return IIO_VAL_FRACTIONAL;
0200     }
0201 
0202     return -EINVAL;
0203 }
0204 
0205 static int mcp4131_write_raw(struct iio_dev *indio_dev,
0206                  struct iio_chan_spec const *chan,
0207                  int val, int val2, long mask)
0208 {
0209     int err;
0210     struct mcp4131_data *data = iio_priv(indio_dev);
0211     int address = chan->channel << MCP4131_WIPER_SHIFT;
0212 
0213     switch (mask) {
0214     case IIO_CHAN_INFO_RAW:
0215         if (val > data->cfg->max_pos || val < 0)
0216             return -EINVAL;
0217         break;
0218 
0219     default:
0220         return -EINVAL;
0221     }
0222 
0223     mutex_lock(&data->lock);
0224 
0225     data->buf[0] = address << MCP4131_WIPER_SHIFT;
0226     data->buf[0] |= MCP4131_WRITE | (val >> 8);
0227     data->buf[1] = val & 0xFF; /* 8 bits here */
0228 
0229     err = spi_write(data->spi, data->buf, 2);
0230     mutex_unlock(&data->lock);
0231 
0232     return err;
0233 }
0234 
0235 static const struct iio_info mcp4131_info = {
0236     .read_raw = mcp4131_read_raw,
0237     .write_raw = mcp4131_write_raw,
0238 };
0239 
0240 static int mcp4131_probe(struct spi_device *spi)
0241 {
0242     int err;
0243     struct device *dev = &spi->dev;
0244     unsigned long devid;
0245     struct mcp4131_data *data;
0246     struct iio_dev *indio_dev;
0247 
0248     indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
0249     if (!indio_dev)
0250         return -ENOMEM;
0251 
0252     data = iio_priv(indio_dev);
0253     spi_set_drvdata(spi, indio_dev);
0254     data->spi = spi;
0255     data->cfg = device_get_match_data(&spi->dev);
0256     if (!data->cfg) {
0257         devid = spi_get_device_id(spi)->driver_data;
0258         data->cfg = &mcp4131_cfg[devid];
0259     }
0260 
0261     mutex_init(&data->lock);
0262 
0263     indio_dev->info = &mcp4131_info;
0264     indio_dev->channels = mcp4131_channels;
0265     indio_dev->num_channels = data->cfg->wipers;
0266     indio_dev->name = spi_get_device_id(spi)->name;
0267 
0268     err = devm_iio_device_register(dev, indio_dev);
0269     if (err) {
0270         dev_info(&spi->dev, "Unable to register %s\n", indio_dev->name);
0271         return err;
0272     }
0273 
0274     return 0;
0275 }
0276 
0277 static const struct of_device_id mcp4131_dt_ids[] = {
0278     { .compatible = "microchip,mcp4131-502",
0279         .data = &mcp4131_cfg[MCP413x_502] },
0280     { .compatible = "microchip,mcp4131-103",
0281         .data = &mcp4131_cfg[MCP413x_103] },
0282     { .compatible = "microchip,mcp4131-503",
0283         .data = &mcp4131_cfg[MCP413x_503] },
0284     { .compatible = "microchip,mcp4131-104",
0285         .data = &mcp4131_cfg[MCP413x_104] },
0286     { .compatible = "microchip,mcp4132-502",
0287         .data = &mcp4131_cfg[MCP413x_502] },
0288     { .compatible = "microchip,mcp4132-103",
0289         .data = &mcp4131_cfg[MCP413x_103] },
0290     { .compatible = "microchip,mcp4132-503",
0291         .data = &mcp4131_cfg[MCP413x_503] },
0292     { .compatible = "microchip,mcp4132-104",
0293         .data = &mcp4131_cfg[MCP413x_104] },
0294     { .compatible = "microchip,mcp4141-502",
0295         .data = &mcp4131_cfg[MCP414x_502] },
0296     { .compatible = "microchip,mcp4141-103",
0297         .data = &mcp4131_cfg[MCP414x_103] },
0298     { .compatible = "microchip,mcp4141-503",
0299         .data = &mcp4131_cfg[MCP414x_503] },
0300     { .compatible = "microchip,mcp4141-104",
0301         .data = &mcp4131_cfg[MCP414x_104] },
0302     { .compatible = "microchip,mcp4142-502",
0303         .data = &mcp4131_cfg[MCP414x_502] },
0304     { .compatible = "microchip,mcp4142-103",
0305         .data = &mcp4131_cfg[MCP414x_103] },
0306     { .compatible = "microchip,mcp4142-503",
0307         .data = &mcp4131_cfg[MCP414x_503] },
0308     { .compatible = "microchip,mcp4142-104",
0309         .data = &mcp4131_cfg[MCP414x_104] },
0310     { .compatible = "microchip,mcp4151-502",
0311         .data = &mcp4131_cfg[MCP415x_502] },
0312     { .compatible = "microchip,mcp4151-103",
0313         .data = &mcp4131_cfg[MCP415x_103] },
0314     { .compatible = "microchip,mcp4151-503",
0315         .data = &mcp4131_cfg[MCP415x_503] },
0316     { .compatible = "microchip,mcp4151-104",
0317         .data = &mcp4131_cfg[MCP415x_104] },
0318     { .compatible = "microchip,mcp4152-502",
0319         .data = &mcp4131_cfg[MCP415x_502] },
0320     { .compatible = "microchip,mcp4152-103",
0321         .data = &mcp4131_cfg[MCP415x_103] },
0322     { .compatible = "microchip,mcp4152-503",
0323         .data = &mcp4131_cfg[MCP415x_503] },
0324     { .compatible = "microchip,mcp4152-104",
0325         .data = &mcp4131_cfg[MCP415x_104] },
0326     { .compatible = "microchip,mcp4161-502",
0327         .data = &mcp4131_cfg[MCP416x_502] },
0328     { .compatible = "microchip,mcp4161-103",
0329         .data = &mcp4131_cfg[MCP416x_103] },
0330     { .compatible = "microchip,mcp4161-503",
0331         .data = &mcp4131_cfg[MCP416x_503] },
0332     { .compatible = "microchip,mcp4161-104",
0333         .data = &mcp4131_cfg[MCP416x_104] },
0334     { .compatible = "microchip,mcp4162-502",
0335         .data = &mcp4131_cfg[MCP416x_502] },
0336     { .compatible = "microchip,mcp4162-103",
0337         .data = &mcp4131_cfg[MCP416x_103] },
0338     { .compatible = "microchip,mcp4162-503",
0339         .data = &mcp4131_cfg[MCP416x_503] },
0340     { .compatible = "microchip,mcp4162-104",
0341         .data = &mcp4131_cfg[MCP416x_104] },
0342     { .compatible = "microchip,mcp4231-502",
0343         .data = &mcp4131_cfg[MCP423x_502] },
0344     { .compatible = "microchip,mcp4231-103",
0345         .data = &mcp4131_cfg[MCP423x_103] },
0346     { .compatible = "microchip,mcp4231-503",
0347         .data = &mcp4131_cfg[MCP423x_503] },
0348     { .compatible = "microchip,mcp4231-104",
0349         .data = &mcp4131_cfg[MCP423x_104] },
0350     { .compatible = "microchip,mcp4232-502",
0351         .data = &mcp4131_cfg[MCP423x_502] },
0352     { .compatible = "microchip,mcp4232-103",
0353         .data = &mcp4131_cfg[MCP423x_103] },
0354     { .compatible = "microchip,mcp4232-503",
0355         .data = &mcp4131_cfg[MCP423x_503] },
0356     { .compatible = "microchip,mcp4232-104",
0357         .data = &mcp4131_cfg[MCP423x_104] },
0358     { .compatible = "microchip,mcp4241-502",
0359         .data = &mcp4131_cfg[MCP424x_502] },
0360     { .compatible = "microchip,mcp4241-103",
0361         .data = &mcp4131_cfg[MCP424x_103] },
0362     { .compatible = "microchip,mcp4241-503",
0363         .data = &mcp4131_cfg[MCP424x_503] },
0364     { .compatible = "microchip,mcp4241-104",
0365         .data = &mcp4131_cfg[MCP424x_104] },
0366     { .compatible = "microchip,mcp4242-502",
0367         .data = &mcp4131_cfg[MCP424x_502] },
0368     { .compatible = "microchip,mcp4242-103",
0369         .data = &mcp4131_cfg[MCP424x_103] },
0370     { .compatible = "microchip,mcp4242-503",
0371         .data = &mcp4131_cfg[MCP424x_503] },
0372     { .compatible = "microchip,mcp4242-104",
0373         .data = &mcp4131_cfg[MCP424x_104] },
0374     { .compatible = "microchip,mcp4251-502",
0375         .data = &mcp4131_cfg[MCP425x_502] },
0376     { .compatible = "microchip,mcp4251-103",
0377         .data = &mcp4131_cfg[MCP425x_103] },
0378     { .compatible = "microchip,mcp4251-503",
0379         .data = &mcp4131_cfg[MCP425x_503] },
0380     { .compatible = "microchip,mcp4251-104",
0381         .data = &mcp4131_cfg[MCP425x_104] },
0382     { .compatible = "microchip,mcp4252-502",
0383         .data = &mcp4131_cfg[MCP425x_502] },
0384     { .compatible = "microchip,mcp4252-103",
0385         .data = &mcp4131_cfg[MCP425x_103] },
0386     { .compatible = "microchip,mcp4252-503",
0387         .data = &mcp4131_cfg[MCP425x_503] },
0388     { .compatible = "microchip,mcp4252-104",
0389         .data = &mcp4131_cfg[MCP425x_104] },
0390     { .compatible = "microchip,mcp4261-502",
0391         .data = &mcp4131_cfg[MCP426x_502] },
0392     { .compatible = "microchip,mcp4261-103",
0393         .data = &mcp4131_cfg[MCP426x_103] },
0394     { .compatible = "microchip,mcp4261-503",
0395         .data = &mcp4131_cfg[MCP426x_503] },
0396     { .compatible = "microchip,mcp4261-104",
0397         .data = &mcp4131_cfg[MCP426x_104] },
0398     { .compatible = "microchip,mcp4262-502",
0399         .data = &mcp4131_cfg[MCP426x_502] },
0400     { .compatible = "microchip,mcp4262-103",
0401         .data = &mcp4131_cfg[MCP426x_103] },
0402     { .compatible = "microchip,mcp4262-503",
0403         .data = &mcp4131_cfg[MCP426x_503] },
0404     { .compatible = "microchip,mcp4262-104",
0405         .data = &mcp4131_cfg[MCP426x_104] },
0406     {}
0407 };
0408 MODULE_DEVICE_TABLE(of, mcp4131_dt_ids);
0409 
0410 static const struct spi_device_id mcp4131_id[] = {
0411     { "mcp4131-502", MCP413x_502 },
0412     { "mcp4131-103", MCP413x_103 },
0413     { "mcp4131-503", MCP413x_503 },
0414     { "mcp4131-104", MCP413x_104 },
0415     { "mcp4132-502", MCP413x_502 },
0416     { "mcp4132-103", MCP413x_103 },
0417     { "mcp4132-503", MCP413x_503 },
0418     { "mcp4132-104", MCP413x_104 },
0419     { "mcp4141-502", MCP414x_502 },
0420     { "mcp4141-103", MCP414x_103 },
0421     { "mcp4141-503", MCP414x_503 },
0422     { "mcp4141-104", MCP414x_104 },
0423     { "mcp4142-502", MCP414x_502 },
0424     { "mcp4142-103", MCP414x_103 },
0425     { "mcp4142-503", MCP414x_503 },
0426     { "mcp4142-104", MCP414x_104 },
0427     { "mcp4151-502", MCP415x_502 },
0428     { "mcp4151-103", MCP415x_103 },
0429     { "mcp4151-503", MCP415x_503 },
0430     { "mcp4151-104", MCP415x_104 },
0431     { "mcp4152-502", MCP415x_502 },
0432     { "mcp4152-103", MCP415x_103 },
0433     { "mcp4152-503", MCP415x_503 },
0434     { "mcp4152-104", MCP415x_104 },
0435     { "mcp4161-502", MCP416x_502 },
0436     { "mcp4161-103", MCP416x_103 },
0437     { "mcp4161-503", MCP416x_503 },
0438     { "mcp4161-104", MCP416x_104 },
0439     { "mcp4162-502", MCP416x_502 },
0440     { "mcp4162-103", MCP416x_103 },
0441     { "mcp4162-503", MCP416x_503 },
0442     { "mcp4162-104", MCP416x_104 },
0443     { "mcp4231-502", MCP423x_502 },
0444     { "mcp4231-103", MCP423x_103 },
0445     { "mcp4231-503", MCP423x_503 },
0446     { "mcp4231-104", MCP423x_104 },
0447     { "mcp4232-502", MCP423x_502 },
0448     { "mcp4232-103", MCP423x_103 },
0449     { "mcp4232-503", MCP423x_503 },
0450     { "mcp4232-104", MCP423x_104 },
0451     { "mcp4241-502", MCP424x_502 },
0452     { "mcp4241-103", MCP424x_103 },
0453     { "mcp4241-503", MCP424x_503 },
0454     { "mcp4241-104", MCP424x_104 },
0455     { "mcp4242-502", MCP424x_502 },
0456     { "mcp4242-103", MCP424x_103 },
0457     { "mcp4242-503", MCP424x_503 },
0458     { "mcp4242-104", MCP424x_104 },
0459     { "mcp4251-502", MCP425x_502 },
0460     { "mcp4251-103", MCP425x_103 },
0461     { "mcp4251-503", MCP425x_503 },
0462     { "mcp4251-104", MCP425x_104 },
0463     { "mcp4252-502", MCP425x_502 },
0464     { "mcp4252-103", MCP425x_103 },
0465     { "mcp4252-503", MCP425x_503 },
0466     { "mcp4252-104", MCP425x_104 },
0467     { "mcp4261-502", MCP426x_502 },
0468     { "mcp4261-103", MCP426x_103 },
0469     { "mcp4261-503", MCP426x_503 },
0470     { "mcp4261-104", MCP426x_104 },
0471     { "mcp4262-502", MCP426x_502 },
0472     { "mcp4262-103", MCP426x_103 },
0473     { "mcp4262-503", MCP426x_503 },
0474     { "mcp4262-104", MCP426x_104 },
0475     {}
0476 };
0477 MODULE_DEVICE_TABLE(spi, mcp4131_id);
0478 
0479 static struct spi_driver mcp4131_driver = {
0480     .driver = {
0481         .name   = "mcp4131",
0482         .of_match_table = mcp4131_dt_ids,
0483     },
0484     .probe      = mcp4131_probe,
0485     .id_table   = mcp4131_id,
0486 };
0487 
0488 module_spi_driver(mcp4131_driver);
0489 
0490 MODULE_AUTHOR("Slawomir Stepien <sst@poczta.fm>");
0491 MODULE_DESCRIPTION("MCP4131 digital potentiometer");
0492 MODULE_LICENSE("GPL v2");