Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion drivers/iio/adc/ad7124.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
#define AD7124_STATUS_POR_FLAG_MSK BIT(4)

/* AD7124_ADC_CONTROL */
#define AD7124_ADC_STATUS_EN_MSK BIT(10)
#define AD7124_ADC_STATUS_EN(x) FIELD_PREP(AD7124_ADC_STATUS_EN_MSK, x)
#define AD7124_ADC_CTRL_REF_EN_MSK BIT(8)
#define AD7124_ADC_CTRL_REF_EN(x) FIELD_PREP(AD7124_ADC_CTRL_REF_EN_MSK, x)
#define AD7124_ADC_CTRL_PWR_MSK GENMASK(7, 6)
Expand Down Expand Up @@ -519,12 +521,32 @@ static int ad7124_set_channel(struct ad_sigma_delta *sd, unsigned int slot,
return ret;
}

static int ad7124_append_status(struct ad_sigma_delta *sd, bool append)
{
struct ad7124_state *st = container_of(sd, struct ad7124_state, sd);
unsigned int adc_control = st->adc_control;
int ret;

adc_control &= ~AD7124_ADC_STATUS_EN_MSK;
adc_control |= AD7124_ADC_STATUS_EN(append);

ret = ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, adc_control);
if (ret < 0)
return ret;

st->adc_control = adc_control;

return 0;
}

static const struct ad_sigma_delta_info ad7124_sigma_delta_info = {
.set_channel = ad7124_set_channel,
.append_status = ad7124_append_status,
.set_mode = ad7124_set_mode,
.has_registers = true,
.addr_shift = 0,
.read_mask = BIT(6),
.status_ch_mask = GENMASK(3, 0),
.data_reg = AD7124_DATA,
.irq_flags = IRQF_TRIGGER_FALLING
};
Expand Down Expand Up @@ -914,8 +936,8 @@ static int ad7124_probe(struct spi_device *spi)

st->chip_info = info;

ad_sd_init(&st->sd, indio_dev, spi, &ad7124_sigma_delta_info);
st->sd.num_slots = AD7124_SEQUENCER_SLOTS;
ad_sd_init(&st->sd, indio_dev, spi, &ad7124_sigma_delta_info);

indio_dev->name = st->chip_info->name;
indio_dev->modes = INDIO_DIRECT_MODE;
Expand Down
53 changes: 46 additions & 7 deletions drivers/iio/adc/ad7173.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Licensed under the GPL-2.
*/

#include "linux/bitfield.h"
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/kernel.h>
Expand Down Expand Up @@ -77,6 +78,10 @@
#define AD7173_GPIO_GP_DATA1 BIT(1)
#define AD7173_GPIO_GP_DATA0 BIT(0)

#define AD7173_INTERFACE_DATA_STAT BIT(6)
#define AD7173_INTERFACE_DATA_STAT_EN(x) \
FIELD_PREP(AD7173_INTERFACE_DATA_STAT, x)

