From nobody Sat May 10 03:53:55 2025 Delivered-To: importer2@patchew.org Received-SPF: pass (zohomail.com: domain of vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; envelope-from=linux-kernel-owner@vger.kernel.org; helo=vger.kernel.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail(p=none dis=none) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mx.zohomail.com with SMTP id 1638704461552308.1051133313556; Sun, 5 Dec 2021 03:41:01 -0800 (PST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233360AbhLELoY (ORCPT ); Sun, 5 Dec 2021 06:44:24 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55028 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233185AbhLELoV (ORCPT ); Sun, 5 Dec 2021 06:44:21 -0500 Received: from mail-ed1-x52b.google.com (mail-ed1-x52b.google.com [IPv6:2a00:1450:4864:20::52b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E685DC061714; Sun, 5 Dec 2021 03:40:53 -0800 (PST) Received: by mail-ed1-x52b.google.com with SMTP id w1so31101214edc.6; Sun, 05 Dec 2021 03:40:53 -0800 (PST) Received: from demon-pc.localdomain ([188.24.96.74]) by smtp.gmail.com with ESMTPSA id g11sm5870469edz.53.2021.12.05.03.40.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 05 Dec 2021 03:40:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=5Y8SZh/fUjZzS5/Y2/K/xo9Vkl5DVyyFl68Ne+hrQ7Y=; b=qqDG36Im/SqcWK08VkQJsKEabZenC0fo96rTKOG7DutFouW/AOFgA0yyhn4RXprTSL aTUIGLQx9FZjc6LwvcyDqaebuuzWeZGedtvwafCTMx1ym9iHU1w7sQoBPV+cx7oajmaP VF7V5fbrjSRy4g6GemCL4CGH1hu03W2iJmfRGUvpCENjqpscZi0R66jIHYlqXOdOEDap I2VgvcOZv+guM1RSU17lSHzp/BKGw93mE5x51+cl40x2YSsmCkJlLq9hzLpZTgQ0TdAV L6G8LZ7rV+sy4fWC2u16zkxRBnfZCWEYzBr8WPDf11JktIcaFlmf6UNh3bYLp11ecExx 1jvw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=5Y8SZh/fUjZzS5/Y2/K/xo9Vkl5DVyyFl68Ne+hrQ7Y=; b=0/GBNMMEjBUIdA3gzXtqHCaMayuAOLV7VbI5IgpygZ1vigduQ/u4CP8TLoTzne+Mo5 sUJCSphwG5l4CIOP2xCn6zorVCAeuoaOwtcbtUFR0BJfjEHy4CeiIEefgekEiu4oALRt AWrZ74tXpd5bpAjtnbZL+O+/QtckW12RviEyn/o1ucw+z36aFAp6hzWIwhvj5GEMuDon XAbmwRQCNvTqPSfyepE61c91OeqSxvZV4Boou2MtstiG0C+dlnp/oP8CPFgQlj9LpZBg gz9UO9xgh+vwJbCU7ZUkWEw99xHcAJVBbzlZStCX0Q6UDpcDI7gyc9XAuUza5BDqESRT K0mQ== X-Gm-Message-State: AOAM531zjbKTlF+W90n0ID7D/je6NvmoQ68XD62xbtODaRP+syPBaYUN +t3gFPuNI2hxv+bwm04ZgEQ= X-Google-Smtp-Source: ABdhPJwqYwGdGa5w3/MJ/UjlgrAYMR2ZqIipFIVSgD545YJ+M8PLeZryRW+gcuIiM9ZvALUvJTh1Ew== X-Received: by 2002:a17:906:4bcf:: with SMTP id x15mr38029624ejv.273.1638704452461; Sun, 05 Dec 2021 03:40:52 -0800 (PST) From: Cosmin Tanislav X-Google-Original-From: Cosmin Tanislav Cc: cosmin.tanislav@analog.com, demonsingur@gmail.com, Lars-Peter Clausen , Michael Hennerich , Rob Herring , linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Linus Walleij , Bartosz Golaszewski , linux-gpio@vger.kernel.org Subject: [PATCH v11 1/3] iio: add addac subdirectory Date: Sun, 5 Dec 2021 13:40:43 +0200 Message-Id: <20211205114045.173612-2-cosmin.tanislav@analog.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20211205114045.173612-1-cosmin.tanislav@analog.com> References: <20211205114045.173612-1-cosmin.tanislav@analog.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1638704462913100001 Content-Type: text/plain; charset="utf-8" For IIO devices that expose both ADC and DAC functionality. Signed-off-by: Cosmin Tanislav --- drivers/iio/Kconfig | 1 + drivers/iio/Makefile | 1 + drivers/iio/addac/Kconfig | 8 ++++++++ drivers/iio/addac/Makefile | 6 ++++++ 4 files changed, 16 insertions(+) create mode 100644 drivers/iio/addac/Kconfig create mode 100644 drivers/iio/addac/Makefile diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 2334ad249b46..4fb4321a72cb 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -70,6 +70,7 @@ config IIO_TRIGGERED_EVENT =20 source "drivers/iio/accel/Kconfig" source "drivers/iio/adc/Kconfig" +source "drivers/iio/addac/Kconfig" source "drivers/iio/afe/Kconfig" source "drivers/iio/amplifiers/Kconfig" source "drivers/iio/cdc/Kconfig" diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 65e39bd4f934..8d48c70fee4d 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_IIO_TRIGGERED_EVENT) +=3D industrialio-trigg= ered-event.o =20 obj-y +=3D accel/ obj-y +=3D adc/ +obj-y +=3D addac/ obj-y +=3D afe/ obj-y +=3D amplifiers/ obj-y +=3D buffer/ diff --git a/drivers/iio/addac/Kconfig b/drivers/iio/addac/Kconfig new file mode 100644 index 000000000000..2e64d7755d5e --- /dev/null +++ b/drivers/iio/addac/Kconfig @@ -0,0 +1,8 @@ +# +# ADC DAC drivers +# +# When adding new entries keep the list in alphabetical order + +menu "Analog to digital and digital to analog converters" + +endmenu diff --git a/drivers/iio/addac/Makefile b/drivers/iio/addac/Makefile new file mode 100644 index 000000000000..b888b9ee12da --- /dev/null +++ b/drivers/iio/addac/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for industrial I/O ADDAC drivers +# + +# When adding new entries keep the list in alphabetical order --=20 2.34.1 From nobody Sat May 10 03:53:55 2025 Delivered-To: importer2@patchew.org Received-SPF: pass (zohomail.com: domain of vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; envelope-from=linux-kernel-owner@vger.kernel.org; helo=vger.kernel.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail(p=none dis=none) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mx.zohomail.com with SMTP id 1638704462685382.3456257848553; Sun, 5 Dec 2021 03:41:02 -0800 (PST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233393AbhLELo0 (ORCPT ); Sun, 5 Dec 2021 06:44:26 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55032 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233239AbhLELoW (ORCPT ); Sun, 5 Dec 2021 06:44:22 -0500 Received: from mail-ed1-x52e.google.com (mail-ed1-x52e.google.com [IPv6:2a00:1450:4864:20::52e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3BCA3C061714; Sun, 5 Dec 2021 03:40:55 -0800 (PST) Received: by mail-ed1-x52e.google.com with SMTP id r11so30908502edd.9; Sun, 05 Dec 2021 03:40:55 -0800 (PST) Received: from demon-pc.localdomain ([188.24.96.74]) by smtp.gmail.com with ESMTPSA id g11sm5870469edz.53.2021.12.05.03.40.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 05 Dec 2021 03:40:53 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=drn9vOsb6iAqHOzxfiDRNaCywjfVvDrfai5oCr/V7F0=; b=ItdtRMS/lg1Nqsg0+bW4N9ziBFYOBJgR8lkJ8vWxcwavF0wKpJsfoc0KTFXehlop+7 ujSjX6BCxXTsxST1He7rU4YmnL96YX1ybn4cgQjwq7DNHU5vVv+DoYImUHsi0ztHUiqH QG6vV0Qp30qk75QnKm4MFRtlb9DWo2SwTdCav1UH9snI9FQsTEJcgtYXgRxYx8QWPsEy aUCd/Gr4Tfmshv1dXwu2MKHLIeiRygtEW9lzqFtR+CSzLAhgmaDmS+GZyAoHnfuxDBu4 okrJvfoqkAiVJ/8lYIf+md9uiw0Jbltos5tbKsqfvsmlGDGwjAcf86YUlLoenKHQQCNj OBtg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=drn9vOsb6iAqHOzxfiDRNaCywjfVvDrfai5oCr/V7F0=; b=L4t5U6uE50xWOkNhioztMnryH37gHHqL1pxemTVtVrBHP+LBBich3aKOF6u6J9Hlf3 o8k7/8/ttvOkO46tqQGT7xM7QvThziQkaAd1a83YxXDKSoOqVL2P90z/DxBbvdkPYRy4 8I+qON8DaURNBSZ22EweCPcvrkSyxJkQWGPDFJ++Tc4ZpIU6IxookF0aKrkoZ3feXcGC uZD/P6iM0lJ/29NFJJsIKxTV4BOI+hKGX0dk4sKLLI8e5eqnDCuPMzPqAyP0LEp1ckNp zVbu/HbiV/42fu9Pa9X6petE80H60iQ6V+tneK3kbjclCbv0WtgI18BdH3pQ6g2BCuP+ /9ZA== X-Gm-Message-State: AOAM531tQ0O0MWCADRAgSVJAH9Z2tJ2fGfZKmd5kDwPHu4QrVRjYJ3kr 8VM6VQY6dTYYyivXVMm/Byw= X-Google-Smtp-Source: ABdhPJySi6STQ/lvXg/qAYojlAG3FXggNYDYBxlEiELtMRz1kTg35mWUSkv0OJegf14wVPWkyfqlSw== X-Received: by 2002:a17:907:3e8a:: with SMTP id hs10mr37153257ejc.58.1638704453784; Sun, 05 Dec 2021 03:40:53 -0800 (PST) From: Cosmin Tanislav X-Google-Original-From: Cosmin Tanislav Cc: cosmin.tanislav@analog.com, demonsingur@gmail.com, Lars-Peter Clausen , Michael Hennerich , Rob Herring , linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Linus Walleij , Bartosz Golaszewski , linux-gpio@vger.kernel.org, Rob Herring Subject: [PATCH v11 2/3] dt-bindings: iio: add AD74413R Date: Sun, 5 Dec 2021 13:40:44 +0200 Message-Id: <20211205114045.173612-3-cosmin.tanislav@analog.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20211205114045.173612-1-cosmin.tanislav@analog.com> References: <20211205114045.173612-1-cosmin.tanislav@analog.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1638704463122100003 Content-Type: text/plain; charset="utf-8" The AD74412R and AD74413R are quad-channel, software configurable, input/output solutions for building and process control applications. They contain functionality for analog output, analog input, digital input, resistance temperature detector, and thermocouple measurements integrated into a single chip solution with an SPI interface. The devices feature a 16-bit ADC and four configurable 13-bit DACs to provide four configurable input/output channels and a suite of diagnostic functions. The AD74413R differentiates itself from the AD74412R by being HART-compatible. Signed-off-by: Cosmin Tanislav Reviewed-by: Rob Herring Reviewed-by: Linus Walleij --- .../bindings/iio/addac/adi,ad74413r.yaml | 158 ++++++++++++++++++ include/dt-bindings/iio/addac/adi,ad74413r.h | 21 +++ 2 files changed, 179 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/addac/adi,ad74413= r.yaml create mode 100644 include/dt-bindings/iio/addac/adi,ad74413r.h diff --git a/Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml = b/Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml new file mode 100644 index 000000000000..baa65a521bad --- /dev/null +++ b/Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml @@ -0,0 +1,158 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/addac/adi,ad74413r.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD74412R/AD74413R device + +maintainers: + - Cosmin Tanislav + +description: | + The AD74412R and AD74413R are quad-channel software configurable input/o= utput + solutions for building and process control applications. They contain + functionality for analog output, analog input, digital input, resistance + temperature detector, and thermocouple measurements integrated + into a single chip solution with an SPI interface. + The devices feature a 16-bit ADC and four configurable 13-bit DACs to pr= ovide + four configurable input/output channels and a suite of diagnostic functi= ons. + The AD74413R differentiates itself from the AD74412R by being HART-compa= tible. + https://www.analog.com/en/products/ad74412r.html + https://www.analog.com/en/products/ad74413r.html + +properties: + compatible: + enum: + - adi,ad74412r + - adi,ad74413r + + reg: + maxItems: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + spi-max-frequency: + maximum: 1000000 + + spi-cpol: true + + interrupts: + maxItems: 1 + + refin-supply: true + + shunt-resistor-micro-ohms: + description: + Shunt (sense) resistor value in micro-Ohms. + default: 100000000 + +required: + - compatible + - reg + - spi-max-frequency + - spi-cpol + - refin-supply + +additionalProperties: false + +patternProperties: + "^channel@[0-3]$": + type: object + description: Represents the external channels which are connected to t= he device. + + properties: + reg: + description: | + The channel number. It can have up to 4 channels numbered from 0= to 3. + minimum: 0 + maximum: 3 + + adi,ch-func: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + Channel function. + HART functions are not supported on AD74412R. + 0 - CH_FUNC_HIGH_IMPEDANCE + 1 - CH_FUNC_VOLTAGE_OUTPUT + 2 - CH_FUNC_CURRENT_OUTPUT + 3 - CH_FUNC_VOLTAGE_INPUT + 4 - CH_FUNC_CURRENT_INPUT_EXT_POWER + 5 - CH_FUNC_CURRENT_INPUT_LOOP_POWER + 6 - CH_FUNC_RESISTANCE_INPUT + 7 - CH_FUNC_DIGITAL_INPUT_LOGIC + 8 - CH_FUNC_DIGITAL_INPUT_LOOP_POWER + 9 - CH_FUNC_CURRENT_INPUT_EXT_POWER_HART + 10 - CH_FUNC_CURRENT_INPUT_LOOP_POWER_HART + minimum: 0 + maximum: 10 + default: 0 + + adi,gpo-comparator: + type: boolean + description: | + Whether to configure GPO as a comparator or not. + When not configured as a comparator, the GPO will be treated as = an + output-only GPIO. + + required: + - reg + +examples: + - | + #include + #include + #include + + spi { + #address-cells =3D <1>; + #size-cells =3D <0>; + + cs-gpios =3D <&gpio 17 GPIO_ACTIVE_LOW>; + status =3D "okay"; + + ad74413r@0 { + compatible =3D "adi,ad74413r"; + reg =3D <0>; + spi-max-frequency =3D <1000000>; + spi-cpol; + + #address-cells =3D <1>; + #size-cells =3D <0>; + + interrupt-parent =3D <&gpio>; + interrupts =3D <26 IRQ_TYPE_EDGE_FALLING>; + + refin-supply =3D <&ad74413r_refin>; + + channel@0 { + reg =3D <0>; + + adi,ch-func =3D ; + }; + + channel@1 { + reg =3D <1>; + + adi,ch-func =3D ; + }; + + channel@2 { + reg =3D <2>; + + adi,ch-func =3D ; + adi,gpo-comparator; + }; + + channel@3 { + reg =3D <3>; + + adi,ch-func =3D ; + }; + }; + }; +... diff --git a/include/dt-bindings/iio/addac/adi,ad74413r.h b/include/dt-bind= ings/iio/addac/adi,ad74413r.h new file mode 100644 index 000000000000..204f92bbd79f --- /dev/null +++ b/include/dt-bindings/iio/addac/adi,ad74413r.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _DT_BINDINGS_ADI_AD74413R_H +#define _DT_BINDINGS_ADI_AD74413R_H + +#define CH_FUNC_HIGH_IMPEDANCE 0x0 +#define CH_FUNC_VOLTAGE_OUTPUT 0x1 +#define CH_FUNC_CURRENT_OUTPUT 0x2 +#define CH_FUNC_VOLTAGE_INPUT 0x3 +#define CH_FUNC_CURRENT_INPUT_EXT_POWER 0x4 +#define CH_FUNC_CURRENT_INPUT_LOOP_POWER 0x5 +#define CH_FUNC_RESISTANCE_INPUT 0x6 +#define CH_FUNC_DIGITAL_INPUT_LOGIC 0x7 +#define CH_FUNC_DIGITAL_INPUT_LOOP_POWER 0x8 +#define CH_FUNC_CURRENT_INPUT_EXT_POWER_HART 0x9 +#define CH_FUNC_CURRENT_INPUT_LOOP_POWER_HART 0xA + +#define CH_FUNC_MIN CH_FUNC_HIGH_IMPEDANCE +#define CH_FUNC_MAX CH_FUNC_CURRENT_INPUT_LOOP_POWER_HART + +#endif /* _DT_BINDINGS_ADI_AD74413R_H */ --=20 2.34.1 From nobody Sat May 10 03:53:55 2025 Delivered-To: importer2@patchew.org Received-SPF: pass (zohomail.com: domain of vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; envelope-from=linux-kernel-owner@vger.kernel.org; helo=vger.kernel.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail(p=none dis=none) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mx.zohomail.com with SMTP id 1638704464276533.6762725622335; Sun, 5 Dec 2021 03:41:04 -0800 (PST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233405AbhLELo2 (ORCPT ); Sun, 5 Dec 2021 06:44:28 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55046 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233336AbhLELoY (ORCPT ); Sun, 5 Dec 2021 06:44:24 -0500 Received: from mail-ed1-x531.google.com (mail-ed1-x531.google.com [IPv6:2a00:1450:4864:20::531]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D0007C061751; Sun, 5 Dec 2021 03:40:56 -0800 (PST) Received: by mail-ed1-x531.google.com with SMTP id v1so31223767edx.2; Sun, 05 Dec 2021 03:40:56 -0800 (PST) Received: from demon-pc.localdomain ([188.24.96.74]) by smtp.gmail.com with ESMTPSA id g11sm5870469edz.53.2021.12.05.03.40.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 05 Dec 2021 03:40:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=q80Y3FA2M9Ghr+fnzJ+IsmsQxAWMpC4Y+Pmx3w+0dfg=; b=bR6YIQPg0KJ28jPYCA1LG+3Lz84JHpE6IjvEFIE4OJNrCVNhF4ZqD+I8OiPBAzbCcc CzlrBb0K3yxcGYjZlACg8YTvEeab/YEaInLLWxQMukX747rIBgRplitKzu6bf1SiZIiD 4obWPN3KnmcMNQmwSsN6/aWXud7v/P3cdsLURYAlJxeZpSTiUstfSkhVa/PYo298vvLw x3aRKCkJWjAW2GHgX8WQcZHBhCvQRHu5ssXlp4gjmNRMMozc4Ekb7YdfQjh6IoVy9x81 gwvEsSFxEnWL/3iR2NagamV/jJnc4dpDoTw+jhsyAcWndV9Xne+Ag2CTtmW1vxmhz1io xInQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=q80Y3FA2M9Ghr+fnzJ+IsmsQxAWMpC4Y+Pmx3w+0dfg=; b=W/Lb3WX3WqRto3Z4Z0y9fXY1uYLVmE18NOZzTeIvvrdyNG05+i5bJaItLfRHMQAwW0 A2/nDSYMKG4Afc1EOxqMvhXQGAWi7TrUQacwEW3n0gUyh9/ahjSF793eCEoN6w0GKPc5 6MRQ4FakKjxLJM2PL3VxY1bd5v6NAvExaXKAd2s4y8109OoD6neUZBRvAvDn2ksFrPfU y6UhoT5GnaF6SZiSKqnKlkpC5HfeCoSOKRq6Yhcj/q9ZxmfNIybw4mZmWjQA1H+PsjMT fqDaHULWrI7bOzoDWfs1C+lJeIT0HHbp4WT613wsRR8BgRMyikfi3689eNadz3MgfngV 3rig== X-Gm-Message-State: AOAM533d6zqLBv8c+4bVBNpg78lVCbqC4iDF263l+kRmklrWulmEamfy Q0VQmGtFOwKBvpLF3yIKMrI= X-Google-Smtp-Source: ABdhPJwdYHJCdIYpY/yiuVIHeaB6dpS4v+mWA2qHGKtNP03cYjgG4d2cnnKKDKHIAGmrq/mrJ8YXog== X-Received: by 2002:a17:906:794a:: with SMTP id l10mr36963672ejo.192.1638704455126; Sun, 05 Dec 2021 03:40:55 -0800 (PST) From: Cosmin Tanislav X-Google-Original-From: Cosmin Tanislav Cc: cosmin.tanislav@analog.com, demonsingur@gmail.com, Lars-Peter Clausen , Michael Hennerich , Rob Herring , linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Linus Walleij , Bartosz Golaszewski , linux-gpio@vger.kernel.org Subject: [PATCH v11 3/3] iio: addac: add AD74413R driver Date: Sun, 5 Dec 2021 13:40:45 +0200 Message-Id: <20211205114045.173612-4-cosmin.tanislav@analog.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20211205114045.173612-1-cosmin.tanislav@analog.com> References: <20211205114045.173612-1-cosmin.tanislav@analog.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1638704465296100001 The AD74412R and AD74413R are quad-channel, software configurable, input/output solutions for building and process control applications. They contain functionality for analog output, analog input, digital input, resistance temperature detector, and thermocouple measurements integrated into a single chip solution with an SPI interface. The devices feature a 16-bit ADC and four configurable 13-bit DACs to provide four configurable input/output channels and a suite of diagnostic functions. The AD74413R differentiates itself from the AD74412R by being HART-compatible. When configured with channel 0 as voltage output, channel 1 as current output, channel 2 as voltage input and channel 3 as current input, the following structure is created under the corresponding IIO device. =2E =E2=94=9C=E2=94=80=E2=94=80 in_current0_offset =E2=94=9C=E2=94=80=E2=94=80 in_current0_raw =E2=94=9C=E2=94=80=E2=94=80 in_current0_sampling_frequency =E2=94=9C=E2=94=80=E2=94=80 in_current0_sampling_frequency_available =E2=94=9C=E2=94=80=E2=94=80 in_current0_scale =E2=94=9C=E2=94=80=E2=94=80 in_voltage1_offset =E2=94=9C=E2=94=80=E2=94=80 in_voltage1_raw =E2=94=9C=E2=94=80=E2=94=80 in_voltage1_sampling_frequency =E2=94=9C=E2=94=80=E2=94=80 in_voltage1_sampling_frequency_available =E2=94=9C=E2=94=80=E2=94=80 in_voltage1_scale =E2=94=9C=E2=94=80=E2=94=80 in_voltage2_offset =E2=94=9C=E2=94=80=E2=94=80 in_voltage2_raw =E2=94=9C=E2=94=80=E2=94=80 in_voltage2_sampling_frequency =E2=94=9C=E2=94=80=E2=94=80 in_voltage2_sampling_frequency_available =E2=94=9C=E2=94=80=E2=94=80 in_voltage2_scale =E2=94=9C=E2=94=80=E2=94=80 in_current3_offset =E2=94=9C=E2=94=80=E2=94=80 in_current3_raw =E2=94=9C=E2=94=80=E2=94=80 in_current3_sampling_frequency =E2=94=9C=E2=94=80=E2=94=80 in_current3_sampling_frequency_available =E2=94=9C=E2=94=80=E2=94=80 in_current3_scale =E2=94=9C=E2=94=80=E2=94=80 out_voltage0_raw =E2=94=9C=E2=94=80=E2=94=80 out_voltage0_scale =E2=94=9C=E2=94=80=E2=94=80 out_current1_raw =E2=94=9C=E2=94=80=E2=94=80 out_current1_scale =E2=94=9C=E2=94=80=E2=94=80 name =E2=94=9C=E2=94=80=E2=94=80 buffer =E2=94=82=C2=A0=C2=A0 =E2=94=9C=E2=94=80=E2=94=80 data_available =E2=94=82=C2=A0=C2=A0 =E2=94=9C=E2=94=80=E2=94=80 enable =E2=94=82=C2=A0=C2=A0 =E2=94=9C=E2=94=80=E2=94=80 length =E2=94=82=C2=A0=C2=A0 =E2=94=94=E2=94=80=E2=94=80 watermark =E2=94=94=E2=94=80=E2=94=80 scan_elements =C2=A0=C2=A0 =E2=94=9C=E2=94=80=E2=94=80 in_current0_en =C2=A0=C2=A0 =E2=94=9C=E2=94=80=E2=94=80 in_current0_index =C2=A0=C2=A0 =E2=94=9C=E2=94=80=E2=94=80 in_current0_type =C2=A0=C2=A0 =E2=94=9C=E2=94=80=E2=94=80 in_voltage1_en =C2=A0=C2=A0 =E2=94=9C=E2=94=80=E2=94=80 in_voltage1_index =C2=A0=C2=A0 =E2=94=9C=E2=94=80=E2=94=80 in_voltage1_type =C2=A0=C2=A0 =E2=94=9C=E2=94=80=E2=94=80 in_voltage2_en =C2=A0=C2=A0 =E2=94=9C=E2=94=80=E2=94=80 in_voltage2_index =C2=A0=C2=A0 =E2=94=9C=E2=94=80=E2=94=80 in_voltage2_type =C2=A0=C2=A0 =E2=94=9C=E2=94=80=E2=94=80 in_current3_en =C2=A0=C2=A0 =E2=94=9C=E2=94=80=E2=94=80 in_current3_index =C2=A0=C2=A0 =E2=94=94=E2=94=80=E2=94=80 in_current3_type Signed-off-by: Cosmin Tanislav Reviewed-by: Linus Walleij --- MAINTAINERS | 9 + drivers/iio/addac/Kconfig | 12 + drivers/iio/addac/Makefile | 1 + drivers/iio/addac/ad74413r.c | 1475 ++++++++++++++++++++++++++++++++++ 4 files changed, 1497 insertions(+) create mode 100644 drivers/iio/addac/ad74413r.c diff --git a/MAINTAINERS b/MAINTAINERS index 46c915468801..57fb0f19ee08 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1069,6 +1069,15 @@ W: http://ez.analog.com/community/linux-device-drive= rs F: Documentation/devicetree/bindings/iio/adc/adi,ad7780.yaml F: drivers/iio/adc/ad7780.c =20 +ANALOG DEVICES INC AD74413R DRIVER +M: Cosmin Tanislav +L: linux-iio@vger.kernel.org +S: Supported +W: http://ez.analog.com/community/linux-device-drivers +F: Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml +F: drivers/iio/addac/ad74413r.c +F: include/dt-bindings/iio/addac/adi,ad74413r.h + ANALOG DEVICES INC AD9389B DRIVER M: Hans Verkuil L: linux-media@vger.kernel.org diff --git a/drivers/iio/addac/Kconfig b/drivers/iio/addac/Kconfig index 2e64d7755d5e..138492362f20 100644 --- a/drivers/iio/addac/Kconfig +++ b/drivers/iio/addac/Kconfig @@ -5,4 +5,16 @@ =20 menu "Analog to digital and digital to analog converters" =20 +config AD74413R + tristate "Analog Devices AD74412R/AD74413R driver" + depends on GPIOLIB && SPI + select REGMAP_SPI + select CRC8 + help + Say yes here to build support for Analog Devices AD74412R/AD74413R + quad-channel software configurable input/output solution. + + To compile this driver as a module, choose M here: the + module will be called ad74413r. + endmenu diff --git a/drivers/iio/addac/Makefile b/drivers/iio/addac/Makefile index b888b9ee12da..cfd4bbe64ad3 100644 --- a/drivers/iio/addac/Makefile +++ b/drivers/iio/addac/Makefile @@ -4,3 +4,4 @@ # =20 # When adding new entries keep the list in alphabetical order +obj-$(CONFIG_AD74413R) +=3D ad74413r.o diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c new file mode 100644 index 000000000000..3ef1ec883a14 --- /dev/null +++ b/drivers/iio/addac/ad74413r.c @@ -0,0 +1,1475 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Analog Devices, Inc. + * Author: Cosmin Tanislav + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define AD74413R_CRC_POLYNOMIAL 0x7 +DECLARE_CRC8_TABLE(ad74413r_crc8_table); + +#define AD74413R_CHANNEL_MAX 4 + +#define AD74413R_FRAME_SIZE 4 + +struct ad74413r_chip_info { + const char *name; + bool hart_support; +}; + +struct ad74413r_channel_config { + u32 func; + bool gpo_comparator; + bool initialized; +}; + +struct ad74413r_channels { + struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +struct ad74413r_state { + struct ad74413r_channel_config channel_configs[AD74413R_CHANNEL_MAX]; + unsigned int gpo_gpio_offsets[AD74413R_CHANNEL_MAX]; + unsigned int comp_gpio_offsets[AD74413R_CHANNEL_MAX]; + struct gpio_chip gpo_gpiochip; + struct gpio_chip comp_gpiochip; + struct completion adc_data_completion; + unsigned int num_gpo_gpios; + unsigned int num_comparator_gpios; + u32 sense_resistor_ohms; + + /* + * Synchronize consecutive operations when doing a one-shot + * conversion and when updating the ADC samples SPI message. + */ + struct mutex lock; + + const struct ad74413r_chip_info *chip_info; + struct spi_device *spi; + struct regulator *refin_reg; + struct regmap *regmap; + struct device *dev; + struct iio_trigger *trig; + + size_t adc_active_channels; + struct spi_message adc_samples_msg; + struct spi_transfer adc_samples_xfer[AD74413R_CHANNEL_MAX + 1]; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + struct { + u8 rx_buf[AD74413R_FRAME_SIZE * AD74413R_CHANNEL_MAX]; + s64 timestamp; + } adc_samples_buf ____cacheline_aligned; + + u8 adc_samples_tx_buf[AD74413R_FRAME_SIZE * AD74413R_CHANNEL_MAX]; + u8 reg_tx_buf[AD74413R_FRAME_SIZE]; + u8 reg_rx_buf[AD74413R_FRAME_SIZE]; +}; + +#define AD74413R_REG_NOP 0x00 + +#define AD74413R_REG_CH_FUNC_SETUP_X(x) (0x01 + (x)) +#define AD74413R_CH_FUNC_SETUP_MASK GENMASK(3, 0) + +#define AD74413R_REG_ADC_CONFIG_X(x) (0x05 + (x)) +#define AD74413R_ADC_CONFIG_RANGE_MASK GENMASK(7, 5) +#define AD74413R_ADC_CONFIG_REJECTION_MASK GENMASK(4, 3) +#define AD74413R_ADC_RANGE_10V 0b000 +#define AD74413R_ADC_RANGE_2P5V_EXT_POW 0b001 +#define AD74413R_ADC_RANGE_2P5V_INT_POW 0b010 +#define AD74413R_ADC_RANGE_5V_BI_DIR 0b011 +#define AD74413R_ADC_REJECTION_50_60 0b00 +#define AD74413R_ADC_REJECTION_NONE 0b01 +#define AD74413R_ADC_REJECTION_50_60_HART 0b10 +#define AD74413R_ADC_REJECTION_HART 0b11 + +#define AD74413R_REG_DIN_CONFIG_X(x) (0x09 + (x)) +#define AD74413R_DIN_DEBOUNCE_MASK GENMASK(4, 0) +#define AD74413R_DIN_DEBOUNCE_LEN BIT(5) + +#define AD74413R_REG_DAC_CODE_X(x) (0x16 + (x)) +#define AD74413R_DAC_CODE_MAX GENMASK(12, 0) +#define AD74413R_DAC_VOLTAGE_MAX 11000 + +#define AD74413R_REG_GPO_PAR_DATA 0x0d +#define AD74413R_REG_GPO_CONFIG_X(x) (0x0e + (x)) +#define AD74413R_GPO_CONFIG_DATA_MASK BIT(3) +#define AD74413R_GPO_CONFIG_SELECT_MASK GENMASK(2, 0) +#define AD74413R_GPO_CONFIG_100K_PULL_DOWN 0b000 +#define AD74413R_GPO_CONFIG_LOGIC 0b001 +#define AD74413R_GPO_CONFIG_LOGIC_PARALLEL 0b010 +#define AD74413R_GPO_CONFIG_COMPARATOR 0b011 +#define AD74413R_GPO_CONFIG_HIGH_IMPEDANCE 0b100 + +#define AD74413R_REG_ADC_CONV_CTRL 0x23 +#define AD74413R_CONV_SEQ_MASK GENMASK(9, 8) +#define AD74413R_CONV_SEQ_ON 0b00 +#define AD74413R_CONV_SEQ_SINGLE 0b01 +#define AD74413R_CONV_SEQ_CONTINUOUS 0b10 +#define AD74413R_CONV_SEQ_OFF 0b11 +#define AD74413R_CH_EN_MASK(x) BIT(x) + +#define AD74413R_REG_DIN_COMP_OUT 0x25 +#define AD74413R_DIN_COMP_OUT_SHIFT_X(x) x + +#define AD74413R_REG_ADC_RESULT_X(x) (0x26 + (x)) +#define AD74413R_ADC_RESULT_MAX GENMASK(15, 0) + +#define AD74413R_REG_READ_SELECT 0x41 + +#define AD74413R_REG_CMD_KEY 0x44 +#define AD74413R_CMD_KEY_LDAC 0x953a +#define AD74413R_CMD_KEY_RESET1 0x15fa +#define AD74413R_CMD_KEY_RESET2 0xaf51 + +static const int ad74413r_adc_sampling_rates[] =3D { + 20, 4800, +}; + +static const int ad74413r_adc_sampling_rates_hart[] =3D { + 10, 20, 1200, 4800, +}; + +static int ad74413r_crc(u8 *buf) +{ + return crc8(ad74413r_crc8_table, buf, 3, 0); +} + +static void ad74413r_format_reg_write(u8 reg, u16 val, u8 *buf) +{ + buf[0] =3D reg; + put_unaligned_be16(val, &buf[1]); + buf[3] =3D ad74413r_crc(buf); +} + +static int ad74413r_reg_write(void *context, unsigned int reg, unsigned in= t val) +{ + struct ad74413r_state *st =3D context; + + ad74413r_format_reg_write(reg, val, st->reg_tx_buf); + + return spi_write(st->spi, st->reg_tx_buf, AD74413R_FRAME_SIZE); +} + +static int ad74413r_crc_check(struct ad74413r_state *st, u8 *buf) +{ + u8 expected_crc =3D ad74413r_crc(buf); + + if (buf[3] !=3D expected_crc) { + dev_err(st->dev, "Bad CRC %02x for %02x%02x%02x\n", + buf[3], buf[0], buf[1], buf[2]); + return -EINVAL; + } + + return 0; +} + +static int ad74413r_reg_read(void *context, unsigned int reg, unsigned int= *val) +{ + struct ad74413r_state *st =3D context; + struct spi_transfer reg_read_xfer[] =3D { + { + .tx_buf =3D st->reg_tx_buf, + .len =3D AD74413R_FRAME_SIZE, + .cs_change =3D 1, + }, + { + .rx_buf =3D st->reg_rx_buf, + .len =3D AD74413R_FRAME_SIZE, + }, + }; + int ret; + + ad74413r_format_reg_write(AD74413R_REG_READ_SELECT, reg, + st->reg_tx_buf); + + ret =3D spi_sync_transfer(st->spi, reg_read_xfer, + ARRAY_SIZE(reg_read_xfer)); + if (ret) + return ret; + + ret =3D ad74413r_crc_check(st, st->reg_rx_buf); + if (ret) + return ret; + + *val =3D get_unaligned_be16(&st->reg_rx_buf[1]); + + return 0; +} + +const struct regmap_config ad74413r_regmap_config =3D { + .reg_bits =3D 8, + .val_bits =3D 16, + .reg_read =3D ad74413r_reg_read, + .reg_write =3D ad74413r_reg_write, +}; + +static int ad74413r_set_gpo_config(struct ad74413r_state *st, + unsigned int offset, u8 mode) +{ + return regmap_update_bits(st->regmap, AD74413R_REG_GPO_CONFIG_X(offset), + AD74413R_GPO_CONFIG_SELECT_MASK, mode); +} + +static const unsigned int ad74413r_debounce_map[AD74413R_DIN_DEBOUNCE_LEN]= =3D { + 0, 13, 18, 24, 32, 42, 56, 75, + 100, 130, 180, 240, 320, 420, 560, 750, + 1000, 1300, 1800, 2400, 3200, 4200, 5600, 7500, + 10000, 13000, 18000, 24000, 32000, 42000, 56000, 75000, +}; + +static int ad74413r_set_comp_debounce(struct ad74413r_state *st, + unsigned int offset, + unsigned int debounce) +{ + unsigned int val =3D AD74413R_DIN_DEBOUNCE_LEN - 1; + unsigned int i; + + for (i =3D 0; i < AD74413R_DIN_DEBOUNCE_LEN; i++) + if (debounce <=3D ad74413r_debounce_map[i]) { + val =3D i; + break; + } + + return regmap_update_bits(st->regmap, + AD74413R_REG_DIN_CONFIG_X(offset), + AD74413R_DIN_DEBOUNCE_MASK, + val); +} + +static void ad74413r_gpio_set(struct gpio_chip *chip, + unsigned int offset, int val) +{ + struct ad74413r_state *st =3D gpiochip_get_data(chip); + unsigned int real_offset =3D st->gpo_gpio_offsets[offset]; + int ret; + + ret =3D ad74413r_set_gpo_config(st, real_offset, + AD74413R_GPO_CONFIG_LOGIC); + if (ret) + return; + + regmap_update_bits(st->regmap, AD74413R_REG_GPO_CONFIG_X(real_offset), + AD74413R_GPO_CONFIG_DATA_MASK, + val ? AD74413R_GPO_CONFIG_DATA_MASK : 0); +} + +static void ad74413r_gpio_set_multiple(struct gpio_chip *chip, + unsigned long *mask, + unsigned long *bits) +{ + struct ad74413r_state *st =3D gpiochip_get_data(chip); + unsigned long real_mask =3D 0; + unsigned long real_bits =3D 0; + unsigned int offset =3D 0; + int ret; + + for_each_set_bit_from(offset, mask, AD74413R_CHANNEL_MAX) { + unsigned int real_offset =3D st->gpo_gpio_offsets[offset]; + + ret =3D ad74413r_set_gpo_config(st, real_offset, + AD74413R_GPO_CONFIG_LOGIC_PARALLEL); + if (ret) + return; + + real_mask |=3D BIT(real_offset); + if (*bits & offset) + real_bits |=3D BIT(real_offset); + } + + regmap_update_bits(st->regmap, AD74413R_REG_GPO_PAR_DATA, + real_mask, real_bits); +} + +static int ad74413r_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct ad74413r_state *st =3D gpiochip_get_data(chip); + unsigned int real_offset =3D st->comp_gpio_offsets[offset]; + unsigned int status; + int ret; + + ret =3D regmap_read(st->regmap, AD74413R_REG_DIN_COMP_OUT, &status); + if (ret) + return ret; + + status &=3D AD74413R_DIN_COMP_OUT_SHIFT_X(real_offset); + + return status ? 1 : 0; +} + +static int ad74413r_gpio_get_multiple(struct gpio_chip *chip, + unsigned long *mask, + unsigned long *bits) +{ + struct ad74413r_state *st =3D gpiochip_get_data(chip); + unsigned int offset =3D 0; + unsigned int val; + int ret; + + ret =3D regmap_read(st->regmap, AD74413R_REG_DIN_COMP_OUT, &val); + if (ret) + return ret; + + for_each_set_bit_from(offset, mask, AD74413R_CHANNEL_MAX) { + unsigned int real_offset =3D st->comp_gpio_offsets[offset]; + + if (val & BIT(real_offset)) + *bits |=3D offset; + } + + return ret; +} + +static int ad74413r_gpio_get_gpo_direction(struct gpio_chip *chip, + unsigned int offset) +{ + return GPIO_LINE_DIRECTION_OUT; +} + +static int ad74413r_gpio_get_comp_direction(struct gpio_chip *chip, + unsigned int offset) +{ + return GPIO_LINE_DIRECTION_IN; +} + +static int ad74413r_gpio_set_gpo_config(struct gpio_chip *chip, + unsigned int offset, + unsigned long config) +{ + struct ad74413r_state *st =3D gpiochip_get_data(chip); + unsigned int real_offset =3D st->gpo_gpio_offsets[offset]; + + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_BIAS_PULL_DOWN: + return ad74413r_set_gpo_config(st, real_offset, + AD74413R_GPO_CONFIG_100K_PULL_DOWN); + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + return ad74413r_set_gpo_config(st, real_offset, + AD74413R_GPO_CONFIG_HIGH_IMPEDANCE); + default: + return -ENOTSUPP; + } +} + +static int ad74413r_gpio_set_comp_config(struct gpio_chip *chip, + unsigned int offset, + unsigned long config) +{ + struct ad74413r_state *st =3D gpiochip_get_data(chip); + unsigned int real_offset =3D st->comp_gpio_offsets[offset]; + + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_INPUT_DEBOUNCE: + return ad74413r_set_comp_debounce(st, real_offset, + pinconf_to_config_argument(config)); + default: + return -ENOTSUPP; + } +} + +static int ad74413r_reset(struct ad74413r_state *st) +{ + int ret; + + ret =3D regmap_write(st->regmap, AD74413R_REG_CMD_KEY, + AD74413R_CMD_KEY_RESET1); + if (ret) + return ret; + + return regmap_write(st->regmap, AD74413R_REG_CMD_KEY, + AD74413R_CMD_KEY_RESET2); +} + +static int ad74413r_set_channel_dac_code(struct ad74413r_state *st, + unsigned int channel, int dac_code) +{ + struct reg_sequence reg_seq[2] =3D { + { AD74413R_REG_DAC_CODE_X(channel), dac_code }, + { AD74413R_REG_CMD_KEY, AD74413R_CMD_KEY_LDAC }, + }; + + return regmap_multi_reg_write(st->regmap, reg_seq, 2); +} + +static int ad74413r_set_channel_function(struct ad74413r_state *st, + unsigned int channel, u8 func) +{ + return regmap_update_bits(st->regmap, + AD74413R_REG_CH_FUNC_SETUP_X(channel), + AD74413R_CH_FUNC_SETUP_MASK, func); +} + +static int ad74413r_set_adc_conv_seq(struct ad74413r_state *st, + unsigned int status) +{ + int ret; + + /* + * These bits do not clear when a conversion completes. + * To enable a subsequent conversion, repeat the write. + */ + ret =3D regmap_write_bits(st->regmap, AD74413R_REG_ADC_CONV_CTRL, + AD74413R_CONV_SEQ_MASK, + FIELD_PREP(AD74413R_CONV_SEQ_MASK, status)); + if (ret) + return ret; + + /* + * Wait 100us before starting conversions. + */ + usleep_range(100, 120); + + return 0; +} + +static int ad74413r_set_adc_channel_enable(struct ad74413r_state *st, + unsigned int channel, + bool status) +{ + return regmap_update_bits(st->regmap, AD74413R_REG_ADC_CONV_CTRL, + AD74413R_CH_EN_MASK(channel), + status ? AD74413R_CH_EN_MASK(channel) : 0); +} + +static int ad74413r_get_adc_range(struct ad74413r_state *st, + unsigned int channel, + unsigned int *val) +{ + int ret; + + ret =3D regmap_read(st->regmap, AD74413R_REG_ADC_CONFIG_X(channel), val); + if (ret) + return ret; + + *val =3D FIELD_GET(AD74413R_ADC_CONFIG_RANGE_MASK, *val); + + return 0; +} + +static int ad74413r_get_adc_rejection(struct ad74413r_state *st, + unsigned int channel, + unsigned int *val) +{ + int ret; + + ret =3D regmap_read(st->regmap, AD74413R_REG_ADC_CONFIG_X(channel), val); + if (ret) + return ret; + + *val =3D FIELD_GET(AD74413R_ADC_CONFIG_REJECTION_MASK, *val); + + return 0; +} + +static int ad74413r_set_adc_rejection(struct ad74413r_state *st, + unsigned int channel, + unsigned int val) +{ + return regmap_update_bits(st->regmap, + AD74413R_REG_ADC_CONFIG_X(channel), + AD74413R_ADC_CONFIG_REJECTION_MASK, + FIELD_PREP(AD74413R_ADC_CONFIG_REJECTION_MASK, + val)); +} + +static int ad74413r_rejection_to_rate(struct ad74413r_state *st, + unsigned int rej, int *val) +{ + switch (rej) { + case AD74413R_ADC_REJECTION_50_60: + *val =3D 20; + return 0; + case AD74413R_ADC_REJECTION_NONE: + *val =3D 4800; + return 0; + case AD74413R_ADC_REJECTION_50_60_HART: + *val =3D 10; + return 0; + case AD74413R_ADC_REJECTION_HART: + *val =3D 1200; + return 0; + default: + dev_err(st->dev, "ADC rejection invalid\n"); + return -EINVAL; + } +} + +static int ad74413r_rate_to_rejection(struct ad74413r_state *st, + int rate, unsigned int *val) +{ + switch (rate) { + case 20: + *val =3D AD74413R_ADC_REJECTION_50_60; + return 0; + case 4800: + *val =3D AD74413R_ADC_REJECTION_NONE; + return 0; + case 10: + *val =3D AD74413R_ADC_REJECTION_50_60_HART; + return 0; + case 1200: + *val =3D AD74413R_ADC_REJECTION_HART; + return 0; + default: + dev_err(st->dev, "ADC rate invalid\n"); + return -EINVAL; + } +} + +static int ad74413r_range_to_voltage_range(struct ad74413r_state *st, + unsigned int range, int *val) +{ + switch (range) { + case AD74413R_ADC_RANGE_10V: + *val =3D 10000; + return 0; + case AD74413R_ADC_RANGE_2P5V_EXT_POW: + case AD74413R_ADC_RANGE_2P5V_INT_POW: + *val =3D 2500; + return 0; + case AD74413R_ADC_RANGE_5V_BI_DIR: + *val =3D 5000; + return 0; + default: + dev_err(st->dev, "ADC range invalid\n"); + return -EINVAL; + } +} + +static int ad74413r_range_to_voltage_offset(struct ad74413r_state *st, + unsigned int range, int *val) +{ + switch (range) { + case AD74413R_ADC_RANGE_10V: + case AD74413R_ADC_RANGE_2P5V_EXT_POW: + *val =3D 0; + return 0; + case AD74413R_ADC_RANGE_2P5V_INT_POW: + case AD74413R_ADC_RANGE_5V_BI_DIR: + *val =3D -2500; + return 0; + default: + dev_err(st->dev, "ADC range invalid\n"); + return -EINVAL; + } +} + +static int ad74413r_range_to_voltage_offset_raw(struct ad74413r_state *st, + unsigned int range, int *val) +{ + switch (range) { + case AD74413R_ADC_RANGE_10V: + case AD74413R_ADC_RANGE_2P5V_EXT_POW: + *val =3D 0; + return 0; + case AD74413R_ADC_RANGE_2P5V_INT_POW: + *val =3D -((int)AD74413R_ADC_RESULT_MAX); + return 0; + case AD74413R_ADC_RANGE_5V_BI_DIR: + *val =3D -((int)AD74413R_ADC_RESULT_MAX / 2); + return 0; + default: + dev_err(st->dev, "ADC range invalid\n"); + return -EINVAL; + } +} + +static int ad74413r_get_output_voltage_scale(struct ad74413r_state *st, + int *val, int *val2) +{ + *val =3D AD74413R_DAC_VOLTAGE_MAX; + *val2 =3D AD74413R_DAC_CODE_MAX; + + return IIO_VAL_FRACTIONAL; +} + +static int ad74413r_get_output_current_scale(struct ad74413r_state *st, + int *val, int *val2) +{ + *val =3D regulator_get_voltage(st->refin_reg); + *val2 =3D st->sense_resistor_ohms * AD74413R_DAC_CODE_MAX * 1000; + + return IIO_VAL_FRACTIONAL; +} + +static int ad74413r_get_input_voltage_scale(struct ad74413r_state *st, + unsigned int channel, + int *val, int *val2) +{ + unsigned int range; + int ret; + + ret =3D ad74413r_get_adc_range(st, channel, &range); + if (ret) + return ret; + + ret =3D ad74413r_range_to_voltage_range(st, range, val); + if (ret) + return ret; + + *val2 =3D AD74413R_ADC_RESULT_MAX; + + return IIO_VAL_FRACTIONAL; +} + +static int ad74413r_get_input_voltage_offset(struct ad74413r_state *st, + unsigned int channel, int *val) +{ + unsigned int range; + int ret; + + ret =3D ad74413r_get_adc_range(st, channel, &range); + if (ret) + return ret; + + ret =3D ad74413r_range_to_voltage_offset_raw(st, range, val); + if (ret) + return ret; + + return IIO_VAL_INT; +} + +static int ad74413r_get_input_current_scale(struct ad74413r_state *st, + unsigned int channel, int *val, + int *val2) +{ + unsigned int range; + int ret; + + ret =3D ad74413r_get_adc_range(st, channel, &range); + if (ret) + return ret; + + ret =3D ad74413r_range_to_voltage_range(st, range, val); + if (ret) + return ret; + + *val2 =3D AD74413R_ADC_RESULT_MAX * st->sense_resistor_ohms; + + return IIO_VAL_FRACTIONAL; +} + +static int ad74413_get_input_current_offset(struct ad74413r_state *st, + unsigned int channel, int *val) +{ + unsigned int range; + int voltage_range; + int voltage_offset; + int ret; + + ret =3D ad74413r_get_adc_range(st, channel, &range); + if (ret) + return ret; + + ret =3D ad74413r_range_to_voltage_range(st, range, &voltage_range); + if (ret) + return ret; + + ret =3D ad74413r_range_to_voltage_offset(st, range, &voltage_offset); + if (ret) + return ret; + + *val =3D voltage_offset * AD74413R_ADC_RESULT_MAX / voltage_range; + + return IIO_VAL_INT; +} + +static int ad74413r_get_adc_rate(struct ad74413r_state *st, + unsigned int channel, int *val) +{ + unsigned int rejection; + int ret; + + ret =3D ad74413r_get_adc_rejection(st, channel, &rejection); + if (ret) + return ret; + + ret =3D ad74413r_rejection_to_rate(st, rejection, val); + if (ret) + return ret; + + return IIO_VAL_INT; +} + +static int ad74413r_set_adc_rate(struct ad74413r_state *st, + unsigned int channel, int val) +{ + unsigned int rejection; + int ret; + + ret =3D ad74413r_rate_to_rejection(st, val, &rejection); + if (ret) + return ret; + + return ad74413r_set_adc_rejection(st, channel, rejection); +} + +static irqreturn_t ad74413r_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf =3D p; + struct iio_dev *indio_dev =3D pf->indio_dev; + struct ad74413r_state *st =3D iio_priv(indio_dev); + u8 *rx_buf =3D st->adc_samples_buf.rx_buf; + unsigned int i; + int ret; + + ret =3D spi_sync(st->spi, &st->adc_samples_msg); + if (ret) + goto out; + + for (i =3D 0; i < st->adc_active_channels; i++) + ad74413r_crc_check(st, &rx_buf[i * AD74413R_FRAME_SIZE]); + + iio_push_to_buffers_with_timestamp(indio_dev, &st->adc_samples_buf, + iio_get_time_ns(indio_dev)); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static irqreturn_t ad74413r_adc_data_interrupt(int irq, void *data) +{ + struct iio_dev *indio_dev =3D data; + struct ad74413r_state *st =3D iio_priv(indio_dev); + + if (iio_buffer_enabled(indio_dev)) + iio_trigger_poll(st->trig); + else + complete(&st->adc_data_completion); + + return IRQ_HANDLED; +} + +static int _ad74413r_get_single_adc_result(struct ad74413r_state *st, + unsigned int channel, int *val) +{ + unsigned int uval; + int ret; + + reinit_completion(&st->adc_data_completion); + + ret =3D ad74413r_set_adc_channel_enable(st, channel, true); + if (ret) + return ret; + + ret =3D ad74413r_set_adc_conv_seq(st, AD74413R_CONV_SEQ_SINGLE); + if (ret) + return ret; + + ret =3D wait_for_completion_timeout(&st->adc_data_completion, + msecs_to_jiffies(1000)); + if (!ret) { + ret =3D -ETIMEDOUT; + return ret; + } + + ret =3D regmap_read(st->regmap, AD74413R_REG_ADC_RESULT_X(channel), + &uval); + if (ret) + return ret; + + ret =3D ad74413r_set_adc_conv_seq(st, AD74413R_CONV_SEQ_OFF); + if (ret) + return ret; + + ret =3D ad74413r_set_adc_channel_enable(st, channel, false); + if (ret) + return ret; + + *val =3D uval; + + return IIO_VAL_INT; +} + +static int ad74413r_get_single_adc_result(struct iio_dev *indio_dev, + unsigned int channel, int *val) +{ + struct ad74413r_state *st =3D iio_priv(indio_dev); + int ret; + + ret =3D iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + mutex_lock(&st->lock); + ret =3D _ad74413r_get_single_adc_result(st, channel, val); + mutex_unlock(&st->lock); + + iio_device_release_direct_mode(indio_dev); + + return ret; +} + +static void ad74413r_adc_to_resistance_result(int adc_result, int *val) +{ + if (adc_result =3D=3D AD74413R_ADC_RESULT_MAX) + adc_result =3D AD74413R_ADC_RESULT_MAX - 1; + + *val =3D DIV_ROUND_CLOSEST(adc_result * 2100, + AD74413R_ADC_RESULT_MAX - adc_result); +} + +static int ad74413r_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *active_scan_mask) +{ + struct ad74413r_state *st =3D iio_priv(indio_dev); + struct spi_transfer *xfer =3D st->adc_samples_xfer; + u8 *rx_buf =3D &st->adc_samples_buf.rx_buf[-1 * AD74413R_FRAME_SIZE]; + u8 *tx_buf =3D st->adc_samples_tx_buf; + unsigned int channel; + int ret; + + mutex_lock(&st->lock); + + spi_message_init(&st->adc_samples_msg); + st->adc_active_channels =3D 0; + + for_each_clear_bit(channel, active_scan_mask, AD74413R_CHANNEL_MAX) { + ret =3D ad74413r_set_adc_channel_enable(st, channel, false); + if (ret) + goto out; + } + + if (*active_scan_mask =3D=3D 0) + goto out; + + /* + * The read select register is used to select which register's value + * will be sent by the slave on the next SPI frame. + * + * Create an SPI message that, on each step, writes to the read select + * register to select the ADC result of the next enabled channel, and + * reads the ADC result of the previous enabled channel. + * + * Example: + * W: [WCH1] [WCH2] [WCH2] [WCH3] [ ] + * R: [ ] [RCH1] [RCH2] [RCH3] [RCH4] + */ + + for_each_set_bit(channel, active_scan_mask, AD74413R_CHANNEL_MAX) { + ret =3D ad74413r_set_adc_channel_enable(st, channel, true); + if (ret) + goto out; + + st->adc_active_channels++; + + if (xfer =3D=3D st->adc_samples_xfer) + xfer->rx_buf =3D NULL; + else + xfer->rx_buf =3D rx_buf; + + xfer->tx_buf =3D tx_buf; + xfer->len =3D AD74413R_FRAME_SIZE; + xfer->cs_change =3D 1; + + ad74413r_format_reg_write(AD74413R_REG_READ_SELECT, + AD74413R_REG_ADC_RESULT_X(channel), + tx_buf); + + spi_message_add_tail(xfer, &st->adc_samples_msg); + + xfer++; + tx_buf +=3D AD74413R_FRAME_SIZE; + rx_buf +=3D AD74413R_FRAME_SIZE; + } + + xfer->rx_buf =3D rx_buf; + xfer->tx_buf =3D NULL; + xfer->len =3D AD74413R_FRAME_SIZE; + xfer->cs_change =3D 0; + + spi_message_add_tail(xfer, &st->adc_samples_msg); + +out: + mutex_unlock(&st->lock); + + return ret; +} + +static int ad74413r_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad74413r_state *st =3D iio_priv(indio_dev); + + return ad74413r_set_adc_conv_seq(st, AD74413R_CONV_SEQ_CONTINUOUS); +} + +static int ad74413r_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad74413r_state *st =3D iio_priv(indio_dev); + + return ad74413r_set_adc_conv_seq(st, AD74413R_CONV_SEQ_OFF); +} + +static int ad74413r_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad74413r_state *st =3D iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->output) + return ad74413r_get_output_voltage_scale(st, + val, val2); + else + return ad74413r_get_input_voltage_scale(st, + chan->channel, val, val2); + case IIO_CURRENT: + if (chan->output) + return ad74413r_get_output_current_scale(st, + val, val2); + else + return ad74413r_get_input_current_scale(st, + chan->channel, val, val2); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_VOLTAGE: + return ad74413r_get_input_voltage_offset(st, + chan->channel, val); + case IIO_CURRENT: + return ad74413_get_input_current_offset(st, + chan->channel, val); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_RAW: + if (chan->output) + return -EINVAL; + + return ad74413r_get_single_adc_result(indio_dev, chan->channel, + val); + case IIO_CHAN_INFO_PROCESSED: { + int ret; + + ret =3D ad74413r_get_single_adc_result(indio_dev, chan->channel, + val); + if (ret) + return ret; + + ad74413r_adc_to_resistance_result(*val, val); + + return ret; + } + case IIO_CHAN_INFO_SAMP_FREQ: + return ad74413r_get_adc_rate(st, chan->channel, val); + default: + return -EINVAL; + } +} + +static int ad74413r_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ad74413r_state *st =3D iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_RAW: + if (!chan->output) + return -EINVAL; + + if (val < 0 || val > AD74413R_DAC_CODE_MAX) { + dev_err(st->dev, "Invalid DAC code\n"); + return -EINVAL; + } + + return ad74413r_set_channel_dac_code(st, chan->channel, val); + case IIO_CHAN_INFO_SAMP_FREQ: + return ad74413r_set_adc_rate(st, chan->channel, val); + default: + return -EINVAL; + } +} + +static int ad74413r_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + struct ad74413r_state *st =3D iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (st->chip_info->hart_support) { + *vals =3D ad74413r_adc_sampling_rates_hart; + *length =3D ARRAY_SIZE(ad74413r_adc_sampling_rates_hart); + } else { + *vals =3D ad74413r_adc_sampling_rates; + *length =3D ARRAY_SIZE(ad74413r_adc_sampling_rates); + } + *type =3D IIO_VAL_INT; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static const struct iio_buffer_setup_ops ad74413r_buffer_ops =3D { + .postenable =3D &ad74413r_buffer_postenable, + .predisable =3D &ad74413r_buffer_predisable, +}; + +static const struct iio_trigger_ops ad74413r_trigger_ops =3D { + .validate_device =3D iio_trigger_validate_own_device, +}; + +static const struct iio_info ad74413r_info =3D { + .read_raw =3D &ad74413r_read_raw, + .write_raw =3D &ad74413r_write_raw, + .read_avail =3D &ad74413r_read_avail, + .update_scan_mode =3D &ad74413r_update_scan_mode, +}; + +#define AD74413R_DAC_CHANNEL(_type, extra_mask_separate) \ + { \ + .type =3D (_type), \ + .indexed =3D 1, \ + .output =3D 1, \ + .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) \ + | (extra_mask_separate), \ + } + +#define AD74413R_ADC_CHANNEL(_type, extra_mask_separate) \ + { \ + .type =3D (_type), \ + .indexed =3D 1, \ + .output =3D 0, \ + .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) \ + | BIT(IIO_CHAN_INFO_SAMP_FREQ) \ + | (extra_mask_separate), \ + .info_mask_separate_available =3D \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_type =3D { \ + .sign =3D 'u', \ + .realbits =3D 16, \ + .storagebits =3D 32, \ + .shift =3D 8, \ + .endianness =3D IIO_BE, \ + }, \ + } + +#define AD74413R_ADC_VOLTAGE_CHANNEL \ + AD74413R_ADC_CHANNEL(IIO_VOLTAGE, BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_OFFSET)) + +#define AD74413R_ADC_CURRENT_CHANNEL \ + AD74413R_ADC_CHANNEL(IIO_CURRENT, BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_OFFSET)) + +static struct iio_chan_spec ad74413r_voltage_output_channels[] =3D { + AD74413R_DAC_CHANNEL(IIO_VOLTAGE, BIT(IIO_CHAN_INFO_SCALE)), + AD74413R_ADC_CURRENT_CHANNEL, +}; + +static struct iio_chan_spec ad74413r_current_output_channels[] =3D { + AD74413R_DAC_CHANNEL(IIO_CURRENT, BIT(IIO_CHAN_INFO_SCALE)), + AD74413R_ADC_VOLTAGE_CHANNEL, +}; + +static struct iio_chan_spec ad74413r_voltage_input_channels[] =3D { + AD74413R_ADC_VOLTAGE_CHANNEL, +}; + +static struct iio_chan_spec ad74413r_current_input_channels[] =3D { + AD74413R_ADC_CURRENT_CHANNEL, +}; + +static struct iio_chan_spec ad74413r_resistance_input_channels[] =3D { + AD74413R_ADC_CHANNEL(IIO_RESISTANCE, BIT(IIO_CHAN_INFO_PROCESSED)), +}; + +static struct iio_chan_spec ad74413r_digital_input_channels[] =3D { + AD74413R_ADC_VOLTAGE_CHANNEL, +}; + +#define _AD74413R_CHANNELS(_channels) \ + { \ + .channels =3D _channels, \ + .num_channels =3D ARRAY_SIZE(_channels), \ + } + +#define AD74413R_CHANNELS(name) \ + _AD74413R_CHANNELS(ad74413r_ ## name ## _channels) + +static const struct ad74413r_channels ad74413r_channels_map[] =3D { + [CH_FUNC_HIGH_IMPEDANCE] =3D AD74413R_CHANNELS(voltage_input), + [CH_FUNC_VOLTAGE_OUTPUT] =3D AD74413R_CHANNELS(voltage_output), + [CH_FUNC_CURRENT_OUTPUT] =3D AD74413R_CHANNELS(current_output), + [CH_FUNC_VOLTAGE_INPUT] =3D AD74413R_CHANNELS(voltage_input), + [CH_FUNC_CURRENT_INPUT_EXT_POWER] =3D AD74413R_CHANNELS(current_input), + [CH_FUNC_CURRENT_INPUT_LOOP_POWER] =3D AD74413R_CHANNELS(current_input), + [CH_FUNC_RESISTANCE_INPUT] =3D AD74413R_CHANNELS(resistance_input), + [CH_FUNC_DIGITAL_INPUT_LOGIC] =3D AD74413R_CHANNELS(digital_input), + [CH_FUNC_DIGITAL_INPUT_LOOP_POWER] =3D AD74413R_CHANNELS(digital_input), + [CH_FUNC_CURRENT_INPUT_EXT_POWER_HART] =3D AD74413R_CHANNELS(current_inpu= t), + [CH_FUNC_CURRENT_INPUT_LOOP_POWER_HART] =3D AD74413R_CHANNELS(current_inp= ut), +}; + +static int ad74413r_parse_channel_config(struct iio_dev *indio_dev, + struct fwnode_handle *channel_node) +{ + struct ad74413r_state *st =3D iio_priv(indio_dev); + struct ad74413r_channel_config *config; + u32 index; + int ret; + + ret =3D fwnode_property_read_u32(channel_node, "reg", &index); + if (ret) { + dev_err(st->dev, "Failed to read channel reg: %d\n", ret); + return ret; + } + + if (index > AD74413R_CHANNEL_MAX) { + dev_err(st->dev, "Channel index %u is too large\n", index); + return -EINVAL; + } + + config =3D &st->channel_configs[index]; + if (config->initialized) { + dev_err(st->dev, "Channel %u already initialized\n", index); + return -EINVAL; + } + + config->func =3D CH_FUNC_HIGH_IMPEDANCE; + fwnode_property_read_u32(channel_node, "adi,ch-func", &config->func); + + if (config->func < CH_FUNC_MIN || config->func > CH_FUNC_MAX) { + dev_err(st->dev, "Invalid channel function %u\n", config->func); + return -EINVAL; + } + + if (!st->chip_info->hart_support && + (config->func =3D=3D CH_FUNC_CURRENT_INPUT_EXT_POWER_HART || + config->func =3D=3D CH_FUNC_CURRENT_INPUT_LOOP_POWER_HART)) { + dev_err(st->dev, "Unsupported HART function %u\n", config->func); + return -EINVAL; + } + + if (config->func =3D=3D CH_FUNC_DIGITAL_INPUT_LOGIC || + config->func =3D=3D CH_FUNC_DIGITAL_INPUT_LOOP_POWER) + st->num_comparator_gpios++; + + config->gpo_comparator =3D fwnode_property_read_bool(channel_node, + "adi,gpo-comparator"); + + if (!config->gpo_comparator) + st->num_gpo_gpios++; + + indio_dev->num_channels +=3D ad74413r_channels_map[config->func].num_chan= nels; + + config->initialized =3D true; + + return 0; +} + +static int ad74413r_parse_channel_configs(struct iio_dev *indio_dev) +{ + struct ad74413r_state *st =3D iio_priv(indio_dev); + struct fwnode_handle *channel_node =3D NULL; + int ret; + + fwnode_for_each_available_child_node(dev_fwnode(st->dev), channel_node) { + ret =3D ad74413r_parse_channel_config(indio_dev, channel_node); + if (ret) + goto put_channel_node; + } + + return 0; + +put_channel_node: + fwnode_handle_put(channel_node); + + return ret; +} + +static int ad74413r_setup_channels(struct iio_dev *indio_dev) +{ + struct ad74413r_state *st =3D iio_priv(indio_dev); + struct ad74413r_channel_config *config; + struct iio_chan_spec *channels, *chans; + unsigned int i, num_chans, chan_i; + int ret; + + channels =3D devm_kcalloc(st->dev, sizeof(*channels), + indio_dev->num_channels, GFP_KERNEL); + if (!channels) + return -ENOMEM; + + indio_dev->channels =3D channels; + + for (i =3D 0; i < AD74413R_CHANNEL_MAX; i++) { + config =3D &st->channel_configs[i]; + chans =3D ad74413r_channels_map[config->func].channels; + num_chans =3D ad74413r_channels_map[config->func].num_channels; + + memcpy(channels, chans, num_chans * sizeof(*chans)); + + for (chan_i =3D 0; chan_i < num_chans; chan_i++) { + struct iio_chan_spec *chan =3D &channels[chan_i]; + + chan->channel =3D i; + if (chan->output) + chan->scan_index =3D -1; + else + chan->scan_index =3D i; + } + + ret =3D ad74413r_set_channel_function(st, i, config->func); + if (ret) + return ret; + + channels +=3D num_chans; + } + + return 0; +} + +static int ad74413r_setup_gpios(struct ad74413r_state *st) +{ + struct ad74413r_channel_config *config; + unsigned int comp_gpio_i =3D 0; + unsigned int gpo_gpio_i =3D 0; + unsigned int i; + u8 gpo_config; + int ret; + + for (i =3D 0; i < AD74413R_CHANNEL_MAX; i++) { + config =3D &st->channel_configs[i]; + + if (config->gpo_comparator) { + gpo_config =3D AD74413R_GPO_CONFIG_COMPARATOR; + } else { + gpo_config =3D AD74413R_GPO_CONFIG_LOGIC; + st->gpo_gpio_offsets[gpo_gpio_i++] =3D i; + } + + if (config->func =3D=3D CH_FUNC_DIGITAL_INPUT_LOGIC || + config->func =3D=3D CH_FUNC_DIGITAL_INPUT_LOOP_POWER) + st->comp_gpio_offsets[comp_gpio_i++] =3D i; + + ret =3D ad74413r_set_gpo_config(st, i, gpo_config); + if (ret) + return ret; + } + + return 0; +} + +static void ad74413r_regulator_disable(void *regulator) +{ + regulator_disable(regulator); +} + +static int ad74413r_probe(struct spi_device *spi) +{ + struct ad74413r_state *st; + struct iio_dev *indio_dev; + int ret; + + indio_dev =3D devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st =3D iio_priv(indio_dev); + + st->spi =3D spi; + st->dev =3D &spi->dev; + st->chip_info =3D device_get_match_data(&spi->dev); + mutex_init(&st->lock); + init_completion(&st->adc_data_completion); + + st->regmap =3D devm_regmap_init(st->dev, NULL, st, + &ad74413r_regmap_config); + if (IS_ERR(st->regmap)) + return PTR_ERR(st->regmap); + + st->refin_reg =3D devm_regulator_get(st->dev, "refin"); + if (IS_ERR(st->refin_reg)) + return dev_err_probe(st->dev, PTR_ERR(st->refin_reg), + "Failed to get refin regulator\n"); + + ret =3D regulator_enable(st->refin_reg); + if (ret) + return ret; + + ret =3D devm_add_action_or_reset(st->dev, ad74413r_regulator_disable, + st->refin_reg); + if (ret) + return ret; + + st->sense_resistor_ohms =3D 100000000; + device_property_read_u32(st->dev, "shunt-resistor-micro-ohms", + &st->sense_resistor_ohms); + st->sense_resistor_ohms /=3D 1000000; + + st->trig =3D devm_iio_trigger_alloc(st->dev, "%s-dev%d", + st->chip_info->name, iio_device_id(indio_dev)); + if (!st->trig) + return -ENOMEM; + + st->trig->ops =3D &ad74413r_trigger_ops; + iio_trigger_set_drvdata(st->trig, st); + + ret =3D devm_iio_trigger_register(st->dev, st->trig); + if (ret) + return ret; + + indio_dev->name =3D st->chip_info->name; + indio_dev->modes =3D INDIO_DIRECT_MODE; + indio_dev->info =3D &ad74413r_info; + indio_dev->trig =3D iio_trigger_get(st->trig); + + ret =3D ad74413r_reset(st); + if (ret) + return ret; + + ret =3D ad74413r_parse_channel_configs(indio_dev); + if (ret) + return ret; + + ret =3D ad74413r_setup_channels(indio_dev); + if (ret) + return ret; + + ret =3D ad74413r_setup_gpios(st); + if (ret) + return ret; + + if (st->num_gpo_gpios) { + st->gpo_gpiochip.owner =3D THIS_MODULE; + st->gpo_gpiochip.label =3D st->chip_info->name; + st->gpo_gpiochip.base =3D -1; + st->gpo_gpiochip.ngpio =3D st->num_gpo_gpios; + st->gpo_gpiochip.parent =3D st->dev; + st->gpo_gpiochip.can_sleep =3D true; + st->gpo_gpiochip.set =3D ad74413r_gpio_set; + st->gpo_gpiochip.set_multiple =3D ad74413r_gpio_set_multiple; + st->gpo_gpiochip.set_config =3D ad74413r_gpio_set_gpo_config; + st->gpo_gpiochip.get_direction =3D + ad74413r_gpio_get_gpo_direction; + + ret =3D devm_gpiochip_add_data(st->dev, &st->gpo_gpiochip, st); + if (ret) + return ret; + } + + if (st->num_comparator_gpios) { + st->comp_gpiochip.owner =3D THIS_MODULE; + st->comp_gpiochip.label =3D st->chip_info->name; + st->comp_gpiochip.base =3D -1; + st->comp_gpiochip.ngpio =3D st->num_comparator_gpios; + st->comp_gpiochip.parent =3D st->dev; + st->comp_gpiochip.can_sleep =3D true; + st->comp_gpiochip.get =3D ad74413r_gpio_get; + st->comp_gpiochip.get_multiple =3D ad74413r_gpio_get_multiple; + st->comp_gpiochip.set_config =3D ad74413r_gpio_set_comp_config; + st->comp_gpiochip.get_direction =3D + ad74413r_gpio_get_comp_direction; + + ret =3D devm_gpiochip_add_data(st->dev, &st->comp_gpiochip, st); + if (ret) + return ret; + } + + ret =3D ad74413r_set_adc_conv_seq(st, AD74413R_CONV_SEQ_OFF); + if (ret) + return ret; + + ret =3D devm_request_irq(st->dev, spi->irq, ad74413r_adc_data_interrupt, + 0, st->chip_info->name, indio_dev); + if (ret) + return dev_err_probe(st->dev, ret, "Failed to request irq\n"); + + ret =3D devm_iio_triggered_buffer_setup(st->dev, indio_dev, + &iio_pollfunc_store_time, + &ad74413r_trigger_handler, + &ad74413r_buffer_ops); + if (ret) + return ret; + + return devm_iio_device_register(st->dev, indio_dev); +} + +static int ad74413r_unregister_driver(struct spi_driver *spi) +{ + spi_unregister_driver(spi); + + return 0; +} + +static int __init ad74413r_register_driver(struct spi_driver *spi) +{ + crc8_populate_msb(ad74413r_crc8_table, AD74413R_CRC_POLYNOMIAL); + + return spi_register_driver(spi); +} + +static const struct ad74413r_chip_info ad74412r_chip_info_data =3D { + .hart_support =3D false, + .name =3D "ad74412r", +}; + +static const struct ad74413r_chip_info ad74413r_chip_info_data =3D { + .hart_support =3D true, + .name =3D "ad74413r", +}; + +static const struct of_device_id ad74413r_dt_id[] =3D { + { + .compatible =3D "adi,ad74412r", + .data =3D &ad74412r_chip_info_data, + }, + { + .compatible =3D "adi,ad74413r", + .data =3D &ad74413r_chip_info_data, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, ad74413r_dt_id); + +static struct spi_driver ad74413r_driver =3D { + .driver =3D { + .name =3D "ad74413r", + .of_match_table =3D ad74413r_dt_id, + }, + .probe =3D ad74413r_probe, +}; + +module_driver(ad74413r_driver, + ad74413r_register_driver, + ad74413r_unregister_driver); + +MODULE_AUTHOR("Cosmin Tanislav "); +MODULE_DESCRIPTION("Analog Devices AD74413R ADDAC"); +MODULE_LICENSE("GPL v2"); --=20 2.34.1