OSDN Git Service

iio: adc: aspeed: Add compensation phase.
authorBilly Tsai <billy_tsai@aspeedtech.com>
Wed, 22 Sep 2021 08:15:18 +0000 (16:15 +0800)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Tue, 19 Oct 2021 07:27:32 +0000 (08:27 +0100)
This patch adds a compensation phase to improve the accuracy of ADC
measurement. This is the built-in function through input half of the
reference voltage to get the ADC offset.

Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
Link: https://lore.kernel.org/r/20210922081520.30580-10-billy_tsai@aspeedtech.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
drivers/iio/adc/aspeed_adc.c

index 0b21600..e10f445 100644 (file)
@@ -103,6 +103,7 @@ struct aspeed_adc_data {
        struct reset_control    *rst;
        int                     vref_mv;
        u32                     sample_period_ns;
+       int                     cv;
 };
 
 #define ASPEED_CHAN(_idx, _data_reg_addr) {                    \
@@ -112,7 +113,8 @@ struct aspeed_adc_data {
        .address = (_data_reg_addr),                            \
        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
        .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |  \
-                               BIT(IIO_CHAN_INFO_SAMP_FREQ),   \
+                               BIT(IIO_CHAN_INFO_SAMP_FREQ) |  \
+                               BIT(IIO_CHAN_INFO_OFFSET),      \
 }
 
 static const struct iio_chan_spec aspeed_adc_iio_channels[] = {
@@ -134,6 +136,51 @@ static const struct iio_chan_spec aspeed_adc_iio_channels[] = {
        ASPEED_CHAN(15, 0x2E),
 };
 
+static int aspeed_adc_compensation(struct iio_dev *indio_dev)
+{
+       struct aspeed_adc_data *data = iio_priv(indio_dev);
+       u32 index, adc_raw = 0;
+       u32 adc_engine_control_reg_val;
+
+       adc_engine_control_reg_val =
+               readl(data->base + ASPEED_REG_ENGINE_CONTROL);
+       adc_engine_control_reg_val &= ~ASPEED_ADC_OP_MODE;
+       adc_engine_control_reg_val |=
+               (FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) |
+                ASPEED_ADC_ENGINE_ENABLE);
+       /*
+        * Enable compensating sensing:
+        * After that, the input voltage of ADC will force to half of the reference
+        * voltage. So the expected reading raw data will become half of the max
+        * value. We can get compensating value = 0x200 - ADC read raw value.
+        * It is recommended to average at least 10 samples to get a final CV.
+        */
+       writel(adc_engine_control_reg_val | ASPEED_ADC_CTRL_COMPENSATION |
+                      ASPEED_ADC_CTRL_CHANNEL_ENABLE(0),
+              data->base + ASPEED_REG_ENGINE_CONTROL);
+       /*
+        * After enable compensating sensing mode need to wait some time for ADC stable
+        * Experiment result is 1ms.
+        */
+       mdelay(1);
+
+       for (index = 0; index < 16; index++) {
+               /*
+                * Waiting for the sampling period ensures that the value acquired
+                * is fresh each time.
+                */
+               ndelay(data->sample_period_ns);
+               adc_raw += readw(data->base + aspeed_adc_iio_channels[0].address);
+       }
+       adc_raw >>= 4;
+       data->cv = BIT(ASPEED_RESOLUTION_BITS - 1) - adc_raw;
+       writel(adc_engine_control_reg_val,
+              data->base + ASPEED_REG_ENGINE_CONTROL);
+       dev_dbg(data->dev, "Compensating value = %d\n", data->cv);
+
+       return 0;
+}
+
 static int aspeed_adc_set_sampling_rate(struct iio_dev *indio_dev, u32 rate)
 {
        struct aspeed_adc_data *data = iio_priv(indio_dev);
@@ -163,6 +210,10 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev,
                *val = readw(data->base + chan->address);
                return IIO_VAL_INT;
 
+       case IIO_CHAN_INFO_OFFSET:
+               *val = data->cv;
+               return IIO_VAL_INT;
+
        case IIO_CHAN_INFO_SCALE:
                *val = data->vref_mv;
                *val2 = ASPEED_RESOLUTION_BITS;
@@ -447,6 +498,7 @@ static int aspeed_adc_probe(struct platform_device *pdev)
                        return ret;
        }
 
+       aspeed_adc_compensation(indio_dev);
        /* Start all channels in normal mode. */
        adc_engine_control_reg_val =
                readl(data->base + ASPEED_REG_ENGINE_CONTROL);