From nobody Thu Oct 9 08:43:03 2025 Received: from mx0b-00128a01.pphosted.com (mx0a-00128a01.pphosted.com [148.163.135.77]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 40CF52F003F; Wed, 18 Jun 2025 17:35:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.163.135.77 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268142; cv=none; b=fsIFKlcPrEX+XfUQvgHPpeO0HXzMzCuozLtXAiPO5tYAu7a7WxeupgsHq747fo8B+dVnSxsc1dJcOTxGv+xCSvgd5U9YHuDhnlQnf78W3gPDkyZ06ZEdxfzuoKDE/e0sJwW7/tJtPge2PXksy6Tnn0RnVA3TMk7nUqCUw4YZ99o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268142; c=relaxed/simple; bh=WbsCy+GPkWgS6stWIAF1maMc+H2Zq33MtpmdVGtV+Hw=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=YvootyMC20bfz7CJahVv5bMtkoEJ4p+c61aO+arlHjnY8JGho/o6BmP36Qmfl6Y/5+1msuoQjmAr0D5zbXY2qtq4Op4jNr2xpRenor9somOCfa6KM6lDFFVovN3coBHH3guPxShqcX2GaWYZKA3qi24/HKtdWqxU7RyVt2K3Tyg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com; spf=pass smtp.mailfrom=analog.com; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b=rqtS3NBw; arc=none smtp.client-ip=148.163.135.77 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=analog.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b="rqtS3NBw" Received: from pps.filterd (m0375855.ppops.net [127.0.0.1]) by mx0b-00128a01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 55IFgFxP009662; Wed, 18 Jun 2025 13:35:19 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=analog.com; h=cc :content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=DKIM; bh=KxVzX oiZah2yMLjs8CpqIjJkwr6UYTAtxyjXpbL1NQs=; b=rqtS3NBwW1wdEcQKYY0m5 xglMcBgl2Efsu+wLj2X+twAvEn3qlKT4ErjSGT0jO8J+QTulzLzX65HcCgW7Z089 rPNQ+CkLb370NgmKQkncY56yNP8Dkpd+a9hrlyWC6A2U7MiKSnEYx0tzNDLrFv3o qS8eftv+4XUka0ZpaI1J3PfcMvMP2ruiUNNXr8WcNExvl0ShiWib4CXWgS71EFT4 KZL0OXr96ImDP2QH29iIYRK5UkxuDjAH5OfWmXjcmZF9i0zFw6LEld8QfjVaBwl3 K8zYOMgR8lS2kBLzzYanGFEaWJGdxkPY3wIaVvFlCWgNsdmDeaDPhNkXtXKucZbr w== Received: from nwd2mta3.analog.com ([137.71.173.56]) by mx0b-00128a01.pphosted.com (PPS) with ESMTPS id 47btfytdyn-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 18 Jun 2025 13:35:18 -0400 (EDT) Received: from ASHBMBX9.ad.analog.com (ASHBMBX9.ad.analog.com [10.64.17.10]) by nwd2mta3.analog.com (8.14.7/8.14.7) with ESMTP id 55IHZHEb031561 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Wed, 18 Jun 2025 13:35:17 -0400 Received: from ASHBCASHYB4.ad.analog.com (10.64.17.132) by ASHBMBX9.ad.analog.com (10.64.17.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 18 Jun 2025 13:35:17 -0400 Received: from ASHBMBX8.ad.analog.com (10.64.17.5) by ASHBCASHYB4.ad.analog.com (10.64.17.132) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 18 Jun 2025 13:35:16 -0400 Received: from zeus.spd.analog.com (10.66.68.11) by ashbmbx8.ad.analog.com (10.64.17.5) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Wed, 18 Jun 2025 13:35:16 -0400 Received: from work.ad.analog.com (HYB-hERzalRezfV.ad.analog.com [10.65.205.9]) by zeus.spd.analog.com (8.15.1/8.15.1) with ESMTP id 55IHYwPv007396; Wed, 18 Jun 2025 13:35:01 -0400 From: Marcelo Schmitt To: , , , CC: , , , , , , , , , , , , , Subject: [PATCH v6 01/12] dt-bindings: iio: adc: Add AD4170 Date: Wed, 18 Jun 2025 14:34:57 -0300 Message-ID: <6399c1eb6d8e1bbdf720f189a7244b1d75a90ed2.1750258776.git.marcelo.schmitt@analog.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ADIRuleOP-NewSCL: Rule Triggered X-Proofpoint-ORIG-GUID: wehAopHpPO8X4ePHZlrtwaQFZLAldGvH X-Authority-Analysis: v=2.4 cv=QsVe3Uyd c=1 sm=1 tr=0 ts=6852f8d6 cx=c_pps a=PpDZqlmH/M8setHirZLBMw==:117 a=PpDZqlmH/M8setHirZLBMw==:17 a=IkcTkHD0fZMA:10 a=6IFa9wvqVegA:10 a=gEfo2CItAAAA:8 a=gAnH3GRIAAAA:8 a=VwQbUJbxAAAA:8 a=8m0O0hpeTvo6OBoM9V0A:9 a=3ZKOabzyN94A:10 a=QEXdDO2ut3YA:10 a=sptkURWiP4Gy88Gu7hUp:22 X-Proofpoint-GUID: wehAopHpPO8X4ePHZlrtwaQFZLAldGvH X-Proofpoint-Spam-Details-Enc: AW1haW4tMjUwNjE4MDE0OSBTYWx0ZWRfX085dFhCY7Iao GrQbnErFYfj55e8x8yy4nhga11BdVefZeCPcY0rLSL0ii6tqQdpaRUFGKbzCe+LT8b8r9mla729 aeACnfCknYAZBpw45sfyMMe9PX4jGx/Nayj/nONH4OSo3351kr2/Rnleql/Y+qHh4f0r3BW7akk 3B1BuqWZsgT43Hw1mbtuNiJEva8chqeECPWbE3vWI2h+sC20RxN+WnOL+GcQtoGhuCZlKKTFqJH HEy0YqJnUageQAzdIaJGR+J/Wz8fI3eqlk0GkIQTztNzZx+U2wbpK+3Ezf3GB0qyw89LARTEjDO jBFf2Z4u2l/9HrGhydEGdq/zThswqsewcmQMBNolZq1nBAKU92jNvqYClrNKz81EDrVSOqLNXmr 2+HWrkQrMhQnVwboX2IGz6Qh55a8/xpa5UyrWup8w7aHaB0TBY9XrEM7rQ6OVRZrPjvpJQJb X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1099,Hydra:6.0.736,FMLib:17.12.80.40 definitions=2025-06-18_05,2025-06-18_03,2025-03-28_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 mlxscore=0 lowpriorityscore=0 impostorscore=0 malwarescore=0 bulkscore=0 phishscore=0 suspectscore=0 spamscore=0 mlxlogscore=999 priorityscore=1501 adultscore=0 clxscore=1015 classifier=spam authscore=0 authtc=n/a authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.19.0-2505280000 definitions=main-2506180149 Add device tree documentation for AD4170 and similar sigma-delta ADCs. The AD4170 is a 24-bit, multichannel, sigma-delta ADC. Signed-off-by: Marcelo Schmitt Acked-by: Conor Dooley --- Change log v5 -> v6 - Made reference-buffer string type. - Moved required section before patternProperties. - Made avss, refin1n, refin2n documentation open to accepting positive and negative voltage specifications where appropriate. The point of making avss-supply, refin1n-supply and refin2n-supply document= ation open to negative voltage values is to allow device tree to specify the regu= lator true voltage level so the drivers won't need to workaround negative supplie= s in the future. .../bindings/iio/adc/adi,ad4170.yaml | 558 ++++++++++++++++++ MAINTAINERS | 7 + 2 files changed, 565 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ad4170.ya= ml diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml b/Do= cumentation/devicetree/bindings/iio/adc/adi,ad4170.yaml new file mode 100644 index 000000000000..b7fe664bb87d --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml @@ -0,0 +1,558 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/adi,ad4170.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD4170 and similar Analog to Digital Converters + +maintainers: + - Marcelo Schmitt + +description: | + Analog Devices AD4170 series of Sigma-delta Analog to Digital Converters. + Specifications can be found at: + https://www.analog.com/media/en/technical-documentation/data-sheets/ad= 4170-4.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/ad= 4190-4.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/ad= 4195-4.pdf + +$ref: /schemas/spi/spi-peripheral-props.yaml# + +$defs: + reference-buffer: + description: | + Enable precharge buffer, full buffer, or skip reference buffering of + the positive/negative voltage reference. Because the output impedance + of the source driving the voltage reference inputs may be dynamic, + resistive/capacitive combinations of those inputs can cause DC gain + errors if the reference inputs go unbuffered into the ADC. Enable + reference buffering if the provided reference source has dynamic high + impedance output. Note the absolute voltage allowed on REFINn+ and R= EFINn- + inputs is from AVSS - 50 mV to AVDD + 50 mV when the reference buffe= rs are + disabled but narrows to AVSS to AVDD when reference buffering is ena= bled + or in precharge mode. The valid options for this property are: + 0: Reference precharge buffer. + 1: Full reference buffering. + 2: Bypass reference buffers (buffering disabled). + $ref: /schemas/types.yaml#/definitions/string + enum: [ precharge, full, disabled ] + default: full + +properties: + compatible: + enum: + - adi,ad4170 + - adi,ad4190 + - adi,ad4195 + + avss-supply: + description: + Reference voltage supply for AVSS. A =E2=88=922.625V minimum and 0V = maximum supply + that powers the chip. If not provided, AVSS is assumed to be at syst= em + ground (0V). + + avdd-supply: + description: + A supply of 4.75V to 5.25V relative to AVSS that powers the chip (AV= DD). + + iovdd-supply: + description: 1.7V to 5.25V reference supply to the serial interface (I= OVDD). + + refin1p-supply: + description: REFIN+ supply that can be used as reference for conversio= n. + + refin1n-supply: + description: REFIN- supply that can be used as reference for conversio= n. + + refin2p-supply: + description: REFIN2+ supply that can be used as reference for conversi= on. + + refin2n-supply: + description: REFIN2- supply that can be used as reference for conversi= on. + + spi-cpol: true + + spi-cpha: true + + interrupts: + description: + Interrupt for signaling the completion of conversion results. The da= ta + ready signal (RDY) used as interrupt is by default provided on the S= DO + pin. Alternatively, it can be provided on the DIG_AUX1 pin in which = case + the chip disables the RDY function on SDO. Thus, there can be only o= ne + data ready interrupt enabled at a time. + + interrupt-names: + description: + Specify which pin should be configured as Data Ready interrupt. + enum: + - sdo + - dig_aux1 + + clocks: + maxItems: 1 + description: + Optional external clock source. Can specify either an external clock= or + external crystal. + + clock-names: + enum: + - ext-clk + - xtal + default: ext-clk + + '#clock-cells': + const: 0 + + clock-output-names: + maxItems: 1 + + gpio-controller: true + + "#gpio-cells": + const: 2 + description: | + The first cell is for the GPIO number: 0 to 3. + The second cell takes standard GPIO flags. + + ldac-gpios: + description: + GPIO connected to DIG_AUX2 pin to be used as LDAC toggle to control = the + transfer of data from the DAC_INPUT_A register to the DAC. + maxItems: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + adi,vbias-pins: + description: Analog inputs to apply a voltage bias of (AVDD =E2=88=92 = AVSS) / 2 to. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 9 + items: + minimum: 0 + maximum: 8 + +allOf: + # Some devices don't have integrated DAC + - if: + properties: + compatible: + contains: + enum: + - adi,ad4190 + - adi,ad4195 + then: + properties: + ldac-gpios: false + + # Require to specify the interrupt pin when using interrupts + - if: + required: + - interrupts + then: + required: + - interrupt-names + + # If an external clock is set, the internal clock cannot go out and vice= versa + - oneOf: + - required: [clocks] + properties: + '#clock-cells': false + - required: ['#clock-cells'] + properties: + clocks: false + +required: + - compatible + - reg + - avdd-supply + - iovdd-supply + - spi-cpol + - spi-cpha + +unevaluatedProperties: false + +patternProperties: + "^channel@[0-9a-f]$": + $ref: /schemas/iio/adc/adc.yaml# + unevaluatedProperties: false + description: + Represents the external channels which are connected to the ADC. + + properties: + reg: + description: + The channel number. + minimum: 0 + maximum: 15 + + diff-channels: + description: | + This property is used for defining the inputs of a differential + voltage channel. The first value is the positive input and the s= econd + value is the negative input of the channel. + + Besides the analog input pins AIN0 to AIN8, there are special in= puts + that can be selected with the following values: + 17: Internal temperature sensor + 18: (AVDD-AVSS)/5 + 19: (IOVDD-DGND)/5 + 20: DAC output + 21: ALDO + 22: DLDO + 23: AVSS + 24: DGND + 25: REFIN+ + 26: REFIN- + 27: REFIN2+ + 28: REFIN2- + 29: REFOUT + For the internal temperature sensor, use the input number for bo= th + inputs (i.e. diff-channels =3D <17 17>). + items: + enum: [0, 1, 2, 3, 4, 5, 6, 7, 8, 17, 18, 19, 20, 21, 22, 23, 24= , 25, + 26, 27, 28, 29] + + adi,reference-select: + description: | + Select the reference source to use when converting on the + specific channel. Valid values are: + 0: REFIN+/REFIN- + 1: REFIN2+/REFIN2=E2=88=92 + 2: REFOUT/AVSS (internal reference) + 3: AVDD/AVSS + If not specified, REFOUT/AVSS is used. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2, 3] + default: 1 + + adi,positive-reference-buffer: + $ref: '#/$defs/reference-buffer' + + adi,negative-reference-buffer: + $ref: '#/$defs/reference-buffer' + + adi,sensor-type: + description: + The AD4170 and similar designs have features to aid interfacing = with + load cell weigh scale, RTD, and thermocouple sensors. Each of th= ose + sensor types requires either distinct wiring configuration or + external circuitry for proper sensor operation and can use diffe= rent + ADC chip functionality on their setups. A key characteristic of = those + external sensors is that they must be excited either by voltage = supply + or by ADC chip excitation signals. The sensor can then be read t= hrough + a pair of analog inputs. This property specifies which particular + sensor type is connected to the ADC so it can be properly setup = and + handled. Omit this property for conventional (not weigh scale, R= TD, or + thermocouple) ADC channel setups. + $ref: /schemas/types.yaml#/definitions/string + enum: [ weighscale, rtd, thermocouple ] + + adi,excitation-pin-0: + description: + Analog input to apply excitation current to while the channel + is active. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 20 + default: 0 + + adi,excitation-pin-1: + description: + Analog input to apply excitation current to while the channel + is active. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 20 + default: 0 + + adi,excitation-pin-2: + description: + Analog input to apply excitation current to while the channel + is active. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 20 + default: 0 + + adi,excitation-pin-3: + description: + Analog input to apply excitation current to while the channel + is active. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 20 + default: 0 + + adi,excitation-current-0-microamp: + description: + Excitation current in microamperes to be applied to pin specifie= d in + adi,excitation-pin-0 while this channel is active. + enum: [0, 10, 50, 100, 250, 500, 1000, 1500] + default: 0 + + adi,excitation-current-1-microamp: + description: + Excitation current in microamperes to be applied to pin specifie= d in + adi,excitation-pin-1 while this channel is active. + enum: [0, 10, 50, 100, 250, 500, 1000, 1500] + default: 0 + + adi,excitation-current-2-microamp: + description: + Excitation current in microamperes to be applied to pin specifie= d in + adi,excitation-pin-2 while this channel is active. + enum: [0, 10, 50, 100, 250, 500, 1000, 1500] + default: 0 + + adi,excitation-current-3-microamp: + description: + Excitation current in microamperes to be applied to pin specifie= d in + adi,excitation-pin-3 while this channel is active. + enum: [0, 10, 50, 100, 250, 500, 1000, 1500] + default: 0 + + adi,excitation-ac: + type: boolean + description: + Whether the external sensor has to be AC or DC excited. When omi= tted, + it is DC excited. + + allOf: + - oneOf: + - required: [single-channel, common-mode-channel] + properties: + diff-channels: false + - required: [diff-channels] + properties: + single-channel: false + common-mode-channel: false + # Usual ADC channels don't need external circuitry excitation. + - if: + not: + required: + - adi,sensor-type + then: + properties: + adi,excitation-pin-0: false + adi,excitation-pin-1: false + adi,excitation-pin-2: false + adi,excitation-pin-3: false + adi,excitation-current-0-microamp: false + adi,excitation-current-1-microamp: false + adi,excitation-current-2-microamp: false + adi,excitation-current-3-microamp: false + adi,excitation-ac: false + # Weigh scale bridge AC excited with one pair of predefined signals. + - if: + allOf: + - properties: + adi,sensor-type: + contains: + const: weighscale + - required: + - adi,excitation-ac + - adi,excitation-pin-2 + - adi,excitation-pin-3 + - not: + required: + - adi,excitation-current-2-microamp + - adi,excitation-current-3-microamp + then: + properties: + adi,excitation-pin-2: + const: 19 + adi,excitation-pin-3: + const: 20 + # Weigh scale bridge AC excited with two pairs of predefined signals. + - if: + allOf: + - properties: + adi,sensor-type: + contains: + const: weighscale + - required: + - adi,excitation-ac + - adi,excitation-pin-0 + - adi,excitation-pin-1 + - adi,excitation-pin-2 + - adi,excitation-pin-3 + - not: + required: + - adi,excitation-current-0-microamp + - adi,excitation-current-1-microamp + - adi,excitation-current-2-microamp + - adi,excitation-current-3-microamp + then: + properties: + adi,excitation-pin-0: + const: 17 + adi,excitation-pin-1: + const: 18 + adi,excitation-pin-2: + const: 19 + adi,excitation-pin-3: + const: 20 + +examples: + - | + #include + spi { + #address-cells =3D <1>; + #size-cells =3D <0>; + + adc@0 { + compatible =3D "adi,ad4170"; + reg =3D <0>; + spi-max-frequency =3D <20000000>; + spi-cpol; + spi-cpha; + avdd-supply =3D <&avdd>; + iovdd-supply =3D <&iovdd>; + clocks =3D <&clk>; + clock-names =3D "xtal"; + interrupts =3D <0 IRQ_TYPE_EDGE_FALLING>; + interrupt-names =3D "dig_aux1"; + adi,vbias-pins =3D <7>; + #address-cells =3D <1>; + #size-cells =3D <0>; + + // Sample AIN0 with respect to DGND throughout AVDD/DGND input= range + // Pseudo-differential unipolar + channel@0 { + reg =3D <0>; + single-channel =3D <0>; + common-mode-channel =3D <24>; + adi,reference-select =3D <3>; + }; + // Weigh scale sensor + channel@1 { + reg =3D <1>; + bipolar; + diff-channels =3D <1 2>; + adi,reference-select =3D <0>; + adi,positive-reference-buffer =3D "precharge"; + adi,negative-reference-buffer =3D "precharge"; + adi,sensor-type =3D "weighscale"; + adi,excitation-pin-2 =3D <19>; + adi,excitation-pin-3 =3D <20>; + adi,excitation-ac; + }; + // RTD sensor + channel@2 { + reg =3D <2>; + bipolar; + diff-channels =3D <3 4>; + adi,reference-select =3D <0>; + adi,sensor-type =3D "rtd"; + adi,excitation-pin-0 =3D <5>; + adi,excitation-pin-1 =3D <6>; + adi,excitation-current-0-microamp =3D <500>; + adi,excitation-current-1-microamp =3D <500>; + adi,excitation-ac; + }; + // Thermocouple sensor + channel@3 { + reg =3D <3>; + bipolar; + diff-channels =3D <7 8>; + adi,reference-select =3D <0>; + adi,sensor-type =3D "thermocouple"; + adi,excitation-pin-0 =3D <18>; + adi,excitation-current-0-microamp =3D <500>; + }; + }; + }; + - | + #include + spi { + #address-cells =3D <1>; + #size-cells =3D <0>; + + adc@0 { + compatible =3D "adi,ad4170"; + reg =3D <0>; + spi-max-frequency =3D <20000000>; + spi-cpol; + spi-cpha; + avdd-supply =3D <&avdd>; + iovdd-supply =3D <&iovdd>; + #clock-cells =3D <0>; + clock-output-names =3D "ad4170-clk16mhz"; + interrupts =3D <0 IRQ_TYPE_EDGE_FALLING>; + interrupt-names =3D "dig_aux1"; + #address-cells =3D <1>; + #size-cells =3D <0>; + + // Sample AIN0 with respect to AIN1 throughout AVDD/AVSS input= range + // Differential bipolar. If AVSS < 0V, differential true bipol= ar + channel@0 { + reg =3D <0>; + bipolar; + diff-channels =3D <0 1>; + adi,reference-select =3D <3>; + }; + // Sample AIN2 with respect to DGND throughout AVDD/DGND input= range + // Pseudo-differential unipolar + channel@1 { + reg =3D <1>; + single-channel =3D <2>; + common-mode-channel =3D <24>; + adi,reference-select =3D <3>; + }; + // Sample AIN3 with respect to 2.5V throughout AVDD/AVSS input= range + // Pseudo-differential bipolar + channel@2 { + reg =3D <2>; + bipolar; + single-channel =3D <3>; + common-mode-channel =3D <29>; + adi,reference-select =3D <3>; + }; + // Sample AIN4 with respect to DGND throughout AVDD/AVSS input= range + // Pseudo-differential bipolar + channel@3 { + reg =3D <3>; + bipolar; + single-channel =3D <4>; + common-mode-channel =3D <24>; + adi,reference-select =3D <3>; + }; + // Sample AIN5 with respect to 2.5V throughout AVDD/AVSS input= range + // Pseudo-differential unipolar (AD4170 datasheet page 46 exam= ple) + channel@4 { + reg =3D <4>; + single-channel =3D <5>; + common-mode-channel =3D <29>; + adi,reference-select =3D <3>; + }; + // Sample AIN6 with respect to 2.5V throughout REFIN+/REFIN- i= nput range + // Pseudo-differential bipolar + channel@5 { + reg =3D <5>; + bipolar; + single-channel =3D <6>; + common-mode-channel =3D <29>; + adi,reference-select =3D <0>; + }; + // Weigh scale sensor + channel@6 { + reg =3D <6>; + bipolar; + diff-channels =3D <7 8>; + adi,reference-select =3D <0>; + adi,sensor-type =3D "weighscale"; + adi,excitation-pin-0 =3D <17>; + adi,excitation-pin-1 =3D <18>; + adi,excitation-pin-2 =3D <19>; + adi,excitation-pin-3 =3D <20>; + adi,excitation-ac; + }; + }; + }; +... + diff --git a/MAINTAINERS b/MAINTAINERS index abfd5ded8735..44735314a43e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1392,6 +1392,13 @@ F: Documentation/ABI/testing/sysfs-bus-iio-adc-ad4130 F: Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml F: drivers/iio/adc/ad4130.c =20 +ANALOG DEVICES INC AD4170 DRIVER +M: Marcelo Schmitt +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml + ANALOG DEVICES INC AD4695 DRIVER M: Michael Hennerich M: Nuno S=C3=A1 --=20 2.47.2 From nobody Thu Oct 9 08:43:03 2025 Received: from mx0a-00128a01.pphosted.com (mx0a-00128a01.pphosted.com [148.163.135.77]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 90AA72F0C5B; Wed, 18 Jun 2025 17:36:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.163.135.77 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268184; cv=none; b=P1V1oeyTs4IJegM41wLtJ7Ti2wXyOx3OUwa4fVOPU/HKhxjbKM09UN2xB1x1c/TrJwlHW6EkXoC2fijZmBgPZgLFSoQ7jcgTvKjoDF4bqCwzwwR4rv5y0PYQxvRQUmci1iHvaZxuu6Ym0XhnU9Vvt3axJXcCYe1zOqH9kF6Y7Gc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268184; c=relaxed/simple; bh=LJVy9E525F1sjiUptPsPnKxRSQVAXq8CXzGyydpQ1q0=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=qFlRYxDpac519V8A2E8GEYkYqYkUj7WQuAB89wT3QIfKOl/Jzl4RTuNtL0rp8rySCN+lyHmSyF8kkE/6VYp7bl9VWmYqZhj2aqDUsgzXekPf9UHJyIYV6llnYWwXWmgnqNufGXN1vEvyu7rK8upivnnrh+NNRq70b9d8sLcj46s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com; spf=pass smtp.mailfrom=analog.com; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b=alMqQFCh; arc=none smtp.client-ip=148.163.135.77 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=analog.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b="alMqQFCh" Received: from pps.filterd (m0167088.ppops.net [127.0.0.1]) by mx0a-00128a01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 55IGSPlN032314; Wed, 18 Jun 2025 13:35:59 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=analog.com; h=cc :content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=DKIM; bh=lBjc0 h19C6XmSNbEQuDmtKja2SWIf5cEoqNfO/TrvD0=; b=alMqQFCh7NYDkZ0XKkGuM xLGwBgUrBiK/pTkLad01ST8eNh50XZOyc3Jo13X8RZziahjjdd4ry7NPBxqU5M4d NvoQZhM4RoKWra1S6WULy/zd7+O8XASDCQOyAxvJNWEo8Fd+4l1oTpYp+DEs7jzQ oMqgbSOGWTcuGcedDUuLF/CQ4k80osuLYuxF9KU0H7dxAVrqxVxZfH9wVt2f/70k E0fyWGgtQsEdYZtgXoQCs8XxraqS+tm31WTFtFOxi+cNhhV4rbHnOrzSZ+CBrgWm kZogZzAJp4abkgtRT6IcvNMFXOiMKS8LNL00pr3WVLfo4kAnKhgVg2aD7AQ28JDr A== Received: from nwd2mta4.analog.com ([137.71.173.58]) by mx0a-00128a01.pphosted.com (PPS) with ESMTPS id 47bfshdd0d-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 18 Jun 2025 13:35:58 -0400 (EDT) Received: from ASHBMBX8.ad.analog.com (ASHBMBX8.ad.analog.com [10.64.17.5]) by nwd2mta4.analog.com (8.14.7/8.14.7) with ESMTP id 55IHZvBI015842 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Wed, 18 Jun 2025 13:35:57 -0400 Received: from ASHBMBX8.ad.analog.com (10.64.17.5) by ASHBMBX8.ad.analog.com (10.64.17.5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 18 Jun 2025 13:35:57 -0400 Received: from zeus.spd.analog.com (10.66.68.11) by ashbmbx8.ad.analog.com (10.64.17.5) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Wed, 18 Jun 2025 13:35:57 -0400 Received: from work.ad.analog.com (HYB-hERzalRezfV.ad.analog.com [10.65.205.9]) by zeus.spd.analog.com (8.15.1/8.15.1) with ESMTP id 55IHZeJ9007443; Wed, 18 Jun 2025 13:35:43 -0400 From: Marcelo Schmitt To: , , , CC: Ana-Maria Cusco , , , , , , , , , , , , , , Subject: [PATCH v6 02/12] iio: adc: Add basic support for AD4170 Date: Wed, 18 Jun 2025 14:35:38 -0300 Message-ID: <51a3055eb4a5c643cc4ced749f452d4e9b64ecf8.1750258776.git.marcelo.schmitt@analog.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ADIRuleOP-NewSCL: Rule Triggered X-Proofpoint-ORIG-GUID: dRD-gFi50eEbkFTpybeSTs_SRBqtosJf X-Authority-Analysis: v=2.4 cv=SKhCVPvH c=1 sm=1 tr=0 ts=6852f8fe cx=c_pps a=3WNzaoukacrqR9RwcOSAdA==:117 a=3WNzaoukacrqR9RwcOSAdA==:17 a=IkcTkHD0fZMA:10 a=6IFa9wvqVegA:10 a=gAnH3GRIAAAA:8 a=VwQbUJbxAAAA:8 a=QHhQT6YjlfCz_d8WS5QA:9 a=pSMNRXS9UbP0vK1Z:21 a=3ZKOabzyN94A:10 a=QEXdDO2ut3YA:10 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjUwNjE4MDE0OSBTYWx0ZWRfX2rT3w5D9q1Rz ZccjCNvH233+AS5JqLTWeGGmO7VbhZaNOLaGJL3yJddEhghK4ydcaEWNnWZxLo2c2HvneShtq0q myb3AwnHO63mdcJfcetYGg8Ad+aYqBpq2BrchGXVOMu7lWbuEPa7nIlCYJV3B8sQt2jd5zLumo0 b//vKZNdgxwPGPgS774canP/CChg9tzaLnnY5i++BcSrhVuq5voARV1fVsxM5Rj6iSzBbftSG1B z8CDpPglIRgYdov6otbrCLLGSmUoZzVzCrurcZPUhYReuNfg3wj6aT8oXD1iCjALESdXBU5pt/X Q6Xf6Eg8dN6FLOLnYjvui9eSOUaNFqAcaCklOdVrcoEazAc3mOGPF3sq+w6xHTxQ/6+pFv/0UU7 fJcyFyQDuw2COI/286ol6vif3CNA9lSIygKJMB/vdkrxw8DJWNjavL1WCTWYX07FfY/unCT+ X-Proofpoint-GUID: dRD-gFi50eEbkFTpybeSTs_SRBqtosJf X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1099,Hydra:6.0.736,FMLib:17.12.80.40 definitions=2025-06-18_05,2025-06-18_03,2025-03-28_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 impostorscore=0 bulkscore=0 adultscore=0 lowpriorityscore=0 spamscore=0 suspectscore=0 mlxscore=0 malwarescore=0 mlxlogscore=999 clxscore=1011 phishscore=0 priorityscore=1501 classifier=spam authscore=0 authtc=n/a authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.19.0-2505280000 definitions=main-2506180149 From: Ana-Maria Cusco The AD4170 is a multichannel, low noise, 24-bit precision sigma-delta analog to digital converter. The AD4170 design offers a flexible data acquisition solution with crosspoint multiplexed analog inputs, configurable ADC voltage reference inputs, ultra-low noise integrated PGA, digital filtering, wide range of configurable output data rates, internal oscillator and temperature sensor, four GPIOs, and integrated features for interfacing with load cell weigh scales, RTD, and thermocouple sensors. Add basic support for the AD4170 ADC with the following features: - Single-shot read. - Analog front end PGA configuration. - Differential and pseudo-differential input configuration. Signed-off-by: Ana-Maria Cusco Co-developed-by: Marcelo Schmitt Signed-off-by: Marcelo Schmitt --- Change log v5 -> v6 - Added trailing commas to enum declarations. - Updated int pins_fn -> unsigned int pins_fn. - Use local string to simplify dt parsing and error handling. - Updated 'if (ret < 0)' to 'if (ret)' wherever appropriate. - Expanded code comment to clarify that refp can only be >=3D at ad4170_get= _input_range(). - Pass fwnode_property_read_u32() return to dev_err_probe() in case of erro= r. - Declared a define for the constant 2 byte size SPI instruction phase. - Dropped use of static_assert(). MAINTAINERS | 1 + drivers/iio/adc/Kconfig | 12 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ad4170.c | 1552 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 1566 insertions(+) create mode 100644 drivers/iio/adc/ad4170.c diff --git a/MAINTAINERS b/MAINTAINERS index 44735314a43e..66d261ebbaf8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1398,6 +1398,7 @@ L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml +F: drivers/iio/adc/ad4170.c =20 ANALOG DEVICES INC AD4695 DRIVER M: Michael Hennerich diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 3bd03df9a976..03966d07a721 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -84,6 +84,18 @@ config AD4130 To compile this driver as a module, choose M here: the module will be called ad4130. =20 + +config AD4170 + tristate "Analog Device AD4170 ADC Driver" + depends on SPI + select REGMAP_SPI + help + Say yes here to build support for Analog Devices AD4170 SPI analog + to digital converters (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad4170. + config AD4695 tristate "Analog Device AD4695 ADC Driver" depends on SPI diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 6516ccb4d63b..d99c35ff9a1c 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_AD4000) +=3D ad4000.o obj-$(CONFIG_AD4030) +=3D ad4030.o obj-$(CONFIG_AD4080) +=3D ad4080.o obj-$(CONFIG_AD4130) +=3D ad4130.o +obj-$(CONFIG_AD4170) +=3D ad4170.o obj-$(CONFIG_AD4695) +=3D ad4695.o obj-$(CONFIG_AD4851) +=3D ad4851.o obj-$(CONFIG_AD7091R) +=3D ad7091r-base.o diff --git a/drivers/iio/adc/ad4170.c b/drivers/iio/adc/ad4170.c new file mode 100644 index 000000000000..58716ad6e7fc --- /dev/null +++ b/drivers/iio/adc/ad4170.c @@ -0,0 +1,1552 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Analog Devices, Inc. + * Author: Ana-Maria Cusco + * Author: Marcelo Schmitt + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * AD4170 registers + * Multibyte register addresses point to the most significant byte which i= s the + * address to use to get the most significant byte first (address accessed= is + * decremented by one for each data byte) + * + * Each register address define follows the AD4170__REG form. + * Each mask follows the AD4170__ form. + * E.g. AD4170_PIN_MUXING_DIG_AUX1_CTRL_MSK is for accessing DIG_AUX1_CTRL= field + * of PIN_MUXING_REG. + * Each constant follows the AD4170___ for= m. + * E.g. AD4170_PIN_MUXING_DIG_AUX1_DISABLED is the value written to + * DIG_AUX1_CTRL field of PIN_MUXING register to disable DIG_AUX1 pin. + * Some register names and register field names are shortened versions of + * their datasheet counterpart names to provide better code readability. + */ +#define AD4170_CONFIG_A_REG 0x00 +#define AD4170_DATA_24B_REG 0x1E +#define AD4170_PIN_MUXING_REG 0x69 +#define AD4170_ADC_CTRL_REG 0x71 +#define AD4170_CHAN_EN_REG 0x79 +#define AD4170_CHAN_SETUP_REG(x) (0x81 + 4 * (x)) +#define AD4170_CHAN_MAP_REG(x) (0x83 + 4 * (x)) +#define AD4170_MISC_REG(x) (0xC1 + 14 * (x)) +#define AD4170_AFE_REG(x) (0xC3 + 14 * (x)) +#define AD4170_FILTER_REG(x) (0xC5 + 14 * (x)) +#define AD4170_FILTER_FS_REG(x) (0xC7 + 14 * (x)) +#define AD4170_OFFSET_REG(x) (0xCA + 14 * (x)) +#define AD4170_GAIN_REG(x) (0xCD + 14 * (x)) + +#define AD4170_REG_READ_MASK BIT(14) + +/* AD4170_CONFIG_A_REG - INTERFACE_CONFIG_A REGISTER */ +#define AD4170_SW_RESET_MSK (BIT(7) | BIT(0)) + +/* AD4170_PIN_MUXING_REG */ +#define AD4170_PIN_MUXING_DIG_AUX1_CTRL_MSK GENMASK(5, 4) + +/* AD4170_ADC_CTRL_REG */ +#define AD4170_ADC_CTRL_MULTI_DATA_REG_SEL_MSK BIT(7) +#define AD4170_ADC_CTRL_MODE_MSK GENMASK(3, 0) + +/* AD4170_CHAN_EN_REG */ +#define AD4170_CHAN_EN(ch) BIT(ch) + +/* AD4170_CHAN_SETUP_REG */ +#define AD4170_CHAN_SETUP_SETUP_MSK GENMASK(2, 0) + +/* AD4170_CHAN_MAP_REG */ +#define AD4170_CHAN_MAP_AINP_MSK GENMASK(12, 8) +#define AD4170_CHAN_MAP_AINM_MSK GENMASK(4, 0) + +/* AD4170_AFE_REG */ +#define AD4170_AFE_REF_BUF_M_MSK GENMASK(11, 10) +#define AD4170_AFE_REF_BUF_P_MSK GENMASK(9, 8) +#define AD4170_AFE_REF_SELECT_MSK GENMASK(6, 5) +#define AD4170_AFE_BIPOLAR_MSK BIT(4) +#define AD4170_AFE_PGA_GAIN_MSK GENMASK(3, 0) + +/* AD4170 register constants */ + +/* AD4170_CHAN_MAP_REG constants */ +#define AD4170_CHAN_MAP_AIN(x) (x) +#define AD4170_CHAN_MAP_TEMP_SENSOR 17 +#define AD4170_CHAN_MAP_AVDD_AVSS_P 18 +#define AD4170_CHAN_MAP_AVDD_AVSS_N 18 +#define AD4170_CHAN_MAP_IOVDD_DGND_P 19 +#define AD4170_CHAN_MAP_IOVDD_DGND_N 19 +#define AD4170_CHAN_MAP_AVSS 23 +#define AD4170_CHAN_MAP_DGND 24 +#define AD4170_CHAN_MAP_REFIN1_P 25 +#define AD4170_CHAN_MAP_REFIN1_N 26 +#define AD4170_CHAN_MAP_REFIN2_P 27 +#define AD4170_CHAN_MAP_REFIN2_N 28 +#define AD4170_CHAN_MAP_REFOUT 29 + +/* AD4170_PIN_MUXING_REG constants */ +#define AD4170_PIN_MUXING_DIG_AUX1_DISABLED 0x0 +#define AD4170_PIN_MUXING_DIG_AUX1_RDY 0x1 + +/* AD4170_ADC_CTRL_REG constants */ +#define AD4170_ADC_CTRL_MODE_SINGLE 0x4 +#define AD4170_ADC_CTRL_MODE_IDLE 0x7 + +/* Device properties and auxiliary constants */ + +#define AD4170_NUM_ANALOG_PINS 9 +#define AD4170_MAX_CHANNELS 16 +#define AD4170_MAX_ANALOG_PINS 8 +#define AD4170_MAX_SETUPS 8 +#define AD4170_INVALID_SETUP 9 +#define AD4170_SPI_INST_PHASE_LEN 2 +#define AD4170_SPI_MAX_XFER_LEN 6 + +#define AD4170_INT_REF_2_5V 2500000 + +/* Internal and external clock properties */ +#define AD4170_INT_CLOCK_16MHZ (16 * HZ_PER_MHZ) + +#define AD4170_NUM_PGA_OPTIONS 10 + +#define AD4170_GAIN_REG_DEFAULT 0x555555 + +static const unsigned int ad4170_reg_size[] =3D { + [AD4170_CONFIG_A_REG] =3D 1, + [AD4170_DATA_24B_REG] =3D 3, + [AD4170_PIN_MUXING_REG] =3D 2, + [AD4170_ADC_CTRL_REG] =3D 2, + [AD4170_CHAN_EN_REG] =3D 2, + /* + * CHANNEL_SETUP and CHANNEL_MAP register are all 2 byte size each and + * their addresses are interleaved such that we have CHANNEL_SETUP0 + * address followed by CHANNEL_MAP0 address, followed by CHANNEL_SETUP1, + * and so on until CHANNEL_MAP15. + * Thus, initialize the register size for them only once. + */ + [AD4170_CHAN_SETUP_REG(0) ... AD4170_CHAN_MAP_REG(AD4170_MAX_CHANNELS - 1= )] =3D 2, + /* + * MISC, AFE, FILTER, FILTER_FS, OFFSET, and GAIN register addresses are + * also interleaved but MISC, AFE, FILTER, FILTER_FS, OFFSET are 16-bit + * while OFFSET, GAIN are 24-bit registers so we can't init them all to + * the same size. + */ + [AD4170_MISC_REG(0) ... AD4170_FILTER_FS_REG(0)] =3D 2, + [AD4170_MISC_REG(1) ... AD4170_FILTER_FS_REG(1)] =3D 2, + [AD4170_MISC_REG(2) ... AD4170_FILTER_FS_REG(2)] =3D 2, + [AD4170_MISC_REG(3) ... AD4170_FILTER_FS_REG(3)] =3D 2, + [AD4170_MISC_REG(4) ... AD4170_FILTER_FS_REG(4)] =3D 2, + [AD4170_MISC_REG(5) ... AD4170_FILTER_FS_REG(5)] =3D 2, + [AD4170_MISC_REG(6) ... AD4170_FILTER_FS_REG(6)] =3D 2, + [AD4170_MISC_REG(7) ... AD4170_FILTER_FS_REG(7)] =3D 2, + [AD4170_OFFSET_REG(0) ... AD4170_GAIN_REG(0)] =3D 3, + [AD4170_OFFSET_REG(1) ... AD4170_GAIN_REG(1)] =3D 3, + [AD4170_OFFSET_REG(2) ... AD4170_GAIN_REG(2)] =3D 3, + [AD4170_OFFSET_REG(3) ... AD4170_GAIN_REG(3)] =3D 3, + [AD4170_OFFSET_REG(4) ... AD4170_GAIN_REG(4)] =3D 3, + [AD4170_OFFSET_REG(5) ... AD4170_GAIN_REG(5)] =3D 3, + [AD4170_OFFSET_REG(6) ... AD4170_GAIN_REG(6)] =3D 3, + [AD4170_OFFSET_REG(7) ... AD4170_GAIN_REG(7)] =3D 3, +}; + +enum ad4170_ref_buf { + AD4170_REF_BUF_PRE, /* Pre-charge referrence buffer */ + AD4170_REF_BUF_FULL, /* Full referrence buffering */ + AD4170_REF_BUF_BYPASS, /* Bypass referrence buffering */ +}; + +/* maps adi,positive/negative-reference-buffer property values to enum */ +static const char * const ad4170_ref_buf_str[] =3D { + [AD4170_REF_BUF_PRE] =3D "precharge", + [AD4170_REF_BUF_FULL] =3D "full", + [AD4170_REF_BUF_BYPASS] =3D "disabled", +}; + +enum ad4170_ref_select { + AD4170_REF_REFIN1, + AD4170_REF_REFIN2, + AD4170_REF_REFOUT, + AD4170_REF_AVDD, +}; + +enum ad4170_regulator { + AD4170_AVDD_SUP, + AD4170_AVSS_SUP, + AD4170_IOVDD_SUP, + AD4170_REFIN1P_SUP, + AD4170_REFIN1N_SUP, + AD4170_REFIN2P_SUP, + AD4170_REFIN2N_SUP, + AD4170_MAX_SUP, +}; + +enum ad4170_int_pin_sel { + AD4170_INT_PIN_SDO, + AD4170_INT_PIN_DIG_AUX1, +}; + +static const char * const ad4170_int_pin_names[] =3D { + [AD4170_INT_PIN_SDO] =3D "sdo", + [AD4170_INT_PIN_DIG_AUX1] =3D "dig_aux1", +}; + +struct ad4170_chip_info { + const char *name; +}; + +static const struct ad4170_chip_info ad4170_chip_info =3D { + .name =3D "ad4170", +}; + +static const struct ad4170_chip_info ad4190_chip_info =3D { + .name =3D "ad4190", +}; + +static const struct ad4170_chip_info ad4195_chip_info =3D { + .name =3D "ad4195", +}; + +/* + * There are 8 of each MISC, AFE, FILTER, FILTER_FS, OFFSET, and GAIN + * configuration registers. That is, there are 8 miscellaneous registers, = MISC0 + * to MISC7. Each MISC register is associated with a setup; MISCN is assoc= iated + * with setup number N. The other 5 above mentioned types of registers have + * analogous structure. A setup is a set of those registers. For example, + * setup 1 comprises of MISC1, AFE1, FILTER1, FILTER_FS1, OFFSET1, and GAI= N1 + * registers. Also, there are 16 CHANNEL_SETUP registers (CHANNEL_SETUP0 to + * CHANNEL_SETUP15). Each channel setup is associated with one of the 8 po= ssible + * setups. Thus, AD4170 can support up to 16 channels but, since there are= only + * 8 available setups, channels must share settings if more than 8 channel= s are + * configured. + * + * If this struct is modified, ad4170_setup_eq() will probably need to be + * updated too. + */ +struct ad4170_setup { + u16 misc; + u16 afe; + u16 filter; + u16 filter_fs; + u32 offset; /* For calibration purposes */ + u32 gain; /* For calibration purposes */ +}; + +struct ad4170_setup_info { + struct ad4170_setup setup; + unsigned int enabled_channels; + unsigned int channels; +}; + +struct ad4170_chan_info { + unsigned int input_range_uv; + unsigned int setup_num; /* Index to access state setup_infos array */ + struct ad4170_setup setup; /* cached setup */ + int offset_tbl[10]; + u32 scale_tbl[10][2]; + bool initialized; + bool enabled; +}; + +struct ad4170_state { + struct mutex lock; /* Protect read-modify-write and multi write sequences= */ + int vrefs_uv[AD4170_MAX_SUP]; + u32 mclk_hz; + struct ad4170_setup_info setup_infos[AD4170_MAX_SETUPS]; + struct iio_chan_spec chans[AD4170_MAX_CHANNELS]; + struct ad4170_chan_info chan_infos[AD4170_MAX_CHANNELS]; + struct spi_device *spi; + struct regmap *regmap; + struct completion completion; + unsigned int pins_fn[AD4170_NUM_ANALOG_PINS]; + u32 int_pin_sel; + /* + * DMA (thus cache coherency maintenance) requires the transfer buffers + * to live in their own cache lines. + */ + u8 tx_buf[AD4170_SPI_MAX_XFER_LEN] __aligned(IIO_DMA_MINALIGN); + u8 rx_buf[4]; +}; + +static int ad4170_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap, reg, readval); + + return regmap_write(st->regmap, reg, writeval); +} + +static int ad4170_get_reg_size(struct ad4170_state *st, unsigned int reg, + unsigned int *size) +{ + if (reg >=3D ARRAY_SIZE(ad4170_reg_size)) + return -EINVAL; + + *size =3D ad4170_reg_size[reg]; + + return 0; +} + +static int ad4170_reg_write(void *context, unsigned int reg, unsigned int = val) +{ + struct ad4170_state *st =3D context; + unsigned int size; + int ret; + + ret =3D ad4170_get_reg_size(st, reg, &size); + if (ret) + return ret; + + put_unaligned_be16(reg, st->tx_buf); + switch (size) { + case 3: + put_unaligned_be24(val, &st->tx_buf[AD4170_SPI_INST_PHASE_LEN]); + break; + case 2: + put_unaligned_be16(val, &st->tx_buf[AD4170_SPI_INST_PHASE_LEN]); + break; + case 1: + st->tx_buf[AD4170_SPI_INST_PHASE_LEN] =3D val; + break; + default: + return -EINVAL; + } + + return spi_write(st->spi, st->tx_buf, AD4170_SPI_INST_PHASE_LEN + size); +} + +static int ad4170_reg_read(void *context, unsigned int reg, unsigned int *= val) +{ + struct ad4170_state *st =3D context; + struct spi_transfer t[] =3D { + { + .tx_buf =3D st->tx_buf, + .len =3D AD4170_SPI_INST_PHASE_LEN, + }, + { + .rx_buf =3D st->rx_buf, + }, + }; + unsigned int size; + int ret; + + ret =3D ad4170_get_reg_size(st, reg, &size); + if (ret) + return ret; + + put_unaligned_be16(AD4170_REG_READ_MASK | reg, st->tx_buf); + t[1].len =3D size; + + ret =3D spi_sync_transfer(st->spi, t, ARRAY_SIZE(t)); + if (ret) + return ret; + + switch (size) { + case 3: + *val =3D get_unaligned_be24(st->rx_buf); + return 0; + case 2: + *val =3D get_unaligned_be16(st->rx_buf); + return 0; + case 1: + *val =3D st->rx_buf[0]; + return 0; + default: + return -EINVAL; + } +} + +static const struct regmap_config ad4170_regmap_config =3D { + .reg_read =3D ad4170_reg_read, + .reg_write =3D ad4170_reg_write, +}; + +static bool ad4170_setup_eq(struct ad4170_setup *a, struct ad4170_setup *b) +{ + if (a->misc !=3D b->misc || + a->afe !=3D b->afe || + a->filter !=3D b->filter || + a->filter_fs !=3D b->filter_fs || + a->offset !=3D b->offset || + a->gain !=3D b->gain) + return false; + + return true; +} + +static int ad4170_find_setup(struct ad4170_state *st, + struct ad4170_setup *target_setup, + unsigned int *setup_num, bool *overwrite) +{ + unsigned int i; + + *setup_num =3D AD4170_INVALID_SETUP; + *overwrite =3D false; + + for (i =3D 0; i < AD4170_MAX_SETUPS; i++) { + struct ad4170_setup_info *setup_info =3D &st->setup_infos[i]; + + /* Immediately accept a matching setup. */ + if (ad4170_setup_eq(target_setup, &setup_info->setup)) { + *setup_num =3D i; + return 0; + } + + /* Ignore all setups which are used by enabled channels. */ + if (setup_info->enabled_channels) + continue; + + /* Find the least used slot. */ + if (*setup_num =3D=3D AD4170_INVALID_SETUP || + setup_info->channels < st->setup_infos[*setup_num].channels) + *setup_num =3D i; + } + + if (*setup_num =3D=3D AD4170_INVALID_SETUP) + return -EINVAL; + + *overwrite =3D true; + return 0; +} + +static void ad4170_unlink_channel(struct ad4170_state *st, unsigned int ch= annel) +{ + struct ad4170_chan_info *chan_info =3D &st->chan_infos[channel]; + struct ad4170_setup_info *setup_info =3D &st->setup_infos[chan_info->setu= p_num]; + + chan_info->setup_num =3D AD4170_INVALID_SETUP; + setup_info->channels--; +} + +static int ad4170_unlink_setup(struct ad4170_state *st, unsigned int setup= _num) +{ + unsigned int i; + + for (i =3D 0; i < AD4170_MAX_CHANNELS; i++) { + struct ad4170_chan_info *chan_info =3D &st->chan_infos[i]; + + if (!chan_info->initialized || chan_info->setup_num !=3D setup_num) + continue; + + ad4170_unlink_channel(st, i); + } + return 0; +} + +static int ad4170_link_channel_setup(struct ad4170_state *st, + unsigned int chan_addr, + unsigned int setup_num) +{ + struct ad4170_setup_info *setup_info =3D &st->setup_infos[setup_num]; + struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan_addr]; + int ret; + + ret =3D regmap_update_bits(st->regmap, AD4170_CHAN_SETUP_REG(chan_addr), + AD4170_CHAN_SETUP_SETUP_MSK, + FIELD_PREP(AD4170_CHAN_SETUP_SETUP_MSK, setup_num)); + if (ret) + return ret; + + chan_info->setup_num =3D setup_num; + setup_info->channels++; + return 0; +} + +static int ad4170_write_setup(struct ad4170_state *st, unsigned int setup_= num, + struct ad4170_setup *setup) +{ + int ret; + + /* + * It is recommended to place the ADC in standby mode or idle mode to + * write to OFFSET and GAIN registers. + */ + ret =3D regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_MODE_MSK, + FIELD_PREP(AD4170_ADC_CTRL_MODE_MSK, + AD4170_ADC_CTRL_MODE_IDLE)); + if (ret) + return ret; + + ret =3D regmap_write(st->regmap, AD4170_MISC_REG(setup_num), setup->misc); + if (ret) + return ret; + + ret =3D regmap_write(st->regmap, AD4170_AFE_REG(setup_num), setup->afe); + if (ret) + return ret; + + ret =3D regmap_write(st->regmap, AD4170_FILTER_REG(setup_num), + setup->filter); + if (ret) + return ret; + + ret =3D regmap_write(st->regmap, AD4170_FILTER_FS_REG(setup_num), + setup->filter_fs); + if (ret) + return ret; + + ret =3D regmap_write(st->regmap, AD4170_OFFSET_REG(setup_num), + setup->offset); + if (ret) + return ret; + + ret =3D regmap_write(st->regmap, AD4170_GAIN_REG(setup_num), setup->gain); + if (ret) + return ret; + + memcpy(&st->setup_infos[setup_num].setup, setup, sizeof(*setup)); + return 0; +} + +static int ad4170_write_channel_setup(struct ad4170_state *st, + unsigned int chan_addr, bool on_enable) +{ + struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan_addr]; + bool overwrite; + int setup_num; + int ret; + + /* + * Similar to AD4130 driver, the following cases need to be handled. + * + * 1. Enabled and linked channel with setup changes: + * - Find a setup. If not possible, return error. + * - Unlink channel from current setup. + * - If the setup found has only disabled channels linked to it, + * unlink all channels, and write the new setup to it. + * - Link channel to new setup. + * + * 2. Soon to be enabled and unlinked channel: + * - Find a setup. If not possible, return error. + * - If the setup found has only disabled channels linked to it, + * unlink all channels, and write the new setup to it. + * - Link channel to the setup. + * + * 3. Disabled and linked channel with setup changes: + * - Unlink channel from current setup. + * + * 4. Soon to be enabled and linked channel: + * 5. Disabled and unlinked channel with setup changes: + * - Do nothing. + */ + + /* Cases 3, 4, and 5 */ + if (chan_info->setup_num !=3D AD4170_INVALID_SETUP) { + /* Case 4 */ + if (on_enable) + return 0; + + /* Case 3 */ + if (!chan_info->enabled) { + ad4170_unlink_channel(st, chan_addr); + return 0; + } + } else if (!on_enable && !chan_info->enabled) { + /* Case 5 */ + return 0; + } + + /* Cases 1 & 2 */ + ret =3D ad4170_find_setup(st, &chan_info->setup, &setup_num, &overwrite); + if (ret) + return ret; + + if (chan_info->setup_num !=3D AD4170_INVALID_SETUP) + /* Case 1 */ + ad4170_unlink_channel(st, chan_addr); + + if (overwrite) { + ret =3D ad4170_unlink_setup(st, setup_num); + if (ret) + return ret; + + ret =3D ad4170_write_setup(st, setup_num, &chan_info->setup); + if (ret) + return ret; + } + + return ad4170_link_channel_setup(st, chan_addr, setup_num); +} + +static int ad4170_set_channel_enable(struct ad4170_state *st, + unsigned int chan_addr, bool status) +{ + struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan_addr]; + struct ad4170_setup_info *setup_info; + int ret; + + if (chan_info->enabled =3D=3D status) + return 0; + + if (status) { + ret =3D ad4170_write_channel_setup(st, chan_addr, true); + if (ret) + return ret; + } + + setup_info =3D &st->setup_infos[chan_info->setup_num]; + + ret =3D regmap_update_bits(st->regmap, AD4170_CHAN_EN_REG, + AD4170_CHAN_EN(chan_addr), + status ? AD4170_CHAN_EN(chan_addr) : 0); + if (ret) + return ret; + + setup_info->enabled_channels +=3D status ? 1 : -1; + chan_info->enabled =3D status; + return 0; +} + +static const struct iio_chan_spec ad4170_channel_template =3D { + .type =3D IIO_VOLTAGE, + .indexed =3D 1, + .differential =3D 1, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .info_mask_separate_available =3D BIT(IIO_CHAN_INFO_SCALE), + .scan_type =3D { + .realbits =3D 24, + .storagebits =3D 32, + .endianness =3D IIO_BE, + }, +}; + +/* + * Receives the number of a multiplexed AD4170 input (ain_n), and stores t= he + * voltage (in =C2=B5V) of the specified input into ain_voltage. If the in= put number + * is a ordinary analog input (AIN0 to AIN8), stores zero into ain_voltage. + * If a voltage regulator required by a special input is unavailable, retu= rn + * error code. Return 0 on success. + */ +static int ad4170_get_ain_voltage_uv(struct ad4170_state *st, int ain_n, + int *ain_voltage) +{ + struct device *dev =3D &st->spi->dev; + int v_diff; + + *ain_voltage =3D 0; + if (ain_n <=3D AD4170_CHAN_MAP_TEMP_SENSOR) + return 0; + + switch (ain_n) { + case AD4170_CHAN_MAP_AVDD_AVSS_N: + v_diff =3D st->vrefs_uv[AD4170_AVDD_SUP] - st->vrefs_uv[AD4170_AVSS_SUP]; + *ain_voltage =3D v_diff / 5; + return 0; + case AD4170_CHAN_MAP_IOVDD_DGND_N: + *ain_voltage =3D st->vrefs_uv[AD4170_IOVDD_SUP] / 5; + return 0; + case AD4170_CHAN_MAP_AVSS: + *ain_voltage =3D st->vrefs_uv[AD4170_AVSS_SUP]; + return 0; + case AD4170_CHAN_MAP_DGND: + *ain_voltage =3D 0; + return 0; + case AD4170_CHAN_MAP_REFIN1_P: + if (st->vrefs_uv[AD4170_REFIN1P_SUP] =3D=3D -ENODEV) + return dev_err_probe(dev, -ENODEV, + "input set to REFIN+ but ref not provided\n"); + + *ain_voltage =3D st->vrefs_uv[AD4170_REFIN1P_SUP]; + return 0; + case AD4170_CHAN_MAP_REFIN1_N: + if (st->vrefs_uv[AD4170_REFIN1N_SUP] =3D=3D -ENODEV) + return dev_err_probe(dev, -ENODEV, + "input set to REFIN- but ref not provided\n"); + + *ain_voltage =3D st->vrefs_uv[AD4170_REFIN1N_SUP]; + return 0; + case AD4170_CHAN_MAP_REFIN2_P: + if (st->vrefs_uv[AD4170_REFIN2P_SUP] =3D=3D -ENODEV) + return dev_err_probe(dev, -ENODEV, + "input set to REFIN2+ but ref not provided\n"); + + *ain_voltage =3D st->vrefs_uv[AD4170_REFIN2P_SUP]; + return 0; + case AD4170_CHAN_MAP_REFIN2_N: + if (st->vrefs_uv[AD4170_REFIN2N_SUP] =3D=3D -ENODEV) + return dev_err_probe(dev, -ENODEV, + "input set to REFIN2- but ref not provided\n"); + + *ain_voltage =3D st->vrefs_uv[AD4170_REFIN2N_SUP]; + return 0; + case AD4170_CHAN_MAP_REFOUT: + /* REFOUT is 2.5V relative to AVSS so take that into account */ + *ain_voltage =3D st->vrefs_uv[AD4170_AVSS_SUP] + AD4170_INT_REF_2_5V; + return 0; + default: + return -EINVAL; + } +} + +static int ad4170_validate_channel_input(struct ad4170_state *st, int pin,= bool com) +{ + /* Check common-mode input pin is mapped to a special input. */ + if (com && (pin < AD4170_CHAN_MAP_AVDD_AVSS_P || pin > AD4170_CHAN_MAP_RE= FOUT)) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Invalid common-mode input pin number. %d\n", + pin); + + /* Check differential input pin is mapped to a analog input pin. */ + if (!com && pin > AD4170_MAX_ANALOG_PINS) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Invalid analog input pin number. %d\n", + pin); + + return 0; +} + +/* + * Verifies whether the channel input configuration is valid by checking t= he + * input numbers. + * Returns 0 on valid channel input configuration. -EINVAL otherwise. + */ +static int ad4170_validate_channel(struct ad4170_state *st, + struct iio_chan_spec const *chan) +{ + int ret; + + ret =3D ad4170_validate_channel_input(st, chan->channel, false); + if (ret) + return ret; + + return ad4170_validate_channel_input(st, chan->channel2, + !chan->differential); +} + +/* + * Verifies whether the channel configuration is valid by checking the pro= vided + * input type, polarity, and voltage references result in a sane input ran= ge. + * Returns negative error code on failure. + */ +static int ad4170_get_input_range(struct ad4170_state *st, + struct iio_chan_spec const *chan, + unsigned int ch_reg, unsigned int ref_sel) +{ + bool bipolar =3D chan->scan_type.sign =3D=3D 's'; + struct device *dev =3D &st->spi->dev; + int refp, refn, ain_voltage, ret; + + switch (ref_sel) { + case AD4170_REF_REFIN1: + if (st->vrefs_uv[AD4170_REFIN1P_SUP] =3D=3D -ENODEV || + st->vrefs_uv[AD4170_REFIN1N_SUP] =3D=3D -ENODEV) + return dev_err_probe(dev, -ENODEV, + "REFIN=C2=B1 selected but not provided\n"); + + refp =3D st->vrefs_uv[AD4170_REFIN1P_SUP]; + refn =3D st->vrefs_uv[AD4170_REFIN1N_SUP]; + break; + case AD4170_REF_REFIN2: + if (st->vrefs_uv[AD4170_REFIN2P_SUP] =3D=3D -ENODEV || + st->vrefs_uv[AD4170_REFIN2N_SUP] =3D=3D -ENODEV) + return dev_err_probe(dev, -ENODEV, + "REFIN2=C2=B1 selected but not provided\n"); + + refp =3D st->vrefs_uv[AD4170_REFIN2P_SUP]; + refn =3D st->vrefs_uv[AD4170_REFIN2N_SUP]; + break; + case AD4170_REF_AVDD: + refp =3D st->vrefs_uv[AD4170_AVDD_SUP]; + refn =3D st->vrefs_uv[AD4170_AVSS_SUP]; + break; + case AD4170_REF_REFOUT: + /* REFOUT is 2.5 V relative to AVSS */ + refp =3D st->vrefs_uv[AD4170_AVSS_SUP] + AD4170_INT_REF_2_5V; + refn =3D st->vrefs_uv[AD4170_AVSS_SUP]; + break; + default: + return -EINVAL; + } + + /* + * Find out the analog input range from the channel type, polarity, and + * voltage reference selection. + * AD4170 channels are either differential or pseudo-differential. + * Diff input voltage range: =E2=88=92VREF/gain to +VREF/gain (datasheet = page 6) + * Pseudo-diff input voltage range: 0 to VREF/gain (datasheet page 6) + */ + if (chan->differential) { + if (!bipolar) + return dev_err_probe(dev, -EINVAL, + "Channel %u differential unipolar\n", + ch_reg); + + /* + * Differential bipolar channel. + * avss-supply is never above 0V. + * Assuming refin1n-supply not above 0V. + * Assuming refin2n-supply not above 0V. + */ + return refp + abs(refn); + } + /* + * Some configurations can lead to invalid setups. + * For example, if AVSS =3D -2.5V, REF_SELECT set to REFOUT (REFOUT/AVSS), + * and pseudo-diff channel configuration set, then the input range + * should go from 0V to +VREF (single-ended - datasheet pg 10), but + * REFOUT/AVSS range would be -2.5V to 0V. + * Check the positive reference is higher than 0V for pseudo-diff + * channels. + * Note that at this point in the code, refp can only be >=3D 0 since all + * error codes from reading the regulator voltage have been checked + * either at ad4170_regulator_setup() or above in this function. + */ + if (refp =3D=3D 0) + return dev_err_probe(dev, -EINVAL, + "REF+ =3D=3D GND for pseudo-diff chan %u\n", + ch_reg); + + if (bipolar) + return refp; + + /* + * Pseudo-differential unipolar channel. + * Input expected to swing from IN- to +VREF. + */ + ret =3D ad4170_get_ain_voltage_uv(st, chan->channel2, &ain_voltage); + if (ret) + return ret; + + if (refp - ain_voltage <=3D 0) + return dev_err_probe(dev, -EINVAL, + "Negative input >=3D REF+ for pseudo-diff chan %u\n", + ch_reg); + + return refp - ain_voltage; +} + +static int __ad4170_read_sample(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + unsigned long settling_time_ms; + int ret; + + reinit_completion(&st->completion); + ret =3D regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_MODE_MSK, + FIELD_PREP(AD4170_ADC_CTRL_MODE_MSK, + AD4170_ADC_CTRL_MODE_SINGLE)); + if (ret) + return ret; + + /* + * When a channel is manually selected by the user, the ADC needs an + * extra time to provide the first stable conversion. The ADC settling + * time depends on the filter type, filter frequency, and ADC clock + * frequency (see datasheet page 53). The maximum settling time among + * all filter configurations is 6291164 / fCLK. Use that formula to wait + * for sufficient time whatever the filter configuration may be. + */ + settling_time_ms =3D DIV_ROUND_UP(6291164 * MILLI, st->mclk_hz); + ret =3D wait_for_completion_timeout(&st->completion, + msecs_to_jiffies(settling_time_ms)); + if (!ret) + dev_dbg(&st->spi->dev, + "No Data Ready signal. Reading after delay.\n"); + + ret =3D regmap_read(st->regmap, AD4170_DATA_24B_REG, val); + if (ret) + return ret; + + if (chan->scan_type.sign =3D=3D 's') + *val =3D sign_extend32(*val, chan->scan_type.realbits - 1); + + return 0; +} + +static int ad4170_read_sample(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + struct device *dev =3D &st->spi->dev; + int ret, ret2; + + /* + * The ADC sequences through all enabled channels. That can lead to + * incorrect channel being sampled if a previous read would have left a + * different channel enabled. Thus, always enable and disable the + * channel on single-shot read. + */ + ret =3D ad4170_set_channel_enable(st, chan->address, true); + if (ret) + return ret; + + ret =3D __ad4170_read_sample(indio_dev, chan, val); + if (ret) { + dev_err(dev, "failed to read sample: %d\n", ret); + + ret2 =3D ad4170_set_channel_enable(st, chan->address, false); + if (ret2) + dev_err(dev, "failed to disable channel: %d\n", ret2); + + return ret; + } + + ret =3D ad4170_set_channel_enable(st, chan->address, false); + if (ret) + return ret; + + return IIO_VAL_INT; +} + +static int ad4170_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan->address]; + struct ad4170_setup *setup =3D &chan_info->setup; + unsigned int pga; + int ret; + + guard(mutex)(&st->lock); + switch (info) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret =3D ad4170_read_sample(indio_dev, chan, val); + iio_device_release_direct(indio_dev); + return ret; + case IIO_CHAN_INFO_SCALE: + pga =3D FIELD_GET(AD4170_AFE_PGA_GAIN_MSK, setup->afe); + *val =3D chan_info->scale_tbl[pga][0]; + *val2 =3D chan_info->scale_tbl[pga][1]; + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OFFSET: + pga =3D FIELD_GET(AD4170_AFE_PGA_GAIN_MSK, setup->afe); + *val =3D chan_info->offset_tbl[pga]; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad4170_fill_scale_tbl(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan->address]; + struct device *dev =3D &st->spi->dev; + int bipolar =3D chan->scan_type.sign =3D=3D 's' ? 1 : 0; + int precision_bits =3D chan->scan_type.realbits; + int pga, ainm_voltage, ret; + unsigned long long offset; + + ainm_voltage =3D 0; + ret =3D ad4170_get_ain_voltage_uv(st, chan->channel2, &ainm_voltage); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to fill scale table\n"); + + for (pga =3D 0; pga < AD4170_NUM_PGA_OPTIONS; pga++) { + u64 nv; + unsigned int lshift, rshift; + + /* + * The PGA options are numbered from 0 to 9, with option 0 being + * a gain of 2^0 (no actual gain), and 7 meaning a gain of 2^7. + * Option 8, though, sets a gain of 0.5, so the input signal can + * be attenuated by 2 rather than amplified. Option 9, allows + * the signal to bypass the PGA circuitry (no gain). + * + * The scale factor to get ADC output codes to values in mV + * units is given by: + * _scale =3D (input_range / gain) / 2^precision + * AD4170 gain is a power of 2 so the above can be written as + * _scale =3D input_range / 2^(precision + gain) + * Keep the input range in =C2=B5V to avoid truncating the less + * significan bits when right shifting it so to preserve scale + * precision. + */ + nv =3D (u64)chan_info->input_range_uv * NANO; + lshift =3D !!(pga & BIT(3)); /* handle options 8 and 9 */ + rshift =3D precision_bits - bipolar + (pga & GENMASK(2, 0)) - lshift; + chan_info->scale_tbl[pga][0] =3D 0; + chan_info->scale_tbl[pga][1] =3D div_u64(nv >> rshift, MILLI); + + /* + * If the negative input is not at GND, the conversion result + * (which is relative to IN-) will be offset by the level at IN-. + * Use the scale factor the other way around to go from a known + * voltage to the corresponding ADC output code. + * With that, we are able to get to what would be the output + * code for the voltage at the negative input. + * If the negative input is not fixed, there is no offset. + */ + offset =3D ((unsigned long long)abs(ainm_voltage)) * MICRO; + offset =3D DIV_ROUND_CLOSEST_ULL(offset, chan_info->scale_tbl[pga][1]); + + /* + * After divided by the scale, offset will always fit into 31 + * bits. For _raw + _offset to be relative to GND, the value + * provided as _offset is of opposite sign than the real offset. + */ + if (ainm_voltage > 0) + chan_info->offset_tbl[pga] =3D -(int)(offset); + else + chan_info->offset_tbl[pga] =3D (int)(offset); + } + return 0; +} + +static int ad4170_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan->address]; + + switch (info) { + case IIO_CHAN_INFO_SCALE: + *vals =3D (int *)chan_info->scale_tbl; + *length =3D ARRAY_SIZE(chan_info->scale_tbl) * 2; + *type =3D IIO_VAL_INT_PLUS_NANO; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int ad4170_set_pga(struct ad4170_state *st, + struct iio_chan_spec const *chan, int val, int val2) +{ + struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan->address]; + struct ad4170_setup *setup =3D &chan_info->setup; + unsigned int pga; + + for (pga =3D 0; pga < AD4170_NUM_PGA_OPTIONS; pga++) { + if (val =3D=3D chan_info->scale_tbl[pga][0] && + val2 =3D=3D chan_info->scale_tbl[pga][1]) + break; + } + + if (pga =3D=3D AD4170_NUM_PGA_OPTIONS) + return -EINVAL; + + guard(mutex)(&st->lock); + setup->afe &=3D ~AD4170_AFE_PGA_GAIN_MSK; + setup->afe |=3D FIELD_PREP(AD4170_AFE_PGA_GAIN_MSK, pga); + + return ad4170_write_channel_setup(st, chan->address, false); +} + +static int __ad4170_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long info) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SCALE: + return ad4170_set_pga(st, chan, val, val2); + default: + return -EINVAL; + } +} + +static int ad4170_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long info) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret =3D __ad4170_write_raw(indio_dev, chan, val, val2, info); + iio_device_release_direct(indio_dev); + return ret; +} + +static int ad4170_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } +} + +static const struct iio_info ad4170_info =3D { + .read_raw =3D ad4170_read_raw, + .read_avail =3D ad4170_read_avail, + .write_raw =3D ad4170_write_raw, + .write_raw_get_fmt =3D ad4170_write_raw_get_fmt, + .debugfs_reg_access =3D ad4170_debugfs_reg_access, +}; + +static int ad4170_soft_reset(struct ad4170_state *st) +{ + int ret; + + ret =3D regmap_write(st->regmap, AD4170_CONFIG_A_REG, + AD4170_SW_RESET_MSK); + if (ret) + return ret; + + /* AD4170-4 requires 1 ms between reset and any register access. */ + fsleep(1 * USEC_PER_MSEC); + + return 0; +} + +static int ad4170_parse_reference(struct ad4170_state *st, + struct fwnode_handle *child, + struct ad4170_setup *setup) +{ + struct device *dev =3D &st->spi->dev; + const char *propname; + u32 aux; + int ret; + + /* Optional positive reference buffering */ + propname =3D "adi,positive-reference-buffer"; + ret =3D device_property_match_property_string(dev, propname, + ad4170_ref_buf_str, + ARRAY_SIZE(ad4170_ref_buf_str)); + + /* Default to full precharge buffer enabled. */ + setup->afe |=3D FIELD_PREP(AD4170_AFE_REF_BUF_P_MSK, + ret >=3D 0 ? ret : AD4170_REF_BUF_FULL); + + /* Optional negative reference buffering */ + propname =3D "adi,negative-reference-buffer"; + ret =3D device_property_match_property_string(dev, propname, + ad4170_ref_buf_str, + ARRAY_SIZE(ad4170_ref_buf_str)); + + /* Default to full precharge buffer enabled. */ + setup->afe |=3D FIELD_PREP(AD4170_AFE_REF_BUF_M_MSK, + ret >=3D 0 ? ret : AD4170_REF_BUF_FULL); + + /* Optional voltage reference selection */ + propname =3D "adi,reference-select"; + aux =3D AD4170_REF_REFOUT; /* Default reference selection. */ + fwnode_property_read_u32(child, propname, &aux); + if (aux > AD4170_REF_AVDD) + return dev_err_probe(dev, -EINVAL, "Invalid %s: %u\n", + propname, aux); + + setup->afe |=3D FIELD_PREP(AD4170_AFE_REF_SELECT_MSK, aux); + + return 0; +} + +static int ad4170_parse_adc_channel_type(struct device *dev, + struct fwnode_handle *child, + struct iio_chan_spec *chan) +{ + const char *propname, *propname2; + int ret, ret2; + u32 pins[2]; + + /* Parse pseudo-differential channel configuration */ + propname =3D "single-channel"; + propname2 =3D "common-mode-channel"; + ret =3D fwnode_property_read_u32(child, propname, &pins[0]); + ret2 =3D fwnode_property_read_u32(child, propname2, &pins[1]); + if (!ret && ret2) + return dev_err_probe(dev, ret, + "When %s is defined, %s must be defined too\n", + propname, propname2); + + if (!ret && !ret2) { + chan->differential =3D false; + chan->channel =3D pins[0]; + chan->channel2 =3D pins[1]; + return 0; + } + /* Failed to parse pseudo-diff chan props so try diff chan */ + + /* Parse differential channel configuration */ + propname2 =3D "diff-channels"; + ret =3D fwnode_property_read_u32_array(child, propname2, pins, + ARRAY_SIZE(pins)); + if (!ret) { + chan->differential =3D true; + chan->channel =3D pins[0]; + chan->channel2 =3D pins[1]; + return 0; + } + return dev_err_probe(dev, ret, "Channel must define one of %s or %s.\n", + propname, propname2); +} + +static int ad4170_parse_channel_node(struct iio_dev *indio_dev, + struct fwnode_handle *child, + unsigned int chan_num) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + struct device *dev =3D &st->spi->dev; + struct ad4170_chan_info *chan_info; + struct ad4170_setup *setup; + struct iio_chan_spec *chan; + unsigned int ref_select; + unsigned int ch_reg; + bool bipolar; + int ret; + + ret =3D fwnode_property_read_u32(child, "reg", &ch_reg); + if (ret) + return dev_err_probe(dev, ret, "Failed to read channel reg\n"); + + if (ch_reg >=3D AD4170_MAX_CHANNELS) + return dev_err_probe(dev, -EINVAL, + "Channel idx greater than no of channels\n"); + + chan =3D &st->chans[chan_num]; + *chan =3D ad4170_channel_template; + + chan->address =3D ch_reg; + chan->scan_index =3D ch_reg; + chan_info =3D &st->chan_infos[chan->address]; + + chan_info->setup_num =3D AD4170_INVALID_SETUP; + chan_info->initialized =3D true; + + setup =3D &chan_info->setup; + ret =3D ad4170_parse_reference(st, child, setup); + if (ret) + return ret; + + ret =3D ad4170_parse_adc_channel_type(dev, child, chan); + if (ret) + return ret; + + bipolar =3D fwnode_property_read_bool(child, "bipolar"); + setup->afe |=3D FIELD_PREP(AD4170_AFE_BIPOLAR_MSK, bipolar); + if (bipolar) + chan->scan_type.sign =3D 's'; + else + chan->scan_type.sign =3D 'u'; + + ret =3D ad4170_validate_channel(st, chan); + if (ret) + return ret; + + ref_select =3D FIELD_GET(AD4170_AFE_REF_SELECT_MSK, setup->afe); + ret =3D ad4170_get_input_range(st, chan, ch_reg, ref_select); + if (ret < 0) + return dev_err_probe(dev, ret, "Invalid input config\n"); + + chan_info->input_range_uv =3D ret; + return 0; +} + +static int ad4170_parse_channels(struct iio_dev *indio_dev) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + struct device *dev =3D &st->spi->dev; + unsigned int num_channels; + unsigned int chan_num; + int ret; + + num_channels =3D device_get_child_node_count(dev); + + if (num_channels > AD4170_MAX_CHANNELS) + return dev_err_probe(dev, -EINVAL, "Too many channels\n"); + + chan_num =3D 0; + device_for_each_child_node_scoped(dev, child) { + ret =3D ad4170_parse_channel_node(indio_dev, child, chan_num++); + if (ret) + return ret; + } + + indio_dev->num_channels =3D num_channels; + indio_dev->channels =3D st->chans; + + return 0; +} + +static int ad4170_parse_firmware(struct iio_dev *indio_dev) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + struct device *dev =3D &st->spi->dev; + int reg_data, ret; + + st->mclk_hz =3D AD4170_INT_CLOCK_16MHZ; + + /* On power on, device defaults to using SDO pin for data ready signal */ + st->int_pin_sel =3D AD4170_INT_PIN_SDO; + ret =3D device_property_match_property_string(dev, "interrupt-names", + ad4170_int_pin_names, + ARRAY_SIZE(ad4170_int_pin_names)); + if (ret >=3D 0) + st->int_pin_sel =3D ret; + + reg_data =3D FIELD_PREP(AD4170_PIN_MUXING_DIG_AUX1_CTRL_MSK, + st->int_pin_sel =3D=3D AD4170_INT_PIN_DIG_AUX1 ? + AD4170_PIN_MUXING_DIG_AUX1_RDY : + AD4170_PIN_MUXING_DIG_AUX1_DISABLED); + + ret =3D regmap_update_bits(st->regmap, AD4170_PIN_MUXING_REG, + AD4170_PIN_MUXING_DIG_AUX1_CTRL_MSK, reg_data); + if (ret) + return ret; + + return ad4170_parse_channels(indio_dev); +} + +static int ad4170_initial_config(struct iio_dev *indio_dev) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + struct device *dev =3D &st->spi->dev; + unsigned int i; + int ret; + + ret =3D regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_MODE_MSK, + FIELD_PREP(AD4170_ADC_CTRL_MODE_MSK, + AD4170_ADC_CTRL_MODE_IDLE)); + if (ret) + return dev_err_probe(dev, ret, + "Failed to set ADC mode to idle\n"); + + for (i =3D 0; i < indio_dev->num_channels; i++) { + struct ad4170_chan_info *chan_info; + struct iio_chan_spec const *chan; + struct ad4170_setup *setup; + unsigned int val; + + chan =3D &indio_dev->channels[i]; + chan_info =3D &st->chan_infos[chan->address]; + + setup =3D &chan_info->setup; + setup->gain =3D AD4170_GAIN_REG_DEFAULT; + ret =3D ad4170_write_channel_setup(st, chan->address, false); + if (ret) + return dev_err_probe(dev, ret, + "Failed to write channel setup\n"); + + val =3D FIELD_PREP(AD4170_CHAN_MAP_AINP_MSK, chan->channel) | + FIELD_PREP(AD4170_CHAN_MAP_AINM_MSK, chan->channel2); + + ret =3D regmap_write(st->regmap, AD4170_CHAN_MAP_REG(i), val); + if (ret) + return dev_err_probe(dev, ret, + "Failed to write CHAN_MAP_REG\n"); + + ret =3D ad4170_fill_scale_tbl(indio_dev, chan); + if (ret) + return dev_err_probe(dev, ret, + "Failed to fill scale tbl\n"); + } + + /* Disable all channels to avoid reading from unexpected channel */ + ret =3D regmap_write(st->regmap, AD4170_CHAN_EN_REG, 0); + if (ret) + return dev_err_probe(dev, ret, + "Failed to disable channels\n"); + + /* + * Configure channels to share the same data output register, i.e. data + * can be read from the same register address regardless of channel + * number. + */ + return regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_MULTI_DATA_REG_SEL_MSK, + AD4170_ADC_CTRL_MULTI_DATA_REG_SEL_MSK); +} + +static irqreturn_t ad4170_irq_handler(int irq, void *dev_id) +{ + struct iio_dev *indio_dev =3D dev_id; + struct ad4170_state *st =3D iio_priv(indio_dev); + + complete(&st->completion); + + return IRQ_HANDLED; +}; + +static int ad4170_regulator_setup(struct ad4170_state *st) +{ + struct device *dev =3D &st->spi->dev; + int ret; + + /* Required regulators */ + ret =3D devm_regulator_get_enable_read_voltage(dev, "avdd"); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get AVDD voltage.\n"); + + st->vrefs_uv[AD4170_AVDD_SUP] =3D ret; + + ret =3D devm_regulator_get_enable_read_voltage(dev, "iovdd"); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get IOVDD voltage.\n"); + + st->vrefs_uv[AD4170_IOVDD_SUP] =3D ret; + + /* Optional regulators */ + ret =3D devm_regulator_get_enable_read_voltage(dev, "avss"); + if (ret < 0 && ret !=3D -ENODEV) + return dev_err_probe(dev, ret, "Failed to get AVSS voltage.\n"); + + /* + * Assume AVSS at GND (0V) if not provided. + * REVISIT: AVSS is never above system ground level (i.e. AVSS is either + * GND or a negative voltage). But we currently don't have support for + * reading negative voltages with the regulator framework. So, the + * current AD4170 support reads a positive value from the regulator, + * then inverts sign to make that negative. + */ + st->vrefs_uv[AD4170_AVSS_SUP] =3D ret =3D=3D -ENODEV ? 0 : -ret; + + ret =3D devm_regulator_get_enable_read_voltage(dev, "refin1p"); + if (ret < 0 && ret !=3D -ENODEV) + return dev_err_probe(dev, ret, "Failed to get REFIN+ voltage.\n"); + + st->vrefs_uv[AD4170_REFIN1P_SUP] =3D ret; + + ret =3D devm_regulator_get_enable_read_voltage(dev, "refin1n"); + if (ret < 0 && ret !=3D -ENODEV) + return dev_err_probe(dev, ret, "Failed to get REFIN- voltage.\n"); + + /* + * Negative supplies are assumed to provide negative voltage. + * REVISIT when support for negative regulator voltage read be available + * in the regulator framework. + */ + st->vrefs_uv[AD4170_REFIN1N_SUP] =3D ret =3D=3D -ENODEV ? -ENODEV : -ret; + + ret =3D devm_regulator_get_enable_read_voltage(dev, "refin2p"); + if (ret < 0 && ret !=3D -ENODEV) + return dev_err_probe(dev, ret, "Failed to get REFIN2+ voltage.\n"); + + st->vrefs_uv[AD4170_REFIN2P_SUP] =3D ret; + + ret =3D devm_regulator_get_enable_read_voltage(dev, "refin2n"); + if (ret < 0 && ret !=3D -ENODEV) + return dev_err_probe(dev, ret, "Failed to get REFIN2- voltage.\n"); + + /* + * Negative supplies are assumed to provide negative voltage. + * REVISIT when support for negative regulator voltage read be available + * in the regulator framework. + */ + st->vrefs_uv[AD4170_REFIN2N_SUP] =3D ret =3D=3D -ENODEV ? -ENODEV : -ret; + + return 0; +} + +static int ad4170_probe(struct spi_device *spi) +{ + const struct ad4170_chip_info *chip; + struct device *dev =3D &spi->dev; + struct iio_dev *indio_dev; + struct ad4170_state *st; + 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; + + ret =3D devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + chip =3D spi_get_device_match_data(spi); + if (!chip) + return -EINVAL; + + indio_dev->name =3D chip->name; + indio_dev->info =3D &ad4170_info; + + st->regmap =3D devm_regmap_init(dev, NULL, st, &ad4170_regmap_config); + if (IS_ERR(st->regmap)) + return dev_err_probe(dev, PTR_ERR(st->regmap), + "Failed to initialize regmap\n"); + + ret =3D ad4170_regulator_setup(st); + if (ret) + return ret; + + ret =3D ad4170_soft_reset(st); + if (ret) + return ret; + + ret =3D ad4170_parse_firmware(indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to parse firmware\n"); + + ret =3D ad4170_initial_config(indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to setup device\n"); + + init_completion(&st->completion); + + if (spi->irq) { + ret =3D devm_request_irq(dev, spi->irq, &ad4170_irq_handler, + IRQF_ONESHOT, indio_dev->name, indio_dev); + if (ret) + return ret; + } + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id ad4170_id_table[] =3D { + { "ad4170", (kernel_ulong_t)&ad4170_chip_info }, + { "ad4190", (kernel_ulong_t)&ad4190_chip_info }, + { "ad4195", (kernel_ulong_t)&ad4195_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad4170_id_table); + +static const struct of_device_id ad4170_of_match[] =3D { + { .compatible =3D "adi,ad4170", .data =3D &ad4170_chip_info }, + { .compatible =3D "adi,ad4190", .data =3D &ad4190_chip_info }, + { .compatible =3D "adi,ad4195", .data =3D &ad4195_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad4170_of_match); + +static struct spi_driver ad4170_driver =3D { + .driver =3D { + .name =3D "ad4170", + .of_match_table =3D ad4170_of_match, + }, + .probe =3D ad4170_probe, + .id_table =3D ad4170_id_table, +}; +module_spi_driver(ad4170_driver); + +MODULE_AUTHOR("Ana-Maria Cusco "); +MODULE_AUTHOR("Marcelo Schmitt "); +MODULE_DESCRIPTION("Analog Devices AD4170 SPI driver"); +MODULE_LICENSE("GPL"); --=20 2.47.2 From nobody Thu Oct 9 08:43:03 2025 Received: from mx0a-00128a01.pphosted.com (mx0a-00128a01.pphosted.com [148.163.135.77]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 62B802F198E; Wed, 18 Jun 2025 17:36:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.163.135.77 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268204; cv=none; b=hJuuteFkAdniJKWFvakt56HZKTHjqH80Dk+r+WbdgYCbe4/34wT0TQ9df5UYacitee0ErR9xgmHYhwirfy8YW5jLMKfMnNPI5iQhHAfnkcEiNjsLtEZZLE/1PHQ93eO0nygc2Um+fNOTIrdyYxwJ8xsj5a982y/ZQIG/BnBVA7E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268204; c=relaxed/simple; bh=XHZknbguom8fkunil8PybHRQ+S6GekFYDx/PRHD+ISw=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=EkfoW/YybFztqisL4Vqgn+oKp/c5+JLc4DIXRh9GNz0csiMXB0jhh4TTxKfB1OHuCXvKpiCJiHWQK6Wp5XO+bYW9IuPDZa8HmIeQa6qZMQ2yw1dD6sBO/aLm5cn6awmvZNPF1LwqdbmgI/JYv8MgUk7CBpcz2NdIwO6jLG+v/Qs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com; spf=pass smtp.mailfrom=analog.com; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b=C7JZFRxw; arc=none smtp.client-ip=148.163.135.77 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=analog.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b="C7JZFRxw" Received: from pps.filterd (m0167088.ppops.net [127.0.0.1]) by mx0a-00128a01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 55IGv7Lg032726; Wed, 18 Jun 2025 13:36:25 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=analog.com; h=cc :content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=DKIM; bh=l1pBi DClr02yKY17/Yq7AB4mIpKfmz6p8rCSOFHt8tM=; b=C7JZFRxwwjvLXcCQGtbXK wohQtukJsGwHXx+WVjFJb8PlsA5BJtWgXpFRJOul0gfPsuy0oNtkzHNwQm7w9RtE hKNCxZSIbc9ePWaYeOeMdhEHA+Rbj1b7lJNAAA+oeLkLZCcZSoUkgAn5tOOogSW0 817HeVlSqkKcwwcrD0CSgDg+wcTQIfoQhcZZ9LX/CSlXbis54DePYWnv/xmQXsIu HP8gmogah6H6FoT1MgD1B3wXOgynwQgRefXfak486SPc8MMDZZVD6jHqto+dpIrx iBIQOjr0HOzPEuDy4+WH9UDi91e5azXKX1lR2G7gd8mbYVAjx+xni99h2mFvOomk A== Received: from nwd2mta4.analog.com ([137.71.173.58]) by mx0a-00128a01.pphosted.com (PPS) with ESMTPS id 47bfshdd50-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 18 Jun 2025 13:36:25 -0400 (EDT) Received: from ASHBMBX8.ad.analog.com (ASHBMBX8.ad.analog.com [10.64.17.5]) by nwd2mta4.analog.com (8.14.7/8.14.7) with ESMTP id 55IHaOg7015881 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Wed, 18 Jun 2025 13:36:24 -0400 Received: from ASHBMBX8.ad.analog.com (10.64.17.5) by ASHBMBX8.ad.analog.com (10.64.17.5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 18 Jun 2025 13:36:24 -0400 Received: from zeus.spd.analog.com (10.66.68.11) by ashbmbx8.ad.analog.com (10.64.17.5) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Wed, 18 Jun 2025 13:36:24 -0400 Received: from work.ad.analog.com (HYB-hERzalRezfV.ad.analog.com [10.65.205.9]) by zeus.spd.analog.com (8.15.1/8.15.1) with ESMTP id 55IHa5WJ007447; Wed, 18 Jun 2025 13:36:08 -0400 From: Marcelo Schmitt To: , , , CC: , , , , , , , , , , , , , Subject: [PATCH v6 03/12] iio: adc: ad4170: Add support for calibration gain Date: Wed, 18 Jun 2025 14:36:04 -0300 Message-ID: <49119fe62e7791caa2a8ad6eb2a3c7ecc175f113.1750258776.git.marcelo.schmitt@analog.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ADIRuleOP-NewSCL: Rule Triggered X-Proofpoint-ORIG-GUID: 59MakZl-BaqHock-EaLVQZjeGZmZFaB3 X-Authority-Analysis: v=2.4 cv=SKhCVPvH c=1 sm=1 tr=0 ts=6852f919 cx=c_pps a=3WNzaoukacrqR9RwcOSAdA==:117 a=3WNzaoukacrqR9RwcOSAdA==:17 a=6IFa9wvqVegA:10 a=gAnH3GRIAAAA:8 a=s1qB4CsIdprNQQnv0OQA:9 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjUwNjE4MDE0OSBTYWx0ZWRfX8LAht16QBu4e BQeAxOrKaYxzP96696HTrVpytzbGCEG53HJGpPKH6HWecy+WaHQZ1cy0PgkhtrnkA3i8JAzZERF o23KYzHpFv4Rol8t1VtKsfY6hcM8tVLRfjroBB59M/aTlBWSZBV5QPajVpTvWERjtBUbJOz+YBo DcBa6d4dxSutQGc3SPSeeiYDAdkfVTMAdlz7V2aX+KA0OYIiuDXmxG8JJIEMa1iCO2/kyTzUUEI eBKeBBRsm90HAoxq9PnSARObcqkRCD4y/jruoSe6O3aQX42fC4z2DFcBRwJ/n1iYbM9GciAd9UZ DJ/wB0O5yeZMueJtvDSKQkKgg5Yto+dlUbJ6JW0RLmjd2sTNZdXMJopaGlFedPlScxx7pK80jOU t8HwKTRK/veARIRuqo7BgSvtRQ8wjwZXp44ug62KqCFPDzB5R9UdsgVp0qi0xF+SAbvh50Gi X-Proofpoint-GUID: 59MakZl-BaqHock-EaLVQZjeGZmZFaB3 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1099,Hydra:6.0.736,FMLib:17.12.80.40 definitions=2025-06-18_05,2025-06-18_03,2025-03-28_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 impostorscore=0 bulkscore=0 adultscore=0 lowpriorityscore=0 spamscore=0 suspectscore=0 mlxscore=0 malwarescore=0 mlxlogscore=999 clxscore=1015 phishscore=0 priorityscore=1501 classifier=spam authscore=0 authtc=n/a authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.19.0-2505280000 definitions=main-2506180149 Content-Type: text/plain; charset="utf-8" Add support for ADC calibration gain configuration. Signed-off-by: Marcelo Schmitt --- No changes in v6. drivers/iio/adc/ad4170.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/iio/adc/ad4170.c b/drivers/iio/adc/ad4170.c index 58716ad6e7fc..7b85771ff0d1 100644 --- a/drivers/iio/adc/ad4170.c +++ b/drivers/iio/adc/ad4170.c @@ -628,6 +628,7 @@ static const struct iio_chan_spec ad4170_channel_templa= te =3D { .differential =3D 1, .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET), .info_mask_separate_available =3D BIT(IIO_CHAN_INFO_SCALE), .scan_type =3D { @@ -945,6 +946,9 @@ static int ad4170_read_raw(struct iio_dev *indio_dev, pga =3D FIELD_GET(AD4170_AFE_PGA_GAIN_MSK, setup->afe); *val =3D chan_info->offset_tbl[pga]; return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBSCALE: + *val =3D setup->gain; + return IIO_VAL_INT; default: return -EINVAL; } @@ -1059,6 +1063,18 @@ static int ad4170_set_pga(struct ad4170_state *st, return ad4170_write_channel_setup(st, chan->address, false); } =20 +static int ad4170_set_calib_gain(struct ad4170_state *st, + struct iio_chan_spec const *chan, int val) +{ + struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan->address]; + struct ad4170_setup *setup =3D &chan_info->setup; + + guard(mutex)(&st->lock); + setup->gain =3D val; + + return ad4170_write_channel_setup(st, chan->address, false); +} + static int __ad4170_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long info) @@ -1068,6 +1084,8 @@ static int __ad4170_write_raw(struct iio_dev *indio_d= ev, switch (info) { case IIO_CHAN_INFO_SCALE: return ad4170_set_pga(st, chan, val, val2); + case IIO_CHAN_INFO_CALIBSCALE: + return ad4170_set_calib_gain(st, chan, val); default: return -EINVAL; } @@ -1094,6 +1112,8 @@ static int ad4170_write_raw_get_fmt(struct iio_dev *i= ndio_dev, switch (info) { case IIO_CHAN_INFO_SCALE: return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_CALIBSCALE: + return IIO_VAL_INT; default: return -EINVAL; } --=20 2.47.2 From nobody Thu Oct 9 08:43:03 2025 Received: from mx0a-00128a01.pphosted.com (mx0a-00128a01.pphosted.com [148.163.135.77]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CE8311E8323; Wed, 18 Jun 2025 17:37:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.163.135.77 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268238; cv=none; b=M7rhYylIaCHwbUdVgiicaNW8bbyjjvebrOAcWGEnIXI1B7TQZ/uJdtvevpdfM3pnNHzk8JEBN68j8R4VDCYlNdKi8uWk8GadPsHmHWuyoxiksa18zwVYB1oK738bXeuD2gqRIORkvFpyCsO2W7xCNYc2mtnmy7KWAiidB3uhu6I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268238; c=relaxed/simple; bh=m6YubeSw5ekT8lpN0+uUwhJnEiSFE5vohhR5O5jeQQk=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=jFMJKKyftv20taEt56FGfXso3A5KQjx6FE8wvQ0XPzc13/lPDdbwWwBqR55YiCVnCF64LSxYzZqFagUyVRhnClQgPFrnIQH86gi1WxxaKw2EKUmiV9tYJhxBItj1uyn3sHklkw9IwA1PsNxWYYZoFCQzBAMXEZM+ODsVQuFQlHc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com; spf=pass smtp.mailfrom=analog.com; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b=egW8spP4; arc=none smtp.client-ip=148.163.135.77 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=analog.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b="egW8spP4" Received: from pps.filterd (m0167089.ppops.net [127.0.0.1]) by mx0a-00128a01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 55IG2Gbp027608; Wed, 18 Jun 2025 13:37:00 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=analog.com; h=cc :content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=DKIM; bh=OWGVn FcPIk0gEmfPaY1OLr914IuzfYU3xB4ziCOu5DI=; b=egW8spP4mtUFARyHFNcqq fXex8wJON/cggCmwxIZiIlB2QPd55U2+K9T0TBtgb72OG27QoD1R5Z87liytPwPg X4/woanmkXWBTazocWYaAIOx2ZJXms0biW+rM5JojUioQCxOWbMuSwrISQQYFsqa s93cwAs2O4g/J0HF3uyfkCpoBWO6wgRhAL+KikGAMN50F6lTpEGWSAqawwGA3I9C QVK68xoXbVCRQY85ZideaT/f/10s7YLkKCIs02AejiGDFeelu4YQ8vSw2YxcwhVW mdmoGGQ1FstaT0pIWR3AksS+flj9C1xamPz/oTE47eQmH1lAz6qAVoGt55vfrxQU Q== Received: from nwd2mta4.analog.com ([137.71.173.58]) by mx0a-00128a01.pphosted.com (PPS) with ESMTPS id 47bfxcwd45-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 18 Jun 2025 13:36:59 -0400 (EDT) Received: from ASHBMBX8.ad.analog.com (ASHBMBX8.ad.analog.com [10.64.17.5]) by nwd2mta4.analog.com (8.14.7/8.14.7) with ESMTP id 55IHak6L015953 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Wed, 18 Jun 2025 13:36:46 -0400 Received: from ASHBMBX8.ad.analog.com (10.64.17.5) by ASHBMBX8.ad.analog.com (10.64.17.5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 18 Jun 2025 13:36:46 -0400 Received: from zeus.spd.analog.com (10.66.68.11) by ashbmbx8.ad.analog.com (10.64.17.5) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Wed, 18 Jun 2025 13:36:46 -0400 Received: from work.ad.analog.com (HYB-hERzalRezfV.ad.analog.com [10.65.205.9]) by zeus.spd.analog.com (8.15.1/8.15.1) with ESMTP id 55IHaUYM007456; Wed, 18 Jun 2025 13:36:33 -0400 From: Marcelo Schmitt To: , , , CC: , , , , , , , , , , , , , Subject: [PATCH v6 04/12] iio: adc: ad4170: Add support for calibration bias Date: Wed, 18 Jun 2025 14:36:29 -0300 Message-ID: <1f759b391faea46fe3fd3fd25792db0312bf3969.1750258776.git.marcelo.schmitt@analog.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ADIRuleOP-NewSCL: Rule Triggered X-Proofpoint-ORIG-GUID: lVQW6ye8m4GGWZRTLpNGQLovTM5Kkeu4 X-Proofpoint-GUID: lVQW6ye8m4GGWZRTLpNGQLovTM5Kkeu4 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjUwNjE4MDE1MCBTYWx0ZWRfX6x85RNOD3p5B DJq0nlEzdjM0FVx7NXDINqZPnLH9iTRLJ5qjst1FxjUpCR2nBgph8FsjcAu5l4ROVZAz5wAHDjM erc8rAaQqrFExk8os1+HzT462ikdrE5cqZyxePYjL9IU/rartX55d9KUwunmHp1rP0iW0EQI9FA qzWxumDeiJ+XpQ21N+3aYGbRCRGKzq0DErvt6ZFLkoEWzkRiS3g3hMd2jPjJY/XXr5BxeMo+3Fa +Fu0uW2O9ILS1xlKof8PCvS1/vU3ZBwAZKQCi7Sc9jVDgtgX4Zn9D9qMpBvP2+m7INgUJMwFa7o qQP7WMy82Salr0zA7AbQd6/bY3ZnxnEc8NARWBQ099kgi6dUniUFVQcFS3KD9kBpWOHTqoNQTim Om/47cjHHb3PdH5Uw31DOpQta2DPGDlWY8JFcZsubenmKS6mg9OQNB8jRa4l1fc0L/JSHB4D X-Authority-Analysis: v=2.4 cv=Jb28rVKV c=1 sm=1 tr=0 ts=6852f93c cx=c_pps a=3WNzaoukacrqR9RwcOSAdA==:117 a=3WNzaoukacrqR9RwcOSAdA==:17 a=6IFa9wvqVegA:10 a=gAnH3GRIAAAA:8 a=1daVv4cnZmPfqeT0XEsA:9 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1099,Hydra:6.0.736,FMLib:17.12.80.40 definitions=2025-06-18_05,2025-06-18_03,2025-03-28_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 malwarescore=0 mlxlogscore=999 clxscore=1011 mlxscore=0 priorityscore=1501 lowpriorityscore=0 phishscore=0 adultscore=0 suspectscore=0 spamscore=0 impostorscore=0 bulkscore=0 classifier=spam authscore=0 authtc=n/a authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.19.0-2505280000 definitions=main-2506180150 Content-Type: text/plain; charset="utf-8" Add support for ADC calibration bias/offset configuration. Signed-off-by: Marcelo Schmitt --- No changes in v6. drivers/iio/adc/ad4170.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/iio/adc/ad4170.c b/drivers/iio/adc/ad4170.c index 7b85771ff0d1..5c442af51f76 100644 --- a/drivers/iio/adc/ad4170.c +++ b/drivers/iio/adc/ad4170.c @@ -628,6 +628,7 @@ static const struct iio_chan_spec ad4170_channel_templa= te =3D { .differential =3D 1, .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET), .info_mask_separate_available =3D BIT(IIO_CHAN_INFO_SCALE), @@ -946,6 +947,9 @@ static int ad4170_read_raw(struct iio_dev *indio_dev, pga =3D FIELD_GET(AD4170_AFE_PGA_GAIN_MSK, setup->afe); *val =3D chan_info->offset_tbl[pga]; return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + *val =3D setup->offset; + return IIO_VAL_INT; case IIO_CHAN_INFO_CALIBSCALE: *val =3D setup->gain; return IIO_VAL_INT; @@ -1063,6 +1067,18 @@ static int ad4170_set_pga(struct ad4170_state *st, return ad4170_write_channel_setup(st, chan->address, false); } =20 +static int ad4170_set_calib_offset(struct ad4170_state *st, + struct iio_chan_spec const *chan, int val) +{ + struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan->address]; + struct ad4170_setup *setup =3D &chan_info->setup; + + guard(mutex)(&st->lock); + setup->offset =3D val; + + return ad4170_write_channel_setup(st, chan->address, false); +} + static int ad4170_set_calib_gain(struct ad4170_state *st, struct iio_chan_spec const *chan, int val) { @@ -1084,6 +1100,8 @@ static int __ad4170_write_raw(struct iio_dev *indio_d= ev, switch (info) { case IIO_CHAN_INFO_SCALE: return ad4170_set_pga(st, chan, val, val2); + case IIO_CHAN_INFO_CALIBBIAS: + return ad4170_set_calib_offset(st, chan, val); case IIO_CHAN_INFO_CALIBSCALE: return ad4170_set_calib_gain(st, chan, val); default: @@ -1112,6 +1130,7 @@ static int ad4170_write_raw_get_fmt(struct iio_dev *i= ndio_dev, switch (info) { case IIO_CHAN_INFO_SCALE: return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_CALIBBIAS: case IIO_CHAN_INFO_CALIBSCALE: return IIO_VAL_INT; default: --=20 2.47.2 From nobody Thu Oct 9 08:43:03 2025 Received: from mx0a-00128a01.pphosted.com (mx0a-00128a01.pphosted.com [148.163.135.77]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1F84D1E8323; Wed, 18 Jun 2025 17:37:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.163.135.77 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268248; cv=none; b=CX+fMg8GvvaHgw+QGS9sVAMerdXJ3f4hMhcpF5eEg4ke46lnunpl2DfUz4iSMUph6F4oTWptJJ4jis1UCN6g6mJPtIpV3MKJtEmut3YDUdcLQVDHOnwW9i7yHJweI/mZcp3j/NQez1U2N31aQ42sXLZR+QPzFi+d92xyiELefqo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268248; c=relaxed/simple; bh=EkMkmb2apmpGfdW8a0cKv5WdVY0/XoKw7zMvdFh3c6M=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=MbVfWRkHfSvI7mWcO3gU1R4MeLEm83i+70YOcYNPQw+USTVMoHJjInIuFwLFKNDvbX6nXuFEIgFUQYigKGDuRStLCL13LplC8nJvB7vxMXL0GrDNR3LOE08HO2Ib41rVKI0zFv6hi+q2GlvOD/Zm/qRMdjADO1AobSbR5XNrjdE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com; spf=pass smtp.mailfrom=analog.com; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b=H9l9aCZq; arc=none smtp.client-ip=148.163.135.77 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=analog.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b="H9l9aCZq" Received: from pps.filterd (m0167088.ppops.net [127.0.0.1]) by mx0a-00128a01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 55IGSPm7032314; Wed, 18 Jun 2025 13:37:11 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=analog.com; h=cc :content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=DKIM; bh=Fzq/m fM41vs4V3T90HK6Qq/7jrwfsMZwqU9a6+JLdec=; b=H9l9aCZq5eNpn9mbTZsEs RZv3ZriqWlmzMU46UwiJ4WQOkUSIQ56DlMLbwCXh11urDLqtsa3FbNr2OSKsp+vr ZLFfXf9cm+CN6+ccFYqPRL5TqCEhYXxQJT2Sg5WroOelwI8i1TIepHR3eG4a+Jx4 RLgQEAI/MTbiMQOnKTPBcLUwaHIF3UTYVSeXyY5J/3mCVeecsZwqVE3dU5c4DKzi SayycY/ygUn7YuRg7NUC25hFk3SY2HgwArXMeQiC6Rag6WeNvf2CCjl6RHO+sXLX oL+p0IKJ1y18WxQX66c8+XRffqabpLAPj3i65dcrWk5+Y5cCiTu1Nx9GpN2gqa55 Q== Received: from nwd2mta3.analog.com ([137.71.173.56]) by mx0a-00128a01.pphosted.com (PPS) with ESMTPS id 47bfshdda3-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 18 Jun 2025 13:37:10 -0400 (EDT) Received: from ASHBMBX9.ad.analog.com (ASHBMBX9.ad.analog.com [10.64.17.10]) by nwd2mta3.analog.com (8.14.7/8.14.7) with ESMTP id 55IHb9vk031809 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Wed, 18 Jun 2025 13:37:09 -0400 Received: from ASHBCASHYB5.ad.analog.com (10.64.17.133) by ASHBMBX9.ad.analog.com (10.64.17.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 18 Jun 2025 13:37:08 -0400 Received: from ASHBMBX8.ad.analog.com (10.64.17.5) by ASHBCASHYB5.ad.analog.com (10.64.17.133) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 18 Jun 2025 13:37:08 -0400 Received: from zeus.spd.analog.com (10.66.68.11) by ashbmbx8.ad.analog.com (10.64.17.5) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Wed, 18 Jun 2025 13:37:08 -0400 Received: from work.ad.analog.com (HYB-hERzalRezfV.ad.analog.com [10.65.205.9]) by zeus.spd.analog.com (8.15.1/8.15.1) with ESMTP id 55IHaqdX007469; Wed, 18 Jun 2025 13:36:55 -0400 From: Marcelo Schmitt To: , , , CC: , , , , , , , , , , , , , Subject: [PATCH v6 05/12] Documentation: ABI: IIO: Add sinc5+avg to the filter_type_available list Date: Wed, 18 Jun 2025 14:36:51 -0300 Message-ID: <58854f63fb664b9d99a5404b02794718c01a34ea.1750258776.git.marcelo.schmitt@analog.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ADIRuleOP-NewSCL: Rule Triggered X-Proofpoint-ORIG-GUID: 7h90lcZu7U0-0kBir70y7TYlOrrM77ys X-Authority-Analysis: v=2.4 cv=SKhCVPvH c=1 sm=1 tr=0 ts=6852f947 cx=c_pps a=PpDZqlmH/M8setHirZLBMw==:117 a=PpDZqlmH/M8setHirZLBMw==:17 a=6IFa9wvqVegA:10 a=gAnH3GRIAAAA:8 a=PC3yhc9nkE4EivMybyoA:9 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjUwNjE4MDE1MCBTYWx0ZWRfX3d/5Citj9BI/ pBmncswX5yVXeltCEmU2ieeoYODXE0RfckZOpoyeHH0dO4G/e1Ls9v3sFCKAvNimHtGWq41kZTO ZteAyqeOFfW2h4lBGTaQjYvnda9pY0GSW4fbTGi6fRCZ7zVNZxCrLfg/dPI8uJ1YH5I3xWWI/HW rnDO6BWopGvquG9Aca5BsjCg4AqWvTdBFfiCMpcry1dJT+2QZxcHe6ZB4XfyMbnHK+0OnbmtMte YB0EdPVv/CVOLneyiIhU7E2g8Zsmey+QszFlmbMAA5UyRvZzsIh/Ml+Vdm2mDnZGbDdI86mgP7m J8GHqLiJYqDwQP/tk8LuJs8/28dhIfe6sy+PrIEjQbRFEQCuJK1QwTzNpsOQ2TpZsOAiz/p44Xb bFAAlSaX18oyzQXZJvr7OyvTGXhNf2CGjgXjkcTQbsT22stV1PtoabUgLnWfxFeuEsZC9iyc X-Proofpoint-GUID: 7h90lcZu7U0-0kBir70y7TYlOrrM77ys X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1099,Hydra:6.0.736,FMLib:17.12.80.40 definitions=2025-06-18_05,2025-06-18_03,2025-03-28_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 impostorscore=0 bulkscore=0 adultscore=0 lowpriorityscore=0 spamscore=0 suspectscore=0 mlxscore=0 malwarescore=0 mlxlogscore=999 clxscore=1011 phishscore=0 priorityscore=1501 classifier=spam authscore=0 authtc=n/a authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.19.0-2505280000 definitions=main-2506180150 Content-Type: text/plain; charset="utf-8" Add the sinc5+avg filter type to the list of possible values for the filter_type_available attribute. The sinc5+avg filter type is handled by the ad4170 driver. Signed-off-by: Marcelo Schmitt --- New patch in v6. Documentation/ABI/testing/sysfs-bus-iio | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/te= sting/sysfs-bus-iio index 3bc386995fb6..c1f657182ad8 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -2321,6 +2321,7 @@ Description: * "sinc3+pf3" - Sinc3 + device specific Post Filter 3. * "sinc3+pf4" - Sinc3 + device specific Post Filter 4. * "sinc5+pf1" - Sinc5 + device specific Post Filter 1. + * "sinc5+avg" - Sinc3 + averaging by 4. * "wideband" - filter with wideband low ripple passband and sharp transition band. =20 --=20 2.47.2 From nobody Thu Oct 9 08:43:03 2025 Received: from mx0a-00128a01.pphosted.com (mx0a-00128a01.pphosted.com [148.163.135.77]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 39FDD2F3628; Wed, 18 Jun 2025 17:37:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.163.135.77 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268268; cv=none; b=SkXTbSt25Poyv8vGD+Lq+MVZc232eZK7xTW143gyMCPBg8W5an+Mhtq0hATohGoXZ3WEkEXh1euw8vdm8gdfFForlGpMSQ1KsjsYbUGcbo0jy6MzCJMUvpUjv2CWJEhOP1VBdJaDvLp4qPhQrCiE5lPMMZ3iKrp8rJVIZZbPqvQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268268; c=relaxed/simple; bh=hq4jb5zbdJ69/4WO3FhuFOScE4w9mcAU8dCvzm7UQ0k=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=AngwMLvuppGs9t/xO4QEvHwhAurZ3YGrMXh9x8QxWn2/GIuv9aSzJJwUn/arlk6+O1je7jCsrDUdET1HKr2WmRzjXGMaNHCGM5DtAlxNDVhgx8TK6HC1k1Ls1kvrx1t/4zeHeQzvUGBi/vheN6MAztuHhCiJaMXFvI/2Kdm8Jk8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com; spf=pass smtp.mailfrom=analog.com; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b=GBMu+UtJ; arc=none smtp.client-ip=148.163.135.77 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=analog.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b="GBMu+UtJ" Received: from pps.filterd (m0167088.ppops.net [127.0.0.1]) by mx0a-00128a01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 55IGv7MI032726; Wed, 18 Jun 2025 13:37:28 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=analog.com; h=cc :content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=DKIM; bh=6cs56 +HkxQNwRFC1BNLq6wA7YR5wDqfbZ0P4Za/ISxQ=; b=GBMu+UtJx5TxwISQs/48q n5eTpgCcmo/WSBkv9xFUfqvWZZiatuq5kkyGJ3IxKZd5mdVsv0Oyf04NeH6ojyMF LVC8xSWXMgDryrM5Rpufy+AS8+P+RTiKz2n05LQq4yUUTcjWrI7XJMCOdPQwQDiq XVdBWh3ii1i/qWa/oIb4PPy2haP+1TqUXtqM+LDRwu/DAH8/wgYjUlVXuvg4Hw5z FJjxlz+4N8g9fF/g12Io4Qfi3jCz+u1gNEwVJTzBJoqabryl47JO8eImC9g0LCtB zMo0zjYwr6AUMi+6cgVcTH/RlcVYjV1iq5JFLWiGw7UskHuidACarZCmB6qOdEWY Q== Received: from nwd2mta4.analog.com ([137.71.173.58]) by mx0a-00128a01.pphosted.com (PPS) with ESMTPS id 47bfshddd3-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 18 Jun 2025 13:37:28 -0400 (EDT) Received: from ASHBMBX8.ad.analog.com (ASHBMBX8.ad.analog.com [10.64.17.5]) by nwd2mta4.analog.com (8.14.7/8.14.7) with ESMTP id 55IHbRqE016107 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Wed, 18 Jun 2025 13:37:27 -0400 Received: from ASHBCASHYB5.ad.analog.com (10.64.17.133) by ASHBMBX8.ad.analog.com (10.64.17.5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 18 Jun 2025 13:37:27 -0400 Received: from ASHBMBX8.ad.analog.com (10.64.17.5) by ASHBCASHYB5.ad.analog.com (10.64.17.133) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 18 Jun 2025 13:37:26 -0400 Received: from zeus.spd.analog.com (10.66.68.11) by ashbmbx8.ad.analog.com (10.64.17.5) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Wed, 18 Jun 2025 13:37:26 -0400 Received: from work.ad.analog.com (HYB-hERzalRezfV.ad.analog.com [10.65.205.9]) by zeus.spd.analog.com (8.15.1/8.15.1) with ESMTP id 55IHbAxE007475; Wed, 18 Jun 2025 13:37:13 -0400 From: Marcelo Schmitt To: , , , CC: , , , , , , , , , , , , , Subject: [PATCH v6 06/12] iio: adc: ad4170: Add digital filter and sample frequency config support Date: Wed, 18 Jun 2025 14:37:09 -0300 Message-ID: X-Mailer: git-send-email 2.39.2 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ADIRuleOP-NewSCL: Rule Triggered X-Proofpoint-ORIG-GUID: iKOTuN7aD0B0jXHETmMPxCs13M6L_7C1 X-Authority-Analysis: v=2.4 cv=SKhCVPvH c=1 sm=1 tr=0 ts=6852f958 cx=c_pps a=3WNzaoukacrqR9RwcOSAdA==:117 a=3WNzaoukacrqR9RwcOSAdA==:17 a=6IFa9wvqVegA:10 a=gAnH3GRIAAAA:8 a=2xRyMRvjrxaZbrfWgYAA:9 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjUwNjE4MDE1MCBTYWx0ZWRfXzXbXjlIu7XiA ZtKDePLcgSHYzjaTMMuKvZgrDT2z94vdZ1BBKPq9c2BUCITKVfjc0yw9FbIReExigwrEH1L9HJK XugYOYGLKvHZ7hNpnMhu98ucBgI/Wo7+GFqdpaHWTVc5kyZfsYJKbNq6+ainzBu48nFa04g+UIv M/BeMZDXYIGZHQEYNfNgOBASAlS6Va1FhhKiUDStdrKuOYJC5/RxBEhBwrmeepXMGD2RqrkLh/m d+P1D631kntvKPizY98H2c0gPAbZuuwgHbwX+4Iap1UYgzwpeodx4SLyQ+Oe1xMaI9OpDqI99J4 7Tir40l360zXGwB03Z29QPgWYsVdQ6fom16CGyDYnIidnoWLL6h2p1G9c5nqAX/QwmA2NbvmuIM PpSOWekHNnRp04zIEt/DoR+ALq1NWYPuFQdy7i9OuLhqIDk3aFAGML6RvOiP8RD/b/xWyTxO X-Proofpoint-GUID: iKOTuN7aD0B0jXHETmMPxCs13M6L_7C1 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1099,Hydra:6.0.736,FMLib:17.12.80.40 definitions=2025-06-18_05,2025-06-18_03,2025-03-28_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 impostorscore=0 bulkscore=0 adultscore=0 lowpriorityscore=0 spamscore=0 suspectscore=0 mlxscore=0 malwarescore=0 mlxlogscore=999 clxscore=1015 phishscore=0 priorityscore=1501 classifier=spam authscore=0 authtc=n/a authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.19.0-2505280000 definitions=main-2506180150 Content-Type: text/plain; charset="utf-8" Add support for sinc3, sinc5, and averaged sinc5 digital filters along with sample frequency configuration. Signed-off-by: Marcelo Schmitt --- No changes in v6. I had forgotten about the post filter features and am not keen to implement= them now. May add support for those in a follow up patch or in v7. drivers/iio/adc/ad4170.c | 262 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 259 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/ad4170.c b/drivers/iio/adc/ad4170.c index 5c442af51f76..b229a24b40de 100644 --- a/drivers/iio/adc/ad4170.c +++ b/drivers/iio/adc/ad4170.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -88,6 +89,9 @@ #define AD4170_AFE_BIPOLAR_MSK BIT(4) #define AD4170_AFE_PGA_GAIN_MSK GENMASK(3, 0) =20 +/* AD4170_FILTER_REG */ +#define AD4170_FILTER_FILTER_TYPE_MSK GENMASK(3, 0) + /* AD4170 register constants */ =20 /* AD4170_CHAN_MAP_REG constants */ @@ -113,6 +117,11 @@ #define AD4170_ADC_CTRL_MODE_SINGLE 0x4 #define AD4170_ADC_CTRL_MODE_IDLE 0x7 =20 +/* AD4170_FILTER_REG constants */ +#define AD4170_FILTER_FILTER_TYPE_SINC5_AVG 0x0 +#define AD4170_FILTER_FILTER_TYPE_SINC5 0x4 +#define AD4170_FILTER_FILTER_TYPE_SINC3 0x6 + /* Device properties and auxiliary constants */ =20 #define AD4170_NUM_ANALOG_PINS 9 @@ -122,6 +131,7 @@ #define AD4170_INVALID_SETUP 9 #define AD4170_SPI_INST_PHASE_LEN 2 #define AD4170_SPI_MAX_XFER_LEN 6 +#define AD4170_DEFAULT_SAMP_RATE (125 * HZ_PER_KHZ) =20 #define AD4170_INT_REF_2_5V 2500000 =20 @@ -130,6 +140,12 @@ =20 #define AD4170_NUM_PGA_OPTIONS 10 =20 +/* Digital filter properties */ +#define AD4170_SINC3_MIN_FS 4 +#define AD4170_SINC3_MAX_FS 65532 +#define AD4170_SINC5_MIN_FS 1 +#define AD4170_SINC5_MAX_FS 256 + #define AD4170_GAIN_REG_DEFAULT 0x555555 =20 static const unsigned int ad4170_reg_size[] =3D { @@ -190,6 +206,12 @@ enum ad4170_ref_select { AD4170_REF_AVDD, }; =20 +enum ad4170_filter_type { + AD4170_SINC5_AVG, + AD4170_SINC5, + AD4170_SINC3, +}; + enum ad4170_regulator { AD4170_AVDD_SUP, AD4170_AVSS_SUP, @@ -211,6 +233,18 @@ static const char * const ad4170_int_pin_names[] =3D { [AD4170_INT_PIN_DIG_AUX1] =3D "dig_aux1", }; =20 +static const unsigned int ad4170_sinc3_filt_fs_tbl[] =3D { + 4, 8, 12, 16, 20, 40, 48, 80, /* 0 - 7 */ + 100, 256, 500, 1000, 5000, 8332, 10000, 25000, /* 8 - 15 */ + 50000, 65532, /* 16 - 17 */ +}; + +#define AD4170_MAX_FS_TBL_SIZE ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl) + +static const unsigned int ad4170_sinc5_filt_fs_tbl[] =3D { + 1, 2, 4, 8, 12, 16, 20, 40, 48, 80, 100, 256, +}; + struct ad4170_chip_info { const char *name; }; @@ -268,6 +302,12 @@ struct ad4170_chan_info { bool enabled; }; =20 +static const char * const ad4170_filt_names[] =3D { + [AD4170_SINC5_AVG] =3D "sinc5+avg", + [AD4170_SINC5] =3D "sinc5", + [AD4170_SINC3] =3D "sinc3", +}; + struct ad4170_state { struct mutex lock; /* Protect read-modify-write and multi write sequences= */ int vrefs_uv[AD4170_MAX_SUP]; @@ -277,6 +317,7 @@ struct ad4170_state { struct ad4170_chan_info chan_infos[AD4170_MAX_CHANNELS]; struct spi_device *spi; struct regmap *regmap; + int sps_tbl[ARRAY_SIZE(ad4170_filt_names)][AD4170_MAX_FS_TBL_SIZE][2]; struct completion completion; unsigned int pins_fn[AD4170_NUM_ANALOG_PINS]; u32 int_pin_sel; @@ -288,6 +329,38 @@ struct ad4170_state { u8 rx_buf[4]; }; =20 +static void ad4170_fill_sps_tbl(struct ad4170_state *st) +{ + unsigned int tmp0, tmp1, i; + + /* + * The ODR can be calculated the same way for sinc5+avg, sinc5, and + * sinc3 filter types with the exception that sinc5 filter has a + * narrowed range of allowed FILTER_FS values. + */ + for (i =3D 0; i < ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl); i++) { + tmp0 =3D div_u64_rem(st->mclk_hz, 32 * ad4170_sinc3_filt_fs_tbl[i], + &tmp1); + tmp1 =3D mult_frac(tmp1, MICRO, 32 * ad4170_sinc3_filt_fs_tbl[i]); + /* Fill sinc5+avg filter SPS table */ + st->sps_tbl[AD4170_SINC5_AVG][i][0] =3D tmp0; /* Integer part */ + st->sps_tbl[AD4170_SINC5_AVG][i][1] =3D tmp1; /* Fractional part */ + + /* Fill sinc3 filter SPS table */ + st->sps_tbl[AD4170_SINC3][i][0] =3D tmp0; /* Integer part */ + st->sps_tbl[AD4170_SINC3][i][1] =3D tmp1; /* Fractional part */ + } + /* Sinc5 filter ODR doesn't use all FILTER_FS bits */ + for (i =3D 0; i < ARRAY_SIZE(ad4170_sinc5_filt_fs_tbl); i++) { + tmp0 =3D div_u64_rem(st->mclk_hz, 32 * ad4170_sinc5_filt_fs_tbl[i], + &tmp1); + tmp1 =3D mult_frac(tmp1, MICRO, 32 * ad4170_sinc5_filt_fs_tbl[i]); + /* Fill sinc5 filter SPS table */ + st->sps_tbl[AD4170_SINC5][i][0] =3D tmp0; /* Integer part */ + st->sps_tbl[AD4170_SINC5][i][1] =3D tmp1; /* Fractional part */ + } +} + static int ad4170_debugfs_reg_access(struct iio_dev *indio_dev, unsigned int reg, unsigned int writeval, unsigned int *readval) @@ -622,6 +695,100 @@ static int ad4170_set_channel_enable(struct ad4170_st= ate *st, return 0; } =20 +static int __ad4170_get_filter_type(unsigned int filter) +{ + u16 f_conf =3D FIELD_GET(AD4170_FILTER_FILTER_TYPE_MSK, filter); + + switch (f_conf) { + case AD4170_FILTER_FILTER_TYPE_SINC5_AVG: + return AD4170_SINC5_AVG; + case AD4170_FILTER_FILTER_TYPE_SINC5: + return AD4170_SINC5; + case AD4170_FILTER_FILTER_TYPE_SINC3: + return AD4170_SINC3; + default: + return -EINVAL; + } +} + +static int ad4170_set_filter_type(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + unsigned int val) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan->address]; + struct ad4170_setup *setup =3D &chan_info->setup; + unsigned int filter_type_conf; + int ret; + + switch (val) { + case AD4170_SINC5_AVG: + filter_type_conf =3D AD4170_FILTER_FILTER_TYPE_SINC5_AVG; + break; + case AD4170_SINC5: + filter_type_conf =3D AD4170_FILTER_FILTER_TYPE_SINC5; + break; + case AD4170_SINC3: + filter_type_conf =3D AD4170_FILTER_FILTER_TYPE_SINC3; + break; + default: + return -EINVAL; + } + + /* + * The filters provide the same ODR for a given filter_fs value but + * there are different minimum and maximum filter_fs limits for each + * filter. The filter_fs value will be adjusted if the current filter_fs + * is out of the limits of the just requested filter. Since the + * filter_fs value affects the ODR (sampling_frequency), changing the + * filter may lead to a change in the sampling frequency. + */ + scoped_guard(mutex, &st->lock) { + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + if (val =3D=3D AD4170_SINC5_AVG || val =3D=3D AD4170_SINC3) + setup->filter_fs =3D clamp(val, AD4170_SINC3_MIN_FS, + AD4170_SINC3_MAX_FS); + else + setup->filter_fs =3D clamp(val, AD4170_SINC5_MIN_FS, + AD4170_SINC5_MAX_FS); + + setup->filter &=3D ~AD4170_FILTER_FILTER_TYPE_MSK; + setup->filter |=3D FIELD_PREP(AD4170_FILTER_FILTER_TYPE_MSK, + filter_type_conf); + + ret =3D ad4170_write_channel_setup(st, chan->address, false); + iio_device_release_direct(indio_dev); + } + + return ret; +} + +static int ad4170_get_filter_type(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan->address]; + struct ad4170_setup *setup =3D &chan_info->setup; + + return __ad4170_get_filter_type(setup->filter); +} + +static const struct iio_enum ad4170_filter_type_enum =3D { + .items =3D ad4170_filt_names, + .num_items =3D ARRAY_SIZE(ad4170_filt_names), + .get =3D ad4170_get_filter_type, + .set =3D ad4170_set_filter_type, +}; + +static const struct iio_chan_spec_ext_info ad4170_filter_type_ext_info[] = =3D { + IIO_ENUM("filter_type", IIO_SEPARATE, &ad4170_filter_type_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE, + &ad4170_filter_type_enum), + { } +}; + static const struct iio_chan_spec ad4170_channel_template =3D { .type =3D IIO_VOLTAGE, .indexed =3D 1, @@ -630,8 +797,11 @@ static const struct iio_chan_spec ad4170_channel_templ= ate =3D { BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_CALIBSCALE) | - BIT(IIO_CHAN_INFO_OFFSET), - .info_mask_separate_available =3D BIT(IIO_CHAN_INFO_SCALE), + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate_available =3D BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .ext_info =3D ad4170_filter_type_ext_info, .scan_type =3D { .realbits =3D 24, .storagebits =3D 32, @@ -926,7 +1096,8 @@ static int ad4170_read_raw(struct iio_dev *indio_dev, struct ad4170_state *st =3D iio_priv(indio_dev); struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan->address]; struct ad4170_setup *setup =3D &chan_info->setup; - unsigned int pga; + enum ad4170_filter_type f_type; + unsigned int pga, fs_idx; int ret; =20 guard(mutex)(&st->lock); @@ -947,6 +1118,27 @@ static int ad4170_read_raw(struct iio_dev *indio_dev, pga =3D FIELD_GET(AD4170_AFE_PGA_GAIN_MSK, setup->afe); *val =3D chan_info->offset_tbl[pga]; return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + f_type =3D __ad4170_get_filter_type(setup->filter); + switch (f_type) { + case AD4170_SINC5_AVG: + case AD4170_SINC3: + fs_idx =3D find_closest(setup->filter_fs, + ad4170_sinc3_filt_fs_tbl, + ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl)); + *val =3D st->sps_tbl[f_type][fs_idx][0]; + *val2 =3D st->sps_tbl[f_type][fs_idx][1]; + return IIO_VAL_INT_PLUS_MICRO; + case AD4170_SINC5: + fs_idx =3D find_closest(setup->filter_fs, + ad4170_sinc5_filt_fs_tbl, + ARRAY_SIZE(ad4170_sinc5_filt_fs_tbl)); + *val =3D st->sps_tbl[f_type][fs_idx][0]; + *val2 =3D st->sps_tbl[f_type][fs_idx][1]; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } case IIO_CHAN_INFO_CALIBBIAS: *val =3D setup->offset; return IIO_VAL_INT; @@ -1032,6 +1224,7 @@ static int ad4170_read_avail(struct iio_dev *indio_de= v, { struct ad4170_state *st =3D iio_priv(indio_dev); struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan->address]; + enum ad4170_filter_type f_type; =20 switch (info) { case IIO_CHAN_INFO_SCALE: @@ -1039,6 +1232,21 @@ static int ad4170_read_avail(struct iio_dev *indio_d= ev, *length =3D ARRAY_SIZE(chan_info->scale_tbl) * 2; *type =3D IIO_VAL_INT_PLUS_NANO; return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + f_type =3D ad4170_get_filter_type(indio_dev, chan); + *vals =3D (int *)st->sps_tbl[f_type]; + *type =3D IIO_VAL_INT_PLUS_MICRO; + switch (f_type) { + case AD4170_SINC5_AVG: + case AD4170_SINC3: + *length =3D ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl) * 2; + return IIO_AVAIL_LIST; + case AD4170_SINC5: + *length =3D ARRAY_SIZE(ad4170_sinc5_filt_fs_tbl) * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } default: return -EINVAL; } @@ -1067,6 +1275,42 @@ static int ad4170_set_pga(struct ad4170_state *st, return ad4170_write_channel_setup(st, chan->address, false); } =20 +static int ad4170_set_channel_freq(struct ad4170_state *st, + struct iio_chan_spec const *chan, int val, + int val2) +{ + struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan->address]; + struct ad4170_setup *setup =3D &chan_info->setup; + enum ad4170_filter_type f_type =3D __ad4170_get_filter_type(setup->filter= ); + unsigned int filt_fs_tbl_size, i; + + switch (f_type) { + case AD4170_SINC5_AVG: + case AD4170_SINC3: + filt_fs_tbl_size =3D ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl); + break; + case AD4170_SINC5: + filt_fs_tbl_size =3D ARRAY_SIZE(ad4170_sinc5_filt_fs_tbl); + break; + } + + for (i =3D 0; i < filt_fs_tbl_size; i++) { + if (st->sps_tbl[f_type][i][0] =3D=3D val && + st->sps_tbl[f_type][i][1] =3D=3D val2) + break; + } + if (i =3D=3D filt_fs_tbl_size) + return -EINVAL; + + guard(mutex)(&st->lock); + if (f_type =3D=3D AD4170_SINC5) + setup->filter_fs =3D ad4170_sinc5_filt_fs_tbl[i]; + else + setup->filter_fs =3D ad4170_sinc3_filt_fs_tbl[i]; + + return ad4170_write_channel_setup(st, chan->address, false); +} + static int ad4170_set_calib_offset(struct ad4170_state *st, struct iio_chan_spec const *chan, int val) { @@ -1100,6 +1344,8 @@ static int __ad4170_write_raw(struct iio_dev *indio_d= ev, switch (info) { case IIO_CHAN_INFO_SCALE: return ad4170_set_pga(st, chan, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + return ad4170_set_channel_freq(st, chan, val, val2); case IIO_CHAN_INFO_CALIBBIAS: return ad4170_set_calib_offset(st, chan, val); case IIO_CHAN_INFO_CALIBSCALE: @@ -1130,6 +1376,8 @@ static int ad4170_write_raw_get_fmt(struct iio_dev *i= ndio_dev, switch (info) { case IIO_CHAN_INFO_SCALE: return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_CALIBBIAS: case IIO_CHAN_INFO_CALIBSCALE: return IIO_VAL_INT; @@ -1366,6 +1614,8 @@ static int ad4170_initial_config(struct iio_dev *indi= o_dev) unsigned int i; int ret; =20 + ad4170_fill_sps_tbl(st); + ret =3D regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, AD4170_ADC_CTRL_MODE_MSK, FIELD_PREP(AD4170_ADC_CTRL_MODE_MSK, @@ -1398,6 +1648,12 @@ static int ad4170_initial_config(struct iio_dev *ind= io_dev) return dev_err_probe(dev, ret, "Failed to write CHAN_MAP_REG\n"); =20 + ret =3D ad4170_set_channel_freq(st, chan, + AD4170_DEFAULT_SAMP_RATE, 0); + if (ret) + return dev_err_probe(dev, ret, + "Failed to set channel freq\n"); + ret =3D ad4170_fill_scale_tbl(indio_dev, chan); if (ret) return dev_err_probe(dev, ret, --=20 2.47.2 From nobody Thu Oct 9 08:43:03 2025 Received: from mx0a-00128a01.pphosted.com (mx0a-00128a01.pphosted.com [148.163.135.77]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7CEBA2F3656; Wed, 18 Jun 2025 17:38:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.163.135.77 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268293; cv=none; b=D2nuvzJiRKvZZzhmDTVl/U3sZZxKAFq7NaOHCHMhrd4QInJpK0ElpbaP9LA8itOETL3zKpwkZ4V6mnjR3BqTR3mjsdkfkRRKwiMAXtauvg6LJ+TBvbL+iJ9HbWXr8dW49D0IKB4ZoQqh17gN+fRto2xKqvKvJYaQecTIHxBUxzo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268293; c=relaxed/simple; bh=ohJZuP3dn0PU/0V+ca3vsVPOb7wEixXemFa32dqprxA=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=VDb9TDxs3MDY6VBOgtKcKar5YWsFu9Xj3TA50e1LvkN08W6L5QUBEjBlcbWZ5NQJnwHSROsgekj7xBN6WQx9v8jDNbVQkpkYQxW/5tG/408swsImctqFR/KSvMYdurxHBKmiDY0Fa8q5HT1rp66g/x0cgkdD51TTMU6abp39akI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com; spf=pass smtp.mailfrom=analog.com; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b=hNCtp7HU; arc=none smtp.client-ip=148.163.135.77 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=analog.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b="hNCtp7HU" Received: from pps.filterd (m0167089.ppops.net [127.0.0.1]) by mx0a-00128a01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 55IGMPdR028436; Wed, 18 Jun 2025 13:37:51 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=analog.com; h=cc :content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=DKIM; bh=kkRp8 wSxlB97fB4UHPeKvyqAEfwkAw+fwtDdvhK1YtI=; b=hNCtp7HUzOZT/KFtCBQmN t+jbIGjRQHVzCHgO+EW47+BCJxXea9MqdaiPrKiLcovWxIRpEA97fyQ+m26rxKFo wKHtB0ULHK4JUsLs+v319kO49qgCrYtadsPLaS0cdsfhfpDpt29U0IhMpNGer0i1 IbMS4PfFDEp3SelB7gxLVeKVB6+AvkX94IjdWteiOGPYAbRlArhMdqPCdtmFwPdr jFzLEMTfszmE13/voqZhK2PMjcv7jrVt9HeGu6vkfFgNsA6sn8RwcIo7XkuvDufV JInNnlJHhJxNnHMKgfIRiJ/f2Qrj8snazifVpOW/xeeMwngU3ic2UoD77TZtH3Bs g== Received: from nwd2mta4.analog.com ([137.71.173.58]) by mx0a-00128a01.pphosted.com (PPS) with ESMTPS id 47bfxcwdc3-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 18 Jun 2025 13:37:51 -0400 (EDT) Received: from ASHBMBX8.ad.analog.com (ASHBMBX8.ad.analog.com [10.64.17.5]) by nwd2mta4.analog.com (8.14.7/8.14.7) with ESMTP id 55IHboGc016164 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Wed, 18 Jun 2025 13:37:50 -0400 Received: from ASHBCASHYB4.ad.analog.com (10.64.17.132) by ASHBMBX8.ad.analog.com (10.64.17.5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 18 Jun 2025 13:37:50 -0400 Received: from ASHBMBX8.ad.analog.com (10.64.17.5) by ASHBCASHYB4.ad.analog.com (10.64.17.132) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 18 Jun 2025 13:37:50 -0400 Received: from zeus.spd.analog.com (10.66.68.11) by ashbmbx8.ad.analog.com (10.64.17.5) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Wed, 18 Jun 2025 13:37:50 -0400 Received: from work.ad.analog.com (HYB-hERzalRezfV.ad.analog.com [10.65.205.9]) by zeus.spd.analog.com (8.15.1/8.15.1) with ESMTP id 55IHbVxK007482; Wed, 18 Jun 2025 13:37:34 -0400 From: Marcelo Schmitt To: , , , CC: , , , , , , , , , , , , , Subject: [PATCH v6 07/12] iio: adc: ad4170: Add support for buffered data capture Date: Wed, 18 Jun 2025 14:37:30 -0300 Message-ID: X-Mailer: git-send-email 2.39.2 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ADIRuleOP-NewSCL: Rule Triggered X-Proofpoint-ORIG-GUID: 1YlMBSJpPxr4PGG6WgyIICUh6dwCNK75 X-Proofpoint-GUID: 1YlMBSJpPxr4PGG6WgyIICUh6dwCNK75 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjUwNjE4MDE1MCBTYWx0ZWRfX0VNYqtBqp81g p+l9QKoz1PUkniDb6kiGpftJoQ9dS2Ul2IO8GwHUANleMfIkaVxGrmqScywiUZERxs/vsrnTEiD eoAWxLk/PdGVALrB3u5uyE9ORCjZsIOSCkMI2I8K067cS82i1/PuSnJuItY348qASsVbi5c5jnp CXaCqDzb2X6yow1greVp9FyqoMUzziGr5qffIhNmLCopGimcLMyfrLkDyuYLa18ALq7O4FTAYjX ChwsAmsIcX6Z6rizJH2h9nBSTF3JMENdIwwyHBOzPn9Cbd2YdAoyFyUJFmxs0rD7CvuoKUYn7LC 8HnV3CtJe6tkbdAYbz+HQwY3wOlYdBwOMadHp29rfGEetTryEBFxiW0rN0pIHaqcjHfyhqMM9v9 j84FwFYciZj2lYCD3ttu7l684SVuZKr2h9Y0faSGUbJSiKmByG30mKHbRjKBx4MkIgzE6jX/ X-Authority-Analysis: v=2.4 cv=Jb28rVKV c=1 sm=1 tr=0 ts=6852f96f cx=c_pps a=3WNzaoukacrqR9RwcOSAdA==:117 a=3WNzaoukacrqR9RwcOSAdA==:17 a=6IFa9wvqVegA:10 a=gAnH3GRIAAAA:8 a=S2ps9YoUDC_6q7dLIc8A:9 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1099,Hydra:6.0.736,FMLib:17.12.80.40 definitions=2025-06-18_05,2025-06-18_03,2025-03-28_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 malwarescore=0 mlxlogscore=999 clxscore=1015 mlxscore=0 priorityscore=1501 lowpriorityscore=0 phishscore=0 adultscore=0 suspectscore=0 spamscore=0 impostorscore=0 bulkscore=0 classifier=spam authscore=0 authtc=n/a authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.19.0-2505280000 definitions=main-2506180150 Content-Type: text/plain; charset="utf-8" Extend the AD4170 driver to allow buffered data capture in continuous read mode. In continuous read mode, the chip skips the instruction phase and outputs just ADC sample data, enabling faster sample rates to be reached. The internal channel sequencer always starts sampling from channel 0 and channel 0 must be enabled if more than one channel is selected for data capture. The scan mask validation callback checks if the aforementioned condition is met. Signed-off-by: Marcelo Schmitt --- Change log v5 -> v6 - Used local variable to minimize the risk of race conditions when checking= IIO scan_mask. drivers/iio/adc/Kconfig | 2 + drivers/iio/adc/ad4170.c | 217 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 218 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 03966d07a721..b12dcc04c894 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -89,6 +89,8 @@ config AD4170 tristate "Analog Device AD4170 ADC Driver" depends on SPI select REGMAP_SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER help Say yes here to build support for Analog Devices AD4170 SPI analog to digital converters (ADC). diff --git a/drivers/iio/adc/ad4170.c b/drivers/iio/adc/ad4170.c index b229a24b40de..2acd4316b079 100644 --- a/drivers/iio/adc/ad4170.c +++ b/drivers/iio/adc/ad4170.c @@ -14,7 +14,11 @@ #include #include #include +#include #include +#include +#include +#include #include #include #include @@ -59,6 +63,7 @@ #define AD4170_FILTER_FS_REG(x) (0xC7 + 14 * (x)) #define AD4170_OFFSET_REG(x) (0xCA + 14 * (x)) #define AD4170_GAIN_REG(x) (0xCD + 14 * (x)) +#define AD4170_ADC_CTRL_CONT_READ_EXIT_REG 0x200 /* virtual reg */ =20 #define AD4170_REG_READ_MASK BIT(14) =20 @@ -70,6 +75,7 @@ =20 /* AD4170_ADC_CTRL_REG */ #define AD4170_ADC_CTRL_MULTI_DATA_REG_SEL_MSK BIT(7) +#define AD4170_ADC_CTRL_CONT_READ_MSK GENMASK(5, 4) #define AD4170_ADC_CTRL_MODE_MSK GENMASK(3, 0) =20 /* AD4170_CHAN_EN_REG */ @@ -114,9 +120,13 @@ #define AD4170_PIN_MUXING_DIG_AUX1_RDY 0x1 =20 /* AD4170_ADC_CTRL_REG constants */ +#define AD4170_ADC_CTRL_MODE_CONT 0x0 #define AD4170_ADC_CTRL_MODE_SINGLE 0x4 #define AD4170_ADC_CTRL_MODE_IDLE 0x7 =20 +#define AD4170_ADC_CTRL_CONT_READ_DISABLE 0x0 +#define AD4170_ADC_CTRL_CONT_READ_ENABLE 0x1 + /* AD4170_FILTER_REG constants */ #define AD4170_FILTER_FILTER_TYPE_SINC5_AVG 0x0 #define AD4170_FILTER_FILTER_TYPE_SINC5 0x4 @@ -148,6 +158,8 @@ =20 #define AD4170_GAIN_REG_DEFAULT 0x555555 =20 +#define AD4170_ADC_CTRL_CONT_READ_EXIT 0xA5 + static const unsigned int ad4170_reg_size[] =3D { [AD4170_CONFIG_A_REG] =3D 1, [AD4170_DATA_24B_REG] =3D 3, @@ -184,6 +196,7 @@ static const unsigned int ad4170_reg_size[] =3D { [AD4170_OFFSET_REG(5) ... AD4170_GAIN_REG(5)] =3D 3, [AD4170_OFFSET_REG(6) ... AD4170_GAIN_REG(6)] =3D 3, [AD4170_OFFSET_REG(7) ... AD4170_GAIN_REG(7)] =3D 3, + [AD4170_ADC_CTRL_CONT_READ_EXIT_REG] =3D 0, }; =20 enum ad4170_ref_buf { @@ -318,6 +331,10 @@ struct ad4170_state { struct spi_device *spi; struct regmap *regmap; int sps_tbl[ARRAY_SIZE(ad4170_filt_names)][AD4170_MAX_FS_TBL_SIZE][2]; + __be32 bounce_buffer[AD4170_MAX_CHANNELS]; + struct spi_message msg; + struct spi_transfer xfer; + struct iio_trigger *trig; struct completion completion; unsigned int pins_fn[AD4170_NUM_ANALOG_PINS]; u32 int_pin_sel; @@ -405,6 +422,10 @@ static int ad4170_reg_write(void *context, unsigned in= t reg, unsigned int val) case 1: st->tx_buf[AD4170_SPI_INST_PHASE_LEN] =3D val; break; + case 0: + /* Write continuous read exit code */ + st->tx_buf[0] =3D AD4170_ADC_CTRL_CONT_READ_EXIT; + return spi_write(st->spi, st->tx_buf, 1); default: return -EINVAL; } @@ -805,6 +826,7 @@ static const struct iio_chan_spec ad4170_channel_templa= te =3D { .scan_type =3D { .realbits =3D 24, .storagebits =3D 32, + .shift =3D 8, .endianness =3D IIO_BE, }, }; @@ -1386,11 +1408,27 @@ static int ad4170_write_raw_get_fmt(struct iio_dev = *indio_dev, } } =20 +static int ad4170_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *active_scan_mask) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + unsigned int chan_index; + int ret; + + iio_for_each_active_channel(indio_dev, chan_index) { + ret =3D ad4170_set_channel_enable(st, chan_index, true); + if (ret) + return ret; + } + return 0; +} + static const struct iio_info ad4170_info =3D { .read_raw =3D ad4170_read_raw, .read_avail =3D ad4170_read_avail, .write_raw =3D ad4170_write_raw, .write_raw_get_fmt =3D ad4170_write_raw_get_fmt, + .update_scan_mode =3D ad4170_update_scan_mode, .debugfs_reg_access =3D ad4170_debugfs_reg_access, }; =20 @@ -1676,16 +1714,179 @@ static int ad4170_initial_config(struct iio_dev *i= ndio_dev) AD4170_ADC_CTRL_MULTI_DATA_REG_SEL_MSK); } =20 +static int ad4170_prepare_spi_message(struct ad4170_state *st) +{ + /* + * Continuous data register read is enabled on buffer postenable so + * no instruction phase is needed meaning we don't need to send the + * register address to read data. Transfer only needs the read buffer. + */ + st->xfer.rx_buf =3D &st->rx_buf; + st->xfer.len =3D BITS_TO_BYTES(ad4170_channel_template.scan_type.realbits= ); + + spi_message_init_with_transfers(&st->msg, &st->xfer, 1); + + return devm_spi_optimize_message(&st->spi->dev, st->spi, &st->msg); +} + +static int ad4170_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + int ret; + + ret =3D regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_MODE_MSK, + FIELD_PREP(AD4170_ADC_CTRL_MODE_MSK, + AD4170_ADC_CTRL_MODE_CONT)); + if (ret) + return ret; + + /* + * This enables continuous read of the ADC data register. The ADC must + * be in continuous conversion mode. + */ + return regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_CONT_READ_MSK, + FIELD_PREP(AD4170_ADC_CTRL_CONT_READ_MSK, + AD4170_ADC_CTRL_CONT_READ_ENABLE)); +} + +static int ad4170_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + unsigned int i; + int ret; + + /* + * Use a high register address (virtual register) to request a write of + * 0xA5 to the ADC during the first 8 SCLKs of the ADC data read cycle, + * thus exiting continuous read. + */ + ret =3D regmap_write(st->regmap, AD4170_ADC_CTRL_CONT_READ_EXIT_REG, 0); + if (ret) + return ret; + + ret =3D regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_CONT_READ_MSK, + FIELD_PREP(AD4170_ADC_CTRL_CONT_READ_MSK, + AD4170_ADC_CTRL_CONT_READ_DISABLE)); + if (ret) + return ret; + + ret =3D regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_MODE_MSK, + FIELD_PREP(AD4170_ADC_CTRL_MODE_MSK, + AD4170_ADC_CTRL_MODE_IDLE)); + if (ret) + return ret; + + /* + * The ADC sequences through all the enabled channels (see datasheet + * page 95). That can lead to incorrect channel being read if a + * single-shot read (or buffered read with different active_scan_mask) + * is done after buffer disable. Disable all channels so only requested + * channels will be read. + */ + for (i =3D 0; i < indio_dev->num_channels; i++) { + ret =3D ad4170_set_channel_enable(st, i, false); + if (ret) + return ret; + } + + return 0; +} + +static bool ad4170_validate_scan_mask(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + unsigned int masklength =3D iio_get_masklength(indio_dev); + unsigned int enabled; + + /* + * The channel sequencer cycles through the enabled channels in + * sequential order, from channel 0 to channel 15, bypassing disabled + * channels. When more than one channel is enabled, channel 0 must + * always be enabled. See datasheet channel_en register description at + * page 95. + */ + enabled =3D bitmap_weight(scan_mask, masklength); + if (enabled > 1) + return test_bit(0, scan_mask); + + return enabled =3D=3D 1; +} + +static const struct iio_buffer_setup_ops ad4170_buffer_ops =3D { + .postenable =3D ad4170_buffer_postenable, + .predisable =3D ad4170_buffer_predisable, + .validate_scan_mask =3D ad4170_validate_scan_mask, +}; + +static irqreturn_t ad4170_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf =3D p; + struct iio_dev *indio_dev =3D pf->indio_dev; + struct ad4170_state *st =3D iio_priv(indio_dev); + unsigned int chan_index; + unsigned int i =3D 0; + int ret; + + iio_for_each_active_channel(indio_dev, chan_index) { + ret =3D spi_sync(st->spi, &st->msg); + if (ret) + goto err_out; + + memcpy(&st->bounce_buffer[i++], st->rx_buf, ARRAY_SIZE(st->rx_buf)); + } + + iio_push_to_buffers(indio_dev, st->bounce_buffer); +err_out: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static const struct iio_trigger_ops ad4170_trigger_ops =3D { + .validate_device =3D iio_trigger_validate_own_device, +}; + static irqreturn_t ad4170_irq_handler(int irq, void *dev_id) { struct iio_dev *indio_dev =3D dev_id; struct ad4170_state *st =3D iio_priv(indio_dev); =20 - complete(&st->completion); + if (iio_buffer_enabled(indio_dev)) + iio_trigger_poll(st->trig); + else + complete(&st->completion); =20 return IRQ_HANDLED; }; =20 +static int ad4170_trigger_setup(struct iio_dev *indio_dev) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + struct device *dev =3D &st->spi->dev; + int ret; + + st->trig =3D devm_iio_trigger_alloc(dev, "%s-trig%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!st->trig) + return -ENOMEM; + + st->trig->ops =3D &ad4170_trigger_ops; + st->trig->dev.parent =3D dev; + + iio_trigger_set_drvdata(st->trig, indio_dev); + ret =3D devm_iio_trigger_register(dev, st->trig); + if (ret) + return dev_err_probe(dev, ret, "Failed to register trigger\n"); + + indio_dev->trig =3D iio_trigger_get(st->trig); + + return 0; +} + static int ad4170_regulator_setup(struct ad4170_state *st) { struct device *dev =3D &st->spi->dev; @@ -1810,8 +2011,22 @@ static int ad4170_probe(struct spi_device *spi) IRQF_ONESHOT, indio_dev->name, indio_dev); if (ret) return ret; + + ret =3D ad4170_trigger_setup(indio_dev); + if (ret) + return ret; } =20 + ret =3D ad4170_prepare_spi_message(st); + if (ret) + return dev_err_probe(dev, ret, "Failed to prepare SPI message\n"); + + ret =3D devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + &ad4170_trigger_handler, + &ad4170_buffer_ops); + if (ret) + return dev_err_probe(dev, ret, "Failed to setup read buffer\n"); + return devm_iio_device_register(dev, indio_dev); } =20 --=20 2.47.2 From nobody Thu Oct 9 08:43:03 2025 Received: from mx0a-00128a01.pphosted.com (mx0a-00128a01.pphosted.com [148.163.135.77]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C99B62F49E1; Wed, 18 Jun 2025 17:38:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.163.135.77 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268307; cv=none; b=C31bpSQhkcpQyTyqbF6uKZTFmvEoq2wlQUdk9wrPZQKzmlxhZojTS3RQQdqNvW9DQ2B3GC6vNcwVqifQbeFmsmCskOwgUPtl0tglSVDjpexwNLsVYLDPielhJj/VKTwpRAAkt+AJ7el9rxSE1ur4t6GHOhxZSaQ9kkGnwEj5Jqw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268307; c=relaxed/simple; bh=mjZYWaRkcRdjarRU3mciTYZiQMkNMt8qhA2kEYBByug=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=m+Pgede25emd0dnDB+qEK2C+7Q7FspMBYWJJD9AcnxEAQYVuh5sliBEIIqZJmXBb3zMmwR0b3vvAzpqrGSi4TLBUshO/Zca2IA2wZ9eiFXVEwToIbUWvreemhPZFSebzh7F7AlnuXBZvAKRmL50M+SMnC6e8yoeEtdnJOwjpwWM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com; spf=pass smtp.mailfrom=analog.com; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b=1Z83l+zA; arc=none smtp.client-ip=148.163.135.77 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=analog.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b="1Z83l+zA" Received: from pps.filterd (m0167089.ppops.net [127.0.0.1]) by mx0a-00128a01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 55IG2GcP027608; Wed, 18 Jun 2025 13:38:09 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=analog.com; h=cc :content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=DKIM; bh=yIzda 7vTpc+HHdRa8iOboc63iEN2HWlM5y0ZFmHhelI=; b=1Z83l+zAbId7mLaSjdPVQ YeUaokilvDoN20PzVlcAZqGggmJBlCN7Jedtj6X8cBAu7xnBPf/nVH7eTHWFhFWS MXfB50UimLRnauIel3lH+kyCVxlVT/D38JKusLbsb2mhs/jXDu/TrrpAqY26ZOdH YgRj6NB9Bw+TFiEHcCJnQ0ExewcBi21u7V/FIFU4HsZ/+3jenGvtTWJNYBkd53eV rY77BtiLBcuDBPcSxkTLCLVE0L+WhEKejU/kLQhMZRbanqZY0pYBuUIrby/hzBp4 jcSh1ZH8mdg3s8PrnQl9lIjkYUGn2wHre3d/AzXbkkPdKj40bgVZM+x8kYVmXfb+ Q== Received: from nwd2mta4.analog.com ([137.71.173.58]) by mx0a-00128a01.pphosted.com (PPS) with ESMTPS id 47bfxcwdd1-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 18 Jun 2025 13:38:08 -0400 (EDT) Received: from ASHBMBX8.ad.analog.com (ASHBMBX8.ad.analog.com [10.64.17.5]) by nwd2mta4.analog.com (8.14.7/8.14.7) with ESMTP id 55IHc7R9016190 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Wed, 18 Jun 2025 13:38:07 -0400 Received: from ASHBCASHYB5.ad.analog.com (10.64.17.133) by ASHBMBX8.ad.analog.com (10.64.17.5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 18 Jun 2025 13:38:07 -0400 Received: from ASHBMBX8.ad.analog.com (10.64.17.5) by ASHBCASHYB5.ad.analog.com (10.64.17.133) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 18 Jun 2025 13:38:06 -0400 Received: from zeus.spd.analog.com (10.66.68.11) by ashbmbx8.ad.analog.com (10.64.17.5) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Wed, 18 Jun 2025 13:38:06 -0400 Received: from work.ad.analog.com (HYB-hERzalRezfV.ad.analog.com [10.65.205.9]) by zeus.spd.analog.com (8.15.1/8.15.1) with ESMTP id 55IHbo42007486; Wed, 18 Jun 2025 13:37:53 -0400 From: Marcelo Schmitt To: , , , CC: , , , , , , , , , , , , , Subject: [PATCH v6 08/12] iio: adc: ad4170: Add clock provider support Date: Wed, 18 Jun 2025 14:37:49 -0300 Message-ID: <46c3b9da0270e748db1676f176156093237980e7.1750258776.git.marcelo.schmitt@analog.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ADIRuleOP-NewSCL: Rule Triggered X-Proofpoint-ORIG-GUID: cCnAUvFOtcjnVCAQ4WtswMq30Z20FPS0 X-Proofpoint-GUID: cCnAUvFOtcjnVCAQ4WtswMq30Z20FPS0 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjUwNjE4MDE1MCBTYWx0ZWRfXwHrF68xtBU1g sNNh5yUUGSTtz0rTk9/seIp405drmnOk3jI3spbK4qmABWjgRjNBQLsgpfB5c1xOlWzfeX5snzE 7ha12xaNKEStaHNcShQfBR2LHfglQZVOcfE/kPB/YD0LGp9fbj2QR8iPpw3yhLGtMIF7KNq5fF4 u0dLUD2jDU49i2QKHyCckBF/fuPfy+/Dke/QCGvTTkwkoiBgJI9vT28ovLC7ectQgc4TyqVjn4P lYuX13xqukZgHj7+FywDMaxc74wM0Cj6sJ20V9eNFl/5LgF6owS+BqylYlYHrFLWLsJ7QQEVYHg iAD9Wgr3w+0gEQad+jClTCq44NQr7gyTo/ZfqGnyz9n8NXCs49pnkaTzCP7g3QdBiUKTFrw2wjZ SWA3eZ7n6eKPVuL2dzy70FFR2uYUTEkKJhigFdWAsgvqRQ9xqGSMr1yYjaID2ZREmyn6jYCM X-Authority-Analysis: v=2.4 cv=Jb28rVKV c=1 sm=1 tr=0 ts=6852f980 cx=c_pps a=3WNzaoukacrqR9RwcOSAdA==:117 a=3WNzaoukacrqR9RwcOSAdA==:17 a=IkcTkHD0fZMA:10 a=6IFa9wvqVegA:10 a=gAnH3GRIAAAA:8 a=FbnkXhljGIpD8s1dgEMA:9 a=3ZKOabzyN94A:10 a=QEXdDO2ut3YA:10 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1099,Hydra:6.0.736,FMLib:17.12.80.40 definitions=2025-06-18_05,2025-06-18_03,2025-03-28_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 malwarescore=0 mlxlogscore=999 clxscore=1011 mlxscore=0 priorityscore=1501 lowpriorityscore=0 phishscore=0 adultscore=0 suspectscore=0 spamscore=0 impostorscore=0 bulkscore=0 classifier=spam authscore=0 authtc=n/a authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.19.0-2505280000 definitions=main-2506180150 The AD4170 chip can use an externally supplied clock at the XTAL2 pin, or an external crystal connected to the XTAL1 and XTAL2 pins. Alternatively, the AD4170 can provide its 16 MHz internal clock at the XTAL2 pin. In addition, the chip has a programmable clock divider that allows dividing the external or internal clock frequency, however, control for that is not provided in this patch. Extend the AD4170 driver so it effectively uses the provided external clock, if any, or supplies its own clock as a clock provider. Reviewed-by: Nuno S=C3=A1 Signed-off-by: Marcelo Schmitt --- Change log v5 -> v6 - Now using device_property_present() to check #clock-cells presence. drivers/iio/adc/Kconfig | 1 + drivers/iio/adc/ad4170.c | 147 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 147 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index b12dcc04c894..32e5177ceebe 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -91,6 +91,7 @@ config AD4170 select REGMAP_SPI select IIO_BUFFER select IIO_TRIGGERED_BUFFER + depends on COMMON_CLK help Say yes here to build support for Analog Devices AD4170 SPI analog to digital converters (ADC). diff --git a/drivers/iio/adc/ad4170.c b/drivers/iio/adc/ad4170.c index 2acd4316b079..21921844f5e6 100644 --- a/drivers/iio/adc/ad4170.c +++ b/drivers/iio/adc/ad4170.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include #include @@ -53,6 +55,7 @@ #define AD4170_CONFIG_A_REG 0x00 #define AD4170_DATA_24B_REG 0x1E #define AD4170_PIN_MUXING_REG 0x69 +#define AD4170_CLOCK_CTRL_REG 0x6B #define AD4170_ADC_CTRL_REG 0x71 #define AD4170_CHAN_EN_REG 0x79 #define AD4170_CHAN_SETUP_REG(x) (0x81 + 4 * (x)) @@ -73,6 +76,9 @@ /* AD4170_PIN_MUXING_REG */ #define AD4170_PIN_MUXING_DIG_AUX1_CTRL_MSK GENMASK(5, 4) =20 +/* AD4170_CLOCK_CTRL_REG */ +#define AD4170_CLOCK_CTRL_CLOCKSEL_MSK GENMASK(1, 0) + /* AD4170_ADC_CTRL_REG */ #define AD4170_ADC_CTRL_MULTI_DATA_REG_SEL_MSK BIT(7) #define AD4170_ADC_CTRL_CONT_READ_MSK GENMASK(5, 4) @@ -100,6 +106,12 @@ =20 /* AD4170 register constants */ =20 +/* AD4170_CLOCK_CTRL_REG constants */ +#define AD4170_CLOCK_CTRL_CLOCKSEL_INT 0x0 +#define AD4170_CLOCK_CTRL_CLOCKSEL_INT_OUT 0x1 +#define AD4170_CLOCK_CTRL_CLOCKSEL_EXT 0x2 +#define AD4170_CLOCK_CTRL_CLOCKSEL_EXT_XTAL 0x3 + /* AD4170_CHAN_MAP_REG constants */ #define AD4170_CHAN_MAP_AIN(x) (x) #define AD4170_CHAN_MAP_TEMP_SENSOR 17 @@ -147,6 +159,8 @@ =20 /* Internal and external clock properties */ #define AD4170_INT_CLOCK_16MHZ (16 * HZ_PER_MHZ) +#define AD4170_EXT_CLOCK_MHZ_MIN (1 * HZ_PER_MHZ) +#define AD4170_EXT_CLOCK_MHZ_MAX (17 * HZ_PER_MHZ) =20 #define AD4170_NUM_PGA_OPTIONS 10 =20 @@ -164,6 +178,7 @@ static const unsigned int ad4170_reg_size[] =3D { [AD4170_CONFIG_A_REG] =3D 1, [AD4170_DATA_24B_REG] =3D 3, [AD4170_PIN_MUXING_REG] =3D 2, + [AD4170_CLOCK_CTRL_REG] =3D 2, [AD4170_ADC_CTRL_REG] =3D 2, [AD4170_CHAN_EN_REG] =3D 2, /* @@ -236,6 +251,10 @@ enum ad4170_regulator { AD4170_MAX_SUP, }; =20 +static const char *const ad4170_clk_sel[] =3D { + "ext-clk", "xtal", +}; + enum ad4170_int_pin_sel { AD4170_INT_PIN_SDO, AD4170_INT_PIN_DIG_AUX1, @@ -338,6 +357,8 @@ struct ad4170_state { struct completion completion; unsigned int pins_fn[AD4170_NUM_ANALOG_PINS]; u32 int_pin_sel; + struct clk_hw int_clk_hw; + unsigned int clock_ctrl; /* * DMA (thus cache coherency maintenance) requires the transfer buffers * to live in their own cache lines. @@ -1616,13 +1637,137 @@ static int ad4170_parse_channels(struct iio_dev *i= ndio_dev) return 0; } =20 +static struct ad4170_state *clk_hw_to_ad4170(struct clk_hw *hw) +{ + return container_of(hw, struct ad4170_state, int_clk_hw); +} + +static unsigned long ad4170_sel_clk(struct ad4170_state *st, + unsigned int clk_sel) +{ + st->clock_ctrl &=3D ~AD4170_CLOCK_CTRL_CLOCKSEL_MSK; + st->clock_ctrl |=3D FIELD_PREP(AD4170_CLOCK_CTRL_CLOCKSEL_MSK, clk_sel); + return regmap_write(st->regmap, AD4170_CLOCK_CTRL_REG, st->clock_ctrl); +} + +static unsigned long ad4170_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return AD4170_INT_CLOCK_16MHZ; +} + +static int ad4170_clk_output_is_enabled(struct clk_hw *hw) +{ + struct ad4170_state *st =3D clk_hw_to_ad4170(hw); + u32 clk_sel; + + clk_sel =3D FIELD_GET(AD4170_CLOCK_CTRL_CLOCKSEL_MSK, st->clock_ctrl); + return clk_sel =3D=3D AD4170_CLOCK_CTRL_CLOCKSEL_INT_OUT; +} + +static int ad4170_clk_output_prepare(struct clk_hw *hw) +{ + struct ad4170_state *st =3D clk_hw_to_ad4170(hw); + + return ad4170_sel_clk(st, AD4170_CLOCK_CTRL_CLOCKSEL_INT_OUT); +} + +static void ad4170_clk_output_unprepare(struct clk_hw *hw) +{ + struct ad4170_state *st =3D clk_hw_to_ad4170(hw); + + ad4170_sel_clk(st, AD4170_CLOCK_CTRL_CLOCKSEL_INT); +} + +static const struct clk_ops ad4170_int_clk_ops =3D { + .recalc_rate =3D ad4170_clk_recalc_rate, + .is_enabled =3D ad4170_clk_output_is_enabled, + .prepare =3D ad4170_clk_output_prepare, + .unprepare =3D ad4170_clk_output_unprepare, +}; + +static int ad4170_register_clk_provider(struct iio_dev *indio_dev) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + struct device *dev =3D indio_dev->dev.parent; + struct clk_init_data init =3D {}; + int ret; + + if (device_property_read_string(dev, "clock-output-names", &init.name)) { + init.name =3D devm_kasprintf(dev, GFP_KERNEL, "%pfw", + dev_fwnode(dev)); + if (!init.name) + return -ENOMEM; + } + + init.ops =3D &ad4170_int_clk_ops; + + st->int_clk_hw.init =3D &init; + ret =3D devm_clk_hw_register(dev, &st->int_clk_hw); + if (ret) + return ret; + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + &st->int_clk_hw); +} + +static int ad4170_clock_select(struct iio_dev *indio_dev) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + struct device *dev =3D &st->spi->dev; + struct clk *ext_clk; + int ret; + + ext_clk =3D devm_clk_get_optional_enabled(dev, NULL); + if (IS_ERR(ext_clk)) + return dev_err_probe(dev, PTR_ERR(ext_clk), + "Failed to get external clock\n"); + + if (!ext_clk) { + /* Use internal clock reference */ + st->mclk_hz =3D AD4170_INT_CLOCK_16MHZ; + st->clock_ctrl |=3D FIELD_PREP(AD4170_CLOCK_CTRL_CLOCKSEL_MSK, + AD4170_CLOCK_CTRL_CLOCKSEL_INT_OUT); + + if (!device_property_present(&st->spi->dev, "#clock-cells")) + return 0; + + return ad4170_register_clk_provider(indio_dev); + } + + /* Read optional clock-names prop to specify the external clock type */ + ret =3D device_property_match_property_string(dev, "clock-names", + ad4170_clk_sel, + ARRAY_SIZE(ad4170_clk_sel)); + + ret =3D ret < 0 ? 0 : ret; /* Default to external clock if no clock-names= */ + st->clock_ctrl |=3D FIELD_PREP(AD4170_CLOCK_CTRL_CLOCKSEL_MSK, + AD4170_CLOCK_CTRL_CLOCKSEL_EXT + ret); + + st->mclk_hz =3D clk_get_rate(ext_clk); + if (st->mclk_hz < AD4170_EXT_CLOCK_MHZ_MIN || + st->mclk_hz > AD4170_EXT_CLOCK_MHZ_MAX) { + return dev_err_probe(dev, -EINVAL, + "Invalid external clock frequency %u\n", + st->mclk_hz); + } + + return 0; +} + static int ad4170_parse_firmware(struct iio_dev *indio_dev) { struct ad4170_state *st =3D iio_priv(indio_dev); struct device *dev =3D &st->spi->dev; int reg_data, ret; =20 - st->mclk_hz =3D AD4170_INT_CLOCK_16MHZ; + ret =3D ad4170_clock_select(indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to setup device clock\n"); + + ret =3D regmap_write(st->regmap, AD4170_CLOCK_CTRL_REG, st->clock_ctrl); + if (ret) + return ret; =20 /* On power on, device defaults to using SDO pin for data ready signal */ st->int_pin_sel =3D AD4170_INT_PIN_SDO; --=20 2.47.2 From nobody Thu Oct 9 08:43:03 2025 Received: from mx0a-00128a01.pphosted.com (mx0a-00128a01.pphosted.com [148.163.135.77]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 256441E8323; Wed, 18 Jun 2025 17:38:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.163.135.77 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268332; cv=none; b=JNLlJKznVG2rsUL4E6LakvgjD5Qvh4LWmnpxK5cmoF+djRISzz+QKq0W9eNETiskUEfz7V0CG38yn8HH7KgX9JAI3B7/TMG5br1xDWVSwqfgB7Q25lG7tSA3CldnBHUwowNy1uEwI0PBK2l5CGCzXCfwQC3rwp/Lgi3J3ydFH44= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268332; c=relaxed/simple; bh=dau+XvDHWs1GEJ9J2OFwDLYZxnqpl1lDT9asmdflVR0=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=VIZDT1jCvtKwoH/HgBrsMu1IANrWwWi+BwavwYC/HbnCrwJmHwIj1X6LO2Xs8Jd2MSLeCsqxNOjEhh45fHWZyT67DDr5XHyTfO07/O/4kR6qTG31zhnaX5xdslXXiuUTmPxlOtWV1tABZ07VgFxKSynZmD4VksUj4bOSfCd/Lqc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com; spf=pass smtp.mailfrom=analog.com; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b=VopXhwip; arc=none smtp.client-ip=148.163.135.77 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=analog.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b="VopXhwip" Received: from pps.filterd (m0167089.ppops.net [127.0.0.1]) by mx0a-00128a01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 55IFbKZE027666; Wed, 18 Jun 2025 13:38:32 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=analog.com; h=cc :content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=DKIM; bh=rLwqf lLu6hpnIfd6CyHnH/lQ2eF/fiocikrUPNyd9XI=; b=VopXhwipuNJOF9xArjLHz ksrJiIZCwGj4kzGAbcBurpJRIiP8Jj5ivJZifJ0pYQQdJhegvhm1nBjTTSYlxccE mqQXdQI437rR0RjpllRYT2jL6EJqW6cPuf9oU84g3h6pjFnyL5VOk9TEjfn0AbGT lLbvBDDnUM/1ocj4w+DSpXh602dba1CzWVvNF6xBiql2Xmq7/nBlRIG86tXVs5UE ZOoQ2D1PpJPfJFoee3ABnzpZGjxyssHVa+ZLTsl3L+R0f5ZjrR0NKwCUW2+yHRAL aaH5RhKDK0lwOXLzMfR8RuRJB3ZKiRBLcrtUqaX3ruRi3CcaWf/bAT4W0ZBTvDn+ g== Received: from nwd2mta3.analog.com ([137.71.173.56]) by mx0a-00128a01.pphosted.com (PPS) with ESMTPS id 47bfxcwdf0-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 18 Jun 2025 13:38:31 -0400 (EDT) Received: from ASHBMBX9.ad.analog.com (ASHBMBX9.ad.analog.com [10.64.17.10]) by nwd2mta3.analog.com (8.14.7/8.14.7) with ESMTP id 55IHcU0q031981 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Wed, 18 Jun 2025 13:38:30 -0400 Received: from ASHBCASHYB4.ad.analog.com (10.64.17.132) by ASHBMBX9.ad.analog.com (10.64.17.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 18 Jun 2025 13:38:30 -0400 Received: from ASHBMBX8.ad.analog.com (10.64.17.5) by ASHBCASHYB4.ad.analog.com (10.64.17.132) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 18 Jun 2025 13:38:30 -0400 Received: from zeus.spd.analog.com (10.66.68.11) by ashbmbx8.ad.analog.com (10.64.17.5) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Wed, 18 Jun 2025 13:38:30 -0400 Received: from work.ad.analog.com (HYB-hERzalRezfV.ad.analog.com [10.65.205.9]) by zeus.spd.analog.com (8.15.1/8.15.1) with ESMTP id 55IHcDFE007639; Wed, 18 Jun 2025 13:38:16 -0400 From: Marcelo Schmitt To: , , , CC: , , , , , , , , , , , , , , Bartosz Golaszewski Subject: [PATCH v6 09/12] iio: adc: ad4170: Add GPIO controller support Date: Wed, 18 Jun 2025 14:38:12 -0300 Message-ID: <388c56894287671b4a23333584a0841480feb1ae.1750258776.git.marcelo.schmitt@analog.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ADIRuleOP-NewSCL: Rule Triggered X-Proofpoint-ORIG-GUID: lXp9E7fTp4DkSp_sEi7XaG7mXWP2bseH X-Proofpoint-GUID: lXp9E7fTp4DkSp_sEi7XaG7mXWP2bseH X-Proofpoint-Spam-Details-Enc: AW1haW4tMjUwNjE4MDE1MCBTYWx0ZWRfX8OFQiXyYIcqc GoLBroRCVd2ZqTFHSWWJqH573EfVV9R8L7/jc2jUK54fNrFwAmX+RanP9mluBVbLE2HaBo73v7b 78skf2s8FtaU3NyzM7pMsYyz0AQ/vRHNWGw8zc3MOKIyO6xllMFZ8BgmMDIoWifvTDoBKmzxGUB Zw/60VmK2JwEJvpEAaQXR8q9z7hhXFEUdURBQflqK9ez3raB06fgOvbOiRiAlOQJR9F26cfRAfg SovuutH0vYnAw7U+hMM61PfEj6+RyzpoAqyeqwFzCg+GuLvoKUQfJ58l3hDDhCG2CZskMkVLURP mlxa0Qw+FIvqQSrxXwIANwStoOPhPKOYc/brDR/05UxH5UyaqmhY0kSSXmgDnGxFZS/bWkA+/7T D3kD6rCjs5HnbUy+vQxE4d6RcvstNfF4o/jdZi5bdA6pHCjRjjh7yd4mHUzVt90nDzsjdyTf X-Authority-Analysis: v=2.4 cv=Jb28rVKV c=1 sm=1 tr=0 ts=6852f997 cx=c_pps a=PpDZqlmH/M8setHirZLBMw==:117 a=PpDZqlmH/M8setHirZLBMw==:17 a=6IFa9wvqVegA:10 a=KKAkSRfTAAAA:8 a=gAnH3GRIAAAA:8 a=kZiCEncQAPmOuCAweDIA:9 a=cvBusfyB2V15izCimMoJ:22 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1099,Hydra:6.0.736,FMLib:17.12.80.40 definitions=2025-06-18_05,2025-06-18_03,2025-03-28_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 malwarescore=0 mlxlogscore=999 clxscore=1015 mlxscore=0 priorityscore=1501 lowpriorityscore=0 phishscore=0 adultscore=0 suspectscore=0 spamscore=0 impostorscore=0 bulkscore=0 classifier=spam authscore=0 authtc=n/a authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.19.0-2505280000 definitions=main-2506180150 Content-Type: text/plain; charset="utf-8" The AD4170 has four multifunctional pins that can be used as GPIOs. The GPIO functionality can be accessed when the AD4170 chip is not busy performing continuous data capture or handling any other register read/write request. Also, the AD4170 does not provide any interrupt based on GPIO pin states so AD4170 GPIOs can't be used as interrupt sources. Implement gpio_chip callbacks to make AD4170 GPIO pins controllable through the gpiochip interface. Acked-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Signed-off-by: Marcelo Schmitt --- Change log v5 -> v6 - picked up Linus' review tag. drivers/iio/adc/Kconfig | 1 + drivers/iio/adc/ad4170.c | 224 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 224 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 32e5177ceebe..0c16b2d5947d 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -92,6 +92,7 @@ config AD4170 select IIO_BUFFER select IIO_TRIGGERED_BUFFER depends on COMMON_CLK + depends on GPIOLIB help Say yes here to build support for Analog Devices AD4170 SPI analog to digital converters (ADC). diff --git a/drivers/iio/adc/ad4170.c b/drivers/iio/adc/ad4170.c index 21921844f5e6..11d04c50e613 100644 --- a/drivers/iio/adc/ad4170.c +++ b/drivers/iio/adc/ad4170.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -66,6 +67,9 @@ #define AD4170_FILTER_FS_REG(x) (0xC7 + 14 * (x)) #define AD4170_OFFSET_REG(x) (0xCA + 14 * (x)) #define AD4170_GAIN_REG(x) (0xCD + 14 * (x)) +#define AD4170_GPIO_MODE_REG 0x191 +#define AD4170_GPIO_OUTPUT_REG 0x193 +#define AD4170_GPIO_INPUT_REG 0x195 #define AD4170_ADC_CTRL_CONT_READ_EXIT_REG 0x200 /* virtual reg */ =20 #define AD4170_REG_READ_MASK BIT(14) @@ -104,6 +108,12 @@ /* AD4170_FILTER_REG */ #define AD4170_FILTER_FILTER_TYPE_MSK GENMASK(3, 0) =20 +/* AD4170_GPIO_MODE_REG */ +#define AD4170_GPIO_MODE_GPIO0_MSK GENMASK(1, 0) +#define AD4170_GPIO_MODE_GPIO1_MSK GENMASK(3, 2) +#define AD4170_GPIO_MODE_GPIO2_MSK GENMASK(5, 4) +#define AD4170_GPIO_MODE_GPIO3_MSK GENMASK(7, 6) + /* AD4170 register constants */ =20 /* AD4170_CLOCK_CTRL_REG constants */ @@ -144,9 +154,14 @@ #define AD4170_FILTER_FILTER_TYPE_SINC5 0x4 #define AD4170_FILTER_FILTER_TYPE_SINC3 0x6 =20 +/* AD4170_GPIO_MODE_REG constants */ +#define AD4170_GPIO_MODE_GPIO_INPUT 1 +#define AD4170_GPIO_MODE_GPIO_OUTPUT 2 + /* Device properties and auxiliary constants */ =20 #define AD4170_NUM_ANALOG_PINS 9 +#define AD4170_NUM_GPIO_PINS 4 #define AD4170_MAX_CHANNELS 16 #define AD4170_MAX_ANALOG_PINS 8 #define AD4170_MAX_SETUPS 8 @@ -174,6 +189,9 @@ =20 #define AD4170_ADC_CTRL_CONT_READ_EXIT 0xA5 =20 +/* GPIO pin functions */ +#define AD4170_GPIO_UNASSIGNED 0x00 + static const unsigned int ad4170_reg_size[] =3D { [AD4170_CONFIG_A_REG] =3D 1, [AD4170_DATA_24B_REG] =3D 3, @@ -211,6 +229,9 @@ static const unsigned int ad4170_reg_size[] =3D { [AD4170_OFFSET_REG(5) ... AD4170_GAIN_REG(5)] =3D 3, [AD4170_OFFSET_REG(6) ... AD4170_GAIN_REG(6)] =3D 3, [AD4170_OFFSET_REG(7) ... AD4170_GAIN_REG(7)] =3D 3, + [AD4170_GPIO_MODE_REG] =3D 2, + [AD4170_GPIO_OUTPUT_REG] =3D 2, + [AD4170_GPIO_INPUT_REG] =3D 2, [AD4170_ADC_CTRL_CONT_READ_EXIT_REG] =3D 0, }; =20 @@ -358,7 +379,9 @@ struct ad4170_state { unsigned int pins_fn[AD4170_NUM_ANALOG_PINS]; u32 int_pin_sel; struct clk_hw int_clk_hw; + struct gpio_chip gpiochip; unsigned int clock_ctrl; + int gpio_fn[AD4170_NUM_GPIO_PINS]; /* * DMA (thus cache coherency maintenance) requires the transfer buffers * to live in their own cache lines. @@ -1468,6 +1491,194 @@ static int ad4170_soft_reset(struct ad4170_state *s= t) return 0; } =20 +static int ad4170_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct iio_dev *indio_dev =3D gpiochip_get_data(gc); + struct ad4170_state *st =3D iio_priv(indio_dev); + unsigned int val; + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret =3D regmap_read(st->regmap, AD4170_GPIO_MODE_REG, &val); + if (ret) + goto err_release; + + /* + * If the GPIO is configured as an input, read the current value from + * AD4170_GPIO_INPUT_REG. Otherwise, read the input value from + * AD4170_GPIO_OUTPUT_REG. + */ + if (val & BIT(offset * 2)) + ret =3D regmap_read(st->regmap, AD4170_GPIO_INPUT_REG, &val); + else + ret =3D regmap_read(st->regmap, AD4170_GPIO_OUTPUT_REG, &val); + if (ret) + goto err_release; + + ret =3D !!(val & BIT(offset)); +err_release: + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad4170_gpio_set(struct gpio_chip *gc, unsigned int offset, int = value) +{ + struct iio_dev *indio_dev =3D gpiochip_get_data(gc); + struct ad4170_state *st =3D iio_priv(indio_dev); + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret =3D regmap_assign_bits(st->regmap, AD4170_GPIO_OUTPUT_REG, + BIT(offset), !!value); + + iio_device_release_direct(indio_dev); + return ret; +} + +static int ad4170_gpio_get_direction(struct gpio_chip *gc, unsigned int of= fset) +{ + struct iio_dev *indio_dev =3D gpiochip_get_data(gc); + struct ad4170_state *st =3D iio_priv(indio_dev); + unsigned int val; + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret =3D regmap_read(st->regmap, AD4170_GPIO_MODE_REG, &val); + if (ret) + goto err_release; + + if (val & BIT(offset * 2 + 1)) + ret =3D GPIO_LINE_DIRECTION_OUT; + else + ret =3D GPIO_LINE_DIRECTION_IN; + +err_release: + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad4170_gpio_direction_input(struct gpio_chip *gc, unsigned int = offset) +{ + struct iio_dev *indio_dev =3D gpiochip_get_data(gc); + struct ad4170_state *st =3D iio_priv(indio_dev); + unsigned long gpio_mask; + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + switch (offset) { + case 0: + gpio_mask =3D AD4170_GPIO_MODE_GPIO0_MSK; + break; + case 1: + gpio_mask =3D AD4170_GPIO_MODE_GPIO1_MSK; + break; + case 2: + gpio_mask =3D AD4170_GPIO_MODE_GPIO2_MSK; + break; + case 3: + gpio_mask =3D AD4170_GPIO_MODE_GPIO3_MSK; + break; + default: + ret =3D -EINVAL; + goto err_release; + } + ret =3D regmap_update_bits(st->regmap, AD4170_GPIO_MODE_REG, gpio_mask, + AD4170_GPIO_MODE_GPIO_INPUT << (2 * offset)); + +err_release: + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad4170_gpio_direction_output(struct gpio_chip *gc, + unsigned int offset, int value) +{ + struct iio_dev *indio_dev =3D gpiochip_get_data(gc); + struct ad4170_state *st =3D iio_priv(indio_dev); + unsigned long gpio_mask; + int ret; + + ret =3D ad4170_gpio_set(gc, offset, value); + if (ret) + return ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + switch (offset) { + case 0: + gpio_mask =3D AD4170_GPIO_MODE_GPIO0_MSK; + break; + case 1: + gpio_mask =3D AD4170_GPIO_MODE_GPIO1_MSK; + break; + case 2: + gpio_mask =3D AD4170_GPIO_MODE_GPIO2_MSK; + break; + case 3: + gpio_mask =3D AD4170_GPIO_MODE_GPIO3_MSK; + break; + default: + ret =3D -EINVAL; + goto err_release; + } + ret =3D regmap_update_bits(st->regmap, AD4170_GPIO_MODE_REG, gpio_mask, + AD4170_GPIO_MODE_GPIO_OUTPUT << (2 * offset)); + +err_release: + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad4170_gpio_init_valid_mask(struct gpio_chip *gc, + unsigned long *valid_mask, + unsigned int ngpios) +{ + struct ad4170_state *st =3D gpiochip_get_data(gc); + unsigned int i; + + /* Only expose GPIOs that were not assigned any other function. */ + for (i =3D 0; i < ngpios; i++) { + bool valid =3D st->gpio_fn[i] =3D=3D AD4170_GPIO_UNASSIGNED; + + __assign_bit(i, valid_mask, valid); + } + + return 0; +} + +static int ad4170_gpio_init(struct iio_dev *indio_dev) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + + st->gpiochip.label =3D "ad4170_gpios"; + st->gpiochip.base =3D -1; + st->gpiochip.ngpio =3D AD4170_NUM_GPIO_PINS; + st->gpiochip.parent =3D &st->spi->dev; + st->gpiochip.can_sleep =3D true; + st->gpiochip.init_valid_mask =3D ad4170_gpio_init_valid_mask; + st->gpiochip.get_direction =3D ad4170_gpio_get_direction; + st->gpiochip.direction_input =3D ad4170_gpio_direction_input; + st->gpiochip.direction_output =3D ad4170_gpio_direction_output; + st->gpiochip.get =3D ad4170_gpio_get; + st->gpiochip.set_rv =3D ad4170_gpio_set; + st->gpiochip.owner =3D THIS_MODULE; + + return devm_gpiochip_add_data(&st->spi->dev, &st->gpiochip, indio_dev); +} + static int ad4170_parse_reference(struct ad4170_state *st, struct fwnode_handle *child, struct ad4170_setup *setup) @@ -1787,7 +1998,18 @@ static int ad4170_parse_firmware(struct iio_dev *ind= io_dev) if (ret) return ret; =20 - return ad4170_parse_channels(indio_dev); + ret =3D ad4170_parse_channels(indio_dev); + if (ret) + return ret; + + /* Only create a GPIO chip if flagged for it */ + if (device_property_read_bool(&st->spi->dev, "gpio-controller")) { + ret =3D ad4170_gpio_init(indio_dev); + if (ret) + return ret; + } + + return 0; } =20 static int ad4170_initial_config(struct iio_dev *indio_dev) --=20 2.47.2 From nobody Thu Oct 9 08:43:03 2025 Received: from mx0a-00128a01.pphosted.com (mx0a-00128a01.pphosted.com [148.163.135.77]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6C7902F3C15; Wed, 18 Jun 2025 17:39:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.163.135.77 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268353; cv=none; b=Da5zK9fpFM0LIkhnPB/ru5KtZelisqohQxK3lr+D6z01kfW2Mt+f5p+z73pKeLulSyxJSNOaZSJRyCyxUQMsgYxnjmJSTW4AjfV3y07eyCXQygAUZheNMKpSvMFYOvEHhy9wyIz/8RT3DPXSAsDugx52rvcv78sJysWZsSYR+vo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268353; c=relaxed/simple; bh=08bWYMz32yLZVNjx4zvxQMWn/tjywoOzh+reY8l4ymg=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=mPm1jRUGS6psSOyqEQjIuQvKxtSjyD9xcx3PHniFhQyF666BIoOLdsfRR6cnMKcpyp2eqNer74OD53jBuWZp43fu1Go0pnLVUxRWYxttTqigetUuzZkj9PvOuXNC7kUsmvh/A4eehHy+eX7GlB1ph6tPCbVmxIE40peHJMwyWfk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com; spf=pass smtp.mailfrom=analog.com; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b=ePG9EX9M; arc=none smtp.client-ip=148.163.135.77 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=analog.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b="ePG9EX9M" Received: from pps.filterd (m0167088.ppops.net [127.0.0.1]) by mx0a-00128a01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 55IGrPIp031572; Wed, 18 Jun 2025 13:38:53 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=analog.com; h=cc :content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=DKIM; bh=+KTUn Dpn7mA7MtRdW5o3dNVpw+XFGREoQLUcho9hDiU=; b=ePG9EX9Mx0i4kZRsg/4Tl kRtjUEH6McCTTbuV5uXTt/4xslxX5ujg0uEcVu+M8a9ZP/4u1+7J3Y5LGyXuObF0 gHVLxaWGXjmA9G3F7d43z1CTKW2YaikDVfE+/zBAnh7HA9tQvetx5SfX4eDF8eC8 Ct+4nVziFCYEpCK8a7vyqPPSGLySHDgFKRrvXMiPxmJ2SpkToC8xWmhaMvAhlxPP Sz/dT3YuK7QfbwON0+/c0jmzb5WLQVAlY4QvqPd4aVGmeLSAI32A9jn2yX0xsOzO 82dyideWmdFNh+tpxvWyA0iEJ4E3cKudTxxy3V+CGGGhRr/p2525xkBxrubj5FYw g== Received: from nwd2mta3.analog.com ([137.71.173.56]) by mx0a-00128a01.pphosted.com (PPS) with ESMTPS id 47bfshddm0-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 18 Jun 2025 13:38:52 -0400 (EDT) Received: from ASHBMBX9.ad.analog.com (ASHBMBX9.ad.analog.com [10.64.17.10]) by nwd2mta3.analog.com (8.14.7/8.14.7) with ESMTP id 55IHcpuw032016 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Wed, 18 Jun 2025 13:38:51 -0400 Received: from ASHBMBX8.ad.analog.com (10.64.17.5) by ASHBMBX9.ad.analog.com (10.64.17.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 18 Jun 2025 13:38:51 -0400 Received: from zeus.spd.analog.com (10.66.68.11) by ashbmbx8.ad.analog.com (10.64.17.5) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Wed, 18 Jun 2025 13:38:51 -0400 Received: from work.ad.analog.com (HYB-hERzalRezfV.ad.analog.com [10.65.205.9]) by zeus.spd.analog.com (8.15.1/8.15.1) with ESMTP id 55IHcXgL007643; Wed, 18 Jun 2025 13:38:35 -0400 From: Marcelo Schmitt To: , , , CC: , , , , , , , , , , , , , Subject: [PATCH v6 10/12] iio: adc: ad4170: Add support for internal temperature sensor Date: Wed, 18 Jun 2025 14:38:31 -0300 Message-ID: <3de8cbbe8b7f5b3b21436ba71e782997b0250463.1750258776.git.marcelo.schmitt@analog.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ADIRuleOP-NewSCL: Rule Triggered X-Proofpoint-ORIG-GUID: FPP4ZCP5WM3FktPGK7lILhspIuXR8PXH X-Authority-Analysis: v=2.4 cv=SKhCVPvH c=1 sm=1 tr=0 ts=6852f9ac cx=c_pps a=PpDZqlmH/M8setHirZLBMw==:117 a=PpDZqlmH/M8setHirZLBMw==:17 a=IkcTkHD0fZMA:10 a=6IFa9wvqVegA:10 a=gAnH3GRIAAAA:8 a=o3DtMFfdRpqB0q9FUmIA:9 a=3ZKOabzyN94A:10 a=QEXdDO2ut3YA:10 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjUwNjE4MDE1MCBTYWx0ZWRfXy6SDf0E/NV5n +lYm3MZo3BTnqYOn3nWKLgW5OywJ6RgB2S7HjZauSpXnyZvrhQxFPOo2YmWN6D8jwBuTIhq1kl3 UPUIYGXcjP7NJ+KdiriB2NE4bOLcIcibXtsq6HlxSn6rLVqIYkXBG67oWj5m5bereTzds9283HW AMZ0HGTNkIMIzhSet7GFg5YgKsdH3QoK26s5AFM75kX4WAUzrBdgUl+jhSCHPJ0y3q6JrnKVqe0 gf+xTSK81UKQXiAFdfdgR0/65lGkKiQdTwtjHXGyNQNlYJYtI5xhEmLfkUB4UNWJICijB05RZ1C WJ8pa/BB85R/mHSfRJNriOO4fWBt27lOb87/jcdM0cmMuQDogPgipIrcpzTc4B4+0MIU1F6yWZB beJ0Unnp65TyUgTNuf8oAOmARu8l0ymMbUz6duB9tf+VnBS2marCxLM3/FeQ2ulcz9WYCQ6z X-Proofpoint-GUID: FPP4ZCP5WM3FktPGK7lILhspIuXR8PXH X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1099,Hydra:6.0.736,FMLib:17.12.80.40 definitions=2025-06-18_05,2025-06-18_03,2025-03-28_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 impostorscore=0 bulkscore=0 adultscore=0 lowpriorityscore=0 spamscore=0 suspectscore=0 mlxscore=0 malwarescore=0 mlxlogscore=999 clxscore=1015 phishscore=0 priorityscore=1501 classifier=spam authscore=0 authtc=n/a authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.19.0-2505280000 definitions=main-2506180150 The AD4170 has an internal temperature sensor that can be read using the ADC. Whenever possible, configure an IIO channel to provide the chip's temperature. Reviewed-by: Nuno S=C3=A1 Signed-off-by: Marcelo Schmitt --- No changes since v3. drivers/iio/adc/ad4170.c | 72 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/ad4170.c b/drivers/iio/adc/ad4170.c index 11d04c50e613..3ee66ac651f1 100644 --- a/drivers/iio/adc/ad4170.c +++ b/drivers/iio/adc/ad4170.c @@ -875,6 +875,27 @@ static const struct iio_chan_spec ad4170_channel_templ= ate =3D { }, }; =20 +static const struct iio_chan_spec ad4170_temp_channel_template =3D { + .type =3D IIO_TEMP, + .indexed =3D 0, + .channel =3D 17, + .channel2 =3D 17, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate_available =3D BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_type =3D { + .sign =3D 's', + .realbits =3D 24, + .storagebits =3D 32, + .shift =3D 8, + .endianness =3D IIO_BE, + }, +}; + /* * Receives the number of a multiplexed AD4170 input (ain_n), and stores t= he * voltage (in =C2=B5V) of the specified input into ain_voltage. If the in= put number @@ -1177,9 +1198,27 @@ static int ad4170_read_raw(struct iio_dev *indio_dev, return ret; case IIO_CHAN_INFO_SCALE: pga =3D FIELD_GET(AD4170_AFE_PGA_GAIN_MSK, setup->afe); - *val =3D chan_info->scale_tbl[pga][0]; - *val2 =3D chan_info->scale_tbl[pga][1]; - return IIO_VAL_INT_PLUS_NANO; + switch (chan->type) { + case IIO_VOLTAGE: + *val =3D chan_info->scale_tbl[pga][0]; + *val2 =3D chan_info->scale_tbl[pga][1]; + return IIO_VAL_INT_PLUS_NANO; + + case IIO_TEMP: + /* + * The scale_tbl converts output codes to mV units so + * multiply by MILLI to make the factor convert to =C2=B5V. + * Then, apply the temperature sensor change sensitivity + * of 477 =CE=BCV/K. Finally, multiply the result by MILLI + * again to comply with milli degrees Celsius IIO ABI. + */ + *val =3D 0; + *val2 =3D DIV_ROUND_CLOSEST(chan_info->scale_tbl[pga][1] * MILLI, 477) * + MILLI; + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } case IIO_CHAN_INFO_OFFSET: pga =3D FIELD_GET(AD4170_AFE_PGA_GAIN_MSK, setup->afe); *val =3D chan_info->offset_tbl[pga]; @@ -1835,6 +1874,9 @@ static int ad4170_parse_channels(struct iio_dev *indi= o_dev) if (num_channels > AD4170_MAX_CHANNELS) return dev_err_probe(dev, -EINVAL, "Too many channels\n"); =20 + /* Add one for temperature */ + num_channels =3D min(num_channels + 1, AD4170_MAX_CHANNELS); + chan_num =3D 0; device_for_each_child_node_scoped(dev, child) { ret =3D ad4170_parse_channel_node(indio_dev, child, chan_num++); @@ -1842,6 +1884,30 @@ static int ad4170_parse_channels(struct iio_dev *ind= io_dev) return ret; } =20 + /* + * Add internal temperature sensor channel if the maximum number of + * channels has not been reached. + */ + if (num_channels < AD4170_MAX_CHANNELS) { + struct ad4170_setup *setup =3D &st->chan_infos[chan_num].setup; + + st->chans[chan_num] =3D ad4170_temp_channel_template; + st->chans[chan_num].address =3D chan_num; + st->chans[chan_num].scan_index =3D chan_num; + + st->chan_infos[chan_num].setup_num =3D AD4170_INVALID_SETUP; + st->chan_infos[chan_num].initialized =3D true; + + setup->afe |=3D FIELD_PREP(AD4170_AFE_REF_SELECT_MSK, + AD4170_REF_AVDD); + + ret =3D ad4170_get_input_range(st, &st->chans[chan_num], chan_num, + AD4170_REF_AVDD); + if (ret < 0) + return dev_err_probe(dev, ret, "Invalid input config\n"); + + st->chan_infos[chan_num].input_range_uv =3D ret; + } indio_dev->num_channels =3D num_channels; indio_dev->channels =3D st->chans; =20 --=20 2.47.2 From nobody Thu Oct 9 08:43:03 2025 Received: from mx0a-00128a01.pphosted.com (mx0a-00128a01.pphosted.com [148.163.135.77]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1A79F2F3C15; Wed, 18 Jun 2025 17:39:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.163.135.77 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268373; cv=none; b=AmNqLpH5MiRjo6G9tiykUChQFfx4EFFF/zPTNesbcfhb+UxQI0NPFXuLUIMwChA1x4fnke8gH6VyA+Bv87BOe+Mer9O5vARacn5UDURBb08+K21bqROC2N7s5et4MCQSKanZiQ2fNkVmDmtHVKn6kMqoCYzazF13SA+a2A3uzd4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268373; c=relaxed/simple; bh=eaNAzW1tit1cogTrJuHp+4zHP6mkHwWwnlo/iryUimM=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=pl9ZnTgn9ZakXPvboZ4tsNdJCvLj6La6hYkYCNRPWTNwjlo5tX3NH8tGCcujez0y986owjwWqTqMataaDhRmVE5x2P2gGcoBi2Z2eR8c1I2uAfyEdQCrgk+kqXY5ER5rUr0ATAUxP62erDafROy4j4aO53Bz+L5upsh3GAcCai4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com; spf=pass smtp.mailfrom=analog.com; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b=EkMhTe4x; arc=none smtp.client-ip=148.163.135.77 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=analog.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b="EkMhTe4x" Received: from pps.filterd (m0167088.ppops.net [127.0.0.1]) by mx0a-00128a01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 55IGLEwO031898; Wed, 18 Jun 2025 13:39:14 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=analog.com; h=cc :content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=DKIM; bh=UNDuR xMSroemxggprMyKdB5z0w2cPAkYxjTkLM6fWv0=; b=EkMhTe4xcsRosQIz+Rn+z 7lIhvrMhpfwEN3Tji42a+Bb+OigbhjPnIdkFG6HxY3BoLN+KH6jkGmdLxsK2M6mu ZLz1j6xtxHWuhxvxMh6V+0/IImCP4cU84U7YXZ3BtHP2zUSweiFLOZNc//eAbt/r Wniy1EfVmrVjDQ8LfJPayUiVyuORGF97GnJyzZ8g4kVNgcqNSnilyn0pWpd+vK/u JIr1SK5rXVGhPo0DhRVeSID2bQpGNgbAsI82wCixH9CcZgmONqxTcgSWtbhbtvI6 vXYDwX7OKjO1SSPcXNN7pEXC+tvhOq25Yn4xRAomqxKkTKNuutKGtHoIEU19Gij5 Q== Received: from nwd2mta3.analog.com ([137.71.173.56]) by mx0a-00128a01.pphosted.com (PPS) with ESMTPS id 47bfshddnj-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 18 Jun 2025 13:39:13 -0400 (EDT) Received: from ASHBMBX9.ad.analog.com (ASHBMBX9.ad.analog.com [10.64.17.10]) by nwd2mta3.analog.com (8.14.7/8.14.7) with ESMTP id 55IHdC2E032051 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Wed, 18 Jun 2025 13:39:12 -0400 Received: from ASHBMBX8.ad.analog.com (10.64.17.5) by ASHBMBX9.ad.analog.com (10.64.17.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 18 Jun 2025 13:39:12 -0400 Received: from zeus.spd.analog.com (10.66.68.11) by ashbmbx8.ad.analog.com (10.64.17.5) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Wed, 18 Jun 2025 13:39:12 -0400 Received: from work.ad.analog.com (HYB-hERzalRezfV.ad.analog.com [10.65.205.9]) by zeus.spd.analog.com (8.15.1/8.15.1) with ESMTP id 55IHcq3C007648; Wed, 18 Jun 2025 13:38:55 -0400 From: Marcelo Schmitt To: , , , CC: , , , , , , , , , , , , , Subject: [PATCH v6 11/12] iio: adc: ad4170: Add support for weigh scale and RTD sensors Date: Wed, 18 Jun 2025 14:38:51 -0300 Message-ID: <724d1749d3c90841e85dbf8409821e77664ca118.1750258776.git.marcelo.schmitt@analog.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ADIRuleOP-NewSCL: Rule Triggered X-Proofpoint-ORIG-GUID: E1K1T4Oso98r-bxhxHKmCfBdocReWXpj X-Authority-Analysis: v=2.4 cv=SKhCVPvH c=1 sm=1 tr=0 ts=6852f9c2 cx=c_pps a=PpDZqlmH/M8setHirZLBMw==:117 a=PpDZqlmH/M8setHirZLBMw==:17 a=6IFa9wvqVegA:10 a=gAnH3GRIAAAA:8 a=b54mGPezs8RaO1Bbk_YA:9 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjUwNjE4MDE1MCBTYWx0ZWRfX0W9sPpBvfX/I MB/nPt8UO27TBRP3acTtMDdHqmiV6kJ6ZvpoC+jVacwHG+WRUECWD4Yt+PlGdniC0IPc15TnqZg 939yjtZziaUELrmkVV7DKTdSR2YIqhZaRulW8AIraT4Ugk7Tod/YjR7+W26IJF9jXvehQxfdrwe oHglKCObi0jkh6mmSZSN08FXJddAcGJgzUk0sw007CBRIlxxkAGObkSJnKq8LT+4oOdZC2sRuta 9w3N+rogVCk6Wz1F2y4zu1lk3QGla8EQasmzxZM4wUC7c2RzcAaxw6kYQYMCRwHnHWCJNHhECbN Ss4StGaeY7bpxZmEIsFGiM4LWku7P6YZbRNH3iRMMFjaNmW/VCur4w22pddq0RW3KTsaRhLhopG VRzvgFLHO/R2LNtaMntdPi6zofEbGhOFn6Vb6cGqMHhifaGMV5c7Q0OyFBh/U9VJolXjsVfD X-Proofpoint-GUID: E1K1T4Oso98r-bxhxHKmCfBdocReWXpj X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1099,Hydra:6.0.736,FMLib:17.12.80.40 definitions=2025-06-18_05,2025-06-18_03,2025-03-28_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 impostorscore=0 bulkscore=0 adultscore=0 lowpriorityscore=0 spamscore=0 suspectscore=0 mlxscore=0 malwarescore=0 mlxlogscore=999 clxscore=1011 phishscore=0 priorityscore=1501 classifier=spam authscore=0 authtc=n/a authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.19.0-2505280000 definitions=main-2506180150 Content-Type: text/plain; charset="utf-8" The AD4170 design has features to aid interfacing with weigh scale and RTD sensors that are expected to be setup with external circuitry for proper sensor operation. A key characteristic of those sensors is that the circuit they are in must be excited with a pair of signals. The external circuit can be excited either by voltage supply or by AD4170 excitation signals. The sensor can then be read through a different pair of lines that are connected to AD4170 ADC. Configure AD4170 to handle external circuit sensors. Signed-off-by: Marcelo Schmitt --- No changes in v6. drivers/iio/adc/ad4170.c | 494 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 490 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/ad4170.c b/drivers/iio/adc/ad4170.c index 3ee66ac651f1..2a2d4a05e9af 100644 --- a/drivers/iio/adc/ad4170.c +++ b/drivers/iio/adc/ad4170.c @@ -67,6 +67,8 @@ #define AD4170_FILTER_FS_REG(x) (0xC7 + 14 * (x)) #define AD4170_OFFSET_REG(x) (0xCA + 14 * (x)) #define AD4170_GAIN_REG(x) (0xCD + 14 * (x)) +#define AD4170_V_BIAS_REG 0x135 +#define AD4170_CURRENT_SRC_REG(x) (0x139 + 2 * (x)) #define AD4170_GPIO_MODE_REG 0x191 #define AD4170_GPIO_OUTPUT_REG 0x193 #define AD4170_GPIO_INPUT_REG 0x195 @@ -98,6 +100,10 @@ #define AD4170_CHAN_MAP_AINP_MSK GENMASK(12, 8) #define AD4170_CHAN_MAP_AINM_MSK GENMASK(4, 0) =20 +/* AD4170_MISC_REG */ +#define AD4170_MISC_CHOP_IEXC_MSK GENMASK(15, 14) +#define AD4170_MISC_CHOP_ADC_MSK GENMASK(9, 8) + /* AD4170_AFE_REG */ #define AD4170_AFE_REF_BUF_M_MSK GENMASK(11, 10) #define AD4170_AFE_REF_BUF_P_MSK GENMASK(9, 8) @@ -108,12 +114,19 @@ /* AD4170_FILTER_REG */ #define AD4170_FILTER_FILTER_TYPE_MSK GENMASK(3, 0) =20 +/* AD4170_CURRENT_SRC_REG */ +#define AD4170_CURRENT_SRC_I_OUT_PIN_MSK GENMASK(12, 8) +#define AD4170_CURRENT_SRC_I_OUT_VAL_MSK GENMASK(2, 0) + /* AD4170_GPIO_MODE_REG */ #define AD4170_GPIO_MODE_GPIO0_MSK GENMASK(1, 0) #define AD4170_GPIO_MODE_GPIO1_MSK GENMASK(3, 2) #define AD4170_GPIO_MODE_GPIO2_MSK GENMASK(5, 4) #define AD4170_GPIO_MODE_GPIO3_MSK GENMASK(7, 6) =20 +/* AD4170_GPIO_OUTPUT_REG */ +#define AD4170_GPIO_OUTPUT_GPIO_MSK(x) BIT(x) + /* AD4170 register constants */ =20 /* AD4170_CLOCK_CTRL_REG constants */ @@ -137,6 +150,11 @@ #define AD4170_CHAN_MAP_REFIN2_N 28 #define AD4170_CHAN_MAP_REFOUT 29 =20 +/* AD4170_MISC_REG constants */ +#define AD4170_MISC_CHOP_IEXC_PAIR1 0x1 +#define AD4170_MISC_CHOP_IEXC_PAIR2 0x2 +#define AD4170_MISC_CHOP_IEXC_BOTH 0x3 + /* AD4170_PIN_MUXING_REG constants */ #define AD4170_PIN_MUXING_DIG_AUX1_DISABLED 0x0 #define AD4170_PIN_MUXING_DIG_AUX1_RDY 0x1 @@ -154,6 +172,10 @@ #define AD4170_FILTER_FILTER_TYPE_SINC5 0x4 #define AD4170_FILTER_FILTER_TYPE_SINC3 0x6 =20 +/* AD4170_CURRENT_SRC_REG constants */ +#define AD4170_CURRENT_SRC_I_OUT_PIN_AIN(x) (x) +#define AD4170_CURRENT_SRC_I_OUT_PIN_GPIO(x) ((x) + 17) + /* AD4170_GPIO_MODE_REG constants */ #define AD4170_GPIO_MODE_GPIO_INPUT 1 #define AD4170_GPIO_MODE_GPIO_OUTPUT 2 @@ -168,6 +190,7 @@ #define AD4170_INVALID_SETUP 9 #define AD4170_SPI_INST_PHASE_LEN 2 #define AD4170_SPI_MAX_XFER_LEN 6 +#define AD4170_NUM_CURRENT_SRC 4 #define AD4170_DEFAULT_SAMP_RATE (125 * HZ_PER_KHZ) =20 #define AD4170_INT_REF_2_5V 2500000 @@ -189,8 +212,19 @@ =20 #define AD4170_ADC_CTRL_CONT_READ_EXIT 0xA5 =20 +/* Analog pin functions */ +#define AD4170_PIN_UNASSIGNED 0x00 +#define AD4170_PIN_ANALOG_IN 0x01 +#define AD4170_PIN_CURRENT_OUT 0x02 +#define AD4170_PIN_VBIAS 0x04 + /* GPIO pin functions */ #define AD4170_GPIO_UNASSIGNED 0x00 +#define AD4170_GPIO_AC_EXCITATION 0x02 +#define AD4170_GPIO_OUTPUT 0x04 + +/* Current source */ +#define AD4170_CURRENT_SRC_DISABLED 0xFF =20 static const unsigned int ad4170_reg_size[] =3D { [AD4170_CONFIG_A_REG] =3D 1, @@ -229,6 +263,8 @@ static const unsigned int ad4170_reg_size[] =3D { [AD4170_OFFSET_REG(5) ... AD4170_GAIN_REG(5)] =3D 3, [AD4170_OFFSET_REG(6) ... AD4170_GAIN_REG(6)] =3D 3, [AD4170_OFFSET_REG(7) ... AD4170_GAIN_REG(7)] =3D 3, + [AD4170_V_BIAS_REG] =3D 2, + [AD4170_CURRENT_SRC_REG(0) ... AD4170_CURRENT_SRC_REG(3)] =3D 2, [AD4170_GPIO_MODE_REG] =3D 2, [AD4170_GPIO_OUTPUT_REG] =3D 2, [AD4170_GPIO_INPUT_REG] =3D 2, @@ -298,6 +334,41 @@ static const unsigned int ad4170_sinc5_filt_fs_tbl[] = =3D { 1, 2, 4, 8, 12, 16, 20, 40, 48, 80, 100, 256, }; =20 +static const unsigned int ad4170_iout_pin_tbl[] =3D { + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(0), + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(1), + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(2), + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(3), + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(4), + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(5), + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(6), + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(7), + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(8), + AD4170_CURRENT_SRC_I_OUT_PIN_GPIO(0), + AD4170_CURRENT_SRC_I_OUT_PIN_GPIO(1), + AD4170_CURRENT_SRC_I_OUT_PIN_GPIO(2), + AD4170_CURRENT_SRC_I_OUT_PIN_GPIO(3), +}; + +static const unsigned int ad4170_iout_current_ua_tbl[] =3D { + 0, 10, 50, 100, 250, 500, 1000, 1500, +}; + +enum ad4170_sensor_enum { + AD4170_ADC_SENSOR =3D 0, + AD4170_WEIGH_SCALE_SENSOR =3D 1, + AD4170_RTD_SENSOR =3D 2, + AD4170_THERMOCOUPLE_SENSOR =3D 3, +}; + +/* maps adi,sensor-type property value to enum */ +static const char * const ad4170_sensor_type[] =3D { + [AD4170_ADC_SENSOR] =3D "adc", + [AD4170_WEIGH_SCALE_SENSOR] =3D "weighscale", + [AD4170_RTD_SENSOR] =3D "rtd", + [AD4170_THERMOCOUPLE_SENSOR] =3D "thermocouple", +}; + struct ad4170_chip_info { const char *name; }; @@ -382,6 +453,7 @@ struct ad4170_state { struct gpio_chip gpiochip; unsigned int clock_ctrl; int gpio_fn[AD4170_NUM_GPIO_PINS]; + unsigned int cur_src_pins[AD4170_NUM_CURRENT_SRC]; /* * DMA (thus cache coherency maintenance) requires the transfer buffers * to live in their own cache lines. @@ -910,6 +982,19 @@ static int ad4170_get_ain_voltage_uv(struct ad4170_sta= te *st, int ain_n, int v_diff; =20 *ain_voltage =3D 0; + /* + * The voltage bias (vbias) sets the common-mode voltage of the channel + * to (AVDD + AVSS)/2. If provided, AVSS supply provides the magnitude + * (absolute value) of the negative voltage supplied to the AVSS pin. + * So, we do AVDD - AVSS to compute the DC voltage generated by the bias + * voltage generator. + */ + if (st->pins_fn[ain_n] & AD4170_PIN_VBIAS) { + int v_diff =3D st->vrefs_uv[AD4170_AVDD_SUP] - st->vrefs_uv[AD4170_AVSS_= SUP]; + *ain_voltage =3D v_diff / 2; + return 0; + } + if (ain_n <=3D AD4170_CHAN_MAP_TEMP_SENSOR) return 0; =20 @@ -964,6 +1049,19 @@ static int ad4170_get_ain_voltage_uv(struct ad4170_st= ate *st, int ain_n, } } =20 +static int ad4170_validate_analog_input(struct ad4170_state *st, int pin) +{ + if (pin <=3D AD4170_MAX_ANALOG_PINS) { + if (st->pins_fn[pin] & AD4170_PIN_CURRENT_OUT) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Pin %d already used with fn %u.\n", + pin, st->pins_fn[pin]); + + st->pins_fn[pin] |=3D AD4170_PIN_ANALOG_IN; + } + return 0; +} + static int ad4170_validate_channel_input(struct ad4170_state *st, int pin,= bool com) { /* Check common-mode input pin is mapped to a special input. */ @@ -978,7 +1076,7 @@ static int ad4170_validate_channel_input(struct ad4170= _state *st, int pin, bool "Invalid analog input pin number. %d\n", pin); =20 - return 0; + return ad4170_validate_analog_input(st, pin); } =20 /* @@ -1718,6 +1816,371 @@ static int ad4170_gpio_init(struct iio_dev *indio_d= ev) return devm_gpiochip_add_data(&st->spi->dev, &st->gpiochip, indio_dev); } =20 +static int ad4170_validate_excitation_pin(struct ad4170_state *st, u32 pin) +{ + struct device *dev =3D &st->spi->dev; + unsigned int i; + + /* Check the pin number is valid */ + for (i =3D 0; i < ARRAY_SIZE(ad4170_iout_pin_tbl); i++) + if (ad4170_iout_pin_tbl[i] =3D=3D pin) + break; + + if (i =3D=3D ARRAY_SIZE(ad4170_iout_pin_tbl)) + return dev_err_probe(dev, -EINVAL, + "Invalid excitation pin: %u\n", + pin); + + /* Check the pin is available */ + if (pin <=3D AD4170_MAX_ANALOG_PINS) { + if (st->pins_fn[pin] !=3D AD4170_PIN_UNASSIGNED) + return dev_err_probe(dev, -EINVAL, + "Pin %u already used with fn %u\n", + pin, st->pins_fn[pin]); + + st->pins_fn[pin] |=3D AD4170_PIN_CURRENT_OUT; + } else { + unsigned int gpio =3D pin - AD4170_CURRENT_SRC_I_OUT_PIN_GPIO(0); + + if (st->gpio_fn[gpio] !=3D AD4170_GPIO_UNASSIGNED) + return dev_err_probe(dev, -EINVAL, + "GPIO %u already used with fn %u\n", + gpio, st->gpio_fn[gpio]); + + st->gpio_fn[gpio] |=3D AD4170_GPIO_AC_EXCITATION; + } + + return 0; +} + +static int ad4170_validate_excitation_pins(struct ad4170_state *st, + u32 *exc_pins, int num_exc_pins) +{ + unsigned int i; + int ret; + + for (i =3D 0; i < num_exc_pins; i++) { + ret =3D ad4170_validate_excitation_pin(st, exc_pins[i]); + if (ret) + return ret; + } + return 0; +} + +static const char *const ad4170_i_out_pin_dt_props[] =3D { + "adi,excitation-pin-0", + "adi,excitation-pin-1", + "adi,excitation-pin-2", + "adi,excitation-pin-3", +}; + +static const char *const ad4170_i_out_val_dt_props[] =3D { + "adi,excitation-current-0-microamp", + "adi,excitation-current-1-microamp", + "adi,excitation-current-2-microamp", + "adi,excitation-current-3-microamp", +}; + +/* + * Parses firmware data describing output current source setup. There are 4 + * excitation currents (IOUT0 to IOUT3) that can be configured independent= ly. + * Excitation currents are added if they are output on the same pin. + */ +static int ad4170_parse_exc_current(struct ad4170_state *st, + struct fwnode_handle *child, + unsigned int *exc_pins, + unsigned int *exc_curs, + unsigned int *num_exc_pins) +{ + struct device *dev =3D &st->spi->dev; + unsigned int num_pins, i, j; + u32 pin, val; + int ret; + + num_pins =3D 0; + for (i =3D 0; i < AD4170_NUM_CURRENT_SRC; i++) { + /* Parse excitation current output pin properties. */ + pin =3D AD4170_CURRENT_SRC_I_OUT_PIN_AIN(0); + ret =3D fwnode_property_read_u32(child, ad4170_i_out_pin_dt_props[i], + &pin); + if (ret) + continue; + + exc_pins[num_pins] =3D pin; + + /* Parse excitation current value properties. */ + val =3D ad4170_iout_current_ua_tbl[0]; + fwnode_property_read_u32(child, + ad4170_i_out_val_dt_props[i], &val); + + for (j =3D 0; j < ARRAY_SIZE(ad4170_iout_current_ua_tbl); j++) + if (ad4170_iout_current_ua_tbl[j] =3D=3D val) + break; + + if (j =3D=3D ARRAY_SIZE(ad4170_iout_current_ua_tbl)) + return dev_err_probe(dev, -EINVAL, "Invalid %s: %uuA\n", + ad4170_i_out_val_dt_props[i], val); + + exc_curs[num_pins] =3D j; + num_pins++; + } + *num_exc_pins =3D num_pins; + + return 0; +} + +static int ad4170_setup_current_src(struct ad4170_state *st, + struct fwnode_handle *child, + struct ad4170_setup *setup, u32 *exc_pins, + unsigned int *exc_curs, int num_exc_pins, + bool ac_excited) +{ + unsigned int exc_cur_pair, i, j; + int ret; + + for (i =3D 0; i < num_exc_pins; i++) { + unsigned int exc_cur =3D exc_curs[i]; + unsigned int pin =3D exc_pins[i]; + unsigned int current_src =3D 0; + + for (j =3D 0; j < AD4170_NUM_CURRENT_SRC; j++) + if (st->cur_src_pins[j] =3D=3D AD4170_CURRENT_SRC_DISABLED) + break; + + if (j =3D=3D AD4170_NUM_CURRENT_SRC) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Too many excitation current sources\n"); + + current_src |=3D FIELD_PREP(AD4170_CURRENT_SRC_I_OUT_PIN_MSK, pin); + current_src |=3D FIELD_PREP(AD4170_CURRENT_SRC_I_OUT_VAL_MSK, exc_cur); + st->cur_src_pins[j] =3D pin; + ret =3D regmap_write(st->regmap, AD4170_CURRENT_SRC_REG(j), + current_src); + if (ret) + return ret; + } + + if (!ac_excited) + return 0; + + if (num_exc_pins < 2) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Current chopping requested but only one pin provided: %u\n", + exc_pins[0]); + + /* + * Two use cases to handle here: + * - 2 pairs of excitation currents; + * - 1 pair of excitation currents. + */ + if (num_exc_pins =3D=3D 4) { + for (i =3D 0; i < AD4170_NUM_CURRENT_SRC; i++) + if (st->cur_src_pins[i] !=3D exc_pins[i]) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Unable to use 4 exc pins\n"); + } else { + /* + * Excitation current chopping is configured in pairs. Current + * sources IOUT0 and IOUT1 form pair 1, IOUT2 and IOUT3 make up + * pair 2. So, if current chopping was requested, check if the + * first end of the first pair of excitation currents is + * available. Try the next pair if IOUT0 has already been + * configured for another channel. + */ + i =3D st->cur_src_pins[0] =3D=3D exc_pins[0] ? 0 : 2; + + if (st->cur_src_pins[i] !=3D exc_pins[0] || + st->cur_src_pins[i + 1] !=3D exc_pins[1]) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Failed to setup current chopping\n"); + + st->cur_src_pins[i] =3D exc_pins[0]; + st->cur_src_pins[i + 1] =3D exc_pins[1]; + + if (i =3D=3D 0) + exc_cur_pair =3D AD4170_MISC_CHOP_IEXC_PAIR1; + else + exc_cur_pair =3D AD4170_MISC_CHOP_IEXC_PAIR2; + } + + /* + * Configure excitation current chopping. + * Chop both pairs if using four excitation pins. + */ + setup->misc |=3D FIELD_PREP(AD4170_MISC_CHOP_IEXC_MSK, + num_exc_pins =3D=3D 2 ? + exc_cur_pair : + AD4170_MISC_CHOP_IEXC_BOTH); + + return 0; +} + +static int ad4170_setup_bridge(struct ad4170_state *st, + struct fwnode_handle *child, + struct ad4170_setup *setup, u32 *exc_pins, + unsigned int *exc_curs, int num_exc_pins, + bool ac_excited) +{ + unsigned long gpio_mask; + unsigned int i; + int ret; + + /* + * If a specific current is provided through + * adi,excitation-current-n-microamp, set excitation pins provided + * through adi,excitation-pin-n to excite the bridge circuit. + */ + for (i =3D 0; i < num_exc_pins; i++) + if (exc_curs[i] > 0) + return ad4170_setup_current_src(st, child, setup, exc_pins, + exc_curs, num_exc_pins, + ac_excited); + + /* + * Else, use predefined ACX1, ACX1 negated, ACX2, ACX2 negated signals + * to AC excite the bridge. Those signals are output on GPIO2, GPIO0, + * GPIO3, and GPIO1, respectively. If only two pins are specified for AC + * excitation, use ACX1 and ACX2 (GPIO2 and GPIO3). + * + * Also, to avoid any short-circuit condition when more than one channel + * is enabled, set GPIO2 and GPIO0 high, and set GPIO1 and GPIO3 low to + * DC excite the bridge whenever a channel without AC excitation is + * selected. That is needed because GPIO pins are controlled by the next + * highest priority GPIO function when a channel doesn't enable AC + * excitation. See datasheet Figure 113 Weigh Scale (AC Excitation) for + * the reference circuit diagram. + */ + if (num_exc_pins =3D=3D 2) { + setup->misc |=3D FIELD_PREP(AD4170_MISC_CHOP_ADC_MSK, 0x3); + + gpio_mask =3D AD4170_GPIO_MODE_GPIO3_MSK | AD4170_GPIO_MODE_GPIO2_MSK; + ret =3D regmap_update_bits(st->regmap, AD4170_GPIO_MODE_REG, gpio_mask, + FIELD_PREP(AD4170_GPIO_MODE_GPIO3_MSK, + AD4170_GPIO_MODE_GPIO_OUTPUT) | + FIELD_PREP(AD4170_GPIO_MODE_GPIO2_MSK, + AD4170_GPIO_MODE_GPIO_OUTPUT)); + if (ret) + return ret; + + /* + * Set GPIO2 high and GPIO3 low to DC excite the bridge when + * a different channel is selected. + */ + gpio_mask =3D AD4170_GPIO_OUTPUT_GPIO_MSK(3) | + AD4170_GPIO_OUTPUT_GPIO_MSK(2); + ret =3D regmap_update_bits(st->regmap, AD4170_GPIO_OUTPUT_REG, gpio_mask, + FIELD_PREP(AD4170_GPIO_OUTPUT_GPIO_MSK(3), 0) | + FIELD_PREP(AD4170_GPIO_OUTPUT_GPIO_MSK(2), 1)); + if (ret) + return ret; + + st->gpio_fn[3] |=3D AD4170_GPIO_OUTPUT; + st->gpio_fn[2] |=3D AD4170_GPIO_OUTPUT; + } else { + setup->misc |=3D FIELD_PREP(AD4170_MISC_CHOP_ADC_MSK, 0x2); + + gpio_mask =3D AD4170_GPIO_MODE_GPIO3_MSK | AD4170_GPIO_MODE_GPIO2_MSK | + AD4170_GPIO_MODE_GPIO1_MSK | AD4170_GPIO_MODE_GPIO0_MSK; + ret =3D regmap_update_bits(st->regmap, AD4170_GPIO_MODE_REG, gpio_mask, + FIELD_PREP(AD4170_GPIO_MODE_GPIO3_MSK, + AD4170_GPIO_MODE_GPIO_OUTPUT) | + FIELD_PREP(AD4170_GPIO_MODE_GPIO2_MSK, + AD4170_GPIO_MODE_GPIO_OUTPUT) | + FIELD_PREP(AD4170_GPIO_MODE_GPIO1_MSK, + AD4170_GPIO_MODE_GPIO_OUTPUT) | + FIELD_PREP(AD4170_GPIO_MODE_GPIO0_MSK, + AD4170_GPIO_MODE_GPIO_OUTPUT)); + if (ret) + return ret; + + /* + * Set GPIO2 and GPIO0 high, and set GPIO1 and GPIO3 low to DC + * excite the bridge when a different channel is selected. + */ + gpio_mask =3D AD4170_GPIO_OUTPUT_GPIO_MSK(3) | + AD4170_GPIO_OUTPUT_GPIO_MSK(2) | + AD4170_GPIO_OUTPUT_GPIO_MSK(1) | + AD4170_GPIO_OUTPUT_GPIO_MSK(0); + ret =3D regmap_update_bits(st->regmap, AD4170_GPIO_OUTPUT_REG, gpio_mask, + FIELD_PREP(AD4170_GPIO_OUTPUT_GPIO_MSK(3), 0) | + FIELD_PREP(AD4170_GPIO_OUTPUT_GPIO_MSK(2), 1) | + FIELD_PREP(AD4170_GPIO_OUTPUT_GPIO_MSK(1), 0) | + FIELD_PREP(AD4170_GPIO_OUTPUT_GPIO_MSK(0), 1)); + if (ret) + return ret; + + st->gpio_fn[3] |=3D AD4170_GPIO_OUTPUT; + st->gpio_fn[2] |=3D AD4170_GPIO_OUTPUT; + st->gpio_fn[1] |=3D AD4170_GPIO_OUTPUT; + st->gpio_fn[0] |=3D AD4170_GPIO_OUTPUT; + } + + return 0; +} + +static int ad4170_setup_rtd(struct ad4170_state *st, + struct fwnode_handle *child, + struct ad4170_setup *setup, u32 *exc_pins, + unsigned int *exc_curs, int num_exc_pins, bool ac_excited) +{ + return ad4170_setup_current_src(st, child, setup, exc_pins, + exc_curs, num_exc_pins, ac_excited); +} + +static int ad4170_parse_external_sensor(struct ad4170_state *st, + struct fwnode_handle *child, + struct ad4170_setup *setup, + struct iio_chan_spec *chan, u8 s_type) +{ + unsigned int num_exc_pins, reg_val; + struct device *dev =3D &st->spi->dev; + u32 pins[2], exc_pins[4], exc_curs[4]; + bool ac_excited, vbias; + int ret; + + ret =3D fwnode_property_read_u32_array(child, "diff-channels", pins, + ARRAY_SIZE(pins)); + if (ret) + return dev_err_probe(dev, ret, + "Failed to read sensor diff-channels\n"); + + chan->differential =3D true; + chan->channel =3D pins[0]; + chan->channel2 =3D pins[1]; + + ret =3D ad4170_parse_exc_current(st, child, exc_pins, exc_curs, &num_exc_= pins); + if (ret) + return ret; + + /* The external sensor may not need excitation from the ADC chip. */ + if (num_exc_pins =3D=3D 0) + return 0; + + ret =3D ad4170_validate_excitation_pins(st, exc_pins, num_exc_pins); + if (ret) + return ret; + + ac_excited =3D fwnode_property_read_bool(child, "adi,excitation-ac"); + + if (s_type =3D=3D AD4170_THERMOCOUPLE_SENSOR) { + vbias =3D fwnode_property_read_bool(child, "adi,vbias"); + if (vbias) { + st->pins_fn[chan->channel2] |=3D AD4170_PIN_VBIAS; + reg_val =3D BIT(chan->channel2); + return regmap_write(st->regmap, AD4170_V_BIAS_REG, + reg_val); + } + } + if (s_type =3D=3D AD4170_WEIGH_SCALE_SENSOR) + ret =3D ad4170_setup_bridge(st, child, setup, exc_pins, exc_curs, + num_exc_pins, ac_excited); + else + ret =3D ad4170_setup_rtd(st, child, setup, exc_pins, exc_curs, + num_exc_pins, ac_excited); + + return ret; +} + static int ad4170_parse_reference(struct ad4170_state *st, struct fwnode_handle *child, struct ad4170_setup *setup) @@ -1810,6 +2273,7 @@ static int ad4170_parse_channel_node(struct iio_dev *= indio_dev, struct ad4170_setup *setup; struct iio_chan_spec *chan; unsigned int ref_select; + unsigned int s_type; unsigned int ch_reg; bool bipolar; int ret; @@ -1837,10 +2301,28 @@ static int ad4170_parse_channel_node(struct iio_dev= *indio_dev, if (ret) return ret; =20 - ret =3D ad4170_parse_adc_channel_type(dev, child, chan); - if (ret) - return ret; + ret =3D fwnode_property_match_property_string(child, "adi,sensor-type", + ad4170_sensor_type, + ARRAY_SIZE(ad4170_sensor_type)); =20 + /* Default to conventional ADC channel if sensor type not present */ + s_type =3D ret < 0 ? AD4170_ADC_SENSOR : ret; + switch (s_type) { + case AD4170_ADC_SENSOR: + ret =3D ad4170_parse_adc_channel_type(dev, child, chan); + if (ret) + return ret; + + break; + case AD4170_WEIGH_SCALE_SENSOR: + case AD4170_THERMOCOUPLE_SENSOR: + case AD4170_RTD_SENSOR: + ret =3D ad4170_parse_external_sensor(st, child, setup, chan, s_type); + if (ret) + return ret; + + break; + } bipolar =3D fwnode_property_read_bool(child, "bipolar"); setup->afe |=3D FIELD_PREP(AD4170_AFE_BIPOLAR_MSK, bipolar); if (bipolar) @@ -2037,6 +2519,7 @@ static int ad4170_parse_firmware(struct iio_dev *indi= o_dev) struct ad4170_state *st =3D iio_priv(indio_dev); struct device *dev =3D &st->spi->dev; int reg_data, ret; + unsigned int i; =20 ret =3D ad4170_clock_select(indio_dev); if (ret) @@ -2046,6 +2529,9 @@ static int ad4170_parse_firmware(struct iio_dev *indi= o_dev) if (ret) return ret; =20 + for (i =3D 0; i < AD4170_NUM_CURRENT_SRC; i++) + st->cur_src_pins[i] =3D AD4170_CURRENT_SRC_DISABLED; + /* On power on, device defaults to using SDO pin for data ready signal */ st->int_pin_sel =3D AD4170_INT_PIN_SDO; ret =3D device_property_match_property_string(dev, "interrupt-names", --=20 2.47.2 From nobody Thu Oct 9 08:43:03 2025 Received: from mx0a-00128a01.pphosted.com (mx0a-00128a01.pphosted.com [148.163.135.77]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B5E842F363C; Wed, 18 Jun 2025 17:39:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.163.135.77 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268393; cv=none; b=HcFMVVbKxk3aMzIcp0nAvqDgpSKvMgp1ox7n6BA79bqVrJkKmuooTZr3t3XkBl5kh0Ft+yCdqEipaUo/AlF5wF+qSu89Pc1hlZRMLFhcNA6nxT8zNgMenAOklyLMSiYROoBP4xRwPM5N0WMFeNL/R+WzxwH93mwM1E9iuj6Rpa8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750268393; c=relaxed/simple; bh=/pPJSNAfLSroCKYHXJLWwJVsQr93kBLP7GVw4wmp2I4=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=kEFV7Wt9wzmoDn37o+dqZrACdwQol9DNFCAQo9Ds6+0hNa1ubgNZlhmZzbpuASDlqmw8LpxrmdHXk2o1vjXlnyN0pqMD4JsiGqLNXNacIe6JC5Pfy2FAg4Xh5MlQCLIralG/YMWKw7YwDI/3HhGRMRpBNIRQwVQV1jwHai/6AxA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com; spf=pass smtp.mailfrom=analog.com; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b=TTJLXC1c; arc=none smtp.client-ip=148.163.135.77 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=analog.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b="TTJLXC1c" Received: from pps.filterd (m0167088.ppops.net [127.0.0.1]) by mx0a-00128a01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 55IG03ao031954; Wed, 18 Jun 2025 13:39:33 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=analog.com; h=cc :content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=DKIM; bh=HTMmK MgAUBd8WEFA9l2xGUFLTHm+A38dMxJbz03dPNY=; b=TTJLXC1ctWFL3aQ38Qqsz rEP74v9btSVWUiwaiRtVLBz3VN/tCfkQubWS3FJ08zG+XkZmNWStNlZFZxv6QbEw 1ufkLn6mJxwzPjZwtH+0YE1mLJ4qYfAhUzRUyXb24KTR6iEAV+l6ay6WOFzDsM64 QcpSZX5GCRoVfp3gLNPEl2QSW3OlVl1OtfnX1hVJ3Z+Y8P2S0bFxqxU8vtuBiCl4 Zdvri0lxwn+Llkkl3kkJiaaUJ1XIMEEpi2MjC7SScF3gwdfsehfzIfpqoz6VGrTw VSQMaacxPIm+BdvYTtYUmc7iuLD/mgPp14AiXq5Q9LTiNMU/IGsvI6G0Ssv3Kipo w== Received: from nwd2mta4.analog.com ([137.71.173.58]) by mx0a-00128a01.pphosted.com (PPS) with ESMTPS id 47bfshddpc-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 18 Jun 2025 13:39:32 -0400 (EDT) Received: from ASHBMBX8.ad.analog.com (ASHBMBX8.ad.analog.com [10.64.17.5]) by nwd2mta4.analog.com (8.14.7/8.14.7) with ESMTP id 55IHdVpY016246 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Wed, 18 Jun 2025 13:39:31 -0400 Received: from ASHBMBX8.ad.analog.com (10.64.17.5) by ASHBMBX8.ad.analog.com (10.64.17.5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 18 Jun 2025 13:39:31 -0400 Received: from zeus.spd.analog.com (10.66.68.11) by ashbmbx8.ad.analog.com (10.64.17.5) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Wed, 18 Jun 2025 13:39:31 -0400 Received: from work.ad.analog.com (HYB-hERzalRezfV.ad.analog.com [10.65.205.9]) by zeus.spd.analog.com (8.15.1/8.15.1) with ESMTP id 55IHdDLx007658; Wed, 18 Jun 2025 13:39:15 -0400 From: Marcelo Schmitt To: , , , CC: , , , , , , , , , , , , , Subject: [PATCH v6 12/12] iio: adc: ad4170: Add timestamp channel Date: Wed, 18 Jun 2025 14:39:12 -0300 Message-ID: <63ebf4408a118a749481ecb3f5ce7ad67cedfa7b.1750258776.git.marcelo.schmitt@analog.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ADIRuleOP-NewSCL: Rule Triggered X-Proofpoint-ORIG-GUID: wbgdZ0c0wYrprg2Qn6H9Y4KcxBfgpDQV X-Authority-Analysis: v=2.4 cv=SKhCVPvH c=1 sm=1 tr=0 ts=6852f9d4 cx=c_pps a=3WNzaoukacrqR9RwcOSAdA==:117 a=3WNzaoukacrqR9RwcOSAdA==:17 a=6IFa9wvqVegA:10 a=gAnH3GRIAAAA:8 a=ooJTjvKFDTQpXrV7GjsA:9 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjUwNjE4MDE1MCBTYWx0ZWRfX8r2pr08BrSmd ftGGA6y4QdW9K/9Ig3fcG4o8Hz+N9NnwK08dYyAZ5MJ8Xpr0RRz1SjAMooWB9JUp/y3SWizUpC9 A91KdbN1JwbNg6ZPZ9uQT9RlwNZ6jbwMp0BKzJAhU1oJXV7ensto+tnRIgkT9eB1h4B68DXpVqB v5UxoTeJ/k45sXZgHnS9cBlvuKAcwWqVNgIywAlUsxavwcY1WXBTtppXaRCI6NRgaM0cpqLc22g XmG6wJICxUxpcb2CwspgAY9BYUJY61O+8g69/TQH8NXgKCL5KPkLVg9VzTB0v48/R5N4YyKHQLm 6/poQI6ElkC23WnQstmm1gQi29CyOseusM0X0LLNyRLUV8S1fXgE9BT4W+v+XvVc8eilzMqqNKy e/wbKu6Rwhl65f/fPBjZ6Erc0v0n5ZJbN+0Cs3RKdZx6FlYahJKADd4rmdUmo/LKUYpfTI3b X-Proofpoint-GUID: wbgdZ0c0wYrprg2Qn6H9Y4KcxBfgpDQV X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1099,Hydra:6.0.736,FMLib:17.12.80.40 definitions=2025-06-18_05,2025-06-18_03,2025-03-28_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 impostorscore=0 bulkscore=0 adultscore=0 lowpriorityscore=0 spamscore=0 suspectscore=0 mlxscore=0 malwarescore=0 mlxlogscore=999 clxscore=1015 phishscore=0 priorityscore=1501 classifier=spam authscore=0 authtc=n/a authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.19.0-2505280000 definitions=main-2506180150 Content-Type: text/plain; charset="utf-8" Add timestamp channel allowing to record the moment at which ADC samples are captured in buffered read mode. Signed-off-by: Marcelo Schmitt --- No changes in v6. drivers/iio/adc/ad4170.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/ad4170.c b/drivers/iio/adc/ad4170.c index 2a2d4a05e9af..33b9a6b2255b 100644 --- a/drivers/iio/adc/ad4170.c +++ b/drivers/iio/adc/ad4170.c @@ -185,6 +185,7 @@ #define AD4170_NUM_ANALOG_PINS 9 #define AD4170_NUM_GPIO_PINS 4 #define AD4170_MAX_CHANNELS 16 +#define AD4170_MAX_IIO_CHANNELS (AD4170_MAX_CHANNELS + 1) #define AD4170_MAX_ANALOG_PINS 8 #define AD4170_MAX_SETUPS 8 #define AD4170_INVALID_SETUP 9 @@ -437,7 +438,7 @@ struct ad4170_state { int vrefs_uv[AD4170_MAX_SUP]; u32 mclk_hz; struct ad4170_setup_info setup_infos[AD4170_MAX_SETUPS]; - struct iio_chan_spec chans[AD4170_MAX_CHANNELS]; + struct iio_chan_spec chans[AD4170_MAX_IIO_CHANNELS]; struct ad4170_chan_info chan_infos[AD4170_MAX_CHANNELS]; struct spi_device *spi; struct regmap *regmap; @@ -454,6 +455,7 @@ struct ad4170_state { unsigned int clock_ctrl; int gpio_fn[AD4170_NUM_GPIO_PINS]; unsigned int cur_src_pins[AD4170_NUM_CURRENT_SRC]; + unsigned int num_adc_chans; /* * DMA (thus cache coherency maintenance) requires the transfer buffers * to live in their own cache lines. @@ -2389,7 +2391,16 @@ static int ad4170_parse_channels(struct iio_dev *ind= io_dev) return dev_err_probe(dev, ret, "Invalid input config\n"); =20 st->chan_infos[chan_num].input_range_uv =3D ret; + chan_num++; } + st->num_adc_chans =3D chan_num; + + /* Add timestamp channel */ + struct iio_chan_spec ts_chan =3D IIO_CHAN_SOFT_TIMESTAMP(chan_num); + + st->chans[chan_num] =3D ts_chan; + num_channels =3D num_channels + 1; + indio_dev->num_channels =3D num_channels; indio_dev->channels =3D st->chans; =20 @@ -2581,7 +2592,7 @@ static int ad4170_initial_config(struct iio_dev *indi= o_dev) return dev_err_probe(dev, ret, "Failed to set ADC mode to idle\n"); =20 - for (i =3D 0; i < indio_dev->num_channels; i++) { + for (i =3D 0; i < st->num_adc_chans; i++) { struct ad4170_chan_info *chan_info; struct iio_chan_spec const *chan; struct ad4170_setup *setup; @@ -2706,7 +2717,7 @@ static int ad4170_buffer_predisable(struct iio_dev *i= ndio_dev) * is done after buffer disable. Disable all channels so only requested * channels will be read. */ - for (i =3D 0; i < indio_dev->num_channels; i++) { + for (i =3D 0; i < st->num_adc_chans; i++) { ret =3D ad4170_set_channel_enable(st, i, false); if (ret) return ret; @@ -2758,7 +2769,9 @@ static irqreturn_t ad4170_trigger_handler(int irq, vo= id *p) memcpy(&st->bounce_buffer[i++], st->rx_buf, ARRAY_SIZE(st->rx_buf)); } =20 - iio_push_to_buffers(indio_dev, st->bounce_buffer); + iio_push_to_buffers_with_ts(indio_dev, st->bounce_buffer, + sizeof(st->bounce_buffer), + iio_get_time_ns(indio_dev)); err_out: iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; --=20 2.47.2