mbox series

[v3,00/27] iio: resolver: move ad2s1210 out of staging

Message ID 20230929-ad2s1210-mainline-v3-0-fa4364281745@baylibre.com
Headers show
Series iio: resolver: move ad2s1210 out of staging | expand

Message

David Lechner Sept. 29, 2023, 5:23 p.m. UTC
From: David Lechner <david@lechnology.com>

v3 changes:

* Added description of A0/A1 lines in DT bindings.
* Added power supply regulators to DT bindings.
* Dropped "staging: iio: Documentation: document IIO resolver AD2S1210
  sysfs attributes" (these attributes are being removed instead).
* Dropped applied patches:
  * "staging: iio: resolver: ad2s1210: fix ad2s1210_show_fault"
  * "iio: adc: MCP3564: fix the static checker warning"
* Split "staging: iio: resolver: ad2s1210: fix probe" into multiple patches.
* Moved sorting imports to separate patch.
* Renamed fclkin to clkin_hz.
* Added __be16 sample field to state struct for reading raw samples.
* Split out new function ad2s1210_single_conversion() from
  ad2s1210_read_raw().
* Split out new ad2s1210_get_hysteresis() and ad2s1210_set_hysteresis()
  functions.
* Fixed multi-line comment style.
* Added notes about soft reset not resetting config registers.
* Made use of FIELD_PREP() macro.
* Added more explanation to regmap commit message.
* Removed datasheet names from channel specs.
* Replaced "staging: iio: resolver: ad2s1210: rename fexcit attribute"
  with "staging: iio: resolver: ad2s1210: convert fexcit to channel
  attribute".
* Replaced "staging: iio: resolver: ad2s1210: add phase_lock_range
  attributes" with "staging: iio: resolver: ad2s1210: add phase lock
  range support"
* Added additional patches to convert custom device attributes to event
  attributes.
* Added patch to add channel label attributes.

v2 changes:
* Address initial device tree patch feedback
* Drop "iio: sysfs: add IIO_DEVICE_ATTR_NAMED_RW macro" (related cleanups
  also dropped for now, will address in a future series if needed)
* Apply improvements as a series of patches to the staging driver. It is
  not quite ready for the move out of staging patch yet.

This series has been tested on actual hardware using a EVAL-AD2S1210 evaluation
board. (Note: not all device tree features have been implemented in the driver
since the eval board doesn't support them out of the box. We plan to add them
later if needed.)

Most of the questions about dealing with faults from the v2 cover letter
have been addressed. There is still the question about what to do with
the current `fault` attribute (it is the only custom device attribute
remaining from the original staging driver). It was suggested to split it
out into multiple attributes in a subdirectory. Since we now have events
for all of the faults, I'm wondering if this is something that is still needed.
In the current implementation, it is possible to start listening to events,
clear the faults and then read a sample to trigger events for any current
faults so we have a way to get current faults already.

There is also the matter of clearing faults. Writing the excitation
frequency has a side-effect of clearing the faults, so we could use
that as the reset. Or we could change the current fault attribute to
write-only and rename it. Or is there a better way that I have overlooked?

Once this last issue is addressed, I think this driver will be ready
for consideration for moving out of staging.
---
David Lechner (27):
      dt-bindings: iio: resolver: add devicetree bindings for ad2s1210
      staging: iio: resolver: ad2s1210: fix use before initialization
      staging: iio: resolver: ad2s1210: remove call to spi_setup()
      staging: iio: resolver: ad2s1210: check return of ad2s1210_initial()
      staging: iio: resolver: ad2s1210: remove spi_set_drvdata()
      staging: iio: resolver: ad2s1210: sort imports
      staging: iio: resolver: ad2s1210: always use 16-bit value for raw read
      staging: iio: resolver: ad2s1210: implement IIO_CHAN_INFO_SCALE
      staging: iio: resolver: ad2s1210: use devicetree to get CLKIN rate
      staging: iio: resolver: ad2s1210: use regmap for config registers
      staging: iio: resolver: ad2s1210: add debugfs reg access
      staging: iio: resolver: ad2s1210: remove config attribute
      staging: iio: resolver: ad2s1210: rework gpios
      staging: iio: resolver: ad2s1210: implement hysteresis as channel attr
      staging: iio: resolver: ad2s1210: refactor setting excitation frequency
      staging: iio: resolver: ad2s1210: read excitation frequency from control register
      staging: iio: resolver: ad2s1210: convert fexcit to channel attribute
      staging: iio: resolver: ad2s1210: convert resolution to devicetree property
      staging: iio: resolver: ad2s1210: add phase lock range support
      staging: iio: resolver: ad2s1210: add triggered buffer support
      staging: iio: resolver: ad2s1210: convert LOT threshold attrs to event attrs
      staging: iio: resolver: ad2s1210: convert LOS threshold to event attr
      staging: iio: resolver: ad2s1210: convert DOS overrange threshold to event attr
      staging: iio: resolver: ad2s1210: convert DOS mismatch threshold to event attr
      staging: iio: resolver: ad2s1210: rename DOS reset min/max attrs
      staging: iio: resolver: ad2s1210: implement fault events
      staging: iio: resolver: ad2s1210: add label attribute support

 .../bindings/iio/resolver/adi,ad2s1210.yaml        |  177 +++
 .../Documentation/sysfs-bus-iio-resolver-ad2s1210  |   27 +
 drivers/staging/iio/resolver/Kconfig               |    1 +
 drivers/staging/iio/resolver/ad2s1210.c            | 1583 +++++++++++++++-----
 4 files changed, 1391 insertions(+), 397 deletions(-)
---
base-commit: 5e99f692d4e32e3250ab18d511894ca797407aec
change-id: 20230925-ad2s1210-mainline-2791ef75e386

Comments

David Lechner Sept. 29, 2023, 5:49 p.m. UTC | #1
On Fri, Sep 29, 2023 at 12:25 PM David Lechner <dlechner@baylibre.com> wrote:
>
> From: David Lechner <david@lechnology.com>
>

Ugh, an automated tool picked up my personal email on this for some
reason. This extra From: line can be dropped from any patches that get
picked up on this round. I will make sure to double-check next time.
David Lechner Sept. 29, 2023, 5:53 p.m. UTC | #2
On Fri, Sep 29, 2023 at 12:25 PM David Lechner <dlechner@baylibre.com> wrote:
>
> The AD2S1210 resolver has a hysteresis feature that can be used to
> prevent flicker in the LSB of the position register. This can be either
> enabled or disabled. Disabling hysteresis is useful for increasing
> precision by oversampling.
>
> Signed-off-by: David Lechner <dlechner@baylibre.com>
> ---

...

> +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 hysteresis_available[] = { 0, 1 };

This is basically an enable/disable. Should the 1 value be changed to the
appropriate radians value since this is hysteresis on the position
(angle) channel?

> +
> +       switch (mask) {
> +       case IIO_CHAN_INFO_HYSTERESIS:
> +               switch (chan->type) {
> +               case IIO_ANGL:
> +                       *vals = hysteresis_available;
> +                       *type = IIO_VAL_INT;
> +                       *length = ARRAY_SIZE(hysteresis_available);
> +                       return IIO_AVAIL_LIST;
> +               default:
> +                       return -EINVAL;
> +               }
> +       default:
> +               return -EINVAL;
> +       }
> +}
>
David Lechner Sept. 29, 2023, 10:02 p.m. UTC | #3
On 9/29/23 12:23 PM, David Lechner wrote:
> From: David Lechner <david@lechnology.com>
> 
> v3 changes:
> 
> * Added description of A0/A1 lines in DT bindings.
> * Added power supply regulators to DT bindings.
> * Dropped "staging: iio: Documentation: document IIO resolver AD2S1210
>    sysfs attributes" (these attributes are being removed instead).
> * Dropped applied patches:
>    * "staging: iio: resolver: ad2s1210: fix ad2s1210_show_fault"
>    * "iio: adc: MCP3564: fix the static checker warning"
> * Split "staging: iio: resolver: ad2s1210: fix probe" into multiple patches.
> * Moved sorting imports to separate patch.
> * Renamed fclkin to clkin_hz.
> * Added __be16 sample field to state struct for reading raw samples.
> * Split out new function ad2s1210_single_conversion() from
>    ad2s1210_read_raw().
> * Split out new ad2s1210_get_hysteresis() and ad2s1210_set_hysteresis()
>    functions.
> * Fixed multi-line comment style.
> * Added notes about soft reset not resetting config registers.
> * Made use of FIELD_PREP() macro.
> * Added more explanation to regmap commit message.
> * Removed datasheet names from channel specs.
> * Replaced "staging: iio: resolver: ad2s1210: rename fexcit attribute"
>    with "staging: iio: resolver: ad2s1210: convert fexcit to channel
>    attribute".
> * Replaced "staging: iio: resolver: ad2s1210: add phase_lock_range
>    attributes" with "staging: iio: resolver: ad2s1210: add phase lock
>    range support"
> * Added additional patches to convert custom device attributes to event
>    attributes.
> * Added patch to add channel label attributes.
> 
> v2 changes:
> * Address initial device tree patch feedback
> * Drop "iio: sysfs: add IIO_DEVICE_ATTR_NAMED_RW macro" (related cleanups
>    also dropped for now, will address in a future series if needed)
> * Apply improvements as a series of patches to the staging driver. It is
>    not quite ready for the move out of staging patch yet.
> 
> This series has been tested on actual hardware using a EVAL-AD2S1210 evaluation
> board. (Note: not all device tree features have been implemented in the driver
> since the eval board doesn't support them out of the box. We plan to add them
> later if needed.)
> 
> Most of the questions about dealing with faults from the v2 cover letter
> have been addressed. There is still the question about what to do with
> the current `fault` attribute (it is the only custom device attribute
> remaining from the original staging driver). It was suggested to split it
> out into multiple attributes in a subdirectory. Since we now have events
> for all of the faults, I'm wondering if this is something that is still needed.
> In the current implementation, it is possible to start listening to events,
> clear the faults and then read a sample to trigger events for any current
> faults so we have a way to get current faults already.
> 
> There is also the matter of clearing faults. Writing the excitation
> frequency has a side-effect of clearing the faults, so we could use
> that as the reset. Or we could change the current fault attribute to
> write-only and rename it. Or is there a better way that I have overlooked?
> 
> Once this last issue is addressed, I think this driver will be ready
> for consideration for moving out of staging.
> ---
> David Lechner (27):
>        dt-bindings: iio: resolver: add devicetree bindings for ad2s1210
>        staging: iio: resolver: ad2s1210: fix use before initialization
>        staging: iio: resolver: ad2s1210: remove call to spi_setup()
>        staging: iio: resolver: ad2s1210: check return of ad2s1210_initial()
>        staging: iio: resolver: ad2s1210: remove spi_set_drvdata()
>        staging: iio: resolver: ad2s1210: sort imports
>        staging: iio: resolver: ad2s1210: always use 16-bit value for raw read
>        staging: iio: resolver: ad2s1210: implement IIO_CHAN_INFO_SCALE
>        staging: iio: resolver: ad2s1210: use devicetree to get CLKIN rate
>        staging: iio: resolver: ad2s1210: use regmap for config registers
>        staging: iio: resolver: ad2s1210: add debugfs reg access
>        staging: iio: resolver: ad2s1210: remove config attribute
>        staging: iio: resolver: ad2s1210: rework gpios
>        staging: iio: resolver: ad2s1210: implement hysteresis as channel attr
>        staging: iio: resolver: ad2s1210: refactor setting excitation frequency
>        staging: iio: resolver: ad2s1210: read excitation frequency from control register
>        staging: iio: resolver: ad2s1210: convert fexcit to channel attribute
>        staging: iio: resolver: ad2s1210: convert resolution to devicetree property
>        staging: iio: resolver: ad2s1210: add phase lock range support
>        staging: iio: resolver: ad2s1210: add triggered buffer support
>        staging: iio: resolver: ad2s1210: convert LOT threshold attrs to event attrs
>        staging: iio: resolver: ad2s1210: convert LOS threshold to event attr
>        staging: iio: resolver: ad2s1210: convert DOS overrange threshold to event attr
>        staging: iio: resolver: ad2s1210: convert DOS mismatch threshold to event attr
>        staging: iio: resolver: ad2s1210: rename DOS reset min/max attrs
>        staging: iio: resolver: ad2s1210: implement fault events
>        staging: iio: resolver: ad2s1210: add label attribute support
> 
>   .../bindings/iio/resolver/adi,ad2s1210.yaml        |  177 +++
>   .../Documentation/sysfs-bus-iio-resolver-ad2s1210  |   27 +
>   drivers/staging/iio/resolver/Kconfig               |    1 +
>   drivers/staging/iio/resolver/ad2s1210.c            | 1583 +++++++++++++++-----
>   4 files changed, 1391 insertions(+), 397 deletions(-)
> ---
> base-commit: 5e99f692d4e32e3250ab18d511894ca797407aec
> change-id: 20230925-ad2s1210-mainline-2791ef75e386
> 

In the end, this is what sysfs looks like:


root@analog:~# tree /sys/bus/iio/devices/iio\:device1/
/sys/bus/iio/devices/iio:device1/
├── buffer
│   ├── data_available
│   ├── direction
│   ├── enable
│   ├── length
│   └── watermark
├── buffer0
│   ├── data_available
│   ├── direction
│   ├── enable
│   ├── in_angl0_en
│   ├── in_angl0_index
│   ├── in_angl0_type
│   ├── in_anglvel0_en
│   ├── in_anglvel0_index
│   ├── in_anglvel0_type
│   ├── in_timestamp_en
│   ├── in_timestamp_index
│   ├── in_timestamp_type
│   ├── length
│   └── watermark
├── current_timestamp_clock
├── dev
├── events
│   ├── in_altvoltage0_mag_reset_max
│   ├── in_altvoltage0_mag_reset_max_available
│   ├── in_altvoltage0_mag_reset_min
│   ├── in_altvoltage0_mag_reset_min_available
│   ├── in_altvoltage0_mag_value
│   ├── in_altvoltage0_mag_value_available
│   ├── in_altvoltage0_thresh_falling_value
│   ├── in_altvoltage0_thresh_falling_value_available
│   ├── in_altvoltage0_thresh_rising_value
│   ├── in_altvoltage0_thresh_rising_value_available
│   ├── in_angl1_thresh_rising_hysteresis
│   ├── in_angl1_thresh_rising_hysteresis_available
│   ├── in_angl1_thresh_rising_value
│   ├── in_angl1_thresh_rising_value_available
│   ├── in_phase0_mag_value
│   └── in_phase0_mag_value_available
├── fault
├── in_altvoltage0_label
├── in_altvoltage1_x_label
├── in_altvoltage1_y_label
├── in_angl0_hysteresis
├── in_angl0_hysteresis_available
├── in_angl0_label
├── in_angl0_raw
├── in_angl0_scale
├── in_angl1_label
├── in_anglvel0_label
├── in_anglvel0_raw
├── in_anglvel0_scale
├── in_phase0_label
├── name
├── of_node -> ../../../../../../../../firmware/devicetree/base/axi/spi@e0006000/ad2s1210@0
├── out_altvoltage0_frequency
├── out_altvoltage0_frequency_available
├── out_altvoltage0_label
├── power
│   ├── autosuspend_delay_ms
│   ├── control
│   ├── runtime_active_time
│   ├── runtime_status
│   └── runtime_suspended_time
├── scan_elements
│   ├── in_angl0_en
│   ├── in_angl0_index
│   ├── in_angl0_type
│   ├── in_anglvel0_en
│   ├── in_anglvel0_index
│   ├── in_anglvel0_type
│   ├── in_timestamp_en
│   ├── in_timestamp_index
│   └── in_timestamp_type
├── subsystem -> ../../../../../../../../bus/iio
├── trigger
│   └── current_trigger
├── uevent
└── waiting_for_supplier

8 directories, 72 files

And this is what the output of iio_info looks like:
(note: iio_info does not currently support events so those
attributes are not visible here)

         iio:device1: ad2s1210 (buffer capable)
                 9 channels found:
                         angl0:  (input, index: 0, format: be:U16/16>>0)
                         5 channel-specific attributes found:
                                 attr  0: hysteresis value: 1
                                 attr  1: hysteresis_available value: 0 1
                                 attr  2: label value: position
                                 attr  3: raw value: 12578
                                 attr  4: scale value: 0.000095874
                         anglvel0:  (input, index: 1, format: be:S16/16>>0)
                         3 channel-specific attributes found:
                                 attr  0: label value: velocity
                                 attr  1: raw value: 5
                                 attr  2: scale value: 0.023968449
                         timestamp:  (input, index: 2, format: le:S64/64>>0)
                         phase0:  (input)
                         1 channel-specific attributes found:
                                 attr  0: label value: synthetic reference
                         altvoltage0:  (output)
                         3 channel-specific attributes found:
                                 attr  0: frequency value: 10000
                                 attr  1: frequency_available value: [2000 250 20000]
                                 attr  2: label value: excitation
                         angl1:  (input)
                         1 channel-specific attributes found:
                                 attr  0: label value: tracking error
                         altvoltage1_y:  (input)
                         1 channel-specific attributes found:
                                 attr  0: label value: sine
                         altvoltage0:  (input)
                         1 channel-specific attributes found:
                                 attr  0: label value: monitor signal
                         altvoltage1_x:  (input)
                         1 channel-specific attributes found:
                                 attr  0: label value: cosine
                 3 device-specific attributes found:
                                 attr  0: current_timestamp_clock value: realtime
                                 attr  1: fault value: 0x00
                                 attr  2: waiting_for_supplier value: 0
                 2 buffer-specific attributes found:
                                 attr  0: data_available value: 0
                                 attr  1: direction value: in
                 1 debug attributes found:
                                 debug attr  0: direct_reg_access ERROR: Input/output error (5)
                 Current trigger: trigger0(test)
Jonathan Cameron Sept. 30, 2023, 2:26 p.m. UTC | #4
On Fri, 29 Sep 2023 12:49:42 -0500
David Lechner <dlechner@baylibre.com> wrote:

> On Fri, Sep 29, 2023 at 12:25 PM David Lechner <dlechner@baylibre.com> wrote:
> >
> > From: David Lechner <david@lechnology.com>
> >  
> 
> Ugh, an automated tool picked up my personal email on this for some
> reason. This extra From: line can be dropped from any patches that get
> picked up on this round. I will make sure to double-check next time.

Seems b4 picks up the second, correct From anyway which is handy ;)

Jonathan
Jonathan Cameron Sept. 30, 2023, 2:28 p.m. UTC | #5
On Fri, 29 Sep 2023 12:23:07 -0500
David Lechner <dlechner@baylibre.com> wrote:

> From: David Lechner <david@lechnology.com>
> 
> From: David Lechner <dlechner@baylibre.com>
> 
> This fixes a use before initialization in ad2s1210_probe(). The
> ad2s1210_setup_gpios() function uses st->sdev but it was being called
> before this field was initialized.
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>
Applied to the togreg banch of iio.git and pushed out as testing for 0-day to
poke at it.

I didn't pull this out as a fix to upstream quicker because it would
make a mess of the rest of applying the rest of the series.

Maybe we want to consider backporting some of these at somepoint.

Jonathan

> ---
> 
> v3 changes:
> * This is a new patch split out from "staging: iio: resolver: ad2s1210:
>  fix probe"
> 
>  drivers/staging/iio/resolver/ad2s1210.c | 7 ++++---
>  1 file changed, 4 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c
> index f695ca0547e4..3f08b59f4e19 100644
> --- a/drivers/staging/iio/resolver/ad2s1210.c
> +++ b/drivers/staging/iio/resolver/ad2s1210.c
> @@ -658,9 +658,6 @@ static int ad2s1210_probe(struct spi_device *spi)
>  	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);
>  
> @@ -671,6 +668,10 @@ static int ad2s1210_probe(struct spi_device *spi)
>  	st->resolution = 12;
>  	st->fexcit = AD2S1210_DEF_EXCIT;
>  
> +	ret = ad2s1210_setup_gpios(st);
> +	if (ret < 0)
> +		return ret;
> +
>  	indio_dev->info = &ad2s1210_info;
>  	indio_dev->modes = INDIO_DIRECT_MODE;
>  	indio_dev->channels = ad2s1210_channels;
>
Jonathan Cameron Sept. 30, 2023, 2:35 p.m. UTC | #6
On Fri, 29 Sep 2023 12:23:08 -0500
David Lechner <dlechner@baylibre.com> wrote:

> From: David Lechner <david@lechnology.com>
> 
> From: David Lechner <dlechner@baylibre.com>
> 
> This removes the call to spi_setup() in the ad2s1210 driver.
> 
> Setting MODE_3 was incorrect. It should be MODE_1 but we can let the
> device tree select this and avoid the need to call spi_setup().
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>

Applied and pushed out as testing for all the normal reasons.

Thanks,

Jonathan

> ---
> 
> v3 changes:
> * This is a new patch split out from "staging: iio: resolver: ad2s1210:
>  fix probe"
> 
>  drivers/staging/iio/resolver/ad2s1210.c | 2 --
>  1 file changed, 2 deletions(-)
> 
> diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c
> index 3f08b59f4e19..8fde08887f7f 100644
> --- a/drivers/staging/iio/resolver/ad2s1210.c
> +++ b/drivers/staging/iio/resolver/ad2s1210.c
> @@ -683,8 +683,6 @@ static int ad2s1210_probe(struct spi_device *spi)
>  		return ret;
>  
>  	st->fclkin = spi->max_speed_hz;
> -	spi->mode = SPI_MODE_3;
> -	spi_setup(spi);
>  	ad2s1210_initial(st);
>  
>  	return 0;
>
Jonathan Cameron Sept. 30, 2023, 2:37 p.m. UTC | #7
On Fri, 29 Sep 2023 12:23:09 -0500
David Lechner <dlechner@baylibre.com> wrote:

