diff options
Diffstat (limited to 'drivers/iio/adc/sun4i-gpadc-iio.c')
-rw-r--r-- | drivers/iio/adc/sun4i-gpadc-iio.c | 170 |
1 files changed, 138 insertions, 32 deletions
diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c index e53182510150..b23527309088 100644 --- a/drivers/iio/adc/sun4i-gpadc-iio.c +++ b/drivers/iio/adc/sun4i-gpadc-iio.c @@ -85,6 +85,12 @@ static const struct gpadc_data sun6i_gpadc_data = { .adc_chan_mask = SUN6I_GPADC_CTRL1_ADC_CHAN_MASK, }; +static const struct gpadc_data sun8i_a33_gpadc_data = { + .temp_offset = -1662, + .temp_scale = 162, + .tp_mode_en = SUN8I_GPADC_CTRL1_CHOP_TEMP_EN, +}; + struct sun4i_gpadc_iio { struct iio_dev *indio_dev; struct completion completion; @@ -96,6 +102,7 @@ struct sun4i_gpadc_iio { unsigned int temp_data_irq; atomic_t ignore_temp_data_irq; const struct gpadc_data *data; + bool no_irq; /* prevents concurrent reads of temperature and ADC */ struct mutex mutex; }; @@ -138,6 +145,23 @@ static const struct iio_chan_spec sun4i_gpadc_channels_no_temp[] = { SUN4I_GPADC_ADC_CHANNEL(3, "adc_chan3"), }; +static const struct iio_chan_spec sun8i_a33_gpadc_channels[] = { + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .datasheet_name = "temp_adc", + }, +}; + +static const struct regmap_config sun4i_gpadc_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .fast_io = true, +}; + static int sun4i_prepare_for_irq(struct iio_dev *indio_dev, int channel, unsigned int irq) { @@ -247,6 +271,17 @@ static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val) { struct sun4i_gpadc_iio *info = iio_priv(indio_dev); + if (info->no_irq) { + pm_runtime_get_sync(indio_dev->dev.parent); + + regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, val); + + pm_runtime_mark_last_busy(indio_dev->dev.parent); + pm_runtime_put_autosuspend(indio_dev->dev.parent); + + return 0; + } + return sun4i_gpadc_read(indio_dev, 0, val, info->temp_data_irq); } @@ -454,31 +489,69 @@ static int sun4i_irq_init(struct platform_device *pdev, const char *name, return 0; } -static int sun4i_gpadc_probe(struct platform_device *pdev) +static const struct of_device_id sun4i_gpadc_of_id[] = { + { + .compatible = "allwinner,sun8i-a33-ths", + .data = &sun8i_a33_gpadc_data, + }, + { /* sentinel */ } +}; + +static int sun4i_gpadc_probe_dt(struct platform_device *pdev, + struct iio_dev *indio_dev) { - struct sun4i_gpadc_iio *info; - struct iio_dev *indio_dev; + 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; - struct sun4i_gpadc_dev *sun4i_gpadc_dev; - sun4i_gpadc_dev = dev_get_drvdata(pdev->dev.parent); + of_dev = of_match_device(sun4i_gpadc_of_id, &pdev->dev); + if (!of_dev) + return -ENODEV; + + info->no_irq = true; + info->data = (struct gpadc_data *)of_dev->data; + indio_dev->num_channels = ARRAY_SIZE(sun8i_a33_gpadc_channels); + indio_dev->channels = sun8i_a33_gpadc_channels; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(base)) + return PTR_ERR(base); + + info->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &sun4i_gpadc_regmap_config); + if (IS_ERR(info->regmap)) { + ret = PTR_ERR(info->regmap); + dev_err(&pdev->dev, "failed to init regmap: %d\n", ret); + return ret; + } - indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info)); - if (!indio_dev) - return -ENOMEM; + if (!IS_ENABLED(CONFIG_THERMAL_OF)) + return 0; - info = iio_priv(indio_dev); - platform_set_drvdata(pdev, indio_dev); + tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, info, + &sun4i_ts_tz_ops); + if (IS_ERR(tzd)) + dev_err(&pdev->dev, "could not register thermal sensor: %ld\n", + PTR_ERR(tzd)); - mutex_init(&info->mutex); + return PTR_ERR_OR_ZERO(tzd); +} + +static int sun4i_gpadc_probe_mfd(struct platform_device *pdev, + struct iio_dev *indio_dev) +{ + struct sun4i_gpadc_iio *info = iio_priv(indio_dev); + struct sun4i_gpadc_dev *sun4i_gpadc_dev = + dev_get_drvdata(pdev->dev.parent); + int ret; + + info->no_irq = false; info->regmap = sun4i_gpadc_dev->regmap; - info->indio_dev = indio_dev; - init_completion(&info->completion); - indio_dev->name = dev_name(&pdev->dev); - indio_dev->dev.parent = &pdev->dev; - indio_dev->dev.of_node = pdev->dev.of_node; - indio_dev->info = &sun4i_gpadc_iio_info; - indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->num_channels = ARRAY_SIZE(sun4i_gpadc_channels); indio_dev->channels = sun4i_gpadc_channels; @@ -519,8 +592,7 @@ static int sun4i_gpadc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "could not register thermal sensor: %ld\n", PTR_ERR(tzd)); - ret = PTR_ERR(tzd); - goto err; + return PTR_ERR(tzd); } } else { indio_dev->num_channels = @@ -528,36 +600,69 @@ static int sun4i_gpadc_probe(struct platform_device *pdev) indio_dev->channels = sun4i_gpadc_channels_no_temp; } - pm_runtime_set_autosuspend_delay(&pdev->dev, - SUN4I_GPADC_AUTOSUSPEND_DELAY); - pm_runtime_use_autosuspend(&pdev->dev); - pm_runtime_set_suspended(&pdev->dev); - pm_runtime_enable(&pdev->dev); - if (IS_ENABLED(CONFIG_THERMAL_OF)) { ret = sun4i_irq_init(pdev, "TEMP_DATA_PENDING", sun4i_gpadc_temp_data_irq_handler, "temp_data", &info->temp_data_irq, &info->ignore_temp_data_irq); if (ret < 0) - goto err; + return ret; } ret = sun4i_irq_init(pdev, "FIFO_DATA_PENDING", sun4i_gpadc_fifo_data_irq_handler, "fifo_data", &info->fifo_data_irq, &info->ignore_fifo_data_irq); if (ret < 0) - goto err; + return ret; if (IS_ENABLED(CONFIG_THERMAL_OF)) { ret = iio_map_array_register(indio_dev, sun4i_gpadc_hwmon_maps); if (ret < 0) { dev_err(&pdev->dev, "failed to register iio map array\n"); - goto err; + return ret; } } + return 0; +} + +static int sun4i_gpadc_probe(struct platform_device *pdev) +{ + struct sun4i_gpadc_iio *info; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + platform_set_drvdata(pdev, indio_dev); + + mutex_init(&info->mutex); + info->indio_dev = indio_dev; + init_completion(&info->completion); + indio_dev->name = dev_name(&pdev->dev); + indio_dev->dev.parent = &pdev->dev; + indio_dev->dev.of_node = pdev->dev.of_node; + indio_dev->info = &sun4i_gpadc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + if (pdev->dev.of_node) + ret = sun4i_gpadc_probe_dt(pdev, indio_dev); + else + ret = sun4i_gpadc_probe_mfd(pdev, indio_dev); + + if (ret) + return ret; + + pm_runtime_set_autosuspend_delay(&pdev->dev, + SUN4I_GPADC_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_enable(&pdev->dev); + ret = devm_iio_device_register(&pdev->dev, indio_dev); if (ret < 0) { dev_err(&pdev->dev, "could not register the device\n"); @@ -567,10 +672,9 @@ static int sun4i_gpadc_probe(struct platform_device *pdev) return 0; err_map: - if (IS_ENABLED(CONFIG_THERMAL_OF)) + if (!info->no_irq && IS_ENABLED(CONFIG_THERMAL_OF)) iio_map_array_unregister(indio_dev); -err: pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); @@ -580,10 +684,11 @@ err: static int sun4i_gpadc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct sun4i_gpadc_iio *info = iio_priv(indio_dev); pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); - if (IS_ENABLED(CONFIG_THERMAL_OF)) + if (!info->no_irq && IS_ENABLED(CONFIG_THERMAL_OF)) iio_map_array_unregister(indio_dev); return 0; @@ -599,6 +704,7 @@ static const struct platform_device_id sun4i_gpadc_id[] = { static struct platform_driver sun4i_gpadc_driver = { .driver = { .name = "sun4i-gpadc-iio", + .of_match_table = sun4i_gpadc_of_id, .pm = &sun4i_gpadc_pm_ops, }, .id_table = sun4i_gpadc_id, |