On 22 September 2015 16:36:49 BST, Peter Rosin <[email protected]> wrote:
>From: Peter Rosin <[email protected]>
>
>Add support for Microchip digital potentiometers and rheostats
>       MCP4531, MCP4532, MCP4551, MCP4552
>       MCP4631, MCP4632, MCP4651, MCP4652
>
>These are either single (45xx) or dual (46xx) wipers with either
>129 (4x3x) or 257 (4x5x) steps, and configured either as
>potentiometers (4xx1) or rheostats (4xx2). They all use i2c
>client addresses of the 0101xxx form.
>
>Datasheet: http://www.microchip.com/downloads/en/DeviceDoc/22096b.pdf
>
>Signed-off-by: Peter Rosin <[email protected]>

Mostly fine though I think you may have gone a bit too much for conciseness over
 clarity.  Also the scale appears known so should be exposed to userspace.
>---
> MAINTAINERS                         |    6 ++
> drivers/iio/Kconfig                 |    1 +
> drivers/iio/Makefile                |    1 +
> drivers/iio/potentiometer/Kconfig   |   20 ++++
> drivers/iio/potentiometer/Makefile  |    6 ++
>drivers/iio/potentiometer/mcp4531.c |  196
>+++++++++++++++++++++++++++++++++++
> 6 files changed, 230 insertions(+)
> create mode 100644 drivers/iio/potentiometer/Kconfig
> create mode 100644 drivers/iio/potentiometer/Makefile
> create mode 100644 drivers/iio/potentiometer/mcp4531.c
>
>diff --git a/MAINTAINERS b/MAINTAINERS
>index b60e2b2369d2..27862156c7a7 100644
>--- a/MAINTAINERS
>+++ b/MAINTAINERS
>@@ -6600,6 +6600,12 @@ W:      http://linuxtv.org
> S:    Maintained
> F:    drivers/media/radio/radio-maxiradio*
> 
>+MCP4531 MICROCHIP DIGITAL POTENTIOMETER DRIVER
>+M:    Peter Rosin <[email protected]>
>+L:    [email protected]
>+S:    Maintained
>+F:    drivers/iio/potentiometer/mcp4531.c
>+
> MEDIA DRIVERS FOR RENESAS - VSP1
> M:    Laurent Pinchart <[email protected]>
> L:    [email protected]
>diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
>index 4011effe4c05..7cc87f322655 100644
>--- a/drivers/iio/Kconfig
>+++ b/drivers/iio/Kconfig
>@@ -73,6 +73,7 @@ source "drivers/iio/orientation/Kconfig"
> if IIO_TRIGGER
>    source "drivers/iio/trigger/Kconfig"
> endif #IIO_TRIGGER
>+source "drivers/iio/potentiometer/Kconfig"
> source "drivers/iio/pressure/Kconfig"
> source "drivers/iio/proximity/Kconfig"
> source "drivers/iio/temperature/Kconfig"
>diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
>index 698afc2d17ce..121c814e366b 100644
>--- a/drivers/iio/Makefile
>+++ b/drivers/iio/Makefile
>@@ -23,6 +23,7 @@ obj-y += imu/
> obj-y += light/
> obj-y += magnetometer/
> obj-y += orientation/
>+obj-y += potentiometer/
> obj-y += pressure/
> obj-y += proximity/
> obj-y += temperature/
>diff --git a/drivers/iio/potentiometer/Kconfig
>b/drivers/iio/potentiometer/Kconfig
>new file mode 100644
>index 000000000000..fd75db73e582
>--- /dev/null
>+++ b/drivers/iio/potentiometer/Kconfig
>@@ -0,0 +1,20 @@
>+#
>+# Potentiometer drivers
>+#
>+# When adding new entries keep the list in alphabetical order
>+
>+menu "Digital potentiometers"
>+
>+config MCP4531
>+      tristate "Microchip MCP45xx/MCP46xx Digital Potentiometer driver"
>+      depends on I2C
>+      help
>+        Say yes here to build support for the Microchip
>+        MCP4531, MCP4532, MCP4551, MCP4552,
>+        MCP4631, MCP4632, MCP4651, MCP4652
>+        digital potentiomenter chips.
>+
>+        To compile this driver as a module, choose M here: the
>+        module will be called mcp4531.
>+
>+endmenu
>diff --git a/drivers/iio/potentiometer/Makefile
>b/drivers/iio/potentiometer/Makefile
>new file mode 100644
>index 000000000000..8afe49227012
>--- /dev/null
>+++ b/drivers/iio/potentiometer/Makefile
>@@ -0,0 +1,6 @@
>+#
>+# Makefile for industrial I/O potentiometer drivers
>+#
>+
>+# When adding new entries keep the list in alphabetical order
>+obj-$(CONFIG_MCP4531) += mcp4531.o
>diff --git a/drivers/iio/potentiometer/mcp4531.c
>b/drivers/iio/potentiometer/mcp4531.c
>new file mode 100644
>index 000000000000..04faf56dabe0
>--- /dev/null
>+++ b/drivers/iio/potentiometer/mcp4531.c
>@@ -0,0 +1,196 @@
>+/*
>+ * Industrial I/O driver for Microchip digital potentiometers
>+ * Copyright (c) 2015  Axentia Technologies AB
>+ * Author: Peter Rosin <[email protected]>
>+ *
>+ * Datasheet:
>http://www.microchip.com/downloads/en/DeviceDoc/22096b.pdf
>+ *
>+ * DEVID      #Wipers #Positions      Resistor Opts (kOhm)    i2c address
>+ * mcp4531    1       129             5, 10, 50, 100          010111x
>+ * mcp4532    1       129             5, 10, 50, 100          01011xx
>+ * mcp4551    1       257             5, 10, 50, 100          010111x
>+ * mcp4552    1       257             5, 10, 50, 100          01011xx
>+ * mcp4631    2       129             5, 10, 50, 100          0101xxx
>+ * mcp4632    2       129             5, 10, 50, 100          01011xx
>+ * mcp4651    2       257             5, 10, 50, 100          0101xxx
>+ * mcp4652    2       257             5, 10, 50, 100          01011xx
>+ *
>+ * This program is free software; you can redistribute it and/or
>modify it
>+ * under the terms of the GNU General Public License version 2 as
>published by
>+ * the Free Software Foundation.
>+ */
>+
>+#include <linux/module.h>
>+#include <linux/i2c.h>
>+#include <linux/err.h>
>+
>+#include <linux/iio/iio.h>
>+
>+#define MCP4531_CONF(wipers, max_pos, uid) \
>+      ((((wipers) & 0xf) << 10) | \
>+       (((max_pos) & 0xf) << 6) | \
>+       ((uid) & 0x3f))