> From: David Lechner <david@lechnology.com>
> 
> From: David Lechner <dlechner@baylibre.com>
> 
> This adds a check to the return value of ad2s1210_initial() since it
> can fail. The call is also moved before devm_iio_device_register() so
> that we don't have to unregister the device if it fails.
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>
Applied

> ---
> 
> v3 changes:
> * This is a new patch split out from "staging: iio: resolver: ad2s1210:
>  fix probe"
> 
>  drivers/staging/iio/resolver/ad2s1210.c | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c
> index 8fde08887f7f..b5e071d7c5fd 100644
> --- a/drivers/staging/iio/resolver/ad2s1210.c
> +++ b/drivers/staging/iio/resolver/ad2s1210.c
> @@ -672,6 +672,10 @@ static int ad2s1210_probe(struct spi_device *spi)
>  	if (ret < 0)
>  		return ret;
>  
> +	ret = ad2s1210_initial(st);
> +	if (ret < 0)
> +		return ret;
> +
>  	indio_dev->info = &ad2s1210_info;
>  	indio_dev->modes = INDIO_DIRECT_MODE;
>  	indio_dev->channels = ad2s1210_channels;
> @@ -683,7 +687,6 @@ static int ad2s1210_probe(struct spi_device *spi)
>  		return ret;
>  
>  	st->fclkin = spi->max_speed_hz;
> -	ad2s1210_initial(st);
>  
>  	return 0;
>  }
>
Jonathan Cameron Sept. 30, 2023, 2:38 p.m. UTC | #8
On Fri, 29 Sep 2023 12:23:10 -0500
David Lechner <dlechner@baylibre.com> wrote:

> From: David Lechner <david@lechnology.com>
> 
> From: David Lechner <dlechner@baylibre.com>
> 
> Since we never call spi_get_drvdata(), we can remove spi_set_drvdata().
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>
Applied.

> ---
> 
> v3 changes:
> * This is a new patch split out from "staging: iio: resolver: ad2s1210:
>  fix probe"
> 
>  drivers/staging/iio/resolver/ad2s1210.c | 2 --
>  1 file changed, 2 deletions(-)
> 
> diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c
> index b5e071d7c5fd..28015322f562 100644
> --- a/drivers/staging/iio/resolver/ad2s1210.c
> +++ b/drivers/staging/iio/resolver/ad2s1210.c
> @@ -659,8 +659,6 @@ static int ad2s1210_probe(struct spi_device *spi)
>  		return -ENOMEM;
>  	st = iio_priv(indio_dev);
>  
> -	spi_set_drvdata(spi, indio_dev);
> -
>  	mutex_init(&st->lock);
>  	st->sdev = spi;
>  	st->hysteresis = true;
>
Jonathan Cameron Sept. 30, 2023, 2:39 p.m. UTC | #9
On Fri, 29 Sep 2023 12:23:11 -0500
David Lechner <dlechner@baylibre.com> wrote:

> From: David Lechner <david@lechnology.com>
> 
> From: David Lechner <dlechner@baylibre.com>
> 
> There are quite a few imports and we will be adding more so it will
> make it easier to read if they are sorted.
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>
Applied

> ---
> 
> v3 changes:
> * This is a new patch split out from "staging: iio: resolver: ad2s1210:
>  use devicetree to get fclkin"
> 
>  drivers/staging/iio/resolver/ad2s1210.c | 12 ++++++------
>  1 file changed, 6 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c
> index 28015322f562..832f86bf15e5 100644
> --- a/drivers/staging/iio/resolver/ad2s1210.c
> +++ b/drivers/staging/iio/resolver/ad2s1210.c
> @@ -4,16 +4,16 @@
>   *
>   * Copyright (c) 2010-2010 Analog Devices Inc.
>   */
> -#include <linux/types.h>
> -#include <linux/mutex.h>
> +#include <linux/delay.h>
>  #include <linux/device.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
>  #include <linux/of.h>
> -#include <linux/spi/spi.h>
>  #include <linux/slab.h>
> +#include <linux/spi/spi.h>
>  #include <linux/sysfs.h>
> -#include <linux/delay.h>
> -#include <linux/gpio/consumer.h>
> -#include <linux/module.h>
> +#include <linux/types.h>
>  
>  #include <linux/iio/iio.h>
>  #include <linux/iio/sysfs.h>
>
Jonathan Cameron Sept. 30, 2023, 2:41 p.m. UTC | #10
On Fri, 29 Sep 2023 12:23:12 -0500
David Lechner <dlechner@baylibre.com> wrote:

> From: David Lechner <david@lechnology.com>
> 
> From: David Lechner <dlechner@baylibre.com>
> 
> This removes the special handling for resolutions lower than 16 bits.
> This will allow us to use a fixed scale independent of the resolution.
> 
> A new sample field is added to store the raw data instead of reusing
> the config mode rx buffer so that we don't have to do a cast or worry
> about unaligned access.
> 
> Also, for the record, according to the datasheet, the logic for the
> special handling based on hysteresis that was removed was incorrect.
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>
LGTM

Applied.

Thanks,

Jonathan

> ---
> 
> v3 changes:
> * Added __be16 sample field to state struct and use instead of rx buffer.
> 
>  drivers/staging/iio/resolver/ad2s1210.c | 24 ++++++++----------------
>  1 file changed, 8 insertions(+), 16 deletions(-)
> 
> diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c
> index 832f86bf15e5..f9774dff2df4 100644
> --- a/drivers/staging/iio/resolver/ad2s1210.c
> +++ b/drivers/staging/iio/resolver/ad2s1210.c
> @@ -95,7 +95,11 @@ struct ad2s1210_state {
>  	bool hysteresis;
>  	u8 resolution;
>  	enum ad2s1210_mode mode;
> -	u8 rx[2] __aligned(IIO_DMA_MINALIGN);
> +	/** For reading raw sample value via SPI. */
> +	__be16 sample __aligned(IIO_DMA_MINALIGN);
> +	/** SPI transmit buffer. */
> +	u8 rx[2];
> +	/** SPI receive buffer. */
>  	u8 tx[2];
>  };
>  
> @@ -464,10 +468,7 @@ static int ad2s1210_read_raw(struct iio_dev *indio_dev,
>  			     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);
> @@ -487,26 +488,17 @@ static int ad2s1210_read_raw(struct iio_dev *indio_dev,
>  	}
>  	if (ret < 0)
>  		goto error_ret;
> -	ret = spi_read(st->sdev, st->rx, 2);
> +	ret = spi_read(st->sdev, &st->sample, 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;
> +		*val = be16_to_cpu(st->sample);
>  		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;
> +		*val = (s16)be16_to_cpu(st->sample);
>  		ret = IIO_VAL_INT;
>  		break;
>  	default:
>
Jonathan Cameron Sept. 30, 2023, 2:43 p.m. UTC | #11
On Fri, 29 Sep 2023 12:23:13 -0500
David Lechner <dlechner@baylibre.com> wrote:

> From: David Lechner <david@lechnology.com>
> 
> From: David Lechner <dlechner@baylibre.com>
> 
> This adds an implementation of IIO_CHAN_INFO_SCALE to the ad2s1210
> resolver driver. This allows userspace to get the scale factor for the
> raw values.
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>
Applied

Thanks,

> ---
> 
> v3 changes:
> * Split ad2s1210_read_raw() into two functions to reduce complexity.
> * Use early return instead of break in switch statements.
> 
>  drivers/staging/iio/resolver/ad2s1210.c | 53 ++++++++++++++++++++++++++++-----
>  1 file changed, 45 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c
> index f9774dff2df4..a710598a64f0 100644
> --- a/drivers/staging/iio/resolver/ad2s1210.c
> +++ b/drivers/staging/iio/resolver/ad2s1210.c
> @@ -461,13 +461,10 @@ static ssize_t ad2s1210_store_reg(struct device *dev,
>  	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)
> +static int ad2s1210_single_conversion(struct ad2s1210_state *st,
> +				      struct iio_chan_spec const *chan,
> +				      int *val)
>  {
> -	struct ad2s1210_state *st = iio_priv(indio_dev);
>  	int ret = 0;
>  
>  	mutex_lock(&st->lock);
> @@ -514,6 +511,44 @@ static int ad2s1210_read_raw(struct iio_dev *indio_dev,
>  	return ret;
>  }
>  
> +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);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		return ad2s1210_single_conversion(st, chan, val);
> +	case IIO_CHAN_INFO_SCALE:
> +		switch (chan->type) {
> +		case IIO_ANGL:
> +			/* approx 0.3 arc min converted to radians */
> +			*val = 0;
> +			*val2 = 95874;
> +			return IIO_VAL_INT_PLUS_NANO;
> +		case IIO_ANGL_VEL:
> +			*val = st->fclkin;
> +			*val2 = ad2s1210_velocity_scale[st->resolution];
> +			return IIO_VAL_FRACTIONAL;
> +		default:
> +			return -EINVAL;
> +		}
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
>  static IIO_DEVICE_ATTR(fclkin, 0644,
>  		       ad2s1210_show_fclkin, ad2s1210_store_fclkin, 0);
>  static IIO_DEVICE_ATTR(fexcit, 0644,
> @@ -552,12 +587,14 @@ static const struct iio_chan_spec ad2s1210_channels[] = {
>  		.type = IIO_ANGL,
>  		.indexed = 1,
>  		.channel = 0,
> -		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +				      BIT(IIO_CHAN_INFO_SCALE),
>  	}, {
>  		.type = IIO_ANGL_VEL,
>  		.indexed = 1,
>  		.channel = 0,
> -		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +				      BIT(IIO_CHAN_INFO_SCALE),
>  	}
>  };
>  
>
Jonathan Cameron Sept. 30, 2023, 2:44 p.m. UTC | #12
On Fri, 29 Sep 2023 12:23:14 -0500
David Lechner <dlechner@baylibre.com> wrote:

> From: David Lechner <david@lechnology.com>
> 
> From: David Lechner <dlechner@baylibre.com>
> 
> This removes the fclkin sysfs attribute and replaces it with getting
> the CLKIN clock rate using the clk subsystem (i.e. from the devicetree).
> 
> CLKIN comes from an external oscillator that is connected directly to
> the AD2S1210 chip, so users of the sysfs attributes should not need to
> be concerned with this.
> 
> The fclkin field (the datasheet name) is renamed to clkin_hz to be more
> obvious that it is a frequency in Hz.
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>
LGTM

Applied

J
> ---
> 
> v3 changes:
> * Don't sort imports in this patch.
> * Renamed fexcit to clkin_hz.
> * Fixed ad2s1210_setup_clocks() being called in an earlier patch.
> 
>  drivers/staging/iio/resolver/Kconfig    |  1 +
>  drivers/staging/iio/resolver/ad2s1210.c | 81 ++++++++++++---------------------
>  2 files changed, 30 insertions(+), 52 deletions(-)
> 
> diff --git a/drivers/staging/iio/resolver/Kconfig b/drivers/staging/iio/resolver/Kconfig
> index 6d1e2622e0b0..bebb35822c9e 100644
> --- a/drivers/staging/iio/resolver/Kconfig
> +++ b/drivers/staging/iio/resolver/Kconfig
> @@ -7,6 +7,7 @@ menu "Resolver to digital converters"
>  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
> diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c
> index a710598a64f0..c8723b6f3a3b 100644
> --- a/drivers/staging/iio/resolver/ad2s1210.c
> +++ b/drivers/staging/iio/resolver/ad2s1210.c
> @@ -3,7 +3,9 @@
>   * ad2s1210.c support for the ADI Resolver to Digital Converters: AD2S1210
>   *
>   * Copyright (c) 2010-2010 Analog Devices Inc.
> + * Copyright (c) 2023 BayLibre, SAS
>   */
> +#include <linux/clk.h>
>  #include <linux/delay.h>
>  #include <linux/device.h>
>  #include <linux/gpio/consumer.h>
> @@ -90,7 +92,8 @@ struct ad2s1210_state {
>  	struct mutex lock;
>  	struct spi_device *sdev;
>  	struct gpio_desc *gpios[5];
> -	unsigned int fclkin;
> +	/** The external oscillator frequency in Hz. */
> +	unsigned long clkin_hz;
>  	unsigned int fexcit;
>  	bool hysteresis;
>  	u8 resolution;
> @@ -165,7 +168,7 @@ int ad2s1210_update_frequency_control_word(struct ad2s1210_state *st)
>  	int ret;
>  	unsigned char fcw;
>  
> -	fcw = (unsigned char)(st->fexcit * (1 << 15) / st->fclkin);
> +	fcw = (unsigned char)(st->fexcit * (1 << 15) / st->clkin_hz);
>  	if (fcw < AD2S1210_MIN_FCW || fcw > AD2S1210_MAX_FCW) {
>  		dev_err(&st->sdev->dev, "ad2s1210: FCW out of range\n");
>  		return -ERANGE;
> @@ -201,45 +204,6 @@ static inline int ad2s1210_soft_reset(struct ad2s1210_state *st)
>  	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)
> @@ -537,7 +501,7 @@ static int ad2s1210_read_raw(struct iio_dev *indio_dev,
>  			*val2 = 95874;
>  			return IIO_VAL_INT_PLUS_NANO;
>  		case IIO_ANGL_VEL:
> -			*val = st->fclkin;
> +			*val = st->clkin_hz;
>  			*val2 = ad2s1210_velocity_scale[st->resolution];
>  			return IIO_VAL_FRACTIONAL;
>  		default:
> @@ -549,8 +513,6 @@ static int ad2s1210_read_raw(struct iio_dev *indio_dev,
>  	}
>  }
>  
> -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,
> @@ -599,7 +561,6 @@ static const struct iio_chan_spec ad2s1210_channels[] = {
>  };
>  
>  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,
> @@ -657,6 +618,24 @@ static const struct iio_info ad2s1210_info = {
>  	.attrs = &ad2s1210_attribute_group,
>  };
>  
> +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->clkin_hz = clk_get_rate(clk);
> +	if (st->clkin_hz < AD2S1210_MIN_CLKIN || st->clkin_hz > AD2S1210_MAX_CLKIN)
> +		return dev_err_probe(dev, -EINVAL,
> +				     "clock frequency out of range: %lu\n",
> +				     st->clkin_hz);
> +
> +	return 0;
> +}
> +
>  static int ad2s1210_setup_gpios(struct ad2s1210_state *st)
>  {
>  	struct spi_device *spi = st->sdev;
> @@ -695,6 +674,10 @@ static int ad2s1210_probe(struct spi_device *spi)
>  	st->resolution = 12;
>  	st->fexcit = AD2S1210_DEF_EXCIT;
>  
> +	ret = ad2s1210_setup_clocks(st);
> +	if (ret < 0)
> +		return ret;
> +
>  	ret = ad2s1210_setup_gpios(st);
>  	if (ret < 0)
>  		return ret;
> @@ -709,13 +692,7 @@ static int ad2s1210_probe(struct spi_device *spi)
>  	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;
> -
> -	return 0;
> +	return devm_iio_device_register(&spi->dev, indio_dev);
>  }
>  
>  static const struct of_device_id ad2s1210_of_match[] = {
>
Jonathan Cameron Sept. 30, 2023, 2:51 p.m. UTC | #13
On Fri, 29 Sep 2023 12:23:15 -0500
David Lechner <dlechner@baylibre.com> wrote:

> From: David Lechner <david@lechnology.com>
> 
> From: David Lechner <dlechner@baylibre.com>
> 
> This makes use of the regmap API to read and write the configuration
> registers. This simplifies code quite a bit and makes it safer
> (previously, it was easy to write a bad value to the config registers
> which causes the chip to lock up and need to be reset).
> 
> This chip has multiple modes of operation. In normal mode, we do not use
> regmap since there is no addressing - data is just bitshifted out during
> the SPI read. In config mode, we use regmap since it requires writing
> the address (with read/write flag) before reading and writing.
> 
> We don't use the lock provided by the regmap because we need to also
> synchronize with the normal mode SPI reads and with the various GPIOs.
> 
> There is also a quirk when reading registers (other than the fault
> register). If the address/data bit is set in the value read, then it
> indicates there is a configuration parity error and the data is not
> valid. Previously, this was checked in a few places, but not
> consistently. Now, we always check it in the regmap read function.
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>

This was a complex change, so I'm partly relying on the fact it clearly
works after the change to be sure it is correct ;)

Anyhow, code is much cleaner and probably right, so applied to the
togreg branch of iio.git and pushed out as testing for all the normal
reasons.

Thanks,

Jonathan
Jonathan Cameron Sept. 30, 2023, 2:52 p.m. UTC | #14
On Fri, 29 Sep 2023 12:23:16 -0500
David Lechner <dlechner@baylibre.com> wrote:

> From: David Lechner <david@lechnology.com>
> 
> From: David Lechner <dlechner@baylibre.com>
> 
> This add an implementation of debugfs_reg_access for the AD2S1210
> driver.
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>

I never really care if a driver implements this or not.
However, if you find it useful that's fine, so applied.

Thanks,

Jonathan

> ---
> 
> v3 changes: None
> 
>  drivers/staging/iio/resolver/ad2s1210.c | 20 ++++++++++++++++++++
>  1 file changed, 20 insertions(+)
> 
> diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c
> index 0663a51d04ad..31415fbb6384 100644
> --- a/drivers/staging/iio/resolver/ad2s1210.c
> +++ b/drivers/staging/iio/resolver/ad2s1210.c
> @@ -614,9 +614,29 @@ static int ad2s1210_initial(struct ad2s1210_state *st)
>  	return ret;
>  }
>  
> +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 const struct iio_info ad2s1210_info = {
>  	.read_raw = ad2s1210_read_raw,
>  	.attrs = &ad2s1210_attribute_group,
> +	.debugfs_reg_access = &ad2s1210_debugfs_reg_access,
>  };
>  
>  static int ad2s1210_setup_clocks(struct ad2s1210_state *st)
>
Jonathan Cameron Sept. 30, 2023, 2:53 p.m. UTC | #15
On Fri, 29 Sep 2023 12:23:17 -0500
David Lechner <dlechner@baylibre.com> wrote:

> From: David Lechner <david@lechnology.com>
> 
> From: David Lechner <dlechner@baylibre.com>
> 
> This removes the config register sysfs attribute.
> 
> Writing to the config register directly can be dangerous and userspace
> should not need to have to know the register layout. This register
> can still be accessed though debugfs if needed.
> 
> We can add new attributes to set specific flags in the config register
> in the future if needed (e.g. `enable_hysterisis` and
> `phase_lock_range`).
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>
Applied.
Jonathan Cameron Sept. 30, 2023, 2:55 p.m. UTC | #16
On Fri, 29 Sep 2023 12:23:18 -0500
David Lechner <dlechner@baylibre.com> wrote:

> From: David Lechner <david@lechnology.com>
> 
> From: David Lechner <dlechner@baylibre.com>
> 
> - Remove "adi," prefix from gpio names.
> - Sample gpio is now expected to be active low.
> - Convert A0 and A1 gpios to "mode-gpios" gpio array.
> - Convert RES0 and RES1 gpios to "resolution-gpios" gpio array.
> - Remove extraneous lookup tables.
> - Remove unused mode field from state struct.
> - Swap argument order of ad2s1210_set_mode() while we are touching this.
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>
Applied,

Thanks,
Jonathan

> ---
> 
> v3 changes:
> * Fixed multiline comment style.
> 
>  drivers/staging/iio/resolver/ad2s1210.c | 164 +++++++++++++++++---------------
>  1 file changed, 85 insertions(+), 79 deletions(-)
> 
> diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c
> index 2b9377447f6a..0ec3598b600a 100644
> --- a/drivers/staging/iio/resolver/ad2s1210.c
> +++ b/drivers/staging/iio/resolver/ad2s1210.c
> @@ -58,39 +58,21 @@
>  #define AD2S1210_DEF_EXCIT	10000
>  
>  enum ad2s1210_mode {
> -	MOD_POS = 0,
> -	MOD_VEL,
> -	MOD_CONFIG,
> -	MOD_RESERVED,
> +	MOD_POS = 0b00,
> +	MOD_VEL = 0b01,
> +	MOD_RESERVED = 0b10,
> +	MOD_CONFIG = 0b11,
>  };
>  
> -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];
> +	/** GPIO pin connected to SAMPLE line. */
> +	struct gpio_desc *sample_gpio;
> +	/** GPIO pins connected to A0 and A1 lines. */
> +	struct gpio_descs *mode_gpios;
> +	/** GPIO pins connected to RES0 and RES1 lines. */
> +	struct gpio_descs *resolution_gpios;
>  	/** Used to access config registers. */
>  	struct regmap *regmap;
>  	/** The external oscillator frequency in Hz. */
> @@ -98,7 +80,6 @@ struct ad2s1210_state {
>  	unsigned int fexcit;
>  	bool hysteresis;
>  	u8 resolution;
> -	enum ad2s1210_mode mode;
>  	/** For reading raw sample value via SPI. */
>  	__be16 sample __aligned(IIO_DMA_MINALIGN);
>  	/** SPI transmit buffer. */
> @@ -107,18 +88,15 @@ struct ad2s1210_state {
>  	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)
> +static int ad2s1210_set_mode(struct ad2s1210_state *st, enum ad2s1210_mode mode)
>  {
> -	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;
> +	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);
>  }
>  
>  /*
> @@ -143,6 +121,7 @@ static int ad2s1210_regmap_reg_write(void *context, unsigned int reg,
>  			.tx_buf = &st->tx[1],
>  		},
>  	};
> +	int ret;
>  
>  	/* values can only be 7 bits, the MSB indicates an address */
>  	if (val & ~0x7F)
> @@ -151,7 +130,9 @@ static int ad2s1210_regmap_reg_write(void *context, unsigned int reg,
>  	st->tx[0] = reg;
>  	st->tx[1] = val;
>  
> -	ad2s1210_set_mode(MOD_CONFIG, st);
> +	ret = ad2s1210_set_mode(st, MOD_CONFIG);
> +	if (ret < 0)
> +		return ret;
>  
>  	return spi_sync_transfer(st->sdev, xfers, ARRAY_SIZE(xfers));
>  }
> @@ -180,7 +161,10 @@ static int ad2s1210_regmap_reg_read(void *context, unsigned int reg,
>  	};
>  	int ret;
>  
> -	ad2s1210_set_mode(MOD_CONFIG, st);
> +	ret = ad2s1210_set_mode(st, MOD_CONFIG);
> +	if (ret < 0)
> +		return ret;
> +
>  	st->tx[0] = reg;
>  	/*
>  	 * Must be valid register address here otherwise this could write data.
> @@ -219,16 +203,16 @@ int ad2s1210_update_frequency_control_word(struct ad2s1210_state *st)
>  	return regmap_write(st->regmap, AD2S1210_REG_EXCIT_FREQ, 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)
> +static int ad2s1210_set_resolution_gpios(struct ad2s1210_state *st,
> +					 u8 resolution)
>  {
> -	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]);
> +	struct gpio_descs *gpios = st->resolution_gpios;
> +	DECLARE_BITMAP(bitmap, 2);
> +
> +	bitmap[0] = (resolution - 10) >> 1;
> +
> +	return gpiod_set_array_value(gpios->ndescs, gpios->desc, gpios->info,
> +				     bitmap);
>  }
>  
>  static inline int ad2s1210_soft_reset(struct ad2s1210_state *st)
> @@ -305,10 +289,13 @@ static ssize_t ad2s1210_store_resolution(struct device *dev,
>  	if (ret < 0)
>  		goto error_ret;
>  
> -	st->resolution =
> -		ad2s1210_resolution_value[data & AD2S1210_SET_RES];
> -	ad2s1210_set_resolution_pin(st);
> +	ret = ad2s1210_set_resolution_gpios(st, udata);
> +	if (ret < 0)
> +		goto error_ret;
> +
> +	st->resolution = udata;
>  	ret = len;
> +
>  error_ret:
>  	mutex_unlock(&st->lock);
>  	return ret;
> @@ -339,15 +326,19 @@ static ssize_t ad2s1210_clear_fault(struct device *dev,
>  	int ret;
>  
>  	mutex_lock(&st->lock);
> -	gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 0);
> +
> +	gpiod_set_value(st->sample_gpio, 1);
>  	/* delay (2 * tck + 20) nano seconds */
>  	udelay(1);
> -	gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 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->gpios[AD2S1210_SAMPLE], 0);
> -	gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 1);
> +
> +	gpiod_set_value(st->sample_gpio, 1);
> +	gpiod_set_value(st->sample_gpio, 0);
> +
>  error_ret:
>  	mutex_unlock(&st->lock);
>  
> @@ -393,19 +384,19 @@ static int ad2s1210_single_conversion(struct ad2s1210_state *st,
>  				      struct iio_chan_spec const *chan,
>  				      int *val)
>  {
> -	int ret = 0;
> +	int ret;
>  
>  	mutex_lock(&st->lock);
> -	gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 0);
> +	gpiod_set_value(st->sample_gpio, 1);
>  	/* delay (6 * tck + 20) nano seconds */
>  	udelay(1);
>  
>  	switch (chan->type) {
>  	case IIO_ANGL:
> -		ad2s1210_set_mode(MOD_POS, st);
> +		ret = ad2s1210_set_mode(st, MOD_POS);
>  		break;
>  	case IIO_ANGL_VEL:
> -		ad2s1210_set_mode(MOD_VEL, st);
> +		ret = ad2s1210_set_mode(st, MOD_VEL);
>  		break;
>  	default:
>  		ret = -EINVAL;
> @@ -432,7 +423,7 @@ static int ad2s1210_single_conversion(struct ad2s1210_state *st,
>  	}
>  
>  error_ret:
> -	gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 1);
> +	gpiod_set_value(st->sample_gpio, 0);
>  	/* delay (2 * tck + 20) nano seconds */
>  	udelay(1);
>  	mutex_unlock(&st->lock);
> @@ -546,7 +537,9 @@ static int ad2s1210_initial(struct ad2s1210_state *st)
>  	int ret;
>  
>  	mutex_lock(&st->lock);
> -	ad2s1210_set_resolution_pin(st);
> +	ret = ad2s1210_set_resolution_gpios(st, st->resolution);
> +	if (ret < 0)
> +		return ret;
>  
>  	/* Use default config register value plus resolution from devicetree. */
>  	data = FIELD_PREP(AD2S1210_PHASE_LOCK_RANGE_44, 1);
> @@ -612,20 +605,34 @@ static int ad2s1210_setup_clocks(struct ad2s1210_state *st)
>  
>  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;
> -		}
> -	}
> +	struct device *dev = &st->sdev->dev;
> +
> +	/* 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");
> +
> +	/* both pins high means that we start with 16-bit resolution */
> +	st->resolution_gpios = devm_gpiod_get_array(dev, "resolution",
> +						    GPIOD_OUT_HIGH);
> +	if (IS_ERR(st->resolution_gpios))
> +		return dev_err_probe(dev, PTR_ERR(st->resolution_gpios),
> +				     "failed to request resolution GPIOs\n");
> +
> +	if (st->resolution_gpios->ndescs != 2)
> +		return dev_err_probe(dev, -EINVAL,
> +				     "requires exactly 2 resolution-gpios\n");
>  
>  	return 0;
>  }
> @@ -690,7 +697,6 @@ static int ad2s1210_probe(struct spi_device *spi)
>  	mutex_init(&st->lock);
>  	st->sdev = spi;
>  	st->hysteresis = true;
> -	st->mode = MOD_CONFIG;
>  	st->resolution = 12;
>  	st->fexcit = AD2S1210_DEF_EXCIT;
>  
>
Jonathan Cameron Sept. 30, 2023, 3 p.m. UTC | #17
On Fri, 29 Sep 2023 12:53:00 -0500
David Lechner <dlechner@baylibre.com> wrote:

> On Fri, Sep 29, 2023 at 12:25 PM David Lechner <dlechner@baylibre.com> wrote:
> >
> > The AD2S1210 resolver has a hysteresis feature that can be used to
> > prevent flicker in the LSB of the position register. This can be either
> > enabled or disabled. Disabling hysteresis is useful for increasing
> > precision by oversampling.
> >
> > Signed-off-by: David Lechner <dlechner@baylibre.com>
> > ---  
> 
> ...
> 
> > +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 hysteresis_available[] = { 0, 1 };  
> 
> This is basically an enable/disable. Should the 1 value be changed to the
> appropriate radians value since this is hysteresis on the position
> (angle) channel?

Good point. However it should be in the _raw units. The text is
slightly more explicit on this for
the variant of hysteresis applied to threshold events as it's
added or substracted from a threshold (and thresholds are in
_raw readings unless only _processed is available).

Does that make 0, 1 correct as we are talking about LSB only?

Jonathan


> 
> > +
> > +       switch (mask) {
> > +       case IIO_CHAN_INFO_HYSTERESIS:
> > +               switch (chan->type) {
> > +               case IIO_ANGL:
> > +                       *vals = hysteresis_available;
> > +                       *type = IIO_VAL_INT;
> > +                       *length = ARRAY_SIZE(hysteresis_available);
> > +                       return IIO_AVAIL_LIST;
> > +               default:
> > +                       return -EINVAL;
> > +               }
> > +       default:
> > +               return -EINVAL;
> > +       }
> > +}
> >
Jonathan Cameron Sept. 30, 2023, 3:03 p.m. UTC | #18
On Fri, 29 Sep 2023 12:23:19 -0500
David Lechner <dlechner@baylibre.com> wrote:

> From: David Lechner <david@lechnology.com>
> 
> From: David Lechner <dlechner@baylibre.com>
> 
> The AD2S1210 resolver has a hysteresis feature that can be used to
> prevent flicker in the LSB of the position register. This can be either
> enabled or disabled. Disabling hysteresis is useful for increasing
> precision by oversampling.
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>
One trivial thing inline.

Thanks,

Jonathan

> ---
> 
> v3 changes:
> * Refactored into more functions to reduce complexity of switch statements.
> * Use early return instead of break in switch statements.
> 
>  drivers/staging/iio/resolver/ad2s1210.c | 86 +++++++++++++++++++++++++++++++--
>  1 file changed, 83 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c
> index 0ec3598b600a..a82cb124a12f 100644
> --- a/drivers/staging/iio/resolver/ad2s1210.c
> +++ b/drivers/staging/iio/resolver/ad2s1210.c
> @@ -78,7 +78,6 @@ struct ad2s1210_state {
>  	/** The external oscillator frequency in Hz. */
>  	unsigned long clkin_hz;
>  	unsigned int fexcit;
> -	bool hysteresis;
>  	u8 resolution;
>  	/** For reading raw sample value via SPI. */
>  	__be16 sample __aligned(IIO_DMA_MINALIGN);
> @@ -430,6 +429,35 @@ static int ad2s1210_single_conversion(struct ad2s1210_state *st,
>  	return ret;
>  }
>  
> +static int ad2s1210_get_hysteresis(struct ad2s1210_state *st, int *val)
> +{
> +	int ret;
> +
> +	mutex_lock(&st->lock);
> +	ret = regmap_test_bits(st->regmap, AD2S1210_REG_CONTROL,
> +			       AD2S1210_ENABLE_HYSTERESIS);
> +	mutex_unlock(&st->lock);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	*val = !!ret;

regmap_test_bits() is documented as returning 1 or 0 anyway.
So this !! isn't necessary..

> +	return IIO_VAL_INT;
> +}
> +
> +static int ad2s1210_set_hysteresis(struct ad2s1210_state *st, int val)
> +{
> +	int ret;
> +
> +	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);
> +
> +	return ret;
> +}
> +
>  static const int ad2s1210_velocity_scale[] = {
>  	17089132, /* 8.192MHz / (2*pi * 2500 / 2^15) */
>  	42722830, /* 8.192MHz / (2*pi * 1000 / 2^15) */
> @@ -462,7 +490,55 @@ static int ad2s1210_read_raw(struct iio_dev *indio_dev,
>  		default:
>  			return -EINVAL;
>  		}
> +	case IIO_CHAN_INFO_HYSTERESIS:
> +		switch (chan->type) {
> +		case IIO_ANGL:
> +			return ad2s1210_get_hysteresis(st, val);
> +		default:
> +			return -EINVAL;
> +		}
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +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 hysteresis_available[] = { 0, 1 };
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_HYSTERESIS:
> +		switch (chan->type) {
> +		case IIO_ANGL:
> +			*vals = hysteresis_available;
> +			*type = IIO_VAL_INT;
> +			*length = ARRAY_SIZE(hysteresis_available);
> +			return IIO_AVAIL_LIST;
> +		default:
> +			return -EINVAL;
> +		}
> +	default:
> +		return -EINVAL;
> +	}
> +}
>  
> +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);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_HYSTERESIS:
> +		switch (chan->type) {
> +		case IIO_ANGL:
> +			return ad2s1210_set_hysteresis(st, val);
> +		default:
> +			return -EINVAL;
> +		}
>  	default:
>  		return -EINVAL;
>  	}
> @@ -503,7 +579,10 @@ static const struct iio_chan_spec ad2s1210_channels[] = {
>  		.indexed = 1,
>  		.channel = 0,
>  		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> -				      BIT(IIO_CHAN_INFO_SCALE),
> +				      BIT(IIO_CHAN_INFO_SCALE) |
> +				      BIT(IIO_CHAN_INFO_HYSTERESIS),
> +		.info_mask_separate_available =
> +					BIT(IIO_CHAN_INFO_HYSTERESIS),
>  	}, {
>  		.type = IIO_ANGL_VEL,
>  		.indexed = 1,
> @@ -581,6 +660,8 @@ static int ad2s1210_debugfs_reg_access(struct iio_dev *indio_dev,
>  
>  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,
>  };
> @@ -696,7 +777,6 @@ static int ad2s1210_probe(struct spi_device *spi)
>  
>  	mutex_init(&st->lock);
>  	st->sdev = spi;
> -	st->hysteresis = true;
>  	st->resolution = 12;
>  	st->fexcit = AD2S1210_DEF_EXCIT;
>  
>
Jonathan Cameron Sept. 30, 2023, 3:06 p.m. UTC | #19
On Fri, 29 Sep 2023 12:23:20 -0500
David Lechner <dlechner@baylibre.com> wrote:

> From: David Lechner <david@lechnology.com>
> 
> From: David Lechner <dlechner@baylibre.com>
> 
> This combines the ad2s1210_update_frequency_control_word() and
> ad2s1210_soft_reset() functions into a single function since they
> both have to be called together. (The software reset does not reset
> any configuration registers, it only updates the excitation output
> and resets the tracking loop.)
> 
> Also clean up a few things while touching this:
> - move AD2S1210_DEF_EXCIT macro with similar macros
> - remove unnecessary dev_err() calls
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>
I'm sure it'll will stop me at some point but I'll keep trying to apply
patches even though I skipped previous one on basis the fewer I see
again, the less terrifying my inbox looks :)

So applied this one and pushed out as testing. Bit of fuzz as you'd expect.

Thanks,

Jonathan

