iio: saradc: add support for g12a

PD#156734: iio: saradc: add support for g12a

Add period sampling mode for g12a and next, here are a brief overview:

1. Enable period sampling mode

echo 1  > /sys/bus/iio/devices/iio:device0/buffer/enable

2. Set the sampling interval
The sampling interval can be set in dts by the two params:
<1> amlogic,delay-per-tick
<2> amlogic,ticks-per-period

If there are no define the params above, and the channel list is
sampled every 1ms by default.

3. Read sampling data from buffer
this currently provides no buffer events so it is up to userspace to
work out how often to read from the buffer. therefore, if you want to
read continuous sampling data from buffer, please use application.

Eg:
[kernel]/tools/iio/iio_generic_buffer.c

Change-Id: I27f904e2736768eacc9d73ff24078bd659e37049
Signed-off-by: Xingyu Chen <xingyu.chen@amlogic.com>
This commit is contained in:
Xingyu Chen
2018-01-18 16:25:35 +08:00
committed by Yixun Lan
parent 948c49455e
commit 2b0ffa74a5
7 changed files with 426 additions and 76 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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 = <GIC_SPI 200 IRQ_TYPE_EDGE_RISING>;
reg = <0x0 0xff809000 0x0 0x48>;
};
soc {
compatible = "simple-bus";
#address-cells = <2>;

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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 <linux/bitfield.h>
@@ -35,6 +35,9 @@
#include <linux/amlogic/iomap.h>
#include <dt-bindings/iio/adc/amlogic-saradc.h>
#include <asm/barrier.h>
#include <linux/iio/buffer.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/slab.h>
#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, &regval);
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, &regval);
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 <martin.blumenstingl@googlemail.com> and Amlogic");
MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
MODULE_DESCRIPTION("Amlogic Meson SAR ADC driver");
MODULE_LICENSE("GPL v2");