Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933503AbcJTJ0u (ORCPT ); Thu, 20 Oct 2016 05:26:50 -0400 Received: from mail-db5eur01on0111.outbound.protection.outlook.com ([104.47.2.111]:55104 "EHLO EUR01-DB5-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1758461AbcJTJ0o (ORCPT ); Thu, 20 Oct 2016 05:26:44 -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 2/4] iio: dpot-dac: DAC driver based on a digital potentiometer Date: Thu, 20 Oct 2016 11:26:00 +0200 Message-ID: <1476955562-13673-3-git-send-email-peda@axentia.se> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1476955562-13673-1-git-send-email-peda@axentia.se> References: <1476955562-13673-1-git-send-email-peda@axentia.se> MIME-Version: 1.0 Content-Type: text/plain X-Originating-IP: [217.210.101.82] X-ClientProxiedBy: DB6P18901CA0022.EURP189.PROD.OUTLOOK.COM (10.169.208.160) To AM5PR0201MB2308.eurprd02.prod.outlook.com (10.169.243.7) X-MS-Office365-Filtering-Correlation-Id: c0848fbe-8b54-4c73-d5e8-08d3f8cb32c9 X-Microsoft-Exchange-Diagnostics: 1;AM5PR0201MB2308;2:fyaOn3HeGafTY3nU8vmfoyan2p/s+x968/IK70RsqaG3PBoC/x/4cX/JW/MFhm0lbVRflxRJKoTt48Dw3HZYNMptsSpxpPKvOM+/GdGnem3bCx9iMFe3XEJQ2qfYIk5iFY+X0NTDETx5fgGmxwkK006PiqonMi+HQ1aWsaMzhdiaFwyCV0hK5UOTRYa7z/L/8PSxd3HGQtqcX8Brc56SLA==;3:ujty2HGtojsCtSsQVLSkphZpSzUNiBEJ/oj9Vvrgo0gbb0VrrqnP4PQRWhr+I9IU47sQA9fIpVLfepzxLJSvC73rP81lJJv4rB07uCLfnYOJNsrBRXHtvGFmG0Mmrm5TcUuNnBt6SJ7zxzphP0bBdw== X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:AM5PR0201MB2308; X-Microsoft-Exchange-Diagnostics: 1;AM5PR0201MB2308;25:Q2fyxuMrtrvtYp0ScpfTtKEwklMzTRsRb/oASe7/o+f9q5YbZdhT9apm1L6Z0IOTQwaJmCtMS7GN4/c1jVUs5k5c601CHtoVUoaRTJxJB6LJy54JmeJkKV782MsVzpw79hYcv39jP2sbmehXTqBoq0k8pJdaP+rpb4dHVxoZrBmxObdnij7Za4ZMbT0Y0cwDII9VnGZqq4VjH2DkJkK84aWaT23gBZ4OxZJGddz8NfTGpP3tWoAAn1GYH9gO/EUFFfputD+SOy9xyRlaA5PfHXcTn6cBgZcNlGWnfrIbHqONC+6AdtQRsi+osLHwWjYFk03E2tJuqff2gAC/J7isH+vPJJnxjLDe7QaNNhJjZ6QhM8NgU6Nq3SRuu4EKdvy0+/HDxIMMzqOjS8LpmvARA7DJVPax2Otw0caYq+Q0JtRYoKVtVkLa90IX/15ZwHSQpPk1uVmKLe3Unj3kaYZ37tF23w6G/gWX38rJif71H0fRyKxkC3D/PDxuizEHQP37tdObOHtNJv1kLMwpBowhvqXUzszqBuSiDX+l7PVMKL+J7Gd/8nx0Oabt2UjP/5Indu6mN6COhEyIE6W26+daA02NkgAl2e0E14ygg3hmoxGM9Yfi8WrMqbLMIyyZKH+E7Of/mDAX6Z2aUCpkzNIly9w+eXqHmwUWeJjnNCftkj2hKV6j4SWQTh0J1oWJLPou7UF6tbpVTrstm7cW/mr1muwArgaLsXbnhkcCqEOXiN/RH3pwSv809KcGNZd6OX70 X-Microsoft-Exchange-Diagnostics: 1;AM5PR0201MB2308;31:R+1/XIbncwl/MNEE+cxOVguR1s3J/0I0zj7XMWgXBjQHKQ3speuXBtTQow3Q7bMZn3OevbX7oDxG/IncDmnZgOKzPIMsHQUaYd+59OgM1seHznjgZ+EI9pV+qG7nXM0L+xdIa7R7ZkftcA/Kyykh5btPpTsgM4Or0C5k/wIUJIXlJDLbHp6/fcneO0zzQDzNsvWhVPPQGlHP24tbOJfG5sn206KvCeKspLLAN99Dvi+lLFZeFZHOJPFaFRE1vmT/;4:pTxDcV9M1dtOnVvrXfhlP0SMZbBTjXZP3n5+rr7FpprdhIiQ8yJ9ET6qrP1JA6WvSGipIYySQkYAFa6p6/mEBzHXZ7T/aKav3ciBKAsadMqkTij2092H9Bc7MZiDtKXZnMQpS/L2+bqR2b8oF/d0Nufu9tvH4O8PWJ6G16JMMXGYa/5Q7em1q0xfuu2+NZxndSi74iDk1NVH40GK6JkM/t/j8fzvu65lTyDrqkIXi3O/jFpjn9P58vh0r1uUYceO+qBKnxcMOS/GlNsVYS8PA6b6O/nh/Mf9EhOC2aKAFP591xAv21mRqNRDcqOfKbc3npIIw1e+LXqnXcx+CINsoAq9i7u51juYasoMEfkHF6BXQUrAz6DOCO7bfIUVGsxg/iuMuKOSfVGyFidB1W/jTeA3CpMDdyY6AxgWXqPB5vHI9BErkFBsyJIBt1VGEeioz7wbOYhTIM09Px59Cz7yMjx6gC1rIxEhQLFubpQW5pY= 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:AM5PR0201MB2308;BCL:0;PCL:0;RULEID:;SRVR:AM5PR0201MB2308; X-Forefront-PRVS: 01018CB5B3 X-Forefront-Antispam-Report: SFV:NSPM;SFS:(10019020)(4630300001)(6009001)(6069001)(7916002)(189002)(199003)(6116002)(8666005)(77096005)(97736004)(50226002)(2351001)(19580405001)(19580395003)(68736007)(8676002)(229853001)(36756003)(101416001)(66066001)(189998001)(81166006)(81156014)(92566002)(47776003)(76176999)(50986999)(110136003)(7846002)(6916009)(2906002)(586003)(105586002)(3846002)(6666003)(7736002)(5660300001)(4326007)(50466002)(305945005)(48376002)(74482002)(106356001)(33646002)(86362001)(5003940100001)(2950100002)(42186005)(7059030)(2004002)(42262002);DIR:OUT;SFP:1102;SCL:1;SRVR:AM5PR0201MB2308;H:localhost.localdomain;FPR:;SPF:None;PTR:InfoNoRecords;MX:1;A:1;LANG:en; X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1;AM5PR0201MB2308;23:mqunkOD5ZL5Ufx69PyrO3/HO1WtKwdc/07k+2r2?= =?us-ascii?Q?OZiI76hA0ZVMJNWrGyynsqI83eW0tRnKx9fJSBoGvW+6heQvLzVIu8Qdp15u?= =?us-ascii?Q?g6G7oe2YedufdhLXyzyNSYBodYSd+40jZJkh9IGrZ9h6EuDCz82dZCRxrgt7?= =?us-ascii?Q?BFUqB6o72psGyVzLQXH+DKo5P9bnUlgNlouU6jfaX1aYJsLQBY0mrPhziOI8?= =?us-ascii?Q?uBvc0lkhB8kg/uFYCPKWf8MdFw0wu2/AAsTlgxsoeqajlQeAv32GTONYKYSS?= =?us-ascii?Q?gy7s6v6poelg7mnIZ94njIYfB7YXNIYzl42YiyjOi9D0ZOyDCs3UF6Jz9erl?= =?us-ascii?Q?pAbv108qdmef+wCqGFhaOIsTxFxAQTyBmg+tFxpdUFq13OnOI6gC2je6XUaq?= =?us-ascii?Q?x3wUPZ7yfi5fEzn5vAvqVOPfpIOJ+qmcaFtaiw80KdV64BhZ5h0VwxhFPISC?= =?us-ascii?Q?wPqMeDJVCTXF8v9iBdHpAeCE8lpmQos/kzcNU/aVL32oiOfgS1WByNAswkwx?= =?us-ascii?Q?6/mO+pZa8yPkoW/Okf+If+PlGSVCGoPi9iOusw0XdKAP3mSY+o3WsVd8cPE8?= =?us-ascii?Q?J3yGLc8jhWjkEgbfLfi7nb93TWwSy6BBxDpt4olGxYSeBjXgOCVkYQE4wMfO?= =?us-ascii?Q?A2D+Zl2TuWKjkUpWq+39riOUipXAsKFA7wPi2qbTkzU9sMI2L6g3xZzV254i?= =?us-ascii?Q?U/PF4m5Pnfam6laPUYWmKqWEnI4wcWZPryTv1Lm1Ml7N42x7w/UMdLYNRHJB?= =?us-ascii?Q?/JdTyVi2SGQicON8uhA4SEsPAOzyV2v+7X8Q57SKOsDrQrPy7VbSqBIjBGOc?= =?us-ascii?Q?9c96UxdE4Z4NMDTMP/5KYx1JHADgD0/2YCx2xfQtyrKOAX47my7eXnAo64h6?= =?us-ascii?Q?IAvk7aFrrobX5ykIwCjVTagb9cBVKWMLabCrD/Vzf1t79DSKisSs+ycrzU5s?= =?us-ascii?Q?drRT17sdbxgL/rz7gXzgvTrIfjqaur/U2whRiQyrd4pl0fqxsbLjGUVHYOdn?= =?us-ascii?Q?rXTjGpoH9sD387pToefTY7LyFEMSgz5mwUx7thaIlu7X1AC9liQr0hEiryCD?= =?us-ascii?Q?Xei5yZMmMBBRGiSO6g2u6GcqX1n2rTfBfjFxbAnrDx3sVwwHHp/eUowVzvuW?= =?us-ascii?Q?j9AFNM00Ji8dyK93uVERa9Ff32dVUr59Q2YS/hcdS9ovpTF8y+Wq8jxwj9CJ?= =?us-ascii?Q?w4KBaeK5HhLTWYw+djB47PfPXHqqinpnQ97U7?= X-Microsoft-Exchange-Diagnostics: 1;AM5PR0201MB2308;6:9AT8qArhRyYIBnxjN0L/ji6LUp7+oE6yjPM+nUnCXWu8Ji6lG/FClfeI4xeb/5eT7SipkvHURvTflBJIkjjkOCm1itsJ9BJNywK1D2AD3WDdh+UPmxTfTFgJrPVyp4BGNZtyL2NGgfG2NFQC1xfC6vFafQWaxTKYy0nlUwmMM7lIFb9ssudDQMre7SX3ak/R4eK6p+SN9//+R40iVDku7bU8xJraPXuJbo74ImNaq9VYM7pc7EkY4R2H7ny8sZdHZuDswjNXVJXFcUraVsqGC+ZKwht77jl+JFKTpvjHlfqQMOZEm+3MO7+M0Jd9Ud251HdzNkiwCpunzpZa45GYiw==;5:fKg5KjV2iaEI/vhRSQeOtQtvKOmyinbz3rdKjO2kx/OAZllzKiXz1mAma90fTT3vHRZjc2hVo5swTCLfTJFOs3gNheWDELBE1C2AqOZ0rDAEV35UC22j7eNlA8eGMRXMY+O0qGRjm63D55HDOYYCE6Gg7KdUPCPBlXgM7YuULEw=;24:SI0CzxYrtNE7by+y7vhwMZQFwmfmAjOK4pD+WE/+WXCEbdB86TrXIo4auBtlmsCrzbV8ulyaOoqjPyXEX+X3V8YQoUOYZ7K4ALN4ylO+Zdo= SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-Microsoft-Exchange-Diagnostics: 1;AM5PR0201MB2308;7:5jsbYS3tRY1+neriEhCFNsHOz7cXvH2QDbNWeHoub7V4kq0GYQzma8iCae0tmxzrxtWHLiU5IqedIl6RpKOYfnwfArVjoG58YFEeGIraGLySH8HDCL9n9jb6zfP5lV7ehFxwsnC3bYFzTglaf9F73/77EW1t+KTrjazYYYugJ5hkaQy/VsEXCCfyDjihut/WmrbkSqgSzfvPCXgVSRDXsoQiC2KcMt/SttTy9qIK1BvbDdt4FCeZ5k2/17TyrxgauP8EGlkxuZ682hU4k9AwFoNx+90x1iJ5YPx0cmzSWy6TAqnnkMnvkqeuN4kt4VHTCzDW6fRvVKmSZT1Q63NVr8ap7VfBuSgwg2ky69lEATc= X-OriginatorOrg: axentia.se X-MS-Exchange-CrossTenant-OriginalArrivalTime: 20 Oct 2016 09:26:39.0068 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM5PR0201MB2308 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 8207 Lines: 298 It is assumed the 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 --- MAINTAINERS | 1 + drivers/iio/dac/Kconfig | 10 +++ drivers/iio/dac/Makefile | 1 + drivers/iio/dac/dpot-dac.c | 219 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 231 insertions(+) create mode 100644 drivers/iio/dac/dpot-dac.c diff --git a/MAINTAINERS b/MAINTAINERS index c68b72088945..8c8aae24b96b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6116,6 +6116,7 @@ M: Peter Rosin L: linux-iio@vger.kernel.org S: Maintained 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..934d4138fcdb 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 iio-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..94fbb4b27287 --- /dev/null +++ b/drivers/iio/dac/dpot-dac.c @@ -0,0 +1,219 @@ +/* + * 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 the 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), + .output = 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 = (s64)*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: + *val2 = 1; + ret = IIO_VAL_FRACTIONAL; + break; + } + *val *= regulator_get_voltage(dac->vref) / 1000; + *val2 *= dac->max_ohms; + + return ret; + } + + 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, + .write_raw = dpot_dac_write_raw, + .driver_module = THIS_MODULE, +}; + +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); + } + + switch (iio_read_channel_scale(dac->dpot, &ret, NULL)) { + case IIO_VAL_INT: + case IIO_VAL_FRACTIONAL: + case IIO_VAL_FRACTIONAL_LOG2: + break; + default: + dev_err(dev, "dpot has a scale that is too weird\n"); + return -EINVAL; + } + + 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 = of_property_read_u32(dev->of_node, "dpot-dac,max-ohms", + &dac->max_ohms); + if (ret) { + dev_err(dev, "the dpot-dac,max-ohms property is missing\n"); + return ret; + } + + 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