OSDN Git Service

iio: potentiometer: mcp4018: driver for Microchip digital potentiometers
authorPeter Rosin <peda@axentia.se>
Thu, 8 Mar 2018 19:58:30 +0000 (20:58 +0100)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Sat, 10 Mar 2018 16:03:25 +0000 (16:03 +0000)
Add support for Microchip digital potentiometers and rheostats
MCP4017, MCP4018, MCP4019

They all have one wiper with 128 steps and come in 5, 10, 50 and 100 kOhm
variations.

Datasheet: http://www.microchip.com/downloads/en/DeviceDoc/22147a.pdf

Signed-off-by: Peter Rosin <peda@axentia.se>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
MAINTAINERS
drivers/iio/potentiometer/Kconfig
drivers/iio/potentiometer/Makefile
drivers/iio/potentiometer/mcp4018.c [new file with mode: 0644]

index 885d200..36f1442 100644 (file)
@@ -8586,11 +8586,12 @@ W:      https://linuxtv.org
 S:     Maintained
 F:     drivers/media/radio/radio-maxiradio*
 
-MCP4531 MICROCHIP DIGITAL POTENTIOMETER DRIVER
+MCP4018 AND MCP4531 MICROCHIP DIGITAL POTENTIOMETER DRIVERS
 M:     Peter Rosin <peda@axentia.se>
 L:     linux-iio@vger.kernel.org
 S:     Maintained
 F:     Documentation/ABI/testing/sysfs-bus-iio-potentiometer-mcp4531
+F:     drivers/iio/potentiometer/mcp4018.c
 F:     drivers/iio/potentiometer/mcp4531.c
 
 MEASUREMENT COMPUTING CIO-DAC IIO DRIVER
index 8daf6d1..79ec2eb 100644 (file)
@@ -47,6 +47,17 @@ config MAX5487
           To compile this driver as a module, choose M here: the
           module will be called max5487.
 
+config MCP4018
+       tristate "Microchip MCP4017/18/19 Digital Potentiometer driver"
+       depends on I2C
+       help
+         Say yes here to build support for the Microchip
+         MCP4017, MCP4018, MCP4019
+         digital potentiometer chips.
+
+         To compile this driver as a module, choose M here: the
+         module will be called mcp4018.
+
 config MCP4131
        tristate "Microchip MCP413X/414X/415X/416X/423X/424X/425X/426X Digital Potentiometer driver"
        depends on SPI
index dbc139c..4af6578 100644 (file)
@@ -8,6 +8,7 @@ obj-$(CONFIG_AD5272) += ad5272.o
 obj-$(CONFIG_DS1803) += ds1803.o
 obj-$(CONFIG_MAX5481) += max5481.o
 obj-$(CONFIG_MAX5487) += max5487.o
+obj-$(CONFIG_MCP4018) += mcp4018.o
 obj-$(CONFIG_MCP4131) += mcp4131.o
 obj-$(CONFIG_MCP4531) += mcp4531.o
 obj-$(CONFIG_TPL0102) += tpl0102.o
