diff options
Diffstat (limited to 'drivers/iio/adc/meson_saradc.c')
-rw-r--r-- | drivers/iio/adc/meson_saradc.c | 243 |
1 files changed, 213 insertions, 30 deletions
diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index 028ccd218f82..729becb2d3d9 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -18,6 +18,7 @@ #include <linux/io.h> #include <linux/iio/iio.h> #include <linux/module.h> +#include <linux/nvmem-consumer.h> #include <linux/interrupt.h> #include <linux/of.h> #include <linux/of_irq.h> @@ -165,6 +166,14 @@ #define MESON_SAR_ADC_MAX_FIFO_SIZE 32 #define MESON_SAR_ADC_TIMEOUT 100 /* ms */ +#define MESON_SAR_ADC_VOLTAGE_AND_TEMP_CHANNEL 6 +#define MESON_SAR_ADC_TEMP_OFFSET 27 + +/* temperature sensor calibration information in eFuse */ +#define MESON_SAR_ADC_EFUSE_BYTES 4 +#define MESON_SAR_ADC_EFUSE_BYTE3_UPPER_ADC_VAL GENMASK(6, 0) +#define MESON_SAR_ADC_EFUSE_BYTE3_IS_CALIBRATED BIT(7) + /* for use with IIO_VAL_INT_PLUS_MICRO */ #define MILLION 1000000 @@ -175,16 +184,25 @@ .address = _chan, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_AVERAGE_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ - BIT(IIO_CHAN_INFO_CALIBBIAS) | \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_CALIBBIAS) | \ BIT(IIO_CHAN_INFO_CALIBSCALE), \ .datasheet_name = "SAR_ADC_CH"#_chan, \ } -/* - * TODO: the hardware supports IIO_TEMP for channel 6 as well which is - * currently not supported by this driver. - */ +#define MESON_SAR_ADC_TEMP_CHAN(_chan) { \ + .type = IIO_TEMP, \ + .channel = _chan, \ + .address = MESON_SAR_ADC_VOLTAGE_AND_TEMP_CHANNEL, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_AVERAGE_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_CALIBBIAS) | \ + BIT(IIO_CHAN_INFO_CALIBSCALE), \ + .datasheet_name = "TEMP_SENSOR", \ +} + static const struct iio_chan_spec meson_sar_adc_iio_channels[] = { MESON_SAR_ADC_CHAN(0), MESON_SAR_ADC_CHAN(1), @@ -197,6 +215,19 @@ static const struct iio_chan_spec meson_sar_adc_iio_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(8), }; +static const struct iio_chan_spec meson_sar_adc_and_temp_iio_channels[] = { + MESON_SAR_ADC_CHAN(0), + MESON_SAR_ADC_CHAN(1), + MESON_SAR_ADC_CHAN(2), + MESON_SAR_ADC_CHAN(3), + MESON_SAR_ADC_CHAN(4), + MESON_SAR_ADC_CHAN(5), + MESON_SAR_ADC_CHAN(6), + MESON_SAR_ADC_CHAN(7), + MESON_SAR_ADC_TEMP_CHAN(8), + IIO_CHAN_SOFT_TIMESTAMP(9), +}; + enum meson_sar_adc_avg_mode { NO_AVERAGING = 0x0, MEAN_AVERAGING = 0x1, @@ -225,6 +256,9 @@ struct meson_sar_adc_param { u32 bandgap_reg; unsigned int resolution; const struct regmap_config *regmap_config; + u8 temperature_trimming_bits; + unsigned int temperature_multiplier; + unsigned int temperature_divider; }; struct meson_sar_adc_data { @@ -246,6 +280,9 @@ struct meson_sar_adc_priv { struct completion done; int calibbias; int calibscale; + bool temperature_sensor_calibrated; + u8 temperature_sensor_coefficient; + u16 temperature_sensor_adc_val; }; static const struct regmap_config meson_sar_adc_regmap_config_gxbb = { @@ -389,9 +426,16 @@ static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev, MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK, regval); - if (chan->address == 6) - regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, - MESON_SAR_ADC_DELTA_10_TEMP_SEL, 0); + if (chan->address == MESON_SAR_ADC_VOLTAGE_AND_TEMP_CHANNEL) { + if (chan->type == IIO_TEMP) + regval = MESON_SAR_ADC_DELTA_10_TEMP_SEL; + else + regval = 0; + + regmap_update_bits(priv->regmap, + MESON_SAR_ADC_DELTA_10, + MESON_SAR_ADC_DELTA_10_TEMP_SEL, regval); + } } static void meson_sar_adc_set_chan7_mux(struct iio_dev *indio_dev, @@ -506,8 +550,12 @@ static int meson_sar_adc_get_sample(struct iio_dev *indio_dev, enum meson_sar_adc_num_samples avg_samples, int *val) { + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); int ret; + if (chan->type == IIO_TEMP && !priv->temperature_sensor_calibrated) + return -ENOTSUPP; + ret = meson_sar_adc_lock(indio_dev); if (ret) return ret; @@ -555,17 +603,31 @@ static int meson_sar_adc_iio_info_read_raw(struct iio_dev *indio_dev, break; case IIO_CHAN_INFO_SCALE: - ret = regulator_get_voltage(priv->vref); - if (ret < 0) { - dev_err(indio_dev->dev.parent, - "failed to get vref voltage: %d\n", ret); - return ret; + if (chan->type == IIO_VOLTAGE) { + ret = regulator_get_voltage(priv->vref); + if (ret < 0) { + dev_err(indio_dev->dev.parent, + "failed to get vref voltage: %d\n", + ret); + return ret; + } + + *val = ret / 1000; + *val2 = priv->param->resolution; + return IIO_VAL_FRACTIONAL_LOG2; + } else if (chan->type == IIO_TEMP) { + /* SoC specific multiplier and divider */ + *val = priv->param->temperature_multiplier; + *val2 = priv->param->temperature_divider; + + /* celsius to millicelsius */ + *val *= 1000; + + return IIO_VAL_FRACTIONAL; + } else { + return -EINVAL; } - *val = ret / 1000; - *val2 = priv->param->resolution; - return IIO_VAL_FRACTIONAL_LOG2; - case IIO_CHAN_INFO_CALIBBIAS: *val = priv->calibbias; return IIO_VAL_INT; @@ -575,6 +637,13 @@ static int meson_sar_adc_iio_info_read_raw(struct iio_dev *indio_dev, *val2 = priv->calibscale % MILLION; return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OFFSET: + *val = DIV_ROUND_CLOSEST(MESON_SAR_ADC_TEMP_OFFSET * + priv->param->temperature_divider, + priv->param->temperature_multiplier); + *val -= priv->temperature_sensor_adc_val; + return IIO_VAL_INT; + default: return -EINVAL; } @@ -587,8 +656,11 @@ static int meson_sar_adc_clk_init(struct iio_dev *indio_dev, struct clk_init_data init; const char *clk_parents[1]; - init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%pOF#adc_div", - indio_dev->dev.of_node); + init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%s#adc_div", + dev_name(indio_dev->dev.parent)); + if (!init.name) + return -ENOMEM; + init.flags = 0; init.ops = &clk_divider_ops; clk_parents[0] = __clk_get_name(priv->clkin); @@ -606,8 +678,11 @@ static int meson_sar_adc_clk_init(struct iio_dev *indio_dev, if (WARN_ON(IS_ERR(priv->adc_div_clk))) return PTR_ERR(priv->adc_div_clk); - init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%pOF#adc_en", - indio_dev->dev.of_node); + init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%s#adc_en", + dev_name(indio_dev->dev.parent)); + if (!init.name) + return -ENOMEM; + init.flags = CLK_SET_RATE_PARENT; init.ops = &clk_gate_ops; clk_parents[0] = __clk_get_name(priv->adc_div_clk); @@ -625,6 +700,65 @@ static int meson_sar_adc_clk_init(struct iio_dev *indio_dev, return 0; } +static int meson_sar_adc_temp_sensor_init(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + u8 *buf, trimming_bits, trimming_mask, upper_adc_val; + struct nvmem_cell *temperature_calib; + size_t read_len; + int ret; + + temperature_calib = devm_nvmem_cell_get(&indio_dev->dev, + "temperature_calib"); + if (IS_ERR(temperature_calib)) { + ret = PTR_ERR(temperature_calib); + + /* + * leave the temperature sensor disabled if no calibration data + * was passed via nvmem-cells. + */ + if (ret == -ENODEV) + return 0; + + if (ret != -EPROBE_DEFER) + dev_err(indio_dev->dev.parent, + "failed to get temperature_calib cell\n"); + + return ret; + } + + read_len = MESON_SAR_ADC_EFUSE_BYTES; + buf = nvmem_cell_read(temperature_calib, &read_len); + if (IS_ERR(buf)) { + dev_err(indio_dev->dev.parent, + "failed to read temperature_calib cell\n"); + return PTR_ERR(buf); + } else if (read_len != MESON_SAR_ADC_EFUSE_BYTES) { + kfree(buf); + dev_err(indio_dev->dev.parent, + "invalid read size of temperature_calib cell\n"); + return -EINVAL; + } + + trimming_bits = priv->param->temperature_trimming_bits; + trimming_mask = BIT(trimming_bits) - 1; + + priv->temperature_sensor_calibrated = + buf[3] & MESON_SAR_ADC_EFUSE_BYTE3_IS_CALIBRATED; + priv->temperature_sensor_coefficient = buf[2] & trimming_mask; + + upper_adc_val = FIELD_GET(MESON_SAR_ADC_EFUSE_BYTE3_UPPER_ADC_VAL, + buf[3]); + + priv->temperature_sensor_adc_val = buf[2]; + priv->temperature_sensor_adc_val |= upper_adc_val << BITS_PER_BYTE; + priv->temperature_sensor_adc_val >>= trimming_bits; + + kfree(buf); + + return 0; +} + static int meson_sar_adc_init(struct iio_dev *indio_dev) { struct meson_sar_adc_priv *priv = iio_priv(indio_dev); @@ -649,10 +783,12 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev) meson_sar_adc_stop_sample_engine(indio_dev); - /* update the channel 6 MUX to select the temperature sensor */ + /* + * disable this bit as seems to be only relevant for Meson6 (based + * on the vendor driver), which we don't support at the moment. + */ regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, - MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL, - MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL); + MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL, 0); /* disable all channels by default */ regmap_write(priv->regmap, MESON_SAR_ADC_CHAN_LIST, 0x0); @@ -709,6 +845,29 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev) regval |= MESON_SAR_ADC_AUX_SW_XP_DRIVE_SW; regmap_write(priv->regmap, MESON_SAR_ADC_AUX_SW, regval); + if (priv->temperature_sensor_calibrated) { + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, + MESON_SAR_ADC_DELTA_10_TS_REVE1, + MESON_SAR_ADC_DELTA_10_TS_REVE1); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, + MESON_SAR_ADC_DELTA_10_TS_REVE0, + MESON_SAR_ADC_DELTA_10_TS_REVE0); + + /* + * set bits [3:0] of the TSC (temperature sensor coefficient) + * to get the correct values when reading the temperature. + */ + regval = FIELD_PREP(MESON_SAR_ADC_DELTA_10_TS_C_MASK, + priv->temperature_sensor_coefficient); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, + MESON_SAR_ADC_DELTA_10_TS_C_MASK, regval); + } else { + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, + MESON_SAR_ADC_DELTA_10_TS_REVE1, 0); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, + MESON_SAR_ADC_DELTA_10_TS_REVE0, 0); + } + ret = clk_set_parent(priv->adc_sel_clk, priv->clkin); if (ret) { dev_err(indio_dev->dev.parent, @@ -894,6 +1053,17 @@ static const struct meson_sar_adc_param meson_sar_adc_meson8_param = { .bandgap_reg = MESON_SAR_ADC_DELTA_10, .regmap_config = &meson_sar_adc_regmap_config_meson8, .resolution = 10, + .temperature_trimming_bits = 4, + .temperature_multiplier = 18 * 10000, + .temperature_divider = 1024 * 10 * 85, +}; + +static const struct meson_sar_adc_param meson_sar_adc_meson8b_param = { + .has_bl30_integration = false, + .clock_rate = 1150000, + .bandgap_reg = MESON_SAR_ADC_DELTA_10, + .regmap_config = &meson_sar_adc_regmap_config_meson8, + .resolution = 10, }; static const struct meson_sar_adc_param meson_sar_adc_gxbb_param = { @@ -918,12 +1088,12 @@ static const struct meson_sar_adc_data meson_sar_adc_meson8_data = { }; static const struct meson_sar_adc_data meson_sar_adc_meson8b_data = { - .param = &meson_sar_adc_meson8_param, + .param = &meson_sar_adc_meson8b_param, .name = "meson-meson8b-saradc", }; static const struct meson_sar_adc_data meson_sar_adc_meson8m2_data = { - .param = &meson_sar_adc_meson8_param, + .param = &meson_sar_adc_meson8b_param, .name = "meson-meson8m2-saradc", }; @@ -1009,9 +1179,6 @@ static int meson_sar_adc_probe(struct platform_device *pdev) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &meson_sar_adc_iio_info; - indio_dev->channels = meson_sar_adc_iio_channels; - indio_dev->num_channels = ARRAY_SIZE(meson_sar_adc_iio_channels); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) @@ -1078,6 +1245,22 @@ static int meson_sar_adc_probe(struct platform_device *pdev) priv->calibscale = MILLION; + if (priv->param->temperature_trimming_bits) { + ret = meson_sar_adc_temp_sensor_init(indio_dev); + if (ret) + return ret; + } + + if (priv->temperature_sensor_calibrated) { + indio_dev->channels = meson_sar_adc_and_temp_iio_channels; + indio_dev->num_channels = + ARRAY_SIZE(meson_sar_adc_and_temp_iio_channels); + } else { + indio_dev->channels = meson_sar_adc_iio_channels; + indio_dev->num_channels = + ARRAY_SIZE(meson_sar_adc_iio_channels); + } + ret = meson_sar_adc_init(indio_dev); if (ret) goto err; |