From patchwork Thu Mar 22 10:23:35 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Sean_Nyekj=C3=A6r?= X-Patchwork-Id: 10301173 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 588F060385 for ; Thu, 22 Mar 2018 10:24:16 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 485C1287CF for ; Thu, 22 Mar 2018 10:24:16 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 3924C29AA1; Thu, 22 Mar 2018 10:24:16 +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=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 EAB4E287CF for ; Thu, 22 Mar 2018 10:24:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754073AbeCVKYN (ORCPT ); Thu, 22 Mar 2018 06:24:13 -0400 Received: from mail-eopbgr50136.outbound.protection.outlook.com ([40.107.5.136]:35411 "EHLO EUR03-VE1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1753342AbeCVKYJ (ORCPT ); Thu, 22 Mar 2018 06:24:09 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=prevasonline.onmicrosoft.com; s=selector1-prevas-dk; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version; bh=1yo1Fx6SubCWX0MHa6pqShyyAB6zLBNbxWjQVlQ38tA=; b=NXDTt7mdYUq6ZsdEh3xahVraeIAtNwxb1zYD/I0VlaGSbMeUJgpb4CnbmAYEkBByvHWJ/7ijKtfzCNixvJXEEcc7+2DZI6bnafkKBnh20CAiz0vVDJjPI6JqvU1uOZ414vHAKjNLXjLku/nam5KweYMSqkp+EMC0TOYzzrqAS6U= Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=Sean.Nyekjaer@prevas.dk; Received: from skn.prevas.se (81.216.59.226) by DB5PR10MB0230.EURPRD10.PROD.OUTLOOK.COM (2603:10a6:0:18::12) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P256) id 15.20.588.14; Thu, 22 Mar 2018 10:24:05 +0000 From: Sean Nyekjaer To: jic23@kernel.org, robh+dt@kernel.org Cc: Sean Nyekjaer , knaack.h@gmx.de, lars@metafoo.de, pmeerw@pmeerw.net, mark.rutland@arm.com, linux-iio@vger.kernel.org, devicetree@vger.kernel.org Subject: [RFC PATCH 1/2] iio: dac: add TI DAC5755 family support Date: Thu, 22 Mar 2018 11:23:35 +0100 Message-Id: <20180322102336.32268-1-sean.nyekjaer@prevas.dk> X-Mailer: git-send-email 2.16.2 MIME-Version: 1.0 X-Originating-IP: [81.216.59.226] X-ClientProxiedBy: HE1PR0701CA0078.eurprd07.prod.outlook.com (2603:10a6:3:64::22) To DB5PR10MB0230.EURPRD10.PROD.OUTLOOK.COM (2603:10a6:0:18::12) X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 975fab40-e21b-4fd3-d38e-08d58fdf0b08 X-Microsoft-Antispam: UriScan:; BCL:0; PCL:0; RULEID:(7020095)(4652020)(5600026)(4604075)(4534165)(4627221)(201703031133081)(201702281549075)(2017052603328)(7153060)(7193020); SRVR:DB5PR10MB0230; X-Microsoft-Exchange-Diagnostics: 1; DB5PR10MB0230; 3:U/Z7uU9EPr7107hwXhHoI+7v+qtK2fEzzkxx/p8yZ0HniTHZNvCuGyvyUYtNZDe1ZQjx3qi3Sv5D0sXL5xPxdYMMaiErcJkNVolPGlOgVa6o7hDhbmyV19E65FEz3qLTEQlAIv7j9iWI+FNhvrp7NWsRhp+2+LFp6QDIv9Ta8y6Zmam+CVHMbRVKQnjZ7uubEffug3j+rLhHvxPEoelA9vgnE840NVf0s1ud/SL9iuLFgLJOgvXlsTclZGe2oscY; 25:ekp0jvCcht/JhIqbvhzWHiBNSnVvsYtvt7QfyR8X2penmB8uu5+j63Zbbgz4OQubf8S1zKl5XZBoyw2y3rm+1WGwb1Sd2nfnGPHSIA6gvu760B7mekr2X7yE0isl7Liti48SdrijZzI+dsugH439ieM3lAX19yvgzeM5YZB85318sl3YFImNux9tQdNrd9k6Lc3bqbeBN25tW73tq7dVlZ2nZ/b4sVOXg6Ay071IB1bASD5pxvXtNI1B61Ko7q1NqodVWsixOI1msVN/7AMvzAGXcPYAPO/bZkffTm7ecnmDKJggvdzJjsQFn2eTUY5OAGvpFS8qyMf7OfBN3Kdxtw==; 31:GT1f+FwSSGY18Bk1AbbT8HA6drBrz3P07ECvBJbxRZPH6z4DHhNprAU33O5FL74ijdj/nG4z8yUz2ib5xG7C/oOTmolPFT+yhX3wxZBupTCIeZriv7nkPOPhGx504V2rliW1eSafOuBXUAd2Ro+car6VGcKvJo1JOXiYc919bsJ3fDG2dK0SvimDRnwQjIYW/0Loy3W/LnCgunY50MRDjYqhrMKC7jYLJC8kvUdbQfg= X-MS-TrafficTypeDiagnostic: DB5PR10MB0230: X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:(31051911155226)(106291317490208); X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(6040522)(2401047)(5005006)(8121501046)(10201501046)(93006095)(93001095)(3002001)(3231221)(944501327)(52105095)(6041310)(20161123562045)(201703131423095)(201702281528075)(20161123555045)(201703061421075)(201703061406153)(20161123564045)(20161123558120)(20161123560045)(6072148)(201708071742011); SRVR:DB5PR10MB0230; BCL:0; PCL:0; RULEID:; SRVR:DB5PR10MB0230; X-Microsoft-Exchange-Diagnostics: 1; DB5PR10MB0230; 4:ULAKgtyZh/x3KYi+3rmHzn/skwuo5uxXxDZaPhx9bMwpm99mE+s7HGtauW08y8iPSziRi8tn9BnKsnTBUeH/Ytk/RzPEplVTKRIEtdTrD6sR3L7GClE/OimvNP0ARbJKC2qeCWxM11QelQ1Iryo67+JEPEXaGY0zgPdQDTJd9Yky/0LeOYbxMa3OQAUO3FGwquhAnsK0icGQ7fDVaMdlF4CKMfmupSMGb0qML7y/u0K4REF6LR4Wu/r6ek3IOvTwAWDXblrnGRgx7RPIT79H7y//VMKfBuRXRCsDkrwicUUZpdmvkeRYwC2ZqtG6QOCbalts1PDnlzUZ5QAirpMHopNA5lCWEotTqoBeWJkvR20= X-Forefront-PRVS: 0619D53754 X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10019020)(39850400004)(396003)(39380400002)(366004)(346002)(376002)(189003)(199004)(105586002)(97736004)(7736002)(6506007)(305945005)(16586007)(316002)(53416004)(4326008)(3846002)(386003)(8976002)(8936002)(575784001)(86362001)(1076002)(6116002)(66066001)(2906002)(36756003)(69596002)(51416003)(16526019)(81166006)(81156014)(186003)(52116002)(25786009)(8666007)(74482002)(6486002)(8676002)(5660300001)(50466002)(47776003)(48376002)(53936002)(6306002)(6666003)(59450400001)(26005)(68736007)(6512007)(72206003)(966005)(50226002)(478600001)(106356001)(2004002)(217873001); DIR:OUT; SFP:1102; SCL:1; SRVR:DB5PR10MB0230; H:skn.prevas.se; FPR:; SPF:None; PTR:InfoNoRecords; MX:1; A:1; LANG:en; Received-SPF: None (protection.outlook.com: prevas.dk does not designate permitted sender hosts) X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; DB5PR10MB0230; 23:h4oMpAaFZOHsiKtqh/O2bNdN+9eV+n0U1P2+BqERA?= =?us-ascii?Q?hINZxZ9kXWym+BiteSuU2nS9kYZovn1EnrH0TVVn6NM4VN42d+SZaP0f/2eW?= =?us-ascii?Q?wwSdKDPand1EqY81xPMsBZCG2hWBQ5fj7+2VmS6hRnXqsV/WqgWzbGxeGmNB?= =?us-ascii?Q?STC0fuF6dxDYQsVYTdZKGN7sJC9xf7RqV/iY4vOQxef/BmC21ekkaF8br9ww?= =?us-ascii?Q?tMpa2UrtpDBcLMdAqhCeSsfMYkb1WgSL1MPC/rn/SedgQTrZ6XHxs4zQbyNF?= =?us-ascii?Q?GcdfE8O/yBOKJC4t0D2ECo9A1C8acYvPzBbGGqNHgvoFbBHCKoEmsiU3DPk0?= =?us-ascii?Q?NJ4//f44qiGa0y7kqBoyvC0hDZg2zt7w34GOd56sE0csCP3ABPVJIoeJTJ7q?= =?us-ascii?Q?gcE4wlGuGvkMfzn0gByxK3OOi7duSv54+9cRFCrrCOnI4cwVzIkD54DgWKY0?= =?us-ascii?Q?NfWuyrUAp5tX0eFbuSw5n1ssVKBtwhqqQzhkMSLuFbf0c9UTQMw4Xkyb/1fa?= =?us-ascii?Q?BUf1ZtyYD8+77jXDfUpGQkDFDpzt0GUpEoC4wiCoRXAoEVGbfH+6Y68vCTDt?= =?us-ascii?Q?K9pRTL30VRT4ppovN2zN4cG3xy/vuIbMotlQlUEwzRFSk0vtsgOjNDjYEqks?= =?us-ascii?Q?/hzV3IQ6+SPKO3RPB5vNcmsfGWSelebI7u8yHIoi0d+C2QKJ+kh+1ILM/P7H?= =?us-ascii?Q?jHy/a+nCqvqH1b3aGqZHtDr5Kgkj1j2Fgd7JV+SgqUhiPlbJq/cOZt0B/sqb?= =?us-ascii?Q?JvS3QdHajSdLSBcPBmKDej5AJnMLICY80gq3ETgFXTi8yo4LG0eRgbboZnvt?= =?us-ascii?Q?fK9Xh4OQDAaYxLPJdXVSuKfTjzsiiK9gnk1zWR/ZsP4Rv1nRa7mSNqf8tRHb?= =?us-ascii?Q?LF3h8dSuihXuXngUcmcSLq05S4K+17oI8AG2Wzq6gF0TA+RnDA5h+1LNLvDz?= =?us-ascii?Q?MppGFNJ8jw3KUg5VyJkiVdWEyL8mzA99YPtQ0kDm+WnOFAzESOks/LBAHFPq?= =?us-ascii?Q?6qI9ctiAQjBlG4Px2C678f0aZoBWypRkn6w3Q8MXGflySqRva316dYjhB6ef?= =?us-ascii?Q?M0Buf+ECAeC1neBfeU17Rmm7aoQdNCyvMf7GACawrMEj3t5CH5DVT8aG+UJD?= =?us-ascii?Q?fE8S5PfjqbXjJmfnmRgwOJFvkGT7ffx25/y9RAdnl+NxVEcAkpkuLOR9id0C?= =?us-ascii?Q?CO3G5rXt1tmdKdJZKRzCU29bVaon116sEWrB/g2E7AgvepmA+omXyFdYlE8I?= =?us-ascii?Q?MkpSBwMuS/pHmdJgdL3pPGCaX0oOaEMSyX3O05SdLLYhdkIHl5sy6Z52dXtU?= =?us-ascii?Q?P1M4ntIvh0b2O/h2/x5VRE=3D?= X-Microsoft-Antispam-Message-Info: 40Gxxi3wCweg+1OEqcfmj931NcOYx4eQe6/HwYy0UbiWdFS9tdmTpp9tsJx8VdSYeE4Ukhf4bKb7TJX8B+jqtF2m/lrpQV+HCe1427tFJFU9wD40Qb6eCoR1J85fzHtSntE91SwmvAHpaT3eDJCz3QNJ33I+ZiQ9Q9IGRB+WwiwJwxTYjoYUtWx+a/xXaDLR X-Microsoft-Exchange-Diagnostics: 1; DB5PR10MB0230; 6:FP1C1PecyErPApljaA4Dtvp6Ep42oKUIMIyXgp123nPMLIg69/7vTqDSgXPaYTp9HXha+9d5qfIc5tsJVuslThYxFc3DkeD0/YQoKLXRok5Lfd2ONZc2kX0J7r4Cj5FbZ7fxQOYDw0CSnpZT6eEmwZ9HN8OtM/YTR2ZTA9DRmR2545djbKd7bC1UxtVpVofnYdP54SusrcpoWoDjYCCKHHZbRiGKVmm2uxzY9+FvhgrLQnxFtxT8vHPQXBgFOWzAXJ1NNAjivYssEeMVijHwSuyfyCY+Q4BjOYBSHyC3d5U4fdlX3HLMzaybhEU2ebkjNOqby2iZJMIxRQJ+1fPvx/+B1HexUUno/FAswiPYdD0=; 5:wcBN6OdsycHTYFINAA2JfsbMtRE1nYrXVgXhYYcLzYy05Vtx/8b305+hed727/7Ft2ueQj8wLQjvR7AuKR+WOle923KwHaB0i9BBitYqD+i0trnhZh4CxOADsCdRta7q9Wn12AO8VmYARZeJ2184RRdUyDijQ5Hznh0Zt53oWu8=; 24:UerrDl6y6F25gHFY/iVW5WZ/VjpkqxS+yR+tV38TZQMcj2ILG+J5YvAVDegH1vIt3eIoJaACEVNhqchF+ozbGCk+tR/5A7mq/TtlEy1q+pk=; 7:WEi5JA+f1/CyVQvl6J5tbprcxhNQHUjHW9Ry1SFjuAnhI+zFPbKcPXF1k/R+Cc7P3vMKgnK88sWyR/VXM18VSG752L81WrcNnLhKwhSRRBxJ0Dek2oh2v5dDq9cPM2Mrh4I9PTgXEJXPhFUh9XXTxeKDjBjVhRt/b9eXcZBD+0+mqa8M/AtT+xLSo9w4PUeT8olt9g+zivotxXUZLUVUjz5NhcyV9rln8Uzp85oJpjsP97deJgHdlokg+im3PrFT SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: prevas.dk X-MS-Exchange-CrossTenant-OriginalArrivalTime: 22 Mar 2018 10:24:05.9805 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 975fab40-e21b-4fd3-d38e-08d58fdf0b08 X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: d350cf71-778d-4780-88f5-071a4cb1ed61 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB5PR10MB0230 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 support for the Texas Intruments DAC5755 Family. Signed-off-by: Sean Nyekjaer --- drivers/iio/dac/Kconfig | 10 ++ drivers/iio/dac/Makefile | 1 + drivers/iio/dac/ti-dac5571.c | 395 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 406 insertions(+) create mode 100644 drivers/iio/dac/ti-dac5571.c diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 965d5c0d2468..c78c02f455b9 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -320,6 +320,16 @@ config TI_DAC082S085 If compiled as a module, it will be called ti-dac082s085. +config TI_DAC5571 + tristate "Texas Instruments 8/10/12/16-bit 1/2/4-channel DAC driver" + depends on I2C + help + Driver for the Texas Instruments + DAC5571, DAC6571, DAC7571, DAC5574, DAC6574, DAC7574, DAC5573, + DAC6573, DAC7573. + + If compiled as a module, it will be called ti-dac5571. + config VF610_DAC tristate "Vybrid vf610 DAC driver" depends on OF diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 81e710ed7491..1ab358d522ee 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -35,4 +35,5 @@ obj-$(CONFIG_MCP4922) += mcp4922.o obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o obj-$(CONFIG_STM32_DAC) += stm32-dac.o obj-$(CONFIG_TI_DAC082S085) += ti-dac082s085.o +obj-$(CONFIG_TI_DAC5571) += ti-dac5571.o obj-$(CONFIG_VF610_DAC) += vf610_dac.o diff --git a/drivers/iio/dac/ti-dac5571.c b/drivers/iio/dac/ti-dac5571.c new file mode 100644 index 000000000000..cf4ba151ed7e --- /dev/null +++ b/drivers/iio/dac/ti-dac5571.c @@ -0,0 +1,395 @@ +/* + * ti-dac5571.c - Texas Instruments 8/10/12-bit 1/4-channel DAC driver + * + * Copyright (C) 2018 Prevas A/S + * + * http://www.ti.com/lit/ds/symlink/dac5571.pdf + * http://www.ti.com/lit/ds/symlink/dac6571.pdf + * http://www.ti.com/lit/ds/symlink/dac7571.pdf + * http://www.ti.com/lit/ds/symlink/dac5574.pdf + * http://www.ti.com/lit/ds/symlink/dac6574.pdf + * http://www.ti.com/lit/ds/symlink/dac7574.pdf + * http://www.ti.com/lit/ds/symlink/dac5573.pdf + * http://www.ti.com/lit/ds/symlink/dac6573.pdf + * http://www.ti.com/lit/ds/symlink/dac7573.pdf + * + * 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 + +enum chip_id { + single_8bit, single_10bit, single_12bit, + quad_8bit, quad_10bit, quad_12bit +}; + +struct dac5571_spec { + u8 num_channels; + u8 resolution; +}; + +static const struct dac5571_spec dac5571_spec[] = { + [single_8bit] = {.num_channels = 1, .resolution = 8}, + [single_10bit] = {.num_channels = 1, .resolution = 10}, + [single_12bit] = {.num_channels = 1, .resolution = 12}, + [quad_8bit] = {.num_channels = 4, .resolution = 8}, + [quad_10bit] = {.num_channels = 4, .resolution = 10}, + [quad_12bit] = {.num_channels = 4, .resolution = 12}, +}; + +struct dac5571_data { + struct i2c_client *client; + int id; + struct mutex lock; + struct regulator *vref; + u16 val[4]; + bool powerdown; + u8 powerdown_mode; + u8 resolution; + u8 channels; + u8 buf[3] ____cacheline_aligned; +}; + +#define POWERDOWN(mode) ((mode) + 1) + +static int dac5571_cmd(struct dac5571_data *data, int channel, + u8 pwrdwn, u16 val) +{ + int ret; + u8 shift; + + switch (data->channels) { + case 1: + shift = 12 - data->resolution; + data->buf[0] = (val << shift); + data->buf[1] = pwrdwn << 4 | (val >> (8 - shift)); + ret = i2c_master_send(data->client, data->buf, 2); + break; + case 4: + shift = 16 - data->resolution; + data->buf[0] = val << shift; + data->buf[1] = val >> (8 - shift) | pwrdwn << 6; + data->buf[2] = channel << 1 | 1 << 4; + if (pwrdwn) + data->buf[2] |= 1; + ret = i2c_master_send(data->client, data->buf, 3); + break; + default: + ret = -EINVAL; + break; + }; + + return ret; +} + +static const char *const dac5571_powerdown_modes[] = { + "1kohm_to_gnd", "100kohm_to_gnd", "three_state", +}; + +static int dac5571_get_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct dac5571_data *data = iio_priv(indio_dev); + + return data->powerdown_mode; +} + +static int dac5571_set_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct dac5571_data *data = iio_priv(indio_dev); + int ret = 0; + + if (data->powerdown_mode == mode) + return 0; + + mutex_lock(&data->lock); + if (data->powerdown) { + ret = dac5571_cmd(data, chan->channel, POWERDOWN(mode), 0); + if (ret) + goto out; + } + data->powerdown_mode = mode; + + out: + mutex_unlock(&data->lock); + return ret; +} + +static const struct iio_enum dac5571_powerdown_mode = { + .items = dac5571_powerdown_modes, + .num_items = ARRAY_SIZE(dac5571_powerdown_modes), + .get = dac5571_get_powerdown_mode, + .set = dac5571_set_powerdown_mode, +}; + +static ssize_t dac5571_read_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct dac5571_data *data = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", data->powerdown); +} + +static ssize_t dac5571_write_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct dac5571_data *data = iio_priv(indio_dev); + bool powerdown; + int ret; + + ret = strtobool(buf, &powerdown); + if (ret) + return ret; + + if (data->powerdown == powerdown) + return len; + + mutex_lock(&data->lock); + if (powerdown) + ret = + dac5571_cmd(data, chan->channel, + POWERDOWN(data->powerdown_mode), 0); + else + ret = dac5571_cmd(data, chan->channel, 0, data->val[0]); + if (!ret) + data->powerdown = powerdown; + mutex_unlock(&data->lock); + + return ret ? ret : len; +} + + +static const struct iio_chan_spec_ext_info dac5571_ext_info[] = { + { + .name = "powerdown", + .read = dac5571_read_powerdown, + .write = dac5571_write_powerdown, + .shared = IIO_SHARED_BY_TYPE, + }, + IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &dac5571_powerdown_mode), + IIO_ENUM_AVAILABLE("powerdown_mode", &dac5571_powerdown_mode), + {}, +}; + +#define dac5571_CHANNEL(chan) { \ + .type = IIO_VOLTAGE, \ + .channel = (chan), \ + .address = (chan), \ + .indexed = true, \ + .output = true, \ + .datasheet_name = (const char[]){ 'A' + (chan), 0 }, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .ext_info = dac5571_ext_info, \ +} + +static const struct iio_chan_spec dac5571_channels[] = { + dac5571_CHANNEL(0), + dac5571_CHANNEL(1), + dac5571_CHANNEL(2), + dac5571_CHANNEL(3), +}; + +static int dac5571_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct dac5571_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + *val = data->val[chan->channel]; + ret = IIO_VAL_INT; + break; + + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(data->vref); + if (ret < 0) + return ret; + + *val = ret / 1000; + *val2 = data->resolution; + ret = IIO_VAL_FRACTIONAL_LOG2; + break; + + default: + ret = -EINVAL; + } + + return ret; +} + +static int dac5571_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct dac5571_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (data->val[chan->channel] == val) + return 0; + + if (val >= (1 << data->resolution) || val < 0) + return -EINVAL; + + if (data->powerdown) + return -EBUSY; + + mutex_lock(&data->lock); + ret = dac5571_cmd(data, chan->channel, 0, val); + if (!ret) + data->val[chan->channel] = val; + mutex_unlock(&data->lock); + break; + + default: + ret = -EINVAL; + } + + return ret; +} + +static int dac5571_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + return IIO_VAL_INT; +} + +static const struct iio_info dac5571_info = { + .read_raw = dac5571_read_raw, + .write_raw = dac5571_write_raw, + .write_raw_get_fmt = dac5571_write_raw_get_fmt, +}; + +static int dac5571_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + const struct dac5571_spec *spec; + struct dac5571_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + if (client->dev.of_node) + data->id = (enum chip_id)of_device_get_match_data(&client->dev); + else + data->id = id->driver_data; + + indio_dev->dev.parent = dev; + indio_dev->dev.of_node = client->dev.of_node; + indio_dev->info = &dac5571_info; + indio_dev->name = id->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = dac5571_channels; + + spec = &dac5571_spec[id->driver_data]; + indio_dev->num_channels = spec->num_channels; + data->resolution = spec->resolution; + data->channels = spec->num_channels; + + data->vref = devm_regulator_get(dev, "vref"); + if (IS_ERR(data->vref)) + return PTR_ERR(data->vref); + + ret = regulator_enable(data->vref); + if (ret < 0) + return ret; + + mutex_init(&data->lock); + + ret = dac5571_cmd(data, 0, 0, 0); + if (ret) { + dev_err(dev, "failed to initialize outputs to 0\n"); + goto err; + } + + ret = iio_device_register(indio_dev); + if (ret) + goto err; + + return 0; + + err: + mutex_destroy(&data->lock); + regulator_disable(data->vref); + return ret; +} + +static int dac5571_remove(struct i2c_client *i2c) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(i2c); + struct dac5571_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + mutex_destroy(&data->lock); + regulator_disable(data->vref); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id dac5571_of_id[] = { + {.compatible = "ti,dac5571"}, + {.compatible = "ti,dac6571"}, + {.compatible = "ti,dac7571"}, + {.compatible = "ti,dac5574"}, + {.compatible = "ti,dac6574"}, + {.compatible = "ti,dac7574"}, + {.compatible = "ti,dac5573"}, + {.compatible = "ti,dac6573"}, + {.compatible = "ti,dac7573"}, + {} +}; +MODULE_DEVICE_TABLE(of, dac5571_of_id); +#endif + +static const struct i2c_device_id dac5571_id[] = { + {"dac5571", single_8bit}, + {"dac6571", single_10bit}, + {"dac7571", single_12bit}, + {"dac5574", quad_8bit}, + {"dac6574", quad_10bit}, + {"dac7574", quad_12bit}, + {"dac5573", quad_8bit}, + {"dac6573", quad_10bit}, + {"dac7573", quad_12bit}, + {} +}; +MODULE_DEVICE_TABLE(i2c, dac5571_id); + +static struct i2c_driver dac5571_driver = { + .driver = { + .name = "ti-dac5571", + }, + .probe = dac5571_probe, + .remove = dac5571_remove, + .id_table = dac5571_id, +}; +module_i2c_driver(dac5571_driver); + +MODULE_AUTHOR("Sean Nyekjaer "); +MODULE_DESCRIPTION("Texas Instruments 8/10/12-bit 1/4-channel DAC driver"); +MODULE_LICENSE("GPL v2");