From patchwork Mon Sep 18 22:09:59 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ismail Kose X-Patchwork-Id: 9957705 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id B9B26601E9 for ; Mon, 18 Sep 2017 22:11:07 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id AA7C0281E1 for ; Mon, 18 Sep 2017 22:11:07 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 9EDF528DB7; Mon, 18 Sep 2017 22:11:07 +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=-6.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=unavailable 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 BDA5B281E1 for ; Mon, 18 Sep 2017 22:11:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751210AbdIRWKv (ORCPT ); Mon, 18 Sep 2017 18:10:51 -0400 Received: from mail-sn1nam01on0103.outbound.protection.outlook.com ([104.47.32.103]:27008 "EHLO NAM01-SN1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1750883AbdIRWKt (ORCPT ); Mon, 18 Sep 2017 18:10:49 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=maximintegrated.onmicrosoft.com; s=selector1-maximintegrated-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version; bh=ROXfUG/Vs/JnQqKVpqfB1/1FXI2WEaik1LRurNH9B4U=; b=WqAuy99Z743a0a0lrOwSu5+VXpSITdGQgN0BrXsI2N2XeV6dhLuYa1lVRdkpRvAFMxSiYJheWhGMwwymPV1CGrobP0kDnillaZAlToqDBnKFEerJu25Lz2w0H3Brnszwxg6w631BaWZTZbR7AOXf2eJHEy3rWwZdsmZkQhk5qog= Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=Ismail.Kose@maximintegrated.com; Received: from kose.maxim-ic.com (204.17.143.20) by BN6PR11MB1858.namprd11.prod.outlook.com (10.175.99.150) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P256) id 15.20.56.11; Mon, 18 Sep 2017 22:10:43 +0000 From: Ismail Kose Cc: ihkose@gmail.com, Ismail.Kose@maximintegrated.com, Jonathan Cameron , Hartmut Knaack , Lars-Peter Clausen , Peter Meerwald-Stadler , Rob Herring , Mark Rutland , Maxime Roussin-Belanger , Mike Looijmans , Peter Rosin , Fabrice Gasnier , Jean-Francois Dagenais , linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v3] iio: dac: ds4422/ds4424 dac driver Date: Mon, 18 Sep 2017 15:09:59 -0700 Message-Id: <20170918221029.3610-1-Ismail.Kose@maximintegrated.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20170623230404.2283-1-Ismail.Kose@maximintegrated.com> References: <20170623230404.2283-1-Ismail.Kose@maximintegrated.com> MIME-Version: 1.0 X-Originating-IP: [204.17.143.20] X-ClientProxiedBy: MWHPR10CA0023.namprd10.prod.outlook.com (10.172.48.33) To BN6PR11MB1858.namprd11.prod.outlook.com (10.175.99.150) X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: ca96d745-a71a-42e6-1592-08d4fee21c38 X-MS-Office365-Filtering-HT: Tenant X-Microsoft-Antispam: UriScan:; BCL:0; PCL:0; RULEID:(300000500095)(300135000095)(300000501095)(300135300095)(22001)(300000502095)(300135100095)(2017030254152)(48565401081)(300000503095)(300135400095)(2017052603199)(201703131423075)(201703031133081)(201702281549075)(300000504095)(300135200095)(300000505095)(300135600095)(300000506095)(300135500095); SRVR:BN6PR11MB1858; X-Microsoft-Exchange-Diagnostics: 1; BN6PR11MB1858; 3:sYCQRF7kQgLph8b3/wHe7iVadLjYEUBVS1Ud5Cf709yQRsRgOkncxnq9TARYwwuQFeSPvbFycNQxIqkug5kaQnZ7i2EJn19kA4kJlf5xGJPKN2YJjLO8BSf7b3UhvRTf758DsE8ZxNqK2Obx/ZO0b7aID7TfY57SwsFdc8Ren4NISRllj+Z4kNXuX2UW2KZrVKC2gBv1uNPzw/74P+y8fn3gjF/gQOFpoJTUtG4sBY8ZiCasqnwcTTLUwCbsHJ8P; 25:5E8p0eroj9N03Bi9Oa1LbNDsT8/GIST1myUbMr0Vrp4A7RKlTQp9chy9H07snpmiQh64HqQtLKyV2n6Ev/a1K+c74zEdDknwa5sZGhjHs/qdv5K9AipjP+9wvkj4gZ4ATpgGXcAUkHFIKWO0FHC+aRxjbEBTsej9cfs3abXLmEifB4VwnxrTQfaJvntP16o08lEs/JSW/ZJG7IUWpPQoHnI9uMv3JqPS+dI431aeMR9pYOW9IGIG0I3RsEK3AMra9IYc68jRjlFJqKRv39lSIXT+YBu97mnQI1g1li/i8GUACTScdrpR4cRJt+BJYUPwSApprF7m4EfQ6BLh55Nx/w==; 31:RbnkTSMxAO4aBbYjwif+tEysFn0zsYP/QRTcejKWqg3NXBlFW+Va+potfnyqidmTYHhjy14bFoNwvi2Ht49xOwjsNiyd51CKPNm2TBu+wrZ4nA8SjyHYfxdr6t1m4x9dqbwHe8ocmJC0yUZS3zTgCFSLmLti2u5BGUoESntvw9qmm7gVJCnKH/3UaJPDHdT/CYEITb263Oyo+G6UpdxIY5jhWi2gbyCKxyx6hYUMBJo= X-MS-TrafficTypeDiagnostic: BN6PR11MB1858: X-Microsoft-Exchange-Diagnostics: 1; BN6PR11MB1858; 20:SYzLsY7Mg+/Jp25UvKV55MeOWFnFnWRWlX73DwjoAqcaz9D6qe5vApaBQKqAIDqxMSrcEnHLWckxPxSjeSrdODlpDwPcyaKPPVQlHhHie+2plc02LDdXksSXiu+rF4SfctpQV92pG9aWOYSOdQ5wylIGcbAIS7UG03iKWDwp2lQqYJZ4JAgGwNNzWWV7rStJ5Zj1tkVn3IimvIwWFnYp9JhysCyLRyWUoNjUqdgvkeaJbD06VG+ZVMlNcJdJkqjVlUi+tMHq+1q4cNhofymu1nJWwSu4+WW9IU1Y+NS4JOMxsjNH6VldxzN+97W7UPZguZgD/gqCLEQTL9EB2ejfb5KqiBko89zb2KTdSGIx56P3WCeR42WD/da2BQZJRVvjQfA8rCmvb7q/ppg5BGOJuJ602gKB3cx/nCY2V9A22C0BmJfr+pRoVugPc0UjuTf4prZkCwhnarxY8dh7iqW2GncaBYZ0hc0+EFnRLdUttVpUAfMmkX1H8EAV1YR9b3tl; 4:BZ+bSbYGHOn2NnK1u76kq/kXarfikwiKEvk81SuK2AYP96AI4AJHEzCPoRExT5Y4VlrSoZtWw09AzxX4mbTTW5jS2/neJuSgB8KcHoub1Y0XDstsU3wO2lRe2oJKN8KsmdAKaeUgEYKvlNkIQWWrJ+9OPNl1sPbcG0wBb0+/PgUIZma4e96tFbSlacF7ySeFJSMuVmy76Z6vYCE+AvV5q2ePaVxIN9iOjrI/ppCYPDcmbPd65awY54suX7W3kg+Fl810pPs2wFriuzvi3QTu0CZhHOJaPqXiQE3Y/NTpui8= 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)(8121501046)(5005006)(100000703101)(100105400095)(93006095)(93001095)(10201501046)(3002001)(6055026)(6041248)(20161123555025)(20161123560025)(201703131423075)(201702281528075)(201703061421075)(201703061406153)(20161123562025)(20161123564025)(20161123558100)(6072148)(201708071742011)(100000704101)(100105200095)(100000705101)(100105500095); SRVR:BN6PR11MB1858; BCL:0; PCL:0; RULEID:(100000800101)(100110000095)(100000801101)(100110300095)(100000802101)(100110100095)(100000803101)(100110400095)(100000804101)(100110200095)(100000805101)(100110500095); SRVR:BN6PR11MB1858; X-Forefront-PRVS: 04347F8039 X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10019020)(6009001)(346002)(39860400002)(376002)(189002)(199003)(8676002)(50986999)(105586002)(6486002)(1076002)(7736002)(25786009)(47776003)(6306002)(6666003)(66066001)(53936002)(5003940100001)(81166006)(109986005)(16586007)(8666007)(316002)(16526017)(86362001)(189998001)(8936002)(81156014)(106356001)(53416004)(3846002)(6506006)(54906002)(97736004)(305945005)(6116002)(6512007)(39060400002)(4326008)(72206003)(48376002)(50466002)(69596002)(2906002)(1671002)(5660300001)(478600001)(101416001)(36756003)(68736007)(50226002)(76176999)(7416002)(2950100002)(266003)(2004002); DIR:OUT; SFP:1102; SCL:1; SRVR:BN6PR11MB1858; H:kose.maxim-ic.com; FPR:; SPF:None; PTR:InfoNoRecords; MX:1; A:1; LANG:en; Received-SPF: None (protection.outlook.com: maximintegrated.com does not designate permitted sender hosts) X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; BN6PR11MB1858; 23:Y9RWbD29QwzJlfq+kS8pdkYogFHIo94G+vuACh6PA?= =?us-ascii?Q?DsKdOoI2OIhZGsY9Aqd1g3UxG7leUekPIFxTRA5LUck9lwv7PuTQGE9brH/S?= =?us-ascii?Q?oqiJAeO1HkpqSsbI8clHHmHkKp5+u2S0P3EaDBI+/rsGW1SqshCnamv+kf8q?= =?us-ascii?Q?E9OrHZPL5KBW7YNG50J+iWB1Gpsg2PjQA7RnVz6+Y3Wcl1cIB5NdT9N0vXoJ?= =?us-ascii?Q?nXPk8CK3Na3a1vMFuPkcyvmzI3g2ps2dElKbGBY14X6Iz6vtrN8vB2kw4my0?= =?us-ascii?Q?Kin7Stz7Y0y4t8S8XQ24GGzxFasJWGUMmDDZBQzpQKpR8QHrvS1lceM4+PTJ?= =?us-ascii?Q?LOE89w3hk6LOK6SPd3ecKjOtrGe9H9PzSBHGUSCIkAHtK41k8g6v8XtqdkgD?= =?us-ascii?Q?pqWx3t0Fw68YB8yvAmofa2ktMlxWt0uc8dWteef8mCYANHNedj+VxYm6Aid2?= =?us-ascii?Q?AKGBl8RwCjwNo3Ue0i+GVgew2rb/rJtbMSd15JkSTNY9Y8rtuvyEjADSDoB2?= =?us-ascii?Q?Ew3Fr6hCclhRzkaWJBNYmhAOU/5FU7jlY+JcZXiaHikhPTfltAbnG5T5DlOD?= =?us-ascii?Q?/YLGTIx8ul7IcusC3/1BMsQYNnKJtutnwYIO73K7D/MdTHKp/pzCDZb5yuh7?= =?us-ascii?Q?SAZty5y83fy8ZpVsPzOEXJ5uB4V7OAREZNUUmbH7bg3BMdezgmO82E2f6sB+?= =?us-ascii?Q?sfey3CRjNNriI68hMzEAQDve4o3SXnSAUcDQeyTMsBVriDSib+IuvXESz+SX?= =?us-ascii?Q?ucW9sdM4Sa513IeAyaIgF3FcEz2+W9tcx6HzxYgAo+K2iEEalxinf2qIchWf?= =?us-ascii?Q?ijPZpSGE5Bo2RxUFgMympfiFEIjM/gMfBeLkfFWsLpMqk2o/QoRbF9uq17Vw?= =?us-ascii?Q?QoUnsBfMQLQ9VCoC4XEV6zfuAlQWotmkWHt5sZe7KWe0u6pIHAiKAgDdFaKF?= =?us-ascii?Q?uhk8UcH1AXFPpPP7QyySi28qbba1xxhb4ZEj53NBWOkjhDp/1FOOiXJub8XY?= =?us-ascii?Q?ggCC+FNNx4D9nSLrbJ++6UmtrVguc4XOZqH708tfsX+GlNCrsURpTBv4sKy5?= =?us-ascii?Q?p9/jbmBaho9foeZSXrt+Xtpj2MOpIDfq/Tu7RiiTFboYPJCBd4zpRX/jLeD5?= =?us-ascii?Q?nvWqsa5zQNS4bZx9lXzrwf1Hb4509ft8J8ctjuOdHjzlXEhI4zGU+mkL9lZE?= =?us-ascii?Q?JcUwnLuqZ16g4yR6wIC1qyPI0w9VHXoYKGx9ab12TOmx6jjREwd9nj6LbLCX?= =?us-ascii?Q?++MGhL12RxplesU1vy7vWYfo24LSB9P84H+hpalvHny6lOlasuxoGhg/PxyH?= =?us-ascii?Q?TPIRwIaku43tgIxqeC8jg8=3D?= X-Microsoft-Exchange-Diagnostics: 1; BN6PR11MB1858; 6:OeUHB7RVvS1p8fTRNIpBW47o+iGd76KFdcwhE1YjhLswQoRkWUFDMsxB23BN7JVN+4evU0O/t9QHCg5V/TdY4Zck/DZJK6rcrVoNGV7cKlUMV4wJZcubrPHW/Zw1qqVoGKHvYerSAfaIFEVPyOyPRVAAwM2fob9SubLIkEvRLbuv/a7uuWsGbitJhreIhnnROagUcfZKw+hicjd3XawT7cLVLK9peDKsD1bYNOrqV8dY76ZeEIp0nGdalv1cVynhSkVxn/La+8oxO8KVg9xPKkQe3ltxTGnEDleyimRi0mU1/cEu2ptwLAVPIm1HgrfE4NkEgjlwJ6clOsM6AjIorw==; 5:LeQQyK7qTw/W+JpuAqy1dNjXSBhPmYJ6FrsUPn4ILMadOiQtZG7lNAExjYoPXT2KA2pHpJoNSsTMELFCpXXkyG6c9aabjNiUot1r45Z+PdBBPO8QK4iJFU6g00LK+koI3x2IDCmrf3SNsKu7b/0nEg==; 24:J6R/aJvwfafMyO5gNiHiXuSrzwEET5/8BrBiFbZ/xhXQytxERkp9EE14bppf53sJ9VxVXH+GAd9Mgo01soHTScDonMkjDE3G2Ukd0EMqVCw=; 7:ATyB9YiipTfn/kgh2MrLKsgqnvJYYTpvr7N6eMBoZZm2/XlPfs78hxQ3rbWeRerLbiMYT445YtSFp/I7CpYPjnJydHhJWQ3ixJS/wN4Vh43M8oGbM3hqvlR7mSlHKDzcENJY1iYmY7ynMihBCGJTDoMJiL6PJB4eEyXIjb69RSnFWcRLyY/WaeGVfakxr4CcckzrXb/VZbb91Gh7FRcZ8MEmVvjd/wG6zsA9P/wYsVw= SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: maximintegrated.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 18 Sep 2017 22:10:43.7479 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: fbd909df-ea69-4788-a554-f24b7854ad03 X-MS-Exchange-Transport-CrossTenantHeadersStamped: BN6PR11MB1858 To: unlisted-recipients:; (no To-header on input) 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 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. Signed-off-by: Ismail Kose --- v3: * Removed iio-map and platform file support * Removed ds4424.h file * Fixed ADC value read bug * Removed confused comments * Added device tree binding file * Fixed bugs in probe and read function v2: (https://lkml.org/lkml/2017/9/14/546) * Fixed coding style and removed unncessary code * Removed min/max rfs, ifs-scale, etc values from device tree * Removed unused code, definitions and some comments * Removed regulator control and made standard structure * Removed data processing and channel scale * Removed device tree binding file .../devicetree/bindings/iio/dac/ds4424.txt | 20 ++ drivers/iio/dac/Kconfig | 9 + drivers/iio/dac/Makefile | 1 + drivers/iio/dac/ds4424.c | 399 +++++++++++++++++++++ 4 files changed, 429 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/dac/ds4424.txt create mode 100644 drivers/iio/dac/ds4424.c diff --git a/Documentation/devicetree/bindings/iio/dac/ds4424.txt b/Documentation/devicetree/bindings/iio/dac/ds4424.txt new file mode 100644 index 000000000000..840b11e1d813 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/ds4424.txt @@ -0,0 +1,20 @@ +Maxim Integrated DS4422/DS4424 7-bit Sink/Source Current DAC Device Driver + +Datasheet publicly available at: +https://datasheets.maximintegrated.com/en/ds/DS4422-DS4424.pdf + +Required properties: + - compatible: Must be "maxim,ds4422" or "maxim,ds4424" + - reg: Should contain the DAC I2C address + - maxim,rfs-resistors-ohms: Should contain reference resistor + +Optional properties: + - vcc-supply: Power supply is optional. If not defined, driver will ignore it. + +Example: + ds4224@10 { + compatible = "maxim,ds4424"; + reg = <0x10>; /* When A0, A1 pins are ground */ + vcc-supply = "dac_vcc_3v3"; + maxim,rfs-resistors-ohms = <400 800 1000 1600>; + }; 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..46ae28081cf0 --- /dev/null +++ b/drivers/iio/dac/ds4424.c @@ -0,0 +1,399 @@ +/* + * 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 + +#define DS4422_MAX_DAC_CHANNELS 2 +#define DS4424_MAX_DAC_CHANNELS 4 + +#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), \ + .address = DS4424_DAC_ADDR(chan), \ +} + +/* + * 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 two's complement code considering the bit[6:0] is a absolute + * distance from the zero point. + */ +union ds4424_raw_data { + struct { + u8 dx:7; + u8 source_bit:1; + }; + u8 bits; +}; + +enum ds4424_device_ids { + ID_DS4422, + ID_DS4424, +}; + +struct ds4424_data { + struct i2c_client *client; + struct mutex lock; + __maybe_unused uint16_t save[DS4424_MAX_DAC_CHANNELS]; + uint32_t rfs_res[DS4424_MAX_DAC_CHANNELS]; + struct regulator *vcc_reg; + uint8_t raw[DS4424_MAX_DAC_CHANNELS]; +}; + +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_read_byte_data(data->client, DS4424_DAC_ADDR(channel)); + if (ret < 0) + goto fail; + + *val = ret; + mutex_unlock(&data->lock); + return 0; + +fail: + mutex_unlock(&data->lock); + return ret; +} + +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_MIN || val > S8_MAX) + 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, + "%s failed. ret: %d\n", __func__, 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; + int 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; + int 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, +}; + +#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]); + + 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) +{ + 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) { + dev_err(&client->dev, + "Not found DT.\n"); + return -EPERM; + } + + 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; + } + + 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"); + regulator_disable(data->vcc_reg); + return -ENXIO; + } + + indio_dev->channels = ds4424_channels; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ds4424_info; + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&client->dev, + "iio_device_register failed. ret: %d\n", ret); + regulator_disable(data->vcc_reg); + } + + 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); + 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");