iio: adc: sun4i-gpadc-iio: fix parent device being used in devm function
authorQuentin Schulz <quentin.schulz@free-electrons.com>
Thu, 18 May 2017 06:36:07 +0000 (08:36 +0200)
committerJonathan Cameron <jic23@kernel.org>
Sun, 21 May 2017 14:17:40 +0000 (15:17 +0100)
For the sake of DT binding stability, this IIO driver is a child of an
MFD driver for Allwinner A10, A13 and A31 because there already exists a
DT binding for this IP. The MFD driver has a DT node but the IIO driver
does not.

The IIO device registers the temperature sensor in the thermal framework
using the DT node of the parent, the MFD device, so the thermal
framework could match the phandle to the MFD device in the DT and the
struct device used to register in the thermal framework.

devm_thermal_zone_of_sensor_register was previously used to register the
thermal sensor with the parent struct device of the IIO device,
representing the MFD device. By doing so, we registered actually the
parent in the devm routine and not the actual IIO device.

This lead to the devm unregister function not being called when the IIO
module driver is removed. It resulted in the thermal framework still
polling the get_temp function of the IIO module while the device doesn't
exist anymore, thus generated a kernel panic.

Use the non-devm function instead and do the unregister manually in the
remove function.

Fixes: d1caa9905538 ("iio: adc: add support for Allwinner SoCs ADC")

Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Reported-by: Corentin Labbe <clabbe.montjoie@gmail.com>
Reviewed-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
drivers/iio/adc/sun4i-gpadc-iio.c

index 1c83b45043bf39457e1da63b958fcf3fbe43d18c..81d4c39e414a4da6b0f8df0ebd8922d371a909d7 100644 (file)
@@ -105,6 +105,8 @@ struct sun4i_gpadc_iio {
        bool                            no_irq;
        /* prevents concurrent reads of temperature and ADC */
        struct mutex                    mutex;
+       struct thermal_zone_device      *tzd;
+       struct device                   *sensor_device;
 };
 
 #define SUN4I_GPADC_ADC_CHANNEL(_channel, _name) {             \
@@ -502,7 +504,6 @@ static int sun4i_gpadc_probe_dt(struct platform_device *pdev,
 {
        struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
        const struct of_device_id *of_dev;
-       struct thermal_zone_device *tzd;
        struct resource *mem;
        void __iomem *base;
        int ret;
@@ -532,13 +533,14 @@ static int sun4i_gpadc_probe_dt(struct platform_device *pdev,
        if (!IS_ENABLED(CONFIG_THERMAL_OF))
                return 0;
 
-       tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, info,
-                                                  &sun4i_ts_tz_ops);
-       if (IS_ERR(tzd))
+       info->sensor_device = &pdev->dev;
+       info->tzd = thermal_zone_of_sensor_register(info->sensor_device, 0,
+                                                   info, &sun4i_ts_tz_ops);
+       if (IS_ERR(info->tzd))
                dev_err(&pdev->dev, "could not register thermal sensor: %ld\n",
-                       PTR_ERR(tzd));
+                       PTR_ERR(info->tzd));
 
-       return PTR_ERR_OR_ZERO(tzd);
+       return PTR_ERR_OR_ZERO(info->tzd);
 }
 
 static int sun4i_gpadc_probe_mfd(struct platform_device *pdev,
@@ -584,15 +586,15 @@ static int sun4i_gpadc_probe_mfd(struct platform_device *pdev,
                 * of_node, and the device from this driver as third argument to
                 * return the temperature.
                 */
-               struct thermal_zone_device *tzd;
-               tzd = devm_thermal_zone_of_sensor_register(pdev->dev.parent, 0,
-                                                          info,
-                                                          &sun4i_ts_tz_ops);
-               if (IS_ERR(tzd)) {
+               info->sensor_device = pdev->dev.parent;
+               info->tzd = thermal_zone_of_sensor_register(info->sensor_device,
+                                                           0, info,
+                                                           &sun4i_ts_tz_ops);
+               if (IS_ERR(info->tzd)) {
                        dev_err(&pdev->dev,
                                "could not register thermal sensor: %ld\n",
-                               PTR_ERR(tzd));
-                       return PTR_ERR(tzd);
+                               PTR_ERR(info->tzd));
+                       return PTR_ERR(info->tzd);
                }
        } else {
                indio_dev->num_channels =
@@ -688,7 +690,13 @@ static int sun4i_gpadc_remove(struct platform_device *pdev)
 
        pm_runtime_put(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
-       if (!info->no_irq && IS_ENABLED(CONFIG_THERMAL_OF))
+
+       if (!IS_ENABLED(CONFIG_THERMAL_OF))
+               return 0;
+
+       thermal_zone_of_sensor_unregister(info->sensor_device, info->tzd);
+
+       if (!info->no_irq)
                iio_map_array_unregister(indio_dev);
 
        return 0;