diff --git a/drivers/iio/potentiometer/mcp4018.c b/drivers/iio/potentiometer/mcp4018.c
new file mode 100644 (file)
index 0000000..601b25d
--- /dev/null
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Industrial I/O driver for Microchip digital potentiometers
+ * Copyright (c) 2018  Axentia Technologies AB
+ * Author: Peter Rosin <peda@axentia.se>
+ *
+ * Datasheet: http://www.microchip.com/downloads/en/DeviceDoc/22147a.pdf
+ *
+ * DEVID       #Wipers #Positions      Resistor Opts (kOhm)
+ * mcp4017     1       128             5, 10, 50, 100
+ * mcp4018     1       128             5, 10, 50, 100
+ * mcp4019     1       128             5, 10, 50, 100
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#define MCP4018_WIPER_MAX 127
+
+struct mcp4018_cfg {
+       int kohms;
+};
+
+enum mcp4018_type {
+       MCP4018_502,
+       MCP4018_103,
+       MCP4018_503,
+       MCP4018_104,
+};
+
+static const struct mcp4018_cfg mcp4018_cfg[] = {
+       [MCP4018_502] = { .kohms =   5, },
+       [MCP4018_103] = { .kohms =  10, },
+       [MCP4018_503] = { .kohms =  50, },
+       [MCP4018_104] = { .kohms = 100, },
+};
+
+struct mcp4018_data {
+       struct i2c_client *client;
+       const struct mcp4018_cfg *cfg;
+};
+
+static const struct iio_chan_spec mcp4018_channel = {
+       .type = IIO_RESISTANCE,
+       .indexed = 1,
+       .output = 1,
+       .channel = 0,
+       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+       .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+};
+
+static int mcp4018_read_raw(struct iio_dev *indio_dev,
+                           struct iio_chan_spec const *chan,
+                           int *val, int *val2, long mask)
+{
+       struct mcp4018_data *data = iio_priv(indio_dev);
+       s32 ret;
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               ret = i2c_smbus_read_byte(data->client);
+               if (ret < 0)
+                       return ret;
+               *val = ret;
+               return IIO_VAL_INT;
+       case IIO_CHAN_INFO_SCALE:
+               *val = 1000 * data->cfg->kohms;
+               *val2 = MCP4018_WIPER_MAX;
+               return IIO_VAL_FRACTIONAL;
+       }
+
+       return -EINVAL;
+}
+
+static int mcp4018_write_raw(struct iio_dev *indio_dev,
+                            struct iio_chan_spec const *chan,
+                            int val, int val2, long mask)
+{
+       struct mcp4018_data *data = iio_priv(indio_dev);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               if (val > MCP4018_WIPER_MAX || val < 0)
+                       return -EINVAL;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return i2c_smbus_write_byte(data->client, val);
+}
+
+static const struct iio_info mcp4018_info = {
+       .read_raw = mcp4018_read_raw,
+       .write_raw = mcp4018_write_raw,
+};
+
+#ifdef CONFIG_OF
+
+#define MCP4018_COMPATIBLE(of_compatible, cfg) {       \
+       .compatible = of_compatible,                    \
+       .data = &mcp4018_cfg[cfg],                      \
+}
+
+static const struct of_device_id mcp4018_of_match[] = {
+       MCP4018_COMPATIBLE("microchip,mcp4017-502", MCP4018_502),
+       MCP4018_COMPATIBLE("microchip,mcp4017-103", MCP4018_103),
+       MCP4018_COMPATIBLE("microchip,mcp4017-503", MCP4018_503),
+       MCP4018_COMPATIBLE("microchip,mcp4017-104", MCP4018_104),
+       MCP4018_COMPATIBLE("microchip,mcp4018-502", MCP4018_502),
+       MCP4018_COMPATIBLE("microchip,mcp4018-103", MCP4018_103),
+       MCP4018_COMPATIBLE("microchip,mcp4018-503", MCP4018_503),
+       MCP4018_COMPATIBLE("microchip,mcp4018-104", MCP4018_104),
+       MCP4018_COMPATIBLE("microchip,mcp4019-502", MCP4018_502),
+       MCP4018_COMPATIBLE("microchip,mcp4019-103", MCP4018_103),
+       MCP4018_COMPATIBLE("microchip,mcp4019-503", MCP4018_503),
+       MCP4018_COMPATIBLE("microchip,mcp4019-104", MCP4018_104),
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mcp4018_of_match);
+
+#endif
+
+static int mcp4018_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       struct device *dev = &client->dev;
+       struct mcp4018_data *data;
+       struct iio_dev *indio_dev;
+       const struct of_device_id *match;
+
+       if (!i2c_check_functionality(client->adapter,
+                                    I2C_FUNC_SMBUS_BYTE)) {
+               dev_err(dev, "SMBUS Byte transfers not supported\n");
+               return -EOPNOTSUPP;
+       }
+
+       indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+       if (!indio_dev)
+               return -ENOMEM;
+       data = iio_priv(indio_dev);
+       i2c_set_clientdata(client, indio_dev);
+       data->client = client;
+
+       match = of_match_device(of_match_ptr(mcp4018_of_match), dev);
+       if (match)
+               data->cfg = of_device_get_match_data(dev);
+       else
+               data->cfg = &mcp4018_cfg[id->driver_data];
+
+       indio_dev->dev.parent = dev;
+       indio_dev->info = &mcp4018_info;
+       indio_dev->channels = &mcp4018_channel;
+       indio_dev->num_channels = 1;
+       indio_dev->name = client->name;
+
+       return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct i2c_device_id mcp4018_id[] = {
+       { "mcp4017-502", MCP4018_502 },
+       { "mcp4017-103", MCP4018_103 },
+       { "mcp4017-503", MCP4018_503 },
+       { "mcp4017-104", MCP4018_104 },
+       { "mcp4018-502", MCP4018_502 },
+       { "mcp4018-103", MCP4018_103 },
+       { "mcp4018-503", MCP4018_503 },
+       { "mcp4018-104", MCP4018_104 },
+       { "mcp4019-502", MCP4018_502 },
+       { "mcp4019-103", MCP4018_103 },
+       { "mcp4019-503", MCP4018_503 },
+       { "mcp4019-104", MCP4018_104 },
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, mcp4018_id);
+
+static struct i2c_driver mcp4018_driver = {
+       .driver = {
+               .name   = "mcp4018",
+               .of_match_table = of_match_ptr(mcp4018_of_match),
+       },
+       .probe          = mcp4018_probe,
+       .id_table       = mcp4018_id,
+};
+
+module_i2c_driver(mcp4018_driver);
+
+MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
+MODULE_DESCRIPTION("MCP4018 digital potentiometer");
+MODULE_LICENSE("GPL");