This strike me as harder to read than it need be. I think I would be clearer 
done 
By setting dev_id to an index into an array to static const structs with the 
various 
parts explicitly brought out as fields.
Preferably assigned with c99 syntax for really easy reading :)

>+
>+#define MCP4531_UID(conf)     ((conf) & 0x3f)
>+#define MCP4531_MAX_POS(conf) (((conf) >> 6) & 0xf)
>+#define MCP4531_WIPERS(conf)  (((conf) >> 10) & 0xf)
>+
>+#define MCP4531_WRITE (0 << 2)
>+#define MCP4531_INCR  (1 << 2)
>+#define MCP4531_DECR  (2 << 2)
>+#define MCP4531_READ  (3 << 2)
>+
>+#define MCP4531_WIPER_SHIFT (4)
>+
>+enum mcp4531_devid {
>+      MCP4531_ID = MCP4531_CONF(1, 7, 0),
>+      MCP4532_ID = MCP4531_CONF(1, 7, 1),
>+      MCP4551_ID = MCP4531_CONF(1, 8, 2),
>+      MCP4552_ID = MCP4531_CONF(1, 8, 3),
>+      MCP4631_ID = MCP4531_CONF(2, 7, 4),
>+      MCP4632_ID = MCP4531_CONF(2, 7, 5),
>+      MCP4651_ID = MCP4531_CONF(2, 8, 6),
>+      MCP4652_ID = MCP4531_CONF(2, 8, 7),
>+};
>+
>+struct mcp4531_data {
>+      struct i2c_client *client;
>+      unsigned int max_pos;
>+      unsigned long devid;
>+};
>+
>+#define MCP4531_CHANNEL(ch) {                         \
>+      .type = IIO_RESISTANCE,                         \
>+      .indexed = 1,                                   \
>+      .output = 1,                                    \
>+      .channel = (ch),                                \
>+      .address = (ch) << MCP4531_WIPER_SHIFT,         \
Why not drop specifying address and use channel with the shift applied at call 
site?

