From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751812AbdINUZM (ORCPT ); Thu, 14 Sep 2017 16:25:12 -0400 Received: from mail-dm3nam03on0128.outbound.protection.outlook.com ([104.47.41.128]:9536 "EHLO NAM03-DM3-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1751673AbdINUZJ (ORCPT ); Thu, 14 Sep 2017 16:25:09 -0400 Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=Ismail.Kose@maximintegrated.com; From: Ismail Kose To: jic23@kernel.org, knaack.h@gmx.de, lars@metafoo.de, pmeerw@pmeerw.net, robh+dt@kernel.org, mark.rutland@arm.com, vilhelm.gray@gmail.com, linus.walleij@linaro.org, jeff.dagenais@gmail.com, fabrice.gasnier@st.com Cc: gwenhael.goavec-merou@trabucayre.com, peda@axentia.se, maxime.roussinbelanger@gmail.com, mike.looijmans@topic.nl, ihkose@gmail.com, Ismail.Kose@maximintegrated.com, linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH] iio: dac: ds4422/ds4424 dac driver Date: Thu, 14 Sep 2017 13:24:49 -0700 Message-Id: <20170914202449.31283-1-Ismail.Kose@maximintegrated.com> X-Mailer: git-send-email 2.11.0 MIME-Version: 1.0 Content-Type: text/plain X-Originating-IP: [204.17.143.20] X-ClientProxiedBy: DM5PR2001CA0019.namprd20.prod.outlook.com (10.172.43.29) To DM5PR11MB1868.namprd11.prod.outlook.com (10.175.87.9) X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 0678ff0e-668f-4154-4092-08d4fbaeb005 X-MS-Office365-Filtering-HT: Tenant X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:(300000500095)(300135000095)(300000501095)(300135300095)(300000502095)(300135100095)(22001)(2017030254152)(300000503095)(300135400095)(48565401081)(2017052603199)(201703131423075)(201703031133081)(201702281549075)(300000504095)(300135200095)(300000505095)(300135600095)(300000506095)(300135500095);SRVR:DM5PR11MB1868; X-Microsoft-Exchange-Diagnostics: 1;DM5PR11MB1868;3:SB04QWxZ1g08seN4sWEd6VgPAObkEJ+rQ+/805DXA18hJN6iwNKFod2UvlaEbqJuYNRpQiFdPbgy7DxAB+9PvkoWjxbk09o5mMHMp3nG6k+UzHnr5GJpXAVQaEp76bXRJmIggxNRGXuext3hP5Ddve0UFuvnvc7ofp4yE1HsszYOj718yWfdkIy47OpECUDq//rV3il6HOfoNQ0jEhz9ep5J++MZx8A5EJryYLKLCBtcwMHIfzgJANBr28Nl3u/P;25:7i4o20Op93BZ5yjRtv1SPlf7brNuZ8WxOvTYYLEO6XcEnXXaQnkO+5IdEp9oIs9dG2D7JW8w55JWfopxMnSR8QRlEB9wNMq9wosKxN6e7hxypJ/nvBRFliL6aFqunTy5KTE4Hmw430cqaT0jI6os91Gkg7mQtcfbCNxlWBEYvM614No1pOZPYkWi84zBx5zepQnJlXrirgd71dxhrpicn15p9eVswFxPfpNYCtS5T4P74oCTi18P5P/9BZA/sgesOMVS3eu3kK1oFscogwSU1ZWW/c/sLq8NlHnUkfhyYP09UteyAAjbc6jaf/Kb7TSCgAKTiAtt1Om39ODLFihFdw==;31:fjntKHaPJRznQc1Wjv5Hj6yHQI26gtr3h7AXehXILTl7MPlQSDVXdKZvkfnxd01FnsKr4b/K54iCIUrdVfRAHMrCiEi4UVa3hhDg3/7ahx4ADaeNpAJAAEiD6p6mbnqYBy/kRi1o4PLrnO0U0dtNvUIe2DNVKemMZlgZqJWuy5jRmGfQnla49v8uuDYD7YHw/Av9mFHOAwhOK7xwmjVCfegra+g1KvgegLl6nzKytVA= X-MS-TrafficTypeDiagnostic: DM5PR11MB1868: X-Microsoft-Exchange-Diagnostics: 1;DM5PR11MB1868;20:MtaAKQLK/nZ2bTXPSLqr/PI50PUV5AtrJMDOYGr/J4Lcnnz3cofRuvUebfvDDVTenkmz3FLm8wg4MoETpAZi30DjI3Gw6K1gwtTI77D9E+XS15Vo9lX18m+Uw2qnQfyhu/yo2w4uJFdT3vodD6U1WLXL/+Dk66ldFlrSfVn49WqYEp4Kqg87guC/kntgUN4BhenGOHYpRVNfCepJY7cgXlKIWqDSvR3MoAJ3x8Jqq1JaJJCCCL0COnc/1573GD6tid/H3Rx1qVXHEVwafHZWOAwYbxEdYB5Ow+0qgpuodU+JRSOtx4h83hMapDPvX7grExCBx1cGyl4up0EgZsc9wks3XCVN2kNN4Fgr2U89hX2RSf6w0sZ1dxbTKUI12DUDb+ivN8esExL2fdFUQtN8U3PfcPLOIZzBYohQ5J79Nvv1DKJUpThWphP7BRUWyOq4O0vcVcEN04LOcDntFtOqsVk/vwiMh2fr06tHddvytY4B0PgtTFXyWIUDutKziC7h;4:KEa6YZm2ZI437vo2/0gDRQasQK8Sebmt3Fc8mj4hbRon9VgJNiL5Uamwomq5Amzjtxl3l+ZmKwgu+q4FtcKQN7/OQ4zpuFSfuPc4gUUmk+Hdg1C1S/W9EYv/1pBigsA/t9dhyWO0cPHAqEo6oOu6dI7z7TdfNo0IO507VIE44bpQMCqExcbgvrjbMJmnGR2vbziacLTtYaO0bhvDSgVVYQozGqJSXxmmVlnfRRRyu/2LH3/+bQ9iHmD0CYgTzgmr7p6KKEcBq9qS1rJzY7BdwR3ZpKcxflJhkFaOQqrcKhU= X-Exchange-Antispam-Report-Test: UriScan:(108721460000369); X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(100000700101)(100105000095)(100000701101)(100105300095)(100000702101)(100105100095)(6040450)(2401047)(5005006)(8121501046)(10201501046)(100000703101)(100105400095)(93006095)(93001095)(3002001)(6055026)(6041248)(20161123560025)(20161123555025)(201703131423075)(201702281528075)(201703061421075)(201703061406153)(20161123564025)(20161123562025)(20161123558100)(6072148)(201708071742011)(100000704101)(100105200095)(100000705101)(100105500095);SRVR:DM5PR11MB1868;BCL:0;PCL:0;RULEID:(100000800101)(100110000095)(100000801101)(100110300095)(100000802101)(100110100095)(100000803101)(100110400095)(100000804101)(100110200095)(100000805101)(100110500095);SRVR:DM5PR11MB1868; X-Forefront-PRVS: 0430FA5CB7 X-Forefront-Antispam-Report: SFV:NSPM;SFS:(10019020)(6009001)(376002)(39860400002)(346002)(199003)(189002)(6486002)(6666003)(2906002)(97736004)(5660300001)(7416002)(189998001)(68736007)(48376002)(36756003)(8936002)(6116002)(7736002)(3846002)(50466002)(50226002)(81156014)(6506006)(1076002)(50986999)(8676002)(81166006)(53936002)(101416001)(305945005)(6512007)(39060400002)(25786009)(86362001)(66066001)(6306002)(53416004)(69596002)(47776003)(5003940100001)(110136004)(16586007)(105586002)(316002)(4326008)(966005)(106356001)(8666007)(72206003)(16526017)(478600001)(921003)(2004002)(1121003);DIR:OUT;SFP:1102;SCL:1;SRVR:DM5PR11MB1868;H:kose.maxim-ic.com;FPR:;SPF:None;PTR:InfoNoRecords;A:1;MX:1;LANG:en; X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1;DM5PR11MB1868;23:wsAMoKcafrDFiweODgPDDIhoqZPu/B57ZBiTZeLqE?= =?us-ascii?Q?1jZhoU+3Kt1nCvCFBZzdnM02fXqFlmiaUcP3VzJuXqLTpdCiwMCmyhqVOfl7?= =?us-ascii?Q?bYjAo8sV06WQXtY4gou12jEaemxNnHxcoegLmcEj883IVpaLATgchmAn8pKC?= =?us-ascii?Q?fmbp3NT5n929QvFsymcHYEoi+4G5hlI79cdlrX24lPA9126Fz/OHskagY+ye?= =?us-ascii?Q?6rJOQWS5WTM1Nst88akkNV1CykbNkrOpjJy78gGYLLFhqnFSDGr8DYJpdarA?= =?us-ascii?Q?6HsRNnuvXmyxFn3zdyMBYvG+9+/r7hGONFEdcRXtIPnofFXrVX36ey9AQM2K?= =?us-ascii?Q?v172E84PXV8kP/uhW6d1wdi917PWsodySq4PhK5SZHvDqfLh0EOTbKz3CXsU?= =?us-ascii?Q?iyKW+Pr/Kl8jb+Sig3CHGEXuNXn7YEPHRErB5gmywEkuzcXxFOrnAIabj0zD?= =?us-ascii?Q?dnTzWtDBb3vFHvTpcIg/572h3ZGPSIP80eMoC4mlVDRhdytdkcorY+U4MUtH?= =?us-ascii?Q?jYGw0vYat7opPKV2WER0iJBcY73CzVMWg4kEvF66M9cMfVUlvnTsrL7BPjNW?= =?us-ascii?Q?eT0SfLMH4j8CHhBsvpx8tclTojooNmQJ/9OHJNIwrRx1QpriLVEcnLmW0IdW?= =?us-ascii?Q?UTSoGu2p3Ri8xk7BH+WSNgH/knzAKziihf4swDd55LxCm9vgtQ/6nLNaIO6C?= =?us-ascii?Q?j33aGU4CCnrFLYatI4plInaDF2abnOum4jIeg+7uDLNZfVtGSCXcn0KCFVGo?= =?us-ascii?Q?iIYb7DFq2E+xB7BD07V1/xYBbRCGyR/8P49YilDuyl7c4TKpy3nnZF1WM9yp?= =?us-ascii?Q?lWFdb9Hw1EXQmw7aQeekneKEqj1X5fdfWp5HHPt6As5yQTn8FmUB2nvXhq8E?= =?us-ascii?Q?RI3qPoxqTeM4QpnqRdWfJirS2aoVYaGQSoolPkkNPVMAifSI/vqTXnW3O3b7?= =?us-ascii?Q?Jktzw6/Cpjkt4IoJ+buTyfJw2ADVP0ss1wg/YOmjGC2acPpvrRqg0qC41U5c?= =?us-ascii?Q?+Vyef6s2chjvG5WLSk09Ae9iqz5ygLJ0QTPrGlpNlYsupYMr2Wx5JbDeMyVI?= =?us-ascii?Q?JN9JKqEGWAfxmqr7du2I2vNauPsyH6jKJ17/PBruC4aRjch1VvykF2QRKaKn?= =?us-ascii?Q?XMXwnf1WUT+CpOvZVhIG5pDDAnWVsIqVCuqNp5M6BoF0zUqt/YhGgXztV+JO?= =?us-ascii?Q?wajHpqNtbQ+Kf+VE/i17czfTmX7DOPqNTbCqI1289MH66SHTefC6BEdiJjjC?= =?us-ascii?Q?nWCWiPC5TeodNBNlY6XeB0+xq9xDHS/g04cScFo?= X-Microsoft-Exchange-Diagnostics: 1;DM5PR11MB1868;6:wcNPKHFCuCY4WvLb2p1+h6+I/XfNANzJDwTXu5B2kKb6pGfl54hpXPZqNmZT/w53RcuymTVVH1TcyKPQeWvLTWO4w5h0Eg1EdThOfgqCK2wf9LATE5yT0Joqmh1jI0llZkhM+eVyraHbqtMNzPAPKxA0FkRWS0JrsVTDNd8FchBJx+CmD6aDyU9NJKi7Mi/dGc8PSuUa2mml/NQUsVYrPiE8zxVCkOB8hsKhveIN2tx4vtcWVA+ZYojaE/S3iodUGlTh52YKg6v3arnN9d+SzEg/2ze4zoEt05GN+vHnRdhMeX/rt9RYOTJzvuQfWv1DW3sXjI1yoSA97b+gKhh84A==;5:Gbp4cXmsk2MZyKZ3k/NTMMMsyiKrSSzs3JBWRbNJ/bVeDhX9kyeHiRoOWuO+pzaIgSdIbAWS+Jbt9ksKhBa7eskXhGskD/uZduF7LqdmvuX/e/Y8ofCzSEzKln+roBlpcfRm/aOFt4kqSRGz/ECarA==;24:WT6O6VbV+18fzu+owMf+FzFwFHCioL/pkr3+DY7Gl7rAuh9onEC8U7881C+4aJlHJMq67E/dzSPOqnCrhG4fNYAd/VCwHpaZ35XPnYP39Xw=;7:UQztzA6+N7VRJwZZgYo+PG6rEjqgFNj0xnFp5ttOGv+n3sTFoKb5sr/ImGrLhlNowTtgBdjrRBtT9yBo02eI4mTaiaNmxFqnlJvocmCsnLLv9SB3E6GmwOpGQ55NaJMO97M99ChvTWo0aKCT1WJt7lTfW5BGzCDQpkEEh17HfklTN7O5woqsmC8zQruPeFQbCEZjZ6YbjLLwpuFkMgFUQ/1KTa9mIvsKv03b5sRFgIw= SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: maximintegrated.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 14 Sep 2017 20:25:05.1629 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: fbd909df-ea69-4788-a554-f24b7854ad03 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM5PR11MB1868 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: "Ismail H. Kose" This patch provides an iio device driver for DS4422/DS4424 chips that support two/four channel 7-bit Sink/Source Current DAC. The driver supports device tree and platform files for the configurations. Datasheet publicly available at: https://datasheets.maximintegrated.com/en/ds/DS4422-DS4424.pdf Signed-off-by: Ismail Kose --- drivers/iio/dac/Kconfig | 9 + drivers/iio/dac/Makefile | 1 + drivers/iio/dac/ds4424.c | 476 +++++++++++++++++++++++++++++++++++++++++ include/linux/iio/dac/ds4424.h | 24 +++ 4 files changed, 510 insertions(+) create mode 100644 drivers/iio/dac/ds4424.c create mode 100644 include/linux/iio/dac/ds4424.h diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 25bed2d7d2b9..db6ceba1c76f 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -310,4 +310,13 @@ config VF610_DAC This driver can also be built as a module. If so, the module will be called vf610_dac. +config DS4424 + tristate "Maxim Integrated DS4422/DS4424 DAC driver" + depends on I2C + help + If you say yes here you get support for Maxim chips DS4422, DS4424. + + This driver can also be built as a module. If so, the module + will be called ds4424. + endmenu diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 603587cc2f07..f29f929a0587 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -33,3 +33,4 @@ obj-$(CONFIG_MCP4922) += mcp4922.o obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o obj-$(CONFIG_STM32_DAC) += stm32-dac.o obj-$(CONFIG_VF610_DAC) += vf610_dac.o +obj-$(CONFIG_DS4424) += ds4424.o diff --git a/drivers/iio/dac/ds4424.c b/drivers/iio/dac/ds4424.c new file mode 100644 index 000000000000..8765bf20ab62 --- /dev/null +++ b/drivers/iio/dac/ds4424.c @@ -0,0 +1,476 @@ +/* + * Maxim Integrated + * 7-bit, Multi-Channel Sink/Source Current DAC Driver + * Copyright (C) 2017 Maxim Integrated + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DS4424_DAC_ADDR(chan) ((chan) + 0xf8) +#define DS4424_SOURCE_I 1 +#define DS4424_SINK_I 0 + +#define DS4424_CHANNEL(chan) { \ + .type = IIO_CURRENT, \ + .indexed = 1, \ + .output = 1, \ + .channel = chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET), \ + .address = DS4424_DAC_ADDR(chan), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 8, \ + .storagebits = 8, \ + }, \ +} + +union ds4424_raw_data { + struct { + u8 dx:7; + u8 source_bit:1; /* 1 is source, 0 is sink */ + }; + u8 bits; +}; + +enum ds4424_device_ids { + ID_DS4422, + ID_DS4424, +}; + +struct ds4424_data { + struct i2c_client *client; + struct mutex lock; + uint16_t raw[DS4424_MAX_DAC_CHANNELS]; + __maybe_unused uint16_t save[DS4424_MAX_DAC_CHANNELS]; + uint32_t rfs_res[DS4424_MAX_DAC_CHANNELS]; + struct regulator *vcc_reg; +}; + +static struct iio_map ds4424_iio_maps[] = { + { .consumer_dev_name = "ds4424_dac-consumer-dev_name-1", + .consumer_channel = "ds4424_dac1", + .adc_channel_label = "OUT1" + }, + { + .consumer_dev_name = "ds4424_dac-consumer-dev_name-2", + .consumer_channel = "ds4424_dac2", + .adc_channel_label = "OUT2" + }, + { + .consumer_dev_name = "ds4424_dac-consumer-dev_name-3", + .consumer_channel = "ds4424_dac3", + .adc_channel_label = "OUT3" + }, + { + .consumer_dev_name = "ds4424_dac-consumer-dev_name-4", + .consumer_channel = "ds4424_dac4", + .adc_channel_label = "OUT4" + }, + { }, +}; + + +static const struct ds4424_pdata ds4424_pdata = { + .rfs_res = {400, 800, 1000, 1600}, + .dac_iio_map = ds4424_iio_maps, +}; + +static const struct iio_chan_spec ds4424_channels[] = { + DS4424_CHANNEL(0), + DS4424_CHANNEL(1), + DS4424_CHANNEL(2), + DS4424_CHANNEL(3), +}; + +static int ds4424_get_value(struct iio_dev *indio_dev, + int *val, int channel) +{ + struct ds4424_data *data = iio_priv(indio_dev); + int ret; + + mutex_lock(&data->lock); + ret = i2c_smbus_write_byte_data(data->client, + DS4424_DAC_ADDR(channel), 1); + if (ret < 0) + goto fail; + + ret = i2c_smbus_read_byte_data(data->client, 1); + if (ret < 0) + goto fail; + + *val = ret; + mutex_unlock(&data->lock); + return 0; + +fail: + mutex_unlock(&data->lock); + return ret; +} + +/* + * DS4424 DAC control register 8 bits + * [7] 0: to sink; 1: to source + * [6:0] steps to sink/source + * bit[7] looks like a sign bit, but the value of the register is + * not a complemental code considering the bit[6:0] is a absolute + * distance from the zero point. + */ + +/* + * val is positive if sourcing + * val is negative if sinking + * val can be -127 to 127 + */ +static int ds4424_set_value(struct iio_dev *indio_dev, + int val, struct iio_chan_spec const *chan) +{ + struct ds4424_data *data = iio_priv(indio_dev); + int ret; + + if (val < 0 || val > U8_MAX) + return -EINVAL; + + mutex_lock(&data->lock); + ret = i2c_smbus_write_byte_data(data->client, + DS4424_DAC_ADDR(chan->channel), val); + if (ret < 0) { + mutex_unlock(&data->lock); + return ret; + } + + data->raw[chan->channel] = val; + mutex_unlock(&data->lock); + + return 0; +} + +static int ds4424_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + union ds4424_raw_data raw; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = ds4424_get_value(indio_dev, val, chan->channel); + if (ret < 0) { + pr_err("%s : ds4424_get_value returned %d\n", + __func__, ret); + return ret; + } + raw.bits = *val; + *val = raw.dx; + if (raw.source_bit == DS4424_SINK_I) + *val = -*val; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int ds4424_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + union ds4424_raw_data raw; + + if (val2 != 0) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + + if (val > S8_MAX || val < S8_MIN) + return -EINVAL; + + if (val > 0) { + raw.source_bit = DS4424_SOURCE_I; + raw.dx = val; + } else { + raw.source_bit = DS4424_SINK_I; + raw.dx = -val; + } + + return ds4424_set_value(indio_dev, raw.bits, chan); + + default: + return -EINVAL; + } +} + +static int ds4424_verify_chip(struct iio_dev *indio_dev) +{ + int ret = 0, val; + + ret = ds4424_get_value(indio_dev, &val, DS4424_DAC_ADDR(0)); + if (ret < 0) + dev_err(&indio_dev->dev, + "ds4424 i2c read failed. ret: %d\n", ret); + + return ret; +} + +static int __maybe_unused ds4424_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ds4424_data *data = iio_priv(indio_dev); + int ret = 0; + u32 i; + + for (i = 0; i < indio_dev->num_channels; i++) { + data->save[i] = data->raw[i]; + ret = ds4424_set_value(indio_dev, 0, + &indio_dev->channels[i]); + if (ret < 0) + return ret; + } + return ret; +} + +static int __maybe_unused ds4424_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ds4424_data *data = iio_priv(indio_dev); + int ret = 0; + u32 i; + + for (i = 0; i < indio_dev->num_channels; i++) { + ret = ds4424_set_value(indio_dev, data->save[i], + &indio_dev->channels[i]); + if (ret < 0) + return ret; + } + return ret; +} + +static SIMPLE_DEV_PM_OPS(ds4424_pm_ops, ds4424_suspend, ds4424_resume); + +static const struct iio_info ds4424_info = { + .read_raw = ds4424_read_raw, + .write_raw = ds4424_write_raw, + .driver_module = THIS_MODULE, +}; + +#ifdef CONFIG_OF +static int ds4424_parse_dt(struct iio_dev *indio_dev) +{ + int ret; + int len; + int count; + struct property *prop; + struct ds4424_data *data = iio_priv(indio_dev); + struct device_node *node = indio_dev->dev.of_node; + + if (!node) { + pr_info("%s:%d ds4424 DT not found\n", __func__, __LINE__); + return -ENODEV; + } + + prop = of_find_property(node, "maxim,rfs-resistors-ohms", &len); + if (!prop) { + pr_err("ds4424 maxim,rfs-resistor not found in DT.\n"); + return -EINVAL; + } + + if (len != (DS4424_MAX_DAC_CHANNELS * sizeof(uint32_t))) { + pr_err("ds4424 maxim,rfs-resistor length in DT not valid.\n"); + return -EINVAL; + } + + ret = of_property_read_u32_array(node, "maxim,rfs-resistors-ohms", + data->rfs_res, DS4424_MAX_DAC_CHANNELS); + if (ret < 0) { + pr_err("ds4424 reading maxim,rfs-resistors from DT failed.\n"); + return ret; + } + + pr_info("ds4424 maxim,rfs-resistors: %d, %d, %d, %d\n", + data->rfs_res[0], data->rfs_res[1], + data->rfs_res[2], data->rfs_res[3]); + + count = of_property_count_strings(node, "maxim,dac-iio-map"); + if (count < 0) { + pr_info("ds4424 maxim,dac-iio-map not found in DT\n"); + return count; + } + + if (count != DS4424_MAX_DAC_CHANNELS * 3 && + count != DS4424_MAX_DAC_CHANNELS * 3) { + pr_info("ds4424 maxim,dac-iio-map in DT not valid.\n"); + return -EINVAL; + } + + return 0; +} +#else +static int ds4424_parse_dt(struct iio_dev *indio_dev) +{ + return -ENODEV; +} +#endif + +static int ds4424_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct ds4424_pdata *pdata; + struct ds4424_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) { + dev_err(&client->dev, "iio dev alloc failed.\n"); + return -ENOMEM; + } + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + indio_dev->name = id->name; + indio_dev->dev.of_node = client->dev.of_node; + indio_dev->dev.parent = &client->dev; + + if (client->dev.of_node) { + indio_dev->dev.of_node = client->dev.of_node; + ret = ds4424_parse_dt(indio_dev); + if (ret < 0) { + dev_err(&client->dev, + "%s - of_node error\n", __func__); + return -EINVAL; + } + } else { + pdata = client->dev.platform_data; + if (!pdata) { + dev_err(&client->dev, + "dts/platform data not found.\n"); + pdata = &ds4424_pdata; + } + + pdata = client->dev.platform_data; + memcpy(data->rfs_res, pdata->rfs_res, + sizeof(uint32_t) * DS4424_MAX_DAC_CHANNELS); + } + + data->vcc_reg = devm_regulator_get(&client->dev, "vcc"); + if (IS_ERR(data->vcc_reg)) { + ret = PTR_ERR(data->vcc_reg); + dev_err(&client->dev, + "Failed to get vcc-supply regulator: %d\n", ret); + return ret; + } + + mutex_init(&data->lock); + ret = regulator_enable(data->vcc_reg); + if (ret < 0) { + dev_err(&client->dev, + "Unable to enable the regulator.\n"); + return ret; + } + + usleep_range(1000, 1200); + ret = ds4424_verify_chip(indio_dev); + if (ret < 0) + return -ENXIO; + + switch (id->driver_data) { + case ID_DS4422: + indio_dev->num_channels = DS4422_MAX_DAC_CHANNELS; + break; + case ID_DS4424: + indio_dev->num_channels = DS4424_MAX_DAC_CHANNELS; + break; + default: + dev_err(&client->dev, + "ds4424: Invalid chip id.\n"); + indio_dev->num_channels = DS4424_MAX_DAC_CHANNELS; + break; + } + + indio_dev->channels = ds4424_channels; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ds4424_info; + + ret = iio_map_array_register(indio_dev, ds4424_pdata.dac_iio_map); + if (ret < 0) + goto err_iio_map_register; + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&client->dev, + "iio_device_register failed. ret: %d\n", ret); + goto err_iio_dev_register; + } + + return ret; + +err_iio_map_register: + regulator_disable(data->vcc_reg); + +err_iio_dev_register: + iio_map_array_unregister(indio_dev); + return ret; +} + +static int ds4424_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ds4424_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_map_array_unregister(indio_dev); + regulator_disable(data->vcc_reg); + + return 0; +} + +static const struct i2c_device_id ds4424_id[] = { + { "ds4422", ID_DS4422 }, + { "ds4424", ID_DS4424 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ds4424_id); + +static const struct of_device_id ds4424_of_match[] = { + { .compatible = "maxim,ds4422" }, + { .compatible = "maxim,ds4424" }, + { } +}; + +MODULE_DEVICE_TABLE(of, ds4424_of_match); + +static struct i2c_driver ds4424_driver = { + .driver = { + .name = "ds4424", + .of_match_table = ds4424_of_match, + .pm = &ds4424_pm_ops, + }, + .probe = ds4424_probe, + .remove = ds4424_remove, + .id_table = ds4424_id, +}; +module_i2c_driver(ds4424_driver); + +MODULE_DESCRIPTION("Maxim DS4424 DAC Driver"); +MODULE_AUTHOR("Ismail H. Kose "); +MODULE_AUTHOR("Vishal Sood "); +MODULE_AUTHOR("David Jung "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/iio/dac/ds4424.h b/include/linux/iio/dac/ds4424.h new file mode 100644 index 000000000000..2c814118df38 --- /dev/null +++ b/include/linux/iio/dac/ds4424.h @@ -0,0 +1,24 @@ +/* + * Maxim Integrated + * 7-bit, Multi-Channel Sink/Source Current DAC Driver + * Copyright (C) 2017 Maxim Integrated + * + * 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. + */ + +#ifndef IIO_DAC_DS4424_H_ +#define IIO_DAC_DS4424_H_ +#include +#include + +#define DS4422_MAX_DAC_CHANNELS 2 +#define DS4424_MAX_DAC_CHANNELS 4 + +struct ds4424_pdata { + const char *vcc_supply_name; + uint32_t rfs_res[DS4424_MAX_DAC_CHANNELS]; + struct iio_map *dac_iio_map; +}; +#endif /* IIO_DAC_DS4424_H_ */ -- 2.11.0