diff --git a/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.txt b/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.txt index 04747709c164..f41e9453dcfd 100644 --- a/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.txt +++ b/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.txt @@ -2,6 +2,7 @@ Required properties: - compatible: depending on the SoC this should be one of: + - "amlogic,meson-g12a-saradc" for G12A - "amlogic,meson-txlx-saradc" for TXLX - "amlogic,meson-axg-saradc" for AXG - "amlogic,meson-gxl-saradc" for GXL diff --git a/arch/arm/configs/meson32_defconfig b/arch/arm/configs/meson32_defconfig index 8d688b82219f..cd9c3a04c324 100644 --- a/arch/arm/configs/meson32_defconfig +++ b/arch/arm/configs/meson32_defconfig @@ -320,6 +320,8 @@ CONFIG_PM_DEVFREQ=y CONFIG_EXTCON=y CONFIG_MEMORY=y CONFIG_IIO=y +CONFIG_IIO_BUFFER=y +CONFIG_IIO_KFIFO_BUF=y CONFIG_PWM=y CONFIG_RESET_CONTROLLER=y CONFIG_GENERIC_PHY=y diff --git a/arch/arm64/boot/dts/amlogic/mesong12a.dtsi b/arch/arm64/boot/dts/amlogic/mesong12a.dtsi index 34e2807be534..dc5f0d3852f1 100644 --- a/arch/arm64/boot/dts/amlogic/mesong12a.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesong12a.dtsi @@ -270,6 +270,16 @@ clocks = <&xtal>; }; + saradc:saradc { + compatible = "amlogic,meson-g12a-saradc"; + status = "okay"; + #io-channel-cells = <1>; + clocks = <&xtal>, <&clkc CLKID_SARADC_GATE>; + clock-names = "xtal", "saradc_clk"; + interrupts = ; + reg = <0x0 0xff809000 0x0 0x48>; + }; + soc { compatible = "simple-bus"; #address-cells = <2>; diff --git a/arch/arm64/configs/meson64_defconfig b/arch/arm64/configs/meson64_defconfig index 97a78e3bc8c9..6af564a96b88 100644 --- a/arch/arm64/configs/meson64_defconfig +++ b/arch/arm64/configs/meson64_defconfig @@ -507,6 +507,8 @@ CONFIG_DEVFREQ_GOV_POWERSAVE=y CONFIG_DEVFREQ_GOV_USERSPACE=y CONFIG_EXTCON=y CONFIG_IIO=y +CONFIG_IIO_BUFFER=y +CONFIG_IIO_KFIFO_BUF=y CONFIG_PWM=y CONFIG_ANDROID=y CONFIG_ANDROID_BINDER_IPC=y diff --git a/arch/arm64/configs/meson64_smarthome_defconfig b/arch/arm64/configs/meson64_smarthome_defconfig index 9a2af3d1d6cc..8587ebef43d3 100644 --- a/arch/arm64/configs/meson64_smarthome_defconfig +++ b/arch/arm64/configs/meson64_smarthome_defconfig @@ -464,6 +464,8 @@ CONFIG_DEVFREQ_GOV_POWERSAVE=y CONFIG_DEVFREQ_GOV_USERSPACE=y CONFIG_EXTCON=y CONFIG_IIO=y +CONFIG_IIO_BUFFER=y +CONFIG_IIO_KFIFO_BUF=y CONFIG_PWM=y CONFIG_ANDROID=y CONFIG_ANDROID_BINDER_IPC=y diff --git a/drivers/amlogic/iio/adc/Kconfig b/drivers/amlogic/iio/adc/Kconfig index 462f82599d1a..351cee62522e 100644 --- a/drivers/amlogic/iio/adc/Kconfig +++ b/drivers/amlogic/iio/adc/Kconfig @@ -9,6 +9,8 @@ config AMLOGIC_SARADC bool "Meson SAR ADC support" depends on REGMAP_MMIO depends on IIO + depends on IIO_BUFFER + depends on IIO_KFIFO_BUF default n help Say Y here if you want to use the Meson SAR ADC. diff --git a/drivers/amlogic/iio/adc/meson_saradc.c b/drivers/amlogic/iio/adc/meson_saradc.c index 09f7324f02cc..aaa859ed1939 100644 --- a/drivers/amlogic/iio/adc/meson_saradc.c +++ b/drivers/amlogic/iio/adc/meson_saradc.c @@ -13,8 +13,8 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * The sar adc is work in polling mode by default, or work in IRQ mode - * by defining the macro MESON_SAR_ADC_IRQ_MODE. + * The sar adc is work in polling mode for single sampling, or work in IRQ mode + * for periodic sampling. */ #include @@ -35,6 +35,9 @@ #include #include #include +#include +#include +#include #define MESON_SAR_ADC_REG0 0x00 #define MESON_SAR_ADC_REG0_PANEL_DETECT BIT(31) @@ -60,6 +63,7 @@ #define MESON_SAR_ADC_CHAN_LIST 0x04 #define MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK GENMASK(26, 24) + #define MESON_SAR_ADC_CHAN_LIST_ENTRY_SHIFT(_chan) (_chan * 3) #define MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(_chan) \ (GENMASK(2, 0) << ((_chan) * 3)) @@ -171,11 +175,26 @@ #define MESON_SAR_ADC_REG11 0x2c #define MESON_SAR_ADC_REG11_VREF_SEL BIT(0) #define MESON_SAR_ADC_REG11_BANDGAP_EN BIT(13) + #define MESON_SAR_ADC_REG11_CHNL_REGS_EN BIT(30) + #define MESON_SAR_ADC_REG11_FIFO_EN BIT(31) #define MESON_SAR_ADC_REG13 0x34 #define MESON_SAR_ADC_REG13_12BIT_CALIBRATION_MASK GENMASK(13, 8) -#define MESON_SAR_ADC_MAX_FIFO_SIZE 16 +/* NOTE: the registers below is introduced first on G12A platform */ +#define MESON_SAR_ADC_CHNLX_BASE 0x38 +#define MESON_SAR_ADC_CHNLX_SAMPLE_VALUE_SHIFT(_chan) \ + ((_chan) * 16) +#define MESON_SAR_ADC_CHNLX_ID_SHIFT(_chan) \ + (12 + (_chan) * 16) +#define MESON_SAR_ADC_CHNLX_VALID_SHIFT(_chan) \ + (15 + (_chan) * 16) +#define MESON_SAR_ADC_CHNL01 0x38 +#define MESON_SAR_ADC_CHNL23 0x3c +#define MESON_SAR_ADC_CHNL45 0x40 +#define MESON_SAR_ADC_CHNL67 0x44 + +#define MESON_SAR_ADC_MAX_FIFO_SIZE 32 #define MESON_SAR_ADC_TIMEOUT 100 /* ms */ #define P_HHI_DPLL_TOP_0 0x10c6 @@ -187,12 +206,19 @@ .type = IIO_VOLTAGE, \ .indexed = 1, \ .channel = _chan, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ - BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \ + .scan_index = _chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \ BIT(IIO_CHAN_INFO_PROCESSED), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ BIT(IIO_CHAN_INFO_CALIBBIAS) | \ BIT(IIO_CHAN_INFO_CALIBSCALE), \ + .scan_type = { \ + .sign = 'u', \ + .storagebits = 16, \ + .shift = 0, \ + .endianness = IIO_CPU, \ + }, \ .datasheet_name = "SAR_ADC_CH"#_chan, \ } @@ -231,6 +257,12 @@ static const struct iio_chan_spec meson_sar_adc_iio_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(8), }; +enum meson_sar_adc_sample_mode { + SINGLE_MODE, + PERIOD_MODE, + MAX_MODE, +}; + enum meson_sar_adc_avg_mode { NO_AVERAGING = 0x0, MEAN_AVERAGING = 0x1, @@ -284,6 +316,8 @@ struct meson_sar_adc_reg_diff { * @obt_temp_chan6: whether to read data of temp sensor by channel 6 * @has_bl30_integration: * @vref_sel: txlx and later: VDDA; others(txl etc): calibration voltage + * @period_support: periodic sampling support + * @has_chnl_regs: whether support for chnl[X] registers * @resolution: gxl and later: 12bit; others(gxtvbb etc): 10bit * @name: * @regs_diff: to describe the differences of the registers @@ -292,31 +326,36 @@ struct meson_sar_adc_data { bool obt_temp_chan6; bool has_bl30_integration; bool vref_sel; - unsigned int resolution; + bool period_support; + bool has_chnl_regs; + unsigned int resolution; const char *name; struct meson_sar_adc_reg_diff regs_diff; }; struct meson_sar_adc_priv { - struct regmap *regmap; - const struct meson_sar_adc_data *data; + struct regmap *regmap; + const struct meson_sar_adc_data *data; struct clk *clkin; struct clk *clk81_gate; struct clk *adc_clk; - struct clk_gate clk_gate; + struct clk_gate clk_gate; struct clk *adc_div_clk; - struct clk_divider clk_div; - struct completion done; - int calibbias; - int calibscale; - int chan7_mux_sel; + struct clk_divider clk_div; + int calibbias; + int calibscale; + int chan7_mux_sel; + int delay_per_tick; + int ticks_per_period; + int active_channel_cnt; + u8 *datum_buf; }; static const struct regmap_config meson_sar_adc_regmap_config = { .reg_bits = 8, .val_bits = 32, .reg_stride = 4, - .max_register = MESON_SAR_ADC_REG13, + .max_register = MESON_SAR_ADC_CHNL67, }; static unsigned int meson_sar_adc_get_fifo_count(struct iio_dev *indio_dev) @@ -340,7 +379,6 @@ static int meson_sar_adc_calib_val(struct iio_dev *indio_dev, int val) return clamp(tmp, 0, (1 << priv->data->resolution) - 1); } -#ifndef MESON_SAR_ADC_IRQ_MODE static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev) { struct meson_sar_adc_priv *priv = iio_priv(indio_dev); @@ -361,7 +399,6 @@ static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev) return 0; } -#endif static int meson_sar_adc_read_raw_sample(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, @@ -370,14 +407,8 @@ static int meson_sar_adc_read_raw_sample(struct iio_dev *indio_dev, struct meson_sar_adc_priv *priv = iio_priv(indio_dev); int regval, fifo_chan, fifo_val, count; -#ifdef MESON_SAR_ADC_IRQ_MODE - if (!wait_for_completion_timeout(&priv->done, - msecs_to_jiffies(MESON_SAR_ADC_TIMEOUT))) - return -ETIMEDOUT; -#else if (meson_sar_adc_wait_busy_clear(indio_dev)) return -ETIMEDOUT; -#endif count = meson_sar_adc_get_fifo_count(indio_dev); if (count != 1) { @@ -404,6 +435,46 @@ static int meson_sar_adc_read_raw_sample(struct iio_dev *indio_dev, return 0; } +static int meson_sar_adc_read_raw_sample_from_chnl(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + unsigned int regval; + int grp_off; + int chan_off; + int fifo_chan; + int fifo_val; + bool is_valid; + + grp_off = (chan->channel / 2) << 2; + chan_off = chan->channel % 2; + + regmap_read(priv->regmap, + MESON_SAR_ADC_CHNLX_BASE + grp_off, ®val); + + is_valid = (regval >> MESON_SAR_ADC_CHNLX_VALID_SHIFT(chan_off)) & 0x1; + if (!is_valid) { + dev_err(&indio_dev->dev, + "ADC chnl reg have no valid sampling data\n"); + return -EINVAL; + } + + fifo_chan = (regval >> MESON_SAR_ADC_CHNLX_ID_SHIFT(chan_off)) & 0x7; + if (fifo_chan != chan->channel) { + dev_err(&indio_dev->dev, + "ADC Dout entry belongs to channel %d instead of %d\n", + fifo_chan, chan->channel); + return -EINVAL; + } + fifo_val = regval >> MESON_SAR_ADC_CHNLX_SAMPLE_VALUE_SHIFT(chan_off); + fifo_val &= GENMASK(priv->data->resolution - 1, 0); + + /* to fix the sample value by software */ + *val = meson_sar_adc_calib_val(indio_dev, fifo_val); + + return 0; +} static void meson_sar_adc_set_averaging(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum meson_sar_adc_avg_mode mode, @@ -441,7 +512,8 @@ static int meson_sar_adc_temp_sensor_init(struct iio_dev *indio_dev) } static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) + const struct iio_chan_spec *chan, + unsigned char idx) { struct meson_sar_adc_priv *priv = iio_priv(indio_dev); u32 regval; @@ -451,27 +523,15 @@ static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev, * time. to keep it simple we're only working with one *internal* * channel, which starts counting at index 0 (which means: count = 1). */ - regval = FIELD_PREP(MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK, 0); + + regval = FIELD_PREP(MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK, idx); regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_LIST, MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK, regval); /* map channel index 0 to the channel which we want to read */ - regval = FIELD_PREP(MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0), - chan->channel); + regval = chan->channel << MESON_SAR_ADC_CHAN_LIST_ENTRY_SHIFT(idx), regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_LIST, - MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0), regval); - - regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK, - chan->channel); - regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW, - MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK, - regval); - - regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK, - chan->channel); - regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW, - MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK, - regval); + MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(idx), regval); if (IS_CHAN6(chan->channel)) { if (priv->data->obt_temp_chan6) @@ -500,15 +560,6 @@ static void meson_sar_adc_start_sample_engine(struct iio_dev *indio_dev) { struct meson_sar_adc_priv *priv = iio_priv(indio_dev); -#ifdef MESON_SAR_ADC_IRQ_MODE - reinit_completion(&priv->done); -#endif - -#ifdef MESON_SAR_ADC_IRQ_MODE - regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, - MESON_SAR_ADC_REG0_FIFO_IRQ_EN, - MESON_SAR_ADC_REG0_FIFO_IRQ_EN); -#endif regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE, MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE); @@ -522,10 +573,6 @@ static void meson_sar_adc_stop_sample_engine(struct iio_dev *indio_dev) { struct meson_sar_adc_priv *priv = iio_priv(indio_dev); -#ifdef MESON_SAR_ADC_IRQ_MODE - regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, - MESON_SAR_ADC_REG0_FIFO_IRQ_EN, 0); -#endif regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, MESON_SAR_ADC_REG0_SAMPLING_STOP, MESON_SAR_ADC_REG0_SAMPLING_STOP); @@ -599,19 +646,33 @@ 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; + ret = meson_sar_adc_lock(indio_dev); if (ret) { meson_sar_adc_unlock(indio_dev); return ret; } + if (iio_buffer_enabled(indio_dev)) { + if (priv->data->has_chnl_regs) { + ret = meson_sar_adc_read_raw_sample_from_chnl(indio_dev, + chan, val); + meson_sar_adc_unlock(indio_dev); + + return (ret == 0) ? IIO_VAL_INT : ret; + } + meson_sar_adc_unlock(indio_dev); + return -EBUSY; + } + /* clear the FIFO to make sure we're not reading old values */ meson_sar_adc_clear_fifo(indio_dev); meson_sar_adc_set_averaging(indio_dev, chan, avg_mode, avg_samples); - meson_sar_adc_enable_channel(indio_dev, chan); + meson_sar_adc_enable_channel(indio_dev, chan, 0); meson_sar_adc_start_sample_engine(indio_dev); ret = meson_sar_adc_read_raw_sample(indio_dev, chan, val); @@ -711,6 +772,19 @@ static int meson_sar_adc_iio_info_write_raw(struct iio_dev *indio_dev, } } +static int meson_sar_adc_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + + kfree(priv->datum_buf); + priv->datum_buf = kmalloc(indio_dev->scan_bytes, GFP_KERNEL); + if (!priv->datum_buf) + return -ENOMEM; + + return 0; +} + static int meson_sar_adc_clk_init(struct iio_dev *indio_dev, void __iomem *base) @@ -845,11 +919,7 @@ static int meson_sar_adc_hw_enable(struct iio_dev *indio_dev) meson_sar_adc_unlock(indio_dev); return ret; } -#ifdef MESON_SAR_ADC_IRQ_MODE - regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, - MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, - FIELD_PREP(MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, 1)); -#endif + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, MESON_SAR_ADC_REG11_BANDGAP_EN, MESON_SAR_ADC_REG11_BANDGAP_EN); @@ -911,7 +981,7 @@ static int meson_sar_adc_hw_disable(struct iio_dev *indio_dev) return 0; } -#ifdef MESON_SAR_ADC_IRQ_MODE + static irqreturn_t meson_sar_adc_irq(int irq, void *data) { struct iio_dev *indio_dev = data; @@ -926,11 +996,46 @@ static irqreturn_t meson_sar_adc_irq(int irq, void *data) if (cnt < threshold) return IRQ_NONE; - complete(&priv->done); + disable_irq_nosync(irq); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t meson_sar_adc_worker(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + u16 fifo_cnt; + u16 fifo_val; + u32 regval; + u32 i = 0; + u32 j = 0; + + fifo_cnt = meson_sar_adc_get_fifo_count(indio_dev); + + for (j = 0; j < fifo_cnt; j = j + i) { + for (i = 0; i < priv->active_channel_cnt; i++) { + regmap_read(priv->regmap, + MESON_SAR_ADC_FIFO_RD, ®val); + + fifo_val = FIELD_GET( + MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK, + regval); + fifo_val &= GENMASK(priv->data->resolution - 1, 0); + + priv->datum_buf[i*2] = fifo_val & 0xff; + priv->datum_buf[i*2+1] = (fifo_val >> 8) & 0xff; + } + iio_push_to_buffers_with_timestamp(indio_dev, priv->datum_buf, + iio_get_time_ns(indio_dev)); + } + + meson_sar_adc_clear_fifo(indio_dev); + enable_irq(irq); return IRQ_HANDLED; } -#endif + static int meson_sar_adc_calib(struct iio_dev *indio_dev) { struct meson_sar_adc_priv *priv = iio_priv(indio_dev); @@ -972,6 +1077,167 @@ out: return ret; } +static int meson_sar_adc_sample_mode_set(struct iio_dev *indio_dev, + enum meson_sar_adc_sample_mode mode) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + + if (mode != SINGLE_MODE && mode != PERIOD_MODE) + return -EINVAL; + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_SAMPLING_STOP, + (mode == SINGLE_MODE) ? + MESON_SAR_ADC_REG0_SAMPLING_STOP : 0); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_CONTINUOUS_EN, + (mode == PERIOD_MODE) ? + MESON_SAR_ADC_REG0_CONTINUOUS_EN : 0); + + return 0; +} + +static void meson_sar_adc_chan_spec_update(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + struct iio_chan_spec *chan; + int i; + + for (i = 0; i < indio_dev->num_channels; i++) { + chan = (struct iio_chan_spec *)indio_dev->channels + i; + if (chan->channel < 0) + continue; + chan->scan_type.realbits = priv->data->resolution; + } +} + +static int meson_sar_adc_iio_buffer_setup(struct iio_dev *indio_dev, + irqreturn_t (*pollfunc_bh)(int irq, void *p), + irqreturn_t (*pollfunc_th)(int irq, void *p), + int irq, unsigned long flags, + const struct iio_buffer_setup_ops *setup_ops) { + + struct iio_buffer *buffer; + int ret; + + buffer = iio_kfifo_allocate(); + if (!buffer) + return -ENOMEM; + + iio_device_attach_buffer(indio_dev, buffer); + + ret = devm_request_threaded_irq(indio_dev->dev.parent, irq, + pollfunc_th, + pollfunc_bh, + flags, + indio_dev->name, + indio_dev); + if (ret) + goto error_kfifo_free; + + indio_dev->setup_ops = setup_ops; + indio_dev->modes |= INDIO_BUFFER_SOFTWARE; + + return 0; + +error_kfifo_free: + iio_kfifo_free(indio_dev->buffer); + + return ret; +} + +static int meson_sar_adc_iio_buffer_cleanup(struct iio_dev *indio_dev) +{ + iio_kfifo_free(indio_dev->buffer); + + return 0; +} + +static int meson_sar_adc_buffer_postenable(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + const struct iio_chan_spec *chan; + unsigned char idx = 0; + unsigned char bit; + + meson_sar_adc_sample_mode_set(indio_dev, PERIOD_MODE); + + /* set sampling period time */ + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK, + FIELD_PREP(MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK, + priv->delay_per_tick)); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_BLOCK_DLY_MASK, + FIELD_PREP(MESON_SAR_ADC_REG3_BLOCK_DLY_MASK, + priv->ticks_per_period)); + + meson_sar_adc_clear_fifo(indio_dev); + + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->num_channels) { + chan = indio_dev->channels + bit; + + if (chan->channel < 0) + continue; + meson_sar_adc_enable_channel(indio_dev, chan, idx); + idx++; + } + + priv->active_channel_cnt = idx; + + /* + * generate interrupt when fifo contains N samples, and the N + * is required to align base on the number of active scan channel + */ + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, + FIELD_PREP(MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, + 16 - (16 % idx))); + + /* enable irq */ + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_FIFO_IRQ_EN, + MESON_SAR_ADC_REG0_FIFO_IRQ_EN); + + /* + * enable chnl regs which save the sampling value for + * individual channel + */ + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, + MESON_SAR_ADC_REG11_CHNL_REGS_EN, + MESON_SAR_ADC_REG11_CHNL_REGS_EN); + + meson_sar_adc_start_sample_engine(indio_dev); + + return 0; +} + +static int meson_sar_adc_buffer_predisable(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + + meson_sar_adc_stop_sample_engine(indio_dev); + + meson_sar_adc_sample_mode_set(indio_dev, SINGLE_MODE); + + /* disable irq */ + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_FIFO_IRQ_EN, 0); + + /* disable chnl regs */ + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, + MESON_SAR_ADC_REG11_CHNL_REGS_EN, 0); + return 0; +} + +static const struct iio_buffer_setup_ops meson_sar_adc_buffer_setup_ops = { + .postenable = meson_sar_adc_buffer_postenable, + .predisable = meson_sar_adc_buffer_predisable, +}; + static ssize_t chan7_mux_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1019,10 +1285,24 @@ static const struct attribute_group meson_sar_adc_attr_group = { static const struct iio_info meson_sar_adc_iio_info = { .read_raw = meson_sar_adc_iio_info_read_raw, .write_raw = meson_sar_adc_iio_info_write_raw, + .update_scan_mode = meson_sar_adc_update_scan_mode, .attrs = &meson_sar_adc_attr_group, .driver_module = THIS_MODULE, }; +struct meson_sar_adc_data meson_sar_adc_g12a_data = { + .obt_temp_chan6 = false, + .has_bl30_integration = false, + .period_support = true, + .has_chnl_regs = true, + .vref_sel = VDDA_AS_VREF, + .resolution = SAR_ADC_12BIT, + .name = "meson-g12a-saradc", + .regs_diff = { + .reg3_ring_counter_disable = BIT_HIGH, + }, +}; + struct meson_sar_adc_data meson_sar_adc_txlx_data = { .obt_temp_chan6 = false, .has_bl30_integration = true, @@ -1080,6 +1360,9 @@ struct meson_sar_adc_data meson_sar_adc_m8b_data = { static const struct of_device_id meson_sar_adc_of_match[] = { { + .compatible = "amlogic,meson-g12a-saradc", + .data = &meson_sar_adc_g12a_data, + }, { .compatible = "amlogic,meson-txlx-saradc", .data = &meson_sar_adc_txlx_data, }, { @@ -1107,9 +1390,7 @@ static int meson_sar_adc_probe(struct platform_device *pdev) void __iomem *base; const struct of_device_id *match; int ret; -#ifdef MESON_SAR_ADC_IRQ_MODE int irq; -#endif indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv)); if (!indio_dev) { @@ -1118,9 +1399,6 @@ static int meson_sar_adc_probe(struct platform_device *pdev) } priv = iio_priv(indio_dev); -#ifdef MESON_SAR_ADC_IRQ_MODE - init_completion(&priv->done); -#endif match = of_match_device(meson_sar_adc_of_match, &pdev->dev); priv->data = match->data; @@ -1133,20 +1411,17 @@ static int meson_sar_adc_probe(struct platform_device *pdev) indio_dev->channels = meson_sar_adc_iio_channels; indio_dev->num_channels = ARRAY_SIZE(meson_sar_adc_iio_channels); + meson_sar_adc_chan_spec_update(indio_dev); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) return PTR_ERR(base); -#ifdef MESON_SAR_ADC_IRQ_MODE + irq = irq_of_parse_and_map(pdev->dev.of_node, 0); if (!irq) return -EINVAL; - ret = devm_request_irq(&pdev->dev, irq, meson_sar_adc_irq, IRQF_SHARED, - dev_name(&pdev->dev), indio_dev); - if (ret) - return ret; -#endif priv->regmap = devm_regmap_init_mmio(&pdev->dev, base, &meson_sar_adc_regmap_config); if (IS_ERR(priv->regmap)) @@ -1187,6 +1462,36 @@ static int meson_sar_adc_probe(struct platform_device *pdev) priv->calibscale = MILLION; + if (priv->data->period_support) { + ret = of_property_read_u32(pdev->dev.of_node, + "amlogic,delay-per-tick", &priv->delay_per_tick); + if (ret) { + dev_info(&pdev->dev, + "set delay per tick to <1ms> by default."); + /* 1ms per tick */ + priv->delay_per_tick = 3; + } + + ret = of_property_read_u32(pdev->dev.of_node, + "amlogic,ticks-per-period", &priv->ticks_per_period); + if (ret) { + dev_info(&pdev->dev, + "set ticks per period to <1> by default."); + /* 1 ticks per sampling period */ + priv->ticks_per_period = 1; + } + + ret = meson_sar_adc_iio_buffer_setup(indio_dev, + &meson_sar_adc_worker, + &meson_sar_adc_irq, + irq, + IRQF_SHARED, + &meson_sar_adc_buffer_setup_ops); + + if (ret) + return ret; + } + ret = meson_sar_adc_init(indio_dev); if (ret) goto err; @@ -1210,14 +1515,19 @@ static int meson_sar_adc_probe(struct platform_device *pdev) err_hw: meson_sar_adc_hw_disable(indio_dev); err: + meson_sar_adc_iio_buffer_cleanup(indio_dev); + return ret; } static int meson_sar_adc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); iio_device_unregister(indio_dev); + meson_sar_adc_iio_buffer_cleanup(indio_dev); + kfree(priv->datum_buf); return meson_sar_adc_hw_disable(indio_dev); } @@ -1225,15 +1535,36 @@ static int meson_sar_adc_remove(struct platform_device *pdev) static int __maybe_unused meson_sar_adc_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); + int ret; - return meson_sar_adc_hw_disable(indio_dev); + if (iio_buffer_enabled(indio_dev)) { + ret = meson_sar_adc_buffer_predisable(indio_dev); + if (ret) + return ret; + } + + ret = meson_sar_adc_hw_disable(indio_dev); + if (ret) + return ret; + + return 0; } static int __maybe_unused meson_sar_adc_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); + int ret; - return meson_sar_adc_hw_enable(indio_dev); + ret = meson_sar_adc_hw_enable(indio_dev); + if (ret) + return ret; + + if (iio_buffer_enabled(indio_dev)) { + ret = meson_sar_adc_buffer_postenable(indio_dev); + if (ret) + return ret; + } + return 0; } static SIMPLE_DEV_PM_OPS(meson_sar_adc_pm_ops, @@ -1251,6 +1582,6 @@ static struct platform_driver meson_sar_adc_driver = { module_platform_driver(meson_sar_adc_driver); -MODULE_AUTHOR("Martin Blumenstingl and Amlogic"); +MODULE_AUTHOR("Martin Blumenstingl "); MODULE_DESCRIPTION("Amlogic Meson SAR ADC driver"); MODULE_LICENSE("GPL v2");