Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756919AbcJWWlL (ORCPT ); Sun, 23 Oct 2016 18:41:11 -0400 Received: from mail-db5eur01on0133.outbound.protection.outlook.com ([104.47.2.133]:26400 "EHLO EUR01-DB5-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1756769AbcJWWlG (ORCPT ); Sun, 23 Oct 2016 18:41:06 -0400 Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=peda@axentia.se; From: Peter Rosin To: CC: Peter Rosin , Jonathan Cameron , Hartmut Knaack , Lars-Peter Clausen , Peter Meerwald-Stadler , Rob Herring , Mark Rutland , , Subject: [PATCH v3 6/8] iio: dpot-dac: DAC driver based on a digital potentiometer Date: Mon, 24 Oct 2016 00:39:39 +0200 Message-ID: <1477262381-7800-7-git-send-email-peda@axentia.se> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1477262381-7800-1-git-send-email-peda@axentia.se> References: <1477262381-7800-1-git-send-email-peda@axentia.se> MIME-Version: 1.0 Content-Type: text/plain X-Originating-IP: [217.210.101.82] X-ClientProxiedBy: DB5PR08CA0016.eurprd08.prod.outlook.com (10.163.102.154) To DB6PR0201MB2312.eurprd02.prod.outlook.com (10.169.222.151) X-MS-Office365-Filtering-Correlation-Id: 14f317fd-c28b-44b1-d512-08d3fb95ab8b X-Microsoft-Exchange-Diagnostics: 1;DB6PR0201MB2312;2:kvyIWpDAlJQPP6ktzDwrpU3CksRJfST6VBk980usL3/XkQGC/LAx3crc6IYfjqsUmQ67F/uyC5/TH7y68sPNCPpo7GagIWRXfLHsgAuSW+USCz+CjwOvz2YKD5yjnu8Ld3Fh8OePywDX+S3b8JF3U7g4c+19eWjXDAj6a8i6GAyGR60FmIXOBNWJvfxIZvWQF6ZgOmcbNqoKf1VEExLzJg==;3:QXl634FyMO+CgGkdnm6hww2Hdc9W1+7lE9mDnMT3SMI2IJd+CHqWF2EfKYoF6QuNUOCM75Luz8UuV5dcBFNKEMRDlJ2jZzoMZ5rJCsUDVdgfxEvDdnS2qcZgbLll6jsYsYNUTNvcplcHvJ3Ey8WWJg== X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:DB6PR0201MB2312; X-Microsoft-Exchange-Diagnostics: 1;DB6PR0201MB2312;25:O7iU/sPcWAaIj8JrV70cqddHc+yK/oif52h58jAD0VR71E1GM/NMFEMyzQuKmJTIamkjbSWayFTenyDD9To/K7BoO1YUzVRAkNpB5lbpAMV0gR9lIfNfnZgE2lZxXchA9EmRMzIaxqJQ4oXGkUkRcnuhscfQ7qh5jHjWRmzaR1tREgSErj53WfT5UilAMG1aqSqX3lwbi3mL/ed0QrakgOItief90lKjxGiIEmRIXB0+RCeCmZznnNMZdWjHznUDC2DxsXVK/uLuC8RZM5a9LSBIlCobsIKiAioLX7zAwzp9lfwzF5UP/03EEjLM0zk9ep7Gqbhb7aA3Y4TLZPQnDy10ehDBv5dNX3ivCCHEN4ISD/t9XRgoPNuessOyUgZ5DN2TOZxVgH5dSCsZnt8YG9VX4Ee2n1Uqp6BVzg8V4kEi1L3kdJu38+tNzn04He6YyV3ipnFKpVs2WWsExQ07KiUdJ/nNJ0zYnqbM4JVKE6IkE2YUlli7RTXraeA5V4slY5Rwl12RIqSh/0xoar0talx9HOKFIrMvsxfh0xYgVY/Cc+7rUn72XIXSYnxta3qBpHK9e8/rrQTXQ9BQ2zRUyppqXIFieNcrrFYV8JIkfzQa2+3gKRKVFU76lxYJqe3okL5c/5/v64iu9ZObfbzTljm8YxCqGCobi7zwFqKGxrxk349RzGeikVQmwEDvAVh76gxCK+RjKJavMkQlcu1xMz3g7avzWEQsYUnmL4DmGOyGbR51Hh7krAxEy58HaOKeb+Sdjyl3mp7AkbohmAvnIw== X-Microsoft-Exchange-Diagnostics: 1;DB6PR0201MB2312;31:ko2go0g+6Gqba94HYsSK1i9q4Pq1fGFN/CilfBPO2p4YfY2MqmCa3C9R5+yl+CieEOMcZ8OnLeS7vDGw6I1xMXFT4YSJiJLvIVZ+LE8gx6y2gWcgujCXIH2XwuWFlEMEX292eI2c1oy1iRT4MT5vJCjuyUVDWyth5373LEepAVAzuVD2/lyPXs7AgiXO5R5/jOfbh44Oh+kCL0PoTPTWPuHd8jib3nPQ15HxCSeTqNhoMIwlKTX3UQ4Vi0OMhVeW;4:GEuhxqXyr2pxNym8N3RB2/MKzpPTK9uVVsE+b9h3E4jbBkTYSf8d+IX5uwpoCv8jk996Ds19I2iFFa/nj41KRK2DGNYvH7ZfrOOoG/hUede4/4j0fOpINKRgYjVOTQsIbqOCxUDSj8Ln7t5NOHcnWRUtxRpXIzKoFozChVc0WZmY12n9fOwSAJ+nMlaidXkiFEx4kqfQJThNpKPXNRGvWTmSY3dG4EeCHqzPDaFrfKUQhmjidOI089Rxm6rWXsx7YATNe774nJa1OmkudTJlxqEfdMSFB33ni3jzXcOd7q3keQ98A+VDvmV9iHt/3V4oL0XhilHr7vFZP+kfBSD0ry3Bp2+hQrHjac/wgJ0gmkK/QimjT/Dvcxswa7cJeE+eRzbsT1ViCz+0W3907J5vqJ4yY5Eg0FJ2x0i41ofKdLy31QLZIB0fqUmiVvMu8+zo0X/SSHuDZOdVXsELBQscw+joV30WOd69ml/L+wXTmE4= X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:(9452136761055); X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(6040176)(601004)(2401047)(5005006)(8121501046)(10201501046)(3002001)(6043046)(6042046);SRVR:DB6PR0201MB2312;BCL:0;PCL:0;RULEID:;SRVR:DB6PR0201MB2312; X-Forefront-PRVS: 0104247462 X-Forefront-Antispam-Report: SFV:NSPM;SFS:(10019020)(4630300001)(6009001)(6069001)(7916002)(189002)(199003)(50944005)(5660300001)(68736007)(4326007)(97736004)(50226002)(5003940100001)(2906002)(77096005)(81156014)(92566002)(81166006)(8676002)(101416001)(6116002)(3846002)(586003)(86362001)(575784001)(189998001)(19580405001)(19580395003)(66066001)(47776003)(6916009)(2950100002)(6666003)(7846002)(8666005)(7736002)(33646002)(76176999)(50986999)(110136003)(48376002)(305945005)(50466002)(36756003)(74482002)(229853001)(2351001)(42186005)(106356001)(105586002)(7059030)(2004002)(42262002);DIR:OUT;SFP:1102;SCL:1;SRVR:DB6PR0201MB2312;H:localhost.localdomain;FPR:;SPF:None;PTR:InfoNoRecords;MX:1;A:1;LANG:en; X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1;DB6PR0201MB2312;23:d4ATh5vPg46kE1BeqrX8F240ggYbvg0WypV2/bn?= =?us-ascii?Q?Jmyor421WRj5bDGGgfDJ7JXFOwpa84Rj8InkwMZiIw3qcOaBKJ/qPSB2G1n7?= =?us-ascii?Q?gHgN0HlNuZfu4Qk+NA9xvoIJv/kUmmSTYI4r/A8hE1v3kxI96O0+0jYfyj2L?= =?us-ascii?Q?ZWufrNxL2tdhG0YIcOgDEG7wqbfNCeJEsQoQrgXL1uFJ1fSsm6y41f+GSHsU?= =?us-ascii?Q?JHeBOF0mxp0Vbtu2tmoZZN/Dv90E/uBqnGlzuf9Z6l8XzyicgvC+EfJzuYjl?= =?us-ascii?Q?8dQbTqh+MPuhnznHiBov9qk3hVy6rl6dpF3YmS2OjBe2H8+Lg5rZGuLf8XaC?= =?us-ascii?Q?jLMVnW7QXQa7BxlLKYuvz4DkIe5HfIQQxUGNcLCnnGOeRDztzp0NxEk2dLjE?= =?us-ascii?Q?cGAr8TfT+n/bp1e+nZIWXXXwO+ToxjnS9sdy7jSoMxvimBuhnv4qXEUlO4ko?= =?us-ascii?Q?23fgj5betTrgVkqRI+wii4PpzB+//E950kqUzclqFz31iBvfiDax6oIUhfsS?= =?us-ascii?Q?l6dKtRmgnbfEpNX4XuUECYZR9azaKLuljj7bCKdpphleFOnjvK7G+ZtYps80?= =?us-ascii?Q?K2A0/moyW8G/Ln+y4yaEHhjlVcRTKyekAcYlIx1Mf4bkmFWaJpMlMe4jnBRc?= =?us-ascii?Q?o2kWZiLSbskkXnkVMv43PiVp8xZ9ryD0VLa4RwI/qKPYx/8wtdpb33tC2sCH?= =?us-ascii?Q?vLUirYEoXqQ8eUSZp7l9uGSUtaXhSzlPRT4arMBRMh5JgRL6QnyvSnnPuhQQ?= =?us-ascii?Q?8bDhUmGLWj+UTrXRqhFajmJZqeiJV17o48PI/TV4YNJsx3qGw82FzELoPLDP?= =?us-ascii?Q?iMrKIBxDnx2WfawtAVuQPakIZBcBtIEOkSNP1harIQwsHgz97gnKvPchlI7d?= =?us-ascii?Q?IzXGpHYYVOfM36lJXVBU+jYIgScV9AXgcdItdhiBlsBym6w9V63huGpvHF2+?= =?us-ascii?Q?hs3vzo4I6ZtU29yNUm+4qsuMZ66vDU9egCNTC3n9JEMdw+gUYujMLsTYIAWx?= =?us-ascii?Q?spMObdB/ZQNiFQO+vfY5lYRrkZAPf+i9Ah4+cVq2ErsEjiaKB+cn8hApIgll?= =?us-ascii?Q?ncUTfF/wsuEhQkB6I61uHHEZ031FIfM/iJgkMTqtsztYv76i/F7ftO3xRTDZ?= =?us-ascii?Q?CEX47wwnG8kMncihjaxnoDlOdSrC2LpqLIZ9ZDsjm2XHh7R5VIVk/+nY7xjz?= =?us-ascii?Q?J1W+WEFr7vdx1Yfj12uPC+Nqz7eWuePAMWLcDuVxTTK3wxjWGkiZqf1nLEZd?= =?us-ascii?Q?aBjra4jQkAvijdFR9WrQ=3D?= X-Microsoft-Exchange-Diagnostics: 1;DB6PR0201MB2312;6:vYZmELWyjAaieEXfLWr7ez0QfnZ66v8LCP6fM07axWe01SMXSIUIc4MHlGx/r702gvEjlg/5iaVjr1MW5ZqAG/8H1oBAzCrhUVA0oLtAWJkzdwiiRsaHtvjBdrJdJu3D00/e6yrgiMAqNStLhx+smjTOm2TooYBYAuXsQVtCqQeYITco8uhlf68LNhAOr3oa1po91NoEmwtb3RFKOTN9MZWe+gPNw+Hsy5dHe65H9rbYal9GF4BnyGv+Ism21Ew7SFLt3yTgPwpn5Dth5Se5GJ1FiVudSZcEocSMQcNpoeEkUGU7c5zovbGqONE+1dlK8Y3SwZ4w+r7WuZJyJEuO9w==;5:QBTz7EsfDTMlTmP2enOB9nVTDS9SaUsaYLr9LN1kJrVvmKcBjz6EfBrRYfc4zVTDBZ9ez4gexAj8DbLkdoGkM18o0khuXdSAQa932KwkPk7XfvDKBH+5HtMLWuGb2LaLxW03ZzSanANLXvBR2NsnhsLINr3fvkO3K2DXIZYHaG0=;24:mCvi/+Bg1L6PVyjJ3aHl2seCe00CfFrLPegtYF1s5tpQLwAcsJ7RRekdrl/os3IgdbJWSi3HJDNXsrHDZlKhdgUFGBxMEbmJKKmZ2ECrbD8= SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-Microsoft-Exchange-Diagnostics: 1;DB6PR0201MB2312;7:yPNmAMsQFD/EqPeIQrL+tuirMYUOv08KM0nbIDY0YrqyFhA+zYFmXwL+3L4bWQ/zuwiCB8uw4kYElXS1iam2bMC5BW9JzVpOddg6kQG+IZQ+c9sn+tmyxcWvhnpCcosJygFm9FV6yWwXma/hHqiYJfhB4Ap17jp7Yoap6SzH+8BKa8hUzC5o82vZEXXWLimJf/4p4j6IiiDzbnOiyzisPrJN/pjnhaYa+7sdNdtmGEw/zcp5ARMO/eGDrFS45ILBjL8LKsHE+puRrhP39aTyR8S7OZZQwNuXsazZSabSbNNcd+q4MmZ9LdkLrZmKDDBmSyjvsOABouKQYZigdCG4KbojKwO6T/K/olO31Vv48NQ= X-OriginatorOrg: axentia.se X-MS-Exchange-CrossTenant-OriginalArrivalTime: 23 Oct 2016 22:41:02.4609 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB6PR0201MB2312 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 10268 Lines: 364 It is assumed that the dpot is used as a voltage divider between the current dpot wiper setting and the maximum resistance of the dpot. The divided voltage is provided by a vref regulator. .------. .-----------. | | | vref |--' .---. | regulator |--. | | '-----------' | | d | | | p | | | o | wiper | | t |<---------+ | | | | '---' dac output voltage | | '------+------------+ Signed-off-by: Peter Rosin --- .../ABI/testing/sysfs-bus-iio-dac-dpot-dac | 8 + MAINTAINERS | 2 + drivers/iio/dac/Kconfig | 10 + drivers/iio/dac/Makefile | 1 + drivers/iio/dac/dpot-dac.c | 267 +++++++++++++++++++++ 5 files changed, 288 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-dac-dpot-dac create mode 100644 drivers/iio/dac/dpot-dac.c diff --git a/Documentation/ABI/testing/sysfs-bus-iio-dac-dpot-dac b/Documentation/ABI/testing/sysfs-bus-iio-dac-dpot-dac new file mode 100644 index 000000000000..580e93f373f6 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-dac-dpot-dac @@ -0,0 +1,8 @@ +What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_raw_available +Date: October 2016 +KernelVersion: 4.9 +Contact: Peter Rosin +Description: + The range of available values represented as the minimum value, + the step and the maximum value, all enclosed in square brackets. + Example: [0 1 256] diff --git a/MAINTAINERS b/MAINTAINERS index 6218010128dc..d7375f45ff0f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6115,7 +6115,9 @@ IIO DIGITAL POTENTIOMETER DAC M: Peter Rosin L: linux-iio@vger.kernel.org S: Maintained +F: Documentation/ABI/testing/sysfs-bus-iio-dac-dpot-dac F: Documentation/devicetree/bindings/iio/dac/dpot-dac.txt +F: drivers/iio/dac/dpot-dac.c IIO SUBSYSTEM AND DRIVERS M: Jonathan Cameron diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 120b24478469..d3084028905b 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -200,6 +200,16 @@ config AD8801 To compile this driver as a module choose M here: the module will be called ad8801. +config DPOT_DAC + tristate "DAC emulation using a DPOT" + depends on OF + help + Say yes here to build support for DAC emulation using a digital + potentiometer. + + To compile this driver as a module, choose M here: the module will be + called dpot-dac. + config LPC18XX_DAC tristate "NXP LPC18xx DAC driver" depends on ARCH_LPC18XX || COMPILE_TEST diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 27642bbf75f2..f01bf4a99867 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_AD5686) += ad5686.o obj-$(CONFIG_AD7303) += ad7303.o obj-$(CONFIG_AD8801) += ad8801.o obj-$(CONFIG_CIO_DAC) += cio-dac.o +obj-$(CONFIG_DPOT_DAC) += dpot-dac.o obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o obj-$(CONFIG_M62332) += m62332.o obj-$(CONFIG_MAX517) += max517.o diff --git a/drivers/iio/dac/dpot-dac.c b/drivers/iio/dac/dpot-dac.c new file mode 100644 index 000000000000..f227a211d34d --- /dev/null +++ b/drivers/iio/dac/dpot-dac.c @@ -0,0 +1,267 @@ +/* + * IIO DAC emulation driver using a digital potentiometer + * + * Copyright (C) 2016 Axentia Technologies AB + * + * Author: Peter Rosin + * + * 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. + */ + +/* + * It is assumed that the dpot is used as a voltage divider between the + * current dpot wiper setting and the maximum resistance of the dpot. The + * divided voltage is provided by a vref regulator. + * + * .------. + * .-----------. | | + * | vref |--' .---. + * | regulator |--. | | + * '-----------' | | d | + * | | p | + * | | o | wiper + * | | t |<---------+ + * | | | + * | '---' dac output voltage + * | | + * '------+------------+ + */ + +#include +#include +#include +#include +#include +#include +#include + +struct dpot_dac { + struct regulator *vref; + struct iio_channel *dpot; + u32 max_ohms; +}; + +static const struct iio_chan_spec dpot_dac_iio_channel = { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) + | BIT(IIO_CHAN_INFO_SCALE), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), + .output = 1, + .indexed = 1, +}; + +static int dpot_dac_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct dpot_dac *dac = iio_priv(indio_dev); + int ret; + unsigned long long tmp; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return iio_read_channel_raw(dac->dpot, val); + + case IIO_CHAN_INFO_SCALE: + ret = iio_read_channel_scale(dac->dpot, val, val2); + switch (ret) { + case IIO_VAL_FRACTIONAL_LOG2: + tmp = *val * 1000000000LL; + do_div(tmp, dac->max_ohms); + tmp *= regulator_get_voltage(dac->vref) / 1000; + do_div(tmp, 1000000000LL); + *val = tmp; + return ret; + case IIO_VAL_INT: + /* + * Convert integer scale to fractional scale by + * setting the denominator (val2) to one... + */ + *val2 = 1; + ret = IIO_VAL_FRACTIONAL; + /* ...and fall through. */ + case IIO_VAL_FRACTIONAL: + *val *= regulator_get_voltage(dac->vref) / 1000; + *val2 *= dac->max_ohms; + break; + } + + return ret; + } + + return -EINVAL; +} + +static int dpot_dac_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct dpot_dac *dac = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return iio_read_avail_channel_raw(dac->dpot, + vals, type, length); + } + + return -EINVAL; +} + +static int dpot_dac_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct dpot_dac *dac = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return iio_write_channel_raw(dac->dpot, val); + } + + return -EINVAL; +} + +static const struct iio_info dpot_dac_info = { + .read_raw = dpot_dac_read_raw, + .read_avail = dpot_dac_read_avail, + .write_raw = dpot_dac_write_raw, + .driver_module = THIS_MODULE, +}; + +static int dpot_dac_channel_max_ohms(struct iio_dev *indio_dev) +{ + struct device *dev = &indio_dev->dev; + struct dpot_dac *dac = iio_priv(indio_dev); + unsigned long long tmp; + int ret; + int val; + int val2; + int max; + + ret = iio_read_max_channel_raw(dac->dpot, &max); + if (ret < 0) { + dev_err(dev, "dpot does not indicate its raw maximum value\n"); + return ret; + } + + switch (iio_read_channel_scale(dac->dpot, &val, &val2)) { + case IIO_VAL_INT: + return max * val; + case IIO_VAL_FRACTIONAL: + tmp = (unsigned long long)max * val; + do_div(tmp, val2); + return tmp; + case IIO_VAL_FRACTIONAL_LOG2: + tmp = (s64)val * 1000000000LL * max >> val2; + do_div(tmp, 1000000000LL); + return tmp; + default: + dev_err(dev, "dpot has a scale that is too weird\n"); + } + + return -EINVAL; +} + +static int dpot_dac_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct dpot_dac *dac; + enum iio_chan_type type; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*dac)); + if (!indio_dev) + return -ENOMEM; + + platform_set_drvdata(pdev, indio_dev); + dac = iio_priv(indio_dev); + + indio_dev->name = dev_name(dev); + indio_dev->dev.parent = dev; + indio_dev->info = &dpot_dac_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = &dpot_dac_iio_channel; + indio_dev->num_channels = 1; + + dac->vref = devm_regulator_get(dev, "vref"); + if (IS_ERR(dac->vref)) { + if (PTR_ERR(dac->dpot) != -EPROBE_DEFER) + dev_err(&pdev->dev, "failed to get vref regulator\n"); + return PTR_ERR(dac->vref); + } + + dac->dpot = devm_iio_channel_get(dev, "dpot"); + if (IS_ERR(dac->dpot)) { + if (PTR_ERR(dac->dpot) != -EPROBE_DEFER) + dev_err(dev, "failed to get dpot input channel\n"); + return PTR_ERR(dac->dpot); + } + + ret = iio_get_channel_type(dac->dpot, &type); + if (ret < 0) + return ret; + + if (type != IIO_RESISTANCE) { + dev_err(dev, "dpot is of the wrong type\n"); + return -EINVAL; + } + + ret = dpot_dac_channel_max_ohms(indio_dev); + if (ret < 0) + return ret; + dac->max_ohms = ret; + dev_info(dev, "dpot max is %d\n", dac->max_ohms); + + ret = regulator_enable(dac->vref); + if (ret) { + dev_err(dev, "failed to enable the vref regulator\n"); + return ret; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(dev, "failed to register iio device\n"); + goto disable_reg; + } + + return 0; + +disable_reg: + regulator_disable(dac->vref); + return ret; +} + +static int dpot_dac_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct dpot_dac *dac = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + regulator_disable(dac->vref); + + return 0; +} + +static const struct of_device_id dpot_dac_match[] = { + { .compatible = "dpot-dac" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dpot_dac_match); + +static struct platform_driver dpot_dac_driver = { + .probe = dpot_dac_probe, + .remove = dpot_dac_remove, + .driver = { + .name = "iio-dpot-dac", + .of_match_table = dpot_dac_match, + }, +}; +module_platform_driver(dpot_dac_driver); + +MODULE_DESCRIPTION("DAC emulation driver using a digital potentiometer"); +MODULE_AUTHOR("Peter Rosin "); +MODULE_LICENSE("GPL v2"); -- 2.1.4