From patchwork Thu Oct 28 13:48:43 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cosmin Tanislav X-Patchwork-Id: 12590251 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id D85CBC433F5 for ; Thu, 28 Oct 2021 13:49:32 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C433161038 for ; Thu, 28 Oct 2021 13:49:32 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230455AbhJ1Nv6 (ORCPT ); Thu, 28 Oct 2021 09:51:58 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50178 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230508AbhJ1Nvj (ORCPT ); Thu, 28 Oct 2021 09:51:39 -0400 Received: from mail-ed1-x529.google.com (mail-ed1-x529.google.com [IPv6:2a00:1450:4864:20::529]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 41A06C061220; Thu, 28 Oct 2021 06:49:11 -0700 (PDT) Received: by mail-ed1-x529.google.com with SMTP id h7so25610207ede.8; Thu, 28 Oct 2021 06:49:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=3BovXqE0Q/mhEQVcledJTFqExJrjsVNS5gIUqVyJDoY=; b=hv+9b20zOu4SMISP96uhUZGkB2paa1ApPsBGLC1kGOxjNUBR7J/1pT3H88Lwh510nC aRQxp3ZlClR3ZGLQkF4tJURYyS26RWElkVw0G3KJMSLacP/TIfqQE42EdpSSr6AbZXxA Yl9zPBar9YZPqfL1INj1Ny7s+o6WqkqFmDmFSbKxwJseUAb3pvlSdRGdbuFJX141eL9k B2tvIRwt/FtzZM3KfjCQ4U/rpGGjnib7478r7Lsz7YdbZFInIZ6J1XRu5eEAWINTpPfD EySyHbgZ0J/w0rdkbPj4RMP8c9mI3gHLFZpxjdMepCjP5t0I9fmPXDuL4NzqEFaSDjHU GGeA== 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:mime-version :content-transfer-encoding; bh=3BovXqE0Q/mhEQVcledJTFqExJrjsVNS5gIUqVyJDoY=; b=NNgfJcdfd1fiV3oC2sLMTnut+SsGA6PuABqieSkAcipndj6cWYC9w1rhzEt4dpbm6I N+LEFrD7hcuNHTiI2O4lbrnBZR0lsHID59xi4X3Qs5pYnWJV6YHRvtWxCaOSRHuEPOv2 L17iRXM2f+9l+waKyVoUNIgb9VcA8c1Tlv0/StBQaE0/3TFGRiCHSykZgOSWaONESSIj yBFgcuTp6+lYgshBkraAAxv2L5r91+wVZJ33H+RFUH1wJgkeF/ujIfHx6SzCv78KExW1 nWGCORZovoBRcLRQtl9hxhRbD+nhW7MZOFvTVhDoUrSX4C+/3/VWNISXxisZyonJdnhA Pn4g== X-Gm-Message-State: AOAM530/fQ16+lPtx0wIeRyI6L6QA+VWNIKdT0jIkqF+KTlAgqnRNzZo wWcLjlRXqfX9RBntuJ/H1x4= X-Google-Smtp-Source: ABdhPJwB5nJKhRiOxwpp/Pgj4qcD/x4TVfHGhWJIqnq49U0MEpM3I+U11DNwqOiy3JMqCVQ2l2ZbmQ== X-Received: by 2002:a05:6402:42d4:: with SMTP id i20mr6165295edc.337.1635428947629; Thu, 28 Oct 2021 06:49:07 -0700 (PDT) Received: from demon-pc.localdomain ([188.24.96.74]) by smtp.gmail.com with ESMTPSA id x3sm1941934edd.67.2021.10.28.06.49.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 28 Oct 2021 06:49:07 -0700 (PDT) From: Cosmin Tanislav Cc: demonsingur@gmail.com, cosmin.tanislav@analog.com, Lars-Peter Clausen , Michael Hennerich , Jonathan Cameron , Rob Herring , linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 1/3] iio: add adddac subdirectory Date: Thu, 28 Oct 2021 16:48:43 +0300 Message-Id: <20211028134849.3664969-1-demonsingur@gmail.com> X-Mailer: git-send-email 2.33.0 MIME-Version: 1.0 To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-iio@vger.kernel.org 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 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) += industrialio-triggered-event.o obj-y += accel/ obj-y += adc/ +obj-y += addac/ obj-y += afe/ obj-y += amplifiers/ obj-y += 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 From patchwork Thu Oct 28 13:48:44 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cosmin Tanislav X-Patchwork-Id: 12590253 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4149FC433EF for ; Thu, 28 Oct 2021 13:49:42 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 26B6561038 for ; Thu, 28 Oct 2021 13:49:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230265AbhJ1NwH (ORCPT ); Thu, 28 Oct 2021 09:52:07 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50186 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231168AbhJ1Nv4 (ORCPT ); Thu, 28 Oct 2021 09:51:56 -0400 Received: from mail-ed1-x533.google.com (mail-ed1-x533.google.com [IPv6:2a00:1450:4864:20::533]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5F701C06122B; Thu, 28 Oct 2021 06:49:23 -0700 (PDT) Received: by mail-ed1-x533.google.com with SMTP id ee16so12155241edb.10; Thu, 28 Oct 2021 06:49:23 -0700 (PDT) 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=Ce+4++vM2nMn8JiMDvYZpZSZKn5WNDeTKykHl4yeAS8=; b=WqhoRDIiNTPnSfLhHCxB4k7OM5ZTGidY52vVSMqOxncuJeaf2UjsNCPodbMD5u7+PV 5xQztFyHONq7HOBI52JxIqOTOshged4K/oJQV+4kZ6N7w/MOxDA0PTFthL1CwZ4k3fbh GPiE8uTHqMpaJMGNIaD7ROHFoJtownmhJNFq75wmPwNokEMDtbe1SyhsdoXVHZpXIWWM u+g/H1pGuwsKylGjIQzOHqzEOem3XlBj2hnZrwqF2Sr1Ag9QlLp6xNzIIl8jRNSUpcYl apB2zMhmmZkJ/AeeJUnGtj383ztxBC2jZZIJUND/recl6foc6ao7KAnMVtYkV/VSLd8p 9ecg== 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=Ce+4++vM2nMn8JiMDvYZpZSZKn5WNDeTKykHl4yeAS8=; b=coZb+ADxbcQTYeRbvc6kI2R2swysEwIpOTSOTF9jEOTTt9/ilGS3h3Vk8F2J6TH7Ur Mo1ws3Djf1yDVybWr6HFYKCp1jbDeYGvdhufmLtITu55xg+g2acOADFI3xQa4i4LN3/V yhOXpCLY0MAN4bc7yAVYIlpOvYJP85i/fMZOXDAufgOaVWMwBR4CBrPP0lmzybhjfq2w +iBbd6i4XGpBEVkT5e/4uc287BVWUIeAvmaHexcTW+WV3t6RSEu9NZHGWrASoZWn4ruh UI2iV7Bs8oNKhqqqzlTz3ttewF36Jz9neBGGz+nTLhMbCbFQ5i4+KXAuVXCPY3fCLHjk DZlw== X-Gm-Message-State: AOAM530LcOhqTroi5M4WFtcD1gusOtxdPoPZwUrxe9qv0QePNLxSypHe R6gu7C7s+ChVW0jSv/VF27A= X-Google-Smtp-Source: ABdhPJwnFCTfXLZL7UiIuZ779wo+UJuyhjRfJ+FOxEOx2MjASRDB4bgibKBJh5fHMTX2rAO9/CGTHw== X-Received: by 2002:a17:906:e85:: with SMTP id p5mr5682267ejf.159.1635428958251; Thu, 28 Oct 2021 06:49:18 -0700 (PDT) Received: from demon-pc.localdomain ([188.24.96.74]) by smtp.gmail.com with ESMTPSA id x3sm1941934edd.67.2021.10.28.06.49.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 28 Oct 2021 06:49:17 -0700 (PDT) From: Cosmin Tanislav Cc: demonsingur@gmail.com, cosmin.tanislav@analog.com, Lars-Peter Clausen , Michael Hennerich , Jonathan Cameron , Rob Herring , linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 2/3] dt-bindings: iio: add AD74413R Date: Thu, 28 Oct 2021 16:48:44 +0300 Message-Id: <20211028134849.3664969-2-demonsingur@gmail.com> X-Mailer: git-send-email 2.33.0 In-Reply-To: <20211028134849.3664969-1-demonsingur@gmail.com> References: <20211028134849.3664969-1-demonsingur@gmail.com> MIME-Version: 1.0 To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-iio@vger.kernel.org Add device tree bindings for AD74413R. Signed-off-by: Cosmin Tanislav --- .../bindings/iio/addac/adi,ad74413r.yaml | 163 ++++++++++++++++++ include/dt-bindings/iio/addac/adi,ad74413r.h | 30 ++++ 2 files changed, 193 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/addac/adi,ad74413r.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..ed4ee3047fbe --- /dev/null +++ b/Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml @@ -0,0 +1,163 @@ +# 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 AD74413R/AD74412R device driver + +maintainers: + - Cosmin Tanislav + +description: | + The AD74413R and AD74412R 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. + +properties: + compatible: + enum: + - adi,ad74413r + - adi,ad74412r + + reg: + maxItems: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + spi-max-frequency: + maximum: 1000000 + + spi-cpol: true + + interrupts: + maxItems: 1 + + refin-supply: + description: + Reference voltage regulator. + + adi,rsense-resistance-ohms: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + RSense resistance values in Ohms. + +required: + - compatible + - reg + - spi-max-frequency + - spi-cpol + - refin-supply + - adi,rsense-resistance-ohm + +additionalProperties: false + +patternProperties: + "^channel@[0-3]$": + type: object + description: Represents the external channels which are connected to the device. + + properties: + reg: + description: | + The channel number. It can have up to 4 channels numbered from 0 to 3. + maxItems: 1 + 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 + maxItems: 1 + minimum: 0 + maximum: 10 + + adi,gpo-config: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + GPO config. + 0 - GPO_CONFIG_100K_PULL_DOWN + 1 - GPO_CONFIG_LOGIC + 3 - GPO_CONFIG_DEBOUNCED_COMPARATOR + 4 - GPO_CONFIG_HIGH_IMPEDANCE + maxItems: 1 + enum: [0, 1, 3, 4] + + required: + - reg + +examples: + - | + spi0 { + #address-cells = <1>; + #size-cells = <0>; + + cs-gpios = <&gpio 17 GPIO_ACTIVE_LOW>; + status = "okay"; + + ad74413r@0 { + compatible = "adi,ad74413r"; + reg = <0>; + spi-max-frequency = <1000000>; + spi-cpol; + + #address-cells = <1>; + #size-cells = <0>; + + interrupt-parent = <&gpio>; + interrupts = <26 0>; + + refin-supply = <&ad74413r_refin>; + adi,rsense-resistance-ohm = <100>; + + channel@0 { + reg = <0>; + + adi,ch-func = ; + adi,gpo-config = ; + }; + + channel@1 { + reg = <1>; + + adi,ch-func = ; + adi,gpo-config = ; + }; + + channel@2 { + reg = <2>; + + adi,ch-func = ; + adi,gpo-config = ; + }; + + channel@3 { + reg = <3>; + + adi,ch-func = ; + adi,gpo-config = ; + }; + }; + }; +... diff --git a/include/dt-bindings/iio/addac/adi,ad74413r.h b/include/dt-bindings/iio/addac/adi,ad74413r.h new file mode 100644 index 000000000000..bde558d9731c --- /dev/null +++ b/include/dt-bindings/iio/addac/adi,ad74413r.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _DT_BINDINGS_ADI_AD74413R_H +#define _DT_BINDINGS_ADI_AD74413R_H + +#define GPO_CONFIG_100K_PULL_DOWN 0x0 +#define GPO_CONFIG_LOGIC 0x1 +#define GPO_CONFIG_LOGIC_PARALLEL 0x2 +#define GPO_CONFIG_DEBOUNCED_COMPARATOR 0x3 +#define GPO_CONFIG_HIGH_IMPEDANCE 0x4 + +#define GPO_CONFIG_MIN GPO_CONFIG_100K_PULL_DOWN +#define GPO_CONFIG_MAX GPO_CONFIG_HIGH_IMPEDANCE + +#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 */ From patchwork Thu Oct 28 13:48:45 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cosmin Tanislav X-Patchwork-Id: 12590255 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7F07CC433F5 for ; Thu, 28 Oct 2021 13:50:00 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 60B0E61038 for ; Thu, 28 Oct 2021 13:50:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230442AbhJ1NwZ (ORCPT ); Thu, 28 Oct 2021 09:52:25 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50210 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230527AbhJ1NwH (ORCPT ); Thu, 28 Oct 2021 09:52:07 -0400 Received: from mail-ed1-x52c.google.com (mail-ed1-x52c.google.com [IPv6:2a00:1450:4864:20::52c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 27105C061767; Thu, 28 Oct 2021 06:49:40 -0700 (PDT) Received: by mail-ed1-x52c.google.com with SMTP id j21so2135317edt.11; Thu, 28 Oct 2021 06:49:40 -0700 (PDT) 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=6TQhvNoXQOyt4GEzqcuV513IbUHdPgov0eTZA5QoMCc=; b=XMRl5kfWcU/8SXxQ2Bjb0mrRVNOhfPYisa0dEUqkWu8/QZy60SCi7ZlXBiGlnOF2j6 RnLTVsNM2jLBcQSSuYrm9Y/MPvdL3UgSVh6BubtI8NSUIBeDJvcHsDsi+PX3f7UibbaF mQiFX3EeU79Oqi0ADmSqBKz4zDNXDgVYF7jYo7ZIcpyoL9/YiKJ9Jj0Ky3Py8HqREcId gvA1wXvco+YUFVp+ZSCWtK59jyPO5H/OeWC4A5O6d+H5tPzWKdxgityS+SCVgc7oBV2r 5Wioea1YZSXcZpmn//ryippQHHKwMsmj+9TCN1vFUAsVRoCzFRwwwiFid8psNGeyJ3uh uncw== 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=6TQhvNoXQOyt4GEzqcuV513IbUHdPgov0eTZA5QoMCc=; b=xuv4fS3ZHKDLtgQiR5llmZgHlvRsDE8+0YdKPww4aOYc+1FA6+6pNEUXniuQY43L3w iUzSa/Qzq2DXOxRBaSrL/Ou9nWCEvFXWt696qJuv0E6ZuwZcUdw8Arf6Hw5ANiXUcz5o /ZNtt1SrcJnOjx+lHr52h8zQItHldtYs5QeHj/btioEseHOWepuJ0iVnLJbypc382BMO H/jt/PppV74IKWpia8vdVF6qA9ZHyKkNqF5cA0X8ci6f6WcqN9kAk10/1pCuMveViIfv mA4rtLqxUPINb/HdubXI/dvtwEvw2LbRDKMVhOCbS35r81MWt2LBc9y63b6KXAwPWxh9 c36w== X-Gm-Message-State: AOAM531VZRJpVWhjGohGxNzlHT3W06qLUR0GAp15pqvgzUpx+gv3qvPs OzayqTMCOlxnOD51jzMS6tt8aEzjtPO5Gw== X-Google-Smtp-Source: ABdhPJzIWSo/Z0aecXkifFIMhDKb7wS9ubAtYM585NDDI9oqRLjx8wqE6Xg2t6QU2sVzpHspL1KAJg== X-Received: by 2002:a17:906:6149:: with SMTP id p9mr5397934ejl.362.1635428978485; Thu, 28 Oct 2021 06:49:38 -0700 (PDT) Received: from demon-pc.localdomain ([188.24.96.74]) by smtp.gmail.com with ESMTPSA id x3sm1941934edd.67.2021.10.28.06.49.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 28 Oct 2021 06:49:38 -0700 (PDT) From: Cosmin Tanislav Cc: demonsingur@gmail.com, cosmin.tanislav@analog.com, Lars-Peter Clausen , Michael Hennerich , Jonathan Cameron , Rob Herring , linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 3/3] iio: addac: add AD74413R driver Date: Thu, 28 Oct 2021 16:48:45 +0300 Message-Id: <20211028134849.3664969-3-demonsingur@gmail.com> X-Mailer: git-send-email 2.33.0 In-Reply-To: <20211028134849.3664969-1-demonsingur@gmail.com> References: <20211028134849.3664969-1-demonsingur@gmail.com> MIME-Version: 1.0 To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-iio@vger.kernel.org Add IIO ADDAC driver for AD74413R. Signed-off-by: Cosmin Tanislav Reported-by: kernel test robot --- MAINTAINERS | 9 + drivers/iio/addac/Kconfig | 12 + drivers/iio/addac/Makefile | 1 + drivers/iio/addac/ad74413r.c | 1389 ++++++++++++++++++++++++++++++++++ 4 files changed, 1411 insertions(+) create mode 100644 drivers/iio/addac/ad74413r.c diff --git a/MAINTAINERS b/MAINTAINERS index 37982beaf91f..174f0fd3d71c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1069,6 +1069,15 @@ W: http://ez.analog.com/community/linux-device-drivers F: Documentation/devicetree/bindings/iio/adc/adi,ad7780.yaml F: drivers/iio/adc/ad7780.c +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..06b26f8d0144 100644 --- a/drivers/iio/addac/Kconfig +++ b/drivers/iio/addac/Kconfig @@ -5,4 +5,16 @@ menu "Analog to digital and digital to analog converters" +config AD74413R + tristate "Analog Devices AD74413R/AD74412R driver" + depends on GPIOLIB && SPI + select REGMAP_SPI + select CRC8 + help + Say yes here to build support for Analog Devices AD74413R/AD74412R + 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 @@ # # When adding new entries keep the list in alphabetical order +obj-$(CONFIG_AD74413R) += ad74413r.o diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c new file mode 100644 index 000000000000..76d2e09a17ee --- /dev/null +++ b/drivers/iio/addac/ad74413r.c @@ -0,0 +1,1389 @@ +// 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 + +struct ad74413r_config { + bool hart_support; +}; + +struct ad74413r_channel_config { + u32 gpo_config; + u32 func; + bool initialized; +}; + +struct ad74413r_state { + struct ad74413r_channel_config channel_configs[AD74413R_CHANNEL_MAX]; + struct gpio_chip gpiochip; + struct mutex lock; + struct completion adc_data_completion; + + struct spi_device *spi; + struct iio_dev *indio_dev; + struct regulator *refin_reg; + struct regmap *regmap; + struct device *dev; + struct iio_trigger *trig; + const struct ad74413r_config *config; + + u32 rsense_resistance_ohms; + + size_t adc_active_channels; + struct spi_message adc_samples_msg; + struct spi_transfer adc_samples_xfer[AD74413R_CHANNEL_MAX + 1]; + struct { + __be32 rx_buf[AD74413R_CHANNEL_MAX]; + s64 timestamp; + } adc_samples ____cacheline_aligned; + __be32 adc_samples_tx_buf[AD74413R_CHANNEL_MAX]; + __be32 regmap_tx_buf; + __be32 regmap_rx_buf; +}; + +#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_DAC_CODE_X(x) (0x16 + (x)) +#define AD74413R_DAC_CODE_MAX ((1 << 13) - 1) + +#define AD74413R_REG_GPO_PAR_DATA 0x0d +#define AD74413R_REG_GPO_CONFIG_X(x) (0x0e + (x)) +#define AD74413R_GPO_CONFIG_GPO_DATA_MASK BIT(3) +#define AD74413R_GPO_CONFIG_DATA_LOW 0 +#define AD74413R_GPO_CONFIG_DATA_HIGH AD74413R_GPO_CONFIG_GPO_DATA_MASK +#define AD74413R_GPO_CONFIG_GPO_SELECT_MASK GENMASK(2, 0) + +#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_CH_EN_SHIFT(x) x + +#define AD74413R_REG_DIN_COMP_OUT 0x25 +#define AD74413R_DIN_COMP_OUT_MASK_X(x) BIT(x) +#define AD74413R_DIN_COMP_OUT_SHIFT_X(x) x + +#define AD74413R_REG_ADC_RESULT_X(x) (0x26 + (x)) +#define AD74413R_ADC_RESULT_MAX ((1 << 16) - 1) + +#define AD74413R_REG_READ_SELECT 0x41 + +#define AD74413R_REG_CMD_KEY 0x44 +#define AD74413R_CMD_KEY_LDAC 0x953a + +#define AD74413R_REG_SILLICON_REV 0x46 +#define AD74413R_SILLICON_REV_ID_MASK GENMASK(7, 0) +#define AD74413R_SILLICON_REV_ID_EXPECTED 0x8 + +#define AD74413R_ADC_DATA_TIMEOUT 1000 + +static const int ad74413r_adc_sampling_rates[] = { + 20, + 4800, +}; + +static const int ad74413r_adc_sampling_rates_hart[] = { + 10, + 20, + 1200, + 4800, +}; + +static int ad74413r_crc(u8 *buf) +{ + return crc8(ad74413r_crc8_table, buf, 3, 0); +} + +static void ad74413r_format_reg_write(unsigned int reg, unsigned int val, u8 *buf) +{ + buf[0] = reg & 0xff; + buf[1] = (val >> 8) & 0xff; + buf[2] = val & 0xff; + buf[3] = ad74413r_crc(buf); +} + +static int ad74413r_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + struct ad74413r_state *st = spi_get_drvdata(spi); + u8 *buf = (u8 *)&st->regmap_tx_buf; + + ad74413r_format_reg_write(reg, val, buf); + + return spi_write(spi, buf, 4); +} + +static int ad74413r_crc_check(struct ad74413r_state *st, u8 *buf) +{ + u8 expected_crc = ad74413r_crc(buf); + + if (buf[3] != expected_crc) { + dev_err(st->dev, "Bad CRC, data: %02x%02x%02x, expected: %02x, received: %02x\n", + buf[0], buf[1], buf[2], expected_crc, buf[3]); + return -EINVAL; + } + + return 0; +} + +static int ad74413r_reg_read_raw(void *context, unsigned int reg, u8 *buf, + unsigned int count) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + struct ad74413r_state *st = spi_get_drvdata(spi); + unsigned int i; + int ret; + + ret = ad74413r_reg_write(context, AD74413R_REG_READ_SELECT, reg); + if (ret) { + dev_err(dev, "Failed to write read select register: %d\n", ret); + return ret; + } + + for (i = 0; i < count; i++, buf += 4) { + ret = spi_read(spi, buf, 4); + if (ret) + return ret; + + ret = ad74413r_crc_check(st, buf); + if (ret) + return ret; + } + + return 0; +} + +static int ad74413r_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + struct ad74413r_state *st = spi_get_drvdata(spi); + u8 *buf = (u8 *)&st->regmap_rx_buf; + int ret; + + ret = ad74413r_reg_read_raw(context, reg, buf, 1); + if (ret) + return ret; + + *val = (buf[1] << 8) | buf[2]; + + return 0; +} + +const struct regmap_config ad74413r_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .reg_read = ad74413r_reg_read, + .reg_write = ad74413r_reg_write, +}; + +static int ad74413r_set_gpo_mode(struct ad74413r_state *st, unsigned int offset, u8 mode) +{ + if (mode < GPO_CONFIG_MIN || mode > GPO_CONFIG_MAX) { + dev_err(st->dev, "Invalid gpo config select mode\n"); + return -EINVAL; + } + + return regmap_update_bits(st->regmap, AD74413R_REG_GPO_CONFIG_X(offset), + AD74413R_GPO_CONFIG_GPO_SELECT_MASK, mode); +} + +static void ad74413r_gpio_set(struct gpio_chip *chip, unsigned int offset, int val) +{ + struct ad74413r_state *st = gpiochip_get_data(chip); + struct ad74413r_channel_config *channel_config = &st->channel_configs[offset]; + int ret; + + if (channel_config->gpo_config != GPO_CONFIG_LOGIC) { + dev_err(st->dev, "Cannot set gpo %u, not in logic mode", offset); + return; + } + + ret = ad74413r_set_gpo_mode(st, offset, GPO_CONFIG_LOGIC); + if (ret) + return; + + regmap_update_bits(st->regmap, AD74413R_REG_GPO_CONFIG_X(offset), + AD74413R_GPO_CONFIG_GPO_DATA_MASK, + val ? AD74413R_GPO_CONFIG_DATA_HIGH + : AD74413R_GPO_CONFIG_DATA_LOW); +} + +static void ad74413r_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) +{ + struct ad74413r_state *st = gpiochip_get_data(chip); + unsigned int offset = 0; + int ret; + + for_each_set_bit_from(offset, mask, AD74413R_CHANNEL_MAX) { + struct ad74413r_channel_config *channel_config = &st->channel_configs[offset]; + + if (channel_config->gpo_config != GPO_CONFIG_LOGIC) { + dev_err(st->dev, "Cannot set gpo %u, not in logic mode\n", offset); + continue; + } + + ret = ad74413r_set_gpo_mode(st, offset, GPO_CONFIG_LOGIC_PARALLEL); + if (ret) + return; + } + + regmap_update_bits(st->regmap, AD74413R_REG_GPO_PAR_DATA, *mask, *bits); +} + +static int ad74413r_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct ad74413r_state *st = gpiochip_get_data(chip); + struct ad74413r_channel_config *channel_config = &st->channel_configs[offset]; + unsigned int status; + int ret; + + if (channel_config->gpo_config != GPO_CONFIG_DEBOUNCED_COMPARATOR) { + dev_err(st->dev, "Cannot get gpo %u, not in comparator mode\n", offset); + return -EINVAL; + } + + ret = regmap_read(st->regmap, AD74413R_REG_DIN_COMP_OUT, &status); + if (ret) + return ret; + + status &= AD74413R_DIN_COMP_OUT_MASK_X(offset); + status >>= AD74413R_DIN_COMP_OUT_SHIFT_X(offset); + + return status; +} + +static int ad74413r_set_channel_dac_code(struct ad74413r_state *st, unsigned int channel, + uint16_t dac_code) +{ + int ret; + + if (dac_code > AD74413R_DAC_CODE_MAX) { + dev_err(st->dev, "Invalid dac code %d\n", dac_code); + return -EINVAL; + } + + ret = regmap_write(st->regmap, AD74413R_REG_DAC_CODE_X(channel), dac_code); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD74413R_REG_CMD_KEY, AD74413R_CMD_KEY_LDAC); + if (ret) + return ret; + + return 0; +} + +static int ad74413r_channel_set_function(struct ad74413r_state *st, unsigned int channel, u8 func) +{ + if (func < CH_FUNC_MIN || func > CH_FUNC_MAX) { + dev_err(st->dev, "Invalid channel function %d\n", func); + return -EINVAL; + } + + if (!st->config->hart_support && (func == CH_FUNC_CURRENT_INPUT_EXT_POWER_HART + || func == CH_FUNC_CURRENT_INPUT_LOOP_POWER_HART)) { + dev_err(st->dev, "HART function not supported %d\n", func); + return -EINVAL; + } + + 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 to enable the conversion. + */ + ret = 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) +{ + unsigned int val = status << AD74413R_CH_EN_SHIFT(channel); + + return regmap_update_bits(st->regmap, AD74413R_REG_ADC_CONV_CTRL, + AD74413R_CH_EN_MASK(channel), val); +} + +static int ad74413r_get_adc_range(struct ad74413r_state *st, unsigned int channel, + unsigned int *val) +{ + int ret; + + ret = regmap_read(st->regmap, AD74413R_REG_ADC_CONFIG_X(channel), val); + if (ret) + return ret; + + *val = 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 = regmap_read(st->regmap, AD74413R_REG_ADC_CONFIG_X(channel), val); + if (ret) + return ret; + + *val = 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) +{ + if (!st->config->hart_support && (val == AD74413R_ADC_REJECTION_50_60_HART + || val == AD74413R_ADC_REJECTION_HART)) { + dev_err(st->dev, "HART rate not supported %d\n", val); + return -EINVAL; + } + + 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_get_adc_rate(struct ad74413r_state *st, unsigned int channel, int *val) +{ + unsigned int rej; + int ret; + + ret = ad74413r_get_adc_rejection(st, channel, &rej); + if (ret) + return ret; + + switch (rej) { + case AD74413R_ADC_REJECTION_50_60: + *val = 20; + break; + case AD74413R_ADC_REJECTION_NONE: + *val = 4800; + break; + case AD74413R_ADC_REJECTION_50_60_HART: + *val = 10; + break; + case AD74413R_ADC_REJECTION_HART: + *val = 1200; + break; + default: + dev_err(st->dev, "Channel %u ADC rejection invalid\n", channel); + return -EINVAL; + } + + return IIO_VAL_INT; +} + +static int ad74413r_set_adc_rate(struct ad74413r_state *st, unsigned int channel, int val) +{ + unsigned int rej; + + switch (val) { + case 20: + rej = AD74413R_ADC_REJECTION_50_60; + break; + case 4800: + rej = AD74413R_ADC_REJECTION_NONE; + break; + case 10: + rej = AD74413R_ADC_REJECTION_50_60_HART; + break; + case 1200: + rej = AD74413R_ADC_REJECTION_HART; + break; + default: + dev_err(st->dev, "Channel %u ADC rate invalid\n", channel); + return -EINVAL; + } + + return ad74413r_set_adc_rejection(st, channel, rej); +} + +static int ad74413r_get_adc_voltage_range(struct ad74413r_state *st, unsigned int channel, + int *val) +{ + unsigned int range; + int ret; + + ret = ad74413r_get_adc_range(st, channel, &range); + if (ret) + return ret; + + switch (range) { + case AD74413R_ADC_RANGE_10V: + *val = 10000; + break; + case AD74413R_ADC_RANGE_2P5V_EXT_POW: + case AD74413R_ADC_RANGE_2P5V_INT_POW: + *val = 2500; + break; + case AD74413R_ADC_RANGE_5V_BI_DIR: + *val = 5000; + break; + default: + dev_err(st->dev, "Channel %u ADC range invalid\n", channel); + return -EINVAL; + } + + return 0; +} + +static int ad74413r_get_adc_voltage_offset(struct ad74413r_state *st, unsigned int channel, + int *val) +{ + unsigned int range; + int ret; + + ret = ad74413r_get_adc_range(st, channel, &range); + if (ret) + return ret; + + switch (range) { + case AD74413R_ADC_RANGE_10V: + case AD74413R_ADC_RANGE_2P5V_EXT_POW: + *val = 0; + break; + case AD74413R_ADC_RANGE_2P5V_INT_POW: + case AD74413R_ADC_RANGE_5V_BI_DIR: + *val = -2500; + break; + default: + dev_err(st->dev, "Channel %u ADC range invalid\n", channel); + return -EINVAL; + } + + return 0; +} + +static int ad74413r_get_input_voltage_scale(struct ad74413r_state *st, unsigned int channel, + int *val, int *val2) +{ + int ret; + + ret = ad74413r_get_adc_voltage_range(st, channel, val); + if (ret) + return ret; + + *val2 = AD74413R_ADC_RESULT_MAX; + + return IIO_VAL_FRACTIONAL; +} + +static int ad74413r_get_input_voltage_offset(struct ad74413r_state *st, unsigned int channel, + int *val, int *val2) +{ + unsigned int range; + int ret; + + ret = ad74413r_get_adc_range(st, channel, &range); + if (ret) + return ret; + + switch (range) { + case AD74413R_ADC_RANGE_10V: + case AD74413R_ADC_RANGE_2P5V_EXT_POW: + *val = 0; + break; + case AD74413R_ADC_RANGE_2P5V_INT_POW: + *val = -AD74413R_ADC_RESULT_MAX; + break; + case AD74413R_ADC_RANGE_5V_BI_DIR: + *val = -AD74413R_ADC_RESULT_MAX / 2; + break; + default: + dev_err(st->dev, "Channel %u ADC range invalid\n", channel); + return -EINVAL; + } + + *val2 = 0; + + return IIO_VAL_INT; +} + +static int ad74413r_get_input_current_scale(struct ad74413r_state *st, unsigned int channel, + int *val, int *val2) +{ + int ret; + + ret = ad74413r_get_adc_voltage_range(st, channel, val); + if (ret) + return ret; + + *val2 = AD74413R_ADC_RESULT_MAX * st->rsense_resistance_ohms; + + return IIO_VAL_FRACTIONAL; +} + +static int ad74413_get_input_current_offset(struct ad74413r_state *st, unsigned int channel, + int *val, int *val2) +{ + int range; + int offset; + int ret; + + ret = ad74413r_get_adc_voltage_range(st, channel, &range); + if (ret) + return ret; + + ret = ad74413r_get_adc_voltage_offset(st, channel, &offset); + if (ret) + return ret; + + *val = offset * AD74413R_ADC_RESULT_MAX / range; + *val2 = 0; + + return IIO_VAL_INT; +} + +static int ad74413r_get_output_voltage_scale(struct ad74413r_state *st, + int *val, int *val2) +{ + *val = 11000; + *val2 = AD74413R_DAC_CODE_MAX; + + return IIO_VAL_FRACTIONAL; +} + +static int ad74413r_get_output_current_scale(struct ad74413r_state *st, int *val, int *val2) +{ + *val = regulator_get_voltage(st->refin_reg); + *val2 = st->rsense_resistance_ohms * AD74413R_DAC_CODE_MAX * 1000; + + return IIO_VAL_FRACTIONAL; +} + +static irqreturn_t ad74413r_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad74413r_state *st = iio_priv(indio_dev); + unsigned int i; + int ret; + + mutex_lock(&st->lock); + + ret = spi_sync(st->spi, &st->adc_samples_msg); + if (ret) + goto out; + + for (i = 0; i < st->adc_active_channels; i++) + ad74413r_crc_check(st, (u8 *)&st->adc_samples.rx_buf[i]); + + iio_push_to_buffers_with_timestamp(indio_dev, &st->adc_samples, + iio_get_time_ns(indio_dev)); + +out: + iio_trigger_notify_done(indio_dev->trig); + + mutex_unlock(&st->lock); + + return IRQ_HANDLED; +} + +static irqreturn_t ad74413r_adc_data_interrupt(int irq, void *data) +{ + struct ad74413r_state *st = data; + + if (iio_buffer_enabled(st->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; + + ret = iio_device_claim_direct_mode(st->indio_dev); + if (ret) + return ret; + + reinit_completion(&st->adc_data_completion); + + ret = ad74413r_set_adc_channel_enable(st, channel, true); + if (ret) + goto out; + + ret = ad74413r_set_adc_conv_seq(st, AD74413R_CONV_SEQ_SINGLE); + if (ret) + goto out; + + ret = wait_for_completion_timeout(&st->adc_data_completion, + msecs_to_jiffies(AD74413R_ADC_DATA_TIMEOUT)); + if (!ret) { + ret = -ETIMEDOUT; + goto out; + } + + ret = regmap_read(st->regmap, AD74413R_REG_ADC_RESULT_X(channel), &uval); + if (ret) + goto out; + + ret = ad74413r_set_adc_conv_seq(st, AD74413R_CONV_SEQ_OFF); + if (ret) + goto out; + + ret = ad74413r_set_adc_channel_enable(st, channel, false); + if (ret) + goto out; + + *val = uval; + ret = IIO_VAL_INT; + +out: + iio_device_release_direct_mode(st->indio_dev); + + return ret; +} + +static int ad74413r_get_single_resistance_result(struct ad74413r_state *st, unsigned int channel, + int *val) +{ + unsigned int uval; + int ret; + + ret = ad74413r_get_single_adc_result(st, channel, &uval); + if (ret < 0) + return ret; + + if (uval == AD74413R_ADC_RESULT_MAX) + *val = 0; + else + *val = DIV_ROUND_CLOSEST(uval * 2100, AD74413R_ADC_RESULT_MAX - uval); + + return ret; +} + +static int ad74413r_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *active_scan_mask) +{ + struct ad74413r_state *st = iio_priv(indio_dev); + struct spi_transfer *xfer; + unsigned int channel; + int transfer_index = 0; + int ret; + + mutex_lock(&st->lock); + + spi_message_init(&st->adc_samples_msg); + st->adc_active_channels = 0; + + for (channel = 0; channel < AD74413R_CHANNEL_MAX; channel++) { + bool status = test_bit(channel, active_scan_mask); + u8 *tx_buf; + + ret = ad74413r_set_adc_channel_enable(st, channel, status); + if (ret) + goto out; + + if (!status) + continue; + + st->adc_active_channels++; + + xfer = &st->adc_samples_xfer[transfer_index]; + + if (transfer_index == 0) + xfer->rx_buf = NULL; + else + xfer->rx_buf = &st->adc_samples.rx_buf[transfer_index - 1]; + + tx_buf = (u8 *)&st->adc_samples_tx_buf[transfer_index]; + + xfer->tx_buf = tx_buf; + xfer->len = 4; + xfer->cs_change = 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); + + transfer_index++; + } + + if (transfer_index == 0) + goto out; + + xfer = &st->adc_samples_xfer[transfer_index]; + + xfer->tx_buf = NULL; + xfer->rx_buf = &st->adc_samples.rx_buf[transfer_index - 1]; + xfer->len = 4; + xfer->cs_change = 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 = 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 = 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 = iio_priv(indio_dev); + int ret = -EINVAL; + + mutex_lock(&st->lock); + + switch (info) { + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_ALTVOLTAGE: + if (chan->output) + ret = ad74413r_get_output_voltage_scale(st, val, val2); + else + ret = ad74413r_get_input_voltage_scale(st, chan->channel, + val, val2); + break; + + case IIO_CURRENT: + if (chan->output) + ret = ad74413r_get_output_current_scale(st, val, val2); + else + ret = ad74413r_get_input_current_scale(st, chan->channel, + val, val2); + break; + default: + break; + } + break; + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_ALTVOLTAGE: + if (!chan->output) + ret = ad74413r_get_input_voltage_offset(st, chan->channel, + val, val2); + break; + case IIO_CURRENT: + if (!chan->output) + ret = ad74413_get_input_current_offset(st, chan->channel, + val, val2); + break; + default: + break; + } + break; + case IIO_CHAN_INFO_RAW: + if (!chan->output) + ret = ad74413r_get_single_adc_result(st, chan->channel, val); + break; + case IIO_CHAN_INFO_PROCESSED: + switch (chan->type) { + case IIO_RESISTANCE: + if (!chan->output) + ret = ad74413r_get_single_resistance_result(st, chan->channel, + val); + break; + default: + break; + } + break; + case IIO_CHAN_INFO_SAMP_FREQ: + if (!chan->output) + ret = ad74413r_get_adc_rate(st, chan->channel, val); + break; + default: + break; + } + + mutex_unlock(&st->lock); + + return ret; +} + +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 = iio_priv(indio_dev); + int ret = -EINVAL; + + mutex_lock(&st->lock); + + switch (info) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_ALTVOLTAGE: + case IIO_CURRENT: + ret = ad74413r_set_channel_dac_code(st, chan->channel, val); + break; + default: + break; + } + break; + case IIO_CHAN_INFO_SAMP_FREQ: + if (!chan->output) + ret = ad74413r_set_adc_rate(st, chan->channel, val); + break; + default: + break; + } + + mutex_unlock(&st->lock); + + return ret; +} + +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 = iio_priv(indio_dev); + int ret = -EINVAL; + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (st->config->hart_support) { + *vals = ad74413r_adc_sampling_rates_hart; + *length = ARRAY_SIZE(ad74413r_adc_sampling_rates_hart); + } else { + *vals = ad74413r_adc_sampling_rates; + *length = ARRAY_SIZE(ad74413r_adc_sampling_rates); + } + *type = IIO_VAL_INT; + ret = IIO_AVAIL_LIST; + break; + default: + break; + } + + return ret; +} + +static const struct iio_buffer_setup_ops ad74413r_buffer_ops = { + .postenable = &ad74413r_buffer_postenable, + .predisable = &ad74413r_buffer_predisable, +}; + +static const struct iio_trigger_ops ad74413r_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, +}; + +static const struct iio_info ad74413r_info = { + .read_raw = &ad74413r_read_raw, + .write_raw = &ad74413r_write_raw, + .read_avail = &ad74413r_read_avail, + .update_scan_mode = &ad74413r_update_scan_mode, +}; + +#define AD74413R_CHANNEL(_type, _output, extra_mask_separate) \ + .type = _type, \ + .indexed = 1, \ + .output = _output, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | extra_mask_separate + +#define AD74413R_DAC_CHANNEL(_type, extra_mask_separate) \ + { \ + AD74413R_CHANNEL(_type, 1, extra_mask_separate), \ + } + +#define AD74413R_ADC_CHANNEL(_type, extra_mask_separate) \ + { \ + AD74413R_CHANNEL(_type, 0, extra_mask_separate | BIT(IIO_CHAN_INFO_SAMP_FREQ)), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 32, \ + .shift = 8, \ + .endianness = IIO_BE, \ + }, \ + } + +#define AD74413R_ADC_VOLTAGE_CHANNEL \ + AD74413R_ADC_CHANNEL(IIO_ALTVOLTAGE, 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_high_impedance_channels[] = { + AD74413R_ADC_VOLTAGE_CHANNEL, +}; + +static struct iio_chan_spec ad74413r_voltage_output_channels[] = { + AD74413R_DAC_CHANNEL(IIO_ALTVOLTAGE, BIT(IIO_CHAN_INFO_SCALE)), + AD74413R_ADC_CURRENT_CHANNEL, +}; + +static struct iio_chan_spec ad74413r_current_output_channels[] = { + AD74413R_DAC_CHANNEL(IIO_CURRENT, BIT(IIO_CHAN_INFO_SCALE)), + AD74413R_ADC_VOLTAGE_CHANNEL, +}; + +static struct iio_chan_spec ad74413r_voltage_input_channels[] = { + AD74413R_ADC_VOLTAGE_CHANNEL, +}; + +static struct iio_chan_spec ad74413r_current_input_channels[] = { + AD74413R_ADC_CURRENT_CHANNEL, +}; + +static struct iio_chan_spec ad74413r_resistance_input_channels[] = { + AD74413R_ADC_CHANNEL(IIO_RESISTANCE, BIT(IIO_CHAN_INFO_PROCESSED)), +}; + +static struct iio_chan_spec ad74413r_digital_input_channels[] = { + AD74413R_ADC_VOLTAGE_CHANNEL, +}; + +static int ad74413r_of_parse_channel_config(struct ad74413r_state *st, + struct device_node *channel_node) +{ + struct ad74413r_channel_config *channel_config; + u32 index; + int ret; + + ret = of_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; + } + + channel_config = &st->channel_configs[index]; + if (channel_config->initialized) { + dev_err(st->dev, "Channel %u has already been initialized\n", index); + return -EINVAL; + } + + channel_config->func = CH_FUNC_HIGH_IMPEDANCE; + of_property_read_u32(channel_node, "adi,ch-func", &channel_config->func); + + channel_config->gpo_config = GPO_CONFIG_100K_PULL_DOWN; + of_property_read_u32(channel_node, "adi,gpo-config", &channel_config->gpo_config); + + if (channel_config->gpo_config == GPO_CONFIG_LOGIC_PARALLEL) { + dev_err(st->dev, "GPO config logic parallel is unsupported\n"); + return -EINVAL; + } + + ret = ad74413r_channel_set_function(st, index, channel_config->func); + if (ret) + return ret; + + ret = ad74413r_set_gpo_mode(st, index, channel_config->gpo_config); + if (ret) + return ret; + + channel_config->initialized = true; + + return 0; +} + +static int ad74413r_parse_channel_configs(struct ad74413r_state *st) +{ + struct device_node *channel_node = NULL; + int ret; + + for_each_available_child_of_node(st->dev->of_node, channel_node) { + if (!of_node_name_eq(channel_node, "channel")) + continue; + + ret = ad74413r_of_parse_channel_config(st, channel_node); + if (ret) { + dev_err(st->dev, "Failed to parse channel %s config: %d\n", + channel_node->name, ret); + goto put_channel_node; + } + } + +put_channel_node: + of_node_put(channel_node); + + return ret; +} + +static int ad74413r_get_iio_channels(struct ad74413r_state *st, u8 func, + struct iio_chan_spec **iio_channels, unsigned int *count) +{ + switch (func) { + case CH_FUNC_HIGH_IMPEDANCE: + *iio_channels = ad74413r_high_impedance_channels; + *count = ARRAY_SIZE(ad74413r_high_impedance_channels); + break; + case CH_FUNC_VOLTAGE_OUTPUT: + *iio_channels = ad74413r_voltage_output_channels; + *count = ARRAY_SIZE(ad74413r_voltage_output_channels); + break; + case CH_FUNC_CURRENT_OUTPUT: + *iio_channels = ad74413r_current_output_channels; + *count = ARRAY_SIZE(ad74413r_current_output_channels); + break; + case CH_FUNC_VOLTAGE_INPUT: + *iio_channels = ad74413r_voltage_input_channels; + *count = ARRAY_SIZE(ad74413r_voltage_input_channels); + break; + case CH_FUNC_CURRENT_INPUT_EXT_POWER: + case CH_FUNC_CURRENT_INPUT_LOOP_POWER: + case CH_FUNC_CURRENT_INPUT_EXT_POWER_HART: + case CH_FUNC_CURRENT_INPUT_LOOP_POWER_HART: + *iio_channels = ad74413r_current_input_channels; + *count = ARRAY_SIZE(ad74413r_current_input_channels); + break; + case CH_FUNC_RESISTANCE_INPUT: + *iio_channels = ad74413r_resistance_input_channels; + *count = ARRAY_SIZE(ad74413r_resistance_input_channels); + break; + case CH_FUNC_DIGITAL_INPUT_LOGIC: + case CH_FUNC_DIGITAL_INPUT_LOOP_POWER: + *iio_channels = ad74413r_digital_input_channels; + *count = ARRAY_SIZE(ad74413r_digital_input_channels); + break; + default: + dev_err(st->dev, "Unhandled channel config function %d\n", func); + return -EINVAL; + } + + return 0; +} + +static int ad74413r_count_iio_channels(struct ad74413r_state *st) +{ + unsigned int index; + int ret; + + st->indio_dev->num_channels = 0; + + for (index = 0; index < AD74413R_CHANNEL_MAX; index++) { + struct ad74413r_channel_config *channel_config = &st->channel_configs[index]; + struct iio_chan_spec *local_channels; + unsigned int num_local_channels; + + ret = ad74413r_get_iio_channels(st, channel_config->func, &local_channels, + &num_local_channels); + if (ret) + return ret; + + st->indio_dev->num_channels += num_local_channels; + } + + return 0; +} + +static int ad74413r_setup_iio_channels(struct ad74413r_state *st) +{ + struct iio_chan_spec *channels; + unsigned int index; + int ret; + + channels = devm_kcalloc(st->dev, sizeof(*channels), + st->indio_dev->num_channels, GFP_KERNEL); + if (!channels) + return -ENOMEM; + + st->indio_dev->channels = channels; + + for (index = 0; index < AD74413R_CHANNEL_MAX; index++) { + struct ad74413r_channel_config *channel_config = &st->channel_configs[index]; + struct iio_chan_spec *local_channels; + unsigned int num_local_channels; + unsigned int chan_index; + + ret = ad74413r_get_iio_channels(st, channel_config->func, &local_channels, + &num_local_channels); + if (ret) + return ret; + + memcpy(channels, local_channels, num_local_channels * sizeof(*local_channels)); + + for (chan_index = 0; chan_index < num_local_channels; chan_index++) { + struct iio_chan_spec *local_channel = &channels[chan_index]; + + local_channel->channel = index; + if (local_channel->output) + local_channel->scan_index = -1; + else + local_channel->scan_index = index; + + } + + channels += num_local_channels; + } + + 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; + unsigned int sillicon_rev_id; + const char *name; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, st); + + st->spi = spi; + st->dev = &spi->dev; + st->config = of_device_get_match_data(&spi->dev); + st->indio_dev = indio_dev; + mutex_init(&st->lock); + init_completion(&st->adc_data_completion); + + name = dev_name(st->dev); + + st->regmap = devm_regmap_init(st->dev, NULL, &spi->dev, &ad74413r_regmap_config); + if (IS_ERR(st->regmap)) { + ret = PTR_ERR(st->regmap); + dev_err(st->dev, "Failed to create regmap: %d\n", ret); + return ret; + } + + ret = regmap_read(st->regmap, AD74413R_REG_SILLICON_REV, &sillicon_rev_id); + if (ret) { + dev_err(st->dev, "Failed to read sillicon rev: %d\n", ret); + return ret; + } + + sillicon_rev_id = FIELD_GET(AD74413R_SILLICON_REV_ID_MASK, sillicon_rev_id); + if (sillicon_rev_id != AD74413R_SILLICON_REV_ID_EXPECTED) { + dev_err(st->dev, "Read sillicon rev id %d does not match expected rev id %d\n", + sillicon_rev_id, AD74413R_SILLICON_REV_ID_EXPECTED); + return -EINVAL; + } + + st->refin_reg = devm_regulator_get(st->dev, "refin"); + if (IS_ERR(st->refin_reg)) { + dev_err(st->dev, "Failed to get refin regulator: %d\n", ret); + return PTR_ERR(st->refin_reg); + } + + ret = regulator_enable(st->refin_reg); + if (ret) { + dev_err(st->dev, "Failed to enable refin regulator: %d\n", ret); + return ret; + } + + ret = devm_add_action_or_reset(st->dev, ad74413r_regulator_disable, + st->refin_reg); + if (ret) { + dev_err(st->dev, "Failed to add refin regulator disable action: %d\n", ret); + return ret; + } + + ret = of_property_read_u32(st->dev->of_node, "adi,rsense-resistance-ohms", + &st->rsense_resistance_ohms); + if (ret) { + dev_err(st->dev, "Failed to get rsense resistance: %d\n", ret); + return ret; + } + + st->gpiochip.label = name; + st->gpiochip.base = -1; + st->gpiochip.ngpio = AD74413R_CHANNEL_MAX; + st->gpiochip.parent = st->dev; + st->gpiochip.can_sleep = true; + st->gpiochip.set = ad74413r_gpio_set; + st->gpiochip.set_multiple = ad74413r_gpio_set_multiple; + st->gpiochip.owner = THIS_MODULE; + st->gpiochip.get = ad74413r_gpio_get; + + ret = devm_gpiochip_add_data(st->dev, &st->gpiochip, st); + if (ret) { + dev_err(st->dev, "Failed to add gpio chip: %d\n", ret); + return ret; + } + + st->trig = devm_iio_trigger_alloc(st->dev, "%s-dev%d", name, indio_dev->id); + if (!st->trig) + return -ENOMEM; + + st->trig->ops = &ad74413r_trigger_ops; + st->trig->dev.parent = st->dev; + iio_trigger_set_drvdata(st->trig, st); + + ret = devm_iio_trigger_register(st->dev, st->trig); + if (ret) + return ret; + + indio_dev->dev.parent = st->dev; + indio_dev->name = name; + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + indio_dev->info = &ad74413r_info; + indio_dev->trig = iio_trigger_get(st->trig); + + ret = ad74413r_parse_channel_configs(st); + if (ret) + return ret; + + ret = ad74413r_count_iio_channels(st); + if (ret) + return ret; + + ret = ad74413r_setup_iio_channels(st); + if (ret) { + dev_err(st->dev, "Failed to setup iio channels: %d\n", ret); + return ret; + } + + ret = ad74413r_set_adc_conv_seq(st, AD74413R_CONV_SEQ_OFF); + if (ret) + return ret; + + ret = devm_request_irq(st->dev, spi->irq, ad74413r_adc_data_interrupt, + IRQF_TRIGGER_FALLING, name, st); + if (ret) { + dev_err(st->dev, "Failed to request threaded irq: %d\n", ret); + return ret; + } + + ret = devm_iio_triggered_buffer_setup(st->dev, indio_dev, &iio_pollfunc_store_time, + &ad74413r_trigger_handler, &ad74413r_buffer_ops); + if (ret) { + dev_err(st->dev, "Failed to setup triggered buffer: %d\n", 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_config ad74412r_config_data = { + .hart_support = false, +}; + +static const struct ad74413r_config ad74413r_config_data = { + .hart_support = true, +}; + +static const struct of_device_id ad74413r_dt_id[] = { + { + .compatible = "adi,ad74412r", + .data = &ad74412r_config_data, + }, + { + .compatible = "adi,ad74413r", + .data = &ad74413r_config_data, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, ad74413r_dt_id); + +static struct spi_driver ad74413r_driver = { + .driver = { + .name = "ad74413r", + .of_match_table = ad74413r_dt_id, + }, + .probe = 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");