[PATCH v3 3/5] iio: adc: sun4i-gpadc-iio: add support for H3 thermal sensor

Jonathan Cameron jic23 at kernel.org
Sun Jul 23 09:35:11 PDT 2017


On Sun, 23 Jul 2017 22:13:52 +0800
Icenowy Zheng <icenowy at aosc.io> wrote:

> This adds support for the Allwinner H3 thermal sensor.
> 
> Allwinner H3 has a thermal sensor like the one in A33, but have its
> registers nearly all re-arranged, sample clock moved to CCU and a pair
> of bus clock and reset added. It's also the base of newer SoCs' thermal
> sensors.
> 
> Some new options is added to gpadc_data struct, to mark the difference
> between the old GPADCs and THS's and the new THS's.
> 
> Thermal sampling via interrupts are still not supported, and polling
> is used instead.
> 
> The thermal sensors on A64 and H5 is like the one on H3, but with of
> course different formula factors.
> 
> Signed-off-by: Icenowy Zheng <icenowy at aosc.io>
Looks good to me. Just need to give Quentin time to look at it and
*fingers crossed* I should be able to pick up in a few days.

Jonathan 
> ---
> Changes in v3:
> - Clock name changes.
> - Fixed some small issues pointed out by Quentin.
> 
>  drivers/iio/adc/sun4i-gpadc-iio.c | 228 +++++++++++++++++++++++++++++++-------
>  include/linux/mfd/sun4i-gpadc.h   |  27 +++++
>  2 files changed, 215 insertions(+), 40 deletions(-)
> 
> diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
> index 41769bc6a429..5c79ba4d5ef5 100644
> --- a/drivers/iio/adc/sun4i-gpadc-iio.c
> +++ b/drivers/iio/adc/sun4i-gpadc-iio.c
> @@ -22,6 +22,7 @@
>   * shutdown for not being used.
>   */
>  
> +#include <linux/clk.h>
>  #include <linux/completion.h>
>  #include <linux/interrupt.h>
>  #include <linux/io.h>
> @@ -31,6 +32,7 @@
>  #include <linux/platform_device.h>
>  #include <linux/pm_runtime.h>
>  #include <linux/regmap.h>
> +#include <linux/reset.h>
>  #include <linux/thermal.h>
>  #include <linux/delay.h>
>  
> @@ -49,6 +51,8 @@ static unsigned int sun6i_gpadc_chan_select(unsigned int chan)
>  	return SUN6I_GPADC_CTRL1_ADC_CHAN_SELECT(chan);
>  }
>  
> +struct sun4i_gpadc_iio;
> +
>  struct gpadc_data {
>  	int		temp_offset;
>  	int		temp_scale;
> @@ -56,39 +60,12 @@ struct gpadc_data {
>  	unsigned int	tp_adc_select;
>  	unsigned int	(*adc_chan_select)(unsigned int chan);
>  	unsigned int	adc_chan_mask;
> -};
> -
> -static const struct gpadc_data sun4i_gpadc_data = {
> -	.temp_offset = -1932,
> -	.temp_scale = 133,
> -	.tp_mode_en = SUN4I_GPADC_CTRL1_TP_MODE_EN,
> -	.tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
> -	.adc_chan_select = &sun4i_gpadc_chan_select,
> -	.adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
> -};
> -
> -static const struct gpadc_data sun5i_gpadc_data = {
> -	.temp_offset = -1447,
> -	.temp_scale = 100,
> -	.tp_mode_en = SUN4I_GPADC_CTRL1_TP_MODE_EN,
> -	.tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
> -	.adc_chan_select = &sun4i_gpadc_chan_select,
> -	.adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
> -};
> -
> -static const struct gpadc_data sun6i_gpadc_data = {
> -	.temp_offset = -1623,
> -	.temp_scale = 167,
> -	.tp_mode_en = SUN6I_GPADC_CTRL1_TP_MODE_EN,
> -	.tp_adc_select = SUN6I_GPADC_CTRL1_TP_ADC_SELECT,
> -	.adc_chan_select = &sun6i_gpadc_chan_select,
> -	.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_A23_GPADC_CTRL1_CHOP_TEMP_EN,
> +	unsigned int	temp_data;
> +	int		(*sample_start)(struct sun4i_gpadc_iio *info);
> +	int		(*sample_end)(struct sun4i_gpadc_iio *info);
> +	bool		has_bus_clk;
> +	bool		has_bus_rst;
> +	bool		has_mod_clk;
>  };
>  
>  struct sun4i_gpadc_iio {
> @@ -103,6 +80,9 @@ struct sun4i_gpadc_iio {
>  	atomic_t			ignore_temp_data_irq;
>  	const struct gpadc_data		*data;
>  	bool				no_irq;
> +	struct clk			*ths_bus_clk;
> +	struct clk			*mod_clk;
> +	struct reset_control		*reset;
>  	/* prevents concurrent reads of temperature and ADC */
>  	struct mutex			mutex;
>  	struct thermal_zone_device	*tzd;
> @@ -276,7 +256,7 @@ static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val)
>  	if (info->no_irq) {
>  		pm_runtime_get_sync(indio_dev->dev.parent);
>  
> -		regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, val);
> +		regmap_read(info->regmap, info->data->temp_data, val);
>  
>  		pm_runtime_mark_last_busy(indio_dev->dev.parent);
>  		pm_runtime_put_autosuspend(indio_dev->dev.parent);
> @@ -384,10 +364,8 @@ static irqreturn_t sun4i_gpadc_fifo_data_irq_handler(int irq, void *dev_id)
>  	return IRQ_HANDLED;
>  }
>  
> -static int sun4i_gpadc_runtime_suspend(struct device *dev)
> +static int sun4i_gpadc_sample_end(struct sun4i_gpadc_iio *info)
>  {
> -	struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
> -
>  	/* Disable the ADC on IP */
>  	regmap_write(info->regmap, SUN4I_GPADC_CTRL1, 0);
>  	/* Disable temperature sensor on IP */
> @@ -396,10 +374,23 @@ static int sun4i_gpadc_runtime_suspend(struct device *dev)
>  	return 0;
>  }
>  
> -static int sun4i_gpadc_runtime_resume(struct device *dev)
> +static int sun8i_h3_gpadc_sample_end(struct sun4i_gpadc_iio *info)
> +{
> +	/* Disable temperature sensor */
> +	regmap_write(info->regmap, SUN8I_H3_GPADC_CTRL2, 0);
> +
> +	return 0;
> +}
> +
> +static int sun4i_gpadc_runtime_suspend(struct device *dev)
>  {
>  	struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
>  
> +	return info->data->sample_end(info);
> +}
> +
> +static int sun4i_gpadc_sample_start(struct sun4i_gpadc_iio *info)
> +{
>  	/* clkin = 6MHz */
>  	regmap_write(info->regmap, SUN4I_GPADC_CTRL0,
>  		     SUN4I_GPADC_CTRL0_ADC_CLK_DIVIDER(2) |
> @@ -417,6 +408,29 @@ static int sun4i_gpadc_runtime_resume(struct device *dev)
>  	return 0;
>  }
>  
> +static int sun8i_h3_gpadc_sample_start(struct sun4i_gpadc_iio *info)
> +{
> +	regmap_write(info->regmap, SUN8I_H3_GPADC_CTRL2,
> +		     SUN8I_H3_GPADC_CTRL2_TEMP_SENSE_EN |
> +		     SUN8I_H3_GPADC_CTRL2_T_ACQ1(31));
> +	regmap_write(info->regmap, SUN4I_GPADC_CTRL0,
> +		     SUN4I_GPADC_CTRL0_T_ACQ(31));
> +	regmap_write(info->regmap, SUN8I_H3_GPADC_CTRL3,
> +		     SUN4I_GPADC_CTRL3_FILTER_EN |
> +		     SUN4I_GPADC_CTRL3_FILTER_TYPE(1));
> +	regmap_write(info->regmap, SUN8I_H3_GPADC_INTC,
> +		     SUN8I_H3_GPADC_INTC_TEMP_PERIOD(800));
> +
> +	return 0;
> +}
> +
> +static int sun4i_gpadc_runtime_resume(struct device *dev)
> +{
> +	struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
> +
> +	return info->data->sample_start(info);
> +}
> +
>  static int sun4i_gpadc_get_temp(void *data, int *temp)
>  {
>  	struct sun4i_gpadc_iio *info = data;
> @@ -491,11 +505,78 @@ static int sun4i_irq_init(struct platform_device *pdev, const char *name,
>  	return 0;
>  }
>  
> +static const struct gpadc_data sun4i_gpadc_data = {
> +	.temp_offset = -1932,
> +	.temp_scale = 133,
> +	.tp_mode_en = SUN4I_GPADC_CTRL1_TP_MODE_EN,
> +	.tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
> +	.adc_chan_select = &sun4i_gpadc_chan_select,
> +	.adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
> +	.temp_data = SUN4I_GPADC_TEMP_DATA,
> +	.sample_start = sun4i_gpadc_sample_start,
> +	.sample_end = sun4i_gpadc_sample_end,
> +};
> +
> +static const struct gpadc_data sun5i_gpadc_data = {
> +	.temp_offset = -1447,
> +	.temp_scale = 100,
> +	.tp_mode_en = SUN4I_GPADC_CTRL1_TP_MODE_EN,
> +	.tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
> +	.adc_chan_select = &sun4i_gpadc_chan_select,
> +	.adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
> +	.temp_data = SUN4I_GPADC_TEMP_DATA,
> +	.sample_start = sun4i_gpadc_sample_start,
> +	.sample_end = sun4i_gpadc_sample_end,
> +};
> +
> +static const struct gpadc_data sun6i_gpadc_data = {
> +	.temp_offset = -1623,
> +	.temp_scale = 167,
> +	.tp_mode_en = SUN6I_GPADC_CTRL1_TP_MODE_EN,
> +	.tp_adc_select = SUN6I_GPADC_CTRL1_TP_ADC_SELECT,
> +	.adc_chan_select = &sun6i_gpadc_chan_select,
> +	.adc_chan_mask = SUN6I_GPADC_CTRL1_ADC_CHAN_MASK,
> +	.temp_data = SUN4I_GPADC_TEMP_DATA,
> +	.sample_start = sun4i_gpadc_sample_start,
> +	.sample_end = sun4i_gpadc_sample_end,
> +};
> +
> +static const struct gpadc_data sun8i_a33_gpadc_data = {
> +	.temp_offset = -1662,
> +	.temp_scale = 162,
> +	.tp_mode_en = SUN8I_A23_GPADC_CTRL1_CHOP_TEMP_EN,
> +	.temp_data = SUN4I_GPADC_TEMP_DATA,
> +	.sample_start = sun4i_gpadc_sample_start,
> +	.sample_end = sun4i_gpadc_sample_end,
> +};
> +
> +static const struct gpadc_data sun8i_h3_gpadc_data = {
> +	/*
> +	 * The original formula on the datasheet seems to be wrong.
> +	 * These factors are calculated based on the formula in the BSP
> +	 * kernel, which is originally Tem = 217 - (T / 8.253), in which Tem
> +	 * is the temperature in Celsius degree and T is the raw value
> +	 * from the sensor.
> +	 */
> +	.temp_offset = -1791,
> +	.temp_scale = -121,
> +	.temp_data = SUN8I_H3_GPADC_TEMP_DATA,
> +	.sample_start = sun8i_h3_gpadc_sample_start,
> +	.sample_end = sun8i_h3_gpadc_sample_end,
> +	.has_bus_clk = true,
> +	.has_bus_rst = true,
> +	.has_mod_clk = true,
> +};
> +
>  static const struct of_device_id sun4i_gpadc_of_id[] = {
>  	{
>  		.compatible = "allwinner,sun8i-a33-ths",
>  		.data = &sun8i_a33_gpadc_data,
>  	},
> +	{
> +		.compatible = "allwinner,sun8i-h3-ths",
> +		.data = &sun8i_h3_gpadc_data,
> +	},
>  	{ /* sentinel */ }
>  };
>  
> @@ -530,17 +611,75 @@ static int sun4i_gpadc_probe_dt(struct platform_device *pdev,
>  		return ret;
>  	}
>  
> +	if (info->data->has_bus_rst) {
> +		info->reset = devm_reset_control_get(&pdev->dev, NULL);
> +		if (IS_ERR(info->reset)) {
> +			ret = PTR_ERR(info->reset);
> +			return ret;
> +		}
> +
> +		ret = reset_control_deassert(info->reset);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (info->data->has_bus_clk) {
> +		info->ths_bus_clk = devm_clk_get(&pdev->dev, "bus");
> +		if (IS_ERR(info->ths_bus_clk)) {
> +			ret = PTR_ERR(info->ths_bus_clk);
> +			goto assert_reset;
> +		}
> +
> +		ret = clk_prepare_enable(info->ths_bus_clk);
> +		if (ret)
> +			goto assert_reset;
> +	}
> +
> +	if (info->data->has_mod_clk) {
> +		info->mod_clk = devm_clk_get(&pdev->dev, "mod");
> +		if (IS_ERR(info->mod_clk)) {
> +			ret = PTR_ERR(info->mod_clk);
> +			goto disable_bus_clk;
> +		}
> +
> +		/* Running at 6MHz */
> +		ret = clk_set_rate(info->mod_clk, 6000000);
> +		if (ret)
> +			goto disable_bus_clk;
> +
> +		ret = clk_prepare_enable(info->mod_clk);
> +		if (ret)
> +			goto disable_bus_clk;
> +	}
> +
>  	if (!IS_ENABLED(CONFIG_THERMAL_OF))
>  		return 0;
>  
>  	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))
> +	if (IS_ERR(info->tzd)) {
>  		dev_err(&pdev->dev, "could not register thermal sensor: %ld\n",
>  			PTR_ERR(info->tzd));
> +		ret = PTR_ERR(info->tzd);
> +		goto disable_mod_clk;
> +	}
> +
> +	return 0;
> +
> +disable_mod_clk:
> +	if (info->data->has_mod_clk)
> +		clk_disable_unprepare(info->mod_clk);
> +
> +disable_bus_clk:
> +	if (info->data->has_bus_clk)
> +		clk_disable_unprepare(info->ths_bus_clk);
> +
> +assert_reset:
> +	if (info->data->has_bus_rst)
> +		reset_control_assert(info->reset);
>  
> -	return PTR_ERR_OR_ZERO(info->tzd);
> +	return ret;
>  }
>  
>  static int sun4i_gpadc_probe_mfd(struct platform_device *pdev,
> @@ -699,6 +838,15 @@ static int sun4i_gpadc_remove(struct platform_device *pdev)
>  	if (!info->no_irq)
>  		iio_map_array_unregister(indio_dev);
>  
> +	if (info->data->has_mod_clk)
> +		clk_disable_unprepare(info->mod_clk);
> +
> +	if (info->data->has_bus_clk)
> +		clk_disable_unprepare(info->ths_bus_clk);
> +
> +	if (info->data->has_bus_rst)
> +		reset_control_assert(info->reset);
> +
>  	return 0;
>  }
>  
> diff --git a/include/linux/mfd/sun4i-gpadc.h b/include/linux/mfd/sun4i-gpadc.h
> index d31d962bb7d8..f794a2988a93 100644
> --- a/include/linux/mfd/sun4i-gpadc.h
> +++ b/include/linux/mfd/sun4i-gpadc.h
> @@ -42,6 +42,9 @@
>  #define SUN8I_A23_GPADC_CTRL1_CHOP_TEMP_EN		BIT(8)
>  #define SUN8I_A23_GPADC_CTRL1_GPADC_CALI_EN		BIT(7)
>  
> +/* TP_CTRL1 bits for SoCs after H3 */
> +#define SUN8I_H3_GPADC_CTRL1_GPADC_CALI_EN		BIT(17)
> +
>  #define SUN4I_GPADC_CTRL2				0x08
>  
>  #define SUN4I_GPADC_CTRL2_TP_SENSITIVE_ADJUST(x)	((GENMASK(3, 0) & (x)) << 28)
> @@ -49,7 +52,17 @@
>  #define SUN4I_GPADC_CTRL2_PRE_MEA_EN			BIT(24)
>  #define SUN4I_GPADC_CTRL2_PRE_MEA_THRE_CNT(x)		(GENMASK(23, 0) & (x))
>  
> +#define SUN8I_H3_GPADC_CTRL2				0x40
> +
> +#define SUN8I_H3_GPADC_CTRL2_TEMP_SENSE_EN		BIT(0)
> +#define SUN8I_H3_GPADC_CTRL2_T_ACQ1(x)			((GENMASK(15, 0) * (x)) << 16)
> +
>  #define SUN4I_GPADC_CTRL3				0x0c
> +/*
> + * This register is named "Average filter Control Register" in H3 Datasheet,
> + * but the register's definition is the same as the old CTRL3 register.
> + */
> +#define SUN8I_H3_GPADC_CTRL3				0x70
>  
>  #define SUN4I_GPADC_CTRL3_FILTER_EN			BIT(2)
>  #define SUN4I_GPADC_CTRL3_FILTER_TYPE(x)		(GENMASK(1, 0) & (x))
> @@ -71,6 +84,13 @@
>  #define SUN4I_GPADC_INT_FIFOC_TP_UP_IRQ_EN		BIT(1)
>  #define SUN4I_GPADC_INT_FIFOC_TP_DOWN_IRQ_EN		BIT(0)
>  
> +#define SUN8I_H3_GPADC_INTC				0x44
> +
> +#define SUN8I_H3_GPADC_INTC_TEMP_PERIOD(x)		((GENMASK(19, 0) & (x)) << 12)
> +#define SUN8I_H3_GPADC_INTC_TEMP_DATA			BIT(8)
> +#define SUN8I_H3_GPADC_INTC_TEMP_SHUT			BIT(4)
> +#define SUN8I_H3_GPADC_INTC_TEMP_ALARM			BIT(0)
> +
>  #define SUN4I_GPADC_INT_FIFOS				0x14
>  
>  #define SUN4I_GPADC_INT_FIFOS_TEMP_DATA_PENDING		BIT(18)
> @@ -80,9 +100,16 @@
>  #define SUN4I_GPADC_INT_FIFOS_TP_UP_PENDING		BIT(1)
>  #define SUN4I_GPADC_INT_FIFOS_TP_DOWN_PENDING		BIT(0)
>  
> +#define SUN8I_H3_GPADC_INTS				0x44
> +
> +#define SUN8I_H3_GPADC_INTS_TEMP_DATA			BIT(8)
> +#define SUN8I_H3_GPADC_INTS_TEMP_SHUT			BIT(4)
> +#define SUN8I_H3_GPADC_INTS_TEMP_ALARM			BIT(0)
> +
>  #define SUN4I_GPADC_CDAT				0x1c
>  #define SUN4I_GPADC_TEMP_DATA				0x20
>  #define SUN4I_GPADC_DATA				0x24
> +#define SUN8I_H3_GPADC_TEMP_DATA			0x80
>  
>  #define SUN4I_GPADC_IRQ_FIFO_DATA			0
>  #define SUN4I_GPADC_IRQ_TEMP_DATA			1




More information about the linux-arm-kernel mailing list