diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c index 137f577d..b0b231da 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 #include #include #include @@ -31,6 +32,7 @@ #include #include #include +#include #include #include @@ -49,46 +51,22 @@ 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; + int temp_divider; unsigned int tp_mode_en; 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_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 +81,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; @@ -277,7 +258,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); @@ -306,6 +287,15 @@ static int sun4i_gpadc_temp_scale(struct iio_dev *indio_dev, int *val) return 0; } +static int sun4i_gpadc_temp_divider(struct iio_dev *indio_dev, int *val) +{ + struct sun4i_gpadc_iio *info = iio_priv(indio_dev); + + *val = info->data->temp_divider; + + return 0; +} + static int sun4i_gpadc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -383,10 +373,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 */ @@ -395,10 +383,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) | @@ -416,18 +417,52 @@ 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); +} + +/* temperature = ( MINUPA - reg * MULPA) / DIVPA +#define MULPA 25000 +#define DIVPA 214 +#define MINUPA 2170 +static int sun50_th_reg_to_temp(uint32_t reg_data) +{ + return ((MINUPA - (int)reg_data) * MULPA) / DIVPA; +} +*/ + static int sun4i_gpadc_get_temp(void *data, int *temp) { struct sun4i_gpadc_iio *info = data; - int val, scale, offset; + int val, scale, offset, divider; if (sun4i_gpadc_temp_read(info->indio_dev, &val)) return -ETIMEDOUT; sun4i_gpadc_temp_scale(info->indio_dev, &scale); sun4i_gpadc_temp_offset(info->indio_dev, &offset); + sun4i_gpadc_temp_divider(info->indio_dev, ÷r); - *temp = (val + offset) * scale; + *temp = ((val + offset) * scale) / divider; return 0; } @@ -490,11 +525,116 @@ 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, + .temp_divider = 1, + .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, + .temp_divider = 1, + .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, + .temp_divider = 1, + .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, + .temp_divider = 1, + .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_divider = 1, + .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 gpadc_data sun50i_a64_gpadc_data = { + .temp_offset = -2170, + .temp_scale = -1000, + .temp_divider = 8560, + .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 gpadc_data sun50i_h5_gpadc_data = { + /* Not done for now since requires 2 sets of offset+scale */ + .temp_offset = -1321, + .temp_scale = -121, + .temp_divider = 1, + .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, + }, + { + .compatible = "allwinner,sun50i-a64-ths", + .data = &sun50i_a64_gpadc_data, + }, + { + .compatible = "allwinner,sun50i-h5-ths", + .data = &sun50i_h5_gpadc_data, + }, { /* sentinel */ } }; @@ -529,17 +669,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 PTR_ERR_OR_ZERO(info->tzd); + 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 ret; } static int sun4i_gpadc_probe_mfd(struct platform_device *pdev, @@ -698,6 +896,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 139872c2..f794a298 100644 --- a/include/linux/mfd/sun4i-gpadc.h +++ b/include/linux/mfd/sun4i-gpadc.h @@ -38,9 +38,12 @@ #define SUN6I_GPADC_CTRL1_ADC_CHAN_SELECT(x) (GENMASK(3, 0) & BIT(x)) #define SUN6I_GPADC_CTRL1_ADC_CHAN_MASK GENMASK(3, 0) -/* TP_CTRL1 bits for sun8i SoCs */ -#define SUN8I_GPADC_CTRL1_CHOP_TEMP_EN BIT(8) -#define SUN8I_GPADC_CTRL1_GPADC_CALI_EN BIT(7) +/* TP_CTRL1 bits for sun8i A23/A33 SoCs */ +#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 @@ -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