Re: [PATCH v3] iio: adc: npcm: fix unbalanced clk_disable_unprepare()

Jonathan Cameron <[email protected]>
Newsgroups org.ozlabs.lists.openbmc,org.kernel.vger.linux-iio,org.kernel.vger.linux-kernel
Message-ID <20260420132838.147d18b6@jic23-huawei>
On Tue, 14 Apr 2026 13:30:06 +0100
David Carlier <[email protected]> wrote:

> The driver acquired the ADC clock with devm_clk_get() and read its
> rate, but never called clk_prepare_enable(). The probe error path and
> npcm_adc_remove() both called clk_disable_unprepare() unconditionally,
> causing the clk framework's enable/prepare counts to underflow on
> probe failure or module unbind.
> 
> The issue went unnoticed because NPCM BMC firmware leaves the ADC
> clock enabled at boot, so the driver happened to work in practice.
> 
> Switch to devm_clk_get_enabled() so the clock is properly enabled
> during probe and automatically released by the device-managed
> cleanup, and drop the now-redundant clk_disable_unprepare() from
> both the probe error path and remove().
> 
> While at it, drop the duplicate error message on devm_request_irq()
> failure since the IRQ core already logs it.
> 
> Fixes: 9bf85fbc9d8f ("iio: adc: add NPCM ADC driver")
> Signed-off-by: David Carlier <[email protected]>
> Reviewed-by: Andy Shevchenko <[email protected]>
Applied to the fixes-togreg branch of iio.git. Note I'll rebase that on rc1
once available, before sending a pull request.

Thanks,

Jonathan

> ---
> v3: mention the dev_err drop in the commit log and collapse the
>     single-statement -ENODEV branch (Andy Shevchenko).
> v2: drop redundant dev_err() on devm_request_irq() failure since the
>     IRQ core already logs it, and remove the now-single-statement
>     braces (Andy Shevchenko).
> 
>  drivers/iio/adc/npcm_adc.c | 25 ++++++++-----------------
>  1 file changed, 8 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/iio/adc/npcm_adc.c b/drivers/iio/adc/npcm_adc.c
> index ddabb9600d46..61c8b825bda1 100644
> --- a/drivers/iio/adc/npcm_adc.c
> +++ b/drivers/iio/adc/npcm_adc.c
> @@ -231,7 +231,7 @@ static int npcm_adc_probe(struct platform_device *pdev)
>  	if (IS_ERR(info->reset))
>  		return PTR_ERR(info->reset);
>  
> -	info->adc_clk = devm_clk_get(&pdev->dev, NULL);
> +	info->adc_clk = devm_clk_get_enabled(&pdev->dev, NULL);
>  	if (IS_ERR(info->adc_clk)) {
>  		dev_warn(&pdev->dev, "ADC clock failed: can't read clk\n");
>  		return PTR_ERR(info->adc_clk);
> @@ -244,17 +244,13 @@ static int npcm_adc_probe(struct platform_device *pdev)
>  	info->adc_sample_hz = clk_get_rate(info->adc_clk) / ((div + 1) * 2);
>  
>  	irq = platform_get_irq(pdev, 0);
> -	if (irq < 0) {
> -		ret = irq;
> -		goto err_disable_clk;
> -	}
> +	if (irq < 0)
> +		return irq;
>  
>  	ret = devm_request_irq(&pdev->dev, irq, npcm_adc_isr, 0,
>  			       "NPCM_ADC", indio_dev);
> -	if (ret < 0) {
> -		dev_err(dev, "failed requesting interrupt\n");
> -		goto err_disable_clk;
> -	}
> +	if (ret < 0)
> +		return ret;
>  
>  	reg_con = ioread32(info->regs + NPCM_ADCCON);
>  	info->vref = devm_regulator_get_optional(&pdev->dev, "vref");
> @@ -262,7 +258,7 @@ static int npcm_adc_probe(struct platform_device *pdev)
>  		ret = regulator_enable(info->vref);
>  		if (ret) {
>  			dev_err(&pdev->dev, "Can't enable ADC reference voltage\n");
> -			goto err_disable_clk;
> +			return ret;
>  		}
>  
>  		iowrite32(reg_con & ~NPCM_ADCCON_REFSEL,
> @@ -272,10 +268,8 @@ static int npcm_adc_probe(struct platform_device *pdev)
>  		 * Any error which is not ENODEV indicates the regulator
>  		 * has been specified and so is a failure case.
>  		 */
> -		if (PTR_ERR(info->vref) != -ENODEV) {
> -			ret = PTR_ERR(info->vref);
> -			goto err_disable_clk;
> -		}
> +		if (PTR_ERR(info->vref) != -ENODEV)
> +			return PTR_ERR(info->vref);
>  
>  		/* Use internal reference */
>  		iowrite32(reg_con | NPCM_ADCCON_REFSEL,
> @@ -314,8 +308,6 @@ static int npcm_adc_probe(struct platform_device *pdev)
>  	iowrite32(reg_con & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON);
>  	if (!IS_ERR(info->vref))
>  		regulator_disable(info->vref);
> -err_disable_clk:
> -	clk_disable_unprepare(info->adc_clk);
>  
>  	return ret;
>  }
> @@ -332,7 +324,6 @@ static void npcm_adc_remove(struct platform_device *pdev)
>  	iowrite32(regtemp & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON);
>  	if (!IS_ERR(info->vref))
>  		regulator_disable(info->vref);
> -	clk_disable_unprepare(info->adc_clk);
>  }
>  
>  static struct platform_driver npcm_adc_driver = {
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.