Re: [PATCH -next v2] iio: frequency: adf4350: Use device managed functions

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sat, 26 Aug 2023 14:41:30 +0800
Jinjie Ruan <ruanjinjie@xxxxxxxxxx> wrote:

> The devm_clk_get_enabled() helper:
>     - calls devm_clk_get()
>     - calls clk_prepare_enable() and registers what is needed in order to
>       call clk_disable_unprepare() when needed, as a managed resource.
> 
> Also replace devm_regulator_get() and regulator_enable() with
> devm_regulator_get_enable() helper and remove regulator_disable().
> 
> Replace iio_device_register() with devm_iio_device_register() and remove
> iio_device_unregister().
> 
> And st->reg is not used anymore, so remove it.
> 
> This simplifies the code.
> 
> Signed-off-by: Jinjie Ruan <ruanjinjie@xxxxxxxxxx>
> Suggested-by: Jonathan Cameron <jic23@xxxxxxxxxx>

Hi Jinjie,

This patch has exposed what looks like a minor bug to me in the existing code.
I think we should fix that as part of your changes. It's an obscure enough race
condition and lack of powering down on error, that I don't think we need to
separate the fix out from the main patch.

The short description (more inline) is register an additional devm callback
to set the device into software controlled power down mode after the device
is unregistered (and equivalent in the error path.)

Sorry this turned out to be more complex than expected!

Thanks,

Jonathan
 
> ---
> v2:
> - Also use devm_regulator_get_enable() and devm_iio_device_register().
> - Update the commit message and title.
> ---
>  drivers/iio/frequency/adf4350.c | 52 ++++++++-------------------------
>  1 file changed, 12 insertions(+), 40 deletions(-)
> 
> diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c
> index 85e289700c3c..b38d024529eb 100644
> --- a/drivers/iio/frequency/adf4350.c
> +++ b/drivers/iio/frequency/adf4350.c
> @@ -33,7 +33,6 @@ enum {
>  
>  struct adf4350_state {
>  	struct spi_device		*spi;
> -	struct regulator		*reg;
>  	struct gpio_desc		*lock_detect_gpiod;
>  	struct adf4350_platform_data	*pdata;
>  	struct clk			*clk;
> @@ -491,29 +490,20 @@ static int adf4350_probe(struct spi_device *spi)
>  	}
>  
>  	if (!pdata->clkin) {
> -		clk = devm_clk_get(&spi->dev, "clkin");
> +		clk = devm_clk_get_enabled(&spi->dev, "clkin");
>  		if (IS_ERR(clk))
> -			return -EPROBE_DEFER;
> -
> -		ret = clk_prepare_enable(clk);
> -		if (ret < 0)
> -			return ret;
> +			return PTR_ERR(clk);
>  	}
>  
>  	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
> -	if (indio_dev == NULL) {
> -		ret =  -ENOMEM;
> -		goto error_disable_clk;
> -	}
> +	if (indio_dev == NULL)
> +		return -ENOMEM;
>  
>  	st = iio_priv(indio_dev);
>  
> -	st->reg = devm_regulator_get(&spi->dev, "vcc");
> -	if (!IS_ERR(st->reg)) {
> -		ret = regulator_enable(st->reg);
> -		if (ret)
> -			goto error_disable_clk;
> -	}
> +	ret = devm_regulator_get_enable(&spi->dev, "vcc");
> +	if (ret)
> +		return ret;
>  
>  	spi_set_drvdata(spi, indio_dev);
>  	st->spi = spi;
> @@ -544,47 +534,29 @@ static int adf4350_probe(struct spi_device *spi)
>  
>  	st->lock_detect_gpiod = devm_gpiod_get_optional(&spi->dev, NULL,
>  							GPIOD_IN);
> -	if (IS_ERR(st->lock_detect_gpiod)) {
> -		ret = PTR_ERR(st->lock_detect_gpiod);
> -		goto error_disable_reg;
> -	}
> +	if (IS_ERR(st->lock_detect_gpiod))
> +		return PTR_ERR(st->lock_detect_gpiod);
>  
>  	if (pdata->power_up_frequency) {
>  		ret = adf4350_set_freq(st, pdata->power_up_frequency);
>  		if (ret)
> -			goto error_disable_reg;
> +			return ret;
>  	}
>  
> -	ret = iio_device_register(indio_dev);
> +	ret = devm_iio_device_register(&spi->dev, indio_dev);
>  	if (ret)
> -		goto error_disable_reg;
> +		return ret;
>  
>  	return 0;
> -
> -error_disable_reg:
> -	if (!IS_ERR(st->reg))
> -		regulator_disable(st->reg);
> -error_disable_clk:
> -	clk_disable_unprepare(clk);
> -
> -	return ret;
>  }
>  
>  static void adf4350_remove(struct spi_device *spi)
>  {
>  	struct iio_dev *indio_dev = spi_get_drvdata(spi);
>  	struct adf4350_state *st = iio_priv(indio_dev);
> -	struct regulator *reg = st->reg;
>  
>  	st->regs[ADF4350_REG2] |= ADF4350_REG2_POWER_DOWN_EN;
>  	adf4350_sync_config(st);

Unfortunately this shouts "bug" at me.  Nothing to do with your change
as such, but it's made a lot more obvious by it.

Couple of things that are wrong.

1) The device is powered down 'before' we unregister it with the
   subsystem and as such userspace interfaces are still exposed which
   probably won't do the right thing if the chip is powered down.

2) This isn't done in the error paths in probe.

The actual register update here "sort of" balances (in a fairly non obvious fashion)
one in adf4350_set_freq() so I would register a new callback with devm_add_action_or_reset()
after this block
  	if (pdata->power_up_frequency) {
 		ret = adf4350_set_freq(st, pdata->power_up_frequency);
  		if (ret)
 +			return ret;
  	}
(here)

that runs
  	st->regs[ADF4350_REG2] |= ADF4350_REG2_POWER_DOWN_EN;
  	adf4350_sync_config(st);

in both error and remove paths.  Doing that will let us get
rid of the remove function completely.


> -
> -	iio_device_unregister(indio_dev);
> -
> -	clk_disable_unprepare(st->clk);
> -
> -	if (!IS_ERR(reg))
> -		regulator_disable(reg);
>  }
>  
>  static const struct of_device_id adf4350_of_match[] = {




[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux