From patchwork Tue Nov 6 11:31:30 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chris Coffey X-Patchwork-Id: 10670143 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 73A5E15A6 for ; Tue, 6 Nov 2018 11:31:48 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5F83C29C16 for ; Tue, 6 Nov 2018 11:31:48 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 500B629C49; Tue, 6 Nov 2018 11:31:48 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.7 required=2.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 798C329C16 for ; Tue, 6 Nov 2018 11:31:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729272AbeKFU4b (ORCPT ); Tue, 6 Nov 2018 15:56:31 -0500 Received: from mail.babblebit.net ([37.139.8.208]:37834 "EHLO mail.babblebit.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729177AbeKFU4b (ORCPT ); Tue, 6 Nov 2018 15:56:31 -0500 Received: from localhost.localdomain (80-44-164-194.dynamic.dsl.as9105.com [80.44.164.194]) by mail.babblebit.net (Postfix) with ESMTPSA id 3B913201DA; Tue, 6 Nov 2018 11:31:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=babblebit.net; s=mail; t=1541503903; bh=p3A5NXqQam2VsnYQOyu0zIUreCGgvR/oJRo2dJPFQSo=; h=From:To:Cc:Subject:Date:From; b=r9qbLbC3SXUgJahy2qcp1Q5qITtse0v+xIKnb8Qaej9MLn7YnMLgCjOvKo9AGjUTL cV8m4UxOyyrRPY+lJYAOzVS6eBwp4Q00ZAue904XyEfJLAD1xt/XSerLUohepYvtd5 K/h5HR8aQ/coOgtazVjvQ6ORegggKslnQT697BaxxHbEt+SF1UkROTpU0TKStCjpL5 b8Hc15UBcA5M5xuJL+I2cqJNtLWD5Vbfu+iknd6a7ue0AmvVgkUbvDoqQZ2RYTwGRN wPDwC1xm7ISdIyrY0fRcxD5ztCMM2LH7UVSh2IrvU7x/3mq28TXKRQxNprnPOIFDZn DLD+0MMunbYFA== From: Chris Coffey To: Jonathan Cameron , Hartmut Knaack , Lars-Peter Clausen , Peter Meerwald-Stadler Cc: Peter Rosin , Slawomir Stepien , Rob Herring , Mark Rutland , linux-iio@vger.kernel.org, devicetree@vger.kernel.org, Chris Coffey Subject: [PATCH] iio: potentiometer: Add driver for Microchip MCP41xxx/42xxx Date: Tue, 6 Nov 2018 11:31:30 +0000 Message-Id: <20181106113130.9612-1-cmc@babblebit.net> X-Mailer: git-send-email 2.11.0 Sender: linux-iio-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-iio@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch adds driver support for the Microchip MCP41xxx/42xxx family of digital potentiometers: DEVICE Wipers Positions Resistance (kOhm) MCP41010 1 256 10 MCP41050 1 256 50 MCP41100 1 256 100 MCP42010 2 256 10 MCP42050 2 256 50 MCP42100 2 256 100 Datasheet: http://ww1.microchip.com/downloads/en/devicedoc/11195c.pdf Signed-off-by: Chris Coffey --- .../bindings/iio/potentiometer/mcp41010.txt | 29 +++ drivers/iio/potentiometer/Kconfig | 12 ++ drivers/iio/potentiometer/Makefile | 1 + drivers/iio/potentiometer/mcp41010.c | 216 +++++++++++++++++++++ 4 files changed, 258 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/potentiometer/mcp41010.txt create mode 100644 drivers/iio/potentiometer/mcp41010.c diff --git a/Documentation/devicetree/bindings/iio/potentiometer/mcp41010.txt b/Documentation/devicetree/bindings/iio/potentiometer/mcp41010.txt new file mode 100644 index 0000000000..17565acace --- /dev/null +++ b/Documentation/devicetree/bindings/iio/potentiometer/mcp41010.txt @@ -0,0 +1,29 @@ +* Microchip MCP41010/41050/41100/42010/42050/42100 Digital Potentiometer + driver + +Datasheet publicly available at: +http://ww1.microchip.com/downloads/en/devicedoc/11195c.pdf + +The node for this driver must be a child node of a SPI controller, hence +all mandatory properties described in + + Documentation/devicetree/bindings/spi/spi-bus.txt + +must be specified. + +Required properties: + - compatible: Must be one of the following, depending on the + model: + "microchip,mcp41010" + "microchip,mcp41050" + "microchip,mcp41100" + "microchip,mcp42010" + "microchip,mcp42050" + "microchip,mcp42100" + +Example: +mcp41010: potentiometer@0 { + compatible = "mcp41010"; + reg = <0>; + spi-max-frequency = <500000>; +}; diff --git a/drivers/iio/potentiometer/Kconfig b/drivers/iio/potentiometer/Kconfig index 79ec2eba49..6303cbe799 100644 --- a/drivers/iio/potentiometer/Kconfig +++ b/drivers/iio/potentiometer/Kconfig @@ -90,6 +90,18 @@ config MCP4531 To compile this driver as a module, choose M here: the module will be called mcp4531. +config MCP41010 + tristate "Microchip MCP41xxx/MCP42xxx Digital Potentiometer driver" + depends on SPI + help + Say yes here to build support for the Microchip + MCP41010, MCP41050, MCP41100, + MCP42010, MCP42050, MCP42100 + digital potentiometer chips. + + To compile this driver as a module, choose M here: the + module will be called mcp41010. + config TPL0102 tristate "Texas Instruments digital potentiometer driver" depends on I2C diff --git a/drivers/iio/potentiometer/Makefile b/drivers/iio/potentiometer/Makefile index 4af657883c..8ff55138cf 100644 --- a/drivers/iio/potentiometer/Makefile +++ b/drivers/iio/potentiometer/Makefile @@ -11,4 +11,5 @@ obj-$(CONFIG_MAX5487) += max5487.o obj-$(CONFIG_MCP4018) += mcp4018.o obj-$(CONFIG_MCP4131) += mcp4131.o obj-$(CONFIG_MCP4531) += mcp4531.o +obj-$(CONFIG_MCP41010) += mcp41010.o obj-$(CONFIG_TPL0102) += tpl0102.o diff --git a/drivers/iio/potentiometer/mcp41010.c b/drivers/iio/potentiometer/mcp41010.c new file mode 100644 index 0000000000..4068e8eb57 --- /dev/null +++ b/drivers/iio/potentiometer/mcp41010.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Industrial I/O driver for Microchip digital potentiometers + * + * Copyright (c) 2018 Chris Coffey + * Based on: Slawomir Stepien's code from mcp4131.c + * + * Datasheet: http://ww1.microchip.com/downloads/en/devicedoc/11195c.pdf + * + * DEVID #Wipers #Positions Resistance (kOhm) + * mcp41010 1 256 10 + * mcp41050 1 256 50 + * mcp41100 1 256 100 + * mcp42010 2 256 10 + * mcp42050 2 256 50 + * mcp42100 2 256 100 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MCP41010_MAX_WIPERS 2 +#define MCP41010_WRITE (0x01 << 4) +#define MCP41010_WIPER_MAX 255 +#define MCP41010_WIPER_ENABLE BIT(0) + +struct mcp41010_cfg { + int wipers; + int kohms; +}; + +enum mcp41010_type { + MCP41010 = 0, + MCP41050, + MCP41100, + MCP42010, + MCP42050, + MCP42100, +}; + +static const struct mcp41010_cfg mcp41010_cfg[] = { + [MCP41010] = { .wipers = 1, .kohms = 10, }, + [MCP41050] = { .wipers = 1, .kohms = 50, }, + [MCP41100] = { .wipers = 1, .kohms = 100, }, + [MCP42010] = { .wipers = 2, .kohms = 10, }, + [MCP42050] = { .wipers = 2, .kohms = 50, }, + [MCP42100] = { .wipers = 2, .kohms = 100, }, +}; + +struct mcp41010_data { + struct spi_device *spi; + const struct mcp41010_cfg *cfg; + struct mutex lock; /* Protect write sequences */ + unsigned int value[MCP41010_MAX_WIPERS]; /* Cache wiper values */ + u8 buf[2] ____cacheline_aligned; +}; + +#define MCP41010_CHANNEL(ch) { \ + .type = IIO_RESISTANCE, \ + .indexed = 1, \ + .output = 1, \ + .channel = (ch), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +static const struct iio_chan_spec mcp41010_channels[] = { + MCP41010_CHANNEL(0), + MCP41010_CHANNEL(1), +}; + +static int mcp41010_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct mcp41010_data *data = iio_priv(indio_dev); + int channel = chan->channel; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + *val = data->value[channel]; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = 1000 * data->cfg->kohms; + *val2 = MCP41010_WIPER_MAX; + return IIO_VAL_FRACTIONAL; + } + + return -EINVAL; +} + +static int mcp41010_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int err; + struct mcp41010_data *data = iio_priv(indio_dev); + int channel = chan->channel; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val > MCP41010_WIPER_MAX || val < 0) + return -EINVAL; + break; + + default: + return -EINVAL; + } + + mutex_lock(&data->lock); + + data->buf[0] = MCP41010_WIPER_ENABLE << channel; + data->buf[0] |= MCP41010_WRITE; + data->buf[1] = val & 0xff; + + err = spi_write(data->spi, data->buf, 2); + if (!err) + data->value[channel] = val; + + mutex_unlock(&data->lock); + + return err; +} + +static const struct iio_info mcp41010_info = { + .read_raw = mcp41010_read_raw, + .write_raw = mcp41010_write_raw, +}; + +static int mcp41010_probe(struct spi_device *spi) +{ + int err; + struct device *dev = &spi->dev; + unsigned long devid = spi_get_device_id(spi)->driver_data; + struct mcp41010_data *data; + struct iio_dev *indio_dev; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + data->spi = spi; + data->cfg = &mcp41010_cfg[devid]; + + mutex_init(&data->lock); + + indio_dev->dev.parent = dev; + indio_dev->info = &mcp41010_info; + indio_dev->channels = mcp41010_channels; + indio_dev->num_channels = data->cfg->wipers; + indio_dev->name = spi_get_device_id(spi)->name; + + err = devm_iio_device_register(dev, indio_dev); + if (err) { + dev_info(&spi->dev, "Unable to register %s\n", indio_dev->name); + return err; + } + + return 0; +} + +#if defined(CONFIG_OF) +static const struct of_device_id mcp41010_dt_ids[] = { + { .compatible = "microchip,mcp41010", + .data = &mcp41010_cfg[MCP41010] }, + { .compatible = "microchip,mcp41050", + .data = &mcp41010_cfg[MCP41050] }, + { .compatible = "microchip,mcp41100", + .data = &mcp41010_cfg[MCP41100] }, + { .compatible = "microchip,mcp42010", + .data = &mcp41010_cfg[MCP42010] }, + { .compatible = "microchip,mcp42050", + .data = &mcp41010_cfg[MCP42050] }, + { .compatible = "microchip,mcp42100", + .data = &mcp41010_cfg[MCP42100] }, + {} +}; +MODULE_DEVICE_TABLE(of, mcp41010_dt_ids); +#endif /* CONFIG_OF */ + +static const struct spi_device_id mcp41010_id[] = { + { "mcp41010", MCP41010 }, + { "mcp41050", MCP41050 }, + { "mcp41100", MCP41100 }, + { "mcp42010", MCP42010 }, + { "mcp42050", MCP42050 }, + { "mcp42100", MCP42100 }, + {} +}; +MODULE_DEVICE_TABLE(spi, mcp41010_id); + +static struct spi_driver mcp41010_driver = { + .driver = { + .name = "mcp41010", + .of_match_table = of_match_ptr(mcp41010_dt_ids), + }, + .probe = mcp41010_probe, + .id_table = mcp41010_id, +}; + +module_spi_driver(mcp41010_driver); + +MODULE_AUTHOR("Chris Coffey "); +MODULE_DESCRIPTION("MCP41010 digital potentiometer"); +MODULE_LICENSE("GPL v2");