| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * Copyright (c) 2019 Amlogic, Inc. All rights reserved. |
| */ |
| |
| #include "meson_saradc.h" |
| |
| #define MESON_C2_SAR_ADC_FIFO_RD 0x18 |
| #define MESON_C2_SAR_ADC_FIFO_RD_CHAN_ID_MASK GENMASK(24, 22) |
| #define MESON_C2_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK GENMASK(21, 0) |
| |
| #define MESON_C2_SAR_ADC_DELTA_10 0x28 |
| |
| #define MESON_C2_SAR_ADC_REG11 0x2c |
| #define MESON_C2_SAR_ADC_REG11_EOC BIT(7) |
| #define MESON_C2_SAR_ADC_REG11_BG_ADJ_MASK GENMASK(18, 12) |
| #define MESON_C2_SAR_ADC_REG11_CHNL_REGS_EN BIT(30) |
| #define MESON_C2_SAR_ADC_REG11_FIFO_EN BIT(31) |
| |
| #define MESON_C2_SAR_ADC_REG12 0x30 |
| |
| #define MESON_C2_SAR_ADC_REG13 0x34 |
| #define MESON_C2_SAR_ADC_REG13_VREF_SEL BIT(19) |
| #define MESON_C2_SAR_ADC_REG13_VBG_SEL BIT(16) |
| #define MESON_C2_SAR_ADC_REG13_EN_VCM0P9 BIT(1) |
| |
| #define MESON_C2_SAR_ADC_REG14 0x38 |
| |
| #define MESON_C2_SAR_ADC_CH0_CTRL1 0x4c |
| #define MESON_C2_SAR_ADC_CH0_CTRL2 0x50 |
| #define MESON_C2_SAR_ADC_CH0_CTRL3 0x54 |
| |
| #define MESON_C2_SAR_ADC_CHX_CTRL1(_chan) \ |
| (MESON_C2_SAR_ADC_CH0_CTRL1 + (_chan) * 12) |
| |
| #define MESON_C2_SAR_ADC_CHX_CTRL1_DECIM_FILTER BIT(0) |
| #define MESON_C2_SAR_ADC_CHX_CTRL1_DIFF_EN BIT(17) |
| #define MESON_C2_SAR_ADC_CHX_CTRL1_CMV_SEL BIT(18) |
| #define MESON_C2_SAR_ADC_CHX_CTRL1_VREFP_SEL BIT(19) |
| #define MESON_C2_SAR_ADC_CHX_CTRL1_IN_CTRL_MASK GENMASK(23, 21) |
| |
| #define MESON_C2_SAR_ADC_CHX_CTRL2(_chan) \ |
| (MESON_C2_SAR_ADC_CH0_CTRL2 + (_chan) * 12) |
| |
| #define MESON_C2_SAR_ADC_CHX_CTRL3(_chan) \ |
| (MESON_C2_SAR_ADC_CH0_CTRL3 + (_chan) * 12) |
| |
| #define MESON_C2_SAR_ADC_HCIC_CTRL1 0xac |
| #define MESON_C2_SAR_ADC_F1_CTRL 0xb0 |
| #define MESON_C2_SAR_ADC_F2_CTRL 0xb4 |
| #define MESON_C2_SAR_ADC_F3_CTRL 0xb8 |
| #define MESON_C2_SAR_ADC_DECI_FILTER_CTRL 0xbc |
| #define MESON_C2_SAR_ADC_DECI_FILTER_BYPASS BIT(1) |
| #define MESON_C2_SAR_ADC_COEF_RAM_CNTL 0xc0 |
| #define MESON_C2_SAR_ADC_COEF_RAM_DATA 0xc4 |
| |
| #define MESON_C2_SAR_ADC_FIFO_RD_NEW 0xc8 |
| |
| #define MESON_C2_SAR_ADC_RAW 0xcc |
| |
| #define MESON_C2_SAR_ADC_CHNLX_BASE 0xd0 |
| |
| #define MESON_C2_SAR_ADC_CHNLX_VALUE_MASK GENMASK(21, 0) |
| #define MESON_C2_SAR_ADC_CHNLX_ID_MASK GENMASK(24, 22) |
| #define MESON_C2_SAR_ADC_CHNLX_VALID_MASK BIT(25) |
| |
| #define MESON_C2_SAR_ADC_CHNL0 0xd0 |
| #define MESON_C2_SAR_ADC_CHNL1 0xd4 |
| #define MESON_C2_SAR_ADC_CHNL2 0xd8 |
| #define MESON_C2_SAR_ADC_CHNL3 0xdc |
| #define MESON_C2_SAR_ADC_CHNL4 0xe0 |
| #define MESON_C2_SAR_ADC_CHNL5 0xe4 |
| #define MESON_C2_SAR_ADC_CHNL6 0xe8 |
| #define MESON_C2_SAR_ADC_CHNL7 0xec |
| |
| #define SARADC_C2_DISCARD_DATA_CNT 0x2b |
| #define SARADC_C2_SAVE_DATA_CNT 0x01 |
| #define SARADC_C2_DECIM_FILTER_RESOLUTION 22 |
| |
| #define SARADC_C2_CONTINUOUSLY_MODE_CLOCK 600000 |
| |
| #define MESON_C2_SAR_ADC_CHAN(_chan) { \ |
| .type = IIO_VOLTAGE, \ |
| .indexed = 1, \ |
| .channel = _chan, \ |
| .address = _chan, \ |
| .scan_index = _chan, \ |
| .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ |
| BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \ |
| BIT(IIO_CHAN_INFO_PROCESSED) | \ |
| BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),\ |
| .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ |
| .scan_type = { \ |
| .sign = 'u', \ |
| .storagebits = 32, \ |
| .shift = 0, \ |
| .endianness = IIO_CPU, \ |
| }, \ |
| .datasheet_name = "SAR_ADC_CH"#_chan, \ |
| } |
| |
| static struct iio_chan_spec meson_c2_sar_adc_iio_channels[] = { |
| MESON_C2_SAR_ADC_CHAN(0), |
| MESON_C2_SAR_ADC_CHAN(1), |
| MESON_C2_SAR_ADC_CHAN(2), |
| MESON_C2_SAR_ADC_CHAN(3), |
| MESON_C2_SAR_ADC_CHAN(4), |
| MESON_C2_SAR_ADC_CHAN(5), |
| MESON_C2_SAR_ADC_CHAN(6), |
| MESON_C2_SAR_ADC_CHAN(7), |
| IIO_CHAN_SOFT_TIMESTAMP(8), |
| }; |
| |
| static int meson_c2_sar_adc_extra_init(struct iio_dev *indio_dev) |
| { |
| unsigned char i; |
| unsigned int regval; |
| struct meson_sar_adc_priv *priv = iio_priv(indio_dev); |
| |
| regval = FIELD_PREP(MESON_C2_SAR_ADC_REG11_EOC, priv->param->adc_eoc); |
| regmap_update_bits(priv->regmap, MESON_C2_SAR_ADC_REG11, |
| MESON_C2_SAR_ADC_REG11_EOC, regval); |
| |
| /* filter bypass */ |
| regmap_update_bits(priv->regmap, MESON_C2_SAR_ADC_DECI_FILTER_CTRL, |
| MESON_C2_SAR_ADC_DECI_FILTER_BYPASS, |
| MESON_C2_SAR_ADC_DECI_FILTER_BYPASS); |
| |
| regmap_write(priv->regmap, MESON_C2_SAR_ADC_REG13, 0x8815); |
| regmap_write(priv->regmap, MESON_C2_SAR_ADC_REG14, 0x8815); |
| |
| for (i = 0; i < 8; i++) |
| regmap_write(priv->regmap, |
| MESON_C2_SAR_ADC_CHX_CTRL1(i), 0x8815); |
| |
| /* to select the VDDA if the vref is optional */ |
| regval = FIELD_PREP(MESON_C2_SAR_ADC_REG13_VBG_SEL, VDDA_AS_VREF); |
| regval |= FIELD_PREP(MESON_C2_SAR_ADC_REG13_EN_VCM0P9, VDDA_AS_VREF); |
| |
| regmap_update_bits(priv->regmap, MESON_C2_SAR_ADC_REG13, |
| MESON_C2_SAR_ADC_REG13_VBG_SEL | |
| MESON_C2_SAR_ADC_REG13_EN_VCM0P9, |
| priv->param->vref_is_optional ? regval : 0); |
| regmap_update_bits(priv->regmap, MESON_C2_SAR_ADC_REG14, |
| MESON_C2_SAR_ADC_REG13_VBG_SEL | |
| MESON_C2_SAR_ADC_REG13_EN_VCM0P9, |
| priv->param->vref_is_optional ? regval : 0); |
| |
| for (i = 0; i < indio_dev->num_channels; i++) |
| regmap_update_bits(priv->regmap, MESON_C2_SAR_ADC_CHX_CTRL1(i), |
| MESON_C2_SAR_ADC_REG13_VBG_SEL | |
| MESON_C2_SAR_ADC_REG13_EN_VCM0P9, |
| priv->param->vref_is_optional ? regval : 0); |
| |
| return 0; |
| } |
| |
| static void meson_c2_sar_adc_set_ch7_mux(struct iio_dev *indio_dev, |
| enum meson_sar_adc_chan7_mux_sel sel) |
| { |
| unsigned int regval; |
| struct meson_sar_adc_priv *priv = iio_priv(indio_dev); |
| |
| regval = FIELD_PREP(MESON_C2_SAR_ADC_CHX_CTRL1_IN_CTRL_MASK, sel); |
| regmap_update_bits(priv->regmap, MESON_C2_SAR_ADC_CHX_CTRL1(7), |
| MESON_C2_SAR_ADC_CHX_CTRL1_IN_CTRL_MASK, regval); |
| |
| priv->chan7_mux_sel = sel; |
| |
| usleep_range(10, 20); |
| } |
| |
| static int meson_c2_sar_adc_read_fifo(struct iio_dev *indio_dev, |
| const struct iio_chan_spec *chan, |
| bool chk_channel) |
| { |
| int fifo_chan; |
| unsigned int regval; |
| struct meson_sar_adc_priv *priv = iio_priv(indio_dev); |
| |
| regmap_read(priv->regmap, MESON_C2_SAR_ADC_FIFO_RD, ®val); |
| if (chk_channel) { |
| fifo_chan = FIELD_GET(MESON_C2_SAR_ADC_FIFO_RD_CHAN_ID_MASK, |
| regval); |
| if (fifo_chan != chan->address) { |
| dev_err(&indio_dev->dev, |
| "ADC FIFO entry belongs to channel %d instead of %lu\n", |
| fifo_chan, chan->address); |
| return -EINVAL; |
| } |
| } |
| |
| return FIELD_GET(MESON_C2_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK, regval); |
| } |
| |
| static void meson_c2_sar_adc_enable_chnl(struct iio_dev *indio_dev, bool en) |
| { |
| struct meson_sar_adc_priv *priv = iio_priv(indio_dev); |
| |
| regmap_update_bits(priv->regmap, MESON_C2_SAR_ADC_REG11, |
| MESON_C2_SAR_ADC_REG11_CHNL_REGS_EN, |
| en ? MESON_C2_SAR_ADC_REG11_CHNL_REGS_EN : 0); |
| } |
| |
| static int meson_c2_sar_adc_read_chnl(struct iio_dev *indio_dev, |
| const struct iio_chan_spec *chan) |
| { |
| bool is_valid; |
| int channel; |
| unsigned int regval; |
| unsigned int data; |
| unsigned int retry_cnt = 5; |
| struct meson_sar_adc_priv *priv = iio_priv(indio_dev); |
| |
| again: |
| regmap_read(priv->regmap, |
| MESON_C2_SAR_ADC_CHNLX_BASE + (chan->channel << 2), |
| ®val); |
| |
| is_valid = FIELD_GET(MESON_C2_SAR_ADC_CHNLX_VALID_MASK, regval); |
| if (!is_valid) { |
| if (retry_cnt-- > 0) { |
| usleep_range(3 * 1000, 5 * 1000); |
| goto again; |
| } |
| |
| dev_err(&indio_dev->dev, |
| "ADC chnl reg have no valid sampling data\n"); |
| |
| return -EINVAL; |
| } |
| |
| channel = FIELD_GET(MESON_C2_SAR_ADC_CHNLX_ID_MASK, regval); |
| if (channel != chan->channel) { |
| dev_err(&indio_dev->dev, |
| "ADC Dout entry belongs to channel %d instead of %d\n", |
| channel, chan->channel); |
| return -EINVAL; |
| } |
| |
| data = FIELD_GET(MESON_C2_SAR_ADC_CHNLX_VALUE_MASK, regval); |
| |
| regmap_read(priv->regmap, MESON_C2_SAR_ADC_CHX_CTRL1(chan->address), |
| ®val); |
| if (!FIELD_GET(MESON_C2_SAR_ADC_CHX_CTRL1_DECIM_FILTER, regval)) { |
| /* check whether data is negative */ |
| if ((data >> (SARADC_C2_DECIM_FILTER_RESOLUTION - 1)) == 1) |
| data = data - (1 << SARADC_C2_DECIM_FILTER_RESOLUTION); |
| /* convert to unsigned int */ |
| data = (data + (1 << (SARADC_C2_DECIM_FILTER_RESOLUTION - 1))); |
| |
| /* convert to 12bit */ |
| data = data >> (SARADC_C2_DECIM_FILTER_RESOLUTION - |
| priv->param->resolution); |
| } |
| |
| return data; |
| } |
| |
| static void meson_c2_sar_adc_init_ch(struct iio_dev *indio_dev, |
| const struct iio_chan_spec *chan) |
| { |
| struct meson_sar_adc_priv *priv = iio_priv(indio_dev); |
| |
| /* to select single-ended */ |
| regmap_update_bits(priv->regmap, |
| MESON_C2_SAR_ADC_CHX_CTRL1(chan->channel), |
| MESON_C2_SAR_ADC_CHX_CTRL1_DIFF_EN, 0x0); |
| |
| regmap_update_bits(priv->regmap, |
| MESON_C2_SAR_ADC_CHX_CTRL1(chan->channel), |
| MESON_C2_SAR_ADC_CHX_CTRL1_CMV_SEL, |
| FIELD_PREP(MESON_C2_SAR_ADC_CHX_CTRL1_CMV_SEL, |
| priv->param->vrefp_select)); |
| |
| regmap_update_bits(priv->regmap, |
| MESON_C2_SAR_ADC_CHX_CTRL1(chan->channel), |
| MESON_C2_SAR_ADC_CHX_CTRL1_VREFP_SEL, |
| FIELD_PREP(MESON_C2_SAR_ADC_CHX_CTRL1_VREFP_SEL, |
| priv->param->cmv_select)); |
| } |
| |
| static void meson_c2_sar_adc_enable_decim_filter(struct iio_dev *indio_dev, |
| const struct iio_chan_spec *ch, |
| bool en, |
| enum meson_sar_adc_sampling_mode mode) |
| { |
| struct meson_sar_adc_priv *priv = iio_priv(indio_dev); |
| |
| if (en) { |
| regmap_write(priv->regmap, |
| MESON_C2_SAR_ADC_CHX_CTRL2(ch->channel), |
| SARADC_C2_DISCARD_DATA_CNT); |
| regmap_write(priv->regmap, |
| MESON_C2_SAR_ADC_CHX_CTRL3(ch->channel), |
| (mode == CONTINUOUS_MODE) ? |
| priv->continuous_sample_count : |
| SARADC_C2_SAVE_DATA_CNT); |
| regmap_update_bits(priv->regmap, |
| MESON_C2_SAR_ADC_CHX_CTRL1(ch->channel), |
| MESON_C2_SAR_ADC_CHX_CTRL1_DECIM_FILTER, 0); |
| } else { |
| regmap_update_bits(priv->regmap, |
| MESON_C2_SAR_ADC_CHX_CTRL1(ch->channel), |
| MESON_C2_SAR_ADC_CHX_CTRL1_DECIM_FILTER, |
| MESON_C2_SAR_ADC_CHX_CTRL1_DECIM_FILTER); |
| } |
| } |
| |
| static int meson_c2_sar_adc_tuning_clock(struct iio_dev *indio_dev, |
| enum meson_sar_adc_sampling_mode mode) |
| { |
| int ret; |
| struct meson_sar_adc_priv *priv = iio_priv(indio_dev); |
| |
| if (mode != SINGLE_MODE && mode != PERIOD_MODE) |
| return -EINVAL; |
| |
| ret = clk_set_rate(priv->adc_clk, (mode == SINGLE_MODE) ? |
| priv->param->clock_rate : |
| SARADC_C2_CONTINUOUSLY_MODE_CLOCK); |
| |
| if (ret) { |
| dev_err(indio_dev->dev.parent, |
| "failed to set adc clock rate\n"); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static int meson_c2_sar_adc_temp_triming(struct iio_dev *indio_dev) |
| { |
| struct meson_sar_adc_priv *priv = iio_priv(indio_dev); |
| int temp, calib = 0; |
| int ret; |
| unsigned int regval; |
| |
| if (!priv->thermal_zone_dev) |
| return 0; |
| |
| ret = thermal_zone_get_temp(priv->thermal_zone_dev, &temp); |
| if (ret) { |
| dev_err(indio_dev->dev.parent, "get temp fail\n"); |
| return ret; |
| } |
| |
| if (temp <= -30000) |
| calib = -6; |
| else if (temp > -30000 && temp <= -10000) |
| calib = -4; |
| else if (temp > -10000 && temp <= 10000) |
| calib = -2; |
| else if (temp > 10000 && temp <= 30000) |
| calib = 0; |
| else if (temp > 30000 && temp <= 40000) |
| calib = 1; |
| else if (temp > 40000 && temp <= 60000) |
| calib = 2; |
| else if (temp > 60000 && temp <= 80000) |
| calib = 4; |
| else if (temp > 80000) |
| calib = 8; |
| |
| regmap_update_bits(priv->regmap, MESON_C2_SAR_ADC_REG11, |
| MESON_C2_SAR_ADC_REG11_BG_ADJ_MASK, |
| FIELD_PREP(MESON_C2_SAR_ADC_REG11_BG_ADJ_MASK, |
| priv->trimming_val - calib)); |
| |
| regmap_read(priv->regmap, MESON_C2_SAR_ADC_REG11, ®val); |
| dev_info(indio_dev->dev.parent, "temp: %d, calib: %d, REG11: %x\n", |
| temp, calib, regval); |
| |
| return 0; |
| } |
| |
| static void meson_c2_sar_adc_store_triming_val(struct iio_dev *indio_dev) |
| { |
| struct meson_sar_adc_priv *priv = iio_priv(indio_dev); |
| unsigned int regval; |
| |
| regmap_read(priv->regmap, MESON_C2_SAR_ADC_REG11, ®val); |
| priv->trimming_val = FIELD_GET(MESON_C2_SAR_ADC_REG11_BG_ADJ_MASK, |
| regval); |
| |
| dev_info(indio_dev->dev.parent, "saradc_reg11: %x\n", regval); |
| } |
| |
| static const struct meson_sar_adc_diff_ops meson_c2_diff_ops = { |
| .extra_init = meson_c2_sar_adc_extra_init, |
| .set_ch7_mux = meson_c2_sar_adc_set_ch7_mux, |
| .read_fifo = meson_c2_sar_adc_read_fifo, |
| .enable_chnl = meson_c2_sar_adc_enable_chnl, |
| .read_chnl = meson_c2_sar_adc_read_chnl, |
| .init_ch = meson_c2_sar_adc_init_ch, |
| .enable_decim_filter = meson_c2_sar_adc_enable_decim_filter, |
| .tuning_clock = meson_c2_sar_adc_tuning_clock, |
| .temp_triming = meson_c2_sar_adc_temp_triming, |
| .store_triming_val = meson_c2_sar_adc_store_triming_val, |
| }; |
| |
| static const struct regmap_config meson_sar_adc_regmap_config_c2 = { |
| .reg_bits = 8, |
| .val_bits = 32, |
| .reg_stride = 4, |
| .max_register = MESON_C2_SAR_ADC_CHNL7, |
| }; |
| |
| const struct meson_sar_adc_param meson_sar_adc_c2_param __initconst = { |
| .has_bl30_integration = false, |
| .clock_rate = 600000, |
| .regmap_config = &meson_sar_adc_regmap_config_c2, |
| .resolution = 12, |
| .vref_is_optional = false, |
| .has_chnl_regs = true, |
| .vrefp_select = 0, |
| .cmv_select = 0, |
| .adc_eoc = 1, |
| .dops = &meson_c2_diff_ops, |
| .channels = meson_c2_sar_adc_iio_channels, |
| .num_channels = ARRAY_SIZE(meson_c2_sar_adc_iio_channels), |
| }; |