> ---
> 
> v3 changes:
> * Expanded comment on soft reset register write.
> * Fixed multiline comment style.
> 
>  drivers/staging/iio/resolver/ad2s1210.c | 66 +++++++++++++++++----------------
>  1 file changed, 34 insertions(+), 32 deletions(-)
> 
> diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c
> index a82cb124a12f..28ab877e1bc0 100644
> --- a/drivers/staging/iio/resolver/ad2s1210.c
> +++ b/drivers/staging/iio/resolver/ad2s1210.c
> @@ -51,12 +51,11 @@
>  #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
>  
> -#define AD2S1210_DEF_EXCIT	10000
> -
>  enum ad2s1210_mode {
>  	MOD_POS = 0b00,
>  	MOD_VEL = 0b01,
> @@ -188,18 +187,32 @@ static int ad2s1210_regmap_reg_read(void *context, unsigned int reg,
>  	return 0;
>  }
>  
> -static inline
> -int ad2s1210_update_frequency_control_word(struct ad2s1210_state *st)
> +/*
> + * Sets the excitation frequency and performs software reset.
> + *
> + * Must be called with lock held.
> + */
> +static int ad2s1210_reinit_excitation_frequency(struct ad2s1210_state *st,
> +						u16 fexcit)
>  {
> -	unsigned char fcw;
> +	int ret;
> +	u8 fcw;
>  
> -	fcw = (unsigned char)(st->fexcit * (1 << 15) / st->clkin_hz);
> -	if (fcw < AD2S1210_MIN_FCW || fcw > AD2S1210_MAX_FCW) {
> -		dev_err(&st->sdev->dev, "ad2s1210: FCW out of range\n");
> +	fcw = fexcit * (1 << 15) / st->clkin_hz;
> +	if (fcw < AD2S1210_MIN_FCW || fcw > AD2S1210_MAX_FCW)
>  		return -ERANGE;
> -	}
>  
> -	return regmap_write(st->regmap, AD2S1210_REG_EXCIT_FREQ, fcw);
> +	ret = regmap_write(st->regmap, AD2S1210_REG_EXCIT_FREQ, fcw);
> +	if (ret < 0)
> +		return ret;
> +
> +	st->fexcit = fexcit;
> +
> +	/*
> +	 * Software reset reinitializes the excitation frequency output.
> +	 * It does not reset any of the configuration registers.
> +	 */
> +	return regmap_write(st->regmap, AD2S1210_REG_SOFT_RESET, 0);
>  }
>  
>  static int ad2s1210_set_resolution_gpios(struct ad2s1210_state *st,
> @@ -214,11 +227,6 @@ static int ad2s1210_set_resolution_gpios(struct ad2s1210_state *st,
>  				     bitmap);
>  }
>  
> -static inline int ad2s1210_soft_reset(struct ad2s1210_state *st)
> -{
> -	return regmap_write(st->regmap, AD2S1210_REG_SOFT_RESET, 0);
> -}
> -
>  static ssize_t ad2s1210_show_fexcit(struct device *dev,
>  				    struct device_attribute *attr,
>  				    char *buf)
> @@ -233,27 +241,24 @@ static ssize_t ad2s1210_store_fexcit(struct device *dev,
>  				     const char *buf, size_t len)
>  {
>  	struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev));
> -	unsigned int fexcit;
> +	u16 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");
> +	ret = kstrtou16(buf, 10, &fexcit);
> +	if (ret < 0 || fexcit < AD2S1210_MIN_EXCIT || fexcit > AD2S1210_MAX_EXCIT)
>  		return -EINVAL;
> -	}
> +
>  	mutex_lock(&st->lock);
> -	st->fexcit = fexcit;
> -	ret = ad2s1210_update_frequency_control_word(st);
> +	ret = ad2s1210_reinit_excitation_frequency(st, fexcit);
>  	if (ret < 0)
>  		goto error_ret;
> -	ret = ad2s1210_soft_reset(st);
> +
> +	ret = len;
> +
>  error_ret:
>  	mutex_unlock(&st->lock);
>  
> -	return ret < 0 ? ret : len;
> +	return ret;
>  }
>  
>  static ssize_t ad2s1210_show_resolution(struct device *dev,
> @@ -630,10 +635,8 @@ static int ad2s1210_initial(struct ad2s1210_state *st)
>  	if (ret < 0)
>  		goto error_ret;
>  
> -	ret = ad2s1210_update_frequency_control_word(st);
> -	if (ret < 0)
> -		goto error_ret;
> -	ret = ad2s1210_soft_reset(st);
> +	ret = ad2s1210_reinit_excitation_frequency(st, AD2S1210_DEF_EXCIT);
> +
>  error_ret:
>  	mutex_unlock(&st->lock);
>  	return ret;
> @@ -778,7 +781,6 @@ static int ad2s1210_probe(struct spi_device *spi)
>  	mutex_init(&st->lock);
>  	st->sdev = spi;
>  	st->resolution = 12;
> -	st->fexcit = AD2S1210_DEF_EXCIT;
>  
>  	ret = ad2s1210_setup_clocks(st);
>  	if (ret < 0)
>
Jonathan Cameron Sept. 30, 2023, 3:08 p.m. UTC | #20
On Fri, 29 Sep 2023 12:23:21 -0500
David Lechner <dlechner@baylibre.com> wrote:

> From: David Lechner <david@lechnology.com>
> 
> From: David Lechner <dlechner@baylibre.com>
> 
> This modifies the ad2s1210_show_fexcit() function to read the excitation
> frequency from the control register. This way we don't have to keep
> track of the value and don't risk returning a stale value.
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>
Applied.

A bit more fuzz with this one as context was different for the first hunk.

Jonathan

> ---
> 
> v3 changes: None
> 
>  drivers/staging/iio/resolver/ad2s1210.c | 19 +++++++++++++++----
>  1 file changed, 15 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c
> index 28ab877e1bc0..b15d71b17266 100644
> --- a/drivers/staging/iio/resolver/ad2s1210.c
> +++ b/drivers/staging/iio/resolver/ad2s1210.c
> @@ -76,7 +76,6 @@ struct ad2s1210_state {
>  	struct regmap *regmap;
>  	/** The external oscillator frequency in Hz. */
>  	unsigned long clkin_hz;
> -	unsigned int fexcit;
>  	u8 resolution;
>  	/** For reading raw sample value via SPI. */
>  	__be16 sample __aligned(IIO_DMA_MINALIGN);
> @@ -206,8 +205,6 @@ static int ad2s1210_reinit_excitation_frequency(struct ad2s1210_state *st,
>  	if (ret < 0)
>  		return ret;
>  
> -	st->fexcit = fexcit;
> -
>  	/*
>  	 * Software reset reinitializes the excitation frequency output.
>  	 * It does not reset any of the configuration registers.
> @@ -232,8 +229,22 @@ static ssize_t ad2s1210_show_fexcit(struct device *dev,
>  				    char *buf)
>  {
>  	struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev));
> +	unsigned int value;
> +	u16 fexcit;
> +	int ret;
>  
> -	return sprintf(buf, "%u\n", st->fexcit);
> +	mutex_lock(&st->lock);
> +	ret = regmap_read(st->regmap, AD2S1210_REG_EXCIT_FREQ, &value);
> +	if (ret < 0)
> +		goto error_ret;
> +
> +	fexcit = value * st->clkin_hz / (1 << 15);
> +
> +	ret = sprintf(buf, "%u\n", fexcit);
> +
> +error_ret:
> +	mutex_unlock(&st->lock);
> +	return ret;
>  }
>  
>  static ssize_t ad2s1210_store_fexcit(struct device *dev,
>
Jonathan Cameron Sept. 30, 2023, 3:12 p.m. UTC | #21
On Fri, 29 Sep 2023 12:23:22 -0500
David Lechner <dlechner@baylibre.com> wrote:

> From: David Lechner <david@lechnology.com>
> 
> From: David Lechner <dlechner@baylibre.com>
> 
> The ad2s1210 driver has a device-specific attribute `fexcit` for setting
> the frequency of the excitation output. This converts it to a channel in
> order to use standard IIO ABI.
> 
> The excitation frequency is an analog output that generates a sine wave.
> Only the frequency is configurable. According to the datasheet, the
> specified range of the excitation frequency is from 2 kHz to 20 kHz and
> can be set in increments of 250 Hz.
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>
And this is the point where things got non trivial given I skipped
the hysterisis patch.

This looks good to me though, so will pick it up once questions on that
earlier patch are resolved.

Thanks,

Jonathan

> ---
> 
> v3 changes:
> * This is a new patch in v3 instead of "iio: resolver: ad2s1210: rename fexcit
>   attribute"
> 
>  drivers/staging/iio/resolver/ad2s1210.c | 122 ++++++++++++++++++--------------
>  1 file changed, 70 insertions(+), 52 deletions(-)
> 
> diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c
> index b15d71b17266..6accb9e3db46 100644
> --- a/drivers/staging/iio/resolver/ad2s1210.c
> +++ b/drivers/staging/iio/resolver/ad2s1210.c
> @@ -224,54 +224,6 @@ static int ad2s1210_set_resolution_gpios(struct ad2s1210_state *st,
>  				     bitmap);
>  }
>  
> -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));
> -	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->clkin_hz / (1 << 15);
> -
> -	ret = sprintf(buf, "%u\n", fexcit);
> -
> -error_ret:
> -	mutex_unlock(&st->lock);
> -	return ret;
> -}
> -
> -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));
> -	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_reinit_excitation_frequency(st, fexcit);
> -	if (ret < 0)
> -		goto error_ret;
> -
> -	ret = len;
> -
> -error_ret:
> -	mutex_unlock(&st->lock);
> -
> -	return ret;
> -}
> -
>  static ssize_t ad2s1210_show_resolution(struct device *dev,
>  					struct device_attribute *attr,
>  					char *buf)
> @@ -474,6 +426,38 @@ static int ad2s1210_set_hysteresis(struct ad2s1210_state *st, int val)
>  	return ret;
>  }
>  
> +static int ad2s1210_get_excitation_frequency(struct ad2s1210_state *st, int *val)
> +{
> +	unsigned int reg_val;
> +	int ret;
> +
> +	mutex_lock(&st->lock);
> +	ret = regmap_read(st->regmap, AD2S1210_REG_EXCIT_FREQ, &reg_val);
> +	if (ret < 0)
> +		goto error_ret;
> +
> +	*val = reg_val * st->clkin_hz / (1 << 15);
> +	ret = IIO_VAL_INT;
> +
> +error_ret:
> +	mutex_unlock(&st->lock);
> +	return ret;
> +}
> +
> +static int ad2s1210_set_excitation_frequency(struct ad2s1210_state *st, int val)
> +{
> +	int ret;
> +
> +	if (val < AD2S1210_MIN_EXCIT || val > AD2S1210_MAX_EXCIT)
> +		return -EINVAL;
> +
> +	mutex_lock(&st->lock);
> +	ret = ad2s1210_reinit_excitation_frequency(st, val);
> +	mutex_unlock(&st->lock);
> +
> +	return ret;
> +}
> +
>  static const int ad2s1210_velocity_scale[] = {
>  	17089132, /* 8.192MHz / (2*pi * 2500 / 2^15) */
>  	42722830, /* 8.192MHz / (2*pi * 1000 / 2^15) */
> @@ -506,6 +490,13 @@ static int ad2s1210_read_raw(struct iio_dev *indio_dev,
>  		default:
>  			return -EINVAL;
>  		}
> +	case IIO_CHAN_INFO_FREQUENCY:
> +		switch (chan->type) {
> +		case IIO_ALTVOLTAGE:
> +			return ad2s1210_get_excitation_frequency(st, val);
> +		default:
> +			return -EINVAL;
> +		}
>  	case IIO_CHAN_INFO_HYSTERESIS:
>  		switch (chan->type) {
>  		case IIO_ANGL:
> @@ -523,9 +514,23 @@ static int ad2s1210_read_avail(struct iio_dev *indio_dev,
>  			       const int **vals, int *type,
>  			       int *length, long mask)
>  {
> +	static const int excitation_frequency_available[] = {
> +		AD2S1210_MIN_EXCIT,
> +		250, /* step */
> +		AD2S1210_MAX_EXCIT,
> +	};
>  	static const int hysteresis_available[] = { 0, 1 };
>  
>  	switch (mask) {
> +	case IIO_CHAN_INFO_FREQUENCY:
> +		switch (chan->type) {
> +		case IIO_ALTVOLTAGE:
> +			*type = IIO_VAL_INT;
> +			*vals = excitation_frequency_available;
> +			return IIO_AVAIL_RANGE;
> +		default:
> +			return -EINVAL;
> +		}
>  	case IIO_CHAN_INFO_HYSTERESIS:
>  		switch (chan->type) {
>  		case IIO_ANGL:
> @@ -548,6 +553,13 @@ static int ad2s1210_write_raw(struct iio_dev *indio_dev,
>  	struct ad2s1210_state *st = iio_priv(indio_dev);
>  
>  	switch (mask) {
> +	case IIO_CHAN_INFO_FREQUENCY:
> +		switch (chan->type) {
> +		case IIO_ALTVOLTAGE:
> +			return ad2s1210_set_excitation_frequency(st, val);
> +		default:
> +			return -EINVAL;
> +		}
>  	case IIO_CHAN_INFO_HYSTERESIS:
>  		switch (chan->type) {
>  		case IIO_ANGL:
> @@ -560,8 +572,6 @@ static int ad2s1210_write_raw(struct iio_dev *indio_dev,
>  	}
>  }
>  
> -static IIO_DEVICE_ATTR(fexcit, 0644,
> -		       ad2s1210_show_fexcit,	ad2s1210_store_fexcit, 0);
>  static IIO_DEVICE_ATTR(bits, 0644,
>  		       ad2s1210_show_resolution, ad2s1210_store_resolution, 0);
>  static IIO_DEVICE_ATTR(fault, 0644,
> @@ -605,11 +615,19 @@ static const struct iio_chan_spec ad2s1210_channels[] = {
>  		.channel = 0,
>  		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>  				      BIT(IIO_CHAN_INFO_SCALE),
> -	}
> +	}, {
> +		/* excitation frequency output */
> +		.type = IIO_ALTVOLTAGE,
> +		.indexed = 1,
> +		.channel = 0,
> +		.output = 1,
> +		.scan_index = -1,
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_FREQUENCY),
> +		.info_mask_separate_available = BIT(IIO_CHAN_INFO_FREQUENCY),
> +	},
>  };
>  
>  static struct attribute *ad2s1210_attributes[] = {
> -	&iio_dev_attr_fexcit.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,
>
Jonathan Cameron Sept. 30, 2023, 3:15 p.m. UTC | #22
On Fri, 29 Sep 2023 12:23:23 -0500
David Lechner <dlechner@baylibre.com> wrote:

> From: David Lechner <david@lechnology.com>
> 
> From: David Lechner <dlechner@baylibre.com>
> 
> Selecting the resolution was implemented as the `bits` sysfs attribute.
> However, the selection of the resolution depends on how the hardware
> is wired and the specific application, so this is rather a job for
> devicetree to describe.
> 
> A new devicetree property `adi,resolution` to specify the resolution
> required for each chip is added and the `bits` sysfs attribute is
> removed.

Description need updating to reflect property having a different name.

Otherwise this LGTM
> 
> Since the resolution is now supplied by a devicetree property, the
> resolution-gpios are now optional and we can allow for the case where
> the resolution pins on the AD2S1210 are hard-wired instead of requiring
> them to be connected to gpios.
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>
...

>  
> +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);
Doesn't match patch description of what this is called.

> +	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;
> +}
> +
Jonathan Cameron Sept. 30, 2023, 3:18 p.m. UTC | #23
On Fri, 29 Sep 2023 12:23:24 -0500
David Lechner <dlechner@baylibre.com> wrote:

> From: David Lechner <david@lechnology.com>
> 
> From: David Lechner <dlechner@baylibre.com>
> 
> The AD2S1210 chip has a phase lock range feature that allows selecting
> the allowable phase difference between the excitation output and the
> sine and cosine inputs. This can be set to either 44 degrees (default)
> or 360 degrees.
> 
> This patch adds a new phase channel with a threshold event that can be
> used to configure the phase lock range. Actually emitting the event
> will be added in a subsequent patch.
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>
> ---
Looks good to me.
Jonathan Cameron Sept. 30, 2023, 3:29 p.m. UTC | #24
On Fri, 29 Sep 2023 12:23:26 -0500
David Lechner <dlechner@baylibre.com> wrote:

> From: David Lechner <david@lechnology.com>
> 
> From: David Lechner <dlechner@baylibre.com>
> 
> The AD2S1210 monitors the internal error signal (difference between
> estimated angle and measured angle) to determine a loss of position
> tracking (LOT) condition. When the error value exceeds a threshold, a
> fault is triggered. This threshold is user-configurable.
> 
> This patch converts the custom lot_high_thrd and lot_low_thrd attributes
> in the ad2s1210 driver to standard event attributes. This will allow
> tooling to be able to expose these in a generic way.
> 
> Since the low threshold determines the hysteresis, it requires some
> special handling to expose the difference between the high and low
> register values as the hysteresis instead of exposing the low register
> value directly.
> 
> The attributes also return the values in radians now as required by the
> ABI.
> 
> Actually emitting the fault event will be done in a later patch.
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>
> ---

A style comment inline. Otherwise this an any patches before it I skipped
commenting on look fine to me.


> +
> +static int ad2s1210_get_lot_low_threshold(struct ad2s1210_state *st,
> +					  int *val, int *val2)
> +{
> +	unsigned int high_reg_val, low_reg_val;
> +	int ret;
> +
> +	mutex_lock(&st->lock);
> +	ret = regmap_read(st->regmap, AD2S1210_REG_LOT_HIGH_THRD, &high_reg_val);
> +	if (ret < 0)
> +		goto error_ret;
> +
> +	ret = regmap_read(st->regmap, AD2S1210_REG_LOT_LOW_THRD, &low_reg_val);
> +
A separate error path is going to be easier to read.

	return IIO_VAL_INT_PLUS_MICRO;

error_ret:
	mutex_unlock(&st->lock);

	return ret;

Of make use of the new stuff in cleanup.h etc.  something like.

	scoped_guard(mutex)(&st->lock) {
		ret = regmap_read(st->regmap, ...)
		if (ret)
			return ret;

		ret = regmap_read(...)
		if (ret)
			return ret;
	}
	
	/* sysfs value is hysteresis rather than actual low value */

In general you could make could use o
guard(mutex)(&st->lock);
in the other functions where  you hold the lock to the end of the function.

Could do that as a follow on patch though and as that stuff is all
very new, I'm not going to mind if you don't want to use it at all and
just use the approach above.

> +error_ret:
> +	mutex_unlock(&st->lock);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	/* sysfs value is hysteresis rather than actual low value */
> +	*val = 0;
> +	*val2 = (high_reg_val - low_reg_val) *
> +		ad2s1210_lot_threshold_urad_per_lsb[st->resolution];
> +	return IIO_VAL_INT_PLUS_MICRO;
> +}
> +
> +static int ad2s1210_set_lot_low_threshold(struct ad2s1210_state *st,
> +					  int val, int val2)
> +{
> +	unsigned int reg_val, hysteresis;
> +	int ret;
> +
> +	/* all valid values are between 0 and pi/4 radians */
> +	if (val != 0)
> +		return -EINVAL;
> +
> +	hysteresis = val2 / ad2s1210_lot_threshold_urad_per_lsb[st->resolution];
> +
> +	mutex_lock(&st->lock);
> +	ret = regmap_read(st->regmap, AD2S1210_REG_LOT_HIGH_THRD, &reg_val);
> +	if (ret < 0)
> +		goto error_ret;
> +
> +	ret = regmap_write(st->regmap, AD2S1210_REG_LOT_LOW_THRD,
> +			   reg_val - hysteresis);
> +
> +error_ret:
> +	mutex_unlock(&st->lock);
> +
> +	return ret;
> +}
>
Jonathan Cameron Sept. 30, 2023, 3:32 p.m. UTC | #25
On Fri, 29 Sep 2023 12:23:27 -0500
David Lechner <dlechner@baylibre.com> wrote:

> From: David Lechner <david@lechnology.com>
> 
> From: David Lechner <dlechner@baylibre.com>
> 
> The AD2S1210 has a programmable threshold for the loss of signal (LOS)
> fault. This fault is triggered when either the sine or cosine input
> falls below the threshold voltage.
> 
> This patch converts the custom device LOS threshold attribute to an
> event falling edge threshold attribute on a new monitor signal channel.
> The monitor signal is an internal signal that combines the amplitudes
> of the sine and cosine inputs as well as the current angle and position
> output. This signal is used to detect faults in the input signals.
> 
> The attribute now uses millivolts instead of the raw register value in
> accordance with the IIO ABI.
> 
> Emitting the event will be implemented in a later patch.
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>

I think I'm fine with treating these internal signals like this, but
I would ideally like someone from Analog devices to take a look at how
these are being done and make sure our interpretations of the signals
make sense to them.  We are pushing the boundaries a little here (though
we have done similar before for fault events I think.)

Jonathan
Jonathan Cameron Sept. 30, 2023, 3:33 p.m. UTC | #26
On Fri, 29 Sep 2023 12:23:28 -0500
David Lechner <dlechner@baylibre.com> wrote:

> From: David Lechner <david@lechnology.com>
> 
> From: David Lechner <dlechner@baylibre.com>
> 
> The AD2S1210 has a programmable threshold for the degradation of signal
> (DOS) overrange fault. This fault is triggered when either the sine or
> cosine input rises the threshold voltage.

rises above the threshold voltage.

> 
> This patch converts the custom device DOS overrange threshold attribute
> to an event rising edge threshold attribute on the monitor signal
> channel.
> 
> The attribute now uses millivolts instead of the raw register value in
> accordance with the IIO ABI.
> 
> Emitting the event will be implemented in a later patch.
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>
Jonathan Cameron Sept. 30, 2023, 3:42 p.m. UTC | #27
On Fri, 29 Sep 2023 12:23:27 -0500
David Lechner <dlechner@baylibre.com> wrote:

> From: David Lechner <david@lechnology.com>
> 
> From: David Lechner <dlechner@baylibre.com>
> 
> The AD2S1210 has a programmable threshold for the loss of signal (LOS)
> fault. This fault is triggered when either the sine or cosine input
> falls below the threshold voltage.
> 
> This patch converts the custom device LOS threshold attribute to an
> event falling edge threshold attribute on a new monitor signal channel.
> The monitor signal is an internal signal that combines the amplitudes
> of the sine and cosine inputs as well as the current angle and position
> output. This signal is used to detect faults in the input signals.

Hmm. Looking forwards, I'm less sure that we should be shoving all these
error conditions onto one channel. Fundamentally we have
sine and cosine inputs. I think we should treat those as separate channels
and include a third differential channel between them.

So this one becomes a double event (you need to signal it on both
cosine and sine channels).  The DOS overange is similar.
The DOS mismatch is a threshold on the differential channel giving

events/in_altvoltage0_thresh_falling_value
events/in_altvoltage1_thresh_falling_value (these match)
events/in_altvoltage0_thresh_rising_value
events/in_altvoltage1_thresh_rising_value (matches previous which is fine)
events/in_altvoltage1-0_mag_rising_value

Does that work here?  Avoids smashing different types of signals together.
We could even do the LOT as differential between two angle channels
(tracking one and measured one) but meh that's getting complex.

Note this will rely on channel labels to make the above make any sense at all.

Jonathan


> 
> The attribute now uses millivolts instead of the raw register value in
> accordance with the IIO ABI.
> 
> Emitting the event will be implemented in a later patch.
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>
> ---
> 
> v3 changes: This is a new patch in v3
> 
>  drivers/staging/iio/resolver/ad2s1210.c | 75 +++++++++++++++++++++++++++++++--
>  1 file changed, 71 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c
> index 5cc8106800d6..7abbc184c351 100644
> --- a/drivers/staging/iio/resolver/ad2s1210.c
> +++ b/drivers/staging/iio/resolver/ad2s1210.c
> @@ -66,6 +66,11 @@
>  #define PHASE_360_DEG_TO_RAD_INT 6
>  #define PHASE_360_DEG_TO_RAD_MICRO 283185
>  
> +/* Threshold voltage registers have 1 LSB == 38 mV */
> +#define THRESHOLD_MILLIVOLT_PER_LSB 38
> +/* max voltage for threshold registers is 0x7F * 38 mV */
> +#define THRESHOLD_RANGE_STR "[0 38 4826]"
> +
>  enum ad2s1210_mode {
>  	MOD_POS = 0b00,
>  	MOD_VEL = 0b01,
> @@ -448,6 +453,38 @@ static const int ad2s1210_lot_threshold_urad_per_lsb[] = {
>  	1237, /* 16-bit: same as 14-bit */
>  };
>  
> +static int ad2s1210_get_voltage_threshold(struct ad2s1210_state *st,
> +					  unsigned int reg, int *val)
> +{
> +	unsigned int reg_val;
> +	int ret;
> +
> +	mutex_lock(&st->lock);
> +	ret = regmap_read(st->regmap, reg, &reg_val);
> +	mutex_unlock(&st->lock);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	*val = reg_val * THRESHOLD_MILLIVOLT_PER_LSB;
> +	return IIO_VAL_INT;
> +}
> +
> +static int ad2s1210_set_voltage_threshold(struct ad2s1210_state *st,
> +					  unsigned int reg, int val)
> +{
> +	unsigned int reg_val;
> +	int ret;
> +
> +	reg_val = val / THRESHOLD_MILLIVOLT_PER_LSB;
> +
> +	mutex_lock(&st->lock);
> +	ret = regmap_write(st->regmap, reg, reg_val);
> +	mutex_unlock(&st->lock);
> +
> +	return ret;
> +}
> +
>  static int ad2s1210_get_lot_high_threshold(struct ad2s1210_state *st,
>  					   int *val, int *val2)
>  {
> @@ -706,9 +743,6 @@ static int ad2s1210_write_raw(struct iio_dev *indio_dev,
>  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);
> @@ -745,6 +779,16 @@ static const struct iio_event_spec ad2s1210_phase_event_spec[] = {
>  	},
>  };
>  
> +static const struct iio_event_spec ad2s1210_monitor_signal_event_spec[] = {
> +	{
> +		/* Sine/cosine below LOS threshold fault. */
> +		.type = IIO_EV_TYPE_THRESH,
> +		.dir = IIO_EV_DIR_FALLING,
> +		/* Loss of signal threshold. */
> +		.mask_separate = BIT(IIO_EV_INFO_VALUE),
> +	},
> +};
> +
>  static const struct iio_chan_spec ad2s1210_channels[] = {
>  	{
>  		.type = IIO_ANGL,
> @@ -803,12 +847,19 @@ static const struct iio_chan_spec ad2s1210_channels[] = {
>  		.scan_index = -1,
>  		.info_mask_separate = BIT(IIO_CHAN_INFO_FREQUENCY),
>  		.info_mask_separate_available = BIT(IIO_CHAN_INFO_FREQUENCY),
> +	}, {
> +		/* monitor signal */
> +		.type = IIO_ALTVOLTAGE,
> +		.indexed = 1,
> +		.channel = 0,
> +		.scan_index = -1,
> +		.event_spec = ad2s1210_monitor_signal_event_spec,
> +		.num_event_specs = ARRAY_SIZE(ad2s1210_monitor_signal_event_spec),
>  	},
>  };
>  
>  static struct attribute *ad2s1210_attributes[] = {
>  	&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,
> @@ -847,11 +898,13 @@ IIO_CONST_ATTR(in_phase0_mag_value_available,
>  	       __stringify(PHASE_44_DEG_TO_RAD_MICRO) " "
>  	       __stringify(PHASE_360_DEG_TO_RAD_INT) "."
>  	       __stringify(PHASE_360_DEG_TO_RAD_MICRO));
> +IIO_CONST_ATTR(in_altvoltage0_thresh_falling_value_available, THRESHOLD_RANGE_STR);
>  IIO_DEVICE_ATTR_RO(in_angl1_thresh_rising_value_available, 0);
>  IIO_DEVICE_ATTR_RO(in_angl1_thresh_rising_hysteresis_available, 0);
>  
>  static struct attribute *ad2s1210_event_attributes[] = {
>  	&iio_const_attr_in_phase0_mag_value_available.dev_attr.attr,
> +	&iio_const_attr_in_altvoltage0_thresh_falling_value_available.dev_attr.attr,
>  	&iio_dev_attr_in_angl1_thresh_rising_value_available.dev_attr.attr,
>  	&iio_dev_attr_in_angl1_thresh_rising_hysteresis_available.dev_attr.attr,
>  	NULL,
> @@ -904,6 +957,13 @@ static int ad2s1210_read_event_value(struct iio_dev *indio_dev,
>  		default:
>  			return -EINVAL;
>  		}
> +	case IIO_ALTVOLTAGE:
> +		if (chan->output)
> +			return -EINVAL;
> +		if (type == IIO_EV_TYPE_THRESH && dir == IIO_EV_DIR_FALLING)
> +			return ad2s1210_get_voltage_threshold(st,
> +						AD2S1210_REG_LOS_THRD, val);
> +		return -EINVAL;
>  	case IIO_PHASE:
>  		return ad2s1210_get_phase_lock_range(st, val, val2);
>  	default:
> @@ -930,6 +990,13 @@ static int ad2s1210_write_event_value(struct iio_dev *indio_dev,
>  		default:
>  			return -EINVAL;
>  		}
> +	case IIO_ALTVOLTAGE:
> +		if (chan->output)
> +			return -EINVAL;
> +		if (type == IIO_EV_TYPE_THRESH && dir == IIO_EV_DIR_FALLING)
> +			return ad2s1210_set_voltage_threshold(st,
> +						AD2S1210_REG_LOS_THRD, val);
> +		return -EINVAL;
>  	case IIO_PHASE:
>  		return ad2s1210_set_phase_lock_range(st, val, val2);
>  	default:
>
Jonathan Cameron Sept. 30, 2023, 3:46 p.m. UTC | #28
On Sat, 30 Sep 2023 16:42:51 +0100
Jonathan Cameron <jic23@kernel.org> wrote:

> On Fri, 29 Sep 2023 12:23:27 -0500
> David Lechner <dlechner@baylibre.com> wrote:
> 
> > From: David Lechner <david@lechnology.com>
> > 
> > From: David Lechner <dlechner@baylibre.com>
> > 
> > The AD2S1210 has a programmable threshold for the loss of signal (LOS)
> > fault. This fault is triggered when either the sine or cosine input
> > falls below the threshold voltage.
> > 
> > This patch converts the custom device LOS threshold attribute to an
> > event falling edge threshold attribute on a new monitor signal channel.
> > The monitor signal is an internal signal that combines the amplitudes
> > of the sine and cosine inputs as well as the current angle and position
> > output. This signal is used to detect faults in the input signals.  
> 
> Hmm. Looking forwards, I'm less sure that we should be shoving all these
> error conditions onto one channel. Fundamentally we have
> sine and cosine inputs. I think we should treat those as separate channels
> and include a third differential channel between them.
> 
> So this one becomes a double event (you need to signal it on both
> cosine and sine channels).  The DOS overange is similar.
> The DOS mismatch is a threshold on the differential channel giving
> 
> events/in_altvoltage0_thresh_falling_value
> events/in_altvoltage1_thresh_falling_value (these match)
> events/in_altvoltage0_thresh_rising_value
> events/in_altvoltage1_thresh_rising_value (matches previous which is fine)
> events/in_altvoltage1-0_mag_rising_value

Sorry, got the syntax wrong :( Should have checked the ABI docs.

events/in_altvoltage1-altvoltage0_mag_rising_value

> 
> Does that work here?  Avoids smashing different types of signals together.
> We could even do the LOT as differential between two angle channels
> (tracking one and measured one) but meh that's getting complex.
> 
> Note this will rely on channel labels to make the above make any sense at all.
> 
> Jonathan
> 
> 
> > 
> > The attribute now uses millivolts instead of the raw register value in
> > accordance with the IIO ABI.
> > 
> > Emitting the event will be implemented in a later patch.
> > 
> > Signed-off-by: David Lechner <dlechner@baylibre.com>
> > ---
> > 
> > v3 changes: This is a new patch in v3
> > 
> >  drivers/staging/iio/resolver/ad2s1210.c | 75 +++++++++++++++++++++++++++++++--
> >  1 file changed, 71 insertions(+), 4 deletions(-)
> > 
> > diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c
> > index 5cc8106800d6..7abbc184c351 100644
> > --- a/drivers/staging/iio/resolver/ad2s1210.c
> > +++ b/drivers/staging/iio/resolver/ad2s1210.c
> > @@ -66,6 +66,11 @@
> >  #define PHASE_360_DEG_TO_RAD_INT 6
> >  #define PHASE_360_DEG_TO_RAD_MICRO 283185
> >  
> > +/* Threshold voltage registers have 1 LSB == 38 mV */
> > +#define THRESHOLD_MILLIVOLT_PER_LSB 38
> > +/* max voltage for threshold registers is 0x7F * 38 mV */
> > +#define THRESHOLD_RANGE_STR "[0 38 4826]"
> > +
> >  enum ad2s1210_mode {
> >  	MOD_POS = 0b00,
> >  	MOD_VEL = 0b01,
> > @@ -448,6 +453,38 @@ static const int ad2s1210_lot_threshold_urad_per_lsb[] = {
> >  	1237, /* 16-bit: same as 14-bit */
> >  };
> >  
> > +static int ad2s1210_get_voltage_threshold(struct ad2s1210_state *st,
> > +					  unsigned int reg, int *val)
> > +{
> > +	unsigned int reg_val;
> > +	int ret;
> > +
> > +	mutex_lock(&st->lock);
> > +	ret = regmap_read(st->regmap, reg, &reg_val);
> > +	mutex_unlock(&st->lock);
> > +
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	*val = reg_val * THRESHOLD_MILLIVOLT_PER_LSB;
> > +	return IIO_VAL_INT;
> > +}
> > +
> > +static int ad2s1210_set_voltage_threshold(struct ad2s1210_state *st,
> > +					  unsigned int reg, int val)
> > +{
> > +	unsigned int reg_val;
> > +	int ret;
> > +
> > +	reg_val = val / THRESHOLD_MILLIVOLT_PER_LSB;
> > +
> > +	mutex_lock(&st->lock);
> > +	ret = regmap_write(st->regmap, reg, reg_val);
> > +	mutex_unlock(&st->lock);
> > +
> > +	return ret;
> > +}
> > +
> >  static int ad2s1210_get_lot_high_threshold(struct ad2s1210_state *st,
> >  					   int *val, int *val2)
> >  {
> > @@ -706,9 +743,6 @@ static int ad2s1210_write_raw(struct iio_dev *indio_dev,
> >  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);
> > @@ -745,6 +779,16 @@ static const struct iio_event_spec ad2s1210_phase_event_spec[] = {
> >  	},
> >  };
> >  
> > +static const struct iio_event_spec ad2s1210_monitor_signal_event_spec[] = {
> > +	{
> > +		/* Sine/cosine below LOS threshold fault. */
> > +		.type = IIO_EV_TYPE_THRESH,
> > +		.dir = IIO_EV_DIR_FALLING,
> > +		/* Loss of signal threshold. */
> > +		.mask_separate = BIT(IIO_EV_INFO_VALUE),
> > +	},
> > +};
> > +
> >  static const struct iio_chan_spec ad2s1210_channels[] = {
> >  	{
> >  		.type = IIO_ANGL,
> > @@ -803,12 +847,19 @@ static const struct iio_chan_spec ad2s1210_channels[] = {
> >  		.scan_index = -1,
> >  		.info_mask_separate = BIT(IIO_CHAN_INFO_FREQUENCY),
> >  		.info_mask_separate_available = BIT(IIO_CHAN_INFO_FREQUENCY),
> > +	}, {
> > +		/* monitor signal */
> > +		.type = IIO_ALTVOLTAGE,
> > +		.indexed = 1,
> > +		.channel = 0,
> > +		.scan_index = -1,
> > +		.event_spec = ad2s1210_monitor_signal_event_spec,
> > +		.num_event_specs = ARRAY_SIZE(ad2s1210_monitor_signal_event_spec),
> >  	},
> >  };
> >  
> >  static struct attribute *ad2s1210_attributes[] = {
> >  	&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,
> > @@ -847,11 +898,13 @@ IIO_CONST_ATTR(in_phase0_mag_value_available,
> >  	       __stringify(PHASE_44_DEG_TO_RAD_MICRO) " "
> >  	       __stringify(PHASE_360_DEG_TO_RAD_INT) "."
> >  	       __stringify(PHASE_360_DEG_TO_RAD_MICRO));
> > +IIO_CONST_ATTR(in_altvoltage0_thresh_falling_value_available, THRESHOLD_RANGE_STR);
> >  IIO_DEVICE_ATTR_RO(in_angl1_thresh_rising_value_available, 0);
> >  IIO_DEVICE_ATTR_RO(in_angl1_thresh_rising_hysteresis_available, 0);
> >  
> >  static struct attribute *ad2s1210_event_attributes[] = {
> >  	&iio_const_attr_in_phase0_mag_value_available.dev_attr.attr,
> > +	&iio_const_attr_in_altvoltage0_thresh_falling_value_available.dev_attr.attr,
> >  	&iio_dev_attr_in_angl1_thresh_rising_value_available.dev_attr.attr,
> >  	&iio_dev_attr_in_angl1_thresh_rising_hysteresis_available.dev_attr.attr,
> >  	NULL,
> > @@ -904,6 +957,13 @@ static int ad2s1210_read_event_value(struct iio_dev *indio_dev,
> >  		default:
> >  			return -EINVAL;
> >  		}
> > +	case IIO_ALTVOLTAGE:
> > +		if (chan->output)
> > +			return -EINVAL;
> > +		if (type == IIO_EV_TYPE_THRESH && dir == IIO_EV_DIR_FALLING)
> > +			return ad2s1210_get_voltage_threshold(st,
> > +						AD2S1210_REG_LOS_THRD, val);
> > +		return -EINVAL;
> >  	case IIO_PHASE:
> >  		return ad2s1210_get_phase_lock_range(st, val, val2);
> >  	default:
> > @@ -930,6 +990,13 @@ static int ad2s1210_write_event_value(struct iio_dev *indio_dev,
> >  		default:
> >  			return -EINVAL;
> >  		}
> > +	case IIO_ALTVOLTAGE:
> > +		if (chan->output)
> > +			return -EINVAL;
> > +		if (type == IIO_EV_TYPE_THRESH && dir == IIO_EV_DIR_FALLING)
> > +			return ad2s1210_set_voltage_threshold(st,
> > +						AD2S1210_REG_LOS_THRD, val);
> > +		return -EINVAL;
> >  	case IIO_PHASE:
> >  		return ad2s1210_set_phase_lock_range(st, val, val2);
> >  	default:
> >   
>
Jonathan Cameron Sept. 30, 2023, 3:47 p.m. UTC | #29
On Fri, 29 Sep 2023 12:23:30 -0500
David Lechner <dlechner@baylibre.com> wrote:

> From: David Lechner <david@lechnology.com>
> 
> From: David Lechner <dlechner@baylibre.com>
> 
> The AD2S1210 has a programmable threshold for the degradation of signal
> (DOS) mismatch fault. This fault is triggered when the difference in
> amplitude between the sine and cosine inputs exceeds the threshold.
> 
> The DOS reset min/max registers on the chip provide initial values
> for internal tracking of the min/max of the monitor signal after the
> fault register is cleared.
> 
> This patch converts the custom device DOS reset min/max threshold
> attributes custom event attributes on the monitor signal channel.
> 
> The attributes now use millivolts instead of the raw register value in
> accordance with the IIO ABI.
> 
> Emitting the event will be implemented in a later patch.
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>
> ---
> 
> v3 changes: This is a new patch in v3
> 
>  .../Documentation/sysfs-bus-iio-resolver-ad2s1210  | 27 ++++++
>  drivers/staging/iio/resolver/ad2s1210.c            | 99 ++++++++++++----------
>  2 files changed, 82 insertions(+), 44 deletions(-)
> 
> diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-resolver-ad2s1210 b/drivers/staging/iio/Documentation/sysfs-bus-iio-resolver-ad2s1210
> new file mode 100644
> index 000000000000..ea75881b0c77
> --- /dev/null
> +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-resolver-ad2s1210
> @@ -0,0 +1,27 @@
> +What:		/sys/bus/iio/devices/iio:deviceX/events/in_altvoltage0-altvoltage1_thresh_rising_reset_max
Ah. So these are differential.  But the mismatch channel value isn't?  

I also got the format wrong for differential channels. Oops. Should
be the in_altvoltage0-altvoltage1 format for the previous suggestion
to change that channel type to differential.

This looks fine to me as new ABI.

Jonathan



> +KernelVersion:  6.7
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Reading returns the current Degradation of Signal Reset Maximum
> +		Threshold value in millivolts. Writing sets the value.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/events/in_altvoltage0-altvoltage1_thresh_rising_reset_max_available
> +KernelVersion:  6.7
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Reading returns the allowable voltage range for
> +		in_altvoltage0-altvoltage1_thresh_rising_reset_max.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/events/in_altvoltage0-altvoltage1_thresh_rising_reset_min
> +KernelVersion:  6.7
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Reading returns the current Degradation of Signal Reset Minimum
> +		Threshold value in millivolts. Writing sets the value.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/events/in_altvoltage0-altvoltage1_thresh_rising_reset_min_available
> +KernelVersion:  6.7
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Reading returns the allowable voltage range for
> +		in_altvoltage0-altvoltage1_thresh_rising_reset_min.
> diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c
> index aa14edbe8a77..e1c95ec73545 100644
> --- a/drivers/staging/iio/resolver/ad2s1210.c
> +++ b/drivers/staging/iio/resolver/ad2s1210.c
> @@ -283,41 +283,6 @@ static ssize_t ad2s1210_clear_fault(struct device *dev,
>  	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);
> -	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 : sprintf(buf, "%d\n", value);
> -}
> -
> -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 = regmap_write(st->regmap, iattr->address, data);
> -	mutex_unlock(&st->lock);
> -	return ret < 0 ? ret : len;
> -}
> -
>  static int ad2s1210_single_conversion(struct ad2s1210_state *st,
>  				      struct iio_chan_spec const *chan,
>  				      int *val)
> @@ -743,13 +708,6 @@ static int ad2s1210_write_raw(struct iio_dev *indio_dev,
>  static IIO_DEVICE_ATTR(fault, 0644,
>  		       ad2s1210_show_fault, ad2s1210_clear_fault, 0);
>  
> -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 const struct iio_event_spec ad2s1210_position_event_spec[] = {
>  	{
>  		/* Tracking error exceeds LOT threshold fault. */
> @@ -867,8 +825,6 @@ static const struct iio_chan_spec ad2s1210_channels[] = {
>  
>  static struct attribute *ad2s1210_attributes[] = {
>  	&iio_dev_attr_fault.dev_attr.attr,
> -	&iio_dev_attr_dos_rst_max_thrd.dev_attr.attr,
> -	&iio_dev_attr_dos_rst_min_thrd.dev_attr.attr,
>  	NULL,
>  };
>  
> @@ -876,6 +832,49 @@ static const struct attribute_group ad2s1210_attribute_group = {
>  	.attrs = ad2s1210_attributes,
>  };
>  
> +static ssize_t event_attr_voltage_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);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	return sprintf(buf, "%d\n", value * THRESHOLD_MILLIVOLT_PER_LSB);
> +}
> +
> +static ssize_t event_attr_voltage_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));
> +	struct iio_dev_attr *iattr = to_iio_dev_attr(attr);
> +	u16 data;
> +	int ret;
> +
> +	ret = kstrtou16(buf, 10, &data);
> +	if (ret)
> +		return -EINVAL;
> +
> +	mutex_lock(&st->lock);
> +	ret = regmap_write(st->regmap, iattr->address,
> +			   data / THRESHOLD_MILLIVOLT_PER_LSB);
> +	mutex_unlock(&st->lock);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	return len;
> +}
> +
>  static ssize_t
>  in_angl1_thresh_rising_value_available_show(struct device *dev,
>  					    struct device_attribute *attr,
> @@ -906,6 +905,14 @@ IIO_CONST_ATTR(in_phase0_mag_value_available,
>  IIO_CONST_ATTR(in_altvoltage0_thresh_falling_value_available, THRESHOLD_RANGE_STR);
>  IIO_CONST_ATTR(in_altvoltage0_thresh_rising_value_available, THRESHOLD_RANGE_STR);
>  IIO_CONST_ATTR(in_altvoltage0_mag_value_available, THRESHOLD_RANGE_STR);
> +IIO_DEVICE_ATTR(in_altvoltage0_mag_reset_max, 0644,
> +		event_attr_voltage_reg_show, event_attr_voltage_reg_store,
> +		AD2S1210_REG_DOS_RST_MAX_THRD);
> +IIO_CONST_ATTR(in_altvoltage0_mag_reset_max_available, THRESHOLD_RANGE_STR);
> +IIO_DEVICE_ATTR(in_altvoltage0_mag_reset_min, 0644,
> +		event_attr_voltage_reg_show, event_attr_voltage_reg_store,
> +		AD2S1210_REG_DOS_RST_MIN_THRD);
> +IIO_CONST_ATTR(in_altvoltage0_mag_reset_min_available, THRESHOLD_RANGE_STR);
>  IIO_DEVICE_ATTR_RO(in_angl1_thresh_rising_value_available, 0);
>  IIO_DEVICE_ATTR_RO(in_angl1_thresh_rising_hysteresis_available, 0);
>  
> @@ -914,6 +921,10 @@ static struct attribute *ad2s1210_event_attributes[] = {
>  	&iio_const_attr_in_altvoltage0_thresh_falling_value_available.dev_attr.attr,
>  	&iio_const_attr_in_altvoltage0_thresh_rising_value_available.dev_attr.attr,
>  	&iio_const_attr_in_altvoltage0_mag_value_available.dev_attr.attr,
> +	&iio_dev_attr_in_altvoltage0_mag_reset_max.dev_attr.attr,
> +	&iio_const_attr_in_altvoltage0_mag_reset_max_available.dev_attr.attr,
> +	&iio_dev_attr_in_altvoltage0_mag_reset_min.dev_attr.attr,
> +	&iio_const_attr_in_altvoltage0_mag_reset_min_available.dev_attr.attr,
>  	&iio_dev_attr_in_angl1_thresh_rising_value_available.dev_attr.attr,
>  	&iio_dev_attr_in_angl1_thresh_rising_hysteresis_available.dev_attr.attr,
>  	NULL,
>
Jonathan Cameron Sept. 30, 2023, 4 p.m. UTC | #30
On Fri, 29 Sep 2023 12:23:31 -0500
David Lechner <dlechner@baylibre.com> wrote:

> From: David Lechner <david@lechnology.com>
> 
> From: David Lechner <dlechner@baylibre.com>
> 
> When reading the position and velocity on the AD2S1210, there is also a
> 3rd byte following the two data bytes that contains the fault flag bits.
> This patch adds support for reading this byte and generating events when
> faults occur.
> 
> The faults are mapped to various channels and event types in order to
> have a unique event for each fault.
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>

Use of x and y modifiers is a little odd.  What was your reasoning?
Was it just that there was a X_OR_Y modifier?  If so, don't use that!
It seemed like a good idea at the time, but it's not nice to deal with
and requires a channel with that modifier to hang the controls off
+ make sure userspace expects that event code.

> ---
> 
> v3 changes: This is a new patch in v3
> 
>  drivers/staging/iio/resolver/ad2s1210.c | 175 +++++++++++++++++++++++++++++---
>  1 file changed, 161 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c
> index e1c95ec73545..dc3cc3ab855e 100644
> --- a/drivers/staging/iio/resolver/ad2s1210.c
> +++ b/drivers/staging/iio/resolver/ad2s1210.c
> @@ -21,6 +21,7 @@
>  #include <linux/types.h>
>  
>  #include <linux/iio/buffer.h>
> +#include <linux/iio/events.h>
>  #include <linux/iio/iio.h>
>  #include <linux/iio/sysfs.h>
>  #include <linux/iio/trigger_consumer.h>
> @@ -35,6 +36,16 @@
>  #define AD2S1210_SET_ENRES		GENMASK(3, 2)
>  #define AD2S1210_SET_RES		GENMASK(1, 0)
>  
> +/* fault register flags */
> +#define AD2S1210_FAULT_CLIP		BIT(7)
> +#define AD2S1210_FAULT_LOS		BIT(6)
> +#define AD2S1210_FAULT_DOS_OVR		BIT(5)
> +#define AD2S1210_FAULT_DOS_MIS		BIT(4)
> +#define AD2S1210_FAULT_LOT		BIT(3)
> +#define AD2S1210_FAULT_VELOCITY		BIT(2)
> +#define AD2S1210_FAULT_PHASE		BIT(1)
> +#define AD2S1210_FAULT_CONFIG_PARITY	BIT(0)
> +
>  #define AD2S1210_REG_POSITION_MSB	0x80
>  #define AD2S1210_REG_POSITION_LSB	0x81
>  #define AD2S1210_REG_VELOCITY_MSB	0x82
> @@ -71,6 +82,8 @@
>  /* max voltage for threshold registers is 0x7F * 38 mV */
>  #define THRESHOLD_RANGE_STR "[0 38 4826]"
>  
> +#define FAULT_ONESHOT(bit, new, old) (new & bit && !(old & bit))
> +
>  enum ad2s1210_mode {
>  	MOD_POS = 0b00,
>  	MOD_VEL = 0b01,
> @@ -98,8 +111,13 @@ struct ad2s1210_state {
>  	unsigned long clkin_hz;
>  	/** The selected resolution */
>  	enum ad2s1210_resolution resolution;
> +	/** Copy of fault register from the previous read. */
> +	u8 prev_fault_flags;
>  	/** For reading raw sample value via SPI. */
> -	__be16 sample __aligned(IIO_DMA_MINALIGN);
> +	struct {
> +		__be16 raw;
> +		u8 fault;
> +	} sample __aligned(IIO_DMA_MINALIGN);;
>  	/** Scan buffer */
>  	struct {
>  		__be16 chan[2];
> @@ -158,7 +176,15 @@ static int ad2s1210_regmap_reg_write(void *context, unsigned int reg,
>  	if (ret < 0)
>  		return ret;
>  
> -	return spi_sync_transfer(st->sdev, xfers, ARRAY_SIZE(xfers));
> +	ret = spi_sync_transfer(st->sdev, xfers, ARRAY_SIZE(xfers));
> +	if (ret < 0)
> +		return ret;
> +
> +	/* soft reset also clears the fault register */
> +	if (reg == AD2S1210_REG_SOFT_RESET)
> +		st->prev_fault_flags = 0;
> +
> +	return 0;
>  }
>  
>  /*
> @@ -200,6 +226,10 @@ static int ad2s1210_regmap_reg_read(void *context, unsigned int reg,
>  	if (ret < 0)
>  		return ret;
>  
> +	/* reading the fault register also clears it */
> +	if (reg == AD2S1210_REG_FAULT)
> +		st->prev_fault_flags = 0;
> +
>  	/*
>  	 * 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
> @@ -283,14 +313,92 @@ static ssize_t ad2s1210_clear_fault(struct device *dev,
>  	return ret < 0 ? ret : len;
>  }
>  
> -static int ad2s1210_single_conversion(struct ad2s1210_state *st,
> +static void ad2s1210_push_events(struct iio_dev *indio_dev,
> +				 u8 flags, s64 timestamp)
> +{
> +	struct ad2s1210_state *st = iio_priv(indio_dev);
> +
> +	/* Sine/cosine inputs clipped */
> +	if (FAULT_ONESHOT(AD2S1210_FAULT_CLIP, flags, st->prev_fault_flags))
> +		iio_push_event(indio_dev,
> +			       IIO_MOD_EVENT_CODE(IIO_ALTVOLTAGE, 1,
> +			       			  IIO_MOD_X_OR_Y,
Hmm. So this is a weird corner I'd forgotten about and explains your
use of X and Y modifiers. 
Long ago this was added to support a similar case to the one you have,
but mixing and matching modifiers like this doesn't scale, so
I think we'd be better just signally events on both channels.
If nothing else, someone waiting for an event on a specific channel
isn't looking for this weird modifier.

When it was used before we added a channel for MOD_X_OR_Y so events
were enabled on that. It's horrible though so don't do that.

> +						  IIO_EV_TYPE_MAG,
> +						  IIO_EV_DIR_NONE),
> +			       timestamp);
> +
> +	/* Sine/cosine inputs below LOS threshold */
> +	if (FAULT_ONESHOT(AD2S1210_FAULT_LOS, flags, st->prev_fault_flags))
> +		iio_push_event(indio_dev,
> +			       IIO_UNMOD_EVENT_CODE(IIO_ALTVOLTAGE, 0,
> +						    IIO_EV_TYPE_THRESH,
> +						    IIO_EV_DIR_FALLING),
> +			       timestamp);
> +
> +	/* Sine/cosine inputs exceed DOS overrange threshold */
> +	if (FAULT_ONESHOT(AD2S1210_FAULT_DOS_OVR, flags, st->prev_fault_flags))
> +		iio_push_event(indio_dev,
> +			       IIO_UNMOD_EVENT_CODE(IIO_ALTVOLTAGE, 0,
> +						    IIO_EV_TYPE_THRESH,
> +						    IIO_EV_DIR_RISING),
> +			       timestamp);
> +
> +	/* Sine/cosine inputs exceed DOS mismatch threshold */
> +	if (FAULT_ONESHOT(AD2S1210_FAULT_DOS_MIS, flags, st->prev_fault_flags))
> +		iio_push_event(indio_dev,
> +			       IIO_UNMOD_EVENT_CODE(IIO_ALTVOLTAGE, 0,
> +						    IIO_EV_TYPE_MAG,
> +						    IIO_EV_DIR_NONE),
> +			       timestamp);
> +
> +	/* Tracking error exceeds LOT threshold */
> +	if (FAULT_ONESHOT(AD2S1210_FAULT_LOT, flags, st->prev_fault_flags))
> +		iio_push_event(indio_dev,
> +			       IIO_UNMOD_EVENT_CODE(IIO_ANGL, 1,
> +						    IIO_EV_TYPE_THRESH,
> +						    IIO_EV_DIR_RISING),
> +			       timestamp);
> +
> +	/* Velocity exceeds maximum tracking rate */
> +	if (FAULT_ONESHOT(AD2S1210_FAULT_VELOCITY, flags, st->prev_fault_flags))
> +		iio_push_event(indio_dev,
> +			       IIO_UNMOD_EVENT_CODE(IIO_ANGL_VEL, 0,
> +						    IIO_EV_TYPE_THRESH,
> +						    IIO_EV_DIR_RISING),
> +			       timestamp);
> +
> +	/* Phase error exceeds phase lock range */
> +	if (FAULT_ONESHOT(AD2S1210_FAULT_PHASE, flags, st->prev_fault_flags))
> +		iio_push_event(indio_dev,
> +			       IIO_UNMOD_EVENT_CODE(IIO_PHASE, 0,
> +						    IIO_EV_TYPE_MAG,
> +						    IIO_EV_DIR_NONE),
> +			       timestamp);
> +
> +	/* Configuration parity error */
> +	if (FAULT_ONESHOT(AD2S1210_FAULT_CONFIG_PARITY, flags,
> +			  st->prev_fault_flags))
> +		/*
> +		 * Userspace should also get notified of this via error return
> +		 * when trying to write to any attribute that writes a register.
> +		 */
> +		dev_err_ratelimited(&indio_dev->dev,
> +				    "Configuration parity error\n");
> +
> +	st->prev_fault_flags = flags;
> +}
> +
> +static int ad2s1210_single_conversion(struct iio_dev *indio_dev,
>  				      struct iio_chan_spec const *chan,
>  				      int *val)
>  {
> +	struct ad2s1210_state *st = iio_priv(indio_dev);
> +	s64 timestamp;
>  	int ret;
>  
>  	mutex_lock(&st->lock);
>  	gpiod_set_value(st->sample_gpio, 1);
> +	timestamp = iio_get_time_ns(indio_dev);
>  	/* delay (6 * tck + 20) nano seconds */
>  	udelay(1);
>  
> @@ -307,17 +415,17 @@ static int ad2s1210_single_conversion(struct ad2s1210_state *st,
>  	}
>  	if (ret < 0)
>  		goto error_ret;
> -	ret = spi_read(st->sdev, &st->sample, 2);
> +	ret = spi_read(st->sdev, &st->sample, 3);
>  	if (ret < 0)
>  		goto error_ret;
>  
>  	switch (chan->type) {
>  	case IIO_ANGL:
> -		*val = be16_to_cpu(st->sample);
> +		*val = be16_to_cpu(st->sample.raw);
>  		ret = IIO_VAL_INT;
>  		break;
>  	case IIO_ANGL_VEL:
> -		*val = (s16)be16_to_cpu(st->sample);
> +		*val = (s16)be16_to_cpu(st->sample.raw);
>  		ret = IIO_VAL_INT;
>  		break;
>  	default:
> @@ -325,6 +433,8 @@ static int ad2s1210_single_conversion(struct ad2s1210_state *st,
>  		break;
>  	}
>  
> +	ad2s1210_push_events(indio_dev, st->rx[2], timestamp);
> +
>  error_ret:
>  	gpiod_set_value(st->sample_gpio, 0);
>  	/* delay (2 * tck + 20) nano seconds */
> @@ -608,7 +718,7 @@ static int ad2s1210_read_raw(struct iio_dev *indio_dev,
>  
>  	switch (mask) {
>  	case IIO_CHAN_INFO_RAW:
> -		return ad2s1210_single_conversion(st, chan, val);
> +		return ad2s1210_single_conversion(indio_dev, chan, val);
>  	case IIO_CHAN_INFO_SCALE:
>  		switch (chan->type) {
>  		case IIO_ANGL:
> @@ -721,6 +831,14 @@ static const struct iio_event_spec ad2s1210_position_event_spec[] = {
>  	},
>  };
>  
> +static const struct iio_event_spec ad2s1210_velocity_event_spec[] = {
> +	{
> +		/* Velocity exceeds maximum tracking rate fault. */
> +		.type = IIO_EV_TYPE_THRESH,
> +		.dir = IIO_EV_DIR_RISING,
> +	},
> +};
> +
>  static const struct iio_event_spec ad2s1210_phase_event_spec[] = {
>  	{
>  		/* Phase error fault. */
> @@ -754,6 +872,14 @@ static const struct iio_event_spec ad2s1210_monitor_signal_event_spec[] = {
>  	},
>  };
>  
> +static const struct iio_event_spec ad2s1210_sin_cos_event_spec[] = {
> +	{
> +		/* Sine/cosine clipping fault. */
> +		.type = IIO_EV_TYPE_MAG,
> +		.dir = IIO_EV_DIR_NONE,
> +	},
> +};
> +
>  static const struct iio_chan_spec ad2s1210_channels[] = {
>  	{
>  		.type = IIO_ANGL,
> @@ -784,6 +910,8 @@ static const struct iio_chan_spec ad2s1210_channels[] = {
>  		},
>  		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>  				      BIT(IIO_CHAN_INFO_SCALE),
> +		.event_spec = ad2s1210_velocity_event_spec,
> +		.num_event_specs = ARRAY_SIZE(ad2s1210_velocity_event_spec),
>  	},
>  	IIO_CHAN_SOFT_TIMESTAMP(2),
>  	{
> @@ -820,6 +948,26 @@ static const struct iio_chan_spec ad2s1210_channels[] = {
>  		.scan_index = -1,
>  		.event_spec = ad2s1210_monitor_signal_event_spec,
>  		.num_event_specs = ARRAY_SIZE(ad2s1210_monitor_signal_event_spec),
> +	}, {
> +		/* sine input */
> +		.type = IIO_ALTVOLTAGE,
> +		.indexed = 1,
> +		.channel = 1,
> +		.modified = 1,
> +		.channel2 = IIO_MOD_Y,
A bit odd and I'm not sure it's beneficial over just using an index
and a label for the channel. 

If a modifier is necessary then we could add a new one for this.
Another option might be to provide in_altvoltage0_phase 

or... can we map this to i (inphase) and q (quadrature) phases?
Sort of feels like we can, and those modifiers already exist.

Note though that if we do that, we'll need to modify the various
ABI docs etc to include the modifiers whenever it's about the sine
and cosine channels.

> +		.scan_index = -1,
> +		.event_spec = ad2s1210_sin_cos_event_spec,
> +		.num_event_specs = ARRAY_SIZE(ad2s1210_sin_cos_event_spec),
> +	}, {
> +		/* cosine input */
> +		.type = IIO_ALTVOLTAGE,
> +		.indexed = 1,
> +		.channel = 1,
> +		.modified = 1,
> +		.channel2 = IIO_MOD_X,
> +		.scan_index = -1,
> +		.event_spec = ad2s1210_sin_cos_event_spec,
> +		.num_event_specs = ARRAY_SIZE(ad2s1210_sin_cos_event_spec),
>  	},
>  };
>  
> @@ -936,7 +1084,7 @@ static const struct attribute_group ad2s1210_event_attribute_group = {
>  
>  static int ad2s1210_initial(struct ad2s1210_state *st)
>  {
> -	unsigned char data;
> +	unsigned int data;
>  	int ret;
>  
>  	mutex_lock(&st->lock);
> @@ -1073,12 +1221,11 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p)
>  		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);
> +		ret = spi_read(st->sdev, &st->sample, 3);
>  		if (ret < 0)
>  			goto error_ret;
>  
> -		memcpy(&st->scan.chan[chan++], st->rx, 2);
> +		memcpy(&st->scan.chan[chan++], &st->sample.raw, 2);
>  	}
>  
>  	if (test_bit(1, indio_dev->active_scan_mask)) {
> @@ -1086,14 +1233,14 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p)
>  		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);
> +		ret = spi_read(st->sdev, &st->sample, 3);
>  		if (ret < 0)
>  			goto error_ret;
>  
> -		memcpy(&st->scan.chan[chan++], st->rx, 2);
> +		memcpy(&st->scan.chan[chan++], &st->sample.raw, 2);
>  	}
>  
> +	ad2s1210_push_events(indio_dev, st->sample.fault, pf->timestamp);
>  	iio_push_to_buffers_with_timestamp(indio_dev, &st->scan, pf->timestamp);
>  
>  error_ret:
>
David Lechner Sept. 30, 2023, 9:23 p.m. UTC | #31
On Sat, Sep 30, 2023 at 10:00 AM Jonathan Cameron <jic23@kernel.org> wrote:
>
> On Fri, 29 Sep 2023 12:53:00 -0500
> David Lechner <dlechner@baylibre.com> wrote:
>
> > On Fri, Sep 29, 2023 at 12:25 PM David Lechner <dlechner@baylibre.com> wrote:
> > >
> > > The AD2S1210 resolver has a hysteresis feature that can be used to
> > > prevent flicker in the LSB of the position register. This can be either
> > > enabled or disabled. Disabling hysteresis is useful for increasing
> > > precision by oversampling.
> > >
> > > Signed-off-by: David Lechner <dlechner@baylibre.com>
> > > ---
> >
> > ...
> >
> > > +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 hysteresis_available[] = { 0, 1 };
> >
> > This is basically an enable/disable. Should the 1 value be changed to the
> > appropriate radians value since this is hysteresis on the position
> > (angle) channel?
>
> Good point. However it should be in the _raw units. The text is
> slightly more explicit on this for
> the variant of hysteresis applied to threshold events as it's
> added or substracted from a threshold (and thresholds are in
> _raw readings unless only _processed is available).
>
> Does that make 0, 1 correct as we are talking about LSB only?
>

It is currently only correct (as a raw value) if the selected
resolution is 16-bit. So I will need to fix this so the value is `1 <<
(16 - resolution)`.
Dan Carpenter Oct. 2, 2023, 8:07 a.m. UTC | #32
On Fri, Sep 29, 2023 at 12:23:07PM -0500, David Lechner wrote:
> From: David Lechner <david@lechnology.com>
> 
> From: David Lechner <dlechner@baylibre.com>
> 
> This fixes a use before initialization in ad2s1210_probe(). The
> ad2s1210_setup_gpios() function uses st->sdev but it was being called
> before this field was initialized.
> 
> Signed-off-by: David Lechner <dlechner@baylibre.com>
> ---
> 

Fixes: b19e9ad5e2cb ("staging:iio:resolver:ad2s1210 general driver cleanup.")

This would crash the driver right away, on probe.  It's amazing no one
filed a bug report even though the bug is 12 years old.

regards,
dan carpenter
Jonathan Cameron Oct. 2, 2023, 9:17 a.m. UTC | #33
On Mon, 2 Oct 2023 11:07:15 +0300
Dan Carpenter <dan.carpenter@linaro.org> wrote:

> On Fri, Sep 29, 2023 at 12:23:07PM -0500, David Lechner wrote:
> > From: David Lechner <david@lechnology.com>
> > 
> > From: David Lechner <dlechner@baylibre.com>
> > 
> > This fixes a use before initialization in ad2s1210_probe(). The
> > ad2s1210_setup_gpios() function uses st->sdev but it was being called
> > before this field was initialized.
> > 
> > Signed-off-by: David Lechner <dlechner@baylibre.com>
> > ---
> >   
> 
> Fixes: b19e9ad5e2cb ("staging:iio:resolver:ad2s1210 general driver cleanup.")
Thanks but nope, not that one.

At that point ad2s1210_setup_gpios, didn't use st->sdev.
I think this went wrong when the platform data was removed in 

I 'think' it was
Fixes: f356dc6ec26b ("staging: iio: ad2s1210: Switch to the gpio descriptor interface")



> 
> This would crash the driver right away, on probe.  It's amazing no one
> filed a bug report even though the bug is 12 years old.
Only 5 years :)

Welcome to the long tail of IIO Devices and the long term availability of the
hardware - this is still a production part.  Clearly no one was using the
upstream driver for 5 + years, but here comes David who is not only fixing
the bugs but cleaning it up.

Hmm. What happened to roadtest? I was hoping that would solve this sort
of issue by allowing simple testing of basic functionality... Hope it
is still headed for a new version / upstream!

Jonathan

> regards,
> dan carpenter
David Lechner Oct. 2, 2023, 4:09 p.m. UTC | #34
On Sat, Sep 30, 2023 at 10:42 AM Jonathan Cameron <jic23@kernel.org> wrote:
>
> On Fri, 29 Sep 2023 12:23:27 -0500
> David Lechner <dlechner@baylibre.com> wrote:
>
> > From: David Lechner <david@lechnology.com>
> >
> > From: David Lechner <dlechner@baylibre.com>
> >
> > The AD2S1210 has a programmable threshold for the loss of signal (LOS)
> > fault. This fault is triggered when either the sine or cosine input
> > falls below the threshold voltage.
> >
> > This patch converts the custom device LOS threshold attribute to an
> > event falling edge threshold attribute on a new monitor signal channel.
> > The monitor signal is an internal signal that combines the amplitudes
> > of the sine and cosine inputs as well as the current angle and position
> > output. This signal is used to detect faults in the input signals.
>
> Hmm. Looking forwards, I'm less sure that we should be shoving all these
> error conditions onto one channel. Fundamentally we have
> sine and cosine inputs. I think we should treat those as separate channels
> and include a third differential channel between them.

At first, I did consider a differential channel as you suggested in
v2. However, the datasheet is quite clear that the LOS and DOS faults
(and only those faults) come from a signal it calls the "monitor
signal". This signal is defined as:

    Monitor = A1 * sin(theta)  * sin(phi) + A2 * cos(theta) * cos(phi)

where A1 * sin(theta) is the the sine input, A2 * cos(theta) is the
cosine input and phi is the position output. So mathematically
speaking, there is no signal that is the difference between the two
inputs. (See "Theory of Operation" section in the datasheet.)

But if we want to hide these internal details and don't care about a
strict definition of "differential", then what is suggested below
seems fine.

>
> So this one becomes a double event (you need to signal it on both
> cosine and sine channels).  The DOS overange is similar.
> The DOS mismatch is a threshold on the differential channel giving
>
> events/in_altvoltage0_thresh_falling_value
> events/in_altvoltage1_thresh_falling_value (these match)
> events/in_altvoltage0_thresh_rising_value
> events/in_altvoltage1_thresh_rising_value (matches previous which is fine)
> events/in_altvoltage1-altvoltage0_mag_rising_value
>
> Does that work here?  Avoids smashing different types of signals together.
> We could even do the LOT as differential between two angle channels
> (tracking one and measured one) but meh that's getting complex.>
> Note this will rely on channel labels to make the above make any sense at all.

I think this could be OK - I think what matters most is having some
documentation that maps the faults and registers on the chip to the
iio names. Where would the sine/cosine clipping fault fit in though? I
got a bit too creative and used X_OR_Y to differentiate it (see
discussion in "staging: iio: resolver: ad2s1210: implement fault
events"). Strictly speaking, it should probably be a type: threshold,
direction: either event on both the sine and cosine input channels
(another double event) since it occurs if either of the signal exceeds
the power or ground rail voltage. But we already have threshold rising
and threshold falling on these channels with a different meaning. I
guess it could call it magnitude instead of a threshold?
David Lechner Oct. 2, 2023, 4:43 p.m. UTC | #35
On Sat, Sep 30, 2023 at 11:00 AM Jonathan Cameron <jic23@kernel.org> wrote:
>
> On Fri, 29 Sep 2023 12:23:31 -0500
> David Lechner <dlechner@baylibre.com> wrote:
>
> > From: David Lechner <david@lechnology.com>
> >
> > From: David Lechner <dlechner@baylibre.com>
> >
> > When reading the position and velocity on the AD2S1210, there is also a
> > 3rd byte following the two data bytes that contains the fault flag bits.
> > This patch adds support for reading this byte and generating events when
> > faults occur.
> >
> > The faults are mapped to various channels and event types in order to
> > have a unique event for each fault.
> >
> > Signed-off-by: David Lechner <dlechner@baylibre.com>
>
> Use of x and y modifiers is a little odd.  What was your reasoning?
> Was it just that there was a X_OR_Y modifier?  If so, don't use that!
> It seemed like a good idea at the time, but it's not nice to deal with
> and requires a channel with that modifier to hang the controls off
> + make sure userspace expects that event code.
>

Yes, I was perhaps getting a bit too creative here (the two inputs
come from transformers mounted 90 degrees apart so measure X and Y
component of an angle). The only reason I did this was to take
advantage of the possibility of an "OR" event to avoid double events
for a single fault bit. We can just go with the double event on the
two different input channels as discussed in "[PATCH v3 22/27]
staging: iio: resolver: ad2s1210: convert LOS threshold to event
attr".
David Lechner Oct. 2, 2023, 4:58 p.m. UTC | #36
On Sat, Sep 30, 2023 at 11:00 AM Jonathan Cameron <jic23@kernel.org> wrote:
>
> On Fri, 29 Sep 2023 12:23:31 -0500
> David Lechner <dlechner@baylibre.com> wrote:
>
> > From: David Lechner <david@lechnology.com>
> >
> > From: David Lechner <dlechner@baylibre.com>
> >
> > When reading the position and velocity on the AD2S1210, there is also a
> > 3rd byte following the two data bytes that contains the fault flag bits.
> > This patch adds support for reading this byte and generating events when
> > faults occur.
> >
> > The faults are mapped to various channels and event types in order to
> > have a unique event for each fault.
> >
> > Signed-off-by: David Lechner <dlechner@baylibre.com>
>
> Use of x and y modifiers is a little odd.  What was your reasoning?
> Was it just that there was a X_OR_Y modifier?  If so, don't use that!
> It seemed like a good idea at the time, but it's not nice to deal with
> and requires a channel with that modifier to hang the controls off
> + make sure userspace expects that event code.


Regarding the point about "requires a channel with that modifier to
hang the controls off...". Although that comment was about modifiers,
does it also apply in general.

There are several fault events that don't have any configurable
parameters, namely _sine/cosine inputs clipping_ and _velocity exceeds
max tracking rate_. So there won't be any attributes that contain the
event specification for those (e.g. no `events/in_angl0_*`
attributes). It sounds like this would be a problem as well?

Should we consider a IIO_EV_INFO_LABEL so that we can have some sort
of attribute (namely `events/<dir>_<channel spec>_label`) so that
userspace can enumerate expected events for non-configurable events?
David Lechner Oct. 2, 2023, 5:06 p.m. UTC | #37
On Sat, Sep 30, 2023 at 10:47 AM Jonathan Cameron <jic23@kernel.org> wrote:
>
> On Fri, 29 Sep 2023 12:23:30 -0500
> David Lechner <dlechner@baylibre.com> wrote:
>
> > From: David Lechner <david@lechnology.com>
> >
> > From: David Lechner <dlechner@baylibre.com>
> >
> > The AD2S1210 has a programmable threshold for the degradation of signal
> > (DOS) mismatch fault. This fault is triggered when the difference in
> > amplitude between the sine and cosine inputs exceeds the threshold.
> >
> > The DOS reset min/max registers on the chip provide initial values
> > for internal tracking of the min/max of the monitor signal after the
> > fault register is cleared.
> >
> > This patch converts the custom device DOS reset min/max threshold
> > attributes custom event attributes on the monitor signal channel.
> >
> > The attributes now use millivolts instead of the raw register value in
> > accordance with the IIO ABI.
> >
> > Emitting the event will be implemented in a later patch.
> >
> > Signed-off-by: David Lechner <dlechner@baylibre.com>
> > ---
> >
> > v3 changes: This is a new patch in v3
> >
> >  .../Documentation/sysfs-bus-iio-resolver-ad2s1210  | 27 ++++++
> >  drivers/staging/iio/resolver/ad2s1210.c            | 99 ++++++++++++----------
> >  2 files changed, 82 insertions(+), 44 deletions(-)
> >
> > diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-resolver-ad2s1210 b/drivers/staging/iio/Documentation/sysfs-bus-iio-resolver-ad2s1210
> > new file mode 100644
> > index 000000000000..ea75881b0c77
> > --- /dev/null
> > +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-resolver-ad2s1210
> > @@ -0,0 +1,27 @@
> > +What:                /sys/bus/iio/devices/iio:deviceX/events/in_altvoltage0-altvoltage1_thresh_rising_reset_max
> Ah. So these are differential.  But the mismatch channel value isn't?
>
> I also got the format wrong for differential channels. Oops. Should
> be the in_altvoltage0-altvoltage1 format for the previous suggestion
> to change that channel type to differential.
>
> This looks fine to me as new ABI.
>
> Jonathan
>

As discussed in
<https://lore.kernel.org/linux-iio/CAMknhBFKSqXvgOeRjGAOfURzndmxmCffdU6MUirEmfzKqwM_Kg@mail.gmail.com/>,
technically no they are not differential. I started to implement it
that way but changed my mind after understanding the datasheet more in
depth. It looks like I forgot to update the documentation in this
patch to match the final implementation that was submitted.

In any case, I will update the documentation to match the
implementation or vice versa depending on what we decide on for which
channel the events should be associated with.

>
>
> > +KernelVersion:  6.7
> > +Contact:     linux-iio@vger.kernel.org
> > +Description:
> > +             Reading returns the current Degradation of Signal Reset Maximum
> > +             Threshold value in millivolts. Writing sets the value.
> > +
> > +What:                /sys/bus/iio/devices/iio:deviceX/events/in_altvoltage0-altvoltage1_thresh_rising_reset_max_available
> > +KernelVersion:  6.7
> > +Contact:     linux-iio@vger.kernel.org
> > +Description:
> > +             Reading returns the allowable voltage range for
> > +             in_altvoltage0-altvoltage1_thresh_rising_reset_max.
> > +
> > +What:                /sys/bus/iio/devices/iio:deviceX/events/in_altvoltage0-altvoltage1_thresh_rising_reset_min
> > +KernelVersion:  6.7
> > +Contact:     linux-iio@vger.kernel.org
> > +Description:
> > +             Reading returns the current Degradation of Signal Reset Minimum
> > +             Threshold value in millivolts. Writing sets the value.
> > +
> > +What:                /sys/bus/iio/devices/iio:deviceX/events/in_altvoltage0-altvoltage1_thresh_rising_reset_min_available
> > +KernelVersion:  6.7
> > +Contact:     linux-iio@vger.kernel.org
> > +Description:
> > +             Reading returns the allowable voltage range for
> > +             in_altvoltage0-altvoltage1_thresh_rising_reset_min.
> > diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c
> > index aa14edbe8a77..e1c95ec73545 100644
> > --- a/drivers/staging/iio/resolver/ad2s1210.c
> > +++ b/drivers/staging/iio/resolver/ad2s1210.c
> > @@ -283,41 +283,6 @@ static ssize_t ad2s1210_clear_fault(struct device *dev,
> >       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);
> > -     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 : sprintf(buf, "%d\n", value);
> > -}
> > -
> > -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 = regmap_write(st->regmap, iattr->address, data);
> > -     mutex_unlock(&st->lock);
> > -     return ret < 0 ? ret : len;
> > -}
> > -
> >  static int ad2s1210_single_conversion(struct ad2s1210_state *st,
> >                                     struct iio_chan_spec const *chan,
> >                                     int *val)
> > @@ -743,13 +708,6 @@ static int ad2s1210_write_raw(struct iio_dev *indio_dev,
> >  static IIO_DEVICE_ATTR(fault, 0644,
> >                      ad2s1210_show_fault, ad2s1210_clear_fault, 0);
> >
> > -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 const struct iio_event_spec ad2s1210_position_event_spec[] = {
> >       {
> >               /* Tracking error exceeds LOT threshold fault. */
> > @@ -867,8 +825,6 @@ static const struct iio_chan_spec ad2s1210_channels[] = {
> >
> >  static struct attribute *ad2s1210_attributes[] = {
> >       &iio_dev_attr_fault.dev_attr.attr,
> > -     &iio_dev_attr_dos_rst_max_thrd.dev_attr.attr,
> > -     &iio_dev_attr_dos_rst_min_thrd.dev_attr.attr,
> >       NULL,
> >  };
> >
> > @@ -876,6 +832,49 @@ static const struct attribute_group ad2s1210_attribute_group = {
> >       .attrs = ad2s1210_attributes,
> >  };
> >
> > +static ssize_t event_attr_voltage_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);
> > +
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     return sprintf(buf, "%d\n", value * THRESHOLD_MILLIVOLT_PER_LSB);
> > +}
> > +
> > +static ssize_t event_attr_voltage_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));
> > +     struct iio_dev_attr *iattr = to_iio_dev_attr(attr);
> > +     u16 data;
> > +     int ret;
> > +
> > +     ret = kstrtou16(buf, 10, &data);
> > +     if (ret)
> > +             return -EINVAL;
> > +
> > +     mutex_lock(&st->lock);
> > +     ret = regmap_write(st->regmap, iattr->address,
> > +                        data / THRESHOLD_MILLIVOLT_PER_LSB);
> > +     mutex_unlock(&st->lock);
> > +
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     return len;
> > +}
> > +
> >  static ssize_t
> >  in_angl1_thresh_rising_value_available_show(struct device *dev,
> >                                           struct device_attribute *attr,
> > @@ -906,6 +905,14 @@ IIO_CONST_ATTR(in_phase0_mag_value_available,
> >  IIO_CONST_ATTR(in_altvoltage0_thresh_falling_value_available, THRESHOLD_RANGE_STR);
> >  IIO_CONST_ATTR(in_altvoltage0_thresh_rising_value_available, THRESHOLD_RANGE_STR);
> >  IIO_CONST_ATTR(in_altvoltage0_mag_value_available, THRESHOLD_RANGE_STR);
> > +IIO_DEVICE_ATTR(in_altvoltage0_mag_reset_max, 0644,
> > +             event_attr_voltage_reg_show, event_attr_voltage_reg_store,
> > +             AD2S1210_REG_DOS_RST_MAX_THRD);
> > +IIO_CONST_ATTR(in_altvoltage0_mag_reset_max_available, THRESHOLD_RANGE_STR);
> > +IIO_DEVICE_ATTR(in_altvoltage0_mag_reset_min, 0644,
> > +             event_attr_voltage_reg_show, event_attr_voltage_reg_store,
> > +             AD2S1210_REG_DOS_RST_MIN_THRD);
> > +IIO_CONST_ATTR(in_altvoltage0_mag_reset_min_available, THRESHOLD_RANGE_STR);
> >  IIO_DEVICE_ATTR_RO(in_angl1_thresh_rising_value_available, 0);
> >  IIO_DEVICE_ATTR_RO(in_angl1_thresh_rising_hysteresis_available, 0);

These attribute names don't match the documentation above. :-/

> >
> > @@ -914,6 +921,10 @@ static struct attribute *ad2s1210_event_attributes[] = {
> >       &iio_const_attr_in_altvoltage0_thresh_falling_value_available.dev_attr.attr,
> >       &iio_const_attr_in_altvoltage0_thresh_rising_value_available.dev_attr.attr,
> >       &iio_const_attr_in_altvoltage0_mag_value_available.dev_attr.attr,
> > +     &iio_dev_attr_in_altvoltage0_mag_reset_max.dev_attr.attr,
> > +     &iio_const_attr_in_altvoltage0_mag_reset_max_available.dev_attr.attr,
> > +     &iio_dev_attr_in_altvoltage0_mag_reset_min.dev_attr.attr,
> > +     &iio_const_attr_in_altvoltage0_mag_reset_min_available.dev_attr.attr,
> >       &iio_dev_attr_in_angl1_thresh_rising_value_available.dev_attr.attr,
> >       &iio_dev_attr_in_angl1_thresh_rising_hysteresis_available.dev_attr.attr,
> >       NULL,
> >
>
David Lechner Oct. 2, 2023, 6:49 p.m. UTC | #38
On Mon, Oct 2, 2023 at 11:58 AM David Lechner <dlechner@baylibre.com> wrote:
>
> On Sat, Sep 30, 2023 at 11:00 AM Jonathan Cameron <jic23@kernel.org> wrote:
> >
> > On Fri, 29 Sep 2023 12:23:31 -0500
> > David Lechner <dlechner@baylibre.com> wrote:
> >
> > > From: David Lechner <david@lechnology.com>
> > >
> > > From: David Lechner <dlechner@baylibre.com>
> > >
> > > When reading the position and velocity on the AD2S1210, there is also a
> > > 3rd byte following the two data bytes that contains the fault flag bits.
> > > This patch adds support for reading this byte and generating events when
> > > faults occur.
> > >
> > > The faults are mapped to various channels and event types in order to
> > > have a unique event for each fault.
> > >
> > > Signed-off-by: David Lechner <dlechner@baylibre.com>
> >
> > Use of x and y modifiers is a little odd.  What was your reasoning?
> > Was it just that there was a X_OR_Y modifier?  If so, don't use that!
> > It seemed like a good idea at the time, but it's not nice to deal with
> > and requires a channel with that modifier to hang the controls off
> > + make sure userspace expects that event code.
>
>
> Regarding the point about "requires a channel with that modifier to
> hang the controls off...". Although that comment was about modifiers,
> does it also apply in general.
>
> There are several fault events that don't have any configurable
> parameters, namely _sine/cosine inputs clipping_ and _velocity exceeds
> max tracking rate_. So there won't be any attributes that contain the
> event specification for those (e.g. no `events/in_angl0_*`
> attributes). It sounds like this would be a problem as well?
>
> Should we consider a IIO_EV_INFO_LABEL so that we can have some sort
> of attribute (namely `events/<dir>_<channel spec>_label`) so that
> userspace can enumerate expected events for non-configurable events?

Well, I didn't think that all the way through before I hit send.
IIO_EV_INFO_LABEL is clearly not the right way to implement a
`events/*_label` attribute, but however we consider going about it,
the point of adding such an attribute was the main idea.
Hennerich, Michael Oct. 4, 2023, 11:01 a.m. UTC | #39
> -----Original Message-----
> From: Jonathan Cameron <jic23@kernel.org>
> Sent: Samstag, 30. September 2023 17:32
> To: David Lechner <dlechner@baylibre.com>
> Cc: linux-iio@vger.kernel.org; devicetree@vger.kernel.org; linux-
> staging@lists.linux.dev; David Lechner <david@lechnology.com>; Rob Herring
> <robh+dt@kernel.org>; Krzysztof Kozlowski
> <krzysztof.kozlowski+dt@linaro.org>; Conor Dooley <conor+dt@kernel.org>;
> Hennerich, Michael <Michael.Hennerich@analog.com>; Sa, Nuno
> <Nuno.Sa@analog.com>; Axel Haslam <ahaslam@baylibre.com>; Philip Molloy
> <pmolloy@baylibre.com>; linux-kernel@vger.kernel.org
> Subject: Re: [PATCH v3 22/27] staging: iio: resolver: ad2s1210: convert LOS
> threshold to event attr
> 
> 
> On Fri, 29 Sep 2023 12:23:27 -0500
> David Lechner <dlechner@baylibre.com> wrote:
> 
> > From: David Lechner <david@lechnology.com>
> >
> > From: David Lechner <dlechner@baylibre.com>
> >
> > The AD2S1210 has a programmable threshold for the loss of signal (LOS)
> > fault. This fault is triggered when either the sine or cosine input
> > falls below the threshold voltage.
> >
> > This patch converts the custom device LOS threshold attribute to an
> > event falling edge threshold attribute on a new monitor signal channel.
> > The monitor signal is an internal signal that combines the amplitudes
> > of the sine and cosine inputs as well as the current angle and
> > position output. This signal is used to detect faults in the input signals.
> >
> > The attribute now uses millivolts instead of the raw register value in
> > accordance with the IIO ABI.
> >
> > Emitting the event will be implemented in a later patch.
> >
> > Signed-off-by: David Lechner <dlechner@baylibre.com>
> 
> I think I'm fine with treating these internal signals like this, but I would ideally
> like someone from Analog devices to take a look at how these are being done
> and make sure our interpretations of the signals make sense to them.  We are
> pushing the boundaries a little here (though we have done similar before for
> fault events I think.)

Hi Jonathan,
David and I we also had some internal discussion related to this.
I'm sure these fault events and thresholds are understood correctly.
Doing it this or the other way, it needs to be properly documented in order to make sense.
So from my perspective whatever makes the most sense from a IIO ABI
perspective, is the way to forward.

-Michael

> 
> Jonathan
Jonathan Cameron Oct. 5, 2023, 1:38 p.m. UTC | #40
On Sat, 30 Sep 2023 15:55:36 +0100
Jonathan Cameron <jic23@kernel.org> wrote:

> On Fri, 29 Sep 2023 12:23:18 -0500
> David Lechner <dlechner@baylibre.com> wrote:
> 
> > From: David Lechner <david@lechnology.com>
> > 
> > From: David Lechner <dlechner@baylibre.com>
> > 
> > - Remove "adi," prefix from gpio names.
> > - Sample gpio is now expected to be active low.
> > - Convert A0 and A1 gpios to "mode-gpios" gpio array.
> > - Convert RES0 and RES1 gpios to "resolution-gpios" gpio array.
> > - Remove extraneous lookup tables.
> > - Remove unused mode field from state struct.
> > - Swap argument order of ad2s1210_set_mode() while we are touching this.
> > 
> > Signed-off-by: David Lechner <dlechner@baylibre.com>  
> Applied,
0-day ran smatch on this and it picked up that a log isn't released in
an error path.  I've fixed that up with a goto error_ret and will push out a
fresh testing branch for 0-day to take another look at.

...

> > @@ -546,7 +537,9 @@ static int ad2s1210_initial(struct ad2s1210_state *st)
> >  	int ret;
> >  
> >  	mutex_lock(&st->lock);
> > -	ad2s1210_set_resolution_pin(st);
> > +	ret = ad2s1210_set_resolution_gpios(st, st->resolution);
> > +	if (ret < 0)
Exiting with lock held.  There is an error_ret label that releases the lock
so use that.

> > +		return ret;
> >  
> >  	/* Use default config register value plus resolution from devicetree. */
> >  	data = FIELD_PREP(AD2S1210_PHASE_LOCK_RANGE_44, 1);
Jonathan Cameron Oct. 5, 2023, 2:16 p.m. UTC | #41
On Wed, 4 Oct 2023 11:01:56 +0000
"Hennerich, Michael" <Michael.Hennerich@analog.com> wrote:

> > -----Original Message-----
> > From: Jonathan Cameron <jic23@kernel.org>
> > Sent: Samstag, 30. September 2023 17:32
> > To: David Lechner <dlechner@baylibre.com>
> > Cc: linux-iio@vger.kernel.org; devicetree@vger.kernel.org; linux-
> > staging@lists.linux.dev; David Lechner <david@lechnology.com>; Rob Herring
> > <robh+dt@kernel.org>; Krzysztof Kozlowski
> > <krzysztof.kozlowski+dt@linaro.org>; Conor Dooley <conor+dt@kernel.org>;
> > Hennerich, Michael <Michael.Hennerich@analog.com>; Sa, Nuno
> > <Nuno.Sa@analog.com>; Axel Haslam <ahaslam@baylibre.com>; Philip Molloy
> > <pmolloy@baylibre.com>; linux-kernel@vger.kernel.org
> > Subject: Re: [PATCH v3 22/27] staging: iio: resolver: ad2s1210: convert LOS
> > threshold to event attr
> > 
> > 
> > On Fri, 29 Sep 2023 12:23:27 -0500
> > David Lechner <dlechner@baylibre.com> wrote:
> >   
> > > From: David Lechner <david@lechnology.com>
> > >
> > > From: David Lechner <dlechner@baylibre.com>
> > >
> > > The AD2S1210 has a programmable threshold for the loss of signal (LOS)
> > > fault. This fault is triggered when either the sine or cosine input
> > > falls below the threshold voltage.
> > >
> > > This patch converts the custom device LOS threshold attribute to an
> > > event falling edge threshold attribute on a new monitor signal channel.
> > > The monitor signal is an internal signal that combines the amplitudes
> > > of the sine and cosine inputs as well as the current angle and
> > > position output. This signal is used to detect faults in the input signals.
> > >
> > > The attribute now uses millivolts instead of the raw register value in
> > > accordance with the IIO ABI.
> > >
> > > Emitting the event will be implemented in a later patch.
> > >
> > > Signed-off-by: David Lechner <dlechner@baylibre.com>  
> > 
> > I think I'm fine with treating these internal signals like this, but I would ideally
> > like someone from Analog devices to take a look at how these are being done
> > and make sure our interpretations of the signals make sense to them.  We are
> > pushing the boundaries a little here (though we have done similar before for
> > fault events I think.)  
> 
> Hi Jonathan,
> David and I we also had some internal discussion related to this.
> I'm sure these fault events and thresholds are understood correctly.
> Doing it this or the other way, it needs to be properly documented in order to make sense.
> So from my perspective whatever makes the most sense from a IIO ABI
> perspective, is the way to forward.

Great - as long as keep to a logical mapping I quite like the events
approach.  Most of these faults are real thresholds on things being measured
(even if those 'things' are signals from which stuff is derived for the main
measurements the device is making.)

Jonathan

> 
> -Michael
> 
> > 
> > Jonathan  
>
Jonathan Cameron Oct. 5, 2023, 2:37 p.m. UTC | #42
On Mon, 2 Oct 2023 11:09:11 -0500
David Lechner <dlechner@baylibre.com> wrote:

> On Sat, Sep 30, 2023 at 10:42 AM Jonathan Cameron <jic23@kernel.org> wrote:
> >
> > On Fri, 29 Sep 2023 12:23:27 -0500
> > David Lechner <dlechner@baylibre.com> wrote:
> >  
> > > From: David Lechner <david@lechnology.com>
> > >
> > > From: David Lechner <dlechner@baylibre.com>
> > >
> > > The AD2S1210 has a programmable threshold for the loss of signal (LOS)
> > > fault. This fault is triggered when either the sine or cosine input
> > > falls below the threshold voltage.
> > >
> > > This patch converts the custom device LOS threshold attribute to an
> > > event falling edge threshold attribute on a new monitor signal channel.
> > > The monitor signal is an internal signal that combines the amplitudes
> > > of the sine and cosine inputs as well as the current angle and position
> > > output. This signal is used to detect faults in the input signals.  
> >
> > Hmm. Looking forwards, I'm less sure that we should be shoving all these
> > error conditions onto one channel. Fundamentally we have
> > sine and cosine inputs. I think we should treat those as separate channels
> > and include a third differential channel between them.  
> 
> At first, I did consider a differential channel as you suggested in
> v2. However, the datasheet is quite clear that the LOS and DOS faults
> (and only those faults) come from a signal it calls the "monitor
> signal". This signal is defined as:
> 
>     Monitor = A1 * sin(theta)  * sin(phi) + A2 * cos(theta) * cos(phi)
> 
> where A1 * sin(theta) is the the sine input, A2 * cos(theta) is the
> cosine input and phi is the position output. So mathematically
> speaking, there is no signal that is the difference between the two
> inputs. (See "Theory of Operation" section in the datasheet.)

Hmm. That's certainly a bit more complex than I expected.
Relying on the brief description led me astray.

It's related to the differences in the measured and  as if
theta == phi and A1 == A2 (ideal) then it will be A1.

I can see it's relevant to DOS, but not LOS.  The description of LOS
seems to overlap a number of different things unfortunately.



> 
> But if we want to hide these internal details and don't care about a
> strict definition of "differential", then what is suggested below
> seems fine.

Probably best to introduce that monitor signal though we'll have
to be a bit vague about what it is which has the side effect that
anyone trying to understand what on earth these faults are is going
to be confused (having read the datasheet section a couple of times
I'm not 100% sure...)

> 
> >
> > So this one becomes a double event (you need to signal it on both
> > cosine and sine channels).  The DOS overange is similar.
> > The DOS mismatch is a threshold on the differential channel giving
> >
> > events/in_altvoltage0_thresh_falling_value
> > events/in_altvoltage1_thresh_falling_value (these match)
> > events/in_altvoltage0_thresh_rising_value
> > events/in_altvoltage1_thresh_rising_value (matches previous which is fine)
> > events/in_altvoltage1-altvoltage0_mag_rising_value
> >
> > Does that work here?  Avoids smashing different types of signals together.
> > We could even do the LOT as differential between two angle channels
> > (tracking one and measured one) but meh that's getting complex.>
> > Note this will rely on channel labels to make the above make any sense at all.  
> 
> I think this could be OK - I think what matters most is having some
> documentation that maps the faults and registers on the chip to the
> iio names. Where would the sine/cosine clipping fault fit in though? I
> got a bit too creative and used X_OR_Y to differentiate it (see
> discussion in "staging: iio: resolver: ad2s1210: implement fault
> events"). Strictly speaking, it should probably be a type: threshold,
> direction: either event on both the sine and cosine input channels
> (another double event) since it occurs if either of the signal exceeds
> the power or ground rail voltage. But we already have threshold rising
> and threshold falling on these channels with a different meaning. I
> guess it could call it magnitude instead of a threshold?

Tricky indeed.  Though I guess we only hit the clipping case after
LOS or DOS fires or if their thresholds are set too wide (is that
even possible?).  So it is useful to report it as we are already in
error? Or can we combine the cases by treating it as a cap on the
threshold controls for LOS and DOS?

Even when they aren't just there for error reporting, designers
seem to always come up with new create signals to use for event
detection and sometimes it's a real struggle to map them to
something general.

Jonathan
Jonathan Cameron Oct. 5, 2023, 2:52 p.m. UTC | #43
On Mon, 2 Oct 2023 11:58:17 -0500
David Lechner <dlechner@baylibre.com> wrote:

> On Sat, Sep 30, 2023 at 11:00 AM Jonathan Cameron <jic23@kernel.org> wrote:
> >
> > On Fri, 29 Sep 2023 12:23:31 -0500
> > David Lechner <dlechner@baylibre.com> wrote:
> >  
> > > From: David Lechner <david@lechnology.com>
> > >
> > > From: David Lechner <dlechner@baylibre.com>
> > >
> > > When reading the position and velocity on the AD2S1210, there is also a
> > > 3rd byte following the two data bytes that contains the fault flag bits.
> > > This patch adds support for reading this byte and generating events when
> > > faults occur.
> > >
> > > The faults are mapped to various channels and event types in order to
> > > have a unique event for each fault.
> > >
> > > Signed-off-by: David Lechner <dlechner@baylibre.com>  
> >
> > Use of x and y modifiers is a little odd.  What was your reasoning?
> > Was it just that there was a X_OR_Y modifier?  If so, don't use that!
> > It seemed like a good idea at the time, but it's not nice to deal with
> > and requires a channel with that modifier to hang the controls off
> > + make sure userspace expects that event code.  
> 
> 
> Regarding the point about "requires a channel with that modifier to
> hang the controls off...". Although that comment was about modifiers,
> does it also apply in general.
> 
> There are several fault events that don't have any configurable
> parameters, namely _sine/cosine inputs clipping_ and _velocity exceeds
> max tracking rate_. So there won't be any attributes that contain the
> event specification for those (e.g. no `events/in_angl0_*`
> attributes). It sounds like this would be a problem as well?

It's fine to have a channel that doesn't have controls or the ability
to be read.

We do have history of doing what you have here (a couple
of accelerometers do it) but it's esoteric and rather hard for userspace
to comprehend so I'd rather not introduce it for other types of devices.

I think we should go with the most flexible option of allowing
events to trigger when they 'may be true' to incorporate this case.
Unfortunately I can't see another option that would scale to all the
random combinations of events that might occur.  There are all sorts
of extensions we could make to the event descriptions, but only at the
cost of breaking backwards compatibility and simplicity.

SWith hindsight the whole IIO_MOD_X_OR_Y_OR_Z mess was a design error :(
We can teach userspace code about that quirk for accelerations where
the one that would be hard to handle is the AND case used for
freefall detectors (you detect that the signal magnitude is near 0 for
all axes).  I can't think of another option for that one other than
the weird modifier (unlike this case)

> 
> Should we consider a IIO_EV_INFO_LABEL so that we can have some sort
> of attribute (namely `events/<dir>_<channel spec>_label`) so that
> userspace can enumerate expected events for non-configurable events?

Probably needs something similar to channel labelling, so a separate
callback given we don't handle strings, but sure something like this
would be useful and provide 'hints' along the lines of what the
datasheet calls a particular event.  Not however for what event is sent
as such info should be apparent from the event naming.

Jonathan
David Lechner Oct. 5, 2023, 7:25 p.m. UTC | #44
On Thu, Oct 5, 2023 at 9:37 AM Jonathan Cameron <jic23@kernel.org> wrote:
>
> On Mon, 2 Oct 2023 11:09:11 -0500
> David Lechner <dlechner@baylibre.com> wrote:
>
> > On Sat, Sep 30, 2023 at 10:42 AM Jonathan Cameron <jic23@kernel.org> wrote:
> > >
> > > On Fri, 29 Sep 2023 12:23:27 -0500
> > > David Lechner <dlechner@baylibre.com> wrote:
> > >
> > > > From: David Lechner <david@lechnology.com>
> > > >
> > > > From: David Lechner <dlechner@baylibre.com>
> > > >
> > > > The AD2S1210 has a programmable threshold for the loss of signal (LOS)
> > > > fault. This fault is triggered when either the sine or cosine input
> > > > falls below the threshold voltage.
> > > >
> > > > This patch converts the custom device LOS threshold attribute to an
> > > > event falling edge threshold attribute on a new monitor signal channel.
> > > > The monitor signal is an internal signal that combines the amplitudes
> > > > of the sine and cosine inputs as well as the current angle and position
> > > > output. This signal is used to detect faults in the input signals.
> > >
> > > Hmm. Looking forwards, I'm less sure that we should be shoving all these
> > > error conditions onto one channel. Fundamentally we have
> > > sine and cosine inputs. I think we should treat those as separate channels
> > > and include a third differential channel between them.
> >
> > At first, I did consider a differential channel as you suggested in
> > v2. However, the datasheet is quite clear that the LOS and DOS faults
> > (and only those faults) come from a signal it calls the "monitor
> > signal". This signal is defined as:
> >
> >     Monitor = A1 * sin(theta)  * sin(phi) + A2 * cos(theta) * cos(phi)
> >
> > where A1 * sin(theta) is the the sine input, A2 * cos(theta) is the
> > cosine input and phi is the position output. So mathematically
> > speaking, there is no signal that is the difference between the two
> > inputs. (See "Theory of Operation" section in the datasheet.)
>
> Hmm. That's certainly a bit more complex than I expected.
> Relying on the brief description led me astray.
>
> It's related to the differences in the measured and  as if
> theta == phi and A1 == A2 (ideal) then it will be A1.
>
> I can see it's relevant to DOS, but not LOS.  The description of LOS
> seems to overlap a number of different things unfortunately.
>

One thing to watch out for in the datasheet is the difference between
the fault output pins and the fault bits read over the bus. The LOS
output pin does indicate one or more of multiple faults, but we are
not currently using that. We are only looking at the fault bits which
are more granular.

>
>
> >
> > But if we want to hide these internal details and don't care about a
> > strict definition of "differential", then what is suggested below
> > seems fine.
>
> Probably best to introduce that monitor signal though we'll have
> to be a bit vague about what it is which has the side effect that
> anyone trying to understand what on earth these faults are is going
> to be confused (having read the datasheet section a couple of times
> I'm not 100% sure...)
>
> >
> > >
> > > So this one becomes a double event (you need to signal it on both
> > > cosine and sine channels).  The DOS overange is similar.
> > > The DOS mismatch is a threshold on the differential channel giving
> > >
> > > events/in_altvoltage0_thresh_falling_value
> > > events/in_altvoltage1_thresh_falling_value (these match)
> > > events/in_altvoltage0_thresh_rising_value
> > > events/in_altvoltage1_thresh_rising_value (matches previous which is fine)
> > > events/in_altvoltage1-altvoltage0_mag_rising_value
> > >
> > > Does that work here?  Avoids smashing different types of signals together.
> > > We could even do the LOT as differential between two angle channels
> > > (tracking one and measured one) but meh that's getting complex.>
> > > Note this will rely on channel labels to make the above make any sense at all.
> >
> > I think this could be OK - I think what matters most is having some
> > documentation that maps the faults and registers on the chip to the
> > iio names. Where would the sine/cosine clipping fault fit in though? I
> > got a bit too creative and used X_OR_Y to differentiate it (see
> > discussion in "staging: iio: resolver: ad2s1210: implement fault
> > events"). Strictly speaking, it should probably be a type: threshold,
> > direction: either event on both the sine and cosine input channels
> > (another double event) since it occurs if either of the signal exceeds
> > the power or ground rail voltage. But we already have threshold rising
> > and threshold falling on these channels with a different meaning. I
> > guess it could call it magnitude instead of a threshold?
>
> Tricky indeed.  Though I guess we only hit the clipping case after
> LOS or DOS fires or if their thresholds are set too wide (is that
> even possible?).

I suppose it _could_ be possible on the high side if the AVDD voltage
supply was selected to be less than the 4.4V max of the threshold
voltage registers.

> So it is useful to report it as we are already in
> error? Or can we combine the cases by treating it as a cap on the
> threshold controls for LOS and DOS?

I found the clipping error useful while developing this driver since
it help identify that we had a gain setting wrong on the excitation
output (on the circuit board) which in turn caused the inputs to be
overdriven. But, yes when this happened, it also always triggered at
least one or more of the LOS and DOS faults as well.

>
> Even when they aren't just there for error reporting, designers
> seem to always come up with new create signals to use for event
> detection and sometimes it's a real struggle to map them to
> something general.
>
> Jonathan
>
>
Vincent Whitchurch Oct. 6, 2023, 2:48 p.m. UTC | #45
On Mon, 2023-10-02 at 10:17 +0100, Jonathan Cameron wrote:
> Hmm. What happened to roadtest?  I was hoping that would solve this sort
> of issue by allowing simple testing of basic functionality...

Roadtest is alive and well.  Several of my coworkers have been using it
for development and testing of new drivers[0][1][2][3][4] and
patches[5][6], and this has resulted in easier testing and refactoring
during development, more robust code, and of course the ability to
easily detect regressions after the patches are merged.

[0] https://lore.kernel.org/lkml/20230323-add-opt4001-driver-v2-2-0bae0398669d@axis.com/
[1] https://lore.kernel.org/lkml/d218a1bc75402b5ebd6e12a563f7315f83fe966c.1689753076.git.waqar.hameed@axis.com/
[2] https://lore.kernel.org/lkml/7b856b74c4c0f8c6c539d7c692051c9203b103c0.1692699931.git.waqar.hameed@axis.com/
[3] https://lore.kernel.org/lkml/20231002-rx8111-add-timestamp0-v1-1-353727cf7f14@axis.com/
[4] https://lore.kernel.org/lkml/20230502-tps6287x-driver-v3-2-e25140a023f5@axis.com/
[5] https://lore.kernel.org/lkml/20221012102347.153201-1-chenhuiz@axis.com/
[6] https://lore.kernel.org/lkml/20220413114014.2204623-3-camel.guo@axis.com/

In fact, by running our roadtests on newer kernels we have found
numerous bugs[10][12][14] and regressions[7][8][9][11][15] in mainline,
including subsystem-level issues affecting other drivers too.

[7] https://lore.kernel.org/lkml/20230911-regulator-voltage-sel-v1-1-886eb1ade8d8@axis.com/
[8] https://lore.kernel.org/lkml/20230918-power-uaf-v1-1-73c397178c42@axis.com/
[9] https://lore.kernel.org/lkml/20230829-tps-voltages-v1-1-7ba4f958a194@axis.com/
[10] https://lore.kernel.org/lkml/20230613-genirq-nested-v3-1-ae58221143eb@axis.com/
[11] https://lore.kernel.org/lkml/20220503114333.456476-1-camel.guo@axis.com/
[12] https://lore.kernel.org/linux-iio/20220816080828.1218667-1-vincent.whitchurch@axis.com/
[13] https://lore.kernel.org/linux-iio/20220519091925.1053897-1-vincent.whitchurch@axis.com/
[14] https://lore.kernel.org/linux-iio/20220620144231.GA23345@axis.com/
[15] https://lore.kernel.org/linux-spi/YxBX4bXG02E4lSUW@axis.com/

(The above lists are not exhaustive.)

> Hope it is still headed for a new version / upstream!

I pushed out an update with a squash of (most parts of) our internal
version out to the following repo, it's based on v6.6-rc4.

  https://github.com/vwax/linux/tree/roadtest/devel

(There are currently 6 lines of --diff-filter=M against v6.6-rc4 on the
 linked repo.  Two of those are from a patch which is posted and waiting
 for review on the lists, and the rest are for enabling regmap debugfs
 writes which are used from some of the newer tests.)

Since roadtest itself does not require any patches to the kernel or any
out-of-tree modules, the maintenance of the framework would not really
be simplified by putting it in the upstream tree.  However, there is of
course a potentially large benefit to the quality of many kinds of
kernel drivers if roadtest gets used by others, and having it in-tree
could facilitate that.  And it would potentially allow regressions like
the ones we're finding to be caught _before_ they go in, since anyone
can run the tests without special hardware.

The idea of having to maintain it in-tree and doing all the work that
goes along with that (dealing with the expectations of maintainers,
wrangling patches from mailing lists, etc), is something I personally
have had a hard time warming up to, but I have some coworkers who may
potentially be interested in that kind of work, so I wouldn't rule out
another posting of the patch set targeting upstream sometime in the
future.
Jonathan Cameron Oct. 10, 2023, 9:29 a.m. UTC | #46
On Fri, 6 Oct 2023 14:48:29 +0000
Vincent Whitchurch <Vincent.Whitchurch@axis.com> wrote:

Hi Vincent

Thanks for the update,

> On Mon, 2023-10-02 at 10:17 +0100, Jonathan Cameron wrote:
> > Hmm. What happened to roadtest?  I was hoping that would solve this sort
> > of issue by allowing simple testing of basic functionality...  
> 
> Roadtest is alive and well.  Several of my coworkers have been using it
> for development and testing of new drivers[0][1][2][3][4] and
> patches[5][6], and this has resulted in easier testing and refactoring
> during development, more robust code, and of course the ability to
> easily detect regressions after the patches are merged.
> 
> [0] https://lore.kernel.org/lkml/20230323-add-opt4001-driver-v2-2-0bae0398669d@axis.com/
> [1] https://lore.kernel.org/lkml/d218a1bc75402b5ebd6e12a563f7315f83fe966c.1689753076.git.waqar.hameed@axis.com/
> [2] https://lore.kernel.org/lkml/7b856b74c4c0f8c6c539d7c692051c9203b103c0.1692699931.git.waqar.hameed@axis.com/
> [3] https://lore.kernel.org/lkml/20231002-rx8111-add-timestamp0-v1-1-353727cf7f14@axis.com/
> [4] https://lore.kernel.org/lkml/20230502-tps6287x-driver-v3-2-e25140a023f5@axis.com/
> [5] https://lore.kernel.org/lkml/20221012102347.153201-1-chenhuiz@axis.com/
> [6] https://lore.kernel.org/lkml/20220413114014.2204623-3-camel.guo@axis.com/
> 
> In fact, by running our roadtests on newer kernels we have found
> numerous bugs[10][12][14] and regressions[7][8][9][11][15] in mainline,
> including subsystem-level issues affecting other drivers too.
> 
> [7] https://lore.kernel.org/lkml/20230911-regulator-voltage-sel-v1-1-886eb1ade8d8@axis.com/
> [8] https://lore.kernel.org/lkml/20230918-power-uaf-v1-1-73c397178c42@axis.com/
> [9] https://lore.kernel.org/lkml/20230829-tps-voltages-v1-1-7ba4f958a194@axis.com/
> [10] https://lore.kernel.org/lkml/20230613-genirq-nested-v3-1-ae58221143eb@axis.com/
> [11] https://lore.kernel.org/lkml/20220503114333.456476-1-camel.guo@axis.com/
> [12] https://lore.kernel.org/linux-iio/20220816080828.1218667-1-vincent.whitchurch@axis.com/
> [13] https://lore.kernel.org/linux-iio/20220519091925.1053897-1-vincent.whitchurch@axis.com/
> [14] https://lore.kernel.org/linux-iio/20220620144231.GA23345@axis.com/
> [15] https://lore.kernel.org/linux-spi/YxBX4bXG02E4lSUW@axis.com/
> 
> (The above lists are not exhaustive.)
> 

Great stuff!

> > Hope it is still headed for a new version / upstream!  
> 
> I pushed out an update with a squash of (most parts of) our internal
> version out to the following repo, it's based on v6.6-rc4.
> 
>   https://github.com/vwax/linux/tree/roadtest/devel

Thanks.

> 
> (There are currently 6 lines of --diff-filter=M against v6.6-rc4 on the
>  linked repo.  Two of those are from a patch which is posted and waiting
>  for review on the lists, and the rest are for enabling regmap debugfs
>  writes which are used from some of the newer tests.)
> 
> Since roadtest itself does not require any patches to the kernel or any
> out-of-tree modules, the maintenance of the framework would not really
> be simplified by putting it in the upstream tree.  However, there is of
> course a potentially large benefit to the quality of many kinds of
> kernel drivers if roadtest gets used by others, and having it in-tree
> could facilitate that.  And it would potentially allow regressions like
> the ones we're finding to be caught _before_ they go in, since anyone
> can run the tests without special hardware.

Exactly  - my main interest is the dream of getting to the point where
new drivers typically also come with roadtest tests, with the aim that
they will be used for regression testing. For IIO I might lean on
/ ask nicely  few of the bigger contributors to add fairly comprehensive
tests for say one in 3 of their drivers, providing a canary for any
subsystem level problems that might sneak in. The stability gained for
those drivers might also prove it's own benefit to push people to add tests.
At somepoint in the longer term I might even make it a requirement for
upstreaming a new driver + slowly tackle the backlog of existing ones.
From my experiments with it last year, this is a trivial burden fo

> 
> The idea of having to maintain it in-tree and doing all the work that
> goes along with that (dealing with the expectations of maintainers,
> wrangling patches from mailing lists, etc), is something I personally
> have had a hard time warming up to, but I have some coworkers who may
> potentially be interested in that kind of work, so I wouldn't rule out
> another posting of the patch set targeting upstream sometime in the
> future.

I fully appreciate your concern.  I just really like roadtest and want
a smooth way to integrate using it with my upstream maintenance (and occasional
development) process...  I of course can't expect you to commit to anything
though - I'd be delighted if someone else wants to take this forwards but
that would be very much their decision to make!

Having not yet waded into the latest code, how 'stable' is it from the point
of view of modifications to tests?  I can rebase the ones I have out of tree
and see, but I'm after an assessment that incorporates what you are
planning to change in future.

I guess the nasty stuff is if you have a few hundred additional drivers
in the test set, any modification to the way they interact with the core
of roadtest becomes very painful to push into those tests.

One starting point would be to separate the tests directory from the
directories containing roadtest frameworks etc as that would help to
limit scope of responsibility.

If a potential upstream roadtest maintainer is primarily concerned about
review + handling of the actual tests, other than potentially letting in
some ugly code, I'd imagine any subsystem maintainer who opts into this
will take that burden on - perhaps with the occasional question heading
your way. I'd certainly not expect you to have to deal with high patch flows
and would ensure that didn't happen for any IIO tests (any review people
have time for is of course welcome!)

+CC a few maintainers of other subsystems who may be interested (I know
one of them is ;)

Jonathan