>+      .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),   \
>+}
>+
>+static const struct iio_chan_spec mcp4531_channels[] = {
>+      MCP4531_CHANNEL(0),
>+      MCP4531_CHANNEL(1),
>+};
>+
>+static int mcp4531_get_value(struct iio_dev *indio_dev,
>+                           int address, int *val)
>+{
>+      struct mcp4531_data *data = iio_priv(indio_dev);
>+      s32 ret;
>+
>+      ret = i2c_smbus_read_word_swapped(data->client,
>+                                        MCP4531_READ | address);
>+      if (ret < 0)
>+              return ret;
>+      *val = ret;
>+      return 0;
>+}
>+
>+static int mcp4531_set_value(struct iio_dev *indio_dev,
>+                           int address, int val)
>+{
>+      struct mcp4531_data *data = iio_priv(indio_dev);
>+
>+      if (val >= data->max_pos || val < 0)
>+              return -EINVAL;
>+
>+      return i2c_smbus_write_byte_data(data->client,
>+                                       MCP4531_WRITE | address | (val >> 8),
>+                                       val & 0xff);
>+}
As each of these wrappers is so brief and only used once I would favour just 
merging them in where called.

>+
>+static int mcp4531_read_raw(struct iio_dev *indio_dev,
>+                          struct iio_chan_spec const *chan,
>+                          int *val, int *val2, long mask)
>+{
>+      int err;
>+
>+      switch (mask) {
>+      case IIO_CHAN_INFO_RAW:
>+              err = mcp4531_get_value(indio_dev, chan->address, val);
>+              if (err < 0)
>+                      return err;
>+              return IIO_VAL_INT;
>+      }
No scale known? Be nice to relate to real world values.  I gather this may need
 platform data of some type (can't open data sheet on phone for some reason)

>+
>+      return -EINVAL;
>+}
>+
>+static int mcp4531_write_raw(struct iio_dev *indio_dev,
>+                           struct iio_chan_spec const *chan,
>+                           int val, int val2, long mask)
>+{
>+      switch (mask) {
>+      case IIO_CHAN_INFO_RAW:
>+              return mcp4531_set_value(indio_dev, chan->address, val);
>+      }
>+
>+      return -EINVAL;
>+}
>+
>+static const struct iio_info mcp4531_info = {
>+      .read_raw = mcp4531_read_raw,
>+      .write_raw = mcp4531_write_raw,
>+      .driver_module = THIS_MODULE,
>+};
>+
>+static int mcp4531_probe(struct i2c_client *client,
>+                       const struct i2c_device_id *id)
>+{
>+      struct device *dev = &client->dev;
>+      unsigned long devid = id->driver_data;
>+      struct mcp4531_data *data;
>+      struct iio_dev *indio_dev;
>+
>+      if (!i2c_check_functionality(client->adapter,
>+                                   I2C_FUNC_SMBUS_WORD_DATA)) {
>+              dev_err(dev, "SMBUS Word Data not supported\n");
>+              return -EIO;
>+      }
>+
>+      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;
>+      data->devid = devid;
>+
>+      data->max_pos = (1 << MCP4531_MAX_POS(devid)) + 1;
Derive at use site rather than here.

>+
>+      indio_dev->dev.parent = dev;
>+      indio_dev->info = &mcp4531_info;
>+      indio_dev->channels = mcp4531_channels;
>+      indio_dev->num_channels = MCP4531_WIPERS(devid);
>+      indio_dev->name = client->name;
>+
>+      return devm_iio_device_register(dev, indio_dev);
>+}
>+
>+static const struct i2c_device_id mcp4531_id[] = {
>+      { "mcp4531", MCP4531_ID },
>+      { "mcp4532", MCP4532_ID },
>+      { "mcp4551", MCP4551_ID },
>+      { "mcp4552", MCP4552_ID },
>+      { "mcp4631", MCP4631_ID },
>+      { "mcp4632", MCP4632_ID },
>+      { "mcp4651", MCP4651_ID },
>+      { "mcp4652", MCP4652_ID },
>+      {}
>+};
>+MODULE_DEVICE_TABLE(i2c, mcp4531_id);
>+
>+static struct i2c_driver mcp4531_driver = {
>+      .driver = {
>+              .name   = "mcp4531",
>+      },
>+      .probe          = mcp4531_probe,
>+      .id_table       = mcp4531_id,
>+};
>+
>+module_i2c_driver(mcp4531_driver);
>+
>+MODULE_AUTHOR("Peter Rosin <[email protected]>");
>+MODULE_DESCRIPTION("MCP4531 digital potentiometer");
>+MODULE_LICENSE("GPL");

-- 
Sent from my Android device with K-9 Mail. Please excuse my brevity.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to