@@ -87,6 +87,7 @@ struct rzg2l_adc {
const struct rzg2l_adc_hw_params *hw_params;
struct completion completion;
struct mutex lock;
+ int irq;
u16 last_val[RZG2L_ADC_MAX_CHANNELS];
bool was_rpm_active;
};
@@ -430,7 +431,6 @@ static int rzg2l_adc_probe(struct platform_device *pdev)
struct iio_dev *indio_dev;
struct rzg2l_adc *adc;
int ret;
- int irq;
indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
if (!indio_dev)
@@ -464,25 +464,33 @@ static int rzg2l_adc_probe(struct platform_device *pdev)
pm_runtime_set_autosuspend_delay(dev, 300);
pm_runtime_use_autosuspend(dev);
- ret = devm_pm_runtime_enable(dev);
- if (ret)
- return ret;
+ /*
+ * Use non-devres APIs from this point onward, as the ADC clocks are
+ * managed through its power domain. Otherwise, durring repeated
+ * unbind/bind operations, the ADC may be runtime resumed when it
+ * is not part of its power domain, leading to accessing ADC
+ * registers without its clocks being enabled and its PM domain
+ * being turned on.
+ */
+ pm_runtime_enable(dev);
platform_set_drvdata(pdev, indio_dev);
ret = rzg2l_adc_hw_init(dev, adc);
- if (ret)
- return dev_err_probe(&pdev->dev, ret,
- "failed to initialize ADC HW\n");
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret, "failed to initialize ADC HW\n");
+ goto rpm_disable;
+ }
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ goto rpm_disable;
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- return irq;
+ adc->irq = ret;
- ret = devm_request_irq(dev, irq, rzg2l_adc_isr,
- 0, dev_name(dev), adc);
+ ret = request_irq(adc->irq, rzg2l_adc_isr, 0, dev_name(dev), adc);
if (ret < 0)
- return ret;
+ goto rpm_disable;
init_completion(&adc->completion);
@@ -492,7 +500,30 @@ static int rzg2l_adc_probe(struct platform_device *pdev)
indio_dev->channels = adc->data->channels;
indio_dev->num_channels = adc->data->num_channels;
- return devm_iio_device_register(dev, indio_dev);
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto free_irq;
+
+ return 0;
+
+free_irq:
+ free_irq(adc->irq, adc);
+rpm_disable:
+ pm_runtime_disable(dev);
+ pm_runtime_dont_use_autosuspend(dev);
+ return ret;
+}
+
+static void rzg2l_adc_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct rzg2l_adc *adc = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ free_irq(adc->irq, adc);
+ pm_runtime_disable(dev);
+ pm_runtime_dont_use_autosuspend(dev);
}
static const struct rzg2l_adc_hw_params rzg2l_hw_params = {
@@ -614,6 +645,7 @@ static const struct dev_pm_ops rzg2l_adc_pm_ops = {
static struct platform_driver rzg2l_adc_driver = {
.probe = rzg2l_adc_probe,
+ .remove = rzg2l_adc_remove,
.driver = {
.name = DRIVER_NAME,
.of_match_table = rzg2l_adc_match,