From patchwork Tue May 1 08:15:53 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: 10373253 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 B5B536032A for ; Tue, 1 May 2018 08:17:51 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A380E212DA for ; Tue, 1 May 2018 08:17:51 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 94EE928985; Tue, 1 May 2018 08:17:51 +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=-7.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, MAILING_LIST_MULTI, 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 64F98212DA for ; Tue, 1 May 2018 08:17:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752411AbeEAIRm (ORCPT ); Tue, 1 May 2018 04:17:42 -0400 Received: from mail-he1eur01on0130.outbound.protection.outlook.com ([104.47.0.130]:62841 "EHLO EUR01-HE1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1751081AbeEAIR3 (ORCPT ); Tue, 1 May 2018 04:17:29 -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=VKyFgMURo6vTL9TRSq/PiNNfiomH+YNKry+aoxle50Q=; b=kors20c3M1v2kMYR50dCnhWyjfvA9SwChHNcpQscyBSmtJZj1m0bi75x23FWGIe4CMnjC//Jg/uNN8TGO8O65gDLxfgD3Ius8Aic1FHTumy2PQKt+aYzgdQtj5bXOkDQ2MAEEHJm/kbdY6tm1BG69WI55ZC+HI4eTxEGaggkNmI= Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=Sean.Nyekjaer@prevas.dk; Received: from skn.prevas.se (81.216.59.226) by VI1PR10MB0238.EURPRD10.PROD.OUTLOOK.COM (2a01:111:e400:c54c::8) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P256) id 15.20.715.18; Tue, 1 May 2018 08:17:25 +0000 From: Sean Nyekjaer To: jic23@kernel.org, robh+dt@kernel.org Cc: Sean Nyekjaer , lars@metafoo.de, pmeerw@pmeerw.net, linux-iio@vger.kernel.org, devicetree@vger.kernel.org Subject: [PATCH 1/2] iio: dac: add TI DAC5755 family support Date: Tue, 1 May 2018 10:15:53 +0200 Message-Id: <20180501081554.5832-1-sean.nyekjaer@prevas.dk> X-Mailer: git-send-email 2.17.0 MIME-Version: 1.0 X-Originating-IP: [81.216.59.226] X-ClientProxiedBy: HE1PR05CA0197.eurprd05.prod.outlook.com (2603:10a6:3:f9::21) To VI1PR10MB0238.EURPRD10.PROD.OUTLOOK.COM (2a01:111:e400:c54c::8) X-MS-PublicTrafficType: Email X-Microsoft-Antispam: UriScan:; BCL:0; PCL:0; RULEID:(7020095)(4652020)(4534165)(4627221)(201703031133081)(201702281549075)(5600026)(2017052603328)(7153060)(7193020); SRVR:VI1PR10MB0238; X-Microsoft-Exchange-Diagnostics: 1; VI1PR10MB0238; 3:eivjDcecao8ra8dESrTFndKIyLE0Wcdn8ucW6lNsOFy3sR/Er+BhAee4KP5P2kaZclI1upsMeLv7sbi2KZTAGgY8aSnA9ZWiQQ+2l09lIYXLXxYoijRtWg85O/6Gs5H2LSxj9btXl4Yi4uPTx0lTRur1pgHDE5yal0GbRPaz7o9ZTbQLe41DyJDtcZ0PPtMR006XCbgwI7vWHd1L3D2ikRzqAfRMf7Yhbhwj4JV6Y/oleHveI8gSKXCsx/U3WARm; 25:SHkaDda5JwIZEUvcOlsgHe5jaW9yrfjDcpGKXsOu1SQHEr+dGDf0/WxUI6vOBRVS/tItcuyh96UKA5s2/bY/Zzwlu7O09hJri7qTEbfAir+fJTiHkmRrj2TKJ4OnpkxfSeqcntkM2Rj3YCX+iJYJLU1PPqfpn47GIvkjms79mT7tlwmkERitEdWGcAeyw5esitG5jnyYpTF0Y7Nt4pI2yRG1YNuxeyqJTt0IrdGotYFl0QMfASBlMklSPRVEjGWRYStJD0cOTrQJ8UzPttNCeUuSGSmMgaf+/aU8s99hiFKtjMJf/TWcq8zTNsaMVKtcRDwGZyDOlOsN9LiyJGauxA==; 31:noa04SFm0GL8qb3QxdeGprxY3zC3xYI1mhaMOyiTY4fgjHjfQAIWJjbO5KdlbrCiytQKykagRIxThsiMc/cpoisztvBSbUz8r4IKFz3xNL4ESFNaEnBn5C/0h01w6PedUTHmsoL651/pTm1DlA0KPRUB2Mq39oW6kiV2MyevlFjGMzDvQ1nlfNoU1HP3xSuqY+RkJDj1Xbuo2V9Y9T9GClb8XBqUReNuCDPedHVBS6Y= X-MS-TrafficTypeDiagnostic: VI1PR10MB0238: 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)(93006095)(93001095)(3002001)(3231254)(944501410)(52105095)(10201501046)(6041310)(20161123564045)(20161123558120)(20161123562045)(20161123560045)(201703131423095)(201702281528075)(20161123555045)(201703061421075)(201703061406153)(6072148)(201708071742011); SRVR:VI1PR10MB0238; BCL:0; PCL:0; RULEID:; SRVR:VI1PR10MB0238; X-Microsoft-Exchange-Diagnostics: 1; VI1PR10MB0238; 4:YP4LUhfIzi2uXJy1moVasNxELEhd2TsGH96rBvAB0EwzN5tGhFdsfiq1CiXxz1d4Fbj5tE24xJ68lF8T5EHFU9i9a552YC5+WhAbVTBIfekS1OdWsMvgtKs9ldyQutI9qU4qC29GKiZlreuwdzS/voj1DqGrmxNpQtYPn1DWj3jD6M6pgh+o8NI0efF5JkxC6mLyPzHn4iN3oYix1vVZH94MdeBCFAjwFd9hCYOY0DLKwG0P9lCsOaivtyySV9E9xK6WTuL3z/J5eBbSApT7+SlCcfstjCDPQXBZZ4nbF0H9RTzQghsHBWye2pPrh1FUPr9f5ceMkJ3htsnADaQ85ADwIXPVvVvwsIUc8X/RQ3g= X-Forefront-PRVS: 06592CCE58 X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10019020)(396003)(376002)(366004)(39840400004)(346002)(39380400002)(199004)(189003)(6486002)(966005)(72206003)(74482002)(48376002)(66066001)(3846002)(478600001)(2906002)(4326008)(47776003)(50466002)(69596002)(53936002)(6306002)(25786009)(6666003)(5660300001)(36756003)(59450400001)(386003)(8676002)(81156014)(68736007)(6506007)(81166006)(486006)(6512007)(956004)(2616005)(186003)(16526019)(316002)(16586007)(44832011)(8976002)(6116002)(1076002)(97736004)(26005)(305945005)(50226002)(7736002)(476003)(106356001)(105586002)(53416004)(86362001)(52116002)(51416003)(8936002)(2004002); DIR:OUT; SFP:1102; SCL:1; SRVR:VI1PR10MB0238; H:skn.prevas.se; FPR:; SPF:None; LANG:en; PTR:InfoNoRecords; A:1; MX:1; Received-SPF: None (protection.outlook.com: prevas.dk does not designate permitted sender hosts) X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; VI1PR10MB0238; 23:FwyVca8y38XUOp5ynZpjPTQgCvdUse3eMwEcejlYv?= =?us-ascii?Q?ZR7jnfpzmRvr1JUdRudSHqcOfSBIjIzZfSW/uO6lkjLzsdf/902guNSHdaoO?= =?us-ascii?Q?3qgkKdK1Zk+h+/T5PqCaaLzYNY4hjCRZknzhwtb2xxtwXBoNFjEC4qjnnsR4?= =?us-ascii?Q?8NmUFSG4/QpH5sV3NCT2OQcbMwMSoKS9Dw8iqiUXV7QSj7CwW/99wt9mUpDP?= =?us-ascii?Q?uErYmyVInl8xQfQO3UbkCBO9RhZfuAs8+i/j2ysCkPSovhD6c8Fv34SpvEtN?= =?us-ascii?Q?kac9m/bpEmomxlKttozNruuvUElBZR0JSUch4CXBwZPabhel7on2qUHx1XAL?= =?us-ascii?Q?kNNZSCe6cObGoyzGuzLPQZRtArXRflEVQ+G6k48Q+fX7fM5gDvDbO9976viw?= =?us-ascii?Q?ql777AEyu9lD30qrtQYIxr2dpm3ALomEQ7LTcgMf69vR2hALJFxStkckthPd?= =?us-ascii?Q?E7AvpBl0RnRh70MaIqYJZ4Xa/PRO40+jPcJUxcjKjQ/03U5KR6wp8B1U5t0R?= =?us-ascii?Q?6YlCrzQFzFrxgKAlBWhX7mbJOj+P4fbwT3+DNBnt4LWPKRSpzRCw8hiQ18Ga?= =?us-ascii?Q?mQD622Ai6XfDLk0sW0lNSqgTa6steky3FhrE1dROM8fWXy0zb86qsTm8+Lbj?= =?us-ascii?Q?PgmgmYyaTqt3MEAWIH3e4SDAk3zMTuJZ7E2b0KX3LGeDQHhiqDCzzmBYgiKe?= =?us-ascii?Q?X+wQ6LUoA1nLlgkLRHhENwFTLxUu12O2AFM1cSORUfEgrSaceHVIc26CSytm?= =?us-ascii?Q?bjaYI3ZktALZA3ZgGnjRBCupd9zgevi8dsn2cOm87nkGaqH3BQ6Jkkjdw49t?= =?us-ascii?Q?ic1wdly7N01OoZsBqxotORMpkADRuRjugZEagMEyeArsm/zJ1GcrhsnffkTK?= =?us-ascii?Q?heparqyg3P6dReAu96tH3lEjY5vrpJsaWLjtNwmqpGsHy243rqFpeHmmwIjw?= =?us-ascii?Q?j8Pzfo5D5yFGR4s8YJOOuaJyNY4SYmouVApuenF/TXfCUA43B8BRoz2AaYV0?= =?us-ascii?Q?IufX/3sTlXQc7j9+xTxbmhpWZ09FDiUkMnHtqvNnkN0LmpwEURPCWxr0WIBN?= =?us-ascii?Q?vxWnLXBdO69JIfu444szMHrD3imxRiio9kl+5RF3ePXtA/5RtrmBELQXsI8I?= =?us-ascii?Q?uQoD+sgbrLdKVL6mm7e2mFy8BVlkwu3GaQZTedkiY28IU2W0lFGNbsDVLuKe?= =?us-ascii?Q?ZE5lwmBPJmIc1G3Mifwc3gvoNgEjotHdM7MZzzL4eTQYtRIzXJ/sJdsefn3T?= =?us-ascii?Q?zXAz0uI0JGdSN9L4kXqw903iZhTsXn0vL2b3IA5GKWG9IPdNOFZb7fg0zKLD?= =?us-ascii?Q?fVtaLSyVzkEcWGO9ldrudkQ7FEZqZ7xsaH1JvCwrh2W?= X-Microsoft-Antispam-Message-Info: COh2SgyBjBLufoLBaIem9eJ50yIphPtpWkeLbAlAceQANTwWBBJrjD+Vrff2ELFHCjUb7TQLL1dQiQkPgVWAiHdtOSr/5qKxDsrmac79088srbP+zPGLXOq5Ms9bNu+MC8u6JkOUfFM3UQeKMoyy74KKlQYD21nYqjCwZf77dqJoEzrFWQZeSNui/8X+iZSm X-Microsoft-Exchange-Diagnostics: 1; VI1PR10MB0238; 6:mKuxJAQL3XPWivnIE85ilOesCuqY8QGZqWhXP2vhynNP4t4v/N50xnmIQ0KYXrDk82S+RAFVPqHRp6qkP+ovVKKOSgfj4m9xhR2Jv2u1kNxLMsIw6QsLlYNer1WlltNXWjpIYZIVfs/s2kB/T9Ey/7lsbesSwqOivqG/1W9/ozHJjEoGNZvrj7R4b2QZ8gUArqfGdr1Jz8yqavxK6i9+tDUB1YIydbY8fvCldoInXNwGU08+cRLEibUeef7erpu8pM6wjbmVQIN/tJ6TpiuMIk1HIbpEM/UAE7NYgCxoEo8fN2k69e9hVJkLCMhy3Ka5QZWQNBiOp78xmEfeGb4EknHc28N42N6e0QgjnxbTQx1D/J/vyIwrLKzNQtK3knMexLNOd7aIyw9wX4RZAEmAN+OthMYCV5JLefWenIvFbPC9pTiZ8vnBTvEJ2HH9v/Dggyly+GPUWa/3z0WnWoD07w==; 5:vaZQGIYGdazCuXpvRM3hKJ04w8GmKDRTFD8mZjOfYUbEwLxQiIYOJvvFnwqGd1uYl7QQoXcBblB5mRmUojUFEIy+SMElkpoUCBcYVcDA7an1wbsCpGp2DnkRprXbFTmwKxUtIsV2c1gZIu8Y6bBQkjdwST4riA/VnXFkXrxjZyw=; 24:Q3ckfAUj+82g8Yqs7icq0onQE/GN/c0LU4/auUQ/LlolHREO6xbJ27oApYLvGwD2y+pZxAWi9sn/cyEU9eTtL3x+03+Ma5hOr3eS35MgCZo= SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-Microsoft-Exchange-Diagnostics: 1; VI1PR10MB0238; 7:SCgTkaAad9tIdzcOPmBrkVpeQtmBSYVjujr6iZ88Y88TsBTwLMbs4pFHGx2DMtkR8WIPs6rDWuKyKgWTm5qA+ZChuPxeaMH9zZarDW5Hfnm3TWoUeuFMq7yvdn9PAnHUA3TAepY4o5oaD+tmm5IiUHC8HciuUbNOJJeFxkiYXTowarP0C/qsgSAtCuIyyunzV1XRcbkVg/AqY3hVk3dK88ajUQvaLsVYiy/PwLuniRtuf1MbbliXDDWzlc4HUFQL X-MS-Office365-Filtering-Correlation-Id: db53c957-b795-40fe-3084-08d5af3bf8f9 X-OriginatorOrg: prevas.dk X-MS-Exchange-CrossTenant-OriginalArrivalTime: 01 May 2018 08:17:25.1867 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: db53c957-b795-40fe-3084-08d5af3bf8f9 X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: d350cf71-778d-4780-88f5-071a4cb1ed61 X-MS-Exchange-Transport-CrossTenantHeadersStamped: VI1PR10MB0238 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 | 443 +++++++++++++++++++++++++++++++++++ 3 files changed, 454 insertions(+) create mode 100644 drivers/iio/dac/ti-dac5571.c diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 3ff8a32f1385..c77c6cf1d2ba 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -335,6 +335,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, DAC8571, DAC8574. + + 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 4397e2114344..57aa230d34ab 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -37,4 +37,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..5ce110fe1bab --- /dev/null +++ b/drivers/iio/dac/ti-dac5571.c @@ -0,0 +1,443 @@ +/* + * 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 +}; + +#define DAC5571_SINGLE_CHANNEL 1 +#define DAC5571_QUAD_CHANNEL 4 + +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; + struct dac5571_spec const *spec; + int (*dac5571_cmd)(struct dac5571_data *data, int channel, u16 val); + int (*dac5571_pwrdwn)(struct dac5571_data *data, int channel, u8 pwrdwn); + u8 buf[3] ____cacheline_aligned; +}; + +#define DAC5571_POWERDOWN(mode) ((mode) + 1) +#define DAC5571_POWERDOWN_FLAG BIT(0) +#define DAC5571_CHANNEL_SELECT 1 +#define DAC5571_LOADMODE_DIRECT BIT(4) +#define DAC5571_SINGLE_PWRDWN_BITS 4 +#define DAC5571_QUAD_PWRDWN_BITS 6 + +static int dac5571_cmd_single(struct dac5571_data *data, int channel, u16 val) +{ + unsigned int shift; + + shift = 12 - data->spec->resolution; + data->buf[1] = val << shift; + data->buf[0] = val >> (8 - shift); + + if (i2c_master_send(data->client, data->buf, 2) != 2) + return -EIO; + + return 0; +} + + +static int dac5571_cmd_quad(struct dac5571_data *data, int channel, u16 val) +{ + unsigned int shift; + + shift = 16 - data->spec->resolution; + data->buf[2] = val << shift; + data->buf[1] = (val >> (8 - shift)); + data->buf[0] = (channel << DAC5571_CHANNEL_SELECT) | + DAC5571_LOADMODE_DIRECT; + + if (i2c_master_send(data->client, data->buf, 3) != 3) + return -EIO; + + return 0; +} + +static int dac5571_pwrdwn_single(struct dac5571_data *data, int channel, u8 pwrdwn) +{ + unsigned int shift; + + shift = 12 - data->spec->resolution; + data->buf[1] = 0; + data->buf[0] = pwrdwn << DAC5571_SINGLE_PWRDWN_BITS; + + if (i2c_master_send(data->client, data->buf, 2) != 2) + return -EIO; + + return 0; +} + +static int dac5571_pwrdwn_quad(struct dac5571_data *data, int channel, u8 pwrdwn) +{ + unsigned int shift; + + shift = 16 - data->spec->resolution; + data->buf[2] = 0; + data->buf[1] = pwrdwn << DAC5571_QUAD_PWRDWN_BITS; + data->buf[0] = (channel << DAC5571_CHANNEL_SELECT) | + DAC5571_LOADMODE_DIRECT | DAC5571_POWERDOWN_FLAG; + + if (i2c_master_send(data->client, data->buf, 3) != 3) + return -EIO; + + return 0; +} + +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 = data->dac5571_pwrdwn(data, chan->channel, + DAC5571_POWERDOWN(mode)); + 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 = data->dac5571_pwrdwn(data, chan->channel, + DAC5571_POWERDOWN(data->powerdown_mode)); + else + ret = data->dac5571_cmd(data, chan->channel, data->val[0]); + if (ret) + goto out; + + data->powerdown = powerdown; + + out: + 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, name) { \ + .type = IIO_VOLTAGE, \ + .channel = (chan), \ + .address = (chan), \ + .indexed = true, \ + .output = true, \ + .datasheet_name = name, \ + .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, "A"), + dac5571_CHANNEL(1, "B"), + dac5571_CHANNEL(2, "C"), + dac5571_CHANNEL(3, "D"), +}; + +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]; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(data->vref); + if (ret < 0) + return ret; + + *val = ret / 1000; + *val2 = data->spec->resolution; + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } +} + +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->spec->resolution) || val < 0) + return -EINVAL; + + if (data->powerdown) + return -EBUSY; + + mutex_lock(&data->lock); + ret = data->dac5571_cmd(data, chan->channel, val); + if (ret == 0) + data->val[chan->channel] = val; + mutex_unlock(&data->lock); + return ret; + + default: + return -EINVAL; + } +} + +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, i; + + 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; + + 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->spec = spec; + + 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); + + switch (spec->num_channels) { + case DAC5571_SINGLE_CHANNEL: + data->dac5571_cmd = dac5571_cmd_single; + data->dac5571_pwrdwn = dac5571_pwrdwn_single; + break; + case DAC5571_QUAD_CHANNEL: + data->dac5571_cmd = dac5571_cmd_quad; + data->dac5571_pwrdwn = dac5571_pwrdwn_quad; + break; + default: + goto err; + } + + for (i = 0; i < spec->num_channels; i++) { + ret = data->dac5571_cmd(data, i, 0); + if (ret) { + dev_err(dev, "failed to initialize channel %d to 0\n", i); + goto err; + } + } + + ret = iio_device_register(indio_dev); + if (ret) + goto err; + + return 0; + + err: + 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); + 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");