Commit 25179662 authored by Rene Habraken's avatar Rene Habraken
Browse files

backup modified ad9680 driver

parent b76fe430
/*
* Driver for AD9680 and similar high-speed Analog-to-Digital converters
*
* Copyright 2012-2017 Analog Devices Inc.
*
* Licensed under the GPL-2.
*/
#define DEBUG
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include "cf_axi_adc.h"
#define AD9680_REG_CHIP_ID_LOW 0x004
#define AD9680_REG_CHIP_ID_HIGH 0x005
#define AD9680_REG_DEVICE_INDEX 0x008
#define AD9680_REG_PAIR_INDEX 0x009
#define AD9680_REG_INPUT_FS_RANGE 0x025
#define AD9680_REG_CHIP_PIN_CTRL 0x040
#define AD9680_REG_OUTPUT_MODE 0x561
#define AD9680_REG_TEST_MODE 0x550
#define AD9680_REG_THRESH_CTRL 0x245
#define AD9680_REG_THRESH_HI_LSB 0x247
#define AD9680_REG_THRESH_HI_MSB 0x248
#define AD9680_REG_THRESH_LOW_LSB 0x249
#define AD9680_REG_THRESH_LOW_MSB 0x24A
#define AD9680_REG_CHIP_PIN_CTRL_MASK(chn) (0x07 << (3 * (chn)))
#define AD9680_TESTMODE_OFF 0x0
#define AD9680_TESTMODE_MIDSCALE_SHORT 0x1
#define AD9680_TESTMODE_POS_FULLSCALE 0x2
#define AD9680_TESTMODE_NEG_FULLSCALE 0x3
#define AD9680_TESTMODE_ALT_CHECKERBOARD 0x4
#define AD9680_TESTMODE_PN23_SEQ 0x5
#define AD9680_TESTMODE_PN9_SEQ 0x6
#define AD9680_TESTMODE_ONE_ZERO_TOGGLE 0x7
#define AD9680_TESTMODE_USER 0x8
#define AD9680_TESTMODE_RAMP 0xF
#define AD9680_OUTPUT_MODE_OFFSET_BINARY 0x0
#define AD9680_OUTPUT_MODE_TWOS_COMPLEMENT 0x1
#define CHIPID_AD9680 0xC5
#define CHIPID_AD9684 0xD2
#define CHIPID_AD9234 0xCE
#define CHIPID_AD9694 0xDB
#define CHIPID_AD9094 0xE8
enum {
ID_AD9234,
ID_AD9680,
ID_AD9680_x2,
ID_AD9684,
ID_AD9694,
ID_AD9094,
};
enum ad9680_sysref_mode {
AD9680_SYSREF_DISABLED,
AD9680_SYSREF_CONTINUOUS,
AD9680_SYSREF_ONESHOT
};
struct ad9680_sysref_config {
enum ad9680_sysref_mode mode;
bool capture_falling_edge;
bool valid_falling_edge;
};
struct ad9680_jesd204_link_config {
uint8_t did;
uint8_t bid;
uint8_t num_lanes;
uint8_t num_converters;
uint8_t octets_per_frame;
uint8_t frames_per_multiframe;
uint8_t bits_per_sample;
uint8_t converter_resolution;
uint8_t lid[4];
uint8_t lane_mux[4];
bool scrambling;
uint8_t subclass;
struct ad9680_sysref_config sysref;
};
static int ad9680_spi_read(struct spi_device *spi, unsigned int reg)
{
unsigned char buf[3];
int ret;
if (spi) {
buf[0] = 0x80 | (reg >> 8);
buf[1] = reg & 0xFF;
ret = spi_write_then_read(spi, &buf[0], 2, &buf[2], 1);
dev_dbg(&spi->dev, "%s: REG: 0x%X VAL: 0x%X (%d)\n",
__func__, reg, buf[2], ret);
if (ret < 0)
return ret;
return buf[2];
}
return -ENODEV;
}
static int ad9680_spi_write(struct spi_device *spi, unsigned int reg,
unsigned int val)
{
unsigned char buf[3];
int ret;
if (spi) {
buf[0] = reg >> 8;
buf[1] = reg & 0xFF;
buf[2] = val;
ret = spi_write_then_read(spi, buf, 3, NULL, 0);
if (ret < 0)
return ret;
dev_dbg(&spi->dev, "%s: REG: 0x%X VAL: 0x%X (%d)\n",
__func__, reg, val, ret);
return 0;
}
return -ENODEV;
}
static int ad9680_reg_access(struct iio_dev *indio_dev, unsigned int reg,
unsigned int writeval, unsigned int *readval)
{
struct axiadc_converter *conv = iio_device_get_drvdata(indio_dev);
struct spi_device *spi = conv->spi;
int ret;
if (readval == NULL)
return ad9680_spi_write(spi, reg, writeval);
ret = ad9680_spi_read(spi, reg);
if (ret < 0)
return ret;
*readval = ret;
return 0;
}
static int ad9680_select_channel(struct axiadc_converter *conv,
int chan)
{
unsigned int device, pair;
int ret;
if (chan >= 0) {
device = BIT(chan & 0x1);
pair = BIT((chan >> 1) & 1);
} else {
device = 0x3;
pair = 0x3;
}
ret = ad9680_spi_write(conv->spi, AD9680_REG_DEVICE_INDEX, device);
if (ret < 0)
return ret;
return ad9680_spi_write(conv->spi, AD9680_REG_PAIR_INDEX, pair);
}
static int ad9680_channel_write(struct axiadc_converter *conv,
unsigned int chan, unsigned int reg, unsigned int val)
{
int ret;
ret = ad9680_select_channel(conv, chan);
ret |= ad9680_spi_write(conv->spi, reg, val);
ret |= ad9680_select_channel(conv, -1);
return ret;
}
static int ad9680_channel_read(struct axiadc_converter *conv,
unsigned int chan, unsigned int reg)
{
int ret;
ad9680_select_channel(conv, chan);
ret = ad9680_spi_read(conv->spi, reg);
ad9680_select_channel(conv, -1);
return ret;
}
static unsigned int ad9680_pnsel_to_testmode(enum adc_pn_sel sel)
{
switch (sel) {
case ADC_PN9:
return AD9680_TESTMODE_PN9_SEQ;
case ADC_PN23A:
return AD9680_TESTMODE_PN23_SEQ;
default:
return AD9680_TESTMODE_OFF;
}
}
static int ad9680_outputmode_set(struct spi_device *spi, unsigned int mode)
{
int ret;
ret = ad9680_spi_write(spi, AD9680_REG_OUTPUT_MODE, mode);
if (ret < 0)
return ret;
return ad9680_spi_write(spi, AD9680_REG_TEST_MODE,
AD9680_TESTMODE_OFF);
}
static int ad9680_testmode_set(struct iio_dev *indio_dev, unsigned int chan,
unsigned int mode)
{
struct axiadc_converter *conv = iio_device_get_drvdata(indio_dev);
ad9680_channel_write(conv, chan, AD9680_REG_TEST_MODE, mode);
conv->testmode[chan] = mode;
return 0;
}
static int ad9680_set_pnsel(struct iio_dev *indio_dev, unsigned int chan,
enum adc_pn_sel sel)
{
struct axiadc_converter *conv = iio_device_get_drvdata(indio_dev);
unsigned int mode = ad9680_pnsel_to_testmode(sel);
unsigned int output_mode;
int ret;
output_mode = conv->adc_output_mode;
if (mode != AD9680_TESTMODE_OFF)
output_mode &= ~AD9680_OUTPUT_MODE_TWOS_COMPLEMENT;
ret = ad9680_spi_write(conv->spi, AD9680_REG_OUTPUT_MODE, output_mode);
if (ret < 0)
return ret;
return ad9680_testmode_set(indio_dev, chan, mode);
}
static irqreturn_t ad9680_event_handler(struct axiadc_converter *conv,
unsigned int chn)
{
u64 event = IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, chn,
IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING);
s64 timestamp = iio_get_time_ns(conv->indio_dev);
if (conv->indio_dev)
iio_push_event(conv->indio_dev, event, timestamp);
return IRQ_HANDLED;
}
static irqreturn_t ad9680_fdA_handler(int irq, void *private)
{
return ad9680_event_handler(private, 0);
}
static irqreturn_t ad9680_fdB_handler(int irq, void *private)
{
return ad9680_event_handler(private, 1);
}
static int ad9680_read_thresh(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, enum iio_event_type type,
enum iio_event_direction dir, enum iio_event_info info, int *val,
int *val2)
{
struct axiadc_converter *conv = iio_device_get_drvdata(indio_dev);
struct spi_device *spi = conv->spi;
u16 low, high;
mutex_lock(&indio_dev->mlock);
low = (ad9680_spi_read(spi, AD9680_REG_THRESH_LOW_MSB) << 8) |
ad9680_spi_read(spi, AD9680_REG_THRESH_LOW_LSB);
high = (ad9680_spi_read(spi, AD9680_REG_THRESH_HI_MSB) << 8) |
ad9680_spi_read(spi, AD9680_REG_THRESH_HI_LSB);
mutex_unlock(&indio_dev->mlock);
switch (info) {
case IIO_EV_INFO_HYSTERESIS:
*val = high - low;
break;
case IIO_EV_INFO_VALUE:
*val = high;
break;
default:
return -EINVAL;
}
return IIO_VAL_INT;
}
static int ad9680_read_thresh_en(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, enum iio_event_type type,
enum iio_event_direction dir)
{
struct axiadc_converter *conv = iio_device_get_drvdata(indio_dev);
struct spi_device *spi = conv->spi;
int ret;
ret = ad9680_spi_read(spi, AD9680_REG_CHIP_PIN_CTRL);
if (ret < 0)
return ret;
else
return !(ret & AD9680_REG_CHIP_PIN_CTRL_MASK(chan->channel));
}
static int ad9680_write_thresh(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, enum iio_event_type type,
enum iio_event_direction dir, enum iio_event_info info, int val,
int val2)
{
struct axiadc_converter *conv = iio_device_get_drvdata(indio_dev);
struct spi_device *spi = conv->spi;
int ret = 0;
int low, high;
mutex_lock(&indio_dev->mlock);
high = (ad9680_spi_read(spi, AD9680_REG_THRESH_HI_MSB) << 8) |
ad9680_spi_read(spi, AD9680_REG_THRESH_HI_LSB);
switch (info) {
case IIO_EV_INFO_HYSTERESIS:
if (val < 0) {
ret = -EINVAL;
goto unlock;
}
low = high - val;
break;
case IIO_EV_INFO_VALUE:
if (val > 0x7FF) {
ret = -EINVAL;
goto unlock;
}
ad9680_spi_write(spi, AD9680_REG_THRESH_HI_MSB, val >> 8);
ad9680_spi_write(spi, AD9680_REG_THRESH_HI_LSB, val & 0xFF);
/* Calculate the new lower threshold limit */
low = (ad9680_spi_read(spi, AD9680_REG_THRESH_LOW_MSB) << 8) |
ad9680_spi_read(spi, AD9680_REG_THRESH_LOW_LSB);
low = val - high + low;
break;
default:
ret = -EINVAL;
goto unlock;
}
if (low < 0)
low = 0;
ad9680_spi_write(spi, AD9680_REG_THRESH_LOW_MSB, low >> 8);
ad9680_spi_write(spi, AD9680_REG_THRESH_LOW_LSB, low & 0xFF);
unlock:
mutex_unlock(&indio_dev->mlock);
return ret;
}
static int ad9680_write_thresh_en(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, enum iio_event_type type,
enum iio_event_direction dir, int state)
{
struct axiadc_converter *conv = iio_device_get_drvdata(indio_dev);
struct spi_device *spi = conv->spi;
int ret;
mutex_lock(&indio_dev->mlock);
ret = ad9680_spi_read(spi, AD9680_REG_CHIP_PIN_CTRL);
if (ret < 0)
goto err_unlock;
if (state)
ret &= ~AD9680_REG_CHIP_PIN_CTRL_MASK(chan->channel);
else
ret |= AD9680_REG_CHIP_PIN_CTRL_MASK(chan->channel);
ret = ad9680_spi_write(spi, AD9680_REG_CHIP_PIN_CTRL, ret);
err_unlock:
mutex_unlock(&indio_dev->mlock);
return ret;
}
static const int ad9680_scale_table[][2] = {
{1460, 0x08}, {1580, 0x09}, {1700, 0x0A}, {1820, 0x0B},
{1940, 0x00}, {2060, 0x0C},
};
static const int ad9694_scale_table[][2] = {
{1440, 0xa}, {1560, 0xb}, {1680, 0xc}, {1800, 0xd},
{1920, 0xe}, {2040, 0xf}, {2160, 0x0},
};
static void ad9680_scale(struct axiadc_converter *conv, int index,
unsigned int *val, unsigned int *val2)
{
unsigned int tmp;
if (index > conv->chip_info->num_scales) {
*val = 0;
*val2 = 0;
return;
}
tmp = (conv->chip_info->scale_table[index][0] * 1000000ULL) >>
conv->chip_info->channel[0].scan_type.realbits;
*val = tmp / 1000000;
*val2 = tmp % 1000000;
}
static ssize_t ad9680_show_scale_available(struct iio_dev *indio_dev,
uintptr_t private, const struct iio_chan_spec *chan, char *buf)
{
struct axiadc_converter *conv = iio_device_get_drvdata(indio_dev);
unsigned int scale[2];
int i, len = 0;
for (i = 0; i < conv->chip_info->num_scales; i++) {
ad9680_scale(conv, i, &scale[0], &scale[1]);
len += sprintf(buf + len, "%u.%06u ", scale[0], scale[1]);
}
/* replace last space with a newline */
buf[len - 1] = '\n';
return len;
}
static int ad9680_get_scale(struct axiadc_converter *conv,
const struct iio_chan_spec *chan, int *val, int *val2)
{
unsigned int vref_val;
unsigned int i;
switch (conv->id) {
case CHIPID_AD9694:
case CHIPID_AD9094:
vref_val = ad9680_channel_read(conv, chan->channel, 0x1910);
break;
default:
vref_val = ad9680_spi_read(conv->spi, AD9680_REG_INPUT_FS_RANGE);
break;
}
vref_val &= 0xf;
for (i = 0; i < conv->chip_info->num_scales; i++) {
if (vref_val == conv->chip_info->scale_table[i][1])
break;
}
ad9680_scale(conv, i, val, val2);
return IIO_VAL_INT_PLUS_MICRO;
}
static int ad9680_set_scale(struct axiadc_converter *conv,
const struct iio_chan_spec *chan, int val, int val2)
{
unsigned int scale_val[2];
unsigned int scale_raw;
unsigned int i;
for (i = 0; i < conv->chip_info->num_scales; i++) {
ad9680_scale(conv, i, &scale_val[0], &scale_val[1]);
if (scale_val[0] != val || scale_val[1] != val2)
continue;
scale_raw = conv->chip_info->scale_table[i][1];
switch (conv->id) {
case CHIPID_AD9694:
case CHIPID_AD9094:
ad9680_channel_write(conv, chan->channel, 0x1910,
scale_raw);
break;
default:
ad9680_spi_write(conv->spi, AD9680_REG_INPUT_FS_RANGE,
scale_raw);
break;
}
return 0;
}
return -EINVAL;
}
static int ad9680_testmode_read(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct axiadc_converter *conv = iio_device_get_drvdata(indio_dev);
return conv->testmode[chan->channel];
}
static int ad9680_testmode_write(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, unsigned int item)
{
int ret;
mutex_lock(&indio_dev->mlock);
ret = ad9680_testmode_set(indio_dev, chan->channel, item);
mutex_unlock(&indio_dev->mlock);
return ret;
}
static const char * const ad9680_testmodes[] = {
[AD9680_TESTMODE_OFF] = "off",
[AD9680_TESTMODE_MIDSCALE_SHORT] = "midscale_short",
[AD9680_TESTMODE_POS_FULLSCALE] = "pos_fullscale",
[AD9680_TESTMODE_NEG_FULLSCALE] = "neg_fullscale",
[AD9680_TESTMODE_ALT_CHECKERBOARD] = "checkerboard",
[AD9680_TESTMODE_PN23_SEQ] = "pn_long",
[AD9680_TESTMODE_PN9_SEQ] = "pn_short",
[AD9680_TESTMODE_ONE_ZERO_TOGGLE] = "one_zero_toggle",
[AD9680_TESTMODE_USER] = "user",
[AD9680_TESTMODE_RAMP] = "ramp",
};
static const struct iio_enum ad9680_testmode_enum = {
.items = ad9680_testmodes,
.num_items = ARRAY_SIZE(ad9680_testmodes),
.set = ad9680_testmode_write,
.get = ad9680_testmode_read,
};
static struct iio_chan_spec_ext_info axiadc_ext_info[] = {
IIO_ENUM("test_mode", IIO_SEPARATE, &ad9680_testmode_enum),
IIO_ENUM_AVAILABLE("test_mode", &ad9680_testmode_enum),
{
.name = "scale_available",
.read = ad9680_show_scale_available,
.shared = true,
},
{},
};
static const struct iio_event_spec ad9680_events[] = {
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_RISING,
.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |