From patchwork Wed Sep 20 17:02:50 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lechner X-Patchwork-Id: 13393075 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id AF021C0031C for ; Wed, 20 Sep 2023 17:03:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234970AbjITRDa (ORCPT ); Wed, 20 Sep 2023 13:03:30 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45208 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234905AbjITRDa (ORCPT ); Wed, 20 Sep 2023 13:03:30 -0400 Received: from mail-wr1-x429.google.com (mail-wr1-x429.google.com [IPv6:2a00:1450:4864:20::429]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6058FB4 for ; Wed, 20 Sep 2023 10:03:22 -0700 (PDT) Received: by mail-wr1-x429.google.com with SMTP id ffacd0b85a97d-32008b5e2d2so837072f8f.0 for ; Wed, 20 Sep 2023 10:03:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1695229401; x=1695834201; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=7UwBoraJ2UyiNBA95miiJOkVeBFDlpiyzSKW0JhpuxY=; b=dczXK0NjEeB4k5HsB/IIKEVhuY2etLZSk7XPSxzeXkLgijIaBqtlEea+7jqOgT9BPp 0YieHbR4OHoLKX34KwIzMo+u2J+hulgopnTiHceR0JCu+MB7SCgyxmYfMVU97KkGtA75 QskXiAvxIQqay80slY52Pl6yuT8J+AyUkOK7A6xWEW6pDZltHh4kIBh4d/VEMC84Hi5X cMKEvdBFmSkp321n8SC5TkLNqhiL0P95J1xFLHDlBNtmnx4k2PMeNi80aG/A7jaqUoEF opcXdLbPSkpW9NGHTAB6AWxJl82XcDS4FiDd4g9a1xaXmLNfsjyAsllZp9FQ0VaOJvSF 9Ssg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1695229401; x=1695834201; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=7UwBoraJ2UyiNBA95miiJOkVeBFDlpiyzSKW0JhpuxY=; b=ViHlQFF5ACrZf0vOt9P1rumKvgJYNDPi6RCXsdv3GFZHcPLN/1P6/CmTP9u8a0vH2N vxyQtnEqx1WZRNh4Qfppv0N7eUrRFakg8SbDgmNgP83G0/SnVfPp3bnh6Y6q/8Rfh0+j WJAoLLzKfSNXMBNN1TeAJcX/+XTfD8rZu5NEbQR2jDMHXHri+zonoRSZF8IOQPE/3lpy TzT+4RNGhQHrkUyrAdVQccYymlwncJ1yRX6ytfrUYJCNbFAAG53gD8Yx9/HLiaJ6nqT3 sjDGrbRCzElHG7DhwZdGewbYaGDWIUBqTp6R5VR/7GYYRsrliQAUHpCGsqnbTHQ3uEmI cVxg== X-Gm-Message-State: AOJu0Yw/0seYEcQ/fiyvIczxYVEfpQEkanAwXTHEmZ3fD5oIcTDCW6Ol 1j1RJPkTWSyHmqrLg+J1sw/SwRiHqdFcwJWoVSTcQL0y X-Google-Smtp-Source: AGHT+IGfv6ycigK3zstaVUGeKXS+WIWHwXO3vhnmRWYtvJp0cuxzGiHam065zhtkLr4iMwyDcKEuFg== X-Received: by 2002:a5d:4b87:0:b0:31f:6e29:df8d with SMTP id b7-20020a5d4b87000000b0031f6e29df8dmr2465786wrt.6.1695229400529; Wed, 20 Sep 2023 10:03:20 -0700 (PDT) Received: from localhost.localdomain (abordeaux-655-1-129-86.w90-5.abo.wanadoo.fr. [90.5.10.86]) by smtp.gmail.com with ESMTPSA id g10-20020adff3ca000000b003200c918c81sm11221089wrp.112.2023.09.20.10.03.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 20 Sep 2023 10:03:20 -0700 (PDT) From: David Lechner To: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-staging@lists.linux.dev Cc: David Lechner , linux-kernel@vger.kernel.org, Jonathan Cameron , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Michael Hennerich , =?utf-8?q?Nuno_S=C3=A1?= , Axel Haslam , Philip Molloy , Apelete Seketeli Subject: [PATCH 1/4] dt-bindings: iio: resolver: add devicetree bindings for ad2s1210 Date: Wed, 20 Sep 2023 12:02:50 -0500 Message-Id: <20230920170253.203395-2-dlechner@baylibre.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230920170253.203395-1-dlechner@baylibre.com> References: <20230920170253.203395-1-dlechner@baylibre.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-iio@vger.kernel.org This adds new DeviceTree bindings for the Analog Devices, Inc. AD2S1210 resolver-to-digital converter. Signed-off-by: Apelete Seketeli Signed-off-by: David Lechner --- .../bindings/iio/resolver/adi,ad2s1210.yaml | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/resolver/adi,ad2s1210.yaml diff --git a/Documentation/devicetree/bindings/iio/resolver/adi,ad2s1210.yaml b/Documentation/devicetree/bindings/iio/resolver/adi,ad2s1210.yaml new file mode 100644 index 000000000000..cf6838710e52 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/resolver/adi,ad2s1210.yaml @@ -0,0 +1,150 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/resolver/adi,ad2s1210.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD2S1210 Resolver-to-Digital Converter + +maintainers: + - Michael Hennerich + +description: | + The AD2S1210 is a complete 10-bit to 16-bit resolution tracking + resolver-to-digital converter, integrating an on-board programmable + sinusoidal oscillator that provides sine wave excitation for + resolvers. + + The AD2S1210 allows the user to read the angular position or the + angular velocity data directly from the parallel outputs or through + the serial interface. + + A1 A0 Result + 0 0 Normal mode - position output + 0 1 Normal mode - velocity output + 1 0 Reserved + 1 1 Configuration mode + + In normal mode, the resolution of the digital output is selected using + the RES0 and RES1 input pins. In configuration mode, the resolution is + selected by setting the RES0 and RES1 bits in the control register. + + RES1 RES0 Resolution (Bits) + 0 0 10 + 0 1 12 + 1 0 14 + 1 1 16 + + Note on SPI connections: The CS line on the AD2S1210 should hard-wired to + logic low and the WR/FSYNC line on the AD2S1210 should be connected to the + SPI CSn output of the SPI controller. + + Datasheet: + https://www.analog.com/media/en/technical-documentation/data-sheets/ad2s1210.pdf + +properties: + compatible: + const: adi,ad2s1210 + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 25000000 + + spi-cpha: true + + clocks: + maxItems: 1 + description: External oscillator clock (CLKIN). + + reset-gpios: + description: + GPIO connected to the /RESET pin. As the line needs to be low for the + reset to be active, it should be configured as GPIO_ACTIVE_LOW. + maxItems: 1 + + sample-gpios: + description: | + GPIO connected to the /SAMPLE pin. As the line needs to be low to trigger + a sample, it should be configured as GPIO_ACTIVE_LOW. + maxItems: 1 + + mode-gpios: + description: | + GPIO lines connected to the A0 and A1 pins. These pins select the data + transfer mode. + minItems: 2 + maxItems: 2 + + resolution-gpios: + description: | + GPIO lines connected to the RES0 and RES1 pins. These pins select the + resolution of the digital output. If omitted, it is assumed that the + RES0 and RES1 pins are hard-wired to match the assigned-resolution-bits + property. + minItems: 2 + maxItems: 2 + + fault-gpios: + description: | + GPIO lines connected to the LOT and DOS pins. These pins combined indicate + the type of fault present, if any. As these pins a pulled low to indicate + a fault condition, they should be configured as GPIO_ACTIVE_LOW. + minItems: 2 + maxItems: 2 + + adi,fixed-mode: + description: | + This is used to indicate the selected mode if A0 and A1 are hard-wired + instead of connected to GPIOS (i.e. mode-gpios is omitted). + $ref: /schemas/types.yaml#/definitions/string + enum: ["config", "velocity", "position"] + + assigned-resolution-bits: + description: | + Resolution of the digital output required by the application. This + determines the precision of the angle and/or the maximum speed that can + be measured. If resolution-gpios is omitted, it is assumed that RES0 and + RES1 are hard-wired to match this value. + enum: [10, 12, 14, 16] + +required: + - compatible + - reg + - spi-cpha + - clocks + - sample-gpios + - assigned-resolution-bits + +oneOf: + - required: + - mode-gpios + - required: + - adi,fixed-mode + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + + resolver@0 { + compatible = "adi,ad2s1210"; + reg = <0>; + spi-max-frequency = <20000000>; + spi-cpha; + clocks = <&ext_osc>; + sample-gpios = <&gpio0 90 GPIO_ACTIVE_LOW>; + mode-gpios = <&gpio0 86 0>, <&gpio0 87 0>; + resolution-gpios = <&gpio0 88 0>, <&gpio0 89 0>; + assigned-resolution-bits = <16>; + }; + }; From patchwork Wed Sep 20 17:02:51 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lechner X-Patchwork-Id: 13393076 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 20446C04AAF for ; Wed, 20 Sep 2023 17:03:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234157AbjITRDb (ORCPT ); Wed, 20 Sep 2023 13:03:31 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45226 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234979AbjITRDb (ORCPT ); Wed, 20 Sep 2023 13:03:31 -0400 Received: from mail-wm1-x32b.google.com (mail-wm1-x32b.google.com [IPv6:2a00:1450:4864:20::32b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F2515CF for ; Wed, 20 Sep 2023 10:03:22 -0700 (PDT) Received: by mail-wm1-x32b.google.com with SMTP id 5b1f17b1804b1-40473f1fe9fso311275e9.2 for ; Wed, 20 Sep 2023 10:03:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1695229401; x=1695834201; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=aHyufdv37O6S8ZmknToS8IWvP33+rDz8dd7rQRoiFOI=; b=KP9aHxWaZhXlVlNIRB3+T0QZnZ5b9vMs/w1TCxRev6FWiykYqLh5FowfmBVEIplrc4 5CzYIrxjN4Iv85lIgI6B2taeHkDunL9TWA3pVQxSVtPGyqHATtefNe1eIKLPmj096Dw7 XuGMC6VQZlUZ7g1XBXGHsU9/KXxp6cY+a32qXW5R3cP1cgmEOzNSK69AWjKSpVSUbseE FbDQdck2IqDki4vzsrIq5FJQc8XALTwwemOGW16BYdIZ4xarlwl6+nNwO59rMtHfVzPU YTtFLEk/jtRKE2Y4lH33+OppoYXyN+JHzLvHiTonWgDNmjr2Yq7QBAdkhT1JpGCRlu36 tu5A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1695229401; x=1695834201; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=aHyufdv37O6S8ZmknToS8IWvP33+rDz8dd7rQRoiFOI=; b=e5rJXkXJL2Yg8izZqB8Q3OCbEDG1/QN+ACirrs+a57oOmK7LdOfjZmw//JzPs2haHD 1MK1NIRHxxe2TMsH1yXtXLKHj4o+pOXqGCcFtLwcyH1YZUlW67J6M/2kMAaoLEikzQcw 59+aWo0QMIhDjztwVPvdISVFMZODLHXig7PugIlfjzWEbJptLYNX3eUuV1HnMD7dRBCC 2xW9JpZQSKJHlbu5fue/wtGTMvAjXYsTIct3EoSqbUPlhvlPdAYDXrKQh/sPCSGSUNmy /ESfw5fHVLxwM9SvhYW5Kch8tw5lmbPs4GzZA91BYa3grM4yBpZczIxoSuhWO9uVFbH+ soCA== X-Gm-Message-State: AOJu0YylTc5pN8LLBXlw+MUO8ZnsnuAAF0NLZiFF/jSCNTP+f5prJmiE VVKyBkQPc1qeSm9bi3qe6Z+RWuBMAQsPoNX5G8uwXCyO X-Google-Smtp-Source: AGHT+IEeDYURcssAVtKkHWXgELiscbELYLxLWtKeReqzbYPINC/mwrr6PKnp6JmuUytIY6KCPgVkdw== X-Received: by 2002:a5d:4bc1:0:b0:319:7472:f0b6 with SMTP id l1-20020a5d4bc1000000b003197472f0b6mr3079743wrt.15.1695229401308; Wed, 20 Sep 2023 10:03:21 -0700 (PDT) Received: from localhost.localdomain (abordeaux-655-1-129-86.w90-5.abo.wanadoo.fr. [90.5.10.86]) by smtp.gmail.com with ESMTPSA id g10-20020adff3ca000000b003200c918c81sm11221089wrp.112.2023.09.20.10.03.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 20 Sep 2023 10:03:21 -0700 (PDT) From: David Lechner To: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-staging@lists.linux.dev Cc: David Lechner , linux-kernel@vger.kernel.org, Jonathan Cameron , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Michael Hennerich , =?utf-8?q?Nuno_S=C3=A1?= , Axel Haslam , Philip Molloy Subject: [PATCH 2/4] iio: sysfs: add IIO_DEVICE_ATTR_NAMED_RW macro Date: Wed, 20 Sep 2023 12:02:51 -0500 Message-Id: <20230920170253.203395-3-dlechner@baylibre.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230920170253.203395-1-dlechner@baylibre.com> References: <20230920170253.203395-1-dlechner@baylibre.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-iio@vger.kernel.org This adds a new IIO_DEVICE_ATTR_NAMED_RW to handle the case where multiple attributes share a common implementation so the attribute name and the function names need to be different. Signed-off-by: David Lechner --- include/linux/iio/sysfs.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/linux/iio/sysfs.h b/include/linux/iio/sysfs.h index de5bb125815c..ab20c5294e52 100644 --- a/include/linux/iio/sysfs.h +++ b/include/linux/iio/sysfs.h @@ -87,6 +87,10 @@ struct iio_const_attr { struct iio_dev_attr iio_dev_attr_##_vname \ = IIO_ATTR(_name, _mode, _show, _store, _addr) +#define IIO_DEVICE_ATTR_NAMED_RW(_vname, _name, _addr) \ + struct iio_dev_attr iio_dev_attr_##_vname \ + = IIO_ATTR_RW(_name, _addr) + #define IIO_CONST_ATTR(_name, _string) \ struct iio_const_attr iio_const_attr_##_name \ = { .string = _string, \ From patchwork Wed Sep 20 17:02:52 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lechner X-Patchwork-Id: 13393077 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id BD029C04AB4 for ; Wed, 20 Sep 2023 17:03:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235083AbjITRDd (ORCPT ); Wed, 20 Sep 2023 13:03:33 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45252 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234588AbjITRDd (ORCPT ); Wed, 20 Sep 2023 13:03:33 -0400 Received: from mail-wr1-x42b.google.com (mail-wr1-x42b.google.com [IPv6:2a00:1450:4864:20::42b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2E86DD3 for ; Wed, 20 Sep 2023 10:03:24 -0700 (PDT) Received: by mail-wr1-x42b.google.com with SMTP id ffacd0b85a97d-31dd10c2b8bso31033f8f.3 for ; Wed, 20 Sep 2023 10:03:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1695229402; x=1695834202; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=I0qi+KbLecqFKhPv3lS1tkBt/BnZ4XVrImU5Wur+NK0=; b=bEGB4AcMMYeEkrsP3MWy9LN2HdwVqKV6iwa/U3j1tuCxLrL61CWTYIC1LOXKHEsKzx khZZMQVZyorzA+nsbKY1wpNCC/kjQG/XlNEXEnRUa6M15c1VYDz/eKo5a3obQnec78Om /ia1JMb6PigujqvinHzuwMYMbohEjB1OG1wqbJ37kb0krhUqljgMsM9uMzoMuQVK+M/x tWu8oqetCywpD0Mz3/sHlEbHt7FP4xXm+xg066WSetZe4xsyGxVMPdlMLDz5rkXfDhYJ odz7GvoaXRqlqxbudz3uS19MhdmDp6wP62Ge5CuQvl+s7nV1CaRXScJGS82BVFW7eU0A ewYw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1695229402; x=1695834202; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=I0qi+KbLecqFKhPv3lS1tkBt/BnZ4XVrImU5Wur+NK0=; b=VoG5kAurykDv+Sx8L1WyXlMWAeuMq9pXSbWasdR08MTCKAmfg8JwGMlXVdJ96nZ3cy wG5HuBb02gyvOH3WLzcguE1O/1aQNhS5d9Truoa0KRJQgLkITuWPAzfJWKPO34woswL8 6sxeOMiWyzma2BMZdDoYDMYHIRHsnh8p4/tDFCyDY2TPRFKJnzw89q7limpY1oPhDI4l MInAWJA+9O60qAuomRd2gGraTd/5uWi6MVqOhkTHnh8eL5vvseWpg+0CmyTCjRzS9Xw+ wilu1oQaDmz8gVNp9UGcfx94I4KN2V0mUaoDP1ah2lyY+kVsgGMeMDbWyWNFIgQbMxJg N8tg== X-Gm-Message-State: AOJu0Yyy2YUrRLfYcps59zTWderuOo3dXrR7Ew6WMlYhKuJ0bRke7RbI huC8mJ07V64yQde6DlAwca1P6RzZqefHernyGz9nVx4s X-Google-Smtp-Source: AGHT+IHWn8HSO3o4m52y0ff1wXqC4uY9vLIafgCIvOpsgkokz5ae5HWBtPK72dyqhcON6OFbiEftig== X-Received: by 2002:a5d:58cd:0:b0:321:4de3:fd5c with SMTP id o13-20020a5d58cd000000b003214de3fd5cmr2929754wrf.51.1695229402288; Wed, 20 Sep 2023 10:03:22 -0700 (PDT) Received: from localhost.localdomain (abordeaux-655-1-129-86.w90-5.abo.wanadoo.fr. [90.5.10.86]) by smtp.gmail.com with ESMTPSA id g10-20020adff3ca000000b003200c918c81sm11221089wrp.112.2023.09.20.10.03.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 20 Sep 2023 10:03:21 -0700 (PDT) From: David Lechner To: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-staging@lists.linux.dev Cc: David Lechner , linux-kernel@vger.kernel.org, Jonathan Cameron , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Michael Hennerich , =?utf-8?q?Nuno_S=C3=A1?= , Axel Haslam , Philip Molloy Subject: [PATCH 3/4] staging: iio: resolver: remove ad2s1210 driver Date: Wed, 20 Sep 2023 12:02:52 -0500 Message-Id: <20230920170253.203395-4-dlechner@baylibre.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230920170253.203395-1-dlechner@baylibre.com> References: <20230920170253.203395-1-dlechner@baylibre.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-iio@vger.kernel.org This removes the AD2S1210 resolver driver from staging. It will be improved and resubmitted out of staging. Signed-off-by: David Lechner --- drivers/staging/iio/Kconfig | 1 - drivers/staging/iio/Makefile | 1 - drivers/staging/iio/resolver/Kconfig | 18 - drivers/staging/iio/resolver/Makefile | 6 - drivers/staging/iio/resolver/ad2s1210.c | 716 ------------------------ 5 files changed, 742 deletions(-) delete mode 100644 drivers/staging/iio/resolver/Kconfig delete mode 100644 drivers/staging/iio/resolver/Makefile delete mode 100644 drivers/staging/iio/resolver/ad2s1210.c diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig index d3968fe2ebb8..a60631c1f449 100644 --- a/drivers/staging/iio/Kconfig +++ b/drivers/staging/iio/Kconfig @@ -10,6 +10,5 @@ source "drivers/staging/iio/adc/Kconfig" source "drivers/staging/iio/addac/Kconfig" source "drivers/staging/iio/frequency/Kconfig" source "drivers/staging/iio/impedance-analyzer/Kconfig" -source "drivers/staging/iio/resolver/Kconfig" endmenu diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile index c50f1019f829..628583535393 100644 --- a/drivers/staging/iio/Makefile +++ b/drivers/staging/iio/Makefile @@ -8,4 +8,3 @@ obj-y += adc/ obj-y += addac/ obj-y += frequency/ obj-y += impedance-analyzer/ -obj-y += resolver/ diff --git a/drivers/staging/iio/resolver/Kconfig b/drivers/staging/iio/resolver/Kconfig deleted file mode 100644 index 6d1e2622e0b0..000000000000 --- a/drivers/staging/iio/resolver/Kconfig +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Resolver/Synchro drivers -# -menu "Resolver to digital converters" - -config AD2S1210 - tristate "Analog Devices ad2s1210 driver" - depends on SPI - depends on GPIOLIB || COMPILE_TEST - help - Say yes here to build support for Analog Devices spi resolver - to digital converters, ad2s1210, provides direct access via sysfs. - - To compile this driver as a module, choose M here: the - module will be called ad2s1210. - -endmenu diff --git a/drivers/staging/iio/resolver/Makefile b/drivers/staging/iio/resolver/Makefile deleted file mode 100644 index 398631f7e79b..000000000000 --- a/drivers/staging/iio/resolver/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for Resolver/Synchro drivers -# - -obj-$(CONFIG_AD2S1210) += ad2s1210.o diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c deleted file mode 100644 index 06de5823eb8e..000000000000 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ /dev/null @@ -1,716 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * ad2s1210.c support for the ADI Resolver to Digital Converters: AD2S1210 - * - * Copyright (c) 2010-2010 Analog Devices Inc. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define DRV_NAME "ad2s1210" - -#define AD2S1210_DEF_CONTROL 0x7E - -#define AD2S1210_MSB_IS_HIGH 0x80 -#define AD2S1210_MSB_IS_LOW 0x7F -#define AD2S1210_PHASE_LOCK_RANGE_44 0x20 -#define AD2S1210_ENABLE_HYSTERESIS 0x10 -#define AD2S1210_SET_ENRES1 0x08 -#define AD2S1210_SET_ENRES0 0x04 -#define AD2S1210_SET_RES1 0x02 -#define AD2S1210_SET_RES0 0x01 - -#define AD2S1210_SET_RESOLUTION (AD2S1210_SET_RES1 | AD2S1210_SET_RES0) - -#define AD2S1210_REG_POSITION 0x80 -#define AD2S1210_REG_VELOCITY 0x82 -#define AD2S1210_REG_LOS_THRD 0x88 -#define AD2S1210_REG_DOS_OVR_THRD 0x89 -#define AD2S1210_REG_DOS_MIS_THRD 0x8A -#define AD2S1210_REG_DOS_RST_MAX_THRD 0x8B -#define AD2S1210_REG_DOS_RST_MIN_THRD 0x8C -#define AD2S1210_REG_LOT_HIGH_THRD 0x8D -#define AD2S1210_REG_LOT_LOW_THRD 0x8E -#define AD2S1210_REG_EXCIT_FREQ 0x91 -#define AD2S1210_REG_CONTROL 0x92 -#define AD2S1210_REG_SOFT_RESET 0xF0 -#define AD2S1210_REG_FAULT 0xFF - -#define AD2S1210_MIN_CLKIN 6144000 -#define AD2S1210_MAX_CLKIN 10240000 -#define AD2S1210_MIN_EXCIT 2000 -#define AD2S1210_MAX_EXCIT 20000 -#define AD2S1210_MIN_FCW 0x4 -#define AD2S1210_MAX_FCW 0x50 - -#define AD2S1210_DEF_EXCIT 10000 - -enum ad2s1210_mode { - MOD_POS = 0, - MOD_VEL, - MOD_CONFIG, - MOD_RESERVED, -}; - -enum ad2s1210_gpios { - AD2S1210_SAMPLE, - AD2S1210_A0, - AD2S1210_A1, - AD2S1210_RES0, - AD2S1210_RES1, -}; - -struct ad2s1210_gpio { - const char *name; - unsigned long flags; -}; - -static const struct ad2s1210_gpio gpios[] = { - [AD2S1210_SAMPLE] = { .name = "adi,sample", .flags = GPIOD_OUT_LOW }, - [AD2S1210_A0] = { .name = "adi,a0", .flags = GPIOD_OUT_LOW }, - [AD2S1210_A1] = { .name = "adi,a1", .flags = GPIOD_OUT_LOW }, - [AD2S1210_RES0] = { .name = "adi,res0", .flags = GPIOD_OUT_LOW }, - [AD2S1210_RES1] = { .name = "adi,res1", .flags = GPIOD_OUT_LOW }, -}; - -static const unsigned int ad2s1210_resolution_value[] = { 10, 12, 14, 16 }; - -struct ad2s1210_state { - struct mutex lock; - struct spi_device *sdev; - struct gpio_desc *gpios[5]; - unsigned int fclkin; - unsigned int fexcit; - bool hysteresis; - u8 resolution; - enum ad2s1210_mode mode; - u8 rx[2] __aligned(IIO_DMA_MINALIGN); - u8 tx[2]; -}; - -static const int ad2s1210_mode_vals[4][2] = { - [MOD_POS] = { 0, 0 }, - [MOD_VEL] = { 0, 1 }, - [MOD_CONFIG] = { 1, 1 }, -}; - -static inline void ad2s1210_set_mode(enum ad2s1210_mode mode, - struct ad2s1210_state *st) -{ - gpiod_set_value(st->gpios[AD2S1210_A0], ad2s1210_mode_vals[mode][0]); - gpiod_set_value(st->gpios[AD2S1210_A1], ad2s1210_mode_vals[mode][1]); - st->mode = mode; -} - -/* write 1 bytes (address or data) to the chip */ -static int ad2s1210_config_write(struct ad2s1210_state *st, u8 data) -{ - int ret; - - ad2s1210_set_mode(MOD_CONFIG, st); - st->tx[0] = data; - ret = spi_write(st->sdev, st->tx, 1); - if (ret < 0) - return ret; - - return 0; -} - -/* read value from one of the registers */ -static int ad2s1210_config_read(struct ad2s1210_state *st, - unsigned char address) -{ - struct spi_transfer xfers[] = { - { - .len = 1, - .rx_buf = &st->rx[0], - .tx_buf = &st->tx[0], - .cs_change = 1, - }, { - .len = 1, - .rx_buf = &st->rx[1], - .tx_buf = &st->tx[1], - }, - }; - int ret = 0; - - ad2s1210_set_mode(MOD_CONFIG, st); - st->tx[0] = address | AD2S1210_MSB_IS_HIGH; - st->tx[1] = AD2S1210_REG_FAULT; - ret = spi_sync_transfer(st->sdev, xfers, 2); - if (ret < 0) - return ret; - - return st->rx[1]; -} - -static inline -int ad2s1210_update_frequency_control_word(struct ad2s1210_state *st) -{ - int ret; - unsigned char fcw; - - fcw = (unsigned char)(st->fexcit * (1 << 15) / st->fclkin); - if (fcw < AD2S1210_MIN_FCW || fcw > AD2S1210_MAX_FCW) { - dev_err(&st->sdev->dev, "ad2s1210: FCW out of range\n"); - return -ERANGE; - } - - ret = ad2s1210_config_write(st, AD2S1210_REG_EXCIT_FREQ); - if (ret < 0) - return ret; - - return ad2s1210_config_write(st, fcw); -} - -static const int ad2s1210_res_pins[4][2] = { - { 0, 0 }, {0, 1}, {1, 0}, {1, 1} -}; - -static inline void ad2s1210_set_resolution_pin(struct ad2s1210_state *st) -{ - gpiod_set_value(st->gpios[AD2S1210_RES0], - ad2s1210_res_pins[(st->resolution - 10) / 2][0]); - gpiod_set_value(st->gpios[AD2S1210_RES1], - ad2s1210_res_pins[(st->resolution - 10) / 2][1]); -} - -static inline int ad2s1210_soft_reset(struct ad2s1210_state *st) -{ - int ret; - - ret = ad2s1210_config_write(st, AD2S1210_REG_SOFT_RESET); - if (ret < 0) - return ret; - - return ad2s1210_config_write(st, 0x0); -} - -static ssize_t ad2s1210_show_fclkin(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - - return sprintf(buf, "%u\n", st->fclkin); -} - -static ssize_t ad2s1210_store_fclkin(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - unsigned int fclkin; - int ret; - - ret = kstrtouint(buf, 10, &fclkin); - if (ret) - return ret; - if (fclkin < AD2S1210_MIN_CLKIN || fclkin > AD2S1210_MAX_CLKIN) { - dev_err(dev, "ad2s1210: fclkin out of range\n"); - return -EINVAL; - } - - mutex_lock(&st->lock); - st->fclkin = fclkin; - - ret = ad2s1210_update_frequency_control_word(st); - if (ret < 0) - goto error_ret; - ret = ad2s1210_soft_reset(st); -error_ret: - mutex_unlock(&st->lock); - - return ret < 0 ? ret : len; -} - -static ssize_t ad2s1210_show_fexcit(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - - return sprintf(buf, "%u\n", st->fexcit); -} - -static ssize_t ad2s1210_store_fexcit(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - unsigned int fexcit; - int ret; - - ret = kstrtouint(buf, 10, &fexcit); - if (ret < 0) - return ret; - if (fexcit < AD2S1210_MIN_EXCIT || fexcit > AD2S1210_MAX_EXCIT) { - dev_err(dev, - "ad2s1210: excitation frequency out of range\n"); - return -EINVAL; - } - mutex_lock(&st->lock); - st->fexcit = fexcit; - ret = ad2s1210_update_frequency_control_word(st); - if (ret < 0) - goto error_ret; - ret = ad2s1210_soft_reset(st); -error_ret: - mutex_unlock(&st->lock); - - return ret < 0 ? ret : len; -} - -static ssize_t ad2s1210_show_control(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - int ret; - - mutex_lock(&st->lock); - ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL); - mutex_unlock(&st->lock); - return ret < 0 ? ret : sprintf(buf, "0x%x\n", ret); -} - -static ssize_t ad2s1210_store_control(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - unsigned char udata; - unsigned char data; - int ret; - - ret = kstrtou8(buf, 16, &udata); - if (ret) - return -EINVAL; - - mutex_lock(&st->lock); - ret = ad2s1210_config_write(st, AD2S1210_REG_CONTROL); - if (ret < 0) - goto error_ret; - data = udata & AD2S1210_MSB_IS_LOW; - ret = ad2s1210_config_write(st, data); - if (ret < 0) - goto error_ret; - - ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL); - if (ret < 0) - goto error_ret; - if (ret & AD2S1210_MSB_IS_HIGH) { - ret = -EIO; - dev_err(dev, - "ad2s1210: write control register fail\n"); - goto error_ret; - } - st->resolution = - ad2s1210_resolution_value[data & AD2S1210_SET_RESOLUTION]; - ad2s1210_set_resolution_pin(st); - ret = len; - st->hysteresis = !!(data & AD2S1210_ENABLE_HYSTERESIS); - -error_ret: - mutex_unlock(&st->lock); - return ret; -} - -static ssize_t ad2s1210_show_resolution(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - - return sprintf(buf, "%d\n", st->resolution); -} - -static ssize_t ad2s1210_store_resolution(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - unsigned char data; - unsigned char udata; - int ret; - - ret = kstrtou8(buf, 10, &udata); - if (ret || udata < 10 || udata > 16) { - dev_err(dev, "ad2s1210: resolution out of range\n"); - return -EINVAL; - } - mutex_lock(&st->lock); - ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL); - if (ret < 0) - goto error_ret; - data = ret; - data &= ~AD2S1210_SET_RESOLUTION; - data |= (udata - 10) >> 1; - ret = ad2s1210_config_write(st, AD2S1210_REG_CONTROL); - if (ret < 0) - goto error_ret; - ret = ad2s1210_config_write(st, data & AD2S1210_MSB_IS_LOW); - if (ret < 0) - goto error_ret; - ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL); - if (ret < 0) - goto error_ret; - data = ret; - if (data & AD2S1210_MSB_IS_HIGH) { - ret = -EIO; - dev_err(dev, "ad2s1210: setting resolution fail\n"); - goto error_ret; - } - st->resolution = - ad2s1210_resolution_value[data & AD2S1210_SET_RESOLUTION]; - ad2s1210_set_resolution_pin(st); - ret = len; -error_ret: - mutex_unlock(&st->lock); - return ret; -} - -/* read the fault register since last sample */ -static ssize_t ad2s1210_show_fault(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - int ret; - - mutex_lock(&st->lock); - ret = ad2s1210_config_read(st, AD2S1210_REG_FAULT); - mutex_unlock(&st->lock); - - return ret ? ret : sprintf(buf, "0x%x\n", ret); -} - -static ssize_t ad2s1210_clear_fault(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - int ret; - - mutex_lock(&st->lock); - gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 0); - /* delay (2 * tck + 20) nano seconds */ - udelay(1); - gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 1); - ret = ad2s1210_config_read(st, AD2S1210_REG_FAULT); - if (ret < 0) - goto error_ret; - gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 0); - gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 1); -error_ret: - mutex_unlock(&st->lock); - - return ret < 0 ? ret : len; -} - -static ssize_t ad2s1210_show_reg(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - struct iio_dev_attr *iattr = to_iio_dev_attr(attr); - int ret; - - mutex_lock(&st->lock); - ret = ad2s1210_config_read(st, iattr->address); - mutex_unlock(&st->lock); - - return ret < 0 ? ret : sprintf(buf, "%d\n", ret); -} - -static ssize_t ad2s1210_store_reg(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); - unsigned char data; - int ret; - struct iio_dev_attr *iattr = to_iio_dev_attr(attr); - - ret = kstrtou8(buf, 10, &data); - if (ret) - return -EINVAL; - mutex_lock(&st->lock); - ret = ad2s1210_config_write(st, iattr->address); - if (ret < 0) - goto error_ret; - ret = ad2s1210_config_write(st, data & AD2S1210_MSB_IS_LOW); -error_ret: - mutex_unlock(&st->lock); - return ret < 0 ? ret : len; -} - -static int ad2s1210_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long m) -{ - struct ad2s1210_state *st = iio_priv(indio_dev); - u16 negative; - int ret = 0; - u16 pos; - s16 vel; - - mutex_lock(&st->lock); - gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 0); - /* delay (6 * tck + 20) nano seconds */ - udelay(1); - - switch (chan->type) { - case IIO_ANGL: - ad2s1210_set_mode(MOD_POS, st); - break; - case IIO_ANGL_VEL: - ad2s1210_set_mode(MOD_VEL, st); - break; - default: - ret = -EINVAL; - break; - } - if (ret < 0) - goto error_ret; - ret = spi_read(st->sdev, st->rx, 2); - if (ret < 0) - goto error_ret; - - switch (chan->type) { - case IIO_ANGL: - pos = be16_to_cpup((__be16 *)st->rx); - if (st->hysteresis) - pos >>= 16 - st->resolution; - *val = pos; - ret = IIO_VAL_INT; - break; - case IIO_ANGL_VEL: - vel = be16_to_cpup((__be16 *)st->rx); - vel >>= 16 - st->resolution; - if (vel & 0x8000) { - negative = (0xffff >> st->resolution) << st->resolution; - vel |= negative; - } - *val = vel; - ret = IIO_VAL_INT; - break; - default: - mutex_unlock(&st->lock); - return -EINVAL; - } - -error_ret: - gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 1); - /* delay (2 * tck + 20) nano seconds */ - udelay(1); - mutex_unlock(&st->lock); - return ret; -} - -static IIO_DEVICE_ATTR(fclkin, 0644, - ad2s1210_show_fclkin, ad2s1210_store_fclkin, 0); -static IIO_DEVICE_ATTR(fexcit, 0644, - ad2s1210_show_fexcit, ad2s1210_store_fexcit, 0); -static IIO_DEVICE_ATTR(control, 0644, - ad2s1210_show_control, ad2s1210_store_control, 0); -static IIO_DEVICE_ATTR(bits, 0644, - ad2s1210_show_resolution, ad2s1210_store_resolution, 0); -static IIO_DEVICE_ATTR(fault, 0644, - ad2s1210_show_fault, ad2s1210_clear_fault, 0); - -static IIO_DEVICE_ATTR(los_thrd, 0644, - ad2s1210_show_reg, ad2s1210_store_reg, - AD2S1210_REG_LOS_THRD); -static IIO_DEVICE_ATTR(dos_ovr_thrd, 0644, - ad2s1210_show_reg, ad2s1210_store_reg, - AD2S1210_REG_DOS_OVR_THRD); -static IIO_DEVICE_ATTR(dos_mis_thrd, 0644, - ad2s1210_show_reg, ad2s1210_store_reg, - AD2S1210_REG_DOS_MIS_THRD); -static IIO_DEVICE_ATTR(dos_rst_max_thrd, 0644, - ad2s1210_show_reg, ad2s1210_store_reg, - AD2S1210_REG_DOS_RST_MAX_THRD); -static IIO_DEVICE_ATTR(dos_rst_min_thrd, 0644, - ad2s1210_show_reg, ad2s1210_store_reg, - AD2S1210_REG_DOS_RST_MIN_THRD); -static IIO_DEVICE_ATTR(lot_high_thrd, 0644, - ad2s1210_show_reg, ad2s1210_store_reg, - AD2S1210_REG_LOT_HIGH_THRD); -static IIO_DEVICE_ATTR(lot_low_thrd, 0644, - ad2s1210_show_reg, ad2s1210_store_reg, - AD2S1210_REG_LOT_LOW_THRD); - -static const struct iio_chan_spec ad2s1210_channels[] = { - { - .type = IIO_ANGL, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - }, { - .type = IIO_ANGL_VEL, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - } -}; - -static struct attribute *ad2s1210_attributes[] = { - &iio_dev_attr_fclkin.dev_attr.attr, - &iio_dev_attr_fexcit.dev_attr.attr, - &iio_dev_attr_control.dev_attr.attr, - &iio_dev_attr_bits.dev_attr.attr, - &iio_dev_attr_fault.dev_attr.attr, - &iio_dev_attr_los_thrd.dev_attr.attr, - &iio_dev_attr_dos_ovr_thrd.dev_attr.attr, - &iio_dev_attr_dos_mis_thrd.dev_attr.attr, - &iio_dev_attr_dos_rst_max_thrd.dev_attr.attr, - &iio_dev_attr_dos_rst_min_thrd.dev_attr.attr, - &iio_dev_attr_lot_high_thrd.dev_attr.attr, - &iio_dev_attr_lot_low_thrd.dev_attr.attr, - NULL, -}; - -static const struct attribute_group ad2s1210_attribute_group = { - .attrs = ad2s1210_attributes, -}; - -static int ad2s1210_initial(struct ad2s1210_state *st) -{ - unsigned char data; - int ret; - - mutex_lock(&st->lock); - ad2s1210_set_resolution_pin(st); - - ret = ad2s1210_config_write(st, AD2S1210_REG_CONTROL); - if (ret < 0) - goto error_ret; - data = AD2S1210_DEF_CONTROL & ~(AD2S1210_SET_RESOLUTION); - data |= (st->resolution - 10) >> 1; - ret = ad2s1210_config_write(st, data); - if (ret < 0) - goto error_ret; - ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL); - if (ret < 0) - goto error_ret; - - if (ret & AD2S1210_MSB_IS_HIGH) { - ret = -EIO; - goto error_ret; - } - - ret = ad2s1210_update_frequency_control_word(st); - if (ret < 0) - goto error_ret; - ret = ad2s1210_soft_reset(st); -error_ret: - mutex_unlock(&st->lock); - return ret; -} - -static const struct iio_info ad2s1210_info = { - .read_raw = ad2s1210_read_raw, - .attrs = &ad2s1210_attribute_group, -}; - -static int ad2s1210_setup_gpios(struct ad2s1210_state *st) -{ - struct spi_device *spi = st->sdev; - int i, ret; - - for (i = 0; i < ARRAY_SIZE(gpios); i++) { - st->gpios[i] = devm_gpiod_get(&spi->dev, gpios[i].name, - gpios[i].flags); - if (IS_ERR(st->gpios[i])) { - ret = PTR_ERR(st->gpios[i]); - dev_err(&spi->dev, - "ad2s1210: failed to request %s GPIO: %d\n", - gpios[i].name, ret); - return ret; - } - } - - return 0; -} - -static int ad2s1210_probe(struct spi_device *spi) -{ - struct iio_dev *indio_dev; - struct ad2s1210_state *st; - int ret; - - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); - if (!indio_dev) - return -ENOMEM; - st = iio_priv(indio_dev); - ret = ad2s1210_setup_gpios(st); - if (ret < 0) - return ret; - - spi_set_drvdata(spi, indio_dev); - - mutex_init(&st->lock); - st->sdev = spi; - st->hysteresis = true; - st->mode = MOD_CONFIG; - st->resolution = 12; - st->fexcit = AD2S1210_DEF_EXCIT; - - indio_dev->info = &ad2s1210_info; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = ad2s1210_channels; - indio_dev->num_channels = ARRAY_SIZE(ad2s1210_channels); - indio_dev->name = spi_get_device_id(spi)->name; - - ret = devm_iio_device_register(&spi->dev, indio_dev); - if (ret) - return ret; - - st->fclkin = spi->max_speed_hz; - spi->mode = SPI_MODE_3; - spi_setup(spi); - ad2s1210_initial(st); - - return 0; -} - -static const struct of_device_id ad2s1210_of_match[] = { - { .compatible = "adi,ad2s1210", }, - { } -}; -MODULE_DEVICE_TABLE(of, ad2s1210_of_match); - -static const struct spi_device_id ad2s1210_id[] = { - { "ad2s1210" }, - {} -}; -MODULE_DEVICE_TABLE(spi, ad2s1210_id); - -static struct spi_driver ad2s1210_driver = { - .driver = { - .name = DRV_NAME, - .of_match_table = of_match_ptr(ad2s1210_of_match), - }, - .probe = ad2s1210_probe, - .id_table = ad2s1210_id, -}; -module_spi_driver(ad2s1210_driver); - -MODULE_AUTHOR("Graff Yang "); -MODULE_DESCRIPTION("Analog Devices AD2S1210 Resolver to Digital SPI driver"); -MODULE_LICENSE("GPL v2"); From patchwork Wed Sep 20 17:02:53 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lechner X-Patchwork-Id: 13393078 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7CD67C04AAF for ; Wed, 20 Sep 2023 17:03:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235181AbjITRDm (ORCPT ); Wed, 20 Sep 2023 13:03:42 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33494 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235105AbjITRDe (ORCPT ); Wed, 20 Sep 2023 13:03:34 -0400 Received: from mail-wm1-x32e.google.com (mail-wm1-x32e.google.com [IPv6:2a00:1450:4864:20::32e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1BE19DC for ; Wed, 20 Sep 2023 10:03:25 -0700 (PDT) Received: by mail-wm1-x32e.google.com with SMTP id 5b1f17b1804b1-40476ce8b2fso267245e9.3 for ; Wed, 20 Sep 2023 10:03:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1695229403; x=1695834203; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=ZinPJ5PYB2eid5QoM+L4lgEz+bW8cvHUuVjkVPgA4pk=; b=j8UUajMdGwVtd3l/SeAm2L6p/J1g9XQXmUeYFIppqIZ4FRz05OWah4K7Ec9DU7YaYV fWKVjx5LBU6kzJ3M0kumbwQUou/mfWz2RlhTe7hYgm4bX5xuiHePnjAu0rhqunpEyAcB gzaH0X+5J/48OyR53vJOkW31k0g3hC7sz/MLPfi4h3giA5t352SANaS8nKEILQzf3jBn NTWKjPhrT50lJrmE13VgQ2NN/rorl74vOtDs1aBwUoLLqqRjrfz0xzdcPuha/gw+OmTM vMSsZfVfTUugFEl1FujCYXAeJ0Q/MQ5yvh4LFOWtmKyj1DF21wWGw3A4stn9C2KBeblP ozlQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1695229403; x=1695834203; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=ZinPJ5PYB2eid5QoM+L4lgEz+bW8cvHUuVjkVPgA4pk=; b=TWuStAZ/RliAc30k+/Dt6yXJffK/Kk4NsjS+1jJ399d+yQ+bheyktTWayJazwC7buz lk/+uu9eqIkVLiTbTin2FlQUpPX74m/YZW/GUtO1corhk5u3dELsEs20hb0ZKR0XGBI/ NaKKgjKnK8aEvyldLxPA5nbj65urH/M4J/uE77GoQfgkDZtxkx52TMNnSol8kUSCtXh4 RhLTuoFez2mQenS9u3zWgtqzZtJtGkj1W2DgM/JgaoaBr3oGs2OQ8d9X6gIO0uW6exFN ZxGD5/5265lI10NHCIZhUy3wKiztFA9O8bGmjmGgjl44l6VAprsvgvUTgApSLihlowNr x6GA== X-Gm-Message-State: AOJu0YyroeOt0OuPRf3Ohkqfze8olv9j+3YGVOHGRBYq3bpYcbJd0y6S 9m/gC2M/D5cyoZso33nxagxz/YFUheAG6jabThyNIX/n X-Google-Smtp-Source: AGHT+IHkKZGhqZcw0k0xvwEozSAZi6uSahTQ7XYP5azIWJt3sy24MLTbjVNR1j+0gncdx74Uc44lNA== X-Received: by 2002:a5d:6b88:0:b0:317:5168:c21f with SMTP id n8-20020a5d6b88000000b003175168c21fmr2838659wrx.31.1695229403189; Wed, 20 Sep 2023 10:03:23 -0700 (PDT) Received: from localhost.localdomain (abordeaux-655-1-129-86.w90-5.abo.wanadoo.fr. [90.5.10.86]) by smtp.gmail.com with ESMTPSA id g10-20020adff3ca000000b003200c918c81sm11221089wrp.112.2023.09.20.10.03.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 20 Sep 2023 10:03:22 -0700 (PDT) From: David Lechner To: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-staging@lists.linux.dev Cc: David Lechner , linux-kernel@vger.kernel.org, Jonathan Cameron , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Michael Hennerich , =?utf-8?q?Nuno_S=C3=A1?= , Axel Haslam , Philip Molloy Subject: [PATCH 4/4] iio: resolver: add new driver for AD2S1210 Date: Wed, 20 Sep 2023 12:02:53 -0500 Message-Id: <20230920170253.203395-5-dlechner@baylibre.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230920170253.203395-1-dlechner@baylibre.com> References: <20230920170253.203395-1-dlechner@baylibre.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-iio@vger.kernel.org This adds a new driver for Analog Devices, Inc. AD2S1210 resolver to digital converter. The driver is based on the staging driver with the following improvements: Fixes: - Fix use before initialization bug in probe. - Fix not checking error returns in probe. - Remove spi_setup() and spi_set_drvdata() from probe. - Fix ordering of devm_iio_device_register() - Remove incorrect hysteresis logic Changes: - Use BIT/GENMASK macros. - Use devicetree to get CLKIN frequency (was sysfs attribute). - No longer bit-shift the raw value for IIO_CHAN_INFO_RAW. - Use regmap for register access. - Remove config sysfs attribute. - Use gpio array for mode and resolution gpios. - Invert sample gpio logic and use GPIO_ACTIVE_LOW in devicetree. - Change hysteresis to use IIO_CHAN_INFO_HYSTERESIS instead of custom device attribute. - Rename fexcit attribute to excitation_frequency. - Use devicetree to specify resolution instead of sysfs attribute. Additions: - Implement IIO_CHAN_INFO_SCALE. - Implement debugfs register access. - Add phase_lock_range attribute. - Add triggered buffer support. Signed-off-by: David Lechner --- .../testing/sysfs-bus-iio-resolver-ad2s1210 | 109 ++ drivers/iio/resolver/Kconfig | 13 + drivers/iio/resolver/Makefile | 1 + drivers/iio/resolver/ad2s1210.c | 948 ++++++++++++++++++ 4 files changed, 1071 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-resolver-ad2s1210 create mode 100644 drivers/iio/resolver/ad2s1210.c diff --git a/Documentation/ABI/testing/sysfs-bus-iio-resolver-ad2s1210 b/Documentation/ABI/testing/sysfs-bus-iio-resolver-ad2s1210 new file mode 100644 index 000000000000..32890c85168e --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-resolver-ad2s1210 @@ -0,0 +1,109 @@ +What: /sys/bus/iio/devices/iio:deviceX/dos_mis_thrd +KernelVersion: 6.7 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns the current Degradation of Signal Mismatch + Threshold value. Writing sets the value. Valid values are 0 (0V) + to 127 (4.826V). To convert the value to volts, multiply by + 0.038. + +What: /sys/bus/iio/devices/iio:deviceX/dos_ovr_thrd +KernelVersion: 6.7 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns the current Degradation of Signal Overrange + Threshold value. Writing sets the value. Valid values are 0 (0V) + to 127 (4.826V). To convert the value to volts, multiply by + 0.038. + +What: /sys/bus/iio/devices/iio:deviceX/dos_rst_max_thrd +KernelVersion: 6.7 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns the current Degradation of Signal Reset Maximum + Threshold value. Writing sets the value. Valid values are 0 (0V) + to 127 (4.826V). To convert the value to volts, multiply by + 0.038. + +What: /sys/bus/iio/devices/iio:deviceX/dos_rst_min_thrd +KernelVersion: 6.7 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns the current Degradation of Signal Reset Minimum + Threshold value. Writing sets the value. Valid values are 0 (0V) + to 127 (4.826V). To convert the value to volts, multiply by + 0.038. + +What: /sys/bus/iio/devices/iio:deviceX/fault +KernelVersion: 6.7 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns a hex value containing the fault bit flags. + + Bit Description + --- ----------- + D7 Sine/cosine inputs clipped + D6 Sine/cosine inputs below LOS threshold + D5 Sine/cosine inputs exceed DOS overrange threshold + D4 Sine/cosine inputs exceed DOS mismatch threshold + D3 Tracking error exceeds LOT threshold + D2 Velocity exceeds maximum tracking rate + D1 Phase error exceeds phase lock range + D0 Configuration parity error + + Writing any value will clear any fault conditions. + +What: /sys/bus/iio/devices/iio:deviceX/excitation_frequency +KernelVersion: 6.7 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns the current Excitation Frequency in Hz. Writing + sets the Excitation Frequency and performs a software reset on + the device to apply the change. Valid values are 2000 (2kHz) to + 20000 (20kHz). + +What: /sys/bus/iio/devices/iio:deviceX/los_thrd +KernelVersion: 6.7 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns the current Loss of Signal Reset Threshold + value. Writing sets the value. Valid values are 0 (0V) to + 127 (4.826V). To convert the value to volts, multiply by 0.038. + +What: /sys/bus/iio/devices/iio:deviceX/lot_high_thrd +KernelVersion: 6.7 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns the current Loss of Position Tracking Detection + High Threshold value. Writing sets the value. Valid values are + 0 (0 deg) to 127 (9/18/45 deg). The interpretation of the value + depends on the selected resolution. To convert the value to + degrees, multiply by 0.35 for 10-bit resolution, multiply by + 0.14 for 12-bit resolution or multiply by 0.09 for 14 and 16-bit + resolution. + +What: /sys/bus/iio/devices/iio:deviceX/lot_low_thrd +KernelVersion: 6.7 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns the current Loss of Position Tracking Detection + Low Threshold value. Writing sets the value. Valid values are + 0 (0 deg) to 127 (9/18/45 deg). The interpretation of the value + depends on the selected resolution. To convert the value to + degrees, multiply by 0.35 for 10-bit resolution, multiply by + 0.14 for 12-bit resolution or multiply by 0.09 for 14 and 16-bit + resolution. + +What: /sys/bus/iio/devices/iio:deviceX/phase_lock_range +KernelVersion: 6.7 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns the current Phase lock range in degrees. Writing + sets the value in the configuration register. + +What: /sys/bus/iio/devices/iio:deviceX/phase_lock_range_available +KernelVersion: 6.7 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns the possible values for the phase_lock_range + attribute, namely 44 and 360. diff --git a/drivers/iio/resolver/Kconfig b/drivers/iio/resolver/Kconfig index 47dbfead9b31..424529d36080 100644 --- a/drivers/iio/resolver/Kconfig +++ b/drivers/iio/resolver/Kconfig @@ -25,4 +25,17 @@ config AD2S1200 To compile this driver as a module, choose M here: the module will be called ad2s1200. + +config AD2S1210 + tristate "Analog Devices ad2s1210 driver" + depends on SPI + depends on COMMON_CLK + depends on GPIOLIB || COMPILE_TEST + help + Say yes here to build support for Analog Devices spi resolver + to digital converters, ad2s1210, provides direct access via sysfs. + + To compile this driver as a module, choose M here: the + module will be called ad2s1210. + endmenu diff --git a/drivers/iio/resolver/Makefile b/drivers/iio/resolver/Makefile index fa558138ce45..7f6c876c35ae 100644 --- a/drivers/iio/resolver/Makefile +++ b/drivers/iio/resolver/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_AD2S90) += ad2s90.o obj-$(CONFIG_AD2S1200) += ad2s1200.o +obj-$(CONFIG_AD2S1210) += ad2s1210.o diff --git a/drivers/iio/resolver/ad2s1210.c b/drivers/iio/resolver/ad2s1210.c new file mode 100644 index 000000000000..97833fbcbf7a --- /dev/null +++ b/drivers/iio/resolver/ad2s1210.c @@ -0,0 +1,948 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ad2s1210.c support for the ADI Resolver to Digital Converters: AD2S1210 + * + * Copyright (c) 2010-2010 Analog Devices Inc. + * Copyright (C) 2023 BayLibre, SAS + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define DRV_NAME "ad2s1210" + +/* default value of control register on powerup */ +#define AD2S1210_DEF_CONTROL 0x7E + +/* control register flags */ +#define AD2S1210_ADDRESS_DATA BIT(7) +#define AD2S1210_PHASE_LOCK_RANGE_44 BIT(5) +#define AD2S1210_ENABLE_HYSTERESIS BIT(4) +#define AD2S1210_SET_ENRES GENMASK(3, 2) +#define AD2S1210_SET_RES GENMASK(1, 0) + +#define AD2S1210_REG_POSITION_MSB 0x80 +#define AD2S1210_REG_POSITION_LSB 0x81 +#define AD2S1210_REG_VELOCITY_MSB 0x82 +#define AD2S1210_REG_VELOCITY_LSB 0x83 +#define AD2S1210_REG_LOS_THRD 0x88 +#define AD2S1210_REG_DOS_OVR_THRD 0x89 +#define AD2S1210_REG_DOS_MIS_THRD 0x8A +#define AD2S1210_REG_DOS_RST_MAX_THRD 0x8B +#define AD2S1210_REG_DOS_RST_MIN_THRD 0x8C +#define AD2S1210_REG_LOT_HIGH_THRD 0x8D +#define AD2S1210_REG_LOT_LOW_THRD 0x8E +#define AD2S1210_REG_EXCIT_FREQ 0x91 +#define AD2S1210_REG_CONTROL 0x92 +#define AD2S1210_REG_SOFT_RESET 0xF0 +#define AD2S1210_REG_FAULT 0xFF + +#define AD2S1210_MIN_CLKIN 6144000 +#define AD2S1210_MAX_CLKIN 10240000 +#define AD2S1210_MIN_EXCIT 2000 +#define AD2S1210_DEF_EXCIT 10000 +#define AD2S1210_MAX_EXCIT 20000 +#define AD2S1210_MIN_FCW 0x4 +#define AD2S1210_MAX_FCW 0x50 + +enum ad2s1210_mode { + AD2S1210_MODE_POS = 0b00, + AD2S1210_MODE_VEL = 0b01, + AD2S1210_MODE_CONFIG = 0b11, +}; + +enum ad2s1210_resolution { + AD2S1210_RES_10 = 0b00, + AD2S1210_RES_12 = 0b01, + AD2S1210_RES_14 = 0b10, + AD2S1210_RES_16 = 0b11, +}; + +struct ad2s1210_state { + struct mutex lock; + struct spi_device *sdev; + /** GPIO pin connected to SAMPLE line. */ + struct gpio_desc *sample_gpio; + /** GPIO pins connected to A0 and A1 lines. */ + struct gpio_descs *mode_gpios; + /** Used to access config registers. */ + struct regmap *regmap; + /** The external oscillator frequency in Hz. */ + unsigned long fclkin; + /** The selected resolution */ + enum ad2s1210_resolution resolution; + /* Scan buffer */ + struct { + __be16 chan[2]; + /* Ensure timestamp is naturally aligned. */ + s64 timestamp __aligned(8); + } scan; + u8 rx[2] __aligned(IIO_DMA_MINALIGN); + u8 tx[2]; +}; + +static int ad2s1210_set_mode(struct ad2s1210_state *st, enum ad2s1210_mode mode) +{ + struct gpio_descs *gpios = st->mode_gpios; + DECLARE_BITMAP(bitmap, 2); + + bitmap[0] = mode; + + return gpiod_set_array_value(gpios->ndescs, gpios->desc, gpios->info, + bitmap); +} + +/** + * Writes the given data to the given register address. + * + * If the mode is configurable, the device will first be placed in + * configuration mode. + */ +static int ad2s1210_regmap_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct ad2s1210_state *st = context; + struct spi_transfer xfers[] = { + { + .len = 1, + .rx_buf = &st->rx[0], + .tx_buf = &st->tx[0], + .cs_change = 1, + }, { + .len = 1, + .rx_buf = &st->rx[1], + .tx_buf = &st->tx[1], + }, + }; + int ret; + + /* values can only be 7 bits, the MSB indicates an address */ + if (val & ~0x7F) + return -EINVAL; + + st->tx[0] = reg; + st->tx[1] = val; + + ret = ad2s1210_set_mode(st, AD2S1210_MODE_CONFIG); + if (ret < 0) + return ret; + + return spi_sync_transfer(st->sdev, xfers, ARRAY_SIZE(xfers)); +} + +/** + * Reads value from one of the registers. + * + * If the mode is configurable, the device will first be placed in + * configuration mode. + */ +static int ad2s1210_regmap_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct ad2s1210_state *st = context; + struct spi_transfer xfers[] = { + { + .len = 1, + .rx_buf = &st->rx[0], + .tx_buf = &st->tx[0], + .cs_change = 1, + }, { + .len = 1, + .rx_buf = &st->rx[1], + .tx_buf = &st->tx[1], + }, + }; + int ret; + + ret = ad2s1210_set_mode(st, AD2S1210_MODE_CONFIG); + if (ret < 0) + return ret; + + st->tx[0] = reg; + /* Must be valid register address here otherwise this could write data. + * It doesn't matter which one. + */ + st->tx[1] = AD2S1210_REG_FAULT; + + ret = spi_sync_transfer(st->sdev, xfers, ARRAY_SIZE(xfers)); + if (ret < 0) + return ret; + + /* If the D7 bit is set on any read/write register, it indicates a + * parity error. The fault register is read-only and the D7 bit means + * something else there. + */ + if (reg != AD2S1210_REG_FAULT && st->rx[1] & AD2S1210_ADDRESS_DATA) + return -EBADMSG; + + *val = st->rx[1]; + + return 0; +} + +/** + * Sets the excitation frequency and performs software reset. + * + * Must be called with lock held. + */ +static int ad2s1210_set_excitation_frequency(struct ad2s1210_state *st, + u16 fexcit) +{ + int ret; + u8 fcw; + + fcw = fexcit * (1 << 15) / st->fclkin; + if (fcw < AD2S1210_MIN_FCW || fcw > AD2S1210_MAX_FCW) + return -ERANGE; + + ret = regmap_write(st->regmap, AD2S1210_REG_EXCIT_FREQ, fcw); + if (ret < 0) + return ret; + + /* software reset reinitializes the excitation frequency output */ + return regmap_write(st->regmap, AD2S1210_REG_SOFT_RESET, 0); +} + +static ssize_t excitation_frequency_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); + unsigned int value; + u16 fexcit; + int ret; + + mutex_lock(&st->lock); + ret = regmap_read(st->regmap, AD2S1210_REG_EXCIT_FREQ, &value); + if (ret < 0) + goto error_ret; + + fexcit = value * st->fclkin / (1 << 15); + + ret = sysfs_emit(buf, "%u\n", fexcit); + +error_ret: + mutex_unlock(&st->lock); + return ret; +} + +static ssize_t excitation_frequency_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); + u16 fexcit; + int ret; + + ret = kstrtou16(buf, 10, &fexcit); + if (ret < 0 || fexcit < AD2S1210_MIN_EXCIT || fexcit > AD2S1210_MAX_EXCIT) + return -EINVAL; + + mutex_lock(&st->lock); + ret = ad2s1210_set_excitation_frequency(st, fexcit); + if (ret < 0) + goto error_ret; + + ret = len; + +error_ret: + mutex_unlock(&st->lock); + + return ret; +} + +static ssize_t phase_lock_range_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); + int ret; + + mutex_lock(&st->lock); + ret = regmap_test_bits(st->regmap, AD2S1210_REG_CONTROL, + AD2S1210_PHASE_LOCK_RANGE_44); + if (ret < 0) + goto error_ret; + + ret = sysfs_emit(buf, "%d\n", ret ? 44 : 360); + +error_ret: + mutex_unlock(&st->lock); + return ret; +} + +static ssize_t phase_lock_range_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); + u16 udata; + int ret; + + ret = kstrtou16(buf, 10, &udata); + if (ret < 0 || (udata != 44 && udata != 360)) + return -EINVAL; + + mutex_lock(&st->lock); + + ret = regmap_update_bits(st->regmap, AD2S1210_REG_CONTROL, + AD2S1210_PHASE_LOCK_RANGE_44, + udata == 44 ? AD2S1210_PHASE_LOCK_RANGE_44 : 0); + if (ret < 0) + goto error_ret; + + ret = len; + +error_ret: + mutex_unlock(&st->lock); + return ret; +} + +static ssize_t phase_lock_range_available_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "44 360\n"); +} + +/* read the fault register since last sample */ +static ssize_t fault_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); + unsigned int value; + int ret; + + mutex_lock(&st->lock); + ret = regmap_read(st->regmap, AD2S1210_REG_FAULT, &value); + mutex_unlock(&st->lock); + + return ret < 0 ? ret : sysfs_emit(buf, "0x%02x\n", value); +} + +static ssize_t fault_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); + unsigned int value; + int ret; + + mutex_lock(&st->lock); + + gpiod_set_value(st->sample_gpio, 1); + /* delay (2 * tck + 20) nano seconds */ + udelay(1); + gpiod_set_value(st->sample_gpio, 0); + + ret = regmap_read(st->regmap, AD2S1210_REG_FAULT, &value); + if (ret < 0) + goto error_ret; + + gpiod_set_value(st->sample_gpio, 1); + gpiod_set_value(st->sample_gpio, 0); + +error_ret: + mutex_unlock(&st->lock); + + return ret < 0 ? ret : len; +} + +static ssize_t reg_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); + struct iio_dev_attr *iattr = to_iio_dev_attr(attr); + unsigned int value; + int ret; + + mutex_lock(&st->lock); + ret = regmap_read(st->regmap, iattr->address, &value); + mutex_unlock(&st->lock); + + return ret < 0 ? ret : sysfs_emit(buf, "%d\n", value); +} + +static ssize_t reg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); + unsigned char data; + int ret; + struct iio_dev_attr *iattr = to_iio_dev_attr(attr); + + ret = kstrtou8(buf, 10, &data); + if (ret < 0) + return -EINVAL; + + mutex_lock(&st->lock); + ret = regmap_write(st->regmap, iattr->address, data); + mutex_unlock(&st->lock); + return ret < 0 ? ret : len; +} + +static const int ad2s1210_velocity_scale[] = { + 17089132, /* 8.192MHz / (2*pi * 2500 / 2^15) */ + 42722830, /* 8.192MHz / (2*pi * 1000 / 2^15) */ + 85445659, /* 8.192MHz / (2*pi * 500 / 2^15) */ + 341782638, /* 8.192MHz / (2*pi * 125 / 2^15) */ +}; + +static int ad2s1210_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct ad2s1210_state *st = iio_priv(indio_dev); + int ret = 0; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&st->lock); + gpiod_set_value(st->sample_gpio, 1); + /* delay (6 * tck + 20) nano seconds */ + udelay(1); + + switch (chan->type) { + case IIO_ANGL: + ret = ad2s1210_set_mode(st, AD2S1210_MODE_POS); + break; + case IIO_ANGL_VEL: + ret = ad2s1210_set_mode(st, AD2S1210_MODE_VEL); + break; + default: + ret = -EINVAL; + break; + } + if (ret < 0) + goto error_info_raw; + ret = spi_read(st->sdev, st->rx, 2); + if (ret < 0) + goto error_info_raw; + + switch (chan->type) { + case IIO_ANGL: + *val = be16_to_cpup((__be16 *)st->rx); + ret = IIO_VAL_INT; + break; + case IIO_ANGL_VEL: + *val = (s16)be16_to_cpup((__be16 *)st->rx); + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + break; + } + +error_info_raw: + gpiod_set_value(st->sample_gpio, 0); + /* delay (2 * tck + 20) nano seconds */ + udelay(1); + mutex_unlock(&st->lock); + break; + + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_ANGL: + /* approx 0.3 arc min converted to radians */ + *val = 0; + *val2 = 95874; + ret = IIO_VAL_INT_PLUS_NANO; + break; + case IIO_ANGL_VEL: + *val = st->fclkin; + *val2 = ad2s1210_velocity_scale[st->resolution]; + ret = IIO_VAL_FRACTIONAL; + break; + default: + ret = -EINVAL; + break; + } + break; + + case IIO_CHAN_INFO_HYSTERESIS: + switch (chan->type) { + case IIO_ANGL: + mutex_lock(&st->lock); + ret = regmap_test_bits(st->regmap, AD2S1210_REG_CONTROL, + AD2S1210_ENABLE_HYSTERESIS); + if (ret < 0) + goto error_info_hysteresis; + + *val = !!ret; + ret = IIO_VAL_INT; + +error_info_hysteresis: + mutex_unlock(&st->lock); + break; + default: + ret = -EINVAL; + break; + } + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int ad2s1210_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, + int *length, long mask) +{ + static const int available[] = { 0, 1 }; + int ret = -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_HYSTERESIS: + switch (chan->type) { + case IIO_ANGL: + *vals = available; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(available); + ret = IIO_AVAIL_LIST; + break; + default: + break; + } + default: + break; + } + + return ret; +} + +static int ad2s1210_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ad2s1210_state *st = iio_priv(indio_dev); + int ret = -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_HYSTERESIS: + switch (chan->type) { + case IIO_ANGL: + mutex_lock(&st->lock); + ret = regmap_update_bits(st->regmap, AD2S1210_REG_CONTROL, + AD2S1210_ENABLE_HYSTERESIS, + val ? AD2S1210_ENABLE_HYSTERESIS + : 0); + mutex_unlock(&st->lock); + break; + + default: + break; + } + break; + + default: + break; + } + + return ret; +} + +static IIO_DEVICE_ATTR_RW(excitation_frequency, 0); +static IIO_DEVICE_ATTR_RW(phase_lock_range, 0); +static IIO_DEVICE_ATTR_RO(phase_lock_range_available, 0); +static IIO_DEVICE_ATTR_RW(fault, 0); +static IIO_DEVICE_ATTR_NAMED_RW(los_thrd, reg, AD2S1210_REG_LOS_THRD); +static IIO_DEVICE_ATTR_NAMED_RW(dos_ovr_thrd, reg, AD2S1210_REG_DOS_OVR_THRD); +static IIO_DEVICE_ATTR_NAMED_RW(dos_mis_thrd, reg, AD2S1210_REG_DOS_MIS_THRD); +static IIO_DEVICE_ATTR_NAMED_RW(dos_rst_max_thrd, reg, + AD2S1210_REG_DOS_RST_MAX_THRD); +static IIO_DEVICE_ATTR_NAMED_RW(dos_rst_min_thrd, reg, + AD2S1210_REG_DOS_RST_MIN_THRD); +static IIO_DEVICE_ATTR_NAMED_RW(lot_high_thrd, reg, AD2S1210_REG_LOT_HIGH_THRD); +static IIO_DEVICE_ATTR_NAMED_RW(lot_low_thrd, reg, AD2S1210_REG_LOT_LOW_THRD); + +static const struct iio_chan_spec ad2s1210_channels[] = { + { + .type = IIO_ANGL, + .indexed = 1, + .channel = 0, + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_BE, + }, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_HYSTERESIS), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_HYSTERESIS), + .datasheet_name = "position", + }, { + .type = IIO_ANGL_VEL, + .indexed = 1, + .channel = 0, + .scan_index = 1, + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_BE, + }, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .datasheet_name = "velocity", + }, + IIO_CHAN_SOFT_TIMESTAMP(2), +}; + +static struct attribute *ad2s1210_attributes[] = { + &iio_dev_attr_excitation_frequency.dev_attr.attr, + &iio_dev_attr_phase_lock_range.dev_attr.attr, + &iio_dev_attr_phase_lock_range_available.dev_attr.attr, + &iio_dev_attr_fault.dev_attr.attr, + &iio_dev_attr_los_thrd.dev_attr.attr, + &iio_dev_attr_dos_ovr_thrd.dev_attr.attr, + &iio_dev_attr_dos_mis_thrd.dev_attr.attr, + &iio_dev_attr_dos_rst_max_thrd.dev_attr.attr, + &iio_dev_attr_dos_rst_min_thrd.dev_attr.attr, + &iio_dev_attr_lot_high_thrd.dev_attr.attr, + &iio_dev_attr_lot_low_thrd.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ad2s1210_attribute_group = { + .attrs = ad2s1210_attributes, +}; + +static int ad2s1210_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval) +{ + struct ad2s1210_state *st = iio_priv(indio_dev); + int ret; + + mutex_lock(&st->lock); + + if (readval) + ret = regmap_read(st->regmap, reg, readval); + else + ret = regmap_write(st->regmap, reg, writeval); + + mutex_unlock(&st->lock); + + return ret; +} + +static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad2s1210_state *st = iio_priv(indio_dev); + size_t chan = 0; + int ret; + + mutex_lock(&st->lock); + + memset(&st->scan, 0, sizeof(st->scan)); + gpiod_set_value(st->sample_gpio, 1); + + if (test_bit(0, indio_dev->active_scan_mask)) { + ret = ad2s1210_set_mode(st, AD2S1210_MODE_POS); + if (ret < 0) + goto error_ret; + + /* REVIST: we can read 3 bytes here and also get fault flags */ + ret = spi_read(st->sdev, st->rx, 2); + if (ret < 0) + goto error_ret; + + memcpy(&st->scan.chan[chan++], st->rx, 2); + } + + if (test_bit(1, indio_dev->active_scan_mask)) { + ret = ad2s1210_set_mode(st, AD2S1210_MODE_VEL); + if (ret < 0) + goto error_ret; + + /* REVIST: we can read 3 bytes here and also get fault flags */ + ret = spi_read(st->sdev, st->rx, 2); + if (ret < 0) + goto error_ret; + + memcpy(&st->scan.chan[chan++], st->rx, 2); + } + + iio_push_to_buffers_with_timestamp(indio_dev, &st->scan, pf->timestamp); + +error_ret: + gpiod_set_value(st->sample_gpio, 0); + mutex_unlock(&st->lock); + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static const struct iio_info ad2s1210_info = { + .read_raw = ad2s1210_read_raw, + .read_avail = ad2s1210_read_avail, + .write_raw = ad2s1210_write_raw, + .attrs = &ad2s1210_attribute_group, + .debugfs_reg_access = &ad2s1210_debugfs_reg_access, +}; + +static int ad2s1210_setup_properties(struct ad2s1210_state *st) +{ + struct device *dev = &st->sdev->dev; + u32 val; + int ret; + + ret = device_property_read_u32(dev, "assigned-resolution-bits", &val); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to read assigned-resolution-bits property\n"); + + if (val < 10 || val > 16) + return dev_err_probe(dev, -EINVAL, + "resolution out of range: %u\n", val); + + st->resolution = (val - 10) >> 1; + + return 0; +} + +static int ad2s1210_setup_clocks(struct ad2s1210_state *st) +{ + struct device *dev = &st->sdev->dev; + struct clk *clk; + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "failed to get clock\n"); + + st->fclkin = clk_get_rate(clk); + if (st->fclkin < AD2S1210_MIN_CLKIN || st->fclkin > AD2S1210_MAX_CLKIN) + return dev_err_probe(dev, -EINVAL, + "clock frequency out of range: %lu\n", + st->fclkin); + + return 0; +} + +static int ad2s1210_setup_gpios(struct ad2s1210_state *st) +{ + struct device *dev = &st->sdev->dev; + struct gpio_descs *resolution_gpios; + DECLARE_BITMAP(bitmap, 2); + int ret; + + /* should not be sampling on startup */ + st->sample_gpio = devm_gpiod_get(dev, "sample", GPIOD_OUT_LOW); + if (IS_ERR(st->sample_gpio)) + return dev_err_probe(dev, PTR_ERR(st->sample_gpio), + "failed to request sample GPIO\n"); + + /* both pins high means that we start in config mode */ + st->mode_gpios = devm_gpiod_get_array(dev, "mode", GPIOD_OUT_HIGH); + if (IS_ERR(st->mode_gpios)) + return dev_err_probe(dev, PTR_ERR(st->mode_gpios), + "failed to request mode GPIOs\n"); + + if (st->mode_gpios->ndescs != 2) + return dev_err_probe(dev, -EINVAL, + "requires exactly 2 mode-gpios\n"); + + /* If resolution gpios are provided, they get set to the required + * resolution, otherwise it is assumed the RES0 and RES1 pins are + * hard-wired to match the resolution indicated in the devicetree. + */ + resolution_gpios = devm_gpiod_get_array_optional(dev, "resolution", + GPIOD_ASIS); + if (IS_ERR(resolution_gpios)) + return dev_err_probe(dev, PTR_ERR(resolution_gpios), + "failed to request resolution GPIOs\n"); + + if (resolution_gpios) { + if (resolution_gpios->ndescs != 2) + return dev_err_probe(dev, -EINVAL, + "requires exactly 2 resolution-gpios\n"); + + bitmap[0] = st->resolution; + + ret = gpiod_set_array_value(resolution_gpios->ndescs, + resolution_gpios->desc, + resolution_gpios->info, + bitmap); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to set resolution gpios\n"); + } + + return 0; +} + +static const struct regmap_range ad2s1210_regmap_readable_ranges[] = { + regmap_reg_range(AD2S1210_REG_POSITION_MSB, AD2S1210_REG_VELOCITY_LSB), + regmap_reg_range(AD2S1210_REG_LOS_THRD, AD2S1210_REG_LOT_LOW_THRD), + regmap_reg_range(AD2S1210_REG_EXCIT_FREQ, AD2S1210_REG_CONTROL), + regmap_reg_range(AD2S1210_REG_FAULT, AD2S1210_REG_FAULT), +}; + +static const struct regmap_access_table ad2s1210_regmap_rd_table = { + .yes_ranges = ad2s1210_regmap_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(ad2s1210_regmap_readable_ranges), +}; + +static const struct regmap_range ad2s1210_regmap_writeable_ranges[] = { + regmap_reg_range(AD2S1210_REG_LOS_THRD, AD2S1210_REG_LOT_LOW_THRD), + regmap_reg_range(AD2S1210_REG_EXCIT_FREQ, AD2S1210_REG_CONTROL), + regmap_reg_range(AD2S1210_REG_SOFT_RESET, AD2S1210_REG_SOFT_RESET), + regmap_reg_range(AD2S1210_REG_FAULT, AD2S1210_REG_FAULT), +}; + +static const struct regmap_access_table ad2s1210_regmap_wr_table = { + .yes_ranges = ad2s1210_regmap_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(ad2s1210_regmap_writeable_ranges), +}; + +static int ad2s1210_setup_regmap(struct ad2s1210_state *st) +{ + struct device *dev = &st->sdev->dev; + const struct regmap_config config = { + .reg_bits = 8, + .val_bits = 8, + .disable_locking = true, + .reg_read = ad2s1210_regmap_reg_read, + .reg_write = ad2s1210_regmap_reg_write, + .rd_table = &ad2s1210_regmap_rd_table, + .wr_table = &ad2s1210_regmap_wr_table, + .can_sleep = true, + }; + + st->regmap = devm_regmap_init(dev, NULL, st, &config); + if (IS_ERR(st->regmap)) + return dev_err_probe(dev, PTR_ERR(st->regmap), + "failed to allocate register map\n"); + + return 0; +} + +static int ad2s1210_init(struct ad2s1210_state *st) +{ + unsigned char data; + int ret; + + mutex_lock(&st->lock); + + data = AD2S1210_DEF_CONTROL & ~AD2S1210_SET_RES; + data |= st->resolution; + + ret = regmap_write(st->regmap, AD2S1210_REG_CONTROL, data); + if (ret < 0) + goto error_ret; + + ret = ad2s1210_set_excitation_frequency(st, AD2S1210_DEF_EXCIT); + +error_ret: + mutex_unlock(&st->lock); + return ret; +} + +static int ad2s1210_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct ad2s1210_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + mutex_init(&st->lock); + st->sdev = spi; + + ret = ad2s1210_setup_properties(st); + if (ret < 0) + return ret; + + ret = ad2s1210_setup_clocks(st); + if (ret < 0) + return ret; + + ret = ad2s1210_setup_gpios(st); + if (ret < 0) + return ret; + + ret = ad2s1210_setup_regmap(st); + if (ret < 0) + return ret; + + ret = ad2s1210_init(st); + if (ret < 0) + return ret; + + indio_dev->info = &ad2s1210_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ad2s1210_channels; + indio_dev->num_channels = ARRAY_SIZE(ad2s1210_channels); + indio_dev->name = spi_get_device_id(spi)->name; + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, + &iio_pollfunc_store_time, + &ad2s1210_trigger_handler, NULL); + if (ret < 0) + return dev_err_probe(&spi->dev, ret, + "iio triggered buffer setup failed\n"); + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct of_device_id ad2s1210_of_match[] = { + { .compatible = "adi,ad2s1210", }, + { } +}; +MODULE_DEVICE_TABLE(of, ad2s1210_of_match); + +static const struct spi_device_id ad2s1210_id[] = { + { "ad2s1210" }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad2s1210_id); + +static struct spi_driver ad2s1210_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = ad2s1210_of_match, + }, + .probe = ad2s1210_probe, + .id_table = ad2s1210_id, +}; +module_spi_driver(ad2s1210_driver); + +MODULE_AUTHOR("Graff Yang "); +MODULE_DESCRIPTION("Analog Devices AD2S1210 Resolver to Digital SPI driver"); +MODULE_LICENSE("GPL v2");