#define AD7173_SETUP_BIPOLAR BIT(12)
#define AD7173_SETUP_AREF_BUF (0x3 << 10)
#define AD7173_SETUP_AIN_BUF (0x3 << 8)
Expand Down Expand Up @@ -118,6 +123,7 @@ struct ad7173_state {
struct ad_sigma_delta sd;

const struct ad7173_device_info *info;
unsigned int *channel_ains;

#ifdef CONFIG_GPIOLIB
struct gpio_chip gpiochip;
Expand Down Expand Up @@ -438,11 +444,12 @@ static int ad7173_set_channel(struct ad_sigma_delta *sd, unsigned int slot,
unsigned int val;

if (channel == AD_SD_SLOT_DISABLE)
val = 0;
val = 0;
else
val = AD7173_CH_ENABLE | channel;
val = AD7173_CH_ENABLE | AD7173_CH_SETUP_SEL(slot) |
st->channel_ains[channel];

return ad_sd_write_reg(&st->sd, AD7173_REG_CH(slot), 2, val);
return ad_sd_write_reg(&st->sd, AD7173_REG_CH(channel), 2, val);
}

static int ad7173_set_mode(struct ad_sigma_delta *sd,
Expand All @@ -456,14 +463,33 @@ static int ad7173_set_mode(struct ad_sigma_delta *sd,
return ad_sd_write_reg(&st->sd, AD7173_REG_ADC_MODE, 2, st->adc_mode);
}

static int ad7173_append_status(struct ad_sigma_delta *sd, bool append)
{
struct ad7173_state *st = ad_sigma_delta_to_ad7173(sd);
unsigned int interface_mode = st->interface_mode;
int ret;

interface_mode &= ~AD7173_INTERFACE_DATA_STAT;
interface_mode |= AD7173_INTERFACE_DATA_STAT_EN(append);
ret = ad_sd_write_reg(&st->sd, AD7173_REG_INTERFACE_MODE, 2, interface_mode);
if (ret)
return ret;

st->interface_mode = interface_mode;

return 0;
}

static const struct ad_sigma_delta_info ad7173_sigma_delta_info = {
.set_channel = ad7173_set_channel,
.append_status = ad7173_append_status,
.prepare_channel = ad7173_prepare_channel,
.set_mode = ad7173_set_mode,
.has_registers = true,
.data_reg = AD7173_REG_DATA,
.addr_shift = 0,
.read_mask = BIT(6),
.status_ch_mask = GENMASK(3, 0),
.irq_flags = IRQF_TRIGGER_FALLING
};

Expand Down Expand Up @@ -633,7 +659,6 @@ static const struct iio_chan_spec ad7173_temp_channel_template = {
.type = IIO_TEMP,
.indexed = 1,
.channel = 0,
.address = AD7173_CH_ADDRESS(17, 18),
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
Expand All @@ -654,6 +679,7 @@ static int ad7173_of_parse_channel_config(struct iio_dev *indio_dev,
struct device_node *chan_node, *child;
struct iio_chan_spec *chan;
unsigned int num_ext_channels = 0;
unsigned int *channel_ains;
unsigned int num_channels = 0;
unsigned int scan_index = 0;
unsigned int chan_index = 0;
Expand All @@ -669,16 +695,29 @@ static int ad7173_of_parse_channel_config(struct iio_dev *indio_dev,
if (num_channels == 0)
return 0;

if (num_channels > st->info->num_channels) {
dev_err(indio_dev->dev.parent,
"Number of defined channels exceeds the maximum number of supported channels.\n");
return -EINVAL;
}

chan = devm_kcalloc(indio_dev->dev.parent, sizeof(*chan), num_channels,
GFP_KERNEL);
if (!chan)
return -ENOMEM;

channel_ains = devm_kcalloc(indio_dev->dev.parent, sizeof(*channel_ains),
num_channels, GFP_KERNEL);
if (!channel_ains)
return -ENOMEM;

st->channel_ains = channel_ains;
indio_dev->channels = chan;
indio_dev->num_channels = num_channels;

if (st->info->has_temp) {
*chan = ad7173_temp_channel_template;
channel_ains[scan_index] = AD7173_CH_ADDRESS(17, 18);
chan++;
scan_index++;
}
Expand Down Expand Up @@ -708,7 +747,8 @@ static int ad7173_of_parse_channel_config(struct iio_dev *indio_dev,


*chan = ad7173_channel_template;
chan->address = AD7173_CH_ADDRESS(ain[0], ain[1]);
chan->address = scan_index;
channel_ains[scan_index] = AD7173_CH_ADDRESS(ain[0], ain[1]);
chan->scan_index = scan_index;
chan->channel = ain[0];
chan->channel2 = ain[1];
Expand Down Expand Up @@ -747,9 +787,8 @@ static int ad7173_probe(struct spi_device *spi)
id = spi_get_device_id(spi);
st->info = &ad7173_device_info[id->driver_data];

ad_sd_init(&st->sd, indio_dev, spi, &ad7173_sigma_delta_info);

st->sd.num_slots = st->info->num_configs;
ad_sd_init(&st->sd, indio_dev, spi, &ad7173_sigma_delta_info);

spi_set_drvdata(spi, indio_dev);

Expand Down
70 changes: 66 additions & 4 deletions drivers/iio/adc/ad_sigma_delta.c
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,10 @@ static void ad_sd_prepare_transfer_msg(struct iio_dev *indio_dev)
else
data_reg = AD_SD_REG_DATA;

/* Status word will be appended to the sample during transfer */
if (sigma_delta->status_appended)
reg_size++;

BUG_ON(reg_size > 4);
/* We store reg_size bytes samples in a 32 bit word. Keep the upper
* reg_size bytes set to zero.
Expand Down Expand Up @@ -411,6 +415,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
indio_dev->channels[i].address);
if (ret)
goto err_predisable;
sigma_delta->slots[slot] = indio_dev->channels[i].address;
slot++;
}

Expand All @@ -419,9 +424,21 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
if (!sigma_delta->buf_data)
return -ENOMEM;

sigma_delta->active_slots = slot;
ad_sigma_delta_set_active_slots(sigma_delta, slot);
sigma_delta->current_slot = 0;

/*
* Activation of append_status will cause incrementation of the reg_size
* in ad_sd_prepare_transfer_msg() and the transfer size will include
* the status byte.
*/
if (sigma_delta->active_slots > 1) {
ret = ad_sigma_delta_append_status(sigma_delta, true);
if (ret)
return ret;
}

spi_bus_lock(sigma_delta->spi->master);
sigma_delta->bus_locked = true;
sigma_delta->keep_cs_asserted = true;
Expand Down Expand Up @@ -472,6 +489,8 @@ static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev)

sigma_delta->keep_cs_asserted = false;
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
if (sigma_delta->status_appended)
ad_sigma_delta_append_status(sigma_delta, false);

sigma_delta->bus_locked = false;
return spi_bus_unlock(sigma_delta->spi->master);
Expand All @@ -482,24 +501,58 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p)
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
unsigned int status_pos;
unsigned int reg_size;
int ret;

sigma_delta->current_slot++;

ret = spi_sync_locked(sigma_delta->spi, &sigma_delta->spi_msg);

reg_size = sigma_delta->spi_transfer[1].len;
if (sigma_delta->status_appended) {
u8 converted_channel;
/*
* If status_appended is active, reg_size was incremented in
* ad_sd_prepare_transfer_msg().
*
* The status byte will be the byte following the last
* sample byte.
*/
status_pos = reg_size - 1;
converted_channel = ((u8 *)sigma_delta->spi_transfer[1].rx_buf)[status_pos] &
sigma_delta->info->status_ch_mask;
if (converted_channel != sigma_delta->slots[sigma_delta->current_slot]) {
/*
* Desync occurred during continuous sampling of multiple channels.
* Drop this incomplete sample and start from first channel again.
*/
sigma_delta->current_slot = 0;
sigma_delta->spi_transfer[1].rx_buf = sigma_delta->buf_data;
sigma_delta->spi_transfer[1].rx_buf += 4 - reg_size;
goto irq_handled;
}

/*
* Get rid of the status byte and move samples to the right to
* comply with "Keep the upper reg_size bytes set to zero".
*/
memmove(sigma_delta->spi_transfer[1].rx_buf + 1, sigma_delta->spi_transfer[1].rx_buf,
reg_size - 1);
((u8 *)sigma_delta->spi_transfer[1].rx_buf)[0] = 0;
}

sigma_delta->current_slot++;
if (ret == 0 && sigma_delta->current_slot == sigma_delta->active_slots) {
iio_push_to_buffers_with_timestamp(indio_dev,
sigma_delta->buf_data, pf->timestamp);
sigma_delta->current_slot = 0;
sigma_delta->spi_transfer[1].rx_buf = sigma_delta->buf_data;
reg_size = sigma_delta->spi_transfer[1].len;
sigma_delta->spi_transfer[1].rx_buf += 4 - reg_size;
} else {
sigma_delta->spi_transfer[1].rx_buf +=
indio_dev->channels[0].scan_type.storagebits / 8;
}

irq_handled:
iio_trigger_notify_done(indio_dev->trig);
sigma_delta->irq_dis = false;
enable_irq(sigma_delta->spi->irq);
Expand Down Expand Up @@ -609,6 +662,11 @@ int devm_ad_sd_setup_buffer_and_trigger(struct device *dev, struct iio_dev *indi
if (spi_engine_offload_supported(sigma_delta->spi))
indio_dev->modes |= INDIO_BUFFER_HARDWARE;

sigma_delta->slots = devm_kcalloc(dev, sigma_delta->num_slots,
sizeof(*sigma_delta->slots), GFP_KERNEL);
if (!sigma_delta->slots)
return -ENOMEM;

ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
&iio_pollfunc_store_time,
&ad_sd_trigger_handler,
Expand All @@ -635,8 +693,12 @@ int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev,
{
sigma_delta->spi = spi;
sigma_delta->info = info;
sigma_delta->num_slots = 1;
sigma_delta->active_slots = 1;

/* If the field is unset, asume there can only be 1 slot. */
if (!sigma_delta->num_slots)
sigma_delta->num_slots = 1;

iio_device_set_drvdata(indio_dev, sigma_delta);

return 0;
Expand Down
22 changes: 22 additions & 0 deletions include/linux/iio/adc/ad_sigma_delta.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ struct iio_dev;
/**
* struct ad_sigma_delta_info - Sigma Delta driver specific callbacks and options
* @set_channel: Will be called to select the current channel, may be NULL.
* @append_status: Will be called to enable status append at the end of the sample, may be NULL.
* @prepare_channel: Will be called to prepare and configure a channel, may be
* NULL.
* @set_mode: Will be called to select the current mode, may be NULL.
Expand All @@ -42,6 +43,7 @@ struct iio_dev;
* @has_registers: true if the device has writable and readable registers, false
* if there is just one read-only sample data shift register.
* @addr_shift: Shift of the register address in the communications register.
* @status_ch_mask: Mask for the channel number stored in status register.
* @read_mask: Mask for the communications register having the read bit set.
* @data_reg: Address of the data register, if 0 the default address of 0x3 will
* be used.
Expand All @@ -50,13 +52,15 @@ struct iio_dev;
struct ad_sigma_delta_info {
int (*set_channel)(struct ad_sigma_delta *, unsigned int slot,
unsigned int channel);
int (*append_status)(struct ad_sigma_delta *, bool append);
int (*prepare_channel)(struct ad_sigma_delta *, unsigned int slot,
const struct iio_chan_spec *);
int (*set_mode)(struct ad_sigma_delta *, enum ad_sigma_delta_mode mode);
int (*postprocess_sample)(struct ad_sigma_delta *, unsigned int raw_sample);
bool has_registers;
unsigned int addr_shift;
unsigned int read_mask;
unsigned int status_ch_mask;
unsigned int data_reg;
unsigned long irq_flags;
};
Expand Down Expand Up @@ -88,6 +92,9 @@ struct ad_sigma_delta {
const struct ad_sigma_delta_info *info;
unsigned int active_slots;
unsigned int current_slot;
bool status_appended;
/* map slots to channels in order to know what to expect from devices */
unsigned int *slots;

struct spi_message spi_msg;
struct spi_transfer spi_transfer[2];
Expand Down Expand Up @@ -122,6 +129,21 @@ static inline int ad_sigma_delta_set_channel(struct ad_sigma_delta *sd,
return 0;
}

static inline int ad_sigma_delta_append_status(struct ad_sigma_delta *sd, bool append)
{
int ret;

if (sd->info->append_status) {
ret = sd->info->append_status(sd, append);
if (ret < 0)
return ret;

sd->status_appended = append;
}

return 0;
}

static inline int ad_sigma_delta_set_mode(struct ad_sigma_delta *sd,
unsigned int mode)
{
Expand Down